From 6ec244c99c7555f24639c09c274e2d57a968e27d Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 16:13:42 -0800 Subject: [PATCH 001/378] copy valkey scripts from ProxmoxVE/main --- ct/valkey.sh | 45 ++++++++++++++++++++++++++++++++ frontend/public/json/valkey.json | 35 +++++++++++++++++++++++++ install/valkey-install.sh | 26 ++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 ct/valkey.sh create mode 100644 frontend/public/json/valkey.json create mode 100644 install/valkey-install.sh diff --git a/ct/valkey.sh b/ct/valkey.sh new file mode 100644 index 000000000..6e6039f30 --- /dev/null +++ b/ct/valkey.sh @@ -0,0 +1,45 @@ +#!/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: pshankinclarke (lazarillo) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://valkey.io/ + +APP="Valkey" +var_tags="${var_tags:-database}" +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 [[ ! -f /lib/systemd/system/valkey-server.service ]]; then + msg_error "No Valkey Installation Found!" + exit + fi + msg_info "Updating Valkey LXC" + $STD apt update + $STD apt -y upgrade + msg_ok "Updated Valkey LXC" + 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}:6379${CL}" diff --git a/frontend/public/json/valkey.json b/frontend/public/json/valkey.json new file mode 100644 index 000000000..bafe38af8 --- /dev/null +++ b/frontend/public/json/valkey.json @@ -0,0 +1,35 @@ +{ + "name": "Valkey", + "slug": "valkey", + "categories": [ + 9 + ], + "date_created": "2025-11-24", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 6379, + "documentation": "https://valkey.io/docs/", + "config_path": "/etc/valkey/valkey.conf", + "website": "https://valkey.io/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/valkey.webp", + "description": "Valkey is an open source (BSD) high-performance key/value datastore that supports a variety of workloads such as caching, message queues, and can act as a primary database. The project is backed by the Linux Foundation, ensuring it will remain open source forever.", + "install_methods": [ + { + "type": "default", + "script": "ct/valkey.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/valkey-install.sh b/install/valkey-install.sh new file mode 100644 index 000000000..6a0a1441f --- /dev/null +++ b/install/valkey-install.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: pshankinclarke (lazarillo) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://valkey.io/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Valkey" +$STD apt update +$STD apt install -y valkey +sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf +systemctl enable -q --now valkey-server +systemctl restart valkey-server +msg_ok "Installed Valkey" + +motd_ssh +customize +cleanup_lxc From 7f789ea4c4a823a1915eb2c1d8d11aaaeb5eb347 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 12:04:58 -0800 Subject: [PATCH 002/378] test auth --- ct/valkey.sh | 2 +- install/valkey-install.sh | 4 +++- misc/build.func | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ct/valkey.sh b/ct/valkey.sh index 6e6039f30..99825e1df 100644 --- a/ct/valkey.sh +++ b/ct/valkey.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/pshankinclarke/ProxmoxVED/refs/heads/valkey-bind-fix/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/valkey-install.sh b/install/valkey-install.sh index 6a0a1441f..9ae758351 100644 --- a/install/valkey-install.sh +++ b/install/valkey-install.sh @@ -15,8 +15,10 @@ update_os msg_info "Installing Valkey" $STD apt update -$STD apt install -y valkey +$STD apt install -y valkey openssl sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf +PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" +sed -ri "s|(# *)?requirepass .*|requirepass $PASS|" /etc/valkey/valkey.conf systemctl enable -q --now valkey-server systemctl restart valkey-server msg_ok "Installed Valkey" diff --git a/misc/build.func b/misc/build.func index e3ef6b1f8..2ef7806cc 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2825,7 +2825,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - 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/pshankinclarke/ProxmoxVED/refs/heads/valkey-bind-fix/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -2909,7 +2909,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-bind-fix/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From 237517e986231e950d149feb83e54022615c935e Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 13:36:08 -0800 Subject: [PATCH 003/378] fix urls, simplify auth --- ct/valkey.sh | 2 +- install/valkey-install.sh | 2 +- misc/build.func | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/valkey.sh b/ct/valkey.sh index 99825e1df..5c766cc58 100644 --- a/ct/valkey.sh +++ b/ct/valkey.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-bind-fix/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-auth/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/valkey-install.sh b/install/valkey-install.sh index 9ae758351..a7d275b00 100644 --- a/install/valkey-install.sh +++ b/install/valkey-install.sh @@ -18,7 +18,7 @@ $STD apt update $STD apt install -y valkey openssl sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" -sed -ri "s|(# *)?requirepass .*|requirepass $PASS|" /etc/valkey/valkey.conf +echo "requirepass $PASS" >> /etc/valkey/valkey.conf systemctl enable -q --now valkey-server systemctl restart valkey-server msg_ok "Installed Valkey" diff --git a/misc/build.func b/misc/build.func index 2ef7806cc..25a6e2d18 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2825,7 +2825,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-bind-fix/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-auth/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -2909,7 +2909,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-bind-fix/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-auth/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From be610fe0c09ae77a328a3a02c4fdf39a27643581 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 13:51:50 -0800 Subject: [PATCH 004/378] save db pass to file, disable protected mode, user msg --- install/valkey-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/valkey-install.sh b/install/valkey-install.sh index a7d275b00..ca9488fe5 100644 --- a/install/valkey-install.sh +++ b/install/valkey-install.sh @@ -19,9 +19,13 @@ $STD apt install -y valkey openssl sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" echo "requirepass $PASS" >> /etc/valkey/valkey.conf +echo "$PASS" >/root/valkey_pass.txt +chmod 600 /root/valkey_pass.txt +sed -i 's/^protected-mode .*/protected-mode no/' /etc/valkey/valkey.conf systemctl enable -q --now valkey-server systemctl restart valkey-server msg_ok "Installed Valkey" +msg "Valkey password saved to /root/valkey_pass.txt" motd_ssh customize From a2015991387e619b2c435f70791e9deeb66f5f7c Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 14:00:30 -0800 Subject: [PATCH 005/378] fix msg --- install/valkey-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/valkey-install.sh b/install/valkey-install.sh index ca9488fe5..c6bf9f624 100644 --- a/install/valkey-install.sh +++ b/install/valkey-install.sh @@ -25,7 +25,7 @@ sed -i 's/^protected-mode .*/protected-mode no/' /etc/valkey/valkey.conf systemctl enable -q --now valkey-server systemctl restart valkey-server msg_ok "Installed Valkey" -msg "Valkey password saved to /root/valkey_pass.txt" +msg_info "Valkey password saved to /root/valkey_pass.txt" motd_ssh customize From 05169d52f5e16a486b28205742c6903b7760dfd0 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 14:09:46 -0800 Subject: [PATCH 006/378] switch to msg_ok --- install/valkey-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/valkey-install.sh b/install/valkey-install.sh index c6bf9f624..4b2ef7e2c 100644 --- a/install/valkey-install.sh +++ b/install/valkey-install.sh @@ -25,7 +25,7 @@ sed -i 's/^protected-mode .*/protected-mode no/' /etc/valkey/valkey.conf systemctl enable -q --now valkey-server systemctl restart valkey-server msg_ok "Installed Valkey" -msg_info "Valkey password saved to /root/valkey_pass.txt" +msg_ok "Valkey password saved to /root/valkey_pass.txt" motd_ssh customize From 193e120fce36adfb50a44fbe895538fadcbb8bb5 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 15:03:19 -0800 Subject: [PATCH 007/378] try w/ protected-mode enabled --- install/valkey-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/valkey-install.sh b/install/valkey-install.sh index 4b2ef7e2c..a3d121ebe 100644 --- a/install/valkey-install.sh +++ b/install/valkey-install.sh @@ -21,7 +21,6 @@ PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" echo "requirepass $PASS" >> /etc/valkey/valkey.conf echo "$PASS" >/root/valkey_pass.txt chmod 600 /root/valkey_pass.txt -sed -i 's/^protected-mode .*/protected-mode no/' /etc/valkey/valkey.conf systemctl enable -q --now valkey-server systemctl restart valkey-server msg_ok "Installed Valkey" From 39a1b17b6569ddb45aa85f657af79289b45f3a3e Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 15:22:59 -0800 Subject: [PATCH 008/378] update doc --- frontend/public/json/valkey.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/valkey.json b/frontend/public/json/valkey.json index bafe38af8..f441a1148 100644 --- a/frontend/public/json/valkey.json +++ b/frontend/public/json/valkey.json @@ -31,5 +31,5 @@ "username": null, "password": null }, - "notes": [] + "notes": ["On first launch, a random password is set for the default user using `requirepass`; the generated password is stored in `/root/.valkey_password`." ] } From f6a1b9f9f8104ac95d58e805888d5b3cbfa66252 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 15:29:50 -0800 Subject: [PATCH 009/378] fix typo --- frontend/public/json/valkey.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/valkey.json b/frontend/public/json/valkey.json index f441a1148..59e8059ed 100644 --- a/frontend/public/json/valkey.json +++ b/frontend/public/json/valkey.json @@ -31,5 +31,5 @@ "username": null, "password": null }, - "notes": ["On first launch, a random password is set for the default user using `requirepass`; the generated password is stored in `/root/.valkey_password`." ] + "notes": ["On first launch, a random password is set for the default user using `requirepass`; the generated password is stored in `/root/.valkey_pass.txt inside the container`." ] } From b856c223cda36b9d69958bd60cb73776df8cd5ba Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 15:38:41 -0800 Subject: [PATCH 010/378] revert urls to upstream --- ct/valkey.sh | 2 +- misc/build.func | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/valkey.sh b/ct/valkey.sh index 5c766cc58..6e6039f30 100644 --- a/ct/valkey.sh +++ b/ct/valkey.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-auth/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: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 25a6e2d18..e3ef6b1f8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2825,7 +2825,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-auth/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -2909,7 +2909,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-auth/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From 70791715d53a2c5ea43b4034dda455445a91b83c Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 20:48:28 -0800 Subject: [PATCH 011/378] fork redis alpine scripts --- ct/alpine-valkey.sh | 72 ++++++++++++++++++++++++++++++++ install/alpine-valkey-install.sh | 24 +++++++++++ 2 files changed, 96 insertions(+) create mode 100644 ct/alpine-valkey.sh create mode 100644 install/alpine-valkey-install.sh diff --git a/ct/alpine-valkey.sh b/ct/alpine-valkey.sh new file mode 100644 index 000000000..a42216b9f --- /dev/null +++ b/ct/alpine-valkey.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: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://redis.io/ + +APP="Alpine-Redis" +var_tags="${var_tags:-alpine;database}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +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() { + if ! apk -e info newt >/dev/null 2>&1; then + apk add -q newt + fi + LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + while true; do + CHOICE=$( + whiptail --backtitle "Proxmox VE Helper Scripts" --title "Redis Management" --menu "Select option" 11 58 3 \ + "1" "Update Redis" \ + "2" "Allow 0.0.0.0 for listening" \ + "3" "Allow only ${LXCIP} for listening" 3>&2 2>&1 1>&3 + ) + exit_status=$? + if [ $exit_status == 1 ]; then + clear + exit-script + fi + header_info + case $CHOICE in + 1) + msg_info "Updating Redis" + apk update && apk upgrade redis + rc-service redis restart + msg_ok "Updated successfully!" + exit + ;; + 2) + msg_info "Setting Redis to listen on all interfaces" + sed -i 's/^bind .*/bind 0.0.0.0/' /etc/redis.conf + rc-service redis restart + msg_ok "Redis now listens on all interfaces!" + exit + ;; + 3) + msg_info "Setting Redis to listen only on ${LXCIP}" + sed -i "s/^bind .*/bind ${LXCIP}/" /etc/redis.conf + rc-service redis restart + msg_ok "Redis now listens only on ${LXCIP}!" + exit + ;; + esac + done +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${APP} should be reachable on port 6379. + ${BL}redis-cli -h ${IP} -p 6379${CL} \n" diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh new file mode 100644 index 000000000..c04db58da --- /dev/null +++ b/install/alpine-valkey-install.sh @@ -0,0 +1,24 @@ +#!/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://redis.io/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Redis" +$STD apk add redis +$STD sed -i 's/^bind .*/bind 0.0.0.0/' /etc/redis.conf +$STD rc-update add redis default +$STD rc-service redis start +msg_ok "Installed Redis" + +motd_ssh +customize From 7465fc10447a90d9bd08146c2b799721c311cf26 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 21:41:21 -0800 Subject: [PATCH 012/378] Add Alpine-Valkey LXC support --- ct/alpine-valkey.sh | 36 ++++++++++++++++---------------- install/alpine-valkey-install.sh | 16 +++++++------- misc/build.func | 4 ++-- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/ct/alpine-valkey.sh b/ct/alpine-valkey.sh index a42216b9f..6347b830d 100644 --- a/ct/alpine-valkey.sh +++ b/ct/alpine-valkey.sh @@ -1,11 +1,11 @@ #!/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/pshankinclarke/ProxmoxVED/refs/heads/valkey-alpine/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) +# Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://redis.io/ +# Source: https://valkey.io/ -APP="Alpine-Redis" +APP="Alpine-Valkey" var_tags="${var_tags:-alpine;database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" @@ -26,8 +26,8 @@ function update_script() { LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) while true; do CHOICE=$( - whiptail --backtitle "Proxmox VE Helper Scripts" --title "Redis Management" --menu "Select option" 11 58 3 \ - "1" "Update Redis" \ + whiptail --backtitle "Proxmox VE Helper Scripts" --title "Valkey Management" --menu "Select option" 11 58 3 \ + "1" "Update Valkey" \ "2" "Allow 0.0.0.0 for listening" \ "3" "Allow only ${LXCIP} for listening" 3>&2 2>&1 1>&3 ) @@ -39,24 +39,24 @@ function update_script() { header_info case $CHOICE in 1) - msg_info "Updating Redis" - apk update && apk upgrade redis - rc-service redis restart + msg_info "Updating Valkey" + apk update && apk upgrade valkey + rc-service valkey restart msg_ok "Updated successfully!" exit ;; 2) - msg_info "Setting Redis to listen on all interfaces" - sed -i 's/^bind .*/bind 0.0.0.0/' /etc/redis.conf - rc-service redis restart - msg_ok "Redis now listens on all interfaces!" + msg_info "Setting Valkey to listen on all interfaces" + sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf + rc-service valkey restart + msg_ok "Valkey now listens on all interfaces!" exit ;; 3) - msg_info "Setting Redis to listen only on ${LXCIP}" - sed -i "s/^bind .*/bind ${LXCIP}/" /etc/redis.conf - rc-service redis restart - msg_ok "Redis now listens only on ${LXCIP}!" + msg_info "Setting Valkey to listen only on ${LXCIP}" + sed -i "s/^bind .*/bind ${LXCIP}/" /etc/valkey/valkey.conf + rc-service valkey restart + msg_ok "Valkey now listens only on ${LXCIP}!" exit ;; esac @@ -69,4 +69,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${APP} should be reachable on port 6379. - ${BL}redis-cli -h ${IP} -p 6379${CL} \n" + ${BL}valkey-cli -h ${IP} -p 6379${CL} \n" diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index c04db58da..d4515d43b 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) +# Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://redis.io/ +# Source: https://valkey.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -13,12 +13,12 @@ setting_up_container network_check update_os -msg_info "Installing Redis" -$STD apk add redis -$STD sed -i 's/^bind .*/bind 0.0.0.0/' /etc/redis.conf -$STD rc-update add redis default -$STD rc-service redis start -msg_ok "Installed Redis" +msg_info "Installing Valkey" +$STD apk add valkey valkey-openrc +$STD sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf +$STD rc-update add valkey default +$STD rc-service valkey start +msg_ok "Installed Valkey" motd_ssh customize diff --git a/misc/build.func b/misc/build.func index e3ef6b1f8..b694ec9cf 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2825,7 +2825,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - 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/pshankinclarke/ProxmoxVED/refs/heads/valkey-alpine/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -2909,7 +2909,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-alpine/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From d7265db7088e2eb18ef278b6e35fe29acf314bbd Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 21:53:10 -0800 Subject: [PATCH 013/378] get valkey-cli --- install/alpine-valkey-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index d4515d43b..bac37a357 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Valkey" -$STD apk add valkey valkey-openrc +$STD apk add valkey valkey-openrc valkey-cli $STD sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf $STD rc-update add valkey default $STD rc-service valkey start From c8e5dba9c64f18780e40ece7aec11f3a387304e6 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Tue, 2 Dec 2025 20:33:04 -0800 Subject: [PATCH 014/378] fix unbound variable issue --- install/alpine-valkey-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index bac37a357..ce2083d96 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -5,6 +5,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://valkey.io/ +: "${IPV6_METHOD:=none}" source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 From 79a8b33d079480f7f08cf2a768f04e550d14600e Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Tue, 2 Dec 2025 21:06:18 -0800 Subject: [PATCH 015/378] revert urls to upstream --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index b694ec9cf..e3ef6b1f8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2825,7 +2825,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-alpine/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -2909,7 +2909,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-alpine/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From 9d1481232fcf65ecbb999338d840f0ed47a281fa Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Tue, 2 Dec 2025 21:09:07 -0800 Subject: [PATCH 016/378] cleanup --- ct/valkey.sh | 45 -------------------------------- frontend/public/json/valkey.json | 35 ------------------------- install/valkey-install.sh | 31 ---------------------- 3 files changed, 111 deletions(-) delete mode 100644 ct/valkey.sh delete mode 100644 frontend/public/json/valkey.json delete mode 100644 install/valkey-install.sh diff --git a/ct/valkey.sh b/ct/valkey.sh deleted file mode 100644 index 6e6039f30..000000000 --- a/ct/valkey.sh +++ /dev/null @@ -1,45 +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: pshankinclarke (lazarillo) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://valkey.io/ - -APP="Valkey" -var_tags="${var_tags:-database}" -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 [[ ! -f /lib/systemd/system/valkey-server.service ]]; then - msg_error "No Valkey Installation Found!" - exit - fi - msg_info "Updating Valkey LXC" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated Valkey LXC" - 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}:6379${CL}" diff --git a/frontend/public/json/valkey.json b/frontend/public/json/valkey.json deleted file mode 100644 index 59e8059ed..000000000 --- a/frontend/public/json/valkey.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Valkey", - "slug": "valkey", - "categories": [ - 9 - ], - "date_created": "2025-11-24", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 6379, - "documentation": "https://valkey.io/docs/", - "config_path": "/etc/valkey/valkey.conf", - "website": "https://valkey.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/valkey.webp", - "description": "Valkey is an open source (BSD) high-performance key/value datastore that supports a variety of workloads such as caching, message queues, and can act as a primary database. The project is backed by the Linux Foundation, ensuring it will remain open source forever.", - "install_methods": [ - { - "type": "default", - "script": "ct/valkey.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 4, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": ["On first launch, a random password is set for the default user using `requirepass`; the generated password is stored in `/root/.valkey_pass.txt inside the container`." ] -} diff --git a/install/valkey-install.sh b/install/valkey-install.sh deleted file mode 100644 index a3d121ebe..000000000 --- a/install/valkey-install.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: pshankinclarke (lazarillo) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://valkey.io/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Valkey" -$STD apt update -$STD apt install -y valkey openssl -sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf -PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" -echo "requirepass $PASS" >> /etc/valkey/valkey.conf -echo "$PASS" >/root/valkey_pass.txt -chmod 600 /root/valkey_pass.txt -systemctl enable -q --now valkey-server -systemctl restart valkey-server -msg_ok "Installed Valkey" -msg_ok "Valkey password saved to /root/valkey_pass.txt" - -motd_ssh -customize -cleanup_lxc From 5176ca7f829a0ce5a8c15541d6f1d39063f651f0 Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Wed, 3 Dec 2025 20:12:20 -0800 Subject: [PATCH 017/378] unbound variable issue should be fixed --- install/alpine-valkey-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index ce2083d96..bac37a357 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -5,7 +5,6 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://valkey.io/ -: "${IPV6_METHOD:=none}" source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 From 32901cf7cfbe6e7a6afa7bcf4e48edf64cac728d Mon Sep 17 00:00:00 2001 From: Parker Shankin-Clarke <38367844+pshankinclarke@users.noreply.github.com> Date: Thu, 4 Dec 2025 12:53:10 -0800 Subject: [PATCH 018/378] Update source URL for build function --- ct/alpine-valkey.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-valkey.sh b/ct/alpine-valkey.sh index 6347b830d..1f7f96298 100644 --- a/ct/alpine-valkey.sh +++ b/ct/alpine-valkey.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/pshankinclarke/ProxmoxVED/refs/heads/valkey-alpine/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: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 41b4cfc03cdc852f7ce13de5b2170f37ff28105b Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Mon, 17 Nov 2025 15:12:07 -0500 Subject: [PATCH 019/378] Restore manyfold from last pull request --- ct/{deferred => }/manyfold.sh | 0 install/{deferred => }/manyfold-install.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename ct/{deferred => }/manyfold.sh (100%) rename install/{deferred => }/manyfold-install.sh (100%) diff --git a/ct/deferred/manyfold.sh b/ct/manyfold.sh similarity index 100% rename from ct/deferred/manyfold.sh rename to ct/manyfold.sh diff --git a/install/deferred/manyfold-install.sh b/install/manyfold-install.sh similarity index 100% rename from install/deferred/manyfold-install.sh rename to install/manyfold-install.sh From 990fc78926e295bdf2cb2986fed325584d8b2486 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 15:27:12 -0500 Subject: [PATCH 020/378] Update dependecies --- install/manyfold-install.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index f7bb7c2f6..5578a9b3b 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -2,6 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: bvdberg01 +# Co-Author: SunFlowerOwl # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" @@ -18,12 +19,24 @@ $STD apt-get install -y \ rbenv \ libpq-dev \ libarchive-dev \ - git \ libmariadb-dev \ redis-server \ nginx \ libffi-dev \ - libyaml-dev + libyaml-dev \ + libassimp-dev \ + build-essential \ + pkg-config \ + libssl-dev \ + libreadline-dev \ + zlib1g-dev \ + libgmp-dev \ + libmagickwand-dev \ + redis \ + imagemagick \ + libjpeg-dev \ + libwebp-dev \ + libheif-dev msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql From b1f44cf878c56702ac33db18824fed4d53ac3317 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 15:28:11 -0500 Subject: [PATCH 021/378] Fix: Remove PUBLIC_HOSTNAME var due to a bug in manyfold --- install/manyfold-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 5578a9b3b..51fb86d42 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -75,7 +75,6 @@ cat </opt/.env export APP_VERSION=12345 export GUID=1002 export PUID=1001 -export PUBLIC_HOSTNAME=subdomain.somehost.org export PUBLIC_PORT=5000 export REDIS_URL=redis://127.0.0.1:6379/1 export DATABASE_ADAPTER=postgresql From e9e8b529f05eadb0bd7597bfdb8789087908dd18 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 15:28:47 -0500 Subject: [PATCH 022/378] Remove comments --- install/manyfold-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 51fb86d42..2e1e4e5f1 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -99,8 +99,6 @@ $STD bundle install $STD gem install sidekiq $STD npm install --global corepack 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 From 4c3c2fd490ea97cb9d27497ca0d0d7f67a8258ac Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 15:30:09 -0500 Subject: [PATCH 023/378] Creating services using foreman instead --- install/manyfold-install.sh | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 2e1e4e5f1..8e9a68952 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -106,27 +106,12 @@ $STD bin/rails db:migrate $STD bin/rails assets:precompile msg_ok "Installed manyfold" -msg_info "Creating Service" -cat </etc/systemd/system/manyfold.service -[Unit] -Description=Manyfold3d -Requires=network.target - -[Service] -Type=simple -User=root -Group=root -WorkingDirectory=/opt/manyfold -ExecStart=/usr/bin/bash -lc 'source /opt/.env && /opt/manyfold/bin/rails server -b 127.0.0.1 --port 5000 --environment production' -TimeoutSec=30 -RestartSec=15s -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now manyfold - +msg_info "Creating Services" +$STD foreman export systemd /etc/systemd/system -a manyfold -u root -f /opt/manyfold/Procfile +for f in /etc/systemd/system/manyfold-*.service; do + sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/.env \&\& |" "$f" +done +systemctl enable -q --now manyfold manyfold-rails manyfold-default_worker manyfold-performance_worker cat </etc/nginx/sites-available/manyfold.conf server { listen 80; @@ -149,7 +134,7 @@ EOF ln -s /etc/nginx/sites-available/manyfold.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx -msg_ok "Created Service" +msg_ok "Created Services" motd_ssh customize From b19d7f5ba0466a84bd7203d833b583c04a8a0901 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 15:38:14 -0500 Subject: [PATCH 024/378] Upgrade OS from Debian 12 to 13 --- ct/manyfold.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/manyfold.sh b/ct/manyfold.sh index 2d782d551..a4c1112b9 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.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}" header_info "$APP" From c99e41a30e36683f84c0ff0a8d9554fe8a49c375 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 15:43:48 -0500 Subject: [PATCH 025/378] Revert deleted manyfold json --- frontend/public/json/manyfold.json | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/manyfold.json diff --git a/frontend/public/json/manyfold.json b/frontend/public/json/manyfold.json new file mode 100644 index 000000000..bb232666c --- /dev/null +++ b/frontend/public/json/manyfold.json @@ -0,0 +1,35 @@ +{ + "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": [] +} From c5d741d17d77bed73e820d228a95cf590562f297 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 15:45:13 -0500 Subject: [PATCH 026/378] Upgrade needed ressources --- frontend/public/json/manyfold.json | 66 +++++++++++++++--------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/frontend/public/json/manyfold.json b/frontend/public/json/manyfold.json index bb232666c..ce31971fc 100644 --- a/frontend/public/json/manyfold.json +++ b/frontend/public/json/manyfold.json @@ -1,35 +1,35 @@ { - "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": [] + "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": 4, + "ram": 4096, + "hdd": 15, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } From a23e4bff4dc78fc8304e7e7698795f4c10ff2a0d Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 23 Nov 2025 16:18:24 -0500 Subject: [PATCH 027/378] Re introduce git needed by ruby --- install/manyfold-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 8e9a68952..d00d3c5c1 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -19,6 +19,7 @@ $STD apt-get install -y \ rbenv \ libpq-dev \ libarchive-dev \ + git \ libmariadb-dev \ redis-server \ nginx \ From 2ee1cbc25e3d083c3eec0dd32dfd758b33ec33d9 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Mon, 24 Nov 2025 16:29:52 -0500 Subject: [PATCH 028/378] change var_tag --- ct/manyfold.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/manyfold.sh b/ct/manyfold.sh index a4c1112b9..c3c981dee 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # Source: https://github.com/manyfold3d/manyfold APP="Manyfold" -var_tags="${var_tags:-network}" +var_tags="${var_tags:-3d}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" From 7423408982605d806ab35f19ffd984a81d71e332 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Mon, 24 Nov 2025 16:31:02 -0500 Subject: [PATCH 029/378] Fix: Re-introduce corepack to avoid question during install --- install/manyfold-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index d00d3c5c1..f2d7d33eb 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -102,6 +102,8 @@ $STD npm install --global corepack corepack enable yarn chown manyfold:manyfold /opt/.env rm /opt/manyfold/config/credentials.yml.enc +$STD corepack prepare $YARN_VERSION --activate +$STD corepack use $YARN_VERSION $STD bin/rails credentials:edit $STD bin/rails db:migrate $STD bin/rails assets:precompile From 0ed0856c66c09b873e4dda220dee2f5cbb1d56ee Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Mon, 24 Nov 2025 16:33:22 -0500 Subject: [PATCH 030/378] Fix: Service creation error --- install/manyfold-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index f2d7d33eb..df0fde88f 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -110,11 +110,12 @@ $STD bin/rails assets:precompile msg_ok "Installed manyfold" msg_info "Creating Services" +$STD gem install foreman $STD foreman export systemd /etc/systemd/system -a manyfold -u root -f /opt/manyfold/Procfile for f in /etc/systemd/system/manyfold-*.service; do sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/.env \&\& |" "$f" done -systemctl enable -q --now manyfold manyfold-rails manyfold-default_worker manyfold-performance_worker +systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 cat </etc/nginx/sites-available/manyfold.conf server { listen 80; From c66a062114550b284a497a7d3570db7c4848bfdf Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Mon, 24 Nov 2025 16:34:04 -0500 Subject: [PATCH 031/378] Fix: Cable not redirect by reverse-proxy --- install/manyfold-install.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index df0fde88f..9f131f6d1 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -122,6 +122,19 @@ server { server_name manyfold; root /opt/manyfold/public; + location /cable { + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host \$host; + + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "Upgrade"; + + 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; + } + location / { try_files \$uri/index.html \$uri @rails; } From c6df16a41364ee0ebc67af9e809a277f7006d1cc Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Mon, 24 Nov 2025 16:35:36 -0500 Subject: [PATCH 032/378] Start rework updating function --- ct/manyfold.sh | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/ct/manyfold.sh b/ct/manyfold.sh index c3c981dee..439a842ca 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -27,31 +27,30 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - 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 + if check_for_gh_release "manyfold" "manyfold3d/manyfold"; then msg_info "Stopping Service" - systemctl stop apache2 + systemctl stop manyfold manyfold-rails manyfold-default_worker manyfold-performance_worker msg_ok "Stopped Service" - msg_info "Updating ${APP} to v${RELEASE}" - cd /opt - mv /opt/manyfold/ /opt/manyfold-backup + fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated $APP to v${RELEASE}" + msg_info "Update services" + $STD foreman export systemd /etc/systemd/system -a manyfold -u root -f /opt/manyfold/Procfile + for f in /etc/systemd/system/manyfold-*.service; do + sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/.env \&\& |" "$f" + done + msg_ok "Updated services" msg_info "Starting Service" - systemctl start service + systemctl start manyfold manyfold-rails manyfold-default_worker manyfold-performance_worker msg_ok "Started Service" - - msg_info "Cleaning up" - rm -r "/opt/${RELEASE}.zip" - rm -r /opt/manyfold-backup - msg_ok "Cleaned" - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" fi + + msg_info "Cleaning up" + $STD apt -y autoremove + $STD apt -y autoclean + $STD apt -y clean + msg_ok "Cleaned" exit } From eb8f381a9fc21944dec583763ce5907a63ab30c6 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Tue, 25 Nov 2025 15:28:58 -0500 Subject: [PATCH 033/378] Fix: Rails credential warning --- install/manyfold-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 9f131f6d1..d5a3692a2 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -104,6 +104,7 @@ chown manyfold:manyfold /opt/.env rm /opt/manyfold/config/credentials.yml.enc $STD corepack prepare $YARN_VERSION --activate $STD corepack use $YARN_VERSION +export VISUAL="code --wait" $STD bin/rails credentials:edit $STD bin/rails db:migrate $STD bin/rails assets:precompile From 8d1c558af802e4596ceeac3297af23df4fb1b28a Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Tue, 25 Nov 2025 15:32:54 -0500 Subject: [PATCH 034/378] Remove unused credential file --- install/manyfold-install.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index d5a3692a2..206311074 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -51,12 +51,6 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMP $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" -} >>~/manyfold.creds msg_ok "Set up PostgreSQL" fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold" From 5d48467a50687e3211557711e9e4f215cb3402cf Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Tue, 25 Nov 2025 16:10:05 -0500 Subject: [PATCH 035/378] Rework APP version logic --- install/manyfold-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 206311074..1e5391f62 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -54,6 +54,7 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" msg_ok "Set up PostgreSQL" fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold" +RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') RUBY_INSTALL_VERSION=$(cat /opt/manyfold/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') @@ -67,7 +68,7 @@ msg_ok "Added manyfold user" msg_info "Setting .env file" cat </opt/.env -export APP_VERSION=12345 +export APP_VERSION=${RELEASE} export GUID=1002 export PUID=1001 export PUBLIC_PORT=5000 From 3f5f8c4db58089ce9aa36999530662d11f1302c2 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Thu, 27 Nov 2025 17:49:51 -0500 Subject: [PATCH 036/378] Run manyfold as manyfold user --- install/manyfold-install.sh | 75 +++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 1e5391f62..5c3f3872e 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -16,7 +16,6 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ lsb-release \ - rbenv \ libpq-dev \ libarchive-dev \ git \ @@ -54,19 +53,12 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" msg_ok "Set up PostgreSQL" fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold" -RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -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_ruby - -msg_info "Adding manyfold user" +msg_info "Configuring manyfold environment" useradd -m -s /usr/bin/bash manyfold -msg_ok "Added manyfold user" - -msg_info "Setting .env file" +echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>/home/manyfold/.bashrc +echo 'eval "$(rbenv init -)"' >>/home/manyfold/.bashrc +RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') cat </opt/.env export APP_VERSION=${RELEASE} export GUID=1002 @@ -83,31 +75,50 @@ export MULTIUSER=enabled export HTTPS_ONLY=false export RAILS_ENV=production EOF -msg_ok ".env file setup" +RUBY_INSTALL_VERSION=$(cat /opt/manyfold/.ruby-version) +YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') +cat </opt/user_setup.sh +#!/bin/bash + +source /opt/.env +export PATH="/home/manyfold/.rbenv/bin:\$PATH" +eval "\$(/home/manyfold/.rbenv/bin/rbenv init - bash)" +cd /opt/manyfold +rbenv global $RUBY_INSTALL_VERSION +gem install bundler +bundle install +gem install sidekiq +gem install foreman +corepack enable yarn +rm -f /opt/manyfold/config/credentials.yml.enc +corepack prepare $YARN_VERSION --activate +corepack use $YARN_VERSION +export VISUAL="code --wait" +bin/rails credentials:edit +bin/rails db:migrate +bin/rails assets:precompile +EOF +chown -R manyfold:manyfold /opt +$STD chmod +x /opt/user_setup.sh +msg_ok "Configured manyfold environment" + +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby msg_info "Installing Manyfold" -source /opt/.env -cd /opt/manyfold -chown -R manyfold:manyfold /opt/manyfold -$STD gem install bundler -$STD rbenv global $RUBY_INSTALL_VERSION -$STD bundle install -$STD gem install sidekiq -$STD npm install --global corepack -corepack enable yarn -chown manyfold:manyfold /opt/.env -rm /opt/manyfold/config/credentials.yml.enc -$STD corepack prepare $YARN_VERSION --activate -$STD corepack use $YARN_VERSION -export VISUAL="code --wait" -$STD bin/rails credentials:edit -$STD bin/rails db:migrate -$STD bin/rails assets:precompile +chown -R manyfold:manyfold /home/manyfold/.rbenv +npm install --global corepack +$STD sudo -u manyfold bash /opt/user_setup.sh +rm -f /opt/user_setup.sh msg_ok "Installed manyfold" msg_info "Creating Services" -$STD gem install foreman -$STD foreman export systemd /etc/systemd/system -a manyfold -u root -f /opt/manyfold/Procfile +cd /opt/manyfold +source /opt/.env +export RBENV_PATH="/home/manyfold/.rbenv" +export PATH="$RBENV_PATH/bin:$PATH" +eval "$($RBENV_PATH/bin/rbenv init - bash)" +$STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/Procfile for f in /etc/systemd/system/manyfold-*.service; do sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/.env \&\& |" "$f" done From 2e46f96c714b3e673b906d31b934ba216cf2209d Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Fri, 28 Nov 2025 14:32:42 -0500 Subject: [PATCH 037/378] Reduce msg verbosity and improve dir architecture --- install/manyfold-install.sh | 49 +++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 5c3f3872e..cf3a673bd 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -41,7 +41,13 @@ msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql -msg_info "Setting up PostgreSQL" +fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" + +msg_info "Configuring manyfold building environment" +RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) +YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') +RBENV_PATH="/home/manyfold/.rbenv" +PATH="$RBENV_PATH/bin:$PATH" DB_NAME=manyfold DB_USER=manyfold DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) @@ -50,16 +56,11 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMP $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';" -msg_ok "Set up PostgreSQL" - -fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold" - -msg_info "Configuring manyfold environment" useradd -m -s /usr/bin/bash manyfold echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>/home/manyfold/.bashrc echo 'eval "$(rbenv init -)"' >>/home/manyfold/.bashrc RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -cat </opt/.env +cat </opt/manyfold/.env export APP_VERSION=${RELEASE} export GUID=1002 export PUID=1001 @@ -75,22 +76,20 @@ export MULTIUSER=enabled export HTTPS_ONLY=false export RAILS_ENV=production EOF -RUBY_INSTALL_VERSION=$(cat /opt/manyfold/.ruby-version) -YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') -cat </opt/user_setup.sh +cat </opt/manyfold/user_setup.sh #!/bin/bash -source /opt/.env -export PATH="/home/manyfold/.rbenv/bin:\$PATH" -eval "\$(/home/manyfold/.rbenv/bin/rbenv init - bash)" -cd /opt/manyfold +source /opt/manyfold/.env +export PATH="$RBENV_PATH/bin:\$PATH" +eval "\$($RBENV_PATH/bin/rbenv init - bash)" +cd /opt/manyfold/app rbenv global $RUBY_INSTALL_VERSION gem install bundler bundle install gem install sidekiq gem install foreman corepack enable yarn -rm -f /opt/manyfold/config/credentials.yml.enc +rm -f /opt/manyfold/app/config/credentials.yml.enc corepack prepare $YARN_VERSION --activate corepack use $YARN_VERSION export VISUAL="code --wait" @@ -99,8 +98,8 @@ bin/rails db:migrate bin/rails assets:precompile EOF chown -R manyfold:manyfold /opt -$STD chmod +x /opt/user_setup.sh -msg_ok "Configured manyfold environment" +$STD chmod +x /opt/manyfold/user_setup.sh +msg_ok "Configured manyfold building environment" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby @@ -108,26 +107,24 @@ RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfo msg_info "Installing Manyfold" chown -R manyfold:manyfold /home/manyfold/.rbenv npm install --global corepack -$STD sudo -u manyfold bash /opt/user_setup.sh -rm -f /opt/user_setup.sh +$STD sudo -u manyfold bash /opt/manyfold/user_setup.sh +rm -f /opt/manyfold/user_setup.sh msg_ok "Installed manyfold" msg_info "Creating Services" -cd /opt/manyfold -source /opt/.env -export RBENV_PATH="/home/manyfold/.rbenv" -export PATH="$RBENV_PATH/bin:$PATH" +cd /opt/manyfold/app +source /opt/manyfold/.env eval "$($RBENV_PATH/bin/rbenv init - bash)" -$STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/Procfile +$STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile for f in /etc/systemd/system/manyfold-*.service; do - sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/.env \&\& |" "$f" + sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" done systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 cat </etc/nginx/sites-available/manyfold.conf server { listen 80; server_name manyfold; - root /opt/manyfold/public; + root /opt/manyfold/app/public; location /cable { proxy_pass http://127.0.0.1:5000; From 1ceff77bf46c47a8ca44c7908d4b2beb690bf4ea Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Fri, 28 Nov 2025 15:40:35 -0500 Subject: [PATCH 038/378] Provide place to store models --- install/manyfold-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index cf3a673bd..c88f91869 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -43,7 +43,7 @@ PG_VERSION="16" setup_postgresql fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" -msg_info "Configuring manyfold building environment" +msg_info "Configuring manyfold environment" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') RBENV_PATH="/home/manyfold/.rbenv" @@ -97,9 +97,10 @@ bin/rails credentials:edit bin/rails db:migrate bin/rails assets:precompile EOF -chown -R manyfold:manyfold /opt +$STD mkdir -p /opt/manyfold/data +chown -R manyfold:manyfold /opt/manyfold $STD chmod +x /opt/manyfold/user_setup.sh -msg_ok "Configured manyfold building environment" +msg_ok "Configured manyfold environment" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby From 8c06940d962997e65198d360e003214d76702be4 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Fri, 28 Nov 2025 16:38:12 -0500 Subject: [PATCH 039/378] Rework update function --- ct/manyfold.sh | 70 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/ct/manyfold.sh b/ct/manyfold.sh index 439a842ca..0e0d1c003 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -2,6 +2,7 @@ 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 +# Co-Author: SunFlowerOwl # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/manyfold3d/manyfold @@ -27,23 +28,72 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + if check_for_gh_release "manyfold" "manyfold3d/manyfold"; then msg_info "Stopping Service" - systemctl stop manyfold manyfold-rails manyfold-default_worker manyfold-performance_worker + systemctl stop manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 msg_ok "Stopped Service" + msg_info "Backing up data" + source /opt/manyfold/.env + mv /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold/app/config/master.key ~/ + tar -cvzf "/opt/manyfold_${APP_VERSION}_backup.tar.gz" /opt/manyfold/app/ + rm -rf /opt/manyfold/app/ + msg_ok "Backed-up data" + fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" - msg_info "Update services" - $STD foreman export systemd /etc/systemd/system -a manyfold -u root -f /opt/manyfold/Procfile - for f in /etc/systemd/system/manyfold-*.service; do - sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/.env \&\& |" "$f" - done - msg_ok "Updated services" + msg_info "Configuring manyfold environment" + RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) + YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') + RBENV_PATH="/home/manyfold/.rbenv" + RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + sed -i "s/^export APP_VERSION=.*/export APP_VERSION=$RELEASE/" "/opt/manyfold/.env" + cat </opt/manyfold/user_setup.sh +#!/bin/bash - msg_info "Starting Service" - systemctl start manyfold manyfold-rails manyfold-default_worker manyfold-performance_worker - msg_ok "Started Service" +source /opt/manyfold/.env +export PATH="$RBENV_PATH/bin:\$PATH" +eval "\$($RBENV_PATH/bin/rbenv init - bash)" +cd /opt/manyfold/app +rbenv global $RUBY_INSTALL_VERSION +gem install bundler +bundle install +gem install sidekiq +gem install foreman +corepack enable yarn +corepack prepare $YARN_VERSION --activate +corepack use $YARN_VERSION +bin/rails db:migrate +bin/rails assets:precompile +EOF + $STD mkdir -p /opt/manyfold/data + chown -R manyfold:manyfold /opt/manyfold + $STD chmod +x /opt/manyfold/user_setup.sh + msg_ok "Configured manyfold environment" + + RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby + + msg_info "Installing Manyfold" + chown -R manyfold:manyfold /home/manyfold/.rbenv + rm -rf /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config/credentials.yml.enc + mv ~/storage ~/tmp /opt/manyfold/app/ + mv ~/credentials.yml.enc ~/master.key /opt/manyfold/app/config/ + chown -R manyfold:manyfold /opt/manyfold + $STD sudo -u manyfold bash /opt/manyfold/user_setup.sh + rm -f /opt/manyfold/user_setup.sh + msg_ok "Installed manyfold" + + msg_info "Restoring Service" + cd /opt/manyfold/app + source /opt/manyfold/.env + export PATH="$RBENV_PATH//shims:$RBENV_PATH/bin:$PATH" + $STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile + for f in /etc/systemd/system/manyfold-*.service; do + sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" + done + systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 + msg_ok "Restored Service" fi msg_info "Cleaning up" From 49f6a047f27bd211d521cca383aae28cdc4c2586 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sun, 30 Nov 2025 15:31:50 -0500 Subject: [PATCH 040/378] Cleaning up the code --- ct/manyfold.sh | 14 ++++------ frontend/public/json/manyfold.json | 9 ++++-- install/manyfold-install.sh | 45 +++++++++++++++--------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/ct/manyfold.sh b/ct/manyfold.sh index 0e0d1c003..294161a54 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -37,7 +37,7 @@ function update_script() { msg_info "Backing up data" source /opt/manyfold/.env mv /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold/app/config/master.key ~/ - tar -cvzf "/opt/manyfold_${APP_VERSION}_backup.tar.gz" /opt/manyfold/app/ + $STD tar -cvzf "/opt/manyfold_${APP_VERSION}_backup.tar.gz" /opt/manyfold/app/ rm -rf /opt/manyfold/app/ msg_ok "Backed-up data" @@ -46,15 +46,14 @@ function update_script() { msg_info "Configuring manyfold environment" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') - RBENV_PATH="/home/manyfold/.rbenv" RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') sed -i "s/^export APP_VERSION=.*/export APP_VERSION=$RELEASE/" "/opt/manyfold/.env" cat </opt/manyfold/user_setup.sh #!/bin/bash source /opt/manyfold/.env -export PATH="$RBENV_PATH/bin:\$PATH" -eval "\$($RBENV_PATH/bin/rbenv init - bash)" +export PATH="/home/manyfold/.rbenv/bin:\$PATH" +eval "\$(/home/manyfold/.rbenv/bin/rbenv init - bash)" cd /opt/manyfold/app rbenv global $RUBY_INSTALL_VERSION gem install bundler @@ -67,9 +66,6 @@ corepack use $YARN_VERSION bin/rails db:migrate bin/rails assets:precompile EOF - $STD mkdir -p /opt/manyfold/data - chown -R manyfold:manyfold /opt/manyfold - $STD chmod +x /opt/manyfold/user_setup.sh msg_ok "Configured manyfold environment" RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby @@ -80,14 +76,14 @@ EOF mv ~/storage ~/tmp /opt/manyfold/app/ mv ~/credentials.yml.enc ~/master.key /opt/manyfold/app/config/ chown -R manyfold:manyfold /opt/manyfold + chmod +x /opt/manyfold/user_setup.sh $STD sudo -u manyfold bash /opt/manyfold/user_setup.sh rm -f /opt/manyfold/user_setup.sh msg_ok "Installed manyfold" msg_info "Restoring Service" - cd /opt/manyfold/app source /opt/manyfold/.env - export PATH="$RBENV_PATH//shims:$RBENV_PATH/bin:$PATH" + export PATH="/home/manyfold/.rbenv/shims:/home/manyfold/.rbenv/bin:$PATH" $STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile for f in /etc/systemd/system/manyfold-*.service; do sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" diff --git a/frontend/public/json/manyfold.json b/frontend/public/json/manyfold.json index ce31971fc..7080630ad 100644 --- a/frontend/public/json/manyfold.json +++ b/frontend/public/json/manyfold.json @@ -12,7 +12,7 @@ "documentation": "https://manyfold.app/sysadmin/", "website": "https://manyfold.app/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/manyfold.webp", - "config_path": "", + "config_path": "/opt/manyfold/.env", "description": "Manyfold is an open source, self-hosted web application for managing a collection of 3d models, particularly focused on 3d printing.", "install_methods": [ { @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "Setup library on first connection in /opt/manyfold/data", + "type": "info" + } + ] } diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index c88f91869..bad8ab356 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -15,28 +15,32 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - lsb-release \ - libpq-dev \ libarchive-dev \ git \ libmariadb-dev \ redis-server \ nginx \ - libffi-dev \ - libyaml-dev \ libassimp-dev \ - build-essential \ - pkg-config \ - libssl-dev \ - libreadline-dev \ - zlib1g-dev \ - libgmp-dev \ libmagickwand-dev \ - redis \ imagemagick \ + build-essential \ + libtool \ libjpeg-dev \ + libpng-dev \ + libtiff-dev \ libwebp-dev \ - libheif-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 msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql @@ -46,8 +50,7 @@ fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" msg_info "Configuring manyfold environment" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') -RBENV_PATH="/home/manyfold/.rbenv" -PATH="$RBENV_PATH/bin:$PATH" +RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') DB_NAME=manyfold DB_USER=manyfold DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) @@ -57,9 +60,6 @@ $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';" useradd -m -s /usr/bin/bash manyfold -echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>/home/manyfold/.bashrc -echo 'eval "$(rbenv init -)"' >>/home/manyfold/.bashrc -RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') cat </opt/manyfold/.env export APP_VERSION=${RELEASE} export GUID=1002 @@ -80,8 +80,8 @@ cat </opt/manyfold/user_setup.sh #!/bin/bash source /opt/manyfold/.env -export PATH="$RBENV_PATH/bin:\$PATH" -eval "\$($RBENV_PATH/bin/rbenv init - bash)" +export PATH="/home/manyfold/.rbenv/bin:\$PATH" +eval "\$(/home/manyfold/.rbenv/bin/rbenv init - bash)" cd /opt/manyfold/app rbenv global $RUBY_INSTALL_VERSION gem install bundler @@ -98,8 +98,6 @@ bin/rails db:migrate bin/rails assets:precompile EOF $STD mkdir -p /opt/manyfold/data -chown -R manyfold:manyfold /opt/manyfold -$STD chmod +x /opt/manyfold/user_setup.sh msg_ok "Configured manyfold environment" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs @@ -107,15 +105,16 @@ RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfo msg_info "Installing Manyfold" chown -R manyfold:manyfold /home/manyfold/.rbenv +chown -R manyfold:manyfold /opt/manyfold +chmod +x /opt/manyfold/user_setup.sh npm install --global corepack $STD sudo -u manyfold bash /opt/manyfold/user_setup.sh rm -f /opt/manyfold/user_setup.sh msg_ok "Installed manyfold" msg_info "Creating Services" -cd /opt/manyfold/app source /opt/manyfold/.env -eval "$($RBENV_PATH/bin/rbenv init - bash)" +export PATH="/home/manyfold/.rbenv/shims:/home/manyfold/.rbenv/bin:$PATH" $STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile for f in /etc/systemd/system/manyfold-*.service; do sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" From 0c767836f2c7b0322ce4311f6acc559a17997d28 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Fri, 5 Dec 2025 18:07:11 -0500 Subject: [PATCH 041/378] use builtin function to setup imagemagick --- install/manyfold-install.sh | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index bad8ab356..03601ac58 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -20,29 +20,11 @@ $STD apt-get install -y \ libmariadb-dev \ redis-server \ nginx \ - libassimp-dev \ - libmagickwand-dev \ - imagemagick \ - 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 + libassimp-dev msg_ok "Installed Dependencies" +setup_imagemagick + PG_VERSION="16" setup_postgresql fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" From 60472c43e217b6bd99b51b34e8b02b856507b2b1 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sat, 6 Dec 2025 05:54:00 -0500 Subject: [PATCH 042/378] Fix: Show manyfold updateable in json file --- frontend/public/json/manyfold.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/manyfold.json b/frontend/public/json/manyfold.json index 7080630ad..b7b6cdb37 100644 --- a/frontend/public/json/manyfold.json +++ b/frontend/public/json/manyfold.json @@ -6,7 +6,7 @@ ], "date_created": "2025-03-18", "type": "ct", - "updateable": false, + "updateable": true, "privileged": false, "interface_port": 80, "documentation": "https://manyfold.app/sysadmin/", From c5766a57770d9bb3d48b1ee102bd2cf1f0f5c4a3 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl <85146049+SunFlowerOwl@users.noreply.github.com> Date: Sat, 6 Dec 2025 07:18:49 -0500 Subject: [PATCH 043/378] Update frontend/public/json/manyfold.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/manyfold.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/manyfold.json b/frontend/public/json/manyfold.json index b7b6cdb37..26bfa13d9 100644 --- a/frontend/public/json/manyfold.json +++ b/frontend/public/json/manyfold.json @@ -11,7 +11,7 @@ "interface_port": 80, "documentation": "https://manyfold.app/sysadmin/", "website": "https://manyfold.app/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/manyfold.webp", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/manyfold.webp", "config_path": "/opt/manyfold/.env", "description": "Manyfold is an open source, self-hosted web application for managing a collection of 3d models, particularly focused on 3d printing.", "install_methods": [ From ea96873e9e6994b18adcf90a860cf55aabb82e0e Mon Sep 17 00:00:00 2001 From: SunFlowerOwl Date: Sat, 6 Dec 2025 07:20:56 -0500 Subject: [PATCH 044/378] Review: fix way to get release using builtin function --- ct/manyfold.sh | 2 +- install/manyfold-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/manyfold.sh b/ct/manyfold.sh index 294161a54..a7f359d5f 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -46,7 +46,7 @@ function update_script() { msg_info "Configuring manyfold environment" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') - RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + RELEASE=$(get_latest_github_release "manyfold3d/manyfold") sed -i "s/^export APP_VERSION=.*/export APP_VERSION=$RELEASE/" "/opt/manyfold/.env" cat </opt/manyfold/user_setup.sh #!/bin/bash diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 03601ac58..500a9f71c 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -32,7 +32,7 @@ fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" msg_info "Configuring manyfold environment" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') -RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') +RELEASE=$(get_latest_github_release "manyfold3d/manyfold") DB_NAME=manyfold DB_USER=manyfold DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) From 747d0904c0bc232ea88920eda4138a8bb1a79424 Mon Sep 17 00:00:00 2001 From: hoholms Date: Thu, 11 Dec 2025 22:24:54 +0200 Subject: [PATCH 045/378] Add loki (ct) --- ct/alpine-loki.sh | 71 ++++++++++++++++++++++++++++++++++ ct/headers/alpine-loki | 6 +++ ct/headers/loki | 5 +++ ct/loki.sh | 58 +++++++++++++++++++++++++++ frontend/public/json/loki.json | 44 +++++++++++++++++++++ install/alpine-loki-install.sh | 41 ++++++++++++++++++++ install/loki-install.sh | 37 ++++++++++++++++++ 7 files changed, 262 insertions(+) create mode 100644 ct/alpine-loki.sh create mode 100644 ct/headers/alpine-loki create mode 100644 ct/headers/loki create mode 100644 ct/loki.sh create mode 100644 frontend/public/json/loki.json create mode 100644 install/alpine-loki-install.sh create mode 100644 install/loki-install.sh diff --git a/ct/alpine-loki.sh b/ct/alpine-loki.sh new file mode 100644 index 000000000..99528206d --- /dev/null +++ b/ct/alpine-loki.sh @@ -0,0 +1,71 @@ +#!/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: hoholms +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/grafana/loki + +APP="Alpine-Loki" +var_tags="${var_tags:-alpine;monitoring}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +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() { + if ! apk -e info newt >/dev/null 2>&1; then + apk add -q newt + fi + LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + while true; do + CHOICE=$( + whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --menu "Select option" 11 58 3 \ + "1" "Check for Loki Updates" \ + "2" "Allow 0.0.0.0 for listening" \ + "3" "Allow only ${LXCIP} for listening" 3>&2 2>&1 1>&3 + ) + exit_status=$? + if [ $exit_status == 1 ]; then + clear + exit-script + fi + header_info + case $CHOICE in + 1) + $STD apk -U upgrade + msg_ok "Updated successfully!" + exit + ;; + 2) + sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=0.0.0.0/g" /etc/conf.d/loki + service loki restart + msg_ok "Allowed listening on all interfaces!" + exit + ;; + 3) + sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=$LXCIP/g" /etc/conf.d/loki + service loki restart + msg_ok "Allowed listening only on ${LXCIP}!" + exit + ;; + esac + done + exit 0 +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${APP} should be reachable by going to the following URL. + ${BL}http://${IP}:3100${CL} \n" +echo -e "Promtail should be reachable by going to the following URL. + ${BL}http://${IP}:9080${CL} \n" diff --git a/ct/headers/alpine-loki b/ct/headers/alpine-loki new file mode 100644 index 000000000..ca154285e --- /dev/null +++ b/ct/headers/alpine-loki @@ -0,0 +1,6 @@ + ___ __ _ __ __ _ + / | / /___ (_)___ ___ / / ____ / /__(_) + / /| | / / __ \/ / __ \/ _ \______/ / / __ \/ //_/ / + / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / ,< / / +/_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/_/|_/_/ + /_/ diff --git a/ct/headers/loki b/ct/headers/loki new file mode 100644 index 000000000..38b3c722b --- /dev/null +++ b/ct/headers/loki @@ -0,0 +1,5 @@ + __ __ _ + / / ____ / /__(_) + / / / __ \/ //_/ / + / /___/ /_/ / ,< / / +/_____/\____/_/|_/_/ diff --git a/ct/loki.sh b/ct/loki.sh new file mode 100644 index 000000000..ac4b9629e --- /dev/null +++ b/ct/loki.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 community-scripts ORG +# Author: hoholms +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/grafana/loki + +APP="Loki" +var_tags="${var_tags:-monitoring;logs}" +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 ! dpkg -s loki >/dev/null 2>&1; then + msg_error "No ${APP} Installation Found!" + exit 1 + fi + + if [[ -f /etc/apt/sources.list.d/grafana.list ]] || [[ ! -f /etc/apt/sources.list.d/grafana.sources ]]; then + setup_deb822_repo \ + "grafana" \ + "https://apt.grafana.com/gpg.key" \ + "https://apt.grafana.com" \ + "stable" \ + "main" + fi + + msg_info "Updating Loki LXC" + $STD apt update + $STD apt --only-upgrade install -y loki + $STD apt --only-upgrade install -y promtail + 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}:3100${CL}\n" +echo -e "${INFO}${YW} Access promtail using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9080${CL}" diff --git a/frontend/public/json/loki.json b/frontend/public/json/loki.json new file mode 100644 index 000000000..fa3362a86 --- /dev/null +++ b/frontend/public/json/loki.json @@ -0,0 +1,44 @@ +{ + "name": "Loki", + "slug": "loki", + "categories": [9], + "date_created": "2025-12-10", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3100, + "documentation": "https://grafana.com/docs/loki/latest/", + "website": "https://github.com/grafana/loki", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@master/webp/loki.webp", + "config_path": "Debian: /etc/loki/config.yml | Alpine: /etc/loki/loki-local-config.yaml", + "description": "Grafana Loki is a set of open source components that can be composed into a fully featured logging stack. A small index and highly compressed chunks simplifies the operation and significantly lowers the cost of Loki.", + "install_methods": [ + { + "type": "default", + "script": "ct/loki.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "debian", + "version": "13" + } + }, + { + "type": "alpine", + "script": "ct/alpine-loki.sh", + "resources": { + "cpu": 1, + "ram": 256, + "hdd": 1, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/alpine-loki-install.sh b/install/alpine-loki-install.sh new file mode 100644 index 000000000..cc5dbfdee --- /dev/null +++ b/install/alpine-loki-install.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: hoholms +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/grafana/loki + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Loki" +$STD apk add loki +$STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki +$STD rc-service loki start +$STD rc-update add loki default +$STD mkdir /tmp/loki/ +$STD chown -R loki:grafana /tmp/loki/ +$STD mkdir /var/log/loki/ +$STD chown -R loki:grafana /var/log/loki/ +$STD chmod 755 /etc/loki/loki-local-config.yaml +$STD sed -i '/^querier:/,/enable_multi_variant_queries: false/ s/^/#/' /etc/loki/loki-local-config.yaml +$STD echo "output_log=\"\${output_log:-/var/log/loki/output.log}\"" >> /etc/init.d/loki +$STD echo "error_log=\"\${error_log:-/var/log/loki/error.log}\"" >> /etc/init.d/loki +$STD echo "start_stop_daemon_args=\"\${SSD_OPTS} -1 \${output_log} -2 \${error_log}\"" >> /etc/init.d/loki +$STD rc-service loki restart +msg_ok "Installed Loki" + +msg_info "Installing Promtail" +$STD apk add loki-promtail +$STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki +$STD rc-service loki-promtail start +$STD rc-update add loki-promtail default +msg_ok "Installed Promtail" + +motd_ssh +customize diff --git a/install/loki-install.sh b/install/loki-install.sh new file mode 100644 index 000000000..0edc93282 --- /dev/null +++ b/install/loki-install.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: hoholms +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/grafana/loki + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setting up Grafana Repository" +setup_deb822_repo \ + "grafana" \ + "https://apt.grafana.com/gpg.key" \ + "https://apt.grafana.com" \ + "stable" \ + "main" +msg_ok "Grafana Repository setup sucessfully" + +msg_info "Installing Loki" +$STD apt install -y loki +systemctl enable -q --now loki +msg_ok "Installed Loki" + +msg_info "Installing Promtail" +$STD apt install -y promtail +systemctl enable -q --now promtail +msg_ok "Installed Promtail" + +motd_ssh +customize +cleanup_lxc From 2798c23dca7542c04a99c410c23d1e1b828dcdfc Mon Sep 17 00:00:00 2001 From: hoholms Date: Fri, 12 Dec 2025 02:11:57 +0200 Subject: [PATCH 046/378] Address review comments, stop/start loki and promtail services during update --- ct/loki.sh | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ct/loki.sh b/ct/loki.sh index ac4b9629e..707b2fb46 100644 --- a/ct/loki.sh +++ b/ct/loki.sh @@ -29,20 +29,24 @@ function update_script() { exit 1 fi - if [[ -f /etc/apt/sources.list.d/grafana.list ]] || [[ ! -f /etc/apt/sources.list.d/grafana.sources ]]; then - setup_deb822_repo \ - "grafana" \ - "https://apt.grafana.com/gpg.key" \ - "https://apt.grafana.com" \ - "stable" \ - "main" - fi + msg_info "Stopping Loki" + systemctl stop loki + systemctl stop promtail + msg_ok "Stopped Loki" - msg_info "Updating Loki LXC" + msg_info "Updating Loki" $STD apt update $STD apt --only-upgrade install -y loki $STD apt --only-upgrade install -y promtail - msg_ok "Updated successfully!" + msg_ok "Updated Loki" + + msg_info "Starting Loki" + systemctl start loki + systemctl start promtail + msg_ok "Started Loki" + + msg_ok "Update Successful" + exit } From 01d2df1834d55b8e08b36149770e96d8ba596a72 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 14 Dec 2025 16:02:37 +0100 Subject: [PATCH 047/378] feat: add kutt --- ct/kutt.sh | 65 ++++++++++++++++++++++++++++++++++ frontend/public/json/kutt.json | 40 +++++++++++++++++++++ install/kutt-install.sh | 57 +++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 ct/kutt.sh create mode 100644 frontend/public/json/kutt.json create mode 100644 install/kutt-install.sh diff --git a/ct/kutt.sh b/ct/kutt.sh new file mode 100644 index 000000000..20e916553 --- /dev/null +++ b/ct/kutt.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/tomfrenzel/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tomfrenzel +# License: MIT | https://github.com/tomfrenzel/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/thedevs-network/kutt + +APP="Kutt" +var_tags="${var_tags:-sharing}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +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 $APP_DIR ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "kutt" "thedevs-network/kutt"; then + msg_info "Stopping services" + systemctl stop kutt + msg_ok "Stopped services" + + msg_info "Updating packages" + $STD apt-get update + $STD apt-get dist-upgrade + msg_ok "Updated packages" + + fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" + + msg_info "Configuring Kutt" + cd /opt/kutt + npm install + npm run migrate + msg_ok "Configured Kutt" + + msg_info "Starting services" + systemctl start kutt + 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/frontend/public/json/kutt.json b/frontend/public/json/kutt.json new file mode 100644 index 000000000..09e72940c --- /dev/null +++ b/frontend/public/json/kutt.json @@ -0,0 +1,40 @@ +{ + "name": "Kutt", + "slug": "kutt", + "categories": [ + 21 + ], + "date_created": "2025-12-14", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/thedevs-network/kutt/", + "config_path": "/etc/kutt-data/.env", + "website": "https://kutt.it", + "logo": "https://kutt.it/images/favicon-196x196.png", + "description": "Kutt is a modern URL shortener with support for custom domains. Create and edit links, view statistics, manage users, and more.", + "install_methods": [ + { + "type": "default", + "script": "ct/kutt.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 2, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "A valid TLS certificate is **REQUIRED**", + "type": "warning" + } + ] +} diff --git a/install/kutt-install.sh b/install/kutt-install.sh new file mode 100644 index 000000000..28a03af05 --- /dev/null +++ b/install/kutt-install.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tomfrenzel +# License: MIT | https://github.com/tomfrenzel/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/thedevs-network/kutt + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +read -r -p "${TAB3}Enter the hostname of your Kutt instance (eg kutt.domain.tld): " kutt_host +if [[ "$kutt_host" ]]; then + KUTT_HOST="$kutt_host" +fi + +NODE_VERSION="22" setup_nodejs + +fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" + +msg_info "Configuring Kutt" +cd /opt/kutt +cp .example.env ".env" +sed -i "s|DEFAULT_DOMAIN=localhost:3000|DEFAULT_DOMAIN=${KUTT_HOST}|g" ".env" +sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" +npm install +npm run migrate + +cat </etc/systemd/system/kutt.service +[Unit] +Description=Kutt server +After=network-online.target + +[Service] +Type=simple +WorkingDirectory=/opt/kutt +ExecStart=/usr/bin/node server/server.js --production +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now kutt.service +msg_ok "Configured Kutt" + +motd_ssh +customize + +# Cleanup +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 5e069246045776927a44c38313e91528ff538326 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 14 Dec 2025 16:56:15 +0100 Subject: [PATCH 048/378] renaming --- ct/kutt.sh | 4 ++-- install/kutt-install.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index 20e916553..b955a3774 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/tomfrenzel/ProxmoxVED/main/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: tomfrenzel -# License: MIT | https://github.com/tomfrenzel/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/thedevs-network/kutt APP="Kutt" diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 28a03af05..42e501f9a 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: tomfrenzel -# License: MIT | https://github.com/tomfrenzel/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/thedevs-network/kutt source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From 95426e1e1ebf43cb19f7e7470ed4d3cbc2d626c2 Mon Sep 17 00:00:00 2001 From: Tom Frenzel <40773830+tomfrenzel@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:09:37 +0100 Subject: [PATCH 049/378] Update install/kutt-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/kutt-install.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 42e501f9a..35be25b74 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -49,9 +49,4 @@ msg_ok "Configured Kutt" motd_ssh customize - -# Cleanup -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" +cleanup_lxc From 5af193b759e8919ce7e05b14cbed6418cd515ff5 Mon Sep 17 00:00:00 2001 From: Tom Frenzel <40773830+tomfrenzel@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:09:44 +0100 Subject: [PATCH 050/378] Update ct/kutt.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- ct/kutt.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index b955a3774..8845d6a5b 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -34,10 +34,6 @@ function update_script() { systemctl stop kutt msg_ok "Stopped services" - msg_info "Updating packages" - $STD apt-get update - $STD apt-get dist-upgrade - msg_ok "Updated packages" fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" From f4ef536425b2eba0efaffb9fabb7ae377f36b863 Mon Sep 17 00:00:00 2001 From: Tom Frenzel <40773830+tomfrenzel@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:12:28 +0100 Subject: [PATCH 051/378] Update install/kutt-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/kutt-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 35be25b74..c22a6b473 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -44,7 +44,7 @@ Restart=always [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now kutt.service +systemctl enable -q --now kutt msg_ok "Configured Kutt" motd_ssh From 924a67a69c5b05c923765249712f96e703171c54 Mon Sep 17 00:00:00 2001 From: Tom Frenzel <40773830+tomfrenzel@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:19:29 +0100 Subject: [PATCH 052/378] Update install/kutt-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/kutt-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index c22a6b473..05235c469 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -19,7 +19,6 @@ if [[ "$kutt_host" ]]; then fi NODE_VERSION="22" setup_nodejs - fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" msg_info "Configuring Kutt" From 227b7fa88d185af1e0fb77102cfe4ff9d828ad81 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 14 Dec 2025 17:25:10 +0100 Subject: [PATCH 053/378] apply requested changes --- ct/kutt.sh | 5 ++--- install/kutt-install.sh | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index 8845d6a5b..d3b843318 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -34,13 +34,12 @@ function update_script() { systemctl stop kutt msg_ok "Stopped services" - fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" msg_info "Configuring Kutt" cd /opt/kutt - npm install - npm run migrate + $STD npm install + $STD npm run migrate msg_ok "Configured Kutt" msg_info "Starting services" diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 05235c469..102b49e34 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -26,8 +26,8 @@ cd /opt/kutt cp .example.env ".env" sed -i "s|DEFAULT_DOMAIN=localhost:3000|DEFAULT_DOMAIN=${KUTT_HOST}|g" ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" -npm install -npm run migrate +$STD npm install +$STD npm run migrate cat </etc/systemd/system/kutt.service [Unit] From a8cc22c149c7a56b567d256a87a979444402093d Mon Sep 17 00:00:00 2001 From: Tom Frenzel <40773830+tomfrenzel@users.noreply.github.com> Date: Sun, 14 Dec 2025 18:10:59 +0100 Subject: [PATCH 054/378] update icon --- frontend/public/json/kutt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/kutt.json b/frontend/public/json/kutt.json index 09e72940c..032f3d770 100644 --- a/frontend/public/json/kutt.json +++ b/frontend/public/json/kutt.json @@ -12,7 +12,7 @@ "documentation": "https://github.com/thedevs-network/kutt/", "config_path": "/etc/kutt-data/.env", "website": "https://kutt.it", - "logo": "https://kutt.it/images/favicon-196x196.png", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kutt.webp", "description": "Kutt is a modern URL shortener with support for custom domains. Create and edit links, view statistics, manage users, and more.", "install_methods": [ { From 32fda4e1dbe334169ba26f5a1b11e7c9b85e95cd Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Mon, 15 Dec 2025 19:12:40 +0100 Subject: [PATCH 055/378] update ssl warning --- frontend/public/json/kutt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/kutt.json b/frontend/public/json/kutt.json index 032f3d770..d2637f7f1 100644 --- a/frontend/public/json/kutt.json +++ b/frontend/public/json/kutt.json @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "A valid TLS certificate is **REQUIRED**", + "text": "A reverse proxy that serves an SSL certificate is required. Otherwise the login will not work.", "type": "warning" } ] From 601250feeedfe06a8ce828260f62ca73dd6595e1 Mon Sep 17 00:00:00 2001 From: CrazyWolf13 Date: Wed, 17 Dec 2025 20:32:44 +0100 Subject: [PATCH 056/378] update --- tools/addon/pihole-exporter.sh | 1 + tools/addon/qbittorrent-exporter.sh | 250 +++++++++++++++++++--------- 2 files changed, 173 insertions(+), 78 deletions(-) diff --git a/tools/addon/pihole-exporter.sh b/tools/addon/pihole-exporter.sh index f9c60bf3d..73282c18d 100644 --- a/tools/addon/pihole-exporter.sh +++ b/tools/addon/pihole-exporter.sh @@ -176,6 +176,7 @@ EOF # Create update script msg_info "Creating update script" + ensure_usr_local_bin_persist cat <<'UPDATEEOF' >/usr/local/bin/update_pihole-exporter #!/usr/bin/env bash # pihole-exporter Update Script diff --git a/tools/addon/qbittorrent-exporter.sh b/tools/addon/qbittorrent-exporter.sh index 03e459803..9d87a478c 100644 --- a/tools/addon/qbittorrent-exporter.sh +++ b/tools/addon/qbittorrent-exporter.sh @@ -8,17 +8,28 @@ 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) 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/error_handler.func) +load_functions +# Enable error handling +set -Eeuo pipefail +trap 'error_handler' ERR + +# ============================================================================== +# CONFIGURATION +# ============================================================================== VERBOSE=${var_verbose:-no} APP="qbittorrent-exporter" APP_TYPE="tools" -INSTALL_PATH="/opt/qbittorrent-exporter/src/qbittorrent-exporter" +INSTALL_PATH="/opt/qbittorrent-exporter" CONFIG_PATH="/opt/qbittorrent-exporter.env" header_info ensure_usr_local_bin_persist get_current_ip &>/dev/null -# OS Detection +# ============================================================================== +# OS DETECTION +# ============================================================================== if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" SERVICE_PATH="/etc/init.d/qbittorrent-exporter" @@ -30,108 +41,118 @@ else exit 1 fi -# Existing installation -if [[ -f "$INSTALL_PATH" ]]; then - echo -e "${YW}⚠️ qbittorrent-exporter is already installed.${CL}" - echo -n "Uninstall ${APP}? (y/N): " - read -r uninstall_prompt - if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then - msg_info "Uninstalling qbittorrent-exporter" - if [[ "$OS" == "Debian" ]]; then - systemctl disable --now qbittorrent-exporter.service &>/dev/null - rm -f "$SERVICE_PATH" - else - rc-service qbittorrent-exporter stop &>/dev/null - rc-update del qbittorrent-exporter &>/dev/null - rm -f "$SERVICE_PATH" - fi - rm -f "$INSTALL_PATH" "$CONFIG_PATH" ~/.qbittorrent-exporter - msg_ok "${APP} has been uninstalled." - exit 0 - fi - - echo -n "Update qbittorrent-exporter? (y/N): " - read -r update_prompt - if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then - if check_for_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter"; then - fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" - setup_go - msg_info "Updating qbittorrent-exporter" - cd /opt/qbittorrent-exporter/src - $STD /usr/local/bin/go build -o ./qbittorrent-exporter - msg_ok "Updated Successfully!" - fi - exit 0 +# ============================================================================== +# UNINSTALL +# ============================================================================== +function uninstall() { + msg_info "Uninstalling qBittorrent-Exporter" + if [[ "$OS" == "Alpine" ]]; then + rc-service qbittorrent-exporter stop &>/dev/null + rc-update del qbittorrent-exporter &>/dev/null + rm -f "$SERVICE_PATH" else - echo -e "${YW}⚠️ Update skipped. Exiting.${CL}" - exit 0 + systemctl disable -q --now qbittorrent-exporter + rm -f "$SERVICE_PATH" fi -fi + rm -rf "$INSTALL_PATH" "$CONFIG_PATH" + rm -f "/usr/local/bin/update_qbittorrent-exporter" + rm -f "$HOME/.qbittorrent-exporter" + msg_ok "qBittorrent-Exporter has been uninstalled" +} -echo -e "${YW}⚠️ qbittorrent-exporter is not installed.${CL}" -echo -n "Enter URL of qbittorrent, example: (http://127.0.0.1:8080): " -read -er QBITTORRENT_BASE_URL +# ============================================================================== +# UPDATE +# ============================================================================== +function update() { + if check_for_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter"; then + msg_info "Stopping service" + if [[ "$OS" == "Alpine" ]]; then + rc-service qbittorrent-exporter stop &>/dev/null + else + systemctl stop qbittorrent-exporter + fi + msg_ok "Stopped service" -echo -n "Enter qbittorrent username: " -read -er QBITTORRENT_USERNAME + fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" + setup_go -echo -n "Enter qbittorrent password: " -read -rs QBITTORRENT_PASSWORD -echo + msg_info "Building qBittorrent-Exporter" + cd /opt/qbittorrent-exporter/src + $STD /usr/local/bin/go build -o ../qbittorrent-exporter + msg_ok "Built qBittorrent-Exporter" -echo -n "Install qbittorrent-exporter? (y/n): " -read -r install_prompt -if ! [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then - echo -e "${YW}⚠️ Installation skipped. Exiting.${CL}" - exit 0 -fi + msg_info "Starting service" + if [[ "$OS" == "Alpine" ]]; then + rc-service qbittorrent-exporter start &>/dev/null + else + systemctl start qbittorrent-exporter + fi + msg_ok "Started service" + msg_ok "Updated successfully" + exit + fi +} -fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" -setup_go -msg_info "Installing qbittorrent-exporter on ${OS}" -cd /opt/qbittorrent-exporter/src -$STD /usr/local/bin/go build -o ./qbittorrent-exporter -msg_ok "Installed qbittorrent-exporter" +# ============================================================================== +# INSTALL +# ============================================================================== +function install() { + read -erp "Enter URL of qBittorrent, example: (http://127.0.0.1:8080): " QBITTORRENT_BASE_URL + read -erp "Enter qBittorrent username: " QBITTORRENT_USERNAME + read -rsp "Enter qBittorrent password: " QBITTORRENT_PASSWORD + printf "\n" -msg_info "Creating configuration" -cat <"$CONFIG_PATH" + fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" + setup_go + msg_info "Building qBittorrent-Exporter on ${OS}" + cd /opt/qbittorrent-exporter/src + $STD /usr/local/bin/go build -o ../qbittorrent-exporter + msg_ok "Built qBittorrent-Exporter" + + msg_info "Creating configuration" + cat <"$CONFIG_PATH" # https://github.com/martabal/qbittorrent-exporter?tab=readme-ov-file#parameters QBITTORRENT_BASE_URL="${QBITTORRENT_BASE_URL}" QBITTORRENT_USERNAME="${QBITTORRENT_USERNAME}" QBITTORRENT_PASSWORD="${QBITTORRENT_PASSWORD}" EOF -msg_ok "Created configuration" + msg_ok "Created configuration" -msg_info "Creating service" -if [[ "$OS" == "Debian" ]]; then - cat <"$SERVICE_PATH" + msg_info "Creating service" + if [[ "$OS" == "Debian" ]]; then + cat <"$SERVICE_PATH" [Unit] Description=qbittorrent-exporter After=network.target [Service] User=root -WorkingDirectory=/opt/qbittorrent-exporter/src +WorkingDirectory=/opt/qbittorrent-exporter EnvironmentFile=$CONFIG_PATH -ExecStart=/opt/qbittorrent-exporter/src/qbittorrent-exporter +ExecStart=/opt/qbittorrent-exporter/qbittorrent-exporter Restart=always [Install] WantedBy=multi-user.target EOF - systemctl enable -q --now qbittorrent-exporter -else - cat <"$SERVICE_PATH" + systemctl daemon-reload + systemctl enable -q --now qbittorrent-exporter + else + cat <"$SERVICE_PATH" #!/sbin/openrc-run -command="$INSTALL_PATH" -command_args="" +name="qbittorrent-exporter" +description="qBittorrent Exporter for Prometheus" +command="${INSTALL_PATH}/qbittorrent-exporter" command_background=true -directory="/opt/qbittorrent-exporter/src" -pidfile="/opt/qbittorrent-exporter/src/pidfile" +directory="/opt/qbittorrent-exporter" +pidfile="/run/\${RC_SVCNAME}.pid" +output_log="/var/log/qbittorrent-exporter.log" +error_log="/var/log/qbittorrent-exporter.log" depend() { need net + after firewall } start_pre() { @@ -140,10 +161,83 @@ start_pre() { fi } EOF - chmod +x "$SERVICE_PATH" - rc-update add qbittorrent-exporter default &>/dev/null - rc-service qbittorrent-exporter start &>/dev/null -fi -msg_ok "Service created successfully" + chmod +x "$SERVICE_PATH" + $STD rc-update add qbittorrent-exporter default + $STD rc-service qbittorrent-exporter start + fi + msg_ok "Created and started service" -echo -e "${CM} ${GN}${APP} is reachable at: ${BL}http://$CURRENT_IP:8090/metrics${CL}" + # Create update script + msg_info "Creating update script" + ensure_usr_local_bin_persist + cat <<'UPDATEEOF' >/usr/local/bin/update_qbittorrent-exporter +#!/usr/bin/env bash +# qbittorrent-exporter Update Script +type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/addon/qbittorrent-exporter.sh)" +UPDATEEOF + chmod +x /usr/local/bin/update_qbittorrent-exporter + msg_ok "Created update script (/usr/local/bin/update_qbittorrent-exporter)" + + echo "" + msg_ok "qBittorrent-Exporter installed successfully" + msg_ok "Metrics: ${BL}http://${CURRENT_IP}:8090/metrics${CL}" + msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" +} + +# ============================================================================== +# MAIN +# ============================================================================== +header_info +ensure_usr_local_bin_persist +get_current_ip &>/dev/null + +# Handle type=update (called from update script) +if [[ "${type:-}" == "update" ]]; then + if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/qbittorrent-exporter" ]]; then + update + else + msg_error "qBittorrent-Exporter is not installed. Nothing to update." + exit 1 + fi + exit 0 +fi + +# Check if already installed +if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/qbittorrent-exporter" ]]; then + msg_warn "qBittorrent-Exporter is already installed." + echo "" + + echo -n "${TAB}Uninstall qBittorrent-Exporter? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + uninstall + exit 0 + fi + + echo -n "${TAB}Update qBittorrent-Exporter? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + update + exit 0 + fi + + msg_warn "No action selected. Exiting." + exit 0 +fi + +# Fresh installation +msg_warn "qBittorrent-Exporter is not installed." +echo "" +echo -e "${TAB}${INFO} This will install:" +echo -e "${TAB} - qBittorrent Exporter (Go binary)" +echo -e "${TAB} - Systemd/OpenRC service" +echo "" + +echo -n "${TAB}Install qBittorrent-Exporter? (y/N): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + install +else + msg_warn "Installation cancelled. Exiting." + exit 0 +fi From b0b7cb790e921e3efa2f43153839e18a849639b2 Mon Sep 17 00:00:00 2001 From: Tobias Date: Wed, 17 Dec 2025 20:53:21 +0100 Subject: [PATCH 057/378] fix path --- tools/addon/qbittorrent-exporter.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/addon/qbittorrent-exporter.sh b/tools/addon/qbittorrent-exporter.sh index 9d87a478c..d40a4ce15 100644 --- a/tools/addon/qbittorrent-exporter.sh +++ b/tools/addon/qbittorrent-exporter.sh @@ -77,8 +77,8 @@ function update() { setup_go msg_info "Building qBittorrent-Exporter" - cd /opt/qbittorrent-exporter/src - $STD /usr/local/bin/go build -o ../qbittorrent-exporter + cd /opt/qbittorrent-exporter + $STD /usr/local/bin/go build -o ./qbittorrent-exporter msg_ok "Built qBittorrent-Exporter" msg_info "Starting service" @@ -105,8 +105,8 @@ function install() { fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" setup_go msg_info "Building qBittorrent-Exporter on ${OS}" - cd /opt/qbittorrent-exporter/src - $STD /usr/local/bin/go build -o ../qbittorrent-exporter + cd /opt/qbittorrent-exporter + $STD /usr/local/bin/go build -o ./qbittorrent-exporter msg_ok "Built qBittorrent-Exporter" msg_info "Creating configuration" From cadb58e7b3382587e53e7154c1014cb667791205 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Dec 2025 08:36:59 +0000 Subject: [PATCH 058/378] Add NetBird scripts with correct ProxmoxVED source URL Fix the build.func source URL to use ProxmoxVED instead of ProxmoxVE, allowing the scripts to actually run in this repository. --- ct/netbird.sh | 48 ++++++++ frontend/public/json/netbird.json | 50 ++++++++ install/netbird-install.sh | 193 ++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 ct/netbird.sh create mode 100644 frontend/public/json/netbird.json create mode 100644 install/netbird-install.sh diff --git a/ct/netbird.sh b/ct/netbird.sh new file mode 100644 index 000000000..7ad572a45 --- /dev/null +++ b/ct/netbird.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: TechHutTV +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://netbird.io/ + +APP="NetBird" +var_tags="network;vpn" +var_cpu="1" +var_ram="512" +var_disk="4" +var_os="debian" +var_version="13" +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 [[ ! -f /etc/netbird/config.json ]]; then +msg_error "No ${APP} Installation Found!" +exit +fi + +msg_info "Updating ${APP}" +$STD apt update +$STD apt -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 NetBird by entering the container and running:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}netbird up${CL}" diff --git a/frontend/public/json/netbird.json b/frontend/public/json/netbird.json new file mode 100644 index 000000000..7fcefd128 --- /dev/null +++ b/frontend/public/json/netbird.json @@ -0,0 +1,50 @@ +{ + "name": "NetBird", + "slug": "netbird", + "categories": [4], + "date_created": "2025-12-02", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://docs.netbird.io/", + "website": "https://netbird.io/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@master/webp/netbird.webp", + "config_path": "/etc/netbird/config.json", + "description": "NetBird is an open-source VPN management platform that creates secure peer-to-peer networks using WireGuard. It enables secure connectivity between devices anywhere in the world without complex firewall configurations or port forwarding. NetBird offers features like zero-configuration networking, SSO integration, access control policies, and a centralized management dashboard. It's designed to be simple to deploy and manage, making it ideal for connecting remote teams, securing IoT devices, or building secure infrastructure networks.", + "install_methods": [ + { + "type": "default", + "script": "ct/netbird.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The NetBird client (agent) allows a peer to join a pre-existing NetBird deployment. If a NetBird deployment is not yet available, there are both managed and self-hosted options available.", + "type": "info" + }, + { + "text": "After installation, enter the container and run `netbird` to to view the commands.", + "type": "info" + }, + { + "text": "Use a Setup Key from your NetBird dashboard or SSO login to authenticate during setup or in the container.", + "type": "info" + }, + { + "text": "Check connection status with `netbird status`.", + "type": "info" + } + ] +} diff --git a/install/netbird-install.sh b/install/netbird-install.sh new file mode 100644 index 000000000..0bbff962f --- /dev/null +++ b/install/netbird-install.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: TechHutTV +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://netbird.io/ + +source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +echo "" +echo ' _ _ _____ _____ ____ _ _ _____ ____ ' +echo '| \ | ||_ _| ____| _ \| | | | ____| _ \' +echo '| \| | _ | | | _| |_| |_ _ __ __ _| | | \' +echo '| . ` |/ _ \ | | |_ | __ <| | \'__/ _` | | | \' +echo '| |\ | __/ |_| |__| |_) | | | | (__ | |_| |' +echo '|_| \_|\___|\___|\__|____/|_|_| \___|\_____/' +echo "" + +msg_info "Installing Dependencies" +$STD apt install -y \ +curl \ +ca-certificates \ +gnupg +msg_ok "Installed Dependencies" + +msg_info "Setting up NetBird Repository" +curl -sSL https://pkgs.netbird.io/debian/public.key \ +| gpg --dearmor -o /usr/share/keyrings/netbird-archive-keyring.gpg +chmod 0644 /usr/share/keyrings/netbird-archive-keyring.gpg +echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' \ +| tee /etc/apt/sources.list.d/netbird.list > /dev/null +$STD apt update +msg_ok "Set up NetBird Repository" + +msg_info "Installing NetBird" +$STD apt install -y netbird +msg_ok "Installed NetBird" + +msg_info "Enabling NetBird Service" +$STD netbird service install 2>/dev/null || true +$STD netbird service start 2>/dev/null || true +$STD systemctl enable netbird +msg_ok "Enabled NetBird Service" + +# NetBird Deployment Type Selection + +echo "" +echo -e "${BL}NetBird Deployment Type${CL}" +echo "─────────────────────────────────────────" +echo "Are you using NetBird Managed or Self-Hosted?" +echo "" +echo " 1) NetBird Managed (default) - Use NetBird's managed service" +echo " 2) Self-Hosted - Use your own NetBird management server" +echo "" + +read -rp "Select deployment type [1]: " DEPLOYMENT_TYPE +DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-1}" + +NETBIRD_MGMT_URL="" +case "$DEPLOYMENT_TYPE" in +1) +msg_info "Using NetBird Managed service" +;; +2) +echo "" +echo -e "${BL}Self-Hosted Configuration${CL}" +echo "─────────────────────────────────────────" +echo "Enter your NetBird management server URL." +echo "Example: https://management.example.com" +echo "" +read -rp "Management URL: " NETBIRD_MGMT_URL + +if [[ -z "$NETBIRD_MGMT_URL" ]]; then +msg_warn "No management URL provided. Run 'netbird up --management-url ' within the container to connect." +else +# Ensure URL doesn't end with trailing slash +NETBIRD_MGMT_URL="${NETBIRD_MGMT_URL%/}" +msg_info "Management URL configured: ${GN}${NETBIRD_MGMT_URL}${CL}" +fi +;; +*) +msg_warn "Invalid selection. Using NetBird Managed service." +;; +esac + +# NetBird Connection Setup + +echo "" +echo -e "${BL}NetBird Connection Setup${CL}" +echo "─────────────────────────────────────────" +echo "Choose how to connect to your NetBird network:" +echo "" +echo " 1) Setup Key (default) - Use a pre-generated setup key" +echo " 2) SSO Login - Authenticate via browser with your identity provider" +echo " 3) Skip - Configure later with 'netbird up'" +echo "" + +read -rp "Select authentication method [1]: " AUTH_METHOD +AUTH_METHOD="${AUTH_METHOD:-1}" + +case "$AUTH_METHOD" in +1) +# Setup Key authentication +echo "" +echo "Enter your NetBird setup key from the NetBird dashboard." +echo "" +read -rp "Setup key: " NETBIRD_SETUP_KEY +echo "" + +if [[ -z "$NETBIRD_SETUP_KEY" ]]; then +if [[ -n "$NETBIRD_MGMT_URL" ]]; then +msg_warn "No setup key provided. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' within the container to connect." +else +msg_warn "No setup key provided. Run 'netbird up -k ' within the container to connect." +fi +else +echo -e "Setup key: ${GN}${NETBIRD_SETUP_KEY}${CL}" +read -rp "Press Enter to continue or Ctrl+C to cancel..." + +msg_info "Connecting to NetBird with setup key" +if [[ -n "$NETBIRD_MGMT_URL" ]]; then +if netbird up -k "$NETBIRD_SETUP_KEY" --management-url "$NETBIRD_MGMT_URL"; then +msg_ok "Connected to NetBird" +else +msg_warn "Connection failed. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' within the container to retry." +fi +else +if netbird up -k "$NETBIRD_SETUP_KEY"; then +msg_ok "Connected to NetBird" +else +msg_warn "Connection failed. Run 'netbird up -k ' within the container to retry." +fi +fi +fi +;; +2) +# SSO authentication +echo "" +echo -e "${BL}SSO Authentication${CL}" +echo "─────────────────────────────────────────" +echo "A login URL will appear below." +echo "Copy the URL and open it in your browser to authenticate." +echo "" + +msg_info "Starting SSO login" +if [[ -n "$NETBIRD_MGMT_URL" ]]; then +netbird login --management-url "$NETBIRD_MGMT_URL" 2>&1 || true +else +netbird login 2>&1 || true +fi +echo "" + +msg_info "Connecting to NetBird" +if [[ -n "$NETBIRD_MGMT_URL" ]]; then +if netbird up --management-url "$NETBIRD_MGMT_URL"; then +msg_ok "Connected to NetBird" +else +msg_warn "Connection failed. Run 'netbird up --management-url $NETBIRD_MGMT_URL' within the container to retry." +fi +else +if netbird up; then +msg_ok "Connected to NetBird" +else +msg_warn "Connection failed. Run 'netbird up' within the container to retry." +fi +fi +;; +3) +msg_info "Skipping NetBird connection" +if [[ -n "$NETBIRD_MGMT_URL" ]]; then +msg_ok "Run 'netbird up --management-url $NETBIRD_MGMT_URL' within the container to connect." +else +msg_ok "Run 'netbird up' within the container to connect." +fi +;; +*) +msg_warn "Invalid selection. Run 'netbird up' within the container to connect." +;; +esac + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +msg_ok "Cleaned" From 00a837b623d562da4774048e7751fd25fbff35a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Dec 2025 08:50:30 +0000 Subject: [PATCH 059/378] Fix ASCII banner syntax error in netbird-install.sh Use heredoc instead of echo statements to avoid backslash escaping issues that caused bash parsing errors. --- install/netbird-install.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 0bbff962f..95612de20 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -13,14 +13,15 @@ setting_up_container network_check update_os -echo "" -echo ' _ _ _____ _____ ____ _ _ _____ ____ ' -echo '| \ | ||_ _| ____| _ \| | | | ____| _ \' -echo '| \| | _ | | | _| |_| |_ _ __ __ _| | | \' -echo '| . ` |/ _ \ | | |_ | __ <| | \'__/ _` | | | \' -echo '| |\ | __/ |_| |__| |_) | | | | (__ | |_| |' -echo '|_| \_|\___|\___|\__|____/|_|_| \___|\_____/' -echo "" +cat <<'EOF' + + _ _ _ ____ _ _ +| \ | | ___| |_| __ )(_)_ __ __| | +| \| |/ _ \ __| _ \| | '__/ _` | +| |\ | __/ |_| |_) | | | | (_| | +|_| \_|\___|\__|____/|_|_| \__,_| + +EOF msg_info "Installing Dependencies" $STD apt install -y \ From a810fa2718fd13463b285d52d4fce81d267e181a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Dec 2025 08:58:35 +0000 Subject: [PATCH 060/378] Simplify NetBird service enablement --- install/netbird-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 95612de20..675225bbd 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -44,9 +44,7 @@ $STD apt install -y netbird msg_ok "Installed NetBird" msg_info "Enabling NetBird Service" -$STD netbird service install 2>/dev/null || true -$STD netbird service start 2>/dev/null || true -$STD systemctl enable netbird +$STD systemctl enable --now netbird msg_ok "Enabled NetBird Service" # NetBird Deployment Type Selection From 7f5f7f4c1fcce828f8edc4d569c6d81cc1a26059 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Dec 2025 09:13:46 +0000 Subject: [PATCH 061/378] Simplify NetBird scripts for proper container setup - Use ${var:-default} pattern for all variables to enable advanced mode - Remove interactive prompts that blocked automated installs - Use cleanup_lxc instead of manual apt cleanup - Users configure NetBird with 'netbird up' after container creation --- ct/netbird.sh | 14 +-- install/netbird-install.sh | 172 +++---------------------------------- 2 files changed, 17 insertions(+), 169 deletions(-) diff --git a/ct/netbird.sh b/ct/netbird.sh index 7ad572a45..4b5714756 100644 --- a/ct/netbird.sh +++ b/ct/netbird.sh @@ -7,13 +7,13 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Source: https://netbird.io/ APP="NetBird" -var_tags="network;vpn" -var_cpu="1" -var_ram="512" -var_disk="4" -var_os="debian" -var_version="13" -var_unprivileged="1" +var_tags="${var_tags:-network;vpn}" +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}" var_tun="${var_tun:-yes}" header_info "$APP" diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 675225bbd..8c2b12ec8 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -5,7 +5,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netbird.io/ -source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors @@ -13,180 +13,28 @@ setting_up_container network_check update_os -cat <<'EOF' - - _ _ _ ____ _ _ -| \ | | ___| |_| __ )(_)_ __ __| | -| \| |/ _ \ __| _ \| | '__/ _` | -| |\ | __/ |_| |_) | | | | (_| | -|_| \_|\___|\__|____/|_|_| \__,_| - -EOF - msg_info "Installing Dependencies" -$STD apt install -y \ -curl \ -ca-certificates \ -gnupg +$STD apt-get install -y \ + curl \ + ca-certificates \ + gnupg msg_ok "Installed Dependencies" msg_info "Setting up NetBird Repository" -curl -sSL https://pkgs.netbird.io/debian/public.key \ -| gpg --dearmor -o /usr/share/keyrings/netbird-archive-keyring.gpg +curl -sSL https://pkgs.netbird.io/debian/public.key | gpg --dearmor -o /usr/share/keyrings/netbird-archive-keyring.gpg chmod 0644 /usr/share/keyrings/netbird-archive-keyring.gpg -echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' \ -| tee /etc/apt/sources.list.d/netbird.list > /dev/null -$STD apt update +echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | tee /etc/apt/sources.list.d/netbird.list >/dev/null +$STD apt-get update msg_ok "Set up NetBird Repository" msg_info "Installing NetBird" -$STD apt install -y netbird +$STD apt-get install -y netbird msg_ok "Installed NetBird" msg_info "Enabling NetBird Service" $STD systemctl enable --now netbird msg_ok "Enabled NetBird Service" -# NetBird Deployment Type Selection - -echo "" -echo -e "${BL}NetBird Deployment Type${CL}" -echo "─────────────────────────────────────────" -echo "Are you using NetBird Managed or Self-Hosted?" -echo "" -echo " 1) NetBird Managed (default) - Use NetBird's managed service" -echo " 2) Self-Hosted - Use your own NetBird management server" -echo "" - -read -rp "Select deployment type [1]: " DEPLOYMENT_TYPE -DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-1}" - -NETBIRD_MGMT_URL="" -case "$DEPLOYMENT_TYPE" in -1) -msg_info "Using NetBird Managed service" -;; -2) -echo "" -echo -e "${BL}Self-Hosted Configuration${CL}" -echo "─────────────────────────────────────────" -echo "Enter your NetBird management server URL." -echo "Example: https://management.example.com" -echo "" -read -rp "Management URL: " NETBIRD_MGMT_URL - -if [[ -z "$NETBIRD_MGMT_URL" ]]; then -msg_warn "No management URL provided. Run 'netbird up --management-url ' within the container to connect." -else -# Ensure URL doesn't end with trailing slash -NETBIRD_MGMT_URL="${NETBIRD_MGMT_URL%/}" -msg_info "Management URL configured: ${GN}${NETBIRD_MGMT_URL}${CL}" -fi -;; -*) -msg_warn "Invalid selection. Using NetBird Managed service." -;; -esac - -# NetBird Connection Setup - -echo "" -echo -e "${BL}NetBird Connection Setup${CL}" -echo "─────────────────────────────────────────" -echo "Choose how to connect to your NetBird network:" -echo "" -echo " 1) Setup Key (default) - Use a pre-generated setup key" -echo " 2) SSO Login - Authenticate via browser with your identity provider" -echo " 3) Skip - Configure later with 'netbird up'" -echo "" - -read -rp "Select authentication method [1]: " AUTH_METHOD -AUTH_METHOD="${AUTH_METHOD:-1}" - -case "$AUTH_METHOD" in -1) -# Setup Key authentication -echo "" -echo "Enter your NetBird setup key from the NetBird dashboard." -echo "" -read -rp "Setup key: " NETBIRD_SETUP_KEY -echo "" - -if [[ -z "$NETBIRD_SETUP_KEY" ]]; then -if [[ -n "$NETBIRD_MGMT_URL" ]]; then -msg_warn "No setup key provided. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' within the container to connect." -else -msg_warn "No setup key provided. Run 'netbird up -k ' within the container to connect." -fi -else -echo -e "Setup key: ${GN}${NETBIRD_SETUP_KEY}${CL}" -read -rp "Press Enter to continue or Ctrl+C to cancel..." - -msg_info "Connecting to NetBird with setup key" -if [[ -n "$NETBIRD_MGMT_URL" ]]; then -if netbird up -k "$NETBIRD_SETUP_KEY" --management-url "$NETBIRD_MGMT_URL"; then -msg_ok "Connected to NetBird" -else -msg_warn "Connection failed. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' within the container to retry." -fi -else -if netbird up -k "$NETBIRD_SETUP_KEY"; then -msg_ok "Connected to NetBird" -else -msg_warn "Connection failed. Run 'netbird up -k ' within the container to retry." -fi -fi -fi -;; -2) -# SSO authentication -echo "" -echo -e "${BL}SSO Authentication${CL}" -echo "─────────────────────────────────────────" -echo "A login URL will appear below." -echo "Copy the URL and open it in your browser to authenticate." -echo "" - -msg_info "Starting SSO login" -if [[ -n "$NETBIRD_MGMT_URL" ]]; then -netbird login --management-url "$NETBIRD_MGMT_URL" 2>&1 || true -else -netbird login 2>&1 || true -fi -echo "" - -msg_info "Connecting to NetBird" -if [[ -n "$NETBIRD_MGMT_URL" ]]; then -if netbird up --management-url "$NETBIRD_MGMT_URL"; then -msg_ok "Connected to NetBird" -else -msg_warn "Connection failed. Run 'netbird up --management-url $NETBIRD_MGMT_URL' within the container to retry." -fi -else -if netbird up; then -msg_ok "Connected to NetBird" -else -msg_warn "Connection failed. Run 'netbird up' within the container to retry." -fi -fi -;; -3) -msg_info "Skipping NetBird connection" -if [[ -n "$NETBIRD_MGMT_URL" ]]; then -msg_ok "Run 'netbird up --management-url $NETBIRD_MGMT_URL' within the container to connect." -else -msg_ok "Run 'netbird up' within the container to connect." -fi -;; -*) -msg_warn "Invalid selection. Run 'netbird up' within the container to connect." -;; -esac - motd_ssh customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -msg_ok "Cleaned" +cleanup_lxc From 012aa28a5508a73273ff5a2857b75689ea32e821 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Dec 2025 09:20:13 +0000 Subject: [PATCH 062/378] Restore interactive NetBird setup prompts - Add deployment type selection (Managed vs Self-Hosted) - Add authentication method selection (Setup Key, SSO, Skip) - Interactive prompts work in production when run through build.func - Proper indentation and cleanup_lxc usage --- install/netbird-install.sh | 127 +++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 8c2b12ec8..a3b99fd87 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -35,6 +35,133 @@ msg_info "Enabling NetBird Service" $STD systemctl enable --now netbird msg_ok "Enabled NetBird Service" +# NetBird Deployment Type Selection +echo "" +echo -e "${BL}NetBird Deployment Type${CL}" +echo "─────────────────────────────────────────" +echo "Are you using NetBird Managed or Self-Hosted?" +echo "" +echo " 1) NetBird Managed (default) - Use NetBird's managed service" +echo " 2) Self-Hosted - Use your own NetBird management server" +echo "" + +read -r -p "Select deployment type [1]: " DEPLOYMENT_TYPE +DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-1}" + +NETBIRD_MGMT_URL="" +case "$DEPLOYMENT_TYPE" in + 1) + msg_ok "Using NetBird Managed service" + ;; + 2) + echo "" + echo -e "${BL}Self-Hosted Configuration${CL}" + echo "─────────────────────────────────────────" + echo "Enter your NetBird management server URL." + echo "Example: https://management.example.com" + echo "" + read -r -p "Management URL: " NETBIRD_MGMT_URL + + if [[ -z "$NETBIRD_MGMT_URL" ]]; then + msg_warn "No management URL provided. Run 'netbird up --management-url ' to connect." + else + NETBIRD_MGMT_URL="${NETBIRD_MGMT_URL%/}" + msg_ok "Management URL configured: ${NETBIRD_MGMT_URL}" + fi + ;; + *) + msg_warn "Invalid selection. Using NetBird Managed service." + ;; +esac + +# NetBird Connection Setup +echo "" +echo -e "${BL}NetBird Connection Setup${CL}" +echo "─────────────────────────────────────────" +echo "Choose how to connect to your NetBird network:" +echo "" +echo " 1) Setup Key (default) - Use a pre-generated setup key" +echo " 2) SSO Login - Authenticate via browser with your identity provider" +echo " 3) Skip - Configure later with 'netbird up'" +echo "" + +read -r -p "Select authentication method [1]: " AUTH_METHOD +AUTH_METHOD="${AUTH_METHOD:-1}" + +case "$AUTH_METHOD" in + 1) + echo "" + echo "Enter your NetBird setup key from the NetBird dashboard." + echo "" + read -r -p "Setup key: " NETBIRD_SETUP_KEY + echo "" + + if [[ -z "$NETBIRD_SETUP_KEY" ]]; then + if [[ -n "$NETBIRD_MGMT_URL" ]]; then + msg_warn "No setup key provided. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to connect." + else + msg_warn "No setup key provided. Run 'netbird up -k ' to connect." + fi + else + msg_info "Connecting to NetBird with setup key" + if [[ -n "$NETBIRD_MGMT_URL" ]]; then + if $STD netbird up -k "$NETBIRD_SETUP_KEY" --management-url "$NETBIRD_MGMT_URL"; then + msg_ok "Connected to NetBird" + else + msg_warn "Connection failed. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to retry." + fi + else + if $STD netbird up -k "$NETBIRD_SETUP_KEY"; then + msg_ok "Connected to NetBird" + else + msg_warn "Connection failed. Run 'netbird up -k ' to retry." + fi + fi + fi + ;; + 2) + echo "" + echo -e "${BL}SSO Authentication${CL}" + echo "─────────────────────────────────────────" + echo "A login URL will appear below." + echo "Copy the URL and open it in your browser to authenticate." + echo "" + + msg_info "Starting SSO login" + if [[ -n "$NETBIRD_MGMT_URL" ]]; then + netbird login --management-url "$NETBIRD_MGMT_URL" 2>&1 || true + else + netbird login 2>&1 || true + fi + echo "" + + msg_info "Connecting to NetBird" + if [[ -n "$NETBIRD_MGMT_URL" ]]; then + if $STD netbird up --management-url "$NETBIRD_MGMT_URL"; then + msg_ok "Connected to NetBird" + else + msg_warn "Connection failed. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to retry." + fi + else + if $STD netbird up; then + msg_ok "Connected to NetBird" + else + msg_warn "Connection failed. Run 'netbird up' to retry." + fi + fi + ;; + 3) + if [[ -n "$NETBIRD_MGMT_URL" ]]; then + msg_ok "Skipped. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to connect." + else + msg_ok "Skipped. Run 'netbird up' to connect." + fi + ;; + *) + msg_warn "Invalid selection. Run 'netbird up' to connect." + ;; +esac + motd_ssh customize cleanup_lxc From a7ef3c18148c22b22777cee1b325cd3479adf036 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Dec 2025 09:35:31 +0000 Subject: [PATCH 063/378] Add app variable for cleanup_lxc compatibility --- install/netbird-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index a3b99fd87..0d6fa9e27 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -6,6 +6,7 @@ # Source: https://netbird.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +app="netbird" color verb_ip6 catch_errors From 43f75512e6f85906ea691f340883be5c7d891c77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 21:35:49 +0000 Subject: [PATCH 064/378] Delete qbittorrent-exporter (addon) after migration to ProxmoxVE (#1221) Co-authored-by: github-actions[bot] --- .../public/json/qbittorrent-exporter.json | 46 ---- tools/addon/qbittorrent-exporter.sh | 243 ------------------ 2 files changed, 289 deletions(-) delete mode 100644 frontend/public/json/qbittorrent-exporter.json delete mode 100644 tools/addon/qbittorrent-exporter.sh diff --git a/frontend/public/json/qbittorrent-exporter.json b/frontend/public/json/qbittorrent-exporter.json deleted file mode 100644 index dba785b6f..000000000 --- a/frontend/public/json/qbittorrent-exporter.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "qbittorrent Exporter", - "slug": "qbittorrent-exporter", - "categories": [ - 9 - ], - "date_created": "2025-11-21", - "type": "addon", - "updateable": true, - "privileged": false, - "interface_port": 8090, - "documentation": "https://github.com/martabal/qbittorrent-exporter", - "website": "https://github.com/martabal/qbittorrent-exporter", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/qbittorrent.webp", - "config_path": "/opt/qbittorrent-exporter.env", - "description": "A fast and lightweight prometheus exporter for qBittorrent ", - "install_methods": [ - { - "type": "default", - "script": "tools/addon/qbittorrent-exporter.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - }, - { - "type": "alpine", - "script": "tools/addon/qbittorrent-exporter.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/tools/addon/qbittorrent-exporter.sh b/tools/addon/qbittorrent-exporter.sh deleted file mode 100644 index d40a4ce15..000000000 --- a/tools/addon/qbittorrent-exporter.sh +++ /dev/null @@ -1,243 +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/martabal/qbittorrent-exporter - -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) -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/error_handler.func) -load_functions - -# Enable error handling -set -Eeuo pipefail -trap 'error_handler' ERR - -# ============================================================================== -# CONFIGURATION -# ============================================================================== -VERBOSE=${var_verbose:-no} -APP="qbittorrent-exporter" -APP_TYPE="tools" -INSTALL_PATH="/opt/qbittorrent-exporter" -CONFIG_PATH="/opt/qbittorrent-exporter.env" -header_info -ensure_usr_local_bin_persist -get_current_ip &>/dev/null - -# ============================================================================== -# OS DETECTION -# ============================================================================== -if [[ -f "/etc/alpine-release" ]]; then - OS="Alpine" - SERVICE_PATH="/etc/init.d/qbittorrent-exporter" -elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - OS="Debian" - SERVICE_PATH="/etc/systemd/system/qbittorrent-exporter.service" -else - echo -e "${CROSS} Unsupported OS detected. Exiting." - exit 1 -fi - -# ============================================================================== -# UNINSTALL -# ============================================================================== -function uninstall() { - msg_info "Uninstalling qBittorrent-Exporter" - if [[ "$OS" == "Alpine" ]]; then - rc-service qbittorrent-exporter stop &>/dev/null - rc-update del qbittorrent-exporter &>/dev/null - rm -f "$SERVICE_PATH" - else - systemctl disable -q --now qbittorrent-exporter - rm -f "$SERVICE_PATH" - fi - rm -rf "$INSTALL_PATH" "$CONFIG_PATH" - rm -f "/usr/local/bin/update_qbittorrent-exporter" - rm -f "$HOME/.qbittorrent-exporter" - msg_ok "qBittorrent-Exporter has been uninstalled" -} - -# ============================================================================== -# UPDATE -# ============================================================================== -function update() { - if check_for_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter"; then - msg_info "Stopping service" - if [[ "$OS" == "Alpine" ]]; then - rc-service qbittorrent-exporter stop &>/dev/null - else - systemctl stop qbittorrent-exporter - fi - msg_ok "Stopped service" - - fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" - setup_go - - msg_info "Building qBittorrent-Exporter" - cd /opt/qbittorrent-exporter - $STD /usr/local/bin/go build -o ./qbittorrent-exporter - msg_ok "Built qBittorrent-Exporter" - - msg_info "Starting service" - if [[ "$OS" == "Alpine" ]]; then - rc-service qbittorrent-exporter start &>/dev/null - else - systemctl start qbittorrent-exporter - fi - msg_ok "Started service" - msg_ok "Updated successfully" - exit - fi -} - -# ============================================================================== -# INSTALL -# ============================================================================== -function install() { - read -erp "Enter URL of qBittorrent, example: (http://127.0.0.1:8080): " QBITTORRENT_BASE_URL - read -erp "Enter qBittorrent username: " QBITTORRENT_USERNAME - read -rsp "Enter qBittorrent password: " QBITTORRENT_PASSWORD - printf "\n" - - fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" - setup_go - msg_info "Building qBittorrent-Exporter on ${OS}" - cd /opt/qbittorrent-exporter - $STD /usr/local/bin/go build -o ./qbittorrent-exporter - msg_ok "Built qBittorrent-Exporter" - - msg_info "Creating configuration" - cat <"$CONFIG_PATH" -# https://github.com/martabal/qbittorrent-exporter?tab=readme-ov-file#parameters -QBITTORRENT_BASE_URL="${QBITTORRENT_BASE_URL}" -QBITTORRENT_USERNAME="${QBITTORRENT_USERNAME}" -QBITTORRENT_PASSWORD="${QBITTORRENT_PASSWORD}" -EOF - msg_ok "Created configuration" - - msg_info "Creating service" - if [[ "$OS" == "Debian" ]]; then - cat <"$SERVICE_PATH" -[Unit] -Description=qbittorrent-exporter -After=network.target - -[Service] -User=root -WorkingDirectory=/opt/qbittorrent-exporter -EnvironmentFile=$CONFIG_PATH -ExecStart=/opt/qbittorrent-exporter/qbittorrent-exporter -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - systemctl daemon-reload - systemctl enable -q --now qbittorrent-exporter - else - cat <"$SERVICE_PATH" -#!/sbin/openrc-run - -name="qbittorrent-exporter" -description="qBittorrent Exporter for Prometheus" -command="${INSTALL_PATH}/qbittorrent-exporter" -command_background=true -directory="/opt/qbittorrent-exporter" -pidfile="/run/\${RC_SVCNAME}.pid" -output_log="/var/log/qbittorrent-exporter.log" -error_log="/var/log/qbittorrent-exporter.log" - -depend() { - need net - after firewall -} - -start_pre() { - if [ -f "$CONFIG_PATH" ]; then - export \$(grep -v '^#' $CONFIG_PATH | xargs) - fi -} -EOF - chmod +x "$SERVICE_PATH" - $STD rc-update add qbittorrent-exporter default - $STD rc-service qbittorrent-exporter start - fi - msg_ok "Created and started service" - - # Create update script - msg_info "Creating update script" - ensure_usr_local_bin_persist - cat <<'UPDATEEOF' >/usr/local/bin/update_qbittorrent-exporter -#!/usr/bin/env bash -# qbittorrent-exporter Update Script -type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/addon/qbittorrent-exporter.sh)" -UPDATEEOF - chmod +x /usr/local/bin/update_qbittorrent-exporter - msg_ok "Created update script (/usr/local/bin/update_qbittorrent-exporter)" - - echo "" - msg_ok "qBittorrent-Exporter installed successfully" - msg_ok "Metrics: ${BL}http://${CURRENT_IP}:8090/metrics${CL}" - msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" -} - -# ============================================================================== -# MAIN -# ============================================================================== -header_info -ensure_usr_local_bin_persist -get_current_ip &>/dev/null - -# Handle type=update (called from update script) -if [[ "${type:-}" == "update" ]]; then - if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/qbittorrent-exporter" ]]; then - update - else - msg_error "qBittorrent-Exporter is not installed. Nothing to update." - exit 1 - fi - exit 0 -fi - -# Check if already installed -if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/qbittorrent-exporter" ]]; then - msg_warn "qBittorrent-Exporter is already installed." - echo "" - - echo -n "${TAB}Uninstall qBittorrent-Exporter? (y/N): " - read -r uninstall_prompt - if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then - uninstall - exit 0 - fi - - echo -n "${TAB}Update qBittorrent-Exporter? (y/N): " - read -r update_prompt - if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then - update - exit 0 - fi - - msg_warn "No action selected. Exiting." - exit 0 -fi - -# Fresh installation -msg_warn "qBittorrent-Exporter is not installed." -echo "" -echo -e "${TAB}${INFO} This will install:" -echo -e "${TAB} - qBittorrent Exporter (Go binary)" -echo -e "${TAB} - Systemd/OpenRC service" -echo "" - -echo -n "${TAB}Install qBittorrent-Exporter? (y/N): " -read -r install_prompt -if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then - install -else - msg_warn "Installation cancelled. Exiting." - exit 0 -fi From 786be2d8c8137c5c2f537340a94dc1fe857ba940 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:40:29 +0100 Subject: [PATCH 065/378] test unifi --- vm/unifi-os-server-vm.sh | 274 ++++++++++++++++++--------------------- 1 file changed, 127 insertions(+), 147 deletions(-) diff --git a/vm/unifi-os-server-vm.sh b/vm/unifi-os-server-vm.sh index 5e356349b..524896c81 100644 --- a/vm/unifi-os-server-vm.sh +++ b/vm/unifi-os-server-vm.sh @@ -588,96 +588,17 @@ echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" -# --- Inject UniFi Installer via Cloud-Init --- -msg_info "Preparing ${OS_DISPLAY} Cloud Image for UniFi OS" +msg_ok "Downloaded ${OS_DISPLAY} Cloud Image" -# Install virt-customize if not available -if ! command -v virt-customize &>/dev/null; then +# Expand root partition to use full disk space +msg_info "Expanding disk image to ${DISK_SIZE}" + +# Install virt-resize if not available +if ! command -v virt-resize &>/dev/null; then apt-get -qq update >/dev/null apt-get -qq install libguestfs-tools -y >/dev/null fi -# Create UniFi OS installation script and inject it into the image -virt-customize -a "${FILE}" --run-command "cat > /root/install-unifi-os.sh << 'INSTALLSCRIPT' -#!/bin/bash -set -x -exec > /var/log/unifi-install.log 2>&1 - -echo \"=== UniFi OS Installation Started at \$(date) ===\" - -# Wait for cloud-init to complete -if command -v cloud-init >/dev/null 2>&1; then - echo \"Waiting for cloud-init to complete...\" - cloud-init status --wait 2>/dev/null || true -fi - -# Install required packages -echo \"Installing required packages...\" -export DEBIAN_FRONTEND=noninteractive -apt-get update -apt-get install -y curl wget ca-certificates podman uidmap slirp4netns iptables - -# Configure Podman -echo \"Configuring Podman...\" -loginctl enable-linger root - -# Download UniFi OS Server -echo \"Downloading UniFi OS Server ${UOS_VERSION}...\" -cd /root -curl -fsSL '${UOS_URL}' -o unifi-installer.bin -chmod +x unifi-installer.bin - -# Install UniFi OS Server -echo \"Installing UniFi OS Server (this takes 3-5 minutes)...\" -./unifi-installer.bin install - -echo \"Waiting for services to start...\" -sleep 15 - -# Start UniFi OS Server -if systemctl list-unit-files | grep -q unifi-os-server; then - echo \"Starting UniFi OS Server service...\" - systemctl enable unifi-os-server - systemctl start unifi-os-server - sleep 10 - - if systemctl is-active --quiet unifi-os-server; then - echo \"SUCCESS: UniFi OS Server is running\" - else - echo \"WARNING: Checking service status...\" - systemctl status unifi-os-server --no-pager - fi -fi - -touch /root/.unifi-installed -echo \"=== Installation completed at \$(date) ===\" -INSTALLSCRIPT" >/dev/null 2>&1 - -virt-customize -a "${FILE}" --chmod 0755:/root/install-unifi-os.sh >/dev/null 2>&1 - -# Create systemd service for first-boot installation -virt-customize -a "${FILE}" --run-command "cat > /etc/systemd/system/unifi-firstboot.service << 'SVCFILE' -[Unit] -Description=UniFi OS First Boot Installation -After=cloud-init.service network-online.target -Wants=network-online.target -ConditionPathExists=!/root/.unifi-installed - -[Service] -Type=oneshot -ExecStart=/root/install-unifi-os.sh -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target -SVCFILE" >/dev/null 2>&1 - -virt-customize -a "${FILE}" --run-command "systemctl enable unifi-firstboot.service" >/dev/null 2>&1 - -msg_ok "Prepared ${OS_DISPLAY} image with UniFi OS installer" - -# Expand root partition to use full disk space -msg_info "Expanding disk image to ${DISK_SIZE}" qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1 # Detect partition device (sda1 for Ubuntu, vda1 for Debian) @@ -712,21 +633,10 @@ qm set "$VMID" \ qm resize "$VMID" scsi0 "$DISK_SIZE" >/dev/null qm set "$VMID" --agent enabled=1 >/dev/null -# Add Cloud-Init drive (standard Cloud-Init, no custom user-data) +# Add Cloud-Init drive msg_info "Configuring Cloud-Init" setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" >/dev/null 2>&1 -msg_ok "Cloud-Init configured (UniFi OS installs via systemd service on first boot)" - -# Display credentials immediately so user can login -if [ -n "$CLOUDINIT_CRED_FILE" ] && [ -f "$CLOUDINIT_CRED_FILE" ]; then - echo "" - echo -e "${INFO}${BOLD}${GN}Cloud-Init Credentials (save these now!):${CL}" - echo -e "${TAB}${DGN}User: ${BGN}${CLOUDINIT_USER:-root}${CL}" - echo -e "${TAB}${DGN}Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" - echo -e "${TAB}${RD}⚠️ UniFi OS installation runs automatically on first boot${CL}" - echo -e "${TAB}${INFO}Monitor: ${BL}tail -f /var/log/unifi-install.log${CL}" - echo "" -fi +msg_ok "Cloud-Init configured" DESCRIPTION=$( cat </dev/null | jq -r '.[1]["ip-addresses"][]? | select(.["ip-address-type"] == "ipv4") | .["ip-address"]' 2>/dev/null | grep -v "127.0.0.1" | head -1 || echo "") if [ -n "$VM_IP" ]; then - msg_ok "VM IP Address detected: ${VM_IP}" + msg_ok "VM IP Address: ${VM_IP}" break fi sleep 2 done - if [ -n "$VM_IP" ]; then - msg_info "Waiting for UniFi OS installation (via Cloud-Init, takes 5-8 minutes)" - - WAIT_COUNT=0 - MAX_WAIT=600 # 10 minutes max for Cloud-Init installation - PORT_OPEN=0 - LAST_MSG_TIME=0 - - while [ $WAIT_COUNT -lt $MAX_WAIT ]; do - if timeout 2 bash -c ">/dev/tcp/${VM_IP}/11443" 2>/dev/null; then - PORT_OPEN=1 - msg_ok "UniFi OS Server is ready!" - break - fi - - sleep 10 - WAIT_COUNT=$((WAIT_COUNT + 10)) - - # Update message every 30 seconds - if [ $((WAIT_COUNT - LAST_MSG_TIME)) -ge 30 ]; then - echo -e "${BFR}${TAB}${YW}${HOLD}Installation in progress... ${WAIT_COUNT}s elapsed${CL}" - echo -e "${TAB}${INFO}${YW}Monitor: ${BL}ssh ${CLOUDINIT_USER:-root}@${VM_IP} 'tail -f /var/log/unifi-install.log'${CL}" - LAST_MSG_TIME=$WAIT_COUNT - fi - done - - if [ $PORT_OPEN -eq 1 ]; then - echo -e "\n${TAB}${GATEWAY}${BOLD}${GN}✓ UniFi OS Server is ready!${CL}" - echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ Access at: ${BGN}https://${VM_IP}:11443${CL}\n" - else - msg_ok "VM is running, UniFi OS installation in progress" - echo -e "${TAB}${INFO}${YW}Installation runs via systemd service on first boot${CL}" - echo -e "${TAB}${INFO}${YW}This takes 5-8 minutes${CL}" - if [ "$USE_CLOUD_INIT" = "yes" ]; then - echo -e "${TAB}${INFO}${YW}SSH: ${BL}ssh ${CLOUDINIT_USER:-root}@${VM_IP}${CL}" - echo -e "${TAB}${INFO}${YW}Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" - echo -e "${TAB}${INFO}${YW}Monitor: ${BL}tail -f /var/log/unifi-install.log${CL}" - fi - echo -e "${TAB}${INFO}${YW}UniFi OS will be at: ${BGN}https://${VM_IP}:11443${CL}" - fi - else - msg_ok "VM is running (ID: ${VMID})" - echo -e "${TAB}${INFO}${YW}Could not auto-detect IP address${CL}" + if [ -z "$VM_IP" ]; then + msg_error "Could not detect VM IP address" echo -e "${TAB}${INFO}${YW}Use Proxmox Console to login with Cloud-Init credentials${CL}" echo -e "${TAB}${INFO}${YW}User: ${BGN}${CLOUDINIT_USER:-root}${CL} / Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" - echo -e "${TAB}${INFO}${YW}Monitor installation: ${BL}tail -f /var/log/unifi-install.log${CL}" + exit 1 fi + + # Wait for SSH to be ready + msg_info "Waiting for SSH to be ready" + SSH_READY=0 + for i in {1..30}; do + if timeout 5 sshpass -p "${CLOUDINIT_PASSWORD}" ssh -o ConnectTimeout=3 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + "${CLOUDINIT_USER:-root}@${VM_IP}" "echo 'SSH Ready'" >/dev/null 2>&1; then + SSH_READY=1 + msg_ok "SSH connection ready" + break + fi + sleep 2 + done + + if [ $SSH_READY -eq 0 ]; then + msg_error "SSH connection failed" + echo -e "${TAB}${INFO}${YW}Manual login - User: ${BGN}${CLOUDINIT_USER:-root}${CL} / Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" + exit 1 + fi + + # Check if sshpass is installed + if ! command -v sshpass &>/dev/null; then + msg_info "Installing sshpass for automated SSH" + apt-get update -qq >/dev/null 2>&1 + apt-get install -y sshpass -qq >/dev/null 2>&1 + fi + + # Execute UniFi OS installation directly via SSH + msg_info "Installing UniFi OS Server ${UOS_VERSION} (takes 4-6 minutes)" + + # Create installation script + INSTALL_SCRIPT=$( + cat <<'EOFINSTALL' +#!/bin/bash +set -e +export DEBIAN_FRONTEND=noninteractive + +echo "[1/5] Updating system packages..." +apt-get update -qq +apt-get install -y curl wget ca-certificates podman uidmap slirp4netns iptables -qq + +echo "[2/5] Configuring Podman..." +loginctl enable-linger root + +echo "[3/5] Downloading UniFi OS Server installer..." +cd /root +curl -fsSL "UNIFI_URL" -o unifi-installer.bin +chmod +x unifi-installer.bin + +echo "[4/5] Installing UniFi OS Server (this takes 3-5 minutes)..." +./unifi-installer.bin install + +echo "[5/5] Starting UniFi OS Server..." +sleep 15 + +if systemctl list-unit-files | grep -q unifi-os-server; then + systemctl enable unifi-os-server + systemctl start unifi-os-server + sleep 10 + + if systemctl is-active --quiet unifi-os-server; then + echo "✓ UniFi OS Server is running" + else + echo "⚠ Service status:" + systemctl status unifi-os-server --no-pager || true + fi +fi + +echo "Installation completed!" +EOFINSTALL + ) + + # Replace URL placeholder + INSTALL_SCRIPT="${INSTALL_SCRIPT//UNIFI_URL/$UOS_URL}" + + # Execute installation via SSH (with output streaming) + if sshpass -p "${CLOUDINIT_PASSWORD}" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + "${CLOUDINIT_USER:-root}@${VM_IP}" "bash -s" <<<"$INSTALL_SCRIPT" 2>&1 | while IFS= read -r line; do + echo -e "${TAB}${DGN}${line}${CL}" + done; then + msg_ok "UniFi OS Server installed successfully" + else + msg_error "Installation failed" + echo -e "${TAB}${INFO}${YW}Check logs: ${BL}ssh ${CLOUDINIT_USER:-root}@${VM_IP}${CL}" + exit 1 + fi + + # Wait for UniFi OS web interface + msg_info "Waiting for UniFi OS web interface (port 11443)" + PORT_OPEN=0 + for i in {1..60}; do + if timeout 2 bash -c ">/dev/tcp/${VM_IP}/11443" 2>/dev/null; then + PORT_OPEN=1 + msg_ok "UniFi OS Server web interface is ready" + break + fi + sleep 2 + done + + echo "" + if [ $PORT_OPEN -eq 1 ]; then + echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ UniFi OS Server is ready!${CL}" + echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ Access at: ${BGN}https://${VM_IP}:11443${CL}" + else + echo -e "${TAB}${INFO}${YW}UniFi OS is installed but web interface not yet available${CL}" + echo -e "${TAB}${INFO}${YW}Access at: ${BGN}https://${VM_IP}:11443${CL} ${YW}(may take 1-2 more minutes)${CL}" + fi + + echo -e "${TAB}${INFO}${DGN}SSH Access: ${BL}ssh ${CLOUDINIT_USER:-root}@${VM_IP}${CL}" + echo -e "${TAB}${INFO}${DGN}Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" + echo "" fi post_update_to_api "done" "none" From e33a70571069dcd0d5dd2bc160ace6ed9c8fa9de Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 20 Dec 2025 08:42:45 -0500 Subject: [PATCH 066/378] OpenCloud: bump to v4.1.0 --- ct/opencloud.sh | 2 +- install/opencloud-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/opencloud.sh b/ct/opencloud.sh index 78aadba47..acd52b37d 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -29,7 +29,7 @@ function update_script() { exit fi - RELEASE="v4.0.0" + RELEASE="v4.1.0" if check_for_gh_release "opencloud" "opencloud-eu/opencloud" "${RELEASE}"; then msg_info "Stopping services" systemctl stop opencloud opencloud-wopi diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 92b4e55c8..4e406c6f8 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -57,7 +57,7 @@ echo "$COOLPASS" >~/.coolpass msg_ok "Installed Collabora Online" # OpenCloud -fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v4.0.0" "/usr/bin" "opencloud-*-linux-amd64" +fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v4.1.0" "/usr/bin" "opencloud-*-linux-amd64" msg_info "Configuring OpenCloud" DATA_DIR="/var/lib/opencloud/" From 9ed0d3b9bc921e0f70afe088e668c3f7daf125c7 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 20 Dec 2025 08:46:08 -0500 Subject: [PATCH 067/378] nextExplorer: add new OIDC env var --- install/nextexplorer-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/nextexplorer-install.sh b/install/nextexplorer-install.sh index a1f15a7d7..d39598f18 100644 --- a/install/nextexplorer-install.sh +++ b/install/nextexplorer-install.sh @@ -79,6 +79,7 @@ SESSION_SECRET="${SECRET}" # OIDC_CLIENT_SECRET= # OIDC_CALLBACK_URL= # OIDC_SCOPES= +# OIDC_AUTO_CREATE_USERS=true # SEARCH_DEEP= # SEARCH_RIPGREP= From 15ed608b3f96d3ccfef662eacac851a3bd4df692 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 20 Dec 2025 08:50:17 -0500 Subject: [PATCH 068/378] OpenCloud: properly update pkgs --- ct/opencloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/opencloud.sh b/ct/opencloud.sh index acd52b37d..37396bc75 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -37,7 +37,7 @@ function update_script() { msg_info "Updating packages" $STD apt-get update - $STD apt-get dist-upgrade + $STD apt-get dist-upgrade -y msg_ok "Updated packages" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "${RELEASE}" "/usr/bin" "opencloud-*-linux-amd64" From 472050a4b5fcd9f93fa8e7f7a0798a96ab026bb7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 08:16:03 +0000 Subject: [PATCH 069/378] Delete pihole-exporter (addon) after migration to ProxmoxVE (#1225) Co-authored-by: github-actions[bot] --- frontend/public/json/pihole-exporter.json | 46 ---- tools/addon/pihole-exporter.sh | 250 ---------------------- 2 files changed, 296 deletions(-) delete mode 100644 frontend/public/json/pihole-exporter.json delete mode 100644 tools/addon/pihole-exporter.sh diff --git a/frontend/public/json/pihole-exporter.json b/frontend/public/json/pihole-exporter.json deleted file mode 100644 index c27945d03..000000000 --- a/frontend/public/json/pihole-exporter.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "Pi-Hole Exporter", - "slug": "pihole-exporter", - "categories": [ - 9 - ], - "date_created": "2025-12-08", - "type": "addon", - "updateable": true, - "privileged": false, - "interface_port": 9617, - "documentation": "https://github.com/eko/pihole-exporter", - "website": "https://github.com/eko/pihole-exporter", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/pi-hole.webp", - "config_path": "/opt/pihole-exporter.env", - "description": "A Prometheus exporter for PI-Hole's Raspberry PI ad blocker", - "install_methods": [ - { - "type": "default", - "script": "tools/addon/pihole-exporter.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - }, - { - "type": "alpine", - "script": "tools/addon/pihole-exporter.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/tools/addon/pihole-exporter.sh b/tools/addon/pihole-exporter.sh deleted file mode 100644 index 73282c18d..000000000 --- a/tools/addon/pihole-exporter.sh +++ /dev/null @@ -1,250 +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/eko/pihole-exporter/ - -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) -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/error_handler.func) -load_functions - -# Enable error handling -set -Eeuo pipefail -trap 'error_handler' ERR - -# ============================================================================== -# CONFIGURATION -# ============================================================================== -VERBOSE=${var_verbose:-no} -APP="pihole-exporter" -APP_TYPE="tools" -INSTALL_PATH="/opt/pihole-exporter" -CONFIG_PATH="/opt/pihole-exporter.env" -header_info -ensure_usr_local_bin_persist -get_current_ip &>/dev/null - -# ============================================================================== -# OS DETECTION -# ============================================================================== -if [[ -f "/etc/alpine-release" ]]; then - OS="Alpine" - SERVICE_PATH="/etc/init.d/pihole-exporter" -elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - OS="Debian" - SERVICE_PATH="/etc/systemd/system/pihole-exporter.service" -else - echo -e "${CROSS} Unsupported OS detected. Exiting." - exit 1 -fi - -# ============================================================================== -# UNINSTALL -# ============================================================================== -function uninstall() { - msg_info "Uninstalling Pihole-Exporter" - if [[ "$OS" == "Alpine" ]]; then - rc-service pihole-exporter stop &>/dev/null - rc-update del pihole-exporter &>/dev/null - rm -f "$SERVICE_PATH" - else - systemctl disable -q --now pihole-exporter - rm -f "$SERVICE_PATH" - fi - rm -rf "$INSTALL_PATH" "$CONFIG_PATH" - rm -f "/usr/local/bin/update_pihole-exporter" - rm -f "$HOME/.pihole-exporter" - msg_ok "Pihole-Exporter has been uninstalled" -} - -# ============================================================================== -# UPDATE -# ============================================================================== -function update() { - if check_for_gh_release "pihole-exporter" "eko/pihole-exporter"; then - msg_info "Stopping service" - if [[ "$OS" == "Alpine" ]]; then - rc-service pihole-exporter stop &>/dev/null - else - systemctl stop pihole-exporter - fi - msg_ok "Stopped service" - - fetch_and_deploy_gh_release "pihole-exporter" "eko/pihole-exporter" "tarball" "latest" - setup_go - - msg_info "Building Pihole-Exporter" - cd /opt/pihole-exporter/ - $STD /usr/local/bin/go build -o ./pihole-exporter - msg_ok "Built Pihole-Exporter" - - msg_info "Starting service" - if [[ "$OS" == "Alpine" ]]; then - rc-service pihole-exporter start &>/dev/null - else - systemctl start pihole-exporter - fi - msg_ok "Started service" - msg_ok "Updated successfully" - exit - fi -} - -# ============================================================================== -# INSTALL -# ============================================================================== -function install() { - read -erp "Enter the protocol to use (http/https), default https: " pihole_PROTOCOL - read -erp "Enter the hostname of Pihole, example: (127.0.0.1): " pihole_HOSTNAME - read -erp "Enter the port of Pihole, default 443: " pihole_PORT - read -rsp "Enter Pihole password: " pihole_PASSWORD - printf "\n" - read -erp "Do you want to skip TLS-Verification (if using a self-signed Certificate on Pi-Hole) [y/N]: " SKIP_TLS - if [[ "${SKIP_TLS,,}" =~ ^(y|yes)$ ]]; then - pihole_SKIP_TLS="true" - fi - - fetch_and_deploy_gh_release "pihole-exporter" "eko/pihole-exporter" "tarball" "latest" - setup_go - msg_info "Building Pihole-Exporter on ${OS}" - cd /opt/pihole-exporter/ - $STD /usr/local/bin/go build -o ./pihole-exporter - msg_ok "Built Pihole-Exporter" - - msg_info "Creating configuration" - cat <"$CONFIG_PATH" -# https://github.com/eko/pihole-exporter/?tab=readme-ov-file#available-cli-options -PIHOLE_PASSWORD="${pihole_PASSWORD}" -PIHOLE_HOSTNAME="${pihole_HOSTNAME:-127.0.0.1}" -PIHOLE_PORT="${pihole_PORT:-443}" -SKIP_TLS_VERIFICATION="${pihole_SKIP_TLS:-false}" -PIHOLE_PROTOCOL="${pihole_PROTOCOL:-https}" -EOF - msg_ok "Created configuration" - - msg_info "Creating service" - if [[ "$OS" == "Debian" ]]; then - cat <"$SERVICE_PATH" -[Unit] -Description=pihole-exporter -After=network.target - -[Service] -User=root -WorkingDirectory=/opt/pihole-exporter -EnvironmentFile=$CONFIG_PATH -ExecStart=/opt/pihole-exporter/pihole-exporter -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - systemctl daemon-reload - systemctl enable -q --now pihole-exporter - else - cat <"$SERVICE_PATH" -#!/sbin/openrc-run - -name="pihole-exporter" -description="Pi-hole Exporter for Prometheus" -command="${INSTALL_PATH}/pihole-exporter" -command_background=true -directory="/opt/pihole-exporter" -pidfile="/run/\${RC_SVCNAME}.pid" -output_log="/var/log/pihole-exporter.log" -error_log="/var/log/pihole-exporter.log" - -depend() { - need net - after firewall -} - -start_pre() { - if [ -f "$CONFIG_PATH" ]; then - export \$(grep -v '^#' $CONFIG_PATH | xargs) - fi -} -EOF - chmod +x "$SERVICE_PATH" - $STD rc-update add pihole-exporter default - $STD rc-service pihole-exporter start - fi - msg_ok "Created and started service" - - # Create update script - msg_info "Creating update script" - ensure_usr_local_bin_persist - cat <<'UPDATEEOF' >/usr/local/bin/update_pihole-exporter -#!/usr/bin/env bash -# pihole-exporter Update Script -type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/addon/pihole-exporter.sh)" -UPDATEEOF - chmod +x /usr/local/bin/update_pihole-exporter - msg_ok "Created update script (/usr/local/bin/update_pihole-exporter)" - - echo "" - msg_ok "Pihole-Exporter installed successfully" - msg_ok "Metrics: ${BL}http://${CURRENT_IP}:9617/metrics${CL}" - msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" -} - -# ============================================================================== -# MAIN -# ============================================================================== -header_info -ensure_usr_local_bin_persist -get_current_ip &>/dev/null - -# Handle type=update (called from update script) -if [[ "${type:-}" == "update" ]]; then - if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/pihole-exporter" ]]; then - update - else - msg_error "Pihole-Exporter is not installed. Nothing to update." - exit 1 - fi - exit 0 -fi - -# Check if already installed -if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/pihole-exporter" ]]; then - msg_warn "Pihole-Exporter is already installed." - echo "" - - echo -n "${TAB}Uninstall Pihole-Exporter? (y/N): " - read -r uninstall_prompt - if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then - uninstall - exit 0 - fi - - echo -n "${TAB}Update Pihole-Exporter? (y/N): " - read -r update_prompt - if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then - update - exit 0 - fi - - msg_warn "No action selected. Exiting." - exit 0 -fi - -# Fresh installation -msg_warn "Pihole-Exporter is not installed." -echo "" -echo -e "${TAB}${INFO} This will install:" -echo -e "${TAB} - Pi-hole Exporter (Go binary)" -echo -e "${TAB} - Systemd/OpenRC service" -echo "" - -echo -n "${TAB}Install Pihole-Exporter? (y/N): " -read -r install_prompt -if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then - install -else - msg_warn "Installation cancelled. Exiting." - exit 0 -fi From e0dd403107250df3a0c6283966d7849c9815bc0c Mon Sep 17 00:00:00 2001 From: Tobias Date: Sun, 21 Dec 2025 19:55:12 +0100 Subject: [PATCH 070/378] nextcloud exporter --- frontend/public/json/nextcloud-exporter.json | 46 ++++ tools/addon/nextcloud-exporter.sh | 258 +++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 frontend/public/json/nextcloud-exporter.json create mode 100644 tools/addon/nextcloud-exporter.sh diff --git a/frontend/public/json/nextcloud-exporter.json b/frontend/public/json/nextcloud-exporter.json new file mode 100644 index 000000000..ed242739f --- /dev/null +++ b/frontend/public/json/nextcloud-exporter.json @@ -0,0 +1,46 @@ +{ + "name": "Nextcloud Exporter", + "slug": "nextcloud-exporter", + "categories": [ + 9 + ], + "date_created": "2025-12-17", + "type": "addon", + "updateable": true, + "privileged": false, + "interface_port": 9025, + "documentation": "https://github.com/xperimental/nextcloud-exporter", + "website": "https://github.com/xperimental/nextcloud-exporter", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nextcloud.webp", + "config_path": "/opt/nextcloud-exporter.env", + "description": "Prometheus exporter for Nextcloud servers. ", + "install_methods": [ + { + "type": "default", + "script": "tools/addon/nextcloud-exporter.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + }, + { + "type": "alpine", + "script": "tools/addon/nextcloud-exporter.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh new file mode 100644 index 000000000..1dc457771 --- /dev/null +++ b/tools/addon/nextcloud-exporter.sh @@ -0,0 +1,258 @@ +#!/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/xperimental/nextcloud-exporter + +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) +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/error_handler.func) +load_functions + +# Enable error handling +set -Eeuo pipefail +trap 'error_handler' ERR + +# ============================================================================== +# CONFIGURATION +# ============================================================================== +VERBOSE=${var_verbose:-no} +APP="nextcloud-exporter" +APP_TYPE="tools" +INSTALL_PATH="/opt/nextcloud-exporter" +CONFIG_PATH="/opt/nextcloud-exporter.env" +header_info +ensure_usr_local_bin_persist +get_current_ip &>/dev/null + +# ============================================================================== +# OS DETECTION +# ============================================================================== +if [[ -f "/etc/alpine-release" ]]; then + OS="Alpine" + SERVICE_PATH="/etc/init.d/nextcloud-exporter" +elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + OS="Debian" + SERVICE_PATH="/etc/systemd/system/nextcloud-exporter.service" +else + echo -e "${CROSS} Unsupported OS detected. Exiting." + exit 1 +fi + +# ============================================================================== +# UNINSTALL +# ============================================================================== +function uninstall() { + msg_info "Uninstalling Nextcloud-Exporter" + if [[ "$OS" == "Alpine" ]]; then + rc-service nextcloud-exporter stop &>/dev/null + rc-update del nextcloud-exporter &>/dev/null + rm -f "$SERVICE_PATH" + else + systemctl disable -q --now nextcloud-exporter + rm -f "$SERVICE_PATH" + fi + rm -rf "$INSTALL_PATH" "$CONFIG_PATH" + rm -f "/usr/local/bin/update_nextcloud-exporter" + rm -f "$HOME/.nextcloud-exporter" + msg_ok "Nextcloud-Exporter has been uninstalled" +} + +# ============================================================================== +# UPDATE +# ============================================================================== +function update() { + if check_for_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter"; then + msg_info "Stopping service" + if [[ "$OS" == "Alpine" ]]; then + rc-service nextcloud-exporter stop &>/dev/null + else + systemctl stop nextcloud-exporter + fi + msg_ok "Stopped service" + + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuilt" "latest" "nextcloud-exporter_*_amd64.deb" + setup_go + + msg_info "Starting service" + if [[ "$OS" == "Alpine" ]]; then + rc-service nextcloud-exporter start &>/dev/null + else + systemctl start nextcloud-exporter + fi + msg_ok "Started service" + msg_ok "Updated successfully" + exit + fi +} + +# ============================================================================== +# INSTALL +# ============================================================================== +function install() { + read -erp "Enter URL of Nextcloud, example: (http://127.0.0.1:8080): " NEXTCLOUD_SERVER + read -erp "Enter Nextcloud auth token (press Enter to use username/password instead): " NEXTCLOUD_AUTH_TOKEN + + if [[ -z "$NEXTCLOUD_AUTH_TOKEN" ]]; then + read -erp "Enter Nextcloud username: " NEXTCLOUD_USERNAME + read -rsp "Enter Nextcloud password: " NEXTCLOUD_PASSWORD + printf "\n" + fi + + read -erp "Query additional info for apps? [Y/n]: " QUERY_APPS + if [[ "${QUERY_APPS,,}" =~ ^(n|no)$ ]]; then + NEXTCLOUD_INFO_APPS="false" + fi + + read -erp "Query update information? [Y/n]: " QUERY_UPDATES + if [[ "${QUERY_UPDATES,,}" =~ ^(n|no)$ ]]; then + NEXTCLOUD_INFO_UPDATE="false" + fi + + read -erp "Do you want to skip TLS-Verification (if using a self-signed Certificate on Nextcloud) [y/N]: " SKIP_TLS + if [[ "${SKIP_TLS,,}" =~ ^(y|yes)$ ]]; then + NEXTCLOUD_TLS_SKIP_VERIFY="true" + fi + + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuilt" "latest" "nextcloud-exporter_*_amd64.deb" + setup_go + + msg_info "Creating configuration" + cat <"$CONFIG_PATH" +# https://github.com/xperimental/nextcloud-exporter +NEXTCLOUD_SERVER="${NEXTCLOUD_SERVER}" +NEXTCLOUD_AUTH_TOKEN="${NEXTCLOUD_AUTH_TOKEN}" +NEXTCLOUD_USERNAME="${NEXTCLOUD_USERNAME}" +NEXTCLOUD_PASSWORD="${NEXTCLOUD_PASSWORD}" +NEXTCLOUD_INFO_UPDATE=${NEXTCLOUD_INFO_UPDATE:-"true"} +NEXTCLOUD_INFO_APPS=${NEXTCLOUD_INFO_APPS:-"true"} +NEXTCLOUD_TLS_SKIP_VERIFY=${NEXTCLOUD_TLS_SKIP_VERIFY:-"false"} +NEXTCLOUD_LISTEN_ADDRESS=":9205" +EOF + msg_ok "Created configuration" + + msg_info "Creating service" + if [[ "$OS" == "Debian" ]]; then + cat <"$SERVICE_PATH" +[Unit] +Description=nextcloud-exporter +After=network.target + +[Service] +User=root +WorkingDirectory=/opt/nextcloud-exporter +EnvironmentFile=$CONFIG_PATH +ExecStart=/opt/nextcloud-exporter/nextcloud-exporter +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reload + systemctl enable -q --now nextcloud-exporter + else + cat <"$SERVICE_PATH" +#!/sbin/openrc-run + +name="nextcloud-exporter" +description="Nextcloud Exporter for Prometheus" +command="${INSTALL_PATH}/nextcloud-exporter" +command_background=true +directory="/opt/nextcloud-exporter" +pidfile="/run/\${RC_SVCNAME}.pid" +output_log="/var/log/nextcloud-exporter.log" +error_log="/var/log/nextcloud-exporter.log" + +depend() { + need net + after firewall +} + +start_pre() { + if [ -f "$CONFIG_PATH" ]; then + export \$(grep -v '^#' $CONFIG_PATH | xargs) + fi +} +EOF + chmod +x "$SERVICE_PATH" + $STD rc-update add nextcloud-exporter default + $STD rc-service nextcloud-exporter start + fi + msg_ok "Created and started service" + + # Create update script + msg_info "Creating update script" + ensure_usr_local_bin_persist + cat <<'UPDATEEOF' >/usr/local/bin/update_nextcloud-exporter +#!/usr/bin/env bash +# nextcloud-exporter Update Script +type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/addon/nextcloud-exporter.sh)" +UPDATEEOF + chmod +x /usr/local/bin/update_nextcloud-exporter + msg_ok "Created update script (/usr/local/bin/update_nextcloud-exporter)" + + echo "" + msg_ok "Nextcloud-Exporter installed successfully" + msg_ok "Metrics: ${BL}http://${CURRENT_IP}:9205/metrics${CL}" + msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" +} + +# ============================================================================== +# MAIN +# ============================================================================== +header_info +ensure_usr_local_bin_persist +get_current_ip &>/dev/null + +# Handle type=update (called from update script) +if [[ "${type:-}" == "update" ]]; then + if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/nextcloud-exporter" ]]; then + update + else + msg_error "Nextcloud-Exporter is not installed. Nothing to update." + exit 1 + fi + exit 0 +fi + +# Check if already installed +if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/nextcloud-exporter" ]]; then + msg_warn "Nextcloud-Exporter is already installed." + echo "" + + echo -n "${TAB}Uninstall Nextcloud-Exporter? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + uninstall + exit 0 + fi + + echo -n "${TAB}Update Nextcloud-Exporter? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + update + exit 0 + fi + + msg_warn "No action selected. Exiting." + exit 0 +fi + +# Fresh installation +msg_warn "Nextcloud-Exporter is not installed." +echo "" +echo -e "${TAB}${INFO} This will install:" +echo -e "${TAB} - Nextcloud Exporter (Go binary)" +echo -e "${TAB} - Systemd/OpenRC service" +echo "" + +echo -n "${TAB}Install Nextcloud-Exporter? (y/N): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + install +else + msg_warn "Installation cancelled. Exiting." + exit 0 +fi From ee2166f3d3090af212237eb7b4eb1a38c0b7f635 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:02:53 +0100 Subject: [PATCH 071/378] Update categories in piler.json Removed categories 'Email' and 'Archive' and added new numeric categories. --- frontend/public/json/piler.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/piler.json b/frontend/public/json/piler.json index 1b8cb08c3..a864157ad 100644 --- a/frontend/public/json/piler.json +++ b/frontend/public/json/piler.json @@ -2,8 +2,8 @@ "name": "Piler", "slug": "piler", "categories": [ - "Email", - "Archive" + 7, + 18 ], "date_created": "2025-12-15", "type": "ct", From 2dcceaf93deb0c69f372d51d27645f517a9dbf10 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:10:18 +0100 Subject: [PATCH 072/378] fix json --- frontend/public/json/piler.json | 55 ++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/frontend/public/json/piler.json b/frontend/public/json/piler.json index a864157ad..3c00436ef 100644 --- a/frontend/public/json/piler.json +++ b/frontend/public/json/piler.json @@ -1,23 +1,36 @@ { - "name": "Piler", - "slug": "piler", - "categories": [ - 7, - 18 - ], - "date_created": "2025-12-15", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": "80", - "documentation": "https://www.mailpiler.org/", - "website": "https://www.mailpiler.org/", - "logo": "https://www.mailpiler.org/piler-logo.png", - "description": "Piler is a feature rich open source email archiving solution with support for legal hold, deduplication, full text search, and many more features.", - "install_methods": [ - { - "type": "default", - "script": "ct/piler.sh" - } - ] + "name": "Piler", + "slug": "piler", + "categories": [ + 7, + 18 + ], + "date_created": "2025-12-15", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": "80", + "documentation": "https://www.mailpiler.org/", + "config_path": "", + "website": "https://www.mailpiler.org/", + "logo": "https://www.mailpiler.org/piler-logo.png", + "description": "Piler is a feature rich open source email archiving solution with support for legal hold, deduplication, full text search, and many more features.", + "install_methods": [ + { + "type": "default", + "script": "ct/piler.sh", + "resources": { + "cpu": 4, + "ram": 4096, + "hdd": 20, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } From 84d15b7bfa3cdec272ae715ddd9db3843a842d45 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:13:18 +0100 Subject: [PATCH 073/378] string -> number --- frontend/public/json/piler.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/piler.json b/frontend/public/json/piler.json index 3c00436ef..d463e9a80 100644 --- a/frontend/public/json/piler.json +++ b/frontend/public/json/piler.json @@ -9,7 +9,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": "80", + "interface_port": 80, "documentation": "https://www.mailpiler.org/", "config_path": "", "website": "https://www.mailpiler.org/", From 7ac760f9caf071de427712825a55c2c42bd4d64c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 21 Dec 2025 14:52:17 -0500 Subject: [PATCH 074/378] nextExplorer: fix json --- frontend/public/json/nextexplorer.json | 74 ++++++++++++++------------ 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/frontend/public/json/nextexplorer.json b/frontend/public/json/nextexplorer.json index caa88b336..4e753b3cd 100644 --- a/frontend/public/json/nextexplorer.json +++ b/frontend/public/json/nextexplorer.json @@ -1,37 +1,41 @@ { - "name": "nextExplorer", - "slug": "nextexplorer", - "categories": [ - 11, - 12 - ], - "date_created": "2025-12-11", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://explorer.nxz.ai", - "website": "https://github.com/vikramsoni2/nextExplorer", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nextexplorer.webp", - "config_path": "/etc/nextExplorer/.env", - "description": "", - "install_methods": [ - { - "type": "default", - "script": "ct/nextexplorer.sh", - "resources": { - "cpu": 2, - "ram": 3072, - "hdd": 8, - "os": "Debian", - "version": "13" - } - } - ], - "notes": [ - { - "text": "Bind mount your volume(s) in the `/mnt` directory", - "type": "info" - } - ] + "name": "nextExplorer", + "slug": "nextexplorer", + "categories": [ + 11, + 12 + ], + "date_created": "2025-12-11", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://explorer.nxz.ai", + "website": "https://github.com/vikramsoni2/nextExplorer", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nextexplorer.webp", + "config_path": "/etc/nextExplorer/.env", + "description": "Self-hosted file access for teams, homelabs, and agencies with a refined interface.", + "install_methods": [ + { + "type": "default", + "script": "ct/nextexplorer.sh", + "resources": { + "cpu": 2, + "ram": 3072, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Bind mount your volume(s) in the `/mnt` directory", + "type": "info" + } + ] } From a8b7d1cce40e8aa7c7e52cc8013e14a52911ffb1 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:48:36 +0100 Subject: [PATCH 075/378] fixes --- tools/addon/nextcloud-exporter.sh | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index 1dc457771..da6599b1c 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -7,13 +7,12 @@ 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) -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/error_handler.func) -load_functions # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR +load_functions # ============================================================================== # CONFIGURATION @@ -25,7 +24,6 @@ INSTALL_PATH="/opt/nextcloud-exporter" CONFIG_PATH="/opt/nextcloud-exporter.env" header_info ensure_usr_local_bin_persist -get_current_ip &>/dev/null # ============================================================================== # OS DETECTION @@ -73,7 +71,7 @@ function update() { fi msg_ok "Stopped service" - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuilt" "latest" "nextcloud-exporter_*_amd64.deb" + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "nextcloud-exporter_*_amd64.deb" setup_go msg_info "Starting service" @@ -93,7 +91,7 @@ function update() { # ============================================================================== function install() { read -erp "Enter URL of Nextcloud, example: (http://127.0.0.1:8080): " NEXTCLOUD_SERVER - read -erp "Enter Nextcloud auth token (press Enter to use username/password instead): " NEXTCLOUD_AUTH_TOKEN + read -rsp "Enter Nextcloud auth token (press Enter to use username/password instead): " NEXTCLOUD_AUTH_TOKEN if [[ -z "$NEXTCLOUD_AUTH_TOKEN" ]]; then read -erp "Enter Nextcloud username: " NEXTCLOUD_USERNAME @@ -116,7 +114,7 @@ function install() { NEXTCLOUD_TLS_SKIP_VERIFY="true" fi - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuilt" "latest" "nextcloud-exporter_*_amd64.deb" + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "nextcloud-exporter_*_amd64.deb" setup_go msg_info "Creating configuration" @@ -204,7 +202,6 @@ UPDATEEOF # ============================================================================== header_info ensure_usr_local_bin_persist -get_current_ip &>/dev/null # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then From 44706b2426df16c6af68dc1ed2a4a687e6bfb800 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:50:13 +0100 Subject: [PATCH 076/378] Add newline for better readability in install function --- tools/addon/nextcloud-exporter.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index da6599b1c..8f7111cab 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -92,7 +92,8 @@ function update() { function install() { read -erp "Enter URL of Nextcloud, example: (http://127.0.0.1:8080): " NEXTCLOUD_SERVER read -rsp "Enter Nextcloud auth token (press Enter to use username/password instead): " NEXTCLOUD_AUTH_TOKEN - + printf "\n" + if [[ -z "$NEXTCLOUD_AUTH_TOKEN" ]]; then read -erp "Enter Nextcloud username: " NEXTCLOUD_USERNAME read -rsp "Enter Nextcloud password: " NEXTCLOUD_PASSWORD From aaeb8600832374c44982982424379c115d0ca63f Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 02:19:49 +0300 Subject: [PATCH 077/378] added the main ct script --- ct/rustypaste.sh | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 ct/rustypaste.sh diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh new file mode 100644 index 000000000..d12dc499d --- /dev/null +++ b/ct/rustypaste.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: GoldenSpringness +# License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/orhun/rustypaste + +# App Default Values +APP="rustypaste" +var_tags="${var_tags:-pastebin;storage}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +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 + + # Check if installation is present | -f for file, -d for folder + if [[ ! -f "/opt/${APP}/target/release/rustypaste" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + # Crawling the new version and checking whether an update is required + RELEASE=$(curl -s https://api.github.com/repos/orhun/rustypaste/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 ${APP} + msg_ok "Stopped $APP" + + # Creating Backup + msg_info "Creating Backup" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}/upload" + msg_ok "Backup Created" + + # Execute Update + msg_info "Updating $APP to ${RELEASE}" + cd /opt/rustypaste + + git fetch --tags + git switch --detach ${RELEASE} + + cargo build --locked --release + msg_ok "Updated $APP to ${RELEASE}" + + # Starting Services + msg_info "Starting $APP" + systemctl start ${APP} + msg_ok "Started $APP" + + # Last Action + 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}:8000${CL}" From 2a81c8856899fe98277a8c09c06d48fd97e5f953 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 02:20:10 +0300 Subject: [PATCH 078/378] added the installation script --- install/rustypaste-install.sh | 84 +++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 install/rustypaste-install.sh diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh new file mode 100644 index 000000000..dd37538e7 --- /dev/null +++ b/install/rustypaste-install.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: GoldenSpringness +# License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/orhun/rustypaste + +# 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-get install -y \ + curl \ + git \ + build-essential \ + ca-certificates +msg_ok "Dependencies Installed Successfully" + +setup_rust + +msg_info "Setting up ${APPLICATION}" +# Getting the latest release version +RELEASE=$(curl -s https://api.github.com/repos/orhun/rustypaste/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +cd /opt +git clone https://github.com/orhun/rustypaste.git + +if [[ ! -d "/opt/${APPLICATION}" ]]; then + msg_error "Git clone has failed" + exit +fi + +cd ${APPLICATION} +git fetch --tags +git switch --detach ${RELEASE} # checking out to latest release + +sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml # changing the ip and port + +msg_info "Compiling ${APPLICATION}" +cargo build --locked --release # creating the binary + +if [[ ! -f "/opt/${APPLICATION}/target/release/rustypaste" ]]; then + msg_error "Cargo build failed" + exit +fi + +echo "${RELEASE}" >/opt/${APPLICATION}_version.txt # creating version file for the update function +msg_ok "Setting up ${APPLICATION} is Done!" + +# Creating Service (if needed) +msg_info "Creating Service" +cat </etc/systemd/system/${APPLICATION}.service +[Unit] +Description=${APPLICATION} Service +After=network.target + +[Service] +WorkingDirectory=/opt/rustypaste +ExecStart=/opt/${APPLICATION}/target/release/rustypaste +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable -q --now ${APPLICATION}.service +msg_ok "Created Service" + +msg_ok "RustyPaste is Running!" + +motd_ssh +customize + +# Cleanup +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From f6aeb3117c71dd49527c7472831dd5d91038ad10 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 02:20:22 +0300 Subject: [PATCH 079/378] added the configuration json --- frontend/public/json/rustypaste.json | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/rustypaste.json diff --git a/frontend/public/json/rustypaste.json b/frontend/public/json/rustypaste.json new file mode 100644 index 000000000..8f3aed405 --- /dev/null +++ b/frontend/public/json/rustypaste.json @@ -0,0 +1,35 @@ +{ + "name": "RustyPaste", + "slug": "rustypaste", + "categories": [ + 12 + ], + "date_created": "2025-12-22", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://github.com/orhun/rustypaste", + "config_path": "", + "website": "https://github.com/orhun/rustypaste", + "logo": "https://github.com/orhun/rustypaste/raw/master/img/rustypaste_logo.png", + "description": "/opt/rustypaste/config.toml", + "install_methods": [ + { + "type": "default", + "script": "ct/rustypaste.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 20, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} From 6c39ea9e35e3622bc149d7c21ed5307f1acbf16c Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 02:26:15 +0300 Subject: [PATCH 080/378] Changing misc --- misc/build.func | 10 +++++----- misc/install.func | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7ce994d1e..f7ae8fd0f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(wget -qO- https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" diff --git a/misc/install.func b/misc/install.func index f474b4062..82155a6c1 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,8 +173,8 @@ _bootstrap() { fi # Source core functions - 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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) load_functions catch_errors } From cdc6806bf3f425b1db894933a5522c4e11e9ecb4 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 02:38:54 +0300 Subject: [PATCH 081/378] Changed some more misc --- install/rustypaste-install.sh | 2 +- misc/build.func | 4 ++-- misc/install.func | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index dd37538e7..d2e84745c 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Dependencies Installed Successfully" -setup_rust +RUST_VERSION="1.92.0" setup_rust msg_info "Setting up ${APPLICATION}" # Getting the latest release version diff --git a/misc/build.func b/misc/build.func index f7ae8fd0f..2ea6bcaa6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3199,7 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - 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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -3286,7 +3286,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) diff --git a/misc/install.func b/misc/install.func index 82155a6c1..3932ae72c 100644 --- a/misc/install.func +++ b/misc/install.func @@ -174,7 +174,7 @@ _bootstrap() { # Source core functions source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypastemisc/error_handler.func) load_functions catch_errors } From de220d80d7fa6d3ee5bda230ed6952136224f756 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 02:52:26 +0300 Subject: [PATCH 082/378] added some comments + added note to json --- ct/rustypaste.sh | 6 +++--- frontend/public/json/rustypaste.json | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index d12dc499d..ad82534db 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -41,17 +41,17 @@ function update_script() { # Creating Backup msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}/upload" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}/upload" # Backing up full project + all bins msg_ok "Backup Created" # Execute Update msg_info "Updating $APP to ${RELEASE}" cd /opt/rustypaste - git fetch --tags + git fetch --tags # getting newest versions git switch --detach ${RELEASE} - cargo build --locked --release + cargo build --locked --release # recreating the binary msg_ok "Updated $APP to ${RELEASE}" # Starting Services diff --git a/frontend/public/json/rustypaste.json b/frontend/public/json/rustypaste.json index 8f3aed405..c8cbc59e0 100644 --- a/frontend/public/json/rustypaste.json +++ b/frontend/public/json/rustypaste.json @@ -31,5 +31,7 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + "When updating the script it will backup the whole project, make sure to extract it to a safe location or remove", + ] } From c3343846b67e7e8eaccbaf3cb4253516c3ec578a Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 02:56:36 +0300 Subject: [PATCH 083/378] changed the update script location --- misc/install.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/install.func b/misc/install.func index 3932ae72c..747c49717 100644 --- a/misc/install.func +++ b/misc/install.func @@ -911,7 +911,7 @@ EOF # Create update script # Use var_os for OS-based containers, otherwise use app name local update_script_name="${var_os:-$app}" - echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/ct/${update_script_name}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update # Inject SSH authorized keys if provided From 736fd3262e5610d2ee1bcb2460c393955236969b Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 03:20:56 +0300 Subject: [PATCH 084/378] removed build and install changes --- ct/rustypaste.sh | 2 +- misc/build.func | 14 +++++++------- misc/install.func | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index ad82534db..519799b28 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) +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: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 2ea6bcaa6..da34a7f82 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) + source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(wget -qO- https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) + source <(wget -qO- https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://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" @@ -3199,7 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -3286,7 +3286,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func) + source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main//misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) diff --git a/misc/install.func b/misc/install.func index 747c49717..323d3b934 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,8 +173,8 @@ _bootstrap() { fi # Source core functions - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypastemisc/error_handler.func) + source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main//misc/core.func) + source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors } @@ -911,7 +911,7 @@ EOF # Create update script # Use var_os for OS-based containers, otherwise use app name local update_script_name="${var_os:-$app}" - echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/ct/${update_script_name}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update # Inject SSH authorized keys if provided From 42c76d5288f2b3d9f727253cb04fb520677e8e9e Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 03:23:55 +0300 Subject: [PATCH 085/378] minor fixes --- misc/build.func | 14 ++++++-------- misc/install.func | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index da34a7f82..a82f557f1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -187,14 +187,14 @@ variables() { 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://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/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://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(wget -qO- https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/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" @@ -3199,9 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)" - local lxc_exit=$? - + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" set -Eeuo pipefail # Re-enable error handling trap 'error_handler' ERR # Restore ERR trap @@ -3286,7 +3284,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main//misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) diff --git a/misc/install.func b/misc/install.func index 323d3b934..64dea5fe2 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,8 +173,8 @@ _bootstrap() { fi # Source core functions - source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main//misc/core.func) - source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/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 } From 51cd952fd0e3859e3217b5698aaf87f60bfa30d1 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 03:25:09 +0300 Subject: [PATCH 086/378] minor fix --- misc/build.func | 2 ++ misc/install.func | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index a82f557f1..7ce994d1e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3200,6 +3200,8 @@ EOF' trap - ERR # Remove ERR trap completely lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + local lxc_exit=$? + set -Eeuo pipefail # Re-enable error handling trap 'error_handler' ERR # Restore ERR trap diff --git a/misc/install.func b/misc/install.func index 64dea5fe2..f474b4062 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,7 +173,7 @@ _bootstrap() { fi # Source core functions - 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/core.func) source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors From 87237346b360637453cd6539af638c492dc83af5 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Mon, 22 Dec 2025 19:53:59 +0300 Subject: [PATCH 087/378] setting up for reject fixes --- misc/build.func | 4 ++-- misc/install.func | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7ce994d1e..f9ecb9c58 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3199,7 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - 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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -3286,7 +3286,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) diff --git a/misc/install.func b/misc/install.func index f474b4062..0a239111d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,8 +173,8 @@ _bootstrap() { fi # Source core functions - 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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) load_functions catch_errors } From 5c13fc6c53262b42827027953583ed604dcab6fe Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Dec 2025 21:56:40 +0100 Subject: [PATCH 088/378] Disable installation and build steps in hoodik-install.sh Comment out dependency installation and build steps for Hoodik. --- install/hoodik-install.sh | 74 ++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/install/hoodik-install.sh b/install/hoodik-install.sh index 31376dab3..1a88f54e9 100644 --- a/install/hoodik-install.sh +++ b/install/hoodik-install.sh @@ -13,46 +13,47 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - pkg-config \ - libssl-dev \ - libc6-dev \ - libpq-dev \ - clang \ - llvm \ - nettle-dev \ - build-essential \ - make -msg_ok "Installed Dependencies" +#msg_info "Installing Dependencies" +#$STD apt-get install -y \ +# pkg-config \ +# libssl-dev \ +# libc6-dev \ +# libpq-dev \ +# clang \ +# llvm \ +# nettle-dev \ +# build-essential \ +# make +#msg_ok "Installed Dependencies" -setup_rust -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "tarball" "latest" "/opt/hoodik" +#setup_rust +#NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +#fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "tarball" "latest" "/opt/hoodik" +fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "prebuild" "latest" "/opt/hoodik" "*x86_64.tar.gz" -msg_info "Installing wasm-pack" -$STD cargo install wasm-pack -msg_ok "Installed wasm-pack" +#msg_info "Installing wasm-pack" +#$STD cargo install wasm-pack +#msg_ok "Installed wasm-pack" -msg_info "Building Hoodik Frontend" -cd /opt/hoodik -$STD yarn install --frozen-lockfile -$STD yarn wasm-pack -$STD yarn web:build -msg_ok "Built Hoodik Frontend" +#msg_info "Building Hoodik Frontend" +#cd /opt/hoodik +#$STD yarn install --frozen-lockfile +#$STD yarn wasm-pack +#$STD yarn web:build +#msg_ok "Built Hoodik Frontend" -msg_info "Building Hoodik Backend" -cd /opt/hoodik -$STD cargo build --release -cp /opt/hoodik/target/release/hoodik /usr/local/bin/hoodik -chmod +x /usr/local/bin/hoodik -msg_ok "Built Hoodik Backend" +#msg_info "Building Hoodik Backend" +#cd /opt/hoodik +#$STD cargo build --release +#cp /opt/hoodik/target/release/hoodik /usr/local/bin/hoodik +#chmod +x /usr/local/bin/hoodik +#msg_ok "Built Hoodik Backend" -msg_info "Cleaning up build artifacts" -rm -rf /opt/hoodik/target -rm -rf /root/.cargo/registry -rm -rf /opt/hoodik/node_modules -msg_ok "Cleaned up build artifacts" +#msg_info "Cleaning up build artifacts" +#rm -rf /opt/hoodik/target +#rm -rf /root/.cargo/registry +#rm -rf /opt/hoodik/node_modules +#msg_ok "Cleaned up build artifacts" msg_info "Configuring Hoodik" mkdir -p /opt/hoodik_data @@ -80,7 +81,8 @@ Type=simple User=root WorkingDirectory=/opt/hoodik_data EnvironmentFile=/opt/hoodik/.env -ExecStart=/usr/local/bin/hoodik +#ExecStart=/usr/local/bin/hoodik +ExecStart=/opt/hoodik Restart=always RestartSec=5 From 0befe26bedce09bf5428965def521785ea9ad564 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:04:15 +0300 Subject: [PATCH 089/378] changed rejects from pr review --- ct/rustypaste.sh | 47 ++++++++++++++--------------------- install/rustypaste-install.sh | 37 +++++++++------------------ misc/build.func | 14 +++++------ 3 files changed, 38 insertions(+), 60 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 519799b28..1cca586d1 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,11 +1,10 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -s raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste -# App Default Values APP="rustypaste" var_tags="${var_tags:-pastebin;storage}" var_cpu="${var_cpu:-1}" @@ -25,45 +24,37 @@ function update_script() { check_container_storage check_container_resources - # Check if installation is present | -f for file, -d for folder - if [[ ! -f "/opt/${APP}/target/release/rustypaste" ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -f "/opt/rustypaste/target/release/rustypaste" ]]; then + msg_error "No rustypaste Installation Found!" exit fi - # Crawling the new version and checking whether an update is required - RELEASE=$(curl -s https://api.github.com/repos/orhun/rustypaste/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 ${APP} - msg_ok "Stopped $APP" + if check_for_gh_release "rustypaste" "orhun/rustypaste"; then + + msg_info "Stopping rustypaste" + systemctl stop rustypaste + msg_ok "Stopped rustypaste" - # Creating Backup msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}/upload" # Backing up full project + all bins + tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" "/opt/rustypaste/upload" msg_ok "Backup Created" - # Execute Update - msg_info "Updating $APP to ${RELEASE}" + msg_info "Updating rustypaste to latest" cd /opt/rustypaste - git fetch --tags # getting newest versions - git switch --detach ${RELEASE} + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" - cargo build --locked --release # recreating the binary - msg_ok "Updated $APP to ${RELEASE}" + cargo build --locked --release - # Starting Services - msg_info "Starting $APP" - systemctl start ${APP} - msg_ok "Started $APP" + msg_ok "Updated rustypaste to latest" + + msg_info "Starting rustypaste" + systemctl start rustypaste + msg_ok "Started rustypaste" - # Last Action - echo "${RELEASE}" > /opt/${APP}_version.txt msg_ok "Update Successful" else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_ok "No update required. rustypaste is already at latest" fi exit } @@ -73,6 +64,6 @@ build_container description msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${CREATING}${GN}rustypaste 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/rustypaste-install.sh b/install/rustypaste-install.sh index d2e84745c..d028cbaf2 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -5,7 +5,6 @@ # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste -# Import Functions und Setup source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 @@ -25,51 +24,40 @@ msg_ok "Dependencies Installed Successfully" RUST_VERSION="1.92.0" setup_rust -msg_info "Setting up ${APPLICATION}" -# Getting the latest release version -RELEASE=$(curl -s https://api.github.com/repos/orhun/rustypaste/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -cd /opt -git clone https://github.com/orhun/rustypaste.git +msg_info "Setting up rustypaste" -if [[ ! -d "/opt/${APPLICATION}" ]]; then - msg_error "Git clone has failed" - exit -fi +fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" -cd ${APPLICATION} -git fetch --tags -git switch --detach ${RELEASE} # checking out to latest release +cd /opt/rustypaste -sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml # changing the ip and port +sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml -msg_info "Compiling ${APPLICATION}" -cargo build --locked --release # creating the binary +msg_info "Compiling rustypaste" +cargo build --locked --release -if [[ ! -f "/opt/${APPLICATION}/target/release/rustypaste" ]]; then +if [[ ! -f "/opt/rustypaste/target/release/rustypaste" ]]; then msg_error "Cargo build failed" exit fi -echo "${RELEASE}" >/opt/${APPLICATION}_version.txt # creating version file for the update function -msg_ok "Setting up ${APPLICATION} is Done!" +msg_ok "Setting up rustypaste is Done!" -# Creating Service (if needed) msg_info "Creating Service" -cat </etc/systemd/system/${APPLICATION}.service +cat </etc/systemd/system/rustypaste.service [Unit] -Description=${APPLICATION} Service +Description=rustypaste Service After=network.target [Service] WorkingDirectory=/opt/rustypaste -ExecStart=/opt/${APPLICATION}/target/release/rustypaste +ExecStart=/opt/rustypaste/target/release/rustypaste Restart=always [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now ${APPLICATION}.service +systemctl enable -q --now rustypaste.service msg_ok "Created Service" msg_ok "RustyPaste is Running!" @@ -77,7 +65,6 @@ msg_ok "RustyPaste is Running!" motd_ssh customize -# Cleanup msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean diff --git a/misc/build.func b/misc/build.func index f9ecb9c58..f27aaa963 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2577,7 +2577,7 @@ configure_ssh_settings() { # - 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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2706,7 +2706,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - 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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" From 584c09fb6726c3530953e5329a4528e8641ab5f8 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:08:43 +0300 Subject: [PATCH 090/378] changed rejects from pr review --- ct/rustypaste.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 1cca586d1..9346c3c91 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) +source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/main/misc/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE @@ -40,10 +40,12 @@ function update_script() { msg_ok "Backup Created" msg_info "Updating rustypaste to latest" + cd /opt/rustypaste CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" + cargo build --locked --release msg_ok "Updated rustypaste to latest" From fd6e2e1c6c1fb9f31ca9eede5b612a843504eadf Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:10:38 +0300 Subject: [PATCH 091/378] test --- misc/build.func | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index f27aaa963..c16981901 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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" @@ -2577,7 +2577,7 @@ configure_ssh_settings() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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 @@ -2706,7 +2706,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -3199,8 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" - local lxc_exit=$? + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" set -Eeuo pipefail # Re-enable error handling trap 'error_handler' ERR # Restore ERR trap @@ -3286,7 +3285,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func) + source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main//misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From 546f30a0138ab83c1b2170bc0feffe058af08815 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:11:12 +0300 Subject: [PATCH 092/378] test --- ct/rustypaste.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 9346c3c91..38baf934e 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/main/misc/misc/build.func) +source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE From c5770781ca24a22d58b8566c1aa1486c070e804d Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:16:46 +0300 Subject: [PATCH 093/378] test2 --- ct/rustypaste.sh | 2 +- misc/build.func | 16 ++++++++-------- misc/install.func | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 38baf934e..4ec9ad873 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/main/misc/build.func) +source <(curl -s raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index c16981901..d6589d780 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) + source <(wget -qO- https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2577,7 +2577,7 @@ configure_ssh_settings() { # - 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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2706,7 +2706,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - 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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -4193,7 +4193,7 @@ description() { cat < - Logo + Logo

${APP} LXC

diff --git a/misc/install.func b/misc/install.func index 0a239111d..10fbe43a2 100644 --- a/misc/install.func +++ b/misc/install.func @@ -742,10 +742,10 @@ EOF # Source appropriate tools.func based on OS case "$OS_FAMILY" in alpine) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/alpine-tools.func) ;; *) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/tools.func) ;; esac } From 6d39f50ec671c4658c962cf30113dacb6517179d Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:21:48 +0300 Subject: [PATCH 094/378] test3 --- misc/build.func | 18 +++++++++--------- misc/install.func | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/misc/build.func b/misc/build.func index d6589d780..d897b3066 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/api.func) +source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/error_handler.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) - source <(wget -qO- https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/error_handler.func) + source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) + source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2577,7 +2577,7 @@ configure_ssh_settings() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/tools.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2706,7 +2706,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -3199,7 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - 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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" set -Eeuo pipefail # Re-enable error handling trap 'error_handler' ERR # Restore ERR trap @@ -4193,7 +4193,7 @@ description() { cat < - Logo + Logo

${APP} LXC

diff --git a/misc/install.func b/misc/install.func index 10fbe43a2..d4542fa3e 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,8 +173,8 @@ _bootstrap() { fi # Source core functions - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) + 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/error_handler.func) load_functions catch_errors } @@ -742,10 +742,10 @@ EOF # Source appropriate tools.func based on OS case "$OS_FAMILY" in alpine) - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/alpine-tools.func) + source <(curl -fsSL https://https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/alpine-tools.func) ;; *) - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/tools.func) + source <(curl -fsSL https://https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools.func) ;; esac } From 1edea6ab7aaeade1bee5da0add20f79a3c127b9c Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:25:05 +0300 Subject: [PATCH 095/378] test4 --- misc/build.func | 21 +++++++++++---------- misc/install.func | 10 +++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/misc/build.func b/misc/build.func index d897b3066..b62cce8e7 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/api.func) +source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/error_handler.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/core.func) - source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/error_handler.func) + source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2577,7 +2577,7 @@ configure_ssh_settings() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/tools.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2706,7 +2706,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -3199,7 +3199,8 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling trap 'error_handler' ERR # Restore ERR trap @@ -3285,7 +3286,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main//misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) @@ -4193,7 +4194,7 @@ description() { cat < - Logo + Logo

${APP} LXC

diff --git a/misc/install.func b/misc/install.func index d4542fa3e..52e320174 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,8 +173,8 @@ _bootstrap() { fi # Source core functions - 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/error_handler.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/error_handler.func) load_functions catch_errors } @@ -742,10 +742,10 @@ EOF # Source appropriate tools.func based on OS case "$OS_FAMILY" in alpine) - source <(curl -fsSL https://https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/alpine-tools.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/alpine-tools.func) ;; *) - source <(curl -fsSL https://https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools.func) + source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/tools.func) ;; esac } @@ -911,7 +911,7 @@ EOF # Create update script # Use var_os for OS-based containers, otherwise use app name local update_script_name="${var_os:-$app}" - echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/ct/${update_script_name}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update # Inject SSH authorized keys if provided From 432d3f4ef4052525ed35f40e43c5382fd53bc8f9 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 00:33:05 +0300 Subject: [PATCH 096/378] test5 --- ct/rustypaste.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 4ec9ad873..3511abbb9 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) +source <(curl -s https://github.com/GoldenSpringness/ProxmoxVED/raw/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE From 41277477c641a02bec5f005d8730ed49430c9fe2 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 01:33:05 +0300 Subject: [PATCH 097/378] test6 --- ct/rustypaste.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 3511abbb9..e4c1c7080 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://github.com/GoldenSpringness/ProxmoxVED/raw/main/misc/build.func) +source <(curl -s https://github.com/GoldenSpringness/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE From ddd2b951111c82e57a0aae795e8b02e5e400a13a Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 01:34:50 +0300 Subject: [PATCH 098/378] test7 --- ct/rustypaste.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index e4c1c7080..4ec9ad873 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://github.com/GoldenSpringness/ProxmoxVED/main/misc/build.func) +source <(curl -s raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE From 27407cc28a9ec4adcdfb409b429af7aeb7006d6a Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 01:39:14 +0300 Subject: [PATCH 099/378] test8 --- ct/rustypaste.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 4ec9ad873..a69edc715 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/build.func) +source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/feature/rustypaste/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE From 2042551453622d73924931a2a18b9fc90467d3c1 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 01:43:16 +0300 Subject: [PATCH 100/378] fixed some linking problems --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index b62cce8e7..0d93c48c7 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3199,7 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - 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/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -3286,7 +3286,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From 41b2410f5b2b829bd17a07dba904f2acae9f3e83 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 02:08:18 +0300 Subject: [PATCH 101/378] added env vars change to update script --- ct/rustypaste.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index a69edc715..89617bc20 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -45,6 +45,7 @@ function update_script() { CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" + sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml cargo build --locked --release From c2e81abafeb58ecab99cf100468022b847e5f6d7 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 02:21:11 +0300 Subject: [PATCH 102/378] removed local changes --- ct/rustypaste.sh | 2 +- misc/build.func | 20 ++++++++++---------- misc/install.func | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 89617bc20..a28b96f90 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/feature/rustypaste/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: GoldenSpringness # License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 0d93c48c7..7ce994d1e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -184,17 +184,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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 raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(wget -qO- raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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" @@ -2577,7 +2577,7 @@ configure_ssh_settings() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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 @@ -2706,7 +2706,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -3199,7 +3199,7 @@ EOF' set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -3286,7 +3286,7 @@ EOF' if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) @@ -4194,7 +4194,7 @@ description() { cat < - Logo + Logo

${APP} LXC

diff --git a/misc/install.func b/misc/install.func index 52e320174..f474b4062 100644 --- a/misc/install.func +++ b/misc/install.func @@ -173,8 +173,8 @@ _bootstrap() { fi # Source core functions - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/core.func) - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/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 } @@ -742,10 +742,10 @@ EOF # Source appropriate tools.func based on OS case "$OS_FAMILY" in alpine) - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/alpine-tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) ;; *) - source <(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) ;; esac } @@ -911,7 +911,7 @@ EOF # Create update script # Use var_os for OS-based containers, otherwise use app name local update_script_name="${var_os:-$app}" - echo "bash -c \"\$(curl -fsSL raw.githubusercontent.com/GoldenSpringness/ProxmoxVED/refs/heads/feature/rustypaste/ct/${update_script_name}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update # Inject SSH authorized keys if provided From f987c7482d7709f5eb90ebf30cc71cae58a74192 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 17:57:03 +0000 Subject: [PATCH 103/378] Apply suggestions from code review Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- ct/rustypaste.sh | 8 -------- install/rustypaste-install.sh | 24 ++++-------------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index a28b96f90..39b29e4cb 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -30,7 +30,6 @@ function update_script() { fi if check_for_gh_release "rustypaste" "orhun/rustypaste"; then - msg_info "Stopping rustypaste" systemctl stop rustypaste msg_ok "Stopped rustypaste" @@ -40,15 +39,10 @@ function update_script() { msg_ok "Backup Created" msg_info "Updating rustypaste to latest" - cd /opt/rustypaste - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" - sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml - cargo build --locked --release - msg_ok "Updated rustypaste to latest" msg_info "Starting rustypaste" @@ -56,8 +50,6 @@ function update_script() { msg_ok "Started rustypaste" msg_ok "Update Successful" - else - msg_ok "No update required. rustypaste is already at latest" fi exit } diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index d028cbaf2..bab9586d5 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -24,23 +24,13 @@ msg_ok "Dependencies Installed Successfully" RUST_VERSION="1.92.0" setup_rust -msg_info "Setting up rustypaste" - fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" +msg_info "Setting up rustypaste" cd /opt/rustypaste - sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml - -msg_info "Compiling rustypaste" cargo build --locked --release - -if [[ ! -f "/opt/rustypaste/target/release/rustypaste" ]]; then - msg_error "Cargo build failed" - exit -fi - -msg_ok "Setting up rustypaste is Done!" +msg_ok "Set up rustypaste" msg_info "Creating Service" cat </etc/systemd/system/rustypaste.service @@ -57,15 +47,9 @@ Restart=always WantedBy=multi-user.target EOF -systemctl enable -q --now rustypaste.service +systemctl enable -q --now rustypaste msg_ok "Created Service" -msg_ok "RustyPaste is Running!" - motd_ssh customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" +cleanup_lxc From 54c89eb4fb3dadee9e0a2cbea84f1062dde5ee49 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 20:07:31 +0300 Subject: [PATCH 104/378] 2nd review rejects fix - json and apt-get --- frontend/public/json/rustypaste.json | 6 +++--- install/rustypaste-install.sh | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/public/json/rustypaste.json b/frontend/public/json/rustypaste.json index c8cbc59e0..14c35cf2f 100644 --- a/frontend/public/json/rustypaste.json +++ b/frontend/public/json/rustypaste.json @@ -10,10 +10,10 @@ "privileged": false, "interface_port": 8000, "documentation": "https://github.com/orhun/rustypaste", - "config_path": "", + "config_path": "/opt/rustypaste/config.toml", "website": "https://github.com/orhun/rustypaste", "logo": "https://github.com/orhun/rustypaste/raw/master/img/rustypaste_logo.png", - "description": "/opt/rustypaste/config.toml", + "description": "Rustypaste is a minimal file upload/pastebin service.", "install_methods": [ { "type": "default", @@ -32,6 +32,6 @@ "password": null }, "notes": [ - "When updating the script it will backup the whole project, make sure to extract it to a safe location or remove", + "When updating the script it will backup the whole project including all the uploaded files, make sure to extract it to a safe location or remove", ] } diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index bab9586d5..e2c30d3f9 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -15,8 +15,7 @@ update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ - curl \ +$STD apt install -y \ git \ build-essential \ ca-certificates From 7b4785ad3bcbbc24f908d4a129f9da1e02f2bc63 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 23:07:28 +0300 Subject: [PATCH 105/378] removed git installation --- install/rustypaste-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index e2c30d3f9..76349c872 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -16,7 +16,6 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - git \ build-essential \ ca-certificates msg_ok "Dependencies Installed Successfully" From 262d82117224c9ad1517273de1ae44f44494c436 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Tue, 23 Dec 2025 21:08:07 +0000 Subject: [PATCH 106/378] Apply suggestions from code review Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/rustypaste-install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index 76349c872..808fa08a7 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -13,7 +13,6 @@ setting_up_container network_check update_os - msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ @@ -21,7 +20,6 @@ $STD apt install -y \ msg_ok "Dependencies Installed Successfully" RUST_VERSION="1.92.0" setup_rust - fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" msg_info "Setting up rustypaste" @@ -44,7 +42,6 @@ Restart=always [Install] WantedBy=multi-user.target EOF - systemctl enable -q --now rustypaste msg_ok "Created Service" From 5fa8f5433af1bc33a5b7c464adc29c5434c61c85 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Wed, 24 Dec 2025 02:28:37 +0300 Subject: [PATCH 107/378] Added to cargo build --- ct/rustypaste.sh | 2 +- install/rustypaste-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 39b29e4cb..46e03cc80 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -42,7 +42,7 @@ function update_script() { cd /opt/rustypaste CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml - cargo build --locked --release + $STD cargo build --locked --release msg_ok "Updated rustypaste to latest" msg_info "Starting rustypaste" diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index 808fa08a7..188d5b47d 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -25,7 +25,7 @@ fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" " msg_info "Setting up rustypaste" cd /opt/rustypaste sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml -cargo build --locked --release +$STD cargo build --locked --release msg_ok "Set up rustypaste" msg_info "Creating Service" From 0c38df0c2d7b534b333b9437acf4484de0cd9d6f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Dec 2025 13:16:38 +0100 Subject: [PATCH 108/378] Fix gento template discovery --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 7ce994d1e..657382e94 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3728,7 +3728,7 @@ create_lxc_container() { case "$PCT_OSTYPE" in debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; alpine | fedora | rocky | rockylinux | centos | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - gentoo) TEMPLATE_PATTERN="-current_" ;; + gentoo) TEMPLATE_PATTERN="-current-openrc" ;; opensuse) TEMPLATE_PATTERN="-default_" ;; *) TEMPLATE_PATTERN="" ;; esac From c8d12a85f15f491b28433ca735252d45df5e1274 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Dec 2025 13:18:49 +0100 Subject: [PATCH 109/378] Fix gento template discovery --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 657382e94..db3432a8b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3728,7 +3728,7 @@ create_lxc_container() { case "$PCT_OSTYPE" in debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; alpine | fedora | rocky | rockylinux | centos | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - gentoo) TEMPLATE_PATTERN="-current-openrc" ;; + gentoo) TEMPLATE_PATTERN="-current-openrc_" ;; opensuse) TEMPLATE_PATTERN="-default_" ;; *) TEMPLATE_PATTERN="" ;; esac From 28483b6ec80ef981cb461d8b41465819ff8324aa Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Dec 2025 13:23:32 +0100 Subject: [PATCH 110/378] Fix gentoo version --- ct/gentoo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/gentoo.sh b/ct/gentoo.sh index fcb006b19..41f606156 100644 --- a/ct/gentoo.sh +++ b/ct/gentoo.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-gentoo}" -var_version="${var_version:-current}" +var_version="${var_version:-current-openrc}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From 92cbcd5132ce58ce57b9b304f5cec9e99288847d Mon Sep 17 00:00:00 2001 From: MickLesk Date: Wed, 24 Dec 2025 14:27:38 +0100 Subject: [PATCH 111/378] fix: LXC distro compatibility issues - Gentoo: Fix template pattern to match 'gentoo-current-openrc' naming - openSUSE: Sanitize ANSI escape codes from MSG_INFO_SHOWN array keys - Devuan: Use flexible runlevel matching for sysvinit autologin - CentOS/RHEL: Add missing '-' before $TERM in agetty commands Fixes reported issues: - Gentoo template not found (wrong pattern '-current_') - openSUSE 'not a valid identifier' error on msg_ok with color codes - Devuan autologin failing due to strict runlevel pattern - CentOS autologin improvements for LXC console --- misc/build.func | 6406 ++++++++++++++++++++++----------------------- misc/core.func | 874 ++++--- misc/install.func | 1237 ++++----- 3 files changed, 4262 insertions(+), 4255 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7ce994d1e..c1674fb49 100644 --- a/misc/build.func +++ b/misc/build.func @@ -39,46 +39,46 @@ # - Captures app-declared resource defaults (CPU, RAM, Disk) # ------------------------------------------------------------------------------ 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. - SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files - BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + 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. + SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files + BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - # Parse dev_mode early - parse_dev_mode + # Parse dev_mode early + parse_dev_mode - # Setup persistent log directory if logs mode active - if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then - mkdir -p /var/log/community-scripts - BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" - fi + # Setup persistent log directory if logs mode active + if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then + mkdir -p /var/log/community-scripts + BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" + fi - # Get Proxmox VE version and kernel version - if command -v pveversion >/dev/null 2>&1; then - PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - else - PVEVERSION="N/A" - fi - KERNEL_VERSION=$(uname -r) + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + else + 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 + # 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 } # ----------------------------------------------------------------------------- @@ -187,17 +187,17 @@ variables() { 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" + 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" + 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 # ============================================================================== @@ -215,46 +215,46 @@ fi # ------------------------------------------------------------------------------ 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 - # Silent success - only show errors if they exist + # Silent success - only show errors if they exist } # ============================================================================== @@ -270,18 +270,18 @@ maxkeys_check() { # - Returns "Unknown" if OS type cannot be determined # ------------------------------------------------------------------------------ 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" } # ------------------------------------------------------------------------------ @@ -291,16 +291,16 @@ get_current_ip() { # - Removes old IP entries to avoid duplicates # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -311,27 +311,27 @@ update_motd_ip() { # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { - [[ "$SSH" != "yes" ]] && return 0 + [[ "$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}" + 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 - fi - - # Fallback: nichts ausgewählt - msg_warn "No SSH keys to install (skipping)." - return 0 } # ------------------------------------------------------------------------------ @@ -343,55 +343,55 @@ install_ssh_keys_into_ct() { # - 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 + 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 + 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 + 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 ' + # 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 + 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))) + # 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 - fi - FOUND_HOST_KEY_COUNT="$total" - ( - IFS=: - echo "${files[*]}" - ) + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) } # ============================================================================== @@ -406,53 +406,53 @@ find_host_ssh_keys() { # - Arguments: vars_file, key (var_container_storage/var_template_storage), value # ------------------------------------------------------------------------------ _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" + # $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 + # $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") + 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 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 + 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" + _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 + # 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 - # Silent operation - no output message + # Silent operation - no output message } # ============================================================================== @@ -469,83 +469,83 @@ choose_and_set_storage_for_file() { # - Sets up container type, resources, network, SSH, features, and tags # ------------------------------------------------------------------------------ base_settings() { - # Default Settings - CT_TYPE=${var_unprivileged:-"1"} + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} - # 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}" + # 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}" + # 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 - 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}" + 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 - 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}" + 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 - 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} - 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:-""} + 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} + 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:-""} - # 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_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" - APT_CACHER="" - APT_CACHER_IP="" - else - msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" + # 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_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" + APT_CACHER="" + APT_CACHER_IP="" + else + msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" + fi fi - fi - 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}"} - ENABLE_KEYCTL=${var_keyctl:-0} - ENABLE_MKNOD=${var_mknod:-0} - MOUNT_FS=${var_mount_fs:-""} + 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}"} + ENABLE_KEYCTL=${var_keyctl:-0} + ENABLE_MKNOD=${var_mknod:-0} + MOUNT_FS=${var_mount_fs:-""} - # 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 } # ------------------------------------------------------------------------------ @@ -556,49 +556,49 @@ base_settings() { # - Only loads whitelisted var_* keys # ------------------------------------------------------------------------------ load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" - # Allowed var_* keys - local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # Allowed var_* keys + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) - # Whitelist check helper - _is_whitelisted() { - local k="$1" w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 - } + # Whitelist check helper + _is_whitelisted() { + local k="$1" w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } - 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]}" + 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 "$var_key" || continue + [[ "$var_key" != var_* ]] && continue + _is_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 + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi - # Set only if not already exported - [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" - fi - done <"$file" - msg_ok "Loaded ${file}" + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + fi + done <"$file" + msg_ok "Loaded ${file}" } # ------------------------------------------------------------------------------ @@ -611,56 +611,56 @@ load_vars_file() { # - Calls base_settings "$VERBOSE" and echo_default # ------------------------------------------------------------------------------ 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_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # 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_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone 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 - } + # 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 - 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 + # 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" - local canonical="/usr/local/community-scripts/default.vars" - # Silent creation - no msg_info output - mkdir -p /usr/local/community-scripts + # 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 - # Pick storages before writing the file (always ask unless only one) - # Create a minimal temp file to write into - : >"$canonical" + local canonical="/usr/local/community-scripts/default.vars" + # Silent creation - no msg_info output + mkdir -p /usr/local/community-scripts - # Base content (no var_ctid / var_hostname here) - cat >"$canonical" <<'EOF' + # 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. @@ -709,47 +709,47 @@ var_verbose=no # 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 + # 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" - # Silent creation - no output message - } + chmod 0644 "$canonical" + # Silent creation - no output message + } - # 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 - } + # 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 + } - # 1) Ensure file exists - _ensure_default_vars + # 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" + # 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 + # 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 User Defaults (default.vars) on node $PVEHOST_NAME${CL}" - echo_default + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using User Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default } # ------------------------------------------------------------------------------ @@ -760,8 +760,8 @@ EOF # ------------------------------------------------------------------------------ get_app_defaults_path() { - local n="${NSAPP:-${APP,,}}" - echo "/usr/local/community-scripts/defaults/${n}.vars" + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" } # ------------------------------------------------------------------------------ @@ -774,32 +774,32 @@ 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_disk var_fuse var_gpu - 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 - ) + # 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_disk var_fuse var_gpu + 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 + ) fi # Global whitelist check function (used by _load_vars_file_to_map and others) _is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 + 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" + # 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 @@ -807,190 +807,190 @@ _sanitize_value() { # 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" + 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 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 out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" - local found_change=0 + 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 + # 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 - 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" + 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)" + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" - # NET/GW - _net="${NET:-}" - _gate="" - case "${GATE:-}" in - ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; - esac + # 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 + # 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 + # 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 + # 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}" - _nesting="${ENABLE_NESTING:-1}" - _keyctl="${ENABLE_KEYCTL:-0}" - _mknod="${ENABLE_MKNOD:-0}" - _mount_fs="${ALLOW_MOUNT_FS:-}" - _protect="${PROTECT_CT:-no}" - _timezone="${CT_TIMEZONE:-}" - _tags="${TAGS:-}" - _verbose="${VERBOSE:-no}" + # 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}" + _nesting="${ENABLE_NESTING:-1}" + _keyctl="${ENABLE_KEYCTL:-0}" + _mknod="${ENABLE_MKNOD:-0}" + _mount_fs="${ALLOW_MOUNT_FS:-}" + _protect="${PROTECT_CT:-no}" + _timezone="${CT_TIMEZONE:-}" + _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}" + # 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:-}}" + # 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 "# 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")" + 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 "${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")" - # var_ipv6_static removed - static IPs are unique, can't be default + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + # 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")" + [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" - [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" - [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" - [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" - [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" - [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" - [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" + [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" + [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" + [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" + [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" + [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" + [ -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")" - } >"$tmpf" + [ -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" + echo "$tmpf" } # ------------------------------------------------------------------------------ @@ -1001,103 +1001,103 @@ _build_current_app_vars_tmp() { # - 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)" + 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")" + # 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}" + # 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." + break + ;; + esac + done + 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." - break - ;; - esac - done - - rm -f "$new_tmp" "$diff_tmp" } ensure_storage_selection_for_vars_file() { - local 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-) + # 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 + 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 + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container - # Silent operation - no output message + # Silent operation - no output message } 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" + 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" } # ============================================================================== @@ -1113,785 +1113,785 @@ ensure_global_default_vars_file() { # - Allows user to customize all container settings # ------------------------------------------------------------------------------ advanced_settings() { - # Enter alternate screen buffer to prevent flicker between dialogs - tput smcup 2>/dev/null || true - trap 'tput rmcup 2>/dev/null || true' RETURN + # Enter alternate screen buffer to prevent flicker between dialogs + tput smcup 2>/dev/null || true + trap 'tput rmcup 2>/dev/null || true' RETURN - # Initialize defaults - TAGS="community-script;${var_tags:-}" - local STEP=1 - local MAX_STEP=28 + # Initialize defaults + TAGS="community-script;${var_tags:-}" + local STEP=1 + local MAX_STEP=28 - # Store values for back navigation - inherit from var_* app defaults - local _ct_type="${var_unprivileged:-1}" - local _pw="" - local _pw_display="Automatic Login" - local _ct_id="$NEXTID" - local _hostname="$NSAPP" - local _disk_size="${var_disk:-4}" - local _core_count="${var_cpu:-1}" - local _ram_size="${var_ram:-1024}" - local _bridge="${var_brg:-vmbr0}" - local _net="${var_net:-dhcp}" - local _gate="${var_gateway:-}" - local _ipv6_method="${var_ipv6_method:-auto}" - local _ipv6_addr="" - local _ipv6_gate="" - local _apt_cacher="${var_apt_cacher:-no}" - local _apt_cacher_ip="${var_apt_cacher_ip:-}" - local _mtu="${var_mtu:-}" - local _sd="${var_searchdomain:-}" - local _ns="${var_ns:-}" - local _mac="${var_mac:-}" - local _vlan="${var_vlan:-}" - local _tags="$TAGS" - local _enable_fuse="${var_fuse:-no}" - local _enable_tun="${var_tun:-no}" - local _enable_gpu="${var_gpu:-no}" - local _enable_nesting="${var_nesting:-1}" - local _verbose="${var_verbose:-no}" - local _enable_keyctl="${var_keyctl:-0}" - local _enable_mknod="${var_mknod:-0}" - local _mount_fs="${var_mount_fs:-}" - local _protect_ct="${var_protection:-no}" + # Store values for back navigation - inherit from var_* app defaults + local _ct_type="${var_unprivileged:-1}" + local _pw="" + local _pw_display="Automatic Login" + local _ct_id="$NEXTID" + local _hostname="$NSAPP" + local _disk_size="${var_disk:-4}" + local _core_count="${var_cpu:-1}" + local _ram_size="${var_ram:-1024}" + local _bridge="${var_brg:-vmbr0}" + local _net="${var_net:-dhcp}" + local _gate="${var_gateway:-}" + local _ipv6_method="${var_ipv6_method:-auto}" + local _ipv6_addr="" + local _ipv6_gate="" + local _apt_cacher="${var_apt_cacher:-no}" + local _apt_cacher_ip="${var_apt_cacher_ip:-}" + local _mtu="${var_mtu:-}" + local _sd="${var_searchdomain:-}" + local _ns="${var_ns:-}" + local _mac="${var_mac:-}" + local _vlan="${var_vlan:-}" + local _tags="$TAGS" + local _enable_fuse="${var_fuse:-no}" + local _enable_tun="${var_tun:-no}" + local _enable_gpu="${var_gpu:-no}" + local _enable_nesting="${var_nesting:-1}" + local _verbose="${var_verbose:-no}" + local _enable_keyctl="${var_keyctl:-0}" + local _enable_mknod="${var_mknod:-0}" + local _mount_fs="${var_mount_fs:-}" + local _protect_ct="${var_protection:-no}" - # Detect host timezone for default (if not set via var_timezone) - local _host_timezone="" - if command -v timedatectl >/dev/null 2>&1; then - _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") - elif [ -f /etc/timezone ]; then - _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") - fi - local _ct_timezone="${var_timezone:-$_host_timezone}" - - # Helper to show current progress - show_progress() { - local current=$1 - local total=$MAX_STEP - echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" - } - - # Detect available bridges (do this once) - local BRIDGES="" - local BRIDGE_MENU_OPTIONS=() - _detect_bridges() { - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) - BRIDGES="" - local OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true - if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - local start=$(echo "${pair}" | cut -d':' -f1) - local end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - local 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) - - # Build bridge menu - BRIDGE_MENU_OPTIONS=() - if [[ -n "$BRIDGES" ]]; then - while IFS= read -r bridge; do - if [[ -n "$bridge" ]]; then - local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') - BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") - fi - done <<<"$BRIDGES" + # Detect host timezone for default (if not set via var_timezone) + local _host_timezone="" + if command -v timedatectl >/dev/null 2>&1; then + _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") + elif [ -f /etc/timezone ]; then + _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") fi - } - _detect_bridges + local _ct_timezone="${var_timezone:-$_host_timezone}" - # Main wizard loop - while [ $STEP -le $MAX_STEP ]; do - case $STEP in + # Helper to show current progress + show_progress() { + local current=$1 + local total=$MAX_STEP + echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" + } - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 1: Container Type - # ═══════════════════════════════════════════════════════════════════════════ - 1) - local default_on="ON" - local default_off="OFF" - [[ "$_ct_type" == "0" ]] && { - default_on="OFF" - default_off="ON" - } - - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TYPE" \ - --ok-button "Next" --cancel-button "Exit" \ - --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ - "1" "Unprivileged (recommended)" $default_on \ - "0" "Privileged" $default_off \ - 3>&1 1>&2 2>&3); then - [[ -n "$result" ]] && _ct_type="$result" - ((STEP++)) - else - exit_script - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 2: Root Password - # ═══════════════════════════════════════════════════════════════════════════ - 2) - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "ROOT PASSWORD" \ - --ok-button "Next" --cancel-button "Back" \ - --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ - 3>&1 1>&2 2>&3); then - - if [[ -z "$PW1" ]]; then - _pw="" - _pw_display="Automatic Login" - ((STEP++)) - elif [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - elif ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 - else - # Verify password - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "PASSWORD VERIFICATION" \ - --ok-button "Confirm" --cancel-button "Back" \ - --passwordbox "\nVerify Root Password" 10 58 \ - 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - _pw="-password $PW1" - _pw_display="********" - ((STEP++)) - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 + # Detect available bridges (do this once) + local BRIDGES="" + local BRIDGE_MENU_OPTIONS=() + _detect_bridges() { + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) + BRIDGES="" + local OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true + if [ -f "${iface_indexes_tmpfile}" ]; then + while read -r pair; do + local start=$(echo "${pair}" | cut -d':' -f1) + local end=$(echo "${pair}" | cut -d':' -f2) + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + local 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 - else - ((STEP--)) - fi - fi - else - ((STEP--)) - fi - ;; + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 3: Container ID - # ═══════════════════════════════════════════════════════════════════════════ - 3) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER ID" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ - 3>&1 1>&2 2>&3); then - _ct_id="${result:-$NEXTID}" - ((STEP++)) - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 4: Hostname - # ═══════════════════════════════════════════════════════════════════════════ - 4) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "HOSTNAME" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ - 3>&1 1>&2 2>&3); then - local hn_test="${result:-$NSAPP}" - hn_test=$(echo "${hn_test,,}" | tr -d ' ') - if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - _hostname="$hn_test" - ((STEP++)) - else - whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 5: Disk Size - # ═══════════════════════════════════════════════════════════════════════════ - 5) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DISK SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ - 3>&1 1>&2 2>&3); then - local disk_test="${result:-$var_disk}" - if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then - _disk_size="$disk_test" - ((STEP++)) - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 6: CPU Cores - # ═══════════════════════════════════════════════════════════════════════════ - 6) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CPU CORES" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ - 3>&1 1>&2 2>&3); then - local cpu_test="${result:-$var_cpu}" - if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then - _core_count="$cpu_test" - ((STEP++)) - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 7: RAM Size - # ═══════════════════════════════════════════════════════════════════════════ - 7) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "RAM SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ - 3>&1 1>&2 2>&3); then - local ram_test="${result:-$var_ram}" - if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then - _ram_size="$ram_test" - ((STEP++)) - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 8: Network Bridge - # ═══════════════════════════════════════════════════════════════════════════ - 8) - if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then - _bridge="vmbr0" - ((STEP++)) - else - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NETWORK BRIDGE" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect network bridge:" 16 58 6 \ - "${BRIDGE_MENU_OPTIONS[@]}" \ - 3>&1 1>&2 2>&3); then - _bridge="${result:-vmbr0}" - ((STEP++)) - else - ((STEP--)) - fi - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 9: IPv4 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 9) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv4 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3); then - - if [[ "$result" == "static" ]]; then - # Get static IP - local static_ip - if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "STATIC IPv4 ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - # Get gateway - local gateway_ip - if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GATEWAY IP" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Gateway IP address" 10 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - _net="$static_ip" - _gate=",gw=$gateway_ip" - ((STEP++)) - else - whiptail --msgbox "Invalid Gateway IP format." 8 58 + # Build bridge menu + BRIDGE_MENU_OPTIONS=() + if [[ -n "$BRIDGES" ]]; then + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') + BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") fi - fi + done <<<"$BRIDGES" + fi + } + _detect_bridges + + # Main wizard loop + while [ $STEP -le $MAX_STEP ]; do + case $STEP in + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 1: Container Type + # ═══════════════════════════════════════════════════════════════════════════ + 1) + local default_on="ON" + local default_off="OFF" + [[ "$_ct_type" == "0" ]] && { + default_on="OFF" + default_off="ON" + } + + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TYPE" \ + --ok-button "Next" --cancel-button "Exit" \ + --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ + "1" "Unprivileged (recommended)" $default_on \ + "0" "Privileged" $default_off \ + 3>&1 1>&2 2>&3); then + [[ -n "$result" ]] && _ct_type="$result" + ((STEP++)) else - whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 + exit_script fi - fi - else - _net="dhcp" - _gate="" - ((STEP++)) - fi - else - ((STEP--)) - fi - ;; + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 10: IPv6 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 10) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv6 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv6 Address Management:" 16 70 5 \ - "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ - "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ - "static" "Static - Manual IPv6 address configuration" \ - "none" "None - No IPv6 assignment (most containers)" \ - "disable" "Fully Disabled - (breaks some services)" \ - 3>&1 1>&2 2>&3); then + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 2: Root Password + # ═══════════════════════════════════════════════════════════════════════════ + 2) + if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "ROOT PASSWORD" \ + --ok-button "Next" --cancel-button "Back" \ + --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ + 3>&1 1>&2 2>&3); then - _ipv6_method="$result" - case "$result" in - static) - local ipv6_addr - if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "STATIC IPv6 ADDRESS" \ - --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - _ipv6_addr="$ipv6_addr" - # Optional gateway - _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "IPv6 GATEWAY" \ - --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ - 3>&1 1>&2 2>&3) || true - ((STEP++)) + if [[ -z "$PW1" ]]; then + _pw="" + _pw_display="Automatic Login" + ((STEP++)) + elif [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + elif ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + else + # Verify password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "PASSWORD VERIFICATION" \ + --ok-button "Confirm" --cancel-button "Back" \ + --passwordbox "\nVerify Root Password" 10 58 \ + 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + _pw="-password $PW1" + _pw_display="********" + ((STEP++)) + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + ((STEP--)) + fi + fi else - whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 + ((STEP--)) fi - fi - ;; - dhcp) - _ipv6_addr="dhcp" - _ipv6_gate="" - ((STEP++)) - ;; - disable) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - none) - _ipv6_addr="none" - _ipv6_gate="" - ((STEP++)) - ;; - *) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - esac - else - ((STEP--)) - fi - ;; + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 11: MTU Size - # ═══════════════════════════════════════════════════════════════════════════ - 11) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MTU SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mtu="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 3: Container ID + # ═══════════════════════════════════════════════════════════════════════════ + 3) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER ID" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ + 3>&1 1>&2 2>&3); then + _ct_id="${result:-$NEXTID}" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 12: DNS Search Domain - # ═══════════════════════════════════════════════════════════════════════════ - 12) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SEARCH DOMAIN" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _sd="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 4: Hostname + # ═══════════════════════════════════════════════════════════════════════════ + 4) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "HOSTNAME" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ + 3>&1 1>&2 2>&3); then + local hn_test="${result:-$NSAPP}" + hn_test=$(echo "${hn_test,,}" | tr -d ' ') + if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + _hostname="$hn_test" + ((STEP++)) + else + whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 13: DNS Server - # ═══════════════════════════════════════════════════════════════════════════ - 13) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SERVER" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _ns="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 5: Disk Size + # ═══════════════════════════════════════════════════════════════════════════ + 5) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DISK SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ + 3>&1 1>&2 2>&3); then + local disk_test="${result:-$var_disk}" + if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then + _disk_size="$disk_test" + ((STEP++)) + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 14: MAC Address - # ═══════════════════════════════════════════════════════════════════════════ - 14) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MAC ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mac="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 6: CPU Cores + # ═══════════════════════════════════════════════════════════════════════════ + 6) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CPU CORES" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ + 3>&1 1>&2 2>&3); then + local cpu_test="${result:-$var_cpu}" + if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then + _core_count="$cpu_test" + ((STEP++)) + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 15: VLAN Tag - # ═══════════════════════════════════════════════════════════════════════════ - 15) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VLAN TAG" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _vlan="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 7: RAM Size + # ═══════════════════════════════════════════════════════════════════════════ + 7) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "RAM SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ + 3>&1 1>&2 2>&3); then + local ram_test="${result:-$var_ram}" + if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then + _ram_size="$ram_test" + ((STEP++)) + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 16: Tags - # ═══════════════════════════════════════════════════════════════════════════ - 16) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TAGS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ - 3>&1 1>&2 2>&3); then - _tags="${result:-;}" - _tags=$(echo "$_tags" | tr -d '[:space:]') - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 8: Network Bridge + # ═══════════════════════════════════════════════════════════════════════════ + 8) + if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then + _bridge="vmbr0" + ((STEP++)) + else + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NETWORK BRIDGE" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect network bridge:" 16 58 6 \ + "${BRIDGE_MENU_OPTIONS[@]}" \ + 3>&1 1>&2 2>&3); then + _bridge="${result:-vmbr0}" + ((STEP++)) + else + ((STEP--)) + fi + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 17: SSH Settings - # ═══════════════════════════════════════════════════════════════════════════ - 17) - configure_ssh_settings "Step $STEP/$MAX_STEP" - # configure_ssh_settings handles its own flow, always advance - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 9: IPv4 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 9) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv4 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3); then - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 18: FUSE Support - # ═══════════════════════════════════════════════════════════════════════════ - 18) - local fuse_default_flag="--defaultno" - [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" + if [[ "$result" == "static" ]]; then + # Get static IP + local static_ip + if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "STATIC IPv4 ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + # Get gateway + local gateway_ip + if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GATEWAY IP" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nEnter Gateway IP address" 10 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + _net="$static_ip" + _gate=",gw=$gateway_ip" + ((STEP++)) + else + whiptail --msgbox "Invalid Gateway IP format." 8 58 + fi + fi + else + whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 + fi + fi + else + _net="dhcp" + _gate="" + ((STEP++)) + fi + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "FUSE SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $fuse_default_flag \ - --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then - _enable_fuse="yes" - else - if [ $? -eq 1 ]; then - _enable_fuse="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 10: IPv6 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 10) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv6 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv6 Address Management:" 16 70 5 \ + "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ + "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ + "static" "Static - Manual IPv6 address configuration" \ + "none" "None - No IPv6 assignment (most containers)" \ + "disable" "Fully Disabled - (breaks some services)" \ + 3>&1 1>&2 2>&3); then - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 19: TUN/TAP Support - # ═══════════════════════════════════════════════════════════════════════════ - 19) - local tun_default_flag="--defaultno" - [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" + _ipv6_method="$result" + case "$result" in + static) + local ipv6_addr + if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "STATIC IPv6 ADDRESS" \ + --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + _ipv6_addr="$ipv6_addr" + # Optional gateway + _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "IPv6 GATEWAY" \ + --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ + 3>&1 1>&2 2>&3) || true + ((STEP++)) + else + whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 + fi + fi + ;; + dhcp) + _ipv6_addr="dhcp" + _ipv6_gate="" + ((STEP++)) + ;; + disable) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + none) + _ipv6_addr="none" + _ipv6_gate="" + ((STEP++)) + ;; + *) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + esac + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "TUN/TAP SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $tun_default_flag \ - --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then - _enable_tun="yes" - else - if [ $? -eq 1 ]; then - _enable_tun="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 11: MTU Size + # ═══════════════════════════════════════════════════════════════════════════ + 11) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MTU SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mtu="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 20: Nesting Support - # ═══════════════════════════════════════════════════════════════════════════ - 20) - local nesting_default_flag="" - [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 12: DNS Search Domain + # ═══════════════════════════════════════════════════════════════════════════ + 12) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SEARCH DOMAIN" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _sd="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NESTING SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $nesting_default_flag \ - --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then - _enable_nesting="1" - else - if [ $? -eq 1 ]; then - _enable_nesting="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 13: DNS Server + # ═══════════════════════════════════════════════════════════════════════════ + 13) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SERVER" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _ns="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 21: GPU Passthrough - # ═══════════════════════════════════════════════════════════════════════════ - 21) - local gpu_default_flag="--defaultno" - [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 14: MAC Address + # ═══════════════════════════════════════════════════════════════════════════ + 14) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MAC ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mac="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GPU PASSTHROUGH" \ - --ok-button "Next" --cancel-button "Back" \ - $gpu_default_flag \ - --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then - _enable_gpu="yes" - else - if [ $? -eq 1 ]; then - _enable_gpu="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 15: VLAN Tag + # ═══════════════════════════════════════════════════════════════════════════ + 15) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VLAN TAG" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _vlan="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 22: Keyctl Support (Docker/systemd) - # ═══════════════════════════════════════════════════════════════════════════ - 22) - local keyctl_default_flag="--defaultno" - [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 16: Tags + # ═══════════════════════════════════════════════════════════════════════════ + 16) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TAGS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ + 3>&1 1>&2 2>&3); then + _tags="${result:-;}" + _tags=$(echo "$_tags" | tr -d '[:space:]') + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "KEYCTL SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $keyctl_default_flag \ - --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then - _enable_keyctl="1" - else - if [ $? -eq 1 ]; then - _enable_keyctl="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 17: SSH Settings + # ═══════════════════════════════════════════════════════════════════════════ + 17) + configure_ssh_settings "Step $STEP/$MAX_STEP" + # configure_ssh_settings handles its own flow, always advance + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 23: APT Cacher Proxy - # ═══════════════════════════════════════════════════════════════════════════ - 23) - local apt_cacher_default_flag="--defaultno" - [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 18: FUSE Support + # ═══════════════════════════════════════════════════════════════════════════ + 18) + local fuse_default_flag="--defaultno" + [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER PROXY" \ - --ok-button "Next" --cancel-button "Back" \ - $apt_cacher_default_flag \ - --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then - _apt_cacher="yes" - # Ask for IP if enabled - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER IP" \ - --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ - 3>&1 1>&2 2>&3); then - _apt_cacher_ip="$result" - fi - else - if [ $? -eq 1 ]; then - _apt_cacher="no" - _apt_cacher_ip="" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "FUSE SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $fuse_default_flag \ + --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then + _enable_fuse="yes" + else + if [ $? -eq 1 ]; then + _enable_fuse="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 24: Container Timezone - # ═══════════════════════════════════════════════════════════════════════════ - 24) - local tz_hint="$_ct_timezone" - [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 19: TUN/TAP Support + # ═══════════════════════════════════════════════════════════════════════════ + 19) + local tun_default_flag="--defaultno" + [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TIMEZONE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ - 3>&1 1>&2 2>&3); then - _ct_timezone="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "TUN/TAP SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $tun_default_flag \ + --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then + _enable_tun="yes" + else + if [ $? -eq 1 ]; then + _enable_tun="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 25: Container Protection - # ═══════════════════════════════════════════════════════════════════════════ - 25) - local protect_default_flag="--defaultno" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 20: Nesting Support + # ═══════════════════════════════════════════════════════════════════════════ + 20) + local nesting_default_flag="" + [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER PROTECTION" \ - --ok-button "Next" --cancel-button "Back" \ - $protect_default_flag \ - --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then - _protect_ct="yes" - else - if [ $? -eq 1 ]; then - _protect_ct="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NESTING SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $nesting_default_flag \ + --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then + _enable_nesting="1" + else + if [ $? -eq 1 ]; then + _enable_nesting="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 26: Device Node Creation (mknod) - # ═══════════════════════════════════════════════════════════════════════════ - 26) - local mknod_default_flag="--defaultno" - [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 21: GPU Passthrough + # ═══════════════════════════════════════════════════════════════════════════ + 21) + local gpu_default_flag="--defaultno" + [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DEVICE NODE CREATION" \ - --ok-button "Next" --cancel-button "Back" \ - $mknod_default_flag \ - --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then - _enable_mknod="1" - else - if [ $? -eq 1 ]; then - _enable_mknod="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GPU PASSTHROUGH" \ + --ok-button "Next" --cancel-button "Back" \ + $gpu_default_flag \ + --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then + _enable_gpu="yes" + else + if [ $? -eq 1 ]; then + _enable_gpu="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 27: Mount Filesystems - # ═══════════════════════════════════════════════════════════════════════════ - 27) - local mount_hint="" - [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 22: Keyctl Support (Docker/systemd) + # ═══════════════════════════════════════════════════════════════════════════ + 22) + local keyctl_default_flag="--defaultno" + [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MOUNT FILESYSTEMS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ - 3>&1 1>&2 2>&3); then - _mount_fs="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "KEYCTL SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $keyctl_default_flag \ + --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then + _enable_keyctl="1" + else + if [ $? -eq 1 ]; then + _enable_keyctl="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 28: Verbose Mode & Confirmation - # ═══════════════════════════════════════════════════════════════════════════ - 28) - local verbose_default_flag="--defaultno" - [[ "$_verbose" == "yes" ]] && verbose_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 23: APT Cacher Proxy + # ═══════════════════════════════════════════════════════════════════════════ + 23) + local apt_cacher_default_flag="--defaultno" + [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VERBOSE MODE" \ - $verbose_default_flag \ - --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then - _verbose="yes" - else - _verbose="no" - fi - # Build summary - local ct_type_desc="Unprivileged" - [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER PROXY" \ + --ok-button "Next" --cancel-button "Back" \ + $apt_cacher_default_flag \ + --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then + _apt_cacher="yes" + # Ask for IP if enabled + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER IP" \ + --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ + 3>&1 1>&2 2>&3); then + _apt_cacher_ip="$result" + fi + else + if [ $? -eq 1 ]; then + _apt_cacher="no" + _apt_cacher_ip="" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - local nesting_desc="Disabled" - [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 24: Container Timezone + # ═══════════════════════════════════════════════════════════════════════════ + 24) + local tz_hint="$_ct_timezone" + [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" - local keyctl_desc="Disabled" - [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TIMEZONE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ + 3>&1 1>&2 2>&3); then + _ct_timezone="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - local protect_desc="No" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 25: Container Protection + # ═══════════════════════════════════════════════════════════════════════════ + 25) + local protect_default_flag="--defaultno" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" - local tz_display="${_ct_timezone:-Host TZ}" - local apt_display="${_apt_cacher:-no}" - [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER PROTECTION" \ + --ok-button "Next" --cancel-button "Back" \ + $protect_default_flag \ + --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then + _protect_ct="yes" + else + if [ $? -eq 1 ]; then + _protect_ct="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - local summary="Container Type: $ct_type_desc + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 26: Device Node Creation (mknod) + # ═══════════════════════════════════════════════════════════════════════════ + 26) + local mknod_default_flag="--defaultno" + [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" + + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DEVICE NODE CREATION" \ + --ok-button "Next" --cancel-button "Back" \ + $mknod_default_flag \ + --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then + _enable_mknod="1" + else + if [ $? -eq 1 ]; then + _enable_mknod="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 27: Mount Filesystems + # ═══════════════════════════════════════════════════════════════════════════ + 27) + local mount_hint="" + [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" + + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MOUNT FILESYSTEMS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ + 3>&1 1>&2 2>&3); then + _mount_fs="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 28: Verbose Mode & Confirmation + # ═══════════════════════════════════════════════════════════════════════════ + 28) + local verbose_default_flag="--defaultno" + [[ "$_verbose" == "yes" ]] && verbose_default_flag="" + + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VERBOSE MODE" \ + $verbose_default_flag \ + --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then + _verbose="yes" + else + _verbose="no" + fi + # Build summary + local ct_type_desc="Unprivileged" + [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" + + local nesting_desc="Disabled" + [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" + + local keyctl_desc="Disabled" + [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" + + local protect_desc="No" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" + + local tz_display="${_ct_timezone:-Host TZ}" + local apt_display="${_apt_cacher:-no}" + [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" + + local summary="Container Type: $ct_type_desc Container ID: $_ct_id Hostname: $_hostname @@ -1915,105 +1915,105 @@ Advanced: APT Cacher: $apt_display Verbose: $_verbose" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONFIRM SETTINGS" \ - --ok-button "Create LXC" --cancel-button "Back" \ - --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then - ((STEP++)) - else - ((STEP--)) - fi - ;; - esac - done + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONFIRM SETTINGS" \ + --ok-button "Create LXC" --cancel-button "Back" \ + --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then + ((STEP++)) + else + ((STEP--)) + fi + ;; + esac + done - # ═══════════════════════════════════════════════════════════════════════════ - # Apply all collected values to global variables - # ═══════════════════════════════════════════════════════════════════════════ - CT_TYPE="$_ct_type" - PW="$_pw" - CT_ID="$_ct_id" - HN="$_hostname" - DISK_SIZE="$_disk_size" - CORE_COUNT="$_core_count" - RAM_SIZE="$_ram_size" - BRG="$_bridge" - NET="$_net" - GATE="$_gate" - IPV6_METHOD="$_ipv6_method" - IPV6_ADDR="$_ipv6_addr" - IPV6_GATE="$_ipv6_gate" - TAGS="$_tags" - ENABLE_FUSE="$_enable_fuse" - ENABLE_TUN="$_enable_tun" - ENABLE_GPU="$_enable_gpu" - ENABLE_NESTING="$_enable_nesting" - ENABLE_KEYCTL="$_enable_keyctl" - ENABLE_MKNOD="$_enable_mknod" - ALLOW_MOUNT_FS="$_mount_fs" - PROTECT_CT="$_protect_ct" - CT_TIMEZONE="$_ct_timezone" - APT_CACHER="$_apt_cacher" - APT_CACHER_IP="$_apt_cacher_ip" - VERBOSE="$_verbose" + # ═══════════════════════════════════════════════════════════════════════════ + # Apply all collected values to global variables + # ═══════════════════════════════════════════════════════════════════════════ + CT_TYPE="$_ct_type" + PW="$_pw" + CT_ID="$_ct_id" + HN="$_hostname" + DISK_SIZE="$_disk_size" + CORE_COUNT="$_core_count" + RAM_SIZE="$_ram_size" + BRG="$_bridge" + NET="$_net" + GATE="$_gate" + IPV6_METHOD="$_ipv6_method" + IPV6_ADDR="$_ipv6_addr" + IPV6_GATE="$_ipv6_gate" + TAGS="$_tags" + ENABLE_FUSE="$_enable_fuse" + ENABLE_TUN="$_enable_tun" + ENABLE_GPU="$_enable_gpu" + ENABLE_NESTING="$_enable_nesting" + ENABLE_KEYCTL="$_enable_keyctl" + ENABLE_MKNOD="$_enable_mknod" + ALLOW_MOUNT_FS="$_mount_fs" + PROTECT_CT="$_protect_ct" + CT_TIMEZONE="$_ct_timezone" + APT_CACHER="$_apt_cacher" + APT_CACHER_IP="$_apt_cacher_ip" + VERBOSE="$_verbose" - # Update var_* based on user choice (for functions that check these) - var_gpu="$_enable_gpu" - var_fuse="$_enable_fuse" - var_tun="$_enable_tun" - var_nesting="$_enable_nesting" - var_keyctl="$_enable_keyctl" - var_mknod="$_enable_mknod" - var_mount_fs="$_mount_fs" - var_protection="$_protect_ct" - var_timezone="$_ct_timezone" - var_apt_cacher="$_apt_cacher" - var_apt_cacher_ip="$_apt_cacher_ip" + # Update var_* based on user choice (for functions that check these) + var_gpu="$_enable_gpu" + var_fuse="$_enable_fuse" + var_tun="$_enable_tun" + var_nesting="$_enable_nesting" + var_keyctl="$_enable_keyctl" + var_mknod="$_enable_mknod" + var_mount_fs="$_mount_fs" + var_protection="$_protect_ct" + var_timezone="$_ct_timezone" + var_apt_cacher="$_apt_cacher" + var_apt_cacher_ip="$_apt_cacher_ip" - # Format optional values - [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" - [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" - [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" - [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" - [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" + # Format optional values + [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" + [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" + [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" + [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" + [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" - # Alpine UDHCPC fix - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - export SSH_KEYS_FILE + # Alpine UDHCPC fix + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + export SSH_KEYS_FILE - # Exit alternate screen buffer BEFORE displaying summary - # so the summary is visible in the main terminal - tput rmcup 2>/dev/null || true - trap - RETURN # Remove the trap since we already called rmcup + # Exit alternate screen buffer BEFORE displaying summary + # so the summary is visible in the main terminal + tput rmcup 2>/dev/null || true + trap - RETURN # Remove the trap since we already called rmcup - # Display final summary - echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" - echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" - [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" - echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" - [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" - [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" - [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + # Display final summary + echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" + echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" + [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" + echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" + [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" + [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" + [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" } # ============================================================================== @@ -2031,13 +2031,13 @@ Advanced: # - Sets global DIAGNOSTICS variable for API telemetry opt-in/out # ------------------------------------------------------------------------------ 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. @@ -2060,9 +2060,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. @@ -2085,34 +2085,34 @@ 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 } 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 + 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 - 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 } # ------------------------------------------------------------------------------ @@ -2123,25 +2123,25 @@ diagnostics_menu() { # - 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 [ "${var_gpu:-no}" == "yes" ]; then - echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" - fi - 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 " " + 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 [ "${var_gpu:-no}" == "yes" ]; then + echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" + fi + 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 " " } # ------------------------------------------------------------------------------ @@ -2153,196 +2153,196 @@ echo_default() { # - Applies chosen settings and triggers container build # ------------------------------------------------------------------------------ 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) - - # Get timezone using timedatectl (Debian 13+ compatible) - # Fallback to /etc/timezone for older systems - if command -v timedatectl >/dev/null 2>&1; then - timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") - elif [ -f /etc/timezone ]; then - timezone=$(cat /etc/timezone) - else - timezone="UTC" - fi - - # 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" "User 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 - - APPDEFAULTS_OPTION="$appdefaults_option" - SETTINGS_OPTION="$settings_option" - - # Main menu loop - allows returning from Settings - while true; do - 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" + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service fi - # --- 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)" - break - ;; - 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" - break - ;; - 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - defaults_target="/usr/local/community-scripts/default.vars" - break - ;; - "$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)" - break - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - "$SETTINGS_OPTION" | settings | SETTINGS) - settings_menu - # After settings menu, show main menu again - header_info - CHOICE="" - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac - done + NEXTID=$(pvesh get /cluster/nextid) - if [[ -n "$defaults_target" ]]; then - ensure_storage_selection_for_vars_file "$defaults_target" - fi + # Get timezone using timedatectl (Debian 13+ compatible) + # Fallback to /etc/timezone for older systems + if command -v timedatectl >/dev/null 2>&1; then + timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") + elif [ -f /etc/timezone ]; then + timezone=$(cat /etc/timezone) + else + timezone="UTC" + fi - if [[ "$run_maybe_offer" == "yes" ]]; then - maybe_offer_save_app_defaults - fi + # 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" "User 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 + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # Main menu loop - allows returning from Settings + while true; do + 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 + + # --- 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)" + break + ;; + 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" + break + ;; + 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + break + ;; + "$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)" + break + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + # After settings menu, show main menu again + header_info + CHOICE="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + done + + 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" + local vf="/usr/local/community-scripts/default.vars" - # Ensure file exists - if [[ ! -f "$vf" ]]; then - mkdir -p "$(dirname "$vf")" - touch "$vf" - fi + # 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" + # 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" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("3" "Edit App.vars for ${APP}") - settings_items+=("4" "Back to Main Menu") - else - settings_items+=("3" "Back to Main Menu") - fi + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("3" "Edit App.vars for ${APP}") + settings_items+=("4" "Back to Main Menu") + else + settings_items+=("3" "Back to Main Menu") + fi - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || exit_script + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || exit_script - case "$choice" in - 1) diagnostics_menu ;; - 2) nano /usr/local/community-scripts/default.vars ;; - 3) - if [ -f "$(get_app_defaults_path)" ]; then - nano "$(get_app_defaults_path)" - else - # Back was selected (no app.vars available) - return - fi - ;; - 4) - # Back to main menu - return - ;; - esac - done + case "$choice" in + 1) diagnostics_menu ;; + 2) nano /usr/local/community-scripts/default.vars ;; + 3) + if [ -f "$(get_app_defaults_path)" ]; then + nano "$(get_app_defaults_path)" + else + # Back was selected (no app.vars available) + return + fi + ;; + 4) + # Back to main menu + return + ;; + esac + done } # ------------------------------------------------------------------------------ @@ -2352,21 +2352,21 @@ settings_menu() { # - 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) + 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 + 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 - else - echo -e "" - fi } # ------------------------------------------------------------------------------ @@ -2376,18 +2376,18 @@ check_container_resources() { # - 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_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 - fi } # ------------------------------------------------------------------------------ @@ -2397,9 +2397,9 @@ check_container_storage() { # - 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 ' + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' /^[[:space:]]*#/ {next} /^[[:space:]]*$/ {next} # nackt: typ base64 [comment] @@ -2419,45 +2419,45 @@ ssh_extract_keys_from_file() { # - 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 + 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 + 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 + # 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}..." + 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 + 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 } # ------------------------------------------------------------------------------ @@ -2467,105 +2467,105 @@ ssh_build_choices_from_files() { # - 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[@]}" + 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() { - local step_info="${1:-}" - local backtitle="[dev] Proxmox VE Helper Scripts" - [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" + local step_info="${1:-}" + local backtitle="[dev] Proxmox VE Helper Scripts" + [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" + 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" + 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 "$backtitle" --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 "$backtitle" --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 + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "$backtitle" --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 "$backtitle" --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 "$backtitle" --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 "$backtitle" \ - --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 "$backtitle" \ - --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 "$backtitle" --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 + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "$backtitle" --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 - else - whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "$backtitle" \ + --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 "$backtitle" \ + --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 "$backtitle" --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 "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 + fi fi - else - whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 - fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" fi - ;; - none) - : - ;; - esac - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" - fi - - # Always show SSH access dialog - user should be able to enable SSH even without keys - if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi + # Always show SSH access dialog - user should be able to enable SSH even without keys + if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi } # ------------------------------------------------------------------------------ @@ -2577,39 +2577,39 @@ configure_ssh_settings() { # - 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) + 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 + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi } # ============================================================================== @@ -2630,256 +2630,256 @@ start() { # - Posts installation telemetry to API if diagnostics enabled # ------------------------------------------------------------------------------ build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" - # MAC - if [[ -n "$MAC" ]]; then - case "$MAC" in - ,hwaddr=*) NET_STRING+="$MAC" ;; - *) NET_STRING+=",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}" + + # 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 - fi - # IP (immer zwingend, Standard dhcp) - NET_STRING+=",ip=${NET:-dhcp}" + # Build FEATURES string based on container type and user choices + FEATURES="" - # Gateway - if [[ -n "$GATE" ]]; then - case "$GATE" in - ,gw=*) NET_STRING+="$GATE" ;; - *) NET_STRING+=",gw=$GATE" ;; - esac - fi + # Nesting support (user configurable, default enabled) + if [ "${ENABLE_NESTING:-1}" == "1" ]; then + FEATURES="nesting=1" + fi - # VLAN - if [[ -n "$VLAN" ]]; then - case "$VLAN" in - ,tag=*) NET_STRING+="$VLAN" ;; - *) NET_STRING+=",tag=$VLAN" ;; - esac - fi + # Keyctl for unprivileged containers (needed for Docker) + if [ "$CT_TYPE" == "1" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}keyctl=1" + fi - # MTU - if [[ -n "$MTU" ]]; then - case "$MTU" in - ,mtu=*) NET_STRING+="$MTU" ;; - *) NET_STRING+=",mtu=$MTU" ;; - esac - fi + if [ "$ENABLE_FUSE" == "yes" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}fuse=1" + 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 + # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly + # Key insight: Bash cannot export arrays, so we build the options as a string - # Build FEATURES string based on container type and user choices - FEATURES="" + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null - # Nesting support (user configurable, default enabled) - if [ "${ENABLE_NESTING:-1}" == "1" ]; then - FEATURES="nesting=1" - fi + # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - # Keyctl for unprivileged containers (needed for Docker) - if [ "$CT_TYPE" == "1" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}keyctl=1" - fi + # Core exports for install.func + 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" + 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" - if [ "$ENABLE_FUSE" == "yes" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}fuse=1" - fi + # DEV_MODE exports (optional, for debugging) + export BUILD_LOG="$BUILD_LOG" + export INSTALL_LOG="/root/.install-${SESSION_ID}.log" + export dev_mode="${dev_mode:-}" + export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" + export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" + export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" + export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" + export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" + export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" + export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" - # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly - # Key insight: Bash cannot export arrays, so we build the options as a string - - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null - - # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - - # Core exports for install.func - 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" - 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" - - # DEV_MODE exports (optional, for debugging) - export BUILD_LOG="$BUILD_LOG" - export INSTALL_LOG="/root/.install-${SESSION_ID}.log" - export dev_mode="${dev_mode:-}" - export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" - export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" - export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" - export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" - export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" - export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" - export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" - - # Build PCT_OPTIONS as multi-line string - PCT_OPTIONS_STRING=" -features $FEATURES + # Build PCT_OPTIONS as multi-line string + PCT_OPTIONS_STRING=" -features $FEATURES -hostname $HN -tags $TAGS" - # Add storage if specified - if [ -n "$SD" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Add storage if specified + if [ -n "$SD" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $SD" - fi + fi - # Add nameserver if specified - if [ -n "$NS" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Add nameserver if specified + if [ -n "$NS" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NS" - fi + fi - # Network configuration - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Network configuration + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NET_STRING -onboot 1 -cores $CORE_COUNT -memory $RAM_SIZE -unprivileged $CT_TYPE" - # Protection flag (if var_protection was set) - if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Protection flag (if var_protection was set) + if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -protection 1" - fi + fi - # Timezone flag (if var_timezone was set) - if [ -n "${CT_TIMEZONE:-}" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Timezone flag (if var_timezone was set) + if [ -n "${CT_TIMEZONE:-}" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -timezone $CT_TIMEZONE" - fi + fi - # Password (already formatted) - if [ -n "$PW" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Password (already formatted) + if [ -n "$PW" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $PW" - fi - - # Export as string (this works, unlike arrays!) - export PCT_OPTIONS="$PCT_OPTIONS_STRING" - export TEMPLATE_STORAGE="${var_template_storage:-}" - export CONTAINER_STORAGE="${var_container_storage:-}" - - # # DEBUG: Show final PCT_OPTIONS being exported - # echo "[DEBUG] PCT_OPTIONS to be exported:" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Calling create_lxc_container..." - - create_lxc_container || exit $? - - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - - # ============================================================================ - # GPU/USB PASSTHROUGH CONFIGURATION - # ============================================================================ - - # Check if GPU passthrough is enabled - # Returns true only if var_gpu is explicitly set to "yes" - # Can be set via: - # - Environment variable: var_gpu=yes bash -c "..." - # - CT script default: var_gpu="${var_gpu:-no}" - # - Advanced settings wizard - # - App defaults file: /usr/local/community-scripts/defaults/.vars - is_gpu_app() { - [[ "${var_gpu:-no}" == "yes" ]] && return 0 - 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_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") - 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_custom "🎮" "${RD}" "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 + # Export as string (this works, unlike arrays!) + export PCT_OPTIONS="$PCT_OPTIONS_STRING" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + + # # DEBUG: Show final PCT_OPTIONS being exported + # echo "[DEBUG] PCT_OPTIONS to be exported:" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Calling create_lxc_container..." + + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # Check if GPU passthrough is enabled + # Returns true only if var_gpu is explicitly set to "yes" + # Can be set via: + # - Environment variable: var_gpu=yes bash -c "..." + # - CT script default: var_gpu="${var_gpu:-no}" + # - Advanced settings wizard + # - App defaults file: /usr/local/community-scripts/defaults/.vars + is_gpu_app() { + [[ "${var_gpu:-no}" == "yes" ]] && return 0 + 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_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") + done + fi fi - fi - fi - # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] - if echo "$pci_vga_info" | grep -q "\[10de:"; then - msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" + # 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_custom "🎮" "${RD}" "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 - # Simple passthrough - just bind /dev/nvidia* devices if they exist - # Skip directories like /dev/nvidia-caps (they need special handling) - for d in /dev/nvidia*; do - [[ -e "$d" ]] || continue - [[ -d "$d" ]] && continue # Skip directories - NVIDIA_DEVICES+=("$d") - done + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" - fi - fi + # Simple passthrough - just bind /dev/nvidia* devices if they exist + # Skip directories like /dev/nvidia-caps (they need special handling) + for d in /dev/nvidia*; do + [[ -e "$d" ]] || continue + [[ -d "$d" ]] && continue # Skip directories + NVIDIA_DEVICES+=("$d") + done - # Debug output - msg_debug "Intel devices: ${INTEL_DEVICES[*]}" - msg_debug "AMD devices: ${AMD_DEVICES[*]}" - msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" - } + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" + fi + fi - # Configure USB passthrough for privileged containers - configure_usb_passthrough() { - if [[ "$CT_TYPE" != "0" ]]; then - return 0 - fi + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } - msg_info "Configuring automatic USB passthrough (privileged container)" - cat <>"$LXC_CONFIG" + # 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: @@ -2891,454 +2891,454 @@ 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 - msg_ok "USB passthrough configured" - } + msg_ok "USB passthrough configured" + } - # Configure GPU passthrough - configure_gpu_passthrough() { - # Skip if: - # GPU passthrough is enabled when var_gpu="yes": - # - Set via environment variable: var_gpu=yes bash -c "..." - # - Set in CT script: var_gpu="${var_gpu:-no}" - # - Enabled in advanced_settings wizard - # - Configured in app defaults file - if ! is_gpu_app "$APP"; then - return 0 - fi + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if: + # GPU passthrough is enabled when var_gpu="yes": + # - Set via environment variable: var_gpu=yes bash -c "..." + # - Set in CT script: var_gpu="${var_gpu:-no}" + # - Enabled in advanced_settings wizard + # - Configured in app defaults file + if ! is_gpu_app "$APP"; then + return 0 + fi - detect_gpu_devices + detect_gpu_devices - # Count available GPU types - local gpu_count=0 - local available_gpus=() + # 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 [[ ${#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 [[ ${#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 [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi - if [[ $gpu_count -eq 0 ]]; then - msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" - return 0 - fi + if [[ $gpu_count -eq 0 ]]; then + msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" + return 0 + fi - local selected_gpu="" + local selected_gpu="" - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_ok "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^^}" + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_ok "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 + # 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 + 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 + # 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[@]}") + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - # Use pct set to add devices with proper dev0/dev1 format - # GIDs will be detected and set after container starts - local dev_index=0 - for dev in "${devices[@]}"; do - # Add to config using pct set (will be visible in GUI) - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set to add devices with proper dev0/dev1 format + # GIDs will be detected and set after container starts + local dev_index=0 + for dev in "${devices[@]}"; do + # Add to config using pct set (will be visible in GUI) + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" - ;; + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" + ;; - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_warn "No NVIDIA devices available for passthrough" - return 0 - fi + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "No NVIDIA devices available for passthrough" + return 0 + fi - # Use pct set for NVIDIA devices - local dev_index=0 - for dev in "${NVIDIA_DEVICES[@]}"; do - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set for NVIDIA devices + local dev_index=0 + for dev in "${NVIDIA_DEVICES[@]}"; do + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" - ;; - esac - } + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" + ;; + esac + } - # Additional device passthrough - configure_additional_devices() { - # 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 [[ -e /dev/apex_0 ]]; then - 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 - } + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + 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 + } - # Execute pre-start configurations - configure_usb_passthrough - configure_gpu_passthrough - configure_additional_devices + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices - # ============================================================================ - # START CONTAINER AND INSTALL USERLAND - # ============================================================================ + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ - msg_info "Starting LXC Container" - pct start "$CTID" + 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 assignment (IPv4 or IPv6) - local ip_in_lxc="" - for i in {1..20}; do - # Try IPv4 first - ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - # Fallback to IPv6 if IPv4 not available - if [ -z "$ip_in_lxc" ]; then - ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) - fi - [ -n "$ip_in_lxc" ] && break - sleep 1 + # 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 - if [ -z "$ip_in_lxc" ]; then - msg_error "No IP assigned to CT $CTID after 20s" - echo -e "${YW}Troubleshooting:${CL}" - echo " • Verify bridge ${BRG} exists and has connectivity" - echo " • Check if DHCP server is reachable (if using DHCP)" - echo " • Verify static IP configuration (if using static IP)" - echo " • Check Proxmox firewall rules" - echo " • If using Tailscale: Disable MagicDNS temporarily" - exit 1 + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP assignment (IPv4 or IPv6) + local ip_in_lxc="" + for i in {1..20}; do + # Try IPv4 first + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + # Fallback to IPv6 if IPv4 not available + if [ -z "$ip_in_lxc" ]; then + ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) + fi + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + echo -e "${YW}Troubleshooting:${CL}" + echo " • Verify bridge ${BRG} exists and has connectivity" + echo " • Check if DHCP server is reachable (if using DHCP)" + echo " • Verify static IP configuration (if using static IP)" + echo " • Check Proxmox firewall rules" + echo " • If using Tailscale: Disable MagicDNS temporarily" + exit 1 + fi + + # Verify basic connectivity (ping test) + local ping_success=false + for retry in {1..3}; do + if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || + pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || + pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then + ping_success=true + break + fi + sleep 2 + done + + if [ "$ping_success" = false ]; then + msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" + echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" + else + msg_ok "Network in LXC is reachable (ping)" + fi fi - # Verify basic connectivity (ping test) - local ping_success=false - for retry in {1..3}; do - if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || - pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || - pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then - ping_success=true - break - fi - sleep 2 - done + # 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 [ "$ping_success" = false ]; then - msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" - echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" - else - msg_ok "Network in LXC is reachable (ping)" - fi - fi + fix_gpu_gids - # 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 - } + # Continue with standard container setup + msg_info "Customizing LXC Container" - fix_gpu_gids + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi - # Continue with standard container setup - msg_info "Customizing LXC Container" + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi - # # Install GPU userland if configured - # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - # install_gpu_userland "VAAPI" - # fi + # Continue with standard container setup - install core dependencies based on OS + sleep 3 - # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - # install_gpu_userland "NVIDIA" - # fi - - # Continue with standard container setup - install core dependencies based on OS - sleep 3 - - case "$var_os" in - alpine) - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories + case "$var_os" in + alpine) + 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" - ;; + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + ;; - debian | ubuntu | devuan) - # Locale setup for Debian-based - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + debian | ubuntu | devuan) + # Locale setup for Debian-based + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ [[ -n \"\$locale_line\" ]] && echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null 2>&1 && \ export LANG=\$locale_line || true" - # Timezone setup - 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" + # Timezone setup + 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + # Core dependencies + 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 + } + ;; + + fedora | rockylinux | almalinux | centos) + # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS + pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq procps-ng >/dev/null 2>&1 || yum install -y curl sudo mc jq procps-ng >/dev/null 2>&1" || { + msg_error "dnf/yum base packages installation failed" + exit 1 + } + ;; + + opensuse) + # openSUSE + pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq >/dev/null" || { + msg_error "zypper base packages installation failed" + exit 1 + } + ;; + + gentoo) + # Gentoo - emerge is slow, only install essentials + pct exec "$CTID" -- bash -c "emerge --quiet app-misc/jq net-misc/curl app-misc/mc >/dev/null 2>&1" || { + msg_warn "Gentoo base packages installation incomplete - may need manual setup" + } + ;; + + openeuler) + # openEuler (RHEL-compatible) + pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq >/dev/null" || { + msg_error "dnf base packages installation failed" + exit 1 + } + ;; + + *) + msg_warn "Unknown OS '$var_os' - skipping core dependency installation" + ;; + esac + + msg_ok "Customized LXC Container" + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + # NOTE: We disable error handling here because: + # 1. Container errors are caught by error_handler INSIDE container + # 2. Container creates flag file with exit code + # 3. We read flag file and handle cleanup manually below + # 4. We DON'T want host error_handler to fire for lxc-attach command itself + + set +Eeuo pipefail # Disable ALL error handling temporarily + trap - ERR # Remove ERR trap completely + + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + local lxc_exit=$? + + set -Eeuo pipefail # Re-enable error handling + trap 'error_handler' ERR # Restore ERR trap + + # Check for error flag file in container (more reliable than lxc-attach exit code) + local install_exit_code=0 + if [[ -n "${SESSION_ID:-}" ]]; then + local error_flag="/root/.install-${SESSION_ID}.failed" + if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then + install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") + pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true + fi fi - # Core dependencies - 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 - } - ;; - - fedora | rockylinux | almalinux | centos) - # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq procps-ng >/dev/null 2>&1 || yum install -y curl sudo mc jq procps-ng >/dev/null 2>&1" || { - msg_error "dnf/yum base packages installation failed" - exit 1 - } - ;; - - opensuse) - # openSUSE - pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq >/dev/null" || { - msg_error "zypper base packages installation failed" - exit 1 - } - ;; - - gentoo) - # Gentoo - emerge is slow, only install essentials - pct exec "$CTID" -- bash -c "emerge --quiet app-misc/jq net-misc/curl app-misc/mc >/dev/null 2>&1" || { - msg_warn "Gentoo base packages installation incomplete - may need manual setup" - } - ;; - - openeuler) - # openEuler (RHEL-compatible) - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq >/dev/null" || { - msg_error "dnf base packages installation failed" - exit 1 - } - ;; - - *) - msg_warn "Unknown OS '$var_os' - skipping core dependency installation" - ;; - esac - - msg_ok "Customized LXC Container" - - # Install SSH keys - install_ssh_keys_into_ct - - # Run application installer - # NOTE: We disable error handling here because: - # 1. Container errors are caught by error_handler INSIDE container - # 2. Container creates flag file with exit code - # 3. We read flag file and handle cleanup manually below - # 4. We DON'T want host error_handler to fire for lxc-attach command itself - - set +Eeuo pipefail # Disable ALL error handling temporarily - trap - ERR # Remove ERR trap completely - - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" - local lxc_exit=$? - - set -Eeuo pipefail # Re-enable error handling - trap 'error_handler' ERR # Restore ERR trap - - # Check for error flag file in container (more reliable than lxc-attach exit code) - local install_exit_code=0 - if [[ -n "${SESSION_ID:-}" ]]; then - local error_flag="/root/.install-${SESSION_ID}.failed" - if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then - install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") - pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true - fi - fi - - # Fallback to lxc-attach exit code if no flag file - if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then - install_exit_code=$lxc_exit - fi - - # Installation failed? - if [[ $install_exit_code -ne 0 ]]; then - msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" - - # Copy both logs from container before potential deletion - local build_log_copied=false - local install_log_copied=false - - if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then - # Copy BUILD_LOG (creation log) if it exists - if [[ -f "${BUILD_LOG}" ]]; then - cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true - fi - - # Copy INSTALL_LOG from container - if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then - install_log_copied=true - fi - - # Show available logs - echo "" - [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" - [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" + # Fallback to lxc-attach exit code if no flag file + if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then + install_exit_code=$lxc_exit fi - # Dev mode: Keep container or open breakpoint shell - if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then - msg_dev "Keep mode active - container ${CTID} preserved" - return 0 - elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then - msg_dev "Breakpoint mode - opening shell in container ${CTID}" - echo -e "${YW}Type 'exit' to return to host${CL}" - pct enter "$CTID" - echo "" - echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" - if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - msg_ok "Container ${CTID} removed" - else - msg_dev "Container ${CTID} kept for debugging" - fi - exit $install_exit_code - fi + # Installation failed? + if [[ $install_exit_code -ne 0 ]]; then + msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" - # Report failure to API before container cleanup - post_update_to_api "failed" "$install_exit_code" + # Copy both logs from container before potential deletion + local build_log_copied=false + local install_log_copied=false - # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) - echo "" - echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" + if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then + # Copy BUILD_LOG (creation log) if it exists + if [[ -f "${BUILD_LOG}" ]]; then + cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true + fi - if read -t 60 -r response; then - if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then - # Remove container - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - elif [[ "$response" =~ ^[Nn]$ ]]; then - echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" + # Copy INSTALL_LOG from container + if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then + install_log_copied=true + fi - # Dev mode: Setup MOTD/SSH for debugging access to broken container - if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then - echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" - if pct exec "$CTID" -- bash -c " + # Show available logs + echo "" + [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" + [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" + fi + + # Dev mode: Keep container or open breakpoint shell + if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then + msg_dev "Keep mode active - container ${CTID} preserved" + return 0 + elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then + msg_dev "Breakpoint mode - opening shell in container ${CTID}" + echo -e "${YW}Type 'exit' to return to host${CL}" + pct enter "$CTID" + echo "" + echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" + if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + msg_ok "Container ${CTID} removed" + else + msg_dev "Container ${CTID} kept for debugging" + fi + exit $install_exit_code + fi + + # Report failure to API before container cleanup + post_update_to_api "failed" "$install_exit_code" + + # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) + echo "" + echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" + + if read -t 60 -r response; then + if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then + # Remove container + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + elif [[ "$response" =~ ^[Nn]$ ]]; then + echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" + + # Dev mode: Setup MOTD/SSH for debugging access to broken container + if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then + echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" + if pct exec "$CTID" -- bash -c " source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then - local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" - fi + local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" + fi + fi + fi + else + # Timeout - auto-remove + echo -e "\n${YW}No response - auto-removing container${CL}" + echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" fi - fi - else - # Timeout - auto-remove - echo -e "\n${YW}No response - auto-removing container${CL}" - echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - fi - exit $install_exit_code - fi + exit $install_exit_code + 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 + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 fi - ;; - "" | n | no) - msg_custom "ℹ️" "${BL}" "Container was not removed." - ;; - *) - msg_warn "Invalid response. Container was not removed." - ;; - esac + + # 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_custom "ℹ️" "${BL}" "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac } # ------------------------------------------------------------------------------ @@ -3346,81 +3346,81 @@ destroy_lxc() { # ------------------------------------------------------------------------------ # ===== 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}" + 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 - fi - STORAGE_RESULT="$preselect" - return 0 + + 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 + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi - msg_info "Detecting and setting correct GPU group IDs" + msg_info "Detecting and setting correct GPU group IDs" - # Get actual GIDs from 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") + # Get actual GIDs from 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") - # Create groups if they don't exist - if [[ -z "$video_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 - video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" - fi + # Create groups if they don't exist + if [[ -z "$video_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" + fi - if [[ -z "$render_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 - render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" - fi + if [[ -z "$render_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" + fi - # Stop container to update config - pct stop "$CTID" >/dev/null 2>&1 - sleep 1 + # Stop container to update config + pct stop "$CTID" >/dev/null 2>&1 + sleep 1 - # Update dev entries with correct GIDs - sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" - sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" + # Update dev entries with correct GIDs + sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" + sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" - # Restart container - pct start "$CTID" >/dev/null 2>&1 - sleep 2 + # Restart container + pct start "$CTID" >/dev/null 2>&1 + sleep 2 - msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" + msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" - # For privileged containers: also fix permissions inside container - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " + # For privileged containers: also fix permissions inside container + 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 @@ -3434,740 +3434,740 @@ fix_gpu_gids() { done fi " >/dev/null 2>&1 - fi + fi } 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 ]] + 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 + 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 + 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') + 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 + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 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 + + 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 - return 0 - done } create_lxc_container() { - # ------------------------------------------------------------------------------ - # Optional verbose mode (debug tracing) - # ------------------------------------------------------------------------------ - if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + # ------------------------------------------------------------------------------ + # 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}'; } + # ------------------------------------------------------------------------------ + # 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"; } + 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"; } + # 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 + # 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)" + _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 $STD apt-get update && $STD 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." + 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 - 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 - } + 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 $STD apt-get update && $STD 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 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 + } - msg_debug "CTID=$CTID" - msg_debug "PCT_OSTYPE=$PCT_OSTYPE" - msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + # ------------------------------------------------------------------------------ + # 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 + } - # 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 + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" - # 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 - } + # 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 - # 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 + # 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 + } - # 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 + # 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]" - fi + 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 - fi - msg_info "Validating storage '$CONTAINER_STORAGE'" - STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) + msg_info "Validating storage '$CONTAINER_STORAGE'" + STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) - case "$STORAGE_TYPE" in - iscsidirect) exit 212 ;; - iscsi | zfs) exit 213 ;; - cephfs) exit 219 ;; - pbs) exit 224 ;; - linstor | rbd | nfs | cifs) - pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 - ;; - esac + case "$STORAGE_TYPE" in + iscsidirect) exit 212 ;; + iscsi | zfs) exit 213 ;; + cephfs) exit 219 ;; + pbs) exit 224 ;; + linstor | rbd | nfs | cifs) + pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 + ;; + esac - pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 - msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" + pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 + msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" - msg_info "Validating template storage '$TEMPLATE_STORAGE'" - TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) + msg_info "Validating template storage '$TEMPLATE_STORAGE'" + TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) - if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then - msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" - fi - msg_ok "Template storage '$TEMPLATE_STORAGE' validated" - - # 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 + if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" fi - msg_ok "Cluster is quorate" - fi + msg_ok "Template storage '$TEMPLATE_STORAGE' validated" - # ------------------------------------------------------------------------------ - # Template discovery & validation - # Supported OS types (pveam available): alpine, almalinux, centos, debian, - # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu - # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - case "$PCT_OSTYPE" in - debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rocky | rockylinux | centos | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - gentoo) TEMPLATE_PATTERN="-current_" ;; - opensuse) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="" ;; - esac + # 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 + } - msg_info "Searching for template '$TEMPLATE_SEARCH'" + # 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 - # Build regex patterns outside awk/grep for clarity - SEARCH_PATTERN="^${TEMPLATE_SEARCH}" + # ------------------------------------------------------------------------------ + # Template discovery & validation + # Supported OS types (pveam available): alpine, almalinux, centos, debian, + # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | rockylinux | centos | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; + gentoo) TEMPLATE_PATTERN="-current-openrc" ;; + opensuse) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac - #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" - #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" - #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + msg_info "Searching for template '$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 - ) + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" - 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/^/ /' - - 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 - - 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 + 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 ) - 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 + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - 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}-" + msg_ok "Template search completed" - #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + #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 + 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 + + 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 [[ ${#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_custom "🚫" "${YW}" "Installation cancelled" - exit 0 - fi - else - msg_error "No ${PCT_OSTYPE} templates available at all" - exit 225 - fi - fi + 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 - #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + 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}-" - 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 + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" - # 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 + 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 + ) - [[ -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_custom "🚫" "${YW}" "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_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 - - 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_custom "ℹ️" "${BL}" "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 - - # PCT_OPTIONS is now a string (exported from build_container) - # Add rootfs if not already specified - if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then - PCT_OPTIONS="$PCT_OPTIONS - -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" - fi - - # 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}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" - - # # DEBUG: Show the actual command that will be executed - # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" - # echo "[DEBUG] CTID: $CTID" - # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" - # echo "[DEBUG] PCT_OPTIONS (will be word-split):" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Full command line:" - # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - # echo "[DEBUG] ========================================" - - msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - msg_debug "Logfile: $LOGFILE" - - # First attempt (PCT_OPTIONS is a multi-line string, use it directly) - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then - msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 not already on local - if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_info "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 - # Local fallback also failed - check for LXC stack version issue - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x + 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_custom "🚫" "${YW}" "Installation cancelled" + exit 0 fi - exit 209 - fi else - msg_ok "Container successfully created using local fallback." + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 fi - else - # Already on local storage and still failed - check LXC stack version - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi - else - msg_ok "Container successfully created after template repair." 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 - } + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - # Verify config rootfs - grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { - msg_error "RootFS entry missing in container config. See $LOGFILE" - exit 216 - } + 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 - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." + # 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 - # Report container creation to API - post_to_api + [[ -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_custom "🚫" "${YW}" "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_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 + + 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_custom "ℹ️" "${BL}" "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 + + # PCT_OPTIONS is now a string (exported from build_container) + # Add rootfs if not already specified + if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then + PCT_OPTIONS="$PCT_OPTIONS + -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" + fi + + # 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}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" + + # # DEBUG: Show the actual command that will be executed + # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" + # echo "[DEBUG] CTID: $CTID" + # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" + # echo "[DEBUG] PCT_OPTIONS (will be word-split):" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Full command line:" + # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + # echo "[DEBUG] ========================================" + + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + msg_debug "Logfile: $LOGFILE" + + # First attempt (PCT_OPTIONS is a multi-line string, use it directly) + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then + msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 not already on local + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_info "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 + # Local fallback also failed - check for LXC stack version issue + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + else + msg_ok "Container successfully created using local fallback." + fi + else + # Already on local storage and still failed - check LXC stack version + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_ok "Container successfully created after template repair." + 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." + + # Report container creation to API + post_to_api } # ============================================================================== @@ -4187,11 +4187,11 @@ create_lxc_container() { # - Posts final "done" status to API telemetry # ------------------------------------------------------------------------------ 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 @@ -4219,14 +4219,14 @@ description() { EOF - ) - pct set "$CTID" -description "$DESCRIPTION" + ) + 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" } # ============================================================================== @@ -4243,14 +4243,14 @@ EOF # - Only executes on non-zero exit codes # ------------------------------------------------------------------------------ api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - post_update_to_api "failed" "$exit_code" - fi + exit_code=$? + if [ $exit_code -ne 0 ]; then + post_update_to_api "failed" "$exit_code" + 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 diff --git a/misc/core.func b/misc/core.func index 39d655b0e..76339c87c 100644 --- a/misc/core.func +++ b/misc/core.func @@ -31,13 +31,13 @@ _CORE_FUNC_LOADED=1 # - Must be called at start of any script using these utilities # ------------------------------------------------------------------------------ load_functions() { - [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return - __FUNCTIONS_LOADED=1 - color - formatting - icons - default_vars - set_std_mode + [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return + __FUNCTIONS_LOADED=1 + color + formatting + icons + default_vars + set_std_mode } # ------------------------------------------------------------------------------ @@ -48,14 +48,14 @@ load_functions() { # GN (green), DGN (dark green), BGN (background green), CL (clear) # ------------------------------------------------------------------------------ color() { - YW=$(echo "\033[33m") - YWB=$'\e[93m' - 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") + YW=$(echo "\033[33m") + YWB=$'\e[93m' + 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") } # ------------------------------------------------------------------------------ @@ -67,9 +67,9 @@ color() { # - Used by spinner() function to avoid color conflicts # ------------------------------------------------------------------------------ color_spinner() { - CS_YW=$'\033[33m' - CS_YWB=$'\033[93m' - CS_CL=$'\033[m' + CS_YW=$'\033[33m' + CS_YWB=$'\033[93m' + CS_CL=$'\033[m' } # ------------------------------------------------------------------------------ @@ -81,11 +81,11 @@ color_spinner() { # - TAB/TAB3: Indentation spacing # ------------------------------------------------------------------------------ formatting() { - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - HOLD=" " - TAB=" " - TAB3=" " + BFR="\\r\\033[K" + BOLD=$(echo "\033[1m") + HOLD=" " + TAB=" " + TAB3=" " } # ------------------------------------------------------------------------------ @@ -96,34 +96,34 @@ formatting() { # - Icons: CM (checkmark), CROSS (error), INFO (info), HOURGLASS (wait), etc. # ------------------------------------------------------------------------------ icons() { - CM="${TAB}✔️${TAB}" - CROSS="${TAB}✖️${TAB}" - DNSOK="✔️ " - DNSFAIL="${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}" - ICON_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}" - HOURGLASS="${TAB}⏳${TAB}" + CM="${TAB}✔️${TAB}" + CROSS="${TAB}✖️${TAB}" + DNSOK="✔️ " + DNSFAIL="${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}" + ICON_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}" + HOURGLASS="${TAB}⏳${TAB}" } # ------------------------------------------------------------------------------ @@ -135,9 +135,9 @@ icons() { # - i: Counter variable initialized to RETRY_NUM # ------------------------------------------------------------------------------ default_vars() { - RETRY_NUM=10 - RETRY_EVERY=3 - i=$RETRY_NUM + RETRY_NUM=10 + RETRY_EVERY=3 + i=$RETRY_NUM } # ------------------------------------------------------------------------------ @@ -149,17 +149,17 @@ default_vars() { # - If DEV_MODE_TRACE=true: Enables bash tracing (set -x) # ------------------------------------------------------------------------------ set_std_mode() { - if [ "${VERBOSE:-no}" = "yes" ]; then - STD="" - else - STD="silent" - fi + if [ "${VERBOSE:-no}" = "yes" ]; then + STD="" + else + STD="silent" + fi - # Enable bash tracing if trace mode active - if [[ "${DEV_MODE_TRACE:-false}" == "true" ]]; then - set -x - export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' - fi + # Enable bash tracing if trace mode active + if [[ "${DEV_MODE_TRACE:-false}" == "true" ]]; then + set -x + export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + fi } # ------------------------------------------------------------------------------ @@ -177,57 +177,57 @@ set_std_mode() { # - Call this early in script execution # ------------------------------------------------------------------------------ parse_dev_mode() { - local mode - # Initialize all flags to false - export DEV_MODE_MOTD=false - export DEV_MODE_KEEP=false - export DEV_MODE_TRACE=false - export DEV_MODE_PAUSE=false - export DEV_MODE_BREAKPOINT=false - export DEV_MODE_LOGS=false - export DEV_MODE_DRYRUN=false + local mode + # Initialize all flags to false + export DEV_MODE_MOTD=false + export DEV_MODE_KEEP=false + export DEV_MODE_TRACE=false + export DEV_MODE_PAUSE=false + export DEV_MODE_BREAKPOINT=false + export DEV_MODE_LOGS=false + export DEV_MODE_DRYRUN=false - # Parse comma-separated modes - if [[ -n "${dev_mode:-}" ]]; then - IFS=',' read -ra MODES <<<"$dev_mode" - for mode in "${MODES[@]}"; do - mode="$(echo "$mode" | xargs)" # Trim whitespace - case "$mode" in - motd) export DEV_MODE_MOTD=true ;; - keep) export DEV_MODE_KEEP=true ;; - trace) export DEV_MODE_TRACE=true ;; - pause) export DEV_MODE_PAUSE=true ;; - breakpoint) export DEV_MODE_BREAKPOINT=true ;; - logs) export DEV_MODE_LOGS=true ;; - dryrun) export DEV_MODE_DRYRUN=true ;; - *) - if declare -f msg_warn >/dev/null 2>&1; then - msg_warn "Unknown dev_mode: '$mode' (ignored)" - else - echo "[WARN] Unknown dev_mode: '$mode' (ignored)" >&2 + # Parse comma-separated modes + if [[ -n "${dev_mode:-}" ]]; then + IFS=',' read -ra MODES <<<"$dev_mode" + for mode in "${MODES[@]}"; do + mode="$(echo "$mode" | xargs)" # Trim whitespace + case "$mode" in + motd) export DEV_MODE_MOTD=true ;; + keep) export DEV_MODE_KEEP=true ;; + trace) export DEV_MODE_TRACE=true ;; + pause) export DEV_MODE_PAUSE=true ;; + breakpoint) export DEV_MODE_BREAKPOINT=true ;; + logs) export DEV_MODE_LOGS=true ;; + dryrun) export DEV_MODE_DRYRUN=true ;; + *) + if declare -f msg_warn >/dev/null 2>&1; then + msg_warn "Unknown dev_mode: '$mode' (ignored)" + else + echo "[WARN] Unknown dev_mode: '$mode' (ignored)" >&2 + fi + ;; + esac + done + + # Show active dev modes + local active_modes=() + [[ $DEV_MODE_MOTD == true ]] && active_modes+=("motd") + [[ $DEV_MODE_KEEP == true ]] && active_modes+=("keep") + [[ $DEV_MODE_TRACE == true ]] && active_modes+=("trace") + [[ $DEV_MODE_PAUSE == true ]] && active_modes+=("pause") + [[ $DEV_MODE_BREAKPOINT == true ]] && active_modes+=("breakpoint") + [[ $DEV_MODE_LOGS == true ]] && active_modes+=("logs") + [[ $DEV_MODE_DRYRUN == true ]] && active_modes+=("dryrun") + + if [[ ${#active_modes[@]} -gt 0 ]]; then + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "🔧" "${YWB}" "Dev modes active: ${active_modes[*]}" + else + echo "[DEV] Active modes: ${active_modes[*]}" >&2 + fi fi - ;; - esac - done - - # Show active dev modes - local active_modes=() - [[ $DEV_MODE_MOTD == true ]] && active_modes+=("motd") - [[ $DEV_MODE_KEEP == true ]] && active_modes+=("keep") - [[ $DEV_MODE_TRACE == true ]] && active_modes+=("trace") - [[ $DEV_MODE_PAUSE == true ]] && active_modes+=("pause") - [[ $DEV_MODE_BREAKPOINT == true ]] && active_modes+=("breakpoint") - [[ $DEV_MODE_LOGS == true ]] && active_modes+=("logs") - [[ $DEV_MODE_DRYRUN == true ]] && active_modes+=("dryrun") - - if [[ ${#active_modes[@]} -gt 0 ]]; then - if declare -f msg_custom >/dev/null 2>&1; then - msg_custom "🔧" "${YWB}" "Dev modes active: ${active_modes[*]}" - else - echo "[DEV] Active modes: ${active_modes[*]}" >&2 - fi fi - fi } # ============================================================================== @@ -242,13 +242,13 @@ parse_dev_mode() { # - Required because scripts use Bash-specific features # ------------------------------------------------------------------------------ shell_check() { - 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..." - sleep 2 - exit - fi + 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..." + sleep 2 + exit + fi } # ------------------------------------------------------------------------------ @@ -259,13 +259,13 @@ shell_check() { # - Exits with error if not running as root directly # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -276,35 +276,35 @@ root_check() { # - Exits with error message if unsupported version detected # ------------------------------------------------------------------------------ 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}')" - # 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 + # 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 - return 0 - fi - # Check for Proxmox VE 9.x: allow 9.0–9.1 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 1)); then - msg_error "This version of Proxmox VE is not yet supported." - msg_error "Supported: Proxmox VE version 9.0 – 9.1" - exit 1 + # Check for Proxmox VE 9.x: allow 9.0–9.1 + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 0 || MINOR > 1)); then + msg_error "This version of Proxmox VE is not yet supported." + msg_error "Supported: Proxmox VE version 9.0 – 9.1" + exit 1 + fi + return 0 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.9 or 9.0 – 9.1" - 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.9 or 9.0 – 9.1" + exit 1 } # ------------------------------------------------------------------------------ @@ -315,13 +315,13 @@ pve_check() { # - Provides link to ARM64-compatible scripts # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -333,29 +333,29 @@ arch_check() { # - Does not abort execution, only warns # ------------------------------------------------------------------------------ ssh_check() { - if [ -n "$SSH_CLIENT" ]; then - local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") - local host_ip=$(hostname -I | awk '{print $1}') + if [ -n "$SSH_CLIENT" ]; then + local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") + local host_ip=$(hostname -I | awk '{print $1}') - # 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 + # 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 - - # 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 } # ============================================================================== @@ -371,14 +371,14 @@ ssh_check() { # - Fallback to BUILD_LOG if neither is set # ------------------------------------------------------------------------------ get_active_logfile() { - if [[ -n "${INSTALL_LOG:-}" ]]; then - echo "$INSTALL_LOG" - elif [[ -n "${BUILD_LOG:-}" ]]; then - echo "$BUILD_LOG" - else - # Fallback for legacy scripts - echo "/tmp/build-$(date +%Y%m%d_%H%M%S).log" - fi + if [[ -n "${INSTALL_LOG:-}" ]]; then + echo "$INSTALL_LOG" + elif [[ -n "${BUILD_LOG:-}" ]]; then + echo "$BUILD_LOG" + else + # Fallback for legacy scripts + echo "/tmp/build-$(date +%Y%m%d_%H%M%S).log" + fi } # Legacy compatibility: SILENT_LOGFILE points to active log @@ -393,56 +393,56 @@ SILENT_LOGFILE="$(get_active_logfile)" # - Sources explain_exit_code() for detailed error messages # ------------------------------------------------------------------------------ silent() { - local cmd="$*" - local caller_line="${BASH_LINENO[0]:-unknown}" - local logfile="$(get_active_logfile)" + local cmd="$*" + local caller_line="${BASH_LINENO[0]:-unknown}" + local logfile="$(get_active_logfile)" - # Dryrun mode: Show command without executing - if [[ "${DEV_MODE_DRYRUN:-false}" == "true" ]]; then - if declare -f msg_custom >/dev/null 2>&1; then - msg_custom "🔍" "${BL}" "[DRYRUN] $cmd" - else - echo "[DRYRUN] $cmd" >&2 - fi - return 0 - fi - - set +Eeuo pipefail - trap - ERR - - "$@" >>"$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) + # Dryrun mode: Show command without executing + if [[ "${DEV_MODE_DRYRUN:-false}" == "true" ]]; then + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "🔍" "${BL}" "[DRYRUN] $cmd" + else + echo "[DRYRUN] $cmd" >&2 + fi + return 0 fi - local explanation - explanation="$(explain_exit_code "$rc")" + set +Eeuo pipefail + trap - ERR - printf "\e[?25h" - msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" - msg_custom "→" "${YWB}" "${cmd}" + "$@" >>"$logfile" 2>&1 + local rc=$? - if [[ -s "$logfile" ]]; then - local log_lines=$(wc -l <"$logfile") - echo "--- Last 10 lines of silent log ---" - tail -n 10 "$logfile" - echo "-----------------------------------" + set -Eeuo pipefail + trap 'error_handler' ERR - # Show how to view full log if there are more lines - if [[ $log_lines -gt 10 ]]; then - msg_custom "📋" "${YW}" "View full log (${log_lines} lines): ${logfile}" - fi + 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" + msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" + msg_custom "→" "${YWB}" "${cmd}" + + if [[ -s "$logfile" ]]; then + local log_lines=$(wc -l <"$logfile") + echo "--- Last 10 lines of silent log ---" + tail -n 10 "$logfile" + echo "-----------------------------------" + + # Show how to view full log if there are more lines + if [[ $log_lines -gt 10 ]]; then + msg_custom "📋" "${YW}" "View full log (${log_lines} lines): ${logfile}" + fi + fi + + exit "$rc" fi - - exit "$rc" - fi } # ------------------------------------------------------------------------------ @@ -454,14 +454,14 @@ silent() { # - Uses color_spinner() colors for output # ------------------------------------------------------------------------------ spinner() { - local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) - local msg="${SPINNER_MSG:-Processing...}" - local i=0 - while true; do - local index=$((i++ % ${#chars[@]})) - printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${msg}${CS_CL}" - sleep 0.1 - done + local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) + local msg="${SPINNER_MSG:-Processing...}" + local i=0 + while true; do + local index=$((i++ % ${#chars[@]})) + printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${msg}${CS_CL}" + sleep 0.1 + done } # ------------------------------------------------------------------------------ @@ -473,8 +473,8 @@ spinner() { # - Fallback to ANSI codes if tput not available # ------------------------------------------------------------------------------ clear_line() { - tput cr 2>/dev/null || echo -en "\r" - tput el 2>/dev/null || echo -en "\033[K" + tput cr 2>/dev/null || echo -en "\r" + tput el 2>/dev/null || echo -en "\033[K" } # ------------------------------------------------------------------------------ @@ -487,20 +487,20 @@ clear_line() { # - Unsets SPINNER_PID and SPINNER_MSG variables # ------------------------------------------------------------------------------ stop_spinner() { - local pid="${SPINNER_PID:-}" - [[ -z "$pid" && -f /tmp/.spinner.pid ]] && pid=$(/dev/null; then - sleep 0.05 - kill -9 "$pid" 2>/dev/null || true - wait "$pid" 2>/dev/null || true + if [[ -n "$pid" && "$pid" =~ ^[0-9]+$ ]]; then + if kill "$pid" 2>/dev/null; then + sleep 0.05 + kill -9 "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + fi + rm -f /tmp/.spinner.pid fi - rm -f /tmp/.spinner.pid - fi - unset SPINNER_PID SPINNER_MSG - stty sane 2>/dev/null || true + unset SPINNER_PID SPINNER_MSG + stty sane 2>/dev/null || true } # ============================================================================== @@ -517,42 +517,45 @@ stop_spinner() { # - Backgrounds spinner process and stores PID for later cleanup # ------------------------------------------------------------------------------ msg_info() { - local msg="$1" - [[ -z "$msg" ]] && return + local msg="$1" + [[ -z "$msg" ]] && return - if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then - declare -gA MSG_INFO_SHOWN=() - fi - [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return - MSG_INFO_SHOWN["$msg"]=1 - - stop_spinner - SPINNER_MSG="$msg" - - if is_verbose_mode || is_alpine; then - local HOURGLASS="${TAB}⏳${TAB}" - printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2 - - # Pause mode: Wait for Enter after each step - if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then - echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 - read -r + if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then + declare -gA MSG_INFO_SHOWN=() fi - return - fi + # Sanitize message for use as associative array key (remove ANSI codes) + local sanitized_msg + sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g') + [[ -n "${MSG_INFO_SHOWN["$sanitized_msg"]+x}" ]] && return + MSG_INFO_SHOWN["$sanitized_msg"]=1 - color_spinner - spinner & - SPINNER_PID=$! - echo "$SPINNER_PID" >/tmp/.spinner.pid - disown "$SPINNER_PID" 2>/dev/null || true - - # Pause mode: Stop spinner and wait - if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then stop_spinner - echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 - read -r - fi + SPINNER_MSG="$msg" + + if is_verbose_mode || is_alpine; then + local HOURGLASS="${TAB}⏳${TAB}" + printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2 + + # Pause mode: Wait for Enter after each step + if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then + echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 + read -r + fi + return + fi + + color_spinner + spinner & + SPINNER_PID=$! + echo "$SPINNER_PID" >/tmp/.spinner.pid + disown "$SPINNER_PID" 2>/dev/null || true + + # Pause mode: Stop spinner and wait + if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then + stop_spinner + echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 + read -r + fi } # ------------------------------------------------------------------------------ @@ -564,12 +567,15 @@ msg_info() { # - Uses green color for success indication # ------------------------------------------------------------------------------ msg_ok() { - local msg="$1" - [[ -z "$msg" ]] && return - stop_spinner - clear_line - echo -e "$CM ${GN}${msg}${CL}" - unset MSG_INFO_SHOWN["$msg"] + local msg="$1" + [[ -z "$msg" ]] && return + stop_spinner + clear_line + echo -e "$CM ${GN}${msg}${CL}" + # Sanitize message for use as associative array key (remove ANSI codes) + local sanitized_msg + sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g') + unset MSG_INFO_SHOWN["$sanitized_msg"] } # ------------------------------------------------------------------------------ @@ -581,9 +587,9 @@ msg_ok() { # - Outputs to stderr # ------------------------------------------------------------------------------ msg_error() { - stop_spinner - local msg="$1" - echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2 + stop_spinner + local msg="$1" + echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2 } # ------------------------------------------------------------------------------ @@ -595,9 +601,9 @@ msg_error() { # - Outputs to stderr # ------------------------------------------------------------------------------ msg_warn() { - stop_spinner - local msg="$1" - echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 + stop_spinner + local msg="$1" + echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 } # ------------------------------------------------------------------------------ @@ -609,12 +615,12 @@ msg_warn() { # - Useful for specialized status messages # ------------------------------------------------------------------------------ msg_custom() { - local symbol="${1:-"[*]"}" - local color="${2:-"\e[36m"}" - local msg="${3:-}" - [[ -z "$msg" ]] && return - stop_spinner - echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" + local symbol="${1:-"[*]"}" + local color="${2:-"\e[36m"}" + local msg="${3:-}" + [[ -z "$msg" ]] && return + stop_spinner + echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" } # ------------------------------------------------------------------------------ @@ -626,10 +632,10 @@ msg_custom() { # - Uses bright yellow color for debug output # ------------------------------------------------------------------------------ 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 + if [[ "${var_full_verbose:-0}" == "1" ]]; then + [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 + echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" + fi } # ------------------------------------------------------------------------------ @@ -642,9 +648,9 @@ msg_debug() { # - Usage: msg_dev "Container ready for debugging" # ------------------------------------------------------------------------------ msg_dev() { - if [[ -n "${dev_mode:-}" ]]; then - echo -e "${SEARCH}${BOLD}${DGN}🔧 [DEV]${CL} $*" - fi + if [[ -n "${dev_mode:-}" ]]; then + echo -e "${SEARCH}${BOLD}${DGN}🔧 [DEV]${CL} $*" + fi } # # - Displays error message and immediately terminates script @@ -652,8 +658,8 @@ msg_dev() { # - Use for unrecoverable errors that require immediate exit # ------------------------------------------------------------------------------ fatal() { - msg_error "$1" - kill -INT $$ + msg_error "$1" + kill -INT $$ } # ============================================================================== @@ -668,9 +674,9 @@ fatal() { # - Exits with default exit code # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -682,20 +688,20 @@ exit_script() { # - Returns header content or empty string on failure # ------------------------------------------------------------------------------ get_header() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local app_type=${APP_TYPE:-ct} # Default zu 'ct' falls nicht gesetzt - local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/${app_type}/headers/${app_name}" - local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}" + local app_name=$(echo "${APP,,}" | tr -d ' ') + local app_type=${APP_TYPE:-ct} # Default zu 'ct' falls nicht gesetzt + local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/${app_type}/headers/${app_name}" + local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}" - mkdir -p "$(dirname "$local_header_path")" + mkdir -p "$(dirname "$local_header_path")" - if [ ! -s "$local_header_path" ]; then - if ! curl -fsSL "$header_url" -o "$local_header_path"; then - return 1 + if [ ! -s "$local_header_path" ]; then + if ! curl -fsSL "$header_url" -o "$local_header_path"; then + return 1 + fi fi - fi - cat "$local_header_path" 2>/dev/null || true + cat "$local_header_path" 2>/dev/null || true } # ------------------------------------------------------------------------------ @@ -707,18 +713,18 @@ get_header() { # - Returns silently if header not available # ------------------------------------------------------------------------------ header_info() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_content + local app_name=$(echo "${APP,,}" | tr -d ' ') + local header_content - header_content=$(get_header "$app_name") || header_content="" + header_content=$(get_header "$app_name") || header_content="" - clear - local term_width - term_width=$(tput cols 2>/dev/null || echo 120) + clear + local term_width + term_width=$(tput cols 2>/dev/null || echo 120) - if [ -n "$header_content" ]; then - echo "$header_content" - fi + if [ -n "$header_content" ]; then + echo "$header_content" + fi } # ------------------------------------------------------------------------------ @@ -729,14 +735,14 @@ header_info() { # - Required for clear_line() and terminal width detection # ------------------------------------------------------------------------------ ensure_tput() { - if ! command -v tput >/dev/null 2>&1; then - if grep -qi 'alpine' /etc/os-release; then - apk add --no-cache ncurses >/dev/null 2>&1 - elif command -v apt-get >/dev/null 2>&1; then - apt-get update -qq >/dev/null - apt-get install -y -qq ncurses-bin >/dev/null 2>&1 + if ! command -v tput >/dev/null 2>&1; then + if grep -qi 'alpine' /etc/os-release; then + apk add --no-cache ncurses >/dev/null 2>&1 + elif command -v apt-get >/dev/null 2>&1; then + apt-get update -qq >/dev/null + apt-get install -y -qq ncurses-bin >/dev/null 2>&1 + fi fi - fi } # ------------------------------------------------------------------------------ @@ -748,16 +754,16 @@ ensure_tput() { # - Used to adjust behavior for Alpine-specific commands # ------------------------------------------------------------------------------ is_alpine() { - local os_id="${var_os:-${PCT_OSTYPE:-}}" + local os_id="${var_os:-${PCT_OSTYPE:-}}" - if [[ -z "$os_id" && -f /etc/os-release ]]; then - os_id="$( - . /etc/os-release 2>/dev/null - echo "${ID:-}" - )" - fi + if [[ -z "$os_id" && -f /etc/os-release ]]; then + os_id="$( + . /etc/os-release 2>/dev/null + echo "${ID:-}" + )" + fi - [[ "$os_id" == "alpine" ]] + [[ "$os_id" == "alpine" ]] } # ------------------------------------------------------------------------------ @@ -769,14 +775,14 @@ is_alpine() { # - Used by msg_info() to decide between spinner and static output # ------------------------------------------------------------------------------ is_verbose_mode() { - local verbose="${VERBOSE:-${var_verbose:-no}}" - local tty_status - if [[ -t 2 ]]; then - tty_status="interactive" - else - tty_status="not-a-tty" - fi - [[ "$verbose" != "no" || ! -t 2 ]] + local verbose="${VERBOSE:-${var_verbose:-no}}" + local tty_status + if [[ -t 2 ]]; then + tty_status="interactive" + else + tty_status="not-a-tty" + fi + [[ "$verbose" != "no" || ! -t 2 ]] } # ============================================================================== @@ -794,62 +800,62 @@ is_verbose_mode() { # - Run at end of container creation to minimize disk usage # ------------------------------------------------------------------------------ cleanup_lxc() { - msg_info "Cleaning up" - # OS-specific package manager cleanup - if is_alpine; then - $STD apk cache clean 2>/dev/null || true - rm -rf /var/cache/apk/* - elif command -v apt &>/dev/null; then - # Debian/Ubuntu/Devuan - $STD apt -y autoremove 2>/dev/null || true - $STD apt -y autoclean 2>/dev/null || true - $STD apt -y clean 2>/dev/null || true - elif command -v dnf &>/dev/null; then - # Fedora/Rocky/AlmaLinux/CentOS 8+ - $STD dnf clean all 2>/dev/null || true - $STD dnf autoremove -y 2>/dev/null || true - elif command -v yum &>/dev/null; then - # CentOS 7/older RHEL - $STD yum clean all 2>/dev/null || true - elif command -v zypper &>/dev/null; then - # openSUSE - $STD zypper clean --all 2>/dev/null || true - elif command -v emerge &>/dev/null; then - # Gentoo - $STD emerge --quiet --depclean 2>/dev/null || true - $STD eclean-dist -d 2>/dev/null || true - $STD eclean-pkg -d 2>/dev/null || true - fi + msg_info "Cleaning up" + # OS-specific package manager cleanup + if is_alpine; then + $STD apk cache clean 2>/dev/null || true + rm -rf /var/cache/apk/* + elif command -v apt &>/dev/null; then + # Debian/Ubuntu/Devuan + $STD apt -y autoremove 2>/dev/null || true + $STD apt -y autoclean 2>/dev/null || true + $STD apt -y clean 2>/dev/null || true + elif command -v dnf &>/dev/null; then + # Fedora/Rocky/AlmaLinux/CentOS 8+ + $STD dnf clean all 2>/dev/null || true + $STD dnf autoremove -y 2>/dev/null || true + elif command -v yum &>/dev/null; then + # CentOS 7/older RHEL + $STD yum clean all 2>/dev/null || true + elif command -v zypper &>/dev/null; then + # openSUSE + $STD zypper clean --all 2>/dev/null || true + elif command -v emerge &>/dev/null; then + # Gentoo + $STD emerge --quiet --depclean 2>/dev/null || true + $STD eclean-dist -d 2>/dev/null || true + $STD eclean-pkg -d 2>/dev/null || true + fi - # Clear temp artifacts (keep sockets/FIFOs; ignore errors) - 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 + # Clear temp artifacts (keep sockets/FIFOs; ignore errors) + 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 - # Truncate writable log files silently (permission errors ignored) - if command -v truncate >/dev/null 2>&1; then - find /var/log -type f -writable -print0 2>/dev/null | - xargs -0 -n1 truncate -s 0 2>/dev/null || true - fi + # Truncate writable log files silently (permission errors ignored) + if command -v truncate >/dev/null 2>&1; then + find /var/log -type f -writable -print0 2>/dev/null | + xargs -0 -n1 truncate -s 0 2>/dev/null || true + fi - # Node.js npm - if command -v npm &>/dev/null; then $STD npm cache clean --force 2>/dev/null || true; fi - # Node.js yarn - if command -v yarn &>/dev/null; then $STD yarn cache clean 2>/dev/null || true; fi - # Node.js pnpm - if command -v pnpm &>/dev/null; then $STD pnpm store prune 2>/dev/null || true; fi - # Go - if command -v go &>/dev/null; then $STD go clean -cache -modcache 2>/dev/null || true; fi - # Rust cargo - if command -v cargo &>/dev/null; then $STD cargo clean 2>/dev/null || true; fi - # Ruby gem - if command -v gem &>/dev/null; then $STD gem cleanup 2>/dev/null || true; fi - # Composer (PHP) - if command -v composer &>/dev/null; then $STD composer clear-cache 2>/dev/null || true; fi + # Node.js npm + if command -v npm &>/dev/null; then $STD npm cache clean --force 2>/dev/null || true; fi + # Node.js yarn + if command -v yarn &>/dev/null; then $STD yarn cache clean 2>/dev/null || true; fi + # Node.js pnpm + if command -v pnpm &>/dev/null; then $STD pnpm store prune 2>/dev/null || true; fi + # Go + if command -v go &>/dev/null; then $STD go clean -cache -modcache 2>/dev/null || true; fi + # Rust cargo + if command -v cargo &>/dev/null; then $STD cargo clean 2>/dev/null || true; fi + # Ruby gem + if command -v gem &>/dev/null; then $STD gem cleanup 2>/dev/null || true; fi + # Composer (PHP) + if command -v composer &>/dev/null; then $STD composer clear-cache 2>/dev/null || true; fi - if command -v journalctl &>/dev/null; then - $STD journalctl --vacuum-time=10m 2>/dev/null || true - fi - msg_ok "Cleaned" + if command -v journalctl &>/dev/null; then + $STD journalctl --vacuum-time=10m 2>/dev/null || true + fi + msg_ok "Cleaned" } # ------------------------------------------------------------------------------ @@ -863,41 +869,41 @@ cleanup_lxc() { # - Returns 0 if swap active or successfully created, 1 if declined/failed # ------------------------------------------------------------------------------ check_or_create_swap() { - msg_info "Checking for active swap" + msg_info "Checking for active swap" - if swapon --noheadings --show | grep -q 'swap'; then - msg_ok "Swap is active" - return 0 - fi + if swapon --noheadings --show | grep -q 'swap'; then + msg_ok "Swap is active" + return 0 + fi - msg_error "No active swap detected" + msg_error "No active swap detected" - read -p "Do you want to create a swap file? [y/N]: " create_swap - create_swap="${create_swap,,}" # to lowercase + read -p "Do you want to create a swap file? [y/N]: " create_swap + create_swap="${create_swap,,}" # to lowercase - if [[ "$create_swap" != "y" && "$create_swap" != "yes" ]]; then - msg_info "Skipping swap file creation" - return 1 - fi + if [[ "$create_swap" != "y" && "$create_swap" != "yes" ]]; then + msg_info "Skipping swap file creation" + return 1 + fi - read -p "Enter swap size in MB (e.g., 2048 for 2GB): " swap_size_mb - if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then - msg_error "Invalid size input. Aborting." - return 1 - fi + read -p "Enter swap size in MB (e.g., 2048 for 2GB): " swap_size_mb + if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then + msg_error "Invalid size input. Aborting." + return 1 + fi - local swap_file="/swapfile" + local swap_file="/swapfile" - msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" - if dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress && - chmod 600 "$swap_file" && - mkswap "$swap_file" && - swapon "$swap_file"; then - msg_ok "Swap file created and activated successfully" - else - msg_error "Failed to create or activate swap" - return 1 - fi + msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" + if dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress && + chmod 600 "$swap_file" && + mkswap "$swap_file" && + swapon "$swap_file"; then + msg_ok "Swap file created and activated successfully" + else + msg_error "Failed to create or activate swap" + return 1 + fi } # ============================================================================== diff --git a/misc/install.func b/misc/install.func index f474b4062..484a3a1fc 100644 --- a/misc/install.func +++ b/misc/install.func @@ -49,134 +49,134 @@ INIT_SYSTEM="" # systemd, openrc, sysvinit # OS_TYPE, OS_FAMILY, OS_VERSION, PKG_MANAGER, INIT_SYSTEM # ------------------------------------------------------------------------------ detect_os() { - if [[ -f /etc/os-release ]]; then - # shellcheck disable=SC1091 - . /etc/os-release - OS_TYPE="${ID:-unknown}" - OS_VERSION="${VERSION_ID:-unknown}" - elif [[ -f /etc/alpine-release ]]; then - OS_TYPE="alpine" - OS_VERSION=$(cat /etc/alpine-release) - elif [[ -f /etc/debian_version ]]; then - OS_TYPE="debian" - OS_VERSION=$(cat /etc/debian_version) - elif [[ -f /etc/redhat-release ]]; then - OS_TYPE="centos" - OS_VERSION=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1) - elif [[ -f /etc/arch-release ]]; then - OS_TYPE="arch" - OS_VERSION="rolling" - elif [[ -f /etc/gentoo-release ]]; then - OS_TYPE="gentoo" - OS_VERSION=$(cat /etc/gentoo-release | grep -oE '[0-9.]+') - else - OS_TYPE="unknown" - OS_VERSION="unknown" - fi - - # Normalize OS type and determine family - case "$OS_TYPE" in - debian) - OS_FAMILY="debian" - PKG_MANAGER="apt" - ;; - ubuntu) - OS_FAMILY="debian" - PKG_MANAGER="apt" - ;; - devuan) - OS_FAMILY="debian" - PKG_MANAGER="apt" - ;; - alpine) - OS_FAMILY="alpine" - PKG_MANAGER="apk" - ;; - fedora) - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - rocky | rockylinux) - OS_TYPE="rocky" - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - alma | almalinux) - OS_TYPE="alma" - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - centos) - OS_FAMILY="rhel" - # CentOS 7 uses yum, 8+ uses dnf - if [[ "${OS_VERSION%%.*}" -ge 8 ]]; then - PKG_MANAGER="dnf" + if [[ -f /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + OS_TYPE="${ID:-unknown}" + OS_VERSION="${VERSION_ID:-unknown}" + elif [[ -f /etc/alpine-release ]]; then + OS_TYPE="alpine" + OS_VERSION=$(cat /etc/alpine-release) + elif [[ -f /etc/debian_version ]]; then + OS_TYPE="debian" + OS_VERSION=$(cat /etc/debian_version) + elif [[ -f /etc/redhat-release ]]; then + OS_TYPE="centos" + OS_VERSION=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1) + elif [[ -f /etc/arch-release ]]; then + OS_TYPE="arch" + OS_VERSION="rolling" + elif [[ -f /etc/gentoo-release ]]; then + OS_TYPE="gentoo" + OS_VERSION=$(cat /etc/gentoo-release | grep -oE '[0-9.]+') else - PKG_MANAGER="yum" + OS_TYPE="unknown" + OS_VERSION="unknown" fi - ;; - rhel) - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - openeuler) - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - opensuse* | sles) - OS_TYPE="opensuse" - OS_FAMILY="suse" - PKG_MANAGER="zypper" - ;; - gentoo) - OS_FAMILY="gentoo" - PKG_MANAGER="emerge" - ;; - *) - OS_FAMILY="unknown" - PKG_MANAGER="unknown" - ;; - esac - # Detect init system - if command -v systemctl &>/dev/null && [[ -d /run/systemd/system ]]; then - INIT_SYSTEM="systemd" - elif command -v rc-service &>/dev/null || [[ -d /etc/init.d && -f /sbin/openrc ]]; then - INIT_SYSTEM="openrc" - elif [[ -f /etc/inittab ]]; then - INIT_SYSTEM="sysvinit" - else - INIT_SYSTEM="unknown" - fi + # Normalize OS type and determine family + case "$OS_TYPE" in + debian) + OS_FAMILY="debian" + PKG_MANAGER="apt" + ;; + ubuntu) + OS_FAMILY="debian" + PKG_MANAGER="apt" + ;; + devuan) + OS_FAMILY="debian" + PKG_MANAGER="apt" + ;; + alpine) + OS_FAMILY="alpine" + PKG_MANAGER="apk" + ;; + fedora) + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + rocky | rockylinux) + OS_TYPE="rocky" + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + alma | almalinux) + OS_TYPE="alma" + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + centos) + OS_FAMILY="rhel" + # CentOS 7 uses yum, 8+ uses dnf + if [[ "${OS_VERSION%%.*}" -ge 8 ]]; then + PKG_MANAGER="dnf" + else + PKG_MANAGER="yum" + fi + ;; + rhel) + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + openeuler) + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + opensuse* | sles) + OS_TYPE="opensuse" + OS_FAMILY="suse" + PKG_MANAGER="zypper" + ;; + gentoo) + OS_FAMILY="gentoo" + PKG_MANAGER="emerge" + ;; + *) + OS_FAMILY="unknown" + PKG_MANAGER="unknown" + ;; + esac + + # Detect init system + if command -v systemctl &>/dev/null && [[ -d /run/systemd/system ]]; then + INIT_SYSTEM="systemd" + elif command -v rc-service &>/dev/null || [[ -d /etc/init.d && -f /sbin/openrc ]]; then + INIT_SYSTEM="openrc" + elif [[ -f /etc/inittab ]]; then + INIT_SYSTEM="sysvinit" + else + INIT_SYSTEM="unknown" + fi } # ------------------------------------------------------------------------------ # Bootstrap: Ensure curl is available and source core functions # ------------------------------------------------------------------------------ _bootstrap() { - # Minimal bootstrap to get curl installed - if ! command -v curl &>/dev/null; then - printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 - if command -v apt-get &>/dev/null; then - apt-get update &>/dev/null && apt-get install -y curl &>/dev/null - elif command -v apk &>/dev/null; then - apk update &>/dev/null && apk add curl &>/dev/null - elif command -v dnf &>/dev/null; then - dnf install -y curl &>/dev/null - elif command -v yum &>/dev/null; then - yum install -y curl &>/dev/null - elif command -v zypper &>/dev/null; then - zypper install -y curl &>/dev/null - elif command -v emerge &>/dev/null; then - emerge --quiet net-misc/curl &>/dev/null + # Minimal bootstrap to get curl installed + if ! command -v curl &>/dev/null; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + if command -v apt-get &>/dev/null; then + apt-get update &>/dev/null && apt-get install -y curl &>/dev/null + elif command -v apk &>/dev/null; then + apk update &>/dev/null && apk add curl &>/dev/null + elif command -v dnf &>/dev/null; then + dnf install -y curl &>/dev/null + elif command -v yum &>/dev/null; then + yum install -y curl &>/dev/null + elif command -v zypper &>/dev/null; then + zypper install -y curl &>/dev/null + elif command -v emerge &>/dev/null; then + emerge --quiet net-misc/curl &>/dev/null + fi fi - fi - # Source core functions - 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 + # Source core functions + 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 } # Run bootstrap and OS detection @@ -193,30 +193,30 @@ detect_os # Updates package manager cache/database # ------------------------------------------------------------------------------ pkg_update() { - case "$PKG_MANAGER" in - apt) - $STD apt-get update - ;; - apk) - $STD apk update - ;; - dnf) - $STD dnf makecache - ;; - yum) - $STD yum makecache - ;; - zypper) - $STD zypper refresh - ;; - emerge) - $STD emerge --sync - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get update + ;; + apk) + $STD apk update + ;; + dnf) + $STD dnf makecache + ;; + yum) + $STD yum makecache + ;; + zypper) + $STD zypper refresh + ;; + emerge) + $STD emerge --sync + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -225,30 +225,30 @@ pkg_update() { # Upgrades all installed packages # ------------------------------------------------------------------------------ pkg_upgrade() { - case "$PKG_MANAGER" in - apt) - $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade - ;; - apk) - $STD apk -U upgrade - ;; - dnf) - $STD dnf -y upgrade - ;; - yum) - $STD yum -y update - ;; - zypper) - $STD zypper -n update - ;; - emerge) - $STD emerge --quiet --update --deep @world - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + ;; + apk) + $STD apk -U upgrade + ;; + dnf) + $STD dnf -y upgrade + ;; + yum) + $STD yum -y update + ;; + zypper) + $STD zypper -n update + ;; + emerge) + $STD emerge --quiet --update --deep @world + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -259,33 +259,33 @@ pkg_upgrade() { # packages - List of packages to install # ------------------------------------------------------------------------------ pkg_install() { - local packages=("$@") - [[ ${#packages[@]} -eq 0 ]] && return 0 + local packages=("$@") + [[ ${#packages[@]} -eq 0 ]] && return 0 - case "$PKG_MANAGER" in - apt) - $STD apt-get install -y "${packages[@]}" - ;; - apk) - $STD apk add --no-cache "${packages[@]}" - ;; - dnf) - $STD dnf install -y "${packages[@]}" - ;; - yum) - $STD yum install -y "${packages[@]}" - ;; - zypper) - $STD zypper install -y "${packages[@]}" - ;; - emerge) - $STD emerge --quiet "${packages[@]}" - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get install -y "${packages[@]}" + ;; + apk) + $STD apk add --no-cache "${packages[@]}" + ;; + dnf) + $STD dnf install -y "${packages[@]}" + ;; + yum) + $STD yum install -y "${packages[@]}" + ;; + zypper) + $STD zypper install -y "${packages[@]}" + ;; + emerge) + $STD emerge --quiet "${packages[@]}" + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -294,33 +294,33 @@ pkg_install() { # Removes one or more packages # ------------------------------------------------------------------------------ pkg_remove() { - local packages=("$@") - [[ ${#packages[@]} -eq 0 ]] && return 0 + local packages=("$@") + [[ ${#packages[@]} -eq 0 ]] && return 0 - case "$PKG_MANAGER" in - apt) - $STD apt-get remove -y "${packages[@]}" - ;; - apk) - $STD apk del "${packages[@]}" - ;; - dnf) - $STD dnf remove -y "${packages[@]}" - ;; - yum) - $STD yum remove -y "${packages[@]}" - ;; - zypper) - $STD zypper remove -y "${packages[@]}" - ;; - emerge) - $STD emerge --quiet --unmerge "${packages[@]}" - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get remove -y "${packages[@]}" + ;; + apk) + $STD apk del "${packages[@]}" + ;; + dnf) + $STD dnf remove -y "${packages[@]}" + ;; + yum) + $STD yum remove -y "${packages[@]}" + ;; + zypper) + $STD zypper remove -y "${packages[@]}" + ;; + emerge) + $STD emerge --quiet --unmerge "${packages[@]}" + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -329,31 +329,31 @@ pkg_remove() { # Cleans package manager cache to free space # ------------------------------------------------------------------------------ pkg_clean() { - case "$PKG_MANAGER" in - apt) - $STD apt-get autoremove -y - $STD apt-get autoclean - ;; - apk) - $STD apk cache clean - ;; - dnf) - $STD dnf clean all - $STD dnf autoremove -y - ;; - yum) - $STD yum clean all - ;; - zypper) - $STD zypper clean - ;; - emerge) - $STD emerge --quiet --depclean - ;; - *) - return 0 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get autoremove -y + $STD apt-get autoclean + ;; + apk) + $STD apk cache clean + ;; + dnf) + $STD dnf clean all + $STD dnf autoremove -y + ;; + yum) + $STD yum clean all + ;; + zypper) + $STD zypper clean + ;; + emerge) + $STD emerge --quiet --depclean + ;; + *) + return 0 + ;; + esac } # ============================================================================== @@ -366,28 +366,28 @@ pkg_clean() { # Enables a service to start at boot # ------------------------------------------------------------------------------ svc_enable() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl enable "$service" - ;; - openrc) - $STD rc-update add "$service" default - ;; - sysvinit) - if command -v update-rc.d &>/dev/null; then - $STD update-rc.d "$service" defaults - elif command -v chkconfig &>/dev/null; then - $STD chkconfig "$service" on - fi - ;; - *) - msg_warn "Unknown init system, cannot enable $service" - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl enable "$service" + ;; + openrc) + $STD rc-update add "$service" default + ;; + sysvinit) + if command -v update-rc.d &>/dev/null; then + $STD update-rc.d "$service" defaults + elif command -v chkconfig &>/dev/null; then + $STD chkconfig "$service" on + fi + ;; + *) + msg_warn "Unknown init system, cannot enable $service" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -396,27 +396,27 @@ svc_enable() { # Disables a service from starting at boot # ------------------------------------------------------------------------------ svc_disable() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl disable "$service" - ;; - openrc) - $STD rc-update del "$service" default 2>/dev/null || true - ;; - sysvinit) - if command -v update-rc.d &>/dev/null; then - $STD update-rc.d "$service" remove - elif command -v chkconfig &>/dev/null; then - $STD chkconfig "$service" off - fi - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl disable "$service" + ;; + openrc) + $STD rc-update del "$service" default 2>/dev/null || true + ;; + sysvinit) + if command -v update-rc.d &>/dev/null; then + $STD update-rc.d "$service" remove + elif command -v chkconfig &>/dev/null; then + $STD chkconfig "$service" off + fi + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -425,23 +425,23 @@ svc_disable() { # Starts a service immediately # ------------------------------------------------------------------------------ svc_start() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl start "$service" - ;; - openrc) - $STD rc-service "$service" start - ;; - sysvinit) - $STD /etc/init.d/"$service" start - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl start "$service" + ;; + openrc) + $STD rc-service "$service" start + ;; + sysvinit) + $STD /etc/init.d/"$service" start + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -450,23 +450,23 @@ svc_start() { # Stops a running service # ------------------------------------------------------------------------------ svc_stop() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl stop "$service" - ;; - openrc) - $STD rc-service "$service" stop - ;; - sysvinit) - $STD /etc/init.d/"$service" stop - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl stop "$service" + ;; + openrc) + $STD rc-service "$service" stop + ;; + sysvinit) + $STD /etc/init.d/"$service" stop + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -475,23 +475,23 @@ svc_stop() { # Restarts a service # ------------------------------------------------------------------------------ svc_restart() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl restart "$service" - ;; - openrc) - $STD rc-service "$service" restart - ;; - sysvinit) - $STD /etc/init.d/"$service" restart - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl restart "$service" + ;; + openrc) + $STD rc-service "$service" restart + ;; + sysvinit) + $STD /etc/init.d/"$service" restart + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -500,23 +500,23 @@ svc_restart() { # Gets service status (returns 0 if running) # ------------------------------------------------------------------------------ svc_status() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - systemctl is-active --quiet "$service" - ;; - openrc) - rc-service "$service" status &>/dev/null - ;; - sysvinit) - /etc/init.d/"$service" status &>/dev/null - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + systemctl is-active --quiet "$service" + ;; + openrc) + rc-service "$service" status &>/dev/null + ;; + sysvinit) + /etc/init.d/"$service" status &>/dev/null + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -525,15 +525,15 @@ svc_status() { # Reloads init system daemon configuration (for systemd) # ------------------------------------------------------------------------------ svc_reload_daemon() { - case "$INIT_SYSTEM" in - systemd) - $STD systemctl daemon-reload - ;; - *) - # Other init systems don't need this - return 0 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl daemon-reload + ;; + *) + # Other init systems don't need this + return 0 + ;; + esac } # ============================================================================== @@ -547,24 +547,24 @@ svc_reload_daemon() { # Returns: IP address string # ------------------------------------------------------------------------------ get_ip() { - local ip="" + local ip="" - # Try hostname -I first (most common) - if command -v hostname &>/dev/null; then - ip=$(hostname -I 2>/dev/null | awk '{print $1}') - fi + # Try hostname -I first (most common) + if command -v hostname &>/dev/null; then + ip=$(hostname -I 2>/dev/null | awk '{print $1}') + fi - # Fallback to ip command - if [[ -z "$ip" ]] && command -v ip &>/dev/null; then - ip=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1) - fi + # Fallback to ip command + if [[ -z "$ip" ]] && command -v ip &>/dev/null; then + ip=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1) + fi - # Fallback to ifconfig - if [[ -z "$ip" ]] && command -v ifconfig &>/dev/null; then - ip=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -1) - fi + # Fallback to ifconfig + if [[ -z "$ip" ]] && command -v ifconfig &>/dev/null; then + ip=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -1) + fi - echo "$ip" + echo "$ip" } # ------------------------------------------------------------------------------ @@ -574,25 +574,25 @@ get_ip() { # If IPV6_METHOD=disable: disables IPv6 via sysctl # ------------------------------------------------------------------------------ verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE + set_std_mode # Set STD mode based on VERBOSE - if [[ "${IPV6_METHOD:-}" == "disable" ]]; then - msg_info "Disabling IPv6 (this may affect some services)" - mkdir -p /etc/sysctl.d - cat >/etc/sysctl.d/99-disable-ipv6.conf </etc/sysctl.d/99-disable-ipv6.conf </dev/null || true + # For OpenRC, ensure sysctl runs at boot + if [[ "$INIT_SYSTEM" == "openrc" ]]; then + $STD rc-update add sysctl default 2>/dev/null || true + fi + msg_ok "Disabled IPv6" fi - msg_ok "Disabled IPv6" - fi } # ------------------------------------------------------------------------------ @@ -604,36 +604,36 @@ EOF # - Disables network wait services # ------------------------------------------------------------------------------ setting_up_container() { - msg_info "Setting up Container OS" + msg_info "Setting up Container OS" - # Wait for network - local i - for ((i = RETRY_NUM; i > 0; i--)); do - if [[ -n "$(get_ip)" ]]; then - break + # Wait for network + local i + for ((i = RETRY_NUM; i > 0; i--)); do + if [[ -n "$(get_ip)" ]]; then + break + fi + echo 1>&2 -en "${CROSS}${RD} No Network! " + sleep "$RETRY_EVERY" + done + + if [[ -z "$(get_ip)" ]]; 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 [[ -z "$(get_ip)" ]]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi + # Remove Python EXTERNALLY-MANAGED restriction (Debian 12+, Ubuntu 23.04+) + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true - # Remove Python EXTERNALLY-MANAGED restriction (Debian 12+, Ubuntu 23.04+) - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true + # Disable network wait services for faster boot + case "$INIT_SYSTEM" in + systemd) + systemctl disable -q --now systemd-networkd-wait-online.service 2>/dev/null || true + ;; + esac - # Disable network wait services for faster boot - case "$INIT_SYSTEM" in - systemd) - systemctl disable -q --now systemd-networkd-wait-online.service 2>/dev/null || true - ;; - esac - - msg_ok "Set up Container OS" - msg_ok "Network Connected: ${BL}$(get_ip)" + msg_ok "Set up Container OS" + msg_ok "Network Connected: ${BL}$(get_ip)" } # ------------------------------------------------------------------------------ @@ -643,65 +643,65 @@ setting_up_container() { # Tests connectivity to DNS servers and verifies DNS resolution # ------------------------------------------------------------------------------ network_check() { - set +e - trap - ERR - local ipv4_connected=false - local ipv6_connected=false - sleep 1 + set +e + trap - ERR + local ipv4_connected=false + local ipv6_connected=false + sleep 1 - # Check IPv4 connectivity - 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 (if ping6 exists) - if command -v ping6 &>/dev/null; then - if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null; then - msg_ok "IPv6 Internet Connected" - ipv6_connected=true + # Check IPv4 connectivity + 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 "IPv6 Internet Not Connected" + msg_error "IPv4 Internet Not Connected" fi - fi - # Prompt if both fail - 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 IPv6 connectivity (if ping6 exists) + if command -v ping6 &>/dev/null; then + if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null; then + msg_ok "IPv6 Internet Connected" + ipv6_connected=true + else + msg_error "IPv6 Internet Not Connected" + fi + fi + + # Prompt if both fail + 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 + local GIT_HOSTS=("github.com" "raw.githubusercontent.com" "git.community-scripts.org") + local GIT_STATUS="Git DNS:" + local DNS_FAILED=false + + for HOST in "${GIT_HOSTS[@]}"; do + local RESOLVEDIP + RESOLVEDIP=$(getent hosts "$HOST" 2>/dev/null | awk '{ print $1 }' | head -n1) + if [[ -z "$RESOLVEDIP" ]]; then + GIT_STATUS+=" $HOST:(${DNSFAIL:-FAIL})" + DNS_FAILED=true + else + GIT_STATUS+=" $HOST:(${DNSOK:-OK})" + fi + done + + if [[ "$DNS_FAILED" == true ]]; then + fatal "$GIT_STATUS" else - echo -e "${NETWORK}Check Network Settings" - exit 1 + msg_ok "$GIT_STATUS" fi - fi - # DNS resolution checks - local GIT_HOSTS=("github.com" "raw.githubusercontent.com" "git.community-scripts.org") - local GIT_STATUS="Git DNS:" - local DNS_FAILED=false - - for HOST in "${GIT_HOSTS[@]}"; do - local RESOLVEDIP - RESOLVEDIP=$(getent hosts "$HOST" 2>/dev/null | awk '{ print $1 }' | head -n1) - if [[ -z "$RESOLVEDIP" ]]; then - GIT_STATUS+=" $HOST:(${DNSFAIL:-FAIL})" - DNS_FAILED=true - else - GIT_STATUS+=" $HOST:(${DNSOK:-OK})" - 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 + set -e + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # ============================================================================== @@ -714,12 +714,12 @@ network_check() { # Updates container OS and sources appropriate tools.func # ------------------------------------------------------------------------------ update_os() { - msg_info "Updating Container OS" + msg_info "Updating Container OS" - # Configure APT cacher proxy if enabled (Debian/Ubuntu only) - if [[ "$PKG_MANAGER" == "apt" && "${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 + # Configure APT cacher proxy if enabled (Debian/Ubuntu only) + if [[ "$PKG_MANAGER" == "apt" && "${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" @@ -727,27 +727,27 @@ else echo -n "DIRECT" fi EOF - chmod +x /usr/local/bin/apt-proxy-detect.sh - fi + chmod +x /usr/local/bin/apt-proxy-detect.sh + fi - # Update and upgrade - pkg_update - pkg_upgrade + # Update and upgrade + pkg_update + pkg_upgrade - # Remove Python EXTERNALLY-MANAGED restriction - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true + # Remove Python EXTERNALLY-MANAGED restriction + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true - msg_ok "Updated Container OS" + msg_ok "Updated Container OS" - # Source appropriate tools.func based on OS - case "$OS_FAMILY" in - alpine) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) - ;; - *) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - ;; - esac + # Source appropriate tools.func based on OS + case "$OS_FAMILY" in + alpine) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) + ;; + *) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + ;; + esac } # ============================================================================== @@ -760,21 +760,21 @@ EOF # Configures Message of the Day and SSH settings # ------------------------------------------------------------------------------ motd_ssh() { - # Set terminal to 256-color mode - grep -qxF "export TERM='xterm-256color'" /root/.bashrc 2>/dev/null || echo "export TERM='xterm-256color'" >>/root/.bashrc + # Set terminal to 256-color mode + grep -qxF "export TERM='xterm-256color'" /root/.bashrc 2>/dev/null || echo "export TERM='xterm-256color'" >>/root/.bashrc - # Get OS information - local os_name="$OS_TYPE" - local os_version="$OS_VERSION" + # Get OS information + local os_name="$OS_TYPE" + local os_version="$OS_VERSION" - 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 '"') - 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 '"') + fi - # Create MOTD profile script - local PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - cat >"$PROFILE_FILE" <"$PROFILE_FILE" </dev/null || true + # Disable default MOTD scripts (Debian/Ubuntu) + [[ -d /etc/update-motd.d ]] && chmod -x /etc/update-motd.d/* 2>/dev/null || true - # Configure SSH root access if requested - if [[ "${SSH_ROOT:-}" == "yes" ]]; then - # Ensure SSH server is installed - if [[ ! -f /etc/ssh/sshd_config ]]; then - msg_info "Installing SSH server" - case "$PKG_MANAGER" in - apt) - pkg_install openssh-server - ;; - apk) - pkg_install openssh - rc-update add sshd default 2>/dev/null || true - ;; - dnf | yum) - pkg_install openssh-server - ;; - zypper) - pkg_install openssh - ;; - emerge) - pkg_install net-misc/openssh - ;; - esac - msg_ok "Installed SSH server" + # Configure SSH root access if requested + if [[ "${SSH_ROOT:-}" == "yes" ]]; then + # Ensure SSH server is installed + if [[ ! -f /etc/ssh/sshd_config ]]; then + msg_info "Installing SSH server" + case "$PKG_MANAGER" in + apt) + pkg_install openssh-server + ;; + apk) + pkg_install openssh + rc-update add sshd default 2>/dev/null || true + ;; + dnf | yum) + pkg_install openssh-server + ;; + zypper) + pkg_install openssh + ;; + emerge) + pkg_install net-misc/openssh + ;; + esac + msg_ok "Installed SSH server" + fi + + local sshd_config="/etc/ssh/sshd_config" + if [[ -f "$sshd_config" ]]; then + sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" + sed -i "s/PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" + + case "$INIT_SYSTEM" in + systemd) + svc_restart sshd 2>/dev/null || svc_restart ssh 2>/dev/null || true + ;; + openrc) + svc_enable sshd 2>/dev/null || true + svc_start sshd 2>/dev/null || true + ;; + *) + svc_restart sshd 2>/dev/null || true + ;; + esac + fi fi - - local sshd_config="/etc/ssh/sshd_config" - if [[ -f "$sshd_config" ]]; then - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" - sed -i "s/PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" - - case "$INIT_SYSTEM" in - systemd) - svc_restart sshd 2>/dev/null || svc_restart ssh 2>/dev/null || true - ;; - openrc) - svc_enable sshd 2>/dev/null || true - svc_start sshd 2>/dev/null || true - ;; - *) - svc_restart sshd 2>/dev/null || true - ;; - esac - fi - fi } # ============================================================================== @@ -845,82 +845,83 @@ EOF # Customizes container for passwordless login and creates update script # ------------------------------------------------------------------------------ customize() { - if [[ "${PASSWORD:-}" == "" ]]; then - msg_info "Customizing Container" + if [[ "${PASSWORD:-}" == "" ]]; then + msg_info "Customizing Container" - # Remove root password for auto-login - passwd -d root &>/dev/null || true + # Remove root password for auto-login + passwd -d root &>/dev/null || true - case "$INIT_SYSTEM" in - systemd) - # Mask services that block boot in LXC containers - # systemd-homed-firstboot.service hangs waiting for user input on Fedora - systemctl mask systemd-homed-firstboot.service &>/dev/null || true - systemctl mask systemd-homed.service &>/dev/null || true + case "$INIT_SYSTEM" in + systemd) + # Mask services that block boot in LXC containers + # systemd-homed-firstboot.service hangs waiting for user input on Fedora + systemctl mask systemd-homed-firstboot.service &>/dev/null || true + systemctl mask systemd-homed.service &>/dev/null || true - # Configure console-getty for auto-login in LXC containers - # console-getty.service is THE service that handles /dev/console in LXC - # It's present on all systemd distros but not enabled by default on Fedora/RHEL + # Configure console-getty for auto-login in LXC containers + # console-getty.service is THE service that handles /dev/console in LXC + # It's present on all systemd distros but not enabled by default on Fedora/RHEL - if [[ -f /usr/lib/systemd/system/console-getty.service ]]; then - mkdir -p /etc/systemd/system/console-getty.service.d - cat >/etc/systemd/system/console-getty.service.d/override.conf <<'EOF' + if [[ -f /usr/lib/systemd/system/console-getty.service ]]; then + mkdir -p /etc/systemd/system/console-getty.service.d + cat >/etc/systemd/system/console-getty.service.d/override.conf <<'EOF' [Service] ExecStart= -ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud 115200,38400,9600 $TERM +ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud 115200,38400,9600 - $TERM EOF - # Enable console-getty for LXC web console (required on Fedora/RHEL) - systemctl enable console-getty.service &>/dev/null || true - fi + # Enable console-getty for LXC web console (required on Fedora/RHEL) + systemctl enable console-getty.service &>/dev/null || true + fi - # Also configure container-getty@1 (Debian/Ubuntu default in LXC) - if [[ -f /usr/lib/systemd/system/container-getty@.service ]]; then - mkdir -p /etc/systemd/system/container-getty@1.service.d - cat >/etc/systemd/system/container-getty@1.service.d/override.conf <<'EOF' + # Also configure container-getty@1 (Debian/Ubuntu default in LXC) + if [[ -f /usr/lib/systemd/system/container-getty@.service ]]; then + mkdir -p /etc/systemd/system/container-getty@1.service.d + cat >/etc/systemd/system/container-getty@1.service.d/override.conf <<'EOF' [Service] ExecStart= -ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 $TERM +ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 - $TERM EOF - fi + fi - # Reload systemd and restart getty services to apply auto-login - systemctl daemon-reload - systemctl restart console-getty.service &>/dev/null || true - systemctl restart container-getty@1.service &>/dev/null || true - ;; + # Reload systemd and restart getty services to apply auto-login + systemctl daemon-reload + systemctl restart console-getty.service &>/dev/null || true + systemctl restart container-getty@1.service &>/dev/null || true + ;; - openrc) - # Alpine/Gentoo: modify inittab for auto-login - if [[ -f /etc/inittab ]]; then - sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab - fi - touch /root/.hushlogin - ;; + openrc) + # Alpine/Gentoo: modify inittab for auto-login + if [[ -f /etc/inittab ]]; then + sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab + fi + touch /root/.hushlogin + ;; - sysvinit) - # Devuan/older systems - just modify inittab, no telinit needed during install - if [[ -f /etc/inittab ]]; then - sed -i 's|^1:2345:respawn:/sbin/getty.*|1:2345:respawn:/sbin/agetty --autologin root tty1 38400 linux|' /etc/inittab - fi - ;; - esac + sysvinit) + # Devuan/older systems - modify inittab with flexible runlevel matching + if [[ -f /etc/inittab ]]; then + # Match various runlevel patterns (23, 2345, 12345, etc.) and both getty/agetty + sed -i 's|^1:[0-9]*:respawn:/sbin/a\?getty.*|1:2345:respawn:/sbin/agetty --autologin root tty1 38400 linux|' /etc/inittab + fi + ;; + esac - msg_ok "Customized Container" - fi + msg_ok "Customized Container" + fi - # Create update script - # Use var_os for OS-based containers, otherwise use app name - local update_script_name="${var_os:-$app}" - echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update + # Create update script + # Use var_os for OS-based containers, otherwise use app name + local update_script_name="${var_os:-$app}" + echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update + chmod +x /usr/bin/update - # Inject SSH authorized keys if provided - 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 + # Inject SSH authorized keys if provided + 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 } # ============================================================================== @@ -934,8 +935,8 @@ EOF # Returns: 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_tz() { - local tz="$1" - [[ -f "/usr/share/zoneinfo/$tz" ]] + local tz="$1" + [[ -f "/usr/share/zoneinfo/$tz" ]] } # ------------------------------------------------------------------------------ @@ -944,21 +945,21 @@ validate_tz() { # Sets container timezone # ------------------------------------------------------------------------------ set_timezone() { - local tz="$1" - if validate_tz "$tz"; then - ln -sf "/usr/share/zoneinfo/$tz" /etc/localtime - echo "$tz" >/etc/timezone 2>/dev/null || true + local tz="$1" + if validate_tz "$tz"; then + ln -sf "/usr/share/zoneinfo/$tz" /etc/localtime + echo "$tz" >/etc/timezone 2>/dev/null || true - # Update tzdata if available - case "$PKG_MANAGER" in - apt) - dpkg-reconfigure -f noninteractive tzdata 2>/dev/null || true - ;; - esac - msg_ok "Timezone set to $tz" - else - msg_warn "Invalid timezone: $tz" - fi + # Update tzdata if available + case "$PKG_MANAGER" in + apt) + dpkg-reconfigure -f noninteractive tzdata 2>/dev/null || true + ;; + esac + msg_ok "Timezone set to $tz" + else + msg_warn "Invalid timezone: $tz" + fi } # ------------------------------------------------------------------------------ @@ -967,9 +968,9 @@ set_timezone() { # Prints detected OS information (for debugging) # ------------------------------------------------------------------------------ os_info() { - echo "OS Type: $OS_TYPE" - echo "OS Family: $OS_FAMILY" - echo "OS Version: $OS_VERSION" - echo "Pkg Manager: $PKG_MANAGER" - echo "Init System: $INIT_SYSTEM" + echo "OS Type: $OS_TYPE" + echo "OS Family: $OS_FAMILY" + echo "OS Version: $OS_VERSION" + echo "Pkg Manager: $PKG_MANAGER" + echo "Init System: $INIT_SYSTEM" } From 938a570e7e77c14796d3af10854dea110e1362b9 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:32:52 +0100 Subject: [PATCH 112/378] small fixes --- ct/rustypaste.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 46e03cc80..527a7b6bd 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -2,7 +2,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness -# License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste APP="rustypaste" @@ -38,17 +38,17 @@ function update_script() { tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" "/opt/rustypaste/upload" msg_ok "Backup Created" - msg_info "Updating rustypaste to latest" - cd /opt/rustypaste CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" + + msg_info "Updating rustypaste" + cd /opt/rustypaste sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml $STD cargo build --locked --release - msg_ok "Updated rustypaste to latest" - + msg_ok "Updated rustypaste" + msg_info "Starting rustypaste" systemctl start rustypaste msg_ok "Started rustypaste" - msg_ok "Update Successful" fi exit From 35bb3bbbfc2ff06127a62e9004d2353ea8d7ceeb Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:33:34 +0100 Subject: [PATCH 113/378] fix url --- install/rustypaste-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index 188d5b47d..90103851e 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: GoldenSpringness -# License: MIT | https://github.com/GoldenSpringness/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From 15b42cdc9fe5bfa58662e310c2acae99d1fbf8c1 Mon Sep 17 00:00:00 2001 From: GoldenSpring Date: Wed, 24 Dec 2025 19:24:13 +0000 Subject: [PATCH 114/378] Apply suggestions from code review 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/rustypaste-install.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index 90103851e..75f65465d 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -14,13 +14,11 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential \ - ca-certificates +$STD apt install -y build-essential msg_ok "Dependencies Installed Successfully" RUST_VERSION="1.92.0" setup_rust -fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" +fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" msg_info "Setting up rustypaste" cd /opt/rustypaste From 30634376e81700994c4798a6d8a991ccf2243e3b 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, 24 Dec 2025 20:57:45 +0100 Subject: [PATCH 115/378] Update notes structure in rustypaste.json --- frontend/public/json/rustypaste.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/public/json/rustypaste.json b/frontend/public/json/rustypaste.json index 14c35cf2f..ac81889d5 100644 --- a/frontend/public/json/rustypaste.json +++ b/frontend/public/json/rustypaste.json @@ -32,6 +32,9 @@ "password": null }, "notes": [ - "When updating the script it will backup the whole project including all the uploaded files, make sure to extract it to a safe location or remove", + { + "text": "When updating the script it will backup the whole project including all the uploaded files, make sure to extract it to a safe location or remove", + "type": "info" + } ] } From f826c5698606d2d399ac18ea9c93772bd54428d2 Mon Sep 17 00:00:00 2001 From: Tobias Date: Wed, 24 Dec 2025 23:10:53 +0100 Subject: [PATCH 116/378] fix fetch_and_deploy --- tools/addon/nextcloud-exporter.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index 8f7111cab..4ddbe47b4 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -71,7 +71,7 @@ function update() { fi msg_ok "Stopped service" - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "nextcloud-exporter_*_amd64.deb" + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "/opt/nextcloud-exporter" "nextcloud-exporter_*_amd64.deb" setup_go msg_info "Starting service" @@ -93,7 +93,7 @@ function install() { read -erp "Enter URL of Nextcloud, example: (http://127.0.0.1:8080): " NEXTCLOUD_SERVER read -rsp "Enter Nextcloud auth token (press Enter to use username/password instead): " NEXTCLOUD_AUTH_TOKEN printf "\n" - + if [[ -z "$NEXTCLOUD_AUTH_TOKEN" ]]; then read -erp "Enter Nextcloud username: " NEXTCLOUD_USERNAME read -rsp "Enter Nextcloud password: " NEXTCLOUD_PASSWORD @@ -115,7 +115,7 @@ function install() { NEXTCLOUD_TLS_SKIP_VERIFY="true" fi - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "nextcloud-exporter_*_amd64.deb" + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "/opt/nextcloud-exporter" "nextcloud-exporter_*_amd64.deb" setup_go msg_info "Creating configuration" From 6d88b971a3a42b276ef4b0c22f54605b9f34ff27 Mon Sep 17 00:00:00 2001 From: Tobias Date: Wed, 24 Dec 2025 23:31:10 +0100 Subject: [PATCH 117/378] refactor locations --- frontend/public/json/nextcloud-exporter.json | 4 +-- tools/addon/nextcloud-exporter.sh | 27 ++++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/frontend/public/json/nextcloud-exporter.json b/frontend/public/json/nextcloud-exporter.json index ed242739f..5a34c0dd0 100644 --- a/frontend/public/json/nextcloud-exporter.json +++ b/frontend/public/json/nextcloud-exporter.json @@ -8,11 +8,11 @@ "type": "addon", "updateable": true, "privileged": false, - "interface_port": 9025, + "interface_port": 9205, "documentation": "https://github.com/xperimental/nextcloud-exporter", "website": "https://github.com/xperimental/nextcloud-exporter", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nextcloud.webp", - "config_path": "/opt/nextcloud-exporter.env", + "config_path": "/etc/nextcloud-exporter.env", "description": "Prometheus exporter for Nextcloud servers. ", "install_methods": [ { diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index 4ddbe47b4..ca7b7d1b1 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -20,8 +20,8 @@ load_functions VERBOSE=${var_verbose:-no} APP="nextcloud-exporter" APP_TYPE="tools" -INSTALL_PATH="/opt/nextcloud-exporter" -CONFIG_PATH="/opt/nextcloud-exporter.env" +BINARY_PATH="/usr/bin/nextcloud-exporter" +CONFIG_PATH="/etc/nextcloud-exporter.env" header_info ensure_usr_local_bin_persist @@ -52,7 +52,12 @@ function uninstall() { systemctl disable -q --now nextcloud-exporter rm -f "$SERVICE_PATH" fi - rm -rf "$INSTALL_PATH" "$CONFIG_PATH" + + if dpkg -l | grep -q nextcloud-exporter; then + $STD apt-get remove -y nextcloud-exporter || $STD dpkg -r nextcloud-exporter + fi + + rm -f "$CONFIG_PATH" rm -f "/usr/local/bin/update_nextcloud-exporter" rm -f "$HOME/.nextcloud-exporter" msg_ok "Nextcloud-Exporter has been uninstalled" @@ -71,8 +76,7 @@ function update() { fi msg_ok "Stopped service" - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "/opt/nextcloud-exporter" "nextcloud-exporter_*_amd64.deb" - setup_go + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "binary" "latest" msg_info "Starting service" if [[ "$OS" == "Alpine" ]]; then @@ -115,8 +119,7 @@ function install() { NEXTCLOUD_TLS_SKIP_VERIFY="true" fi - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "prebuild" "latest" "/opt/nextcloud-exporter" "nextcloud-exporter_*_amd64.deb" - setup_go + fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "binary" "latest" msg_info "Creating configuration" cat <"$CONFIG_PATH" @@ -141,9 +144,8 @@ After=network.target [Service] User=root -WorkingDirectory=/opt/nextcloud-exporter EnvironmentFile=$CONFIG_PATH -ExecStart=/opt/nextcloud-exporter/nextcloud-exporter +ExecStart=$BINARY_PATH Restart=always [Install] @@ -157,9 +159,8 @@ EOF name="nextcloud-exporter" description="Nextcloud Exporter for Prometheus" -command="${INSTALL_PATH}/nextcloud-exporter" +command="$BINARY_PATH" command_background=true -directory="/opt/nextcloud-exporter" pidfile="/run/\${RC_SVCNAME}.pid" output_log="/var/log/nextcloud-exporter.log" error_log="/var/log/nextcloud-exporter.log" @@ -206,7 +207,7 @@ ensure_usr_local_bin_persist # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then - if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/nextcloud-exporter" ]]; then + if [[ -f "$BINARY_PATH" ]]; then update else msg_error "Nextcloud-Exporter is not installed. Nothing to update." @@ -216,7 +217,7 @@ if [[ "${type:-}" == "update" ]]; then fi # Check if already installed -if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/nextcloud-exporter" ]]; then +if [[ -f "$BINARY_PATH" ]]; then msg_warn "Nextcloud-Exporter is already installed." echo "" From 14fdaa1632db51fc70389b221402a630323ae769 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 25 Dec 2025 00:08:03 +0100 Subject: [PATCH 118/378] fix env file --- tools/addon/nextcloud-exporter.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index ca7b7d1b1..13a99aee1 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -125,9 +125,9 @@ function install() { cat <"$CONFIG_PATH" # https://github.com/xperimental/nextcloud-exporter NEXTCLOUD_SERVER="${NEXTCLOUD_SERVER}" -NEXTCLOUD_AUTH_TOKEN="${NEXTCLOUD_AUTH_TOKEN}" -NEXTCLOUD_USERNAME="${NEXTCLOUD_USERNAME}" -NEXTCLOUD_PASSWORD="${NEXTCLOUD_PASSWORD}" +NEXTCLOUD_AUTH_TOKEN="${NEXTCLOUD_AUTH_TOKEN:-}" +NEXTCLOUD_USERNAME="${NEXTCLOUD_USERNAME:-}" +NEXTCLOUD_PASSWORD="${NEXTCLOUD_PASSWORD:-}" NEXTCLOUD_INFO_UPDATE=${NEXTCLOUD_INFO_UPDATE:-"true"} NEXTCLOUD_INFO_APPS=${NEXTCLOUD_INFO_APPS:-"true"} NEXTCLOUD_TLS_SKIP_VERIFY=${NEXTCLOUD_TLS_SKIP_VERIFY:-"false"} From 039481f650c5dcf40adbeab6d4dd7868209f24e8 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 25 Dec 2025 00:16:19 +0100 Subject: [PATCH 119/378] fix --- tools/addon/nextcloud-exporter.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index 13a99aee1..3de9105cf 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -22,8 +22,6 @@ APP="nextcloud-exporter" APP_TYPE="tools" BINARY_PATH="/usr/bin/nextcloud-exporter" CONFIG_PATH="/etc/nextcloud-exporter.env" -header_info -ensure_usr_local_bin_persist # ============================================================================== # OS DETECTION @@ -195,7 +193,7 @@ UPDATEEOF echo "" msg_ok "Nextcloud-Exporter installed successfully" - msg_ok "Metrics: ${BL}http://${CURRENT_IP}:9205/metrics${CL}" + msg_ok "Metrics: ${BL}http://${LOCAL_IP}:9205/metrics${CL}" msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" } From 39b0aa16120a9ab1f95528d8027316791f598868 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 25 Dec 2025 00:19:01 +0100 Subject: [PATCH 120/378] fix --- tools/addon/nextcloud-exporter.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index 3de9105cf..1097c26e9 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -202,6 +202,7 @@ UPDATEEOF # ============================================================================== header_info ensure_usr_local_bin_persist +import_local_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then From a7d97b15e7976dae9396541e8325d2a62385e537 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 25 Dec 2025 01:09:39 +0100 Subject: [PATCH 121/378] remove alpine --- frontend/public/json/nextcloud-exporter.json | 11 --- tools/addon/nextcloud-exporter.sh | 73 +++----------------- 2 files changed, 9 insertions(+), 75 deletions(-) diff --git a/frontend/public/json/nextcloud-exporter.json b/frontend/public/json/nextcloud-exporter.json index 5a34c0dd0..7866fc152 100644 --- a/frontend/public/json/nextcloud-exporter.json +++ b/frontend/public/json/nextcloud-exporter.json @@ -25,17 +25,6 @@ "os": null, "version": null } - }, - { - "type": "alpine", - "script": "tools/addon/nextcloud-exporter.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } } ], "default_credentials": { diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index 1097c26e9..ae65b9d7e 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -22,34 +22,15 @@ APP="nextcloud-exporter" APP_TYPE="tools" BINARY_PATH="/usr/bin/nextcloud-exporter" CONFIG_PATH="/etc/nextcloud-exporter.env" - -# ============================================================================== -# OS DETECTION -# ============================================================================== -if [[ -f "/etc/alpine-release" ]]; then - OS="Alpine" - SERVICE_PATH="/etc/init.d/nextcloud-exporter" -elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - OS="Debian" - SERVICE_PATH="/etc/systemd/system/nextcloud-exporter.service" -else - echo -e "${CROSS} Unsupported OS detected. Exiting." - exit 1 -fi +SERVICE_PATH="/etc/systemd/system/nextcloud-exporter.service" # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling Nextcloud-Exporter" - if [[ "$OS" == "Alpine" ]]; then - rc-service nextcloud-exporter stop &>/dev/null - rc-update del nextcloud-exporter &>/dev/null - rm -f "$SERVICE_PATH" - else - systemctl disable -q --now nextcloud-exporter - rm -f "$SERVICE_PATH" - fi + systemctl disable -q --now nextcloud-exporter + rm -f "$SERVICE_PATH" if dpkg -l | grep -q nextcloud-exporter; then $STD apt-get remove -y nextcloud-exporter || $STD dpkg -r nextcloud-exporter @@ -67,21 +48,13 @@ function uninstall() { function update() { if check_for_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter"; then msg_info "Stopping service" - if [[ "$OS" == "Alpine" ]]; then - rc-service nextcloud-exporter stop &>/dev/null - else - systemctl stop nextcloud-exporter - fi + systemctl stop nextcloud-exporter msg_ok "Stopped service" fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "binary" "latest" msg_info "Starting service" - if [[ "$OS" == "Alpine" ]]; then - rc-service nextcloud-exporter start &>/dev/null - else - systemctl start nextcloud-exporter - fi + systemctl start nextcloud-exporter msg_ok "Started service" msg_ok "Updated successfully" exit @@ -134,8 +107,7 @@ EOF msg_ok "Created configuration" msg_info "Creating service" - if [[ "$OS" == "Debian" ]]; then - cat <"$SERVICE_PATH" + cat <"$SERVICE_PATH" [Unit] Description=nextcloud-exporter After=network.target @@ -149,35 +121,8 @@ Restart=always [Install] WantedBy=multi-user.target EOF - systemctl daemon-reload - systemctl enable -q --now nextcloud-exporter - else - cat <"$SERVICE_PATH" -#!/sbin/openrc-run - -name="nextcloud-exporter" -description="Nextcloud Exporter for Prometheus" -command="$BINARY_PATH" -command_background=true -pidfile="/run/\${RC_SVCNAME}.pid" -output_log="/var/log/nextcloud-exporter.log" -error_log="/var/log/nextcloud-exporter.log" - -depend() { - need net - after firewall -} - -start_pre() { - if [ -f "$CONFIG_PATH" ]; then - export \$(grep -v '^#' $CONFIG_PATH | xargs) - fi -} -EOF - chmod +x "$SERVICE_PATH" - $STD rc-update add nextcloud-exporter default - $STD rc-service nextcloud-exporter start - fi + systemctl daemon-reload + systemctl enable -q --now nextcloud-exporter msg_ok "Created and started service" # Create update script @@ -243,7 +188,7 @@ msg_warn "Nextcloud-Exporter is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Nextcloud Exporter (Go binary)" -echo -e "${TAB} - Systemd/OpenRC service" +echo -e "${TAB} - Systemd service" echo "" echo -n "${TAB}Install Nextcloud-Exporter? (y/N): " From 9ee22f9af97840a0c55cc6fa52698ccfbefb0c21 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 25 Dec 2025 01:11:32 +0100 Subject: [PATCH 122/378] os check --- tools/addon/nextcloud-exporter.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh index ae65b9d7e..31762d591 100644 --- a/tools/addon/nextcloud-exporter.sh +++ b/tools/addon/nextcloud-exporter.sh @@ -24,6 +24,14 @@ BINARY_PATH="/usr/bin/nextcloud-exporter" CONFIG_PATH="/etc/nextcloud-exporter.env" SERVICE_PATH="/etc/systemd/system/nextcloud-exporter.service" +# ============================================================================== +# OS DETECTION +# ============================================================================== +if ! grep -qE 'ID=debian|ID=ubuntu' /etc/os-release 2>/dev/null; then + echo -e "${CROSS} Unsupported OS detected. This script only supports Debian and Ubuntu." + exit 1 +fi + # ============================================================================== # UNINSTALL # ============================================================================== From a1042482447e910db6f808e78b30789a02913a10 Mon Sep 17 00:00:00 2001 From: AlphaLawless Date: Thu, 25 Dec 2025 22:28:50 -0300 Subject: [PATCH 123/378] update romm script - working now --- ct/romm.sh | 67 +++++---- install/romm-install.sh | 320 ++++++++++++++++++++++++++++------------ misc/tools.func | 61 +++++--- 3 files changed, 304 insertions(+), 144 deletions(-) diff --git a/ct/romm.sh b/ct/romm.sh index 129da9a34..39e0cbc7c 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -2,7 +2,8 @@ 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 +# Co-author: AlphaLawless +# License: MIT | https://github.com/AlphaLawless/ProxmoxVED/raw/main/LICENSE # Source: https://romm.app APP="RomM" @@ -10,10 +11,9 @@ 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_os="${var_os:-debian}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" -var_fuse="${var_fuse:-1}" header_info "$APP" variables @@ -30,35 +30,44 @@ function update_script() { exit fi - msg_info "Stopping $APP" - systemctl stop romm - systemctl stop nginx - msg_ok "Stopped $APP" + if check_for_gh_release "romm" "rommapp/romm"; then + msg_info "Stopping ${APP} services" + systemctl stop romm-backend romm-worker romm-scheduler romm-watcher + msg_ok "Stopped ${APP} services" - msg_info "Updating $APP" - cd /opt/romm/app - git pull + msg_info "Backing up configuration" + cp /opt/romm/.env /opt/romm/.env.backup + msg_ok "Backed up configuration" - # Update backend - cd /opt/romm/app - source /opt/romm/venv/bin/activate - pip install --upgrade pip - pip install poetry - poetry install + msg_info "Updating ${APP}" + fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm" - # Update frontend - cd /opt/romm/app/frontend - npm install - npm run build + cp /opt/romm/.env.backup /opt/romm/.env - echo "Updated on $(date)" >/opt/romm/version.txt - msg_ok "Updated $APP" + cd /opt/romm + $STD uv sync --all-extras - msg_info "Starting $APP" - systemctl start romm - systemctl start nginx - msg_ok "Started $APP" - msg_ok "Update Successful" + cd /opt/romm/backend + $STD uv run alembic upgrade head + + cd /opt/romm/frontend + $STD npm install + $STD npm run build + + # Merge static assets into dist folder + cp -rf /opt/romm/frontend/assets/* /opt/romm/frontend/dist/assets/ + + mkdir -p /opt/romm/frontend/dist/assets/romm + ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources + ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets + msg_ok "Updated ${APP}" + + msg_info "Starting ${APP} services" + systemctl start romm-backend romm-worker romm-scheduler romm-watcher + msg_ok "Started ${APP} services" + + msg_ok "Update Successful" + fi exit } @@ -69,4 +78,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}:8080${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/romm-install.sh b/install/romm-install.sh index 438e4e5a7..8027ab3a6 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -2,9 +2,10 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: DevelopmentCats -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Co-author: AlphaLawless +# License: MIT | https://github.com/AlphaLawless/ProxmoxVED/raw/main/LICENSE # Source: https://romm.app -# Updated: 03/10/2025 +# Updated: 25/12/2025 source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -18,65 +19,126 @@ msg_info "Installing dependencies" $STD apt-get install -y \ acl \ build-essential \ + gcc \ + g++ \ + make \ + git \ + curl \ libssl-dev \ libffi-dev \ + libmagic-dev \ python3-dev \ python3-pip \ python3-venv \ libmariadb3 \ libmariadb-dev \ libpq-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + zlib1g-dev \ + liblzma-dev \ + libncurses5-dev \ + libncursesw5-dev \ + redis-server \ redis-tools \ - p7zip \ + p7zip-full \ tzdata \ - jq -msg_ok "Installed core dependencies" + jq \ + nginx +msg_ok "Installed dependencies" -PYTHON_VERSION="3.12" setup_uv -NODE_VERSION="22" NODE_MODULE="serve" setup_nodejs +UV_VERSION="0.7.19" PYTHON_VERSION="3.13" setup_uv +NODE_VERSION="22" setup_nodejs setup_mariadb +MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db -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 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" - 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 +msg_info "Creating directories" 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" + /var/lib/romm/library/roms \ + /var/lib/romm/library/bios +msg_ok "Created 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" +msg_info "Creating configuration file" +cat >/var/lib/romm/config/config.yml <<'CONFIGEOF' +# RomM Configuration File +# Documentation: https://docs.romm.app/latest/Getting-Started/Configuration-File/ +# Only uncomment the lines you want to use/modify + +# exclude: +# platforms: +# - excluded_folder_a +# roms: +# single_file: +# extensions: +# - xml +# - txt +# names: +# - '._*' +# - '*.nfo' +# multi_file: +# names: +# - downloaded_media +# - media + +# system: +# platforms: +# gc: ngc +# ps1: psx + +# The folder name where your roms are located (relative to library path) +# filesystem: +# roms_folder: 'roms' + +# scan: +# priority: +# metadata: +# - "igdb" +# - "moby" +# - "ss" +# - "ra" +# artwork: +# - "igdb" +# - "moby" +# - "ss" +# region: +# - "us" +# - "eu" +# - "jp" +# language: +# - "en" +# media: +# - box2d +# - box3d +# - screenshot +# - manual + +# emulatorjs: +# debug: false +# cache_limit: null +CONFIGEOF +chmod 644 /var/lib/romm/config/config.yml +msg_ok "Created configuration file" + +msg_info "Building RAHasher (RetroAchievements)" +RAHASHER_VERSION="1.8.1" +cd /tmp +git clone --recursive --branch "$RAHASHER_VERSION" --depth 1 https://github.com/RetroAchievements/RALibretro.git +cd RALibretro +sed -i '22a #include ' ./src/Util.h +sed -i '6a #include ' \ + ./src/libchdr/deps/zlib-1.3.1/gzlib.c \ + ./src/libchdr/deps/zlib-1.3.1/gzread.c \ + ./src/libchdr/deps/zlib-1.3.1/gzwrite.c +$STD make HAVE_CHD=1 -f ./Makefile.RAHasher +cp ./bin64/RAHasher /usr/bin/RAHasher +chmod +x /usr/bin/RAHasher +cd /tmp +rm -rf /tmp/RALibretro +msg_ok "Built RAHasher" fetch_and_deploy_gh_release "romm" "rommapp/romm" @@ -88,13 +150,14 @@ AUTH_SECRET_KEY=$(openssl rand -hex 32) cat >/opt/romm/.env </etc/nginx/sites-available/romm <<'EOF' +upstream romm_backend { + server 127.0.0.1:5000; +} +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 80; + server_name _; + root /opt/romm/frontend/dist; + client_max_body_size 0; + + # Frontend SPA + location / { + try_files $uri $uri/ /index.html; + } + + # EmulatorJS player - requires COOP/COEP headers for SharedArrayBuffer + location ~ ^/rom/.*/ejs$ { + add_header Cross-Origin-Embedder-Policy "require-corp"; + add_header Cross-Origin-Opener-Policy "same-origin"; + try_files $uri /index.html; + } + + # Backend API + location /api { + proxy_pass http://romm_backend; + proxy_buffering off; + proxy_request_buffering off; + 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; + } + + # WebSocket and Netplay + location ~ ^/(ws|netplay) { + proxy_pass http://romm_backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_read_timeout 86400; + } + + # OpenAPI docs + location = /openapi.json { + proxy_pass http://romm_backend; + } + + # Internal library file serving + location /library/ { + internal; + alias /var/lib/romm/library/; + } +} +EOF + +rm -f /etc/nginx/sites-enabled/default +ln -sf /etc/nginx/sites-available/romm /etc/nginx/sites-enabled/romm +$STD nginx -t +systemctl restart nginx +systemctl enable -q nginx +msg_ok "Configured nginx" + +msg_info "Creating services" cat >/etc/systemd/system/romm-backend.service </etc/systemd/system/romm-frontend.service </etc/systemd/system/romm-worker.service </etc/systemd/system/romm-scheduler.service </etc/systemd/system/romm-watcher.service </dev/null || echo "") + # Check if specific version is requested via UV_VERSION environment variable + if [[ -n "${UV_VERSION:-}" ]]; then + TARGET_VERSION="${UV_VERSION}" + USE_PINNED_VERSION=true + else + # Fetch latest version from GitHub API + 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" - return 1 - fi + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest uv version from GitHub API" + return 1 + fi - local LATEST_VERSION - LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//') + TARGET_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" - return 1 + if [[ -z "$TARGET_VERSION" ]]; then + msg_error "Could not parse uv version from GitHub API response" + return 1 + fi fi # Get currently installed version @@ -4734,9 +4741,9 @@ function setup_uv() { 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" + # Scenario 1: Already at target version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$TARGET_VERSION" ]]; then + cache_installed_version "uv" "$TARGET_VERSION" # Check if uvx is needed and missing if [[ "${USE_UVX:-NO}" == "YES" ]] && [[ ! -x "$UVX_BIN" ]]; then @@ -4748,14 +4755,22 @@ function setup_uv() { 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" + # Scenario 2: New install or upgrade/downgrade + if [[ -n "$INSTALLED_VERSION" ]]; then + if [[ "$USE_PINNED_VERSION" == true ]]; then + msg_info "Switching uv from $INSTALLED_VERSION to pinned version $TARGET_VERSION" + else + msg_info "Upgrade uv from $INSTALLED_VERSION to $TARGET_VERSION" + fi else - msg_info "Setup uv $LATEST_VERSION" + if [[ "$USE_PINNED_VERSION" == true ]]; then + msg_info "Setup uv $TARGET_VERSION (pinned)" + else + msg_info "Setup uv $TARGET_VERSION" + fi fi - local UV_URL="https://github.com/astral-sh/uv/releases/download/${LATEST_VERSION}/${UV_TAR}" + local UV_URL="https://github.com/astral-sh/uv/releases/download/${TARGET_VERSION}/${UV_TAR}" $STD curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { msg_error "Failed to download uv from $UV_URL" @@ -4809,8 +4824,8 @@ function setup_uv() { msg_ok "Python $PYTHON_VERSION installed" fi - cache_installed_version "uv" "$LATEST_VERSION" - msg_ok "Setup uv $LATEST_VERSION" + cache_installed_version "uv" "$TARGET_VERSION" + msg_ok "Setup uv $TARGET_VERSION" } # Helper function to install uvx wrapper From 143477a2e352fb21ef60da6ac8ddd2c4eab19539 Mon Sep 17 00:00:00 2001 From: AlphaLawless Date: Thu, 25 Dec 2025 22:30:21 -0300 Subject: [PATCH 124/378] update romm json --- frontend/public/json/romm.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/public/json/romm.json b/frontend/public/json/romm.json index 0e858bdb1..02a5b199a 100644 --- a/frontend/public/json/romm.json +++ b/frontend/public/json/romm.json @@ -8,10 +8,10 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 8080, + "interface_port": 80, "documentation": "https://docs.romm.app/latest/", "website": "https://romm.app/", - "config_path": "/opt", + "config_path": "/opt/romm/.env", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/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": [ @@ -22,14 +22,14 @@ "cpu": 2, "ram": 4096, "hdd": 20, - "os": "ubuntu", - "version": "24.04" + "os": "debian", + "version": "13" } } ], "default_credentials": { - "username": "romm", - "password": "changeme" + "username": null, + "password": null }, "notes": [] } From ca16788d08fb315b75371a95eaabc727dca11511 Mon Sep 17 00:00:00 2001 From: AlphaLawless Date: Thu, 25 Dec 2025 22:46:25 -0300 Subject: [PATCH 125/378] remove my username from license --- ct/romm.sh | 2 +- install/romm-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/romm.sh b/ct/romm.sh index 39e0cbc7c..f6d4119ca 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -3,7 +3,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # Co-author: AlphaLawless -# License: MIT | https://github.com/AlphaLawless/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://romm.app APP="RomM" diff --git a/install/romm-install.sh b/install/romm-install.sh index 8027ab3a6..2f88f52cb 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -3,7 +3,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: DevelopmentCats # Co-author: AlphaLawless -# License: MIT | https://github.com/AlphaLawless/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://romm.app # Updated: 25/12/2025 From 4fc114e30cb333aa3068db6d47307f532e84f7db Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 26 Dec 2025 10:02:06 +0100 Subject: [PATCH 126/378] delete openwebui install test --- install/deferred/openwebui-install.sh | 107 -------------------------- 1 file changed, 107 deletions(-) delete mode 100644 install/deferred/openwebui-install.sh diff --git a/install/deferred/openwebui-install.sh b/install/deferred/openwebui-install.sh deleted file mode 100644 index 0e9384d8f..000000000 --- a/install/deferred/openwebui-install.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck -# Co-Author: havardthom -# 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-get install -y \ - ffmpeg -msg_ok "Installed Dependencies" - -msg_info "Setup Python3" -$STD apt-get install -y --no-install-recommends \ - python3 \ - python3-pip -msg_ok "Setup Python3" - -setup_nodejs - -msg_info "Installing Open WebUI (Patience)" -fetch_and_deploy_gh_release "open-webui/open-webui" -cd /opt/openwebui/backend -$STD pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu -$STD pip3 install -r requirements.txt -U -cd /opt/openwebui -cat </opt/openwebui/.env -# Ollama URL for the backend to connect -# The path '/ollama' will be redirected to the specified backend URL -OLLAMA_BASE_URL=http://0.0.0.0:11434 -OPENAI_API_BASE_URL='' -OPENAI_API_KEY='' -# AUTOMATIC1111_BASE_URL="http://localhost:7860" -# DO NOT TRACK -SCARF_NO_ANALYTICS=true -DO_NOT_TRACK=true -ANONYMIZED_TELEMETRY=false -ENV=prod -ENABLE_OLLAMA_API=false -EOF -$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 -msg_ok "Installed Open WebUI" - -read -r -p "${TAB3}Would you like to add Ollama? " prompt -if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Ollama" - curl -fsSLO 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 - sed -i 's/ENABLE_OLLAMA_API=false/ENABLE_OLLAMA_API=true/g' /opt/openwebui/.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=exec -WorkingDirectory=/opt/openwebui -EnvironmentFile=/opt/openwebui/.env -ExecStart=/opt/openwebui/backend/start.sh - -[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-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From d56d63deaa6381014d544feebb9f1865a79c9e57 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 26 Dec 2025 11:03:02 +0100 Subject: [PATCH 127/378] add Sportarr script --- install/sportarr-install.sh | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 install/sportarr-install.sh diff --git a/install/sportarr-install.sh b/install/sportarr-install.sh new file mode 100644 index 000000000..5ba324bbd --- /dev/null +++ b/install/sportarr-install.sh @@ -0,0 +1,48 @@ +#!/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/Sportarr/Sportarr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" + +msg_info "Setting up Sportarr" +cat </opt/sportarr/.env +Sportarr__DataPath="/config" +ASPNETCORE_URLS="http://*:1867" +ASPNETCORE_ENVIRONMENT="Production" +DOTNET_CLI_TELEMETRY_OPTOUT=1 +DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false +EOF +msg_ok "Setup Sportarr" + +msg_info "Creating Service" +cat </etc/systemd/system/sportarr.service +[Unit] +Description=Sportarr Service +After=network.target + +[Service] +EnvironmentFile=/opt/sportarr/.env +WorkingDirectory=/opt/sportarr +ExecStart=/opt/sportarr/Sportarr +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now sportarr +msg_info "Created Service" + +motd_ssh +customize +cleanup_lxc From ec57af4a13b64e05d8544b0b66816bf4af9c48d4 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 26 Dec 2025 11:10:47 +0100 Subject: [PATCH 128/378] add Sportarr script --- install/sportarr-install.sh | 2 +- install/sportarr.sh | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 install/sportarr.sh diff --git a/install/sportarr-install.sh b/install/sportarr-install.sh index 5ba324bbd..fa2b5a9ef 100644 --- a/install/sportarr-install.sh +++ b/install/sportarr-install.sh @@ -17,7 +17,7 @@ fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" " msg_info "Setting up Sportarr" cat </opt/sportarr/.env -Sportarr__DataPath="/config" +Sportarr__DataPath="/opt/sportarr/config" ASPNETCORE_URLS="http://*:1867" ASPNETCORE_ENVIRONMENT="Production" DOTNET_CLI_TELEMETRY_OPTOUT=1 diff --git a/install/sportarr.sh b/install/sportarr.sh new file mode 100644 index 000000000..6cbc76d83 --- /dev/null +++ b/install/sportarr.sh @@ -0,0 +1,65 @@ +#!/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/Sportarr/Sportarr + +APP="Sportarr" +var_tags="${var_tags:-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/sportarr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "sportarr" "Sportarr/Sportarr"; then + msg_info "Stopping Sportarr Service" + systemctl stop sportarr + msg_ok "Stopped Sportarr Service" + + msg_info "Creating Backup" + + msg_ok "Created Backup" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" + + msg_info "Updating sportarr" + + msg_ok "Updated sportarr" + + msg_info "Restoring Backup" + + msg_ok "Restored Backup" + + msg_info "Starting Sportarr Service" + systemctl start sportarr + msg_ok "Started Sportarr 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}:1867${CL}" From 3417282c370493b41d637c3883a043b5f145c4aa Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 26 Dec 2025 11:12:31 +0100 Subject: [PATCH 129/378] add Sportarr script --- {install => ct}/sportarr.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {install => ct}/sportarr.sh (100%) diff --git a/install/sportarr.sh b/ct/sportarr.sh similarity index 100% rename from install/sportarr.sh rename to ct/sportarr.sh From 18f23497b440b032814a753510a826b75b4a4f42 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Fri, 26 Dec 2025 14:01:14 +0100 Subject: [PATCH 130/378] add tor-snowflake script --- ct/tor-snowflake.sh | 78 +++++++++++++++++++++++++ frontend/public/json/tor-snowflake.json | 35 +++++++++++ install/tor-snowflake-install.sh | 69 ++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 ct/tor-snowflake.sh create mode 100644 frontend/public/json/tor-snowflake.json create mode 100644 install/tor-snowflake-install.sh diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh new file mode 100644 index 000000000..004cc5547 --- /dev/null +++ b/ct/tor-snowflake.sh @@ -0,0 +1,78 @@ +#!/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: KernelSailor +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://snowflake.torproject.org/ + +APP="tor-snowflake" +var_tags="privacy;proxy;tor" +var_cpu="1" +var_ram="512" +var_disk="4" +var_os="debian" +var_version="13" +var_unprivileged="1" +var_nesting="0" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/snowflake ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if ! id snowflake &>/dev/null; then + msg_error "snowflake user not found!" + exit + fi + + msg_info "Updating Container OS" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated Container OS" + + RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Stopping Service" + systemctl stop snowflake-proxy + msg_ok "Stopped Service" + + msg_info "Updating Go" + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) + setup_go + msg_ok "Updated Go" + + msg_info "Updating ${APP} to v${RELEASE}" + cd /opt/snowflake || exit + $STD git fetch --all + $STD git checkout "v${RELEASE}" + chown -R snowflake:snowflake /opt/snowflake + $STD sudo -u snowflake go build -o proxy ./proxy + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated ${APP} to v${RELEASE}" + + msg_info "Starting Service" + systemctl start snowflake-proxy + msg_ok "Started Service" + 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}Tor Snowflake setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Snowflake proxy is running as a systemd service.${CL}" +echo -e "${INFO}${YW} Check status: systemctl status snowflake-proxy${CL}" diff --git a/frontend/public/json/tor-snowflake.json b/frontend/public/json/tor-snowflake.json new file mode 100644 index 000000000..00ed299f9 --- /dev/null +++ b/frontend/public/json/tor-snowflake.json @@ -0,0 +1,35 @@ +{ + "name": "Tor Snowflake", + "slug": "tor-snowflake", + "categories": [ + 4 + ], + "date_created": "2025-12-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://community.torproject.org/relay/setup/snowflake/standalone/", + "website": "https://snowflake.torproject.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tor.webp", + "config_path": null, + "description": "Snowflake is a pluggable transport that proxies traffic through temporary proxies using WebRTC. Snowflake allows users in censored locations to access the open internet by connecting through volunteer-run proxies. Running a Snowflake proxy helps users circumvent internet censorship by forwarding their traffic through your server.", + "install_methods": [ + { + "type": "default", + "script": "ct/tor-snowflake.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh new file mode 100644 index 000000000..f9a1a56f2 --- /dev/null +++ b/install/tor-snowflake-install.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: KernelSailor +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://snowflake.torproject.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 \ + curl \ + git \ + jq +msg_ok "Installed Dependencies" + +setup_go + +msg_info "Creating snowflake user" +useradd -r -s /bin/false -d /opt/snowflake snowflake +msg_ok "Created snowflake user" + +msg_info "Building Snowflake Proxy from Source" +cd /opt || exit +RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') +$STD git clone https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git +cd snowflake || exit +git config --global --add safe.directory /opt/snowflake +$STD git checkout "v${RELEASE}" +chown -R snowflake:snowflake /opt/snowflake +cd proxy || exit +$STD sudo -u snowflake go build -o snowflake-proxy . +cd /opt/snowflake || exit +echo "${RELEASE}" >/opt/tor-snowflake_version.txt +msg_ok "Built Snowflake Proxy v${RELEASE}" + +msg_info "Creating Service" +cat </etc/systemd/system/snowflake-proxy.service +[Unit] +Description=Snowflake Proxy Service +Documentation=https://snowflake.torproject.org/ +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=snowflake +Group=snowflake +WorkingDirectory=/opt/snowflake +ExecStart=/opt/snowflake/proxy/snowflake-proxy -verbose -unsafe-logging +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now snowflake-proxy +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From cf80bb6e5fca26751cba9445fb04eedd0304a33a Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 00:22:24 +0700 Subject: [PATCH 131/378] add fladder --- ct/fladder.sh | 86 +++++++++++++++++++++++++++++++ frontend/public/json/fladder.json | 35 +++++++++++++ install/fladder-install.sh | 59 +++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 ct/fladder.sh create mode 100644 frontend/public/json/fladder.json create mode 100644 install/fladder-install.sh diff --git a/ct/fladder.sh b/ct/fladder.sh new file mode 100644 index 000000000..5eb2d3a51 --- /dev/null +++ b/ct/fladder.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/wendyliga/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: wendyliga +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/DonutWare/Fladder + +APP="Fladder" +var_tags="${var_tags:-media}" +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/fladder ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + # Get latest version from GitHub + RELEASE=$(curl -fsSL https://api.github.com/repos/DonutWare/Fladder/releases/latest | \ + grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') + if [[ -z "$RELEASE" ]]; then + msg_error "Failed to fetch latest release version from GitHub" + exit 1 + fi + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Stopping Service" + systemctl stop nginx + msg_ok "Stopped Service" + + msg_info "Backing up configuration" + if [[ -f /opt/fladder/assets/config/config.json ]]; then + cp /opt/fladder/assets/config/config.json /tmp/fladder_config.json.bak + msg_ok "Configuration backed up" + elif [[ -f /opt/fladder/data/flutter_assets/config/config.json ]]; then + cp /opt/fladder/data/flutter_assets/config/config.json /tmp/fladder_config.json.bak + msg_ok "Configuration backed up" + fi + + msg_info "Updating ${APP} to ${RELEASE}" + cd /opt + wget -q "https://github.com/DonutWare/Fladder/releases/download/${RELEASE}/Fladder-Web-${RELEASE#v}.zip" + rm -rf /opt/fladder + unzip -q "Fladder-Web-${RELEASE#v}.zip" -d fladder + rm -f "Fladder-Web-${RELEASE#v}.zip" + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated ${APP} to ${RELEASE}" + + msg_info "Restoring configuration" + if [[ -f /tmp/fladder_config.json.bak ]]; then + mkdir -p /opt/fladder/assets/config + cp /tmp/fladder_config.json.bak /opt/fladder/assets/config/config.json + rm -f /tmp/fladder_config.json.bak + msg_ok "Configuration restored" + fi + + msg_info "Starting Service" + systemctl start nginx + msg_ok "Started Service" + 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 IP:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/fladder.json b/frontend/public/json/fladder.json new file mode 100644 index 000000000..99a8ffd81 --- /dev/null +++ b/frontend/public/json/fladder.json @@ -0,0 +1,35 @@ +{ + "name": "Fladder", + "slug": "fladder", + "categories": [ + 14 + ], + "date_created": "2025-12-26", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://github.com/DonutWare/Fladder/blob/develop/INSTALL.md#ubuntudebian", + "website": "https://github.com/DonutWare/Fladder", + "logo": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/webp/fladder.webp", + "config_path": "/opt/fladder/assets/config/config.json", + "description": "Fladder is a simple Jellyfin frontend built on top of Flutter. It provides a modern interface to stream and sync content locally, manage libraries, support multiple profiles, and offers direct, transcode and offline playback with media segments skipping.", + "install_methods": [ + { + "type": "default", + "script": "ct/fladder.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/fladder-install.sh b/install/fladder-install.sh new file mode 100644 index 000000000..beb848ca5 --- /dev/null +++ b/install/fladder-install.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: wendyliga +# License: MIT | https://github.com/wendyliga/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/DonutWare/Fladder + +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 \ + wget \ + unzip \ + nginx +msg_ok "Installed Dependencies" + +msg_info "Installing ${APPLICATION}" +# Get latest version from GitHub +RELEASE=$(curl -fsSL https://api.github.com/repos/DonutWare/Fladder/releases/latest | \ + grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') +cd /opt +$STD wget -q "https://github.com/DonutWare/Fladder/releases/download/${RELEASE}/Fladder-Web-${RELEASE#v}.zip" +$STD unzip -o "Fladder-Web-${RELEASE#v}.zip" -d fladder + +rm -f "Fladder-Web-${RELEASE#v}.zip" +echo "${RELEASE}" >/opt/${APPLICATION}_version.txt +msg_ok "Installed ${APPLICATION}" + +msg_info "Configuring Nginx" +cat </etc/nginx/conf.d/fladder.conf +server { + listen 80 default_server; + listen [::]:80 default_server; + + server_name _; + + root /opt/fladder; + index index.html; + + location / { + try_files \$uri \$uri/ /index.html; + } +} +EOF +rm -f /etc/nginx/sites-enabled/default +rm -f /etc/nginx/sites-available/default +systemctl enable -q --now nginx +systemctl reload nginx +msg_ok "Configured Nginx" + +motd_ssh +customize +cleanup_lxc From f2784d025a023f9dec806f301686d55d1195e57d Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 00:27:30 +0700 Subject: [PATCH 132/378] fix: update source URL for build functions in fladder script --- ct/fladder.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/fladder.sh b/ct/fladder.sh index 5eb2d3a51..1c827dd8a 100644 --- a/ct/fladder.sh +++ b/ct/fladder.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/wendyliga/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: wendyliga # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 714307d9f3ac0a2316b4167626a11ff9bf1cfea4 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 00:28:37 +0700 Subject: [PATCH 133/378] fix: correct license URL in fladder installation script --- install/fladder-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/fladder-install.sh b/install/fladder-install.sh index beb848ca5..0ddf937f4 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: wendyliga -# License: MIT | https://github.com/wendyliga/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/DonutWare/Fladder source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From 400dfeb8a0f083296adeae81bc954c0f3285fb6c Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 00:31:43 +0700 Subject: [PATCH 134/378] fix: update license URL in fladder scripts --- ct/fladder.sh | 4 ++-- install/fladder-install.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/fladder.sh b/ct/fladder.sh index 1c827dd8a..5349f194c 100644 --- a/ct/fladder.sh +++ b/ct/fladder.sh @@ -1,8 +1,8 @@ #!/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: wendyliga -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/DonutWare/Fladder APP="Fladder" diff --git a/install/fladder-install.sh b/install/fladder-install.sh index 0ddf937f4..6146e0c7c 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: wendyliga -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/DonutWare/Fladder source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From 8aa09d64dcd41d549f2670147b202516bf3efbba Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Fri, 26 Dec 2025 19:33:12 +0100 Subject: [PATCH 135/378] vars as arguments --- ct/tor-snowflake.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index 004cc5547..b0ca17a96 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -6,14 +6,14 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Source: https://snowflake.torproject.org/ APP="tor-snowflake" -var_tags="privacy;proxy;tor" -var_cpu="1" -var_ram="512" -var_disk="4" -var_os="debian" -var_version="13" -var_unprivileged="1" -var_nesting="0" +var_tags="${var_tags:-privacy;proxy;tor}" +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}" +var_nesting="${var_nesting:-0}" header_info "$APP" variables From 2dd49e9b433603d00d217841a925a704954bda40 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Fri, 26 Dec 2025 19:34:10 +0100 Subject: [PATCH 136/378] apt use new version --- ct/tor-snowflake.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index b0ca17a96..ca37f19f0 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -34,8 +34,8 @@ function update_script() { fi msg_info "Updating Container OS" - $STD apt-get update - $STD apt-get -y upgrade + $STD apt update + $STD apt upgrade -y msg_ok "Updated Container OS" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') From 85652adee932929dce600599450c524bae528161 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Fri, 26 Dec 2025 19:37:59 +0100 Subject: [PATCH 137/378] download code from release instead of git repo --- ct/tor-snowflake.sh | 25 +++++++++---------------- install/tor-snowflake-install.sh | 21 ++++++--------------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index ca37f19f0..62d9501da 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.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: KernelSailor # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -24,14 +24,6 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /opt/snowflake ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if ! id snowflake &>/dev/null; then - msg_error "snowflake user not found!" - exit - fi msg_info "Updating Container OS" $STD apt update @@ -44,17 +36,18 @@ function update_script() { systemctl stop snowflake-proxy msg_ok "Stopped Service" - msg_info "Updating Go" - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) setup_go - msg_ok "Updated Go" msg_info "Updating ${APP} to v${RELEASE}" - cd /opt/snowflake || exit - $STD git fetch --all - $STD git checkout "v${RELEASE}" + cd /opt + $STD curl -fsSL "https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz" -o snowflake.tar.gz + $STD tar -xzf snowflake.tar.gz + rm -rf snowflake + mv "snowflake-v${RELEASE}" snowflake + rm snowflake.tar.gz chown -R snowflake:snowflake /opt/snowflake - $STD sudo -u snowflake go build -o proxy ./proxy + cd /opt/snowflake/proxy + $STD sudo -u snowflake go build -o snowflake-proxy . echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated ${APP} to v${RELEASE}" diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index f9a1a56f2..bd9e7c833 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -13,14 +13,6 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - ca-certificates \ - curl \ - git \ - jq -msg_ok "Installed Dependencies" - setup_go msg_info "Creating snowflake user" @@ -28,16 +20,15 @@ useradd -r -s /bin/false -d /opt/snowflake snowflake msg_ok "Created snowflake user" msg_info "Building Snowflake Proxy from Source" -cd /opt || exit RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') -$STD git clone https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git -cd snowflake || exit -git config --global --add safe.directory /opt/snowflake -$STD git checkout "v${RELEASE}" +cd /opt +$STD curl -fsSL "https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz" -o snowflake.tar.gz +$STD tar -xzf snowflake.tar.gz +mv "snowflake-v${RELEASE}" snowflake +rm snowflake.tar.gz chown -R snowflake:snowflake /opt/snowflake -cd proxy || exit +cd /opt/snowflake/proxy $STD sudo -u snowflake go build -o snowflake-proxy . -cd /opt/snowflake || exit echo "${RELEASE}" >/opt/tor-snowflake_version.txt msg_ok "Built Snowflake Proxy v${RELEASE}" From b00eb4a8897e0194d513dae2f5883193db26fd88 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Dec 2025 09:51:49 +0100 Subject: [PATCH 138/378] add Linkwarden test --- ct/linkwarden.sh | 75 +++++++++++++++++++++++++++++++++++ install/linkwarden-install.sh | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 ct/linkwarden.sh create mode 100644 install/linkwarden-install.sh diff --git a/ct/linkwarden.sh b/ct/linkwarden.sh new file mode 100644 index 000000000..487505a1e --- /dev/null +++ b/ct/linkwarden.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: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://linkwarden.app/ + +APP="Linkwarden" +var_tags="${var_tags:-bookmark}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-12}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-24.04}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/linkwarden ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "linkwarden" "linkwarden/linkwarden"; then + NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs + msg_info "Stopping Service" + systemctl stop linkwarden + msg_ok "Stopped Service" + + RUST_CRATES="monolith" setup_rust + + msg_info "Backing up data" + mv /opt/linkwarden/.env /opt/.env + [ -d /opt/linkwarden/data ] && mv /opt/linkwarden/data /opt/data.bak + rm -rf /opt/linkwarden + msg_ok "Backed up data" + + fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" + + msg_info "Updating ${APP}" + cd /opt/linkwarden + $STD yarn + $STD npx playwright install-deps + $STD yarn playwright install + mv /opt/.env /opt/linkwarden/.env + $STD yarn prisma:generate + $STD yarn web:build + $STD yarn prisma:deploy + [ -d /opt/data.bak ] && mv /opt/data.bak /opt/linkwarden/data + rm -rf ~/.cargo/registry ~/.cargo/git ~/.cargo/.package-cache + rm -rf /root/.cache/yarn + rm -rf /opt/linkwarden/.next/cache + msg_ok "Updated ${APP}" + + msg_info "Starting Service" + systemctl start linkwarden + 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}" diff --git a/install/linkwarden-install.sh b/install/linkwarden-install.sh new file mode 100644 index 000000000..b47c80485 --- /dev/null +++ b/install/linkwarden-install.sh @@ -0,0 +1,75 @@ +#!/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://linkwarden.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 install -y build-essential +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" setup_postgresql +PG_DB_NAME="linkwardendb" PG_DB_USER="linkwarden" setup_postgresql_db +RUST_CRATES="monolith" setup_rust +fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden"\import_local_ip + +read -r -p "${TAB3}Would you like to add Adminer? " prompt +if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + setup_adminer +fi + +msg_info "Installing Linkwarden (Patience)" +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +export PRISMA_HIDE_UPDATE_MESSAGE=1 +export DEBIAN_FRONTEND=noninteractive +corepack enable +SECRET_KEY="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)" +cd /opt/linkwarden +$STD yarn workspaces focus linkwarden @linkwarden/web @linkwarden/worker +$STD npx playwright install-deps +$STD yarn playwright install + +cat </opt/linkwarden/.env +NEXTAUTH_SECRET=${SECRET_KEY} +NEXTAUTH_URL=http://${LOCAL_IP}:3000 +DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} +EOF +$STD yarn prisma:generate +$STD yarn web:build +$STD yarn prisma:deploy +rm -rf ~/.cargo/registry ~/.cargo/git ~/.cargo/.package-cache +rm -rf /root/.cache/yarn +rm -rf /opt/linkwarden/.next/cache +msg_ok "Installed Linkwarden" + +msg_info "Creating Service" +cat </etc/systemd/system/linkwarden.service +[Unit] +Description=Linkwarden Service +After=network.target + +[Service] +Type=exec +Environment=PATH=$PATH +WorkingDirectory=/opt/linkwarden +ExecStart=/usr/bin/yarn concurrently:start + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now linkwarden +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 8812f659824b71ded5bba5dd0fde3f479419b053 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Dec 2025 10:03:24 +0100 Subject: [PATCH 139/378] update Linkwarden --- install/linkwarden-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/linkwarden-install.sh b/install/linkwarden-install.sh index b47c80485..18594997d 100644 --- a/install/linkwarden-install.sh +++ b/install/linkwarden-install.sh @@ -21,7 +21,8 @@ NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql PG_DB_NAME="linkwardendb" PG_DB_USER="linkwarden" setup_postgresql_db RUST_CRATES="monolith" setup_rust -fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden"\import_local_ip +fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" +import_local_ip read -r -p "${TAB3}Would you like to add Adminer? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then From 6c1d63a414a239b51bc950468aba6be483f70f94 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Dec 2025 10:27:21 +0100 Subject: [PATCH 140/378] VE>VED --- ct/linkwarden.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/linkwarden.sh b/ct/linkwarden.sh index 487505a1e..1d8158e2c 100644 --- a/ct/linkwarden.sh +++ b/ct/linkwarden.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: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 56f6d5a2aebbf0921c2b193f08b67835b55e9115 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Dec 2025 10:51:52 +0100 Subject: [PATCH 141/378] Linkwarden: remove playwright --- install/linkwarden-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/linkwarden-install.sh b/install/linkwarden-install.sh index 18594997d..6596e2020 100644 --- a/install/linkwarden-install.sh +++ b/install/linkwarden-install.sh @@ -37,8 +37,8 @@ corepack enable SECRET_KEY="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)" cd /opt/linkwarden $STD yarn workspaces focus linkwarden @linkwarden/web @linkwarden/worker -$STD npx playwright install-deps -$STD yarn playwright install +# $STD npx playwright install-deps +# $STD yarn playwright install cat </opt/linkwarden/.env NEXTAUTH_SECRET=${SECRET_KEY} From 5698870c63aa1072713739745825be8f3b40e4be Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:49:54 +0700 Subject: [PATCH 142/378] fix review --- ct/fladder.sh | 70 ++++++++++++++----------------- frontend/public/json/fladder.json | 2 +- install/fladder-install.sh | 9 ++-- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/ct/fladder.sh b/ct/fladder.sh index 5349f194c..3339da735 100644 --- a/ct/fladder.sh +++ b/ct/fladder.sh @@ -24,55 +24,49 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -d /opt/fladder ]]; then + if [[ ! -f ~/.fladder ]]; then msg_error "No ${APP} Installation Found!" exit fi + # Get latest version from GitHub - RELEASE=$(curl -fsSL https://api.github.com/repos/DonutWare/Fladder/releases/latest | \ - grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') + RELEASE=$(get_latest_github_release "DonutWare/Fladder") if [[ -z "$RELEASE" ]]; then msg_error "Failed to fetch latest release version from GitHub" exit 1 fi - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Stopping Service" - systemctl stop nginx - msg_ok "Stopped Service" - msg_info "Backing up configuration" - if [[ -f /opt/fladder/assets/config/config.json ]]; then - cp /opt/fladder/assets/config/config.json /tmp/fladder_config.json.bak - msg_ok "Configuration backed up" - elif [[ -f /opt/fladder/data/flutter_assets/config/config.json ]]; then - cp /opt/fladder/data/flutter_assets/config/config.json /tmp/fladder_config.json.bak - msg_ok "Configuration backed up" - fi + msg_info "Stopping Service" + systemctl stop nginx + msg_ok "Stopped Service" - msg_info "Updating ${APP} to ${RELEASE}" - cd /opt - wget -q "https://github.com/DonutWare/Fladder/releases/download/${RELEASE}/Fladder-Web-${RELEASE#v}.zip" - rm -rf /opt/fladder - unzip -q "Fladder-Web-${RELEASE#v}.zip" -d fladder - rm -f "Fladder-Web-${RELEASE#v}.zip" - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated ${APP} to ${RELEASE}" - - msg_info "Restoring configuration" - if [[ -f /tmp/fladder_config.json.bak ]]; then - mkdir -p /opt/fladder/assets/config - cp /tmp/fladder_config.json.bak /opt/fladder/assets/config/config.json - rm -f /tmp/fladder_config.json.bak - msg_ok "Configuration restored" - fi - - msg_info "Starting Service" - systemctl start nginx - msg_ok "Started Service" - msg_ok "Updated successfully!" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_info "Backing up configuration" + if [[ -f /opt/fladder/assets/config/config.json ]]; then + cp /opt/fladder/assets/config/config.json /tmp/fladder_config.json.bak + msg_ok "Configuration backed up" fi + + msg_info "Updating ${APP} to ${RELEASE}" + cd /opt + wget -q "https://github.com/DonutWare/Fladder/releases/download/${RELEASE}/Fladder-Web-${RELEASE#v}.zip" + rm -rf /opt/fladder + unzip -q "Fladder-Web-${RELEASE#v}.zip" -d fladder + rm -f "Fladder-Web-${RELEASE#v}.zip" + echo "${RELEASE}" > ~/.fladder + msg_ok "Updated ${APP} to ${RELEASE}" + + msg_info "Restoring configuration" + if [[ -f /tmp/fladder_config.json.bak ]]; then + mkdir -p /opt/fladder/assets/config + cp /tmp/fladder_config.json.bak /opt/fladder/assets/config/config.json + rm -f /tmp/fladder_config.json.bak + msg_ok "Configuration restored" + fi + + msg_info "Starting Service" + systemctl start nginx + msg_ok "Started Service" + msg_ok "Updated successfully!" exit } diff --git a/frontend/public/json/fladder.json b/frontend/public/json/fladder.json index 99a8ffd81..9ee69b853 100644 --- a/frontend/public/json/fladder.json +++ b/frontend/public/json/fladder.json @@ -11,7 +11,7 @@ "interface_port": 80, "documentation": "https://github.com/DonutWare/Fladder/blob/develop/INSTALL.md#ubuntudebian", "website": "https://github.com/DonutWare/Fladder", - "logo": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/webp/fladder.webp", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/fladder.webp", "config_path": "/opt/fladder/assets/config/config.json", "description": "Fladder is a simple Jellyfin frontend built on top of Flutter. It provides a modern interface to stream and sync content locally, manage libraries, support multiple profiles, and offers direct, transcode and offline playback with media segments skipping.", "install_methods": [ diff --git a/install/fladder-install.sh b/install/fladder-install.sh index 6146e0c7c..1514dbdab 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -14,22 +14,23 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ wget \ unzip \ nginx msg_ok "Installed Dependencies" msg_info "Installing ${APPLICATION}" + # Get latest version from GitHub -RELEASE=$(curl -fsSL https://api.github.com/repos/DonutWare/Fladder/releases/latest | \ - grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') +RELEASE=$(get_latest_github_release "DonutWare/Fladder") + cd /opt $STD wget -q "https://github.com/DonutWare/Fladder/releases/download/${RELEASE}/Fladder-Web-${RELEASE#v}.zip" $STD unzip -o "Fladder-Web-${RELEASE#v}.zip" -d fladder rm -f "Fladder-Web-${RELEASE#v}.zip" -echo "${RELEASE}" >/opt/${APPLICATION}_version.txt +echo "${RELEASE}" > ~/.fladder msg_ok "Installed ${APPLICATION}" msg_info "Configuring Nginx" From d85e518d9f211c10bc6ed3839967262f500b9888 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:55:22 +0700 Subject: [PATCH 143/378] improve update script --- ct/fladder.sh | 62 +++++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/ct/fladder.sh b/ct/fladder.sh index 3339da735..fad8c5d14 100644 --- a/ct/fladder.sh +++ b/ct/fladder.sh @@ -29,44 +29,32 @@ function update_script() { exit fi - # Get latest version from GitHub - RELEASE=$(get_latest_github_release "DonutWare/Fladder") - if [[ -z "$RELEASE" ]]; then - msg_error "Failed to fetch latest release version from GitHub" - exit 1 + if check_for_gh_release "Fladder" "DonutWare/Fladder"; then + msg_info "Stopping Service" + systemctl stop nginx + msg_ok "Stopped Service" + + msg_info "Backing up configuration" + if [[ -f /opt/fladder/assets/config/config.json ]]; then + cp /opt/fladder/assets/config/config.json /tmp/fladder_config.json.bak + msg_ok "Configuration backed up" + fi + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" + + msg_info "Restoring configuration" + if [[ -f /tmp/fladder_config.json.bak ]]; then + mkdir -p /opt/fladder/assets/config + cp /tmp/fladder_config.json.bak /opt/fladder/assets/config/config.json + rm -f /tmp/fladder_config.json.bak + msg_ok "Configuration restored" + fi + + msg_info "Starting Service" + systemctl start nginx + msg_ok "Started Service" + msg_ok "Updated successfully!" fi - - msg_info "Stopping Service" - systemctl stop nginx - msg_ok "Stopped Service" - - msg_info "Backing up configuration" - if [[ -f /opt/fladder/assets/config/config.json ]]; then - cp /opt/fladder/assets/config/config.json /tmp/fladder_config.json.bak - msg_ok "Configuration backed up" - fi - - msg_info "Updating ${APP} to ${RELEASE}" - cd /opt - wget -q "https://github.com/DonutWare/Fladder/releases/download/${RELEASE}/Fladder-Web-${RELEASE#v}.zip" - rm -rf /opt/fladder - unzip -q "Fladder-Web-${RELEASE#v}.zip" -d fladder - rm -f "Fladder-Web-${RELEASE#v}.zip" - echo "${RELEASE}" > ~/.fladder - msg_ok "Updated ${APP} to ${RELEASE}" - - msg_info "Restoring configuration" - if [[ -f /tmp/fladder_config.json.bak ]]; then - mkdir -p /opt/fladder/assets/config - cp /tmp/fladder_config.json.bak /opt/fladder/assets/config/config.json - rm -f /tmp/fladder_config.json.bak - msg_ok "Configuration restored" - fi - - msg_info "Starting Service" - systemctl start nginx - msg_ok "Started Service" - msg_ok "Updated successfully!" exit } From 383cb7f5a46398d6ac7362423dfdc0e77a9feec0 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:00:18 +0700 Subject: [PATCH 144/378] use fetch_and_deploy_gh_release --- install/fladder-install.sh | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/install/fladder-install.sh b/install/fladder-install.sh index 1514dbdab..d186ff2a3 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -21,17 +21,7 @@ $STD apt install -y \ msg_ok "Installed Dependencies" msg_info "Installing ${APPLICATION}" - -# Get latest version from GitHub -RELEASE=$(get_latest_github_release "DonutWare/Fladder") - -cd /opt -$STD wget -q "https://github.com/DonutWare/Fladder/releases/download/${RELEASE}/Fladder-Web-${RELEASE#v}.zip" -$STD unzip -o "Fladder-Web-${RELEASE#v}.zip" -d fladder - -rm -f "Fladder-Web-${RELEASE#v}.zip" -echo "${RELEASE}" > ~/.fladder -msg_ok "Installed ${APPLICATION}" +fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" msg_info "Configuring Nginx" cat </etc/nginx/conf.d/fladder.conf From 3c4a18f969db6ba0961d5494a3649855e773c364 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:01:07 +0700 Subject: [PATCH 145/378] fix: remove checks --- ct/fladder.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ct/fladder.sh b/ct/fladder.sh index fad8c5d14..b849d3ccb 100644 --- a/ct/fladder.sh +++ b/ct/fladder.sh @@ -24,11 +24,6 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f ~/.fladder ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "Fladder" "DonutWare/Fladder"; then msg_info "Stopping Service" systemctl stop nginx From 03f028ad26315eb438bff339690319324d5e4654 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:01:59 +0700 Subject: [PATCH 146/378] Update install/fladder-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/fladder-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/fladder-install.sh b/install/fladder-install.sh index d186ff2a3..a965f9c0c 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -20,7 +20,6 @@ $STD apt install -y \ nginx msg_ok "Installed Dependencies" -msg_info "Installing ${APPLICATION}" fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" msg_info "Configuring Nginx" From 75db01ae9047b06dc830c79595fd9bd01e493547 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:02:10 +0700 Subject: [PATCH 147/378] Update install/fladder-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/fladder-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/fladder-install.sh b/install/fladder-install.sh index a965f9c0c..ac22c3dc1 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: wendyliga -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/DonutWare/Fladder source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From f2ed885a7fef7588bd7374932d03c07357227827 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:02:18 +0700 Subject: [PATCH 148/378] Update ct/fladder.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- ct/fladder.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/fladder.sh b/ct/fladder.sh index b849d3ccb..c133d3f95 100644 --- a/ct/fladder.sh +++ b/ct/fladder.sh @@ -2,7 +2,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: wendyliga -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/DonutWare/Fladder APP="Fladder" From e83b46ead99fc8106800d6a2c8deda3dea3a45ea Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:02:38 +0700 Subject: [PATCH 149/378] fix: remove unnecessary dependency installations --- install/fladder-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/fladder-install.sh b/install/fladder-install.sh index d186ff2a3..189431a9d 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -15,8 +15,6 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - wget \ - unzip \ nginx msg_ok "Installed Dependencies" From 7803001474db068f5eadb16b33614a3861a0a3d2 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:03:06 +0700 Subject: [PATCH 150/378] fix: remove multiline --- install/fladder-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/fladder-install.sh b/install/fladder-install.sh index cdc6d7da6..11c42392b 100644 --- a/install/fladder-install.sh +++ b/install/fladder-install.sh @@ -14,8 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y \ - nginx +$STD apt install -y nginx msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" From 7733532441c686795efc8de585d52dc1176f1414 Mon Sep 17 00:00:00 2001 From: Wendy Liga <16457495+wendyliga@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:05:14 +0700 Subject: [PATCH 151/378] revert and improve --- ct/fladder.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ct/fladder.sh b/ct/fladder.sh index c133d3f95..39ce55c9b 100644 --- a/ct/fladder.sh +++ b/ct/fladder.sh @@ -24,6 +24,11 @@ function update_script() { check_container_storage check_container_resources + if [[ ! -d /opt/fladder ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "Fladder" "DonutWare/Fladder"; then msg_info "Stopping Service" systemctl stop nginx From a162586f6c9c2b12f48fe99880f718ce7ab5c51c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:02:07 +0000 Subject: [PATCH 152/378] Delete nextcloud-exporter (addon) after migration to ProxmoxVE (#1235) Co-authored-by: github-actions[bot] --- frontend/public/json/nextcloud-exporter.json | 35 ---- tools/addon/nextcloud-exporter.sh | 209 ------------------- 2 files changed, 244 deletions(-) delete mode 100644 frontend/public/json/nextcloud-exporter.json delete mode 100644 tools/addon/nextcloud-exporter.sh diff --git a/frontend/public/json/nextcloud-exporter.json b/frontend/public/json/nextcloud-exporter.json deleted file mode 100644 index 7866fc152..000000000 --- a/frontend/public/json/nextcloud-exporter.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Nextcloud Exporter", - "slug": "nextcloud-exporter", - "categories": [ - 9 - ], - "date_created": "2025-12-17", - "type": "addon", - "updateable": true, - "privileged": false, - "interface_port": 9205, - "documentation": "https://github.com/xperimental/nextcloud-exporter", - "website": "https://github.com/xperimental/nextcloud-exporter", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nextcloud.webp", - "config_path": "/etc/nextcloud-exporter.env", - "description": "Prometheus exporter for Nextcloud servers. ", - "install_methods": [ - { - "type": "default", - "script": "tools/addon/nextcloud-exporter.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/tools/addon/nextcloud-exporter.sh b/tools/addon/nextcloud-exporter.sh deleted file mode 100644 index 31762d591..000000000 --- a/tools/addon/nextcloud-exporter.sh +++ /dev/null @@ -1,209 +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/xperimental/nextcloud-exporter - -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) -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/error_handler.func) - -# Enable error handling -set -Eeuo pipefail -trap 'error_handler' ERR -load_functions - -# ============================================================================== -# CONFIGURATION -# ============================================================================== -VERBOSE=${var_verbose:-no} -APP="nextcloud-exporter" -APP_TYPE="tools" -BINARY_PATH="/usr/bin/nextcloud-exporter" -CONFIG_PATH="/etc/nextcloud-exporter.env" -SERVICE_PATH="/etc/systemd/system/nextcloud-exporter.service" - -# ============================================================================== -# OS DETECTION -# ============================================================================== -if ! grep -qE 'ID=debian|ID=ubuntu' /etc/os-release 2>/dev/null; then - echo -e "${CROSS} Unsupported OS detected. This script only supports Debian and Ubuntu." - exit 1 -fi - -# ============================================================================== -# UNINSTALL -# ============================================================================== -function uninstall() { - msg_info "Uninstalling Nextcloud-Exporter" - systemctl disable -q --now nextcloud-exporter - rm -f "$SERVICE_PATH" - - if dpkg -l | grep -q nextcloud-exporter; then - $STD apt-get remove -y nextcloud-exporter || $STD dpkg -r nextcloud-exporter - fi - - rm -f "$CONFIG_PATH" - rm -f "/usr/local/bin/update_nextcloud-exporter" - rm -f "$HOME/.nextcloud-exporter" - msg_ok "Nextcloud-Exporter has been uninstalled" -} - -# ============================================================================== -# UPDATE -# ============================================================================== -function update() { - if check_for_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter"; then - msg_info "Stopping service" - systemctl stop nextcloud-exporter - msg_ok "Stopped service" - - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "binary" "latest" - - msg_info "Starting service" - systemctl start nextcloud-exporter - msg_ok "Started service" - msg_ok "Updated successfully" - exit - fi -} - -# ============================================================================== -# INSTALL -# ============================================================================== -function install() { - read -erp "Enter URL of Nextcloud, example: (http://127.0.0.1:8080): " NEXTCLOUD_SERVER - read -rsp "Enter Nextcloud auth token (press Enter to use username/password instead): " NEXTCLOUD_AUTH_TOKEN - printf "\n" - - if [[ -z "$NEXTCLOUD_AUTH_TOKEN" ]]; then - read -erp "Enter Nextcloud username: " NEXTCLOUD_USERNAME - read -rsp "Enter Nextcloud password: " NEXTCLOUD_PASSWORD - printf "\n" - fi - - read -erp "Query additional info for apps? [Y/n]: " QUERY_APPS - if [[ "${QUERY_APPS,,}" =~ ^(n|no)$ ]]; then - NEXTCLOUD_INFO_APPS="false" - fi - - read -erp "Query update information? [Y/n]: " QUERY_UPDATES - if [[ "${QUERY_UPDATES,,}" =~ ^(n|no)$ ]]; then - NEXTCLOUD_INFO_UPDATE="false" - fi - - read -erp "Do you want to skip TLS-Verification (if using a self-signed Certificate on Nextcloud) [y/N]: " SKIP_TLS - if [[ "${SKIP_TLS,,}" =~ ^(y|yes)$ ]]; then - NEXTCLOUD_TLS_SKIP_VERIFY="true" - fi - - fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "binary" "latest" - - msg_info "Creating configuration" - cat <"$CONFIG_PATH" -# https://github.com/xperimental/nextcloud-exporter -NEXTCLOUD_SERVER="${NEXTCLOUD_SERVER}" -NEXTCLOUD_AUTH_TOKEN="${NEXTCLOUD_AUTH_TOKEN:-}" -NEXTCLOUD_USERNAME="${NEXTCLOUD_USERNAME:-}" -NEXTCLOUD_PASSWORD="${NEXTCLOUD_PASSWORD:-}" -NEXTCLOUD_INFO_UPDATE=${NEXTCLOUD_INFO_UPDATE:-"true"} -NEXTCLOUD_INFO_APPS=${NEXTCLOUD_INFO_APPS:-"true"} -NEXTCLOUD_TLS_SKIP_VERIFY=${NEXTCLOUD_TLS_SKIP_VERIFY:-"false"} -NEXTCLOUD_LISTEN_ADDRESS=":9205" -EOF - msg_ok "Created configuration" - - msg_info "Creating service" - cat <"$SERVICE_PATH" -[Unit] -Description=nextcloud-exporter -After=network.target - -[Service] -User=root -EnvironmentFile=$CONFIG_PATH -ExecStart=$BINARY_PATH -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - systemctl daemon-reload - systemctl enable -q --now nextcloud-exporter - msg_ok "Created and started service" - - # Create update script - msg_info "Creating update script" - ensure_usr_local_bin_persist - cat <<'UPDATEEOF' >/usr/local/bin/update_nextcloud-exporter -#!/usr/bin/env bash -# nextcloud-exporter Update Script -type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/addon/nextcloud-exporter.sh)" -UPDATEEOF - chmod +x /usr/local/bin/update_nextcloud-exporter - msg_ok "Created update script (/usr/local/bin/update_nextcloud-exporter)" - - echo "" - msg_ok "Nextcloud-Exporter installed successfully" - msg_ok "Metrics: ${BL}http://${LOCAL_IP}:9205/metrics${CL}" - msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" -} - -# ============================================================================== -# MAIN -# ============================================================================== -header_info -ensure_usr_local_bin_persist -import_local_ip - -# Handle type=update (called from update script) -if [[ "${type:-}" == "update" ]]; then - if [[ -f "$BINARY_PATH" ]]; then - update - else - msg_error "Nextcloud-Exporter is not installed. Nothing to update." - exit 1 - fi - exit 0 -fi - -# Check if already installed -if [[ -f "$BINARY_PATH" ]]; then - msg_warn "Nextcloud-Exporter is already installed." - echo "" - - echo -n "${TAB}Uninstall Nextcloud-Exporter? (y/N): " - read -r uninstall_prompt - if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then - uninstall - exit 0 - fi - - echo -n "${TAB}Update Nextcloud-Exporter? (y/N): " - read -r update_prompt - if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then - update - exit 0 - fi - - msg_warn "No action selected. Exiting." - exit 0 -fi - -# Fresh installation -msg_warn "Nextcloud-Exporter is not installed." -echo "" -echo -e "${TAB}${INFO} This will install:" -echo -e "${TAB} - Nextcloud Exporter (Go binary)" -echo -e "${TAB} - Systemd service" -echo "" - -echo -n "${TAB}Install Nextcloud-Exporter? (y/N): " -read -r install_prompt -if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then - install -else - msg_warn "Installation cancelled. Exiting." - exit 0 -fi From 5c296111c1a013c707ccd40a2eb781b47e34e906 Mon Sep 17 00:00:00 2001 From: Jordan Tomkinson Date: Sun, 28 Dec 2025 12:54:29 +0000 Subject: [PATCH 153/378] Add tracearr --- ct/headers/tracearr | 6 + ct/tracearr.sh | 120 +++++++++++++++++++ frontend/public/json/tracearr.json | 35 ++++++ install/tracearr-install.sh | 183 +++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+) create mode 100644 ct/headers/tracearr create mode 100644 ct/tracearr.sh create mode 100644 frontend/public/json/tracearr.json create mode 100644 install/tracearr-install.sh diff --git a/ct/headers/tracearr b/ct/headers/tracearr new file mode 100644 index 000000000..cec64cede --- /dev/null +++ b/ct/headers/tracearr @@ -0,0 +1,6 @@ + ______ + /_ __/________ _________ ____ ___________ + / / / ___/ __ `/ ___/ _ \/ __ `/ ___/ ___/ + / / / / / /_/ / /__/ __/ /_/ / / / / +/_/ /_/ \__,_/\___/\___/\__,_/_/ /_/ + diff --git a/ct/tracearr.sh b/ct/tracearr.sh new file mode 100644 index 000000000..6f98192d3 --- /dev/null +++ b/ct/tracearr.sh @@ -0,0 +1,120 @@ +#!/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: durzo +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/connorgallopo/Tracearr + +APP="Tracearr" +var_tags="${var_tags:-media}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +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 [[ ! -f /lib/systemd/system/tracearr.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + NODE_VERSION="22" NODE_MODULE="pnpm@10.24.0" setup_nodejs + + if check_for_gh_release "tracearr" "connorgallopo/Tracearr"; then + msg_info "Stopping Services" + systemctl stop tracearr + systemctl stop postgresql + systemctl stop redis + msg_ok "Stopped Services" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" + + msg_info "Building Tracearr" + export TZ=$(cat /etc/timezone) + cd /opt/tracearr.build + $STD pnpm install --frozen-lockfile --force + $STD pnpm turbo telemetry disable + $STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web + rm -rf /opt/tracearr + mkdir -p /opt/tracearr/{packages/shared,apps/server,apps/web,apps/server/src/db} + cp -rf package.json /opt/tracearr/ + cp -rf pnpm-workspace.yaml /opt/tracearr/ + cp -rf pnpm-lock.yaml /opt/tracearr/ + cp -rf apps/server/package.json /opt/tracearr/apps/server/ + cp -rf apps/server/dist /opt/tracearr/apps/server/dist + cp -rf apps/web/dist /opt/tracearr/apps/web/dist + cp -rf packages/shared/package.json /opt/tracearr/packages/shared/ + cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist + cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations + cp -rf data /opt/tracearr/data + rm -rf /opt/tracearr.build + cd /opt/tracearr + $STD pnpm install --prod --frozen-lockfile + $STD chown -R tracearr:tracearr /opt/tracearr + msg_ok "Built Tracearr" + + msg_info "Configuring Tracearr" + if [ ! -d /data/tracearr ]; then + install -d -m 750 -o tracearr -g tracearr /data/tracearr + fi + + if [ -f /data/tracearr/.jwt_secret ]; then + export JWT_SECRET=$(cat /data/tracearr/.jwt_secret) + else + export JWT_SECRET=$(openssl rand -hex 32) + echo "$JWT_SECRET" > /data/tracearr/.jwt_secret + chmod 600 /data/tracearr/.jwt_secret + fi + if [ -f /data/tracearr/.cookie_secret ]; then + export COOKIE_SECRET=$(cat /data/tracearr/.cookie_secret) + else + export COOKIE_SECRET=$(openssl rand -hex 32) + echo "$COOKIE_SECRET" > /data/tracearr/.cookie_secret + chmod 600 /data/tracearr/.cookie_secret + fi + cat </data/tracearr/.env +DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@127.0.0.1:5432/${DB_NAME} +REDIS_URL=redis://127.0.0.1:6379 +PORT=3000 +HOST=0.0.0.0 +NODE_ENV=production +TZ=${TZ} +LOG_LEVEL=info +JWT_SECRET=$JWT_SECRET +COOKIE_SECRET=$COOKIE_SECRET +APP_VERSION=$(cat /root/.tracearr) +#CORS_ORIGIN=http://localhost:5173 +#MOBILE_BETA_MODE=true +EOF + chmod 600 /data/tracearr/.env + chown -R tracearr:tracearr /data/tracearr + msg_ok "Configured Tracearr" + + msg_info "Starting Services" + systemctl start postgresql + systemctl start redis + systemctl start tracearr + 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/frontend/public/json/tracearr.json b/frontend/public/json/tracearr.json new file mode 100644 index 000000000..2544280a4 --- /dev/null +++ b/frontend/public/json/tracearr.json @@ -0,0 +1,35 @@ +{ + "name": "Tracearr", + "slug": "tracearr", + "categories": [ + 13 + ], + "date_created": "2025-12-28", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/connorgallopo/Tracearr#readme", + "config_path": "", + "website": "https://github.com/connorgallopo/Tracearr", + "logo": "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/apps/web/public/logo-transparent.png", + "description": "Tracearr is a streaming access manager for Plex, Jellyfin and Emby servers. It answers the question every server owner eventually asks: \"Who's actually using my server, and are they sharing their login?\"", + "install_methods": [ + { + "type": "default", + "script": "ct/tracearr.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 5, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} \ No newline at end of file diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh new file mode 100644 index 000000000..2c2b7512b --- /dev/null +++ b/install/tracearr-install.sh @@ -0,0 +1,183 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: durzo +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/connorgallopo/Tracearr + +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 +msg_ok "Installed Dependencies" + +NODE_VERSION="22" NODE_MODULE="pnpm@10.24.0" setup_nodejs +PG_VERSION="18" setup_postgresql + +msg_info "Installing TimescaleDB" +setup_deb822_repo \ + "timescaledb" \ + "https://packagecloud.io/timescale/timescaledb/gpgkey" \ + "https://packagecloud.io/timescale/timescaledb/debian" \ + "$(get_os_info codename)" \ + "main" +$STD apt install -y \ + timescaledb-2-postgresql-18 \ + timescaledb-tools \ + timescaledb-toolkit-postgresql-18 +# give timescaledb-tune 50% of total ram in MB +# we need to leave the rest for redis and the webserver. +# We cant use $RAM_SIZE or $var_ram here, which is annoying. +total_ram_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') +ram_for_tsdb=$((total_ram_kb / 1024 / 2)) +$STD timescaledb-tune -yes -memory "$ram_for_tsdb"MB +$STD systemctl restart postgresql +msg_ok "Installed TimescaleDB" + +msg_info "Creating PostgreSQL Database" +DB_NAME=tracearr +DB_USER=tracearr +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 -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS timescaledb;" +$STD sudo -u postgres psql -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit;" +$STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" +$STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL ON SCHEMA public TO $DB_USER;" +$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 "" +} >>~/tracearr.creds +msg_ok "Created PostgreSQL Database" + +fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" + +msg_info "Building Tracearr" +export TZ=$(cat /etc/timezone) +cd /opt/tracearr.build +$STD pnpm install --frozen-lockfile --force +$STD pnpm turbo telemetry disable +$STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web +mkdir -p /opt/tracearr/{packages/shared,apps/server,apps/web,apps/server/src/db} +cp -rf package.json /opt/tracearr/ +cp -rf pnpm-workspace.yaml /opt/tracearr/ +cp -rf pnpm-lock.yaml /opt/tracearr/ +cp -rf apps/server/package.json /opt/tracearr/apps/server/ +cp -rf apps/server/dist /opt/tracearr/apps/server/dist +cp -rf apps/web/dist /opt/tracearr/apps/web/dist +cp -rf packages/shared/package.json /opt/tracearr/packages/shared/ +cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist +cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations +cp -rf data /opt/tracearr/data +rm -rf /opt/tracearr.build +cd /opt/tracearr +$STD pnpm install --prod --frozen-lockfile +msg_ok "Built Tracearr" + +msg_info "Configuring Tracearr" +$STD useradd -r -s /bin/false -U tracearr +$STD chown -R tracearr:tracearr /opt/tracearr +install -d -m 750 -o tracearr -g tracearr /data/tracearr +if [ -f /data/tracearr/.jwt_secret ]; then + export JWT_SECRET=$(cat /data/tracearr/.jwt_secret) +else + export JWT_SECRET=$(openssl rand -hex 32) + echo "$JWT_SECRET" > /data/tracearr/.jwt_secret + chmod 600 /data/tracearr/.jwt_secret +fi + +if [ -f /data/tracearr/.cookie_secret ]; then + export COOKIE_SECRET=$(cat /data/tracearr/.cookie_secret) +else + export COOKIE_SECRET=$(openssl rand -hex 32) + echo "$COOKIE_SECRET" > /data/tracearr/.cookie_secret + chmod 600 /data/tracearr/.cookie_secret +fi +cat </data/tracearr/.env +DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@127.0.0.1:5432/${DB_NAME} +REDIS_URL=redis://127.0.0.1:6379 +PORT=3000 +HOST=0.0.0.0 +NODE_ENV=production +TZ=${TZ} +LOG_LEVEL=info +JWT_SECRET=$JWT_SECRET +COOKIE_SECRET=$COOKIE_SECRET +APP_VERSION=$(cat /root/.tracearr) +#CORS_ORIGIN=http://localhost:5173 +#MOBILE_BETA_MODE=true +EOF +chmod 600 /data/tracearr/.env +chown -R tracearr:tracearr /data/tracearr +msg_ok "Configured Tracearr" + +msg_info "Creating Services" +cat </data/tracearr/prestart.sh +#!/usr/bin/env bash +# ============================================================================= +# Tune PostgreSQL for available resources (runs every startup) +# ============================================================================= +# timescaledb-tune automatically optimizes PostgreSQL settings based on +# available RAM and CPU. Safe to run repeatedly - recalculates if resources change. +if command -v timescaledb-tune &> /dev/null; then + total_ram_kb=\$(grep MemTotal /proc/meminfo | awk '{print \$2}') + ram_for_tsdb=\$((total_ram_kb / 1024 / 2)) + timescaledb-tune -yes -memory "\$ram_for_tsdb"MB --quiet 2>/dev/null \ + || echo "Warning: timescaledb-tune failed (non-fatal)" +fi +# ============================================================================= +# Ensure TimescaleDB decompression limit is set (for existing databases) +# ============================================================================= +# This setting allows migrations to modify compressed hypertable data. +# Without it, bulk UPDATEs on compressed sessions will fail with +# "tuple decompression limit exceeded" errors. +pg_config_file="/etc/postgresql/18/main/postgresql.conf" +if [ -f \$pg_config_file ]; then + if ! grep -q "max_tuples_decompressed_per_dml_transaction" \$pg_config_file; then + echo "" >> \$pg_config_file + echo "# Allow unlimited tuple decompression for migrations on compressed hypertables" >> \$pg_config_file + echo "timescaledb.max_tuples_decompressed_per_dml_transaction = 0" >> \$pg_config_file + fi +fi +systemctl restart postgresql +EOF +chmod +x /data/tracearr/prestart.sh +cat </lib/systemd/system/tracearr.service +[Unit] +Description=Tracearr Web Server +After=network.target postgresql.service redis-server.service + +[Service] +Type=simple +KillMode=control-group +EnvironmentFile=/data/tracearr/.env +WorkingDirectory=/opt/tracearr +ExecStartPre=+/data/tracearr/prestart.sh +ExecStart=node /opt/tracearr/apps/server/dist/index.js +Restart=on-failure +RestartSec=10 +User=tracearr + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now postgresql +systemctl enable -q --now redis-server +systemctl enable -q --now tracearr +msg_ok "Created Services" + +motd_ssh +customize +cleanup_lxc From c11ddd0e2b8ffafe1a41d35a3c3acf7f5d3727a8 Mon Sep 17 00:00:00 2001 From: Casey Jones Date: Sun, 28 Dec 2025 10:48:56 -0600 Subject: [PATCH 154/378] add libmfx-gen1.2 for intel hwaccel --- misc/tools.func | 3 +++ 1 file changed, 3 insertions(+) diff --git a/misc/tools.func b/misc/tools.func index 3a5e64e9d..f7c13fa7c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2630,6 +2630,7 @@ function setup_hwaccel() { ocl-icd-libopencl1 \ intel-opencl-icd \ vainfo \ + libmfx-gen1.2 \ intel-gpu-tools || { msg_error "Failed to install Intel GPU dependencies" return 1 @@ -2657,6 +2658,7 @@ EOF ocl-icd-libopencl1 \ intel-opencl-icd \ vainfo \ + libmfx-gen1.2 \ intel-gpu-tools || { msg_warn "Non-free driver install failed, falling back to open drivers" needs_nonfree=false @@ -2685,6 +2687,7 @@ EOF mesa-va-drivers \ libvpl2 \ vainfo \ + libmfx-gen1.2 \ intel-gpu-tools 2>/dev/null || { msg_warn "Non-free driver install failed, falling back to open drivers" needs_nonfree=false From 8dc0e3b9628ec816793d8896a8e6369ad8b41369 Mon Sep 17 00:00:00 2001 From: Jordan Tomkinson Date: Sun, 28 Dec 2025 16:36:28 +0000 Subject: [PATCH 155/378] PR review feedback --- ct/tracearr.sh | 34 ++++++++++++++++++++++-------- frontend/public/json/tracearr.json | 4 ++-- install/tracearr-install.sh | 22 ++----------------- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index 6f98192d3..535585ea9 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -28,15 +28,13 @@ function update_script() { exit fi - NODE_VERSION="22" NODE_MODULE="pnpm@10.24.0" setup_nodejs - if check_for_gh_release "tracearr" "connorgallopo/Tracearr"; then msg_info "Stopping Services" - systemctl stop tracearr - systemctl stop postgresql - systemctl stop redis + systemctl stop tracearr postgresql redis msg_ok "Stopped Services" + NODE_VERSION="22" NODE_MODULE="pnpm@10.24.0" setup_nodejs + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" msg_info "Building Tracearr" @@ -82,8 +80,28 @@ function update_script() { echo "$COOKIE_SECRET" > /data/tracearr/.cookie_secret chmod 600 /data/tracearr/.cookie_secret fi + if [ ! -f /root/tracearr.creds ]; then + if [ -f /data/tracearr/.env ]; then + PG_DB_NAME=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d'/' -f4) + PG_DB_USER=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d'/' -f3 | cut -d':' -f1) + PG_DB_PASS=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d':' -f3 | cut -d'@' -f1) + { echo "PostgreSQL Credentials" + echo "Database: $PG_DB_NAME" + echo "User: $PG_DB_USER" + echo "Password: $PG_DB_PASS" + } >/root/tracearr.creds + msg_ok "Recreated tracearr.creds file from existing .env" + else + msg_error "No existing tracearr.creds or .env file found. Cannot configure database connection!" + exit 1 + fi + else + PG_DB_NAME=$(grep 'Database:' /root/tracearr.creds | awk '{print $2}') + PG_DB_USER=$(grep 'User:' /root/tracearr.creds | awk '{print $2}') + PG_DB_PASS=$(grep 'Password:' /root/tracearr.creds | awk '{print $2}') + fi cat </data/tracearr/.env -DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@127.0.0.1:5432/${DB_NAME} +DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} REDIS_URL=redis://127.0.0.1:6379 PORT=3000 HOST=0.0.0.0 @@ -101,9 +119,7 @@ EOF msg_ok "Configured Tracearr" msg_info "Starting Services" - systemctl start postgresql - systemctl start redis - systemctl start tracearr + systemctl start postgresql redis tracearr msg_ok "Started Services" msg_ok "Updated successfully!" fi diff --git a/frontend/public/json/tracearr.json b/frontend/public/json/tracearr.json index 2544280a4..0e729d977 100644 --- a/frontend/public/json/tracearr.json +++ b/frontend/public/json/tracearr.json @@ -12,7 +12,7 @@ "documentation": "https://github.com/connorgallopo/Tracearr#readme", "config_path": "", "website": "https://github.com/connorgallopo/Tracearr", - "logo": "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/apps/web/public/logo-transparent.png", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tracearr.webp", "description": "Tracearr is a streaming access manager for Plex, Jellyfin and Emby servers. It answers the question every server owner eventually asks: \"Who's actually using my server, and are they sharing their login?\"", "install_methods": [ { @@ -32,4 +32,4 @@ "password": null }, "notes": [] -} \ No newline at end of file +} diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh index 2c2b7512b..5ed0c9911 100644 --- a/install/tracearr-install.sh +++ b/install/tracearr-install.sh @@ -41,25 +41,7 @@ $STD systemctl restart postgresql msg_ok "Installed TimescaleDB" msg_info "Creating PostgreSQL Database" -DB_NAME=tracearr -DB_USER=tracearr -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 -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS timescaledb;" -$STD sudo -u postgres psql -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit;" -$STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" -$STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL ON SCHEMA public TO $DB_USER;" -$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 "" -} >>~/tracearr.creds +PG_DB_NAME="tracearr" PG_DB_USER="tracearr" PG_DB_EXTENSIONS="timescaledb,timescaledb_toolkit" setup_postgresql_db msg_ok "Created PostgreSQL Database" fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" @@ -106,7 +88,7 @@ else chmod 600 /data/tracearr/.cookie_secret fi cat </data/tracearr/.env -DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@127.0.0.1:5432/${DB_NAME} +DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} REDIS_URL=redis://127.0.0.1:6379 PORT=3000 HOST=0.0.0.0 From a9ef97e5e9a7286db23bb9b1a1198872be33afd8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Dec 2025 20:30:20 +0000 Subject: [PATCH 156/378] Delete mail-archiver (ct) after migration to ProxmoxVE (#1238) Co-authored-by: github-actions[bot] --- ct/mail-archiver.sh | 71 ------------------------- frontend/public/json/mail-archiver.json | 35 ------------ install/mail-archiver-install.sh | 68 ----------------------- 3 files changed, 174 deletions(-) delete mode 100644 ct/mail-archiver.sh delete mode 100644 frontend/public/json/mail-archiver.json delete mode 100644 install/mail-archiver-install.sh diff --git a/ct/mail-archiver.sh b/ct/mail-archiver.sh deleted file mode 100644 index 2205f301f..000000000 --- a/ct/mail-archiver.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: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/s1t5/mail-archiver - -APP="Mail-Archiver" -var_tags="${var_tags:-mail-archiver}" -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/mail-archiver ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "mail-archiver" "s1t5/mail-archiver"; then - msg_info "Stopping Mail-Archiver" - systemctl stop mail-archiver - msg_ok "Stopped Mail-Archiver" - - msg_info "Creating Backup" - cp /opt/mail-archiver/appsettings.json /opt/mail-archiver/.env /opt/ - [[ -d /opt/mail-archiver/DataProtection-Keys ]] && cp -r /opt/mail-archiver/DataProtection-Keys /opt - msg_ok "Created Backup" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "mail-archiver" "s1t5/mail-archiver" "tarball" - - msg_info "Updating Mail-Archiver" - mv /opt/mail-archiver /opt/mail-archiver-build - cd /opt/mail-archiver-build - $STD dotnet restore - $STD dotnet publish -c Release -o /opt/mail-archiver - rm -rf /opt/mail-archiver-build - msg_ok "Updated Mail-Archiver" - - msg_info "Restoring Backup" - cp /opt/appsettings.json /opt/.env /opt/mail-archiver - [[ -d /opt/DataProtection-Keys ]] && cp -r /opt/DataProtection-Keys /opt/mail-archiver/ - msg_ok "Restored Backup" - - msg_info "Starting Mail-Archiver" - systemctl start mail-archiver - msg_ok "Started Mail-Archiver" - 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}:5000${CL}" diff --git a/frontend/public/json/mail-archiver.json b/frontend/public/json/mail-archiver.json deleted file mode 100644 index 006cae8ca..000000000 --- a/frontend/public/json/mail-archiver.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Mail-Archiver", - "slug": "mail-archiver", - "categories": [ - 7 - ], - "date_created": "2025-12-12", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 5000, - "documentation": "https://github.com/s1t5/mail-archiver/blob/main/doc/Index.md", - "config_path": "/opt/mail-archiver/.env, /opt/mail-archiver/appsettings.json", - "website": "https://github.com/s1t5/mail-archiver", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/mail-archiver.webp", - "description": "Mail-Archiver is a web application for archiving, searching, and exporting emails from multiple accounts. Featuring folder sync, attachment support, mailbox migration and a dashboard.", - "install_methods": [ - { - "type": "default", - "script": "ct/mail-archiver.sh", - "resources": { - "cpu": 1, - "ram": 2048, - "hdd": 8, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": "admin", - "password": "secure123!" - }, - "notes": [] -} diff --git a/install/mail-archiver-install.sh b/install/mail-archiver-install.sh deleted file mode 100644 index b309966fc..000000000 --- a/install/mail-archiver-install.sh +++ /dev/null @@ -1,68 +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/s1t5/mail-archiver - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -setup_deb822_repo \ - "microsoft" \ - "https://packages.microsoft.com/keys/microsoft-2025.asc" \ - "https://packages.microsoft.com/debian/13/prod/" \ - "trixie" \ - "main" -$STD apt install -y \ - dotnet-sdk-10.0 \ - aspnetcore-runtime-8.0 -msg_ok "Installed Dependencies" - -PG_VERSION="17" setup_postgresql -PG_DB_NAME="mailarchiver_db" PG_DB_USER="mailarchiver" setup_postgresql_db -fetch_and_deploy_gh_release "mail-archiver" "s1t5/mail-archiver" "tarball" - -msg_info "Setting up Mail-Archiver" -mv /opt/mail-archiver /opt/mail-archiver-build -cd /opt/mail-archiver-build -$STD dotnet restore -$STD dotnet publish -c Release -o /opt/mail-archiver -cp /opt/mail-archiver-build/appsettings.json /opt/mail-archiver/appsettings.json -sed -i "s|\"DefaultConnection\": \"[^\"]*\"|\"DefaultConnection\": \"Host=localhost;Database=mailarchiver_db;Username=mailarchiver;Password=$PG_DB_PASS\"|" /opt/mail-archiver/appsettings.json -rm -rf /opt/mail-archiver-build - -cat </opt/mail-archiver/.env -ASPNETCORE_URLS=http://+:5000 -ASPNETCORE_ENVIRONMENT=Production -TZ=UTC -EOF -msg_ok "Setup Mail-Archiver" - -msg_info "Creating Service" -cat </etc/systemd/system/mail-archiver.service -[Unit] -Description=Mail-Archiver Service -After=network.target - -[Service] -EnvironmentFile=/opt/mail-archiver/.env -WorkingDirectory=/opt/mail-archiver -ExecStart=/usr/bin/dotnet MailArchiver.dll -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now mail-archiver -msg_info "Created Service" - -motd_ssh -customize -cleanup_lxc From 6c01ab9cf406c71e97abff3ca0affea16585d6c2 Mon Sep 17 00:00:00 2001 From: Jordan Tomkinson Date: Sun, 28 Dec 2025 22:45:25 +0000 Subject: [PATCH 157/378] Dynamically fetch pnpm version from package.json --- ct/tracearr.sh | 3 ++- install/tracearr-install.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index 535585ea9..c25066a3e 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -33,7 +33,8 @@ function update_script() { systemctl stop tracearr postgresql redis msg_ok "Stopped Services" - NODE_VERSION="22" NODE_MODULE="pnpm@10.24.0" setup_nodejs + PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')" + NODE_VERSION="22" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh index 5ed0c9911..dcbc6e7ad 100644 --- a/install/tracearr-install.sh +++ b/install/tracearr-install.sh @@ -17,7 +17,8 @@ msg_info "Installing Dependencies" $STD apt install -y redis-server msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@10.24.0" setup_nodejs +PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')" +NODE_VERSION="22" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs PG_VERSION="18" setup_postgresql msg_info "Installing TimescaleDB" From a38a15df5b54d63dd1011d9b143bdbc7b47ec0f3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Dec 2025 10:41:34 +0100 Subject: [PATCH 158/378] Sportarr: update --- ct/sportarr.sh | 14 +---------- frontend/public/json/sportarr.json | 40 ++++++++++++++++++++++++++++++ install/sportarr-install.sh | 6 ++++- 3 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 frontend/public/json/sportarr.json diff --git a/ct/sportarr.sh b/ct/sportarr.sh index 6cbc76d83..2b6b36f37 100644 --- a/ct/sportarr.sh +++ b/ct/sportarr.sh @@ -33,19 +33,7 @@ function update_script() { systemctl stop sportarr msg_ok "Stopped Sportarr Service" - msg_info "Creating Backup" - - msg_ok "Created Backup" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" - - msg_info "Updating sportarr" - - msg_ok "Updated sportarr" - - msg_info "Restoring Backup" - - msg_ok "Restored Backup" + fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" msg_info "Starting Sportarr Service" systemctl start sportarr diff --git a/frontend/public/json/sportarr.json b/frontend/public/json/sportarr.json new file mode 100644 index 000000000..1da25756f --- /dev/null +++ b/frontend/public/json/sportarr.json @@ -0,0 +1,40 @@ +{ + "name": "Sportarr", + "slug": "sportarr", + "categories": [ + 14 + ], + "date_created": "2025-12-12", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 1867, + "documentation": "https://sportarr.net/docs", + "config_path": "/opt/sportarr/.env, /opt/sportarr-data/config/config.xml", + "website": "https://sportarr.net/", + "logo": "", + "description": "Sportarr is an automated media management application for all sports. It works similar to Sonarr and Radarr but specifically designed for combat sports, basketball, football, hockey, motorsports, and hundreds of other sports worldwide.", + "install_methods": [ + { + "type": "default", + "script": "ct/sportarr.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The resources assigned to LXC are considered baseline. Please adjust to match your workload.", + "type": "info" + } + ] +} diff --git a/install/sportarr-install.sh b/install/sportarr-install.sh index fa2b5a9ef..79d9c00e3 100644 --- a/install/sportarr-install.sh +++ b/install/sportarr-install.sh @@ -13,11 +13,15 @@ setting_up_container network_check update_os +msg_info "Installing Dependencies" +$STD apt install -y ffmpeg +msg_ok "Installed Dependencies" + fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" msg_info "Setting up Sportarr" cat </opt/sportarr/.env -Sportarr__DataPath="/opt/sportarr/config" +Sportarr__DataPath="/opt/sportarr-data/config" ASPNETCORE_URLS="http://*:1867" ASPNETCORE_ENVIRONMENT="Production" DOTNET_CLI_TELEMETRY_OPTOUT=1 From 446a51c20dbcee6f0126631c70bd4e331c8262ba Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Dec 2025 10:45:56 +0100 Subject: [PATCH 159/378] Sportarr: add hwaccel --- install/cronmaster-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/cronmaster-install.sh b/install/cronmaster-install.sh index 738e05199..c2a4b651e 100644 --- a/install/cronmaster-install.sh +++ b/install/cronmaster-install.sh @@ -12,6 +12,7 @@ catch_errors setting_up_container network_check update_os +setup_hwaccel msg_info "Installing dependencies" $STD apt install -y pciutils From 20c6d01385dbf6cf4bde633446c2a379c6be933d Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Dec 2025 10:50:48 +0100 Subject: [PATCH 160/378] Sportarr: add hwaccel --- install/sportarr-install.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install/sportarr-install.sh b/install/sportarr-install.sh index 79d9c00e3..26d9eb14d 100644 --- a/install/sportarr-install.sh +++ b/install/sportarr-install.sh @@ -12,9 +12,13 @@ catch_errors setting_up_container network_check update_os +setup_hwaccel msg_info "Installing Dependencies" -$STD apt install -y ffmpeg +$STD apt install -y \ + ffmpeg \ + gosu \ + sqlite3 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" @@ -26,6 +30,7 @@ ASPNETCORE_URLS="http://*:1867" ASPNETCORE_ENVIRONMENT="Production" DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false +LIBVA_DRIVER_NAME=iHD EOF msg_ok "Setup Sportarr" From 327d64e53b030e95c5d18dbaa5200b53e5d3b335 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Dec 2025 11:02:58 +0100 Subject: [PATCH 161/378] Sportarr: update --- ct/sportarr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/sportarr.sh b/ct/sportarr.sh index 2b6b36f37..6d11d4aef 100644 --- a/ct/sportarr.sh +++ b/ct/sportarr.sh @@ -7,7 +7,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Sportarr" var_tags="${var_tags:-arr}" -var_cpu="${var_cpu:-1}" +var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" From 654047e08abf08a3460e07765d3c78c0f46d0646 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Mon, 29 Dec 2025 11:45:58 +0100 Subject: [PATCH 162/378] change App location --- ct/tor-snowflake.sh | 23 +++++++++++---------- install/tor-snowflake-install.sh | 34 ++++++++++++++++---------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index 62d9501da..5fea776e9 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -15,6 +15,8 @@ var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_nesting="${var_nesting:-0}" +SNOWFLAKEUSER="snowflake" + header_info "$APP" variables color @@ -31,24 +33,21 @@ function update_script() { msg_ok "Updated Container OS" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Stopping Service" + VERSION_FILE="/home/${SNOWFLAKEUSER}/.${APP}_version" + if [[ ! -f "${VERSION_FILE}" ]] || [[ "${RELEASE}" != "$(cat "${VERSION_FILE}")" ]]; then systemctl stop snowflake-proxy msg_ok "Stopped Service" setup_go msg_info "Updating ${APP} to v${RELEASE}" - cd /opt - $STD curl -fsSL "https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz" -o snowflake.tar.gz - $STD tar -xzf snowflake.tar.gz - rm -rf snowflake - mv "snowflake-v${RELEASE}" snowflake - rm snowflake.tar.gz - chown -R snowflake:snowflake /opt/snowflake - cd /opt/snowflake/proxy - $STD sudo -u snowflake go build -o snowflake-proxy . - echo "${RELEASE}" >/opt/${APP}_version.txt + $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" + $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tar -xzf snowflake.tar.gz" + $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && rm snowflake.tar.gz" + $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && rm -rf .${APP}" + $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" + $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." + echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_version >/dev/null" msg_ok "Updated ${APP} to v${RELEASE}" msg_info "Starting Service" diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index bd9e7c833..4db5cb7fe 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -13,23 +13,23 @@ setting_up_container network_check update_os +APP="tor-snowflake" +SNOWFLAKEUSER="snowflake" + setup_go -msg_info "Creating snowflake user" -useradd -r -s /bin/false -d /opt/snowflake snowflake -msg_ok "Created snowflake user" +msg_info "Creating ${SNOWFLAKEUSER} user" +useradd -m -r -s /usr/sbin/nologin -d /home/${SNOWFLAKEUSER} ${SNOWFLAKEUSER} +msg_ok "Created ${SNOWFLAKEUSER} user" -msg_info "Building Snowflake Proxy from Source" +msg_info "Building Snowflake" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') -cd /opt -$STD curl -fsSL "https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz" -o snowflake.tar.gz -$STD tar -xzf snowflake.tar.gz -mv "snowflake-v${RELEASE}" snowflake -rm snowflake.tar.gz -chown -R snowflake:snowflake /opt/snowflake -cd /opt/snowflake/proxy -$STD sudo -u snowflake go build -o snowflake-proxy . -echo "${RELEASE}" >/opt/tor-snowflake_version.txt +$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" +$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tar -xzf snowflake.tar.gz" +$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && rm snowflake.tar.gz" +$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" +$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." +echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_version >/dev/null" msg_ok "Built Snowflake Proxy v${RELEASE}" msg_info "Creating Service" @@ -42,10 +42,10 @@ Wants=network-online.target [Service] Type=simple -User=snowflake -Group=snowflake -WorkingDirectory=/opt/snowflake -ExecStart=/opt/snowflake/proxy/snowflake-proxy -verbose -unsafe-logging +User=${SNOWFLAKEUSER} +Group=${SNOWFLAKEUSER} +WorkingDirectory=/home/${SNOWFLAKEUSER}/.${APP} +ExecStart=/home/${SNOWFLAKEUSER}/.${APP}/proxy/snowflake-proxy -verbose -unsafe-logging Restart=always RestartSec=10 From 9702978d0c8a10d2597b707031b1a97627597718 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Mon, 29 Dec 2025 11:46:51 +0100 Subject: [PATCH 163/378] rename snowflake systemd service --- ct/tor-snowflake.sh | 11 ++++++----- install/tor-snowflake-install.sh | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index 5fea776e9..c27e8a3cc 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -35,8 +35,9 @@ function update_script() { RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') VERSION_FILE="/home/${SNOWFLAKEUSER}/.${APP}_version" if [[ ! -f "${VERSION_FILE}" ]] || [[ "${RELEASE}" != "$(cat "${VERSION_FILE}")" ]]; then - systemctl stop snowflake-proxy - msg_ok "Stopped Service" + msg_info "Stopping ${APP} Service" + systemctl stop ${APP} + msg_ok "Stopped ${APP} Service" setup_go @@ -50,9 +51,9 @@ function update_script() { echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_version >/dev/null" msg_ok "Updated ${APP} to v${RELEASE}" - msg_info "Starting Service" - systemctl start snowflake-proxy - msg_ok "Started Service" + msg_info "Starting ${APP} Service" + systemctl start ${APP} + msg_ok "Started ${APP} Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}." diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index 4db5cb7fe..f5b917ce5 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -33,7 +33,7 @@ echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_ver msg_ok "Built Snowflake Proxy v${RELEASE}" msg_info "Creating Service" -cat </etc/systemd/system/snowflake-proxy.service +cat </etc/systemd/system/${APP}.service [Unit] Description=Snowflake Proxy Service Documentation=https://snowflake.torproject.org/ @@ -52,7 +52,7 @@ RestartSec=10 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now snowflake-proxy +systemctl enable -q --now ${APP} msg_ok "Created Service" motd_ssh From e22edd600a1bd821d42ed8095cdd4e767f15f822 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Mon, 29 Dec 2025 11:47:51 +0100 Subject: [PATCH 164/378] rm info notification at the end of script --- ct/tor-snowflake.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index c27e8a3cc..74ed6be57 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -66,6 +66,3 @@ build_container description msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}Tor Snowflake setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Snowflake proxy is running as a systemd service.${CL}" -echo -e "${INFO}${YW} Check status: systemctl status snowflake-proxy${CL}" From 862070cf47bc82d4d2c21fbeb71a329748cc9d52 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Dec 2025 14:47:36 +0100 Subject: [PATCH 165/378] Sportarr: update json --- frontend/public/json/sportarr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/sportarr.json b/frontend/public/json/sportarr.json index 1da25756f..fd0ec06fc 100644 --- a/frontend/public/json/sportarr.json +++ b/frontend/public/json/sportarr.json @@ -12,7 +12,7 @@ "documentation": "https://sportarr.net/docs", "config_path": "/opt/sportarr/.env, /opt/sportarr-data/config/config.xml", "website": "https://sportarr.net/", - "logo": "", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/sportarr.webp", "description": "Sportarr is an automated media management application for all sports. It works similar to Sonarr and Radarr but specifically designed for combat sports, basketball, football, hockey, motorsports, and hundreds of other sports worldwide.", "install_methods": [ { From 65838068c4690e59d0e2e13811f2be4dc6ef85ca Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Mon, 29 Dec 2025 08:22:24 -0800 Subject: [PATCH 166/378] Update install/netbird-install.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> --- install/netbird-install.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 0d6fa9e27..c0149c4dd 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -14,12 +14,6 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - curl \ - ca-certificates \ - gnupg -msg_ok "Installed Dependencies" msg_info "Setting up NetBird Repository" curl -sSL https://pkgs.netbird.io/debian/public.key | gpg --dearmor -o /usr/share/keyrings/netbird-archive-keyring.gpg From 62fcad52ac077dde30e23261c2b7e5670a73bf61 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Mon, 29 Dec 2025 08:22:40 -0800 Subject: [PATCH 167/378] Update install/netbird-install.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> --- install/netbird-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index c0149c4dd..17415acda 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -6,7 +6,6 @@ # Source: https://netbird.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -app="netbird" color verb_ip6 catch_errors From 18817d96a5665ca7eaaf314eb8b9156dfcac2972 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Mon, 29 Dec 2025 08:22:51 -0800 Subject: [PATCH 168/378] Update install/netbird-install.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> --- install/netbird-install.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 17415acda..acd33fb6b 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -15,10 +15,11 @@ update_os msg_info "Setting up NetBird Repository" -curl -sSL https://pkgs.netbird.io/debian/public.key | gpg --dearmor -o /usr/share/keyrings/netbird-archive-keyring.gpg -chmod 0644 /usr/share/keyrings/netbird-archive-keyring.gpg -echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | tee /etc/apt/sources.list.d/netbird.list >/dev/null -$STD apt-get update +setup_deb882_repo \ + "netbird" \ + "https://pkgs.netbird.io/debian/public.key" \ + "https://pkgs.netbird.io/debian" \ + "stable" msg_ok "Set up NetBird Repository" msg_info "Installing NetBird" From 513b096e6effd0904fc15aa25304c78acb34b4d8 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:22:57 +0100 Subject: [PATCH 169/378] Add OCI container deployment script for Proxmox VE This script provides a deployment helper for OCI containers on Proxmox VE 9.1, including functions for checking Proxmox version, parsing image references, and deploying containers with customizable options. --- tools/pve/oci-deploy.sh | 344 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 tools/pve/oci-deploy.sh diff --git a/tools/pve/oci-deploy.sh b/tools/pve/oci-deploy.sh new file mode 100644 index 000000000..12dfb4ba2 --- /dev/null +++ b/tools/pve/oci-deploy.sh @@ -0,0 +1,344 @@ +#!/usr/bin/env bash +# Maintainer: MickLesk (CanbiZ) +# Copyright (c) 2021-2025 community-scripts ORG +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# OCI Container Deployment Helper for Proxmox VE 9.1+ + +set -euo pipefail +shopt -s inherit_errexit nullglob + +# Color codes +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}" +} + +# Check if running on Proxmox VE 9.1+ +check_proxmox_version() { + if ! command -v pveversion &>/dev/null; then + msg_error "This script must be run on Proxmox VE" + exit 1 + fi + + local pve_version=$(pveversion | grep -oP 'pve-manager/\K[0-9.]+' | cut -d. -f1,2) + local major=$(echo "$pve_version" | cut -d. -f1) + local minor=$(echo "$pve_version" | cut -d. -f2) + + if [[ "$major" -lt 9 ]] || { [[ "$major" -eq 9 ]] && [[ "$minor" -lt 1 ]]; }; then + msg_error "Proxmox VE 9.1 or higher required (current: $pve_version)" + exit 1 + fi +} + +# Parse OCI image reference +parse_image_ref() { + local image_ref="$1" + local registry="" + local image="" + local tag="latest" + + # Handle different formats: + # - nginx:latest + # - docker.io/library/nginx:latest + # - ghcr.io/user/repo:tag + + if [[ "$image_ref" =~ ^([^/]+\.[^/]+)/ ]]; then + # Has registry prefix (contains dot) + registry="${BASH_REMATCH[1]}" + image_ref="${image_ref#*/}" + else + # Use docker.io as default + registry="docker.io" + fi + + # Extract tag if present + if [[ "$image_ref" =~ :([^:]+)$ ]]; then + tag="${BASH_REMATCH[1]}" + image="${image_ref%:*}" + else + image="$image_ref" + fi + + # Add library/ prefix for official docker hub images + if [[ "$registry" == "docker.io" ]] && [[ ! "$image" =~ / ]]; then + image="library/$image" + fi + + echo "$registry/$image:$tag" +} + +# Show usage +usage() { + cat </dev/null; then + msg_error "Failed to create container" + exit 1 + fi + + msg_ok "Container created (ID: $vmid)" + + # Set environment variables if provided + if [[ ${#env_vars[@]} -gt 0 ]]; then + msg_info "Configuring environment variables" + for env_var in "${env_vars[@]}"; do + local key="${env_var%%=*}" + local value="${env_var#*=}" + pct set "$vmid" --env "$key=$value" >/dev/null 2>&1 + done + msg_ok "Environment variables configured (${#env_vars[@]} variables)" + fi + + # Start container if requested + if [[ "$start_after" == "1" ]]; then + msg_info "Starting container" + if pct start "$vmid" >/dev/null 2>&1; then + msg_ok "Container started successfully" + + # Wait for network + sleep 3 + local container_ip=$(pct exec "$vmid" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "N/A") + + echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e "${BL}Container Information:${CL}" + echo -e " ID: ${GN}$vmid${CL}" + echo -e " Name: ${GN}$name${CL}" + echo -e " Image: ${GN}$full_image${CL}" + echo -e " IP: ${GN}$container_ip${CL}" + echo -e " Status: ${GN}Running${CL}" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" + else + msg_error "Failed to start container" + fi + else + echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e "${BL}Container Information:${CL}" + echo -e " ID: ${GN}$vmid${CL}" + echo -e " Name: ${GN}$name${CL}" + echo -e " Image: ${GN}$full_image${CL}" + echo -e " Status: ${YW}Stopped${CL}" + echo -e "\n${YW}Start with: pct start $vmid${CL}" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" + fi +} + +# Parse command line arguments +IMAGE="" +NAME="" +VMID="" +CORES="2" +MEMORY="2048" +DISK="8" +STORAGE="" +NETWORK="vmbr0" +IP="dhcp" +GATEWAY="" +PRIVILEGED="0" +START="0" +ENV_VARS=() +VOLUMES=() + +while [[ $# -gt 0 ]]; do + case $1 in + -h | --help) + usage + ;; + -n | --name) + NAME="$2" + shift 2 + ;; + -i | --vmid) + VMID="$2" + shift 2 + ;; + -c | --cores) + CORES="$2" + shift 2 + ;; + -m | --memory) + MEMORY="$2" + shift 2 + ;; + -d | --disk) + DISK="$2" + shift 2 + ;; + -s | --storage) + STORAGE="$2" + shift 2 + ;; + --network) + NETWORK="$2" + shift 2 + ;; + --ip) + IP="$2" + shift 2 + ;; + --gateway) + GATEWAY="$2" + shift 2 + ;; + -e | --env) + ENV_VARS+=("$2") + shift 2 + ;; + -v | --volume) + VOLUMES+=("$2") + shift 2 + ;; + --privileged) + PRIVILEGED="1" + shift + ;; + --start) + START="1" + shift + ;; + -*) + echo "Unknown option: $1" + usage + ;; + *) + IMAGE="$1" + shift + ;; + esac +done + +# Check if image is provided +if [[ -z "$IMAGE" ]]; then + msg_error "No image specified" + usage +fi + +# Deploy the container +deploy_oci_container "$IMAGE" "$NAME" "$VMID" "$CORES" "$MEMORY" "$DISK" "$STORAGE" "$NETWORK" "$IP" "$GATEWAY" "$PRIVILEGED" "$START" "${ENV_VARS[@]}" + +exit 0 From 2971329b1b4451b73c008efb680556e5f552c106 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Mon, 29 Dec 2025 08:23:03 -0800 Subject: [PATCH 170/378] Update install/netbird-install.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> --- install/netbird-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index acd33fb6b..d86022444 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -40,7 +40,7 @@ echo " 1) NetBird Managed (default) - Use NetBird's managed service" echo " 2) Self-Hosted - Use your own NetBird management server" echo "" -read -r -p "Select deployment type [1]: " DEPLOYMENT_TYPE +read -r -p "${TAB3}Select deployment type [1]: " DEPLOYMENT_TYPE DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-1}" NETBIRD_MGMT_URL="" From ce1e3d76bf83211f4cf5710860b2f3c0afb54762 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:26:33 +0100 Subject: [PATCH 171/378] Refactor OCI deployment script for clarity and functionality Refactor message functions and improve version checks. --- tools/pve/oci-deploy.sh | 660 ++++++++++++++++++++-------------------- 1 file changed, 332 insertions(+), 328 deletions(-) diff --git a/tools/pve/oci-deploy.sh b/tools/pve/oci-deploy.sh index 12dfb4ba2..18ab63e2a 100644 --- a/tools/pve/oci-deploy.sh +++ b/tools/pve/oci-deploy.sh @@ -1,344 +1,348 @@ #!/usr/bin/env bash -# Maintainer: MickLesk (CanbiZ) + # Copyright (c) 2021-2025 community-scripts ORG -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# OCI Container Deployment Helper for Proxmox VE 9.1+ - -set -euo pipefail -shopt -s inherit_errexit nullglob - -# Color codes -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}" -} - -# Check if running on Proxmox VE 9.1+ -check_proxmox_version() { - if ! command -v pveversion &>/dev/null; then - msg_error "This script must be run on Proxmox VE" - exit 1 - fi - - local pve_version=$(pveversion | grep -oP 'pve-manager/\K[0-9.]+' | cut -d. -f1,2) - local major=$(echo "$pve_version" | cut -d. -f1) - local minor=$(echo "$pve_version" | cut -d. -f2) - - if [[ "$major" -lt 9 ]] || { [[ "$major" -eq 9 ]] && [[ "$minor" -lt 1 ]]; }; then - msg_error "Proxmox VE 9.1 or higher required (current: $pve_version)" - exit 1 - fi -} - -# Parse OCI image reference -parse_image_ref() { - local image_ref="$1" - local registry="" - local image="" - local tag="latest" - - # Handle different formats: - # - nginx:latest - # - docker.io/library/nginx:latest - # - ghcr.io/user/repo:tag - - if [[ "$image_ref" =~ ^([^/]+\.[^/]+)/ ]]; then - # Has registry prefix (contains dot) - registry="${BASH_REMATCH[1]}" - image_ref="${image_ref#*/}" - else - # Use docker.io as default - registry="docker.io" - fi - - # Extract tag if present - if [[ "$image_ref" =~ :([^:]+)$ ]]; then - tag="${BASH_REMATCH[1]}" - image="${image_ref%:*}" - else - image="$image_ref" - fi - - # Add library/ prefix for official docker hub images - if [[ "$registry" == "docker.io" ]] && [[ ! "$image" =~ / ]]; then - image="library/$image" - fi - - echo "$registry/$image:$tag" -} - -# Show usage -usage() { - cat </dev/null; then - msg_error "Failed to create container" - exit 1 - fi - - msg_ok "Container created (ID: $vmid)" - - # Set environment variables if provided - if [[ ${#env_vars[@]} -gt 0 ]]; then - msg_info "Configuring environment variables" - for env_var in "${env_vars[@]}"; do - local key="${env_var%%=*}" - local value="${env_var#*=}" - pct set "$vmid" --env "$key=$value" >/dev/null 2>&1 - done - msg_ok "Environment variables configured (${#env_vars[@]} variables)" - fi - - # Start container if requested - if [[ "$start_after" == "1" ]]; then - msg_info "Starting container" - if pct start "$vmid" >/dev/null 2>&1; then - msg_ok "Container started successfully" - - # Wait for network - sleep 3 - local container_ip=$(pct exec "$vmid" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "N/A") - - echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" - echo -e "${BL}Container Information:${CL}" - echo -e " ID: ${GN}$vmid${CL}" - echo -e " Name: ${GN}$name${CL}" - echo -e " Image: ${GN}$full_image${CL}" - echo -e " IP: ${GN}$container_ip${CL}" - echo -e " Status: ${GN}Running${CL}" - echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" - else - msg_error "Failed to start container" - fi - else - echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" - echo -e "${BL}Container Information:${CL}" - echo -e " ID: ${GN}$vmid${CL}" - echo -e " Name: ${GN}$name${CL}" - echo -e " Image: ${GN}$full_image${CL}" - echo -e " Status: ${YW}Stopped${CL}" - echo -e "\n${YW}Start with: pct start $vmid${CL}" - echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" - fi +function msg_info() { + local msg="$1" + echo -e "${INFO} ${YW}${msg}...${CL}" } -# Parse command line arguments -IMAGE="" -NAME="" -VMID="" -CORES="2" -MEMORY="2048" -DISK="8" -STORAGE="" -NETWORK="vmbr0" -IP="dhcp" -GATEWAY="" -PRIVILEGED="0" -START="0" -ENV_VARS=() -VOLUMES=() +function msg_ok() { + local msg="$1" + echo -e "${CM} ${GN}${msg}${CL}" +} -while [[ $# -gt 0 ]]; do - case $1 in - -h | --help) - usage - ;; - -n | --name) - NAME="$2" - shift 2 - ;; - -i | --vmid) - VMID="$2" - shift 2 - ;; - -c | --cores) - CORES="$2" - shift 2 - ;; - -m | --memory) - MEMORY="$2" - shift 2 - ;; - -d | --disk) - DISK="$2" - shift 2 - ;; - -s | --storage) - STORAGE="$2" - shift 2 - ;; - --network) - NETWORK="$2" - shift 2 - ;; - --ip) - IP="$2" - shift 2 - ;; - --gateway) - GATEWAY="$2" - shift 2 - ;; - -e | --env) - ENV_VARS+=("$2") - shift 2 - ;; - -v | --volume) - VOLUMES+=("$2") - shift 2 - ;; - --privileged) - PRIVILEGED="1" - shift - ;; - --start) - START="1" - shift - ;; - -*) - echo "Unknown option: $1" - usage - ;; - *) - IMAGE="$1" - shift - ;; - esac -done +function msg_error() { + local msg="$1" + echo -e "${CROSS} ${RD}${msg}${CL}" +} -# Check if image is provided -if [[ -z "$IMAGE" ]]; then - msg_error "No image specified" - usage +# Check Proxmox version +if ! command -v pveversion &>/dev/null; then + msg_error "This script must be run on Proxmox VE" + exit 1 fi -# Deploy the container -deploy_oci_container "$IMAGE" "$NAME" "$VMID" "$CORES" "$MEMORY" "$DISK" "$STORAGE" "$NETWORK" "$IP" "$GATEWAY" "$PRIVILEGED" "$START" "${ENV_VARS[@]}" +PVE_VER=$(pveversion | grep -oP 'pve-manager/\K[0-9.]+' | cut -d. -f1,2) +MAJOR=$(echo "$PVE_VER" | cut -d. -f1) +MINOR=$(echo "$PVE_VER" | cut -d. -f2) -exit 0 +if [[ "$MAJOR" -lt 9 ]] || { [[ "$MAJOR" -eq 9 ]] && [[ "$MINOR" -lt 1 ]]; }; then + msg_error "Proxmox VE 9.1+ required (current: $PVE_VER)" + exit 1 +fi + +msg_ok "Proxmox VE $PVE_VER detected" + +# Parse OCI image +parse_image() { + local input="$1" + if [[ "$input" =~ ^([^/]+\.[^/]+)/ ]]; then + echo "$input" + elif [[ "$input" =~ / ]]; then + echo "docker.io/$input" + else + echo "docker.io/library/$input" + fi +} + +# Interactive image selection +if [[ -z "${OCI_IMAGE:-}" ]]; then + echo "" + echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e "${BL}Select OCI Image:${CL}" + echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e " ${BL}1)${CL} nginx:alpine - Lightweight web server" + echo -e " ${BL}2)${CL} postgres:16-alpine - PostgreSQL database" + echo -e " ${BL}3)${CL} redis:alpine - Redis cache" + echo -e " ${BL}4)${CL} mariadb:latest - MariaDB database" + echo -e " ${BL}5)${CL} ghcr.io/linkwarden/linkwarden:latest" + echo -e " ${BL}6)${CL} Custom image" + echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo "" + + read -r -p "Select option (1-6): " IMAGE_CHOICE + + case $IMAGE_CHOICE in + 1) OCI_IMAGE="nginx:alpine" ;; + 2) OCI_IMAGE="postgres:16-alpine" ;; + 3) OCI_IMAGE="redis:alpine" ;; + 4) OCI_IMAGE="mariadb:latest" ;; + 5) OCI_IMAGE="ghcr.io/linkwarden/linkwarden:latest" ;; + 6) + read -r -p "Enter OCI image (e.g., ghcr.io/user/repo:tag): " OCI_IMAGE + [[ -z "$OCI_IMAGE" ]] && { msg_error "No image specified"; exit 1; } + ;; + *) + msg_error "Invalid choice" + exit 1 + ;; + esac +fi + +FULL_IMAGE=$(parse_image "$OCI_IMAGE") +msg_ok "Selected: $FULL_IMAGE" + +# Derive container name +if [[ -z "${CT_NAME:-}" ]]; then + DEFAULT_NAME=$(echo "$OCI_IMAGE" | sed 's|.*/||; s/:.*//; s/[^a-zA-Z0-9-]/-/g' | cut -c1-60) + read -r -p "Container name [${DEFAULT_NAME}]: " CT_NAME + CT_NAME=${CT_NAME:-$DEFAULT_NAME} +fi + +# Get next VMID +if [[ -z "${VMID:-}" ]]; then + NEXT_ID=$(pvesh get /cluster/nextid) + read -r -p "Container ID [${NEXT_ID}]: " VMID + VMID=${VMID:-$NEXT_ID} +fi + +# Resources +if [[ -z "${CORES:-}" ]]; then + read -r -p "CPU cores [2]: " CORES + CORES=${CORES:-2} +fi + +if [[ -z "${MEMORY:-}" ]]; then + read -r -p "Memory in MB [2048]: " MEMORY + MEMORY=${MEMORY:-2048} +fi + +if [[ -z "${DISK:-}" ]]; then + read -r -p "Disk size in GB [8]: " DISK + DISK=${DISK:-8} +fi + +# Storage +if [[ -z "${STORAGE:-}" ]]; then + AVAIL_STORAGE=$(pvesm status | awk '/^local-(zfs|lvm)/ {print $1; exit}') + [[ -z "$AVAIL_STORAGE" ]] && AVAIL_STORAGE="local" + read -r -p "Storage [${AVAIL_STORAGE}]: " STORAGE + STORAGE=${STORAGE:-$AVAIL_STORAGE} +fi + +# Network +if [[ -z "${BRIDGE:-}" ]]; then + read -r -p "Network bridge [vmbr0]: " BRIDGE + BRIDGE=${BRIDGE:-vmbr0} +fi + +if [[ -z "${IP_MODE:-}" ]]; then + read -r -p "IP mode (dhcp/static) [dhcp]: " IP_MODE + IP_MODE=${IP_MODE:-dhcp} +fi + +if [[ "$IP_MODE" == "static" ]]; then + read -r -p "Static IP (CIDR, e.g., 192.168.1.100/24): " STATIC_IP + read -r -p "Gateway IP: " GATEWAY +fi + +# Environment variables +declare -a ENV_VARS=() + +case "$OCI_IMAGE" in + postgres*|postgresql*) + echo "" + msg_info "PostgreSQL requires environment variables" + read -r -p "PostgreSQL password: " -s PG_PASS + echo "" + ENV_VARS+=("POSTGRES_PASSWORD=$PG_PASS") + + read -r -p "Create database (optional): " PG_DB + [[ -n "$PG_DB" ]] && ENV_VARS+=("POSTGRES_DB=$PG_DB") + + read -r -p "PostgreSQL user (optional): " PG_USER + [[ -n "$PG_USER" ]] && ENV_VARS+=("POSTGRES_USER=$PG_USER") + ;; + + mariadb*|mysql*) + echo "" + msg_info "MariaDB/MySQL requires environment variables" + read -r -p "Root password: " -s MYSQL_PASS + echo "" + ENV_VARS+=("MYSQL_ROOT_PASSWORD=$MYSQL_PASS") + + read -r -p "Create database (optional): " MYSQL_DB + [[ -n "$MYSQL_DB" ]] && ENV_VARS+=("MYSQL_DATABASE=$MYSQL_DB") + + read -r -p "Create user (optional): " MYSQL_USER + if [[ -n "$MYSQL_USER" ]]; then + ENV_VARS+=("MYSQL_USER=$MYSQL_USER") + read -r -p "User password: " -s MYSQL_USER_PASS + echo "" + ENV_VARS+=("MYSQL_PASSWORD=$MYSQL_USER_PASS") + fi + ;; + + *linkwarden*) + echo "" + msg_info "Linkwarden configuration" + read -r -p "NEXTAUTH_SECRET (press Enter to generate): " NEXTAUTH_SECRET + if [[ -z "$NEXTAUTH_SECRET" ]]; then + NEXTAUTH_SECRET=$(openssl rand -base64 32) + fi + ENV_VARS+=("NEXTAUTH_SECRET=$NEXTAUTH_SECRET") + + read -r -p "NEXTAUTH_URL [http://localhost:3000]: " NEXTAUTH_URL + NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000} + ENV_VARS+=("NEXTAUTH_URL=$NEXTAUTH_URL") + + read -r -p "DATABASE_URL (PostgreSQL connection string): " DATABASE_URL + [[ -n "$DATABASE_URL" ]] && ENV_VARS+=("DATABASE_URL=$DATABASE_URL") + ;; +esac + +# Additional env vars +read -r -p "Add custom environment variables? (y/N): " ADD_ENV +if [[ "${ADD_ENV,,}" =~ ^(y|yes)$ ]]; then + while true; do + read -r -p "Enter KEY=VALUE (or press Enter to finish): " CUSTOM_ENV + [[ -z "$CUSTOM_ENV" ]] && break + ENV_VARS+=("$CUSTOM_ENV") + done +fi + +# Privileged mode +read -r -p "Run as privileged container? (y/N): " PRIV_MODE +if [[ "${PRIV_MODE,,}" =~ ^(y|yes)$ ]]; then + UNPRIVILEGED="0" +else + UNPRIVILEGED="1" +fi + +# Auto-start +read -r -p "Start container after creation? (Y/n): " AUTO_START +if [[ "${AUTO_START,,}" =~ ^(n|no)$ ]]; then + START_AFTER="no" +else + START_AFTER="yes" +fi + +# Summary +echo "" +echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" +echo -e "${BL}Container Configuration Summary:${CL}" +echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" +echo -e " Image: $FULL_IMAGE" +echo -e " ID: $VMID" +echo -e " Name: $CT_NAME" +echo -e " CPUs: $CORES" +echo -e " Memory: ${MEMORY}MB" +echo -e " Disk: ${DISK}GB" +echo -e " Storage: $STORAGE" +echo -e " Network: $BRIDGE ($IP_MODE)" +[[ ${#ENV_VARS[@]} -gt 0 ]] && echo -e " Env vars: ${#ENV_VARS[@]} configured" +echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" +echo "" + +read -r -p "Proceed with creation? (Y/n): " CONFIRM +if [[ "${CONFIRM,,}" =~ ^(n|no)$ ]]; then + msg_error "Cancelled by user" + exit 0 +fi + +# Create container +msg_info "Creating container $VMID" + +PCT_CMD="pct create $VMID oci://$FULL_IMAGE" +PCT_CMD+=" --hostname $CT_NAME" +PCT_CMD+=" --cores $CORES" +PCT_CMD+=" --memory $MEMORY" +PCT_CMD+=" --rootfs ${STORAGE}:${DISK}" +PCT_CMD+=" --unprivileged $UNPRIVILEGED" + +if [[ "$IP_MODE" == "static" && -n "$STATIC_IP" ]]; then + PCT_CMD+=" --net0 name=eth0,bridge=$BRIDGE,ip=$STATIC_IP" + [[ -n "$GATEWAY" ]] && PCT_CMD+=",gw=$GATEWAY" +else + PCT_CMD+=" --net0 name=eth0,bridge=$BRIDGE,ip=dhcp" +fi + +if eval "$PCT_CMD" 2>&1; then + msg_ok "Container created" +else + msg_error "Failed to create container" + exit 1 +fi + +# Set environment variables +if [[ ${#ENV_VARS[@]} -gt 0 ]]; then + msg_info "Configuring environment variables" + for env_var in "${ENV_VARS[@]}"; do + if pct set "$VMID" -env "$env_var" &>/dev/null; then + : + else + msg_error "Failed to set: $env_var" + fi + done + msg_ok "Environment variables configured (${#ENV_VARS[@]} variables)" +fi + +# Start container +if [[ "$START_AFTER" == "yes" ]]; then + msg_info "Starting container" + if pct start "$VMID" 2>&1; then + msg_ok "Container started" + + # Wait for network + sleep 3 + CT_IP=$(pct exec "$VMID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "N/A") + + echo "" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e "${BL}Container Information:${CL}" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e " ID: ${GN}$VMID${CL}" + echo -e " Name: ${GN}$CT_NAME${CL}" + echo -e " Image: ${GN}$FULL_IMAGE${CL}" + echo -e " IP: ${GN}$CT_IP${CL}" + echo -e " Status: ${GN}Running${CL}" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo "" + echo -e "${INFO} ${YW}Access console:${CL} pct console $VMID" + echo -e "${INFO} ${YW}View logs:${CL} pct logs $VMID" + echo "" + else + msg_error "Failed to start container" + fi +else + echo "" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e "${BL}Container Information:${CL}" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e " ID: ${GN}$VMID${CL}" + echo -e " Name: ${GN}$CT_NAME${CL}" + echo -e " Image: ${GN}$FULL_IMAGE${CL}" + echo -e " Status: ${YW}Stopped${CL}" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo "" + echo -e "${INFO} ${YW}Start with:${CL} pct start $VMID" + echo "" +fi From dae69a669015d5d15af634c7d482a752283353a9 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:33:12 +0100 Subject: [PATCH 172/378] Update oci-deploy.sh --- tools/pve/oci-deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/oci-deploy.sh b/tools/pve/oci-deploy.sh index 18ab63e2a..6918ff7c2 100644 --- a/tools/pve/oci-deploy.sh +++ b/tools/pve/oci-deploy.sh @@ -271,7 +271,7 @@ fi # Create container msg_info "Creating container $VMID" -PCT_CMD="pct create $VMID oci://$FULL_IMAGE" +PCT_CMD="pct create $VMID --ostemplate oci://$FULL_IMAGE" PCT_CMD+=" --hostname $CT_NAME" PCT_CMD+=" --cores $CORES" PCT_CMD+=" --memory $MEMORY" From 3f490abff1b8850a77098aed58f9228a59281f91 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:35:10 +0100 Subject: [PATCH 173/378] Refactor OCI image handling in oci-deploy.sh --- tools/pve/oci-deploy.sh | 152 +++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 74 deletions(-) diff --git a/tools/pve/oci-deploy.sh b/tools/pve/oci-deploy.sh index 6918ff7c2..705595a1c 100644 --- a/tools/pve/oci-deploy.sh +++ b/tools/pve/oci-deploy.sh @@ -5,8 +5,8 @@ # Source: https://www.proxmox.com/ function header_info { - clear - cat <<"EOF" + clear + cat <<"EOF" ____ ________ ______ __ _ / __ \/ ____/ / / ____/___ ____ / /_____ _(_)___ ___ _____ / / / / / / / / / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/ @@ -30,18 +30,18 @@ APP="OCI-Container" header_info function msg_info() { - local msg="$1" - echo -e "${INFO} ${YW}${msg}...${CL}" + local msg="$1" + echo -e "${INFO} ${YW}${msg}...${CL}" } function msg_ok() { - local msg="$1" - echo -e "${CM} ${GN}${msg}${CL}" + local msg="$1" + echo -e "${CM} ${GN}${msg}${CL}" } function msg_error() { - local msg="$1" - echo -e "${CROSS} ${RD}${msg}${CL}" + local msg="$1" + echo -e "${CROSS} ${RD}${msg}${CL}" } # Check Proxmox version @@ -87,23 +87,26 @@ if [[ -z "${OCI_IMAGE:-}" ]]; then echo -e " ${BL}6)${CL} Custom image" echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" echo "" - + read -r -p "Select option (1-6): " IMAGE_CHOICE - + case $IMAGE_CHOICE in - 1) OCI_IMAGE="nginx:alpine" ;; - 2) OCI_IMAGE="postgres:16-alpine" ;; - 3) OCI_IMAGE="redis:alpine" ;; - 4) OCI_IMAGE="mariadb:latest" ;; - 5) OCI_IMAGE="ghcr.io/linkwarden/linkwarden:latest" ;; - 6) - read -r -p "Enter OCI image (e.g., ghcr.io/user/repo:tag): " OCI_IMAGE - [[ -z "$OCI_IMAGE" ]] && { msg_error "No image specified"; exit 1; } - ;; - *) - msg_error "Invalid choice" + 1) OCI_IMAGE="nginx:alpine" ;; + 2) OCI_IMAGE="postgres:16-alpine" ;; + 3) OCI_IMAGE="redis:alpine" ;; + 4) OCI_IMAGE="mariadb:latest" ;; + 5) OCI_IMAGE="ghcr.io/linkwarden/linkwarden:latest" ;; + 6) + read -r -p "Enter OCI image (e.g., ghcr.io/user/repo:tag): " OCI_IMAGE + [[ -z "$OCI_IMAGE" ]] && { + msg_error "No image specified" exit 1 - ;; + } + ;; + *) + msg_error "Invalid choice" + exit 1 + ;; esac fi @@ -168,55 +171,55 @@ fi declare -a ENV_VARS=() case "$OCI_IMAGE" in - postgres*|postgresql*) +postgres* | postgresql*) + echo "" + msg_info "PostgreSQL requires environment variables" + read -r -p "PostgreSQL password: " -s PG_PASS + echo "" + ENV_VARS+=("POSTGRES_PASSWORD=$PG_PASS") + + read -r -p "Create database (optional): " PG_DB + [[ -n "$PG_DB" ]] && ENV_VARS+=("POSTGRES_DB=$PG_DB") + + read -r -p "PostgreSQL user (optional): " PG_USER + [[ -n "$PG_USER" ]] && ENV_VARS+=("POSTGRES_USER=$PG_USER") + ;; + +mariadb* | mysql*) + echo "" + msg_info "MariaDB/MySQL requires environment variables" + read -r -p "Root password: " -s MYSQL_PASS + echo "" + ENV_VARS+=("MYSQL_ROOT_PASSWORD=$MYSQL_PASS") + + read -r -p "Create database (optional): " MYSQL_DB + [[ -n "$MYSQL_DB" ]] && ENV_VARS+=("MYSQL_DATABASE=$MYSQL_DB") + + read -r -p "Create user (optional): " MYSQL_USER + if [[ -n "$MYSQL_USER" ]]; then + ENV_VARS+=("MYSQL_USER=$MYSQL_USER") + read -r -p "User password: " -s MYSQL_USER_PASS echo "" - msg_info "PostgreSQL requires environment variables" - read -r -p "PostgreSQL password: " -s PG_PASS - echo "" - ENV_VARS+=("POSTGRES_PASSWORD=$PG_PASS") - - read -r -p "Create database (optional): " PG_DB - [[ -n "$PG_DB" ]] && ENV_VARS+=("POSTGRES_DB=$PG_DB") - - read -r -p "PostgreSQL user (optional): " PG_USER - [[ -n "$PG_USER" ]] && ENV_VARS+=("POSTGRES_USER=$PG_USER") - ;; - - mariadb*|mysql*) - echo "" - msg_info "MariaDB/MySQL requires environment variables" - read -r -p "Root password: " -s MYSQL_PASS - echo "" - ENV_VARS+=("MYSQL_ROOT_PASSWORD=$MYSQL_PASS") - - read -r -p "Create database (optional): " MYSQL_DB - [[ -n "$MYSQL_DB" ]] && ENV_VARS+=("MYSQL_DATABASE=$MYSQL_DB") - - read -r -p "Create user (optional): " MYSQL_USER - if [[ -n "$MYSQL_USER" ]]; then - ENV_VARS+=("MYSQL_USER=$MYSQL_USER") - read -r -p "User password: " -s MYSQL_USER_PASS - echo "" - ENV_VARS+=("MYSQL_PASSWORD=$MYSQL_USER_PASS") - fi - ;; - - *linkwarden*) - echo "" - msg_info "Linkwarden configuration" - read -r -p "NEXTAUTH_SECRET (press Enter to generate): " NEXTAUTH_SECRET - if [[ -z "$NEXTAUTH_SECRET" ]]; then - NEXTAUTH_SECRET=$(openssl rand -base64 32) - fi - ENV_VARS+=("NEXTAUTH_SECRET=$NEXTAUTH_SECRET") - - read -r -p "NEXTAUTH_URL [http://localhost:3000]: " NEXTAUTH_URL - NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000} - ENV_VARS+=("NEXTAUTH_URL=$NEXTAUTH_URL") - - read -r -p "DATABASE_URL (PostgreSQL connection string): " DATABASE_URL - [[ -n "$DATABASE_URL" ]] && ENV_VARS+=("DATABASE_URL=$DATABASE_URL") - ;; + ENV_VARS+=("MYSQL_PASSWORD=$MYSQL_USER_PASS") + fi + ;; + +*linkwarden*) + echo "" + msg_info "Linkwarden configuration" + read -r -p "NEXTAUTH_SECRET (press Enter to generate): " NEXTAUTH_SECRET + if [[ -z "$NEXTAUTH_SECRET" ]]; then + NEXTAUTH_SECRET=$(openssl rand -base64 32) + fi + ENV_VARS+=("NEXTAUTH_SECRET=$NEXTAUTH_SECRET") + + read -r -p "NEXTAUTH_URL [http://localhost:3000]: " NEXTAUTH_URL + NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000} + ENV_VARS+=("NEXTAUTH_URL=$NEXTAUTH_URL") + + read -r -p "DATABASE_URL (PostgreSQL connection string): " DATABASE_URL + [[ -n "$DATABASE_URL" ]] && ENV_VARS+=("DATABASE_URL=$DATABASE_URL") + ;; esac # Additional env vars @@ -271,11 +274,12 @@ fi # Create container msg_info "Creating container $VMID" -PCT_CMD="pct create $VMID --ostemplate oci://$FULL_IMAGE" +# Build pct create command +PCT_CMD="pct create $VMID" PCT_CMD+=" --hostname $CT_NAME" PCT_CMD+=" --cores $CORES" PCT_CMD+=" --memory $MEMORY" -PCT_CMD+=" --rootfs ${STORAGE}:${DISK}" +PCT_CMD+=" --rootfs ${STORAGE}:${DISK},oci=${FULL_IMAGE}" PCT_CMD+=" --unprivileged $UNPRIVILEGED" if [[ "$IP_MODE" == "static" && -n "$STATIC_IP" ]]; then @@ -310,11 +314,11 @@ if [[ "$START_AFTER" == "yes" ]]; then msg_info "Starting container" if pct start "$VMID" 2>&1; then msg_ok "Container started" - + # Wait for network sleep 3 CT_IP=$(pct exec "$VMID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "N/A") - + echo "" echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" echo -e "${BL}Container Information:${CL}" From b9bbc3b38a6ac8e10b76d8e3433f3fdb22894aaf Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Mon, 29 Dec 2025 08:43:14 -0800 Subject: [PATCH 174/378] Fix description formatting in netbird.json --- frontend/public/json/netbird.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/netbird.json b/frontend/public/json/netbird.json index 7fcefd128..fe67583b3 100644 --- a/frontend/public/json/netbird.json +++ b/frontend/public/json/netbird.json @@ -11,7 +11,7 @@ "website": "https://netbird.io/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@master/webp/netbird.webp", "config_path": "/etc/netbird/config.json", - "description": "NetBird is an open-source VPN management platform that creates secure peer-to-peer networks using WireGuard. It enables secure connectivity between devices anywhere in the world without complex firewall configurations or port forwarding. NetBird offers features like zero-configuration networking, SSO integration, access control policies, and a centralized management dashboard. It's designed to be simple to deploy and manage, making it ideal for connecting remote teams, securing IoT devices, or building secure infrastructure networks.", + "description": "NetBird is an open source VPN management platform that creates secure peer-to-peer networks using WireGuard. It enables secure connectivity between devices anywhere in the world without complex firewall configurations or port forwarding. NetBird offers features like zero-configuration networking, SSO integration, access control policies, and a centralized management dashboard. It's designed to be simple to deploy and manage, making it ideal for connecting remote teams, securing IoT devices, or building secure infrastructure networks.", "install_methods": [ { "type": "default", From 2e5376c4d61dfc84656390eb56f7e0d2ce7195c1 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Mon, 29 Dec 2025 18:18:02 +0100 Subject: [PATCH 175/378] Revert "rename snowflake systemd service" This reverts commit 9702978d0c8a10d2597b707031b1a97627597718. --- ct/tor-snowflake.sh | 11 +++++------ install/tor-snowflake-install.sh | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index 74ed6be57..a3d97604d 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -35,9 +35,8 @@ function update_script() { RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') VERSION_FILE="/home/${SNOWFLAKEUSER}/.${APP}_version" if [[ ! -f "${VERSION_FILE}" ]] || [[ "${RELEASE}" != "$(cat "${VERSION_FILE}")" ]]; then - msg_info "Stopping ${APP} Service" - systemctl stop ${APP} - msg_ok "Stopped ${APP} Service" + systemctl stop snowflake-proxy + msg_ok "Stopped Service" setup_go @@ -51,9 +50,9 @@ function update_script() { echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_version >/dev/null" msg_ok "Updated ${APP} to v${RELEASE}" - msg_info "Starting ${APP} Service" - systemctl start ${APP} - msg_ok "Started ${APP} Service" + msg_info "Starting Service" + systemctl start snowflake-proxy + msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}." diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index f5b917ce5..4db5cb7fe 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -33,7 +33,7 @@ echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_ver msg_ok "Built Snowflake Proxy v${RELEASE}" msg_info "Creating Service" -cat </etc/systemd/system/${APP}.service +cat </etc/systemd/system/snowflake-proxy.service [Unit] Description=Snowflake Proxy Service Documentation=https://snowflake.torproject.org/ @@ -52,7 +52,7 @@ RestartSec=10 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now ${APP} +systemctl enable -q --now snowflake-proxy msg_ok "Created Service" motd_ssh From 9de46cae2d8b5e42181c4360189f1330f3d6f5dd Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Mon, 29 Dec 2025 18:19:11 +0100 Subject: [PATCH 176/378] hardcode username instead of var --- ct/tor-snowflake.sh | 25 +++++++++++-------------- install/tor-snowflake-install.sh | 27 +++++++++++++-------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index a3d97604d..1c63c4be5 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -15,8 +15,6 @@ var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_nesting="${var_nesting:-0}" -SNOWFLAKEUSER="snowflake" - header_info "$APP" variables color @@ -33,29 +31,28 @@ function update_script() { msg_ok "Updated Container OS" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') - VERSION_FILE="/home/${SNOWFLAKEUSER}/.${APP}_version" - if [[ ! -f "${VERSION_FILE}" ]] || [[ "${RELEASE}" != "$(cat "${VERSION_FILE}")" ]]; then + if [[ ! -f "/home/snowflake/.${APP}_version" ]] || [[ "${RELEASE}" != "$(cat "/home/snowflake/.${APP}_version")" ]]; then systemctl stop snowflake-proxy msg_ok "Stopped Service" setup_go - msg_info "Updating ${APP} to v${RELEASE}" - $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" - $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tar -xzf snowflake.tar.gz" - $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && rm snowflake.tar.gz" - $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && rm -rf .${APP}" - $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" - $STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." - echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_version >/dev/null" - msg_ok "Updated ${APP} to v${RELEASE}" + msg_info "Updating Snowflake" + $STD sudo -H -u snowflake bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" + $STD sudo -H -u snowflake bash -c "cd ~ && tar -xzf snowflake.tar.gz" + $STD sudo -H -u snowflake bash -c "cd ~ && rm snowflake.tar.gz" + $STD sudo -H -u snowflake bash -c "cd ~ && rm -rf .${APP}" + $STD sudo -H -u snowflake bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" + $STD sudo -H -u snowflake bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." + echo "${RELEASE}" | sudo -H -u snowflake bash -c "cd ~ && tee .${APP}_version >/dev/null" + msg_ok "Updated Snowflake to v${RELEASE}" msg_info "Starting Service" systemctl start snowflake-proxy msg_ok "Started Service" msg_ok "Updated successfully!" else - msg_ok "No update required. ${APP} is already at v${RELEASE}." + msg_ok "No update required. Snowflake is already at v${RELEASE}." fi exit } diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index 4db5cb7fe..df2caea6b 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -14,22 +14,21 @@ network_check update_os APP="tor-snowflake" -SNOWFLAKEUSER="snowflake" setup_go -msg_info "Creating ${SNOWFLAKEUSER} user" -useradd -m -r -s /usr/sbin/nologin -d /home/${SNOWFLAKEUSER} ${SNOWFLAKEUSER} -msg_ok "Created ${SNOWFLAKEUSER} user" +msg_info "Creating snowflake user" +useradd -m -r -s /usr/sbin/nologin -d /home/snowflake snowflake +msg_ok "Created snowflake user" msg_info "Building Snowflake" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') -$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" -$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tar -xzf snowflake.tar.gz" -$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && rm snowflake.tar.gz" -$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" -$STD sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." -echo "${RELEASE}" | sudo -H -u ${SNOWFLAKEUSER} bash -c "cd ~ && tee .${APP}_version >/dev/null" +$STD sudo -H -u snowflake bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" +$STD sudo -H -u snowflake bash -c "cd ~ && tar -xzf snowflake.tar.gz" +$STD sudo -H -u snowflake bash -c "cd ~ && rm snowflake.tar.gz" +$STD sudo -H -u snowflake bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" +$STD sudo -H -u snowflake bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." +echo "${RELEASE}" | sudo -H -u snowflake bash -c "cd ~ && tee .${APP}_version >/dev/null" msg_ok "Built Snowflake Proxy v${RELEASE}" msg_info "Creating Service" @@ -42,10 +41,10 @@ Wants=network-online.target [Service] Type=simple -User=${SNOWFLAKEUSER} -Group=${SNOWFLAKEUSER} -WorkingDirectory=/home/${SNOWFLAKEUSER}/.${APP} -ExecStart=/home/${SNOWFLAKEUSER}/.${APP}/proxy/snowflake-proxy -verbose -unsafe-logging +User=snowflake +Group=snowflake +WorkingDirectory=/home/snowflake/.${APP} +ExecStart=/home/snowflake/.${APP}/proxy/snowflake-proxy -verbose -unsafe-logging Restart=always RestartSec=10 From 27227e11fbcfad134ebcd9ba72f6b21861c45f44 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Mon, 29 Dec 2025 18:51:54 +0100 Subject: [PATCH 177/378] fix: incomplete message block --- ct/tor-snowflake.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index 1c63c4be5..155e2163c 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -32,6 +32,7 @@ function update_script() { RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') if [[ ! -f "/home/snowflake/.${APP}_version" ]] || [[ "${RELEASE}" != "$(cat "/home/snowflake/.${APP}_version")" ]]; then + msg_info "Stopping Service" systemctl stop snowflake-proxy msg_ok "Stopped Service" From 8a10af959c40c3424e9e770a6949116c2174aeb1 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Mon, 29 Dec 2025 19:56:08 +0100 Subject: [PATCH 178/378] move app dir back to /opt/tor-snowflake --- ct/tor-snowflake.sh | 17 +++++++++-------- install/tor-snowflake-install.sh | 19 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index 155e2163c..929704417 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -31,7 +31,7 @@ function update_script() { msg_ok "Updated Container OS" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') - if [[ ! -f "/home/snowflake/.${APP}_version" ]] || [[ "${RELEASE}" != "$(cat "/home/snowflake/.${APP}_version")" ]]; then + if [[ ! -f "/opt/tor-snowflake/version" ]] || [[ "${RELEASE}" != "$(cat "/opt/tor-snowflake/version")" ]]; then msg_info "Stopping Service" systemctl stop snowflake-proxy msg_ok "Stopped Service" @@ -39,13 +39,14 @@ function update_script() { setup_go msg_info "Updating Snowflake" - $STD sudo -H -u snowflake bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" - $STD sudo -H -u snowflake bash -c "cd ~ && tar -xzf snowflake.tar.gz" - $STD sudo -H -u snowflake bash -c "cd ~ && rm snowflake.tar.gz" - $STD sudo -H -u snowflake bash -c "cd ~ && rm -rf .${APP}" - $STD sudo -H -u snowflake bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" - $STD sudo -H -u snowflake bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." - echo "${RELEASE}" | sudo -H -u snowflake bash -c "cd ~ && tee .${APP}_version >/dev/null" + $STD bash -c "cd /opt && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" + $STD bash -c "cd /opt && tar -xzf snowflake.tar.gz" + $STD rm -rf /opt/snowflake.tar.gz + $STD rm -rf /opt/tor-snowflake + $STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake + $STD chown -R snowflake:snowflake /opt/tor-snowflake + $STD sudo -H -u snowflake bash -c "cd /opt/tor-snowflake/proxy && go build -o snowflake-proxy ." + echo "${RELEASE}" >/opt/tor-snowflake/version msg_ok "Updated Snowflake to v${RELEASE}" msg_info "Starting Service" diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index df2caea6b..97f3fab95 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -13,8 +13,6 @@ setting_up_container network_check update_os -APP="tor-snowflake" - setup_go msg_info "Creating snowflake user" @@ -23,12 +21,13 @@ msg_ok "Created snowflake user" msg_info "Building Snowflake" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') -$STD sudo -H -u snowflake bash -c "cd ~ && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" -$STD sudo -H -u snowflake bash -c "cd ~ && tar -xzf snowflake.tar.gz" -$STD sudo -H -u snowflake bash -c "cd ~ && rm snowflake.tar.gz" -$STD sudo -H -u snowflake bash -c "cd ~ && mv snowflake-v${RELEASE} .${APP}" -$STD sudo -H -u snowflake bash -c "cd ~/.${APP}/proxy && go build -o snowflake-proxy ." -echo "${RELEASE}" | sudo -H -u snowflake bash -c "cd ~ && tee .${APP}_version >/dev/null" +$STD bash -c "cd /opt && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" +$STD bash -c "cd /opt && tar -xzf snowflake.tar.gz" +$STD rm -rf /opt/snowflake.tar.gz +$STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake +$STD chown -R snowflake:snowflake /opt/tor-snowflake +$STD sudo -H -u snowflake bash -c "cd /opt/tor-snowflake/proxy && go build -o snowflake-proxy ." +echo "${RELEASE}" >/opt/tor-snowflake/version msg_ok "Built Snowflake Proxy v${RELEASE}" msg_info "Creating Service" @@ -43,8 +42,8 @@ Wants=network-online.target Type=simple User=snowflake Group=snowflake -WorkingDirectory=/home/snowflake/.${APP} -ExecStart=/home/snowflake/.${APP}/proxy/snowflake-proxy -verbose -unsafe-logging +WorkingDirectory=/opt/tor-snowflake/proxy +ExecStart=/opt/tor-snowflake/proxy/snowflake-proxy -verbose -unsafe-logging Restart=always RestartSec=10 From f948688dda6963c9eced1eb06203e7d0b105b856 Mon Sep 17 00:00:00 2001 From: Marc Went Date: Mon, 29 Dec 2025 22:43:58 +0100 Subject: [PATCH 179/378] Add wishlist app --- ct/headers/wishlist | 6 +++ ct/wishlist.sh | 78 ++++++++++++++++++++++++++++++ frontend/public/json/wishlist.json | 33 +++++++++++++ install/wishlist-install.sh | 68 ++++++++++++++++++++++++++ 4 files changed, 185 insertions(+) create mode 100644 ct/headers/wishlist create mode 100644 ct/wishlist.sh create mode 100644 frontend/public/json/wishlist.json create mode 100644 install/wishlist-install.sh diff --git a/ct/headers/wishlist b/ct/headers/wishlist new file mode 100644 index 000000000..9571bf8fe --- /dev/null +++ b/ct/headers/wishlist @@ -0,0 +1,6 @@ + _ ___ __ ___ __ +| | / (_)____/ /_ / (_)____/ /_ +| | /| / / / ___/ __ \/ / / ___/ __/ +| |/ |/ / (__ ) / / / / (__ ) /_ +|__/|__/_/____/_/ /_/_/_/____/\__/ + diff --git a/ct/wishlist.sh b/ct/wishlist.sh new file mode 100644 index 000000000..7ab5807f9 --- /dev/null +++ b/ct/wishlist.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: Dunky13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/cmintey/wishlist + +APP="Wishlist" +var_tags="${var_tags:-sharing}" +var_cpu="${var_cpu:-1}" +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/wishlist ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs + + if check_for_gh_release "wishlist" "cmintey/wishlist"; then + msg_info "Stopping Service" + systemctl stop wishlist + msg_ok "Service Stopped" + + cp /opt/wishlist/.env /opt/ + cp -R /opt/wishlist/uploads /opt/ + cp -R /opt/wishlist/data /opt/ + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" + LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") + + + msg_info "Updating ${APP}" + cd /opt/wishlist || exit + + $STD pnpm install + $STD pnpm svelte-kit sync + $STD pnpm prisma generate + $STD sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) + export VERSION="${LATEST_APP_VERSION}" + export SHA="${LATEST_APP_VERSION}" + $STD pnpm run build + $STD pnpm prune --prod + $STD chmod +x /opt/wishlist/entrypoint.sh + + mv /opt/.env /opt/wishlist/.env + mv /opt/uploads /opt/wishlist/uploads + mv /opt/data /opt/wishlist/data + + msg_ok "Updated ${APP}" + + msg_info "Starting Service" + systemctl start wishlist + 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}:3280${CL}" diff --git a/frontend/public/json/wishlist.json b/frontend/public/json/wishlist.json new file mode 100644 index 000000000..fe802a0cf --- /dev/null +++ b/frontend/public/json/wishlist.json @@ -0,0 +1,33 @@ +{ + "name": "Wishlist", + "slug": "wishlist", + "categories": [0], + "date_created": "2025-12-29", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3280, + "documentation": null, + "config_path": "/opt/wishlist/.env", + "website": "https://github.com/cmintey/wishlist", + "logo": "https://github.com/cmintey/wishlist/blob/main/static/android-chrome-512x512.png?raw=true", + "description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!", + "install_methods": [ + { + "type": "default", + "script": "ct/wishlist.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 5, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh new file mode 100644 index 000000000..a9f35ddaf --- /dev/null +++ b/install/wishlist-install.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: Dunky13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/cmintey/wishlist + +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 python3 openssl git caddy +msg_ok "Installed dependencies" + +NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs +fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" +LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") + +msg_info "Installing Wishlist" +cd /opt/wishlist || exit +cat </opt/wishlist/.env + NODE_ENV=production + BODY_SIZE_LIMIT=5000000 + ORIGIN="http://0.0.0.0:3280" # The URL your users will be connecting to + TOKEN_TIME=72 # hours until signup and password reset tokens expire + DEFAULT_CURRENCY=EUR + MAX_IMAGE_SIZE=5000000 # 5 megabytes +EOF +$STD pnpm install +$STD pnpm svelte-kit sync +$STD pnpm prisma generate +$STD sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) +export VERSION="${LATEST_APP_VERSION}" +export SHA="${LATEST_APP_VERSION}" +$STD pnpm run build +$STD pnpm prune --prod +$STD chmod +x /opt/wishlist/entrypoint.sh +msg_ok "Installed Wishlist" + +mkdir -p /opt/wishlist/uploads +mkdir -p /opt/wishlist/data + +msg_info "Creating Service" +cat </etc/systemd/system/wishlist.service +[Unit] +Description=Wishlist Service +After=network.target + +[Service] +WorkingDirectory=/opt/wishlist +ExecStart=/usr/bin/env sh -c './entrypoint.sh' +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now wishlist +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc +echo "Edit /opt/wishlist/.env to customize settings" From 7b20b0fead9d955829be475635121221ff5c2533 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Tue, 30 Dec 2025 10:23:28 +0100 Subject: [PATCH 180/378] simplify download commands --- ct/tor-snowflake.sh | 4 ++-- install/tor-snowflake-install.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index 929704417..cbfa18f13 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -39,8 +39,8 @@ function update_script() { setup_go msg_info "Updating Snowflake" - $STD bash -c "cd /opt && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" - $STD bash -c "cd /opt && tar -xzf snowflake.tar.gz" + $STD curl -fsSL "https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz" -o /opt/snowflake.tar.gz + $STD tar -xzf /opt/snowflake.tar.gz -C /opt $STD rm -rf /opt/snowflake.tar.gz $STD rm -rf /opt/tor-snowflake $STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index 97f3fab95..145580e82 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -21,8 +21,8 @@ msg_ok "Created snowflake user" msg_info "Building Snowflake" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') -$STD bash -c "cd /opt && curl -fsSL 'https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz' -o snowflake.tar.gz" -$STD bash -c "cd /opt && tar -xzf snowflake.tar.gz" +$STD curl -fsSL "https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/archive/v${RELEASE}/snowflake-v${RELEASE}.tar.gz" -o /opt/snowflake.tar.gz +$STD tar -xzf /opt/snowflake.tar.gz -C /opt $STD rm -rf /opt/snowflake.tar.gz $STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake $STD chown -R snowflake:snowflake /opt/tor-snowflake From c4e5ce623d7ae965e25d42388f13109b091e85b9 Mon Sep 17 00:00:00 2001 From: KernelSailor Date: Tue, 30 Dec 2025 10:33:45 +0100 Subject: [PATCH 181/378] change version file location to /opt/tor-snowflake_version.txt --- ct/tor-snowflake.sh | 4 ++-- install/tor-snowflake-install.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index cbfa18f13..ae5086ef2 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -31,7 +31,7 @@ function update_script() { msg_ok "Updated Container OS" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') - if [[ ! -f "/opt/tor-snowflake/version" ]] || [[ "${RELEASE}" != "$(cat "/opt/tor-snowflake/version")" ]]; then + if [[ ! -f "/opt/tor-snowflake_version.txt" ]] || [[ "${RELEASE}" != "$(cat "/opt/tor-snowflake_version.txt")" ]]; then msg_info "Stopping Service" systemctl stop snowflake-proxy msg_ok "Stopped Service" @@ -46,7 +46,7 @@ function update_script() { $STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake $STD chown -R snowflake:snowflake /opt/tor-snowflake $STD sudo -H -u snowflake bash -c "cd /opt/tor-snowflake/proxy && go build -o snowflake-proxy ." - echo "${RELEASE}" >/opt/tor-snowflake/version + echo "${RELEASE}" >/opt/tor-snowflake_version.txt msg_ok "Updated Snowflake to v${RELEASE}" msg_info "Starting Service" diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index 145580e82..8ccb7f999 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -27,7 +27,7 @@ $STD rm -rf /opt/snowflake.tar.gz $STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake $STD chown -R snowflake:snowflake /opt/tor-snowflake $STD sudo -H -u snowflake bash -c "cd /opt/tor-snowflake/proxy && go build -o snowflake-proxy ." -echo "${RELEASE}" >/opt/tor-snowflake/version +echo "${RELEASE}" >/opt/tor-snowflake_version.txt msg_ok "Built Snowflake Proxy v${RELEASE}" msg_info "Creating Service" From ce7ae6fb4e766bbb36fcffd2e8caa4e2af3018fb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Dec 2025 14:20:12 +0100 Subject: [PATCH 182/378] papra --- ct/papra.sh | 48 ++++++++++++++++++ install/papra-install.sh | 104 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 ct/papra.sh create mode 100644 install/papra-install.sh diff --git a/ct/papra.sh b/ct/papra.sh new file mode 100644 index 000000000..c872bb0cb --- /dev/null +++ b/ct/papra.sh @@ -0,0 +1,48 @@ +#!/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://github.com/CorentinTh/papra + +APP="Papra" +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:-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/papra ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + systemctl stop papra + cd /opt/papra + git fetch + git pull + $STD pnpm install --frozen-lockfile + $STD pnpm --filter "@papra/app-client..." run build + $STD pnpm --filter "@papra/app-server..." run build + systemctl start papra + 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/papra-install.sh b/install/papra-install.sh new file mode 100644 index 000000000..335563145 --- /dev/null +++ b/install/papra-install.sh @@ -0,0 +1,104 @@ +#!/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/CorentinTh/papra + +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 \ + git \ + build-essential \ + tesseract-ocr \ + tesseract-ocr-all +msg_ok "Installed Dependencies" + +NODE_VERSION="24" setup_nodejs + +msg_info "Cloning Papra Repository" +cd /opt +RELEASE=$(curl -s https://api.github.com/repos/CorentinTh/papra/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') +$STD git clone --depth=1 --branch ${RELEASE} https://github.com/CorentinTh/papra.git +cd papra +msg_ok "Cloned Papra Repository" + +msg_info "Setup Papra" +export COREPACK_ENABLE_NETWORK=1 +$STD corepack enable +$STD corepack prepare pnpm@10.19.0 --activate +$STD pnpm install --frozen-lockfile --ignore-scripts +$STD pnpm --filter "@papra/app-client..." run build +$STD pnpm --filter "@papra/app-server..." run build +msg_ok "Set up Papra" + +msg_info "Configuring Papra" +CONTAINER_IP=$(hostname -I | awk '{print $1}') +BETTER_AUTH_SECRET=$(openssl rand -hex 32) + +mkdir -p /opt/papra/app-data/db +mkdir -p /opt/papra/app-data/documents + +cat >/opt/papra/.env </etc/systemd/system/papra.service < Date: Tue, 30 Dec 2025 14:21:16 +0100 Subject: [PATCH 183/378] Add Papra app metadata and update install script Added papra.json with metadata for the Papra document management system. Updated papra-install.sh to use the new papra-hq GitHub repository for cloning and release retrieval. --- frontend/public/json/papra.json | 56 +++++++++++++++++++++++++++++++++ install/papra-install.sh | 4 +-- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 frontend/public/json/papra.json diff --git a/frontend/public/json/papra.json b/frontend/public/json/papra.json new file mode 100644 index 000000000..f3f613b2a --- /dev/null +++ b/frontend/public/json/papra.json @@ -0,0 +1,56 @@ +{ + "name": "Papra", + "slug": "papra", + "categories": [ + 12 + ], + "date_created": "2025-12-30", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 1221, + "documentation": "https://github.com/CorentinTh/papra", + "website": "https://github.com/CorentinTh/papra", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/papra.webp", + "config_path": "/opt/papra/.env", + "description": "Papra is a modern, self-hosted document management system with full-text search, OCR support, and automatic document processing. Built with Node.js and featuring a clean web interface for organizing and managing your documents.", + "install_methods": [ + { + "type": "default", + "script": "ct/papra.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 10, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "First visit will prompt you to create an account", + "type": "info" + }, + { + "text": "Tesseract OCR is pre-installed for all languages", + "type": "info" + }, + { + "text": "Documents are stored in /opt/papra/app-data/documents", + "type": "info" + }, + { + "text": "Ingestion folder available at /opt/papra/ingestion for automatic document import", + "type": "info" + }, + { + "text": "Email functionality runs in dry-run mode by default", + "type": "warning" + } + ] +} diff --git a/install/papra-install.sh b/install/papra-install.sh index 335563145..2c698030b 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -25,8 +25,8 @@ NODE_VERSION="24" setup_nodejs msg_info "Cloning Papra Repository" cd /opt -RELEASE=$(curl -s https://api.github.com/repos/CorentinTh/papra/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') -$STD git clone --depth=1 --branch ${RELEASE} https://github.com/CorentinTh/papra.git +RELEASE=$(curl -s https://api.github.com/repos/papra-hq/papra/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') +$STD git clone --depth=1 --branch ${RELEASE} https://github.com/papra-hq/papra.git cd papra msg_ok "Cloned Papra Repository" From 03e2c209638eec6a64be23aae3c133a73fd21c5b Mon Sep 17 00:00:00 2001 From: Marc Went Date: Tue, 30 Dec 2025 15:46:38 +0100 Subject: [PATCH 184/378] implement review suggestions by @CrazyWolf13 --- ct/headers/wishlist | 6 ------ ct/wishlist.sh | 19 +++++++++---------- frontend/public/json/wishlist.json | 11 ++++++++--- install/wishlist-install.sh | 15 +++++++++------ 4 files changed, 26 insertions(+), 25 deletions(-) delete mode 100644 ct/headers/wishlist diff --git a/ct/headers/wishlist b/ct/headers/wishlist deleted file mode 100644 index 9571bf8fe..000000000 --- a/ct/headers/wishlist +++ /dev/null @@ -1,6 +0,0 @@ - _ ___ __ ___ __ -| | / (_)____/ /_ / (_)____/ /_ -| | /| / / / ___/ __ \/ / / ___/ __/ -| |/ |/ / (__ ) / / / / (__ ) /_ -|__/|__/_/____/_/ /_/_/_/____/\__/ - diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 7ab5807f9..963a68537 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.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/Dunky13/ProxmoxVE/refs/heads/feature/wishlist/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: Dunky13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -27,34 +27,33 @@ function update_script() { exit fi - NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs - if check_for_gh_release "wishlist" "cmintey/wishlist"; then + NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs + msg_info "Stopping Service" systemctl stop wishlist - msg_ok "Service Stopped" + msg_ok "Stopped Service" - cp /opt/wishlist/.env /opt/ + cp /opt/wishlist/.env /opt/wishlist.env cp -R /opt/wishlist/uploads /opt/ cp -R /opt/wishlist/data /opt/ CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") - msg_info "Updating ${APP}" - cd /opt/wishlist || exit + cd /opt/wishlist $STD pnpm install $STD pnpm svelte-kit sync $STD pnpm prisma generate - $STD sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) + sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) export VERSION="${LATEST_APP_VERSION}" export SHA="${LATEST_APP_VERSION}" $STD pnpm run build $STD pnpm prune --prod - $STD chmod +x /opt/wishlist/entrypoint.sh + chmod +x /opt/wishlist/entrypoint.sh - mv /opt/.env /opt/wishlist/.env + mv /opt/wishlist.env /opt/wishlist/.env mv /opt/uploads /opt/wishlist/uploads mv /opt/data /opt/wishlist/data diff --git a/frontend/public/json/wishlist.json b/frontend/public/json/wishlist.json index fe802a0cf..ce1e351a9 100644 --- a/frontend/public/json/wishlist.json +++ b/frontend/public/json/wishlist.json @@ -7,10 +7,10 @@ "updateable": true, "privileged": false, "interface_port": 3280, - "documentation": null, + "documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started", "config_path": "/opt/wishlist/.env", "website": "https://github.com/cmintey/wishlist", - "logo": "https://github.com/cmintey/wishlist/blob/main/static/android-chrome-512x512.png?raw=true", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/cmintey-wishlist.png", "description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!", "install_methods": [ { @@ -29,5 +29,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "Edit /opt/wishlist/.env in the LXC container to customize settings. Restart the container after making changes.", + "type": "info" + } + ] } diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index a9f35ddaf..e0c09ede9 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -22,7 +22,8 @@ fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") msg_info "Installing Wishlist" -cd /opt/wishlist || exit +cd /opt/wishlist + cat </opt/wishlist/.env NODE_ENV=production BODY_SIZE_LIMIT=5000000 @@ -31,19 +32,21 @@ cat </opt/wishlist/.env DEFAULT_CURRENCY=EUR MAX_IMAGE_SIZE=5000000 # 5 megabytes EOF + $STD pnpm install $STD pnpm svelte-kit sync $STD pnpm prisma generate -$STD sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) -export VERSION="${LATEST_APP_VERSION}" -export SHA="${LATEST_APP_VERSION}" +sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) + +export VERSION="${LATEST_APP_VERSION}" +export SHA="${LATEST_APP_VERSION}" $STD pnpm run build $STD pnpm prune --prod -$STD chmod +x /opt/wishlist/entrypoint.sh -msg_ok "Installed Wishlist" +chmod +x /opt/wishlist/entrypoint.sh mkdir -p /opt/wishlist/uploads mkdir -p /opt/wishlist/data +msg_ok "Installed Wishlist" msg_info "Creating Service" cat </etc/systemd/system/wishlist.service From c80b5c4f51ab8217a7b8509d64648a68c8fa0ded Mon Sep 17 00:00:00 2001 From: Marc Went Date: Tue, 30 Dec 2025 15:59:30 +0100 Subject: [PATCH 185/378] forgotten one echo, cleaned up --- install/wishlist-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index e0c09ede9..44cb20954 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -68,4 +68,3 @@ msg_ok "Created Service" motd_ssh customize cleanup_lxc -echo "Edit /opt/wishlist/.env to customize settings" From 6dd0011ed1396d9aadb20e4f1454614239539ba2 Mon Sep 17 00:00:00 2001 From: Jordan Tomkinson Date: Thu, 1 Jan 2026 13:32:15 +0000 Subject: [PATCH 186/378] fix update script app check --- ct/tracearr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index c25066a3e..39142d781 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -23,7 +23,7 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -f /lib/systemd/system/tracearr.service ]]; then + if [[ ! -f /etc/systemd/system/tracearr.service ]]; then msg_error "No ${APP} Installation Found!" exit fi From d61b83163b71ffb3713199f421e07f9db413363e Mon Sep 17 00:00:00 2001 From: Jordan Tomkinson Date: Thu, 1 Jan 2026 14:16:48 +0000 Subject: [PATCH 187/378] import build changes from upstream --- ct/tracearr.sh | 15 ++++++++------- install/tracearr-install.sh | 3 ++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index 39142d781..124d9f265 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -56,9 +56,10 @@ function update_script() { cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations cp -rf data /opt/tracearr/data + mkdir -p /opt/tracearr/data/image-cache rm -rf /opt/tracearr.build cd /opt/tracearr - $STD pnpm install --prod --frozen-lockfile + $STD pnpm install --prod --frozen-lockfile --ignore-scripts $STD chown -R tracearr:tracearr /opt/tracearr msg_ok "Built Tracearr" @@ -87,9 +88,9 @@ function update_script() { PG_DB_USER=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d'/' -f3 | cut -d':' -f1) PG_DB_PASS=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d':' -f3 | cut -d'@' -f1) { echo "PostgreSQL Credentials" - echo "Database: $PG_DB_NAME" - echo "User: $PG_DB_USER" - echo "Password: $PG_DB_PASS" + echo "Database Name: $PG_DB_NAME" + echo "Database User: $PG_DB_USER" + echo "Database Password: $PG_DB_PASS" } >/root/tracearr.creds msg_ok "Recreated tracearr.creds file from existing .env" else @@ -97,9 +98,9 @@ function update_script() { exit 1 fi else - PG_DB_NAME=$(grep 'Database:' /root/tracearr.creds | awk '{print $2}') - PG_DB_USER=$(grep 'User:' /root/tracearr.creds | awk '{print $2}') - PG_DB_PASS=$(grep 'Password:' /root/tracearr.creds | awk '{print $2}') + PG_DB_NAME=$(grep 'Database Name:' /root/tracearr.creds | awk '{print $3}') + PG_DB_USER=$(grep 'Database User:' /root/tracearr.creds | awk '{print $3}') + PG_DB_PASS=$(grep 'Database Password:' /root/tracearr.creds | awk '{print $3}') fi cat </data/tracearr/.env DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh index dcbc6e7ad..bc5dd6ee6 100644 --- a/install/tracearr-install.sh +++ b/install/tracearr-install.sh @@ -64,9 +64,10 @@ cp -rf packages/shared/package.json /opt/tracearr/packages/shared/ cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations cp -rf data /opt/tracearr/data +mkdir -p /opt/tracearr/data/image-cache rm -rf /opt/tracearr.build cd /opt/tracearr -$STD pnpm install --prod --frozen-lockfile +$STD pnpm install --prod --frozen-lockfile --ignore-scripts msg_ok "Built Tracearr" msg_info "Configuring Tracearr" From 0e76224d4a57e464e8cccd4e0d32fbd18d1deb6b Mon Sep 17 00:00:00 2001 From: Marc Went Date: Thu, 1 Jan 2026 16:31:58 +0100 Subject: [PATCH 188/378] Load in .env on startup, app doesn't use dotenv apparently --- install/wishlist-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index 44cb20954..d6dbea7ba 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -56,7 +56,7 @@ After=network.target [Service] WorkingDirectory=/opt/wishlist -ExecStart=/usr/bin/env sh -c './entrypoint.sh' +ExecStart=/usr/bin/env bash -c '[ -f /opt/wishlist/.env ] && { set -a; . /opt/wishlist/.env || true; set +a;}; ./entrypoint.sh' Restart=on-failure [Install] From 5a987efb62dd2b99b483ea5a260d6a8899f8a985 Mon Sep 17 00:00:00 2001 From: Marc Went Date: Thu, 1 Jan 2026 20:35:40 +0100 Subject: [PATCH 189/378] all but one request changed - testing last one --- ct/wishlist.sh | 16 +++++++++------- frontend/public/json/wishlist.json | 2 +- install/wishlist-install.sh | 22 ++++++++++++---------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 963a68537..1255759e0 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/Dunky13/ProxmoxVE/refs/heads/feature/wishlist/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: Dunky13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -34,9 +34,11 @@ function update_script() { systemctl stop wishlist msg_ok "Stopped Service" - cp /opt/wishlist/.env /opt/wishlist.env - cp -R /opt/wishlist/uploads /opt/ - cp -R /opt/wishlist/data /opt/ + mkdir -p /opt/wishlist-backup + cp /opt/wishlist/.env /opt/wishlist-backup/.env + cp -R /opt/wishlist/uploads /opt/wishlist-backup/uploads + cp -R /opt/wishlist/data /opt/wishlist-backup/data + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") @@ -53,9 +55,9 @@ function update_script() { $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh - mv /opt/wishlist.env /opt/wishlist/.env - mv /opt/uploads /opt/wishlist/uploads - mv /opt/data /opt/wishlist/data + mv /opt/wishlist-backup/.env /opt/wishlist/.env + mv /opt/wishlist-backup/uploads /opt/wishlist/uploads + mv /opt/wishlist-backup/data /opt/wishlist/data msg_ok "Updated ${APP}" diff --git a/frontend/public/json/wishlist.json b/frontend/public/json/wishlist.json index ce1e351a9..e9b7dba80 100644 --- a/frontend/public/json/wishlist.json +++ b/frontend/public/json/wishlist.json @@ -10,7 +10,7 @@ "documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started", "config_path": "/opt/wishlist/.env", "website": "https://github.com/cmintey/wishlist", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/cmintey-wishlist.png", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp", "description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!", "install_methods": [ { diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index d6dbea7ba..6282d269d 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -14,7 +14,12 @@ network_check update_os msg_info "Installing dependencies" -$STD apt install -y build-essential python3 openssl git caddy +$STD apt install -y \ + build-essential \ + python3 \ + openssl \ + git \ + caddy msg_ok "Installed dependencies" NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs @@ -23,21 +28,18 @@ LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") msg_info "Installing Wishlist" cd /opt/wishlist - cat </opt/wishlist/.env - NODE_ENV=production - BODY_SIZE_LIMIT=5000000 - ORIGIN="http://0.0.0.0:3280" # The URL your users will be connecting to - TOKEN_TIME=72 # hours until signup and password reset tokens expire - DEFAULT_CURRENCY=EUR - MAX_IMAGE_SIZE=5000000 # 5 megabytes +NODE_ENV=production +BODY_SIZE_LIMIT=5000000 +ORIGIN="http://0.0.0.0:3280" # The URL your users will be connecting to +TOKEN_TIME=72 # hours until signup and password reset tokens expire +DEFAULT_CURRENCY=EUR +MAX_IMAGE_SIZE=5000000 # 5 megabytes EOF - $STD pnpm install $STD pnpm svelte-kit sync $STD pnpm prisma generate sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) - export VERSION="${LATEST_APP_VERSION}" export SHA="${LATEST_APP_VERSION}" $STD pnpm run build From 0def93e9d8b42f45cc2c0747f8e375d7fe77010b Mon Sep 17 00:00:00 2001 From: Marc Went Date: Thu, 1 Jan 2026 20:56:56 +0100 Subject: [PATCH 190/378] fix env file loading --- install/wishlist-install.sh | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index 6282d269d..09cde7218 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -28,14 +28,8 @@ LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") msg_info "Installing Wishlist" cd /opt/wishlist -cat </opt/wishlist/.env -NODE_ENV=production -BODY_SIZE_LIMIT=5000000 -ORIGIN="http://0.0.0.0:3280" # The URL your users will be connecting to -TOKEN_TIME=72 # hours until signup and password reset tokens expire -DEFAULT_CURRENCY=EUR -MAX_IMAGE_SIZE=5000000 # 5 megabytes -EOF +cp .env.example .env +echo "NODE_ENV=production" >> /opt/wishlist/.env $STD pnpm install $STD pnpm svelte-kit sync $STD pnpm prisma generate @@ -58,7 +52,8 @@ After=network.target [Service] WorkingDirectory=/opt/wishlist -ExecStart=/usr/bin/env bash -c '[ -f /opt/wishlist/.env ] && { set -a; . /opt/wishlist/.env || true; set +a;}; ./entrypoint.sh' +EnvironmentFile=/opt/wishlist/.env +ExecStart=/usr/bin/env sh -c './entrypoint.sh' Restart=on-failure [Install] From 80579a457ce74a2a4395a386c7b9e44627d48c5c Mon Sep 17 00:00:00 2001 From: Jordan Tomkinson Date: Thu, 1 Jan 2026 20:11:17 +0000 Subject: [PATCH 191/378] remove all .env regeneration logic to satisfy review --- ct/tracearr.sh | 52 ------------------------------------- install/tracearr-install.sh | 17 ++---------- 2 files changed, 2 insertions(+), 67 deletions(-) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index 124d9f265..1a72c2a32 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -64,58 +64,6 @@ function update_script() { msg_ok "Built Tracearr" msg_info "Configuring Tracearr" - if [ ! -d /data/tracearr ]; then - install -d -m 750 -o tracearr -g tracearr /data/tracearr - fi - - if [ -f /data/tracearr/.jwt_secret ]; then - export JWT_SECRET=$(cat /data/tracearr/.jwt_secret) - else - export JWT_SECRET=$(openssl rand -hex 32) - echo "$JWT_SECRET" > /data/tracearr/.jwt_secret - chmod 600 /data/tracearr/.jwt_secret - fi - if [ -f /data/tracearr/.cookie_secret ]; then - export COOKIE_SECRET=$(cat /data/tracearr/.cookie_secret) - else - export COOKIE_SECRET=$(openssl rand -hex 32) - echo "$COOKIE_SECRET" > /data/tracearr/.cookie_secret - chmod 600 /data/tracearr/.cookie_secret - fi - if [ ! -f /root/tracearr.creds ]; then - if [ -f /data/tracearr/.env ]; then - PG_DB_NAME=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d'/' -f4) - PG_DB_USER=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d'/' -f3 | cut -d':' -f1) - PG_DB_PASS=$(grep 'DATABASE_URL=' /data/tracearr/.env | cut -d':' -f3 | cut -d'@' -f1) - { echo "PostgreSQL Credentials" - echo "Database Name: $PG_DB_NAME" - echo "Database User: $PG_DB_USER" - echo "Database Password: $PG_DB_PASS" - } >/root/tracearr.creds - msg_ok "Recreated tracearr.creds file from existing .env" - else - msg_error "No existing tracearr.creds or .env file found. Cannot configure database connection!" - exit 1 - fi - else - PG_DB_NAME=$(grep 'Database Name:' /root/tracearr.creds | awk '{print $3}') - PG_DB_USER=$(grep 'Database User:' /root/tracearr.creds | awk '{print $3}') - PG_DB_PASS=$(grep 'Database Password:' /root/tracearr.creds | awk '{print $3}') - fi - cat </data/tracearr/.env -DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} -REDIS_URL=redis://127.0.0.1:6379 -PORT=3000 -HOST=0.0.0.0 -NODE_ENV=production -TZ=${TZ} -LOG_LEVEL=info -JWT_SECRET=$JWT_SECRET -COOKIE_SECRET=$COOKIE_SECRET -APP_VERSION=$(cat /root/.tracearr) -#CORS_ORIGIN=http://localhost:5173 -#MOBILE_BETA_MODE=true -EOF chmod 600 /data/tracearr/.env chown -R tracearr:tracearr /data/tracearr msg_ok "Configured Tracearr" diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh index bc5dd6ee6..7a3f72277 100644 --- a/install/tracearr-install.sh +++ b/install/tracearr-install.sh @@ -74,21 +74,8 @@ msg_info "Configuring Tracearr" $STD useradd -r -s /bin/false -U tracearr $STD chown -R tracearr:tracearr /opt/tracearr install -d -m 750 -o tracearr -g tracearr /data/tracearr -if [ -f /data/tracearr/.jwt_secret ]; then - export JWT_SECRET=$(cat /data/tracearr/.jwt_secret) -else - export JWT_SECRET=$(openssl rand -hex 32) - echo "$JWT_SECRET" > /data/tracearr/.jwt_secret - chmod 600 /data/tracearr/.jwt_secret -fi - -if [ -f /data/tracearr/.cookie_secret ]; then - export COOKIE_SECRET=$(cat /data/tracearr/.cookie_secret) -else - export COOKIE_SECRET=$(openssl rand -hex 32) - echo "$COOKIE_SECRET" > /data/tracearr/.cookie_secret - chmod 600 /data/tracearr/.cookie_secret -fi +export JWT_SECRET=$(openssl rand -hex 32) +export COOKIE_SECRET=$(openssl rand -hex 32) cat </data/tracearr/.env DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} REDIS_URL=redis://127.0.0.1:6379 From 61ef62b67d381ba5c51f62a06750ab1170c83fb1 Mon Sep 17 00:00:00 2001 From: Marc Went Date: Thu, 1 Jan 2026 21:11:49 +0100 Subject: [PATCH 192/378] next round of review implementation --- ct/wishlist.sh | 6 +++--- install/wishlist-install.sh | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 1255759e0..9a304d5ec 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.sh @@ -55,9 +55,9 @@ function update_script() { $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh - mv /opt/wishlist-backup/.env /opt/wishlist/.env - mv /opt/wishlist-backup/uploads /opt/wishlist/uploads - mv /opt/wishlist-backup/data /opt/wishlist/data + cp /opt/wishlist-backup/.env /opt/wishlist/.env + cp -R /opt/wishlist-backup/uploads /opt/wishlist/uploads + cp -R /opt/wishlist-backup/data /opt/wishlist/data msg_ok "Updated ${APP}" diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index 09cde7218..38a75eb5b 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -18,7 +18,6 @@ $STD apt install -y \ build-essential \ python3 \ openssl \ - git \ caddy msg_ok "Installed dependencies" From faafa260611e935a5650a541233b05d88c418634 Mon Sep 17 00:00:00 2001 From: Marc Went Date: Thu, 1 Jan 2026 21:20:19 +0100 Subject: [PATCH 193/378] fix styling --- ct/wishlist.sh | 7 ++----- install/wishlist-install.sh | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 9a304d5ec..63e9e2db4 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.sh @@ -42,7 +42,7 @@ function update_script() { CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") - msg_info "Updating ${APP}" + msg_info "Updating Wishlist" cd /opt/wishlist $STD pnpm install @@ -54,13 +54,10 @@ function update_script() { $STD pnpm run build $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh - cp /opt/wishlist-backup/.env /opt/wishlist/.env cp -R /opt/wishlist-backup/uploads /opt/wishlist/uploads cp -R /opt/wishlist-backup/data /opt/wishlist/data - - msg_ok "Updated ${APP}" - + msg_ok "Updated Wishlist" msg_info "Starting Service" systemctl start wishlist msg_ok "Started Service" diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index 38a75eb5b..b60b1e13c 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -38,7 +38,6 @@ export SHA="${LATEST_APP_VERSION}" $STD pnpm run build $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh - mkdir -p /opt/wishlist/uploads mkdir -p /opt/wishlist/data msg_ok "Installed Wishlist" From 13e30787a215ced224a765089a1e947db23dbc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benito=20Rodr=C3=ADguez?= Date: Fri, 2 Jan 2026 11:48:37 +0100 Subject: [PATCH 194/378] Investbrain scripts --- ct/investbrain.sh | 117 ++++++++++++++ frontend/public/json/investbrain.json | 58 +++++++ install/investbrain-install.sh | 213 ++++++++++++++++++++++++++ 3 files changed, 388 insertions(+) create mode 100644 ct/investbrain.sh create mode 100644 frontend/public/json/investbrain.json create mode 100644 install/investbrain-install.sh diff --git a/ct/investbrain.sh b/ct/investbrain.sh new file mode 100644 index 000000000..957ab6a2c --- /dev/null +++ b/ct/investbrain.sh @@ -0,0 +1,117 @@ +#!/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: Benito Rodríguez (b3ni) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/investbrainapp/investbrain + +APP="Investbrain" +var_tags="${var_tags:-finance;portfolio;investing}" +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/investbrain ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + PG_VERSION="17" setup_postgresql + + if check_for_gh_release "investbrain" "investbrainapp/investbrain"; then + msg_info "Stopping Services" + systemctl stop nginx + systemctl stop php8.4-fpm + supervisorctl stop all + msg_ok "Services Stopped" + + msg_info "Creating Backup" + rm -f /opt/.env.backup + rm -rf /opt/storage.backup + cp /opt/investbrain/.env /opt/.env.backup + cp -r /opt/investbrain/storage /opt/storage.backup + msg_ok "Created Backup" + + RELEASE=$(curl -fsSL https://api.github.com/repos/investbrainapp/investbrain/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + msg_info "Updating Investbrain to $RELEASE" + rm -rf /opt/investbrain-new + mkdir -p /opt/investbrain-new + curl -fsSL "https://github.com/investbrainapp/investbrain/archive/refs/tags/v${RELEASE}.tar.gz" | tar -xz --strip-components=1 -C /opt/investbrain-new + + cd /opt/investbrain + cp -r /opt/investbrain-new/* /opt/investbrain/ + rm -rf /opt/investbrain/storage + rm -rf /opt/investbrain-new + + cp /opt/.env.backup /opt/investbrain/.env + cp -r /opt/storage.backup/ /opt/investbrain/storage + + chown -R www-data:www-data /opt/investbrain + chmod -R 775 /opt/investbrain/storage + mkdir -p /opt/investbrain/storage/framework/cache/data + mkdir -p /opt/investbrain/storage/framework/sessions + mkdir -p /opt/investbrain/storage/framework/views + mkdir -p /opt/investbrain/storage/logs + mkdir -p /opt/investbrain/bootstrap/cache + chown -R www-data:www-data /opt/investbrain/{storage,bootstrap/cache} + + PHP_VERSION="8.4" PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php + setup_composer + + export COMPOSER_ALLOW_SUPERUSER=1 + $STD composer install --no-interaction --no-dev --optimize-autoloader + + $STD npm install + $STD npm run build + + $STD php artisan storage:link + $STD php artisan migrate --force + + $STD php artisan cache:clear + $STD php artisan view:clear + $STD php artisan route:clear + $STD php artisan event:clear + $STD php artisan route:cache + $STD php artisan event:cache + + chown -R www-data:www-data /opt/investbrain + chmod -R 755 /opt/investbrain/storage /opt/investbrain/bootstrap/cache + echo "${RELEASE}" >/root/.investbrain + + rm -rf /opt/.env.backup /opt/storage.backup + msg_ok "Updated Investbrain" + + msg_info "Starting Services" + systemctl start php8.4-fpm + systemctl start nginx + supervisorctl start all + msg_ok "Services Started" + + msg_ok "Updated Successfully!" + else + msg_ok "No update available" + 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/investbrain.json b/frontend/public/json/investbrain.json new file mode 100644 index 000000000..445562ed0 --- /dev/null +++ b/frontend/public/json/investbrain.json @@ -0,0 +1,58 @@ +{ + "name": "Investbrain", + "slug": "investbrain", + "categories": [23], + "date_created": "2025-12-26", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://github.com/investbrainapp/investbrain", + "website": "https://investbra.in", + "logo": "https://raw.githubusercontent.com/investbrainapp/investbrain/main/investbrain-logo.png", + "config_path": "/opt/investbrain/.env", + "description": "Investbrain is a smart open-source investment tracker that helps you manage, track, and make informed decisions about your investments.", + "install_methods": [ + { + "type": "default", + "script": "ct/investbrain.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Access the UI via http://:8000/register to create the first user.", + "type": "info" + }, + { + "text": "Default market data provider is Yahoo Finance. Configure others in `.env` with `MARKET_DATA_PROVIDER=yahoo,alphavantage`", + "type": "info" + }, + { + "text": "Refresh market data: `cd /opt/investbrain && php artisan refresh:market-data`", + "type": "info" + }, + { + "text": "Enable AI chat by setting `AI_CHAT_ENABLED=true` and `OPENAI_API_KEY` in `.env`", + "type": "info" + }, + { + "text": "View logs: `ls /opt/investbrain/storage/logs/` (daily rotation: laravel-YYYY-MM-DD.log)", + "type": "info" + }, + { + "text": "Database credentials: `cat ~/investbrain.creds`", + "type": "info" + } + ] +} diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh new file mode 100644 index 000000000..7fc57d345 --- /dev/null +++ b/install/investbrain-install.sh @@ -0,0 +1,213 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Benito Rodríguez (b3ni) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/investbrainapp/investbrain + +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 && apt-get install -y \ + nginx \ + supervisor \ + redis-server \ + libfreetype-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + zlib1g-dev \ + libzip-dev \ + libicu-dev \ + libpq-dev +msg_ok "Installed Dependencies" + +PHP_VERSION="8.4" PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php +setup_composer +NODE_VERSION="22" setup_nodejs +PG_VERSION="17" setup_postgresql +PG_DB_NAME="investbrain" PG_DB_USER="investbrain" setup_postgresql_db + +msg_info "Setting up Investbrain" +RELEASE=$(curl -fsSL https://api.github.com/repos/investbrainapp/investbrain/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') +mkdir -p /opt/investbrain +cd /opt/investbrain +curl -fsSL "https://github.com/investbrainapp/investbrain/archive/refs/tags/v${RELEASE}.tar.gz" | tar -xz --strip-components=1 +LOCAL_IP=$(hostname -I | awk '{print $1}') +APP_KEY=$(openssl rand -base64 32) + +cat </opt/investbrain/.env +APP_KEY=base64:${APP_KEY} +APP_PORT=8000 +APP_URL=http://${LOCAL_IP}:8000 +ASSET_URL=http://${LOCAL_IP}:8000 + +LOG_CHANNEL=daily +LOG_LEVEL=warning + +REGISTRATION_ENABLED=true + +AI_CHAT_ENABLED=false +OPENAI_API_KEY= +OPENAI_ORGANIZATION= + +MARKET_DATA_PROVIDER=yahoo +ALPHAVANTAGE_API_KEY= +FINNHUB_API_KEY= +ALPACA_API_KEY= +ALPACA_API_SECRET= +TWELVEDATA_API_SECRET= + +MARKET_DATA_REFRESH=30 +DAILY_CHANGE_TIME= + +DB_CONNECTION=pgsql +DB_HOST=127.0.0.1 +DB_PORT=5432 +DB_DATABASE=${PG_DB_NAME} +DB_USERNAME=${PG_DB_USER} +DB_PASSWORD=${PG_DB_PASS} + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +CACHE_STORE=redis +CACHE_PREFIX= + +SESSION_DRIVER=redis +SESSION_LIFETIME=120 + +QUEUE_CONNECTION=redis + +MAIL_MAILER=log +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_FROM_ADDRESS="investbrain@${LOCAL_IP}" + +VITE_APP_NAME=Investbrain +EOF + +msg_ok "Setup Investbrain" + +msg_info "Installing Investbrain (Patience)" +export COMPOSER_ALLOW_SUPERUSER=1 +$STD composer install --no-interaction --no-dev --optimize-autoloader +$STD npm install +$STD npm run build +msg_ok "Installed Investbrain" + +msg_info "Setting up Storage" +mkdir -p /opt/investbrain/storage/framework/cache +mkdir -p /opt/investbrain/storage/framework/sessions +mkdir -p /opt/investbrain/storage/framework/views +mkdir -p /opt/investbrain/storage/app +mkdir -p /opt/investbrain/storage/logs +chmod -R 775 /opt/investbrain/storage +chown -R www-data:www-data /opt/investbrain/storage +msg_ok "Setup Storage" + +msg_info "Running Migrations" +$STD php artisan migrate --force +$STD php artisan storage:link +msg_ok "Ran Migrations" + +msg_info "Clearing and Caching" +$STD php artisan cache:clear +$STD php artisan view:clear +$STD php artisan route:clear +$STD php artisan event:clear +$STD php artisan route:cache +chown -R www-data:www-data /opt/investbrain +chmod -R 755 /opt/investbrain/bootstrap/cache +echo "${RELEASE}" >/opt/investbrain_version.txt +msg_ok "Cleared and Cached" + +msg_info "Configuring Nginx" +PHPVER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "\n";') +cat </etc/nginx/sites-available/investbrain.conf +server { + listen 8000 default_server; + listen [::]:8000 default_server; + server_name _; + + root /opt/investbrain/public; + index index.php; + + client_max_body_size 50M; + charset utf-8; + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + location / { + try_files \$uri \$uri/ /index.php?\$query_string; + } + + location ~ \.php\$ { + fastcgi_pass unix:/var/run/php/php${PHPVER}-fpm.sock; + fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; + include fastcgi_params; + fastcgi_hide_header X-Powered-By; + fastcgi_read_timeout 300; + } + + location ~ /\.(?!well-known).* { + deny all; + } + + error_log /var/log/nginx/investbrain_error.log; + access_log /var/log/nginx/investbrain_access.log; +} +EOF + +ln -sf /etc/nginx/sites-available/investbrain.conf /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +$STD systemctl reload nginx +msg_ok "Configured Nginx" + +msg_info "Setting up Supervisor" +cat </etc/supervisor/conf.d/investbrain.conf +[program:investbrain-queue] +process_name=%%(program_name)s_%%(process_num)02d +command=php /opt/investbrain/artisan queue:work --sleep=3 --tries=1 --memory=256 --timeout=3600 +user=www-data +autostart=true +autorestart=true +redirect_stderr=true +stdout_logfile=/opt/investbrain/storage/logs/queue.log +stdout_logfile_maxbytes=50MB +stdout_logfile_backups=10 +numprocs=1 +EOF + +$STD supervisorctl reread +$STD supervisorctl update +$STD supervisorctl start all +msg_ok "Setup Supervisor" + +msg_info "Setting up Cron for Scheduler" +cat </etc/cron.d/investbrain-scheduler +* * * * * www-data php /opt/investbrain/artisan schedule:run >> /dev/null 2>&1 +EOF +chmod 644 /etc/cron.d/investbrain-scheduler +$STD systemctl restart cron +msg_ok "Setup Cron for Scheduler" + +{ + echo "" + echo "Investbrain Database Credentials" + echo "Database Name: ${PG_DB_NAME}" + echo "Database User: ${PG_DB_USER}" + echo "Database Password: ${PG_DB_PASS}" +} >>~/investbrain.creds + +motd_ssh +customize +cleanup_lxc From 8c5b7b4adef5feffcc4c5e2ea0795e6beafdc0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benito=20Rodr=C3=ADguez?= Date: Fri, 2 Jan 2026 18:48:25 +0100 Subject: [PATCH 195/378] Refactor Investbrain installation and update script to use fetch_and_deploy_gh_release function --- ct/investbrain.sh | 8 +++----- install/investbrain-install.sh | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/ct/investbrain.sh b/ct/investbrain.sh index 957ab6a2c..8ca359314 100644 --- a/ct/investbrain.sh +++ b/ct/investbrain.sh @@ -31,7 +31,7 @@ function update_script() { PG_VERSION="17" setup_postgresql - if check_for_gh_release "investbrain" "investbrainapp/investbrain"; then + if check_for_gh_release "Investbrain" "investbrainapp/investbrain"; then msg_info "Stopping Services" systemctl stop nginx systemctl stop php8.4-fpm @@ -45,11 +45,10 @@ function update_script() { cp -r /opt/investbrain/storage /opt/storage.backup msg_ok "Created Backup" - RELEASE=$(curl -fsSL https://api.github.com/repos/investbrainapp/investbrain/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - msg_info "Updating Investbrain to $RELEASE" + msg_info "Updating Investbrain" rm -rf /opt/investbrain-new mkdir -p /opt/investbrain-new - curl -fsSL "https://github.com/investbrainapp/investbrain/archive/refs/tags/v${RELEASE}.tar.gz" | tar -xz --strip-components=1 -C /opt/investbrain-new + fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain-new" cd /opt/investbrain cp -r /opt/investbrain-new/* /opt/investbrain/ @@ -89,7 +88,6 @@ function update_script() { chown -R www-data:www-data /opt/investbrain chmod -R 755 /opt/investbrain/storage /opt/investbrain/bootstrap/cache - echo "${RELEASE}" >/root/.investbrain rm -rf /opt/.env.backup /opt/storage.backup msg_ok "Updated Investbrain" diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh index 7fc57d345..8c5dc356f 100644 --- a/install/investbrain-install.sh +++ b/install/investbrain-install.sh @@ -33,11 +33,9 @@ NODE_VERSION="22" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="investbrain" PG_DB_USER="investbrain" setup_postgresql_db -msg_info "Setting up Investbrain" -RELEASE=$(curl -fsSL https://api.github.com/repos/investbrainapp/investbrain/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') +msg_info "Installing Investbrain" mkdir -p /opt/investbrain -cd /opt/investbrain -curl -fsSL "https://github.com/investbrainapp/investbrain/archive/refs/tags/v${RELEASE}.tar.gz" | tar -xz --strip-components=1 +fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" LOCAL_IP=$(hostname -I | awk '{print $1}') APP_KEY=$(openssl rand -base64 32) @@ -126,7 +124,6 @@ $STD php artisan event:clear $STD php artisan route:cache chown -R www-data:www-data /opt/investbrain chmod -R 755 /opt/investbrain/bootstrap/cache -echo "${RELEASE}" >/opt/investbrain_version.txt msg_ok "Cleared and Cached" msg_info "Configuring Nginx" From 3fe25cd0aa31800f5fe396960fbf416862f45d67 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sat, 3 Jan 2026 14:52:07 +0100 Subject: [PATCH 196/378] remove custom domain configuration --- install/kutt-install.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 102b49e34..1926e63cc 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -13,18 +13,12 @@ setting_up_container network_check update_os -read -r -p "${TAB3}Enter the hostname of your Kutt instance (eg kutt.domain.tld): " kutt_host -if [[ "$kutt_host" ]]; then - KUTT_HOST="$kutt_host" -fi - NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" msg_info "Configuring Kutt" cd /opt/kutt cp .example.env ".env" -sed -i "s|DEFAULT_DOMAIN=localhost:3000|DEFAULT_DOMAIN=${KUTT_HOST}|g" ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" $STD npm install $STD npm run migrate From 714320a75417578324188497e50a4490fa9eabee Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sat, 3 Jan 2026 15:25:27 +0100 Subject: [PATCH 197/378] add caddy for ssl termination --- frontend/public/json/kutt.json | 7 +------ install/kutt-install.sh | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/frontend/public/json/kutt.json b/frontend/public/json/kutt.json index d2637f7f1..d93424b33 100644 --- a/frontend/public/json/kutt.json +++ b/frontend/public/json/kutt.json @@ -31,10 +31,5 @@ "username": null, "password": null }, - "notes": [ - { - "text": "A reverse proxy that serves an SSL certificate is required. Otherwise the login will not work.", - "type": "warning" - } - ] + "notes": [] } diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 1926e63cc..3ebd09422 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -13,6 +13,10 @@ setting_up_container network_check update_os +msg_info "Installing Dependencies" +$STD apt-get install -y caddy +msg_ok "Installed Dependencies" + NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" @@ -22,7 +26,19 @@ cp .example.env ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" $STD npm install $STD npm run migrate +msg_ok "Configured Kutt" +msg_info "Configuring SSL" +LXCIP=$(hostname -I | awk '{print $1}') +cat </etc/caddy/Caddyfile +$LXCIP { + reverse_proxy localhost:3000 +} +EOF +$STD systemctl restart caddy +msg_ok "Configured SSL" + +msg_info "Creating Services" cat </etc/systemd/system/kutt.service [Unit] Description=Kutt server @@ -37,8 +53,8 @@ Restart=always [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now kutt -msg_ok "Configured Kutt" +$STD systemctl enable -q --now kutt +msg_ok "Created Services" motd_ssh customize From a5f8709fb72f371753c8e90ce778c765077f0bc0 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sat, 3 Jan 2026 16:26:33 +0100 Subject: [PATCH 198/378] use standardized ip retrieval --- install/kutt-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 3ebd09422..b446125f4 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -29,9 +29,9 @@ $STD npm run migrate msg_ok "Configured Kutt" msg_info "Configuring SSL" -LXCIP=$(hostname -I | awk '{print $1}') +import_local_ip cat </etc/caddy/Caddyfile -$LXCIP { +$LOCAL_IP { reverse_proxy localhost:3000 } EOF From 2bd92fb05a606f12d4e4955fb20c37d3737421e7 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sat, 3 Jan 2026 17:41:49 +0100 Subject: [PATCH 199/378] move ip retrieval call --- install/kutt-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index b446125f4..89d06fcba 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -19,6 +19,7 @@ msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" +import_local_ip msg_info "Configuring Kutt" cd /opt/kutt @@ -29,7 +30,6 @@ $STD npm run migrate msg_ok "Configured Kutt" msg_info "Configuring SSL" -import_local_ip cat </etc/caddy/Caddyfile $LOCAL_IP { reverse_proxy localhost:3000 From eedea88d817ec5900c2ab7682cdcca2732241624 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 3 Jan 2026 18:52:21 +0100 Subject: [PATCH 200/378] Delete ct/homarr.sh --- ct/homarr.sh | 97 ---------------------------------------------------- 1 file changed, 97 deletions(-) delete mode 100644 ct/homarr.sh diff --git a/ct/homarr.sh b/ct/homarr.sh deleted file mode 100644 index 151823f0b..000000000 --- a/ct/homarr.sh +++ /dev/null @@ -1,97 +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://homarr.dev/ - -APP="homarr" -var_tags="${var_tags:-arr;dashboard}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -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/homarr ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "homarr" "Meierschlumpf/homarr"; then - msg_info "Stopping Services (Patience)" - systemctl stop homarr - systemctl stop redis-server - msg_ok "Services Stopped" - - - if ! { grep -q '^REDIS_IS_EXTERNAL=' /opt/homarr/.env 2>/dev/null || grep -q '^REDIS_IS_EXTERNAL=' /opt/homarr.env 2>/dev/null; }; then - msg_info "Fixing old structure" - systemctl disable -q --now nginx - $STD apt install -y musl-dev - # Error: ec 15 21:05:23 homarr run.sh[330]: ⨯ Error: libc.musl-x86_64.so.1: cannot open shared object file: No such file or di> - # Dec 15 21:05:23 homarr run.sh[330]: at ignore-listed frames { - # Dec 15 21:05:23 homarr run.sh[330]: code: 'ERR_DLOPEN_FAILED' - # Dec 15 21:05:23 homarr run.sh[330]: } - ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 - cp /opt/homarr/.env /opt/homarr.env - echo "REDIS_IS_EXTERNAL='true'" >> /opt/homarr.env - sed -i 's|^ExecStart=.*|ExecStart=/opt/homarr/run.sh|' /etc/systemd/system/homarr.service - sed -i 's|^EnvironmentFile=.*|EnvironmentFile=-/opt/homarr.env|' /etc/systemd/system/homarr.service - chown -R redis:redis /appdata/redis - chmod 755 /appdata/redis - mkdir -p /etc/systemd/system/redis-server.service.d/ - cat > /etc/systemd/system/redis-server.service.d/override.conf << 'EOF' -[Service] -ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis -EOF - systemctl daemon-reload - rm /opt/run_homarr.sh - msg_ok "Fixed old structure" - fi - - msg_info "Updating Nodejs" - $STD apt update - $STD apt upgrade nodejs -y - msg_ok "Updated Nodejs" - - NODE_VERSION=$(curl -s https://raw.githubusercontent.com/Meierschlumpf/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') - setup_nodejs - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "latest" "/opt/homarr" "source-amd64.tar.gz" - - msg_info "Updating Homarr" - cp /opt/homarr/redis.conf /etc/redis/redis.conf - rm /etc/nginx/nginx.conf - mkdir -p /etc/nginx/templates - cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf - msg_ok "Updated Homarr" - - msg_info "Starting Services" - chmod +x /opt/homarr/run.sh - systemctl start homarr - systemctl start redis-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}:7575${CL}" From 353c459431d422aa97fe24d66e96abe39555a21e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 3 Jan 2026 18:52:34 +0100 Subject: [PATCH 201/378] Delete install/homarr-install.sh --- install/homarr-install.sh | 88 --------------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 install/homarr-install.sh diff --git a/install/homarr-install.sh b/install/homarr-install.sh deleted file mode 100644 index 85051c2f1..000000000 --- a/install/homarr-install.sh +++ /dev/null @@ -1,88 +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/homarr-labs/homarr - -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 \ - nginx \ - gettext \ - openssl -msg_ok "Installed Dependencies" - -NODE_VERSION=$(curl -s https://raw.githubusercontent.com/Meierschlumpf/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') -setup_nodejs -fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "latest" "/opt/homarr" "source-debian-amd64.tar.gz" - -msg_info "Installing Homarr" -mkdir -p /opt/homarr_db -touch /opt/homarr_db/db.sqlite -SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)" -cd /opt/homarr -cat </opt/homarr.env -DB_DRIVER='better-sqlite3' -DB_DIALECT='sqlite' -SECRET_ENCRYPTION_KEY='${SECRET_ENCRYPTION_KEY}' -DB_URL='/opt/homarr_db/db.sqlite' -TURBO_TELEMETRY_DISABLED=1 -AUTH_PROVIDERS='credentials' -NODE_ENV='production' -REDIS_IS_EXTERNAL='true' -EOF -msg_ok "Installed Homarr" - -msg_info "Copying config files" -mkdir -p /appdata/redis -chown -R redis:redis /appdata/redis -chmod 744 /appdata/redis -cp /opt/homarr/redis.conf /etc/redis/redis.conf -rm /etc/nginx/nginx.conf -mkdir -p /etc/nginx/templates -cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf -echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr -chmod +x /usr/bin/homarr -msg_ok "Copied config files" - -msg_info "Creating Services" -mkdir -p /etc/systemd/system/redis-server.service.d/ -cat > /etc/systemd/system/redis-server.service.d/override.conf << 'EOF' -[Service] -ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis -EOF -cat </etc/systemd/system/homarr.service -[Unit] -Requires=redis-server.service -After=redis-server.service -Description=Homarr Service -After=network.target - -[Service] -Type=exec -WorkingDirectory=/opt/homarr -EnvironmentFile=-/opt/homarr.env -ExecStart=/opt/homarr/run.sh - -[Install] -WantedBy=multi-user.target -EOF -chmod +x /opt/homarr/run.sh -systemctl daemon-reload -systemctl enable -q --now redis-server && sleep 5 -systemctl enable -q --now homarr -systemctl disable -q --now nginx -msg_ok "Created Services" - -motd_ssh -customize -cleanup_lxc From beab56ab9ea81686ccd67bde2f746da8bdd8129e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 3 Jan 2026 18:55:28 +0100 Subject: [PATCH 202/378] kutt: fixes --- install/kutt-install.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 89d06fcba..155acae68 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y caddy +$STD apt install -y caddy msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs @@ -27,16 +27,14 @@ cp .example.env ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" $STD npm install $STD npm run migrate -msg_ok "Configured Kutt" -msg_info "Configuring SSL" cat </etc/caddy/Caddyfile $LOCAL_IP { reverse_proxy localhost:3000 } EOF -$STD systemctl restart caddy -msg_ok "Configured SSL" +systemctl restart caddy +msg_ok "Configured Kutt" msg_info "Creating Services" cat </etc/systemd/system/kutt.service @@ -53,7 +51,7 @@ Restart=always [Install] WantedBy=multi-user.target EOF -$STD systemctl enable -q --now kutt +systemctl enable -q --now kutt msg_ok "Created Services" motd_ssh From eb8f4aadbcdd59eab5b486d051a9f2bf68e165ca Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 3 Jan 2026 19:05:24 +0100 Subject: [PATCH 203/378] kutt: fixes --- install/kutt-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 155acae68..5bd99cb57 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -25,6 +25,7 @@ msg_info "Configuring Kutt" cd /opt/kutt cp .example.env ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" +sed -i "s|DEFAULT_DOMAIN=.*|DEFAULT_DOMAIN=https://$LOCAL_IP|g" ".env" $STD npm install $STD npm run migrate @@ -45,7 +46,7 @@ After=network-online.target [Service] Type=simple WorkingDirectory=/opt/kutt -ExecStart=/usr/bin/node server/server.js --production +ExecStart=/usr/bin/npm start Restart=always [Install] From a7e67ca27ede48764f96dfb8a4c63ffde4b54f53 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 3 Jan 2026 19:13:15 +0100 Subject: [PATCH 204/378] kutt: fix indentation --- ct/kutt.sh | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index d3b843318..e9a9788f9 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -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 [[ ! -d $APP_DIR ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "kutt" "thedevs-network/kutt"; then - msg_info "Stopping services" - systemctl stop kutt - msg_ok "Stopped services" - - fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" - - msg_info "Configuring Kutt" - cd /opt/kutt - $STD npm install - $STD npm run migrate - msg_ok "Configured Kutt" - - msg_info "Starting services" - systemctl start kutt - msg_ok "Started services" - msg_ok "Updated successfully" - fi + if [[ ! -d /opt/kutt ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + if check_for_gh_release "kutt" "thedevs-network/kutt"; then + msg_info "Stopping services" + systemctl stop kutt + msg_ok "Stopped services" + + fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" + + msg_info "Configuring Kutt" + cd /opt/kutt + $STD npm install + $STD npm run migrate + msg_ok "Configured Kutt" + + msg_info "Starting services" + systemctl start kutt + msg_ok "Started services" + msg_ok "Updated successfully" + fi + exit } start From 87a5a2f39b727f42798a648d069dc68fb6a7fd4f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 3 Jan 2026 19:41:10 +0100 Subject: [PATCH 205/378] kutt: add missing backup procedure --- ct/kutt.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index e9a9788f9..09f0a3efe 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -34,7 +34,21 @@ function update_script() { systemctl stop kutt msg_ok "Stopped services" - fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" + msg_info "Backing up data" + mkdir -p /opt/kutt-backup + [ -d /opt/kutt/custom ] && cp -r /opt/kutt/custom /opt/kutt-backup/ + [ -d /opt/kutt/db ] && cp -r /opt/kutt/db /opt/kutt-backup/ + cp /opt/kutt/.env /opt/kutt-backup/ + msg_ok "Backed up data" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" + + msg_info "Restoring data" + [ -d /opt/kutt-backup/custom ] && cp -r /opt/kutt-backup/custom /opt/kutt/ + [ -d /opt/kutt-backup/db ] && cp -r /opt/kutt-backup/db /opt/kutt/ + [ -f /opt/kutt-backup/.env ] && cp /opt/kutt-backup/.env /opt/kutt/ + rm -rf /opt/kutt-backup + msg_ok "Restored data" msg_info "Configuring Kutt" cd /opt/kutt @@ -57,4 +71,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 2cbfeeceeb4d78e2e07131a872eb67e80c3390cf Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Sat, 3 Jan 2026 22:41:38 +0100 Subject: [PATCH 206/378] remove line --- ct/ubuntu.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/ubuntu.sh b/ct/ubuntu.sh index f208d7d09..da8992235 100644 --- a/ct/ubuntu.sh +++ b/ct/ubuntu.sh @@ -6,7 +6,6 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ubuntu.com/ -echo -e "Loading..." APP="Ubuntu" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" From 1153b1f6353af66c2f7ff26ddc26d35881220ce9 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Sat, 3 Jan 2026 22:43:09 +0100 Subject: [PATCH 207/378] typo --- ct/romm.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ct/romm.sh b/ct/romm.sh index f6d4119ca..78302a108 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -31,9 +31,9 @@ function update_script() { fi if check_for_gh_release "romm" "rommapp/romm"; then - msg_info "Stopping ${APP} services" + msg_info "Stopping Services" systemctl stop romm-backend romm-worker romm-scheduler romm-watcher - msg_ok "Stopped ${APP} services" + msg_ok "Stopped Services" msg_info "Backing up configuration" cp /opt/romm/.env /opt/romm/.env.backup @@ -62,11 +62,10 @@ function update_script() { ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets msg_ok "Updated ${APP}" - msg_info "Starting ${APP} services" + msg_info "Starting Services" systemctl start romm-backend romm-worker romm-scheduler romm-watcher - msg_ok "Started ${APP} services" - - msg_ok "Update Successful" + msg_ok "Started Services" + msg_ok "Updated successfully" fi exit } From be669ffdce65daade5d5633e2c1d851b0fa9c313 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 4 Jan 2026 12:04:44 +0100 Subject: [PATCH 208/378] kutt: custom hostname option --- ct/kutt.sh | 2 +- frontend/public/json/kutt.json | 7 +++++- install/kutt-install.sh | 39 +++++++++++++++++++++++----------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index 09f0a3efe..69da55bc1 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -71,4 +71,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}${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:3000 or https://${CL}" diff --git a/frontend/public/json/kutt.json b/frontend/public/json/kutt.json index d93424b33..a62e198d7 100644 --- a/frontend/public/json/kutt.json +++ b/frontend/public/json/kutt.json @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "Kutt needs so be served with an SSL certificate for its login to work. During install, you will be prompted to choose if you want to have Caddy installed for SSL termination, or if you want to use your own reverse proxy. You can also skip SSL termination during install and configure it later.", + "type": "info" + } + ] } diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 5bd99cb57..c741c478f 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -13,28 +13,43 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt install -y caddy -msg_ok "Installed Dependencies" +echo "${TAB3}How would you like to handle SSL termination?" +echo "${TAB3}[i]-Internal (self-signed SSL Certificate) [e]-External (use your own reverse proxy)" +read -rp "${TAB3}Enter your choice (default: i): " ssl_choice +ssl_choice=${ssl_choice:-i} +case "${ssl_choice,,}" in +i) + import_local_ip + DEFAULT_HOST="$LOCAL_IP:3000" + + msg_info "Confiuring Caddy" + $STD apt install -y caddy + cat </etc/caddy/Caddyfile +:3000 { + reverse_proxy localhost:3000 +} +EOF + systemctl restart caddy + msg_ok "Installed Caddy" + ;; +e) + read -r -p "${TAB3}Enter the hostname you want to use for Kutt (eg. kutt.example.com)" custom_host + if [[ "$custom_host" ]]; then + DEFAULT_HOST="$custom_host" + fi + ;; +esac NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" -import_local_ip msg_info "Configuring Kutt" cd /opt/kutt cp .example.env ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" -sed -i "s|DEFAULT_DOMAIN=.*|DEFAULT_DOMAIN=https://$LOCAL_IP|g" ".env" +sed -i "s|DEFAULT_HOST=.*|DEFAULT_HOST=https://$DEFAULT_HOST|g" ".env" $STD npm install $STD npm run migrate - -cat </etc/caddy/Caddyfile -$LOCAL_IP { - reverse_proxy localhost:3000 -} -EOF -systemctl restart caddy msg_ok "Configured Kutt" msg_info "Creating Services" From 121478d4df19d1b3f1c93a3da6345fed62d8b7b9 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 4 Jan 2026 13:04:41 +0100 Subject: [PATCH 209/378] kutt: use port 443 --- ct/kutt.sh | 2 +- frontend/public/json/kutt.json | 4 ++-- install/kutt-install.sh | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index 69da55bc1..fb791b457 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -71,4 +71,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}:3000 or https://${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP} or https://${CL}" diff --git a/frontend/public/json/kutt.json b/frontend/public/json/kutt.json index a62e198d7..f4c8186c7 100644 --- a/frontend/public/json/kutt.json +++ b/frontend/public/json/kutt.json @@ -8,7 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 3000, + "interface_port": 443, "documentation": "https://github.com/thedevs-network/kutt/", "config_path": "/etc/kutt-data/.env", "website": "https://kutt.it", @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "Kutt needs so be served with an SSL certificate for its login to work. During install, you will be prompted to choose if you want to have Caddy installed for SSL termination, or if you want to use your own reverse proxy. You can also skip SSL termination during install and configure it later.", + "text": "Kutt needs so be served with an SSL certificate for its login to work. During install, you will be prompted to choose if you want to have Caddy installed for SSL termination or if you want to use your own reverse proxy (in that case point your reverse porxy to port 3000).", "type": "info" } ] diff --git a/install/kutt-install.sh b/install/kutt-install.sh index c741c478f..43b2aa684 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -20,17 +20,17 @@ ssl_choice=${ssl_choice:-i} case "${ssl_choice,,}" in i) import_local_ip - DEFAULT_HOST="$LOCAL_IP:3000" + DEFAULT_HOST="$LOCAL_IP:443" msg_info "Confiuring Caddy" $STD apt install -y caddy cat </etc/caddy/Caddyfile -:3000 { +:443 { reverse_proxy localhost:3000 } EOF systemctl restart caddy - msg_ok "Installed Caddy" + msg_ok "Configured Caddy" ;; e) read -r -p "${TAB3}Enter the hostname you want to use for Kutt (eg. kutt.example.com)" custom_host From 6c80eedc97f22cbf6932eedb30d824482bb43aa4 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 4 Jan 2026 13:06:45 +0100 Subject: [PATCH 210/378] kutt: fix typo --- install/kutt-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 43b2aa684..9ce877fed 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -22,7 +22,7 @@ i) import_local_ip DEFAULT_HOST="$LOCAL_IP:443" - msg_info "Confiuring Caddy" + msg_info "Configuring Caddy" $STD apt install -y caddy cat </etc/caddy/Caddyfile :443 { @@ -33,7 +33,7 @@ EOF msg_ok "Configured Caddy" ;; e) - read -r -p "${TAB3}Enter the hostname you want to use for Kutt (eg. kutt.example.com)" custom_host + read -r -p "${TAB3}Enter the hostname you want to use for Kutt (eg. kutt.example.com): " custom_host if [[ "$custom_host" ]]; then DEFAULT_HOST="$custom_host" fi @@ -47,7 +47,7 @@ msg_info "Configuring Kutt" cd /opt/kutt cp .example.env ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" -sed -i "s|DEFAULT_HOST=.*|DEFAULT_HOST=https://$DEFAULT_HOST|g" ".env" +sed -i "s|DEFAULT_DOMAIN=.*|DEFAULT_DOMAIN=https://$DEFAULT_HOST|g" ".env" $STD npm install $STD npm run migrate msg_ok "Configured Kutt" From 16934033116f36812999a185f34bf98ef35426df Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 4 Jan 2026 13:29:55 +0100 Subject: [PATCH 211/378] kutt: fix Caddyfile --- install/kutt-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 9ce877fed..4e28b9132 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -25,8 +25,8 @@ i) msg_info "Configuring Caddy" $STD apt install -y caddy cat </etc/caddy/Caddyfile -:443 { - reverse_proxy localhost:3000 +$LOCAL_IP { + reverse_proxy localhost:3000 } EOF systemctl restart caddy From 1d1fdc7d46a90a1298b684f6e85f286dca814c49 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 4 Jan 2026 13:42:23 +0100 Subject: [PATCH 212/378] kutt: remove unnecessary port fom default host --- install/kutt-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 4e28b9132..04efd100b 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -20,7 +20,7 @@ ssl_choice=${ssl_choice:-i} case "${ssl_choice,,}" in i) import_local_ip - DEFAULT_HOST="$LOCAL_IP:443" + DEFAULT_HOST="$LOCAL_IP" msg_info "Configuring Caddy" $STD apt install -y caddy From 1d9b65c0d7bdd836e9225dd8908a5924f87bb82c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 5 Jan 2026 15:05:39 +0100 Subject: [PATCH 213/378] Update romm-install.sh --- install/romm-install.sh | 89 ++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/install/romm-install.sh b/install/romm-install.sh index 2f88f52cb..b566c0c5d 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -17,35 +17,32 @@ update_os msg_info "Installing dependencies" $STD apt-get install -y \ - acl \ - build-essential \ - gcc \ - g++ \ - make \ - git \ - curl \ - libssl-dev \ - libffi-dev \ - libmagic-dev \ - python3-dev \ - python3-pip \ - python3-venv \ - libmariadb3 \ - libmariadb-dev \ - libpq-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - zlib1g-dev \ - liblzma-dev \ - libncurses5-dev \ - libncursesw5-dev \ - redis-server \ - redis-tools \ - p7zip-full \ - tzdata \ - jq \ - nginx + acl \ + build-essential \ + gcc \ + g++ \ + make \ + libssl-dev \ + libffi-dev \ + libmagic-dev \ + python3-dev \ + python3-pip \ + python3-venv \ + libmariadb3 \ + libmariadb-dev \ + libpq-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + zlib1g-dev \ + liblzma-dev \ + libncurses5-dev \ + libncursesw5-dev \ + redis-server \ + redis-tools \ + p7zip-full \ + tzdata \ + nginx msg_ok "Installed dependencies" UV_VERSION="0.7.19" PYTHON_VERSION="3.13" setup_uv @@ -55,11 +52,11 @@ MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db msg_info "Creating directories" mkdir -p /opt/romm \ - /var/lib/romm/config \ - /var/lib/romm/resources \ - /var/lib/romm/assets/{saves,states,screenshots} \ - /var/lib/romm/library/roms \ - /var/lib/romm/library/bios + /var/lib/romm/config \ + /var/lib/romm/resources \ + /var/lib/romm/assets/{saves,states,screenshots} \ + /var/lib/romm/library/roms \ + /var/lib/romm/library/bios msg_ok "Created directories" msg_info "Creating configuration file" @@ -123,16 +120,15 @@ CONFIGEOF chmod 644 /var/lib/romm/config/config.yml msg_ok "Created configuration file" +fetch_and_deploy_gh_release "RetroAchievements" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux.zip" + msg_info "Building RAHasher (RetroAchievements)" -RAHASHER_VERSION="1.8.1" -cd /tmp -git clone --recursive --branch "$RAHASHER_VERSION" --depth 1 https://github.com/RetroAchievements/RALibretro.git -cd RALibretro +cd /opt/RALibretro sed -i '22a #include ' ./src/Util.h sed -i '6a #include ' \ - ./src/libchdr/deps/zlib-1.3.1/gzlib.c \ - ./src/libchdr/deps/zlib-1.3.1/gzread.c \ - ./src/libchdr/deps/zlib-1.3.1/gzwrite.c + ./src/libchdr/deps/zlib-1.3.1/gzlib.c \ + ./src/libchdr/deps/zlib-1.3.1/gzread.c \ + ./src/libchdr/deps/zlib-1.3.1/gzwrite.c $STD make HAVE_CHD=1 -f ./Makefile.RAHasher cp ./bin64/RAHasher /usr/bin/RAHasher chmod +x /usr/bin/RAHasher @@ -180,27 +176,22 @@ EOF chmod 600 /opt/romm/.env msg_ok "Created environment file" -msg_info "Installing backend" +msg_info "Setup Romm backend" cd /opt/romm - -# Limit concurrent downloads to avoid DNS resolution failures in LXC containers -# See: https://github.com/astral-sh/uv/issues/12054 export UV_CONCURRENT_DOWNLOADS=1 $STD uv sync --all-extras cd /opt/romm/backend $STD uv run alembic upgrade head msg_ok "Installed backend" -msg_info "Installing frontend" +msg_info "Setup Romm frontend" cd /opt/romm/frontend $STD npm install $STD npm run build - mkdir -p /opt/romm/frontend/dist/assets/romm ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets -msg_ok "Installed frontend" - +msg_ok "Setup Romm frontend" msg_info "Configuring nginx" cat >/etc/nginx/sites-available/romm <<'EOF' upstream romm_backend { From 61b0ecf7028170b21fbca76cd241b0704959ddcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benito=20Rodr=C3=ADguez?= Date: Mon, 5 Jan 2026 17:20:25 +0100 Subject: [PATCH 214/378] Update Investbrain installation script and improve backup management --- ct/investbrain.sh | 45 +++++-------- frontend/public/json/investbrain.json | 92 +++++++++++---------------- install/investbrain-install.sh | 42 ++---------- 3 files changed, 59 insertions(+), 120 deletions(-) diff --git a/ct/investbrain.sh b/ct/investbrain.sh index 8ca359314..6e3c55c59 100644 --- a/ct/investbrain.sh +++ b/ct/investbrain.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://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: Benito Rodríguez (b3ni) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -29,56 +29,44 @@ function update_script() { exit fi - PG_VERSION="17" setup_postgresql - if check_for_gh_release "Investbrain" "investbrainapp/investbrain"; then msg_info "Stopping Services" - systemctl stop nginx - systemctl stop php8.4-fpm + systemctl stop nginx php8.4-fpm supervisorctl stop all msg_ok "Services Stopped" + PHP_VERSION="8.4" PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php + setup_composer + NODE_VERSION="22" setup_nodejs + PG_VERSION="17" setup_postgresql + msg_info "Creating Backup" rm -f /opt/.env.backup - rm -rf /opt/storage.backup + rm -rf /opt/investbrain_backup cp /opt/investbrain/.env /opt/.env.backup - cp -r /opt/investbrain/storage /opt/storage.backup + cp -r /opt/investbrain/storage /opt/investbrain_backup msg_ok "Created Backup" - msg_info "Updating Investbrain" rm -rf /opt/investbrain-new mkdir -p /opt/investbrain-new fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain-new" + msg_info "Updating Investbrain" cd /opt/investbrain cp -r /opt/investbrain-new/* /opt/investbrain/ rm -rf /opt/investbrain/storage rm -rf /opt/investbrain-new cp /opt/.env.backup /opt/investbrain/.env - cp -r /opt/storage.backup/ /opt/investbrain/storage - - chown -R www-data:www-data /opt/investbrain - chmod -R 775 /opt/investbrain/storage - mkdir -p /opt/investbrain/storage/framework/cache/data - mkdir -p /opt/investbrain/storage/framework/sessions - mkdir -p /opt/investbrain/storage/framework/views - mkdir -p /opt/investbrain/storage/logs - mkdir -p /opt/investbrain/bootstrap/cache - chown -R www-data:www-data /opt/investbrain/{storage,bootstrap/cache} - - PHP_VERSION="8.4" PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php - setup_composer + cp -r /opt/investbrain_backup/ /opt/investbrain/storage + mkdir -p /opt/investbrain/storage/{framework/cache,framework/sessions,framework/views,app,logs} export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-interaction --no-dev --optimize-autoloader - $STD npm install $STD npm run build - $STD php artisan storage:link $STD php artisan migrate --force - $STD php artisan cache:clear $STD php artisan view:clear $STD php artisan route:clear @@ -87,20 +75,17 @@ function update_script() { $STD php artisan event:cache chown -R www-data:www-data /opt/investbrain - chmod -R 755 /opt/investbrain/storage /opt/investbrain/bootstrap/cache + chmod -R 775 /opt/investbrain/storage /opt/investbrain/bootstrap/cache - rm -rf /opt/.env.backup /opt/storage.backup + rm -rf /opt/.env.backup /opt/investbrain_backup msg_ok "Updated Investbrain" msg_info "Starting Services" - systemctl start php8.4-fpm - systemctl start nginx + systemctl start php8.4-fpm nginx supervisorctl start all msg_ok "Services Started" msg_ok "Updated Successfully!" - else - msg_ok "No update available" fi exit } diff --git a/frontend/public/json/investbrain.json b/frontend/public/json/investbrain.json index 445562ed0..19c50e9c6 100644 --- a/frontend/public/json/investbrain.json +++ b/frontend/public/json/investbrain.json @@ -1,58 +1,40 @@ { - "name": "Investbrain", - "slug": "investbrain", - "categories": [23], - "date_created": "2025-12-26", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8000, - "documentation": "https://github.com/investbrainapp/investbrain", - "website": "https://investbra.in", - "logo": "https://raw.githubusercontent.com/investbrainapp/investbrain/main/investbrain-logo.png", - "config_path": "/opt/investbrain/.env", - "description": "Investbrain is a smart open-source investment tracker that helps you manage, track, and make informed decisions about your investments.", - "install_methods": [ - { - "type": "default", - "script": "ct/investbrain.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Access the UI via http://:8000/register to create the first user.", - "type": "info" + "name": "Investbrain", + "slug": "investbrain", + "categories": [ + 23 + ], + "date_created": "2025-12-26", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://github.com/investbrainapp/investbrain", + "website": "https://investbra.in", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/investbrain.webp", + "config_path": "/opt/investbrain/.env", + "description": "Investbrain is a smart open-source investment tracker that helps you manage, track, and make informed decisions about your investments.", + "install_methods": [ + { + "type": "default", + "script": "ct/investbrain.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null }, - { - "text": "Default market data provider is Yahoo Finance. Configure others in `.env` with `MARKET_DATA_PROVIDER=yahoo,alphavantage`", - "type": "info" - }, - { - "text": "Refresh market data: `cd /opt/investbrain && php artisan refresh:market-data`", - "type": "info" - }, - { - "text": "Enable AI chat by setting `AI_CHAT_ENABLED=true` and `OPENAI_API_KEY` in `.env`", - "type": "info" - }, - { - "text": "View logs: `ls /opt/investbrain/storage/logs/` (daily rotation: laravel-YYYY-MM-DD.log)", - "type": "info" - }, - { - "text": "Database credentials: `cat ~/investbrain.creds`", - "type": "info" - } - ] + "notes": [ + { + "text": "Database credentials: `cat ~/investbrain.creds`", + "type": "info" + } + ] } diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh index 8c5dc356f..05dee6c1c 100644 --- a/install/investbrain-install.sh +++ b/install/investbrain-install.sh @@ -33,12 +33,13 @@ NODE_VERSION="22" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="investbrain" PG_DB_USER="investbrain" setup_postgresql_db -msg_info "Installing Investbrain" mkdir -p /opt/investbrain fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" LOCAL_IP=$(hostname -I | awk '{print $1}') APP_KEY=$(openssl rand -base64 32) +msg_info "Installing Investbrain (Patience)" +cd /opt/investbrain cat </opt/investbrain/.env APP_KEY=base64:${APP_KEY} APP_PORT=8000 @@ -91,43 +92,24 @@ MAIL_FROM_ADDRESS="investbrain@${LOCAL_IP}" VITE_APP_NAME=Investbrain EOF - -msg_ok "Setup Investbrain" - -msg_info "Installing Investbrain (Patience)" export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-interaction --no-dev --optimize-autoloader $STD npm install $STD npm run build -msg_ok "Installed Investbrain" - -msg_info "Setting up Storage" -mkdir -p /opt/investbrain/storage/framework/cache -mkdir -p /opt/investbrain/storage/framework/sessions -mkdir -p /opt/investbrain/storage/framework/views -mkdir -p /opt/investbrain/storage/app -mkdir -p /opt/investbrain/storage/logs -chmod -R 775 /opt/investbrain/storage -chown -R www-data:www-data /opt/investbrain/storage -msg_ok "Setup Storage" - -msg_info "Running Migrations" +mkdir -p /opt/investbrain/storage/{framework/cache,framework/sessions,framework/views,app,logs} $STD php artisan migrate --force $STD php artisan storage:link -msg_ok "Ran Migrations" - -msg_info "Clearing and Caching" $STD php artisan cache:clear $STD php artisan view:clear $STD php artisan route:clear $STD php artisan event:clear $STD php artisan route:cache +$STD php artisan event:cache chown -R www-data:www-data /opt/investbrain -chmod -R 755 /opt/investbrain/bootstrap/cache -msg_ok "Cleared and Cached" +chmod -R 775 /opt/investbrain/bootstrap/cache +msg_ok "Installed Investbrain" msg_info "Configuring Nginx" -PHPVER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "\n";') cat </etc/nginx/sites-available/investbrain.conf server { listen 8000 default_server; @@ -148,7 +130,7 @@ server { } location ~ \.php\$ { - fastcgi_pass unix:/var/run/php/php${PHPVER}-fpm.sock; + fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; fastcgi_hide_header X-Powered-By; @@ -163,7 +145,6 @@ server { access_log /var/log/nginx/investbrain_access.log; } EOF - ln -sf /etc/nginx/sites-available/investbrain.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx @@ -183,7 +164,6 @@ stdout_logfile_maxbytes=50MB stdout_logfile_backups=10 numprocs=1 EOF - $STD supervisorctl reread $STD supervisorctl update $STD supervisorctl start all @@ -197,14 +177,6 @@ chmod 644 /etc/cron.d/investbrain-scheduler $STD systemctl restart cron msg_ok "Setup Cron for Scheduler" -{ - echo "" - echo "Investbrain Database Credentials" - echo "Database Name: ${PG_DB_NAME}" - echo "Database User: ${PG_DB_USER}" - echo "Database Password: ${PG_DB_PASS}" -} >>~/investbrain.creds - motd_ssh customize cleanup_lxc From 8c8978892320f24143a424194e0cdc8c2e54754e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benito=20Rodr=C3=ADguez?= Date: Mon, 5 Jan 2026 18:19:46 +0100 Subject: [PATCH 215/378] Update Investbrain installation script --- ct/investbrain.sh | 11 +---------- install/investbrain-install.sh | 7 +++---- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/ct/investbrain.sh b/ct/investbrain.sh index 6e3c55c59..08aa4f382 100644 --- a/ct/investbrain.sh +++ b/ct/investbrain.sh @@ -47,20 +47,13 @@ function update_script() { cp -r /opt/investbrain/storage /opt/investbrain_backup msg_ok "Created Backup" - rm -rf /opt/investbrain-new - mkdir -p /opt/investbrain-new - fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain-new" + fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" msg_info "Updating Investbrain" cd /opt/investbrain - cp -r /opt/investbrain-new/* /opt/investbrain/ rm -rf /opt/investbrain/storage - rm -rf /opt/investbrain-new - cp /opt/.env.backup /opt/investbrain/.env cp -r /opt/investbrain_backup/ /opt/investbrain/storage - mkdir -p /opt/investbrain/storage/{framework/cache,framework/sessions,framework/views,app,logs} - export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-interaction --no-dev --optimize-autoloader $STD npm install @@ -73,10 +66,8 @@ function update_script() { $STD php artisan event:clear $STD php artisan route:cache $STD php artisan event:cache - chown -R www-data:www-data /opt/investbrain chmod -R 775 /opt/investbrain/storage /opt/investbrain/bootstrap/cache - rm -rf /opt/.env.backup /opt/investbrain_backup msg_ok "Updated Investbrain" diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh index 05dee6c1c..cfc80c3e0 100644 --- a/install/investbrain-install.sh +++ b/install/investbrain-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get update && apt-get install -y \ +$STD apt install -y \ nginx \ supervisor \ redis-server \ @@ -33,12 +33,11 @@ NODE_VERSION="22" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="investbrain" PG_DB_USER="investbrain" setup_postgresql_db -mkdir -p /opt/investbrain fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" -LOCAL_IP=$(hostname -I | awk '{print $1}') -APP_KEY=$(openssl rand -base64 32) +import_local_ip msg_info "Installing Investbrain (Patience)" +APP_KEY=$(openssl rand -base64 32) cd /opt/investbrain cat </opt/investbrain/.env APP_KEY=base64:${APP_KEY} From a690b701fb3a092b174a0ffc192d04580bf79ff9 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:31:48 +0100 Subject: [PATCH 216/378] refactor --- ct/investbrain.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ct/investbrain.sh b/ct/investbrain.sh index 08aa4f382..ec4cdb0a3 100644 --- a/ct/investbrain.sh +++ b/ct/investbrain.sh @@ -11,7 +11,7 @@ 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_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -30,12 +30,13 @@ function update_script() { fi if check_for_gh_release "Investbrain" "investbrainapp/investbrain"; then + PHP_VERSION="8.4" msg_info "Stopping Services" - systemctl stop nginx php8.4-fpm + systemctl stop nginx php${PHP_VERSION}-fpm supervisorctl stop all msg_ok "Services Stopped" - PHP_VERSION="8.4" PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php + PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php setup_composer NODE_VERSION="22" setup_nodejs PG_VERSION="17" setup_postgresql @@ -72,7 +73,7 @@ function update_script() { msg_ok "Updated Investbrain" msg_info "Starting Services" - systemctl start php8.4-fpm nginx + systemctl start php${PHP_VERSION}-fpm nginx supervisorctl start all msg_ok "Services Started" From a56d6449d905de5dda9e556d42c91e2ee3399c89 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:32:04 +0100 Subject: [PATCH 217/378] Update investbrain.json --- frontend/public/json/investbrain.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/investbrain.json b/frontend/public/json/investbrain.json index 19c50e9c6..7ed3fbccc 100644 --- a/frontend/public/json/investbrain.json +++ b/frontend/public/json/investbrain.json @@ -23,7 +23,7 @@ "ram": 2048, "hdd": 4, "os": "debian", - "version": "12" + "version": "13" } } ], From 17aefd2f2712f3813d964e973e57ff4f447c3294 Mon Sep 17 00:00:00 2001 From: kwoodland Date: Sun, 28 Dec 2025 23:44:54 +0100 Subject: [PATCH 218/378] feat: add kitchenowl app --- ct/kitchenowl.sh | 84 +++++++++++++++++ frontend/public/json/kitchenowl.json | 35 ++++++++ install/kitchenowl-install.sh | 130 +++++++++++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 ct/kitchenowl.sh create mode 100644 frontend/public/json/kitchenowl.json create mode 100644 install/kitchenowl-install.sh diff --git a/ct/kitchenowl.sh b/ct/kitchenowl.sh new file mode 100644 index 000000000..25d54663f --- /dev/null +++ b/ct/kitchenowl.sh @@ -0,0 +1,84 @@ +#!/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: snazzybean +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/TomBursch/kitchenowl + +APP="KitchenOwl" +var_tags="${var_tags:-food;recipes}" +var_cpu="${var_cpu:-1}" +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/kitchenowl ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "kitchenowl" "TomBursch/kitchenowl"; then + msg_info "Stopping Service" + systemctl stop kitchenowl + msg_ok "Stopped Service" + + if [[ -d /opt/kitchenowl_backup/data ]]; then + msg_ok "Using existing backup from previous update attempt" + else + msg_info "Backing up KitchenOwl" + mkdir -p /opt/kitchenowl_backup + cp -r /opt/kitchenowl/data /opt/kitchenowl_backup/ + cp -f /opt/kitchenowl/kitchenowl.env /opt/kitchenowl_backup/ + msg_ok "Backed up KitchenOwl" + fi + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" + sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" + + msg_info "Restoring KitchenOwl data" + cp -r /opt/kitchenowl_backup/data /opt/kitchenowl/ + cp -f /opt/kitchenowl_backup/kitchenowl.env /opt/kitchenowl/ + rm -rf /opt/kitchenowl_backup + msg_ok "Restored KitchenOwl data" + + msg_info "Installing Dependencies" + cd /opt/kitchenowl/backend + $STD uv sync --frozen + msg_ok "Dependencies installed" + + msg_info "Running Database Migrations" + cd /opt/kitchenowl/backend + set -a + source /opt/kitchenowl/kitchenowl.env + set +a + $STD uv run flask db upgrade + msg_ok "Database Migrations Complete" + + msg_info "Starting Service" + systemctl start kitchenowl + msg_ok "Started Service" + msg_ok "Updated Successfully" + 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}:80${CL}" diff --git a/frontend/public/json/kitchenowl.json b/frontend/public/json/kitchenowl.json new file mode 100644 index 000000000..e6c150616 --- /dev/null +++ b/frontend/public/json/kitchenowl.json @@ -0,0 +1,35 @@ +{ + "name": "KitchenOwl", + "slug": "kitchenowl", + "categories": [ + 13 + ], + "date_created": "2025-12-28", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://docs.kitchenowl.org/", + "website": "https://kitchenowl.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kitchenowl.webp", + "config_path": "/opt/kitchenowl/kitchenowl.env", + "description": "KitchenOwl is a smart self-hosted grocery list and recipe manager with real-time synchronization, recipe management, meal planning, and expense tracking.", + "install_methods": [ + { + "type": "default", + "script": "ct/kitchenowl.sh", + "resources": { + "cpu": 1, + "ram": 2048, + "hdd": 6, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh new file mode 100644 index 000000000..9a7d2da3e --- /dev/null +++ b/install/kitchenowl-install.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: snazzybean +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/TomBursch/kitchenowl + +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 \ + nginx \ + build-essential \ + libpq-dev \ + libffi-dev \ + libssl-dev +msg_ok "Installed Dependencies" + +PYTHON_VERSION="3.12" setup_uv + +CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" +rm -rf /opt/kitchenowl/web +CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" + +msg_info "Setting up KitchenOwl" +cd /opt/kitchenowl/backend +$STD uv sync --frozen +sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py +mkdir -p /nltk_data +$STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng punkt_tab +JWT_SECRET=$(openssl rand -hex 32) +import_local_ip +mkdir -p /opt/kitchenowl/data +cat </opt/kitchenowl/kitchenowl.env +STORAGE_PATH=/opt/kitchenowl/data +JWT_SECRET_KEY=${JWT_SECRET} +NLTK_DATA=/nltk_data +FRONT_URL=http://${LOCAL_IP} +FLASK_APP=wsgi.py +FLASK_ENV=production +EOF +set -a +source /opt/kitchenowl/kitchenowl.env +set +a +$STD uv run flask db upgrade +msg_ok "Set up KitchenOwl" + +msg_info "Creating Systemd Service" +cat </etc/systemd/system/kitchenowl.service +[Unit] +Description=KitchenOwl Backend +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/kitchenowl/backend +EnvironmentFile=/opt/kitchenowl/kitchenowl.env +ExecStart=/usr/local/bin/uv run wsgi.py +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now kitchenowl +msg_ok "Created and Started Service" + +msg_info "Configuring Nginx" +rm -f /etc/nginx/sites-enabled/default +cat <<'EOF' >/etc/nginx/sites-available/kitchenowl.conf +server { + listen 80; + server_name _; + + root /opt/kitchenowl/web; + index index.html; + + client_max_body_size 100M; + + # Security Headers + add_header X-Frame-Options "SAMEORIGIN" 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; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api { + proxy_pass http://127.0.0.1:5000; + 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; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + location /socket.io { + proxy_pass http://127.0.0.1:5000; + 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; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + # WebSocket Timeouts - allow long-lived connections + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + } +} +EOF +ln -sf /etc/nginx/sites-available/kitchenowl.conf /etc/nginx/sites-enabled/ +$STD systemctl reload nginx +msg_ok "Configured Nginx" + +motd_ssh +customize +cleanup_lxc From ab61100ce7794078aa289f2b5e979bc36d648ce5 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 5 Jan 2026 20:42:13 +0100 Subject: [PATCH 219/378] yubal --- .vscode/.shellcheckrc | 2 +- ct/yubal.sh | 66 ++++++++++++++++++++++++ frontend/public/json/yubal.json | 48 +++++++++++++++++ install/yubal-install.sh | 91 +++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 ct/yubal.sh create mode 100644 frontend/public/json/yubal.json create mode 100644 install/yubal-install.sh diff --git a/.vscode/.shellcheckrc b/.vscode/.shellcheckrc index 5cafdf6aa..4631bd3af 100644 --- a/.vscode/.shellcheckrc +++ b/.vscode/.shellcheckrc @@ -1 +1 @@ -disable=SC2034,SC1091,SC2155,SC2086,SC2317,SC2181 +disable=SC2034,SC1091,SC2155,SC2086,SC2317,SC2181,SC2164 diff --git a/ct/yubal.sh b/ct/yubal.sh new file mode 100644 index 000000000..86ccec37e --- /dev/null +++ b/ct/yubal.sh @@ -0,0 +1,66 @@ +#!/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/guillevc/yubal + +APP="Yubal" +var_tags="${var_tags:-music;media}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +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 [[ ! -d /opt/yubal ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "yubal" "guillevc/yubal"; then + msg_info "Stopping Services" + systemctl stop yubal + msg_ok "Stopped Services" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yubal" "guillevc/yubal" "tarball" "latest" "/opt/yubal" + + msg_info "Building Frontend" + cd /opt/yubal/web + $STD bun install --frozen-lockfile + VERSION=$(get_latest_github_release "guillevc/yubal") + $STD VITE_VERSION=$VERSION VITE_COMMIT_SHA=$VERSION VITE_IS_RELEASE=true bun run build + msg_ok "Built Frontend" + + msg_info "Installing Python Dependencies" + cd /opt/yubal + $STD uv sync --no-dev --frozen + msg_ok "Installed Python Dependencies" + + msg_info "Starting Services" + systemctl start yubal + 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}:8000${CL}" diff --git a/frontend/public/json/yubal.json b/frontend/public/json/yubal.json new file mode 100644 index 000000000..24c9f1247 --- /dev/null +++ b/frontend/public/json/yubal.json @@ -0,0 +1,48 @@ +{ + "name": "Yubal", + "slug": "yubal", + "categories": [ + 13 + ], + "date_created": "2026-01-05", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://github.com/guillevc/yubal/blob/master/README.md", + "website": "https://github.com/guillevc/yubal", + "config_path": "/opt/yubal.env", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/yubal.webp", + "description": "Yubal is a self-hosted music management application that helps you organize, tag, and stream your music collection. Features music library organization with Beets, YouTube music downloads, and a modern web interface.", + "install_methods": [ + { + "type": "default", + "script": "ct/yubal.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 15, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "First visit will prompt you to set up your music library", + "type": "info" + }, + { + "text": "Music files should be stored in /opt/yubal/data", + "type": "info" + }, + { + "text": "Configuration can be modified in /opt/yubal.env", + "type": "info" + } + ] +} diff --git a/install/yubal-install.sh b/install/yubal-install.sh new file mode 100644 index 000000000..cd9a819b9 --- /dev/null +++ b/install/yubal-install.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Crazywolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/guillevc/yubal + +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 \ + libssl-dev \ + libffi-dev \ + python3-dev \ + ffmpeg +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/bunx /usr/local/bin/bunx +msg_ok "Installed Bun" + +UV_VERSION="0.7.19" PYTHON_VERSION="3.12" setup_uv + +msg_info "Installing Deno" +curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh +msg_ok "Installed Deno" + +msg_info "Creating directories" +mkdir -p /opt/yubal \ + /opt/yubal/data \ + /opt/yubal/ytdlp +msg_ok "Created directories" + +fetch_and_deploy_gh_release "yubal" "guillevc/yubal" "tarball" "latest" "/opt/yubal" + +msg_info "Building Frontend" +cd /opt/yubal/web +$STD bun install --frozen-lockfile +VERSION=$(get_latest_github_release "guillevc/yubal") +VITE_VERSION=$VERSION VITE_COMMIT_SHA=$VERSION VITE_IS_RELEASE=true $STD bun run build +msg_ok "Built Frontend" + +msg_info "Installing Python Dependencies" +cd /opt/yubal +export UV_CONCURRENT_DOWNLOADS=1 +$STD uv sync --no-dev --frozen +msg_ok "Installed Python Dependencies" + +msg_info "Creating Service" +cat </opt/yubal.env +YUBAL_HOST=0.0.0.0 +YUBAL_PORT=8000 +YUBAL_DATA_DIR=/opt/yubal/data +YUBAL_BEETS_DIR=/opt/yubal/beets +YUBAL_YTDLP_DIR=/opt/yubal/ytdlp +PYTHONUNBUFFERED=1 +EOF +cat </etc/systemd/system/yubal.service +[Unit] +Description=Yubal Music Management +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/yubal +EnvironmentFile=/opt/yubal.env +Environment="PATH=/opt/yubal/.venv/bin:/usr/local/bin:/usr/bin:/bin" +ExecStart=/opt/yubal/.venv/bin/python -m yubal +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now yubal +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 162886ab3323e2c483951d653017c7631c9decbf Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 5 Jan 2026 21:18:35 +0100 Subject: [PATCH 220/378] fix json --- frontend/public/json/yubal.json | 79 ++++++++++++++------------------- 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/frontend/public/json/yubal.json b/frontend/public/json/yubal.json index 24c9f1247..42bc7676d 100644 --- a/frontend/public/json/yubal.json +++ b/frontend/public/json/yubal.json @@ -1,48 +1,35 @@ { - "name": "Yubal", - "slug": "yubal", - "categories": [ - 13 - ], - "date_created": "2026-01-05", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8000, - "documentation": "https://github.com/guillevc/yubal/blob/master/README.md", - "website": "https://github.com/guillevc/yubal", - "config_path": "/opt/yubal.env", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/yubal.webp", - "description": "Yubal is a self-hosted music management application that helps you organize, tag, and stream your music collection. Features music library organization with Beets, YouTube music downloads, and a modern web interface.", - "install_methods": [ - { - "type": "default", - "script": "ct/yubal.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 15, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "First visit will prompt you to set up your music library", - "type": "info" - }, - { - "text": "Music files should be stored in /opt/yubal/data", - "type": "info" - }, - { - "text": "Configuration can be modified in /opt/yubal.env", - "type": "info" - } - ] + "name": "Yubal", + "slug": "yubal", + "categories": [ + 13 + ], + "date_created": "2026-01-05", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://github.com/guillevc/yubal/blob/master/README.md", + "website": "https://github.com/guillevc/yubal", + "config_path": "/opt/yubal.env", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/yubal.webp", + "description": "YouTube Music album downloader with Spotify metadata auto-tagging.", + "install_methods": [ + { + "type": "default", + "script": "ct/yubal.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 15, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } From 0fbbd38c74cc9bd4e91bb72c39d1f55cf3c91e4a Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 5 Jan 2026 21:23:03 +0100 Subject: [PATCH 221/378] fix std --- install/yubal-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/yubal-install.sh b/install/yubal-install.sh index cd9a819b9..bc47d6e2a 100644 --- a/install/yubal-install.sh +++ b/install/yubal-install.sh @@ -32,7 +32,7 @@ msg_ok "Installed Bun" UV_VERSION="0.7.19" PYTHON_VERSION="3.12" setup_uv msg_info "Installing Deno" -curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh +$STD curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh msg_ok "Installed Deno" msg_info "Creating directories" From 3b47aee56b47ded0b71f5466c0925b7a7273f552 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 5 Jan 2026 22:02:42 +0100 Subject: [PATCH 222/378] fix std --- install/yubal-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/yubal-install.sh b/install/yubal-install.sh index bc47d6e2a..187db3aa0 100644 --- a/install/yubal-install.sh +++ b/install/yubal-install.sh @@ -32,7 +32,7 @@ msg_ok "Installed Bun" UV_VERSION="0.7.19" PYTHON_VERSION="3.12" setup_uv msg_info "Installing Deno" -$STD curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh +$STD sh -c "curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh -s -- -y" msg_ok "Installed Deno" msg_info "Creating directories" From c5786b9210c56ffed36d19cf071bdc5750b40051 Mon Sep 17 00:00:00 2001 From: Tobias Date: Tue, 6 Jan 2026 12:36:02 +0100 Subject: [PATCH 223/378] happ new year - well a bit late :D --- .gitattributes | 4 ++++ .github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md | 2 +- .github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh | 4 ++-- .../install/AppName-install.md | 2 +- .../install/AppName-install.sh | 2 +- .../scripts/app-test/pr-alpine-install.func | 4 ++-- .github/workflows/scripts/app-test/pr-build.func | 2 +- .../workflows/scripts/app-test/pr-create-lxc.sh | 2 +- .../workflows/scripts/app-test/pr-install.func | 6 +++--- bak/core_bak/build copy 2.func | 2 +- bak/core_bak/build refactor_menu.func | 2 +- ...d.func - advanced-backup-20251127-154005.func | 2 +- bak/deferred/build.func.backup-20251029-123804 | 2 +- bak/deferred/build.func.backup-20251029-124205 | 2 +- bak/deferred/build.func.backup-20251029-124307 | 2 +- bak/deferred/build.func.backup-20251029-124334 | 2 +- ...build.func.backup-refactoring-20251029-125644 | 2 +- ct/almalinux.sh | 2 +- ct/alpine-ntfy.sh | 2 +- ct/alpine-valkey.sh | 2 +- ct/byparr.sh | 2 +- ct/centos.sh | 2 +- ct/debian.sh | 2 +- ct/deferred/ampache.sh | 2 +- ct/deferred/docspell.sh | 2 +- ct/deferred/jumpserver.sh | 2 +- ct/deferred/kanba.sh | 2 +- ct/deferred/maxun.sh | 2 +- ct/deferred/pixelfed.sh | 2 +- ct/deferred/polaris.sh | 2 +- ct/deferred/roundcubemail.sh | 2 +- ct/deferred/squirrelserversmanager.sh | 2 +- ct/devuan.sh | 2 +- ct/ente.sh | 2 +- ct/fedora.sh | 2 +- ct/freepbx.sh | 2 +- ct/frigate.sh | 2 +- ct/garmin-grafana.sh | 2 +- ct/gentoo.sh | 2 +- ct/hoodik.sh | 6 +++--- ct/investbrain.sh | 2 +- ct/kutt.sh | 2 +- ct/linkwarden.sh | 2 +- ct/manyfold.sh | 2 +- ct/nextexplorer.sh | 2 +- ct/opencloud.sh | 2 +- ct/openeuler.sh | 2 +- ct/opensuse.sh | 2 +- ct/papra.sh | 2 +- ct/piler.sh | 2 +- ct/pixelfed.sh | 2 +- ct/postgresus.sh | 2 +- ct/rockylinux.sh | 2 +- ct/romm.sh | 2 +- ct/rustypaste.sh | 4 ++-- ct/rybbit.sh | 2 +- ct/sportarr.sh | 2 +- ct/transmission-openvpn.sh | 2 +- ct/yubal.sh | 2 +- docs/contribution/GUIDE.md | 4 ++-- docs/contribution/templates_ct/AppName.md | 2 +- docs/contribution/templates_ct/AppName.sh | 4 ++-- .../templates_install/AppName-install.md | 2 +- .../templates_install/AppName-install.sh | 2 +- docs/ct/DETAILED_GUIDE.md | 2 +- docs/install/DETAILED_GUIDE.md | 4 ++-- install/almalinux-install.sh | 2 +- install/alpine-ntfy-install.sh | 2 +- install/alpine-valkey-install.sh | 2 +- install/byparr-install.sh | 2 +- install/centos-install.sh | 2 +- install/cronmaster-install.sh | 2 +- install/debian-install.sh | 2 +- install/deferred/docspell-install.sh | 2 +- install/deferred/jumpserver-install.sh | 2 +- install/deferred/maxun-install.sh | 2 +- install/deferred/maybe_death/kanba-install.sh | 2 +- install/deferred/nimbus-install.sh | 2 +- install/deferred/ocis-install.sh | 2 +- install/deferred/polaris-install.sh | 2 +- install/deferred/timescaledb-install.sh | 2 +- install/deferred/vikunja-install.sh | 2 +- install/devuan-install.sh | 2 +- install/ente-install.sh | 2 +- install/fedora-install.sh | 2 +- install/freepbx-install.sh | 2 +- install/frigate-install.sh | 2 +- install/garmin-grafana-install.sh | 2 +- install/gentoo-install.sh | 2 +- install/homarr-install.sh | 2 +- install/hoodik-install.sh | 2 +- install/investbrain-install.sh | 2 +- install/kutt-install.sh | 2 +- install/linkwarden-install.sh | 2 +- install/manyfold-install.sh | 2 +- install/nextexplorer-install.sh | 2 +- install/opencloud-install.sh | 2 +- install/openeuler-install.sh | 2 +- install/opensuse-install.sh | 2 +- install/papra-install.sh | 2 +- install/piler-install.sh | 2 +- install/pixelfed-install.sh | 2 +- install/postgresus-install.sh | 2 +- install/rockylinux-install.sh | 2 +- install/romm-install.sh | 2 +- install/rustypaste-install.sh | 2 +- install/rybbit-install.sh | 2 +- install/sportarr-install.sh | 2 +- install/transmission-openvpn-install.sh | 2 +- install/yubal-install.sh | 2 +- misc/alpine-install.func | 2 +- misc/api.func | 2 +- misc/bak_december/build.func.pve9-update | 2 +- misc/bak_december/install.func.unified | 2 +- misc/build.func | 2 +- misc/cloud-init.func | 2 +- misc/core.func | 2 +- misc/error_handler.func | 2 +- misc/install.func | 2 +- misc/old_misc/alpine-install.func | 2 +- misc/old_misc/api.func | 2 +- misc/old_misc/core.func | 2 +- misc/vm-core.func | 2 +- tools/addon/_template.sh | 2 +- tools/addon/add-iptag.sh | 12 ++++++------ tools/addon/copyparty.sh | 2 +- tools/addon/filebrowser-quantum.sh | 2 +- tools/addon/filebrowser.sh | 2 +- tools/addon/jellystat.sh | 2 +- tools/pve/add-iptag.sh | 2 +- tools/pve/clean-lxcs.sh | 2 +- tools/pve/execute.sh | 10 +++++----- tools/pve/kernel-clean.sh | 2 +- tools/pve/lxc-delete.sh | 2 +- tools/pve/lxc-update.sh | 16 ++++++++-------- tools/pve/oci-deploy.sh | 10 +++++----- tools/pve/pve-privilege-converter.sh | 2 +- tools/pve/switch_from_VED_to_VE.sh | 2 +- tools/pve/update-apps.sh | 2 +- vm/allstarlink-vm.sh | 2 +- vm/archlinux-vm.sh | 2 +- vm/debian-vm-test-helper.sh | 2 +- vm/debian-vm.sh | 2 +- vm/docker-vm-debug.sh | 2 +- vm/docker-vm.sh | 2 +- vm/docker-vm.sh.bak | 2 +- vm/k3s-vm.sh | 2 +- vm/opnsense-vm.sh | 2 +- vm/ubuntu2204-vm.sh | 2 +- vm/ubuntu2410-vm.sh | 2 +- vm/umbrel-os-vm.sh | 2 +- vm/unifi-os-server-vm.sh | 2 +- 152 files changed, 185 insertions(+), 181 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..c963d3209 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Enforce LF for shell-related files only +*.sh text eol=lf +*.func text eol=lf +*.md text eol=lf diff --git a/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md index d334b0c07..23d821e4d 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md +++ b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md @@ -65,7 +65,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ Example: ```bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] diff --git a/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh index 075fc694b..fbf862e9d 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh +++ b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] @@ -9,7 +9,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ APP="[APP_NAME]" # Name of the app (e.g. Google, Adventurelog, Apache-Guacamole" var_tags="[TAGS]" -# Tags for Proxmox VE, maximum 2 pcs., no spaces allowed, separated by a semicolon ; (e.g. database | adblock;dhcp) +# Tags for Proxmox VE, maximum 2 pcs., no spaces allowed, separated by a semicolon ; (e.g. database | adblock;dhcp) var_cpu="[CPU]" # Number of cores (1-X) (e.g. 4) - default are 2 var_ram="[RAM]" diff --git a/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.md b/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.md index 376956d2f..8e23af37f 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.md +++ b/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.md @@ -57,7 +57,7 @@ Example: ```bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] diff --git a/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh b/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh index 01d725dd7..eb573fa32 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh +++ b/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] diff --git a/.github/workflows/scripts/app-test/pr-alpine-install.func b/.github/workflows/scripts/app-test/pr-alpine-install.func index 9c13a6bc0..95d85cb72 100644 --- a/.github/workflows/scripts/app-test/pr-alpine-install.func +++ b/.github/workflows/scripts/app-test/pr-alpine-install.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -83,4 +83,4 @@ motd_ssh() { customize() { return -} \ No newline at end of file +} diff --git a/.github/workflows/scripts/app-test/pr-build.func b/.github/workflows/scripts/app-test/pr-build.func index 0e136ef04..2f143d3d8 100644 --- a/.github/workflows/scripts/app-test/pr-build.func +++ b/.github/workflows/scripts/app-test/pr-build.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/.github/workflows/scripts/app-test/pr-create-lxc.sh b/.github/workflows/scripts/app-test/pr-create-lxc.sh index 69b45a216..99d6b7742 100644 --- a/.github/workflows/scripts/app-test/pr-create-lxc.sh +++ b/.github/workflows/scripts/app-test/pr-create-lxc.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/.github/workflows/scripts/app-test/pr-install.func b/.github/workflows/scripts/app-test/pr-install.func index 2ff48df0c..8b45f154b 100644 --- a/.github/workflows/scripts/app-test/pr-install.func +++ b/.github/workflows/scripts/app-test/pr-install.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -15,7 +15,7 @@ catch_errors() { error_handler() { local line_number="$1" local command="$2" - local error_message="Failure in line $line_number while executing command '$command'" + local error_message="Failure in line $line_number while executing command '$command'" echo -e "\n$error_message\n" >&2 exit 1 } @@ -90,4 +90,4 @@ motd_ssh() { customize() { return -} \ No newline at end of file +} diff --git a/bak/core_bak/build copy 2.func b/bak/core_bak/build copy 2.func index b0b0404cd..45b304253 100644 --- a/bak/core_bak/build copy 2.func +++ b/bak/core_bak/build copy 2.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # Revision: 1 diff --git a/bak/core_bak/build refactor_menu.func b/bak/core_bak/build refactor_menu.func index 93e3b7cfd..0c37261e2 100644 --- a/bak/core_bak/build refactor_menu.func +++ b/bak/core_bak/build refactor_menu.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # Revision: 1 diff --git a/bak/core_bak/build.func - advanced-backup-20251127-154005.func b/bak/core_bak/build.func - advanced-backup-20251127-154005.func index 2e7920e6b..f7a8acd96 100644 --- a/bak/core_bak/build.func - advanced-backup-20251127-154005.func +++ b/bak/core_bak/build.func - advanced-backup-20251127-154005.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # Revision: 1 diff --git a/bak/deferred/build.func.backup-20251029-123804 b/bak/deferred/build.func.backup-20251029-123804 index 9c8a1fc84..3bd10337c 100644 --- a/bak/deferred/build.func.backup-20251029-123804 +++ b/bak/deferred/build.func.backup-20251029-123804 @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Revision: 1 diff --git a/bak/deferred/build.func.backup-20251029-124205 b/bak/deferred/build.func.backup-20251029-124205 index 7e0556d61..919b0001a 100644 --- a/bak/deferred/build.func.backup-20251029-124205 +++ b/bak/deferred/build.func.backup-20251029-124205 @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Revision: 1 diff --git a/bak/deferred/build.func.backup-20251029-124307 b/bak/deferred/build.func.backup-20251029-124307 index d452f4637..2d514c644 100644 --- a/bak/deferred/build.func.backup-20251029-124307 +++ b/bak/deferred/build.func.backup-20251029-124307 @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Revision: 1 diff --git a/bak/deferred/build.func.backup-20251029-124334 b/bak/deferred/build.func.backup-20251029-124334 index d452f4637..2d514c644 100644 --- a/bak/deferred/build.func.backup-20251029-124334 +++ b/bak/deferred/build.func.backup-20251029-124334 @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Revision: 1 diff --git a/bak/deferred/build.func.backup-refactoring-20251029-125644 b/bak/deferred/build.func.backup-refactoring-20251029-125644 index d452f4637..2d514c644 100644 --- a/bak/deferred/build.func.backup-refactoring-20251029-125644 +++ b/bak/deferred/build.func.backup-refactoring-20251029-125644 @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Revision: 1 diff --git a/ct/almalinux.sh b/ct/almalinux.sh index 955caf418..44de61d49 100644 --- a/ct/almalinux.sh +++ b/ct/almalinux.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://almalinux.org/ diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index 4fc65b5d1..1e39c9f24 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ntfy.sh/ diff --git a/ct/alpine-valkey.sh b/ct/alpine-valkey.sh index 1f7f96298..4b08a945b 100644 --- a/ct/alpine-valkey.sh +++ b/ct/alpine-valkey.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://valkey.io/ diff --git a/ct/byparr.sh b/ct/byparr.sh index 4ebeb3a68..aab1be8a8 100644 --- a/ct/byparr.sh +++ b/ct/byparr.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ThePhaseless/Byparr diff --git a/ct/centos.sh b/ct/centos.sh index a6ee3de84..9c32c8365 100644 --- a/ct/centos.sh +++ b/ct/centos.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.centos.org/centos-stream/ diff --git a/ct/debian.sh b/ct/debian.sh index e5bdd8a3c..8986ec423 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: diff --git a/ct/deferred/ampache.sh b/ct/deferred/ampache.sh index ada18f098..18f2bce41 100644 --- a/ct/deferred/ampache.sh +++ b/ct/deferred/ampache.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: diff --git a/ct/deferred/docspell.sh b/ct/deferred/docspell.sh index 93eb6806f..396b88310 100644 --- a/ct/deferred/docspell.sh +++ b/ct/deferred/docspell.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/community-scripts/ProxmoxVE diff --git a/ct/deferred/jumpserver.sh b/ct/deferred/jumpserver.sh index f512024c9..1213a5581 100644 --- a/ct/deferred/jumpserver.sh +++ b/ct/deferred/jumpserver.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/jumpserver/jumpserver diff --git a/ct/deferred/kanba.sh b/ct/deferred/kanba.sh index 270f5e00f..1c765bea1 100644 --- a/ct/deferred/kanba.sh +++ b/ct/deferred/kanba.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/Kanba-co/kanba diff --git a/ct/deferred/maxun.sh b/ct/deferred/maxun.sh index ab303b763..8374da056 100644 --- a/ct/deferred/maxun.sh +++ b/ct/deferred/maxun.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/getmaxun/maxun diff --git a/ct/deferred/pixelfed.sh b/ct/deferred/pixelfed.sh index 8ddab519c..e02ff0fd9 100644 --- a/ct/deferred/pixelfed.sh +++ b/ct/deferred/pixelfed.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: diff --git a/ct/deferred/polaris.sh b/ct/deferred/polaris.sh index c290d7706..376f216f8 100644 --- a/ct/deferred/polaris.sh +++ b/ct/deferred/polaris.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/agersant/polaris diff --git a/ct/deferred/roundcubemail.sh b/ct/deferred/roundcubemail.sh index 75d0e3bcd..569675fbf 100644 --- a/ct/deferred/roundcubemail.sh +++ b/ct/deferred/roundcubemail.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: diff --git a/ct/deferred/squirrelserversmanager.sh b/ct/deferred/squirrelserversmanager.sh index 4403ab5f0..663822dcd 100644 --- a/ct/deferred/squirrelserversmanager.sh +++ b/ct/deferred/squirrelserversmanager.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: diff --git a/ct/devuan.sh b/ct/devuan.sh index 82bd83f83..b1bc6acee 100644 --- a/ct/devuan.sh +++ b/ct/devuan.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.devuan.org/ diff --git a/ct/ente.sh b/ct/ente.sh index 5733886ca..cf834013f 100644 --- a/ct/ente.sh +++ b/ct/ente.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.debian.org/ diff --git a/ct/fedora.sh b/ct/fedora.sh index df252051e..d79815be7 100644 --- a/ct/fedora.sh +++ b/ct/fedora.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://fedoraproject.org/ diff --git a/ct/freepbx.sh b/ct/freepbx.sh index fd2395c91..2f3a9f36c 100644 --- a/ct/freepbx.sh +++ b/ct/freepbx.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/freepbx/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Arian Nasr (arian-nasr) # Updated by: Javier Pastor (vsc55) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/frigate.sh b/ct/frigate.sh index 9dd49dc19..b70b22083 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Authors: MickLesk (CanbiZ) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://frigate.video/ diff --git a/ct/garmin-grafana.sh b/ct/garmin-grafana.sh index f0742dd82..0d486f795 100644 --- a/ct/garmin-grafana.sh +++ b/ct/garmin-grafana.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/raw/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: aliaksei135 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/arpanghosh8453/garmin-grafana diff --git a/ct/gentoo.sh b/ct/gentoo.sh index fcb006b19..b42a3858c 100644 --- a/ct/gentoo.sh +++ b/ct/gentoo.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.gentoo.org/ diff --git a/ct/hoodik.sh b/ct/hoodik.sh index b692d04f7..89e48de62 100644 --- a/ct/hoodik.sh +++ b/ct/hoodik.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/hudikhq/hoodik @@ -43,13 +43,13 @@ function update_script() { rm -rf /opt/hoodik fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "tarball" "latest" "/opt/hoodik" cd /opt/hoodik - + msg_info "Building Frontend" $STD yarn install --frozen-lockfile $STD yarn wasm-pack $STD yarn web:build msg_ok "Built Frontend" - + msg_info "Building Backend" $STD cargo build --release cp /opt/hoodik/target/release/hoodik /usr/local/bin/hoodik diff --git a/ct/investbrain.sh b/ct/investbrain.sh index ec4cdb0a3..ac9181e0d 100644 --- a/ct/investbrain.sh +++ b/ct/investbrain.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Benito Rodríguez (b3ni) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/investbrainapp/investbrain diff --git a/ct/kutt.sh b/ct/kutt.sh index fb791b457..685d9b362 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: tomfrenzel # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/thedevs-network/kutt diff --git a/ct/linkwarden.sh b/ct/linkwarden.sh index 1d8158e2c..acf7e7138 100644 --- a/ct/linkwarden.sh +++ b/ct/linkwarden.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkwarden.app/ diff --git a/ct/manyfold.sh b/ct/manyfold.sh index a7f359d5f..cef4da3eb 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # Co-Author: SunFlowerOwl # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/nextexplorer.sh b/ct/nextexplorer.sh index 323867052..b82c3d750 100644 --- a/ct/nextexplorer.sh +++ b/ct/nextexplorer.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/vikramsoni2/nextExplorer diff --git a/ct/opencloud.sh b/ct/opencloud.sh index 37396bc75..5528a5924 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://opencloud.eu diff --git a/ct/openeuler.sh b/ct/openeuler.sh index 3600739c2..21b34ec88 100644 --- a/ct/openeuler.sh +++ b/ct/openeuler.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.openeuler.org/ diff --git a/ct/opensuse.sh b/ct/opensuse.sh index 08b45460f..6b82225fc 100644 --- a/ct/opensuse.sh +++ b/ct/opensuse.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.opensuse.org/ diff --git a/ct/papra.sh b/ct/papra.sh index c872bb0cb..79c486546 100644 --- a/ct/papra.sh +++ b/ct/papra.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/CorentinTh/papra diff --git a/ct/piler.sh b/ct/piler.sh index e592f2e3f..3eb1b4148 100644 --- a/ct/piler.sh +++ b/ct/piler.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.mailpiler.org/ diff --git a/ct/pixelfed.sh b/ct/pixelfed.sh index aa93359d1..f506bb1ef 100644 --- a/ct/pixelfed.sh +++ b/ct/pixelfed.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://pixelfed.org/ diff --git a/ct/postgresus.sh b/ct/postgresus.sh index a726e79f5..ba2c8cfee 100644 --- a/ct/postgresus.sh +++ b/ct/postgresus.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/RostislavDugin/postgresus diff --git a/ct/rockylinux.sh b/ct/rockylinux.sh index 5cbd26ee0..733cda5e7 100644 --- a/ct/rockylinux.sh +++ b/ct/rockylinux.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://rockylinux.org/ diff --git a/ct/romm.sh b/ct/romm.sh index 78302a108..38e0b159e 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # Co-author: AlphaLawless # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 527a7b6bd..3b33ded97 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste @@ -45,7 +45,7 @@ function update_script() { sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml $STD cargo build --locked --release msg_ok "Updated rustypaste" - + msg_info "Starting rustypaste" systemctl start rustypaste msg_ok "Started rustypaste" diff --git a/ct/rybbit.sh b/ct/rybbit.sh index 62447664c..60ee32893 100644 --- a/ct/rybbit.sh +++ b/ct/rybbit.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/rybbit-io/rybbit diff --git a/ct/sportarr.sh b/ct/sportarr.sh index 6d11d4aef..4a22c9858 100644 --- a/ct/sportarr.sh +++ b/ct/sportarr.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/Sportarr/Sportarr diff --git a/ct/transmission-openvpn.sh b/ct/transmission-openvpn.sh index 647339241..95938fbeb 100644 --- a/ct/transmission-openvpn.sh +++ b/ct/transmission-openvpn.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: SunFlowerOwl # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/haugene/docker-transmission-openvpn diff --git a/ct/yubal.sh b/ct/yubal.sh index 86ccec37e..d876f8283 100644 --- a/ct/yubal.sh +++ b/ct/yubal.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 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Crazywolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/guillevc/yubal diff --git a/docs/contribution/GUIDE.md b/docs/contribution/GUIDE.md index 757e86c21..b3f8ec839 100644 --- a/docs/contribution/GUIDE.md +++ b/docs/contribution/GUIDE.md @@ -330,7 +330,7 @@ echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:PORT${CL}" ```bash #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT # Source: https://github.com/example/myapp @@ -558,7 +558,7 @@ fi ```bash #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # Co-Author: AnotherAuthor (for collaborative work) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/docs/contribution/templates_ct/AppName.md b/docs/contribution/templates_ct/AppName.md index d334b0c07..23d821e4d 100644 --- a/docs/contribution/templates_ct/AppName.md +++ b/docs/contribution/templates_ct/AppName.md @@ -65,7 +65,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ Example: ```bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] diff --git a/docs/contribution/templates_ct/AppName.sh b/docs/contribution/templates_ct/AppName.sh index 075fc694b..fbf862e9d 100644 --- a/docs/contribution/templates_ct/AppName.sh +++ b/docs/contribution/templates_ct/AppName.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] @@ -9,7 +9,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ APP="[APP_NAME]" # Name of the app (e.g. Google, Adventurelog, Apache-Guacamole" var_tags="[TAGS]" -# Tags for Proxmox VE, maximum 2 pcs., no spaces allowed, separated by a semicolon ; (e.g. database | adblock;dhcp) +# Tags for Proxmox VE, maximum 2 pcs., no spaces allowed, separated by a semicolon ; (e.g. database | adblock;dhcp) var_cpu="[CPU]" # Number of cores (1-X) (e.g. 4) - default are 2 var_ram="[RAM]" diff --git a/docs/contribution/templates_install/AppName-install.md b/docs/contribution/templates_install/AppName-install.md index 376956d2f..8e23af37f 100644 --- a/docs/contribution/templates_install/AppName-install.md +++ b/docs/contribution/templates_install/AppName-install.md @@ -57,7 +57,7 @@ Example: ```bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] diff --git a/docs/contribution/templates_install/AppName-install.sh b/docs/contribution/templates_install/AppName-install.sh index 01d725dd7..eb573fa32 100644 --- a/docs/contribution/templates_install/AppName-install.sh +++ b/docs/contribution/templates_install/AppName-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: [SOURCE_URL] diff --git a/docs/ct/DETAILED_GUIDE.md b/docs/ct/DETAILED_GUIDE.md index ba5a00ad3..928ff55a0 100644 --- a/docs/ct/DETAILED_GUIDE.md +++ b/docs/ct/DETAILED_GUIDE.md @@ -142,7 +142,7 @@ msg_ok "Completed Successfully!\n" ```bash #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/example/project diff --git a/docs/install/DETAILED_GUIDE.md b/docs/install/DETAILED_GUIDE.md index 0ac4fddb5..8a6912518 100644 --- a/docs/install/DETAILED_GUIDE.md +++ b/docs/install/DETAILED_GUIDE.md @@ -85,7 +85,7 @@ RANDOM_UUID # Session UUID for telemetry #!/usr/bin/env bash # [1] Shebang # [2] Copyright/Metadata -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT # Source: https://example.com @@ -118,7 +118,7 @@ cleanup_lxc ```bash #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/application/repo diff --git a/install/almalinux-install.sh b/install/almalinux-install.sh index af557ad4b..0ead2dd88 100644 --- a/install/almalinux-install.sh +++ b/install/almalinux-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://almalinux.org/ diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 04aeddc07..197c46a72 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ntfy.sh/ diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index bac37a357..66501b3eb 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://valkey.io/ diff --git a/install/byparr-install.sh b/install/byparr-install.sh index e71bef1b0..1616b9551 100644 --- a/install/byparr-install.sh +++ b/install/byparr-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ThePhaseless/Byparr diff --git a/install/centos-install.sh b/install/centos-install.sh index ed5d24f2e..8ee318e9d 100644 --- a/install/centos-install.sh +++ b/install/centos-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.centos.org/centos-stream/ diff --git a/install/cronmaster-install.sh b/install/cronmaster-install.sh index c2a4b651e..16b10816a 100644 --- a/install/cronmaster-install.sh +++ b/install/cronmaster-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/fccview/cronmaster diff --git a/install/debian-install.sh b/install/debian-install.sh index 99453e489..45fa3fcf3 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: diff --git a/install/deferred/docspell-install.sh b/install/deferred/docspell-install.sh index 43534d727..794e4c9bb 100644 --- a/install/deferred/docspell-install.sh +++ b/install/deferred/docspell-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | diff --git a/install/deferred/jumpserver-install.sh b/install/deferred/jumpserver-install.sh index 4ccfb09eb..41a13f022 100644 --- a/install/deferred/jumpserver-install.sh +++ b/install/deferred/jumpserver-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/jumpserver/jumpserver diff --git a/install/deferred/maxun-install.sh b/install/deferred/maxun-install.sh index 704ff04dd..130b90dd7 100644 --- a/install/deferred/maxun-install.sh +++ b/install/deferred/maxun-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/getmaxun/maxun diff --git a/install/deferred/maybe_death/kanba-install.sh b/install/deferred/maybe_death/kanba-install.sh index 10a306ddc..55027ce4f 100644 --- a/install/deferred/maybe_death/kanba-install.sh +++ b/install/deferred/maybe_death/kanba-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/Kanba-co/kanba diff --git a/install/deferred/nimbus-install.sh b/install/deferred/nimbus-install.sh index c49430481..53ba6b6cb 100644 --- a/install/deferred/nimbus-install.sh +++ b/install/deferred/nimbus-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/arunavo4/gitea-mirror diff --git a/install/deferred/ocis-install.sh b/install/deferred/ocis-install.sh index 987b0397e..1214dc3a1 100644 --- a/install/deferred/ocis-install.sh +++ b/install/deferred/ocis-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: diff --git a/install/deferred/polaris-install.sh b/install/deferred/polaris-install.sh index 4f4e77277..8bc6eccf8 100644 --- a/install/deferred/polaris-install.sh +++ b/install/deferred/polaris-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/agersant/polaris diff --git a/install/deferred/timescaledb-install.sh b/install/deferred/timescaledb-install.sh index edcb20faa..aa66a349a 100644 --- a/install/deferred/timescaledb-install.sh +++ b/install/deferred/timescaledb-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/deferred/vikunja-install.sh b/install/deferred/vikunja-install.sh index f56d073d3..7774bd2c7 100644 --- a/install/deferred/vikunja-install.sh +++ b/install/deferred/vikunja-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://vikunja.io/ diff --git a/install/devuan-install.sh b/install/devuan-install.sh index 366f394fd..cbd60de17 100644 --- a/install/devuan-install.sh +++ b/install/devuan-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.devuan.org/ diff --git a/install/ente-install.sh b/install/ente-install.sh index 9a4fbdee6..fa77381d8 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ente-io/ente diff --git a/install/fedora-install.sh b/install/fedora-install.sh index d59592745..e9f1609b0 100644 --- a/install/fedora-install.sh +++ b/install/fedora-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://fedoraproject.org/ diff --git a/install/freepbx-install.sh b/install/freepbx-install.sh index 4a4bd2715..53f9145de 100644 --- a/install/freepbx-install.sh +++ b/install/freepbx-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Arian Nasr (arian-nasr) # Updated by: Javier Pastor (vsc55) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 4d09ec7ac..26822920c 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Authors: MickLesk (CanbiZ) # Co-Authors: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/garmin-grafana-install.sh b/install/garmin-grafana-install.sh index 70070f8c1..46853fae5 100644 --- a/install/garmin-grafana-install.sh +++ b/install/garmin-grafana-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: aliaksei135 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/arpanghosh8453/garmin-grafana diff --git a/install/gentoo-install.sh b/install/gentoo-install.sh index 025226dfd..afa4ccbc7 100644 --- a/install/gentoo-install.sh +++ b/install/gentoo-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.gentoo.org/ diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 85051c2f1..1ead7a69e 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/homarr-labs/homarr diff --git a/install/hoodik-install.sh b/install/hoodik-install.sh index 1a88f54e9..4e81df1b4 100644 --- a/install/hoodik-install.sh +++ b/install/hoodik-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/hudikhq/hoodik diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh index cfc80c3e0..119972787 100644 --- a/install/investbrain-install.sh +++ b/install/investbrain-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Benito Rodríguez (b3ni) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/investbrainapp/investbrain diff --git a/install/kutt-install.sh b/install/kutt-install.sh index 04efd100b..b2330fe0f 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tomfrenzel # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/thedevs-network/kutt diff --git a/install/linkwarden-install.sh b/install/linkwarden-install.sh index 6596e2020..8f4b569a5 100644 --- a/install/linkwarden-install.sh +++ b/install/linkwarden-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkwarden.app/ diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 500a9f71c..1c82d07d7 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # Co-Author: SunFlowerOwl # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/nextexplorer-install.sh b/install/nextexplorer-install.sh index d39598f18..355221cb4 100644 --- a/install/nextexplorer-install.sh +++ b/install/nextexplorer-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/vikramsoni2/nextExplorer diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 4e406c6f8..32ef1ed21 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://opencloud.eu diff --git a/install/openeuler-install.sh b/install/openeuler-install.sh index 2335cbab5..b323b7867 100644 --- a/install/openeuler-install.sh +++ b/install/openeuler-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.openeuler.org/ diff --git a/install/opensuse-install.sh b/install/opensuse-install.sh index a144ca238..e41dccbfc 100644 --- a/install/opensuse-install.sh +++ b/install/opensuse-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.opensuse.org/ diff --git a/install/papra-install.sh b/install/papra-install.sh index 2c698030b..3a1de4b2b 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/CorentinTh/papra diff --git a/install/piler-install.sh b/install/piler-install.sh index 18b9374ac..42ce2d065 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.mailpiler.org/ diff --git a/install/pixelfed-install.sh b/install/pixelfed-install.sh index a122fe356..f2b9ee78e 100644 --- a/install/pixelfed-install.sh +++ b/install/pixelfed-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://pixelfed.org/ diff --git a/install/postgresus-install.sh b/install/postgresus-install.sh index 7eaf731d8..f5d0cdd4d 100644 --- a/install/postgresus-install.sh +++ b/install/postgresus-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/RostislavDugin/postgresus diff --git a/install/rockylinux-install.sh b/install/rockylinux-install.sh index 4922bd520..247377dc5 100644 --- a/install/rockylinux-install.sh +++ b/install/rockylinux-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://rockylinux.org/ diff --git a/install/romm-install.sh b/install/romm-install.sh index b566c0c5d..654099eaf 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: DevelopmentCats # Co-author: AlphaLawless # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index 75f65465d..1777780c0 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste diff --git a/install/rybbit-install.sh b/install/rybbit-install.sh index 7bec945aa..8f45864ef 100644 --- a/install/rybbit-install.sh +++ b/install/rybbit-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/rybbit-io/rybbit diff --git a/install/sportarr-install.sh b/install/sportarr-install.sh index 26d9eb14d..c9cb4d78e 100644 --- a/install/sportarr-install.sh +++ b/install/sportarr-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/Sportarr/Sportarr diff --git a/install/transmission-openvpn-install.sh b/install/transmission-openvpn-install.sh index eec848203..72b9b985d 100644 --- a/install/transmission-openvpn-install.sh +++ b/install/transmission-openvpn-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: SunFlowerOwl # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/haugene/docker-transmission-openvpn diff --git a/install/yubal-install.sh b/install/yubal-install.sh index 187db3aa0..56ea58e78 100644 --- a/install/yubal-install.sh +++ b/install/yubal-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Crazywolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/guillevc/yubal diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 9c4169a40..0c2e9c587 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/api.func b/misc/api.func index cae06c153..f6f284bec 100644 --- a/misc/api.func +++ b/misc/api.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE diff --git a/misc/bak_december/build.func.pve9-update b/misc/bak_december/build.func.pve9-update index 8583b8fa1..7ce1537a6 100644 --- a/misc/bak_december/build.func.pve9-update +++ b/misc/bak_december/build.func.pve9-update @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # Revision: 1 diff --git a/misc/bak_december/install.func.unified b/misc/bak_december/install.func.unified index 14f5f7c02..c222061ac 100644 --- a/misc/bak_december/install.func.unified +++ b/misc/bak_december/install.func.unified @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk # Co-Author: michelroegl-brunner diff --git a/misc/build.func b/misc/build.func index c1674fb49..34d599e3e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # Revision: 1 diff --git a/misc/cloud-init.func b/misc/cloud-init.func index 9a5ca45e0..f4d570ee7 100644 --- a/misc/cloud-init.func +++ b/misc/cloud-init.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: community-scripts ORG # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/branch/main/LICENSE # Revision: 1 diff --git a/misc/core.func b/misc/core.func index 76339c87c..682fb1080 100644 --- a/misc/core.func +++ b/misc/core.func @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE # ============================================================================== diff --git a/misc/error_handler.func b/misc/error_handler.func index afbc9e1c5..9af40f0fa 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -2,7 +2,7 @@ # ------------------------------------------------------------------------------ # ERROR HANDLER - ERROR & SIGNAL MANAGEMENT # ------------------------------------------------------------------------------ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # ------------------------------------------------------------------------------ diff --git a/misc/install.func b/misc/install.func index 484a3a1fc..124f58941 100644 --- a/misc/install.func +++ b/misc/install.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk # Co-Author: michelroegl-brunner diff --git a/misc/old_misc/alpine-install.func b/misc/old_misc/alpine-install.func index ddea81ecc..90a0ff00b 100644 --- a/misc/old_misc/alpine-install.func +++ b/misc/old_misc/alpine-install.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/old_misc/api.func b/misc/old_misc/api.func index d42f919fc..f4091d551 100644 --- a/misc/old_misc/api.func +++ b/misc/old_misc/api.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE diff --git a/misc/old_misc/core.func b/misc/old_misc/core.func index 1faba7296..5328d2a10 100644 --- a/misc/old_misc/core.func +++ b/misc/old_misc/core.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE # ------------------------------------------------------------------------------ diff --git a/misc/vm-core.func b/misc/vm-core.func index 099999de7..5412eb14c 100644 --- a/misc/vm-core.func +++ b/misc/vm-core.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE set -euo pipefail diff --git a/tools/addon/_template.sh b/tools/addon/_template.sh index e184da29f..39851fef7 100644 --- a/tools/addon/_template.sh +++ b/tools/addon/_template.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: YourName # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://example.com/project diff --git a/tools/addon/add-iptag.sh b/tools/addon/add-iptag.sh index c3bf88cf9..b0c02e6e4 100644 --- a/tools/addon/add-iptag.sh +++ b/tools/addon/add-iptag.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) && Desert_Gamer # License: MIT # Source: https://github.com/gitsang/iptag @@ -200,7 +200,7 @@ ip_in_cidrs() { 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 @@ -340,14 +340,14 @@ check_status() { update_all() { local type="$1" list_cmd="pct" vmids count=0 [[ "$type" == "vm" ]] && list_cmd="qm" - + vmids=$($list_cmd list 2>/dev/null | grep -v VMID | awk '{print $1}') for vmid in $vmids; do ((count++)); done - + echo "Found ${count} running ${type}s" [[ $count -eq 0 ]] && return - for vmid in $vmids; do + for vmid in $vmids; do update_tags "$type" "$vmid" done } @@ -361,7 +361,7 @@ check() { local interval_var="${type^^}_STATUS_CHECK_INTERVAL" local last_check_var="last_${type}_check_time" local last_update_var="last_update_${type}_time" - + if [[ "${!interval_var}" -gt 0 ]] && (( current_time - ${!last_check_var} >= ${!interval_var} )); then echo "Checking ${type^^} status..." eval "${last_check_var}=\$current_time" diff --git a/tools/addon/copyparty.sh b/tools/addon/copyparty.sh index 98f7ad72d..cf9451a0a 100644 --- a/tools/addon/copyparty.sh +++ b/tools/addon/copyparty.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/9001/copyparty diff --git a/tools/addon/filebrowser-quantum.sh b/tools/addon/filebrowser-quantum.sh index 74911a788..7cad2a477 100644 --- a/tools/addon/filebrowser-quantum.sh +++ b/tools/addon/filebrowser-quantum.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/addon/filebrowser.sh b/tools/addon/filebrowser.sh index b648c1555..8fcb4824b 100644 --- a/tools/addon/filebrowser.sh +++ b/tools/addon/filebrowser.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/addon/jellystat.sh b/tools/addon/jellystat.sh index 7975d0d22..2c10cc552 100644 --- a/tools/addon/jellystat.sh +++ b/tools/addon/jellystat.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/CyferShepard/Jellystat diff --git a/tools/pve/add-iptag.sh b/tools/pve/add-iptag.sh index 742a07a02..3872fc7b8 100644 --- a/tools/pve/add-iptag.sh +++ b/tools/pve/add-iptag.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) && Desert_Gamer # License: MIT diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index e6d06c229..a835263bb 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/pve/execute.sh b/tools/pve/execute.sh index fc19be981..24b03ff3a 100644 --- a/tools/pve/execute.sh +++ b/tools/pve/execute.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: jeroenzwart # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -10,10 +10,10 @@ function header_info() { cat <<"EOF" ______ __ __ _ ________ / ____/ _____ _______ __/ /____ / / | |/ / ____/ - / __/ | |/_/ _ \/ ___/ / / / __/ _ \ / / | / / - / /____> Date: Tue, 6 Jan 2026 12:41:07 +0100 Subject: [PATCH 224/378] copy gitattributes from VE repo --- .gitattributes | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index c963d3209..841e80bc0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,38 @@ -# Enforce LF for shell-related files only -*.sh text eol=lf -*.func text eol=lf -*.md text eol=lf +# --------------------------------------- +# Treat Shell files as first-class code +# --------------------------------------- +*.sh linguist-detectable=true +*.bash linguist-language=Shell +*.func linguist-language=Shell +*.install linguist-language=Shell + +# --------------------------------------- +# Treat Golang files as Go (for /api/) +api/**/*.go linguist-language=Go + +# --------------------------------------- +# Treat frontend as JavaScript/TypeScript (optional) +frontend/**/*.ts linguist-language=TypeScript +frontend/**/*.js linguist-language=JavaScript + +# --------------------------------------- +# Exclude documentation from stats +*.md linguist-documentation +docs/** linguist-documentation +README.md linguist-documentation +CONTRIBUTING.md linguist-documentation +SECURITY.md linguist-documentation + +# --------------------------------------- +# Exclude generated/config files +*.json linguist-generated +frontend/public/json/*.json linguist-generated=false +*.lock linguist-generated +*.yml linguist-generated +*.yaml linguist-generated +.github/** linguist-generated +.vscode/** linguist-generated + +# --------------------------------------- +# Standard text handling +* text=auto eol=lf From 7c27cceec69ce4a0a1eb06484ff199b9cf406367 Mon Sep 17 00:00:00 2001 From: Tobias Date: Tue, 6 Jan 2026 12:43:41 +0100 Subject: [PATCH 225/378] some more copyright --- bak/deferred/create_lxc.sh | 2 +- ct/alpine.sh | 2 +- ct/deferred/alpine-homarr.sh | 2 +- ct/deferred/nginxproxymanager.sh | 2 +- ct/deferred/ocis.sh | 2 +- ct/heimdall-dashboard.sh | 2 +- ct/petio.sh | 2 +- ct/plex.sh | 2 +- ct/plex2.sh | 2 +- ct/ubuntu.sh | 2 +- install/alpine-install.sh | 2 +- install/deferred/nginxproxymanager-install.sh | 2 +- install/docker-install.sh | 2 +- install/heimdall-dashboard-install.sh | 2 +- install/petio-install.sh | 2 +- install/plex-install.sh | 2 +- install/plex2-install.sh | 2 +- install/ubuntu-install.sh | 2 +- misc/bak_december/install copy.func | 2 +- misc/bak_december/install.func.bak | 2 +- misc/old_misc/build.func | 2 +- misc/old_misc/create_lxc.sh | 2 +- misc/old_misc/install.func | 2 +- tools/addon/code-server.sh | 12 +++++------ tools/addon/glances.sh | 2 +- tools/addon/grafana-loki.sh | 2 +- tools/addon/netdata.sh | 2 +- tools/pve/clean.sh | 2 +- tools/pve/container-restore-from-backup.sh | 14 ++++++------- tools/pve/core-restore-from-backup.sh | 10 +++++----- tools/pve/frigate-support.sh | 2 +- tools/pve/host-backup.sh | 2 +- tools/pve/post-pve-install.sh | 2 +- tools/pve/pyenv.sh | 10 +++++----- tools/pve/usb-passthrough.sh | 2 +- vm/haos-vm.sh | 2 +- vm/mikrotik-routeros.sh | 4 ++-- vm/nextcloud-vm.sh | 2 +- vm/openwrt.sh | 2 +- vm/owncloud-vm.sh | 2 +- vm/pimox-haos-vm.sh | 20 +++++++++---------- vm/ubuntu2404-vm.sh | 2 +- 42 files changed, 71 insertions(+), 71 deletions(-) diff --git a/bak/deferred/create_lxc.sh b/bak/deferred/create_lxc.sh index a85f27478..31fb86c94 100644 --- a/bak/deferred/create_lxc.sh +++ b/bak/deferred/create_lxc.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/alpine.sh b/ct/alpine.sh index 51767cb01..2f07dc38b 100644 --- a/ct/alpine.sh +++ b/ct/alpine.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://alpinelinux.org/ diff --git a/ct/deferred/alpine-homarr.sh b/ct/deferred/alpine-homarr.sh index c78398d65..28cf0ab79 100644 --- a/ct/deferred/alpine-homarr.sh +++ b/ct/deferred/alpine-homarr.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-2026 tteck # Author: tteck (tteckster) | Co-Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://homarr.dev/ diff --git a/ct/deferred/nginxproxymanager.sh b/ct/deferred/nginxproxymanager.sh index b1ff024d7..0609c3e3a 100644 --- a/ct/deferred/nginxproxymanager.sh +++ b/ct/deferred/nginxproxymanager.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nginxproxymanager.com/ diff --git a/ct/deferred/ocis.sh b/ct/deferred/ocis.sh index 167b4593d..412edf093 100644 --- a/ct/deferred/ocis.sh +++ b/ct/deferred/ocis.sh @@ -1,6 +1,6 @@ #!/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 +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.debian.org/ diff --git a/ct/heimdall-dashboard.sh b/ct/heimdall-dashboard.sh index c4fc9349a..c1c64b0b9 100644 --- a/ct/heimdall-dashboard.sh +++ b/ct/heimdall-dashboard.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-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://heimdall.site/ diff --git a/ct/petio.sh b/ct/petio.sh index f9c4d1370..c0587cbd0 100644 --- a/ct/petio.sh +++ b/ct/petio.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://petio.tv/ diff --git a/ct/plex.sh b/ct/plex.sh index be50ac0aa..94a89f032 100644 --- a/ct/plex.sh +++ b/ct/plex.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-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.plex.tv/ diff --git a/ct/plex2.sh b/ct/plex2.sh index 4d925f420..be731017e 100644 --- a/ct/plex2.sh +++ b/ct/plex2.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-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.plex.tv/ diff --git a/ct/ubuntu.sh b/ct/ubuntu.sh index da8992235..bcb60fab3 100644 --- a/ct/ubuntu.sh +++ b/ct/ubuntu.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) # source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/github.func) -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ubuntu.com/ diff --git a/install/alpine-install.sh b/install/alpine-install.sh index 2916be18f..4d8a5971a 100644 --- a/install/alpine-install.sh +++ b/install/alpine-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/install/deferred/nginxproxymanager-install.sh b/install/deferred/nginxproxymanager-install.sh index 34db0d6a1..aa5f05d12 100644 --- a/install/deferred/nginxproxymanager-install.sh +++ b/install/deferred/nginxproxymanager-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nginxproxymanager.com/ diff --git a/install/docker-install.sh b/install/docker-install.sh index 8edde48bc..d693f69bd 100644 --- a/install/docker-install.sh +++ b/install/docker-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.docker.com/ diff --git a/install/heimdall-dashboard-install.sh b/install/heimdall-dashboard-install.sh index 9346ea4fd..fc1ea92cb 100644 --- a/install/heimdall-dashboard-install.sh +++ b/install/heimdall-dashboard-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://heimdall.site/ diff --git a/install/petio-install.sh b/install/petio-install.sh index ff4337c71..6dc6472fb 100644 --- a/install/petio-install.sh +++ b/install/petio-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://petio.tv/ diff --git a/install/plex-install.sh b/install/plex-install.sh index 641bed6e2..8e786f0cc 100644 --- a/install/plex-install.sh +++ b/install/plex-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.plex.tv/ diff --git a/install/plex2-install.sh b/install/plex2-install.sh index 64a61e847..bb29392c7 100644 --- a/install/plex2-install.sh +++ b/install/plex2-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.plex.tv/ diff --git a/install/ubuntu-install.sh b/install/ubuntu-install.sh index 97283d838..80aee17c6 100644 --- a/install/ubuntu-install.sh +++ b/install/ubuntu-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/misc/bak_december/install copy.func b/misc/bak_december/install copy.func index e9e319576..2abfccdf8 100644 --- a/misc/bak_december/install copy.func +++ b/misc/bak_december/install copy.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk # Co-Author: michelroegl-brunner diff --git a/misc/bak_december/install.func.bak b/misc/bak_december/install.func.bak index f72e080b7..5d6244550 100644 --- a/misc/bak_december/install.func.bak +++ b/misc/bak_december/install.func.bak @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk # Co-Author: michelroegl-brunner diff --git a/misc/old_misc/build.func b/misc/old_misc/build.func index d895d3c8d..060c1eafb 100644 --- a/misc/old_misc/build.func +++ b/misc/old_misc/build.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk # Co-Author: michelroegl-brunner diff --git a/misc/old_misc/create_lxc.sh b/misc/old_misc/create_lxc.sh index 975311f6a..b13aca462 100644 --- a/misc/old_misc/create_lxc.sh +++ b/misc/old_misc/create_lxc.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/old_misc/install.func b/misc/old_misc/install.func index bd51cde15..4c7309a25 100644 --- a/misc/old_misc/install.func +++ b/misc/old_misc/install.func @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT diff --git a/tools/addon/code-server.sh b/tools/addon/code-server.sh index d2700a2bb..7d1c3b720 100644 --- a/tools/addon/code-server.sh +++ b/tools/addon/code-server.sh @@ -1,18 +1,18 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE function header_info { cat <<"EOF" - ______ __ _____ + ______ __ _____ / ____/___ ____/ /__ / ___/___ ______ _____ _____ / / / __ \/ __ / _ \ \__ \/ _ \/ ___/ | / / _ \/ ___/ -/ /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / -\____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ - +/ /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / +\____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ + EOF } IP=$(hostname -I | awk '{print $1}') @@ -86,7 +86,7 @@ systemctl enable -q --now code-server@$USER cat <~/.config/code-server/config.yaml bind-addr: 0.0.0.0:8680 auth: none -password: +password: cert: false EOF systemctl restart code-server@$USER diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 0f1b76f11..2928fe46f 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/addon/grafana-loki.sh b/tools/addon/grafana-loki.sh index b68a80478..9d7822729 100644 --- a/tools/addon/grafana-loki.sh +++ b/tools/addon/grafana-loki.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/tools/addon/netdata.sh b/tools/addon/netdata.sh index cab0c8081..4883a5b56 100644 --- a/tools/addon/netdata.sh +++ b/tools/addon/netdata.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/pve/clean.sh b/tools/pve/clean.sh index 87d3c9af8..4c9bf24f5 100644 --- a/tools/pve/clean.sh +++ b/tools/pve/clean.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/pve/container-restore-from-backup.sh b/tools/pve/container-restore-from-backup.sh index 5588dd179..d69e1c442 100644 --- a/tools/pve/container-restore-from-backup.sh +++ b/tools/pve/container-restore-from-backup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -30,12 +30,12 @@ done clear function header_info { cat <<"EOF" - __ __ ___ _ __ __ - / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ - / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ - / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ -/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ - RESTORE FROM BACKUP + __ __ ___ _ __ __ + / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ + / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ + / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ +/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ + RESTORE FROM BACKUP EOF } diff --git a/tools/pve/core-restore-from-backup.sh b/tools/pve/core-restore-from-backup.sh index ee21b3ffc..422898594 100644 --- a/tools/pve/core-restore-from-backup.sh +++ b/tools/pve/core-restore-from-backup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -30,12 +30,12 @@ done clear function header_info { cat <<"EOF" - __ __ ___ _ __ __ ______ - / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ + __ __ ___ _ __ __ ______ + / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ ___/ _ \ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / __/ -/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ - RESTORE FROM BACKUP +/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ + RESTORE FROM BACKUP EOF } diff --git a/tools/pve/frigate-support.sh b/tools/pve/frigate-support.sh index dd7e1edf3..c48e03bf2 100644 --- a/tools/pve/frigate-support.sh +++ b/tools/pve/frigate-support.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/tools/pve/host-backup.sh b/tools/pve/host-backup.sh index 8662c706c..8e963169b 100644 --- a/tools/pve/host-backup.sh +++ b/tools/pve/host-backup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 42df5d77a..26b330ce3 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteckster | MickLesk (CanbiZ) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/pve/pyenv.sh b/tools/pve/pyenv.sh index 78deaeaad..21376de1c 100644 --- a/tools/pve/pyenv.sh +++ b/tools/pve/pyenv.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -60,7 +60,7 @@ git clone https://github.com/pyenv/pyenv.git ~/.pyenv &>/dev/null set +e echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc -echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init --path)"\nfi' >> ~/.bashrc +echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init --path)"\nfi' >> ~/.bashrc msg_ok "Installed pyenv" . ~/.bashrc set -e @@ -110,7 +110,7 @@ pip3 install --pre esphome &>/dev/null cat </srv/esphome/start.sh #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -149,8 +149,8 @@ libgirepository1.0-dev \ libpango1.0-dev \ libgif-dev \ g++ &>/dev/null -python3 -m pip install wheel -pip3 install --upgrade pip +python3 -m pip install wheel +pip3 install --upgrade pip pip install python-matter-server[server] msg_ok "Installed Matter Server" echo -e "Start server > python -m matter_server.server" diff --git a/tools/pve/usb-passthrough.sh b/tools/pve/usb-passthrough.sh index fa26f0100..c5205961b 100644 --- a/tools/pve/usb-passthrough.sh +++ b/tools/pve/usb-passthrough.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index ea7ff30d0..7bb4dc81a 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/vm/mikrotik-routeros.sh b/vm/mikrotik-routeros.sh index cc6e7fdb8..7d3b37f63 100644 --- a/vm/mikrotik-routeros.sh +++ b/vm/mikrotik-routeros.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -322,7 +322,7 @@ DESCRIPTION=$( spend Coffee

- + GitHub diff --git a/vm/nextcloud-vm.sh b/vm/nextcloud-vm.sh index 083534c3f..9966046fa 100644 --- a/vm/nextcloud-vm.sh +++ b/vm/nextcloud-vm.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/vm/openwrt.sh b/vm/openwrt.sh index d5c8a0f38..c6e097014 100644 --- a/vm/openwrt.sh +++ b/vm/openwrt.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Jon Spriggs (jontheniceguy) # License: MIT diff --git a/vm/owncloud-vm.sh b/vm/owncloud-vm.sh index 7db765ad3..5ebcf92ba 100644 --- a/vm/owncloud-vm.sh +++ b/vm/owncloud-vm.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/vm/pimox-haos-vm.sh b/vm/pimox-haos-vm.sh index b925ab442..bf0141fee 100644 --- a/vm/pimox-haos-vm.sh +++ b/vm/pimox-haos-vm.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -9,16 +9,16 @@ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-sc function header_info { cat <<"EOF" - ____ _ __ ___ - / __ \(_) |/ /___ _ __ - / /_/ / / /|_/ / __ \| |/_/ - / ____/ / / / / /_/ /> < + ____ _ __ ___ + / __ \(_) |/ /___ _ __ + / /_/ / / /|_/ / __ \| |/_/ + / ____/ / / / / /_/ /> < __ __ /_/_ /_/_/ /_/\____/_/|_| __ ____ _____ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / __ \/ ___/ - / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / /\__ \ - / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /_/ /___/ / -/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____//____/ - + / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / /\__ \ + / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /_/ /___/ / +/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____//____/ + EOF } clear @@ -357,7 +357,7 @@ DESCRIPTION=$( spend Coffee

- + GitHub diff --git a/vm/ubuntu2404-vm.sh b/vm/ubuntu2404-vm.sh index 04afdc82a..98b01e4d4 100644 --- a/vm/ubuntu2404-vm.sh +++ b/vm/ubuntu2404-vm.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 5d7084c0bbe01b0c20c0be7133ba8b9c1510f25d Mon Sep 17 00:00:00 2001 From: Tobias Date: Tue, 6 Jan 2026 17:05:47 +0100 Subject: [PATCH 226/378] chore: refactor update messages --- .github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md | 2 +- .github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh | 2 +- ct/almalinux.sh | 2 +- ct/alpine-ntfy.sh | 7 ++++--- ct/alpine-valkey.sh | 3 ++- ct/alpine.sh | 2 +- ct/byparr.sh | 4 ++-- ct/centos.sh | 6 +++--- ct/debian.sh | 6 +++--- ct/deferred/alpine-homarr.sh | 2 +- ct/deferred/ampache.sh | 2 +- ct/deferred/docspell.sh | 2 +- ct/deferred/jumpserver.sh | 2 +- ct/deferred/kanba.sh | 2 +- ct/deferred/maxun.sh | 2 +- ct/deferred/netbootxyz.sh | 2 +- ct/deferred/nginxproxymanager.sh | 2 +- ct/deferred/ocis.sh | 2 +- ct/deferred/pixelfed.sh | 2 +- ct/deferred/polaris.sh | 2 +- ct/deferred/roundcubemail.sh | 2 +- ct/deferred/squirrelserversmanager.sh | 2 +- ct/devuan.sh | 6 +++--- ct/ente.sh | 6 +++--- ct/fedora.sh | 6 +++--- ct/freepbx.sh | 12 ++++++------ ct/frigate.sh | 2 +- ct/garmin-grafana.sh | 14 ++++++-------- ct/gentoo.sh | 6 +++--- ct/heimdall-dashboard.sh | 2 +- ct/hoodik.sh | 7 +++---- ct/investbrain.sh | 3 +-- ct/kutt.sh | 2 +- ct/linkwarden.sh | 2 +- ct/manyfold.sh | 9 ++------- ct/nextexplorer.sh | 2 +- ct/opencloud.sh | 2 +- ct/openeuler.sh | 7 ++++--- ct/opensuse.sh | 7 ++++--- ct/papra.sh | 7 ++++--- ct/petio.sh | 11 ++++++----- ct/piler.sh | 8 ++++---- ct/pixelfed.sh | 4 ++-- ct/plex.sh | 6 +++--- ct/plex2.sh | 6 +++--- ct/postgresus.sh | 2 +- ct/rockylinux.sh | 7 ++++--- ct/romm.sh | 6 +++--- ct/rustypaste.sh | 12 ++++++------ ct/rybbit.sh | 7 ++++--- ct/sportarr.sh | 10 +++++----- ct/transmission-openvpn.sh | 7 ++----- ct/ubuntu.sh | 7 ++++--- ct/yubal.sh | 4 ++-- docs/contribution/GUIDE.md | 2 +- docs/contribution/templates_ct/AppName.md | 2 +- docs/contribution/templates_ct/AppName.sh | 2 +- docs/ct/DETAILED_GUIDE.md | 6 +++--- tools/addon/netdata.sh | 4 ++-- vm/allstarlink-vm.sh | 2 +- vm/archlinux-vm.sh | 2 +- vm/debian-vm-test-helper.sh | 2 +- vm/debian-vm.sh | 2 +- vm/docker-vm-debug.sh | 2 +- vm/docker-vm.sh | 2 +- vm/docker-vm.sh.bak | 2 +- vm/haos-vm.sh | 2 +- vm/k3s-vm.sh | 2 +- vm/mikrotik-routeros.sh | 2 +- vm/nextcloud-vm.sh | 2 +- vm/opnsense-vm.sh | 2 +- vm/owncloud-vm.sh | 2 +- vm/pimox-haos-vm.sh | 2 +- vm/ubuntu2204-vm.sh | 2 +- vm/ubuntu2404-vm.sh | 2 +- vm/ubuntu2410-vm.sh | 2 +- vm/umbrel-os-vm.sh | 2 +- vm/unifi-os-server-vm.sh | 2 +- 78 files changed, 155 insertions(+), 158 deletions(-) diff --git a/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md index 23d821e4d..8507c7c2d 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md +++ b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md @@ -266,7 +266,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh index fbf862e9d..cf9333c4f 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh +++ b/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh @@ -80,7 +80,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/ct/almalinux.sh b/ct/almalinux.sh index 44de61d49..58d9c00a6 100644 --- a/ct/almalinux.sh +++ b/ct/almalinux.sh @@ -37,5 +37,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index 1e39c9f24..3a36648b0 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -27,14 +27,15 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating ntfy LXC" $STD apk -U upgrade setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy - msg_ok "Updated $APP LXC" + msg_ok "Updated ntfy LXC" msg_info "Restarting ntfy" rc-service ntfy restart msg_ok "Restarted ntfy" + msg_ok "Updated successfully!" exit } @@ -42,7 +43,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/alpine-valkey.sh b/ct/alpine-valkey.sh index 4b08a945b..2765aff7f 100644 --- a/ct/alpine-valkey.sh +++ b/ct/alpine-valkey.sh @@ -42,6 +42,7 @@ function update_script() { msg_info "Updating Valkey" apk update && apk upgrade valkey rc-service valkey restart + msg_ok "Updated Valkey" msg_ok "Updated successfully!" exit ;; @@ -67,6 +68,6 @@ start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable on port 6379. ${BL}valkey-cli -h ${IP} -p 6379${CL} \n" diff --git a/ct/alpine.sh b/ct/alpine.sh index 2f07dc38b..b843597ff 100644 --- a/ct/alpine.sh +++ b/ct/alpine.sh @@ -38,4 +38,4 @@ start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/ct/byparr.sh b/ct/byparr.sh index aab1be8a8..4831404b9 100644 --- a/ct/byparr.sh +++ b/ct/byparr.sh @@ -38,7 +38,7 @@ function update_script() { msg_info "Starting Service" systemctl start byparr msg_ok "Started Service" - msg_ok "Updated Successfully!" + msg_ok "Updated successfully!" fi exit } @@ -47,7 +47,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/ct/centos.sh b/ct/centos.sh index 9c32c8365..54dea3950 100644 --- a/ct/centos.sh +++ b/ct/centos.sh @@ -27,9 +27,9 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating CentOS LXC" $STD dnf -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated CentOS LXC" exit } @@ -37,5 +37,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/debian.sh b/ct/debian.sh index 8986ec423..88fbb5550 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -30,10 +30,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Debian LXC" $STD apt update $STD apt upgrade -y - msg_ok "Updated $APP LXC" + msg_ok "Updated Debian LXC" cleanup_lxc exit } @@ -42,5 +42,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/deferred/alpine-homarr.sh b/ct/deferred/alpine-homarr.sh index 28cf0ab79..785d0ebaa 100644 --- a/ct/deferred/alpine-homarr.sh +++ b/ct/deferred/alpine-homarr.sh @@ -108,7 +108,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}:7575${CL}" diff --git a/ct/deferred/ampache.sh b/ct/deferred/ampache.sh index 18f2bce41..068b2675a 100644 --- a/ct/deferred/ampache.sh +++ b/ct/deferred/ampache.sh @@ -42,7 +42,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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.php${CL}" diff --git a/ct/deferred/docspell.sh b/ct/deferred/docspell.sh index 396b88310..9c3764982 100644 --- a/ct/deferred/docspell.sh +++ b/ct/deferred/docspell.sh @@ -45,7 +45,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}:7880${CL}" diff --git a/ct/deferred/jumpserver.sh b/ct/deferred/jumpserver.sh index 1213a5581..4ac5edf38 100644 --- a/ct/deferred/jumpserver.sh +++ b/ct/deferred/jumpserver.sh @@ -59,7 +59,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/deferred/kanba.sh b/ct/deferred/kanba.sh index 1c765bea1..848b6a798 100644 --- a/ct/deferred/kanba.sh +++ b/ct/deferred/kanba.sh @@ -38,7 +38,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/deferred/maxun.sh b/ct/deferred/maxun.sh index 8374da056..4d39ecc5e 100644 --- a/ct/deferred/maxun.sh +++ b/ct/deferred/maxun.sh @@ -43,7 +43,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/deferred/netbootxyz.sh b/ct/deferred/netbootxyz.sh index 201073af9..2205aec37 100644 --- a/ct/deferred/netbootxyz.sh +++ b/ct/deferred/netbootxyz.sh @@ -124,6 +124,6 @@ start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}http://${IP}:3000${CL} \n" diff --git a/ct/deferred/nginxproxymanager.sh b/ct/deferred/nginxproxymanager.sh index 0609c3e3a..6d59efe8f 100644 --- a/ct/deferred/nginxproxymanager.sh +++ b/ct/deferred/nginxproxymanager.sh @@ -154,7 +154,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/deferred/ocis.sh b/ct/deferred/ocis.sh index 412edf093..b48547579 100644 --- a/ct/deferred/ocis.sh +++ b/ct/deferred/ocis.sh @@ -38,7 +38,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/pixelfed.sh b/ct/deferred/pixelfed.sh index e02ff0fd9..f9f6ba2e2 100644 --- a/ct/deferred/pixelfed.sh +++ b/ct/deferred/pixelfed.sh @@ -40,6 +40,6 @@ start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${APP} Setup should be reachable by going to the following URL. ${BL}http://${IP}:8000${CL} \n" diff --git a/ct/deferred/polaris.sh b/ct/deferred/polaris.sh index 376f216f8..ab9edabd5 100644 --- a/ct/deferred/polaris.sh +++ b/ct/deferred/polaris.sh @@ -34,7 +34,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}:5050${CL}" diff --git a/ct/deferred/roundcubemail.sh b/ct/deferred/roundcubemail.sh index 569675fbf..93ae76e8e 100644 --- a/ct/deferred/roundcubemail.sh +++ b/ct/deferred/roundcubemail.sh @@ -59,6 +59,6 @@ start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}http://${IP}/installer ${CL} \n" diff --git a/ct/deferred/squirrelserversmanager.sh b/ct/deferred/squirrelserversmanager.sh index 663822dcd..94e85d30e 100644 --- a/ct/deferred/squirrelserversmanager.sh +++ b/ct/deferred/squirrelserversmanager.sh @@ -52,6 +52,6 @@ msg_info "Setting Container to Normal Resources" pct set $CTID -memory 1024 pct set $CTID -cores 1 msg_ok "Set Container to Normal Resources" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}http://${IP}:80${CL} \n" diff --git a/ct/devuan.sh b/ct/devuan.sh index b1bc6acee..489c1f8d1 100644 --- a/ct/devuan.sh +++ b/ct/devuan.sh @@ -27,10 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Devuan LXC" $STD apt-get update $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated Devuan LXC" exit } @@ -38,5 +38,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/ente.sh b/ct/ente.sh index cf834013f..7e71a7303 100644 --- a/ct/ente.sh +++ b/ct/ente.sh @@ -27,10 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Ente LXC" $STD apt-get update $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated Ente LXC" exit } @@ -38,5 +38,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/fedora.sh b/ct/fedora.sh index d79815be7..66a67247c 100644 --- a/ct/fedora.sh +++ b/ct/fedora.sh @@ -27,9 +27,9 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Fedora LXC" $STD dnf -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated Fedora LXC" exit } @@ -37,5 +37,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/freepbx.sh b/ct/freepbx.sh index 2f3a9f36c..e16a1c47a 100644 --- a/ct/freepbx.sh +++ b/ct/freepbx.sh @@ -30,16 +30,16 @@ function update_script() { exit fi - msg_info "Updating $APP LXC" + msg_info "Updating FreePBX LXC" $STD apt-get update $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated FreePBX LXC" - msg_info "Updating $APP Modules" + msg_info "Updating FreePBX Modules" $STD fwconsole ma updateall $STD fwconsole reload - msg_ok "Updated $APP Modules" - + msg_ok "Updated FreePBX Modules" + msg_ok "Updated successfully!" exit } @@ -61,7 +61,7 @@ fi build_container description -msg_ok "Completed Successfully!\n" +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/frigate.sh b/ct/frigate.sh index b70b22083..ecdc17126 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -35,7 +35,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}:5000${CL}" diff --git a/ct/garmin-grafana.sh b/ct/garmin-grafana.sh index 0d486f795..cdda1b605 100644 --- a/ct/garmin-grafana.sh +++ b/ct/garmin-grafana.sh @@ -32,11 +32,11 @@ function update_script() { 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" + msg_info "Stopping Services" systemctl stop garmin-grafana systemctl stop grafana-server systemctl stop influxdb - msg_ok "Stopped $APP" + msg_ok "Stopped Services" if [[ ! -f /opt/garmin-grafana/.env ]]; then msg_error "No .env file found in /opt/garmin-grafana/.env" @@ -59,7 +59,6 @@ function update_script() { 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 @@ -73,18 +72,17 @@ function update_script() { cp -r /opt/garmin-grafana-backup/.garminconnect /opt/garmin-grafana/.garminconnect msg_ok "Updated $APP to v${RELEASE}" - msg_info "Starting $APP" + msg_info "Starting Services" systemctl start garmin-grafana systemctl start grafana-server systemctl start influxdb - msg_ok "Started $APP" - + msg_ok "Started Services" msg_info "Cleaning Up" rm -rf /opt/garmin-grafana-backup msg_ok "Cleanup Completed" echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Update Successful" + msg_ok "Updated Successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi @@ -95,7 +93,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/gentoo.sh b/ct/gentoo.sh index b42a3858c..d26706d5d 100644 --- a/ct/gentoo.sh +++ b/ct/gentoo.sh @@ -27,10 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Gentoo LXC" $STD emerge --sync $STD emerge --quiet --update --deep @world - msg_ok "Updated $APP LXC" + msg_ok "Updated Gentoo LXC" exit } @@ -38,5 +38,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/heimdall-dashboard.sh b/ct/heimdall-dashboard.sh index c1c64b0b9..6fe51c7ba 100644 --- a/ct/heimdall-dashboard.sh +++ b/ct/heimdall-dashboard.sh @@ -74,7 +74,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}:7990${CL}" diff --git a/ct/hoodik.sh b/ct/hoodik.sh index 89e48de62..f6fb3b526 100644 --- a/ct/hoodik.sh +++ b/ct/hoodik.sh @@ -38,7 +38,7 @@ function update_script() { cp /opt/hoodik/.env /tmp/hoodik.env.bak msg_ok "Backed up Configuration" - msg_info "Updating ${APP} (Patience - this takes 15-20 minutes)" + msg_info "Updating Hoodik (Patience - this takes 15-20 minutes)" source ~/.cargo/env rm -rf /opt/hoodik fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "tarball" "latest" "/opt/hoodik" @@ -54,7 +54,7 @@ function update_script() { $STD cargo build --release cp /opt/hoodik/target/release/hoodik /usr/local/bin/hoodik chmod +x /usr/local/bin/hoodik - msg_ok "Updated ${APP}" + msg_ok "Updated Hoodik" msg_info "Restoring Configuration" cp /tmp/hoodik.env.bak /opt/hoodik/.env @@ -70,7 +70,6 @@ function update_script() { msg_info "Starting Services" systemctl start hoodik msg_ok "Started Services" - msg_ok "Updated Successfully" fi exit @@ -80,7 +79,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}:5443${CL}" diff --git a/ct/investbrain.sh b/ct/investbrain.sh index ac9181e0d..4539f8a94 100644 --- a/ct/investbrain.sh +++ b/ct/investbrain.sh @@ -76,7 +76,6 @@ function update_script() { systemctl start php${PHP_VERSION}-fpm nginx supervisorctl start all msg_ok "Services Started" - msg_ok "Updated Successfully!" fi exit @@ -86,7 +85,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/kutt.sh b/ct/kutt.sh index 685d9b362..f1bc257bf 100644 --- a/ct/kutt.sh +++ b/ct/kutt.sh @@ -68,7 +68,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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} or https://${CL}" diff --git a/ct/linkwarden.sh b/ct/linkwarden.sh index acf7e7138..8753ce8f4 100644 --- a/ct/linkwarden.sh +++ b/ct/linkwarden.sh @@ -69,7 +69,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/manyfold.sh b/ct/manyfold.sh index cef4da3eb..08ea18617 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -90,13 +90,8 @@ EOF done systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 msg_ok "Restored Service" + msg_ok "Updated successfully!" fi - - msg_info "Cleaning up" - $STD apt -y autoremove - $STD apt -y autoclean - $STD apt -y clean - msg_ok "Cleaned" exit } @@ -104,7 +99,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/nextexplorer.sh b/ct/nextexplorer.sh index b82c3d750..6b2b651b4 100644 --- a/ct/nextexplorer.sh +++ b/ct/nextexplorer.sh @@ -68,7 +68,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/opencloud.sh b/ct/opencloud.sh index 5528a5924..6c047fa6d 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -54,7 +54,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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://${CL}" diff --git a/ct/openeuler.sh b/ct/openeuler.sh index 21b34ec88..087f5d853 100644 --- a/ct/openeuler.sh +++ b/ct/openeuler.sh @@ -27,9 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating OpenEuler LXC" $STD dnf -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated OpenEuler LXC" + msg_ok "Updated successfully!" exit } @@ -37,5 +38,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!\n" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/opensuse.sh b/ct/opensuse.sh index 6b82225fc..94cf4043d 100644 --- a/ct/opensuse.sh +++ b/ct/opensuse.sh @@ -27,9 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating OpenSUSE LXC" $STD zypper -n update - msg_ok "Updated $APP LXC" + msg_ok "Updated OpenSUSE LXC" + msg_ok "Updated successfully!" exit } @@ -37,5 +38,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!\n" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/papra.sh b/ct/papra.sh index 79c486546..7804d77e8 100644 --- a/ct/papra.sh +++ b/ct/papra.sh @@ -27,7 +27,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Papra LXC" systemctl stop papra cd /opt/papra git fetch @@ -36,7 +36,8 @@ function update_script() { $STD pnpm --filter "@papra/app-client..." run build $STD pnpm --filter "@papra/app-server..." run build systemctl start papra - msg_ok "Updated $APP LXC" + msg_ok "Updated Papra LXC" + msg_ok "Updated successfully!" exit } @@ -44,5 +45,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!\n" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/petio.sh b/ct/petio.sh index c0587cbd0..e70e5d93f 100644 --- a/ct/petio.sh +++ b/ct/petio.sh @@ -26,12 +26,13 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP" - systemctl stop petio.service + msg_info "Updating Pepito" + systemctl stop petio 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" + systemctl start petio + msg_ok "Updated Pepito" + msg_ok "Updated successfully!" exit } @@ -39,7 +40,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}" diff --git a/ct/piler.sh b/ct/piler.sh index 3eb1b4148..bfd2831c0 100644 --- a/ct/piler.sh +++ b/ct/piler.sh @@ -67,11 +67,11 @@ function update_script() { chown piler:piler /etc/piler/piler.conf msg_ok "Restored Configuration" - msg_info "Starting Piler Services" + msg_info "Starting Services" $STD systemctl start manticore $STD systemctl start piler - msg_ok "Started Piler Services" - msg_ok "Updated Successfully to v${RELEASE_NEW}" + msg_ok "Started Services" + msg_ok "Updated successfully!" else msg_ok "No update available (current: v${RELEASE_OLD})" fi @@ -82,7 +82,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/pixelfed.sh b/ct/pixelfed.sh index f506bb1ef..30581a249 100644 --- a/ct/pixelfed.sh +++ b/ct/pixelfed.sh @@ -61,7 +61,7 @@ function update_script() { msg_info "Starting Services" systemctl start pixelfed-horizon pixelfed-scheduler.timer msg_ok "Services started" - msg_ok "Updated Successfully" + msg_ok "Updated successfully!" fi exit } @@ -70,7 +70,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/plex.sh b/ct/plex.sh index 94a89f032..00590c47c 100644 --- a/ct/plex.sh +++ b/ct/plex.sh @@ -34,10 +34,10 @@ function update_script() { "2" "Install plexupdate" OFF \ 3>&1 1>&2 2>&3) if [ "$UPD" == "1" ]; then - msg_info "Updating ${APP} LXC" + msg_info "Updating Plex LXC" $STD apt update $STD apt -y upgrade - msg_ok "Updated ${APP} LXC" + msg_ok "Updated Plex LXC" msg_ok "Updated successfully!" exit fi @@ -53,7 +53,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/plex2.sh b/ct/plex2.sh index be731017e..dff1fdd62 100644 --- a/ct/plex2.sh +++ b/ct/plex2.sh @@ -34,10 +34,10 @@ function update_script() { "2" "Install plexupdate" OFF \ 3>&1 1>&2 2>&3) if [ "$UPD" == "1" ]; then - msg_info "Updating ${APP} LXC" + msg_info "Updating Plex" $STD apt update $STD apt -y upgrade - msg_ok "Updated ${APP} LXC" + msg_ok "Updated Plex" msg_ok "Updated successfully!" exit fi @@ -53,7 +53,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/postgresus.sh b/ct/postgresus.sh index ba2c8cfee..f3906c320 100644 --- a/ct/postgresus.sh +++ b/ct/postgresus.sh @@ -69,7 +69,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/rockylinux.sh b/ct/rockylinux.sh index 733cda5e7..24ff57599 100644 --- a/ct/rockylinux.sh +++ b/ct/rockylinux.sh @@ -27,9 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Rocky Linux LXC" $STD dnf -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated Rocky Linux LXC" + msg_ok "Completed successfully!" exit } @@ -37,5 +38,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!\n" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/romm.sh b/ct/romm.sh index 38e0b159e..3d9c37a1b 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -39,7 +39,7 @@ function update_script() { cp /opt/romm/.env /opt/romm/.env.backup msg_ok "Backed up configuration" - msg_info "Updating ${APP}" + msg_info "Updating ROMM" fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm" cp /opt/romm/.env.backup /opt/romm/.env @@ -60,7 +60,7 @@ function update_script() { mkdir -p /opt/romm/frontend/dist/assets/romm ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets - msg_ok "Updated ${APP}" + msg_ok "Updated ROMM" msg_info "Starting Services" systemctl start romm-backend romm-worker romm-scheduler romm-watcher @@ -74,7 +74,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/rustypaste.sh b/ct/rustypaste.sh index 3b33ded97..504e142dd 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -30,9 +30,9 @@ function update_script() { fi if check_for_gh_release "rustypaste" "orhun/rustypaste"; then - msg_info "Stopping rustypaste" + msg_info "Stopping Services" systemctl stop rustypaste - msg_ok "Stopped rustypaste" + msg_ok "Stopped Services" msg_info "Creating Backup" tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" "/opt/rustypaste/upload" @@ -46,10 +46,10 @@ function update_script() { $STD cargo build --locked --release msg_ok "Updated rustypaste" - msg_info "Starting rustypaste" + msg_info "Starting Services" systemctl start rustypaste - msg_ok "Started rustypaste" - msg_ok "Update Successful" + msg_ok "Started Services" + msg_ok "Updated successfully!" fi exit } @@ -58,7 +58,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}rustypaste 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/rybbit.sh b/ct/rybbit.sh index 60ee32893..621e0e1b3 100644 --- a/ct/rybbit.sh +++ b/ct/rybbit.sh @@ -27,10 +27,11 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating Rybbit LXC" $STD apt-get update $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated Rybbit LXC" + msg_ok "Updated successfully!" exit } @@ -38,5 +39,5 @@ start build_container description -msg_ok "Completed Successfully!" +msg_ok "Completed successfully!\n" msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/sportarr.sh b/ct/sportarr.sh index 4a22c9858..68c932992 100644 --- a/ct/sportarr.sh +++ b/ct/sportarr.sh @@ -29,15 +29,15 @@ function update_script() { fi if check_for_gh_release "sportarr" "Sportarr/Sportarr"; then - msg_info "Stopping Sportarr Service" + msg_info "Stopping Services" systemctl stop sportarr - msg_ok "Stopped Sportarr Service" + msg_ok "Stopped Services" fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" - msg_info "Starting Sportarr Service" + msg_info "Starting Services" systemctl start sportarr - msg_ok "Started Sportarr Service" + msg_ok "Started Services" msg_ok "Updated successfully!" fi exit @@ -47,7 +47,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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}:1867${CL}" diff --git a/ct/transmission-openvpn.sh b/ct/transmission-openvpn.sh index 95938fbeb..313ab7db9 100644 --- a/ct/transmission-openvpn.sh +++ b/ct/transmission-openvpn.sh @@ -67,12 +67,9 @@ function update_script() { 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" - + msg_ok "Updated successfully!" exit } @@ -80,7 +77,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/ct/ubuntu.sh b/ct/ubuntu.sh index bcb60fab3..4f24cd6a0 100644 --- a/ct/ubuntu.sh +++ b/ct/ubuntu.sh @@ -28,10 +28,11 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating ${APP} LXC" + msg_info "Updating Ubuntu LXC" $STD apt-get update $STD apt-get -y upgrade - msg_ok "Updated ${APP} LXC" + msg_ok "Updated Ubuntu LXC" + msg_ok "Updated successfully!" exit } @@ -39,5 +40,5 @@ start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" diff --git a/ct/yubal.sh b/ct/yubal.sh index d876f8283..ca42db372 100644 --- a/ct/yubal.sh +++ b/ct/yubal.sh @@ -51,7 +51,7 @@ function update_script() { msg_info "Starting Services" systemctl start yubal msg_ok "Started Services" - msg_ok "Updated Successfully" + msg_ok "Updated successfully!" fi exit } @@ -60,7 +60,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/docs/contribution/GUIDE.md b/docs/contribution/GUIDE.md index b3f8ec839..52c4d950f 100644 --- a/docs/contribution/GUIDE.md +++ b/docs/contribution/GUIDE.md @@ -310,7 +310,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/docs/contribution/templates_ct/AppName.md b/docs/contribution/templates_ct/AppName.md index 23d821e4d..8507c7c2d 100644 --- a/docs/contribution/templates_ct/AppName.md +++ b/docs/contribution/templates_ct/AppName.md @@ -266,7 +266,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/docs/contribution/templates_ct/AppName.sh b/docs/contribution/templates_ct/AppName.sh index fbf862e9d..cf9333c4f 100644 --- a/docs/contribution/templates_ct/AppName.sh +++ b/docs/contribution/templates_ct/AppName.sh @@ -80,7 +80,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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/docs/ct/DETAILED_GUIDE.md b/docs/ct/DETAILED_GUIDE.md index 928ff55a0..cd2f8d11d 100644 --- a/docs/ct/DETAILED_GUIDE.md +++ b/docs/ct/DETAILED_GUIDE.md @@ -131,7 +131,7 @@ function update_script() { ... } # [10] Update function (optional) start # [11] Launch container creation build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" ``` --- @@ -254,7 +254,7 @@ build_container description # Display success message -msg_ok "Completed Successfully!\n" +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}" @@ -386,7 +386,7 @@ function update_script() { start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" ``` --- diff --git a/tools/addon/netdata.sh b/tools/addon/netdata.sh index 4883a5b56..8d7d3a263 100644 --- a/tools/addon/netdata.sh +++ b/tools/addon/netdata.sh @@ -132,7 +132,7 @@ install() { $STD apt-get update $STD apt-get install -y netdata msg_ok "Installed Netdata" - msg_ok "Completed Successfully!\n" + msg_ok "Completed successfully!\n" echo -e "\n Netdata should be reachable at${BL} http://$(hostname -I | awk '{print $1}'):19999 ${CL}\n" } @@ -150,7 +150,7 @@ uninstall() { $STD apt autoremove -y $STD userdel netdata || true msg_ok "Uninstalled Netdata" - msg_ok "Completed Successfully!\n" + msg_ok "Completed successfully!\n" } header_info diff --git a/vm/allstarlink-vm.sh b/vm/allstarlink-vm.sh index e7d07a893..d0f04f78c 100644 --- a/vm/allstarlink-vm.sh +++ b/vm/allstarlink-vm.sh @@ -494,4 +494,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started AllStarLink VM" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/archlinux-vm.sh b/vm/archlinux-vm.sh index f60cf3fe3..43511ee33 100644 --- a/vm/archlinux-vm.sh +++ b/vm/archlinux-vm.sh @@ -525,4 +525,4 @@ if [ "$START_VM" == "yes" ]; then fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/debian-vm-test-helper.sh b/vm/debian-vm-test-helper.sh index 4cf217b7a..a8a80a3b7 100644 --- a/vm/debian-vm-test-helper.sh +++ b/vm/debian-vm-test-helper.sh @@ -402,5 +402,5 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Debian 12 VM" fi -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" diff --git a/vm/debian-vm.sh b/vm/debian-vm.sh index 7f20be15e..38d7f432f 100644 --- a/vm/debian-vm.sh +++ b/vm/debian-vm.sh @@ -376,5 +376,5 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Debian 12 VM" fi -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" msg_custom "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" diff --git a/vm/docker-vm-debug.sh b/vm/docker-vm-debug.sh index 8445c499c..b571287d7 100644 --- a/vm/docker-vm-debug.sh +++ b/vm/docker-vm-debug.sh @@ -914,4 +914,4 @@ if [ "$USE_CLOUD_INIT" = "yes" ]; then fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 9f9436484..013988b0c 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -777,4 +777,4 @@ if [ "$USE_CLOUD_INIT" = "yes" ]; then fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/docker-vm.sh.bak b/vm/docker-vm.sh.bak index ff4147365..9e700b9fa 100644 --- a/vm/docker-vm.sh.bak +++ b/vm/docker-vm.sh.bak @@ -629,7 +629,7 @@ if [[ "$START_VM" == "yes" ]]; then fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" # ---- Hinweise/Debug (Cloud-Init) -------------------------------------------- # In der VM prüfen: diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index 7bb4dc81a..df256948c 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -548,4 +548,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Home Assistant OS VM" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/k3s-vm.sh b/vm/k3s-vm.sh index e1d8adb36..ca20e5662 100644 --- a/vm/k3s-vm.sh +++ b/vm/k3s-vm.sh @@ -386,5 +386,5 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Debian 12 VM" fi -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" msg_custom "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" diff --git a/vm/mikrotik-routeros.sh b/vm/mikrotik-routeros.sh index 7d3b37f63..901eab49f 100644 --- a/vm/mikrotik-routeros.sh +++ b/vm/mikrotik-routeros.sh @@ -346,4 +346,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Mikrotik RouterOS CHR VM" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/nextcloud-vm.sh b/vm/nextcloud-vm.sh index 9966046fa..93ea21f4f 100644 --- a/vm/nextcloud-vm.sh +++ b/vm/nextcloud-vm.sh @@ -481,4 +481,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started $NAME" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/opnsense-vm.sh b/vm/opnsense-vm.sh index 0d2acd133..131382929 100644 --- a/vm/opnsense-vm.sh +++ b/vm/opnsense-vm.sh @@ -686,7 +686,7 @@ sleep 10 send_line_to_vm "0" msg_ok "Started OPNsense VM" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" if [ "$IP_ADDR" != "" ]; then echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP_ADDR}${CL}" diff --git a/vm/owncloud-vm.sh b/vm/owncloud-vm.sh index 5ebcf92ba..23769982d 100644 --- a/vm/owncloud-vm.sh +++ b/vm/owncloud-vm.sh @@ -482,4 +482,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started $NAME" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/pimox-haos-vm.sh b/vm/pimox-haos-vm.sh index bf0141fee..4ee0d0c9e 100644 --- a/vm/pimox-haos-vm.sh +++ b/vm/pimox-haos-vm.sh @@ -382,4 +382,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Home Assistant OS VM" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/ubuntu2204-vm.sh b/vm/ubuntu2204-vm.sh index 6cdc22643..14a89b939 100644 --- a/vm/ubuntu2204-vm.sh +++ b/vm/ubuntu2204-vm.sh @@ -522,6 +522,6 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Ubuntu 22.04 VM" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "Setup Cloud-Init before starting \n More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" diff --git a/vm/ubuntu2404-vm.sh b/vm/ubuntu2404-vm.sh index 98b01e4d4..7bcfc4137 100644 --- a/vm/ubuntu2404-vm.sh +++ b/vm/ubuntu2404-vm.sh @@ -524,6 +524,6 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Ubuntu 24.04 VM" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "Setup Cloud-Init before starting \n More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" diff --git a/vm/ubuntu2410-vm.sh b/vm/ubuntu2410-vm.sh index 71498c60f..46d900c6e 100644 --- a/vm/ubuntu2410-vm.sh +++ b/vm/ubuntu2410-vm.sh @@ -658,6 +658,6 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started $APP" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "Setup Cloud-Init before starting \n More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" diff --git a/vm/umbrel-os-vm.sh b/vm/umbrel-os-vm.sh index 7942e8802..6e7e4b25f 100644 --- a/vm/umbrel-os-vm.sh +++ b/vm/umbrel-os-vm.sh @@ -527,4 +527,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Umbrel OS VM" fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" diff --git a/vm/unifi-os-server-vm.sh b/vm/unifi-os-server-vm.sh index 8de2da1da..7ea19eb4f 100644 --- a/vm/unifi-os-server-vm.sh +++ b/vm/unifi-os-server-vm.sh @@ -815,4 +815,4 @@ EOFINSTALL fi post_update_to_api "done" "none" -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" From dcccd9675be74ae9afbee1e4aeb5dda2c461466a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:40:09 +0000 Subject: [PATCH 227/378] Delete sportarr (ct) after migration to ProxmoxVE (#1249) Co-authored-by: github-actions[bot] --- ct/sportarr.sh | 53 --------------------------- frontend/public/json/sportarr.json | 40 --------------------- install/sportarr-install.sh | 57 ------------------------------ 3 files changed, 150 deletions(-) delete mode 100644 ct/sportarr.sh delete mode 100644 frontend/public/json/sportarr.json delete mode 100644 install/sportarr-install.sh diff --git a/ct/sportarr.sh b/ct/sportarr.sh deleted file mode 100644 index 68c932992..000000000 --- a/ct/sportarr.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-2026 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/Sportarr/Sportarr - -APP="Sportarr" -var_tags="${var_tags:-arr}" -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 [[ ! -d /opt/sportarr ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "sportarr" "Sportarr/Sportarr"; then - msg_info "Stopping Services" - systemctl stop sportarr - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" - - msg_info "Starting Services" - systemctl start sportarr - 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}:1867${CL}" diff --git a/frontend/public/json/sportarr.json b/frontend/public/json/sportarr.json deleted file mode 100644 index fd0ec06fc..000000000 --- a/frontend/public/json/sportarr.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Sportarr", - "slug": "sportarr", - "categories": [ - 14 - ], - "date_created": "2025-12-12", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 1867, - "documentation": "https://sportarr.net/docs", - "config_path": "/opt/sportarr/.env, /opt/sportarr-data/config/config.xml", - "website": "https://sportarr.net/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/sportarr.webp", - "description": "Sportarr is an automated media management application for all sports. It works similar to Sonarr and Radarr but specifically designed for combat sports, basketball, football, hockey, motorsports, and hundreds of other sports worldwide.", - "install_methods": [ - { - "type": "default", - "script": "ct/sportarr.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 8, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "The resources assigned to LXC are considered baseline. Please adjust to match your workload.", - "type": "info" - } - ] -} diff --git a/install/sportarr-install.sh b/install/sportarr-install.sh deleted file mode 100644 index c9cb4d78e..000000000 --- a/install/sportarr-install.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/Sportarr/Sportarr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os -setup_hwaccel - -msg_info "Installing Dependencies" -$STD apt install -y \ - ffmpeg \ - gosu \ - sqlite3 -msg_ok "Installed Dependencies" - -fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-x64-*.tar.gz" - -msg_info "Setting up Sportarr" -cat </opt/sportarr/.env -Sportarr__DataPath="/opt/sportarr-data/config" -ASPNETCORE_URLS="http://*:1867" -ASPNETCORE_ENVIRONMENT="Production" -DOTNET_CLI_TELEMETRY_OPTOUT=1 -DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false -LIBVA_DRIVER_NAME=iHD -EOF -msg_ok "Setup Sportarr" - -msg_info "Creating Service" -cat </etc/systemd/system/sportarr.service -[Unit] -Description=Sportarr Service -After=network.target - -[Service] -EnvironmentFile=/opt/sportarr/.env -WorkingDirectory=/opt/sportarr -ExecStart=/opt/sportarr/Sportarr -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now sportarr -msg_info "Created Service" - -motd_ssh -customize -cleanup_lxc From b411e6ccd41e5d815e01189512119c00d7f6f38a Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 6 Jan 2026 20:02:37 +0100 Subject: [PATCH 228/378] fix papra --- install/papra-install.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index 3a1de4b2b..bd6376f27 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -3,7 +3,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/CorentinTh/papra +# Source: https://github.com/papra-hq/papra source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -15,7 +15,6 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - git \ build-essential \ tesseract-ocr \ tesseract-ocr-all @@ -23,14 +22,11 @@ msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs -msg_info "Cloning Papra Repository" -cd /opt -RELEASE=$(curl -s https://api.github.com/repos/papra-hq/papra/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') -$STD git clone --depth=1 --branch ${RELEASE} https://github.com/papra-hq/papra.git -cd papra -msg_ok "Cloned Papra Repository" +RELEASE=$(curl -fsSL https://api.github.com/repos/papra-hq/papra/releases | grep -oP '"tag_name":\s*"\K@papra/docker@[^"]+' | head -n1) +fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE}" "/opt/papra" msg_info "Setup Papra" +cd /opt/papra export COREPACK_ENABLE_NETWORK=1 $STD corepack enable $STD corepack prepare pnpm@10.19.0 --activate From a5857b2a5898007e908e3d4f8adbe1045a1d3da0 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 6 Jan 2026 20:07:52 +0100 Subject: [PATCH 229/378] encode url --- install/papra-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index bd6376f27..d17db0a3b 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -23,7 +23,8 @@ msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/papra-hq/papra/releases | grep -oP '"tag_name":\s*"\K@papra/docker@[^"]+' | head -n1) -fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE}" "/opt/papra" +RELEASE_ENCODED=$(printf '%s' "$RELEASE" | jq -sRr @uri) +fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE_ENCODED}" "/opt/papra" msg_info "Setup Papra" cd /opt/papra From e06cd4458486fcf274a5ceb404b2f74d67544a4d Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 6 Jan 2026 20:10:34 +0100 Subject: [PATCH 230/378] --- install/papra-install.sh | 3 +-- misc/tools.func | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index d17db0a3b..bd6376f27 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -23,8 +23,7 @@ msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/papra-hq/papra/releases | grep -oP '"tag_name":\s*"\K@papra/docker@[^"]+' | head -n1) -RELEASE_ENCODED=$(printf '%s' "$RELEASE" | jq -sRr @uri) -fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE_ENCODED}" "/opt/papra" +fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE}" "/opt/papra" msg_info "Setup Papra" cd /opt/papra diff --git a/misc/tools.func b/misc/tools.func index f7c13fa7c..4538f067c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1759,7 +1759,10 @@ function fetch_and_deploy_gh_release() { if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then # GitHub API's tarball_url/zipball_url can return HTTP 300 Multiple Choices # when a branch and tag share the same name. Use explicit refs/tags/ URL instead. - local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$tag_name.tar.gz" + # URL-encode the tag_name for special characters (e.g., @papra/docker@25.12.0) + local tag_encoded + tag_encoded=$(printf '%s' "$tag_name" | jq -sRr @uri) + local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$tag_encoded.tar.gz" filename="${app_lc}-${version}.tar.gz" curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" || { From 48ff029e4a22de5cd641d61771d4a794c2fe74b6 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 6 Jan 2026 20:24:31 +0100 Subject: [PATCH 231/378] Update pull_request_template.md --- .github/pull_request_template.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b1a6ee56f..3a9225cb6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -37,3 +37,14 @@ Link: # ## 📋 Additional Information (optional) + +--- + +## 📦 Application Requirements (for new scripts) + +> Required for **🆕 New script** submissions. +> PRs that do not meet these requirements may be closed without review. +- [ ] The application is **at least 6 months old** +- [ ] The application is **actively maintained** +- [ ] The application has **200+ GitHub stars** +- [ ] Official **release tarballs** are published From 15d22d7f9815d3e28819ac8218e06b09e8ecf4fd Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 6 Jan 2026 20:24:31 +0100 Subject: [PATCH 232/378] filename fix --- misc/tools.func | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 4538f067c..1a58ab825 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1763,7 +1763,9 @@ function fetch_and_deploy_gh_release() { local tag_encoded tag_encoded=$(printf '%s' "$tag_name" | jq -sRr @uri) local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$tag_encoded.tar.gz" - filename="${app_lc}-${version}.tar.gz" + # Sanitize version for filename (replace / with -) + local version_safe="${version//\//-}" + filename="${app_lc}-${version_safe}.tar.gz" curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" || { msg_error "Download failed: $direct_tarball_url" From f5b9938bc1593505633849c0a483d595ae813b6a Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 6 Jan 2026 20:35:09 +0100 Subject: [PATCH 233/378] finalize papra --- install/papra-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index bd6376f27..b30998959 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -83,8 +83,9 @@ Type=simple User=root WorkingDirectory=/opt/papra/apps/papra-server EnvironmentFile=/opt/papra/.env -ExecStartPre=/usr/bin/pnpm --silent run migration:apply -ExecStart=/usr/bin/pnpm --silent run start +Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ExecStartPre=/usr/bin/corepack pnpm --silent run migration:apply +ExecStart=/usr/bin/corepack pnpm --silent run start Restart=on-failure RestartSec=10 From 50e8da849a70af645530fed7875fac2507f3fcc8 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 6 Jan 2026 20:35:25 +0100 Subject: [PATCH 234/378] fix pnpm --- install/papra-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index b30998959..87287d36f 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -15,9 +15,9 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - build-essential \ - tesseract-ocr \ - tesseract-ocr-all + build-essential \ + tesseract-ocr \ + tesseract-ocr-all msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs From 28db028ba7b404bf75e1b2ba7b858e5cea551d6b Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 6 Jan 2026 20:51:39 +0100 Subject: [PATCH 235/378] testing --- ct/papra.sh | 39 ++++++++++++++++++++++++++------------- install/papra-install.sh | 3 ++- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/ct/papra.sh b/ct/papra.sh index 7804d77e8..29c11adc1 100644 --- a/ct/papra.sh +++ b/ct/papra.sh @@ -3,7 +3,7 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/CorentinTh/papra +# Source: https://github.com/papra-hq/papra APP="Papra" var_tags="${var_tags:-document-management}" @@ -27,17 +27,28 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating Papra LXC" - systemctl stop papra - cd /opt/papra - git fetch - git pull - $STD pnpm install --frozen-lockfile - $STD pnpm --filter "@papra/app-client..." run build - $STD pnpm --filter "@papra/app-server..." run build - systemctl start papra - msg_ok "Updated Papra LXC" - msg_ok "Updated successfully!" + RELEASE=$(curl -fsSL https://api.github.com/repos/papra-hq/papra/releases | grep -oP '"tag_name":\s*"\K@papra/docker@[^"]+' | head -n1) + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Stopping Service" + systemctl stop papra + msg_ok "Stopped Service" + + msg_info "Updating ${APP} to ${RELEASE}" + cd /opt/papra + fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE}" "/opt/papra" + $STD pnpm install --frozen-lockfile + $STD pnpm --filter "@papra/app-client..." run build + $STD pnpm --filter "@papra/app-server..." run build + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated ${APP} to ${RELEASE}" + + msg_info "Starting Service" + systemctl start papra + msg_ok "Started Service" + msg_ok "Updated successfully!" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi exit } @@ -46,4 +57,6 @@ build_container description msg_ok "Completed successfully!\n" -msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" +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}:1221${CL}" diff --git a/install/papra-install.sh b/install/papra-install.sh index 87287d36f..efb8cfff0 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -30,7 +30,7 @@ cd /opt/papra export COREPACK_ENABLE_NETWORK=1 $STD corepack enable $STD corepack prepare pnpm@10.19.0 --activate -$STD pnpm install --frozen-lockfile --ignore-scripts +$STD pnpm install --frozen-lockfile $STD pnpm --filter "@papra/app-client..." run build $STD pnpm --filter "@papra/app-server..." run build msg_ok "Set up Papra" @@ -94,6 +94,7 @@ WantedBy=multi-user.target EOF systemctl enable -q --now papra +echo "${RELEASE}" >/opt/Papra_version.txt msg_ok "Created and Started Papra Service" motd_ssh From 471726e3ee9ec8eb1de99e073b13d0343d902a76 Mon Sep 17 00:00:00 2001 From: Tobias Date: Wed, 7 Jan 2026 13:08:35 +0100 Subject: [PATCH 236/378] fix yubal update --- ct/yubal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/yubal.sh b/ct/yubal.sh index ca42db372..8be7de01d 100644 --- a/ct/yubal.sh +++ b/ct/yubal.sh @@ -40,7 +40,7 @@ function update_script() { cd /opt/yubal/web $STD bun install --frozen-lockfile VERSION=$(get_latest_github_release "guillevc/yubal") - $STD VITE_VERSION=$VERSION VITE_COMMIT_SHA=$VERSION VITE_IS_RELEASE=true bun run build + VITE_VERSION=$VERSION VITE_COMMIT_SHA=$VERSION VITE_IS_RELEASE=true $STD bun run build msg_ok "Built Frontend" msg_info "Installing Python Dependencies" From 5120597cb67277c3467aeeec232b32980bec4250 Mon Sep 17 00:00:00 2001 From: Tobias Date: Wed, 7 Jan 2026 13:12:38 +0100 Subject: [PATCH 237/378] fix yubal data --- install/yubal-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/yubal-install.sh b/install/yubal-install.sh index 56ea58e78..1b3ba3dfc 100644 --- a/install/yubal-install.sh +++ b/install/yubal-install.sh @@ -37,7 +37,7 @@ msg_ok "Installed Deno" msg_info "Creating directories" mkdir -p /opt/yubal \ - /opt/yubal/data \ + /opt/yubal_data \ /opt/yubal/ytdlp msg_ok "Created directories" @@ -60,14 +60,14 @@ msg_info "Creating Service" cat </opt/yubal.env YUBAL_HOST=0.0.0.0 YUBAL_PORT=8000 -YUBAL_DATA_DIR=/opt/yubal/data +YUBAL_DATA_DIR=/opt/yubal_data YUBAL_BEETS_DIR=/opt/yubal/beets YUBAL_YTDLP_DIR=/opt/yubal/ytdlp PYTHONUNBUFFERED=1 EOF cat </etc/systemd/system/yubal.service [Unit] -Description=Yubal Music Management +Description=Yubal After=network.target [Service] From dc5a033f44368f8958d1e0bc0c04189929d65fb4 Mon Sep 17 00:00:00 2001 From: Marc Went Date: Wed, 7 Jan 2026 15:02:42 +0100 Subject: [PATCH 238/378] minor tweak, not stripping v from version --- ct/wishlist.sh | 2 +- install/wishlist-install.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 63e9e2db4..866a7fb2c 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.sh @@ -40,7 +40,7 @@ function update_script() { cp -R /opt/wishlist/data /opt/wishlist-backup/data CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" - LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") + LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist" false) msg_info "Updating Wishlist" cd /opt/wishlist diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index b60b1e13c..9e6683e9b 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -23,7 +23,7 @@ msg_ok "Installed dependencies" NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" -LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") +LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist" false) msg_info "Installing Wishlist" cd /opt/wishlist @@ -33,8 +33,8 @@ $STD pnpm install $STD pnpm svelte-kit sync $STD pnpm prisma generate sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) -export VERSION="${LATEST_APP_VERSION}" -export SHA="${LATEST_APP_VERSION}" +export VERSION="v${LATEST_APP_VERSION}" +export SHA="v${LATEST_APP_VERSION}" $STD pnpm run build $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh From a0f54d8bf66c51286827f09f71e4ef726678f67d Mon Sep 17 00:00:00 2001 From: Tobias Date: Wed, 7 Jan 2026 15:44:06 +0100 Subject: [PATCH 239/378] fix yubal env --- ct/yubal.sh | 8 ++++++++ install/yubal-install.sh | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ct/yubal.sh b/ct/yubal.sh index 8be7de01d..ad57ca984 100644 --- a/ct/yubal.sh +++ b/ct/yubal.sh @@ -34,8 +34,16 @@ function update_script() { systemctl stop yubal msg_ok "Stopped Services" + msg_info "Backing up" + cp /opt/yubal/.env /opt/yubal.env + msg_ok "Backed up" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yubal" "guillevc/yubal" "tarball" "latest" "/opt/yubal" + msg_info "Restoring Backup" + mv /opt/yubal.env /opt/yubal/.env + msg_ok "Restored Backup" + msg_info "Building Frontend" cd /opt/yubal/web $STD bun install --frozen-lockfile diff --git a/install/yubal-install.sh b/install/yubal-install.sh index 1b3ba3dfc..4e2b9d2eb 100644 --- a/install/yubal-install.sh +++ b/install/yubal-install.sh @@ -57,7 +57,7 @@ $STD uv sync --no-dev --frozen msg_ok "Installed Python Dependencies" msg_info "Creating Service" -cat </opt/yubal.env +cat </opt/yubal/.env YUBAL_HOST=0.0.0.0 YUBAL_PORT=8000 YUBAL_DATA_DIR=/opt/yubal_data @@ -74,7 +74,7 @@ After=network.target Type=simple User=root WorkingDirectory=/opt/yubal -EnvironmentFile=/opt/yubal.env +EnvironmentFile=/opt/yubal/.env Environment="PATH=/opt/yubal/.venv/bin:/usr/local/bin:/usr/bin:/bin" ExecStart=/opt/yubal/.venv/bin/python -m yubal Restart=always From 2c348d4d94fc7b61568cd5fe16e2e12dc6337bf1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:23:36 +0100 Subject: [PATCH 240/378] Automate VM provisioning via serial console Replaces SSH-based installation with serial console automation using a new send_line_to_vm function. The script now logs in, installs Podman, downloads and installs UniFi OS Server, and retrieves the VM IP address directly via the console. This simplifies the process and removes SSH and sshpass dependencies. --- vm/unifi-os-server-vm.sh | 239 ++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 117 deletions(-) diff --git a/vm/unifi-os-server-vm.sh b/vm/unifi-os-server-vm.sh index 7ea19eb4f..134a8b9ad 100644 --- a/vm/unifi-os-server-vm.sh +++ b/vm/unifi-os-server-vm.sh @@ -107,6 +107,77 @@ function cleanup_vmid() { fi } +function send_line_to_vm() { + local line="$1" + echo -e "${DGN}Sending: ${YW}${line}${CL}" + for ((i = 0; i < ${#line}; i++)); do + character=${line:i:1} + case $character in + " ") character="spc" ;; + "-") character="minus" ;; + "=") character="equal" ;; + ",") character="comma" ;; + ".") character="dot" ;; + "/") character="slash" ;; + "'") character="apostrophe" ;; + ";") character="semicolon" ;; + '\') character="backslash" ;; + '\`') character="grave_accent" ;; + "[") character="bracket_left" ;; + "]") character="bracket_right" ;; + "_") character="shift-minus" ;; + "+") character="shift-equal" ;; + "?") character="shift-slash" ;; + "<") character="shift-comma" ;; + ">") character="shift-dot" ;; + '"') character="shift-apostrophe" ;; + ":") character="shift-semicolon" ;; + "|") character="shift-backslash" ;; + "~") character="shift-grave_accent" ;; + "{") character="shift-bracket_left" ;; + "}") character="shift-bracket_right" ;; + "A") character="shift-a" ;; + "B") character="shift-b" ;; + "C") character="shift-c" ;; + "D") character="shift-d" ;; + "E") character="shift-e" ;; + "F") character="shift-f" ;; + "G") character="shift-g" ;; + "H") character="shift-h" ;; + "I") character="shift-i" ;; + "J") character="shift-j" ;; + "K") character="shift-k" ;; + "L") character="shift-l" ;; + "M") character="shift-m" ;; + "N") character="shift-n" ;; + "O") character="shift-o" ;; + "P") character="shift-p" ;; + "Q") character="shift-q" ;; + "R") character="shift-r" ;; + "S") character="shift-s" ;; + "T") character="shift-t" ;; + "U") character="shift-u" ;; + "V") character="shift-v" ;; + "W") character="shift-w" ;; + "X") character="shift-x" ;; + "Y") character="shift-y" ;; + "Z") character="shift-z" ;; + "!") character="shift-1" ;; + "@") character="shift-2" ;; + "#") character="shift-3" ;; + '$') character="shift-4" ;; + "%") character="shift-5" ;; + "^") character="shift-6" ;; + "&") character="shift-7" ;; + "*") character="shift-8" ;; + "(") character="shift-9" ;; + ")") character="shift-0" ;; + esac + qm sendkey $VMID "$character" + done + qm sendkey $VMID ret +} + function cleanup() { popd >/dev/null post_update_to_api "done" "none" @@ -229,7 +300,7 @@ function select_os() { OS_DISPLAY="Ubuntu 24.04 LTS" ;; esac - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}${OS_DISPLAY}${CL}" + #echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}${OS_DISPLAY}${CL}" else exit-script fi @@ -238,14 +309,14 @@ function select_os() { function select_cloud_init() { # UniFi OS Server ALWAYS requires Cloud-Init for automated installation USE_CLOUD_INIT="yes" - echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}yes (required for UniFi OS)${CL}" + #echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}yes (required for UniFi OS)${CL}" } function get_image_url() { local arch=$(dpkg --print-architecture) case $OS_TYPE in debian) - # Always use generic (Cloud-Init) variant for UniFi OS + # Always use /dev/null | jq -r '.[1]["ip-addresses"][]? | select(.["ip-address-type"] == "ipv4") | .["ip-address"]' 2>/dev/null | grep -v "127.0.0.1" | head -1 || echo "") - + VM_IP=$(qm guest cmd $VMID network-get-interfaces 2>/dev/null | jq -r '.[] | select(.name != "lo") | .["ip-addresses"][]? | select(.["ip-address-type"] == "ipv4") | .["ip-address"]' 2>/dev/null | head -1 || echo "") if [ -n "$VM_IP" ]; then - msg_ok "VM IP Address: ${VM_IP}" break fi sleep 2 done - if [ -z "$VM_IP" ]; then - msg_error "Could not detect VM IP address" - echo -e "${TAB}${INFO}${YW}Use Proxmox Console to login with Cloud-Init credentials${CL}" - echo -e "${TAB}${INFO}${YW}User: ${BGN}${CLOUDINIT_USER:-root}${CL} / Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" - exit 1 - fi - - # Wait for SSH to be ready - msg_info "Waiting for SSH to be ready" - SSH_READY=0 - for i in {1..30}; do - if timeout 5 sshpass -p "${CLOUDINIT_PASSWORD}" ssh -o ConnectTimeout=3 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "${CLOUDINIT_USER:-root}@${VM_IP}" "echo 'SSH Ready'" >/dev/null 2>&1; then - SSH_READY=1 - msg_ok "SSH connection ready" - break - fi - sleep 2 - done - - if [ $SSH_READY -eq 0 ]; then - msg_error "SSH connection failed" - echo -e "${TAB}${INFO}${YW}Manual login - User: ${BGN}${CLOUDINIT_USER:-root}${CL} / Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" - exit 1 - fi - - # Check if sshpass is installed - if ! command -v sshpass &>/dev/null; then - msg_info "Installing sshpass for automated SSH" - apt-get update -qq >/dev/null 2>&1 - apt-get install -y sshpass -qq >/dev/null 2>&1 - fi - - # Execute UniFi OS installation directly via SSH - msg_info "Installing UniFi OS Server ${UOS_VERSION} (takes 4-6 minutes)" - - # Create installation script - INSTALL_SCRIPT=$( - cat <<'EOFINSTALL' -#!/bin/bash -set -e -export DEBIAN_FRONTEND=noninteractive - -echo "[1/5] Updating system packages..." -apt-get update -qq -apt-get install -y curl wget ca-certificates podman uidmap slirp4netns iptables -qq - -echo "[2/5] Configuring Podman..." -loginctl enable-linger root - -echo "[3/5] Downloading UniFi OS Server installer..." -cd /root -curl -fsSL "UNIFI_URL" -o unifi-installer.bin -chmod +x unifi-installer.bin - -echo "[4/5] Installing UniFi OS Server (this takes 3-5 minutes)..." -./unifi-installer.bin install - -echo "[5/5] Starting UniFi OS Server..." -sleep 15 - -if systemctl list-unit-files | grep -q unifi-os-server; then - systemctl enable unifi-os-server - systemctl start unifi-os-server - sleep 10 - - if systemctl is-active --quiet unifi-os-server; then - echo "✓ UniFi OS Server is running" + if [ -n "$VM_IP" ]; then + msg_ok "VM IP Address: ${VM_IP}" else - echo "⚠ Service status:" - systemctl status unifi-os-server --no-pager || true + msg_info "IP address will be shown in VM console" + send_line_to_vm "ip -4 addr show | grep inet | grep -v 127.0.0.1" fi -fi - -echo "Installation completed!" -EOFINSTALL - ) - - # Replace URL placeholder - INSTALL_SCRIPT="${INSTALL_SCRIPT//UNIFI_URL/$UOS_URL}" - - # Execute installation via SSH (with output streaming) - if sshpass -p "${CLOUDINIT_PASSWORD}" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "${CLOUDINIT_USER:-root}@${VM_IP}" "bash -s" <<<"$INSTALL_SCRIPT" 2>&1 | while IFS= read -r line; do - echo -e "${TAB}${DGN}${line}${CL}" - done; then - msg_ok "UniFi OS Server installed successfully" - else - msg_error "Installation failed" - echo -e "${TAB}${INFO}${YW}Check logs: ${BL}ssh ${CLOUDINIT_USER:-root}@${VM_IP}${CL}" - exit 1 - fi - - # Wait for UniFi OS web interface - msg_info "Waiting for UniFi OS web interface (port 11443)" - PORT_OPEN=0 - for i in {1..60}; do - if timeout 2 bash -c ">/dev/tcp/${VM_IP}/11443" 2>/dev/null; then - PORT_OPEN=1 - msg_ok "UniFi OS Server web interface is ready" - break - fi - sleep 2 - done echo "" - if [ $PORT_OPEN -eq 1 ]; then - echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ UniFi OS Server is ready!${CL}" + echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ UniFi OS Server installation complete!${CL}" + if [ -n "$VM_IP" ]; then echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ Access at: ${BGN}https://${VM_IP}:11443${CL}" else - echo -e "${TAB}${INFO}${YW}UniFi OS is installed but web interface not yet available${CL}" - echo -e "${TAB}${INFO}${YW}Access at: ${BGN}https://${VM_IP}:11443${CL} ${YW}(may take 1-2 more minutes)${CL}" + echo -e "${TAB}${INFO}${YW}Access via: ${BGN}https://:11443${CL}" fi - - echo -e "${TAB}${INFO}${DGN}SSH Access: ${BL}ssh ${CLOUDINIT_USER:-root}@${VM_IP}${CL}" - echo -e "${TAB}${INFO}${DGN}Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" + echo -e "${TAB}${INFO}${DGN}Console login - User: ${BGN}root${CL} / Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" + echo -e "${TAB}${INFO}${YW}Note: UniFi OS may take 1-2 more minutes to fully start${CL}" echo "" fi From 6173f5316e96f6a37ac4cbd4b91aec74cafa71ea Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:35:58 +0100 Subject: [PATCH 241/378] Update unifi-os-server-vm.sh --- vm/unifi-os-server-vm.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/vm/unifi-os-server-vm.sh b/vm/unifi-os-server-vm.sh index 134a8b9ad..c3ed0369f 100644 --- a/vm/unifi-os-server-vm.sh +++ b/vm/unifi-os-server-vm.sh @@ -109,7 +109,6 @@ function cleanup_vmid() { function send_line_to_vm() { local line="$1" - echo -e "${DGN}Sending: ${YW}${line}${CL}" for ((i = 0; i < ${#line}; i++)); do character=${line:i:1} case $character in From e4c30ebca5003b9bbdba0ae363e0f8645f94890b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:53:40 +0100 Subject: [PATCH 242/378] ip --- vm/unifi-os-server-vm.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/vm/unifi-os-server-vm.sh b/vm/unifi-os-server-vm.sh index c3ed0369f..33aa1619b 100644 --- a/vm/unifi-os-server-vm.sh +++ b/vm/unifi-os-server-vm.sh @@ -787,11 +787,20 @@ if [ "$START_VM" == "yes" ]; then sleep 300 msg_ok "UniFi OS Server installed" - # Step 4: Get IP address for final message + # Step 4: Start Guest Agent for IP detection + msg_info "Starting QEMU Guest Agent" + send_line_to_vm "systemctl start qemu-guest-agent" + sleep 3 + msg_ok "Guest Agent started" + + # Logout from VM console + send_line_to_vm "exit" + sleep 2 + + # Get IP from outside via Guest Agent msg_info "Detecting VM IP address" - sleep 5 VM_IP="" - for i in {1..30}; do + for i in {1..15}; do VM_IP=$(qm guest cmd $VMID network-get-interfaces 2>/dev/null | jq -r '.[] | select(.name != "lo") | .["ip-addresses"][]? | select(.["ip-address-type"] == "ipv4") | .["ip-address"]' 2>/dev/null | head -1 || echo "") if [ -n "$VM_IP" ]; then break @@ -802,8 +811,7 @@ if [ "$START_VM" == "yes" ]; then if [ -n "$VM_IP" ]; then msg_ok "VM IP Address: ${VM_IP}" else - msg_info "IP address will be shown in VM console" - send_line_to_vm "ip -4 addr show | grep inet | grep -v 127.0.0.1" + msg_info "Could not detect IP - check VM console" fi echo "" From b09d6de6819f3517c91b8de0fa722b5954c64adb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:54:29 +0100 Subject: [PATCH 243/378] ram --- vm/unifi-os-server-vm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/unifi-os-server-vm.sh b/vm/unifi-os-server-vm.sh index 33aa1619b..bce838602 100644 --- a/vm/unifi-os-server-vm.sh +++ b/vm/unifi-os-server-vm.sh @@ -341,7 +341,7 @@ function default_settings() { HN="unifi-server-os" CPU_TYPE=" -cpu host" CORE_COUNT="2" - RAM_SIZE="4096" + RAM_SIZE="6144" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" From aca555c06863da0d2caa1c47b7d71e43f6bcdbf3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 7 Jan 2026 19:23:25 +0100 Subject: [PATCH 244/378] add GWN Manager script --- ct/gwn-manager.sh | 42 +++++++++++++++++++++++++++ install/gwn-manager-install.sh | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 ct/gwn-manager.sh create mode 100644 install/gwn-manager-install.sh diff --git a/ct/gwn-manager.sh b/ct/gwn-manager.sh new file mode 100644 index 000000000..73d0938b8 --- /dev/null +++ b/ct/gwn-manager.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-2026 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager + +APP="GWN Manager" +var_tags="${var_tags:-network;management}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-6144}" +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 /gwn ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Application is updated via the 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}https://${IP}:8443${CL}" diff --git a/install/gwn-manager-install.sh b/install/gwn-manager-install.sh new file mode 100644 index 000000000..6e3bd6f73 --- /dev/null +++ b/install/gwn-manager-install.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager + +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 \ + xfonts-utils \ + fontconfig +msg_ok "Installed Dependencies" + +msg_info "Setting up GWN Manager" +RELEASE=$(curl -s https://www.grandstream.com/support/tools#gwntools \ + | grep -oP 'https://firmware\.grandstream\.com/GWN_Manager-[^"]+-Ubuntu\.tar\.gz') +download_with_progress "$RELEASE" "/tmp/gwnmanager.tar.gz" +cd /tmp +tar -xzf gwnmanager.tar.gz --strip-components=1 +$STD ./install +msg_ok "Setup GWN Manager" + +msg_info "Creating Service" +cat </etc/systemd/system/gwnmanager.service +[Unit] +Description=GWN Manager +After=network.target + +[Service] +Type=simple +WorkingDirectory=/gwn +ExecStart=/gwn/gwn start +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now gwnmanager +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 2d0131288d5b9b9a482e1b10ba0e38bccb3cf59d Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 7 Jan 2026 19:30:50 +0100 Subject: [PATCH 245/378] update GWN Manager --- ct/gwn-manager.sh | 2 +- frontend/public/json/gwn-manager.json | 35 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 frontend/public/json/gwn-manager.json diff --git a/ct/gwn-manager.sh b/ct/gwn-manager.sh index 73d0938b8..03c9a086d 100644 --- a/ct/gwn-manager.sh +++ b/ct/gwn-manager.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.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager -APP="GWN Manager" +APP="GWN-Manager" var_tags="${var_tags:-network;management}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-6144}" diff --git a/frontend/public/json/gwn-manager.json b/frontend/public/json/gwn-manager.json new file mode 100644 index 000000000..1a78754de --- /dev/null +++ b/frontend/public/json/gwn-manager.json @@ -0,0 +1,35 @@ +{ + "name": "GWN Manager", + "slug": "gwn-manager", + "categories": [ + 9 + ], + "date_created": "2025-12-02", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8443, + "documentation": "https://documentation.grandstream.com/article-categories/gwn-mgmt/", + "website": "https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gwn-manager.webp", + "config_path": "", + "description": "GWN Manager is a web-based management interface for GrandStream GWN series devices.", + "install_methods": [ + { + "type": "default", + "script": "ct/gwn-manager.sh", + "resources": { + "cpu": 2, + "ram": 6144, + "hdd": 8, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} From 58381e11dee90b01a72ca01e431e068cf0d4aa7e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 7 Jan 2026 20:03:02 +0100 Subject: [PATCH 246/378] finish GWN Manager script --- frontend/public/json/gwn-manager.json | 7 ++++++- install/gwn-manager-install.sh | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/public/json/gwn-manager.json b/frontend/public/json/gwn-manager.json index 1a78754de..83d86b9ba 100644 --- a/frontend/public/json/gwn-manager.json +++ b/frontend/public/json/gwn-manager.json @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "Gentoo compiles packages from source. Initial setup may take time.", + "type": "warning" + } + ] } diff --git a/install/gwn-manager-install.sh b/install/gwn-manager-install.sh index 6e3bd6f73..72b5b7603 100644 --- a/install/gwn-manager-install.sh +++ b/install/gwn-manager-install.sh @@ -19,10 +19,10 @@ $STD apt install -y \ fontconfig msg_ok "Installed Dependencies" -msg_info "Setting up GWN Manager" +msg_info "Setting up GWN Manager (Patience)" RELEASE=$(curl -s https://www.grandstream.com/support/tools#gwntools \ | grep -oP 'https://firmware\.grandstream\.com/GWN_Manager-[^"]+-Ubuntu\.tar\.gz') -download_with_progress "$RELEASE" "/tmp/gwnmanager.tar.gz" +download_file "$RELEASE" "/tmp/gwnmanager.tar.gz" cd /tmp tar -xzf gwnmanager.tar.gz --strip-components=1 $STD ./install @@ -33,6 +33,7 @@ cat </etc/systemd/system/gwnmanager.service [Unit] Description=GWN Manager After=network.target +Requires=network.target [Service] Type=simple @@ -44,7 +45,7 @@ RestartSec=10 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now gwnmanager +systemctl enable -q gwnmanager msg_ok "Created Service" motd_ssh From 619015e68f00493c5be7a937d75141b665ca790a Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 7 Jan 2026 20:36:02 +0100 Subject: [PATCH 247/378] finish GWN Manager script --- ct/gwn-manager.sh | 2 +- frontend/public/json/gwn-manager.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ct/gwn-manager.sh b/ct/gwn-manager.sh index 03c9a086d..3c63e2c68 100644 --- a/ct/gwn-manager.sh +++ b/ct/gwn-manager.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - msg_info "Application is updated via the web interface!" + msg_ok "Application is updated via the web interface!" exit } diff --git a/frontend/public/json/gwn-manager.json b/frontend/public/json/gwn-manager.json index 83d86b9ba..39f0f53ae 100644 --- a/frontend/public/json/gwn-manager.json +++ b/frontend/public/json/gwn-manager.json @@ -12,8 +12,8 @@ "documentation": "https://documentation.grandstream.com/article-categories/gwn-mgmt/", "website": "https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gwn-manager.webp", - "config_path": "", - "description": "GWN Manager is a web-based management interface for GrandStream GWN series devices.", + "config_path": "/gwn/conf/gwn.conf", + "description": "GWN Manager is a free on-premise enterprise-grade, management platform for Grandstream GWN series devices. Typically deployed on a customer’s private network, this flexible, scalable solution offers simplified configuration and management.", "install_methods": [ { "type": "default", @@ -33,8 +33,8 @@ }, "notes": [ { - "text": "Gentoo compiles packages from source. Initial setup may take time.", - "type": "warning" + "text": "Installation package is pulled from GrandStream website. Installation may take a while.", + "type": "info" } ] } From 5d03b3cedcfad8066c8524d71fe59008e763afa3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 7 Jan 2026 21:29:30 +0100 Subject: [PATCH 248/378] fix RoMM release fetching --- install/romm-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/romm-install.sh b/install/romm-install.sh index 654099eaf..1f442c10f 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -120,7 +120,7 @@ CONFIGEOF chmod 644 /var/lib/romm/config/config.yml msg_ok "Created configuration file" -fetch_and_deploy_gh_release "RetroAchievements" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux.zip" +fetch_and_deploy_gh_release "RetroAchievements" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux-*.zip" msg_info "Building RAHasher (RetroAchievements)" cd /opt/RALibretro From a8fcd742b5033146cb1301e65e92aebb9abbd596 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 7 Jan 2026 21:32:39 +0100 Subject: [PATCH 249/378] add cronmaster ct --- ct/cronmaster.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 ct/cronmaster.sh diff --git a/ct/cronmaster.sh b/ct/cronmaster.sh new file mode 100644 index 000000000..2c95f9afd --- /dev/null +++ b/ct/cronmaster.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: + +APP="CRONMASTER" +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}" +var_gpu="${var_gpu:-yes}" +#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 /opt/cronmaster ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating Debian LXC" + $STD apt update + $STD apt upgrade -y + msg_ok "Updated Debian LXC" + cleanup_lxc + exit +} + +start +build_container +description + +msg_ok "Completed successfully!" +msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" From 386a13d727955316ba93a7eb1b1af8d82258a10b Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 7 Jan 2026 21:33:38 +0100 Subject: [PATCH 250/378] add cronmaster ct --- ct/cronmaster.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ct/cronmaster.sh b/ct/cronmaster.sh index 2c95f9afd..7a2e0ac50 100644 --- a/ct/cronmaster.sh +++ b/ct/cronmaster.sh @@ -7,13 +7,12 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV APP="CRONMASTER" var_tags="${var_tags:-}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-8192}" -var_disk="${var_disk:-20}" +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}" -var_gpu="${var_gpu:-yes}" #var_fuse="${var_fuse:-no}" #var_tun="${var_tun:-no}" From f3a5fa89282bfcd69af7b324114dd0544cbfafe6 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Thu, 8 Jan 2026 09:51:36 +0100 Subject: [PATCH 251/378] kutt: fix build.func and config location --- ct/kutt.sh | 2 +- frontend/public/json/kutt.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/kutt.sh b/ct/kutt.sh index f1bc257bf..79b1c2671 100644 --- a/ct/kutt.sh +++ b/ct/kutt.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-2026 community-scripts ORG # Author: tomfrenzel # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE diff --git a/frontend/public/json/kutt.json b/frontend/public/json/kutt.json index f4c8186c7..1200e34ae 100644 --- a/frontend/public/json/kutt.json +++ b/frontend/public/json/kutt.json @@ -10,7 +10,7 @@ "privileged": false, "interface_port": 443, "documentation": "https://github.com/thedevs-network/kutt/", - "config_path": "/etc/kutt-data/.env", + "config_path": "/opt/kutt/.env", "website": "https://kutt.it", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kutt.webp", "description": "Kutt is a modern URL shortener with support for custom domains. Create and edit links, view statistics, manage users, and more.", From 46e0d4f573abf3b5eb3de374a4582fefc9acbc55 Mon Sep 17 00:00:00 2001 From: sfriedrich Date: Thu, 8 Jan 2026 11:34:56 +0100 Subject: [PATCH 252/378] feat: add forgejo runner --- ct/forgejo-runner.sh | 95 ++++++++++++++++++++ install/forgejo-runner-install.sh | 140 ++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 ct/forgejo-runner.sh create mode 100644 install/forgejo-runner-install.sh diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh new file mode 100644 index 000000000..c8196a4ea --- /dev/null +++ b/ct/forgejo-runner.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) + +# Copyright (c) 2026 +# Author: Simon Friedrich +# License: MIT +# Source: https://forgejo.org/ + +APP="Forgejo Runner" +var_tags="${var_tags:-ci}" +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}" + +# REQUIRED for Podman-in-LXC +var_unprivileged="1" +var_nesting="1" +var_keyctl="1" + +# ------------------------------------------------- +# Framework setup +# ------------------------------------------------- +header_info "$APP" +variables +color +catch_errors + +# ------------------------------------------------- +# Description +# ------------------------------------------------- +function description() { + cat </dev/null 2>&1; then + gpg --keyserver hkps://keys.openpgp.org --recv "$GPG_KEY" >/dev/null 2>&1 +fi + +gpg --verify /tmp/forgejo-runner.asc /usr/local/bin/forgejo-runner >/dev/null 2>&1 \ + && msg_ok "Signature valid" \ + || { msg_error "Signature verification failed"; exit 1; } + +# ------------------------------------------------- +# Runner registration +# ------------------------------------------------- +msg_info "Registering Forgejo Runner" + +export DOCKER_HOST="unix:///run/podman/podman.sock" + +forgejo-runner register \ + --instance "$FORGEJO_INSTANCE" \ + --token "$FORGEJO_RUNNER_TOKEN" \ + --name "$HOSTNAME" \ + --labels "linux-${ARCH}:docker://node:20-bookworm" \ + --no-interactive + +msg_ok "Runner registered" + +# ------------------------------------------------- +# systemd service +# ------------------------------------------------- +msg_info "Creating systemd service" + +cat </etc/systemd/system/forgejo-runner.service +[Unit] +Description=Forgejo Runner +Documentation=https://forgejo.org/docs/latest/admin/actions/ +After=podman.socket +Requires=podman.socket + +[Service] +User=root +WorkingDirectory=/root +Environment=DOCKER_HOST=unix:///run/podman/podman.sock +ExecStart=/usr/local/bin/forgejo-runner daemon +Restart=on-failure +RestartSec=10 +TimeoutSec=0 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable --now forgejo-runner +msg_ok "Forgejo Runner service enabled" + +motd_ssh +customize +cleanup_lxc \ No newline at end of file From b104727293e08f16ce078bc060af993037ab2986 Mon Sep 17 00:00:00 2001 From: lengschder97 Date: Fri, 9 Jan 2026 09:22:12 +0100 Subject: [PATCH 253/378] Fixed comments --- ct/forgejo-runner.sh | 38 +++++--------------------- install/forgejo-runner-install.sh | 44 ++++--------------------------- 2 files changed, 11 insertions(+), 71 deletions(-) diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh index c8196a4ea..95d002e26 100644 --- a/ct/forgejo-runner.sh +++ b/ct/forgejo-runner.sh @@ -1,9 +1,9 @@ #!/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) 2026 +# Copyright (c) 2021-2026 community-scripts ORG # Author: Simon Friedrich -# License: MIT +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://forgejo.org/ APP="Forgejo Runner" @@ -14,10 +14,9 @@ var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" -# REQUIRED for Podman-in-LXC -var_unprivileged="1" -var_nesting="1" -var_keyctl="1" +var_unprivileged="${var_unprivileged:-1}" +var_nesting="${var_nesting:-1}" +var_keyctl="${var_keyctl:-1}" # ------------------------------------------------- # Framework setup @@ -27,28 +26,6 @@ variables color catch_errors -# ------------------------------------------------- -# Description -# ------------------------------------------------- -function description() { - cat </dev/null 2>&1; then - gpg --keyserver hkps://keys.openpgp.org --recv "$GPG_KEY" >/dev/null 2>&1 -fi - -gpg --verify /tmp/forgejo-runner.asc /usr/local/bin/forgejo-runner >/dev/null 2>&1 \ - && msg_ok "Signature valid" \ - || { msg_error "Signature verification failed"; exit 1; } - -# ------------------------------------------------- -# Runner registration -# ------------------------------------------------- msg_info "Registering Forgejo Runner" export DOCKER_HOST="unix:///run/podman/podman.sock" @@ -106,9 +76,6 @@ forgejo-runner register \ msg_ok "Runner registered" -# ------------------------------------------------- -# systemd service -# ------------------------------------------------- msg_info "Creating systemd service" cat </etc/systemd/system/forgejo-runner.service @@ -131,8 +98,7 @@ TimeoutSec=0 WantedBy=multi-user.target EOF -systemctl daemon-reload -systemctl enable --now forgejo-runner +systemctl enable -q --now forgejo-runner msg_ok "Forgejo Runner service enabled" motd_ssh From 0b73dfe1b2271a13a4a07cb227ff9f4d7b02df3a Mon Sep 17 00:00:00 2001 From: Marc Went Date: Fri, 9 Jan 2026 10:11:03 +0100 Subject: [PATCH 254/378] fix review --- ct/wishlist.sh | 2 +- frontend/public/json/wishlist.json | 7 +------ install/wishlist-install.sh | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 866a7fb2c..2285ef6f3 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.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-2026 community-scripts ORG # Author: Dunky13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/cmintey/wishlist diff --git a/frontend/public/json/wishlist.json b/frontend/public/json/wishlist.json index e9b7dba80..3024b6e72 100644 --- a/frontend/public/json/wishlist.json +++ b/frontend/public/json/wishlist.json @@ -29,10 +29,5 @@ "username": null, "password": null }, - "notes": [ - { - "text": "Edit /opt/wishlist/.env in the LXC container to customize settings. Restart the container after making changes.", - "type": "info" - } - ] + "notes": [] } diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index 9e6683e9b..3c5b9025d 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2026 community-scripts ORG # Author: Dunky13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/cmintey/wishlist From 8c551c881ddc3132676a20a6c8cc4e26689c7efd Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:20:52 +0100 Subject: [PATCH 255/378] Clean up comments in tracearr-install.sh Removed comments about RAM allocation for timescaledb-tune. --- install/tracearr-install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh index 7a3f72277..57c71d715 100644 --- a/install/tracearr-install.sh +++ b/install/tracearr-install.sh @@ -32,9 +32,6 @@ $STD apt install -y \ timescaledb-2-postgresql-18 \ timescaledb-tools \ timescaledb-toolkit-postgresql-18 -# give timescaledb-tune 50% of total ram in MB -# we need to leave the rest for redis and the webserver. -# We cant use $RAM_SIZE or $var_ram here, which is annoying. total_ram_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') ram_for_tsdb=$((total_ram_kb / 1024 / 2)) $STD timescaledb-tune -yes -memory "$ram_for_tsdb"MB From bed6e8bc983115c619593ad89a5548fa35eeda5b Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:34:35 +0100 Subject: [PATCH 256/378] Update release version output location --- install/tor-snowflake-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tor-snowflake-install.sh b/install/tor-snowflake-install.sh index 8ccb7f999..24c4a564a 100644 --- a/install/tor-snowflake-install.sh +++ b/install/tor-snowflake-install.sh @@ -27,7 +27,7 @@ $STD rm -rf /opt/snowflake.tar.gz $STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake $STD chown -R snowflake:snowflake /opt/tor-snowflake $STD sudo -H -u snowflake bash -c "cd /opt/tor-snowflake/proxy && go build -o snowflake-proxy ." -echo "${RELEASE}" >/opt/tor-snowflake_version.txt +echo "${RELEASE}" >~/.tor-snowflake msg_ok "Built Snowflake Proxy v${RELEASE}" msg_info "Creating Service" From b26566d8f93f854fe3e6b6031005557f825b68a7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:36:26 +0100 Subject: [PATCH 257/378] Update snowflake version storage location --- ct/tor-snowflake.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/tor-snowflake.sh b/ct/tor-snowflake.sh index ae5086ef2..2125380b5 100644 --- a/ct/tor-snowflake.sh +++ b/ct/tor-snowflake.sh @@ -31,7 +31,7 @@ function update_script() { msg_ok "Updated Container OS" RELEASE=$(curl -fsSL https://gitlab.torproject.org/api/v4/projects/tpo%2Fanti-censorship%2Fpluggable-transports%2Fsnowflake/releases | jq -r '.[0].tag_name' | sed 's/^v//') - if [[ ! -f "/opt/tor-snowflake_version.txt" ]] || [[ "${RELEASE}" != "$(cat "/opt/tor-snowflake_version.txt")" ]]; then + if [[ ! -f "~/.tor-snowflake" ]] || [[ "${RELEASE}" != "$(cat "~/.tor-snowflake")" ]]; then msg_info "Stopping Service" systemctl stop snowflake-proxy msg_ok "Stopped Service" @@ -46,7 +46,7 @@ function update_script() { $STD mv /opt/snowflake-v${RELEASE} /opt/tor-snowflake $STD chown -R snowflake:snowflake /opt/tor-snowflake $STD sudo -H -u snowflake bash -c "cd /opt/tor-snowflake/proxy && go build -o snowflake-proxy ." - echo "${RELEASE}" >/opt/tor-snowflake_version.txt + echo "${RELEASE}" >~/.tor-snowflake msg_ok "Updated Snowflake to v${RELEASE}" msg_info "Starting Service" From 0f5ea21f0a2cbdd75777f8256d83dc8cf3f3b691 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:42:59 +0100 Subject: [PATCH 258/378] refactor --- ct/tracearr.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index 1a72c2a32..cf77b25ec 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -35,7 +35,6 @@ function update_script() { PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')" NODE_VERSION="22" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" msg_info "Building Tracearr" @@ -80,7 +79,7 @@ start build_container description -msg_ok "Completed Successfully!\n" +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 902ed1a2705f26f41d23f305ebe4cd1f608c67e6 Mon Sep 17 00:00:00 2001 From: Tobias Date: Fri, 9 Jan 2026 14:09:20 +0100 Subject: [PATCH 259/378] yubal fix env --- ct/yubal.sh | 8 -------- install/yubal-install.sh | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/ct/yubal.sh b/ct/yubal.sh index ad57ca984..8be7de01d 100644 --- a/ct/yubal.sh +++ b/ct/yubal.sh @@ -34,16 +34,8 @@ function update_script() { systemctl stop yubal msg_ok "Stopped Services" - msg_info "Backing up" - cp /opt/yubal/.env /opt/yubal.env - msg_ok "Backed up" - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yubal" "guillevc/yubal" "tarball" "latest" "/opt/yubal" - msg_info "Restoring Backup" - mv /opt/yubal.env /opt/yubal/.env - msg_ok "Restored Backup" - msg_info "Building Frontend" cd /opt/yubal/web $STD bun install --frozen-lockfile diff --git a/install/yubal-install.sh b/install/yubal-install.sh index 4e2b9d2eb..c007e0317 100644 --- a/install/yubal-install.sh +++ b/install/yubal-install.sh @@ -57,9 +57,9 @@ $STD uv sync --no-dev --frozen msg_ok "Installed Python Dependencies" msg_info "Creating Service" -cat </opt/yubal/.env +cat </opt/yubal.env YUBAL_HOST=0.0.0.0 -YUBAL_PORT=8000 +YUBAL_PORT=8001 YUBAL_DATA_DIR=/opt/yubal_data YUBAL_BEETS_DIR=/opt/yubal/beets YUBAL_YTDLP_DIR=/opt/yubal/ytdlp @@ -74,7 +74,7 @@ After=network.target Type=simple User=root WorkingDirectory=/opt/yubal -EnvironmentFile=/opt/yubal/.env +EnvironmentFile=/opt/yubal.env Environment="PATH=/opt/yubal/.venv/bin:/usr/local/bin:/usr/bin:/bin" ExecStart=/opt/yubal/.venv/bin/python -m yubal Restart=always From ca1203753cd327429cddef1d3a3e9a3e78dcfe95 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 9 Jan 2026 08:13:51 -0500 Subject: [PATCH 260/378] nextExplorer: add Collabora integration options --- install/nextexplorer-install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/install/nextexplorer-install.sh b/install/nextexplorer-install.sh index 355221cb4..1d7b40dd3 100644 --- a/install/nextexplorer-install.sh +++ b/install/nextexplorer-install.sh @@ -91,6 +91,12 @@ SESSION_SECRET="${SECRET}" # ONLYOFFICE_FORCE_SAVE= # ONLYOFFICE_FILE_EXTENSIONS= +# COLLABORA_URL= +# COLLABORA_DISCOVERY_URL= +# COLLABORA_SECRET= +# COLLABORA_LANG= +# COLLABORA_FILE_EXTENSIONS= + SHOW_VOLUME_USAGE=true # USER_DIR_ENABLED= # SKIP_HOME= From b14bb9158e7d31b50fc7652e99d467429d7c740e Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 9 Jan 2026 14:28:56 +0100 Subject: [PATCH 261/378] Add workflow to create/update issues on New script merge --- .github/workflows/update_issue.yml | 165 +++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 .github/workflows/update_issue.yml diff --git a/.github/workflows/update_issue.yml b/.github/workflows/update_issue.yml new file mode 100644 index 000000000..38b339057 --- /dev/null +++ b/.github/workflows/update_issue.yml @@ -0,0 +1,165 @@ +name: Update Issue on PR Merge + +on: + pull_request: + types: [closed] + branches: + - main + +permissions: + issues: write + pull-requests: read + +jobs: + update_issues: + if: github.event.pull_request.merged == true && github.repository == 'community-scripts/ProxmoxVED' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Extract Script Names from Changed Files + id: extract_scripts + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + CHANGED_FILES=$(gh pr diff ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --name-only) + + SCRIPT_NAMES=() + + while IFS= read -r FILE; do + if [[ $FILE =~ ^ct/(.+)\.sh$ ]]; then + SCRIPT_NAME="${BASH_REMATCH[1]}" + if [[ ! " ${SCRIPT_NAMES[@]} " =~ " ${SCRIPT_NAME} " ]]; then + SCRIPT_NAMES+=("$SCRIPT_NAME") + fi + elif [[ $FILE =~ ^install/(.+)-install\.sh$ ]]; then + SCRIPT_NAME="${BASH_REMATCH[1]}" + if [[ ! " ${SCRIPT_NAMES[@]} " =~ " ${SCRIPT_NAME} " ]]; then + SCRIPT_NAMES+=("$SCRIPT_NAME") + fi + fi + done <<< "$CHANGED_FILES" + + if [ ${#SCRIPT_NAMES[@]} -eq 0 ]; then + echo "No script files found in PR" + echo "script_names=[]" >> $GITHUB_OUTPUT + exit 0 + fi + + JSON_NAMES=$(printf '%s\n' "${SCRIPT_NAMES[@]}" | jq -R . | jq -s .) + echo "script_names=$JSON_NAMES" >> $GITHUB_OUTPUT + echo "Found script names: ${SCRIPT_NAMES[*]}" + + - name: Process Each Script + if: steps.extract_scripts.outputs.script_names != '[]' + uses: actions/github-script@v7 + env: + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + SCRIPT_NAMES: ${{ steps.extract_scripts.outputs.script_names }} + with: + script: | + const scriptNames = JSON.parse(process.env.SCRIPT_NAMES); + const prAuthor = process.env.PR_AUTHOR; + const message = `@${prAuthor} This PR got merged now and is in the testing phase, it will be migrated to ProxmoxVE when testing is completed`; + const labelName = 'Ready For Testing'; + + for (const scriptName of scriptNames) { + console.log(`Processing script: ${scriptName}`); + + const { data: issues } = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + per_page: 100 + }); + + const scriptNameLower = scriptName.toLowerCase(); + let existingIssue = issues.find(issue => + !issue.pull_request && issue.title.toLowerCase() === scriptNameLower + ); + + if (!existingIssue) { + const { data: searchResults } = await github.rest.search.issuesAndPullRequests({ + q: `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open ${scriptName}`, + per_page: 100 + }); + + existingIssue = searchResults.items.find(issue => + issue.title.toLowerCase() === scriptNameLower + ); + } + + if (existingIssue) { + console.log(`Found existing issue #${existingIssue.number}: ${existingIssue.title}`); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssue.number, + body: message + }); + + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssue.number + }); + + const hasLabel = labels.some(label => label.name === labelName); + + if (!hasLabel) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssue.number, + labels: [labelName] + }); + console.log(`Added "${labelName}" label to issue #${existingIssue.number}`); + } else { + console.log(`Issue #${existingIssue.number} already has "${labelName}" label`); + } + } else { + console.log(`No existing issue found for "${scriptName}", creating new issue`); + + const scriptType = 'CT (LXC Container)'; + + const formattedName = scriptName + .split(/[-_]/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' '); + + const issueBody = `# 🛠️ **New Script** +Create an Issue when you want to merge a new Script. The name of the Issue must be the same as your APP.sh file. (Example: SnipeIT, snipeit.sh; Alpine-Docker, alpine-docker.sh) + +### Name of the Script + +${formattedName} + +### Script Type + +${scriptType} + +### 📋 Script Details + +This script has been merged and is ready for testing. ${message}`; + + const { data: newIssue } = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: scriptName, + body: issueBody, + labels: ['task', labelName] + }); + + console.log(`Created new issue #${newIssue.number}: ${newIssue.title}`); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: newIssue.number, + body: message + }); + } + } + From e73c618c4f63eaff077f2890b7a118539cf86737 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 9 Jan 2026 14:38:51 +0100 Subject: [PATCH 262/378] Add workflow to create/update issues on New script merge --- .github/workflows/update_issue.yml | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/.github/workflows/update_issue.yml b/.github/workflows/update_issue.yml index 38b339057..20d1ac678 100644 --- a/.github/workflows/update_issue.yml +++ b/.github/workflows/update_issue.yml @@ -129,20 +129,14 @@ jobs: .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) .join(' '); - const issueBody = `# 🛠️ **New Script** -Create an Issue when you want to merge a new Script. The name of the Issue must be the same as your APP.sh file. (Example: SnipeIT, snipeit.sh; Alpine-Docker, alpine-docker.sh) - -### Name of the Script - -${formattedName} - -### Script Type - -${scriptType} - -### 📋 Script Details - -This script has been merged and is ready for testing. ${message}`; + const issueBody = '# 🛠️ **New Script**\n' + + 'Create an Issue when you want to merge a new Script. The name of the Issue must be the same as your APP.sh file. (Example: SnipeIT, snipeit.sh; Alpine-Docker, alpine-docker.sh)\n\n' + + '### Name of the Script\n\n' + + formattedName + '\n\n' + + '### Script Type\n\n' + + scriptType + '\n\n' + + '### 📋 Script Details\n\n' + + 'This script has been merged and is ready for testing. ' + message; const { data: newIssue } = await github.rest.issues.create({ owner: context.repo.owner, From 986700fe4c1907b91fe1dbca3b8c9797a046506f Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 9 Jan 2026 14:46:38 +0100 Subject: [PATCH 263/378] Add workflow to create/update issues on New script merge --- .github/workflows/update_issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update_issue.yml b/.github/workflows/update_issue.yml index 20d1ac678..569c808c8 100644 --- a/.github/workflows/update_issue.yml +++ b/.github/workflows/update_issue.yml @@ -47,7 +47,7 @@ jobs: exit 0 fi - JSON_NAMES=$(printf '%s\n' "${SCRIPT_NAMES[@]}" | jq -R . | jq -s .) + JSON_NAMES=$(printf '%s\n' "${SCRIPT_NAMES[@]}" | jq -R . | jq -s -c .) echo "script_names=$JSON_NAMES" >> $GITHUB_OUTPUT echo "Found script names: ${SCRIPT_NAMES[*]}" From b326c65fb42d024c7e50c1149b1f7219ba08a206 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 9 Jan 2026 14:50:26 +0100 Subject: [PATCH 264/378] test Workflow --- ct/test.sh | 0 install/test-install.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 ct/test.sh create mode 100644 install/test-install.sh diff --git a/ct/test.sh b/ct/test.sh new file mode 100644 index 000000000..e69de29bb diff --git a/install/test-install.sh b/install/test-install.sh new file mode 100644 index 000000000..e69de29bb From 36f749cba2971605454439f08d3f4d4747e75a88 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 9 Jan 2026 14:52:51 +0100 Subject: [PATCH 265/378] Update Workflow --- .github/workflows/update_issue.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update_issue.yml b/.github/workflows/update_issue.yml index 569c808c8..6f0cc64bb 100644 --- a/.github/workflows/update_issue.yml +++ b/.github/workflows/update_issue.yml @@ -136,14 +136,14 @@ jobs: '### Script Type\n\n' + scriptType + '\n\n' + '### 📋 Script Details\n\n' + - 'This script has been merged and is ready for testing. ' + message; + 'This script has been merged and is ready for testing. '; const { data: newIssue } = await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: scriptName, body: issueBody, - labels: ['task', labelName] + labels: [labelName] }); console.log(`Created new issue #${newIssue.number}: ${newIssue.title}`); From 24081cf2057662d1532011d129aa2b1b02e45640 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 9 Jan 2026 14:55:40 +0100 Subject: [PATCH 266/378] Delete ct/test.sh --- ct/test.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ct/test.sh diff --git a/ct/test.sh b/ct/test.sh deleted file mode 100644 index e69de29bb..000000000 From bdf39953786b6af0d6dd720484da0a5f9724ac68 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 9 Jan 2026 14:57:03 +0100 Subject: [PATCH 267/378] Delete install/test-install.sh --- install/test-install.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 install/test-install.sh diff --git a/install/test-install.sh b/install/test-install.sh deleted file mode 100644 index e69de29bb..000000000 From 041fef02b04a4c8143884f73aec9f4df02964f9e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:16:05 +0100 Subject: [PATCH 268/378] Export PHP_VERSION for setup_php --- install/investbrain-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh index 119972787..58c8fb8e8 100644 --- a/install/investbrain-install.sh +++ b/install/investbrain-install.sh @@ -27,7 +27,8 @@ $STD apt install -y \ libpq-dev msg_ok "Installed Dependencies" -PHP_VERSION="8.4" PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php +export PHP_VERSION="8.4" +PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php setup_composer NODE_VERSION="22" setup_nodejs PG_VERSION="17" setup_postgresql From 0121cb15a804381fbdb58ae642571ea4bf6b97d4 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:26:12 +0100 Subject: [PATCH 269/378] Fix composer install command path --- install/investbrain-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh index 58c8fb8e8..375499948 100644 --- a/install/investbrain-install.sh +++ b/install/investbrain-install.sh @@ -93,7 +93,7 @@ MAIL_FROM_ADDRESS="investbrain@${LOCAL_IP}" VITE_APP_NAME=Investbrain EOF export COMPOSER_ALLOW_SUPERUSER=1 -$STD composer install --no-interaction --no-dev --optimize-autoloader +$STD /usr/local/bin/composer install --no-interaction --no-dev --optimize-autoloader $STD npm install $STD npm run build mkdir -p /opt/investbrain/storage/{framework/cache,framework/sessions,framework/views,app,logs} From 46d36537c9c421014eba3e5d43b78e22c8d17891 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:27:35 +0100 Subject: [PATCH 270/378] fix --- ct/investbrain.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/investbrain.sh b/ct/investbrain.sh index 4539f8a94..ea5f3f48c 100644 --- a/ct/investbrain.sh +++ b/ct/investbrain.sh @@ -33,7 +33,7 @@ function update_script() { PHP_VERSION="8.4" msg_info "Stopping Services" systemctl stop nginx php${PHP_VERSION}-fpm - supervisorctl stop all + $STD supervisorctl stop all msg_ok "Services Stopped" PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php @@ -56,7 +56,7 @@ function update_script() { cp /opt/.env.backup /opt/investbrain/.env cp -r /opt/investbrain_backup/ /opt/investbrain/storage export COMPOSER_ALLOW_SUPERUSER=1 - $STD composer install --no-interaction --no-dev --optimize-autoloader + $STD /usr/local/bin/composer install --no-interaction --no-dev --optimize-autoloader $STD npm install $STD npm run build $STD php artisan storage:link @@ -74,7 +74,7 @@ function update_script() { msg_info "Starting Services" systemctl start php${PHP_VERSION}-fpm nginx - supervisorctl start all + $STD supervisorctl start all msg_ok "Services Started" msg_ok "Updated Successfully!" fi From 36682d2728fe4fe6720da471cdbc747bc49c30dd Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 20:36:27 +0100 Subject: [PATCH 271/378] Update package installation to use noninteractive mode --- misc/tools.func | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 1a58ab825..69155a0f0 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -184,7 +184,10 @@ install_packages_with_retry() { local retry=0 while [[ $retry -le $max_retries ]]; do - if $STD apt install -y "${packages[@]}" 2>/dev/null; then + if DEBIAN_FRONTEND=noninteractive $STD apt install -y \ + -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" \ + "${packages[@]}" 2>/dev/null; then return 0 fi @@ -211,8 +214,10 @@ upgrade_packages_with_retry() { local retry=0 while [[ $retry -le $max_retries ]]; do - if $STD apt install --only-upgrade -y "${packages[@]}" 2>/dev/null; then - return 0 + if DEBIAN_FRONTEND=noninteractive $STD apt install --only-upgrade -y \ + -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" \ + "${packages[@]}" 2>/dev/null; then fi retry=$((retry + 1)) From 6ef806710f7bf924a1915b462d75e60edbb3a2cb Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Fri, 9 Jan 2026 21:41:55 +0100 Subject: [PATCH 272/378] kutt: fix default domain --- install/kutt-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/kutt-install.sh b/install/kutt-install.sh index b2330fe0f..3482fbb50 100644 --- a/install/kutt-install.sh +++ b/install/kutt-install.sh @@ -47,7 +47,7 @@ msg_info "Configuring Kutt" cd /opt/kutt cp .example.env ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" -sed -i "s|DEFAULT_DOMAIN=.*|DEFAULT_DOMAIN=https://$DEFAULT_HOST|g" ".env" +sed -i "s|DEFAULT_DOMAIN=.*|DEFAULT_DOMAIN=$DEFAULT_HOST|g" ".env" $STD npm install $STD npm run migrate msg_ok "Configured Kutt" From 6df299a13a65e1273c6211f134c901431bd2e976 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 9 Jan 2026 22:38:16 +0100 Subject: [PATCH 273/378] Fix return statement in package installation function --- misc/tools.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/tools.func b/misc/tools.func index 69155a0f0..f38e3e268 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -218,6 +218,7 @@ upgrade_packages_with_retry() { -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ "${packages[@]}" 2>/dev/null; then + return 0 fi retry=$((retry + 1)) From c93788cf8942d8dabf59e802e3cd70b7a908f91d Mon Sep 17 00:00:00 2001 From: MickLesk Date: Fri, 9 Jan 2026 22:42:21 +0100 Subject: [PATCH 274/378] fix ente --- ct/ente.sh | 28 ++++----- install/ente-install.sh | 134 +++++++++++++--------------------------- 2 files changed, 56 insertions(+), 106 deletions(-) diff --git a/ct/ente.sh b/ct/ente.sh index 7e71a7303..b2043bf9f 100644 --- a/ct/ente.sh +++ b/ct/ente.sh @@ -8,10 +8,10 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV APP="Ente" var_tags="${var_tags:-photos}" var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-10}" +var_ram="${var_ram:-6144}" +var_disk="${var_disk:-20}" 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 Ente LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated Ente LXC" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_info "Updating Ente LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated Ente LXC" + exit } start diff --git a/install/ente-install.sh b/install/ente-install.sh index fa77381d8..7b78adabe 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -14,19 +14,22 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ libsodium23 \ libsodium-dev \ pkg-config \ caddy \ - gcc \ - curl \ - jq + gcc msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql +PG_DB_NAME="ente_db" PG_DB_USER="ente" setup_postgresql_db setup_go NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs +RUST_CRATES="wasm-pack" setup_rust +$STD rustup target add wasm32-unknown-unknown +import_local_ip + 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-server" "ente-io/ente" "tarball" "latest" "/opt/ente" fetch_and_deploy_gh_release "ente-cli" "ente-io/ente" "prebuild" "$ENTE_CLI_VERSION" "/usr/local/bin" "ente-$ENTE_CLI_VERSION-linux-amd64.tar.gz" @@ -45,28 +48,15 @@ endpoint: EOF msg_ok "Configured Ente CLI" -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';" +msg_info "Saving Ente Credentials" { - 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 "- Frontend is built with IP: $LOCAL_IP" 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 "- Museum API: http://$LOCAL_IP:8080" + echo "- Photos UI: http://$LOCAL_IP:3000" + echo "- Accounts UI: http://$LOCAL_IP:3001" + echo "- Auth UI: http://$LOCAL_IP:3003" echo "" echo "Post-Installation Steps Required:" echo "1. Create your first user account via the web UI" @@ -78,7 +68,7 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" echo "" echo "Note: Email verification requires manual intervention since SMTP is not configured" } >>~/ente.creds -msg_ok "Set up PostgreSQL" +msg_ok "Saved Ente Credentials" msg_info "Building Museum (server)" cd /opt/ente/server @@ -105,14 +95,13 @@ SECRET_JWT=$(go run tools/gen-random-keys/main.go 2>/dev/null | grep "jwt" | awk 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 port: 5432 - name: $DB_NAME - user: $DB_USER - password: $DB_PASS + name: $PG_DB_NAME + user: $PG_DB_USER + password: $PG_DB_PASS s3: are_local_buckets: true @@ -125,9 +114,9 @@ s3: bucket: ente-dev apps: - public-albums: http://${CONTAINER_IP}:3002 - cast: http://${CONTAINER_IP}:3004 - accounts: http://${CONTAINER_IP}:3001 + public-albums: http://${LOCAL_IP}:3002 + cast: http://${LOCAL_IP}:3004 + accounts: http://${LOCAL_IP}:3001 key: encryption: $SECRET_ENC @@ -149,26 +138,24 @@ msg_ok "Created museum.yaml" read -r -p "Enter the public URL for Ente backend (e.g., https://api.ente.yourdomain.com or http://192.168.1.100:8080) leave empty to use container IP: " backend_url if [[ -z "$backend_url" ]]; then - LOCAL_IP=$(hostname -I | awk '{print $1}') - ENTE_BACKEND_URL="http://$LOCAL_IP:8080" - msg_info "No URL provided" - msg_ok "using local IP: $ENTE_BACKEND_URL\n" + ENTE_BACKEND_URL="http://$LOCAL_IP:8080" + msg_info "No URL provided" + msg_ok "using local IP: $ENTE_BACKEND_URL\n" else - ENTE_BACKEND_URL="$backend_url" - msg_info "URL provided" - msg_ok "Using provided URL: $ENTE_BACKEND_URL\n" + ENTE_BACKEND_URL="$backend_url" + msg_info "URL provided" + msg_ok "Using provided URL: $ENTE_BACKEND_URL\n" fi read -r -p "Enter the public URL for Ente albums (e.g., https://albums.ente.yourdomain.com or http://192.168.1.100:3002) leave empty to use container IP: " albums_url if [[ -z "$albums_url" ]]; then - LOCAL_IP=$(hostname -I | awk '{print $1}') - ENTE_ALBUMS_URL="http://$LOCAL_IP:3002" - msg_info "No URL provided" - msg_ok "using local IP: $ENTE_ALBUMS_URL\n" + ENTE_ALBUMS_URL="http://$LOCAL_IP:3002" + msg_info "No URL provided" + msg_ok "using local IP: $ENTE_ALBUMS_URL\n" else - ENTE_ALBUMS_URL="$albums_url" - msg_info "URL provided" - msg_ok "Using provided URL: $ENTE_ALBUMS_URL\n" + ENTE_ALBUMS_URL="$albums_url" + msg_info "URL provided" + msg_ok "Using provided URL: $ENTE_ALBUMS_URL\n" fi export NEXT_PUBLIC_ENTE_ENDPOINT=$ENTE_BACKEND_URL @@ -177,6 +164,7 @@ export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=$ENTE_ALBUMS_URL msg_info "Building Web Applications" cd /opt/ente/web export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +source "$HOME/.cargo/env" $STD yarn install $STD yarn build $STD yarn build:accounts @@ -188,7 +176,6 @@ 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 @@ -219,6 +206,8 @@ export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=\$ENTE_ALBUMS_URL echo "Building Web Applications\n" +# Ensure Rust/wasm-pack is available for WASM build +source "\$HOME/.cargo/env" cd /opt/ente/web yarn build yarn build:accounts @@ -253,7 +242,6 @@ systemctl enable -q --now ente-museum msg_ok "Created Museum Service" msg_info "Configuring Caddy" -CONTAINER_IP=$(hostname -I | awk '{print $1}') cat </etc/caddy/Caddyfile # Ente Photos - Main Application :3000 { @@ -334,18 +322,15 @@ EOF systemctl reload caddy msg_ok "Configured Caddy" -motd_ssh -customize - msg_info "Creating helper scripts" -cat <<'HELPER_EOF' >/usr/local/bin/ente-get-verification +cat <<'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 +EOF chmod +x /usr/local/bin/ente-get-verification -cat <<'HELPER_EOF' >/usr/local/bin/ente-upgrade-subscription +cat <<'EOF' >/usr/local/bin/ente-upgrade-subscription #!/usr/bin/env bash if [ -z "$1" ]; then echo "Usage: ente-upgrade-subscription " @@ -355,46 +340,11 @@ fi EMAIL="$1" echo "Upgrading subscription for: $EMAIL" ente admin update-subscription -a "$EMAIL" -u "$EMAIL" --no-limit -HELPER_EOF +EOF chmod +x /usr/local/bin/ente-upgrade-subscription msg_ok "Created helper scripts" -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -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 " Albums: ${ENTE_ALBUMS_URL}" -echo -e " Auth: http://${CONTAINER_IP}:3003" -echo -e " API: ${ENTE_BACKEND_URL}" -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" +motd_ssh +customize +cleanup_lxc From 9c05bf6a3134c108733073926c8d8f32f216a672 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Fri, 9 Jan 2026 22:47:04 +0100 Subject: [PATCH 275/378] finish messsage --- ct/ente.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ct/ente.sh b/ct/ente.sh index b2043bf9f..c234048b7 100644 --- a/ct/ente.sh +++ b/ct/ente.sh @@ -38,5 +38,7 @@ 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}:3000${CL}" From cfbfe827f9115d6f83acb78a19a6264297fa9033 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Fri, 9 Jan 2026 22:59:31 +0100 Subject: [PATCH 276/378] fix awk eof --- install/ente-install.sh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index 7b78adabe..cd252832a 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -176,38 +176,38 @@ 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 -cat </opt/ente/rebuild-frontend.sh +cat <<'EOF' >/opt/ente/rebuild-frontend.sh #!/usr/bin/env bash # Rebuild Ente frontend # Prompt for backend URL read -r -p "Enter the public URL for Ente backend (e.g., https://api.ente.yourdomain.com or http://192.168.1.100:8080) leave empty to use container IP: " backend_url -if [[ -z "\$backend_url" ]]; then +if [[ -z "$backend_url" ]]; then LOCAL_IP=$(hostname -I | awk '{print $1}') - ENTE_BACKEND_URL="http://\$LOCAL_IP:8080" - echo "No URL provided, using local IP: \$ENTE_BACKEND_URL\n" + ENTE_BACKEND_URL="http://$LOCAL_IP:8080" + echo "No URL provided, using local IP: $ENTE_BACKEND_URL" else - ENTE_BACKEND_URL="\$backend_url" - echo "Using provided URL: \$ENTE_BACKEND_URL\n" + ENTE_BACKEND_URL="$backend_url" + echo "Using provided URL: $ENTE_BACKEND_URL" fi # Prompt for albums URL read -r -p "Enter the public URL for Ente albums (e.g., https://albums.ente.yourdomain.com or http://192.168.1.100:3002) leave empty to use container IP: " albums_url -if [[ -z "\$albums_url" ]]; then - LOCAL_IP=\$(hostname -I | awk '{print $1}') - ENTE_ALBUMS_URL="http://\$LOCAL_IP:3002" - echo "No URL provided, using local IP: \$ENTE_ALBUMS_URL\n" +if [[ -z "$albums_url" ]]; then + LOCAL_IP=$(hostname -I | awk '{print $1}') + ENTE_ALBUMS_URL="http://$LOCAL_IP:3002" + echo "No URL provided, using local IP: $ENTE_ALBUMS_URL" else - ENTE_ALBUMS_URL="\$albums_url" - echo "Using provided URL: \$ENTE_ALBUMS_URL\n" + ENTE_ALBUMS_URL="$albums_url" + echo "Using provided URL: $ENTE_ALBUMS_URL" fi -export NEXT_PUBLIC_ENTE_ENDPOINT=\$ENTE_BACKEND_URL -export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=\$ENTE_ALBUMS_URL +export NEXT_PUBLIC_ENTE_ENDPOINT=$ENTE_BACKEND_URL +export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=$ENTE_ALBUMS_URL -echo "Building Web Applications\n" +echo "Building Web Applications..." # Ensure Rust/wasm-pack is available for WASM build -source "\$HOME/.cargo/env" +source "$HOME/.cargo/env" cd /opt/ente/web yarn build yarn build:accounts @@ -220,7 +220,7 @@ 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 +EOF chmod +x /opt/ente/rebuild-frontend.sh msg_ok "Built Web Applications" From fa68ef5b8673201cbe49d05257683e75f423c58b Mon Sep 17 00:00:00 2001 From: Jordan Tomkinson Date: Fri, 9 Jan 2026 22:11:14 +0000 Subject: [PATCH 277/378] tracearr: write APP_VERSION to .env on update --- ct/tracearr.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index cf77b25ec..050477041 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -63,6 +63,7 @@ function update_script() { msg_ok "Built Tracearr" msg_info "Configuring Tracearr" + sed -i "s/^APP_VERSION=.*/APP_VERSION=$(cat /root/.tracearr)/" /data/tracearr/.env chmod 600 /data/tracearr/.env chown -R tracearr:tracearr /data/tracearr msg_ok "Configured Tracearr" From 0b01f9387d2eb19df9a847f46795ee5ebbdd0dec Mon Sep 17 00:00:00 2001 From: MickLesk Date: Fri, 9 Jan 2026 23:32:33 +0100 Subject: [PATCH 278/378] test without cleanup --- install/ente-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index cd252832a..a6028e3cb 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -347,4 +347,4 @@ msg_ok "Created helper scripts" motd_ssh customize -cleanup_lxc +#cleanup_lxc From 2d40a0a1125520a243b2917f35625dbdf52a33c8 Mon Sep 17 00:00:00 2001 From: Marc Went Date: Sat, 10 Jan 2026 11:15:36 +0100 Subject: [PATCH 279/378] fix update cp restore --- ct/wishlist.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 2285ef6f3..5a48d1121 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.sh @@ -36,8 +36,8 @@ function update_script() { mkdir -p /opt/wishlist-backup cp /opt/wishlist/.env /opt/wishlist-backup/.env - cp -R /opt/wishlist/uploads /opt/wishlist-backup/uploads - cp -R /opt/wishlist/data /opt/wishlist-backup/data + cp -a /opt/wishlist/uploads /opt/wishlist-backup + cp -a /opt/wishlist/data /opt/wishlist-backup CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist" false) @@ -55,8 +55,8 @@ function update_script() { $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh cp /opt/wishlist-backup/.env /opt/wishlist/.env - cp -R /opt/wishlist-backup/uploads /opt/wishlist/uploads - cp -R /opt/wishlist-backup/data /opt/wishlist/data + cp -a /opt/wishlist-backup/uploads /opt/wishlist + cp -a /opt/wishlist-backup/data /opt/wishlist msg_ok "Updated Wishlist" msg_info "Starting Service" systemctl start wishlist From 23e1910b55ac51c244c38be5907c512c585d41ea Mon Sep 17 00:00:00 2001 From: Marc Went Date: Sat, 10 Jan 2026 11:25:43 +0100 Subject: [PATCH 280/378] clean up remove backup folder --- ct/wishlist.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 5a48d1121..02154d795 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.sh @@ -57,6 +57,7 @@ function update_script() { cp /opt/wishlist-backup/.env /opt/wishlist/.env cp -a /opt/wishlist-backup/uploads /opt/wishlist cp -a /opt/wishlist-backup/data /opt/wishlist + rm -rf /opt/wishlist-backup msg_ok "Updated Wishlist" msg_info "Starting Service" systemctl start wishlist From 77d67f983c3fc9c63bdb49e544379cb9e270c690 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:02:57 -0800 Subject: [PATCH 281/378] Update frontend/public/json/netbird.json Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- frontend/public/json/netbird.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/netbird.json b/frontend/public/json/netbird.json index fe67583b3..b30180fb8 100644 --- a/frontend/public/json/netbird.json +++ b/frontend/public/json/netbird.json @@ -35,7 +35,7 @@ "type": "info" }, { - "text": "After installation, enter the container and run `netbird` to to view the commands.", + "text": "After installation, enter the container and run `netbird` to view the commands.", "type": "info" }, { From 296ca948cf1d8a8a4572b82a47650901351aed83 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:03:06 -0800 Subject: [PATCH 282/378] Update install/netbird-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/netbird-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index d86022444..555dac6fd 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -23,7 +23,7 @@ setup_deb882_repo \ msg_ok "Set up NetBird Repository" msg_info "Installing NetBird" -$STD apt-get install -y netbird +$STD apt install -y netbird msg_ok "Installed NetBird" msg_info "Enabling NetBird Service" From 09556aa6ac22ce2ce60c82d50d322934be774866 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:03:24 -0800 Subject: [PATCH 283/378] Update install/netbird-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/netbird-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 555dac6fd..8fc0beb1f 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -27,7 +27,7 @@ $STD apt install -y netbird msg_ok "Installed NetBird" msg_info "Enabling NetBird Service" -$STD systemctl enable --now netbird +$STD systemctl enable -q --now netbird msg_ok "Enabled NetBird Service" # NetBird Deployment Type Selection From 04bb146fc471e6f4a39567f28e787ce47d5a90ed Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:03:47 -0800 Subject: [PATCH 284/378] Update install/netbird-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/netbird-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 8fc0beb1f..0e904a3dd 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -30,7 +30,7 @@ msg_info "Enabling NetBird Service" $STD systemctl enable -q --now netbird msg_ok "Enabled NetBird Service" -# NetBird Deployment Type Selection +echo "" echo "" echo -e "${BL}NetBird Deployment Type${CL}" echo "─────────────────────────────────────────" From 6f27868a49a5001f5217b7ddd159be9e8d150356 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:03:57 -0800 Subject: [PATCH 285/378] Update install/netbird-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/netbird-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 0e904a3dd..1a4bcbba5 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -69,7 +69,7 @@ case "$DEPLOYMENT_TYPE" in ;; esac -# NetBird Connection Setup +echo "" echo "" echo -e "${BL}NetBird Connection Setup${CL}" echo "─────────────────────────────────────────" From 2d86a4199ebc279bf0ea88653175fa23db83b778 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:13:47 -0800 Subject: [PATCH 286/378] Update frontend/public/json/netbird.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/netbird.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/netbird.json b/frontend/public/json/netbird.json index b30180fb8..2fc4b72eb 100644 --- a/frontend/public/json/netbird.json +++ b/frontend/public/json/netbird.json @@ -9,7 +9,7 @@ "interface_port": null, "documentation": "https://docs.netbird.io/", "website": "https://netbird.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@master/webp/netbird.webp", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/netbird.webp", "config_path": "/etc/netbird/config.json", "description": "NetBird is an open source VPN management platform that creates secure peer-to-peer networks using WireGuard. It enables secure connectivity between devices anywhere in the world without complex firewall configurations or port forwarding. NetBird offers features like zero-configuration networking, SSO integration, access control policies, and a centralized management dashboard. It's designed to be simple to deploy and manage, making it ideal for connecting remote teams, securing IoT devices, or building secure infrastructure networks.", "install_methods": [ From 57646838f2b841bb81ec75d1b1725c5f3decc9c6 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:14:00 -0800 Subject: [PATCH 287/378] Update install/netbird-install.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> --- install/netbird-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 1a4bcbba5..72edecbc1 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG +# Copyright (c) 2021-2026 community-scripts ORG # Author: TechHutTV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netbird.io/ From 1504a65bfb0eedbe3ae0fbf1a51ffdf43d8d1823 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:14:10 -0800 Subject: [PATCH 288/378] Update ct/netbird.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/netbird.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/netbird.sh b/ct/netbird.sh index 4b5714756..e7fde16af 100644 --- a/ct/netbird.sh +++ b/ct/netbird.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 +# Copyright (c) 2021-2026 community-scripts ORG # Author: TechHutTV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netbird.io/ From 452bb94233f5a8e246246d36448328ca223402d8 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins <76761586+TechHutTV@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:26:20 -0800 Subject: [PATCH 289/378] Update ct/netbird.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/netbird.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/netbird.sh b/ct/netbird.sh index e7fde16af..aaa1cdb26 100644 --- a/ct/netbird.sh +++ b/ct/netbird.sh @@ -33,7 +33,7 @@ fi msg_info "Updating ${APP}" $STD apt update -$STD apt -y upgrade +$STD apt upgrade -y msg_ok "Updated Successfully" exit } From bd4f1f8f7a6c13a9942ec1e1c4d587b62609022c 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, 11 Jan 2026 01:59:06 +0100 Subject: [PATCH 290/378] Apply suggestion from @tremor021 --- install/netbird-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 72edecbc1..bd0078521 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -13,7 +13,6 @@ setting_up_container network_check update_os - msg_info "Setting up NetBird Repository" setup_deb882_repo \ "netbird" \ From 07261d16f977197aee65beb3192db5ddf1ef1b8e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 11 Jan 2026 12:56:38 +0100 Subject: [PATCH 291/378] refactor --- ct/netbird.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ct/netbird.sh b/ct/netbird.sh index aaa1cdb26..8495258d1 100644 --- a/ct/netbird.sh +++ b/ct/netbird.sh @@ -22,27 +22,27 @@ 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/netbird/config.json ]]; then -msg_error "No ${APP} Installation Found!" -exit -fi + if [[ ! -f /etc/netbird/config.json ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi -msg_info "Updating ${APP}" -$STD apt update -$STD apt upgrade -y -msg_ok "Updated Successfully" -exit + msg_info "Updating Netbird" + $STD apt update + $STD apt upgrade -y + msg_ok "Updated successfully!" + exit } start build_container description -msg_ok "Completed Successfully!\n" +msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access NetBird by entering the container and running:${CL}" echo -e "${TAB}${GATEWAY}${BGN}netbird up${CL}" From bbfb5bdebe8820f8a4058fdccc153d6a376b4272 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 11 Jan 2026 21:46:52 +0100 Subject: [PATCH 292/378] Update GitHub stars requirement in PR template --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3a9225cb6..1425c8037 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -46,5 +46,5 @@ Link: # > PRs that do not meet these requirements may be closed without review. - [ ] The application is **at least 6 months old** - [ ] The application is **actively maintained** -- [ ] The application has **200+ GitHub stars** +- [ ] The application has **600+ GitHub stars** - [ ] Official **release tarballs** are published From 7129c0dbd9b3e77536d0c82bcfc8fe3a64570827 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 08:51:11 +0100 Subject: [PATCH 293/378] Tools.func: Get Version from Upstream (VE) --- misc/tools.func | 1028 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 777 insertions(+), 251 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 1a58ab825..672af628a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -184,7 +184,10 @@ install_packages_with_retry() { local retry=0 while [[ $retry -le $max_retries ]]; do - if $STD apt install -y "${packages[@]}" 2>/dev/null; then + if DEBIAN_FRONTEND=noninteractive $STD apt install -y \ + -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" \ + "${packages[@]}" 2>/dev/null; then return 0 fi @@ -211,7 +214,10 @@ upgrade_packages_with_retry() { local retry=0 while [[ $retry -le $max_retries ]]; do - if $STD apt install --only-upgrade -y "${packages[@]}" 2>/dev/null; then + if DEBIAN_FRONTEND=noninteractive $STD apt install --only-upgrade -y \ + -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" \ + "${packages[@]}" 2>/dev/null; then return 0 fi @@ -334,9 +340,9 @@ remove_old_tool_version() { $STD apt purge -y nodejs npm >/dev/null 2>&1 || true # 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 list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' 2>/dev/null | while read -r module; do npm uninstall -g "$module" >/dev/null 2>&1 || true - done + done || true fi cleanup_legacy_install "nodejs" cleanup_tool_keyrings "nodesource" @@ -1167,7 +1173,7 @@ cleanup_orphaned_sources() { # Extract Signed-By path from .sources file local keyring_path - keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}') + keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}' 2>/dev/null || true) # If keyring doesn't exist, remove the .sources file if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then @@ -1759,13 +1765,8 @@ function fetch_and_deploy_gh_release() { if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then # GitHub API's tarball_url/zipball_url can return HTTP 300 Multiple Choices # when a branch and tag share the same name. Use explicit refs/tags/ URL instead. - # URL-encode the tag_name for special characters (e.g., @papra/docker@25.12.0) - local tag_encoded - tag_encoded=$(printf '%s' "$tag_name" | jq -sRr @uri) - local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$tag_encoded.tar.gz" - # Sanitize version for filename (replace / with -) - local version_safe="${version//\//-}" - filename="${app_lc}-${version_safe}.tar.gz" + local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$tag_name.tar.gz" + filename="${app_lc}-${version}.tar.gz" curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" || { msg_error "Download failed: $direct_tarball_url" @@ -2078,7 +2079,7 @@ function setup_adminer() { return 1 } local VERSION - VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') + VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}' 2>/dev/null || echo 'unknown') cache_installed_version "adminer" "${VERSION:-unknown}" msg_ok "Setup Adminer (Debian/Ubuntu)" fi @@ -2568,111 +2569,576 @@ function setup_gs() { # Sets up Hardware Acceleration on debian or ubuntu. # # Description: -# - Determites CPU/GPU/APU Vendor -# - Installs the correct libraries and packages -# - Sets up Hardware Acceleration +# - Detects all available GPUs (Intel, AMD, NVIDIA) +# - Allows user to select which GPU(s) to configure (with 60s timeout) +# - Installs the correct libraries and packages for each GPU type +# - Supports: Debian 11/12/13, Ubuntu 22.04/24.04 +# - Intel: Legacy (Gen 6-8), Modern (Gen 9+), Arc +# - AMD: Discrete GPUs, APUs, ROCm compute +# - NVIDIA: Version-matched drivers from CUDA repository # # Notes: -# - Some things are fetched from intel repositories due to not being in debian repositories. +# - Some Intel packages are fetched from GitHub due to missing Debian packages +# - NVIDIA requires matching host driver version # ------------------------------------------------------------------------------ function setup_hwaccel() { - 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 - - # Detect GPU vendor (Intel, AMD, NVIDIA) - local gpu_vendor gpu_info - gpu_info=$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' || echo "") - gpu_vendor=$(echo "$gpu_info" | 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 "") - - if [[ -z "$gpu_vendor" && -z "$cpu_vendor" ]]; then - msg_warn "No GPU or CPU vendor detected - skipping hardware acceleration setup" - msg_ok "Setup Hardware Acceleration (skipped - no GPU detected)" + # Check if user explicitly disabled GPU in advanced settings + # ENABLE_GPU is exported from build.func + if [[ "${ENABLE_GPU:-no}" == "no" ]]; then return 0 fi - # Detect OS with fallbacks + # Check if GPU passthrough is enabled (device nodes must exist) + if [[ ! -d /dev/dri && ! -e /dev/nvidia0 && ! -e /dev/kfd ]]; then + msg_warn "No GPU passthrough detected (/dev/dri, /dev/nvidia*, /dev/kfd not found) - skipping hardware acceleration setup" + return 0 + fi + + msg_info "Setup Hardware Acceleration" + + # Install pciutils if needed + if ! command -v lspci &>/dev/null; then + $STD apt -y update || { + msg_warn "Failed to update package list" + return 0 + } + $STD apt -y install pciutils || { + msg_warn "Failed to install pciutils" + return 0 + } + fi + + # ═══════════════════════════════════════════════════════════════════════════ + # GPU Detection - Build list of all available GPUs with details + # ═══════════════════════════════════════════════════════════════════════════ + local -a GPU_LIST=() + local -a GPU_TYPES=() + local -a GPU_NAMES=() + local gpu_count=0 + + # Get all GPU entries from lspci + while IFS= read -r line; do + [[ -z "$line" ]] && continue + local pci_addr gpu_name gpu_type="" + + pci_addr=$(echo "$line" | awk '{print $1}') + gpu_name=$(echo "$line" | sed 's/^[^ ]* [^:]*: //') + + # Determine GPU type + # Note: Use -w (word boundary) for ATI to avoid matching "CorporATIon" + if echo "$gpu_name" | grep -qi 'Intel'; then + gpu_type="INTEL" + # Subtype detection for Intel + # Order matters: Check Arc first, then Gen9+ (UHD/Iris/HD 5xx-6xx), then Legacy (HD 2xxx-5xxx) + # HD Graphics 530/630 = Gen 9 (Skylake/Kaby Lake) - 3 digits + # HD Graphics 4600/5500 = Gen 7-8 (Haswell/Broadwell) - 4 digits starting with 2-5 + if echo "$gpu_name" | grep -qiE 'Arc|DG[12]'; then + gpu_type="INTEL_ARC" + elif echo "$gpu_name" | grep -qiE 'UHD|Iris|HD Graphics [5-6][0-9]{2}[^0-9]|HD Graphics [5-6][0-9]{2}$'; then + # HD Graphics 5xx/6xx (3 digits) = Gen 9+ (Skylake onwards) + gpu_type="INTEL_GEN9+" + elif echo "$gpu_name" | grep -qiE 'HD Graphics [2-5][0-9]{3}'; then + # HD Graphics 2xxx-5xxx (4 digits) = Gen 6-8 Legacy + gpu_type="INTEL_LEGACY" + fi + elif echo "$gpu_name" | grep -qiwE 'AMD|ATI|Radeon|Advanced Micro Devices'; then + gpu_type="AMD" + elif echo "$gpu_name" | grep -qi 'NVIDIA'; then + gpu_type="NVIDIA" + fi + + if [[ -n "$gpu_type" ]]; then + GPU_LIST+=("$pci_addr") + GPU_TYPES+=("$gpu_type") + GPU_NAMES+=("$gpu_name") + ((gpu_count++)) || true + fi + done < <(lspci 2>/dev/null | grep -Ei 'vga|3d|display') + + # Check for AMD APU via CPU vendor if no discrete GPU found + local cpu_vendor + cpu_vendor=$(lscpu 2>/dev/null | grep -i 'Vendor ID' | awk '{print $3}' 2>/dev/null || echo "") + + if [[ $gpu_count -eq 0 ]]; then + if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + GPU_LIST+=("integrated") + GPU_TYPES+=("AMD_APU") + GPU_NAMES+=("AMD APU (Integrated Graphics)") + ((gpu_count++)) || true + else + msg_warn "No GPU detected - skipping hardware acceleration setup" + return 0 + fi + fi + + # ═══════════════════════════════════════════════════════════════════════════ + # GPU Selection - Let user choose which GPU(s) to configure + # ═══════════════════════════════════════════════════════════════════════════ + local -a SELECTED_INDICES=() + + if [[ $gpu_count -eq 1 ]]; then + # Single GPU - auto-select + SELECTED_INDICES=(0) + msg_ok "Detected GPU: ${GPU_NAMES[0]} (${GPU_TYPES[0]})" + else + # Multiple GPUs - show selection menu + echo "" + msg_info "Multiple GPUs detected:" + echo "" + for i in "${!GPU_LIST[@]}"; do + local type_display="${GPU_TYPES[$i]}" + case "${GPU_TYPES[$i]}" in + INTEL_ARC) type_display="Intel Arc" ;; + INTEL_GEN9+) type_display="Intel Gen9+" ;; + INTEL_LEGACY) type_display="Intel Legacy" ;; + INTEL) type_display="Intel" ;; + AMD) type_display="AMD" ;; + AMD_APU) type_display="AMD APU" ;; + NVIDIA) type_display="NVIDIA" ;; + esac + printf " %d) [%s] %s\n" "$((i + 1))" "$type_display" "${GPU_NAMES[$i]}" + done + printf " A) Configure ALL GPUs\n" + echo "" + + # Read with 60 second timeout + local selection="" + echo -n "Select GPU(s) to configure (1-${gpu_count}, A=all) [timeout 60s, default=all]: " + if read -r -t 60 selection; then + selection="${selection^^}" # uppercase + else + echo "" + msg_info "Timeout - configuring all GPUs automatically" + selection="A" + fi + + # Parse selection + if [[ "$selection" == "A" || -z "$selection" ]]; then + # Select all + for i in "${!GPU_LIST[@]}"; do + SELECTED_INDICES+=("$i") + done + elif [[ "$selection" =~ ^[0-9,]+$ ]]; then + # Parse comma-separated numbers + IFS=',' read -ra nums <<<"$selection" + for num in "${nums[@]}"; do + num=$(echo "$num" | tr -d ' ') + if [[ "$num" =~ ^[0-9]+$ ]] && ((num >= 1 && num <= gpu_count)); then + SELECTED_INDICES+=("$((num - 1))") + fi + done + else + # Invalid - default to all + msg_warn "Invalid selection - configuring all GPUs" + for i in "${!GPU_LIST[@]}"; do + SELECTED_INDICES+=("$i") + done + fi + fi + + # ═══════════════════════════════════════════════════════════════════════════ + # OS Detection + # ═══════════════════════════════════════════════════════════════════════════ local os_id os_codename os_version os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "debian") os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "unknown") os_version=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "") - [[ -z "$os_id" ]] && os_id="debian" - # Determine if we are in a privileged LXC container local in_ct="${CTTYPE:-0}" - case "$gpu_vendor" in - Intel) - # Detect Intel GPU generation for driver selection - # Gen 9+ (Skylake and newer) benefit from non-free drivers - local intel_gen="" - local needs_nonfree=false + # ═══════════════════════════════════════════════════════════════════════════ + # Process Selected GPUs + # ═══════════════════════════════════════════════════════════════════════════ + for idx in "${SELECTED_INDICES[@]}"; do + local gpu_type="${GPU_TYPES[$idx]}" + local gpu_name="${GPU_NAMES[$idx]}" - # Check for specific Intel GPU models that need non-free drivers - if echo "$gpu_info" | grep -Ei 'HD Graphics [56][0-9]{2}|UHD Graphics|Iris|Arc|DG[12]' &>/dev/null; then - needs_nonfree=true - intel_gen="gen9+" + msg_info "Configuring: ${gpu_name}" + + case "$gpu_type" in + # ───────────────────────────────────────────────────────────────────────── + # Intel Arc GPUs (DG1, DG2, Arc A-series) + # ───────────────────────────────────────────────────────────────────────── + INTEL_ARC) + _setup_intel_arc "$os_id" "$os_codename" + ;; + + # ───────────────────────────────────────────────────────────────────────── + # Intel Gen 9+ (Skylake 2015+: UHD, Iris, HD 6xx+) + # ───────────────────────────────────────────────────────────────────────── + INTEL_GEN9+ | INTEL) + _setup_intel_modern "$os_id" "$os_codename" + ;; + + # ───────────────────────────────────────────────────────────────────────── + # Intel Legacy (Gen 6-8: HD 2000-5999, Sandy Bridge to Broadwell) + # ───────────────────────────────────────────────────────────────────────── + INTEL_LEGACY) + _setup_intel_legacy "$os_id" "$os_codename" + ;; + + # ───────────────────────────────────────────────────────────────────────── + # AMD Discrete GPUs + # ───────────────────────────────────────────────────────────────────────── + AMD) + _setup_amd_gpu "$os_id" "$os_codename" + ;; + + # ───────────────────────────────────────────────────────────────────────── + # AMD APU (Integrated Graphics) + # ───────────────────────────────────────────────────────────────────────── + AMD_APU) + _setup_amd_apu "$os_id" "$os_codename" + ;; + + # ───────────────────────────────────────────────────────────────────────── + # NVIDIA GPUs + # ───────────────────────────────────────────────────────────────────────── + NVIDIA) + _setup_nvidia_gpu "$os_id" "$os_codename" "$os_version" + ;; + esac + done + + # ═══════════════════════════════════════════════════════════════════════════ + # Device Permissions + # ═══════════════════════════════════════════════════════════════════════════ + _setup_gpu_permissions "$in_ct" + + cache_installed_version "hwaccel" "1.0" + msg_ok "Setup Hardware Acceleration" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Intel Arc GPU Setup +# ══════════════════════════════════════════════════════════════════════════════ +_setup_intel_arc() { + local os_id="$1" os_codename="$2" + + msg_info "Installing Intel Arc GPU drivers" + + if [[ "$os_id" == "ubuntu" ]]; then + # Ubuntu 22.04+ has Arc support in HWE kernel + $STD apt -y install \ + intel-media-va-driver-non-free \ + intel-opencl-icd \ + vainfo \ + intel-gpu-tools 2>/dev/null || msg_warn "Some Intel Arc packages failed" + + elif [[ "$os_id" == "debian" ]]; then + # Add non-free repos + _add_debian_nonfree "$os_codename" + + # Arc requires latest drivers - fetch from GitHub + # Order matters: libigdgmm first (dependency), then IGC, then compute-runtime + msg_info "Fetching Intel compute-runtime for Arc support" + + # libigdgmm - bundled in compute-runtime releases (Debian version often too old) + fetch_and_deploy_gh_release "libigdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || true + + # Intel Graphics Compiler (note: packages have -2 suffix) + fetch_and_deploy_gh_release "intel-igc-core" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || true + fetch_and_deploy_gh_release "intel-igc-opencl" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || true + + # Compute Runtime (depends on IGC and gmmlib) + fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || true + fetch_and_deploy_gh_release "intel-level-zero-gpu" "intel/compute-runtime" "binary" "latest" "" "libze-intel-gpu1_*_amd64.deb" || true + + $STD apt -y install \ + intel-media-va-driver-non-free \ + ocl-icd-libopencl1 \ + libvpl2 \ + vainfo \ + intel-gpu-tools 2>/dev/null || msg_warn "Some Intel Arc packages failed" + fi + + msg_ok "Intel Arc GPU configured" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Intel Modern GPU Setup (Gen 9+) +# ══════════════════════════════════════════════════════════════════════════════ +_setup_intel_modern() { + local os_id="$1" os_codename="$2" + + msg_info "Installing Intel Gen 9+ GPU drivers" + + if [[ "$os_id" == "ubuntu" ]]; then + $STD apt -y install \ + va-driver-all \ + intel-media-va-driver \ + ocl-icd-libopencl1 \ + vainfo \ + intel-gpu-tools 2>/dev/null || msg_warn "Some Intel packages failed" + + # Try non-free driver for better codec support + $STD apt -y install intel-media-va-driver-non-free 2>/dev/null || true + $STD apt -y install intel-opencl-icd 2>/dev/null || true + $STD apt -y install libmfx-gen1.2 2>/dev/null || true + + elif [[ "$os_id" == "debian" ]]; then + _add_debian_nonfree "$os_codename" + + # For Trixie/Sid: Fetch from GitHub (Debian packages too old or missing) + if [[ "$os_codename" == "trixie" || "$os_codename" == "sid" ]]; then + msg_info "Fetching Intel compute-runtime from GitHub" + + # libigdgmm first (bundled in compute-runtime releases) + fetch_and_deploy_gh_release "libigdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || true + + # Intel Graphics Compiler (note: packages have -2 suffix) + fetch_and_deploy_gh_release "intel-igc-core" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || true + fetch_and_deploy_gh_release "intel-igc-opencl" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || true + + # Compute Runtime + fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || true fi - if [[ "$os_id" == "ubuntu" ]]; then - # Ubuntu: Use packages from Ubuntu repos - $STD apt -y install \ - va-driver-all \ - ocl-icd-libopencl1 \ - intel-opencl-icd \ - vainfo \ - libmfx-gen1.2 \ - intel-gpu-tools || { - msg_error "Failed to install Intel GPU dependencies" - return 1 - } - # Try to install intel-media-va-driver for newer GPUs - $STD apt -y install intel-media-va-driver 2>/dev/null || true + $STD apt -y install \ + intel-media-va-driver-non-free \ + ocl-icd-libopencl1 \ + vainfo \ + libmfx-gen1.2 \ + intel-gpu-tools 2>/dev/null || msg_warn "Some Intel packages failed" - elif [[ "$os_id" == "debian" ]]; then - # Debian: Check version and install appropriate drivers - if [[ "$needs_nonfree" == true ]]; then - # Add non-free repo for intel-media-va-driver-non-free - if [[ "$os_codename" == "bookworm" ]]; then - # Debian 12 Bookworm - if [[ ! -f /etc/apt/sources.list.d/non-free.list && ! -f /etc/apt/sources.list.d/non-free.sources ]]; then - cat </etc/apt/sources.list.d/non-free.sources + # Bookworm has intel-opencl-icd in repos (compatible version) + [[ "$os_codename" == "bookworm" ]] && $STD apt -y install intel-opencl-icd libigdgmm12 2>/dev/null || true + fi + + msg_ok "Intel Gen 9+ GPU configured" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Intel Legacy GPU Setup (Gen 6-8) +# ══════════════════════════════════════════════════════════════════════════════ +_setup_intel_legacy() { + local os_id="$1" os_codename="$2" + + msg_info "Installing Intel Legacy GPU drivers (Gen 6-8)" + + # Legacy GPUs use i965 driver - stable repo packages only + $STD apt -y install \ + va-driver-all \ + i965-va-driver \ + mesa-va-drivers \ + ocl-icd-libopencl1 \ + vainfo \ + intel-gpu-tools 2>/dev/null || msg_warn "Some Intel legacy packages failed" + + # beignet provides OpenCL for older Intel GPUs (Sandy Bridge to Broadwell) + # Note: beignet-opencl-icd was removed in Debian 12+ and Ubuntu 22.04+ + # Check if package is available before attempting installation + if apt-cache show beignet-opencl-icd &>/dev/null; then + $STD apt -y install beignet-opencl-icd 2>/dev/null || msg_warn "beignet-opencl-icd installation failed (optional)" + else + msg_warn "beignet-opencl-icd not available - OpenCL support for legacy Intel GPU limited" + msg_warn "Note: Hardware video encoding/decoding (VA-API) still works without OpenCL" + fi + + msg_ok "Intel Legacy GPU configured" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# AMD Discrete GPU Setup +# ══════════════════════════════════════════════════════════════════════════════ +_setup_amd_gpu() { + local os_id="$1" os_codename="$2" + + msg_info "Installing AMD GPU drivers" + + # Core Mesa drivers + $STD apt -y install \ + mesa-va-drivers \ + mesa-vdpau-drivers \ + mesa-opencl-icd \ + ocl-icd-libopencl1 \ + libdrm-amdgpu1 \ + vainfo \ + clinfo 2>/dev/null || msg_warn "Some AMD packages failed" + + # Firmware for AMD GPUs + if [[ "$os_id" == "debian" ]]; then + _add_debian_nonfree_firmware "$os_codename" + $STD apt -y install firmware-amd-graphics 2>/dev/null || msg_warn "AMD firmware not available" + fi + # Ubuntu includes AMD firmware in linux-firmware by default + + # ROCm for compute (optional - large download) + # Uncomment if needed: + # $STD apt -y install rocm-opencl-runtime 2>/dev/null || true + + msg_ok "AMD GPU configured" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# AMD APU Setup (Integrated Graphics) +# ══════════════════════════════════════════════════════════════════════════════ +_setup_amd_apu() { + local os_id="$1" os_codename="$2" + + msg_info "Installing AMD APU drivers" + + $STD apt -y install \ + mesa-va-drivers \ + mesa-vdpau-drivers \ + mesa-opencl-icd \ + ocl-icd-libopencl1 \ + vainfo 2>/dev/null || msg_warn "Some AMD APU packages failed" + + if [[ "$os_id" == "debian" ]]; then + _add_debian_nonfree_firmware "$os_codename" + $STD apt -y install firmware-amd-graphics 2>/dev/null || true + fi + + msg_ok "AMD APU configured" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# NVIDIA GPU Setup +# ══════════════════════════════════════════════════════════════════════════════ +_setup_nvidia_gpu() { + local os_id="$1" os_codename="$2" os_version="$3" + + msg_info "Installing NVIDIA GPU drivers" + + # Detect host driver version (passed through via /proc) + local nvidia_host_version="" + if [[ -f /proc/driver/nvidia/version ]]; then + nvidia_host_version=$(grep "NVRM version:" /proc/driver/nvidia/version 2>/dev/null | awk '{print $8}') + fi + + if [[ -z "$nvidia_host_version" ]]; then + msg_warn "NVIDIA host driver version not found in /proc/driver/nvidia/version" + msg_warn "Ensure NVIDIA drivers are installed on host and GPU passthrough is enabled" + $STD apt -y install va-driver-all vainfo 2>/dev/null || true + return 0 + fi + + msg_info "Host NVIDIA driver version: ${nvidia_host_version}" + + if [[ "$os_id" == "debian" ]]; then + # Enable non-free components + if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then + if ! grep -q "non-free" /etc/apt/sources.list.d/debian.sources 2>/dev/null; then + sed -i -E 's/Components: (.*)$/Components: \1 contrib non-free non-free-firmware/g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || true + fi + fi + + # Determine CUDA repository + local cuda_repo="debian12" + case "$os_codename" in + bullseye) cuda_repo="debian11" ;; + bookworm) cuda_repo="debian12" ;; + trixie | sid) cuda_repo="debian12" ;; # Forward compatible + esac + + # Add NVIDIA CUDA repository + if [[ ! -f /usr/share/keyrings/cuda-archive-keyring.gpg ]]; then + msg_info "Adding NVIDIA CUDA repository (${cuda_repo})" + local cuda_keyring + cuda_keyring="$(mktemp)" + if curl -fsSL -o "$cuda_keyring" "https://developer.download.nvidia.com/compute/cuda/repos/${cuda_repo}/x86_64/cuda-keyring_1.1-1_all.deb" 2>/dev/null; then + $STD dpkg -i "$cuda_keyring" 2>/dev/null || true + else + msg_warn "Failed to download NVIDIA CUDA keyring" + fi + rm -f "$cuda_keyring" + fi + + # Pin NVIDIA repo for version matching + cat <<'NVIDIA_PIN' >/etc/apt/preferences.d/nvidia-cuda-pin +Package: * +Pin: origin developer.download.nvidia.com +Pin-Priority: 1001 +NVIDIA_PIN + + $STD apt -y update + + # Install version-matched NVIDIA libraries + local nvidia_pkgs="libcuda1=${nvidia_host_version}* libnvcuvid1=${nvidia_host_version}* libnvidia-encode1=${nvidia_host_version}* libnvidia-ml1=${nvidia_host_version}*" + + msg_info "Installing NVIDIA libraries (version ${nvidia_host_version})" + if $STD apt -y install --no-install-recommends $nvidia_pkgs 2>/dev/null; then + msg_ok "Installed version-matched NVIDIA libraries" + else + msg_warn "Version-pinned install failed - trying unpinned" + if $STD apt -y install --no-install-recommends libcuda1 libnvcuvid1 libnvidia-encode1 libnvidia-ml1 2>/dev/null; then + msg_warn "Installed NVIDIA libraries (unpinned) - version mismatch may occur" + else + msg_warn "NVIDIA library installation failed" + fi + fi + + $STD apt -y install --no-install-recommends nvidia-smi 2>/dev/null || true + + elif [[ "$os_id" == "ubuntu" ]]; then + # Ubuntu versioning + local ubuntu_cuda_repo="" + case "$os_version" in + 22.04) ubuntu_cuda_repo="ubuntu2204" ;; + 24.04) ubuntu_cuda_repo="ubuntu2404" ;; + *) ubuntu_cuda_repo="ubuntu2204" ;; # Fallback + esac + + # Add NVIDIA CUDA repository for Ubuntu + if [[ ! -f /usr/share/keyrings/cuda-archive-keyring.gpg ]]; then + msg_info "Adding NVIDIA CUDA repository (${ubuntu_cuda_repo})" + local cuda_keyring + cuda_keyring="$(mktemp)" + if curl -fsSL -o "$cuda_keyring" "https://developer.download.nvidia.com/compute/cuda/repos/${ubuntu_cuda_repo}/x86_64/cuda-keyring_1.1-1_all.deb" 2>/dev/null; then + $STD dpkg -i "$cuda_keyring" 2>/dev/null || true + else + msg_warn "Failed to download NVIDIA CUDA keyring" + fi + rm -f "$cuda_keyring" + fi + + $STD apt -y update + + # Try version-matched install + local nvidia_pkgs="libcuda1=${nvidia_host_version}* libnvcuvid1=${nvidia_host_version}* libnvidia-encode1=${nvidia_host_version}* libnvidia-ml1=${nvidia_host_version}*" + if $STD apt -y install --no-install-recommends $nvidia_pkgs 2>/dev/null; then + msg_ok "Installed version-matched NVIDIA libraries" + else + # Fallback to Ubuntu repo packages + $STD apt -y install --no-install-recommends libnvidia-decode libnvidia-encode nvidia-utils 2>/dev/null || msg_warn "NVIDIA installation failed" + fi + fi + + # VA-API for hybrid setups (Intel + NVIDIA) + $STD apt -y install va-driver-all vainfo 2>/dev/null || true + + msg_ok "NVIDIA GPU configured" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Helper: Add Debian non-free repositories +# ══════════════════════════════════════════════════════════════════════════════ +_add_debian_nonfree() { + local os_codename="$1" + + [[ -f /etc/apt/sources.list.d/non-free.sources ]] && return 0 + + case "$os_codename" in + bullseye) + cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources +Types: deb +URIs: http://deb.debian.org/debian +Suites: bullseye bullseye-updates +Components: non-free +EOF + ;; + bookworm) + cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources Types: deb URIs: http://deb.debian.org/debian Suites: bookworm bookworm-updates Components: non-free non-free-firmware EOF - $STD apt update - fi - $STD apt -y install \ - intel-media-va-driver-non-free \ - ocl-icd-libopencl1 \ - intel-opencl-icd \ - vainfo \ - libmfx-gen1.2 \ - intel-gpu-tools || { - msg_warn "Non-free driver install failed, falling back to open drivers" - needs_nonfree=false - } - - elif [[ "$os_codename" == "trixie" || "$os_codename" == "sid" ]]; then - # Debian 13 Trixie / Sid - if [[ ! -f /etc/apt/sources.list.d/non-free.sources ]]; then - cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources + ;; + trixie | sid) + cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources Types: deb URIs: http://deb.debian.org/debian Suites: trixie trixie-updates @@ -2683,114 +3149,135 @@ URIs: http://deb.debian.org/debian-security Suites: trixie-security Components: non-free non-free-firmware EOF - $STD apt update - fi - $STD apt -y install \ - intel-media-va-driver-non-free \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - libvpl2 \ - vainfo \ - libmfx-gen1.2 \ - intel-gpu-tools 2>/dev/null || { - msg_warn "Non-free driver install failed, falling back to open drivers" - needs_nonfree=false - } - fi - fi - - # Fallback to open drivers or older Intel GPUs - if [[ "$needs_nonfree" == false ]]; then - # Fetch latest Intel drivers from GitHub for Debian - 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" - } - - $STD apt -y install \ - va-driver-all \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - vainfo \ - intel-gpu-tools || { - msg_error "Failed to install Intel GPU dependencies" - return 1 - } - fi - fi - ;; - - AMD) - $STD apt -y install \ - mesa-va-drivers \ - mesa-vdpau-drivers \ - mesa-opencl-icd \ - ocl-icd-libopencl1 \ - vainfo \ - clinfo 2>/dev/null || { - msg_error "Failed to install AMD GPU dependencies" - return 1 - } - - # AMD firmware for better GPU support - if [[ "$os_id" == "debian" ]]; then - $STD apt -y install firmware-amd-graphics 2>/dev/null || true - fi - $STD apt -y install libdrm-amdgpu1 2>/dev/null || true - ;; - - NVIDIA) - # NVIDIA needs manual driver setup or passthrough from host - msg_warn "NVIDIA GPU detected - driver must be installed manually or passed through from host" - # Install basic VA-API support for potential hybrid setups - $STD apt -y install va-driver-all vainfo 2>/dev/null || true - ;; - - *) - # No discrete GPU detected - check for AMD APU - if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then - $STD apt -y install \ - mesa-va-drivers \ - mesa-vdpau-drivers \ - mesa-opencl-icd \ - ocl-icd-libopencl1 \ - vainfo 2>/dev/null || true - else - msg_warn "No supported GPU vendor detected - skipping GPU driver installation" - fi ;; esac + $STD apt -y update +} - # Set permissions for /dev/dri (only in privileged containers and if /dev/dri exists) +# ══════════════════════════════════════════════════════════════════════════════ +# Helper: Add Debian non-free-firmware repository +# ══════════════════════════════════════════════════════════════════════════════ +_add_debian_nonfree_firmware() { + local os_codename="$1" + + [[ -f /etc/apt/sources.list.d/non-free-firmware.sources ]] && return 0 + + case "$os_codename" in + bullseye) + # Debian 11 uses 'non-free' component (no separate non-free-firmware) + cat <<'EOF' >/etc/apt/sources.list.d/non-free-firmware.sources +Types: deb +URIs: http://deb.debian.org/debian +Suites: bullseye bullseye-updates +Components: non-free + +Types: deb +URIs: http://deb.debian.org/debian-security +Suites: bullseye-security +Components: non-free +EOF + ;; + bookworm) + cat <<'EOF' >/etc/apt/sources.list.d/non-free-firmware.sources +Types: deb +URIs: http://deb.debian.org/debian +Suites: bookworm bookworm-updates +Components: non-free-firmware + +Types: deb +URIs: http://deb.debian.org/debian-security +Suites: bookworm-security +Components: non-free-firmware +EOF + ;; + trixie | sid) + cat <<'EOF' >/etc/apt/sources.list.d/non-free-firmware.sources +Types: deb +URIs: http://deb.debian.org/debian +Suites: trixie trixie-updates +Components: non-free-firmware + +Types: deb +URIs: http://deb.debian.org/debian-security +Suites: trixie-security +Components: non-free-firmware +EOF + ;; + esac + $STD apt -y update +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Helper: Setup GPU device permissions +# ══════════════════════════════════════════════════════════════════════════════ +_setup_gpu_permissions() { + local in_ct="$1" + + # /dev/dri permissions (Intel/AMD) if [[ "$in_ct" == "0" && -d /dev/dri ]]; 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 2>/dev/null || true - $STD adduser "$(id -u -n)" render 2>/dev/null || true + if ls /dev/dri/card* /dev/dri/renderD* &>/dev/null; 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 2>/dev/null || true + $STD adduser "$(id -u -n)" render 2>/dev/null || true - # Sync GID for video/render groups between host and container - local host_video_gid host_render_gid - host_video_gid=$(getent group video | cut -d: -f3) - host_render_gid=$(getent group render | cut -d: -f3) - if [[ -n "$host_video_gid" && -n "$host_render_gid" ]]; then - sed -i "s/^video:x:[0-9]*:/video:x:$host_video_gid:/" /etc/group 2>/dev/null || true - sed -i "s/^render:x:[0-9]*:/render:x:$host_render_gid:/" /etc/group 2>/dev/null || true + # Sync GID with host + local host_video_gid host_render_gid + host_video_gid=$(getent group video | cut -d: -f3) + host_render_gid=$(getent group render | cut -d: -f3) + if [[ -n "$host_video_gid" ]]; then + sed -i "s/^video:x:[0-9]*:/video:x:$host_video_gid:/" /etc/group 2>/dev/null || true + fi + if [[ -n "$host_render_gid" ]]; then + sed -i "s/^render:x:[0-9]*:/render:x:$host_render_gid:/" /etc/group 2>/dev/null || true + fi + + # Verify VA-API + if command -v vainfo &>/dev/null; then + if vainfo &>/dev/null; then + msg_info "VA-API verified and working" + else + msg_warn "vainfo test failed - check GPU passthrough" + fi + fi fi fi - cache_installed_version "hwaccel" "1.0" - msg_ok "Setup Hardware Acceleration" + # /dev/nvidia* permissions (NVIDIA) + if ls /dev/nvidia* &>/dev/null 2>&1; then + msg_info "Configuring NVIDIA device permissions" + for nvidia_dev in /dev/nvidia*; do + [[ -e "$nvidia_dev" ]] && { + chgrp video "$nvidia_dev" 2>/dev/null || true + chmod 666 "$nvidia_dev" 2>/dev/null || true + } + done + if [[ -d /dev/nvidia-caps ]]; then + chmod 755 /dev/nvidia-caps 2>/dev/null || true + for caps_dev in /dev/nvidia-caps/*; do + [[ -e "$caps_dev" ]] && { + chgrp video "$caps_dev" 2>/dev/null || true + chmod 666 "$caps_dev" 2>/dev/null || true + } + done + fi + + # Verify nvidia-smi + if command -v nvidia-smi &>/dev/null; then + if nvidia-smi &>/dev/null; then + msg_info "nvidia-smi verified and working" + else + msg_warn "nvidia-smi test failed - check driver version match" + fi + fi + fi + + # /dev/kfd permissions (AMD ROCm) + if [[ -e /dev/kfd ]]; then + chmod 666 /dev/kfd 2>/dev/null || true + msg_info "AMD ROCm compute device configured" + fi } # ------------------------------------------------------------------------------ @@ -3089,6 +3576,11 @@ EOF setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + # Ensure non-interactive mode for all apt operations + export DEBIAN_FRONTEND=noninteractive + export NEEDRESTART_MODE=a + export NEEDRESTART_SUSPEND=1 + # 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 @@ -3127,6 +3619,20 @@ setup_mariadb() { local CURRENT_VERSION="" CURRENT_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true + # Pre-configure debconf to prevent any interactive prompts during install/upgrade + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + debconf-set-selections </etc/tmpfiles.d/mariadb.conf; then + msg_warn "Failed to create /etc/tmpfiles.d/mariadb.conf - runtime directory may not persist on reboot" + fi + + # Create the directory now if it doesn't exist + # Verify mysql user exists before attempting ownership change + if [[ ! -d /run/mysqld ]]; then + mkdir -p /run/mysqld + # Set permissions first (works regardless of user existence) + chmod 755 /run/mysqld + # Set ownership only if mysql user exists + if getent passwd mysql >/dev/null 2>&1; then + chown mysql:mysql /run/mysqld + else + msg_warn "mysql user not found - directory created with correct permissions but ownership not set" + fi + fi + + msg_ok "Configured MariaDB runtime directory persistence" + cache_installed_version "mariadb" "$MARIADB_VERSION" msg_ok "Setup MariaDB $MARIADB_VERSION" } @@ -3316,6 +3839,11 @@ function setup_mongodb() { DISTRO_ID=$(get_os_info id) DISTRO_CODENAME=$(get_os_info codename) + # Ensure non-interactive mode for all apt operations + export DEBIAN_FRONTEND=noninteractive + export NEEDRESTART_MODE=a + export NEEDRESTART_SUSPEND=1 + # Check AVX support if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then local major="${MONGO_VERSION%%.*}" @@ -3434,6 +3962,11 @@ function setup_mysql() { DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + # Ensure non-interactive mode for all apt operations + export DEBIAN_FRONTEND=noninteractive + export NEEDRESTART_MODE=a + export NEEDRESTART_SUSPEND=1 + # Get currently installed version local CURRENT_VERSION="" CURRENT_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true @@ -3528,7 +4061,6 @@ EOF ensure_apt_working || return 1 # 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 . && @@ -3707,19 +4239,19 @@ function setup_nodejs() { # Check if the module is already installed 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:]')" + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" 2>&1 | grep "$MODULE_NAME@" | awk -F@ '{print $2}' 2>/dev/null | tr -d '[:space:]' || echo '')" 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++)) + ((failed_modules++)) || true 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++)) + ((failed_modules++)) || true continue fi fi @@ -3727,7 +4259,7 @@ function setup_nodejs() { 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++)) + ((failed_modules++)) || true continue fi fi @@ -3826,7 +4358,7 @@ EOF # Get available PHP version from repository local AVAILABLE_PHP_VERSION="" - AVAILABLE_PHP_VERSION=$(apt-cache show "php${PHP_VERSION}" 2>/dev/null | grep -m1 "^Version:" | awk '{print $2}' | cut -d- -f1) || true + AVAILABLE_PHP_VERSION=$(apt-cache show "php${PHP_VERSION}" 2>/dev/null | grep -m1 "^Version:" | awk '{print $2}' 2>/dev/null | cut -d- -f1 || true) if [[ -z "$AVAILABLE_PHP_VERSION" ]]; then msg_error "PHP ${PHP_VERSION} not found in configured repositories" @@ -3967,6 +4499,11 @@ function setup_postgresql() { DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + # Ensure non-interactive mode for all apt operations + export DEBIAN_FRONTEND=noninteractive + export NEEDRESTART_MODE=a + export NEEDRESTART_SUSPEND=1 + # Get currently installed version local CURRENT_PG_VERSION="" if command -v psql >/dev/null; then @@ -4424,6 +4961,11 @@ 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) + # Ensure non-interactive mode for all apt operations + export DEBIAN_FRONTEND=noninteractive + export NEEDRESTART_MODE=a + export NEEDRESTART_SUSPEND=1 + # Resolve "latest" version if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | @@ -4486,7 +5028,6 @@ function setup_clickhouse() { "main" # Install packages with retry logic - export DEBIAN_FRONTEND=noninteractive $STD apt update || { msg_error "APT update failed for ClickHouse repository" return 1 @@ -4628,7 +5169,7 @@ function setup_rust() { # Check if already installed if echo "$CRATE_LIST" | grep -q "^${NAME} "; then - INSTALLED_VER=$(echo "$CRATE_LIST" | grep "^${NAME} " | head -1 | awk '{print $2}' | tr -d 'v:') + INSTALLED_VER=$(echo "$CRATE_LIST" | grep "^${NAME} " | head -1 | awk '{print $2}' 2>/dev/null | tr -d 'v:' || echo '') if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then msg_info "Upgrading $NAME from v$INSTALLED_VER to v$VER" @@ -4643,7 +5184,7 @@ function setup_rust() { msg_error "Failed to upgrade $NAME" return 1 } - local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' | tr -d 'v:') + local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' 2>/dev/null | tr -d 'v:' || echo 'unknown') msg_ok "Upgraded $NAME to v$NEW_VER" else msg_ok "$NAME v$INSTALLED_VER already installed" @@ -4661,7 +5202,7 @@ function setup_rust() { msg_error "Failed to install $NAME" return 1 } - local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' | tr -d 'v:') + local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' 2>/dev/null | tr -d 'v:' || echo 'unknown') msg_ok "Installed $NAME v$NEW_VER" fi fi @@ -4683,8 +5224,6 @@ function setup_uv() { local UVX_BIN="/usr/local/bin/uvx" local TMP_DIR=$(mktemp -d) local CACHED_VERSION - local TARGET_VERSION="" - local USE_PINNED_VERSION=false # trap for TMP Cleanup trap "rm -rf '$TMP_DIR'" EXIT @@ -4720,27 +5259,22 @@ function setup_uv() { ensure_dependencies jq - # Check if specific version is requested via UV_VERSION environment variable - if [[ -n "${UV_VERSION:-}" ]]; then - TARGET_VERSION="${UV_VERSION}" - USE_PINNED_VERSION=true - else - # Fetch latest version from GitHub API - local releases_json - releases_json=$(curl -fsSL --max-time 15 \ - "https://api.github.com/repos/astral-sh/uv/releases/latest" 2>/dev/null || echo "") + # 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 "") - if [[ -z "$releases_json" ]]; then - msg_error "Could not fetch latest uv version from GitHub API" - return 1 - fi + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest uv version from GitHub API" + return 1 + fi - TARGET_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//') + local LATEST_VERSION + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//') - if [[ -z "$TARGET_VERSION" ]]; then - msg_error "Could not parse uv version from GitHub API response" - return 1 - fi + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not parse uv version from GitHub API response" + return 1 fi # Get currently installed version @@ -4749,9 +5283,9 @@ function setup_uv() { INSTALLED_VERSION=$("$UV_BIN" --version 2>/dev/null | awk '{print $2}') fi - # Scenario 1: Already at target version - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$TARGET_VERSION" ]]; then - cache_installed_version "uv" "$TARGET_VERSION" + # Scenario 1: Already at latest version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + cache_installed_version "uv" "$LATEST_VERSION" # Check if uvx is needed and missing if [[ "${USE_UVX:-NO}" == "YES" ]] && [[ ! -x "$UVX_BIN" ]]; then @@ -4763,22 +5297,14 @@ function setup_uv() { return 0 fi - # Scenario 2: New install or upgrade/downgrade - if [[ -n "$INSTALLED_VERSION" ]]; then - if [[ "$USE_PINNED_VERSION" == true ]]; then - msg_info "Switching uv from $INSTALLED_VERSION to pinned version $TARGET_VERSION" - else - msg_info "Upgrade uv from $INSTALLED_VERSION to $TARGET_VERSION" - 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 - if [[ "$USE_PINNED_VERSION" == true ]]; then - msg_info "Setup uv $TARGET_VERSION (pinned)" - else - msg_info "Setup uv $TARGET_VERSION" - fi + msg_info "Setup uv $LATEST_VERSION" fi - local UV_URL="https://github.com/astral-sh/uv/releases/download/${TARGET_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" @@ -4832,8 +5358,8 @@ function setup_uv() { msg_ok "Python $PYTHON_VERSION installed" fi - cache_installed_version "uv" "$TARGET_VERSION" - msg_ok "Setup uv $TARGET_VERSION" + cache_installed_version "uv" "$LATEST_VERSION" + msg_ok "Setup uv $LATEST_VERSION" } # Helper function to install uvx wrapper @@ -4998,7 +5524,7 @@ function setup_docker() { # Install or upgrade Docker if [ "$docker_installed" = true ]; then msg_info "Checking for Docker updates" - DOCKER_LATEST_VERSION=$(apt-cache policy docker-ce | grep Candidate | awk '{print $2}' | cut -d':' -f2 | cut -d'-' -f1) + DOCKER_LATEST_VERSION=$(apt-cache policy docker-ce | grep Candidate | awk '{print $2}' 2>/dev/null | cut -d':' -f2 | cut -d'-' -f1 || echo '') if [ "$DOCKER_CURRENT_VERSION" != "$DOCKER_LATEST_VERSION" ]; then msg_info "Updating Docker $DOCKER_CURRENT_VERSION → $DOCKER_LATEST_VERSION" From 37fdd4779b35e4cfcfb362366d668af9e50c4f83 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 08:55:28 +0100 Subject: [PATCH 294/378] Get Features & Fixes from Upstream --- misc/build.func | 124 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 9 deletions(-) diff --git a/misc/build.func b/misc/build.func index 34d599e3e..a8a2f3dfb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -311,7 +311,10 @@ update_motd_ip() { # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { - [[ "$SSH" != "yes" ]] && return 0 + [[ "${SSH:-no}" != "yes" ]] && return 0 + + # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) + : "${SSH_KEYS_FILE:=}" if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then msg_info "Installing selected SSH keys into CT ${CTID}" @@ -394,6 +397,90 @@ find_host_ssh_keys() { ) } +# ============================================================================== +# SECTION 3B: IP RANGE SCANNING +# ============================================================================== + +# ------------------------------------------------------------------------------ +# ip_to_int() / int_to_ip() +# +# - Converts IP address to integer and vice versa for range iteration +# ------------------------------------------------------------------------------ +ip_to_int() { + local IFS=. + read -r i1 i2 i3 i4 <<<"$1" + echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) +} + +int_to_ip() { + local ip=$1 + echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" +} + +# ------------------------------------------------------------------------------ +# resolve_ip_from_range() +# +# - Takes an IP range in format "10.0.0.1/24-10.0.0.10/24" +# - Pings each IP in the range to find the first available one +# - Returns the first free IP with CIDR notation +# - Sets NET_RESOLVED to the resolved IP or empty on failure +# ------------------------------------------------------------------------------ +resolve_ip_from_range() { + local range="$1" + 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_start ip_end + + # Parse range: "10.0.0.1/24-10.0.0.10/24" + ip_start="${range%%-*}" + ip_end="${range##*-}" + + if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then + NET_RESOLVED="" + return 1 + fi + + local ip1="${ip_start%%/*}" + local ip2="${ip_end%%/*}" + local cidr="${ip_start##*/}" + + local start_int=$(ip_to_int "$ip1") + local end_int=$(ip_to_int "$ip2") + + for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do + local ip=$(int_to_ip $ip_int) + msg_info "Checking IP: $ip" + if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then + NET_RESOLVED="$ip/$cidr" + msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" + return 0 + fi + done + + NET_RESOLVED="" + msg_error "No free IP found in range $range" + return 1 +} + +# ------------------------------------------------------------------------------ +# is_ip_range() +# +# - Checks if a string is an IP range (contains - and looks like IP/CIDR) +# - Returns 0 if it's a range, 1 otherwise +# ------------------------------------------------------------------------------ +is_ip_range() { + local value="$1" + local ip_start ip_end + if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then + local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' + ip_start="${value%%-*}" + ip_end="${value##*-}" + if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then + return 0 + fi + fi + return 1 +} + # ============================================================================== # SECTION 4: STORAGE & RESOURCE MANAGEMENT # ============================================================================== @@ -506,6 +593,18 @@ base_settings() { HN=${var_hostname:-$NSAPP} BRG=${var_brg:-"vmbr0"} NET=${var_net:-"dhcp"} + + # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) + if is_ip_range "$NET"; then + msg_info "Scanning IP range: $NET" + if resolve_ip_from_range "$NET"; then + NET="$NET_RESOLVED" + else + msg_error "Could not find free IP in range. Falling back to DHCP." + NET="dhcp" + fi + fi + IPV6_METHOD=${var_ipv6_method:-"none"} IPV6_STATIC=${var_ipv6_static:-""} GATE=${var_gateway:-""} @@ -535,9 +634,16 @@ base_settings() { TAGS="community-script,${var_tags:-}" ENABLE_FUSE=${var_fuse:-"${1:-no}"} ENABLE_TUN=${var_tun:-"${1:-no}"} - ENABLE_KEYCTL=${var_keyctl:-0} - ENABLE_MKNOD=${var_mknod:-0} + + # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) + ENABLE_GPU=${var_gpu:-"no"} + ENABLE_NESTING=${var_nesting:-"1"} + ENABLE_KEYCTL=${var_keyctl:-"0"} + ENABLE_MKNOD=${var_mknod:-"0"} MOUNT_FS=${var_mount_fs:-""} + PROTECT_CT=${var_protection:-"no"} + CT_TIMEZONE=${var_timezone:-"$timezone"} + [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones # 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 @@ -696,12 +802,12 @@ var_fuse=no var_tun=no # Advanced Settings (Proxmox-official features) -var_nesting=1 -var_keyctl=0 -var_mknod=0 -var_mount_fs="" -var_protection=no -var_timezone="" +var_nesting=1 # Allow nesting (required for Docker/LXC in CT) +var_keyctl=0 # Allow keyctl() - needed for Docker (systemd-networkd workaround) +var_mknod=0 # Allow device node creation (requires kernel 5.3+, experimental) +var_mount_fs= # Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) +var_protection=no # Prevent accidental deletion of container +var_timezone= # Container timezone (e.g. Europe/Berlin, leave empty for host timezone) var_tags=community-script var_verbose=no From dfed41f70f7a44185ed9e9a5da577578122999df Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 08:55:45 +0100 Subject: [PATCH 295/378] Format --- misc/build.func | 6512 +++++++++++++++++++++++------------------------ 1 file changed, 3256 insertions(+), 3256 deletions(-) diff --git a/misc/build.func b/misc/build.func index a8a2f3dfb..d3a9d312e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -39,46 +39,46 @@ # - Captures app-declared resource defaults (CPU, RAM, Disk) # ------------------------------------------------------------------------------ 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. - SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files - BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + 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. + SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files + BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - # Parse dev_mode early - parse_dev_mode + # Parse dev_mode early + parse_dev_mode - # Setup persistent log directory if logs mode active - if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then - mkdir -p /var/log/community-scripts - BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" - fi + # Setup persistent log directory if logs mode active + if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then + mkdir -p /var/log/community-scripts + BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" + fi - # Get Proxmox VE version and kernel version - if command -v pveversion >/dev/null 2>&1; then - PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - else - PVEVERSION="N/A" - fi - KERNEL_VERSION=$(uname -r) + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + else + 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 + # 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 } # ----------------------------------------------------------------------------- @@ -187,17 +187,17 @@ variables() { 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" + 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" + 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 # ============================================================================== @@ -215,46 +215,46 @@ fi # ------------------------------------------------------------------------------ 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 - # Silent success - only show errors if they exist + # Silent success - only show errors if they exist } # ============================================================================== @@ -270,18 +270,18 @@ maxkeys_check() { # - Returns "Unknown" if OS type cannot be determined # ------------------------------------------------------------------------------ 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" } # ------------------------------------------------------------------------------ @@ -291,16 +291,16 @@ get_current_ip() { # - Removes old IP entries to avoid duplicates # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -311,30 +311,30 @@ update_motd_ip() { # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { - [[ "${SSH:-no}" != "yes" ]] && return 0 + [[ "${SSH:-no}" != "yes" ]] && return 0 - # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) - : "${SSH_KEYS_FILE:=}" + # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) + : "${SSH_KEYS_FILE:=}" - 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)." + 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 } # ------------------------------------------------------------------------------ @@ -346,55 +346,55 @@ install_ssh_keys_into_ct() { # - 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 + 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 + 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 + 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 ' + # 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 + if ((c > 0)); then + files+=("$f") + total=$((total + c)) fi + done - FOUND_HOST_KEY_COUNT="$total" - ( - IFS=: - echo "${files[*]}" - ) + # 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[*]}" + ) } # ============================================================================== @@ -407,14 +407,14 @@ find_host_ssh_keys() { # - Converts IP address to integer and vice versa for range iteration # ------------------------------------------------------------------------------ ip_to_int() { - local IFS=. - read -r i1 i2 i3 i4 <<<"$1" - echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) + local IFS=. + read -r i1 i2 i3 i4 <<<"$1" + echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) } int_to_ip() { - local ip=$1 - echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" + local ip=$1 + echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" } # ------------------------------------------------------------------------------ @@ -426,39 +426,39 @@ int_to_ip() { # - Sets NET_RESOLVED to the resolved IP or empty on failure # ------------------------------------------------------------------------------ resolve_ip_from_range() { - local range="$1" - 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_start ip_end + local range="$1" + 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_start ip_end - # Parse range: "10.0.0.1/24-10.0.0.10/24" - ip_start="${range%%-*}" - ip_end="${range##*-}" - - if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then - NET_RESOLVED="" - return 1 - fi - - local ip1="${ip_start%%/*}" - local ip2="${ip_end%%/*}" - local cidr="${ip_start##*/}" - - local start_int=$(ip_to_int "$ip1") - local end_int=$(ip_to_int "$ip2") - - for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do - local ip=$(int_to_ip $ip_int) - msg_info "Checking IP: $ip" - if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then - NET_RESOLVED="$ip/$cidr" - msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" - return 0 - fi - done + # Parse range: "10.0.0.1/24-10.0.0.10/24" + ip_start="${range%%-*}" + ip_end="${range##*-}" + if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then NET_RESOLVED="" - msg_error "No free IP found in range $range" return 1 + fi + + local ip1="${ip_start%%/*}" + local ip2="${ip_end%%/*}" + local cidr="${ip_start##*/}" + + local start_int=$(ip_to_int "$ip1") + local end_int=$(ip_to_int "$ip2") + + for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do + local ip=$(int_to_ip $ip_int) + msg_info "Checking IP: $ip" + if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then + NET_RESOLVED="$ip/$cidr" + msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" + return 0 + fi + done + + NET_RESOLVED="" + msg_error "No free IP found in range $range" + return 1 } # ------------------------------------------------------------------------------ @@ -468,17 +468,17 @@ resolve_ip_from_range() { # - Returns 0 if it's a range, 1 otherwise # ------------------------------------------------------------------------------ is_ip_range() { - local value="$1" - local ip_start ip_end - if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then - local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' - ip_start="${value%%-*}" - ip_end="${value##*-}" - if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then - return 0 - fi + local value="$1" + local ip_start ip_end + if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then + local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' + ip_start="${value%%-*}" + ip_end="${value##*-}" + if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then + return 0 fi - return 1 + fi + return 1 } # ============================================================================== @@ -493,53 +493,53 @@ is_ip_range() { # - Arguments: vars_file, key (var_container_storage/var_template_storage), value # ------------------------------------------------------------------------------ _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" + # $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 + # $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") + 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 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 + 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" + _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 + # 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 - # Silent operation - no output message + # Silent operation - no output message } # ============================================================================== @@ -556,102 +556,102 @@ choose_and_set_storage_for_file() { # - Sets up container type, resources, network, SSH, features, and tags # ------------------------------------------------------------------------------ base_settings() { - # Default Settings - CT_TYPE=${var_unprivileged:-"1"} + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} - # 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}" + # 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 + # 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 + 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 + 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} - HN=${var_hostname:-$NSAPP} - BRG=${var_brg:-"vmbr0"} - NET=${var_net:-"dhcp"} + 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} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} - # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) - if is_ip_range "$NET"; then - msg_info "Scanning IP range: $NET" - if resolve_ip_from_range "$NET"; then - NET="$NET_RESOLVED" - else - msg_error "Could not find free IP in range. Falling back to DHCP." - NET="dhcp" - fi + # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) + if is_ip_range "$NET"; then + msg_info "Scanning IP range: $NET" + if resolve_ip_from_range "$NET"; then + NET="$NET_RESOLVED" + else + msg_error "Could not find free IP in range. Falling back to DHCP." + NET="dhcp" fi + fi - 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:-""} + 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:-""} - # 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_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" - APT_CACHER="" - APT_CACHER_IP="" - else - msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" - fi + # 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_custom "⚠️" "${YW}" "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:-""} - 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}"} + 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}"} - # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) - ENABLE_GPU=${var_gpu:-"no"} - ENABLE_NESTING=${var_nesting:-"1"} - ENABLE_KEYCTL=${var_keyctl:-"0"} - ENABLE_MKNOD=${var_mknod:-"0"} - MOUNT_FS=${var_mount_fs:-""} - PROTECT_CT=${var_protection:-"no"} - CT_TIMEZONE=${var_timezone:-"$timezone"} - [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones + # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) + ENABLE_GPU=${var_gpu:-"no"} + ENABLE_NESTING=${var_nesting:-"1"} + ENABLE_KEYCTL=${var_keyctl:-"0"} + ENABLE_MKNOD=${var_mknod:-"0"} + MOUNT_FS=${var_mount_fs:-""} + PROTECT_CT=${var_protection:-"no"} + CT_TIMEZONE=${var_timezone:-"$timezone"} + [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones - # 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 } # ------------------------------------------------------------------------------ @@ -662,49 +662,49 @@ base_settings() { # - Only loads whitelisted var_* keys # ------------------------------------------------------------------------------ load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" - # Allowed var_* keys - local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # Allowed var_* keys + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) - # Whitelist check helper - _is_whitelisted() { - local k="$1" w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 - } + # Whitelist check helper + _is_whitelisted() { + local k="$1" w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } - 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]}" + 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 "$var_key" || continue + [[ "$var_key" != var_* ]] && continue + _is_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 + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi - # Set only if not already exported - [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" - fi - done <"$file" - msg_ok "Loaded ${file}" + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + fi + done <"$file" + msg_ok "Loaded ${file}" } # ------------------------------------------------------------------------------ @@ -717,56 +717,56 @@ load_vars_file() { # - Calls base_settings "$VERBOSE" and echo_default # ------------------------------------------------------------------------------ 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_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # 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_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone 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 + # 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" - # 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 - # 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" + # Silent creation - no msg_info output + mkdir -p /usr/local/community-scripts - local canonical="/usr/local/community-scripts/default.vars" - # Silent creation - no msg_info output - 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" - # 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' + # 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. @@ -815,47 +815,47 @@ var_verbose=no # 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 + # 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" - # Silent creation - no output message - } + chmod 0644 "$canonical" + # Silent creation - no output message + } - # 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 - } + # 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 + } - # 1) Ensure file exists - _ensure_default_vars + # 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" + # 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 + # 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 User Defaults (default.vars) on node $PVEHOST_NAME${CL}" - echo_default + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using User Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default } # ------------------------------------------------------------------------------ @@ -866,8 +866,8 @@ EOF # ------------------------------------------------------------------------------ get_app_defaults_path() { - local n="${NSAPP:-${APP,,}}" - echo "/usr/local/community-scripts/defaults/${n}.vars" + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" } # ------------------------------------------------------------------------------ @@ -880,32 +880,32 @@ 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_disk var_fuse var_gpu - 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 - ) + # 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_disk var_fuse var_gpu + 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 + ) fi # Global whitelist check function (used by _load_vars_file_to_map and others) _is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 + 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" + # 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 @@ -913,190 +913,190 @@ _sanitize_value() { # 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" + 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 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 out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" - local found_change=0 + 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" + # 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 - printf "%b" "$out" + # 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)" + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" - # NET/GW - _net="${NET:-}" - _gate="" - case "${GATE:-}" in - ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; - esac + # 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 + # 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 + # 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 + # 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}" - _nesting="${ENABLE_NESTING:-1}" - _keyctl="${ENABLE_KEYCTL:-0}" - _mknod="${ENABLE_MKNOD:-0}" - _mount_fs="${ALLOW_MOUNT_FS:-}" - _protect="${PROTECT_CT:-no}" - _timezone="${CT_TIMEZONE:-}" - _tags="${TAGS:-}" - _verbose="${VERBOSE:-no}" + # 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}" + _nesting="${ENABLE_NESTING:-1}" + _keyctl="${ENABLE_KEYCTL:-0}" + _mknod="${ENABLE_MKNOD:-0}" + _mount_fs="${ALLOW_MOUNT_FS:-}" + _protect="${PROTECT_CT:-no}" + _timezone="${CT_TIMEZONE:-}" + _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}" + # 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:-}}" + # 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 "# 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")" + 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 "${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")" - # var_ipv6_static removed - static IPs are unique, can't be default + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + # 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")" + [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" - [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" - [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" - [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" - [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" - [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" - [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" + [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" + [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" + [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" + [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" + [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" + [ -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")" - } >"$tmpf" + [ -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" + echo "$tmpf" } # ------------------------------------------------------------------------------ @@ -1107,103 +1107,103 @@ _build_current_app_vars_tmp() { # - 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)" + 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")" + # 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 + # 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 - - # 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." - break - ;; - esac - done - 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" } ensure_storage_selection_for_vars_file() { - local 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-) + # 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 + 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 + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container - # Silent operation - no output message + # Silent operation - no output message } 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" + 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" } # ============================================================================== @@ -1219,785 +1219,785 @@ ensure_global_default_vars_file() { # - Allows user to customize all container settings # ------------------------------------------------------------------------------ advanced_settings() { - # Enter alternate screen buffer to prevent flicker between dialogs - tput smcup 2>/dev/null || true - trap 'tput rmcup 2>/dev/null || true' RETURN + # Enter alternate screen buffer to prevent flicker between dialogs + tput smcup 2>/dev/null || true + trap 'tput rmcup 2>/dev/null || true' RETURN - # Initialize defaults - TAGS="community-script;${var_tags:-}" - local STEP=1 - local MAX_STEP=28 + # Initialize defaults + TAGS="community-script;${var_tags:-}" + local STEP=1 + local MAX_STEP=28 - # Store values for back navigation - inherit from var_* app defaults - local _ct_type="${var_unprivileged:-1}" - local _pw="" - local _pw_display="Automatic Login" - local _ct_id="$NEXTID" - local _hostname="$NSAPP" - local _disk_size="${var_disk:-4}" - local _core_count="${var_cpu:-1}" - local _ram_size="${var_ram:-1024}" - local _bridge="${var_brg:-vmbr0}" - local _net="${var_net:-dhcp}" - local _gate="${var_gateway:-}" - local _ipv6_method="${var_ipv6_method:-auto}" - local _ipv6_addr="" - local _ipv6_gate="" - local _apt_cacher="${var_apt_cacher:-no}" - local _apt_cacher_ip="${var_apt_cacher_ip:-}" - local _mtu="${var_mtu:-}" - local _sd="${var_searchdomain:-}" - local _ns="${var_ns:-}" - local _mac="${var_mac:-}" - local _vlan="${var_vlan:-}" - local _tags="$TAGS" - local _enable_fuse="${var_fuse:-no}" - local _enable_tun="${var_tun:-no}" - local _enable_gpu="${var_gpu:-no}" - local _enable_nesting="${var_nesting:-1}" - local _verbose="${var_verbose:-no}" - local _enable_keyctl="${var_keyctl:-0}" - local _enable_mknod="${var_mknod:-0}" - local _mount_fs="${var_mount_fs:-}" - local _protect_ct="${var_protection:-no}" + # Store values for back navigation - inherit from var_* app defaults + local _ct_type="${var_unprivileged:-1}" + local _pw="" + local _pw_display="Automatic Login" + local _ct_id="$NEXTID" + local _hostname="$NSAPP" + local _disk_size="${var_disk:-4}" + local _core_count="${var_cpu:-1}" + local _ram_size="${var_ram:-1024}" + local _bridge="${var_brg:-vmbr0}" + local _net="${var_net:-dhcp}" + local _gate="${var_gateway:-}" + local _ipv6_method="${var_ipv6_method:-auto}" + local _ipv6_addr="" + local _ipv6_gate="" + local _apt_cacher="${var_apt_cacher:-no}" + local _apt_cacher_ip="${var_apt_cacher_ip:-}" + local _mtu="${var_mtu:-}" + local _sd="${var_searchdomain:-}" + local _ns="${var_ns:-}" + local _mac="${var_mac:-}" + local _vlan="${var_vlan:-}" + local _tags="$TAGS" + local _enable_fuse="${var_fuse:-no}" + local _enable_tun="${var_tun:-no}" + local _enable_gpu="${var_gpu:-no}" + local _enable_nesting="${var_nesting:-1}" + local _verbose="${var_verbose:-no}" + local _enable_keyctl="${var_keyctl:-0}" + local _enable_mknod="${var_mknod:-0}" + local _mount_fs="${var_mount_fs:-}" + local _protect_ct="${var_protection:-no}" - # Detect host timezone for default (if not set via var_timezone) - local _host_timezone="" - if command -v timedatectl >/dev/null 2>&1; then - _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") - elif [ -f /etc/timezone ]; then - _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") - fi - local _ct_timezone="${var_timezone:-$_host_timezone}" + # Detect host timezone for default (if not set via var_timezone) + local _host_timezone="" + if command -v timedatectl >/dev/null 2>&1; then + _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") + elif [ -f /etc/timezone ]; then + _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") + fi + local _ct_timezone="${var_timezone:-$_host_timezone}" - # Helper to show current progress - show_progress() { - local current=$1 - local total=$MAX_STEP - echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" - } + # Helper to show current progress + show_progress() { + local current=$1 + local total=$MAX_STEP + echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" + } - # Detect available bridges (do this once) - local BRIDGES="" - local BRIDGE_MENU_OPTIONS=() - _detect_bridges() { - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) - BRIDGES="" - local OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true - if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - local start=$(echo "${pair}" | cut -d':' -f1) - local end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - local 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) + # Detect available bridges (do this once) + local BRIDGES="" + local BRIDGE_MENU_OPTIONS=() + _detect_bridges() { + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) + BRIDGES="" + local OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true + if [ -f "${iface_indexes_tmpfile}" ]; then + while read -r pair; do + local start=$(echo "${pair}" | cut -d':' -f1) + local end=$(echo "${pair}" | cut -d':' -f2) + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + local 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) - # Build bridge menu - BRIDGE_MENU_OPTIONS=() - if [[ -n "$BRIDGES" ]]; then - while IFS= read -r bridge; do - if [[ -n "$bridge" ]]; then - local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') - BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") - fi - done <<<"$BRIDGES" + # Build bridge menu + BRIDGE_MENU_OPTIONS=() + if [[ -n "$BRIDGES" ]]; then + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') + BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") fi - } - _detect_bridges + done <<<"$BRIDGES" + fi + } + _detect_bridges - # Main wizard loop - while [ $STEP -le $MAX_STEP ]; do - case $STEP in + # Main wizard loop + while [ $STEP -le $MAX_STEP ]; do + case $STEP in - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 1: Container Type - # ═══════════════════════════════════════════════════════════════════════════ - 1) - local default_on="ON" - local default_off="OFF" - [[ "$_ct_type" == "0" ]] && { - default_on="OFF" - default_off="ON" - } + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 1: Container Type + # ═══════════════════════════════════════════════════════════════════════════ + 1) + local default_on="ON" + local default_off="OFF" + [[ "$_ct_type" == "0" ]] && { + default_on="OFF" + default_off="ON" + } - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TYPE" \ - --ok-button "Next" --cancel-button "Exit" \ - --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ - "1" "Unprivileged (recommended)" $default_on \ - "0" "Privileged" $default_off \ - 3>&1 1>&2 2>&3); then - [[ -n "$result" ]] && _ct_type="$result" - ((STEP++)) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TYPE" \ + --ok-button "Next" --cancel-button "Exit" \ + --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ + "1" "Unprivileged (recommended)" $default_on \ + "0" "Privileged" $default_off \ + 3>&1 1>&2 2>&3); then + [[ -n "$result" ]] && _ct_type="$result" + ((STEP++)) + else + exit_script + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 2: Root Password + # ═══════════════════════════════════════════════════════════════════════════ + 2) + if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "ROOT PASSWORD" \ + --ok-button "Next" --cancel-button "Back" \ + --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ + 3>&1 1>&2 2>&3); then + + if [[ -z "$PW1" ]]; then + _pw="" + _pw_display="Automatic Login" + ((STEP++)) + elif [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + elif ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + else + # Verify password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "PASSWORD VERIFICATION" \ + --ok-button "Confirm" --cancel-button "Back" \ + --passwordbox "\nVerify Root Password" 10 58 \ + 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + _pw="-password $PW1" + _pw_display="********" + ((STEP++)) else - exit_script + whiptail --msgbox "Passwords do not match. Please try again." 8 58 fi - ;; + else + ((STEP--)) + fi + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 2: Root Password - # ═══════════════════════════════════════════════════════════════════════════ - 2) - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "ROOT PASSWORD" \ + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 3: Container ID + # ═══════════════════════════════════════════════════════════════════════════ + 3) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER ID" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ + 3>&1 1>&2 2>&3); then + _ct_id="${result:-$NEXTID}" + ((STEP++)) + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 4: Hostname + # ═══════════════════════════════════════════════════════════════════════════ + 4) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "HOSTNAME" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ + 3>&1 1>&2 2>&3); then + local hn_test="${result:-$NSAPP}" + hn_test=$(echo "${hn_test,,}" | tr -d ' ') + if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + _hostname="$hn_test" + ((STEP++)) + else + whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 5: Disk Size + # ═══════════════════════════════════════════════════════════════════════════ + 5) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DISK SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ + 3>&1 1>&2 2>&3); then + local disk_test="${result:-$var_disk}" + if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then + _disk_size="$disk_test" + ((STEP++)) + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 6: CPU Cores + # ═══════════════════════════════════════════════════════════════════════════ + 6) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CPU CORES" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ + 3>&1 1>&2 2>&3); then + local cpu_test="${result:-$var_cpu}" + if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then + _core_count="$cpu_test" + ((STEP++)) + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 7: RAM Size + # ═══════════════════════════════════════════════════════════════════════════ + 7) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "RAM SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ + 3>&1 1>&2 2>&3); then + local ram_test="${result:-$var_ram}" + if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then + _ram_size="$ram_test" + ((STEP++)) + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 8: Network Bridge + # ═══════════════════════════════════════════════════════════════════════════ + 8) + if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then + _bridge="vmbr0" + ((STEP++)) + else + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NETWORK BRIDGE" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect network bridge:" 16 58 6 \ + "${BRIDGE_MENU_OPTIONS[@]}" \ + 3>&1 1>&2 2>&3); then + _bridge="${result:-vmbr0}" + ((STEP++)) + else + ((STEP--)) + fi + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 9: IPv4 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 9) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv4 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3); then + + if [[ "$result" == "static" ]]; then + # Get static IP + local static_ip + if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "STATIC IPv4 ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + # Get gateway + local gateway_ip + if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GATEWAY IP" \ --ok-button "Next" --cancel-button "Back" \ - --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ + --inputbox "\nEnter Gateway IP address" 10 58 "" \ 3>&1 1>&2 2>&3); then - - if [[ -z "$PW1" ]]; then - _pw="" - _pw_display="Automatic Login" - ((STEP++)) - elif [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - elif ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 + if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + _net="$static_ip" + _gate=",gw=$gateway_ip" + ((STEP++)) else - # Verify password - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "PASSWORD VERIFICATION" \ - --ok-button "Confirm" --cancel-button "Back" \ - --passwordbox "\nVerify Root Password" 10 58 \ - 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - _pw="-password $PW1" - _pw_display="********" - ((STEP++)) - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - ((STEP--)) - fi + whiptail --msgbox "Invalid Gateway IP format." 8 58 fi + fi else - ((STEP--)) + whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 fi - ;; + fi + else + _net="dhcp" + _gate="" + ((STEP++)) + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 3: Container ID - # ═══════════════════════════════════════════════════════════════════════════ - 3) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER ID" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ - 3>&1 1>&2 2>&3); then - _ct_id="${result:-$NEXTID}" - ((STEP++)) + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 10: IPv6 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 10) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv6 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv6 Address Management:" 16 70 5 \ + "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ + "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ + "static" "Static - Manual IPv6 address configuration" \ + "none" "None - No IPv6 assignment (most containers)" \ + "disable" "Fully Disabled - (breaks some services)" \ + 3>&1 1>&2 2>&3); then + + _ipv6_method="$result" + case "$result" in + static) + local ipv6_addr + if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "STATIC IPv6 ADDRESS" \ + --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + _ipv6_addr="$ipv6_addr" + # Optional gateway + _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "IPv6 GATEWAY" \ + --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ + 3>&1 1>&2 2>&3) || true + ((STEP++)) else - ((STEP--)) + whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 fi - ;; + fi + ;; + dhcp) + _ipv6_addr="dhcp" + _ipv6_gate="" + ((STEP++)) + ;; + disable) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + none) + _ipv6_addr="none" + _ipv6_gate="" + ((STEP++)) + ;; + *) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + esac + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 4: Hostname - # ═══════════════════════════════════════════════════════════════════════════ - 4) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "HOSTNAME" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ - 3>&1 1>&2 2>&3); then - local hn_test="${result:-$NSAPP}" - hn_test=$(echo "${hn_test,,}" | tr -d ' ') - if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - _hostname="$hn_test" - ((STEP++)) - else - whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 11: MTU Size + # ═══════════════════════════════════════════════════════════════════════════ + 11) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MTU SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mtu="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 5: Disk Size - # ═══════════════════════════════════════════════════════════════════════════ - 5) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DISK SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ - 3>&1 1>&2 2>&3); then - local disk_test="${result:-$var_disk}" - if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then - _disk_size="$disk_test" - ((STEP++)) - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 12: DNS Search Domain + # ═══════════════════════════════════════════════════════════════════════════ + 12) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SEARCH DOMAIN" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _sd="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 6: CPU Cores - # ═══════════════════════════════════════════════════════════════════════════ - 6) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CPU CORES" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ - 3>&1 1>&2 2>&3); then - local cpu_test="${result:-$var_cpu}" - if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then - _core_count="$cpu_test" - ((STEP++)) - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 13: DNS Server + # ═══════════════════════════════════════════════════════════════════════════ + 13) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SERVER" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _ns="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 7: RAM Size - # ═══════════════════════════════════════════════════════════════════════════ - 7) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "RAM SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ - 3>&1 1>&2 2>&3); then - local ram_test="${result:-$var_ram}" - if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then - _ram_size="$ram_test" - ((STEP++)) - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 14: MAC Address + # ═══════════════════════════════════════════════════════════════════════════ + 14) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MAC ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mac="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 8: Network Bridge - # ═══════════════════════════════════════════════════════════════════════════ - 8) - if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then - _bridge="vmbr0" - ((STEP++)) - else - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NETWORK BRIDGE" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect network bridge:" 16 58 6 \ - "${BRIDGE_MENU_OPTIONS[@]}" \ - 3>&1 1>&2 2>&3); then - _bridge="${result:-vmbr0}" - ((STEP++)) - else - ((STEP--)) - fi - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 15: VLAN Tag + # ═══════════════════════════════════════════════════════════════════════════ + 15) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VLAN TAG" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _vlan="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 9: IPv4 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 9) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv4 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3); then + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 16: Tags + # ═══════════════════════════════════════════════════════════════════════════ + 16) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TAGS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ + 3>&1 1>&2 2>&3); then + _tags="${result:-;}" + _tags=$(echo "$_tags" | tr -d '[:space:]') + ((STEP++)) + else + ((STEP--)) + fi + ;; - if [[ "$result" == "static" ]]; then - # Get static IP - local static_ip - if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "STATIC IPv4 ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - # Get gateway - local gateway_ip - if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GATEWAY IP" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Gateway IP address" 10 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - _net="$static_ip" - _gate=",gw=$gateway_ip" - ((STEP++)) - else - whiptail --msgbox "Invalid Gateway IP format." 8 58 - fi - fi - else - whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 - fi - fi - else - _net="dhcp" - _gate="" - ((STEP++)) - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 17: SSH Settings + # ═══════════════════════════════════════════════════════════════════════════ + 17) + configure_ssh_settings "Step $STEP/$MAX_STEP" + # configure_ssh_settings handles its own flow, always advance + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 10: IPv6 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 10) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv6 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv6 Address Management:" 16 70 5 \ - "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ - "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ - "static" "Static - Manual IPv6 address configuration" \ - "none" "None - No IPv6 assignment (most containers)" \ - "disable" "Fully Disabled - (breaks some services)" \ - 3>&1 1>&2 2>&3); then + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 18: FUSE Support + # ═══════════════════════════════════════════════════════════════════════════ + 18) + local fuse_default_flag="--defaultno" + [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" - _ipv6_method="$result" - case "$result" in - static) - local ipv6_addr - if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "STATIC IPv6 ADDRESS" \ - --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - _ipv6_addr="$ipv6_addr" - # Optional gateway - _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "IPv6 GATEWAY" \ - --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ - 3>&1 1>&2 2>&3) || true - ((STEP++)) - else - whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 - fi - fi - ;; - dhcp) - _ipv6_addr="dhcp" - _ipv6_gate="" - ((STEP++)) - ;; - disable) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - none) - _ipv6_addr="none" - _ipv6_gate="" - ((STEP++)) - ;; - *) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - esac - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "FUSE SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $fuse_default_flag \ + --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then + _enable_fuse="yes" + else + if [ $? -eq 1 ]; then + _enable_fuse="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 11: MTU Size - # ═══════════════════════════════════════════════════════════════════════════ - 11) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MTU SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mtu="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 19: TUN/TAP Support + # ═══════════════════════════════════════════════════════════════════════════ + 19) + local tun_default_flag="--defaultno" + [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 12: DNS Search Domain - # ═══════════════════════════════════════════════════════════════════════════ - 12) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SEARCH DOMAIN" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _sd="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "TUN/TAP SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $tun_default_flag \ + --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then + _enable_tun="yes" + else + if [ $? -eq 1 ]; then + _enable_tun="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 13: DNS Server - # ═══════════════════════════════════════════════════════════════════════════ - 13) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SERVER" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _ns="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 20: Nesting Support + # ═══════════════════════════════════════════════════════════════════════════ + 20) + local nesting_default_flag="" + [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 14: MAC Address - # ═══════════════════════════════════════════════════════════════════════════ - 14) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MAC ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mac="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NESTING SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $nesting_default_flag \ + --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then + _enable_nesting="1" + else + if [ $? -eq 1 ]; then + _enable_nesting="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 15: VLAN Tag - # ═══════════════════════════════════════════════════════════════════════════ - 15) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VLAN TAG" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _vlan="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 21: GPU Passthrough + # ═══════════════════════════════════════════════════════════════════════════ + 21) + local gpu_default_flag="--defaultno" + [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 16: Tags - # ═══════════════════════════════════════════════════════════════════════════ - 16) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TAGS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ - 3>&1 1>&2 2>&3); then - _tags="${result:-;}" - _tags=$(echo "$_tags" | tr -d '[:space:]') - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GPU PASSTHROUGH" \ + --ok-button "Next" --cancel-button "Back" \ + $gpu_default_flag \ + --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then + _enable_gpu="yes" + else + if [ $? -eq 1 ]; then + _enable_gpu="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 17: SSH Settings - # ═══════════════════════════════════════════════════════════════════════════ - 17) - configure_ssh_settings "Step $STEP/$MAX_STEP" - # configure_ssh_settings handles its own flow, always advance - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 22: Keyctl Support (Docker/systemd) + # ═══════════════════════════════════════════════════════════════════════════ + 22) + local keyctl_default_flag="--defaultno" + [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 18: FUSE Support - # ═══════════════════════════════════════════════════════════════════════════ - 18) - local fuse_default_flag="--defaultno" - [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "KEYCTL SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $keyctl_default_flag \ + --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then + _enable_keyctl="1" + else + if [ $? -eq 1 ]; then + _enable_keyctl="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "FUSE SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $fuse_default_flag \ - --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then - _enable_fuse="yes" - else - if [ $? -eq 1 ]; then - _enable_fuse="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 23: APT Cacher Proxy + # ═══════════════════════════════════════════════════════════════════════════ + 23) + local apt_cacher_default_flag="--defaultno" + [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 19: TUN/TAP Support - # ═══════════════════════════════════════════════════════════════════════════ - 19) - local tun_default_flag="--defaultno" - [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER PROXY" \ + --ok-button "Next" --cancel-button "Back" \ + $apt_cacher_default_flag \ + --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then + _apt_cacher="yes" + # Ask for IP if enabled + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER IP" \ + --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ + 3>&1 1>&2 2>&3); then + _apt_cacher_ip="$result" + fi + else + if [ $? -eq 1 ]; then + _apt_cacher="no" + _apt_cacher_ip="" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "TUN/TAP SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $tun_default_flag \ - --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then - _enable_tun="yes" - else - if [ $? -eq 1 ]; then - _enable_tun="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 24: Container Timezone + # ═══════════════════════════════════════════════════════════════════════════ + 24) + local tz_hint="$_ct_timezone" + [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 20: Nesting Support - # ═══════════════════════════════════════════════════════════════════════════ - 20) - local nesting_default_flag="" - [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TIMEZONE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ + 3>&1 1>&2 2>&3); then + _ct_timezone="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NESTING SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $nesting_default_flag \ - --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then - _enable_nesting="1" - else - if [ $? -eq 1 ]; then - _enable_nesting="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 25: Container Protection + # ═══════════════════════════════════════════════════════════════════════════ + 25) + local protect_default_flag="--defaultno" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 21: GPU Passthrough - # ═══════════════════════════════════════════════════════════════════════════ - 21) - local gpu_default_flag="--defaultno" - [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER PROTECTION" \ + --ok-button "Next" --cancel-button "Back" \ + $protect_default_flag \ + --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then + _protect_ct="yes" + else + if [ $? -eq 1 ]; then + _protect_ct="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GPU PASSTHROUGH" \ - --ok-button "Next" --cancel-button "Back" \ - $gpu_default_flag \ - --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then - _enable_gpu="yes" - else - if [ $? -eq 1 ]; then - _enable_gpu="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 26: Device Node Creation (mknod) + # ═══════════════════════════════════════════════════════════════════════════ + 26) + local mknod_default_flag="--defaultno" + [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 22: Keyctl Support (Docker/systemd) - # ═══════════════════════════════════════════════════════════════════════════ - 22) - local keyctl_default_flag="--defaultno" - [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DEVICE NODE CREATION" \ + --ok-button "Next" --cancel-button "Back" \ + $mknod_default_flag \ + --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then + _enable_mknod="1" + else + if [ $? -eq 1 ]; then + _enable_mknod="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "KEYCTL SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $keyctl_default_flag \ - --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then - _enable_keyctl="1" - else - if [ $? -eq 1 ]; then - _enable_keyctl="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 27: Mount Filesystems + # ═══════════════════════════════════════════════════════════════════════════ + 27) + local mount_hint="" + [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 23: APT Cacher Proxy - # ═══════════════════════════════════════════════════════════════════════════ - 23) - local apt_cacher_default_flag="--defaultno" - [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MOUNT FILESYSTEMS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ + 3>&1 1>&2 2>&3); then + _mount_fs="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER PROXY" \ - --ok-button "Next" --cancel-button "Back" \ - $apt_cacher_default_flag \ - --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then - _apt_cacher="yes" - # Ask for IP if enabled - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER IP" \ - --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ - 3>&1 1>&2 2>&3); then - _apt_cacher_ip="$result" - fi - else - if [ $? -eq 1 ]; then - _apt_cacher="no" - _apt_cacher_ip="" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 28: Verbose Mode & Confirmation + # ═══════════════════════════════════════════════════════════════════════════ + 28) + local verbose_default_flag="--defaultno" + [[ "$_verbose" == "yes" ]] && verbose_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 24: Container Timezone - # ═══════════════════════════════════════════════════════════════════════════ - 24) - local tz_hint="$_ct_timezone" - [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VERBOSE MODE" \ + $verbose_default_flag \ + --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then + _verbose="yes" + else + _verbose="no" + fi + # Build summary + local ct_type_desc="Unprivileged" + [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TIMEZONE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ - 3>&1 1>&2 2>&3); then - _ct_timezone="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + local nesting_desc="Disabled" + [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 25: Container Protection - # ═══════════════════════════════════════════════════════════════════════════ - 25) - local protect_default_flag="--defaultno" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" + local keyctl_desc="Disabled" + [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER PROTECTION" \ - --ok-button "Next" --cancel-button "Back" \ - $protect_default_flag \ - --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then - _protect_ct="yes" - else - if [ $? -eq 1 ]; then - _protect_ct="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + local protect_desc="No" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 26: Device Node Creation (mknod) - # ═══════════════════════════════════════════════════════════════════════════ - 26) - local mknod_default_flag="--defaultno" - [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" + local tz_display="${_ct_timezone:-Host TZ}" + local apt_display="${_apt_cacher:-no}" + [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DEVICE NODE CREATION" \ - --ok-button "Next" --cancel-button "Back" \ - $mknod_default_flag \ - --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then - _enable_mknod="1" - else - if [ $? -eq 1 ]; then - _enable_mknod="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 27: Mount Filesystems - # ═══════════════════════════════════════════════════════════════════════════ - 27) - local mount_hint="" - [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" - - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MOUNT FILESYSTEMS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ - 3>&1 1>&2 2>&3); then - _mount_fs="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 28: Verbose Mode & Confirmation - # ═══════════════════════════════════════════════════════════════════════════ - 28) - local verbose_default_flag="--defaultno" - [[ "$_verbose" == "yes" ]] && verbose_default_flag="" - - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VERBOSE MODE" \ - $verbose_default_flag \ - --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then - _verbose="yes" - else - _verbose="no" - fi - # Build summary - local ct_type_desc="Unprivileged" - [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" - - local nesting_desc="Disabled" - [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" - - local keyctl_desc="Disabled" - [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" - - local protect_desc="No" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" - - local tz_display="${_ct_timezone:-Host TZ}" - local apt_display="${_apt_cacher:-no}" - [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" - - local summary="Container Type: $ct_type_desc + local summary="Container Type: $ct_type_desc Container ID: $_ct_id Hostname: $_hostname @@ -2021,105 +2021,105 @@ Advanced: APT Cacher: $apt_display Verbose: $_verbose" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONFIRM SETTINGS" \ - --ok-button "Create LXC" --cancel-button "Back" \ - --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then - ((STEP++)) - else - ((STEP--)) - fi - ;; - esac - done + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONFIRM SETTINGS" \ + --ok-button "Create LXC" --cancel-button "Back" \ + --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then + ((STEP++)) + else + ((STEP--)) + fi + ;; + esac + done - # ═══════════════════════════════════════════════════════════════════════════ - # Apply all collected values to global variables - # ═══════════════════════════════════════════════════════════════════════════ - CT_TYPE="$_ct_type" - PW="$_pw" - CT_ID="$_ct_id" - HN="$_hostname" - DISK_SIZE="$_disk_size" - CORE_COUNT="$_core_count" - RAM_SIZE="$_ram_size" - BRG="$_bridge" - NET="$_net" - GATE="$_gate" - IPV6_METHOD="$_ipv6_method" - IPV6_ADDR="$_ipv6_addr" - IPV6_GATE="$_ipv6_gate" - TAGS="$_tags" - ENABLE_FUSE="$_enable_fuse" - ENABLE_TUN="$_enable_tun" - ENABLE_GPU="$_enable_gpu" - ENABLE_NESTING="$_enable_nesting" - ENABLE_KEYCTL="$_enable_keyctl" - ENABLE_MKNOD="$_enable_mknod" - ALLOW_MOUNT_FS="$_mount_fs" - PROTECT_CT="$_protect_ct" - CT_TIMEZONE="$_ct_timezone" - APT_CACHER="$_apt_cacher" - APT_CACHER_IP="$_apt_cacher_ip" - VERBOSE="$_verbose" + # ═══════════════════════════════════════════════════════════════════════════ + # Apply all collected values to global variables + # ═══════════════════════════════════════════════════════════════════════════ + CT_TYPE="$_ct_type" + PW="$_pw" + CT_ID="$_ct_id" + HN="$_hostname" + DISK_SIZE="$_disk_size" + CORE_COUNT="$_core_count" + RAM_SIZE="$_ram_size" + BRG="$_bridge" + NET="$_net" + GATE="$_gate" + IPV6_METHOD="$_ipv6_method" + IPV6_ADDR="$_ipv6_addr" + IPV6_GATE="$_ipv6_gate" + TAGS="$_tags" + ENABLE_FUSE="$_enable_fuse" + ENABLE_TUN="$_enable_tun" + ENABLE_GPU="$_enable_gpu" + ENABLE_NESTING="$_enable_nesting" + ENABLE_KEYCTL="$_enable_keyctl" + ENABLE_MKNOD="$_enable_mknod" + ALLOW_MOUNT_FS="$_mount_fs" + PROTECT_CT="$_protect_ct" + CT_TIMEZONE="$_ct_timezone" + APT_CACHER="$_apt_cacher" + APT_CACHER_IP="$_apt_cacher_ip" + VERBOSE="$_verbose" - # Update var_* based on user choice (for functions that check these) - var_gpu="$_enable_gpu" - var_fuse="$_enable_fuse" - var_tun="$_enable_tun" - var_nesting="$_enable_nesting" - var_keyctl="$_enable_keyctl" - var_mknod="$_enable_mknod" - var_mount_fs="$_mount_fs" - var_protection="$_protect_ct" - var_timezone="$_ct_timezone" - var_apt_cacher="$_apt_cacher" - var_apt_cacher_ip="$_apt_cacher_ip" + # Update var_* based on user choice (for functions that check these) + var_gpu="$_enable_gpu" + var_fuse="$_enable_fuse" + var_tun="$_enable_tun" + var_nesting="$_enable_nesting" + var_keyctl="$_enable_keyctl" + var_mknod="$_enable_mknod" + var_mount_fs="$_mount_fs" + var_protection="$_protect_ct" + var_timezone="$_ct_timezone" + var_apt_cacher="$_apt_cacher" + var_apt_cacher_ip="$_apt_cacher_ip" - # Format optional values - [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" - [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" - [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" - [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" - [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" + # Format optional values + [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" + [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" + [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" + [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" + [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" - # Alpine UDHCPC fix - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - export SSH_KEYS_FILE + # Alpine UDHCPC fix + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + export SSH_KEYS_FILE - # Exit alternate screen buffer BEFORE displaying summary - # so the summary is visible in the main terminal - tput rmcup 2>/dev/null || true - trap - RETURN # Remove the trap since we already called rmcup + # Exit alternate screen buffer BEFORE displaying summary + # so the summary is visible in the main terminal + tput rmcup 2>/dev/null || true + trap - RETURN # Remove the trap since we already called rmcup - # Display final summary - echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" - echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" - [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" - echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" - [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" - [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" - [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + # Display final summary + echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" + echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" + [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" + echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" + [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" + [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" + [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" } # ============================================================================== @@ -2137,13 +2137,13 @@ Advanced: # - Sets global DIAGNOSTICS variable for API telemetry opt-in/out # ------------------------------------------------------------------------------ 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. @@ -2166,9 +2166,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. @@ -2191,34 +2191,34 @@ 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 } 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 + 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 } # ------------------------------------------------------------------------------ @@ -2229,25 +2229,25 @@ diagnostics_menu() { # - 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 [ "${var_gpu:-no}" == "yes" ]; then - echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" - fi - 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 " " + 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 [ "${var_gpu:-no}" == "yes" ]; then + echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" + fi + 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 " " } # ------------------------------------------------------------------------------ @@ -2259,196 +2259,196 @@ echo_default() { # - Applies chosen settings and triggers container build # ------------------------------------------------------------------------------ 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) + + # Get timezone using timedatectl (Debian 13+ compatible) + # Fallback to /etc/timezone for older systems + if command -v timedatectl >/dev/null 2>&1; then + timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") + elif [ -f /etc/timezone ]; then + timezone=$(cat /etc/timezone) + else + timezone="UTC" + fi + + # 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" "User 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 + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # Main menu loop - allows returning from Settings + while true; do + 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 - NEXTID=$(pvesh get /cluster/nextid) + # --- 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)" + break + ;; + 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" + break + ;; + 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + break + ;; + "$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)" + break + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + # After settings menu, show main menu again + header_info + CHOICE="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + done - # Get timezone using timedatectl (Debian 13+ compatible) - # Fallback to /etc/timezone for older systems - if command -v timedatectl >/dev/null 2>&1; then - timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") - elif [ -f /etc/timezone ]; then - timezone=$(cat /etc/timezone) - else - timezone="UTC" - fi + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi - # 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" "User 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 - - APPDEFAULTS_OPTION="$appdefaults_option" - SETTINGS_OPTION="$settings_option" - - # Main menu loop - allows returning from Settings - while true; do - 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 - - # --- 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)" - break - ;; - 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" - break - ;; - 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - defaults_target="/usr/local/community-scripts/default.vars" - break - ;; - "$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)" - break - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - "$SETTINGS_OPTION" | settings | SETTINGS) - settings_menu - # After settings menu, show main menu again - header_info - CHOICE="" - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac - done - - 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 + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi } edit_default_storage() { - local vf="/usr/local/community-scripts/default.vars" + local vf="/usr/local/community-scripts/default.vars" - # Ensure file exists - if [[ ! -f "$vf" ]]; then - mkdir -p "$(dirname "$vf")" - touch "$vf" - fi + # 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" + # 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" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("3" "Edit App.vars for ${APP}") - settings_items+=("4" "Back to Main Menu") - else - settings_items+=("3" "Back to Main Menu") - fi + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("3" "Edit App.vars for ${APP}") + settings_items+=("4" "Back to Main Menu") + else + settings_items+=("3" "Back to Main Menu") + fi - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || exit_script + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || exit_script - case "$choice" in - 1) diagnostics_menu ;; - 2) nano /usr/local/community-scripts/default.vars ;; - 3) - if [ -f "$(get_app_defaults_path)" ]; then - nano "$(get_app_defaults_path)" - else - # Back was selected (no app.vars available) - return - fi - ;; - 4) - # Back to main menu - return - ;; - esac - done + case "$choice" in + 1) diagnostics_menu ;; + 2) nano /usr/local/community-scripts/default.vars ;; + 3) + if [ -f "$(get_app_defaults_path)" ]; then + nano "$(get_app_defaults_path)" + else + # Back was selected (no app.vars available) + return + fi + ;; + 4) + # Back to main menu + return + ;; + esac + done } # ------------------------------------------------------------------------------ @@ -2458,21 +2458,21 @@ settings_menu() { # - 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) + 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 "" + 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 } # ------------------------------------------------------------------------------ @@ -2482,18 +2482,18 @@ check_container_resources() { # - 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 + 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 } # ------------------------------------------------------------------------------ @@ -2503,9 +2503,9 @@ check_container_storage() { # - 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 ' + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' /^[[:space:]]*#/ {next} /^[[:space:]]*$/ {next} # nackt: typ base64 [comment] @@ -2525,45 +2525,45 @@ ssh_extract_keys_from_file() { # - 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 + 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 + 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 + # 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}..." + 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 + 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 } # ------------------------------------------------------------------------------ @@ -2573,105 +2573,105 @@ ssh_build_choices_from_files() { # - 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[@]}" + 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() { - local step_info="${1:-}" - local backtitle="[dev] Proxmox VE Helper Scripts" - [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" + local step_info="${1:-}" + local backtitle="[dev] Proxmox VE Helper Scripts" + [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" + 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" + 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 "$backtitle" --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 "$backtitle" --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 + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "$backtitle" --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 "$backtitle" --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 "$backtitle" --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 + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "$backtitle" --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 "$backtitle" \ + --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 "$backtitle" \ + --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 "$backtitle" --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 - ;; - manual) - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "$backtitle" \ - --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 "$backtitle" \ - --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 "$backtitle" --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 "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 - fi - else - whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 - fi + done + else + whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 fi - ;; - none) - : - ;; - esac - - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" + else + whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 + fi fi + ;; + none) + : + ;; + esac - # Always show SSH access dialog - user should be able to enable SSH even without keys - if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + # Always show SSH access dialog - user should be able to enable SSH even without keys + if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi } # ------------------------------------------------------------------------------ @@ -2683,39 +2683,39 @@ configure_ssh_settings() { # - 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) + 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 + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi } # ============================================================================== @@ -2736,256 +2736,256 @@ start() { # - Posts installation telemetry to API if diagnostics enabled # ------------------------------------------------------------------------------ build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + 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) ;; + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; esac + fi - # Build FEATURES string based on container type and user choices - FEATURES="" + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" - # Nesting support (user configurable, default enabled) - if [ "${ENABLE_NESTING:-1}" == "1" ]; then - FEATURES="nesting=1" - fi + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi - # Keyctl for unprivileged containers (needed for Docker) - if [ "$CT_TYPE" == "1" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}keyctl=1" - fi + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi - if [ "$ENABLE_FUSE" == "yes" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}fuse=1" - fi + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi - # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly - # Key insight: Bash cannot export arrays, so we build the options as a string + # 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 - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null + # Build FEATURES string based on container type and user choices + FEATURES="" - # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + # Nesting support (user configurable, default enabled) + if [ "${ENABLE_NESTING:-1}" == "1" ]; then + FEATURES="nesting=1" + fi - # Core exports for install.func - 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" - 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" + # Keyctl for unprivileged containers (needed for Docker) + if [ "$CT_TYPE" == "1" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}keyctl=1" + fi - # DEV_MODE exports (optional, for debugging) - export BUILD_LOG="$BUILD_LOG" - export INSTALL_LOG="/root/.install-${SESSION_ID}.log" - export dev_mode="${dev_mode:-}" - export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" - export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" - export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" - export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" - export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" - export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" - export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" + if [ "$ENABLE_FUSE" == "yes" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}fuse=1" + fi - # Build PCT_OPTIONS as multi-line string - PCT_OPTIONS_STRING=" -features $FEATURES + # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly + # Key insight: Bash cannot export arrays, so we build the options as a string + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + + # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + + # Core exports for install.func + 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" + 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" + + # DEV_MODE exports (optional, for debugging) + export BUILD_LOG="$BUILD_LOG" + export INSTALL_LOG="/root/.install-${SESSION_ID}.log" + export dev_mode="${dev_mode:-}" + export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" + export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" + export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" + export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" + export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" + export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" + export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" + + # Build PCT_OPTIONS as multi-line string + PCT_OPTIONS_STRING=" -features $FEATURES -hostname $HN -tags $TAGS" - # Add storage if specified - if [ -n "$SD" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING - $SD" - fi - - # Add nameserver if specified - if [ -n "$NS" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING - $NS" - fi - - # Network configuration + # Add storage if specified + if [ -n "$SD" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + $SD" + fi + + # Add nameserver if specified + if [ -n "$NS" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + $NS" + fi + + # Network configuration + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NET_STRING -onboot 1 -cores $CORE_COUNT -memory $RAM_SIZE -unprivileged $CT_TYPE" - # Protection flag (if var_protection was set) - if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Protection flag (if var_protection was set) + if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -protection 1" - fi + fi - # Timezone flag (if var_timezone was set) - if [ -n "${CT_TIMEZONE:-}" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Timezone flag (if var_timezone was set) + if [ -n "${CT_TIMEZONE:-}" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -timezone $CT_TIMEZONE" - fi + fi - # Password (already formatted) - if [ -n "$PW" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Password (already formatted) + if [ -n "$PW" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $PW" + fi + + # Export as string (this works, unlike arrays!) + export PCT_OPTIONS="$PCT_OPTIONS_STRING" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + + # # DEBUG: Show final PCT_OPTIONS being exported + # echo "[DEBUG] PCT_OPTIONS to be exported:" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Calling create_lxc_container..." + + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # Check if GPU passthrough is enabled + # Returns true only if var_gpu is explicitly set to "yes" + # Can be set via: + # - Environment variable: var_gpu=yes bash -c "..." + # - CT script default: var_gpu="${var_gpu:-no}" + # - Advanced settings wizard + # - App defaults file: /usr/local/community-scripts/defaults/.vars + is_gpu_app() { + [[ "${var_gpu:-no}" == "yes" ]] && return 0 + 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_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") + done + fi fi - # Export as string (this works, unlike arrays!) - export PCT_OPTIONS="$PCT_OPTIONS_STRING" - export TEMPLATE_STORAGE="${var_template_storage:-}" - export CONTAINER_STORAGE="${var_container_storage:-}" - - # # DEBUG: Show final PCT_OPTIONS being exported - # echo "[DEBUG] PCT_OPTIONS to be exported:" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Calling create_lxc_container..." - - create_lxc_container || exit $? - - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - - # ============================================================================ - # GPU/USB PASSTHROUGH CONFIGURATION - # ============================================================================ - - # Check if GPU passthrough is enabled - # Returns true only if var_gpu is explicitly set to "yes" - # Can be set via: - # - Environment variable: var_gpu=yes bash -c "..." - # - CT script default: var_gpu="${var_gpu:-no}" - # - Advanced settings wizard - # - App defaults file: /usr/local/community-scripts/defaults/.vars - is_gpu_app() { - [[ "${var_gpu:-no}" == "yes" ]] && return 0 - 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_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") - done - 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_custom "🎮" "${RD}" "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 AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) - if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then - 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 - 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_custom "🎮" "${GN}" "Detected NVIDIA GPU" - # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] - if echo "$pci_vga_info" | grep -q "\[10de:"; then - msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" + # Simple passthrough - just bind /dev/nvidia* devices if they exist + # Skip directories like /dev/nvidia-caps (they need special handling) + for d in /dev/nvidia*; do + [[ -e "$d" ]] || continue + [[ -d "$d" ]] && continue # Skip directories + NVIDIA_DEVICES+=("$d") + done - # Simple passthrough - just bind /dev/nvidia* devices if they exist - # Skip directories like /dev/nvidia-caps (they need special handling) - for d in /dev/nvidia*; do - [[ -e "$d" ]] || continue - [[ -d "$d" ]] && continue # Skip directories - NVIDIA_DEVICES+=("$d") - done + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" + fi + fi - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" - 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() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi - # 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" + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" # Automatic USB passthrough (privileged container) lxc.cgroup2.devices.allow: a lxc.cap.drop: @@ -2997,454 +2997,454 @@ 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 - msg_ok "USB passthrough configured" - } + msg_ok "USB passthrough configured" + } - # Configure GPU passthrough - configure_gpu_passthrough() { - # Skip if: - # GPU passthrough is enabled when var_gpu="yes": - # - Set via environment variable: var_gpu=yes bash -c "..." - # - Set in CT script: var_gpu="${var_gpu:-no}" - # - Enabled in advanced_settings wizard - # - Configured in app defaults file - if ! is_gpu_app "$APP"; then - return 0 - fi + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if: + # GPU passthrough is enabled when var_gpu="yes": + # - Set via environment variable: var_gpu=yes bash -c "..." + # - Set in CT script: var_gpu="${var_gpu:-no}" + # - Enabled in advanced_settings wizard + # - Configured in app defaults file + if ! is_gpu_app "$APP"; then + return 0 + fi - detect_gpu_devices + detect_gpu_devices - # Count available GPU types - local gpu_count=0 - local available_gpus=() + # 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 [[ ${#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 [[ ${#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 [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi - if [[ $gpu_count -eq 0 ]]; then - msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" - return 0 - fi + if [[ $gpu_count -eq 0 ]]; then + msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" + return 0 + fi - local selected_gpu="" + local selected_gpu="" - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_ok "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^^}" + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_ok "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 + # 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 + 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 + # 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[@]}") + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - # Use pct set to add devices with proper dev0/dev1 format - # GIDs will be detected and set after container starts - local dev_index=0 - for dev in "${devices[@]}"; do - # Add to config using pct set (will be visible in GUI) - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set to add devices with proper dev0/dev1 format + # GIDs will be detected and set after container starts + local dev_index=0 + for dev in "${devices[@]}"; do + # Add to config using pct set (will be visible in GUI) + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" - ;; + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" + ;; - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_warn "No NVIDIA devices available for passthrough" - return 0 - fi + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "No NVIDIA devices available for passthrough" + return 0 + fi - # Use pct set for NVIDIA devices - local dev_index=0 - for dev in "${NVIDIA_DEVICES[@]}"; do - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set for NVIDIA devices + local dev_index=0 + for dev in "${NVIDIA_DEVICES[@]}"; do + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" - ;; - esac - } + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" + ;; + esac + } - # Additional device passthrough - configure_additional_devices() { - # 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 - - # Coral TPU passthrough - if [[ -e /dev/apex_0 ]]; then - 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 - } - - # 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 assignment (IPv4 or IPv6) - local ip_in_lxc="" - for i in {1..20}; do - # Try IPv4 first - ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - # Fallback to IPv6 if IPv4 not available - if [ -z "$ip_in_lxc" ]; then - ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) - fi - [ -n "$ip_in_lxc" ] && break - sleep 1 - done - - if [ -z "$ip_in_lxc" ]; then - msg_error "No IP assigned to CT $CTID after 20s" - echo -e "${YW}Troubleshooting:${CL}" - echo " • Verify bridge ${BRG} exists and has connectivity" - echo " • Check if DHCP server is reachable (if using DHCP)" - echo " • Verify static IP configuration (if using static IP)" - echo " • Check Proxmox firewall rules" - echo " • If using Tailscale: Disable MagicDNS temporarily" - exit 1 - fi - - # Verify basic connectivity (ping test) - local ping_success=false - for retry in {1..3}; do - if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || - pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || - pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then - ping_success=true - break - fi - sleep 2 - done - - if [ "$ping_success" = false ]; then - msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" - echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" - else - msg_ok "Network in LXC is reachable (ping)" - 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 - } + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + 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 + } - fix_gpu_gids + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices - # Continue with standard container setup - msg_info "Customizing LXC Container" + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ - # # Install GPU userland if configured - # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - # install_gpu_userland "VAAPI" - # fi + msg_info "Starting LXC Container" + pct start "$CTID" - # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - # install_gpu_userland "NVIDIA" - # fi + # 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 - # Continue with standard container setup - install core dependencies based on OS - sleep 3 + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" - case "$var_os" in - alpine) - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories + # Wait for IP assignment (IPv4 or IPv6) + local ip_in_lxc="" + for i in {1..20}; do + # Try IPv4 first + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + # Fallback to IPv6 if IPv4 not available + if [ -z "$ip_in_lxc" ]; then + ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) + fi + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + echo -e "${YW}Troubleshooting:${CL}" + echo " • Verify bridge ${BRG} exists and has connectivity" + echo " • Check if DHCP server is reachable (if using DHCP)" + echo " • Verify static IP configuration (if using static IP)" + echo " • Check Proxmox firewall rules" + echo " • If using Tailscale: Disable MagicDNS temporarily" + exit 1 + fi + + # Verify basic connectivity (ping test) + local ping_success=false + for retry in {1..3}; do + if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || + pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || + pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then + ping_success=true + break + fi + sleep 2 + done + + if [ "$ping_success" = false ]; then + msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" + echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" + else + msg_ok "Network in LXC is reachable (ping)" + 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 - install core dependencies based on OS + sleep 3 + + case "$var_os" in + alpine) + 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" - ;; + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + ;; - debian | ubuntu | devuan) - # Locale setup for Debian-based - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + debian | ubuntu | devuan) + # Locale setup for Debian-based + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ [[ -n \"\$locale_line\" ]] && echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null 2>&1 && \ export LANG=\$locale_line || true" - # Timezone setup - 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" - fi - - # Core dependencies - 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 - } - ;; - - fedora | rockylinux | almalinux | centos) - # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq procps-ng >/dev/null 2>&1 || yum install -y curl sudo mc jq procps-ng >/dev/null 2>&1" || { - msg_error "dnf/yum base packages installation failed" - exit 1 - } - ;; - - opensuse) - # openSUSE - pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq >/dev/null" || { - msg_error "zypper base packages installation failed" - exit 1 - } - ;; - - gentoo) - # Gentoo - emerge is slow, only install essentials - pct exec "$CTID" -- bash -c "emerge --quiet app-misc/jq net-misc/curl app-misc/mc >/dev/null 2>&1" || { - msg_warn "Gentoo base packages installation incomplete - may need manual setup" - } - ;; - - openeuler) - # openEuler (RHEL-compatible) - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq >/dev/null" || { - msg_error "dnf base packages installation failed" - exit 1 - } - ;; - - *) - msg_warn "Unknown OS '$var_os' - skipping core dependency installation" - ;; - esac - - msg_ok "Customized LXC Container" - - # Install SSH keys - install_ssh_keys_into_ct - - # Run application installer - # NOTE: We disable error handling here because: - # 1. Container errors are caught by error_handler INSIDE container - # 2. Container creates flag file with exit code - # 3. We read flag file and handle cleanup manually below - # 4. We DON'T want host error_handler to fire for lxc-attach command itself - - set +Eeuo pipefail # Disable ALL error handling temporarily - trap - ERR # Remove ERR trap completely - - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" - local lxc_exit=$? - - set -Eeuo pipefail # Re-enable error handling - trap 'error_handler' ERR # Restore ERR trap - - # Check for error flag file in container (more reliable than lxc-attach exit code) - local install_exit_code=0 - if [[ -n "${SESSION_ID:-}" ]]; then - local error_flag="/root/.install-${SESSION_ID}.failed" - if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then - install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") - pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true - fi + # Timezone setup + 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi - # Fallback to lxc-attach exit code if no flag file - if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then - install_exit_code=$lxc_exit + # Core dependencies + 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 + } + ;; + + fedora | rockylinux | almalinux | centos) + # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS + pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq procps-ng >/dev/null 2>&1 || yum install -y curl sudo mc jq procps-ng >/dev/null 2>&1" || { + msg_error "dnf/yum base packages installation failed" + exit 1 + } + ;; + + opensuse) + # openSUSE + pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq >/dev/null" || { + msg_error "zypper base packages installation failed" + exit 1 + } + ;; + + gentoo) + # Gentoo - emerge is slow, only install essentials + pct exec "$CTID" -- bash -c "emerge --quiet app-misc/jq net-misc/curl app-misc/mc >/dev/null 2>&1" || { + msg_warn "Gentoo base packages installation incomplete - may need manual setup" + } + ;; + + openeuler) + # openEuler (RHEL-compatible) + pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq >/dev/null" || { + msg_error "dnf base packages installation failed" + exit 1 + } + ;; + + *) + msg_warn "Unknown OS '$var_os' - skipping core dependency installation" + ;; + esac + + msg_ok "Customized LXC Container" + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + # NOTE: We disable error handling here because: + # 1. Container errors are caught by error_handler INSIDE container + # 2. Container creates flag file with exit code + # 3. We read flag file and handle cleanup manually below + # 4. We DON'T want host error_handler to fire for lxc-attach command itself + + set +Eeuo pipefail # Disable ALL error handling temporarily + trap - ERR # Remove ERR trap completely + + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + local lxc_exit=$? + + set -Eeuo pipefail # Re-enable error handling + trap 'error_handler' ERR # Restore ERR trap + + # Check for error flag file in container (more reliable than lxc-attach exit code) + local install_exit_code=0 + if [[ -n "${SESSION_ID:-}" ]]; then + local error_flag="/root/.install-${SESSION_ID}.failed" + if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then + install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") + pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true + fi + fi + + # Fallback to lxc-attach exit code if no flag file + if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then + install_exit_code=$lxc_exit + fi + + # Installation failed? + if [[ $install_exit_code -ne 0 ]]; then + msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" + + # Copy both logs from container before potential deletion + local build_log_copied=false + local install_log_copied=false + + if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then + # Copy BUILD_LOG (creation log) if it exists + if [[ -f "${BUILD_LOG}" ]]; then + cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true + fi + + # Copy INSTALL_LOG from container + if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then + install_log_copied=true + fi + + # Show available logs + echo "" + [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" + [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" fi - # Installation failed? - if [[ $install_exit_code -ne 0 ]]; then - msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" + # Dev mode: Keep container or open breakpoint shell + if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then + msg_dev "Keep mode active - container ${CTID} preserved" + return 0 + elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then + msg_dev "Breakpoint mode - opening shell in container ${CTID}" + echo -e "${YW}Type 'exit' to return to host${CL}" + pct enter "$CTID" + echo "" + echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" + if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + msg_ok "Container ${CTID} removed" + else + msg_dev "Container ${CTID} kept for debugging" + fi + exit $install_exit_code + fi - # Copy both logs from container before potential deletion - local build_log_copied=false - local install_log_copied=false + # Report failure to API before container cleanup + post_update_to_api "failed" "$install_exit_code" - if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then - # Copy BUILD_LOG (creation log) if it exists - if [[ -f "${BUILD_LOG}" ]]; then - cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true - fi + # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) + echo "" + echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" - # Copy INSTALL_LOG from container - if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then - install_log_copied=true - fi + if read -t 60 -r response; then + if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then + # Remove container + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + elif [[ "$response" =~ ^[Nn]$ ]]; then + echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" - # Show available logs - echo "" - [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" - [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" - fi - - # Dev mode: Keep container or open breakpoint shell - if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then - msg_dev "Keep mode active - container ${CTID} preserved" - return 0 - elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then - msg_dev "Breakpoint mode - opening shell in container ${CTID}" - echo -e "${YW}Type 'exit' to return to host${CL}" - pct enter "$CTID" - echo "" - echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" - if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - msg_ok "Container ${CTID} removed" - else - msg_dev "Container ${CTID} kept for debugging" - fi - exit $install_exit_code - fi - - # Report failure to API before container cleanup - post_update_to_api "failed" "$install_exit_code" - - # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) - echo "" - echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" - - if read -t 60 -r response; then - if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then - # Remove container - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - elif [[ "$response" =~ ^[Nn]$ ]]; then - echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" - - # Dev mode: Setup MOTD/SSH for debugging access to broken container - if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then - echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" - if pct exec "$CTID" -- bash -c " + # Dev mode: Setup MOTD/SSH for debugging access to broken container + if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then + echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" + if pct exec "$CTID" -- bash -c " source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then - local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" - fi - fi - fi - else - # Timeout - auto-remove - echo -e "\n${YW}No response - auto-removing container${CL}" - echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" + fi fi - - exit $install_exit_code + fi + else + # Timeout - auto-remove + echo -e "\n${YW}No response - auto-removing container${CL}" + echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" fi + + exit $install_exit_code + fi } destroy_lxc() { - if [[ -z "$CT_ID" ]]; then - msg_error "No CT_ID found. Nothing to remove." - return 1 + 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 - - # 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_custom "ℹ️" "${BL}" "Container was not removed." - ;; - *) - msg_warn "Invalid response. Container was not removed." - ;; - esac + ;; + "" | n | no) + msg_custom "ℹ️" "${BL}" "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac } # ------------------------------------------------------------------------------ @@ -3452,81 +3452,81 @@ destroy_lxc() { # ------------------------------------------------------------------------------ # ===== 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 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" + 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 - 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 + STORAGE_INFO="Free: ${free} Used: ${used}" fi - STORAGE_RESULT="$preselect" - return 0 + fi + STORAGE_RESULT="$preselect" + return 0 } fix_gpu_gids() { - if [[ -z "${GPU_TYPE:-}" ]]; then - return 0 - fi + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi - msg_info "Detecting and setting correct GPU group IDs" + msg_info "Detecting and setting correct GPU group IDs" - # Get actual GIDs from 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") + # Get actual GIDs from 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") - # Create groups if they don't exist - if [[ -z "$video_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 - video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" - fi + # Create groups if they don't exist + if [[ -z "$video_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" + fi - if [[ -z "$render_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 - render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" - fi + if [[ -z "$render_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" + fi - # Stop container to update config - pct stop "$CTID" >/dev/null 2>&1 - sleep 1 + # Stop container to update config + pct stop "$CTID" >/dev/null 2>&1 + sleep 1 - # Update dev entries with correct GIDs - sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" - sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" + # Update dev entries with correct GIDs + sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" + sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" - # Restart container - pct start "$CTID" >/dev/null 2>&1 - sleep 2 + # Restart container + pct start "$CTID" >/dev/null 2>&1 + sleep 2 - msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" + msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" - # For privileged containers: also fix permissions inside container - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " + # For privileged containers: also fix permissions inside container + 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 @@ -3540,740 +3540,740 @@ fix_gpu_gids() { done fi " >/dev/null 2>&1 - fi + fi } 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 ]] + 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 + 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 + 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') + 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 + 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 - - 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 + 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 + # ------------------------------------------------------------------------------ + # 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}'; } + # ------------------------------------------------------------------------------ + # 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"; } + 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"; } + # 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 + # 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)" + _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)" + 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 $STD apt-get update && $STD 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 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 + } - 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 $STD apt-get update && $STD 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 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 + } - # ------------------------------------------------------------------------------ - # 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}" - 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 - # 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 + } - # 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 - # 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 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]" - 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 fi + fi - msg_info "Validating storage '$CONTAINER_STORAGE'" - STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) + msg_info "Validating storage '$CONTAINER_STORAGE'" + STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) - case "$STORAGE_TYPE" in - iscsidirect) exit 212 ;; - iscsi | zfs) exit 213 ;; - cephfs) exit 219 ;; - pbs) exit 224 ;; - linstor | rbd | nfs | cifs) - pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 - ;; - esac + case "$STORAGE_TYPE" in + iscsidirect) exit 212 ;; + iscsi | zfs) exit 213 ;; + cephfs) exit 219 ;; + pbs) exit 224 ;; + linstor | rbd | nfs | cifs) + pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 + ;; + esac - pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 - msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" + pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 + msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" - msg_info "Validating template storage '$TEMPLATE_STORAGE'" - TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) + msg_info "Validating template storage '$TEMPLATE_STORAGE'" + TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) - if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then - msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" + if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" + fi + msg_ok "Template storage '$TEMPLATE_STORAGE' validated" + + # 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 "Template storage '$TEMPLATE_STORAGE' validated" + msg_ok "Cluster is quorate" + 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 - } + # ------------------------------------------------------------------------------ + # Template discovery & validation + # Supported OS types (pveam available): alpine, almalinux, centos, debian, + # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | rockylinux | centos | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; + gentoo) TEMPLATE_PATTERN="-current-openrc" ;; + opensuse) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac - # 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 + msg_info "Searching for template '$TEMPLATE_SEARCH'" - # ------------------------------------------------------------------------------ - # Template discovery & validation - # Supported OS types (pveam available): alpine, almalinux, centos, debian, - # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu - # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - case "$PCT_OSTYPE" in - debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rocky | rockylinux | centos | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - gentoo) TEMPLATE_PATTERN="-current-openrc" ;; - opensuse) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="" ;; - esac + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" - msg_info "Searching for template '$TEMPLATE_SEARCH'" + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" - # 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="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) - #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" - #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" - #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - 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 + 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/^/ /' + + 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 + + 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 ) - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + 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 - msg_ok "Template search completed" + 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] 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/^/ /' + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" - 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 - - 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 + 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 [[ ${#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_custom "🚫" "${YW}" "Installation cancelled" - exit 0 - fi + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" + #echo "[DEBUG] Found alternative: $TEMPLATE" 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_custom "🚫" "${YW}" "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_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 - - 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." + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 fi + else + msg_custom "🚫" "${YW}" "Installation cancelled" + exit 0 + fi else - $STD msg_ok "Template $TEMPLATE is present and valid." + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 fi + 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_custom "ℹ️" "${BL}" "Continuing with local template $TEMPLATE" - fi - fi + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - 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)) + 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 - 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 + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice - # ------------------------------------------------------------------------------ - # 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 + 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}" - # ------------------------------------------------------------------------------ - # Create LXC Container - # ------------------------------------------------------------------------------ - msg_info "Creating LXC container" + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" - # 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 + 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]}" - # PCT_OPTIONS is now a string (exported from build_container) - # Add rootfs if not already specified - if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then - PCT_OPTIONS="$PCT_OPTIONS - -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" - fi + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi - # 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 - } + 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 - LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" + # 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 - # # DEBUG: Show the actual command that will be executed - # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" - # echo "[DEBUG] CTID: $CTID" - # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" - # echo "[DEBUG] PCT_OPTIONS (will be word-split):" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Full command line:" - # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - # echo "[DEBUG] ========================================" - - msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - msg_debug "Logfile: $LOGFILE" - - # First attempt (PCT_OPTIONS is a multi-line string, use it directly) - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then - msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 not already on local - if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_info "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 - # Local fallback also failed - check for LXC stack version issue - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - else - msg_ok "Container successfully created using local fallback." - fi - else - # Already on local storage and still failed - check LXC stack version - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } else - msg_ok "Container successfully created after template repair." + msg_custom "🚫" "${YW}" "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_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 + + 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_custom "ℹ️" "${BL}" "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 + + # PCT_OPTIONS is now a string (exported from build_container) + # Add rootfs if not already specified + if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then + PCT_OPTIONS="$PCT_OPTIONS + -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" + fi + + # 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}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" + + # # DEBUG: Show the actual command that will be executed + # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" + # echo "[DEBUG] CTID: $CTID" + # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" + # echo "[DEBUG] PCT_OPTIONS (will be word-split):" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Full command line:" + # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + # echo "[DEBUG] ========================================" + + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + msg_debug "Logfile: $LOGFILE" + + # First attempt (PCT_OPTIONS is a multi-line string, use it directly) + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then + msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 - # 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 - } + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then + # Fallback to local storage if not already on local + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_info "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 + # Local fallback also failed - check for LXC stack version issue + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + else + msg_ok "Container successfully created using local fallback." + fi + else + # Already on local storage and still failed - check LXC stack version + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_ok "Container successfully created after template repair." + fi + fi - # Verify config rootfs - grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { - msg_error "RootFS entry missing in container config. See $LOGFILE" - exit 216 - } + # 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 + } - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } - # Report container creation to API - post_to_api + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." + + # Report container creation to API + post_to_api } # ============================================================================== @@ -4293,11 +4293,11 @@ create_lxc_container() { # - Posts final "done" status to API telemetry # ------------------------------------------------------------------------------ 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 @@ -4325,14 +4325,14 @@ description() { EOF - ) - pct set "$CTID" -description "$DESCRIPTION" + ) + 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" } # ============================================================================== @@ -4349,14 +4349,14 @@ EOF # - Only executes on non-zero exit codes # ------------------------------------------------------------------------------ api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - post_update_to_api "failed" "$exit_code" - fi + exit_code=$? + if [ $exit_code -ne 0 ]; then + post_update_to_api "failed" "$exit_code" + 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 698a17089c0cd90907a46f41cd4a97f2ae2b6307 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 09:13:20 +0100 Subject: [PATCH 296/378] fixes --- misc/build.func | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index d3a9d312e..cbad2334c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -693,6 +693,13 @@ load_vars_file() { [[ "$var_key" != var_* ]] && continue _is_whitelisted "$var_key" || continue + # Strip inline comments (everything after unquoted #) + # Handle: var=value # comment OR var="value" # comment + if [[ ! "$var_val" =~ ^[\"\'] ]]; then + # Unquoted value: strip from first # + var_val="${var_val%%#*}" + fi + # Strip quotes if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then var_val="${BASH_REMATCH[1]}" @@ -700,6 +707,9 @@ load_vars_file() { var_val="${BASH_REMATCH[1]}" fi + # Trim trailing whitespace + var_val="${var_val%"${var_val##*[![:space:]]}"}" + # Set only if not already exported [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" fi @@ -802,12 +812,18 @@ var_fuse=no var_tun=no # Advanced Settings (Proxmox-official features) -var_nesting=1 # Allow nesting (required for Docker/LXC in CT) -var_keyctl=0 # Allow keyctl() - needed for Docker (systemd-networkd workaround) -var_mknod=0 # Allow device node creation (requires kernel 5.3+, experimental) -var_mount_fs= # Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) -var_protection=no # Prevent accidental deletion of container -var_timezone= # Container timezone (e.g. Europe/Berlin, leave empty for host timezone) +# var_nesting: Allow nesting (required for Docker/LXC in CT) +var_nesting=1 +# var_keyctl: Allow keyctl() - needed for Docker (systemd-networkd workaround) +var_keyctl=0 +# var_mknod: Allow device node creation (requires kernel 5.3+, experimental) +var_mknod=0 +# var_mount_fs: Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) +var_mount_fs= +# var_protection: Prevent accidental deletion of container +var_protection=no +# var_timezone: Container timezone (e.g. Europe/Berlin, leave empty for host timezone) +var_timezone= var_tags=community-script var_verbose=no From 79b8a934f2e8af80f5fdf2af97946bdaa9a2d58c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 09:44:09 +0100 Subject: [PATCH 297/378] fixes wishlist --- ct/wishlist.sh | 5 +-- frontend/public/json/wishlist.json | 64 +++++++++++++++--------------- install/wishlist-install.sh | 4 +- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/ct/wishlist.sh b/ct/wishlist.sh index 02154d795..e533463e3 100644 --- a/ct/wishlist.sh +++ b/ct/wishlist.sh @@ -7,8 +7,8 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Wishlist" var_tags="${var_tags:-sharing}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" @@ -44,7 +44,6 @@ function update_script() { msg_info "Updating Wishlist" cd /opt/wishlist - $STD pnpm install $STD pnpm svelte-kit sync $STD pnpm prisma generate diff --git a/frontend/public/json/wishlist.json b/frontend/public/json/wishlist.json index 3024b6e72..4d7f7f45b 100644 --- a/frontend/public/json/wishlist.json +++ b/frontend/public/json/wishlist.json @@ -1,33 +1,35 @@ { - "name": "Wishlist", - "slug": "wishlist", - "categories": [0], - "date_created": "2025-12-29", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3280, - "documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started", - "config_path": "/opt/wishlist/.env", - "website": "https://github.com/cmintey/wishlist", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp", - "description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!", - "install_methods": [ - { - "type": "default", - "script": "ct/wishlist.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 5, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] + "name": "Wishlist", + "slug": "wishlist", + "categories": [ + 0 + ], + "date_created": "2025-12-29", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3280, + "documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started", + "config_path": "/opt/wishlist/.env", + "website": "https://github.com/cmintey/wishlist", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp", + "description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!", + "install_methods": [ + { + "type": "default", + "script": "ct/wishlist.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 5, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index 3c5b9025d..5203d2731 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -24,11 +24,13 @@ msg_ok "Installed dependencies" NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist" false) +import_local_ip msg_info "Installing Wishlist" cd /opt/wishlist cp .env.example .env -echo "NODE_ENV=production" >> /opt/wishlist/.env +sed -i "s|^ORIGIN=.*|ORIGIN=http://${LOCAL_IP}:3000|" /opt/wishlist/.env +echo "NODE_ENV=production" >>/opt/wishlist/.env $STD pnpm install $STD pnpm svelte-kit sync $STD pnpm prisma generate From 13747b5768cebbb09d49abf28002e749e65ec92c Mon Sep 17 00:00:00 2001 From: lengschder97 Date: Mon, 12 Jan 2026 12:27:21 +0100 Subject: [PATCH 298/378] Added .json description --- frontend/public/json/forgejo-runner.json | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/forgejo-runner.json diff --git a/frontend/public/json/forgejo-runner.json b/frontend/public/json/forgejo-runner.json new file mode 100644 index 000000000..1ffd96d0b --- /dev/null +++ b/frontend/public/json/forgejo-runner.json @@ -0,0 +1,35 @@ +{ + "name": "Forgejo Runner", + "slug": "forgejo-runner", + "categories": [ + 20 + ], + "date_created": "2026-01-12", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://forgejo.org/docs/latest/admin/actions/runner-installation/", + "config_path": "/root/.runner", + "website": "https://forgejo.org/docs/latest/admin/actions/runner-installation/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/forgejo.webp", + "description": "Forgejo Runner is a lightweight service that executes CI/CD jobs for Forgejo, enabling automated builds, tests, and deployments.", + "install_methods": [ + { + "type": "default", + "script": "ct/forgejo-runner.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} \ No newline at end of file From 47b4ea5c40dd384e87197cececb4225350312f35 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 13:58:25 +0100 Subject: [PATCH 299/378] Improve locale and template handling for LXC builds Adds explicit locale package installation and locale setup for Debian, Ubuntu, Devuan, RHEL-based, openSUSE, and openEuler containers to ensure proper locale generation on minimal templates. Refines template pattern matching for CentOS Stream and Gentoo, and deduplicates openSUSE handling. Also, refactors misc/core.func for consistent indentation and minor logic improvements. --- misc/build.func | 50 ++- misc/core.func | 876 ++++++++++++++++++++++++------------------------ 2 files changed, 482 insertions(+), 444 deletions(-) diff --git a/misc/build.func b/misc/build.func index cbad2334c..f0b2ed10d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3246,6 +3246,9 @@ EOF' ;; debian | ubuntu | devuan) + # First install locales package (required for locale-gen on minimal templates) + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null 2>&1 || true" + # Locale setup for Debian-based pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ @@ -3272,18 +3275,33 @@ EOF' fedora | rockylinux | almalinux | centos) # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq procps-ng >/dev/null 2>&1 || yum install -y curl sudo mc jq procps-ng >/dev/null 2>&1" || { + # Install base packages including glibc-langpack for locale support + pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq procps-ng glibc-langpack-en >/dev/null 2>&1 || yum install -y curl sudo mc jq procps-ng >/dev/null 2>&1" || { msg_error "dnf/yum base packages installation failed" exit 1 } + + # Set locale for RHEL-based systems + pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true + + # Timezone setup for RHEL + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + [[ "${tz:-}" == Etc/* ]] && tz="UTC" + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "timedatectl set-timezone '$tz' 2>/dev/null || ln -sf '/usr/share/zoneinfo/$tz' /etc/localtime" || true + fi ;; opensuse) - # openSUSE - pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq >/dev/null" || { + # openSUSE - add locale package and setup + pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq glibc-locale >/dev/null" || { msg_error "zypper base packages installation failed" exit 1 } + # Set locale for openSUSE + pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true ;; gentoo) @@ -3295,10 +3313,23 @@ EOF' openeuler) # openEuler (RHEL-compatible) - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq >/dev/null" || { + # Note: openEuler may fail pct create due to missing /etc/redhat-release - this is a PVE limitation + pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq glibc-langpack-en >/dev/null" || { msg_error "dnf base packages installation failed" exit 1 } + # Set locale + pct exec "$CTID" -- bash -c "echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true + ;; + + opensuse) + # openSUSE - add locale package and setup + pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq glibc-locale >/dev/null" || { + msg_error "zypper base packages installation failed" + exit 1 + } + # Set locale for openSUSE + pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true ;; *) @@ -3845,12 +3876,19 @@ create_lxc_container() { # Template discovery & validation # Supported OS types (pveam available): alpine, almalinux, centos, debian, # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu + # Template naming conventions: + # - Debian/Ubuntu/Devuan: --standard__.tar.zst + # - Alpine/Fedora/Rocky/CentOS/AlmaLinux/openEuler: --default__.tar.xz + # - Gentoo: gentoo-current-openrc-_.tar.xz (version IS 'current') + # - openSUSE: opensuse--default__.tar.xz + # - CentOS: centos--stream-default__.tar.xz (note: stream in name) # ------------------------------------------------------------------------------ TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" case "$PCT_OSTYPE" in debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rocky | rockylinux | centos | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - gentoo) TEMPLATE_PATTERN="-current-openrc" ;; + alpine | fedora | rockylinux | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; + centos) TEMPLATE_PATTERN="-stream-default_" ;; + gentoo) TEMPLATE_PATTERN="-openrc-" ;; # Pattern after gentoo-current opensuse) TEMPLATE_PATTERN="-default_" ;; *) TEMPLATE_PATTERN="" ;; esac diff --git a/misc/core.func b/misc/core.func index 682fb1080..8c2bf008e 100644 --- a/misc/core.func +++ b/misc/core.func @@ -31,13 +31,13 @@ _CORE_FUNC_LOADED=1 # - Must be called at start of any script using these utilities # ------------------------------------------------------------------------------ load_functions() { - [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return - __FUNCTIONS_LOADED=1 - color - formatting - icons - default_vars - set_std_mode + [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return + __FUNCTIONS_LOADED=1 + color + formatting + icons + default_vars + set_std_mode } # ------------------------------------------------------------------------------ @@ -48,14 +48,14 @@ load_functions() { # GN (green), DGN (dark green), BGN (background green), CL (clear) # ------------------------------------------------------------------------------ color() { - YW=$(echo "\033[33m") - YWB=$'\e[93m' - 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") + YW=$(echo "\033[33m") + YWB=$'\e[93m' + 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") } # ------------------------------------------------------------------------------ @@ -67,9 +67,9 @@ color() { # - Used by spinner() function to avoid color conflicts # ------------------------------------------------------------------------------ color_spinner() { - CS_YW=$'\033[33m' - CS_YWB=$'\033[93m' - CS_CL=$'\033[m' + CS_YW=$'\033[33m' + CS_YWB=$'\033[93m' + CS_CL=$'\033[m' } # ------------------------------------------------------------------------------ @@ -81,11 +81,11 @@ color_spinner() { # - TAB/TAB3: Indentation spacing # ------------------------------------------------------------------------------ formatting() { - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - HOLD=" " - TAB=" " - TAB3=" " + BFR="\\r\\033[K" + BOLD=$(echo "\033[1m") + HOLD=" " + TAB=" " + TAB3=" " } # ------------------------------------------------------------------------------ @@ -96,34 +96,34 @@ formatting() { # - Icons: CM (checkmark), CROSS (error), INFO (info), HOURGLASS (wait), etc. # ------------------------------------------------------------------------------ icons() { - CM="${TAB}✔️${TAB}" - CROSS="${TAB}✖️${TAB}" - DNSOK="✔️ " - DNSFAIL="${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}" - ICON_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}" - HOURGLASS="${TAB}⏳${TAB}" + CM="${TAB}✔️${TAB}" + CROSS="${TAB}✖️${TAB}" + DNSOK="✔️ " + DNSFAIL="${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}" + ICON_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}" + HOURGLASS="${TAB}⏳${TAB}" } # ------------------------------------------------------------------------------ @@ -135,9 +135,9 @@ icons() { # - i: Counter variable initialized to RETRY_NUM # ------------------------------------------------------------------------------ default_vars() { - RETRY_NUM=10 - RETRY_EVERY=3 - i=$RETRY_NUM + RETRY_NUM=10 + RETRY_EVERY=3 + i=$RETRY_NUM } # ------------------------------------------------------------------------------ @@ -149,17 +149,17 @@ default_vars() { # - If DEV_MODE_TRACE=true: Enables bash tracing (set -x) # ------------------------------------------------------------------------------ set_std_mode() { - if [ "${VERBOSE:-no}" = "yes" ]; then - STD="" - else - STD="silent" - fi + if [ "${VERBOSE:-no}" = "yes" ]; then + STD="" + else + STD="silent" + fi - # Enable bash tracing if trace mode active - if [[ "${DEV_MODE_TRACE:-false}" == "true" ]]; then - set -x - export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' - fi + # Enable bash tracing if trace mode active + if [[ "${DEV_MODE_TRACE:-false}" == "true" ]]; then + set -x + export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + fi } # ------------------------------------------------------------------------------ @@ -177,57 +177,57 @@ set_std_mode() { # - Call this early in script execution # ------------------------------------------------------------------------------ parse_dev_mode() { - local mode - # Initialize all flags to false - export DEV_MODE_MOTD=false - export DEV_MODE_KEEP=false - export DEV_MODE_TRACE=false - export DEV_MODE_PAUSE=false - export DEV_MODE_BREAKPOINT=false - export DEV_MODE_LOGS=false - export DEV_MODE_DRYRUN=false + local mode + # Initialize all flags to false + export DEV_MODE_MOTD=false + export DEV_MODE_KEEP=false + export DEV_MODE_TRACE=false + export DEV_MODE_PAUSE=false + export DEV_MODE_BREAKPOINT=false + export DEV_MODE_LOGS=false + export DEV_MODE_DRYRUN=false - # Parse comma-separated modes - if [[ -n "${dev_mode:-}" ]]; then - IFS=',' read -ra MODES <<<"$dev_mode" - for mode in "${MODES[@]}"; do - mode="$(echo "$mode" | xargs)" # Trim whitespace - case "$mode" in - motd) export DEV_MODE_MOTD=true ;; - keep) export DEV_MODE_KEEP=true ;; - trace) export DEV_MODE_TRACE=true ;; - pause) export DEV_MODE_PAUSE=true ;; - breakpoint) export DEV_MODE_BREAKPOINT=true ;; - logs) export DEV_MODE_LOGS=true ;; - dryrun) export DEV_MODE_DRYRUN=true ;; - *) - if declare -f msg_warn >/dev/null 2>&1; then - msg_warn "Unknown dev_mode: '$mode' (ignored)" - else - echo "[WARN] Unknown dev_mode: '$mode' (ignored)" >&2 - fi - ;; - esac - done - - # Show active dev modes - local active_modes=() - [[ $DEV_MODE_MOTD == true ]] && active_modes+=("motd") - [[ $DEV_MODE_KEEP == true ]] && active_modes+=("keep") - [[ $DEV_MODE_TRACE == true ]] && active_modes+=("trace") - [[ $DEV_MODE_PAUSE == true ]] && active_modes+=("pause") - [[ $DEV_MODE_BREAKPOINT == true ]] && active_modes+=("breakpoint") - [[ $DEV_MODE_LOGS == true ]] && active_modes+=("logs") - [[ $DEV_MODE_DRYRUN == true ]] && active_modes+=("dryrun") - - if [[ ${#active_modes[@]} -gt 0 ]]; then - if declare -f msg_custom >/dev/null 2>&1; then - msg_custom "🔧" "${YWB}" "Dev modes active: ${active_modes[*]}" - else - echo "[DEV] Active modes: ${active_modes[*]}" >&2 - fi + # Parse comma-separated modes + if [[ -n "${dev_mode:-}" ]]; then + IFS=',' read -ra MODES <<<"$dev_mode" + for mode in "${MODES[@]}"; do + mode="$(echo "$mode" | xargs)" # Trim whitespace + case "$mode" in + motd) export DEV_MODE_MOTD=true ;; + keep) export DEV_MODE_KEEP=true ;; + trace) export DEV_MODE_TRACE=true ;; + pause) export DEV_MODE_PAUSE=true ;; + breakpoint) export DEV_MODE_BREAKPOINT=true ;; + logs) export DEV_MODE_LOGS=true ;; + dryrun) export DEV_MODE_DRYRUN=true ;; + *) + if declare -f msg_warn >/dev/null 2>&1; then + msg_warn "Unknown dev_mode: '$mode' (ignored)" + else + echo "[WARN] Unknown dev_mode: '$mode' (ignored)" >&2 fi + ;; + esac + done + + # Show active dev modes + local active_modes=() + [[ $DEV_MODE_MOTD == true ]] && active_modes+=("motd") + [[ $DEV_MODE_KEEP == true ]] && active_modes+=("keep") + [[ $DEV_MODE_TRACE == true ]] && active_modes+=("trace") + [[ $DEV_MODE_PAUSE == true ]] && active_modes+=("pause") + [[ $DEV_MODE_BREAKPOINT == true ]] && active_modes+=("breakpoint") + [[ $DEV_MODE_LOGS == true ]] && active_modes+=("logs") + [[ $DEV_MODE_DRYRUN == true ]] && active_modes+=("dryrun") + + if [[ ${#active_modes[@]} -gt 0 ]]; then + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "🔧" "${YWB}" "Dev modes active: ${active_modes[*]}" + else + echo "[DEV] Active modes: ${active_modes[*]}" >&2 + fi fi + fi } # ============================================================================== @@ -242,13 +242,13 @@ parse_dev_mode() { # - Required because scripts use Bash-specific features # ------------------------------------------------------------------------------ shell_check() { - 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..." - sleep 2 - exit - fi + 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..." + sleep 2 + exit + fi } # ------------------------------------------------------------------------------ @@ -259,13 +259,13 @@ shell_check() { # - Exits with error if not running as root directly # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -276,35 +276,35 @@ root_check() { # - Exits with error message if unsupported version detected # ------------------------------------------------------------------------------ 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}')" - # 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 + # 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 9.0–9.1 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 1)); then - msg_error "This version of Proxmox VE is not yet supported." - msg_error "Supported: Proxmox VE version 9.0 – 9.1" - exit 1 - fi - return 0 + # Check for Proxmox VE 9.x: allow 9.0–9.1 + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 0 || MINOR > 1)); then + msg_error "This version of Proxmox VE is not yet supported." + msg_error "Supported: Proxmox VE version 9.0 – 9.1" + 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.9 or 9.0 – 9.1" - 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.9 or 9.0 – 9.1" + exit 1 } # ------------------------------------------------------------------------------ @@ -315,13 +315,13 @@ pve_check() { # - Provides link to ARM64-compatible scripts # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -333,29 +333,29 @@ arch_check() { # - Does not abort execution, only warns # ------------------------------------------------------------------------------ ssh_check() { - if [ -n "$SSH_CLIENT" ]; then - local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") - local host_ip=$(hostname -I | awk '{print $1}') + if [ -n "$SSH_CLIENT" ]; then + local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") + local host_ip=$(hostname -I | awk '{print $1}') - # 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." + # 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 } # ============================================================================== @@ -371,14 +371,14 @@ ssh_check() { # - Fallback to BUILD_LOG if neither is set # ------------------------------------------------------------------------------ get_active_logfile() { - if [[ -n "${INSTALL_LOG:-}" ]]; then - echo "$INSTALL_LOG" - elif [[ -n "${BUILD_LOG:-}" ]]; then - echo "$BUILD_LOG" - else - # Fallback for legacy scripts - echo "/tmp/build-$(date +%Y%m%d_%H%M%S).log" - fi + if [[ -n "${INSTALL_LOG:-}" ]]; then + echo "$INSTALL_LOG" + elif [[ -n "${BUILD_LOG:-}" ]]; then + echo "$BUILD_LOG" + else + # Fallback for legacy scripts + echo "/tmp/build-$(date +%Y%m%d_%H%M%S).log" + fi } # Legacy compatibility: SILENT_LOGFILE points to active log @@ -393,56 +393,56 @@ SILENT_LOGFILE="$(get_active_logfile)" # - Sources explain_exit_code() for detailed error messages # ------------------------------------------------------------------------------ silent() { - local cmd="$*" - local caller_line="${BASH_LINENO[0]:-unknown}" - local logfile="$(get_active_logfile)" + local cmd="$*" + local caller_line="${BASH_LINENO[0]:-unknown}" + local logfile="$(get_active_logfile)" - # Dryrun mode: Show command without executing - if [[ "${DEV_MODE_DRYRUN:-false}" == "true" ]]; then - if declare -f msg_custom >/dev/null 2>&1; then - msg_custom "🔍" "${BL}" "[DRYRUN] $cmd" - else - echo "[DRYRUN] $cmd" >&2 - fi - return 0 + # Dryrun mode: Show command without executing + if [[ "${DEV_MODE_DRYRUN:-false}" == "true" ]]; then + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "🔍" "${BL}" "[DRYRUN] $cmd" + else + echo "[DRYRUN] $cmd" >&2 + fi + return 0 + fi + + set +Eeuo pipefail + trap - ERR + + "$@" >>"$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 - set +Eeuo pipefail - trap - ERR + local explanation + explanation="$(explain_exit_code "$rc")" - "$@" >>"$logfile" 2>&1 - local rc=$? + printf "\e[?25h" + msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" + msg_custom "→" "${YWB}" "${cmd}" - set -Eeuo pipefail - trap 'error_handler' ERR + if [[ -s "$logfile" ]]; then + local log_lines=$(wc -l <"$logfile") + echo "--- Last 10 lines of silent log ---" + tail -n 10 "$logfile" + echo "-----------------------------------" - 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" - msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" - msg_custom "→" "${YWB}" "${cmd}" - - if [[ -s "$logfile" ]]; then - local log_lines=$(wc -l <"$logfile") - echo "--- Last 10 lines of silent log ---" - tail -n 10 "$logfile" - echo "-----------------------------------" - - # Show how to view full log if there are more lines - if [[ $log_lines -gt 10 ]]; then - msg_custom "📋" "${YW}" "View full log (${log_lines} lines): ${logfile}" - fi - fi - - exit "$rc" + # Show how to view full log if there are more lines + if [[ $log_lines -gt 10 ]]; then + msg_custom "📋" "${YW}" "View full log (${log_lines} lines): ${logfile}" + fi fi + + exit "$rc" + fi } # ------------------------------------------------------------------------------ @@ -454,14 +454,14 @@ silent() { # - Uses color_spinner() colors for output # ------------------------------------------------------------------------------ spinner() { - local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) - local msg="${SPINNER_MSG:-Processing...}" - local i=0 - while true; do - local index=$((i++ % ${#chars[@]})) - printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${msg}${CS_CL}" - sleep 0.1 - done + local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) + local msg="${SPINNER_MSG:-Processing...}" + local i=0 + while true; do + local index=$((i++ % ${#chars[@]})) + printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${msg}${CS_CL}" + sleep 0.1 + done } # ------------------------------------------------------------------------------ @@ -473,8 +473,8 @@ spinner() { # - Fallback to ANSI codes if tput not available # ------------------------------------------------------------------------------ clear_line() { - tput cr 2>/dev/null || echo -en "\r" - tput el 2>/dev/null || echo -en "\033[K" + tput cr 2>/dev/null || echo -en "\r" + tput el 2>/dev/null || echo -en "\033[K" } # ------------------------------------------------------------------------------ @@ -487,20 +487,20 @@ clear_line() { # - Unsets SPINNER_PID and SPINNER_MSG variables # ------------------------------------------------------------------------------ stop_spinner() { - local pid="${SPINNER_PID:-}" - [[ -z "$pid" && -f /tmp/.spinner.pid ]] && pid=$(/dev/null; then - sleep 0.05 - kill -9 "$pid" 2>/dev/null || true - wait "$pid" 2>/dev/null || true - fi - rm -f /tmp/.spinner.pid + if [[ -n "$pid" && "$pid" =~ ^[0-9]+$ ]]; then + if kill "$pid" 2>/dev/null; then + sleep 0.05 + kill -9 "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true fi + rm -f /tmp/.spinner.pid + fi - unset SPINNER_PID SPINNER_MSG - stty sane 2>/dev/null || true + unset SPINNER_PID SPINNER_MSG + stty sane 2>/dev/null || true } # ============================================================================== @@ -517,45 +517,45 @@ stop_spinner() { # - Backgrounds spinner process and stores PID for later cleanup # ------------------------------------------------------------------------------ msg_info() { - local msg="$1" - [[ -z "$msg" ]] && return + local msg="$1" + [[ -z "$msg" ]] && return - if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then - declare -gA MSG_INFO_SHOWN=() - fi - # Sanitize message for use as associative array key (remove ANSI codes) - local sanitized_msg - sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g') - [[ -n "${MSG_INFO_SHOWN["$sanitized_msg"]+x}" ]] && return - MSG_INFO_SHOWN["$sanitized_msg"]=1 + if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then + declare -gA MSG_INFO_SHOWN=() + fi + # Sanitize message for use as associative array key (remove ANSI codes and special chars) + local sanitized_msg + sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') + [[ -n "${MSG_INFO_SHOWN["$sanitized_msg"]+x}" ]] && return + MSG_INFO_SHOWN["$sanitized_msg"]=1 - stop_spinner - SPINNER_MSG="$msg" + stop_spinner + SPINNER_MSG="$msg" - if is_verbose_mode || is_alpine; then - local HOURGLASS="${TAB}⏳${TAB}" - printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2 + if is_verbose_mode || is_alpine; then + local HOURGLASS="${TAB}⏳${TAB}" + printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2 - # Pause mode: Wait for Enter after each step - if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then - echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 - read -r - fi - return - fi - - color_spinner - spinner & - SPINNER_PID=$! - echo "$SPINNER_PID" >/tmp/.spinner.pid - disown "$SPINNER_PID" 2>/dev/null || true - - # Pause mode: Stop spinner and wait + # Pause mode: Wait for Enter after each step if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then - stop_spinner - echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 - read -r + echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 + read -r fi + return + fi + + color_spinner + spinner & + SPINNER_PID=$! + echo "$SPINNER_PID" >/tmp/.spinner.pid + disown "$SPINNER_PID" 2>/dev/null || true + + # Pause mode: Stop spinner and wait + if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then + stop_spinner + echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 + read -r + fi } # ------------------------------------------------------------------------------ @@ -567,15 +567,15 @@ msg_info() { # - Uses green color for success indication # ------------------------------------------------------------------------------ msg_ok() { - local msg="$1" - [[ -z "$msg" ]] && return - stop_spinner - clear_line - echo -e "$CM ${GN}${msg}${CL}" - # Sanitize message for use as associative array key (remove ANSI codes) - local sanitized_msg - sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g') - unset MSG_INFO_SHOWN["$sanitized_msg"] + local msg="$1" + [[ -z "$msg" ]] && return + stop_spinner + clear_line + echo -e "$CM ${GN}${msg}${CL}" + # Sanitize message for use as associative array key (remove ANSI codes and special chars) + local sanitized_msg + sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') + unset 'MSG_INFO_SHOWN['"$sanitized_msg"']' 2>/dev/null || true } # ------------------------------------------------------------------------------ @@ -587,9 +587,9 @@ msg_ok() { # - Outputs to stderr # ------------------------------------------------------------------------------ msg_error() { - stop_spinner - local msg="$1" - echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2 + stop_spinner + local msg="$1" + echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2 } # ------------------------------------------------------------------------------ @@ -601,9 +601,9 @@ msg_error() { # - Outputs to stderr # ------------------------------------------------------------------------------ msg_warn() { - stop_spinner - local msg="$1" - echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 + stop_spinner + local msg="$1" + echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 } # ------------------------------------------------------------------------------ @@ -615,12 +615,12 @@ msg_warn() { # - Useful for specialized status messages # ------------------------------------------------------------------------------ msg_custom() { - local symbol="${1:-"[*]"}" - local color="${2:-"\e[36m"}" - local msg="${3:-}" - [[ -z "$msg" ]] && return - stop_spinner - echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" + local symbol="${1:-"[*]"}" + local color="${2:-"\e[36m"}" + local msg="${3:-}" + [[ -z "$msg" ]] && return + stop_spinner + echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" } # ------------------------------------------------------------------------------ @@ -632,10 +632,10 @@ msg_custom() { # - Uses bright yellow color for debug output # ------------------------------------------------------------------------------ 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 + if [[ "${var_full_verbose:-0}" == "1" ]]; then + [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 + echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" + fi } # ------------------------------------------------------------------------------ @@ -648,9 +648,9 @@ msg_debug() { # - Usage: msg_dev "Container ready for debugging" # ------------------------------------------------------------------------------ msg_dev() { - if [[ -n "${dev_mode:-}" ]]; then - echo -e "${SEARCH}${BOLD}${DGN}🔧 [DEV]${CL} $*" - fi + if [[ -n "${dev_mode:-}" ]]; then + echo -e "${SEARCH}${BOLD}${DGN}🔧 [DEV]${CL} $*" + fi } # # - Displays error message and immediately terminates script @@ -658,8 +658,8 @@ msg_dev() { # - Use for unrecoverable errors that require immediate exit # ------------------------------------------------------------------------------ fatal() { - msg_error "$1" - kill -INT $$ + msg_error "$1" + kill -INT $$ } # ============================================================================== @@ -674,9 +674,9 @@ fatal() { # - Exits with default exit code # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -688,20 +688,20 @@ exit_script() { # - Returns header content or empty string on failure # ------------------------------------------------------------------------------ get_header() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local app_type=${APP_TYPE:-ct} # Default zu 'ct' falls nicht gesetzt - local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/${app_type}/headers/${app_name}" - local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}" + local app_name=$(echo "${APP,,}" | tr -d ' ') + local app_type=${APP_TYPE:-ct} # Default zu 'ct' falls nicht gesetzt + local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/${app_type}/headers/${app_name}" + local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}" - mkdir -p "$(dirname "$local_header_path")" + 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 + 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 + cat "$local_header_path" 2>/dev/null || true } # ------------------------------------------------------------------------------ @@ -713,18 +713,18 @@ get_header() { # - Returns silently if header not available # ------------------------------------------------------------------------------ header_info() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_content + local app_name=$(echo "${APP,,}" | tr -d ' ') + local header_content - header_content=$(get_header "$app_name") || header_content="" + header_content=$(get_header "$app_name") || header_content="" - clear - local term_width - term_width=$(tput cols 2>/dev/null || echo 120) + clear + local term_width + term_width=$(tput cols 2>/dev/null || echo 120) - if [ -n "$header_content" ]; then - echo "$header_content" - fi + if [ -n "$header_content" ]; then + echo "$header_content" + fi } # ------------------------------------------------------------------------------ @@ -735,14 +735,14 @@ header_info() { # - Required for clear_line() and terminal width detection # ------------------------------------------------------------------------------ ensure_tput() { - if ! command -v tput >/dev/null 2>&1; then - if grep -qi 'alpine' /etc/os-release; then - apk add --no-cache ncurses >/dev/null 2>&1 - elif command -v apt-get >/dev/null 2>&1; then - apt-get update -qq >/dev/null - apt-get install -y -qq ncurses-bin >/dev/null 2>&1 - fi + if ! command -v tput >/dev/null 2>&1; then + if grep -qi 'alpine' /etc/os-release; then + apk add --no-cache ncurses >/dev/null 2>&1 + elif command -v apt-get >/dev/null 2>&1; then + apt-get update -qq >/dev/null + apt-get install -y -qq ncurses-bin >/dev/null 2>&1 fi + fi } # ------------------------------------------------------------------------------ @@ -754,16 +754,16 @@ ensure_tput() { # - Used to adjust behavior for Alpine-specific commands # ------------------------------------------------------------------------------ is_alpine() { - local os_id="${var_os:-${PCT_OSTYPE:-}}" + local os_id="${var_os:-${PCT_OSTYPE:-}}" - if [[ -z "$os_id" && -f /etc/os-release ]]; then - os_id="$( - . /etc/os-release 2>/dev/null - echo "${ID:-}" - )" - fi + if [[ -z "$os_id" && -f /etc/os-release ]]; then + os_id="$( + . /etc/os-release 2>/dev/null + echo "${ID:-}" + )" + fi - [[ "$os_id" == "alpine" ]] + [[ "$os_id" == "alpine" ]] } # ------------------------------------------------------------------------------ @@ -775,14 +775,14 @@ is_alpine() { # - Used by msg_info() to decide between spinner and static output # ------------------------------------------------------------------------------ is_verbose_mode() { - local verbose="${VERBOSE:-${var_verbose:-no}}" - local tty_status - if [[ -t 2 ]]; then - tty_status="interactive" - else - tty_status="not-a-tty" - fi - [[ "$verbose" != "no" || ! -t 2 ]] + local verbose="${VERBOSE:-${var_verbose:-no}}" + local tty_status + if [[ -t 2 ]]; then + tty_status="interactive" + else + tty_status="not-a-tty" + fi + [[ "$verbose" != "no" || ! -t 2 ]] } # ============================================================================== @@ -800,62 +800,62 @@ is_verbose_mode() { # - Run at end of container creation to minimize disk usage # ------------------------------------------------------------------------------ cleanup_lxc() { - msg_info "Cleaning up" - # OS-specific package manager cleanup - if is_alpine; then - $STD apk cache clean 2>/dev/null || true - rm -rf /var/cache/apk/* - elif command -v apt &>/dev/null; then - # Debian/Ubuntu/Devuan - $STD apt -y autoremove 2>/dev/null || true - $STD apt -y autoclean 2>/dev/null || true - $STD apt -y clean 2>/dev/null || true - elif command -v dnf &>/dev/null; then - # Fedora/Rocky/AlmaLinux/CentOS 8+ - $STD dnf clean all 2>/dev/null || true - $STD dnf autoremove -y 2>/dev/null || true - elif command -v yum &>/dev/null; then - # CentOS 7/older RHEL - $STD yum clean all 2>/dev/null || true - elif command -v zypper &>/dev/null; then - # openSUSE - $STD zypper clean --all 2>/dev/null || true - elif command -v emerge &>/dev/null; then - # Gentoo - $STD emerge --quiet --depclean 2>/dev/null || true - $STD eclean-dist -d 2>/dev/null || true - $STD eclean-pkg -d 2>/dev/null || true - fi + msg_info "Cleaning up" + # OS-specific package manager cleanup + if is_alpine; then + $STD apk cache clean 2>/dev/null || true + rm -rf /var/cache/apk/* + elif command -v apt &>/dev/null; then + # Debian/Ubuntu/Devuan + $STD apt -y autoremove 2>/dev/null || true + $STD apt -y autoclean 2>/dev/null || true + $STD apt -y clean 2>/dev/null || true + elif command -v dnf &>/dev/null; then + # Fedora/Rocky/AlmaLinux/CentOS 8+ + $STD dnf clean all 2>/dev/null || true + $STD dnf autoremove -y 2>/dev/null || true + elif command -v yum &>/dev/null; then + # CentOS 7/older RHEL + $STD yum clean all 2>/dev/null || true + elif command -v zypper &>/dev/null; then + # openSUSE + $STD zypper clean --all 2>/dev/null || true + elif command -v emerge &>/dev/null; then + # Gentoo + $STD emerge --quiet --depclean 2>/dev/null || true + $STD eclean-dist -d 2>/dev/null || true + $STD eclean-pkg -d 2>/dev/null || true + fi - # Clear temp artifacts (keep sockets/FIFOs; ignore errors) - 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 + # Clear temp artifacts (keep sockets/FIFOs; ignore errors) + 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 - # Truncate writable log files silently (permission errors ignored) - if command -v truncate >/dev/null 2>&1; then - find /var/log -type f -writable -print0 2>/dev/null | - xargs -0 -n1 truncate -s 0 2>/dev/null || true - fi + # Truncate writable log files silently (permission errors ignored) + if command -v truncate >/dev/null 2>&1; then + find /var/log -type f -writable -print0 2>/dev/null | + xargs -0 -n1 truncate -s 0 2>/dev/null || true + fi - # Node.js npm - if command -v npm &>/dev/null; then $STD npm cache clean --force 2>/dev/null || true; fi - # Node.js yarn - if command -v yarn &>/dev/null; then $STD yarn cache clean 2>/dev/null || true; fi - # Node.js pnpm - if command -v pnpm &>/dev/null; then $STD pnpm store prune 2>/dev/null || true; fi - # Go - if command -v go &>/dev/null; then $STD go clean -cache -modcache 2>/dev/null || true; fi - # Rust cargo - if command -v cargo &>/dev/null; then $STD cargo clean 2>/dev/null || true; fi - # Ruby gem - if command -v gem &>/dev/null; then $STD gem cleanup 2>/dev/null || true; fi - # Composer (PHP) - if command -v composer &>/dev/null; then $STD composer clear-cache 2>/dev/null || true; fi + # Node.js npm + if command -v npm &>/dev/null; then $STD npm cache clean --force 2>/dev/null || true; fi + # Node.js yarn + if command -v yarn &>/dev/null; then $STD yarn cache clean 2>/dev/null || true; fi + # Node.js pnpm + if command -v pnpm &>/dev/null; then $STD pnpm store prune 2>/dev/null || true; fi + # Go + if command -v go &>/dev/null; then $STD go clean -cache -modcache 2>/dev/null || true; fi + # Rust cargo + if command -v cargo &>/dev/null; then $STD cargo clean 2>/dev/null || true; fi + # Ruby gem + if command -v gem &>/dev/null; then $STD gem cleanup 2>/dev/null || true; fi + # Composer (PHP) + if command -v composer &>/dev/null; then $STD composer clear-cache 2>/dev/null || true; fi - if command -v journalctl &>/dev/null; then - $STD journalctl --vacuum-time=10m 2>/dev/null || true - fi - msg_ok "Cleaned" + if command -v journalctl &>/dev/null; then + $STD journalctl --vacuum-time=10m 2>/dev/null || true + fi + msg_ok "Cleaned" } # ------------------------------------------------------------------------------ @@ -869,41 +869,41 @@ cleanup_lxc() { # - Returns 0 if swap active or successfully created, 1 if declined/failed # ------------------------------------------------------------------------------ check_or_create_swap() { - msg_info "Checking for active swap" + msg_info "Checking for active swap" - if swapon --noheadings --show | grep -q 'swap'; then - msg_ok "Swap is active" - return 0 - fi + if swapon --noheadings --show | grep -q 'swap'; then + msg_ok "Swap is active" + return 0 + fi - msg_error "No active swap detected" + msg_error "No active swap detected" - read -p "Do you want to create a swap file? [y/N]: " create_swap - create_swap="${create_swap,,}" # to lowercase + read -p "Do you want to create a swap file? [y/N]: " create_swap + create_swap="${create_swap,,}" # to lowercase - if [[ "$create_swap" != "y" && "$create_swap" != "yes" ]]; then - msg_info "Skipping swap file creation" - return 1 - fi + if [[ "$create_swap" != "y" && "$create_swap" != "yes" ]]; then + msg_info "Skipping swap file creation" + return 1 + fi - read -p "Enter swap size in MB (e.g., 2048 for 2GB): " swap_size_mb - if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then - msg_error "Invalid size input. Aborting." - return 1 - fi + read -p "Enter swap size in MB (e.g., 2048 for 2GB): " swap_size_mb + if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then + msg_error "Invalid size input. Aborting." + return 1 + fi - local swap_file="/swapfile" + local swap_file="/swapfile" - msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" - if dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress && - chmod 600 "$swap_file" && - mkswap "$swap_file" && - swapon "$swap_file"; then - msg_ok "Swap file created and activated successfully" - else - msg_error "Failed to create or activate swap" - return 1 - fi + msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" + if dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress && + chmod 600 "$swap_file" && + mkswap "$swap_file" && + swapon "$swap_file"; then + msg_ok "Swap file created and activated successfully" + else + msg_error "Failed to create or activate swap" + return 1 + fi } # ============================================================================== From c88fe0a0cd65fcfcfcbbd89bee5a74490c6a8d58 Mon Sep 17 00:00:00 2001 From: lengschder97 Date: Mon, 12 Jan 2026 14:04:10 +0100 Subject: [PATCH 300/378] Fixed greptile comments --- ct/forgejo-runner.sh | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh index 95d002e26..6c63ee339 100644 --- a/ct/forgejo-runner.sh +++ b/ct/forgejo-runner.sh @@ -18,9 +18,6 @@ var_unprivileged="${var_unprivileged:-1}" var_nesting="${var_nesting:-1}" var_keyctl="${var_keyctl:-1}" -# ------------------------------------------------- -# Framework setup -# ------------------------------------------------- header_info "$APP" variables color @@ -41,20 +38,31 @@ function update_script() { msg_ok "Stopped Forgejo Runner" msg_info "Fetching latest Forgejo Runner version" - RELEASE=$(curl -fsSL https://code.forgejo.org/api/v1/repos/forgejo/runner/releases/latest \ + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + ARCH=$(uname -m) + + case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + armv7l) ARCH="armv7" ;; + *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; + esac + + RELEASE=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest \ | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//') msg_info "Updating Forgejo Runner to v${RELEASE}" + curl -fsSL \ - "https://code.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-linux-amd64" \ - -o /usr/local/bin/forgejo-runner + "https://data.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-${OS}-${ARCH}" \ + -o forgejo-runner + chmod +x /usr/local/bin/forgejo-runner msg_ok "Updated Forgejo Runner" msg_info "Starting Forgejo Runner" - systemctl daemon-reload - systemctl start forgejo-runner + systemctl enable -q --now forgejo-runner msg_ok "Started Forgejo Runner" msg_ok "Update completed successfully!" @@ -65,5 +73,7 @@ start build_container description -msg_ok "Completed successfully!" -echo -e "${INFO}${YW}Forgejo Runner is now online and ready.${CL}" \ No newline at end of file +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}" \ No newline at end of file From 06f4a95b7fb3116ff915431c5fad5e36b69df86f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 13:05:30 +0000 Subject: [PATCH 301/378] Delete jellystat (addon) after migration to ProxmoxVE (#1269) Co-authored-by: github-actions[bot] --- frontend/public/json/jellystat.json | 48 ---- tools/addon/jellystat.sh | 384 ---------------------------- 2 files changed, 432 deletions(-) delete mode 100644 frontend/public/json/jellystat.json delete mode 100644 tools/addon/jellystat.sh diff --git a/frontend/public/json/jellystat.json b/frontend/public/json/jellystat.json deleted file mode 100644 index f9bf24f0d..000000000 --- a/frontend/public/json/jellystat.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "Jellystat", - "slug": "jellystat", - "categories": [ - 9 - ], - "date_created": "2025-12-08", - "type": "addon", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/CyferShepard/Jellystat", - "website": "https://github.com/CyferShepard/Jellystat", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/jellystat.webp", - "config_path": "/opt/jellystat/.env", - "description": "A free and open source statistics app for Jellyfin", - "install_methods": [ - { - "type": "default", - "script": "tools/addon/jellystat.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Requires Node.js 20+ and PostgreSQL (auto-installed if missing)", - "type": "info" - }, - { - "text": "Default PostgreSQL credentials: jellystat / jellystat", - "type": "info" - }, - { - "text": "Update with: update_jellystat", - "type": "info" - } - ] -} diff --git a/tools/addon/jellystat.sh b/tools/addon/jellystat.sh deleted file mode 100644 index 2c10cc552..000000000 --- a/tools/addon/jellystat.sh +++ /dev/null @@ -1,384 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/CyferShepard/Jellystat - -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) -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/error_handler.func) - -# Enable error handling -set -Eeuo pipefail -trap 'error_handler' ERR - -# ============================================================================== -# CONFIGURATION -# ============================================================================== -APP="Jellystat" -APP_TYPE="addon" -INSTALL_PATH="/opt/jellystat" -CONFIG_PATH="/opt/jellystat/.env" -DEFAULT_PORT=3000 - -# Initialize all core functions (colors, formatting, icons, STD mode) -load_functions - -# ============================================================================== -# HEADER -# ============================================================================== -function header_info { - clear - cat <<"EOF" - __ ____ __ __ - / /__ / / /_ _______/ /_____ _/ /_ - __ / / _ \/ / / / / / ___/ __/ __ `/ __/ -/ /_/ / __/ / / /_/ (__ ) /_/ /_/ / /_ -\____/\___/_/_/\__, /____/\__/\__,_/\__/ - /____/ -EOF -} - -# ============================================================================== -# HELPER FUNCTIONS -# ============================================================================== -get_ip() { - hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1" -} - -# ============================================================================== -# OS DETECTION -# ============================================================================== -if [[ -f "/etc/alpine-release" ]]; then - msg_error "Alpine is not supported for ${APP}. Use Debian/Ubuntu." - exit 1 -elif [[ -f "/etc/debian_version" ]]; then - OS="Debian" - SERVICE_PATH="/etc/systemd/system/jellystat.service" -else - echo -e "${CROSS} Unsupported OS detected. Exiting." - exit 1 -fi - -# ============================================================================== -# UNINSTALL -# ============================================================================== -function uninstall() { - msg_info "Uninstalling ${APP}" - systemctl disable --now jellystat.service &>/dev/null || true - rm -f "$SERVICE_PATH" - rm -rf "$INSTALL_PATH" - rm -f "/usr/local/bin/update_jellystat" - rm -f "$HOME/.jellystat" - msg_ok "${APP} has been uninstalled" - - # Ask about PostgreSQL database removal - echo "" - echo -n "${TAB}Also remove PostgreSQL database 'jellystat'? (y/N): " - read -r db_prompt - if [[ "${db_prompt,,}" =~ ^(y|yes)$ ]]; then - if command -v psql &>/dev/null; then - msg_info "Removing PostgreSQL database and user" - $STD sudo -u postgres psql -c "DROP DATABASE IF EXISTS jellystat;" &>/dev/null || true - $STD sudo -u postgres psql -c "DROP USER IF EXISTS jellystat;" &>/dev/null || true - msg_ok "Removed PostgreSQL database 'jellystat' and user 'jellystat'" - else - msg_warn "PostgreSQL not found - database may have been removed already" - fi - else - msg_warn "PostgreSQL database was NOT removed. Remove manually if needed:" - echo -e "${TAB} sudo -u postgres psql -c \"DROP DATABASE jellystat;\"" - echo -e "${TAB} sudo -u postgres psql -c \"DROP USER jellystat;\"" - fi -} - -# ============================================================================== -# UPDATE -# ============================================================================== -function update() { - if check_for_gh_release "jellystat" "CyferShepard/Jellystat"; then - msg_info "Stopping service" - systemctl stop jellystat.service &>/dev/null || true - msg_ok "Stopped service" - - msg_info "Backing up configuration" - cp "$CONFIG_PATH" /tmp/jellystat.env.bak 2>/dev/null || true - msg_ok "Backed up configuration" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jellystat" "CyferShepard/Jellystat" "tarball" "latest" "$INSTALL_PATH" - - msg_info "Restoring configuration" - cp /tmp/jellystat.env.bak "$CONFIG_PATH" 2>/dev/null || true - rm -f /tmp/jellystat.env.bak - msg_ok "Restored configuration" - - msg_info "Installing dependencies" - cd "$INSTALL_PATH" - $STD npm install - msg_ok "Installed dependencies" - - msg_info "Building ${APP}" - $STD npm run build - msg_ok "Built ${APP}" - - msg_info "Starting service" - systemctl start jellystat - msg_ok "Started service" - msg_ok "Updated successfully" - exit - fi -} - -# ============================================================================== -# INSTALL -# ============================================================================== -function install() { - local ip - ip=$(get_ip) - - # Setup Node.js (only installs if not present or different version) - if command -v node &>/dev/null; then - msg_ok "Node.js already installed ($(node -v))" - else - NODE_VERSION="22" setup_nodejs - fi - - # Setup PostgreSQL (only installs if not present) - if command -v psql &>/dev/null; then - msg_ok "PostgreSQL already installed" - else - PG_VERSION="17" setup_postgresql - fi - - # Create database and user (skip if already exists) - local DB_NAME="jellystat" - local DB_USER="jellystat" - local DB_PASS - - msg_info "Setting up PostgreSQL database" - - # Check if database already exists - if sudo -u postgres psql -lqt 2>/dev/null | cut -d \| -f 1 | grep -qw "$DB_NAME"; then - msg_warn "Database '${DB_NAME}' already exists - skipping creation" - echo -n "${TAB}Enter existing database password for '${DB_USER}': " - read -rs DB_PASS - echo "" - else - # Generate new password - DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) - - # Check if user exists, create if not - if sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" 2>/dev/null | grep -q 1; then - msg_info "User '${DB_USER}' exists, updating password" - $STD sudo -u postgres psql -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASS}';" || { - msg_error "Failed to update PostgreSQL user" - return 1 - } - else - $STD sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}';" || { - msg_error "Failed to create PostgreSQL user" - return 1 - } - fi - - # Create database (use template0 for UTF8 encoding compatibility) - $STD sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} WITH OWNER ${DB_USER} ENCODING 'UTF8' LC_COLLATE='C' LC_CTYPE='C' TEMPLATE template0;" || { - msg_error "Failed to create PostgreSQL database" - return 1 - } - $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" || { - msg_error "Failed to grant privileges" - return 1 - } - - # Grant schema permissions (required for PostgreSQL 15+) - $STD sudo -u postgres psql -d "${DB_NAME}" -c "GRANT ALL ON SCHEMA public TO ${DB_USER};" || true - - # Configure pg_hba.conf for password authentication on localhost - local PG_HBA - PG_HBA=$(sudo -u postgres psql -tAc "SHOW hba_file;" 2>/dev/null | tr -d ' ') - if [[ -n "$PG_HBA" && -f "$PG_HBA" ]]; then - # Check if md5/scram-sha-256 auth is already configured for local connections - if ! grep -qE "^host\s+${DB_NAME}\s+${DB_USER}\s+127.0.0.1" "$PG_HBA"; then - msg_info "Configuring PostgreSQL authentication" - # Add password auth for jellystat user on localhost (before the default rules) - sed -i "/^# IPv4 local connections:/a host ${DB_NAME} ${DB_USER} 127.0.0.1/32 scram-sha-256" "$PG_HBA" - sed -i "/^# IPv4 local connections:/a host ${DB_NAME} ${DB_USER} ::1/128 scram-sha-256" "$PG_HBA" - # Reload PostgreSQL to apply changes - systemctl reload postgresql - msg_ok "Configured PostgreSQL authentication" - fi - fi - - msg_ok "Created PostgreSQL database '${DB_NAME}'" - fi - - # Generate JWT Secret - local JWT_SECRET - JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c32) - - # Force fresh download by removing version cache - rm -f "$HOME/.jellystat" - mkdir -p "$INSTALL_PATH" - fetch_and_deploy_gh_release "jellystat" "CyferShepard/Jellystat" "tarball" "latest" "$INSTALL_PATH" - - msg_info "Installing dependencies" - cd "$INSTALL_PATH" || { - msg_error "Failed to enter ${INSTALL_PATH}" - return 1 - } - $STD npm install - msg_ok "Installed dependencies" - - msg_info "Building ${APP}" - $STD npm run build - msg_ok "Built ${APP}" - - msg_info "Creating configuration" - cat <"$CONFIG_PATH" -# Jellystat Configuration -# Database -POSTGRES_USER=${DB_USER} -POSTGRES_PASSWORD=${DB_PASS} -POSTGRES_IP=localhost -POSTGRES_PORT=5432 -POSTGRES_DB=${DB_NAME} - -# Security -JWT_SECRET=${JWT_SECRET} - -# Server -JS_LISTEN_IP=0.0.0.0 -JS_BASE_URL=/ -TZ=$(cat /etc/timezone 2>/dev/null || echo "UTC") - -# Optional: GeoLite for IP Geolocation -# JS_GEOLITE_ACCOUNT_ID= -# JS_GEOLITE_LICENSE_KEY= - -# Optional: Master Override (if you forget your password) -# JS_USER=admin -# JS_PASSWORD=admin - -# Optional: Minimum playback duration to record (seconds) -# MINIMUM_SECONDS_TO_INCLUDE_PLAYBACK=1 - -# Optional: Self-signed certificates -REJECT_SELF_SIGNED_CERTIFICATES=true -EOF - chmod 600 "$CONFIG_PATH" - msg_ok "Created configuration" - - msg_info "Creating service" - cat <"$SERVICE_PATH" -[Unit] -Description=Jellystat - Statistics for Jellyfin -After=network.target postgresql.service - -[Service] -Type=simple -User=root -WorkingDirectory=${INSTALL_PATH} -EnvironmentFile=${CONFIG_PATH} -ExecStart=/usr/bin/node ${INSTALL_PATH}/backend/server.js -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target -EOF - systemctl enable --now jellystat &>/dev/null - msg_ok "Created and started service" - - # Create update script (simple wrapper that calls this addon with type=update) - msg_info "Creating update script" - cat <<'UPDATEEOF' >/usr/local/bin/update_jellystat -#!/usr/bin/env bash -# Jellystat Update Script -type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/addon/jellystat.sh)" -UPDATEEOF - chmod +x /usr/local/bin/update_jellystat - msg_ok "Created update script (/usr/local/bin/update_jellystat)" - - # Save credentials - local CREDS_FILE="/root/jellystat.creds" - cat <"$CREDS_FILE" -Jellystat Credentials -===================== -Database User: ${DB_USER} -Database Password: ${DB_PASS} -Database Name: ${DB_NAME} -JWT Secret: ${JWT_SECRET} - -Web UI: http://${ip}:${DEFAULT_PORT} -EOF - chmod 600 "$CREDS_FILE" - - echo "" - msg_ok "${APP} is reachable at: ${BL}http://${ip}:${DEFAULT_PORT}${CL}" - msg_ok "Credentials saved to: ${BL}${CREDS_FILE}${CL}" - echo "" - msg_warn "On first access, you'll need to configure your Jellyfin server connection." -} - -# ============================================================================== -# MAIN -# ============================================================================== - -# Handle type=update (called from update script) -if [[ "${type:-}" == "update" ]]; then - header_info - if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/package.json" ]]; then - update - else - msg_error "${APP} is not installed. Nothing to update." - exit 1 - fi - exit 0 -fi - -header_info - -IP=$(get_ip) - -# Check if already installed -if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/package.json" ]]; then - msg_warn "${APP} is already installed." - echo "" - - echo -n "${TAB}Uninstall ${APP}? (y/N): " - read -r uninstall_prompt - if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then - uninstall - exit 0 - fi - - echo -n "${TAB}Update ${APP}? (y/N): " - read -r update_prompt - if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then - update - exit 0 - fi - - msg_warn "No action selected. Exiting." - exit 0 -fi - -# Fresh installation -msg_warn "${APP} is not installed." -echo "" -echo -e "${TAB}${INFO} This will install:" -echo -e "${TAB} - Node.js 22" -echo -e "${TAB} - PostgreSQL 17" -echo -e "${TAB} - Jellystat" -echo "" - -echo -n "${TAB}Install ${APP}? (y/N): " -read -r install_prompt -if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then - install -else - msg_warn "Installation cancelled. Exiting." - exit 0 -fi From 414607da2c6015e7764b91c0c6797357a2aee8b3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:01:07 +0100 Subject: [PATCH 302/378] Update netbird-install.sh --- install/netbird-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index bd0078521..6154c39ff 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Setting up NetBird Repository" -setup_deb882_repo \ +setup_deb822_repo \ "netbird" \ "https://pkgs.netbird.io/debian/public.key" \ "https://pkgs.netbird.io/debian" \ From 1265dcee0600f296536f4c27df3a079e693b8345 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 12 Jan 2026 11:40:30 -0500 Subject: [PATCH 303/378] nextExplorer: update env --- install/nextexplorer-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/nextexplorer-install.sh b/install/nextexplorer-install.sh index 1d7b40dd3..8ed03263a 100644 --- a/install/nextexplorer-install.sh +++ b/install/nextexplorer-install.sh @@ -69,6 +69,8 @@ AUTH_MODE=both SESSION_SECRET="${SECRET}" # AUTH_MAX_FAILED= # AUTH_LOCK_MINUTES= +# AUTH_USER_EMAIL= +# AUTH_USER_PASSWORD= # OIDC_ENABLED= # OIDC_ISSUER= From 837c41d04126b07b407b8e7d8c692dd620a6b763 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 12 Jan 2026 11:54:46 -0500 Subject: [PATCH 304/378] Byparr: clean install on update --- ct/byparr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/byparr.sh b/ct/byparr.sh index 4831404b9..62104bb11 100644 --- a/ct/byparr.sh +++ b/ct/byparr.sh @@ -33,7 +33,7 @@ function update_script() { systemctl stop byparr msg_ok "Stopped Service" - fetch_and_deploy_gh_release "Byparr" "ThePhaseless/Byparr" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Byparr" "ThePhaseless/Byparr" msg_info "Starting Service" systemctl start byparr From 021a4c612a76bd49cd791d95a267774c9aea06b1 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 12 Jan 2026 13:57:32 -0500 Subject: [PATCH 305/378] OpenCloud: update env --- install/opencloud-install.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 32ef1ed21..99ed46366 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -61,7 +61,7 @@ fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v msg_info "Configuring OpenCloud" DATA_DIR="/var/lib/opencloud/" -CONFIG_DIR="/etc/opencloud" +CONFIG_DIR="${CONFIG_DIR}" ENV_FILE="${CONFIG_DIR}/opencloud.env" mkdir -p "$DATA_DIR" "$CONFIG_DIR"/assets/apps @@ -77,6 +77,18 @@ OC_CONFIG_DIR=${CONFIG_DIR} OC_BASE_DATA_PATH=${DATA_DIR} STORAGE_SYSTEM_OC_ROOT=${DATA_DIR}/storage/metadata +## Web +WEB_ASSET_CORE_PATH=${CONFIG_DIR}/web/assets +WEB_ASSET_APPS_PATH=${CONFIG_DIR}/web/assets/apps +WEB_UI_CONFIG_FILE=${CONFIG_DIR}/web/config.json +# WEB_ASSET_THEMES_PATH=${CONFIG_DIR}/web/assets/themes +# WEB_UI_THEME_PATH= + +## Frontend +FRONTEND_DISABLE_RADICALE=true +FRONTEND_GROUPWARE_ENABLED=false +GRAPH_INCLUDE_OCM_SHAREES=true + ## Proxy PROXY_TLS=false PROXY_CSP_CONFIG_FILE_LOCATION=${CONFIG_DIR}/csp.yaml @@ -91,9 +103,6 @@ COLLABORATION_HTTP_ADDR=0.0.0.0:9300 COLLABORATION_WOPI_SRC=https://${WOPI_HOST} COLLABORATION_JWT_SECRET= -## Applications -WEB_ASSET_APPS_PATH=${CONFIG_DIR}/assets/apps - ## Notifications - Email settings # NOTIFICATIONS_SMTP_HOST= # NOTIFICATIONS_SMTP_PORT= From b8afdab106d464a6feb9ad9329beab60b032c5bf Mon Sep 17 00:00:00 2001 From: MickLesk Date: Mon, 12 Jan 2026 21:34:58 +0100 Subject: [PATCH 306/378] fix: Multi-distro LXC container fixes for autologin and package installation - Rocky/AlmaLinux 10 (EL10): Version detection for DNF 5 with correct packages (langpacks-en instead of glibc-langpack-en), makecache refresh, fallback to minimal install - openSUSE: Install ncurses-utils and terminfo-base, set TERM in /etc/profile.d and /etc/environment to fix 'unknown terminal type' - Gentoo: Fixed template pattern to use underscore (-openrc_) instead of dash, special version handling - openEuler: Set privileged container (var_unprivileged=0) to workaround PVE setup hook limitation - Devuan: Enhanced sysvinit autologin with multiple inittab patterns, fallback console entry, telinit reload - CentOS/all: Updated URLs from raw.githubusercontent.com to git.community-scripts.org - General: Better error handling and removed duplicate code blocks --- ct/openeuler.sh | 7 +- misc/build.func | 6677 +++++++++++++++++++++++---------------------- misc/install.func | 29 +- 3 files changed, 3406 insertions(+), 3307 deletions(-) diff --git a/ct/openeuler.sh b/ct/openeuler.sh index 087f5d853..659eaed60 100644 --- a/ct/openeuler.sh +++ b/ct/openeuler.sh @@ -5,6 +5,11 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.openeuler.org/ +# NOTE: openEuler requires privileged container due to PVE limitation +# PVE's post_create_hook expects /etc/redhat-release which openEuler doesn't have +# This causes "unable to create CT - error in setup task PVE::LXC::Setup::post_create_hook" +# Setting var_unprivileged=0 creates privileged container which bypasses this check + APP="openEuler" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" @@ -12,7 +17,7 @@ var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-openeuler}" var_version="${var_version:-25.03}" -var_unprivileged="${var_unprivileged:-1}" +var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables diff --git a/misc/build.func b/misc/build.func index f0b2ed10d..58351ea0b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -39,46 +39,46 @@ # - Captures app-declared resource defaults (CPU, RAM, Disk) # ------------------------------------------------------------------------------ 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. - SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files - BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + 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. + SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files + BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - # Parse dev_mode early - parse_dev_mode + # Parse dev_mode early + parse_dev_mode - # Setup persistent log directory if logs mode active - if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then - mkdir -p /var/log/community-scripts - BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" - fi + # Setup persistent log directory if logs mode active + if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then + mkdir -p /var/log/community-scripts + BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" + fi - # Get Proxmox VE version and kernel version - if command -v pveversion >/dev/null 2>&1; then - PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - else - PVEVERSION="N/A" - fi - KERNEL_VERSION=$(uname -r) + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + else + 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 + # 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 } # ----------------------------------------------------------------------------- @@ -187,17 +187,17 @@ variables() { 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" + 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" + 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 # ============================================================================== @@ -215,46 +215,46 @@ fi # ------------------------------------------------------------------------------ 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 - # Silent success - only show errors if they exist + # Silent success - only show errors if they exist } # ============================================================================== @@ -270,18 +270,18 @@ maxkeys_check() { # - Returns "Unknown" if OS type cannot be determined # ------------------------------------------------------------------------------ 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" } # ------------------------------------------------------------------------------ @@ -291,16 +291,16 @@ get_current_ip() { # - Removes old IP entries to avoid duplicates # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -311,30 +311,30 @@ update_motd_ip() { # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { - [[ "${SSH:-no}" != "yes" ]] && return 0 + [[ "${SSH:-no}" != "yes" ]] && return 0 - # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) - : "${SSH_KEYS_FILE:=}" + # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) + : "${SSH_KEYS_FILE:=}" - 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}" + 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 - fi - - # Fallback: nichts ausgewählt - msg_warn "No SSH keys to install (skipping)." - return 0 } # ------------------------------------------------------------------------------ @@ -346,55 +346,55 @@ install_ssh_keys_into_ct() { # - 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 + 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 + 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 + 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 ' + # 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 + 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))) + # 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 - fi - FOUND_HOST_KEY_COUNT="$total" - ( - IFS=: - echo "${files[*]}" - ) + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) } # ============================================================================== @@ -407,14 +407,14 @@ find_host_ssh_keys() { # - Converts IP address to integer and vice versa for range iteration # ------------------------------------------------------------------------------ ip_to_int() { - local IFS=. - read -r i1 i2 i3 i4 <<<"$1" - echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) + local IFS=. + read -r i1 i2 i3 i4 <<<"$1" + echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) } int_to_ip() { - local ip=$1 - echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" + local ip=$1 + echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" } # ------------------------------------------------------------------------------ @@ -426,39 +426,39 @@ int_to_ip() { # - Sets NET_RESOLVED to the resolved IP or empty on failure # ------------------------------------------------------------------------------ resolve_ip_from_range() { - local range="$1" - 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_start ip_end + local range="$1" + 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_start ip_end - # Parse range: "10.0.0.1/24-10.0.0.10/24" - ip_start="${range%%-*}" - ip_end="${range##*-}" + # Parse range: "10.0.0.1/24-10.0.0.10/24" + ip_start="${range%%-*}" + ip_end="${range##*-}" - if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then - NET_RESOLVED="" - return 1 - fi - - local ip1="${ip_start%%/*}" - local ip2="${ip_end%%/*}" - local cidr="${ip_start##*/}" - - local start_int=$(ip_to_int "$ip1") - local end_int=$(ip_to_int "$ip2") - - for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do - local ip=$(int_to_ip $ip_int) - msg_info "Checking IP: $ip" - if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then - NET_RESOLVED="$ip/$cidr" - msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" - return 0 + if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then + NET_RESOLVED="" + return 1 fi - done - NET_RESOLVED="" - msg_error "No free IP found in range $range" - return 1 + local ip1="${ip_start%%/*}" + local ip2="${ip_end%%/*}" + local cidr="${ip_start##*/}" + + local start_int=$(ip_to_int "$ip1") + local end_int=$(ip_to_int "$ip2") + + for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do + local ip=$(int_to_ip $ip_int) + msg_info "Checking IP: $ip" + if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then + NET_RESOLVED="$ip/$cidr" + msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" + return 0 + fi + done + + NET_RESOLVED="" + msg_error "No free IP found in range $range" + return 1 } # ------------------------------------------------------------------------------ @@ -468,17 +468,17 @@ resolve_ip_from_range() { # - Returns 0 if it's a range, 1 otherwise # ------------------------------------------------------------------------------ is_ip_range() { - local value="$1" - local ip_start ip_end - if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then - local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' - ip_start="${value%%-*}" - ip_end="${value##*-}" - if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then - return 0 + local value="$1" + local ip_start ip_end + if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then + local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' + ip_start="${value%%-*}" + ip_end="${value##*-}" + if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then + return 0 + fi fi - fi - return 1 + return 1 } # ============================================================================== @@ -493,53 +493,53 @@ is_ip_range() { # - Arguments: vars_file, key (var_container_storage/var_template_storage), value # ------------------------------------------------------------------------------ _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" + # $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 + # $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") + 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 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 + 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" + _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 + # 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 - # Silent operation - no output message + # Silent operation - no output message } # ============================================================================== @@ -556,102 +556,102 @@ choose_and_set_storage_for_file() { # - Sets up container type, resources, network, SSH, features, and tags # ------------------------------------------------------------------------------ base_settings() { - # Default Settings - CT_TYPE=${var_unprivileged:-"1"} + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} - # 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}" + # 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}" + # 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 - 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}" + 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 - 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}" + 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 - 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} - HN=${var_hostname:-$NSAPP} - BRG=${var_brg:-"vmbr0"} - NET=${var_net:-"dhcp"} + 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} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} - # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) - if is_ip_range "$NET"; then - msg_info "Scanning IP range: $NET" - if resolve_ip_from_range "$NET"; then - NET="$NET_RESOLVED" - else - msg_error "Could not find free IP in range. Falling back to DHCP." - NET="dhcp" + # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) + if is_ip_range "$NET"; then + msg_info "Scanning IP range: $NET" + if resolve_ip_from_range "$NET"; then + NET="$NET_RESOLVED" + else + msg_error "Could not find free IP in range. Falling back to DHCP." + NET="dhcp" + fi fi - fi - 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:-""} + 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:-""} - # 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_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" - APT_CACHER="" - APT_CACHER_IP="" - else - msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" + # 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_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" + APT_CACHER="" + APT_CACHER_IP="" + else + msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" + fi fi - fi - 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}"} + 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}"} - # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) - ENABLE_GPU=${var_gpu:-"no"} - ENABLE_NESTING=${var_nesting:-"1"} - ENABLE_KEYCTL=${var_keyctl:-"0"} - ENABLE_MKNOD=${var_mknod:-"0"} - MOUNT_FS=${var_mount_fs:-""} - PROTECT_CT=${var_protection:-"no"} - CT_TIMEZONE=${var_timezone:-"$timezone"} - [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones + # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) + ENABLE_GPU=${var_gpu:-"no"} + ENABLE_NESTING=${var_nesting:-"1"} + ENABLE_KEYCTL=${var_keyctl:-"0"} + ENABLE_MKNOD=${var_mknod:-"0"} + MOUNT_FS=${var_mount_fs:-""} + PROTECT_CT=${var_protection:-"no"} + CT_TIMEZONE=${var_timezone:-"$timezone"} + [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones - # 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 } # ------------------------------------------------------------------------------ @@ -662,59 +662,59 @@ base_settings() { # - Only loads whitelisted var_* keys # ------------------------------------------------------------------------------ load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" - # Allowed var_* keys - local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # Allowed var_* keys + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) - # Whitelist check helper - _is_whitelisted() { - local k="$1" w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 - } + # Whitelist check helper + _is_whitelisted() { + local k="$1" w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } - 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]}" + 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 "$var_key" || continue + [[ "$var_key" != var_* ]] && continue + _is_whitelisted "$var_key" || continue - # Strip inline comments (everything after unquoted #) - # Handle: var=value # comment OR var="value" # comment - if [[ ! "$var_val" =~ ^[\"\'] ]]; then - # Unquoted value: strip from first # - var_val="${var_val%%#*}" - fi + # Strip inline comments (everything after unquoted #) + # Handle: var=value # comment OR var="value" # comment + if [[ ! "$var_val" =~ ^[\"\'] ]]; then + # Unquoted value: strip from first # + var_val="${var_val%%#*}" + fi - # Strip quotes - if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then - var_val="${BASH_REMATCH[1]}" - elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then - var_val="${BASH_REMATCH[1]}" - fi + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi - # Trim trailing whitespace - var_val="${var_val%"${var_val##*[![:space:]]}"}" + # Trim trailing whitespace + var_val="${var_val%"${var_val##*[![:space:]]}"}" - # Set only if not already exported - [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" - fi - done <"$file" - msg_ok "Loaded ${file}" + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + fi + done <"$file" + msg_ok "Loaded ${file}" } # ------------------------------------------------------------------------------ @@ -727,56 +727,56 @@ load_vars_file() { # - Calls base_settings "$VERBOSE" and echo_default # ------------------------------------------------------------------------------ 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_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # 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_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone 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 - } + # 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 - 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 + # 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" - local canonical="/usr/local/community-scripts/default.vars" - # Silent creation - no msg_info output - mkdir -p /usr/local/community-scripts + # 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 - # Pick storages before writing the file (always ask unless only one) - # Create a minimal temp file to write into - : >"$canonical" + local canonical="/usr/local/community-scripts/default.vars" + # Silent creation - no msg_info output + mkdir -p /usr/local/community-scripts - # Base content (no var_ctid / var_hostname here) - cat >"$canonical" <<'EOF' + # 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. @@ -831,47 +831,47 @@ var_verbose=no # 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 + # 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" - # Silent creation - no output message - } + chmod 0644 "$canonical" + # Silent creation - no output message + } - # 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 - } + # 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 + } - # 1) Ensure file exists - _ensure_default_vars + # 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" + # 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 + # 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 User Defaults (default.vars) on node $PVEHOST_NAME${CL}" - echo_default + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using User Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default } # ------------------------------------------------------------------------------ @@ -882,8 +882,8 @@ EOF # ------------------------------------------------------------------------------ get_app_defaults_path() { - local n="${NSAPP:-${APP,,}}" - echo "/usr/local/community-scripts/defaults/${n}.vars" + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" } # ------------------------------------------------------------------------------ @@ -896,32 +896,32 @@ 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_disk var_fuse var_gpu - 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 - ) + # 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_disk var_fuse var_gpu + 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 + ) fi # Global whitelist check function (used by _load_vars_file_to_map and others) _is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 + 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" + # 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 @@ -929,190 +929,190 @@ _sanitize_value() { # 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" + 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 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 out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" - local found_change=0 + 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 + # 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 - 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" + 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)" + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" - # NET/GW - _net="${NET:-}" - _gate="" - case "${GATE:-}" in - ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; - esac + # 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 + # 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 + # 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 + # 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}" - _nesting="${ENABLE_NESTING:-1}" - _keyctl="${ENABLE_KEYCTL:-0}" - _mknod="${ENABLE_MKNOD:-0}" - _mount_fs="${ALLOW_MOUNT_FS:-}" - _protect="${PROTECT_CT:-no}" - _timezone="${CT_TIMEZONE:-}" - _tags="${TAGS:-}" - _verbose="${VERBOSE:-no}" + # 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}" + _nesting="${ENABLE_NESTING:-1}" + _keyctl="${ENABLE_KEYCTL:-0}" + _mknod="${ENABLE_MKNOD:-0}" + _mount_fs="${ALLOW_MOUNT_FS:-}" + _protect="${PROTECT_CT:-no}" + _timezone="${CT_TIMEZONE:-}" + _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}" + # 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:-}}" + # 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 "# 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")" + 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 "${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")" - # var_ipv6_static removed - static IPs are unique, can't be default + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + # 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")" + [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" - [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" - [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" - [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" - [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" - [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" - [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" + [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" + [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" + [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" + [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" + [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" + [ -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")" - } >"$tmpf" + [ -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" + echo "$tmpf" } # ------------------------------------------------------------------------------ @@ -1123,103 +1123,103 @@ _build_current_app_vars_tmp() { # - 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)" + 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")" + # 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}" + # 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." + break + ;; + esac + done + 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." - break - ;; - esac - done - - rm -f "$new_tmp" "$diff_tmp" } ensure_storage_selection_for_vars_file() { - local 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-) + # 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 + 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 + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container - # Silent operation - no output message + # Silent operation - no output message } 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" + 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" } # ============================================================================== @@ -1235,785 +1235,785 @@ ensure_global_default_vars_file() { # - Allows user to customize all container settings # ------------------------------------------------------------------------------ advanced_settings() { - # Enter alternate screen buffer to prevent flicker between dialogs - tput smcup 2>/dev/null || true - trap 'tput rmcup 2>/dev/null || true' RETURN + # Enter alternate screen buffer to prevent flicker between dialogs + tput smcup 2>/dev/null || true + trap 'tput rmcup 2>/dev/null || true' RETURN - # Initialize defaults - TAGS="community-script;${var_tags:-}" - local STEP=1 - local MAX_STEP=28 + # Initialize defaults + TAGS="community-script;${var_tags:-}" + local STEP=1 + local MAX_STEP=28 - # Store values for back navigation - inherit from var_* app defaults - local _ct_type="${var_unprivileged:-1}" - local _pw="" - local _pw_display="Automatic Login" - local _ct_id="$NEXTID" - local _hostname="$NSAPP" - local _disk_size="${var_disk:-4}" - local _core_count="${var_cpu:-1}" - local _ram_size="${var_ram:-1024}" - local _bridge="${var_brg:-vmbr0}" - local _net="${var_net:-dhcp}" - local _gate="${var_gateway:-}" - local _ipv6_method="${var_ipv6_method:-auto}" - local _ipv6_addr="" - local _ipv6_gate="" - local _apt_cacher="${var_apt_cacher:-no}" - local _apt_cacher_ip="${var_apt_cacher_ip:-}" - local _mtu="${var_mtu:-}" - local _sd="${var_searchdomain:-}" - local _ns="${var_ns:-}" - local _mac="${var_mac:-}" - local _vlan="${var_vlan:-}" - local _tags="$TAGS" - local _enable_fuse="${var_fuse:-no}" - local _enable_tun="${var_tun:-no}" - local _enable_gpu="${var_gpu:-no}" - local _enable_nesting="${var_nesting:-1}" - local _verbose="${var_verbose:-no}" - local _enable_keyctl="${var_keyctl:-0}" - local _enable_mknod="${var_mknod:-0}" - local _mount_fs="${var_mount_fs:-}" - local _protect_ct="${var_protection:-no}" + # Store values for back navigation - inherit from var_* app defaults + local _ct_type="${var_unprivileged:-1}" + local _pw="" + local _pw_display="Automatic Login" + local _ct_id="$NEXTID" + local _hostname="$NSAPP" + local _disk_size="${var_disk:-4}" + local _core_count="${var_cpu:-1}" + local _ram_size="${var_ram:-1024}" + local _bridge="${var_brg:-vmbr0}" + local _net="${var_net:-dhcp}" + local _gate="${var_gateway:-}" + local _ipv6_method="${var_ipv6_method:-auto}" + local _ipv6_addr="" + local _ipv6_gate="" + local _apt_cacher="${var_apt_cacher:-no}" + local _apt_cacher_ip="${var_apt_cacher_ip:-}" + local _mtu="${var_mtu:-}" + local _sd="${var_searchdomain:-}" + local _ns="${var_ns:-}" + local _mac="${var_mac:-}" + local _vlan="${var_vlan:-}" + local _tags="$TAGS" + local _enable_fuse="${var_fuse:-no}" + local _enable_tun="${var_tun:-no}" + local _enable_gpu="${var_gpu:-no}" + local _enable_nesting="${var_nesting:-1}" + local _verbose="${var_verbose:-no}" + local _enable_keyctl="${var_keyctl:-0}" + local _enable_mknod="${var_mknod:-0}" + local _mount_fs="${var_mount_fs:-}" + local _protect_ct="${var_protection:-no}" - # Detect host timezone for default (if not set via var_timezone) - local _host_timezone="" - if command -v timedatectl >/dev/null 2>&1; then - _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") - elif [ -f /etc/timezone ]; then - _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") - fi - local _ct_timezone="${var_timezone:-$_host_timezone}" - - # Helper to show current progress - show_progress() { - local current=$1 - local total=$MAX_STEP - echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" - } - - # Detect available bridges (do this once) - local BRIDGES="" - local BRIDGE_MENU_OPTIONS=() - _detect_bridges() { - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) - BRIDGES="" - local OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true - if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - local start=$(echo "${pair}" | cut -d':' -f1) - local end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - local 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) - - # Build bridge menu - BRIDGE_MENU_OPTIONS=() - if [[ -n "$BRIDGES" ]]; then - while IFS= read -r bridge; do - if [[ -n "$bridge" ]]; then - local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') - BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") - fi - done <<<"$BRIDGES" + # Detect host timezone for default (if not set via var_timezone) + local _host_timezone="" + if command -v timedatectl >/dev/null 2>&1; then + _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") + elif [ -f /etc/timezone ]; then + _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") fi - } - _detect_bridges + local _ct_timezone="${var_timezone:-$_host_timezone}" - # Main wizard loop - while [ $STEP -le $MAX_STEP ]; do - case $STEP in + # Helper to show current progress + show_progress() { + local current=$1 + local total=$MAX_STEP + echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" + } - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 1: Container Type - # ═══════════════════════════════════════════════════════════════════════════ - 1) - local default_on="ON" - local default_off="OFF" - [[ "$_ct_type" == "0" ]] && { - default_on="OFF" - default_off="ON" - } - - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TYPE" \ - --ok-button "Next" --cancel-button "Exit" \ - --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ - "1" "Unprivileged (recommended)" $default_on \ - "0" "Privileged" $default_off \ - 3>&1 1>&2 2>&3); then - [[ -n "$result" ]] && _ct_type="$result" - ((STEP++)) - else - exit_script - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 2: Root Password - # ═══════════════════════════════════════════════════════════════════════════ - 2) - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "ROOT PASSWORD" \ - --ok-button "Next" --cancel-button "Back" \ - --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ - 3>&1 1>&2 2>&3); then - - if [[ -z "$PW1" ]]; then - _pw="" - _pw_display="Automatic Login" - ((STEP++)) - elif [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - elif ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 - else - # Verify password - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "PASSWORD VERIFICATION" \ - --ok-button "Confirm" --cancel-button "Back" \ - --passwordbox "\nVerify Root Password" 10 58 \ - 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - _pw="-password $PW1" - _pw_display="********" - ((STEP++)) - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 + # Detect available bridges (do this once) + local BRIDGES="" + local BRIDGE_MENU_OPTIONS=() + _detect_bridges() { + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) + BRIDGES="" + local OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true + if [ -f "${iface_indexes_tmpfile}" ]; then + while read -r pair; do + local start=$(echo "${pair}" | cut -d':' -f1) + local end=$(echo "${pair}" | cut -d':' -f2) + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + local 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 - else - ((STEP--)) - fi - fi - else - ((STEP--)) - fi - ;; + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 3: Container ID - # ═══════════════════════════════════════════════════════════════════════════ - 3) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER ID" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ - 3>&1 1>&2 2>&3); then - _ct_id="${result:-$NEXTID}" - ((STEP++)) - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 4: Hostname - # ═══════════════════════════════════════════════════════════════════════════ - 4) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "HOSTNAME" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ - 3>&1 1>&2 2>&3); then - local hn_test="${result:-$NSAPP}" - hn_test=$(echo "${hn_test,,}" | tr -d ' ') - if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - _hostname="$hn_test" - ((STEP++)) - else - whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 5: Disk Size - # ═══════════════════════════════════════════════════════════════════════════ - 5) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DISK SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ - 3>&1 1>&2 2>&3); then - local disk_test="${result:-$var_disk}" - if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then - _disk_size="$disk_test" - ((STEP++)) - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 6: CPU Cores - # ═══════════════════════════════════════════════════════════════════════════ - 6) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CPU CORES" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ - 3>&1 1>&2 2>&3); then - local cpu_test="${result:-$var_cpu}" - if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then - _core_count="$cpu_test" - ((STEP++)) - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 7: RAM Size - # ═══════════════════════════════════════════════════════════════════════════ - 7) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "RAM SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ - 3>&1 1>&2 2>&3); then - local ram_test="${result:-$var_ram}" - if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then - _ram_size="$ram_test" - ((STEP++)) - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 8: Network Bridge - # ═══════════════════════════════════════════════════════════════════════════ - 8) - if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then - _bridge="vmbr0" - ((STEP++)) - else - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NETWORK BRIDGE" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect network bridge:" 16 58 6 \ - "${BRIDGE_MENU_OPTIONS[@]}" \ - 3>&1 1>&2 2>&3); then - _bridge="${result:-vmbr0}" - ((STEP++)) - else - ((STEP--)) - fi - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 9: IPv4 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 9) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv4 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3); then - - if [[ "$result" == "static" ]]; then - # Get static IP - local static_ip - if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "STATIC IPv4 ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - # Get gateway - local gateway_ip - if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GATEWAY IP" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Gateway IP address" 10 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - _net="$static_ip" - _gate=",gw=$gateway_ip" - ((STEP++)) - else - whiptail --msgbox "Invalid Gateway IP format." 8 58 + # Build bridge menu + BRIDGE_MENU_OPTIONS=() + if [[ -n "$BRIDGES" ]]; then + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') + BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") fi - fi + done <<<"$BRIDGES" + fi + } + _detect_bridges + + # Main wizard loop + while [ $STEP -le $MAX_STEP ]; do + case $STEP in + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 1: Container Type + # ═══════════════════════════════════════════════════════════════════════════ + 1) + local default_on="ON" + local default_off="OFF" + [[ "$_ct_type" == "0" ]] && { + default_on="OFF" + default_off="ON" + } + + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TYPE" \ + --ok-button "Next" --cancel-button "Exit" \ + --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ + "1" "Unprivileged (recommended)" $default_on \ + "0" "Privileged" $default_off \ + 3>&1 1>&2 2>&3); then + [[ -n "$result" ]] && _ct_type="$result" + ((STEP++)) else - whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 + exit_script fi - fi - else - _net="dhcp" - _gate="" - ((STEP++)) - fi - else - ((STEP--)) - fi - ;; + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 10: IPv6 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 10) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv6 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv6 Address Management:" 16 70 5 \ - "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ - "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ - "static" "Static - Manual IPv6 address configuration" \ - "none" "None - No IPv6 assignment (most containers)" \ - "disable" "Fully Disabled - (breaks some services)" \ - 3>&1 1>&2 2>&3); then + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 2: Root Password + # ═══════════════════════════════════════════════════════════════════════════ + 2) + if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "ROOT PASSWORD" \ + --ok-button "Next" --cancel-button "Back" \ + --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ + 3>&1 1>&2 2>&3); then - _ipv6_method="$result" - case "$result" in - static) - local ipv6_addr - if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "STATIC IPv6 ADDRESS" \ - --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - _ipv6_addr="$ipv6_addr" - # Optional gateway - _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "IPv6 GATEWAY" \ - --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ - 3>&1 1>&2 2>&3) || true - ((STEP++)) + if [[ -z "$PW1" ]]; then + _pw="" + _pw_display="Automatic Login" + ((STEP++)) + elif [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + elif ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + else + # Verify password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "PASSWORD VERIFICATION" \ + --ok-button "Confirm" --cancel-button "Back" \ + --passwordbox "\nVerify Root Password" 10 58 \ + 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + _pw="-password $PW1" + _pw_display="********" + ((STEP++)) + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + ((STEP--)) + fi + fi else - whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 + ((STEP--)) fi - fi - ;; - dhcp) - _ipv6_addr="dhcp" - _ipv6_gate="" - ((STEP++)) - ;; - disable) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - none) - _ipv6_addr="none" - _ipv6_gate="" - ((STEP++)) - ;; - *) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - esac - else - ((STEP--)) - fi - ;; + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 11: MTU Size - # ═══════════════════════════════════════════════════════════════════════════ - 11) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MTU SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mtu="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 3: Container ID + # ═══════════════════════════════════════════════════════════════════════════ + 3) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER ID" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ + 3>&1 1>&2 2>&3); then + _ct_id="${result:-$NEXTID}" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 12: DNS Search Domain - # ═══════════════════════════════════════════════════════════════════════════ - 12) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SEARCH DOMAIN" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _sd="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 4: Hostname + # ═══════════════════════════════════════════════════════════════════════════ + 4) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "HOSTNAME" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ + 3>&1 1>&2 2>&3); then + local hn_test="${result:-$NSAPP}" + hn_test=$(echo "${hn_test,,}" | tr -d ' ') + if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + _hostname="$hn_test" + ((STEP++)) + else + whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 13: DNS Server - # ═══════════════════════════════════════════════════════════════════════════ - 13) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SERVER" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _ns="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 5: Disk Size + # ═══════════════════════════════════════════════════════════════════════════ + 5) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DISK SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ + 3>&1 1>&2 2>&3); then + local disk_test="${result:-$var_disk}" + if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then + _disk_size="$disk_test" + ((STEP++)) + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 14: MAC Address - # ═══════════════════════════════════════════════════════════════════════════ - 14) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MAC ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mac="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 6: CPU Cores + # ═══════════════════════════════════════════════════════════════════════════ + 6) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CPU CORES" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ + 3>&1 1>&2 2>&3); then + local cpu_test="${result:-$var_cpu}" + if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then + _core_count="$cpu_test" + ((STEP++)) + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 15: VLAN Tag - # ═══════════════════════════════════════════════════════════════════════════ - 15) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VLAN TAG" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _vlan="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 7: RAM Size + # ═══════════════════════════════════════════════════════════════════════════ + 7) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "RAM SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ + 3>&1 1>&2 2>&3); then + local ram_test="${result:-$var_ram}" + if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then + _ram_size="$ram_test" + ((STEP++)) + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 16: Tags - # ═══════════════════════════════════════════════════════════════════════════ - 16) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TAGS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ - 3>&1 1>&2 2>&3); then - _tags="${result:-;}" - _tags=$(echo "$_tags" | tr -d '[:space:]') - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 8: Network Bridge + # ═══════════════════════════════════════════════════════════════════════════ + 8) + if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then + _bridge="vmbr0" + ((STEP++)) + else + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NETWORK BRIDGE" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect network bridge:" 16 58 6 \ + "${BRIDGE_MENU_OPTIONS[@]}" \ + 3>&1 1>&2 2>&3); then + _bridge="${result:-vmbr0}" + ((STEP++)) + else + ((STEP--)) + fi + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 17: SSH Settings - # ═══════════════════════════════════════════════════════════════════════════ - 17) - configure_ssh_settings "Step $STEP/$MAX_STEP" - # configure_ssh_settings handles its own flow, always advance - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 9: IPv4 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 9) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv4 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3); then - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 18: FUSE Support - # ═══════════════════════════════════════════════════════════════════════════ - 18) - local fuse_default_flag="--defaultno" - [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" + if [[ "$result" == "static" ]]; then + # Get static IP + local static_ip + if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "STATIC IPv4 ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + # Get gateway + local gateway_ip + if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GATEWAY IP" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nEnter Gateway IP address" 10 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + _net="$static_ip" + _gate=",gw=$gateway_ip" + ((STEP++)) + else + whiptail --msgbox "Invalid Gateway IP format." 8 58 + fi + fi + else + whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 + fi + fi + else + _net="dhcp" + _gate="" + ((STEP++)) + fi + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "FUSE SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $fuse_default_flag \ - --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then - _enable_fuse="yes" - else - if [ $? -eq 1 ]; then - _enable_fuse="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 10: IPv6 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 10) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv6 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv6 Address Management:" 16 70 5 \ + "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ + "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ + "static" "Static - Manual IPv6 address configuration" \ + "none" "None - No IPv6 assignment (most containers)" \ + "disable" "Fully Disabled - (breaks some services)" \ + 3>&1 1>&2 2>&3); then - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 19: TUN/TAP Support - # ═══════════════════════════════════════════════════════════════════════════ - 19) - local tun_default_flag="--defaultno" - [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" + _ipv6_method="$result" + case "$result" in + static) + local ipv6_addr + if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "STATIC IPv6 ADDRESS" \ + --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + _ipv6_addr="$ipv6_addr" + # Optional gateway + _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "IPv6 GATEWAY" \ + --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ + 3>&1 1>&2 2>&3) || true + ((STEP++)) + else + whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 + fi + fi + ;; + dhcp) + _ipv6_addr="dhcp" + _ipv6_gate="" + ((STEP++)) + ;; + disable) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + none) + _ipv6_addr="none" + _ipv6_gate="" + ((STEP++)) + ;; + *) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + esac + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "TUN/TAP SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $tun_default_flag \ - --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then - _enable_tun="yes" - else - if [ $? -eq 1 ]; then - _enable_tun="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 11: MTU Size + # ═══════════════════════════════════════════════════════════════════════════ + 11) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MTU SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mtu="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 20: Nesting Support - # ═══════════════════════════════════════════════════════════════════════════ - 20) - local nesting_default_flag="" - [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 12: DNS Search Domain + # ═══════════════════════════════════════════════════════════════════════════ + 12) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SEARCH DOMAIN" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _sd="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NESTING SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $nesting_default_flag \ - --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then - _enable_nesting="1" - else - if [ $? -eq 1 ]; then - _enable_nesting="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 13: DNS Server + # ═══════════════════════════════════════════════════════════════════════════ + 13) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SERVER" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _ns="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 21: GPU Passthrough - # ═══════════════════════════════════════════════════════════════════════════ - 21) - local gpu_default_flag="--defaultno" - [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 14: MAC Address + # ═══════════════════════════════════════════════════════════════════════════ + 14) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MAC ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mac="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GPU PASSTHROUGH" \ - --ok-button "Next" --cancel-button "Back" \ - $gpu_default_flag \ - --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then - _enable_gpu="yes" - else - if [ $? -eq 1 ]; then - _enable_gpu="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 15: VLAN Tag + # ═══════════════════════════════════════════════════════════════════════════ + 15) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VLAN TAG" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _vlan="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 22: Keyctl Support (Docker/systemd) - # ═══════════════════════════════════════════════════════════════════════════ - 22) - local keyctl_default_flag="--defaultno" - [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 16: Tags + # ═══════════════════════════════════════════════════════════════════════════ + 16) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TAGS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ + 3>&1 1>&2 2>&3); then + _tags="${result:-;}" + _tags=$(echo "$_tags" | tr -d '[:space:]') + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "KEYCTL SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $keyctl_default_flag \ - --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then - _enable_keyctl="1" - else - if [ $? -eq 1 ]; then - _enable_keyctl="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 17: SSH Settings + # ═══════════════════════════════════════════════════════════════════════════ + 17) + configure_ssh_settings "Step $STEP/$MAX_STEP" + # configure_ssh_settings handles its own flow, always advance + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 23: APT Cacher Proxy - # ═══════════════════════════════════════════════════════════════════════════ - 23) - local apt_cacher_default_flag="--defaultno" - [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 18: FUSE Support + # ═══════════════════════════════════════════════════════════════════════════ + 18) + local fuse_default_flag="--defaultno" + [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER PROXY" \ - --ok-button "Next" --cancel-button "Back" \ - $apt_cacher_default_flag \ - --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then - _apt_cacher="yes" - # Ask for IP if enabled - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER IP" \ - --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ - 3>&1 1>&2 2>&3); then - _apt_cacher_ip="$result" - fi - else - if [ $? -eq 1 ]; then - _apt_cacher="no" - _apt_cacher_ip="" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "FUSE SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $fuse_default_flag \ + --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then + _enable_fuse="yes" + else + if [ $? -eq 1 ]; then + _enable_fuse="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 24: Container Timezone - # ═══════════════════════════════════════════════════════════════════════════ - 24) - local tz_hint="$_ct_timezone" - [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 19: TUN/TAP Support + # ═══════════════════════════════════════════════════════════════════════════ + 19) + local tun_default_flag="--defaultno" + [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TIMEZONE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ - 3>&1 1>&2 2>&3); then - _ct_timezone="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "TUN/TAP SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $tun_default_flag \ + --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then + _enable_tun="yes" + else + if [ $? -eq 1 ]; then + _enable_tun="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 25: Container Protection - # ═══════════════════════════════════════════════════════════════════════════ - 25) - local protect_default_flag="--defaultno" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 20: Nesting Support + # ═══════════════════════════════════════════════════════════════════════════ + 20) + local nesting_default_flag="" + [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER PROTECTION" \ - --ok-button "Next" --cancel-button "Back" \ - $protect_default_flag \ - --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then - _protect_ct="yes" - else - if [ $? -eq 1 ]; then - _protect_ct="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NESTING SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $nesting_default_flag \ + --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then + _enable_nesting="1" + else + if [ $? -eq 1 ]; then + _enable_nesting="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 26: Device Node Creation (mknod) - # ═══════════════════════════════════════════════════════════════════════════ - 26) - local mknod_default_flag="--defaultno" - [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 21: GPU Passthrough + # ═══════════════════════════════════════════════════════════════════════════ + 21) + local gpu_default_flag="--defaultno" + [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DEVICE NODE CREATION" \ - --ok-button "Next" --cancel-button "Back" \ - $mknod_default_flag \ - --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then - _enable_mknod="1" - else - if [ $? -eq 1 ]; then - _enable_mknod="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GPU PASSTHROUGH" \ + --ok-button "Next" --cancel-button "Back" \ + $gpu_default_flag \ + --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then + _enable_gpu="yes" + else + if [ $? -eq 1 ]; then + _enable_gpu="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 27: Mount Filesystems - # ═══════════════════════════════════════════════════════════════════════════ - 27) - local mount_hint="" - [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 22: Keyctl Support (Docker/systemd) + # ═══════════════════════════════════════════════════════════════════════════ + 22) + local keyctl_default_flag="--defaultno" + [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MOUNT FILESYSTEMS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ - 3>&1 1>&2 2>&3); then - _mount_fs="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "KEYCTL SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $keyctl_default_flag \ + --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then + _enable_keyctl="1" + else + if [ $? -eq 1 ]; then + _enable_keyctl="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 28: Verbose Mode & Confirmation - # ═══════════════════════════════════════════════════════════════════════════ - 28) - local verbose_default_flag="--defaultno" - [[ "$_verbose" == "yes" ]] && verbose_default_flag="" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 23: APT Cacher Proxy + # ═══════════════════════════════════════════════════════════════════════════ + 23) + local apt_cacher_default_flag="--defaultno" + [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VERBOSE MODE" \ - $verbose_default_flag \ - --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then - _verbose="yes" - else - _verbose="no" - fi - # Build summary - local ct_type_desc="Unprivileged" - [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER PROXY" \ + --ok-button "Next" --cancel-button "Back" \ + $apt_cacher_default_flag \ + --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then + _apt_cacher="yes" + # Ask for IP if enabled + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER IP" \ + --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ + 3>&1 1>&2 2>&3); then + _apt_cacher_ip="$result" + fi + else + if [ $? -eq 1 ]; then + _apt_cacher="no" + _apt_cacher_ip="" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - local nesting_desc="Disabled" - [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 24: Container Timezone + # ═══════════════════════════════════════════════════════════════════════════ + 24) + local tz_hint="$_ct_timezone" + [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" - local keyctl_desc="Disabled" - [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TIMEZONE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ + 3>&1 1>&2 2>&3); then + _ct_timezone="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - local protect_desc="No" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 25: Container Protection + # ═══════════════════════════════════════════════════════════════════════════ + 25) + local protect_default_flag="--defaultno" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" - local tz_display="${_ct_timezone:-Host TZ}" - local apt_display="${_apt_cacher:-no}" - [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER PROTECTION" \ + --ok-button "Next" --cancel-button "Back" \ + $protect_default_flag \ + --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then + _protect_ct="yes" + else + if [ $? -eq 1 ]; then + _protect_ct="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - local summary="Container Type: $ct_type_desc + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 26: Device Node Creation (mknod) + # ═══════════════════════════════════════════════════════════════════════════ + 26) + local mknod_default_flag="--defaultno" + [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" + + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DEVICE NODE CREATION" \ + --ok-button "Next" --cancel-button "Back" \ + $mknod_default_flag \ + --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then + _enable_mknod="1" + else + if [ $? -eq 1 ]; then + _enable_mknod="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 27: Mount Filesystems + # ═══════════════════════════════════════════════════════════════════════════ + 27) + local mount_hint="" + [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" + + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MOUNT FILESYSTEMS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ + 3>&1 1>&2 2>&3); then + _mount_fs="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 28: Verbose Mode & Confirmation + # ═══════════════════════════════════════════════════════════════════════════ + 28) + local verbose_default_flag="--defaultno" + [[ "$_verbose" == "yes" ]] && verbose_default_flag="" + + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VERBOSE MODE" \ + $verbose_default_flag \ + --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then + _verbose="yes" + else + _verbose="no" + fi + # Build summary + local ct_type_desc="Unprivileged" + [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" + + local nesting_desc="Disabled" + [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" + + local keyctl_desc="Disabled" + [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" + + local protect_desc="No" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" + + local tz_display="${_ct_timezone:-Host TZ}" + local apt_display="${_apt_cacher:-no}" + [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" + + local summary="Container Type: $ct_type_desc Container ID: $_ct_id Hostname: $_hostname @@ -2037,105 +2037,105 @@ Advanced: APT Cacher: $apt_display Verbose: $_verbose" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONFIRM SETTINGS" \ - --ok-button "Create LXC" --cancel-button "Back" \ - --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then - ((STEP++)) - else - ((STEP--)) - fi - ;; - esac - done + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONFIRM SETTINGS" \ + --ok-button "Create LXC" --cancel-button "Back" \ + --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then + ((STEP++)) + else + ((STEP--)) + fi + ;; + esac + done - # ═══════════════════════════════════════════════════════════════════════════ - # Apply all collected values to global variables - # ═══════════════════════════════════════════════════════════════════════════ - CT_TYPE="$_ct_type" - PW="$_pw" - CT_ID="$_ct_id" - HN="$_hostname" - DISK_SIZE="$_disk_size" - CORE_COUNT="$_core_count" - RAM_SIZE="$_ram_size" - BRG="$_bridge" - NET="$_net" - GATE="$_gate" - IPV6_METHOD="$_ipv6_method" - IPV6_ADDR="$_ipv6_addr" - IPV6_GATE="$_ipv6_gate" - TAGS="$_tags" - ENABLE_FUSE="$_enable_fuse" - ENABLE_TUN="$_enable_tun" - ENABLE_GPU="$_enable_gpu" - ENABLE_NESTING="$_enable_nesting" - ENABLE_KEYCTL="$_enable_keyctl" - ENABLE_MKNOD="$_enable_mknod" - ALLOW_MOUNT_FS="$_mount_fs" - PROTECT_CT="$_protect_ct" - CT_TIMEZONE="$_ct_timezone" - APT_CACHER="$_apt_cacher" - APT_CACHER_IP="$_apt_cacher_ip" - VERBOSE="$_verbose" + # ═══════════════════════════════════════════════════════════════════════════ + # Apply all collected values to global variables + # ═══════════════════════════════════════════════════════════════════════════ + CT_TYPE="$_ct_type" + PW="$_pw" + CT_ID="$_ct_id" + HN="$_hostname" + DISK_SIZE="$_disk_size" + CORE_COUNT="$_core_count" + RAM_SIZE="$_ram_size" + BRG="$_bridge" + NET="$_net" + GATE="$_gate" + IPV6_METHOD="$_ipv6_method" + IPV6_ADDR="$_ipv6_addr" + IPV6_GATE="$_ipv6_gate" + TAGS="$_tags" + ENABLE_FUSE="$_enable_fuse" + ENABLE_TUN="$_enable_tun" + ENABLE_GPU="$_enable_gpu" + ENABLE_NESTING="$_enable_nesting" + ENABLE_KEYCTL="$_enable_keyctl" + ENABLE_MKNOD="$_enable_mknod" + ALLOW_MOUNT_FS="$_mount_fs" + PROTECT_CT="$_protect_ct" + CT_TIMEZONE="$_ct_timezone" + APT_CACHER="$_apt_cacher" + APT_CACHER_IP="$_apt_cacher_ip" + VERBOSE="$_verbose" - # Update var_* based on user choice (for functions that check these) - var_gpu="$_enable_gpu" - var_fuse="$_enable_fuse" - var_tun="$_enable_tun" - var_nesting="$_enable_nesting" - var_keyctl="$_enable_keyctl" - var_mknod="$_enable_mknod" - var_mount_fs="$_mount_fs" - var_protection="$_protect_ct" - var_timezone="$_ct_timezone" - var_apt_cacher="$_apt_cacher" - var_apt_cacher_ip="$_apt_cacher_ip" + # Update var_* based on user choice (for functions that check these) + var_gpu="$_enable_gpu" + var_fuse="$_enable_fuse" + var_tun="$_enable_tun" + var_nesting="$_enable_nesting" + var_keyctl="$_enable_keyctl" + var_mknod="$_enable_mknod" + var_mount_fs="$_mount_fs" + var_protection="$_protect_ct" + var_timezone="$_ct_timezone" + var_apt_cacher="$_apt_cacher" + var_apt_cacher_ip="$_apt_cacher_ip" - # Format optional values - [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" - [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" - [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" - [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" - [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" + # Format optional values + [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" + [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" + [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" + [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" + [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" - # Alpine UDHCPC fix - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - export SSH_KEYS_FILE + # Alpine UDHCPC fix + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + export SSH_KEYS_FILE - # Exit alternate screen buffer BEFORE displaying summary - # so the summary is visible in the main terminal - tput rmcup 2>/dev/null || true - trap - RETURN # Remove the trap since we already called rmcup + # Exit alternate screen buffer BEFORE displaying summary + # so the summary is visible in the main terminal + tput rmcup 2>/dev/null || true + trap - RETURN # Remove the trap since we already called rmcup - # Display final summary - echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" - echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" - [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" - echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" - [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" - [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" - [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + # Display final summary + echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" + echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" + [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" + echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" + [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" + [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" + [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" } # ============================================================================== @@ -2153,13 +2153,13 @@ Advanced: # - Sets global DIAGNOSTICS variable for API telemetry opt-in/out # ------------------------------------------------------------------------------ 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. @@ -2182,9 +2182,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. @@ -2207,34 +2207,34 @@ 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 } 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 + 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 - 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 } # ------------------------------------------------------------------------------ @@ -2245,25 +2245,25 @@ diagnostics_menu() { # - 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 [ "${var_gpu:-no}" == "yes" ]; then - echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" - fi - 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 " " + 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 [ "${var_gpu:-no}" == "yes" ]; then + echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" + fi + 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 " " } # ------------------------------------------------------------------------------ @@ -2275,196 +2275,196 @@ echo_default() { # - Applies chosen settings and triggers container build # ------------------------------------------------------------------------------ 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) - - # Get timezone using timedatectl (Debian 13+ compatible) - # Fallback to /etc/timezone for older systems - if command -v timedatectl >/dev/null 2>&1; then - timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") - elif [ -f /etc/timezone ]; then - timezone=$(cat /etc/timezone) - else - timezone="UTC" - fi - - # 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" "User 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 - - APPDEFAULTS_OPTION="$appdefaults_option" - SETTINGS_OPTION="$settings_option" - - # Main menu loop - allows returning from Settings - while true; do - 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" + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service fi - # --- 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)" - break - ;; - 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" - break - ;; - 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - defaults_target="/usr/local/community-scripts/default.vars" - break - ;; - "$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)" - break - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - "$SETTINGS_OPTION" | settings | SETTINGS) - settings_menu - # After settings menu, show main menu again - header_info - CHOICE="" - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac - done + NEXTID=$(pvesh get /cluster/nextid) - if [[ -n "$defaults_target" ]]; then - ensure_storage_selection_for_vars_file "$defaults_target" - fi + # Get timezone using timedatectl (Debian 13+ compatible) + # Fallback to /etc/timezone for older systems + if command -v timedatectl >/dev/null 2>&1; then + timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") + elif [ -f /etc/timezone ]; then + timezone=$(cat /etc/timezone) + else + timezone="UTC" + fi - if [[ "$run_maybe_offer" == "yes" ]]; then - maybe_offer_save_app_defaults - fi + # 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" "User 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 + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # Main menu loop - allows returning from Settings + while true; do + 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 + + # --- 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)" + break + ;; + 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" + break + ;; + 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + break + ;; + "$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)" + break + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + # After settings menu, show main menu again + header_info + CHOICE="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + done + + 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" + local vf="/usr/local/community-scripts/default.vars" - # Ensure file exists - if [[ ! -f "$vf" ]]; then - mkdir -p "$(dirname "$vf")" - touch "$vf" - fi + # 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" + # 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" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("3" "Edit App.vars for ${APP}") - settings_items+=("4" "Back to Main Menu") - else - settings_items+=("3" "Back to Main Menu") - fi + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("3" "Edit App.vars for ${APP}") + settings_items+=("4" "Back to Main Menu") + else + settings_items+=("3" "Back to Main Menu") + fi - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || exit_script + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || exit_script - case "$choice" in - 1) diagnostics_menu ;; - 2) nano /usr/local/community-scripts/default.vars ;; - 3) - if [ -f "$(get_app_defaults_path)" ]; then - nano "$(get_app_defaults_path)" - else - # Back was selected (no app.vars available) - return - fi - ;; - 4) - # Back to main menu - return - ;; - esac - done + case "$choice" in + 1) diagnostics_menu ;; + 2) nano /usr/local/community-scripts/default.vars ;; + 3) + if [ -f "$(get_app_defaults_path)" ]; then + nano "$(get_app_defaults_path)" + else + # Back was selected (no app.vars available) + return + fi + ;; + 4) + # Back to main menu + return + ;; + esac + done } # ------------------------------------------------------------------------------ @@ -2474,21 +2474,21 @@ settings_menu() { # - 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) + 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 + 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 - else - echo -e "" - fi } # ------------------------------------------------------------------------------ @@ -2498,18 +2498,18 @@ check_container_resources() { # - 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_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 - fi } # ------------------------------------------------------------------------------ @@ -2519,9 +2519,9 @@ check_container_storage() { # - 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 ' + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' /^[[:space:]]*#/ {next} /^[[:space:]]*$/ {next} # nackt: typ base64 [comment] @@ -2541,45 +2541,45 @@ ssh_extract_keys_from_file() { # - 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 + 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 + 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 + # 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}..." + 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 + 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 } # ------------------------------------------------------------------------------ @@ -2589,105 +2589,105 @@ ssh_build_choices_from_files() { # - 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[@]}" + 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() { - local step_info="${1:-}" - local backtitle="[dev] Proxmox VE Helper Scripts" - [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" + local step_info="${1:-}" + local backtitle="[dev] Proxmox VE Helper Scripts" + [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" + 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" + 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 "$backtitle" --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 "$backtitle" --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 + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "$backtitle" --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 "$backtitle" --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 "$backtitle" --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 "$backtitle" \ - --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 "$backtitle" \ - --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 "$backtitle" --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 + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "$backtitle" --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 - else - whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "$backtitle" \ + --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 "$backtitle" \ + --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 "$backtitle" --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 "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 + fi fi - else - whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 - fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" fi - ;; - none) - : - ;; - esac - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" - fi - - # Always show SSH access dialog - user should be able to enable SSH even without keys - if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi + # Always show SSH access dialog - user should be able to enable SSH even without keys + if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi } # ------------------------------------------------------------------------------ @@ -2699,39 +2699,39 @@ configure_ssh_settings() { # - 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) + 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 + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi } # ============================================================================== @@ -2752,256 +2752,256 @@ start() { # - Posts installation telemetry to API if diagnostics enabled # ------------------------------------------------------------------------------ build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" - # MAC - if [[ -n "$MAC" ]]; then - case "$MAC" in - ,hwaddr=*) NET_STRING+="$MAC" ;; - *) NET_STRING+=",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}" + + # 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 - fi - # IP (immer zwingend, Standard dhcp) - NET_STRING+=",ip=${NET:-dhcp}" + # Build FEATURES string based on container type and user choices + FEATURES="" - # Gateway - if [[ -n "$GATE" ]]; then - case "$GATE" in - ,gw=*) NET_STRING+="$GATE" ;; - *) NET_STRING+=",gw=$GATE" ;; - esac - fi + # Nesting support (user configurable, default enabled) + if [ "${ENABLE_NESTING:-1}" == "1" ]; then + FEATURES="nesting=1" + fi - # VLAN - if [[ -n "$VLAN" ]]; then - case "$VLAN" in - ,tag=*) NET_STRING+="$VLAN" ;; - *) NET_STRING+=",tag=$VLAN" ;; - esac - fi + # Keyctl for unprivileged containers (needed for Docker) + if [ "$CT_TYPE" == "1" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}keyctl=1" + fi - # MTU - if [[ -n "$MTU" ]]; then - case "$MTU" in - ,mtu=*) NET_STRING+="$MTU" ;; - *) NET_STRING+=",mtu=$MTU" ;; - esac - fi + if [ "$ENABLE_FUSE" == "yes" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}fuse=1" + 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 + # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly + # Key insight: Bash cannot export arrays, so we build the options as a string - # Build FEATURES string based on container type and user choices - FEATURES="" + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null - # Nesting support (user configurable, default enabled) - if [ "${ENABLE_NESTING:-1}" == "1" ]; then - FEATURES="nesting=1" - fi + # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - # Keyctl for unprivileged containers (needed for Docker) - if [ "$CT_TYPE" == "1" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}keyctl=1" - fi + # Core exports for install.func + 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" + 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" - if [ "$ENABLE_FUSE" == "yes" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}fuse=1" - fi + # DEV_MODE exports (optional, for debugging) + export BUILD_LOG="$BUILD_LOG" + export INSTALL_LOG="/root/.install-${SESSION_ID}.log" + export dev_mode="${dev_mode:-}" + export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" + export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" + export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" + export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" + export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" + export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" + export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" - # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly - # Key insight: Bash cannot export arrays, so we build the options as a string - - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null - - # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - - # Core exports for install.func - 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" - 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" - - # DEV_MODE exports (optional, for debugging) - export BUILD_LOG="$BUILD_LOG" - export INSTALL_LOG="/root/.install-${SESSION_ID}.log" - export dev_mode="${dev_mode:-}" - export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" - export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" - export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" - export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" - export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" - export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" - export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" - - # Build PCT_OPTIONS as multi-line string - PCT_OPTIONS_STRING=" -features $FEATURES + # Build PCT_OPTIONS as multi-line string + PCT_OPTIONS_STRING=" -features $FEATURES -hostname $HN -tags $TAGS" - # Add storage if specified - if [ -n "$SD" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Add storage if specified + if [ -n "$SD" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $SD" - fi + fi - # Add nameserver if specified - if [ -n "$NS" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Add nameserver if specified + if [ -n "$NS" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NS" - fi + fi - # Network configuration - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Network configuration + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NET_STRING -onboot 1 -cores $CORE_COUNT -memory $RAM_SIZE -unprivileged $CT_TYPE" - # Protection flag (if var_protection was set) - if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Protection flag (if var_protection was set) + if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -protection 1" - fi + fi - # Timezone flag (if var_timezone was set) - if [ -n "${CT_TIMEZONE:-}" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Timezone flag (if var_timezone was set) + if [ -n "${CT_TIMEZONE:-}" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -timezone $CT_TIMEZONE" - fi + fi - # Password (already formatted) - if [ -n "$PW" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Password (already formatted) + if [ -n "$PW" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $PW" - fi - - # Export as string (this works, unlike arrays!) - export PCT_OPTIONS="$PCT_OPTIONS_STRING" - export TEMPLATE_STORAGE="${var_template_storage:-}" - export CONTAINER_STORAGE="${var_container_storage:-}" - - # # DEBUG: Show final PCT_OPTIONS being exported - # echo "[DEBUG] PCT_OPTIONS to be exported:" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Calling create_lxc_container..." - - create_lxc_container || exit $? - - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - - # ============================================================================ - # GPU/USB PASSTHROUGH CONFIGURATION - # ============================================================================ - - # Check if GPU passthrough is enabled - # Returns true only if var_gpu is explicitly set to "yes" - # Can be set via: - # - Environment variable: var_gpu=yes bash -c "..." - # - CT script default: var_gpu="${var_gpu:-no}" - # - Advanced settings wizard - # - App defaults file: /usr/local/community-scripts/defaults/.vars - is_gpu_app() { - [[ "${var_gpu:-no}" == "yes" ]] && return 0 - 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_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") - 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_custom "🎮" "${RD}" "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 + # Export as string (this works, unlike arrays!) + export PCT_OPTIONS="$PCT_OPTIONS_STRING" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + + # # DEBUG: Show final PCT_OPTIONS being exported + # echo "[DEBUG] PCT_OPTIONS to be exported:" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Calling create_lxc_container..." + + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # Check if GPU passthrough is enabled + # Returns true only if var_gpu is explicitly set to "yes" + # Can be set via: + # - Environment variable: var_gpu=yes bash -c "..." + # - CT script default: var_gpu="${var_gpu:-no}" + # - Advanced settings wizard + # - App defaults file: /usr/local/community-scripts/defaults/.vars + is_gpu_app() { + [[ "${var_gpu:-no}" == "yes" ]] && return 0 + 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_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") + done + fi fi - fi - fi - # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] - if echo "$pci_vga_info" | grep -q "\[10de:"; then - msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" + # 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_custom "🎮" "${RD}" "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 - # Simple passthrough - just bind /dev/nvidia* devices if they exist - # Skip directories like /dev/nvidia-caps (they need special handling) - for d in /dev/nvidia*; do - [[ -e "$d" ]] || continue - [[ -d "$d" ]] && continue # Skip directories - NVIDIA_DEVICES+=("$d") - done + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" - fi - fi + # Simple passthrough - just bind /dev/nvidia* devices if they exist + # Skip directories like /dev/nvidia-caps (they need special handling) + for d in /dev/nvidia*; do + [[ -e "$d" ]] || continue + [[ -d "$d" ]] && continue # Skip directories + NVIDIA_DEVICES+=("$d") + done - # Debug output - msg_debug "Intel devices: ${INTEL_DEVICES[*]}" - msg_debug "AMD devices: ${AMD_DEVICES[*]}" - msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" - } + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" + fi + fi - # Configure USB passthrough for privileged containers - configure_usb_passthrough() { - if [[ "$CT_TYPE" != "0" ]]; then - return 0 - fi + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } - msg_info "Configuring automatic USB passthrough (privileged container)" - cat <>"$LXC_CONFIG" + # 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: @@ -3013,485 +3013,543 @@ 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 - msg_ok "USB passthrough configured" - } + msg_ok "USB passthrough configured" + } - # Configure GPU passthrough - configure_gpu_passthrough() { - # Skip if: - # GPU passthrough is enabled when var_gpu="yes": - # - Set via environment variable: var_gpu=yes bash -c "..." - # - Set in CT script: var_gpu="${var_gpu:-no}" - # - Enabled in advanced_settings wizard - # - Configured in app defaults file - if ! is_gpu_app "$APP"; then - return 0 - fi + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if: + # GPU passthrough is enabled when var_gpu="yes": + # - Set via environment variable: var_gpu=yes bash -c "..." + # - Set in CT script: var_gpu="${var_gpu:-no}" + # - Enabled in advanced_settings wizard + # - Configured in app defaults file + if ! is_gpu_app "$APP"; then + return 0 + fi - detect_gpu_devices + detect_gpu_devices - # Count available GPU types - local gpu_count=0 - local available_gpus=() + # 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 [[ ${#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 [[ ${#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 [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi - if [[ $gpu_count -eq 0 ]]; then - msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" - return 0 - fi + if [[ $gpu_count -eq 0 ]]; then + msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" + return 0 + fi - local selected_gpu="" + local selected_gpu="" - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_ok "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^^}" + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_ok "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 + # 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 + 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 + # 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[@]}") + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - # Use pct set to add devices with proper dev0/dev1 format - # GIDs will be detected and set after container starts - local dev_index=0 - for dev in "${devices[@]}"; do - # Add to config using pct set (will be visible in GUI) - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set to add devices with proper dev0/dev1 format + # GIDs will be detected and set after container starts + local dev_index=0 + for dev in "${devices[@]}"; do + # Add to config using pct set (will be visible in GUI) + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" - ;; + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" + ;; - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_warn "No NVIDIA devices available for passthrough" - return 0 - fi + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "No NVIDIA devices available for passthrough" + return 0 + fi - # Use pct set for NVIDIA devices - local dev_index=0 - for dev in "${NVIDIA_DEVICES[@]}"; do - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set for NVIDIA devices + local dev_index=0 + for dev in "${NVIDIA_DEVICES[@]}"; do + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" - ;; - esac - } + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" + ;; + esac + } - # Additional device passthrough - configure_additional_devices() { - # 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 [[ -e /dev/apex_0 ]]; then - 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 - } + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + 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 + } - # Execute pre-start configurations - configure_usb_passthrough - configure_gpu_passthrough - configure_additional_devices + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices - # ============================================================================ - # START CONTAINER AND INSTALL USERLAND - # ============================================================================ + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ - msg_info "Starting LXC Container" - pct start "$CTID" + 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 assignment (IPv4 or IPv6) - local ip_in_lxc="" - for i in {1..20}; do - # Try IPv4 first - ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - # Fallback to IPv6 if IPv4 not available - if [ -z "$ip_in_lxc" ]; then - ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) - fi - [ -n "$ip_in_lxc" ] && break - sleep 1 + # 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 - if [ -z "$ip_in_lxc" ]; then - msg_error "No IP assigned to CT $CTID after 20s" - echo -e "${YW}Troubleshooting:${CL}" - echo " • Verify bridge ${BRG} exists and has connectivity" - echo " • Check if DHCP server is reachable (if using DHCP)" - echo " • Verify static IP configuration (if using static IP)" - echo " • Check Proxmox firewall rules" - echo " • If using Tailscale: Disable MagicDNS temporarily" - exit 1 + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP assignment (IPv4 or IPv6) + local ip_in_lxc="" + for i in {1..20}; do + # Try IPv4 first + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + # Fallback to IPv6 if IPv4 not available + if [ -z "$ip_in_lxc" ]; then + ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) + fi + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + echo -e "${YW}Troubleshooting:${CL}" + echo " • Verify bridge ${BRG} exists and has connectivity" + echo " • Check if DHCP server is reachable (if using DHCP)" + echo " • Verify static IP configuration (if using static IP)" + echo " • Check Proxmox firewall rules" + echo " • If using Tailscale: Disable MagicDNS temporarily" + exit 1 + fi + + # Verify basic connectivity (ping test) + local ping_success=false + for retry in {1..3}; do + if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || + pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || + pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then + ping_success=true + break + fi + sleep 2 + done + + if [ "$ping_success" = false ]; then + msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" + echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" + else + msg_ok "Network in LXC is reachable (ping)" + fi fi - # Verify basic connectivity (ping test) - local ping_success=false - for retry in {1..3}; do - if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || - pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || - pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then - ping_success=true - break - fi - sleep 2 - done + # 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 [ "$ping_success" = false ]; then - msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" - echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" - else - msg_ok "Network in LXC is reachable (ping)" - fi - fi + fix_gpu_gids - # 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 - } + # Continue with standard container setup + msg_info "Customizing LXC Container" - fix_gpu_gids + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi - # Continue with standard container setup - msg_info "Customizing LXC Container" + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi - # # Install GPU userland if configured - # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - # install_gpu_userland "VAAPI" - # fi + # Continue with standard container setup - install core dependencies based on OS + sleep 3 - # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - # install_gpu_userland "NVIDIA" - # fi - - # Continue with standard container setup - install core dependencies based on OS - sleep 3 - - case "$var_os" in - alpine) - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories + case "$var_os" in + alpine) + 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" - ;; + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + ;; - debian | ubuntu | devuan) - # First install locales package (required for locale-gen on minimal templates) - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null 2>&1 || true" + debian | ubuntu | devuan) + # First install locales package (required for locale-gen on minimal templates) + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null 2>&1 || true" - # Locale setup for Debian-based - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + # Locale setup for Debian-based + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ [[ -n \"\$locale_line\" ]] && echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null 2>&1 && \ export LANG=\$locale_line || true" - # Timezone setup - 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" + # Timezone setup + 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + # Core dependencies + 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 + } + ;; + + fedora | rockylinux | almalinux | centos) + # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS + # Detect OS major version for EL10+ compatibility (DNF 5, different packages) + local rhel_version + rhel_version=$(pct exec "$CTID" -- bash -c "grep -oP '(?<=VERSION_ID=\")[0-9]+' /etc/os-release 2>/dev/null || echo 9") + + # First run makecache to ensure repos are ready (critical for fresh templates) + msg_info "Initializing package manager (this may take a moment)..." + if ! pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1 || yum makecache 2>&1" >/dev/null 2>&1; then + msg_warn "Package cache update had issues, continuing anyway..." + fi + + # Build package list - EL10+ may not have glibc-langpack-en in same form + local rhel_packages="curl sudo mc jq procps-ng ncurses" + if [[ "$rhel_version" -lt 10 ]]; then + rhel_packages="$rhel_packages glibc-langpack-en" + else + # EL10 uses glibc-all-langpacks or langpacks-en + rhel_packages="$rhel_packages langpacks-en glibc-all-langpacks" + fi + + # Install base packages with better error handling + local install_log="/tmp/dnf_install_${CTID}.log" + if ! pct exec "$CTID" -- bash -c "dnf install -y $rhel_packages 2>&1 | tee $install_log; exit \${PIPESTATUS[0]}" >/dev/null 2>&1; then + # Check if it's just missing optional packages + if pct exec "$CTID" -- bash -c "rpm -q curl sudo mc jq procps-ng" >/dev/null 2>&1; then + msg_warn "Some optional packages may have failed, but core packages installed" + else + # Real failure - try minimal install + msg_warn "Full package install failed, trying minimal set..." + if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo jq 2>&1" >/dev/null 2>&1; then + msg_error "dnf/yum base packages installation failed" + pct exec "$CTID" -- bash -c "cat $install_log 2>/dev/null" || true + exit 1 + fi + fi + fi + + # Set locale for RHEL-based systems + pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true + + # Timezone setup for RHEL + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + [[ "${tz:-}" == Etc/* ]] && tz="UTC" + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "timedatectl set-timezone '$tz' 2>/dev/null || ln -sf '/usr/share/zoneinfo/$tz' /etc/localtime" || true + fi + ;; + + opensuse) + # openSUSE - special handling for terminal/locale issues + msg_info "Initializing package manager for openSUSE..." + pct exec "$CTID" -- bash -c "zypper --non-interactive refresh 2>&1" >/dev/null 2>&1 || true + + # Install packages - ncurses and terminfo are CRITICAL for terminal to work + if ! pct exec "$CTID" -- bash -c "zypper --non-interactive install -y curl sudo mc jq glibc-locale ncurses terminfo-base 2>&1" >/dev/null 2>&1; then + # Try without glibc-locale + if ! pct exec "$CTID" -- bash -c "zypper --non-interactive install -y curl sudo mc jq ncurses terminfo-base 2>&1" >/dev/null 2>&1; then + msg_error "zypper base packages installation failed" + exit 1 + fi + fi + + # Fix 'unknown terminal type' error - set TERM in multiple places + pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true + + # Set TERM globally for all users + pct exec "$CTID" -- bash -c "cat > /etc/profile.d/term.sh << 'EOFTERM' +# Fix terminal type for LXC containers +if [ -z \"\$TERM\" ] || [ \"\$TERM\" = \"dumb\" ] || [ \"\$TERM\" = \"-\" ]; then + export TERM=xterm-256color +fi +EOFTERM +chmod +x /etc/profile.d/term.sh" || true + + # Also set in /etc/environment for non-login shells + pct exec "$CTID" -- bash -c "grep -q '^TERM=' /etc/environment 2>/dev/null || echo 'TERM=xterm-256color' >> /etc/environment" || true + ;; + + gentoo) + # Gentoo - OpenRC based, emerge is slow + # Sync portage first (required for fresh templates) + msg_info "Syncing Gentoo portage (this may take a while)..." + pct exec "$CTID" -- bash -c "emerge --sync >/dev/null 2>&1 || emerge-webrsync >/dev/null 2>&1" || true + + # Install essentials - emerge can be very slow + if ! pct exec "$CTID" -- bash -c "emerge --quiet --noreplace app-misc/jq net-misc/curl app-misc/mc sys-libs/ncurses 2>&1" >/dev/null 2>&1; then + msg_warn "Gentoo base packages installation incomplete - some packages may need manual setup" + # Try just curl and jq as minimum + pct exec "$CTID" -- bash -c "emerge --quiet --noreplace net-misc/curl app-misc/jq 2>&1" >/dev/null 2>&1 || true + fi + + # Set TERM for Gentoo + pct exec "$CTID" -- bash -c "echo 'export TERM=xterm-256color' >> /etc/profile.d/term.sh && chmod +x /etc/profile.d/term.sh" || true + ;; + + openeuler) + # openEuler (RHEL-compatible, uses DNF) + # Note: openEuler requires privileged container due to PVE limitation with /etc/redhat-release + msg_info "Initializing package manager for openEuler..." + pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1" >/dev/null 2>&1 || true + + # openEuler package names may differ from RHEL + local euler_packages="curl sudo mc jq procps-ng ncurses" + if ! pct exec "$CTID" -- bash -c "dnf install -y $euler_packages 2>&1" >/dev/null 2>&1; then + # Try without procps-ng (might be just 'procps' in openEuler) + if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq ncurses 2>&1" >/dev/null 2>&1; then + msg_error "dnf base packages installation failed" + exit 1 + fi + fi + # Set locale + pct exec "$CTID" -- bash -c "echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true + ;; + + *) + msg_warn "Unknown OS '$var_os' - skipping core dependency installation" + ;; + esac + + msg_ok "Customized LXC Container" + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + # NOTE: We disable error handling here because: + # 1. Container errors are caught by error_handler INSIDE container + # 2. Container creates flag file with exit code + # 3. We read flag file and handle cleanup manually below + # 4. We DON'T want host error_handler to fire for lxc-attach command itself + + set +Eeuo pipefail # Disable ALL error handling temporarily + trap - ERR # Remove ERR trap completely + + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)" + local lxc_exit=$? + + set -Eeuo pipefail # Re-enable error handling + trap 'error_handler' ERR # Restore ERR trap + + # Check for error flag file in container (more reliable than lxc-attach exit code) + local install_exit_code=0 + if [[ -n "${SESSION_ID:-}" ]]; then + local error_flag="/root/.install-${SESSION_ID}.failed" + if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then + install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") + pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true + fi fi - # Core dependencies - 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 - } - ;; - - fedora | rockylinux | almalinux | centos) - # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS - # Install base packages including glibc-langpack for locale support - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq procps-ng glibc-langpack-en >/dev/null 2>&1 || yum install -y curl sudo mc jq procps-ng >/dev/null 2>&1" || { - msg_error "dnf/yum base packages installation failed" - exit 1 - } - - # Set locale for RHEL-based systems - pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - - # Timezone setup for RHEL - if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") - fi - [[ "${tz:-}" == Etc/* ]] && tz="UTC" - if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "timedatectl set-timezone '$tz' 2>/dev/null || ln -sf '/usr/share/zoneinfo/$tz' /etc/localtime" || true - fi - ;; - - opensuse) - # openSUSE - add locale package and setup - pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq glibc-locale >/dev/null" || { - msg_error "zypper base packages installation failed" - exit 1 - } - # Set locale for openSUSE - pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - ;; - - gentoo) - # Gentoo - emerge is slow, only install essentials - pct exec "$CTID" -- bash -c "emerge --quiet app-misc/jq net-misc/curl app-misc/mc >/dev/null 2>&1" || { - msg_warn "Gentoo base packages installation incomplete - may need manual setup" - } - ;; - - openeuler) - # openEuler (RHEL-compatible) - # Note: openEuler may fail pct create due to missing /etc/redhat-release - this is a PVE limitation - pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq glibc-langpack-en >/dev/null" || { - msg_error "dnf base packages installation failed" - exit 1 - } - # Set locale - pct exec "$CTID" -- bash -c "echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - ;; - - opensuse) - # openSUSE - add locale package and setup - pct exec "$CTID" -- bash -c "zypper --non-interactive install curl sudo mc jq glibc-locale >/dev/null" || { - msg_error "zypper base packages installation failed" - exit 1 - } - # Set locale for openSUSE - pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - ;; - - *) - msg_warn "Unknown OS '$var_os' - skipping core dependency installation" - ;; - esac - - msg_ok "Customized LXC Container" - - # Install SSH keys - install_ssh_keys_into_ct - - # Run application installer - # NOTE: We disable error handling here because: - # 1. Container errors are caught by error_handler INSIDE container - # 2. Container creates flag file with exit code - # 3. We read flag file and handle cleanup manually below - # 4. We DON'T want host error_handler to fire for lxc-attach command itself - - set +Eeuo pipefail # Disable ALL error handling temporarily - trap - ERR # Remove ERR trap completely - - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" - local lxc_exit=$? - - set -Eeuo pipefail # Re-enable error handling - trap 'error_handler' ERR # Restore ERR trap - - # Check for error flag file in container (more reliable than lxc-attach exit code) - local install_exit_code=0 - if [[ -n "${SESSION_ID:-}" ]]; then - local error_flag="/root/.install-${SESSION_ID}.failed" - if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then - install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") - pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true - fi - fi - - # Fallback to lxc-attach exit code if no flag file - if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then - install_exit_code=$lxc_exit - fi - - # Installation failed? - if [[ $install_exit_code -ne 0 ]]; then - msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" - - # Copy both logs from container before potential deletion - local build_log_copied=false - local install_log_copied=false - - if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then - # Copy BUILD_LOG (creation log) if it exists - if [[ -f "${BUILD_LOG}" ]]; then - cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true - fi - - # Copy INSTALL_LOG from container - if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then - install_log_copied=true - fi - - # Show available logs - echo "" - [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" - [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" + # Fallback to lxc-attach exit code if no flag file + if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then + install_exit_code=$lxc_exit fi - # Dev mode: Keep container or open breakpoint shell - if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then - msg_dev "Keep mode active - container ${CTID} preserved" - return 0 - elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then - msg_dev "Breakpoint mode - opening shell in container ${CTID}" - echo -e "${YW}Type 'exit' to return to host${CL}" - pct enter "$CTID" - echo "" - echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" - if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - msg_ok "Container ${CTID} removed" - else - msg_dev "Container ${CTID} kept for debugging" - fi - exit $install_exit_code - fi + # Installation failed? + if [[ $install_exit_code -ne 0 ]]; then + msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" - # Report failure to API before container cleanup - post_update_to_api "failed" "$install_exit_code" + # Copy both logs from container before potential deletion + local build_log_copied=false + local install_log_copied=false - # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) - echo "" - echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" + if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then + # Copy BUILD_LOG (creation log) if it exists + if [[ -f "${BUILD_LOG}" ]]; then + cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true + fi - if read -t 60 -r response; then - if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then - # Remove container - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - elif [[ "$response" =~ ^[Nn]$ ]]; then - echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" + # Copy INSTALL_LOG from container + if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then + install_log_copied=true + fi - # Dev mode: Setup MOTD/SSH for debugging access to broken container - if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then - echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" - if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func) + # Show available logs + echo "" + [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" + [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" + fi + + # Dev mode: Keep container or open breakpoint shell + if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then + msg_dev "Keep mode active - container ${CTID} preserved" + return 0 + elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then + msg_dev "Breakpoint mode - opening shell in container ${CTID}" + echo -e "${YW}Type 'exit' to return to host${CL}" + pct enter "$CTID" + echo "" + echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" + if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + msg_ok "Container ${CTID} removed" + else + msg_dev "Container ${CTID} kept for debugging" + fi + exit $install_exit_code + fi + + # Report failure to API before container cleanup + post_update_to_api "failed" "$install_exit_code" + + # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) + echo "" + echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" + + if read -t 60 -r response; then + if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then + # Remove container + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + elif [[ "$response" =~ ^[Nn]$ ]]; then + echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" + + # Dev mode: Setup MOTD/SSH for debugging access to broken container + if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then + echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" + if pct exec "$CTID" -- bash -c " + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then - local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" - fi + local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" + fi + fi + fi + else + # Timeout - auto-remove + echo -e "\n${YW}No response - auto-removing container${CL}" + echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" fi - fi - else - # Timeout - auto-remove - echo -e "\n${YW}No response - auto-removing container${CL}" - echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - fi - exit $install_exit_code - fi + exit $install_exit_code + 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 + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 fi - ;; - "" | n | no) - msg_custom "ℹ️" "${BL}" "Container was not removed." - ;; - *) - msg_warn "Invalid response. Container was not removed." - ;; - esac + + # 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_custom "ℹ️" "${BL}" "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac } # ------------------------------------------------------------------------------ @@ -3499,81 +3557,81 @@ destroy_lxc() { # ------------------------------------------------------------------------------ # ===== 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}" + 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 - fi - STORAGE_RESULT="$preselect" - return 0 + + 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 + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi - msg_info "Detecting and setting correct GPU group IDs" + msg_info "Detecting and setting correct GPU group IDs" - # Get actual GIDs from 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") + # Get actual GIDs from 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") - # Create groups if they don't exist - if [[ -z "$video_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 - video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" - fi + # Create groups if they don't exist + if [[ -z "$video_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" + fi - if [[ -z "$render_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 - render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" - fi + if [[ -z "$render_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" + fi - # Stop container to update config - pct stop "$CTID" >/dev/null 2>&1 - sleep 1 + # Stop container to update config + pct stop "$CTID" >/dev/null 2>&1 + sleep 1 - # Update dev entries with correct GIDs - sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" - sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" + # Update dev entries with correct GIDs + sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" + sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" - # Restart container - pct start "$CTID" >/dev/null 2>&1 - sleep 2 + # Restart container + pct start "$CTID" >/dev/null 2>&1 + sleep 2 - msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" + msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" - # For privileged containers: also fix permissions inside container - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " + # For privileged containers: also fix permissions inside container + 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 @@ -3587,747 +3645,760 @@ fix_gpu_gids() { done fi " >/dev/null 2>&1 - fi + fi } 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 ]] + 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 + 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 + 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') + 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 + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 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 + + 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 - return 0 - done } create_lxc_container() { - # ------------------------------------------------------------------------------ - # Optional verbose mode (debug tracing) - # ------------------------------------------------------------------------------ - if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + # ------------------------------------------------------------------------------ + # 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}'; } + # ------------------------------------------------------------------------------ + # 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"; } + 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"; } + # 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 + # 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)" + _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 $STD apt-get update && $STD 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." + 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 - 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 - } + 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 $STD apt-get update && $STD 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 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 + } - msg_debug "CTID=$CTID" - msg_debug "PCT_OSTYPE=$PCT_OSTYPE" - msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + # ------------------------------------------------------------------------------ + # 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 + } - # 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 + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" - # 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 - } + # 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 - # 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 + # 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 + } - # 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 + # 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]" - fi + 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 - fi - msg_info "Validating storage '$CONTAINER_STORAGE'" - STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) + msg_info "Validating storage '$CONTAINER_STORAGE'" + STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) - case "$STORAGE_TYPE" in - iscsidirect) exit 212 ;; - iscsi | zfs) exit 213 ;; - cephfs) exit 219 ;; - pbs) exit 224 ;; - linstor | rbd | nfs | cifs) - pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 - ;; - esac + case "$STORAGE_TYPE" in + iscsidirect) exit 212 ;; + iscsi | zfs) exit 213 ;; + cephfs) exit 219 ;; + pbs) exit 224 ;; + linstor | rbd | nfs | cifs) + pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 + ;; + esac - pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 - msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" + pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 + msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" - msg_info "Validating template storage '$TEMPLATE_STORAGE'" - TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) + msg_info "Validating template storage '$TEMPLATE_STORAGE'" + TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) - if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then - msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" - fi - msg_ok "Template storage '$TEMPLATE_STORAGE' validated" - - # 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 + if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" fi - msg_ok "Cluster is quorate" - fi + msg_ok "Template storage '$TEMPLATE_STORAGE' validated" - # ------------------------------------------------------------------------------ - # Template discovery & validation - # Supported OS types (pveam available): alpine, almalinux, centos, debian, - # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu - # Template naming conventions: - # - Debian/Ubuntu/Devuan: --standard__.tar.zst - # - Alpine/Fedora/Rocky/CentOS/AlmaLinux/openEuler: --default__.tar.xz - # - Gentoo: gentoo-current-openrc-_.tar.xz (version IS 'current') - # - openSUSE: opensuse--default__.tar.xz - # - CentOS: centos--stream-default__.tar.xz (note: stream in name) - # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - case "$PCT_OSTYPE" in - debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rockylinux | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - centos) TEMPLATE_PATTERN="-stream-default_" ;; - gentoo) TEMPLATE_PATTERN="-openrc-" ;; # Pattern after gentoo-current - opensuse) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="" ;; - esac + # 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 + } - msg_info "Searching for template '$TEMPLATE_SEARCH'" + # 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 - # Build regex patterns outside awk/grep for clarity - SEARCH_PATTERN="^${TEMPLATE_SEARCH}" + # ------------------------------------------------------------------------------ + # Template discovery & validation + # Supported OS types (pveam available): alpine, almalinux, centos, debian, + # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu + # Template naming conventions: + # - Debian/Ubuntu/Devuan: --standard__.tar.zst + # - Alpine/Fedora/Rocky/CentOS/AlmaLinux/openEuler: --default__.tar.xz + # - Gentoo: gentoo-current-openrc__.tar.xz (note: underscore before date!) + # - openSUSE: opensuse--default__.tar.xz + # - CentOS: centos--stream-default__.tar.xz (note: stream in name) + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rockylinux | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; + centos) TEMPLATE_PATTERN="-stream-default_" ;; + gentoo) TEMPLATE_PATTERN="-openrc_" ;; # Pattern: gentoo-current-openrc_ (underscore!) + opensuse) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac - #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" - #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" - #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + msg_info "Searching for template '$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 - ) + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" - 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/^/ /' - - 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 - - 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 + 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 ) - 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 + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - 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}-" + msg_ok "Template search completed" - #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + #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 - ) + 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 - 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_custom "🚫" "${YW}" "Installation cancelled" - exit 0 - fi - else - msg_error "No ${PCT_OSTYPE} templates available at all" - exit 225 - fi - fi + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - #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]}" + #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 "" - read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." - 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 - } + # Get all available versions for this OS type + # Special handling for Gentoo which uses 'current' instead of numeric version + if [[ "$PCT_OSTYPE" == "gentoo" ]]; then + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $2}' | + grep "^gentoo-" | + sed -E 's/gentoo-([^-]+)-.*/\1/' | + sort -u 2>/dev/null || sort -u + ) else - msg_custom "🚫" "${YW}" "Installation cancelled" - exit 1 + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $2}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + grep -E '^[0-9]' | + sort -u -V 2>/dev/null || sort -u + ) 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_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 + 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 - msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" - msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + 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}-" - 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 + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" - 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_custom "ℹ️" "${BL}" "Continuing with local template $TEMPLATE" - fi - fi + 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 [[ "$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 - - # PCT_OPTIONS is now a string (exported from build_container) - # Add rootfs if not already specified - if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then - PCT_OPTIONS="$PCT_OPTIONS - -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" - fi - - # 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}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" - - # # DEBUG: Show the actual command that will be executed - # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" - # echo "[DEBUG] CTID: $CTID" - # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" - # echo "[DEBUG] PCT_OPTIONS (will be word-split):" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Full command line:" - # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - # echo "[DEBUG] ========================================" - - msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - msg_debug "Logfile: $LOGFILE" - - # First attempt (PCT_OPTIONS is a multi-line string, use it directly) - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then - msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 not already on local - if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_info "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 - # Local fallback also failed - check for LXC stack version issue - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x + 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_custom "🚫" "${YW}" "Installation cancelled" + exit 0 fi - exit 209 - fi else - msg_ok "Container successfully created using local fallback." + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 fi - else - # Already on local storage and still failed - check LXC stack version - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi - else - msg_ok "Container successfully created after template repair." 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 - } + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - # Verify config rootfs - grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { - msg_error "RootFS entry missing in container config. See $LOGFILE" - exit 216 - } + 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 - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." + # 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 - # Report container creation to API - post_to_api + [[ -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_custom "🚫" "${YW}" "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_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 + + 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_custom "ℹ️" "${BL}" "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 + + # PCT_OPTIONS is now a string (exported from build_container) + # Add rootfs if not already specified + if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then + PCT_OPTIONS="$PCT_OPTIONS + -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" + fi + + # 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}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" + + # # DEBUG: Show the actual command that will be executed + # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" + # echo "[DEBUG] CTID: $CTID" + # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" + # echo "[DEBUG] PCT_OPTIONS (will be word-split):" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Full command line:" + # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + # echo "[DEBUG] ========================================" + + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + msg_debug "Logfile: $LOGFILE" + + # First attempt (PCT_OPTIONS is a multi-line string, use it directly) + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then + msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 not already on local + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_info "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 + # Local fallback also failed - check for LXC stack version issue + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + else + msg_ok "Container successfully created using local fallback." + fi + else + # Already on local storage and still failed - check LXC stack version + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_ok "Container successfully created after template repair." + 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." + + # Report container creation to API + post_to_api } # ============================================================================== @@ -4347,11 +4418,11 @@ create_lxc_container() { # - Posts final "done" status to API telemetry # ------------------------------------------------------------------------------ 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 @@ -4379,14 +4450,14 @@ description() {
EOF - ) - pct set "$CTID" -description "$DESCRIPTION" + ) + 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" } # ============================================================================== @@ -4403,14 +4474,14 @@ EOF # - Only executes on non-zero exit codes # ------------------------------------------------------------------------------ api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - post_update_to_api "failed" "$exit_code" - fi + exit_code=$? + if [ $exit_code -ne 0 ]; then + post_update_to_api "failed" "$exit_code" + 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 diff --git a/misc/install.func b/misc/install.func index 124f58941..685eb0688 100644 --- a/misc/install.func +++ b/misc/install.func @@ -898,11 +898,34 @@ EOF ;; sysvinit) - # Devuan/older systems - modify inittab with flexible runlevel matching + # Devuan/older systems - modify inittab for auto-login + # Devuan 5 (daedalus) uses SysVinit with various inittab formats if [[ -f /etc/inittab ]]; then - # Match various runlevel patterns (23, 2345, 12345, etc.) and both getty/agetty - sed -i 's|^1:[0-9]*:respawn:/sbin/a\?getty.*|1:2345:respawn:/sbin/agetty --autologin root tty1 38400 linux|' /etc/inittab + # Backup original inittab + cp /etc/inittab /etc/inittab.bak 2>/dev/null || true + + # Try multiple patterns to catch different inittab formats + # Pattern 1: Standard format "1:2345:respawn:/sbin/getty..." + sed -i -E 's|^1:[0-9]*:respawn:[^ ]*(getty|agetty)[^$]*$|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab + + # Pattern 2: With console instead of tty1 + sed -i -E 's|^c1:[0-9]*:respawn:[^ ]*(getty|agetty).*console.*$|c1:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab + + # Pattern 3: Devuan specific "co:2345:respawn:/sbin/getty..." + sed -i -E 's|^co:[0-9]*:respawn:[^ ]*(getty|agetty).*$|co:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab + + # Pattern 4: LXC console pattern + if ! grep -q 'autologin root' /etc/inittab; then + # If no autologin was set, add a new console entry + if ! grep -qE '^c[o1]:.*autologin' /etc/inittab; then + echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux" >>/etc/inittab + fi + fi + + # Force a reload of inittab - try multiple methods + telinit q &>/dev/null || init q &>/dev/null || kill -1 1 &>/dev/null || true fi + touch /root/.hushlogin ;; esac From 3d6a5f58b0090d645682f50b3b2c0159fbfee502 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Mon, 12 Jan 2026 21:58:15 +0100 Subject: [PATCH 307/378] fix: Multiple distro issues from testing Devuan: - Fixed sed syntax error: replaced -E with BRE, removed invalid [^$] pattern - Simplified regex patterns for inittab autologin matching openEuler: - Added template patching to inject /etc/redhat-release before pct create - This fixes 'error in setup task PVE::LXC::Setup::post_create_hook' - Reverted to unprivileged container (privileged not needed with patch) openSUSE: - Added --gpg-auto-import-keys to zypper to avoid interactive GPG prompts - This was causing the 15+ minute hangs during package manager init Gentoo: - Changed to use emerge-webrsync (http) instead of emerge --sync (rsync) - webrsync is significantly faster than full rsync sync - Install curl FIRST before other packages (required for install.func) - Better error handling with separate curl check Rocky Linux DNS issue: - Not a script bug - container network/DNS not working - User should check container gateway and DNS settings --- ct/openeuler.sh | 9 +- misc/build.func | 6768 +++++++++++++++++++++++---------------------- misc/install.func | 6 +- 3 files changed, 3414 insertions(+), 3369 deletions(-) diff --git a/ct/openeuler.sh b/ct/openeuler.sh index 659eaed60..893fd0f9a 100644 --- a/ct/openeuler.sh +++ b/ct/openeuler.sh @@ -5,10 +5,9 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.openeuler.org/ -# NOTE: openEuler requires privileged container due to PVE limitation -# PVE's post_create_hook expects /etc/redhat-release which openEuler doesn't have -# This causes "unable to create CT - error in setup task PVE::LXC::Setup::post_create_hook" -# Setting var_unprivileged=0 creates privileged container which bypasses this check +# NOTE: openEuler has a PVE compatibility issue +# PVE's post_create_hook expects /etc/redhat-release which openEuler doesn't have by default +# We handle this in build.func by creating the file after container creation APP="openEuler" var_tags="${var_tags:-os}" @@ -17,7 +16,7 @@ var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-openeuler}" var_version="${var_version:-25.03}" -var_unprivileged="${var_unprivileged:-0}" +var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables diff --git a/misc/build.func b/misc/build.func index 58351ea0b..06923e925 100644 --- a/misc/build.func +++ b/misc/build.func @@ -39,46 +39,46 @@ # - Captures app-declared resource defaults (CPU, RAM, Disk) # ------------------------------------------------------------------------------ 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. - SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files - BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + 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. + SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files + BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - # Parse dev_mode early - parse_dev_mode + # Parse dev_mode early + parse_dev_mode - # Setup persistent log directory if logs mode active - if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then - mkdir -p /var/log/community-scripts - BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" - fi + # Setup persistent log directory if logs mode active + if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then + mkdir -p /var/log/community-scripts + BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" + fi - # Get Proxmox VE version and kernel version - if command -v pveversion >/dev/null 2>&1; then - PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - else - PVEVERSION="N/A" - fi - KERNEL_VERSION=$(uname -r) + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + else + 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 + # 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 } # ----------------------------------------------------------------------------- @@ -187,17 +187,17 @@ variables() { 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" + 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" + 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 # ============================================================================== @@ -215,46 +215,46 @@ fi # ------------------------------------------------------------------------------ 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 - # Silent success - only show errors if they exist + # Silent success - only show errors if they exist } # ============================================================================== @@ -270,18 +270,18 @@ maxkeys_check() { # - Returns "Unknown" if OS type cannot be determined # ------------------------------------------------------------------------------ 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" } # ------------------------------------------------------------------------------ @@ -291,16 +291,16 @@ get_current_ip() { # - Removes old IP entries to avoid duplicates # ------------------------------------------------------------------------------ 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 } # ------------------------------------------------------------------------------ @@ -311,30 +311,30 @@ update_motd_ip() { # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { - [[ "${SSH:-no}" != "yes" ]] && return 0 + [[ "${SSH:-no}" != "yes" ]] && return 0 - # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) - : "${SSH_KEYS_FILE:=}" + # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) + : "${SSH_KEYS_FILE:=}" - 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)." + 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 } # ------------------------------------------------------------------------------ @@ -346,55 +346,55 @@ install_ssh_keys_into_ct() { # - 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 + 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 + 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 + 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 ' + # 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 + if ((c > 0)); then + files+=("$f") + total=$((total + c)) fi + done - FOUND_HOST_KEY_COUNT="$total" - ( - IFS=: - echo "${files[*]}" - ) + # 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[*]}" + ) } # ============================================================================== @@ -407,14 +407,14 @@ find_host_ssh_keys() { # - Converts IP address to integer and vice versa for range iteration # ------------------------------------------------------------------------------ ip_to_int() { - local IFS=. - read -r i1 i2 i3 i4 <<<"$1" - echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) + local IFS=. + read -r i1 i2 i3 i4 <<<"$1" + echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) } int_to_ip() { - local ip=$1 - echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" + local ip=$1 + echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" } # ------------------------------------------------------------------------------ @@ -426,39 +426,39 @@ int_to_ip() { # - Sets NET_RESOLVED to the resolved IP or empty on failure # ------------------------------------------------------------------------------ resolve_ip_from_range() { - local range="$1" - 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_start ip_end + local range="$1" + 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_start ip_end - # Parse range: "10.0.0.1/24-10.0.0.10/24" - ip_start="${range%%-*}" - ip_end="${range##*-}" - - if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then - NET_RESOLVED="" - return 1 - fi - - local ip1="${ip_start%%/*}" - local ip2="${ip_end%%/*}" - local cidr="${ip_start##*/}" - - local start_int=$(ip_to_int "$ip1") - local end_int=$(ip_to_int "$ip2") - - for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do - local ip=$(int_to_ip $ip_int) - msg_info "Checking IP: $ip" - if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then - NET_RESOLVED="$ip/$cidr" - msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" - return 0 - fi - done + # Parse range: "10.0.0.1/24-10.0.0.10/24" + ip_start="${range%%-*}" + ip_end="${range##*-}" + if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then NET_RESOLVED="" - msg_error "No free IP found in range $range" return 1 + fi + + local ip1="${ip_start%%/*}" + local ip2="${ip_end%%/*}" + local cidr="${ip_start##*/}" + + local start_int=$(ip_to_int "$ip1") + local end_int=$(ip_to_int "$ip2") + + for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do + local ip=$(int_to_ip $ip_int) + msg_info "Checking IP: $ip" + if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then + NET_RESOLVED="$ip/$cidr" + msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" + return 0 + fi + done + + NET_RESOLVED="" + msg_error "No free IP found in range $range" + return 1 } # ------------------------------------------------------------------------------ @@ -468,17 +468,17 @@ resolve_ip_from_range() { # - Returns 0 if it's a range, 1 otherwise # ------------------------------------------------------------------------------ is_ip_range() { - local value="$1" - local ip_start ip_end - if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then - local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' - ip_start="${value%%-*}" - ip_end="${value##*-}" - if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then - return 0 - fi + local value="$1" + local ip_start ip_end + if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then + local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' + ip_start="${value%%-*}" + ip_end="${value##*-}" + if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then + return 0 fi - return 1 + fi + return 1 } # ============================================================================== @@ -493,53 +493,53 @@ is_ip_range() { # - Arguments: vars_file, key (var_container_storage/var_template_storage), value # ------------------------------------------------------------------------------ _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" + # $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 + # $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") + 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 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 + 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" + _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 + # 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 - # Silent operation - no output message + # Silent operation - no output message } # ============================================================================== @@ -556,102 +556,102 @@ choose_and_set_storage_for_file() { # - Sets up container type, resources, network, SSH, features, and tags # ------------------------------------------------------------------------------ base_settings() { - # Default Settings - CT_TYPE=${var_unprivileged:-"1"} + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} - # 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}" + # 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 + # 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 + 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 + 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} - HN=${var_hostname:-$NSAPP} - BRG=${var_brg:-"vmbr0"} - NET=${var_net:-"dhcp"} + 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} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} - # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) - if is_ip_range "$NET"; then - msg_info "Scanning IP range: $NET" - if resolve_ip_from_range "$NET"; then - NET="$NET_RESOLVED" - else - msg_error "Could not find free IP in range. Falling back to DHCP." - NET="dhcp" - fi + # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) + if is_ip_range "$NET"; then + msg_info "Scanning IP range: $NET" + if resolve_ip_from_range "$NET"; then + NET="$NET_RESOLVED" + else + msg_error "Could not find free IP in range. Falling back to DHCP." + NET="dhcp" fi + fi - 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:-""} + 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:-""} - # 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_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" - APT_CACHER="" - APT_CACHER_IP="" - else - msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" - fi + # 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_custom "⚠️" "${YW}" "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:-""} - 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}"} + 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}"} - # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) - ENABLE_GPU=${var_gpu:-"no"} - ENABLE_NESTING=${var_nesting:-"1"} - ENABLE_KEYCTL=${var_keyctl:-"0"} - ENABLE_MKNOD=${var_mknod:-"0"} - MOUNT_FS=${var_mount_fs:-""} - PROTECT_CT=${var_protection:-"no"} - CT_TIMEZONE=${var_timezone:-"$timezone"} - [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones + # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) + ENABLE_GPU=${var_gpu:-"no"} + ENABLE_NESTING=${var_nesting:-"1"} + ENABLE_KEYCTL=${var_keyctl:-"0"} + ENABLE_MKNOD=${var_mknod:-"0"} + MOUNT_FS=${var_mount_fs:-""} + PROTECT_CT=${var_protection:-"no"} + CT_TIMEZONE=${var_timezone:-"$timezone"} + [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones - # 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 } # ------------------------------------------------------------------------------ @@ -662,59 +662,59 @@ base_settings() { # - Only loads whitelisted var_* keys # ------------------------------------------------------------------------------ load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" - # Allowed var_* keys - local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # Allowed var_* keys + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) - # Whitelist check helper - _is_whitelisted() { - local k="$1" w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 - } + # Whitelist check helper + _is_whitelisted() { + local k="$1" w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } - 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]}" + 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 "$var_key" || continue + [[ "$var_key" != var_* ]] && continue + _is_whitelisted "$var_key" || continue - # Strip inline comments (everything after unquoted #) - # Handle: var=value # comment OR var="value" # comment - if [[ ! "$var_val" =~ ^[\"\'] ]]; then - # Unquoted value: strip from first # - var_val="${var_val%%#*}" - fi + # Strip inline comments (everything after unquoted #) + # Handle: var=value # comment OR var="value" # comment + if [[ ! "$var_val" =~ ^[\"\'] ]]; then + # Unquoted value: strip from first # + var_val="${var_val%%#*}" + fi - # Strip quotes - if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then - var_val="${BASH_REMATCH[1]}" - elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then - var_val="${BASH_REMATCH[1]}" - fi + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi - # Trim trailing whitespace - var_val="${var_val%"${var_val##*[![:space:]]}"}" + # Trim trailing whitespace + var_val="${var_val%"${var_val##*[![:space:]]}"}" - # Set only if not already exported - [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" - fi - done <"$file" - msg_ok "Loaded ${file}" + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + fi + done <"$file" + msg_ok "Loaded ${file}" } # ------------------------------------------------------------------------------ @@ -727,56 +727,56 @@ load_vars_file() { # - Calls base_settings "$VERBOSE" and echo_default # ------------------------------------------------------------------------------ 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_disk var_fuse var_gpu var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) + # 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_disk var_fuse var_gpu var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone 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 + # 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" - # 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 - # 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" + # Silent creation - no msg_info output + mkdir -p /usr/local/community-scripts - local canonical="/usr/local/community-scripts/default.vars" - # Silent creation - no msg_info output - 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" - # 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' + # 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. @@ -831,47 +831,47 @@ var_verbose=no # 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 + # 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" - # Silent creation - no output message - } + chmod 0644 "$canonical" + # Silent creation - no output message + } - # 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 - } + # 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 + } - # 1) Ensure file exists - _ensure_default_vars + # 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" + # 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 + # 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 User Defaults (default.vars) on node $PVEHOST_NAME${CL}" - echo_default + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using User Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default } # ------------------------------------------------------------------------------ @@ -882,8 +882,8 @@ EOF # ------------------------------------------------------------------------------ get_app_defaults_path() { - local n="${NSAPP:-${APP,,}}" - echo "/usr/local/community-scripts/defaults/${n}.vars" + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" } # ------------------------------------------------------------------------------ @@ -896,32 +896,32 @@ 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_disk var_fuse var_gpu - 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 - ) + # 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_disk var_fuse var_gpu + 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 + ) fi # Global whitelist check function (used by _load_vars_file_to_map and others) _is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 + 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" + # 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 @@ -929,190 +929,190 @@ _sanitize_value() { # 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" + 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 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 out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" - local found_change=0 + 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" + # 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 - printf "%b" "$out" + # 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)" + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" - # NET/GW - _net="${NET:-}" - _gate="" - case "${GATE:-}" in - ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; - esac + # 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 + # 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 + # 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 + # 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}" - _nesting="${ENABLE_NESTING:-1}" - _keyctl="${ENABLE_KEYCTL:-0}" - _mknod="${ENABLE_MKNOD:-0}" - _mount_fs="${ALLOW_MOUNT_FS:-}" - _protect="${PROTECT_CT:-no}" - _timezone="${CT_TIMEZONE:-}" - _tags="${TAGS:-}" - _verbose="${VERBOSE:-no}" + # 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}" + _nesting="${ENABLE_NESTING:-1}" + _keyctl="${ENABLE_KEYCTL:-0}" + _mknod="${ENABLE_MKNOD:-0}" + _mount_fs="${ALLOW_MOUNT_FS:-}" + _protect="${PROTECT_CT:-no}" + _timezone="${CT_TIMEZONE:-}" + _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}" + # 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:-}}" + # 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 "# 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")" + 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 "${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")" - # var_ipv6_static removed - static IPs are unique, can't be default + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + # 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")" + [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" - [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" - [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" - [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" - [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" - [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" - [ -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 "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" + [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" + [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" + [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" + [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" + [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" + [ -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")" - } >"$tmpf" + [ -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" + echo "$tmpf" } # ------------------------------------------------------------------------------ @@ -1123,103 +1123,103 @@ _build_current_app_vars_tmp() { # - 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)" + 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")" + # 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 + # 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 - - # 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." - break - ;; - esac - done - 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_custom "ℹ️" "${BL}" "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_custom "🚫" "${YW}" "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" } ensure_storage_selection_for_vars_file() { - local 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-) + # 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 + 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 + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container - # Silent operation - no output message + # Silent operation - no output message } 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" + 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" } # ============================================================================== @@ -1235,785 +1235,785 @@ ensure_global_default_vars_file() { # - Allows user to customize all container settings # ------------------------------------------------------------------------------ advanced_settings() { - # Enter alternate screen buffer to prevent flicker between dialogs - tput smcup 2>/dev/null || true - trap 'tput rmcup 2>/dev/null || true' RETURN + # Enter alternate screen buffer to prevent flicker between dialogs + tput smcup 2>/dev/null || true + trap 'tput rmcup 2>/dev/null || true' RETURN - # Initialize defaults - TAGS="community-script;${var_tags:-}" - local STEP=1 - local MAX_STEP=28 + # Initialize defaults + TAGS="community-script;${var_tags:-}" + local STEP=1 + local MAX_STEP=28 - # Store values for back navigation - inherit from var_* app defaults - local _ct_type="${var_unprivileged:-1}" - local _pw="" - local _pw_display="Automatic Login" - local _ct_id="$NEXTID" - local _hostname="$NSAPP" - local _disk_size="${var_disk:-4}" - local _core_count="${var_cpu:-1}" - local _ram_size="${var_ram:-1024}" - local _bridge="${var_brg:-vmbr0}" - local _net="${var_net:-dhcp}" - local _gate="${var_gateway:-}" - local _ipv6_method="${var_ipv6_method:-auto}" - local _ipv6_addr="" - local _ipv6_gate="" - local _apt_cacher="${var_apt_cacher:-no}" - local _apt_cacher_ip="${var_apt_cacher_ip:-}" - local _mtu="${var_mtu:-}" - local _sd="${var_searchdomain:-}" - local _ns="${var_ns:-}" - local _mac="${var_mac:-}" - local _vlan="${var_vlan:-}" - local _tags="$TAGS" - local _enable_fuse="${var_fuse:-no}" - local _enable_tun="${var_tun:-no}" - local _enable_gpu="${var_gpu:-no}" - local _enable_nesting="${var_nesting:-1}" - local _verbose="${var_verbose:-no}" - local _enable_keyctl="${var_keyctl:-0}" - local _enable_mknod="${var_mknod:-0}" - local _mount_fs="${var_mount_fs:-}" - local _protect_ct="${var_protection:-no}" + # Store values for back navigation - inherit from var_* app defaults + local _ct_type="${var_unprivileged:-1}" + local _pw="" + local _pw_display="Automatic Login" + local _ct_id="$NEXTID" + local _hostname="$NSAPP" + local _disk_size="${var_disk:-4}" + local _core_count="${var_cpu:-1}" + local _ram_size="${var_ram:-1024}" + local _bridge="${var_brg:-vmbr0}" + local _net="${var_net:-dhcp}" + local _gate="${var_gateway:-}" + local _ipv6_method="${var_ipv6_method:-auto}" + local _ipv6_addr="" + local _ipv6_gate="" + local _apt_cacher="${var_apt_cacher:-no}" + local _apt_cacher_ip="${var_apt_cacher_ip:-}" + local _mtu="${var_mtu:-}" + local _sd="${var_searchdomain:-}" + local _ns="${var_ns:-}" + local _mac="${var_mac:-}" + local _vlan="${var_vlan:-}" + local _tags="$TAGS" + local _enable_fuse="${var_fuse:-no}" + local _enable_tun="${var_tun:-no}" + local _enable_gpu="${var_gpu:-no}" + local _enable_nesting="${var_nesting:-1}" + local _verbose="${var_verbose:-no}" + local _enable_keyctl="${var_keyctl:-0}" + local _enable_mknod="${var_mknod:-0}" + local _mount_fs="${var_mount_fs:-}" + local _protect_ct="${var_protection:-no}" - # Detect host timezone for default (if not set via var_timezone) - local _host_timezone="" - if command -v timedatectl >/dev/null 2>&1; then - _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") - elif [ -f /etc/timezone ]; then - _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") - fi - local _ct_timezone="${var_timezone:-$_host_timezone}" + # Detect host timezone for default (if not set via var_timezone) + local _host_timezone="" + if command -v timedatectl >/dev/null 2>&1; then + _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") + elif [ -f /etc/timezone ]; then + _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") + fi + local _ct_timezone="${var_timezone:-$_host_timezone}" - # Helper to show current progress - show_progress() { - local current=$1 - local total=$MAX_STEP - echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" - } + # Helper to show current progress + show_progress() { + local current=$1 + local total=$MAX_STEP + echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" + } - # Detect available bridges (do this once) - local BRIDGES="" - local BRIDGE_MENU_OPTIONS=() - _detect_bridges() { - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) - BRIDGES="" - local OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true - if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - local start=$(echo "${pair}" | cut -d':' -f1) - local end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - local 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) + # Detect available bridges (do this once) + local BRIDGES="" + local BRIDGE_MENU_OPTIONS=() + _detect_bridges() { + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) + BRIDGES="" + local OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true + if [ -f "${iface_indexes_tmpfile}" ]; then + while read -r pair; do + local start=$(echo "${pair}" | cut -d':' -f1) + local end=$(echo "${pair}" | cut -d':' -f2) + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + local 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) - # Build bridge menu - BRIDGE_MENU_OPTIONS=() - if [[ -n "$BRIDGES" ]]; then - while IFS= read -r bridge; do - if [[ -n "$bridge" ]]; then - local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') - BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") - fi - done <<<"$BRIDGES" + # Build bridge menu + BRIDGE_MENU_OPTIONS=() + if [[ -n "$BRIDGES" ]]; then + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//') + BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") fi - } - _detect_bridges + done <<<"$BRIDGES" + fi + } + _detect_bridges - # Main wizard loop - while [ $STEP -le $MAX_STEP ]; do - case $STEP in + # Main wizard loop + while [ $STEP -le $MAX_STEP ]; do + case $STEP in - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 1: Container Type - # ═══════════════════════════════════════════════════════════════════════════ - 1) - local default_on="ON" - local default_off="OFF" - [[ "$_ct_type" == "0" ]] && { - default_on="OFF" - default_off="ON" - } + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 1: Container Type + # ═══════════════════════════════════════════════════════════════════════════ + 1) + local default_on="ON" + local default_off="OFF" + [[ "$_ct_type" == "0" ]] && { + default_on="OFF" + default_off="ON" + } - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TYPE" \ - --ok-button "Next" --cancel-button "Exit" \ - --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ - "1" "Unprivileged (recommended)" $default_on \ - "0" "Privileged" $default_off \ - 3>&1 1>&2 2>&3); then - [[ -n "$result" ]] && _ct_type="$result" - ((STEP++)) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TYPE" \ + --ok-button "Next" --cancel-button "Exit" \ + --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ + "1" "Unprivileged (recommended)" $default_on \ + "0" "Privileged" $default_off \ + 3>&1 1>&2 2>&3); then + [[ -n "$result" ]] && _ct_type="$result" + ((STEP++)) + else + exit_script + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 2: Root Password + # ═══════════════════════════════════════════════════════════════════════════ + 2) + if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "ROOT PASSWORD" \ + --ok-button "Next" --cancel-button "Back" \ + --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ + 3>&1 1>&2 2>&3); then + + if [[ -z "$PW1" ]]; then + _pw="" + _pw_display="Automatic Login" + ((STEP++)) + elif [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + elif ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + else + # Verify password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "PASSWORD VERIFICATION" \ + --ok-button "Confirm" --cancel-button "Back" \ + --passwordbox "\nVerify Root Password" 10 58 \ + 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + _pw="-password $PW1" + _pw_display="********" + ((STEP++)) else - exit_script + whiptail --msgbox "Passwords do not match. Please try again." 8 58 fi - ;; + else + ((STEP--)) + fi + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 2: Root Password - # ═══════════════════════════════════════════════════════════════════════════ - 2) - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "ROOT PASSWORD" \ + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 3: Container ID + # ═══════════════════════════════════════════════════════════════════════════ + 3) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER ID" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ + 3>&1 1>&2 2>&3); then + _ct_id="${result:-$NEXTID}" + ((STEP++)) + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 4: Hostname + # ═══════════════════════════════════════════════════════════════════════════ + 4) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "HOSTNAME" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ + 3>&1 1>&2 2>&3); then + local hn_test="${result:-$NSAPP}" + hn_test=$(echo "${hn_test,,}" | tr -d ' ') + if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + _hostname="$hn_test" + ((STEP++)) + else + whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 5: Disk Size + # ═══════════════════════════════════════════════════════════════════════════ + 5) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DISK SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ + 3>&1 1>&2 2>&3); then + local disk_test="${result:-$var_disk}" + if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then + _disk_size="$disk_test" + ((STEP++)) + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 6: CPU Cores + # ═══════════════════════════════════════════════════════════════════════════ + 6) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CPU CORES" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ + 3>&1 1>&2 2>&3); then + local cpu_test="${result:-$var_cpu}" + if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then + _core_count="$cpu_test" + ((STEP++)) + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 7: RAM Size + # ═══════════════════════════════════════════════════════════════════════════ + 7) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "RAM SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ + 3>&1 1>&2 2>&3); then + local ram_test="${result:-$var_ram}" + if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then + _ram_size="$ram_test" + ((STEP++)) + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + else + ((STEP--)) + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 8: Network Bridge + # ═══════════════════════════════════════════════════════════════════════════ + 8) + if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then + _bridge="vmbr0" + ((STEP++)) + else + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NETWORK BRIDGE" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect network bridge:" 16 58 6 \ + "${BRIDGE_MENU_OPTIONS[@]}" \ + 3>&1 1>&2 2>&3); then + _bridge="${result:-vmbr0}" + ((STEP++)) + else + ((STEP--)) + fi + fi + ;; + + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 9: IPv4 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 9) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv4 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3); then + + if [[ "$result" == "static" ]]; then + # Get static IP + local static_ip + if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "STATIC IPv4 ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + # Get gateway + local gateway_ip + if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GATEWAY IP" \ --ok-button "Next" --cancel-button "Back" \ - --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ + --inputbox "\nEnter Gateway IP address" 10 58 "" \ 3>&1 1>&2 2>&3); then - - if [[ -z "$PW1" ]]; then - _pw="" - _pw_display="Automatic Login" - ((STEP++)) - elif [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - elif ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 + if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + _net="$static_ip" + _gate=",gw=$gateway_ip" + ((STEP++)) else - # Verify password - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "PASSWORD VERIFICATION" \ - --ok-button "Confirm" --cancel-button "Back" \ - --passwordbox "\nVerify Root Password" 10 58 \ - 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - _pw="-password $PW1" - _pw_display="********" - ((STEP++)) - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - ((STEP--)) - fi + whiptail --msgbox "Invalid Gateway IP format." 8 58 fi + fi else - ((STEP--)) + whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 fi - ;; + fi + else + _net="dhcp" + _gate="" + ((STEP++)) + fi + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 3: Container ID - # ═══════════════════════════════════════════════════════════════════════════ - 3) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER ID" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ - 3>&1 1>&2 2>&3); then - _ct_id="${result:-$NEXTID}" - ((STEP++)) + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 10: IPv6 Configuration + # ═══════════════════════════════════════════════════════════════════════════ + 10) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IPv6 CONFIGURATION" \ + --ok-button "Next" --cancel-button "Back" \ + --menu "\nSelect IPv6 Address Management:" 16 70 5 \ + "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ + "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ + "static" "Static - Manual IPv6 address configuration" \ + "none" "None - No IPv6 assignment (most containers)" \ + "disable" "Fully Disabled - (breaks some services)" \ + 3>&1 1>&2 2>&3); then + + _ipv6_method="$result" + case "$result" in + static) + local ipv6_addr + if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "STATIC IPv6 ADDRESS" \ + --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + _ipv6_addr="$ipv6_addr" + # Optional gateway + _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "IPv6 GATEWAY" \ + --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ + 3>&1 1>&2 2>&3) || true + ((STEP++)) else - ((STEP--)) + whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 fi - ;; + fi + ;; + dhcp) + _ipv6_addr="dhcp" + _ipv6_gate="" + ((STEP++)) + ;; + disable) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + none) + _ipv6_addr="none" + _ipv6_gate="" + ((STEP++)) + ;; + *) + _ipv6_addr="" + _ipv6_gate="" + ((STEP++)) + ;; + esac + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 4: Hostname - # ═══════════════════════════════════════════════════════════════════════════ - 4) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "HOSTNAME" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ - 3>&1 1>&2 2>&3); then - local hn_test="${result:-$NSAPP}" - hn_test=$(echo "${hn_test,,}" | tr -d ' ') - if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - _hostname="$hn_test" - ((STEP++)) - else - whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 11: MTU Size + # ═══════════════════════════════════════════════════════════════════════════ + 11) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MTU SIZE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mtu="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 5: Disk Size - # ═══════════════════════════════════════════════════════════════════════════ - 5) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DISK SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ - 3>&1 1>&2 2>&3); then - local disk_test="${result:-$var_disk}" - if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then - _disk_size="$disk_test" - ((STEP++)) - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 12: DNS Search Domain + # ═══════════════════════════════════════════════════════════════════════════ + 12) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SEARCH DOMAIN" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _sd="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 6: CPU Cores - # ═══════════════════════════════════════════════════════════════════════════ - 6) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CPU CORES" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ - 3>&1 1>&2 2>&3); then - local cpu_test="${result:-$var_cpu}" - if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then - _core_count="$cpu_test" - ((STEP++)) - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 13: DNS Server + # ═══════════════════════════════════════════════════════════════════════════ + 13) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DNS SERVER" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _ns="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 7: RAM Size - # ═══════════════════════════════════════════════════════════════════════════ - 7) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "RAM SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ - 3>&1 1>&2 2>&3); then - local ram_test="${result:-$var_ram}" - if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then - _ram_size="$ram_test" - ((STEP++)) - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 14: MAC Address + # ═══════════════════════════════════════════════════════════════════════════ + 14) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MAC ADDRESS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _mac="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 8: Network Bridge - # ═══════════════════════════════════════════════════════════════════════════ - 8) - if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then - _bridge="vmbr0" - ((STEP++)) - else - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NETWORK BRIDGE" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect network bridge:" 16 58 6 \ - "${BRIDGE_MENU_OPTIONS[@]}" \ - 3>&1 1>&2 2>&3); then - _bridge="${result:-vmbr0}" - ((STEP++)) - else - ((STEP--)) - fi - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 15: VLAN Tag + # ═══════════════════════════════════════════════════════════════════════════ + 15) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VLAN TAG" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ + 3>&1 1>&2 2>&3); then + _vlan="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 9: IPv4 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 9) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv4 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3); then + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 16: Tags + # ═══════════════════════════════════════════════════════════════════════════ + 16) + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TAGS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ + 3>&1 1>&2 2>&3); then + _tags="${result:-;}" + _tags=$(echo "$_tags" | tr -d '[:space:]') + ((STEP++)) + else + ((STEP--)) + fi + ;; - if [[ "$result" == "static" ]]; then - # Get static IP - local static_ip - if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "STATIC IPv4 ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - # Get gateway - local gateway_ip - if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GATEWAY IP" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nEnter Gateway IP address" 10 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - _net="$static_ip" - _gate=",gw=$gateway_ip" - ((STEP++)) - else - whiptail --msgbox "Invalid Gateway IP format." 8 58 - fi - fi - else - whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 - fi - fi - else - _net="dhcp" - _gate="" - ((STEP++)) - fi - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 17: SSH Settings + # ═══════════════════════════════════════════════════════════════════════════ + 17) + configure_ssh_settings "Step $STEP/$MAX_STEP" + # configure_ssh_settings handles its own flow, always advance + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 10: IPv6 Configuration - # ═══════════════════════════════════════════════════════════════════════════ - 10) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "IPv6 CONFIGURATION" \ - --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv6 Address Management:" 16 70 5 \ - "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ - "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ - "static" "Static - Manual IPv6 address configuration" \ - "none" "None - No IPv6 assignment (most containers)" \ - "disable" "Fully Disabled - (breaks some services)" \ - 3>&1 1>&2 2>&3); then + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 18: FUSE Support + # ═══════════════════════════════════════════════════════════════════════════ + 18) + local fuse_default_flag="--defaultno" + [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" - _ipv6_method="$result" - case "$result" in - static) - local ipv6_addr - if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "STATIC IPv6 ADDRESS" \ - --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - _ipv6_addr="$ipv6_addr" - # Optional gateway - _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "IPv6 GATEWAY" \ - --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ - 3>&1 1>&2 2>&3) || true - ((STEP++)) - else - whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 - fi - fi - ;; - dhcp) - _ipv6_addr="dhcp" - _ipv6_gate="" - ((STEP++)) - ;; - disable) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - none) - _ipv6_addr="none" - _ipv6_gate="" - ((STEP++)) - ;; - *) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; - esac - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "FUSE SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $fuse_default_flag \ + --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then + _enable_fuse="yes" + else + if [ $? -eq 1 ]; then + _enable_fuse="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 11: MTU Size - # ═══════════════════════════════════════════════════════════════════════════ - 11) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MTU SIZE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mtu="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 19: TUN/TAP Support + # ═══════════════════════════════════════════════════════════════════════════ + 19) + local tun_default_flag="--defaultno" + [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 12: DNS Search Domain - # ═══════════════════════════════════════════════════════════════════════════ - 12) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SEARCH DOMAIN" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _sd="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "TUN/TAP SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $tun_default_flag \ + --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then + _enable_tun="yes" + else + if [ $? -eq 1 ]; then + _enable_tun="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 13: DNS Server - # ═══════════════════════════════════════════════════════════════════════════ - 13) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DNS SERVER" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _ns="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 20: Nesting Support + # ═══════════════════════════════════════════════════════════════════════════ + 20) + local nesting_default_flag="" + [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 14: MAC Address - # ═══════════════════════════════════════════════════════════════════════════ - 14) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MAC ADDRESS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _mac="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "NESTING SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $nesting_default_flag \ + --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then + _enable_nesting="1" + else + if [ $? -eq 1 ]; then + _enable_nesting="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 15: VLAN Tag - # ═══════════════════════════════════════════════════════════════════════════ - 15) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VLAN TAG" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ - 3>&1 1>&2 2>&3); then - _vlan="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 21: GPU Passthrough + # ═══════════════════════════════════════════════════════════════════════════ + 21) + local gpu_default_flag="--defaultno" + [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 16: Tags - # ═══════════════════════════════════════════════════════════════════════════ - 16) - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TAGS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ - 3>&1 1>&2 2>&3); then - _tags="${result:-;}" - _tags=$(echo "$_tags" | tr -d '[:space:]') - ((STEP++)) - else - ((STEP--)) - fi - ;; + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GPU PASSTHROUGH" \ + --ok-button "Next" --cancel-button "Back" \ + $gpu_default_flag \ + --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then + _enable_gpu="yes" + else + if [ $? -eq 1 ]; then + _enable_gpu="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 17: SSH Settings - # ═══════════════════════════════════════════════════════════════════════════ - 17) - configure_ssh_settings "Step $STEP/$MAX_STEP" - # configure_ssh_settings handles its own flow, always advance - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 22: Keyctl Support (Docker/systemd) + # ═══════════════════════════════════════════════════════════════════════════ + 22) + local keyctl_default_flag="--defaultno" + [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 18: FUSE Support - # ═══════════════════════════════════════════════════════════════════════════ - 18) - local fuse_default_flag="--defaultno" - [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "KEYCTL SUPPORT" \ + --ok-button "Next" --cancel-button "Back" \ + $keyctl_default_flag \ + --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then + _enable_keyctl="1" + else + if [ $? -eq 1 ]; then + _enable_keyctl="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "FUSE SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $fuse_default_flag \ - --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then - _enable_fuse="yes" - else - if [ $? -eq 1 ]; then - _enable_fuse="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 23: APT Cacher Proxy + # ═══════════════════════════════════════════════════════════════════════════ + 23) + local apt_cacher_default_flag="--defaultno" + [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 19: TUN/TAP Support - # ═══════════════════════════════════════════════════════════════════════════ - 19) - local tun_default_flag="--defaultno" - [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER PROXY" \ + --ok-button "Next" --cancel-button "Back" \ + $apt_cacher_default_flag \ + --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then + _apt_cacher="yes" + # Ask for IP if enabled + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "APT CACHER IP" \ + --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ + 3>&1 1>&2 2>&3); then + _apt_cacher_ip="$result" + fi + else + if [ $? -eq 1 ]; then + _apt_cacher="no" + _apt_cacher_ip="" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "TUN/TAP SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $tun_default_flag \ - --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then - _enable_tun="yes" - else - if [ $? -eq 1 ]; then - _enable_tun="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 24: Container Timezone + # ═══════════════════════════════════════════════════════════════════════════ + 24) + local tz_hint="$_ct_timezone" + [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 20: Nesting Support - # ═══════════════════════════════════════════════════════════════════════════ - 20) - local nesting_default_flag="" - [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER TIMEZONE" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ + 3>&1 1>&2 2>&3); then + _ct_timezone="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "NESTING SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $nesting_default_flag \ - --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then - _enable_nesting="1" - else - if [ $? -eq 1 ]; then - _enable_nesting="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 25: Container Protection + # ═══════════════════════════════════════════════════════════════════════════ + 25) + local protect_default_flag="--defaultno" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 21: GPU Passthrough - # ═══════════════════════════════════════════════════════════════════════════ - 21) - local gpu_default_flag="--defaultno" - [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONTAINER PROTECTION" \ + --ok-button "Next" --cancel-button "Back" \ + $protect_default_flag \ + --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then + _protect_ct="yes" + else + if [ $? -eq 1 ]; then + _protect_ct="no" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "GPU PASSTHROUGH" \ - --ok-button "Next" --cancel-button "Back" \ - $gpu_default_flag \ - --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then - _enable_gpu="yes" - else - if [ $? -eq 1 ]; then - _enable_gpu="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 26: Device Node Creation (mknod) + # ═══════════════════════════════════════════════════════════════════════════ + 26) + local mknod_default_flag="--defaultno" + [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 22: Keyctl Support (Docker/systemd) - # ═══════════════════════════════════════════════════════════════════════════ - 22) - local keyctl_default_flag="--defaultno" - [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "DEVICE NODE CREATION" \ + --ok-button "Next" --cancel-button "Back" \ + $mknod_default_flag \ + --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then + _enable_mknod="1" + else + if [ $? -eq 1 ]; then + _enable_mknod="0" + else + ((STEP--)) + continue + fi + fi + ((STEP++)) + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "KEYCTL SUPPORT" \ - --ok-button "Next" --cancel-button "Back" \ - $keyctl_default_flag \ - --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 62; then - _enable_keyctl="1" - else - if [ $? -eq 1 ]; then - _enable_keyctl="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 27: Mount Filesystems + # ═══════════════════════════════════════════════════════════════════════════ + 27) + local mount_hint="" + [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 23: APT Cacher Proxy - # ═══════════════════════════════════════════════════════════════════════════ - 23) - local apt_cacher_default_flag="--defaultno" - [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" + if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "MOUNT FILESYSTEMS" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ + 3>&1 1>&2 2>&3); then + _mount_fs="$result" + ((STEP++)) + else + ((STEP--)) + fi + ;; - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER PROXY" \ - --ok-button "Next" --cancel-button "Back" \ - $apt_cacher_default_flag \ - --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then - _apt_cacher="yes" - # Ask for IP if enabled - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "APT CACHER IP" \ - --inputbox "\nEnter APT Cacher-NG server IP address:" 10 58 "$_apt_cacher_ip" \ - 3>&1 1>&2 2>&3); then - _apt_cacher_ip="$result" - fi - else - if [ $? -eq 1 ]; then - _apt_cacher="no" - _apt_cacher_ip="" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + # ═══════════════════════════════════════════════════════════════════════════ + # STEP 28: Verbose Mode & Confirmation + # ═══════════════════════════════════════════════════════════════════════════ + 28) + local verbose_default_flag="--defaultno" + [[ "$_verbose" == "yes" ]] && verbose_default_flag="" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 24: Container Timezone - # ═══════════════════════════════════════════════════════════════════════════ - 24) - local tz_hint="$_ct_timezone" - [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "VERBOSE MODE" \ + $verbose_default_flag \ + --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then + _verbose="yes" + else + _verbose="no" + fi + # Build summary + local ct_type_desc="Unprivileged" + [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER TIMEZONE" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ - 3>&1 1>&2 2>&3); then - _ct_timezone="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; + local nesting_desc="Disabled" + [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 25: Container Protection - # ═══════════════════════════════════════════════════════════════════════════ - 25) - local protect_default_flag="--defaultno" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" + local keyctl_desc="Disabled" + [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONTAINER PROTECTION" \ - --ok-button "Next" --cancel-button "Back" \ - $protect_default_flag \ - --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then - _protect_ct="yes" - else - if [ $? -eq 1 ]; then - _protect_ct="no" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; + local protect_desc="No" + [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 26: Device Node Creation (mknod) - # ═══════════════════════════════════════════════════════════════════════════ - 26) - local mknod_default_flag="--defaultno" - [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" + local tz_display="${_ct_timezone:-Host TZ}" + local apt_display="${_apt_cacher:-no}" + [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "DEVICE NODE CREATION" \ - --ok-button "Next" --cancel-button "Back" \ - $mknod_default_flag \ - --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then - _enable_mknod="1" - else - if [ $? -eq 1 ]; then - _enable_mknod="0" - else - ((STEP--)) - continue - fi - fi - ((STEP++)) - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 27: Mount Filesystems - # ═══════════════════════════════════════════════════════════════════════════ - 27) - local mount_hint="" - [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" - - if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "MOUNT FILESYSTEMS" \ - --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ - 3>&1 1>&2 2>&3); then - _mount_fs="$result" - ((STEP++)) - else - ((STEP--)) - fi - ;; - - # ═══════════════════════════════════════════════════════════════════════════ - # STEP 28: Verbose Mode & Confirmation - # ═══════════════════════════════════════════════════════════════════════════ - 28) - local verbose_default_flag="--defaultno" - [[ "$_verbose" == "yes" ]] && verbose_default_flag="" - - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "VERBOSE MODE" \ - $verbose_default_flag \ - --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then - _verbose="yes" - else - _verbose="no" - fi - # Build summary - local ct_type_desc="Unprivileged" - [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" - - local nesting_desc="Disabled" - [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" - - local keyctl_desc="Disabled" - [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" - - local protect_desc="No" - [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" - - local tz_display="${_ct_timezone:-Host TZ}" - local apt_display="${_apt_cacher:-no}" - [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" - - local summary="Container Type: $ct_type_desc + local summary="Container Type: $ct_type_desc Container ID: $_ct_id Hostname: $_hostname @@ -2037,105 +2037,105 @@ Advanced: APT Cacher: $apt_display Verbose: $_verbose" - if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "CONFIRM SETTINGS" \ - --ok-button "Create LXC" --cancel-button "Back" \ - --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then - ((STEP++)) - else - ((STEP--)) - fi - ;; - esac - done + if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "CONFIRM SETTINGS" \ + --ok-button "Create LXC" --cancel-button "Back" \ + --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then + ((STEP++)) + else + ((STEP--)) + fi + ;; + esac + done - # ═══════════════════════════════════════════════════════════════════════════ - # Apply all collected values to global variables - # ═══════════════════════════════════════════════════════════════════════════ - CT_TYPE="$_ct_type" - PW="$_pw" - CT_ID="$_ct_id" - HN="$_hostname" - DISK_SIZE="$_disk_size" - CORE_COUNT="$_core_count" - RAM_SIZE="$_ram_size" - BRG="$_bridge" - NET="$_net" - GATE="$_gate" - IPV6_METHOD="$_ipv6_method" - IPV6_ADDR="$_ipv6_addr" - IPV6_GATE="$_ipv6_gate" - TAGS="$_tags" - ENABLE_FUSE="$_enable_fuse" - ENABLE_TUN="$_enable_tun" - ENABLE_GPU="$_enable_gpu" - ENABLE_NESTING="$_enable_nesting" - ENABLE_KEYCTL="$_enable_keyctl" - ENABLE_MKNOD="$_enable_mknod" - ALLOW_MOUNT_FS="$_mount_fs" - PROTECT_CT="$_protect_ct" - CT_TIMEZONE="$_ct_timezone" - APT_CACHER="$_apt_cacher" - APT_CACHER_IP="$_apt_cacher_ip" - VERBOSE="$_verbose" + # ═══════════════════════════════════════════════════════════════════════════ + # Apply all collected values to global variables + # ═══════════════════════════════════════════════════════════════════════════ + CT_TYPE="$_ct_type" + PW="$_pw" + CT_ID="$_ct_id" + HN="$_hostname" + DISK_SIZE="$_disk_size" + CORE_COUNT="$_core_count" + RAM_SIZE="$_ram_size" + BRG="$_bridge" + NET="$_net" + GATE="$_gate" + IPV6_METHOD="$_ipv6_method" + IPV6_ADDR="$_ipv6_addr" + IPV6_GATE="$_ipv6_gate" + TAGS="$_tags" + ENABLE_FUSE="$_enable_fuse" + ENABLE_TUN="$_enable_tun" + ENABLE_GPU="$_enable_gpu" + ENABLE_NESTING="$_enable_nesting" + ENABLE_KEYCTL="$_enable_keyctl" + ENABLE_MKNOD="$_enable_mknod" + ALLOW_MOUNT_FS="$_mount_fs" + PROTECT_CT="$_protect_ct" + CT_TIMEZONE="$_ct_timezone" + APT_CACHER="$_apt_cacher" + APT_CACHER_IP="$_apt_cacher_ip" + VERBOSE="$_verbose" - # Update var_* based on user choice (for functions that check these) - var_gpu="$_enable_gpu" - var_fuse="$_enable_fuse" - var_tun="$_enable_tun" - var_nesting="$_enable_nesting" - var_keyctl="$_enable_keyctl" - var_mknod="$_enable_mknod" - var_mount_fs="$_mount_fs" - var_protection="$_protect_ct" - var_timezone="$_ct_timezone" - var_apt_cacher="$_apt_cacher" - var_apt_cacher_ip="$_apt_cacher_ip" + # Update var_* based on user choice (for functions that check these) + var_gpu="$_enable_gpu" + var_fuse="$_enable_fuse" + var_tun="$_enable_tun" + var_nesting="$_enable_nesting" + var_keyctl="$_enable_keyctl" + var_mknod="$_enable_mknod" + var_mount_fs="$_mount_fs" + var_protection="$_protect_ct" + var_timezone="$_ct_timezone" + var_apt_cacher="$_apt_cacher" + var_apt_cacher_ip="$_apt_cacher_ip" - # Format optional values - [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" - [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" - [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" - [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" - [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" + # Format optional values + [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" + [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" + [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" + [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" + [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" - # Alpine UDHCPC fix - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - export SSH_KEYS_FILE + # Alpine UDHCPC fix + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + export SSH_KEYS_FILE - # Exit alternate screen buffer BEFORE displaying summary - # so the summary is visible in the main terminal - tput rmcup 2>/dev/null || true - trap - RETURN # Remove the trap since we already called rmcup + # Exit alternate screen buffer BEFORE displaying summary + # so the summary is visible in the main terminal + tput rmcup 2>/dev/null || true + trap - RETURN # Remove the trap since we already called rmcup - # Display final summary - echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" - echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" - [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" - echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" - [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" - [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" - [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + # Display final summary + echo -e "\n${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" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${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 "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" + echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" + [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" + echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" + [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" + [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" + [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" } # ============================================================================== @@ -2153,13 +2153,13 @@ Advanced: # - Sets global DIAGNOSTICS variable for API telemetry opt-in/out # ------------------------------------------------------------------------------ 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. @@ -2182,9 +2182,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. @@ -2207,34 +2207,34 @@ 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 } 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 + 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 } # ------------------------------------------------------------------------------ @@ -2245,25 +2245,25 @@ diagnostics_menu() { # - 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 [ "${var_gpu:-no}" == "yes" ]; then - echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" - fi - 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 " " + 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 [ "${var_gpu:-no}" == "yes" ]; then + echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" + fi + 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 " " } # ------------------------------------------------------------------------------ @@ -2275,196 +2275,196 @@ echo_default() { # - Applies chosen settings and triggers container build # ------------------------------------------------------------------------------ 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) + + # Get timezone using timedatectl (Debian 13+ compatible) + # Fallback to /etc/timezone for older systems + if command -v timedatectl >/dev/null 2>&1; then + timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") + elif [ -f /etc/timezone ]; then + timezone=$(cat /etc/timezone) + else + timezone="UTC" + fi + + # 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" "User 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 + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # Main menu loop - allows returning from Settings + while true; do + 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 - NEXTID=$(pvesh get /cluster/nextid) + # --- 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)" + break + ;; + 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" + break + ;; + 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + break + ;; + "$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)" + break + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + # After settings menu, show main menu again + header_info + CHOICE="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + done - # Get timezone using timedatectl (Debian 13+ compatible) - # Fallback to /etc/timezone for older systems - if command -v timedatectl >/dev/null 2>&1; then - timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") - elif [ -f /etc/timezone ]; then - timezone=$(cat /etc/timezone) - else - timezone="UTC" - fi + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi - # 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" "User 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 - - APPDEFAULTS_OPTION="$appdefaults_option" - SETTINGS_OPTION="$settings_option" - - # Main menu loop - allows returning from Settings - while true; do - 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 - - # --- 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)" - break - ;; - 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" - break - ;; - 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - defaults_target="/usr/local/community-scripts/default.vars" - break - ;; - "$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)" - break - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - "$SETTINGS_OPTION" | settings | SETTINGS) - settings_menu - # After settings menu, show main menu again - header_info - CHOICE="" - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac - done - - 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 + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi } edit_default_storage() { - local vf="/usr/local/community-scripts/default.vars" + local vf="/usr/local/community-scripts/default.vars" - # Ensure file exists - if [[ ! -f "$vf" ]]; then - mkdir -p "$(dirname "$vf")" - touch "$vf" - fi + # 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" + # 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" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("3" "Edit App.vars for ${APP}") - settings_items+=("4" "Back to Main Menu") - else - settings_items+=("3" "Back to Main Menu") - fi + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("3" "Edit App.vars for ${APP}") + settings_items+=("4" "Back to Main Menu") + else + settings_items+=("3" "Back to Main Menu") + fi - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || exit_script + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || exit_script - case "$choice" in - 1) diagnostics_menu ;; - 2) nano /usr/local/community-scripts/default.vars ;; - 3) - if [ -f "$(get_app_defaults_path)" ]; then - nano "$(get_app_defaults_path)" - else - # Back was selected (no app.vars available) - return - fi - ;; - 4) - # Back to main menu - return - ;; - esac - done + case "$choice" in + 1) diagnostics_menu ;; + 2) nano /usr/local/community-scripts/default.vars ;; + 3) + if [ -f "$(get_app_defaults_path)" ]; then + nano "$(get_app_defaults_path)" + else + # Back was selected (no app.vars available) + return + fi + ;; + 4) + # Back to main menu + return + ;; + esac + done } # ------------------------------------------------------------------------------ @@ -2474,21 +2474,21 @@ settings_menu() { # - 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) + 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 "" + 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 } # ------------------------------------------------------------------------------ @@ -2498,18 +2498,18 @@ check_container_resources() { # - 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 + 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 } # ------------------------------------------------------------------------------ @@ -2519,9 +2519,9 @@ check_container_storage() { # - 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 ' + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' /^[[:space:]]*#/ {next} /^[[:space:]]*$/ {next} # nackt: typ base64 [comment] @@ -2541,45 +2541,45 @@ ssh_extract_keys_from_file() { # - 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 + 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 + 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 + # 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}..." + 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 + 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 } # ------------------------------------------------------------------------------ @@ -2589,105 +2589,105 @@ ssh_build_choices_from_files() { # - 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[@]}" + 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() { - local step_info="${1:-}" - local backtitle="[dev] Proxmox VE Helper Scripts" - [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" + local step_info="${1:-}" + local backtitle="[dev] Proxmox VE Helper Scripts" + [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" + 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" + 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 "$backtitle" --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 "$backtitle" --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 + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "$backtitle" --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 "$backtitle" --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 "$backtitle" --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 + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "$backtitle" --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 "$backtitle" \ + --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 "$backtitle" \ + --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 "$backtitle" --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 - ;; - manual) - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "$backtitle" \ - --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 "$backtitle" \ - --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 "$backtitle" --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 "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 - fi - else - whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 - fi + done + else + whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 fi - ;; - none) - : - ;; - esac - - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" + else + whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 + fi fi + ;; + none) + : + ;; + esac - # Always show SSH access dialog - user should be able to enable SSH even without keys - if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + # Always show SSH access dialog - user should be able to enable SSH even without keys + if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi } # ------------------------------------------------------------------------------ @@ -2699,39 +2699,39 @@ configure_ssh_settings() { # - 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) + 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 + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi } # ============================================================================== @@ -2752,256 +2752,256 @@ start() { # - Posts installation telemetry to API if diagnostics enabled # ------------------------------------------------------------------------------ build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + 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) ;; + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; esac + fi - # Build FEATURES string based on container type and user choices - FEATURES="" + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" - # Nesting support (user configurable, default enabled) - if [ "${ENABLE_NESTING:-1}" == "1" ]; then - FEATURES="nesting=1" - fi + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi - # Keyctl for unprivileged containers (needed for Docker) - if [ "$CT_TYPE" == "1" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}keyctl=1" - fi + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi - if [ "$ENABLE_FUSE" == "yes" ]; then - [ -n "$FEATURES" ] && FEATURES="$FEATURES," - FEATURES="${FEATURES}fuse=1" - fi + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi - # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly - # Key insight: Bash cannot export arrays, so we build the options as a string + # 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 - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null + # Build FEATURES string based on container type and user choices + FEATURES="" - # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + # Nesting support (user configurable, default enabled) + if [ "${ENABLE_NESTING:-1}" == "1" ]; then + FEATURES="nesting=1" + fi - # Core exports for install.func - 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" - 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" + # Keyctl for unprivileged containers (needed for Docker) + if [ "$CT_TYPE" == "1" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}keyctl=1" + fi - # DEV_MODE exports (optional, for debugging) - export BUILD_LOG="$BUILD_LOG" - export INSTALL_LOG="/root/.install-${SESSION_ID}.log" - export dev_mode="${dev_mode:-}" - export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" - export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" - export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" - export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" - export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" - export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" - export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" + if [ "$ENABLE_FUSE" == "yes" ]; then + [ -n "$FEATURES" ] && FEATURES="$FEATURES," + FEATURES="${FEATURES}fuse=1" + fi - # Build PCT_OPTIONS as multi-line string - PCT_OPTIONS_STRING=" -features $FEATURES + # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly + # Key insight: Bash cannot export arrays, so we build the options as a string + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + + # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + + # Core exports for install.func + 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" + 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" + + # DEV_MODE exports (optional, for debugging) + export BUILD_LOG="$BUILD_LOG" + export INSTALL_LOG="/root/.install-${SESSION_ID}.log" + export dev_mode="${dev_mode:-}" + export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" + export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" + export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" + export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" + export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" + export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" + export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" + + # Build PCT_OPTIONS as multi-line string + PCT_OPTIONS_STRING=" -features $FEATURES -hostname $HN -tags $TAGS" - # Add storage if specified - if [ -n "$SD" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING - $SD" - fi - - # Add nameserver if specified - if [ -n "$NS" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING - $NS" - fi - - # Network configuration + # Add storage if specified + if [ -n "$SD" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + $SD" + fi + + # Add nameserver if specified + if [ -n "$NS" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + $NS" + fi + + # Network configuration + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NET_STRING -onboot 1 -cores $CORE_COUNT -memory $RAM_SIZE -unprivileged $CT_TYPE" - # Protection flag (if var_protection was set) - if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Protection flag (if var_protection was set) + if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -protection 1" - fi + fi - # Timezone flag (if var_timezone was set) - if [ -n "${CT_TIMEZONE:-}" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Timezone flag (if var_timezone was set) + if [ -n "${CT_TIMEZONE:-}" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -timezone $CT_TIMEZONE" - fi + fi - # Password (already formatted) - if [ -n "$PW" ]; then - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + # Password (already formatted) + if [ -n "$PW" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $PW" + fi + + # Export as string (this works, unlike arrays!) + export PCT_OPTIONS="$PCT_OPTIONS_STRING" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + + # # DEBUG: Show final PCT_OPTIONS being exported + # echo "[DEBUG] PCT_OPTIONS to be exported:" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Calling create_lxc_container..." + + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # Check if GPU passthrough is enabled + # Returns true only if var_gpu is explicitly set to "yes" + # Can be set via: + # - Environment variable: var_gpu=yes bash -c "..." + # - CT script default: var_gpu="${var_gpu:-no}" + # - Advanced settings wizard + # - App defaults file: /usr/local/community-scripts/defaults/.vars + is_gpu_app() { + [[ "${var_gpu:-no}" == "yes" ]] && return 0 + 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_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") + done + fi fi - # Export as string (this works, unlike arrays!) - export PCT_OPTIONS="$PCT_OPTIONS_STRING" - export TEMPLATE_STORAGE="${var_template_storage:-}" - export CONTAINER_STORAGE="${var_container_storage:-}" - - # # DEBUG: Show final PCT_OPTIONS being exported - # echo "[DEBUG] PCT_OPTIONS to be exported:" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Calling create_lxc_container..." - - create_lxc_container || exit $? - - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - - # ============================================================================ - # GPU/USB PASSTHROUGH CONFIGURATION - # ============================================================================ - - # Check if GPU passthrough is enabled - # Returns true only if var_gpu is explicitly set to "yes" - # Can be set via: - # - Environment variable: var_gpu=yes bash -c "..." - # - CT script default: var_gpu="${var_gpu:-no}" - # - Advanced settings wizard - # - App defaults file: /usr/local/community-scripts/defaults/.vars - is_gpu_app() { - [[ "${var_gpu:-no}" == "yes" ]] && return 0 - 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_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") - done - 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_custom "🎮" "${RD}" "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 AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) - if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then - 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 - 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_custom "🎮" "${GN}" "Detected NVIDIA GPU" - # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] - if echo "$pci_vga_info" | grep -q "\[10de:"; then - msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" + # Simple passthrough - just bind /dev/nvidia* devices if they exist + # Skip directories like /dev/nvidia-caps (they need special handling) + for d in /dev/nvidia*; do + [[ -e "$d" ]] || continue + [[ -d "$d" ]] && continue # Skip directories + NVIDIA_DEVICES+=("$d") + done - # Simple passthrough - just bind /dev/nvidia* devices if they exist - # Skip directories like /dev/nvidia-caps (they need special handling) - for d in /dev/nvidia*; do - [[ -e "$d" ]] || continue - [[ -d "$d" ]] && continue # Skip directories - NVIDIA_DEVICES+=("$d") - done + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" + fi + fi - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - 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_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" - 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() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi - # 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" + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" # Automatic USB passthrough (privileged container) lxc.cgroup2.devices.allow: a lxc.cap.drop: @@ -3013,336 +3013,337 @@ 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 - msg_ok "USB passthrough configured" - } + msg_ok "USB passthrough configured" + } - # Configure GPU passthrough - configure_gpu_passthrough() { - # Skip if: - # GPU passthrough is enabled when var_gpu="yes": - # - Set via environment variable: var_gpu=yes bash -c "..." - # - Set in CT script: var_gpu="${var_gpu:-no}" - # - Enabled in advanced_settings wizard - # - Configured in app defaults file - if ! is_gpu_app "$APP"; then - return 0 - fi + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if: + # GPU passthrough is enabled when var_gpu="yes": + # - Set via environment variable: var_gpu=yes bash -c "..." + # - Set in CT script: var_gpu="${var_gpu:-no}" + # - Enabled in advanced_settings wizard + # - Configured in app defaults file + if ! is_gpu_app "$APP"; then + return 0 + fi - detect_gpu_devices + detect_gpu_devices - # Count available GPU types - local gpu_count=0 - local available_gpus=() + # 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 [[ ${#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 [[ ${#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 [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi - if [[ $gpu_count -eq 0 ]]; then - msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" - return 0 - fi + if [[ $gpu_count -eq 0 ]]; then + msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" + return 0 + fi - local selected_gpu="" + local selected_gpu="" - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_ok "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^^}" + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_ok "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 + # 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 + 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 + # 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[@]}") + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - # Use pct set to add devices with proper dev0/dev1 format - # GIDs will be detected and set after container starts - local dev_index=0 - for dev in "${devices[@]}"; do - # Add to config using pct set (will be visible in GUI) - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set to add devices with proper dev0/dev1 format + # GIDs will be detected and set after container starts + local dev_index=0 + for dev in "${devices[@]}"; do + # Add to config using pct set (will be visible in GUI) + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" - ;; + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" + ;; - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_warn "No NVIDIA devices available for passthrough" - return 0 - fi + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "No NVIDIA devices available for passthrough" + return 0 + fi - # Use pct set for NVIDIA devices - local dev_index=0 - for dev in "${NVIDIA_DEVICES[@]}"; do - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) - done + # Use pct set for NVIDIA devices + local dev_index=0 + for dev in "${NVIDIA_DEVICES[@]}"; do + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) + done - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" - ;; - esac - } + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" + ;; + esac + } - # Additional device passthrough - configure_additional_devices() { - # 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 - - # Coral TPU passthrough - if [[ -e /dev/apex_0 ]]; then - 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 - } - - # 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 assignment (IPv4 or IPv6) - local ip_in_lxc="" - for i in {1..20}; do - # Try IPv4 first - ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - # Fallback to IPv6 if IPv4 not available - if [ -z "$ip_in_lxc" ]; then - ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) - fi - [ -n "$ip_in_lxc" ] && break - sleep 1 - done - - if [ -z "$ip_in_lxc" ]; then - msg_error "No IP assigned to CT $CTID after 20s" - echo -e "${YW}Troubleshooting:${CL}" - echo " • Verify bridge ${BRG} exists and has connectivity" - echo " • Check if DHCP server is reachable (if using DHCP)" - echo " • Verify static IP configuration (if using static IP)" - echo " • Check Proxmox firewall rules" - echo " • If using Tailscale: Disable MagicDNS temporarily" - exit 1 - fi - - # Verify basic connectivity (ping test) - local ping_success=false - for retry in {1..3}; do - if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || - pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || - pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then - ping_success=true - break - fi - sleep 2 - done - - if [ "$ping_success" = false ]; then - msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" - echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" - else - msg_ok "Network in LXC is reachable (ping)" - 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 - } + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + 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 + } - fix_gpu_gids + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices - # Continue with standard container setup - msg_info "Customizing LXC Container" + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ - # # Install GPU userland if configured - # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - # install_gpu_userland "VAAPI" - # fi + msg_info "Starting LXC Container" + pct start "$CTID" - # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - # install_gpu_userland "NVIDIA" - # fi + # 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 - # Continue with standard container setup - install core dependencies based on OS - sleep 3 + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" - case "$var_os" in - alpine) - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories + # Wait for IP assignment (IPv4 or IPv6) + local ip_in_lxc="" + for i in {1..20}; do + # Try IPv4 first + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + # Fallback to IPv6 if IPv4 not available + if [ -z "$ip_in_lxc" ]; then + ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) + fi + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + echo -e "${YW}Troubleshooting:${CL}" + echo " • Verify bridge ${BRG} exists and has connectivity" + echo " • Check if DHCP server is reachable (if using DHCP)" + echo " • Verify static IP configuration (if using static IP)" + echo " • Check Proxmox firewall rules" + echo " • If using Tailscale: Disable MagicDNS temporarily" + exit 1 + fi + + # Verify basic connectivity (ping test) + local ping_success=false + for retry in {1..3}; do + if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || + pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || + pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then + ping_success=true + break + fi + sleep 2 + done + + if [ "$ping_success" = false ]; then + msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" + echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}" + else + msg_ok "Network in LXC is reachable (ping)" + 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 - install core dependencies based on OS + sleep 3 + + case "$var_os" in + alpine) + 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" - ;; + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + ;; - debian | ubuntu | devuan) - # First install locales package (required for locale-gen on minimal templates) - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null 2>&1 || true" + debian | ubuntu | devuan) + # First install locales package (required for locale-gen on minimal templates) + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null 2>&1 || true" - # Locale setup for Debian-based - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + # Locale setup for Debian-based + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ [[ -n \"\$locale_line\" ]] && echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null 2>&1 && \ export LANG=\$locale_line || true" - # Timezone setup - 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" + # Timezone setup + 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'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + # Core dependencies + 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 + } + ;; + + fedora | rockylinux | almalinux | centos) + # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS + # Detect OS major version for EL10+ compatibility (DNF 5, different packages) + local rhel_version + rhel_version=$(pct exec "$CTID" -- bash -c "grep -oP '(?<=VERSION_ID=\")[0-9]+' /etc/os-release 2>/dev/null || echo 9") + + # First run makecache to ensure repos are ready (critical for fresh templates) + msg_info "Initializing package manager (this may take a moment)..." + if ! pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1 || yum makecache 2>&1" >/dev/null 2>&1; then + msg_warn "Package cache update had issues, continuing anyway..." + fi + + # Build package list - EL10+ may not have glibc-langpack-en in same form + local rhel_packages="curl sudo mc jq procps-ng ncurses" + if [[ "$rhel_version" -lt 10 ]]; then + rhel_packages="$rhel_packages glibc-langpack-en" + else + # EL10 uses glibc-all-langpacks or langpacks-en + rhel_packages="$rhel_packages langpacks-en glibc-all-langpacks" + fi + + # Install base packages with better error handling + local install_log="/tmp/dnf_install_${CTID}.log" + if ! pct exec "$CTID" -- bash -c "dnf install -y $rhel_packages 2>&1 | tee $install_log; exit \${PIPESTATUS[0]}" >/dev/null 2>&1; then + # Check if it's just missing optional packages + if pct exec "$CTID" -- bash -c "rpm -q curl sudo mc jq procps-ng" >/dev/null 2>&1; then + msg_warn "Some optional packages may have failed, but core packages installed" + else + # Real failure - try minimal install + msg_warn "Full package install failed, trying minimal set..." + if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo jq 2>&1" >/dev/null 2>&1; then + msg_error "dnf/yum base packages installation failed" + pct exec "$CTID" -- bash -c "cat $install_log 2>/dev/null" || true + exit 1 fi + fi + fi - # Core dependencies - 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 - } - ;; + # Set locale for RHEL-based systems + pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - fedora | rockylinux | almalinux | centos) - # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS - # Detect OS major version for EL10+ compatibility (DNF 5, different packages) - local rhel_version - rhel_version=$(pct exec "$CTID" -- bash -c "grep -oP '(?<=VERSION_ID=\")[0-9]+' /etc/os-release 2>/dev/null || echo 9") + # Timezone setup for RHEL + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + [[ "${tz:-}" == Etc/* ]] && tz="UTC" + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "timedatectl set-timezone '$tz' 2>/dev/null || ln -sf '/usr/share/zoneinfo/$tz' /etc/localtime" || true + fi + ;; - # First run makecache to ensure repos are ready (critical for fresh templates) - msg_info "Initializing package manager (this may take a moment)..." - if ! pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1 || yum makecache 2>&1" >/dev/null 2>&1; then - msg_warn "Package cache update had issues, continuing anyway..." - fi + opensuse) + # openSUSE - special handling for terminal/locale issues + # Use --gpg-auto-import-keys to avoid interactive prompts that cause hangs + msg_info "Initializing package manager for openSUSE..." + pct exec "$CTID" -- bash -c "zypper --gpg-auto-import-keys --non-interactive refresh 2>&1" >/dev/null 2>&1 || true - # Build package list - EL10+ may not have glibc-langpack-en in same form - local rhel_packages="curl sudo mc jq procps-ng ncurses" - if [[ "$rhel_version" -lt 10 ]]; then - rhel_packages="$rhel_packages glibc-langpack-en" - else - # EL10 uses glibc-all-langpacks or langpacks-en - rhel_packages="$rhel_packages langpacks-en glibc-all-langpacks" - fi + # Install packages - ncurses and terminfo are CRITICAL for terminal to work + if ! pct exec "$CTID" -- bash -c "zypper --gpg-auto-import-keys --non-interactive install -y curl sudo mc jq glibc-locale ncurses terminfo-base 2>&1" >/dev/null 2>&1; then + # Try without glibc-locale + if ! pct exec "$CTID" -- bash -c "zypper --gpg-auto-import-keys --non-interactive install -y curl sudo mc jq ncurses terminfo-base 2>&1" >/dev/null 2>&1; then + msg_error "zypper base packages installation failed" + exit 1 + fi + fi - # Install base packages with better error handling - local install_log="/tmp/dnf_install_${CTID}.log" - if ! pct exec "$CTID" -- bash -c "dnf install -y $rhel_packages 2>&1 | tee $install_log; exit \${PIPESTATUS[0]}" >/dev/null 2>&1; then - # Check if it's just missing optional packages - if pct exec "$CTID" -- bash -c "rpm -q curl sudo mc jq procps-ng" >/dev/null 2>&1; then - msg_warn "Some optional packages may have failed, but core packages installed" - else - # Real failure - try minimal install - msg_warn "Full package install failed, trying minimal set..." - if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo jq 2>&1" >/dev/null 2>&1; then - msg_error "dnf/yum base packages installation failed" - pct exec "$CTID" -- bash -c "cat $install_log 2>/dev/null" || true - exit 1 - fi - fi - fi + # Fix 'unknown terminal type' error - set TERM in multiple places + pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - # Set locale for RHEL-based systems - pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - - # Timezone setup for RHEL - if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") - fi - [[ "${tz:-}" == Etc/* ]] && tz="UTC" - if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "timedatectl set-timezone '$tz' 2>/dev/null || ln -sf '/usr/share/zoneinfo/$tz' /etc/localtime" || true - fi - ;; - - opensuse) - # openSUSE - special handling for terminal/locale issues - msg_info "Initializing package manager for openSUSE..." - pct exec "$CTID" -- bash -c "zypper --non-interactive refresh 2>&1" >/dev/null 2>&1 || true - - # Install packages - ncurses and terminfo are CRITICAL for terminal to work - if ! pct exec "$CTID" -- bash -c "zypper --non-interactive install -y curl sudo mc jq glibc-locale ncurses terminfo-base 2>&1" >/dev/null 2>&1; then - # Try without glibc-locale - if ! pct exec "$CTID" -- bash -c "zypper --non-interactive install -y curl sudo mc jq ncurses terminfo-base 2>&1" >/dev/null 2>&1; then - msg_error "zypper base packages installation failed" - exit 1 - fi - fi - - # Fix 'unknown terminal type' error - set TERM in multiple places - pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - - # Set TERM globally for all users - pct exec "$CTID" -- bash -c "cat > /etc/profile.d/term.sh << 'EOFTERM' + # Set TERM globally for all users + pct exec "$CTID" -- bash -c "cat > /etc/profile.d/term.sh << 'EOFTERM' # Fix terminal type for LXC containers if [ -z \"\$TERM\" ] || [ \"\$TERM\" = \"dumb\" ] || [ \"\$TERM\" = \"-\" ]; then export TERM=xterm-256color @@ -3350,206 +3351,214 @@ fi EOFTERM chmod +x /etc/profile.d/term.sh" || true - # Also set in /etc/environment for non-login shells - pct exec "$CTID" -- bash -c "grep -q '^TERM=' /etc/environment 2>/dev/null || echo 'TERM=xterm-256color' >> /etc/environment" || true - ;; + # Also set in /etc/environment for non-login shells + pct exec "$CTID" -- bash -c "grep -q '^TERM=' /etc/environment 2>/dev/null || echo 'TERM=xterm-256color' >> /etc/environment" || true + ;; - gentoo) - # Gentoo - OpenRC based, emerge is slow - # Sync portage first (required for fresh templates) - msg_info "Syncing Gentoo portage (this may take a while)..." - pct exec "$CTID" -- bash -c "emerge --sync >/dev/null 2>&1 || emerge-webrsync >/dev/null 2>&1" || true + gentoo) + # Gentoo - OpenRC based, emerge is slow + # Use emerge-webrsync (faster, uses http instead of rsync) + msg_info "Syncing Gentoo portage via webrsync (faster than rsync)..." + pct exec "$CTID" -- bash -c "emerge-webrsync 2>&1" >/dev/null 2>&1 || { + msg_warn "emerge-webrsync failed, trying emerge --sync..." + pct exec "$CTID" -- bash -c "emerge --sync 2>&1" >/dev/null 2>&1 || true + } - # Install essentials - emerge can be very slow - if ! pct exec "$CTID" -- bash -c "emerge --quiet --noreplace app-misc/jq net-misc/curl app-misc/mc sys-libs/ncurses 2>&1" >/dev/null 2>&1; then - msg_warn "Gentoo base packages installation incomplete - some packages may need manual setup" - # Try just curl and jq as minimum - pct exec "$CTID" -- bash -c "emerge --quiet --noreplace net-misc/curl app-misc/jq 2>&1" >/dev/null 2>&1 || true - fi - - # Set TERM for Gentoo - pct exec "$CTID" -- bash -c "echo 'export TERM=xterm-256color' >> /etc/profile.d/term.sh && chmod +x /etc/profile.d/term.sh" || true - ;; - - openeuler) - # openEuler (RHEL-compatible, uses DNF) - # Note: openEuler requires privileged container due to PVE limitation with /etc/redhat-release - msg_info "Initializing package manager for openEuler..." - pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1" >/dev/null 2>&1 || true - - # openEuler package names may differ from RHEL - local euler_packages="curl sudo mc jq procps-ng ncurses" - if ! pct exec "$CTID" -- bash -c "dnf install -y $euler_packages 2>&1" >/dev/null 2>&1; then - # Try without procps-ng (might be just 'procps' in openEuler) - if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq ncurses 2>&1" >/dev/null 2>&1; then - msg_error "dnf base packages installation failed" - exit 1 - fi - fi - # Set locale - pct exec "$CTID" -- bash -c "echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - ;; - - *) - msg_warn "Unknown OS '$var_os' - skipping core dependency installation" - ;; - esac - - msg_ok "Customized LXC Container" - - # Install SSH keys - install_ssh_keys_into_ct - - # Run application installer - # NOTE: We disable error handling here because: - # 1. Container errors are caught by error_handler INSIDE container - # 2. Container creates flag file with exit code - # 3. We read flag file and handle cleanup manually below - # 4. We DON'T want host error_handler to fire for lxc-attach command itself - - set +Eeuo pipefail # Disable ALL error handling temporarily - trap - ERR # Remove ERR trap completely - - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)" - local lxc_exit=$? - - set -Eeuo pipefail # Re-enable error handling - trap 'error_handler' ERR # Restore ERR trap - - # Check for error flag file in container (more reliable than lxc-attach exit code) - local install_exit_code=0 - if [[ -n "${SESSION_ID:-}" ]]; then - local error_flag="/root/.install-${SESSION_ID}.failed" - if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then - install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") - pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true - fi + # Install curl FIRST - it's required for install.func to work + msg_info "Installing essential packages for Gentoo..." + if ! pct exec "$CTID" -- bash -c "emerge --quiet --noreplace net-misc/curl 2>&1" >/dev/null 2>&1; then + msg_error "Failed to install curl on Gentoo - this is required" + exit 1 fi - # Fallback to lxc-attach exit code if no flag file - if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then - install_exit_code=$lxc_exit + # Install remaining packages + pct exec "$CTID" -- bash -c "emerge --quiet --noreplace app-misc/jq app-misc/mc sys-libs/ncurses 2>&1" >/dev/null 2>&1 || { + msg_warn "Some Gentoo packages may need manual setup" + } + + # Set TERM for Gentoo + pct exec "$CTID" -- bash -c "echo 'export TERM=xterm-256color' >> /etc/profile.d/term.sh && chmod +x /etc/profile.d/term.sh" || true + ;; + + openeuler) + # openEuler (RHEL-compatible, uses DNF) + # Note: Template was patched with /etc/redhat-release in create_container + msg_info "Initializing package manager for openEuler..." + pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1" >/dev/null 2>&1 || true + + # openEuler package names may differ from RHEL + local euler_packages="curl sudo mc jq procps-ng ncurses" + if ! pct exec "$CTID" -- bash -c "dnf install -y $euler_packages 2>&1" >/dev/null 2>&1; then + # Try without procps-ng (might be just 'procps' in openEuler) + if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq ncurses 2>&1" >/dev/null 2>&1; then + msg_error "dnf base packages installation failed" + exit 1 + fi + fi + # Set locale + pct exec "$CTID" -- bash -c "echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true + ;; + + *) + msg_warn "Unknown OS '$var_os' - skipping core dependency installation" + ;; + esac + + msg_ok "Customized LXC Container" + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + # NOTE: We disable error handling here because: + # 1. Container errors are caught by error_handler INSIDE container + # 2. Container creates flag file with exit code + # 3. We read flag file and handle cleanup manually below + # 4. We DON'T want host error_handler to fire for lxc-attach command itself + + set +Eeuo pipefail # Disable ALL error handling temporarily + trap - ERR # Remove ERR trap completely + + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)" + local lxc_exit=$? + + set -Eeuo pipefail # Re-enable error handling + trap 'error_handler' ERR # Restore ERR trap + + # Check for error flag file in container (more reliable than lxc-attach exit code) + local install_exit_code=0 + if [[ -n "${SESSION_ID:-}" ]]; then + local error_flag="/root/.install-${SESSION_ID}.failed" + if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then + install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") + pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true + fi + fi + + # Fallback to lxc-attach exit code if no flag file + if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then + install_exit_code=$lxc_exit + fi + + # Installation failed? + if [[ $install_exit_code -ne 0 ]]; then + msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" + + # Copy both logs from container before potential deletion + local build_log_copied=false + local install_log_copied=false + + if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then + # Copy BUILD_LOG (creation log) if it exists + if [[ -f "${BUILD_LOG}" ]]; then + cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true + fi + + # Copy INSTALL_LOG from container + if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then + install_log_copied=true + fi + + # Show available logs + echo "" + [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" + [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" fi - # Installation failed? - if [[ $install_exit_code -ne 0 ]]; then - msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" + # Dev mode: Keep container or open breakpoint shell + if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then + msg_dev "Keep mode active - container ${CTID} preserved" + return 0 + elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then + msg_dev "Breakpoint mode - opening shell in container ${CTID}" + echo -e "${YW}Type 'exit' to return to host${CL}" + pct enter "$CTID" + echo "" + echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" + if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + msg_ok "Container ${CTID} removed" + else + msg_dev "Container ${CTID} kept for debugging" + fi + exit $install_exit_code + fi - # Copy both logs from container before potential deletion - local build_log_copied=false - local install_log_copied=false + # Report failure to API before container cleanup + post_update_to_api "failed" "$install_exit_code" - if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then - # Copy BUILD_LOG (creation log) if it exists - if [[ -f "${BUILD_LOG}" ]]; then - cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true - fi + # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) + echo "" + echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" - # Copy INSTALL_LOG from container - if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then - install_log_copied=true - fi + if read -t 60 -r response; then + if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then + # Remove container + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + elif [[ "$response" =~ ^[Nn]$ ]]; then + echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" - # Show available logs - echo "" - [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" - [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" - fi - - # Dev mode: Keep container or open breakpoint shell - if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then - msg_dev "Keep mode active - container ${CTID} preserved" - return 0 - elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then - msg_dev "Breakpoint mode - opening shell in container ${CTID}" - echo -e "${YW}Type 'exit' to return to host${CL}" - pct enter "$CTID" - echo "" - echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" - if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - msg_ok "Container ${CTID} removed" - else - msg_dev "Container ${CTID} kept for debugging" - fi - exit $install_exit_code - fi - - # Report failure to API before container cleanup - post_update_to_api "failed" "$install_exit_code" - - # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) - echo "" - echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" - - if read -t 60 -r response; then - if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then - # Remove container - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - elif [[ "$response" =~ ^[Nn]$ ]]; then - echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" - - # Dev mode: Setup MOTD/SSH for debugging access to broken container - if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then - echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" - if pct exec "$CTID" -- bash -c " + # Dev mode: Setup MOTD/SSH for debugging access to broken container + if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then + echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" + if pct exec "$CTID" -- bash -c " source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then - local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" - fi - fi - fi - else - # Timeout - auto-remove - echo -e "\n${YW}No response - auto-removing container${CL}" - echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" + fi fi - - exit $install_exit_code + fi + else + # Timeout - auto-remove + echo -e "\n${YW}No response - auto-removing container${CL}" + echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" fi + + exit $install_exit_code + fi } destroy_lxc() { - if [[ -z "$CT_ID" ]]; then - msg_error "No CT_ID found. Nothing to remove." - return 1 + 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 - - # 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_custom "ℹ️" "${BL}" "Container was not removed." - ;; - *) - msg_warn "Invalid response. Container was not removed." - ;; - esac + ;; + "" | n | no) + msg_custom "ℹ️" "${BL}" "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac } # ------------------------------------------------------------------------------ @@ -3557,81 +3566,81 @@ destroy_lxc() { # ------------------------------------------------------------------------------ # ===== 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 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" + 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 - 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 + STORAGE_INFO="Free: ${free} Used: ${used}" fi - STORAGE_RESULT="$preselect" - return 0 + fi + STORAGE_RESULT="$preselect" + return 0 } fix_gpu_gids() { - if [[ -z "${GPU_TYPE:-}" ]]; then - return 0 - fi + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi - msg_info "Detecting and setting correct GPU group IDs" + msg_info "Detecting and setting correct GPU group IDs" - # Get actual GIDs from 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") + # Get actual GIDs from 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") - # Create groups if they don't exist - if [[ -z "$video_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 - video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" - fi + # Create groups if they don't exist + if [[ -z "$video_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" + fi - if [[ -z "$render_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 - render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" - fi + if [[ -z "$render_gid" ]]; then + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" + fi - # Stop container to update config - pct stop "$CTID" >/dev/null 2>&1 - sleep 1 + # Stop container to update config + pct stop "$CTID" >/dev/null 2>&1 + sleep 1 - # Update dev entries with correct GIDs - sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" - sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" + # Update dev entries with correct GIDs + sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" + sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" - # Restart container - pct start "$CTID" >/dev/null 2>&1 - sleep 2 + # Restart container + pct start "$CTID" >/dev/null 2>&1 + sleep 2 - msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" + msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" - # For privileged containers: also fix permissions inside container - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " + # For privileged containers: also fix permissions inside container + 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 @@ -3645,760 +3654,797 @@ fix_gpu_gids() { done fi " >/dev/null 2>&1 - fi + fi } 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 ]] + 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 + 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 + 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') + 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 + 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 - - 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 + 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 + # ------------------------------------------------------------------------------ + # 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}'; } + # ------------------------------------------------------------------------------ + # 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"; } + 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"; } + # 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 + # 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)" + _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)" + 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 $STD apt-get update && $STD 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 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 + } - 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 $STD apt-get update && $STD 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 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 + } - # ------------------------------------------------------------------------------ - # 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}" - 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 - # 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 + } - # 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 - # 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 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 + + msg_info "Validating storage '$CONTAINER_STORAGE'" + STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) + + case "$STORAGE_TYPE" in + iscsidirect) exit 212 ;; + iscsi | zfs) exit 213 ;; + cephfs) exit 219 ;; + pbs) exit 224 ;; + linstor | rbd | nfs | cifs) + pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 + ;; + esac + + pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 + msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" + + msg_info "Validating template storage '$TEMPLATE_STORAGE'" + TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) + + if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" + fi + msg_ok "Template storage '$TEMPLATE_STORAGE' validated" + + # 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 + # Supported OS types (pveam available): alpine, almalinux, centos, debian, + # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu + # Template naming conventions: + # - Debian/Ubuntu/Devuan: --standard__.tar.zst + # - Alpine/Fedora/Rocky/CentOS/AlmaLinux/openEuler: --default__.tar.xz + # - Gentoo: gentoo-current-openrc__.tar.xz (note: underscore before date!) + # - openSUSE: opensuse--default__.tar.xz + # - CentOS: centos--stream-default__.tar.xz (note: stream in name) + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rockylinux | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; + centos) TEMPLATE_PATTERN="-stream-default_" ;; + gentoo) TEMPLATE_PATTERN="-openrc_" ;; # Pattern: gentoo-current-openrc_ (underscore!) + opensuse) 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)." + + 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/^/ /' + + 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 + + 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 + # Special handling for Gentoo which uses 'current' instead of numeric version + if [[ "$PCT_OSTYPE" == "gentoo" ]]; then + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $2}' | + grep "^gentoo-" | + sed -E 's/gentoo-([^-]+)-.*/\1/' | + sort -u 2>/dev/null || sort -u + ) 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 + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $2}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + grep -E '^[0-9]' | + sort -u -V 2>/dev/null || sort -u + ) + fi + + 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 - fi - - msg_info "Validating storage '$CONTAINER_STORAGE'" - STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) - - case "$STORAGE_TYPE" in - iscsidirect) exit 212 ;; - iscsi | zfs) exit 213 ;; - cephfs) exit 219 ;; - pbs) exit 224 ;; - linstor | rbd | nfs | cifs) - pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 - ;; - esac - - pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 - msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" - - msg_info "Validating template storage '$TEMPLATE_STORAGE'" - TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) - - if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then - msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" - fi - msg_ok "Template storage '$TEMPLATE_STORAGE' validated" - - # 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 - # Supported OS types (pveam available): alpine, almalinux, centos, debian, - # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu - # Template naming conventions: - # - Debian/Ubuntu/Devuan: --standard__.tar.zst - # - Alpine/Fedora/Rocky/CentOS/AlmaLinux/openEuler: --default__.tar.xz - # - Gentoo: gentoo-current-openrc__.tar.xz (note: underscore before date!) - # - openSUSE: opensuse--default__.tar.xz - # - CentOS: centos--stream-default__.tar.xz (note: stream in name) - # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - case "$PCT_OSTYPE" in - debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rockylinux | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - centos) TEMPLATE_PATTERN="-stream-default_" ;; - gentoo) TEMPLATE_PATTERN="-openrc_" ;; # Pattern: gentoo-current-openrc_ (underscore!) - opensuse) 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)." - - 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/^/ /' - - 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 - - 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 + msg_custom "🚫" "${YW}" "Installation cancelled" + exit 0 + fi else - TEMPLATE="$ONLINE_TEMPLATE" - TEMPLATE_SOURCE="online" + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 fi + fi - # If still no template, try to find alternatives + #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 "" - echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice - # Get all available versions for this OS type - # Special handling for Gentoo which uses 'current' instead of numeric version - if [[ "$PCT_OSTYPE" == "gentoo" ]]; then - mapfile -t AVAILABLE_VERSIONS < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $2}' | - grep "^gentoo-" | - sed -E 's/gentoo-([^-]+)-.*/\1/' | - sort -u 2>/dev/null || sort -u - ) - else - mapfile -t AVAILABLE_VERSIONS < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $2}' | - grep "^${PCT_OSTYPE}-" | - sed -E "s/${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | - grep -E '^[0-9]' | - sort -u -V 2>/dev/null || sort -u - ) - fi + 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}" - 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 + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" - 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}-" + 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]}" - #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_custom "🚫" "${YW}" "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_custom "🚫" "${YW}" "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_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 - - 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 + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else TEMPLATE="$ONLINE_TEMPLATE" - NEED_DOWNLOAD=1 + 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_custom "ℹ️" "${BL}" "Continuing with local template $TEMPLATE" + msg_custom "🚫" "${YW}" "Installation cancelled" + exit 1 fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + 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 + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + 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 + + 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 - - 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 + 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 - # ------------------------------------------------------------------------------ - # 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 + 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_custom "ℹ️" "${BL}" "Continuing with local template $TEMPLATE" fi + fi - # ------------------------------------------------------------------------------ - # Create LXC Container - # ------------------------------------------------------------------------------ - msg_info "Creating LXC container" + 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 - # 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 + 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 - # PCT_OPTIONS is now a string (exported from build_container) - # Add rootfs if not already specified - if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then - PCT_OPTIONS="$PCT_OPTIONS + # ------------------------------------------------------------------------------ + # 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 + + # PCT_OPTIONS is now a string (exported from build_container) + # Add rootfs if not already specified + if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then + PCT_OPTIONS="$PCT_OPTIONS -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" - fi + fi - # 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 - } + # 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}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" + LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" - # # DEBUG: Show the actual command that will be executed - # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" - # echo "[DEBUG] CTID: $CTID" - # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" - # echo "[DEBUG] PCT_OPTIONS (will be word-split):" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Full command line:" - # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - # echo "[DEBUG] ========================================" + # ------------------------------------------------------------------------------ + # openEuler Template Patch: Create /etc/redhat-release inside template + # PVE's post_create_hook expects this file for RHEL-family OS detection + # Without it, container creation fails with "error in setup task" + # ------------------------------------------------------------------------------ + if [[ "${var_os:-}" == "openeuler" ]]; then + msg_info "Patching openEuler template for PVE compatibility..." + local TEMP_EXTRACT_DIR="/tmp/openeuler_template_patch_$$" + local PATCHED_TEMPLATE="${TEMPLATE_PATH%.tar.xz}_patched.tar.xz" - msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - msg_debug "Logfile: $LOGFILE" + # Only patch if not already patched + if [[ ! -f "$PATCHED_TEMPLATE" ]]; then + mkdir -p "$TEMP_EXTRACT_DIR" - # First attempt (PCT_OPTIONS is a multi-line string, use it directly) - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then - msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 + # Extract template + if tar -xf "$TEMPLATE_PATH" -C "$TEMP_EXTRACT_DIR" 2>/dev/null; then + # Create /etc/redhat-release if it doesn't exist + if [[ ! -f "$TEMP_EXTRACT_DIR/etc/redhat-release" ]]; then + echo "openEuler release ${var_version:-25.03}" >"$TEMP_EXTRACT_DIR/etc/redhat-release" fi - # Retry after repair - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then - # Fallback to local storage if not already on local - if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_info "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 - # Local fallback also failed - check for LXC stack version issue - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - else - msg_ok "Container successfully created using local fallback." - fi - else - # Already on local storage and still failed - check LXC stack version - 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 - pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi + # Repack template + if tar -cJf "$PATCHED_TEMPLATE" -C "$TEMP_EXTRACT_DIR" . 2>/dev/null; then + # Replace original with patched version + mv "$PATCHED_TEMPLATE" "$TEMPLATE_PATH" + msg_ok "openEuler template patched successfully" else - msg_ok "Container successfully created after template repair." + msg_warn "Failed to repack template, trying without patch..." fi + else + msg_warn "Failed to extract template for patching, trying without patch..." + fi + + rm -rf "$TEMP_EXTRACT_DIR" + fi + fi + + # # DEBUG: Show the actual command that will be executed + # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" + # echo "[DEBUG] CTID: $CTID" + # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" + # echo "[DEBUG] PCT_OPTIONS (will be word-split):" + # echo "$PCT_OPTIONS" | sed 's/^/ /' + # echo "[DEBUG] Full command line:" + # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + # echo "[DEBUG] ========================================" + + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" + msg_debug "Logfile: $LOGFILE" + + # First attempt (PCT_OPTIONS is a multi-line string, use it directly) + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then + msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating 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 - # 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 - } + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then + # Fallback to local storage if not already on local + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_info "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 + # Local fallback also failed - check for LXC stack version issue + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + else + msg_ok "Container successfully created using local fallback." + fi + else + # Already on local storage and still failed - check LXC stack version + 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 + pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_ok "Container successfully created after template repair." + fi + fi - # Verify config rootfs - grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { - msg_error "RootFS entry missing in container config. See $LOGFILE" - exit 216 - } + # 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 + } - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } - # Report container creation to API - post_to_api + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." + + # Report container creation to API + post_to_api } # ============================================================================== @@ -4418,11 +4464,11 @@ create_lxc_container() { # - Posts final "done" status to API telemetry # ------------------------------------------------------------------------------ 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 @@ -4450,14 +4496,14 @@ description() { EOF - ) - pct set "$CTID" -description "$DESCRIPTION" + ) + 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" } # ============================================================================== @@ -4474,14 +4520,14 @@ EOF # - Only executes on non-zero exit codes # ------------------------------------------------------------------------------ api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - post_update_to_api "failed" "$exit_code" - fi + exit_code=$? + if [ $exit_code -ne 0 ]; then + post_update_to_api "failed" "$exit_code" + 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 diff --git a/misc/install.func b/misc/install.func index 685eb0688..3a0870165 100644 --- a/misc/install.func +++ b/misc/install.func @@ -906,13 +906,13 @@ EOF # Try multiple patterns to catch different inittab formats # Pattern 1: Standard format "1:2345:respawn:/sbin/getty..." - sed -i -E 's|^1:[0-9]*:respawn:[^ ]*(getty|agetty)[^$]*$|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab + sed -i 's|^1:[0-9]*:respawn:.*/\(a\?getty\).*|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab # Pattern 2: With console instead of tty1 - sed -i -E 's|^c1:[0-9]*:respawn:[^ ]*(getty|agetty).*console.*$|c1:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab + sed -i 's|^c1:[0-9]*:respawn:.*/\(a\?getty\).*console.*|c1:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab # Pattern 3: Devuan specific "co:2345:respawn:/sbin/getty..." - sed -i -E 's|^co:[0-9]*:respawn:[^ ]*(getty|agetty).*$|co:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab + sed -i 's|^co:[0-9]*:respawn:.*/\(a\?getty\).*|co:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab # Pattern 4: LXC console pattern if ! grep -q 'autologin root' /etc/inittab; then From 82773d7b8b2f2ec782eabe0feac65c726de94df1 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Mon, 12 Jan 2026 22:09:36 +0100 Subject: [PATCH 308/378] fix(devuan): Add console getty for LXC autologin LXC containers use /dev/console, not tty1! - pct console connects to /dev/console device - Added explicit console entry in inittab for LXC - tty1 autologin still works for direct access - Fixes autologin not working in Devuan containers --- misc/install.func | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/misc/install.func b/misc/install.func index 3a0870165..2a572a418 100644 --- a/misc/install.func +++ b/misc/install.func @@ -900,26 +900,24 @@ EOF sysvinit) # Devuan/older systems - modify inittab for auto-login # Devuan 5 (daedalus) uses SysVinit with various inittab formats + # CRITICAL: LXC uses /dev/console, NOT tty1! pct console connects to console device if [[ -f /etc/inittab ]]; then # Backup original inittab cp /etc/inittab /etc/inittab.bak 2>/dev/null || true - # Try multiple patterns to catch different inittab formats - # Pattern 1: Standard format "1:2345:respawn:/sbin/getty..." + # First, enable autologin on tty1 (for direct access) sed -i 's|^1:[0-9]*:respawn:.*/\(a\?getty\).*|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab - # Pattern 2: With console instead of tty1 - sed -i 's|^c1:[0-9]*:respawn:.*/\(a\?getty\).*console.*|c1:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab - - # Pattern 3: Devuan specific "co:2345:respawn:/sbin/getty..." - sed -i 's|^co:[0-9]*:respawn:.*/\(a\?getty\).*|co:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux|' /etc/inittab - - # Pattern 4: LXC console pattern - if ! grep -q 'autologin root' /etc/inittab; then - # If no autologin was set, add a new console entry - if ! grep -qE '^c[o1]:.*autologin' /etc/inittab; then - echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 38400 linux" >>/etc/inittab - fi + # CRITICAL: Add console entry for LXC - this is what pct console uses! + # Check if there's already a console getty entry + if ! grep -qE '^[^#].*respawn.*console' /etc/inittab; then + # Add new console entry for LXC + echo "" >> /etc/inittab + echo "# LXC console autologin (added by community-scripts)" >> /etc/inittab + echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux" >> /etc/inittab + else + # Enable autologin on existing console entry + sed -i 's|^[^#]*:[0-9]*:respawn:.*/\(a\?getty\).*console.*|co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux|' /etc/inittab fi # Force a reload of inittab - try multiple methods From 46bb2db68326e77433bcbdc5ac9569ebe2d3e097 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Mon, 12 Jan 2026 22:18:45 +0100 Subject: [PATCH 309/378] ui --- misc/install.func | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/install.func b/misc/install.func index 2a572a418..41168a0f0 100644 --- a/misc/install.func +++ b/misc/install.func @@ -912,9 +912,9 @@ EOF # Check if there's already a console getty entry if ! grep -qE '^[^#].*respawn.*console' /etc/inittab; then # Add new console entry for LXC - echo "" >> /etc/inittab - echo "# LXC console autologin (added by community-scripts)" >> /etc/inittab - echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux" >> /etc/inittab + echo "" >>/etc/inittab + echo "# LXC console autologin (added by community-scripts)" >>/etc/inittab + echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux" >>/etc/inittab else # Enable autologin on existing console entry sed -i 's|^[^#]*:[0-9]*:respawn:.*/\(a\?getty\).*console.*|co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux|' /etc/inittab From e666185e28d43e2cd9c3c9bf1826249a0913f349 Mon Sep 17 00:00:00 2001 From: Brandon Hopkins Date: Mon, 12 Jan 2026 16:48:22 -0800 Subject: [PATCH 310/378] Remove SSO Option for Self-Hosed --- install/netbird-install.sh | 138 +++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 58 deletions(-) diff --git a/install/netbird-install.sh b/install/netbird-install.sh index 6154c39ff..c5dace330 100644 --- a/install/netbird-install.sh +++ b/install/netbird-install.sh @@ -74,87 +74,109 @@ echo -e "${BL}NetBird Connection Setup${CL}" echo "─────────────────────────────────────────" echo "Choose how to connect to your NetBird network:" echo "" -echo " 1) Setup Key (default) - Use a pre-generated setup key" -echo " 2) SSO Login - Authenticate via browser with your identity provider" -echo " 3) Skip - Configure later with 'netbird up'" +if [[ "$DEPLOYMENT_TYPE" == "1" ]]; then + echo " 1) Setup Key (default) - Use a pre-generated setup key" + echo " 2) SSO Login - Authenticate via browser with your identity provider" + echo " 3) Skip - Configure later with 'netbird up'" +else + echo " 1) Setup Key (default) - Use a pre-generated setup key" + echo " 2) Skip - Configure later with 'netbird up'" +fi echo "" read -r -p "Select authentication method [1]: " AUTH_METHOD AUTH_METHOD="${AUTH_METHOD:-1}" -case "$AUTH_METHOD" in - 1) - echo "" - echo "Enter your NetBird setup key from the NetBird dashboard." - echo "" - read -r -p "Setup key: " NETBIRD_SETUP_KEY - echo "" +if [[ "$DEPLOYMENT_TYPE" == "1" ]]; then + case "$AUTH_METHOD" in + 1) + echo "" + echo "Enter your NetBird setup key from the NetBird dashboard." + echo "" + read -r -p "Setup key: " NETBIRD_SETUP_KEY + echo "" - if [[ -z "$NETBIRD_SETUP_KEY" ]]; then - if [[ -n "$NETBIRD_MGMT_URL" ]]; then - msg_warn "No setup key provided. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to connect." - else + if [[ -z "$NETBIRD_SETUP_KEY" ]]; then msg_warn "No setup key provided. Run 'netbird up -k ' to connect." - fi - else - msg_info "Connecting to NetBird with setup key" - if [[ -n "$NETBIRD_MGMT_URL" ]]; then - if $STD netbird up -k "$NETBIRD_SETUP_KEY" --management-url "$NETBIRD_MGMT_URL"; then - msg_ok "Connected to NetBird" - else - msg_warn "Connection failed. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to retry." - fi else + msg_info "Connecting to NetBird with setup key" if $STD netbird up -k "$NETBIRD_SETUP_KEY"; then msg_ok "Connected to NetBird" else msg_warn "Connection failed. Run 'netbird up -k ' to retry." fi fi - fi - ;; - 2) - echo "" - echo -e "${BL}SSO Authentication${CL}" - echo "─────────────────────────────────────────" - echo "A login URL will appear below." - echo "Copy the URL and open it in your browser to authenticate." - echo "" + ;; + 2) + echo "" + echo -e "${BL}SSO Authentication${CL}" + echo "─────────────────────────────────────────" + echo "A login URL will appear below." + echo "Copy the URL and open it in your browser to authenticate." + echo "" - msg_info "Starting SSO login" - if [[ -n "$NETBIRD_MGMT_URL" ]]; then - netbird login --management-url "$NETBIRD_MGMT_URL" 2>&1 || true - else + msg_info "Starting SSO login" netbird login 2>&1 || true - fi - echo "" + echo "" - msg_info "Connecting to NetBird" - if [[ -n "$NETBIRD_MGMT_URL" ]]; then - if $STD netbird up --management-url "$NETBIRD_MGMT_URL"; then - msg_ok "Connected to NetBird" - else - msg_warn "Connection failed. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to retry." - fi - else + msg_info "Connecting to NetBird" if $STD netbird up; then msg_ok "Connected to NetBird" else msg_warn "Connection failed. Run 'netbird up' to retry." fi - fi - ;; - 3) - if [[ -n "$NETBIRD_MGMT_URL" ]]; then - msg_ok "Skipped. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to connect." - else + ;; + 3) msg_ok "Skipped. Run 'netbird up' to connect." - fi - ;; - *) - msg_warn "Invalid selection. Run 'netbird up' to connect." - ;; -esac + ;; + *) + msg_warn "Invalid selection. Run 'netbird up' to connect." + ;; + esac +else + case "$AUTH_METHOD" in + 1) + echo "" + echo "Enter your NetBird setup key from the NetBird dashboard." + echo "" + read -r -p "Setup key: " NETBIRD_SETUP_KEY + echo "" + + if [[ -z "$NETBIRD_SETUP_KEY" ]]; then + if [[ -z "$NETBIRD_MGMT_URL" ]]; then + msg_warn "No setup key provided. Run 'netbird up -k --management-url ' to connect." + else + msg_warn "No setup key provided. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to connect." + fi + else + if [[ -z "$NETBIRD_MGMT_URL" ]]; then + msg_error "Management URL is required for self-hosted deployments. Please configure it first." + else + msg_info "Connecting to NetBird with setup key" + if $STD netbird up -k "$NETBIRD_SETUP_KEY" --management-url "$NETBIRD_MGMT_URL"; then + msg_ok "Connected to NetBird" + else + msg_warn "Connection failed. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to retry." + fi + fi + fi + ;; + 2) + if [[ -z "$NETBIRD_MGMT_URL" ]]; then + msg_ok "Skipped. Run 'netbird up --management-url ' to connect." + else + msg_ok "Skipped. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to connect." + fi + ;; + *) + if [[ -z "$NETBIRD_MGMT_URL" ]]; then + msg_warn "Invalid selection. Run 'netbird up --management-url ' to connect." + else + msg_warn "Invalid selection. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to connect." + fi + ;; + esac +fi motd_ssh customize From a0cecbc773e94cf397bc5b21b2f5591f10edec67 Mon Sep 17 00:00:00 2001 From: lengschder97 Date: Tue, 13 Jan 2026 07:49:27 +0100 Subject: [PATCH 311/378] Fixed requested changes --- ct/forgejo-runner.sh | 28 +++++++--------------------- install/forgejo-runner-install.sh | 23 +++++------------------ 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh index 6c63ee339..9b50ad51b 100644 --- a/ct/forgejo-runner.sh +++ b/ct/forgejo-runner.sh @@ -33,38 +33,24 @@ function update_script() { exit 1 fi - msg_info "Stopping Forgejo Runner" + msg_info "Stopping Services" systemctl stop forgejo-runner - msg_ok "Stopped Forgejo Runner" + msg_ok "Stopped Services" msg_info "Fetching latest Forgejo Runner version" OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m) - case "$ARCH" in - x86_64) ARCH="amd64" ;; - aarch64|arm64) ARCH="arm64" ;; - armv7l) ARCH="armv7" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; - esac - - RELEASE=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest \ - | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//') - + RELEASE=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//') msg_info "Updating Forgejo Runner to v${RELEASE}" - - curl -fsSL \ - "https://data.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-${OS}-${ARCH}" \ - -o forgejo-runner - + curl -fsSL "https://data.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-${OS}-${ARCH}" -o forgejo-runner chmod +x /usr/local/bin/forgejo-runner msg_ok "Updated Forgejo Runner" - msg_info "Starting Forgejo Runner" - systemctl enable -q --now forgejo-runner - msg_ok "Started Forgejo Runner" - + msg_info "Starting Services" + systemctl start forgejo-runner + msg_ok "Started Services" msg_ok "Update completed successfully!" exit } diff --git a/install/forgejo-runner-install.sh b/install/forgejo-runner-install.sh index 0f55d77f0..be021e489 100644 --- a/install/forgejo-runner-install.sh +++ b/install/forgejo-runner-install.sh @@ -31,17 +31,13 @@ export FORGEJO_RUNNER_TOKEN="$var_forgejo_runner_token" msg_info "Installing dependencies" $STD apt install -y \ - jq git \ + git \ podman podman-docker msg_ok "Dependencies installed" msg_info "Enabling Podman socket" systemctl enable --now podman.socket -msg_ok "Podman socket enabled" - -RAW_ARCH=$(uname -m) -ARCH=$(echo "$RAW_ARCH" | sed 's/x86_64/amd64/;s/aarch64/arm64/') -msg_info "Detected architecture: $ARCH" +msg_ok "Enabled Podman socket" msg_info "Fetching latest Forgejo Runner release" RUNNER_VERSION=$( @@ -49,22 +45,16 @@ RUNNER_VERSION=$( jq -r .name | sed 's/^v//' ) -[[ -z "$RUNNER_VERSION" ]] && { - msg_error "Unable to determine Forgejo Runner version" - exit 1 -} - msg_ok "Forgejo Runner v${RUNNER_VERSION}" FORGEJO_URL="https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-${ARCH}" msg_info "Downloading Forgejo Runner" -wget -q -O /usr/local/bin/forgejo-runner "$FORGEJO_URL" +curl -fsSL "$FORGEJO_URL" -o /usr/local/bin/forgejo-runner chmod +x /usr/local/bin/forgejo-runner msg_ok "Runner installed" msg_info "Registering Forgejo Runner" - export DOCKER_HOST="unix:///run/podman/podman.sock" forgejo-runner register \ @@ -73,11 +63,9 @@ forgejo-runner register \ --name "$HOSTNAME" \ --labels "linux-${ARCH}:docker://node:20-bookworm" \ --no-interactive - msg_ok "Runner registered" -msg_info "Creating systemd service" - +msg_info "Creating Services" cat </etc/systemd/system/forgejo-runner.service [Unit] Description=Forgejo Runner @@ -97,9 +85,8 @@ TimeoutSec=0 [Install] WantedBy=multi-user.target EOF - systemctl enable -q --now forgejo-runner -msg_ok "Forgejo Runner service enabled" +msg_ok "Created Services" motd_ssh customize From 83c3564d8dceedd008c18f8cb242eb4be092a89a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:25:55 +0100 Subject: [PATCH 312/378] correct wrong jsons --- frontend/public/json/almalinux.json | 2 +- frontend/public/json/alpine.json | 2 +- frontend/public/json/centos.json | 2 +- frontend/public/json/debian.json | 2 +- frontend/public/json/devuan.json | 2 +- frontend/public/json/ente.json | 2 +- frontend/public/json/fedora.json | 2 +- frontend/public/json/fladder.json | 2 +- frontend/public/json/freepbx.json | 2 +- frontend/public/json/garmin-grafana.json | 2 +- frontend/public/json/gentoo.json | 2 +- frontend/public/json/hoodik.json | 2 +- frontend/public/json/opencloud.json | 2 +- frontend/public/json/openeuler.json | 2 +- frontend/public/json/opensuse.json | 2 +- frontend/public/json/piler.json | 67 ++++++++++++------------ frontend/public/json/pixelfed.json | 2 +- frontend/public/json/postgresus.json | 3 +- frontend/public/json/rockylinux.json | 2 +- frontend/public/json/tracearr.json | 66 +++++++++++------------ frontend/public/json/ubuntu.json | 2 +- 21 files changed, 85 insertions(+), 87 deletions(-) diff --git a/frontend/public/json/almalinux.json b/frontend/public/json/almalinux.json index a6e429706..83d4185fb 100644 --- a/frontend/public/json/almalinux.json +++ b/frontend/public/json/almalinux.json @@ -2,7 +2,7 @@ "name": "AlmaLinux", "slug": "almalinux", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/alpine.json b/frontend/public/json/alpine.json index c1d24b87f..b23dff382 100644 --- a/frontend/public/json/alpine.json +++ b/frontend/public/json/alpine.json @@ -2,7 +2,7 @@ "name": "Alpine Linux", "slug": "alpine", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/centos.json b/frontend/public/json/centos.json index 3d9d292e8..976ded699 100644 --- a/frontend/public/json/centos.json +++ b/frontend/public/json/centos.json @@ -2,7 +2,7 @@ "name": "CentOS Stream", "slug": "centos", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/debian.json b/frontend/public/json/debian.json index 119315216..a02834425 100644 --- a/frontend/public/json/debian.json +++ b/frontend/public/json/debian.json @@ -2,7 +2,7 @@ "name": "Debian", "slug": "debian", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/devuan.json b/frontend/public/json/devuan.json index 1681deb21..06b3c96d0 100644 --- a/frontend/public/json/devuan.json +++ b/frontend/public/json/devuan.json @@ -2,7 +2,7 @@ "name": "Devuan", "slug": "devuan", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/ente.json b/frontend/public/json/ente.json index 2c14b1b4b..91759aa05 100644 --- a/frontend/public/json/ente.json +++ b/frontend/public/json/ente.json @@ -2,7 +2,7 @@ "name": "Ente", "slug": "ente", "categories": [ - 13 + 11 ], "date_created": "2025-11-22", "type": "ct", diff --git a/frontend/public/json/fedora.json b/frontend/public/json/fedora.json index f2a65a4c9..15f198bab 100644 --- a/frontend/public/json/fedora.json +++ b/frontend/public/json/fedora.json @@ -2,7 +2,7 @@ "name": "Fedora", "slug": "fedora", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/fladder.json b/frontend/public/json/fladder.json index 9ee69b853..fabf7a1f9 100644 --- a/frontend/public/json/fladder.json +++ b/frontend/public/json/fladder.json @@ -2,7 +2,7 @@ "name": "Fladder", "slug": "fladder", "categories": [ - 14 + 13 ], "date_created": "2025-12-26", "type": "ct", diff --git a/frontend/public/json/freepbx.json b/frontend/public/json/freepbx.json index 768b64d06..601673d5b 100644 --- a/frontend/public/json/freepbx.json +++ b/frontend/public/json/freepbx.json @@ -2,7 +2,7 @@ "name": "FreePBX", "slug": "freepbx", "categories": [ - 17 + 0 ], "date_created": "2025-01-15", "type": "ct", diff --git a/frontend/public/json/garmin-grafana.json b/frontend/public/json/garmin-grafana.json index 8c676b733..ee5df81d3 100644 --- a/frontend/public/json/garmin-grafana.json +++ b/frontend/public/json/garmin-grafana.json @@ -2,7 +2,7 @@ "name": "garmin-grafana", "slug": "garmin-grafana", "categories": [ - 24 + 9 ], "date_created": "2025-05-08", "type": "ct", diff --git a/frontend/public/json/gentoo.json b/frontend/public/json/gentoo.json index fb08ce247..302dd7607 100644 --- a/frontend/public/json/gentoo.json +++ b/frontend/public/json/gentoo.json @@ -2,7 +2,7 @@ "name": "Gentoo", "slug": "gentoo", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/hoodik.json b/frontend/public/json/hoodik.json index 878c6fe17..65bf673d6 100644 --- a/frontend/public/json/hoodik.json +++ b/frontend/public/json/hoodik.json @@ -2,7 +2,7 @@ "name": "Hoodik", "slug": "hoodik", "categories": [ - 4 + 11 ], "date_created": "2025-12-10", "type": "ct", diff --git a/frontend/public/json/opencloud.json b/frontend/public/json/opencloud.json index 4dd93e9c9..29fe38a5c 100644 --- a/frontend/public/json/opencloud.json +++ b/frontend/public/json/opencloud.json @@ -2,7 +2,7 @@ "name": "OpenCloud", "slug": "opencloud", "categories": [ - 2 + 11 ], "date_created": "2025-12-12", "type": "ct", diff --git a/frontend/public/json/openeuler.json b/frontend/public/json/openeuler.json index da7b2383e..4e0dd7cef 100644 --- a/frontend/public/json/openeuler.json +++ b/frontend/public/json/openeuler.json @@ -2,7 +2,7 @@ "name": "openEuler", "slug": "openeuler", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/opensuse.json b/frontend/public/json/opensuse.json index 73bb3610e..8a9e7cee0 100644 --- a/frontend/public/json/opensuse.json +++ b/frontend/public/json/opensuse.json @@ -2,7 +2,7 @@ "name": "openSUSE", "slug": "opensuse", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/piler.json b/frontend/public/json/piler.json index d463e9a80..79d40adb6 100644 --- a/frontend/public/json/piler.json +++ b/frontend/public/json/piler.json @@ -1,36 +1,35 @@ { - "name": "Piler", - "slug": "piler", - "categories": [ - 7, - 18 - ], - "date_created": "2025-12-15", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://www.mailpiler.org/", - "config_path": "", - "website": "https://www.mailpiler.org/", - "logo": "https://www.mailpiler.org/piler-logo.png", - "description": "Piler is a feature rich open source email archiving solution with support for legal hold, deduplication, full text search, and many more features.", - "install_methods": [ - { - "type": "default", - "script": "ct/piler.sh", - "resources": { - "cpu": 4, - "ram": 4096, - "hdd": 20, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] + "name": "Piler", + "slug": "piler", + "categories": [ + 7 + ], + "date_created": "2025-12-15", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://www.mailpiler.org/", + "config_path": "", + "website": "https://www.mailpiler.org/", + "logo": "https://www.mailpiler.org/piler-logo.png", + "description": "Piler is a feature rich open source email archiving solution with support for legal hold, deduplication, full text search, and many more features.", + "install_methods": [ + { + "type": "default", + "script": "ct/piler.sh", + "resources": { + "cpu": 4, + "ram": 4096, + "hdd": 20, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } diff --git a/frontend/public/json/pixelfed.json b/frontend/public/json/pixelfed.json index a520ff8e3..e607c1913 100644 --- a/frontend/public/json/pixelfed.json +++ b/frontend/public/json/pixelfed.json @@ -2,7 +2,7 @@ "name": "Pixelfed", "slug": "pixelfed", "categories": [ - 17 + 0 ], "date_created": "2024-06-15", "type": "ct", diff --git a/frontend/public/json/postgresus.json b/frontend/public/json/postgresus.json index 5d569429a..e60f642f5 100644 --- a/frontend/public/json/postgresus.json +++ b/frontend/public/json/postgresus.json @@ -2,8 +2,7 @@ "name": "Postgresus", "slug": "postgresus", "categories": [ - 6, - 11 + 7 ], "date_created": "2025-12-11", "type": "ct", diff --git a/frontend/public/json/rockylinux.json b/frontend/public/json/rockylinux.json index 9c709b7bf..b0875e6e3 100644 --- a/frontend/public/json/rockylinux.json +++ b/frontend/public/json/rockylinux.json @@ -2,7 +2,7 @@ "name": "Rocky Linux", "slug": "rockylinux", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", diff --git a/frontend/public/json/tracearr.json b/frontend/public/json/tracearr.json index 0e729d977..458ec538b 100644 --- a/frontend/public/json/tracearr.json +++ b/frontend/public/json/tracearr.json @@ -1,35 +1,35 @@ { - "name": "Tracearr", - "slug": "tracearr", - "categories": [ - 13 - ], - "date_created": "2025-12-28", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/connorgallopo/Tracearr#readme", - "config_path": "", - "website": "https://github.com/connorgallopo/Tracearr", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tracearr.webp", - "description": "Tracearr is a streaming access manager for Plex, Jellyfin and Emby servers. It answers the question every server owner eventually asks: \"Who's actually using my server, and are they sharing their login?\"", - "install_methods": [ - { - "type": "default", - "script": "ct/tracearr.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 5, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] + "name": "Tracearr", + "slug": "tracearr", + "categories": [ + 14 + ], + "date_created": "2025-12-28", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/connorgallopo/Tracearr#readme", + "config_path": "", + "website": "https://github.com/connorgallopo/Tracearr", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tracearr.webp", + "description": "Tracearr is a streaming access manager for Plex, Jellyfin and Emby servers. It answers the question every server owner eventually asks: \"Who's actually using my server, and are they sharing their login?\"", + "install_methods": [ + { + "type": "default", + "script": "ct/tracearr.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 5, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } diff --git a/frontend/public/json/ubuntu.json b/frontend/public/json/ubuntu.json index eabf59d8d..4e62dbf2a 100644 --- a/frontend/public/json/ubuntu.json +++ b/frontend/public/json/ubuntu.json @@ -2,7 +2,7 @@ "name": "Ubuntu", "slug": "ubuntu", "categories": [ - 0 + 2 ], "date_created": "2025-12-04", "type": "ct", From 39d9695f7423cfa4b557c50f6327ca8cb89416c3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:28:15 +0100 Subject: [PATCH 313/378] fix broken json --- frontend/public/json/tor-snowflake.json | 66 ++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/frontend/public/json/tor-snowflake.json b/frontend/public/json/tor-snowflake.json index 00ed299f9..d100e5885 100644 --- a/frontend/public/json/tor-snowflake.json +++ b/frontend/public/json/tor-snowflake.json @@ -1,35 +1,35 @@ { - "name": "Tor Snowflake", - "slug": "tor-snowflake", - "categories": [ - 4 - ], - "date_created": "2025-12-19", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": null, - "documentation": "https://community.torproject.org/relay/setup/snowflake/standalone/", - "website": "https://snowflake.torproject.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tor.webp", - "config_path": null, - "description": "Snowflake is a pluggable transport that proxies traffic through temporary proxies using WebRTC. Snowflake allows users in censored locations to access the open internet by connecting through volunteer-run proxies. Running a Snowflake proxy helps users circumvent internet censorship by forwarding their traffic through your server.", - "install_methods": [ - { - "type": "default", - "script": "ct/tor-snowflake.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] + "name": "Tor Snowflake", + "slug": "tor-snowflake", + "categories": [ + 4 + ], + "date_created": "2025-12-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://community.torproject.org/relay/setup/snowflake/standalone/", + "website": "https://snowflake.torproject.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tor.webp", + "config_path": "", + "description": "Snowflake is a pluggable transport that proxies traffic through temporary proxies using WebRTC. Snowflake allows users in censored locations to access the open internet by connecting through volunteer-run proxies. Running a Snowflake proxy helps users circumvent internet censorship by forwarding their traffic through your server.", + "install_methods": [ + { + "type": "default", + "script": "ct/tor-snowflake.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } From 572da9a60a99f99ab001d91480f52adb317b948a Mon Sep 17 00:00:00 2001 From: lengschder97 Date: Tue, 13 Jan 2026 08:36:33 +0100 Subject: [PATCH 314/378] Fixed comments --- ct/forgejo-runner.sh | 10 ++-------- install/forgejo-runner-install.sh | 12 +++--------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh index 9b50ad51b..4542f1bee 100644 --- a/ct/forgejo-runner.sh +++ b/ct/forgejo-runner.sh @@ -38,13 +38,9 @@ function update_script() { msg_ok "Stopped Services" msg_info "Fetching latest Forgejo Runner version" - OS=$(uname -s | tr '[:upper:]' '[:lower:]') - ARCH=$(uname -m) - RELEASE=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//') msg_info "Updating Forgejo Runner to v${RELEASE}" - curl -fsSL "https://data.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-${OS}-${ARCH}" -o forgejo-runner - + curl -fsSL "https://data.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-linux-amd64" -o forgejo-runner chmod +x /usr/local/bin/forgejo-runner msg_ok "Updated Forgejo Runner" @@ -60,6 +56,4 @@ 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}" \ No newline at end of file +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" \ No newline at end of file diff --git a/install/forgejo-runner-install.sh b/install/forgejo-runner-install.sh index be021e489..93b623a3c 100644 --- a/install/forgejo-runner-install.sh +++ b/install/forgejo-runner-install.sh @@ -40,23 +40,17 @@ systemctl enable --now podman.socket msg_ok "Enabled Podman socket" msg_info "Fetching latest Forgejo Runner release" -RUNNER_VERSION=$( - curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | - jq -r .name | sed 's/^v//' -) - +RUNNER_VERSION=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | jq -r .name | sed 's/^v//') msg_ok "Forgejo Runner v${RUNNER_VERSION}" +msg_info "Installing Forgejo Runner" FORGEJO_URL="https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-${ARCH}" - -msg_info "Downloading Forgejo Runner" curl -fsSL "$FORGEJO_URL" -o /usr/local/bin/forgejo-runner chmod +x /usr/local/bin/forgejo-runner -msg_ok "Runner installed" +msg_ok "Installed Forgejo Runner" msg_info "Registering Forgejo Runner" export DOCKER_HOST="unix:///run/podman/podman.sock" - forgejo-runner register \ --instance "$FORGEJO_INSTANCE" \ --token "$FORGEJO_RUNNER_TOKEN" \ From 237d8ae21d2a55f118f8f7573c6f2de0536ccd3e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 07:54:07 +0000 Subject: [PATCH 315/378] Delete fladder (ct) after migration to ProxmoxVE (#1272) Co-authored-by: github-actions[bot] --- ct/fladder.sh | 68 ------------------------------- frontend/public/json/fladder.json | 35 ---------------- install/fladder-install.sh | 46 --------------------- 3 files changed, 149 deletions(-) delete mode 100644 ct/fladder.sh delete mode 100644 frontend/public/json/fladder.json delete mode 100644 install/fladder-install.sh diff --git a/ct/fladder.sh b/ct/fladder.sh deleted file mode 100644 index 39ce55c9b..000000000 --- a/ct/fladder.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: wendyliga -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/DonutWare/Fladder - -APP="Fladder" -var_tags="${var_tags:-media}" -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/fladder ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "Fladder" "DonutWare/Fladder"; then - msg_info "Stopping Service" - systemctl stop nginx - msg_ok "Stopped Service" - - msg_info "Backing up configuration" - if [[ -f /opt/fladder/assets/config/config.json ]]; then - cp /opt/fladder/assets/config/config.json /tmp/fladder_config.json.bak - msg_ok "Configuration backed up" - fi - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" - - msg_info "Restoring configuration" - if [[ -f /tmp/fladder_config.json.bak ]]; then - mkdir -p /opt/fladder/assets/config - cp /tmp/fladder_config.json.bak /opt/fladder/assets/config/config.json - rm -f /tmp/fladder_config.json.bak - msg_ok "Configuration restored" - fi - - msg_info "Starting Service" - systemctl start nginx - 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 IP:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/fladder.json b/frontend/public/json/fladder.json deleted file mode 100644 index fabf7a1f9..000000000 --- a/frontend/public/json/fladder.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Fladder", - "slug": "fladder", - "categories": [ - 13 - ], - "date_created": "2025-12-26", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://github.com/DonutWare/Fladder/blob/develop/INSTALL.md#ubuntudebian", - "website": "https://github.com/DonutWare/Fladder", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/fladder.webp", - "config_path": "/opt/fladder/assets/config/config.json", - "description": "Fladder is a simple Jellyfin frontend built on top of Flutter. It provides a modern interface to stream and sync content locally, manage libraries, support multiple profiles, and offers direct, transcode and offline playback with media segments skipping.", - "install_methods": [ - { - "type": "default", - "script": "ct/fladder.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/fladder-install.sh b/install/fladder-install.sh deleted file mode 100644 index 11c42392b..000000000 --- a/install/fladder-install.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: wendyliga -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/DonutWare/Fladder - -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 nginx -msg_ok "Installed Dependencies" - -fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" - -msg_info "Configuring Nginx" -cat </etc/nginx/conf.d/fladder.conf -server { - listen 80 default_server; - listen [::]:80 default_server; - - server_name _; - - root /opt/fladder; - index index.html; - - location / { - try_files \$uri \$uri/ /index.html; - } -} -EOF -rm -f /etc/nginx/sites-enabled/default -rm -f /etc/nginx/sites-available/default -systemctl enable -q --now nginx -systemctl reload nginx -msg_ok "Configured Nginx" - -motd_ssh -customize -cleanup_lxc From a140be56fcc4846d36ea2a806fc46063b2b44c39 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:57:29 +0100 Subject: [PATCH 316/378] fix workflow --- .github/workflows/move-to-main-repo.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/move-to-main-repo.yaml b/.github/workflows/move-to-main-repo.yaml index e22161dfc..bcc697b5f 100644 --- a/.github/workflows/move-to-main-repo.yaml +++ b/.github/workflows/move-to-main-repo.yaml @@ -181,7 +181,8 @@ jobs: - name: Prepare branch name run: | script_name="${{ steps.list_issues.outputs.script_name }}" - branch_name="add-script-${script_name//[^a-zA-Z0-9_-]/}" + timestamp=$(date +%s) + branch_name="add-script-${script_name//[^a-zA-Z0-9_-]/}-${timestamp}" echo "Using branch: $branch_name" echo "branch_name=$branch_name" >> $GITHUB_ENV @@ -194,6 +195,12 @@ jobs: git clone https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/community-scripts/ProxmoxVE.git ProxmoxVE cd ProxmoxVE + # Check if branch already exists remotely and delete it + if git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then + echo "Branch $branch_name already exists remotely, deleting it..." + git push origin --delete "$branch_name" || true + fi + # Check if files already exist in target repo case "$script_type" in ct) From 095501ea79e41d7696f4d7542af284dc46ba346d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:12:09 +0100 Subject: [PATCH 317/378] finalize kitchenowl --- ct/kitchenowl.sh | 26 ++++++++++---------------- install/kitchenowl-install.sh | 10 +++++----- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/ct/kitchenowl.sh b/ct/kitchenowl.sh index 25d54663f..7517980d0 100644 --- a/ct/kitchenowl.sh +++ b/ct/kitchenowl.sh @@ -34,44 +34,38 @@ function update_script() { systemctl stop kitchenowl msg_ok "Stopped Service" - if [[ -d /opt/kitchenowl_backup/data ]]; then - msg_ok "Using existing backup from previous update attempt" - else - msg_info "Backing up KitchenOwl" - mkdir -p /opt/kitchenowl_backup - cp -r /opt/kitchenowl/data /opt/kitchenowl_backup/ - cp -f /opt/kitchenowl/kitchenowl.env /opt/kitchenowl_backup/ - msg_ok "Backed up KitchenOwl" - fi + msg_info "Backing up KitchenOwl" + mkdir -p /opt/kitchenowl_backup + cp -r /opt/kitchenowl/data /opt/kitchenowl_backup/ + cp -f /opt/kitchenowl/kitchenowl.env /opt/kitchenowl_backup/ + msg_ok "Backed up KitchenOwl" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" - sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" msg_info "Restoring KitchenOwl data" + sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py cp -r /opt/kitchenowl_backup/data /opt/kitchenowl/ cp -f /opt/kitchenowl_backup/kitchenowl.env /opt/kitchenowl/ rm -rf /opt/kitchenowl_backup msg_ok "Restored KitchenOwl data" - msg_info "Installing Dependencies" + msg_info "Updating KitchenOwl" cd /opt/kitchenowl/backend $STD uv sync --frozen - msg_ok "Dependencies installed" - - msg_info "Running Database Migrations" cd /opt/kitchenowl/backend set -a source /opt/kitchenowl/kitchenowl.env set +a $STD uv run flask db upgrade - msg_ok "Database Migrations Complete" + msg_ok "Updated KitchenOwl" msg_info "Starting Service" systemctl start kitchenowl msg_ok "Started Service" - msg_ok "Updated Successfully" + msg_ok "Updated successfully!" fi + exit } start diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh index 9a7d2da3e..6a76d9a55 100644 --- a/install/kitchenowl-install.sh +++ b/install/kitchenowl-install.sh @@ -22,11 +22,11 @@ $STD apt install -y \ libssl-dev msg_ok "Installed Dependencies" -PYTHON_VERSION="3.12" setup_uv - -CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" +PYTHON_VERSION="3.13" setup_uv +import_local_ip +fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" rm -rf /opt/kitchenowl/web -CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" +fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" msg_info "Setting up KitchenOwl" cd /opt/kitchenowl/backend @@ -35,7 +35,6 @@ sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py mkdir -p /nltk_data $STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng punkt_tab JWT_SECRET=$(openssl rand -hex 32) -import_local_ip mkdir -p /opt/kitchenowl/data cat </opt/kitchenowl/kitchenowl.env STORAGE_PATH=/opt/kitchenowl/data @@ -122,6 +121,7 @@ server { } EOF ln -sf /etc/nginx/sites-available/kitchenowl.conf /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Configured Nginx" From fb3f7eb31ae1ab88fd9cc56f38211ace1bbe67ad Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:21:23 +0100 Subject: [PATCH 318/378] fix uv --- install/kitchenowl-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh index 6a76d9a55..f9190fc5f 100644 --- a/install/kitchenowl-install.sh +++ b/install/kitchenowl-install.sh @@ -22,7 +22,7 @@ $STD apt install -y \ libssl-dev msg_ok "Installed Dependencies" -PYTHON_VERSION="3.13" setup_uv +PYTHON_VERSION="3.12" setup_uv import_local_ip fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" rm -rf /opt/kitchenowl/web From 5390948c8366d4b2d4272839a2fd8db90870752f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:33:36 +0100 Subject: [PATCH 319/378] fix frozen --- install/kitchenowl-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh index f9190fc5f..0ef0ce7a3 100644 --- a/install/kitchenowl-install.sh +++ b/install/kitchenowl-install.sh @@ -30,7 +30,7 @@ fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" " msg_info "Setting up KitchenOwl" cd /opt/kitchenowl/backend -$STD uv sync --frozen +$STD uv sync --python 3.12 sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py mkdir -p /nltk_data $STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng punkt_tab From 64eab7566bac080448a65e0c94e6c80520673ed1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:49:27 +0100 Subject: [PATCH 320/378] tedst --- install/kitchenowl-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh index 0ef0ce7a3..e5627e7e6 100644 --- a/install/kitchenowl-install.sh +++ b/install/kitchenowl-install.sh @@ -22,7 +22,7 @@ $STD apt install -y \ libssl-dev msg_ok "Installed Dependencies" -PYTHON_VERSION="3.12" setup_uv +PYTHON_VERSION="3.14" setup_uv import_local_ip fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" rm -rf /opt/kitchenowl/web @@ -30,7 +30,8 @@ fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" " msg_info "Setting up KitchenOwl" cd /opt/kitchenowl/backend -$STD uv sync --python 3.12 +rm -f uv.lock +$STD uv sync sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py mkdir -p /nltk_data $STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng punkt_tab From 6d210f021cf10dbda19d0032e9b62704378940a2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:09:11 +0100 Subject: [PATCH 321/378] ampache --- ct/ampache.sh | 74 +++++++++++++++++++++++++ ct/deferred/ampache.sh | 48 ----------------- install/ampache-install.sh | 58 ++++++++++++++++++++ install/deferred/ampache-install.sh | 83 ----------------------------- 4 files changed, 132 insertions(+), 131 deletions(-) create mode 100644 ct/ampache.sh delete mode 100644 ct/deferred/ampache.sh create mode 100644 install/ampache-install.sh delete mode 100644 install/deferred/ampache-install.sh diff --git a/ct/ampache.sh b/ct/ampache.sh new file mode 100644 index 000000000..eb5e949c0 --- /dev/null +++ b/ct/ampache.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ampache/ampache + +APP="Ampache" +var_tags="${var_tags:-music}" +var_disk="${var_disk:-5}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-2048}" +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/ampache ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "Ampache" "ampache/ampache"; then + msg_info "Stopping Apache" + systemctl stop apache2 + msg_ok "Stopped Apache" + + msg_info "Backing up Configuration" + cp /opt/ampache/config/ampache.cfg.php /tmp/ampache.cfg.php.backup + cp /opt/ampache/rest/.htaccess /tmp/ampache_rest.htaccess.backup + cp /opt/ampache/play/.htaccess /tmp/ampache_play.htaccess.backup + cp /opt/ampache/channel/.htaccess /tmp/ampache_channel.htaccess.backup + msg_ok "Backed up Configuration" + + msg_info "Backup Ampache Folder" + rm -rf /opt/ampache_backup + mv /opt/ampache /opt/ampache_backup + msg_ok "Backed up Ampache" + + fetch_and_deploy_gh_release "Ampache" "ampache/ampache" "release" "latest" "/opt/ampache" "ampache-*_all_php8.4.zip" + + msg_info "Restoring Configuration" + cp /tmp/ampache.cfg.php.backup /opt/ampache/config/ampache.cfg.php + cp /tmp/ampache_rest.htaccess.backup /opt/ampache/rest/.htaccess + cp /tmp/ampache_play.htaccess.backup /opt/ampache/play/.htaccess + cp /tmp/ampache_channel.htaccess.backup /opt/ampache/channel/.htaccess + chmod 664 /opt/ampache/rest/.htaccess /opt/ampache/play/.htaccess /opt/ampache/channel/.htaccess + chown -R www-data:www-data /opt/ampache + rm -f /tmp/ampache*.backup + msg_ok "Restored Configuration" + + msg_info "Starting Apache" + systemctl start apache2 + msg_ok "Started Apache" + 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.php${CL}" diff --git a/ct/deferred/ampache.sh b/ct/deferred/ampache.sh deleted file mode 100644 index 068b2675a..000000000 --- a/ct/deferred/ampache.sh +++ /dev/null @@ -1,48 +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-2026 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: - -APP="Ampache" -var_tags="${var_tags:-music}" -var_disk="${var_disk:-5}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-2048}" -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/ampache ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Updating ${APP} LXC" - cd /opt/ampache - ###### Update Script Here ###### - 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}/install.php${CL}" diff --git a/install/ampache-install.sh b/install/ampache-install.sh new file mode 100644 index 000000000..c3892c15d --- /dev/null +++ b/install/ampache-install.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ampache/ampache + +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 \ + flac \ + vorbis-tools \ + lame \ + ffmpeg \ + inotify-tools \ + libavcodec-extra \ + libmp3lame-dev \ + libtheora-dev \ + libvorbis-dev \ + libvpx-dev +msg_ok "Installed Dependencies" + +PHP_VERSION=8.4 PHP_MODULE=bcmath,bz2,curl,gd,imagick,intl,mbstring,mysql,sqlite3,xml,xmlrpc,zip PHP_APACHE=YES setup_php +setup_mariadb +DB_NAME=ampache DB_USER=ampache setup_mariadb_db + +fetch_and_deploy_gh_release "Ampache" "ampache/ampache" "release" "latest" "/opt/ampache" "ampache-*_all_php8.4.zip" + +msg_info "Setup Ampache" +rm -rf /var/www/html +ln -s /opt/ampache/public /var/www/html +mv /opt/ampache/rest/.htaccess.dist /opt/ampache/rest/.htaccess +mv /opt/ampache/play/.htaccess.dist /opt/ampache/play/.htaccess +mv /opt/ampache/channel/.htaccess.dist /opt/ampache/channel/.htaccess +cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php +chmod 664 /opt/ampache/rest/.htaccess /opt/ampache/play/.htaccess /opt/ampache/channel/.htaccess +chown -R www-data:www-data /opt/ampache +msg_ok "Set up Ampache" + +msg_info "Configuring PHP" +sed -i 's/upload_max_filesize = .*/upload_max_filesize = 100M/' /etc/php/8.4/apache2/php.ini +sed -i 's/post_max_size = .*/post_max_size = 100M/' /etc/php/8.4/apache2/php.ini +sed -i 's/max_execution_time = .*/max_execution_time = 600/' /etc/php/8.4/apache2/php.ini +sed -i 's/memory_limit = .*/memory_limit = 512M/' /etc/php/8.4/apache2/php.ini +$STD a2enmod rewrite +$STD systemctl restart apache2 +msg_ok "Configured PHP" + +motd_ssh +customize +cleanup_lxc diff --git a/install/deferred/ampache-install.sh b/install/deferred/ampache-install.sh deleted file mode 100644 index 35dfe1430..000000000 --- a/install/deferred/ampache-install.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2024 tteck -# Author: MickLesk (Canbiz) -# License: MIT -# https://github.com/tteck/Proxmox/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 (Patience)" -apt-get install -y \ - apache2 \ - cron \ - flac \ - vorbis-tools \ - lame \ - ffmpeg \ - lsb-release \ - gosu \ - wget \ - curl \ - git \ - make \ - inotify-tools \ - libavcodec-extra \ - libev-libevent-dev \ - libmp3lame-dev \ - libtheora-dev \ - libvorbis-dev \ - libvpx-dev -msg_ok "Installed Dependencies" - -PHP_VERSION=8.4 -PHP_MODULE=bcmath,bz2,cli,common,curl,fpm,gd,imagick,intl,mbstring,mysql,sqlite3,xml,xmlrpc,zip -PHP_APACHE=YES -setup_php -setup_mariadb - -msg_info "Setting up Database" -DB_NAME=ampache2 -DB_USER=ampache2 -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -sudo mysql -u root -e "CREATE DATABASE $DB_NAME;" -sudo mysql -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED WITH mysql_native_password AS PASSWORD('$DB_PASS');" -sudo mysql -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" -echo "" >>~/ampache.creds -echo -e "Ampache Database User: \e $DB_USER\e" >>~/ampache.creds -echo -e "Ampache Database Password: \e$DB_PASS\e" >>~/ampache.creds -echo -e "Ampache Database Name: \e$DB_NAME\e" >>~/ampache.creds -msg_ok "Set up database" - -msg_info "Installing Ampache(Patience)" -cd /opt -AMPACHE_VERSION=$(wget -q https://github.com/ampache/ampache/releases/latest -O - | grep "title>Release" | cut -d " " -f 4) -wget https://github.com/ampache/ampache/releases/download/${AMPACHE_VERSION}/ampache-${AMPACHE_VERSION}_all_php8.4.zip -unzip -q ampache-${AMPACHE_VERSION}_all_php8.4.zip -d ampache -rm -rf /var/www/html -ln -s /opt/ampache/public /var/www/html -sudo mv /opt/ampache/rest/.htaccess.dist /opt/ampache/rest/.htaccess -sudo mv /opt/ampache/play/.htaccess.dist /opt/ampache/play/.htaccess -sudo mv /opt/ampache/channel/.htaccess.dist /opt/ampache/channel/.htaccess -sudo cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php -sudo chmod 664 /opt/ampache/rest/.htaccess /opt/ampache/play/.htaccess -sudo sed -i 's/upload_max_filesize = .*/upload_max_filesize = 50M/' /etc/php/8.4/apache2/php.ini && - sudo sed -i 's/post_max_size = .*/post_max_size = 50M/' /etc/php/8.4/apache2/php.ini && - sudo sed -i 's/max_execution_time = .*/max_execution_time = 300/' /etc/php/8.4/apache2/php.ini && - sudo sed -i 's/memory_limit = .*/memory_limit = 256M/' /etc/php/8.4/apache2/php.ini && - sudo systemctl restart apache2 -msg_ok "Installed Ampache" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get autoremove -$STD apt-get autoclean -msg_ok "Cleaned" From 6401b04a8bb155dd7eb737081684c284604b7e40 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:15:16 +0100 Subject: [PATCH 322/378] ampache json --- ct/ampache.sh | 2 +- frontend/public/json/ampache.json | 48 +++++++++++++++++++++++++++++++ install/ampache-install.sh | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 frontend/public/json/ampache.json diff --git a/ct/ampache.sh b/ct/ampache.sh index eb5e949c0..70e8ea9d0 100644 --- a/ct/ampache.sh +++ b/ct/ampache.sh @@ -11,7 +11,7 @@ var_disk="${var_disk:-5}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-2048}" 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/ampache.json b/frontend/public/json/ampache.json new file mode 100644 index 000000000..16df69787 --- /dev/null +++ b/frontend/public/json/ampache.json @@ -0,0 +1,48 @@ +{ + "name": "Ampache", + "slug": "ampache", + "categories": [ + 13 + ], + "date_created": "2026-01-13", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://github.com/ampache/ampache/wiki", + "website": "https://ampache.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/ampache.webp", + "config_path": "/opt/ampache/config/ampache.cfg.php", + "description": "Ampache is a web-based audio streaming application and file manager that allows you to access your music & videos from anywhere. It features a powerful music catalog, multiple user support, transcoding, streaming, and more.", + "install_methods": [ + { + "type": "default", + "script": "ct/ampache.sh", + "resources": { + "cpu": 4, + "ram": 2048, + "hdd": 5, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Complete the web-based setup at http://IP/install.php", + "type": "info" + }, + { + "text": "Database credentials are stored in ~/ampache.creds", + "type": "info" + }, + { + "text": "Supports multiple music formats with transcoding capabilities", + "type": "info" + } + ] +} diff --git a/install/ampache-install.sh b/install/ampache-install.sh index c3892c15d..679572993 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ flac \ vorbis-tools \ lame \ From 532983f3704cd191198df268c837debad9f26111 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:15:49 +0100 Subject: [PATCH 323/378] typo --- install/ampache-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ampache-install.sh b/install/ampache-install.sh index 679572993..b4a254cc7 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -29,7 +29,7 @@ msg_ok "Installed Dependencies" PHP_VERSION=8.4 PHP_MODULE=bcmath,bz2,curl,gd,imagick,intl,mbstring,mysql,sqlite3,xml,xmlrpc,zip PHP_APACHE=YES setup_php setup_mariadb -DB_NAME=ampache DB_USER=ampache setup_mariadb_db +MARIADB_DB_USER=ampache MARIADB_DB_NAME=ampache setup_mariadb_db fetch_and_deploy_gh_release "Ampache" "ampache/ampache" "release" "latest" "/opt/ampache" "ampache-*_all_php8.4.zip" From 80a10bf5c7af64cfcde65c4c5920431b898b7272 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:33:47 +0100 Subject: [PATCH 324/378] sd --- install/kitchenowl-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh index e5627e7e6..eb90ba9a8 100644 --- a/install/kitchenowl-install.sh +++ b/install/kitchenowl-install.sh @@ -30,8 +30,8 @@ fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" " msg_info "Setting up KitchenOwl" cd /opt/kitchenowl/backend -rm -f uv.lock -$STD uv sync +#rm -f uv.lock +$STD uv sync --frozen sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py mkdir -p /nltk_data $STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng punkt_tab From dc9dd2a57cd5e820e70bac0b63d6d0306c843e2e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:37:32 +0100 Subject: [PATCH 325/378] prebuild --- install/ampache-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ampache-install.sh b/install/ampache-install.sh index b4a254cc7..265423f9f 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -31,7 +31,7 @@ PHP_VERSION=8.4 PHP_MODULE=bcmath,bz2,curl,gd,imagick,intl,mbstring,mysql,sqlite setup_mariadb MARIADB_DB_USER=ampache MARIADB_DB_NAME=ampache setup_mariadb_db -fetch_and_deploy_gh_release "Ampache" "ampache/ampache" "release" "latest" "/opt/ampache" "ampache-*_all_php8.4.zip" +fetch_and_deploy_gh_release "ampache" "ampache/ampache" "prebuild" "latest" "/opt/ampache" "ampache-*_all_php8.4.zip" msg_info "Setup Ampache" rm -rf /var/www/html From 2da6c955f35738d0ea74df631d9f23eebdea607f Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:38:06 +0100 Subject: [PATCH 326/378] refactor --- ct/forgejo-runner.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh index 4542f1bee..59145398c 100644 --- a/ct/forgejo-runner.sh +++ b/ct/forgejo-runner.sh @@ -37,7 +37,6 @@ function update_script() { systemctl stop forgejo-runner msg_ok "Stopped Services" - msg_info "Fetching latest Forgejo Runner version" RELEASE=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//') msg_info "Updating Forgejo Runner to v${RELEASE}" curl -fsSL "https://data.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-linux-amd64" -o forgejo-runner @@ -47,7 +46,7 @@ function update_script() { msg_info "Starting Services" systemctl start forgejo-runner msg_ok "Started Services" - msg_ok "Update completed successfully!" + msg_ok "Updated successfully!" exit } @@ -56,4 +55,4 @@ build_container description msg_ok "Completed successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" \ No newline at end of file +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" From bc1b073f74ea51b857f543279ca6ec305322b862 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:40:04 +0100 Subject: [PATCH 327/378] refactor --- install/forgejo-runner-install.sh | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/install/forgejo-runner-install.sh b/install/forgejo-runner-install.sh index 93b623a3c..f6bea2f5f 100644 --- a/install/forgejo-runner-install.sh +++ b/install/forgejo-runner-install.sh @@ -33,19 +33,15 @@ msg_info "Installing dependencies" $STD apt install -y \ git \ podman podman-docker -msg_ok "Dependencies installed" +msg_ok "Installed dependencies" msg_info "Enabling Podman socket" systemctl enable --now podman.socket msg_ok "Enabled Podman socket" -msg_info "Fetching latest Forgejo Runner release" -RUNNER_VERSION=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | jq -r .name | sed 's/^v//') -msg_ok "Forgejo Runner v${RUNNER_VERSION}" - msg_info "Installing Forgejo Runner" -FORGEJO_URL="https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-${ARCH}" -curl -fsSL "$FORGEJO_URL" -o /usr/local/bin/forgejo-runner +RUNNER_VERSION=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | jq -r .name | sed 's/^v//') +curl -fsSL "https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-${ARCH}" -o /usr/local/bin/forgejo-runner chmod +x /usr/local/bin/forgejo-runner msg_ok "Installed Forgejo Runner" @@ -57,7 +53,7 @@ forgejo-runner register \ --name "$HOSTNAME" \ --labels "linux-${ARCH}:docker://node:20-bookworm" \ --no-interactive -msg_ok "Runner registered" +msg_ok "Registered Forgejo Runner" msg_info "Creating Services" cat </etc/systemd/system/forgejo-runner.service @@ -84,4 +80,4 @@ msg_ok "Created Services" motd_ssh customize -cleanup_lxc \ No newline at end of file +cleanup_lxc From 48556b5636d6cebe242360bc240744330aa1181c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:46:55 +0000 Subject: [PATCH 328/378] Delete investbrain (ct) after migration to ProxmoxVE (#1276) Co-authored-by: github-actions[bot] --- ct/investbrain.sh | 91 ------------- frontend/public/json/investbrain.json | 40 ------ install/investbrain-install.sh | 182 -------------------------- 3 files changed, 313 deletions(-) delete mode 100644 ct/investbrain.sh delete mode 100644 frontend/public/json/investbrain.json delete mode 100644 install/investbrain-install.sh diff --git a/ct/investbrain.sh b/ct/investbrain.sh deleted file mode 100644 index ea5f3f48c..000000000 --- a/ct/investbrain.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2026 community-scripts ORG -# Author: Benito Rodríguez (b3ni) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/investbrainapp/investbrain - -APP="Investbrain" -var_tags="${var_tags:-finance;portfolio;investing}" -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/investbrain ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "Investbrain" "investbrainapp/investbrain"; then - PHP_VERSION="8.4" - msg_info "Stopping Services" - systemctl stop nginx php${PHP_VERSION}-fpm - $STD supervisorctl stop all - msg_ok "Services Stopped" - - PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php - setup_composer - NODE_VERSION="22" setup_nodejs - PG_VERSION="17" setup_postgresql - - msg_info "Creating Backup" - rm -f /opt/.env.backup - rm -rf /opt/investbrain_backup - cp /opt/investbrain/.env /opt/.env.backup - cp -r /opt/investbrain/storage /opt/investbrain_backup - msg_ok "Created Backup" - - fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" - - msg_info "Updating Investbrain" - cd /opt/investbrain - rm -rf /opt/investbrain/storage - cp /opt/.env.backup /opt/investbrain/.env - cp -r /opt/investbrain_backup/ /opt/investbrain/storage - export COMPOSER_ALLOW_SUPERUSER=1 - $STD /usr/local/bin/composer install --no-interaction --no-dev --optimize-autoloader - $STD npm install - $STD npm run build - $STD php artisan storage:link - $STD php artisan migrate --force - $STD php artisan cache:clear - $STD php artisan view:clear - $STD php artisan route:clear - $STD php artisan event:clear - $STD php artisan route:cache - $STD php artisan event:cache - chown -R www-data:www-data /opt/investbrain - chmod -R 775 /opt/investbrain/storage /opt/investbrain/bootstrap/cache - rm -rf /opt/.env.backup /opt/investbrain_backup - msg_ok "Updated Investbrain" - - msg_info "Starting Services" - systemctl start php${PHP_VERSION}-fpm nginx - $STD supervisorctl start all - 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}:8000${CL}" diff --git a/frontend/public/json/investbrain.json b/frontend/public/json/investbrain.json deleted file mode 100644 index 7ed3fbccc..000000000 --- a/frontend/public/json/investbrain.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Investbrain", - "slug": "investbrain", - "categories": [ - 23 - ], - "date_created": "2025-12-26", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8000, - "documentation": "https://github.com/investbrainapp/investbrain", - "website": "https://investbra.in", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/investbrain.webp", - "config_path": "/opt/investbrain/.env", - "description": "Investbrain is a smart open-source investment tracker that helps you manage, track, and make informed decisions about your investments.", - "install_methods": [ - { - "type": "default", - "script": "ct/investbrain.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Database credentials: `cat ~/investbrain.creds`", - "type": "info" - } - ] -} diff --git a/install/investbrain-install.sh b/install/investbrain-install.sh deleted file mode 100644 index 375499948..000000000 --- a/install/investbrain-install.sh +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: Benito Rodríguez (b3ni) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/investbrainapp/investbrain - -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 \ - nginx \ - supervisor \ - redis-server \ - libfreetype-dev \ - libjpeg62-turbo-dev \ - libpng-dev \ - zlib1g-dev \ - libzip-dev \ - libicu-dev \ - libpq-dev -msg_ok "Installed Dependencies" - -export PHP_VERSION="8.4" -PHP_FPM=YES PHP_MODULE="gd,zip,intl,pdo,pgsql,pdo-pgsql,bcmath,opcache,mbstring,redis" setup_php -setup_composer -NODE_VERSION="22" setup_nodejs -PG_VERSION="17" setup_postgresql -PG_DB_NAME="investbrain" PG_DB_USER="investbrain" setup_postgresql_db - -fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" -import_local_ip - -msg_info "Installing Investbrain (Patience)" -APP_KEY=$(openssl rand -base64 32) -cd /opt/investbrain -cat </opt/investbrain/.env -APP_KEY=base64:${APP_KEY} -APP_PORT=8000 -APP_URL=http://${LOCAL_IP}:8000 -ASSET_URL=http://${LOCAL_IP}:8000 - -LOG_CHANNEL=daily -LOG_LEVEL=warning - -REGISTRATION_ENABLED=true - -AI_CHAT_ENABLED=false -OPENAI_API_KEY= -OPENAI_ORGANIZATION= - -MARKET_DATA_PROVIDER=yahoo -ALPHAVANTAGE_API_KEY= -FINNHUB_API_KEY= -ALPACA_API_KEY= -ALPACA_API_SECRET= -TWELVEDATA_API_SECRET= - -MARKET_DATA_REFRESH=30 -DAILY_CHANGE_TIME= - -DB_CONNECTION=pgsql -DB_HOST=127.0.0.1 -DB_PORT=5432 -DB_DATABASE=${PG_DB_NAME} -DB_USERNAME=${PG_DB_USER} -DB_PASSWORD=${PG_DB_PASS} - -REDIS_CLIENT=phpredis -REDIS_HOST=127.0.0.1 -REDIS_PASSWORD=null -REDIS_PORT=6379 - -CACHE_STORE=redis -CACHE_PREFIX= - -SESSION_DRIVER=redis -SESSION_LIFETIME=120 - -QUEUE_CONNECTION=redis - -MAIL_MAILER=log -MAIL_HOST=127.0.0.1 -MAIL_PORT=2525 -MAIL_FROM_ADDRESS="investbrain@${LOCAL_IP}" - -VITE_APP_NAME=Investbrain -EOF -export COMPOSER_ALLOW_SUPERUSER=1 -$STD /usr/local/bin/composer install --no-interaction --no-dev --optimize-autoloader -$STD npm install -$STD npm run build -mkdir -p /opt/investbrain/storage/{framework/cache,framework/sessions,framework/views,app,logs} -$STD php artisan migrate --force -$STD php artisan storage:link -$STD php artisan cache:clear -$STD php artisan view:clear -$STD php artisan route:clear -$STD php artisan event:clear -$STD php artisan route:cache -$STD php artisan event:cache -chown -R www-data:www-data /opt/investbrain -chmod -R 775 /opt/investbrain/bootstrap/cache -msg_ok "Installed Investbrain" - -msg_info "Configuring Nginx" -cat </etc/nginx/sites-available/investbrain.conf -server { - listen 8000 default_server; - listen [::]:8000 default_server; - server_name _; - - root /opt/investbrain/public; - index index.php; - - client_max_body_size 50M; - charset utf-8; - - location = /favicon.ico { access_log off; log_not_found off; } - location = /robots.txt { access_log off; log_not_found off; } - - location / { - try_files \$uri \$uri/ /index.php?\$query_string; - } - - location ~ \.php\$ { - fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock; - fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; - include fastcgi_params; - fastcgi_hide_header X-Powered-By; - fastcgi_read_timeout 300; - } - - location ~ /\.(?!well-known).* { - deny all; - } - - error_log /var/log/nginx/investbrain_error.log; - access_log /var/log/nginx/investbrain_access.log; -} -EOF -ln -sf /etc/nginx/sites-available/investbrain.conf /etc/nginx/sites-enabled/ -rm -f /etc/nginx/sites-enabled/default -$STD systemctl reload nginx -msg_ok "Configured Nginx" - -msg_info "Setting up Supervisor" -cat </etc/supervisor/conf.d/investbrain.conf -[program:investbrain-queue] -process_name=%%(program_name)s_%%(process_num)02d -command=php /opt/investbrain/artisan queue:work --sleep=3 --tries=1 --memory=256 --timeout=3600 -user=www-data -autostart=true -autorestart=true -redirect_stderr=true -stdout_logfile=/opt/investbrain/storage/logs/queue.log -stdout_logfile_maxbytes=50MB -stdout_logfile_backups=10 -numprocs=1 -EOF -$STD supervisorctl reread -$STD supervisorctl update -$STD supervisorctl start all -msg_ok "Setup Supervisor" - -msg_info "Setting up Cron for Scheduler" -cat </etc/cron.d/investbrain-scheduler -* * * * * www-data php /opt/investbrain/artisan schedule:run >> /dev/null 2>&1 -EOF -chmod 644 /etc/cron.d/investbrain-scheduler -$STD systemctl restart cron -msg_ok "Setup Cron for Scheduler" - -motd_ssh -customize -cleanup_lxc From 76620bf8c7991a5665c9cdf357a1de5ac988f4b9 Mon Sep 17 00:00:00 2001 From: lengschder97 Date: Tue, 13 Jan 2026 10:58:20 +0100 Subject: [PATCH 329/378] Fixed arch in installer script --- ct/forgejo-runner.sh | 2 +- install/forgejo-runner-install.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh index 59145398c..1dfe36ebc 100644 --- a/ct/forgejo-runner.sh +++ b/ct/forgejo-runner.sh @@ -39,7 +39,7 @@ function update_script() { RELEASE=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//') msg_info "Updating Forgejo Runner to v${RELEASE}" - curl -fsSL "https://data.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-linux-amd64" -o forgejo-runner + curl -fsSL "https://code.forgejo.org/forgejo/runner/releases/download/v${RELEASE}/forgejo-runner-linux-amd64" -o forgejo-runner chmod +x /usr/local/bin/forgejo-runner msg_ok "Updated Forgejo Runner" diff --git a/install/forgejo-runner-install.sh b/install/forgejo-runner-install.sh index f6bea2f5f..367b2b76e 100644 --- a/install/forgejo-runner-install.sh +++ b/install/forgejo-runner-install.sh @@ -41,7 +41,7 @@ msg_ok "Enabled Podman socket" msg_info "Installing Forgejo Runner" RUNNER_VERSION=$(curl -fsSL https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | jq -r .name | sed 's/^v//') -curl -fsSL "https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-${ARCH}" -o /usr/local/bin/forgejo-runner +curl -fsSL "https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-amd64" -o /usr/local/bin/forgejo-runner chmod +x /usr/local/bin/forgejo-runner msg_ok "Installed Forgejo Runner" @@ -51,7 +51,7 @@ forgejo-runner register \ --instance "$FORGEJO_INSTANCE" \ --token "$FORGEJO_RUNNER_TOKEN" \ --name "$HOSTNAME" \ - --labels "linux-${ARCH}:docker://node:20-bookworm" \ + --labels "linux-amd64:docker://node:20-bookworm" \ --no-interactive msg_ok "Registered Forgejo Runner" From 5558262a9e9089944e988f546e11a3c8fba871d1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:06:24 +0100 Subject: [PATCH 330/378] fix paths --- ct/ampache.sh | 14 +++++++------- install/ampache-install.sh | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ct/ampache.sh b/ct/ampache.sh index 70e8ea9d0..b00467652 100644 --- a/ct/ampache.sh +++ b/ct/ampache.sh @@ -34,9 +34,9 @@ function update_script() { msg_info "Backing up Configuration" cp /opt/ampache/config/ampache.cfg.php /tmp/ampache.cfg.php.backup - cp /opt/ampache/rest/.htaccess /tmp/ampache_rest.htaccess.backup - cp /opt/ampache/play/.htaccess /tmp/ampache_play.htaccess.backup - cp /opt/ampache/channel/.htaccess /tmp/ampache_channel.htaccess.backup + cp /opt/ampache/public/rest/.htaccess /tmp/ampache_rest.htaccess.backup + cp /opt/ampache/public/play/.htaccess /tmp/ampache_play.htaccess.backup + cp /opt/ampache/public/channel/.htaccess /tmp/ampache_channel.htaccess.backup msg_ok "Backed up Configuration" msg_info "Backup Ampache Folder" @@ -48,10 +48,10 @@ function update_script() { msg_info "Restoring Configuration" cp /tmp/ampache.cfg.php.backup /opt/ampache/config/ampache.cfg.php - cp /tmp/ampache_rest.htaccess.backup /opt/ampache/rest/.htaccess - cp /tmp/ampache_play.htaccess.backup /opt/ampache/play/.htaccess - cp /tmp/ampache_channel.htaccess.backup /opt/ampache/channel/.htaccess - chmod 664 /opt/ampache/rest/.htaccess /opt/ampache/play/.htaccess /opt/ampache/channel/.htaccess + cp /tmp/ampache_rest.htaccess.backup /opt/ampache/public/rest/.htaccess + cp /tmp/ampache_play.htaccess.backup /opt/ampache/public/play/.htaccess + cp /tmp/ampache_channel.htaccess.backup /opt/ampache/public/channel/.htaccess + chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess /opt/ampache/public/channel/.htaccess chown -R www-data:www-data /opt/ampache rm -f /tmp/ampache*.backup msg_ok "Restored Configuration" diff --git a/install/ampache-install.sh b/install/ampache-install.sh index 265423f9f..f4faac159 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -36,11 +36,11 @@ fetch_and_deploy_gh_release "ampache" "ampache/ampache" "prebuild" "latest" "/op msg_info "Setup Ampache" rm -rf /var/www/html ln -s /opt/ampache/public /var/www/html -mv /opt/ampache/rest/.htaccess.dist /opt/ampache/rest/.htaccess -mv /opt/ampache/play/.htaccess.dist /opt/ampache/play/.htaccess -mv /opt/ampache/channel/.htaccess.dist /opt/ampache/channel/.htaccess +mv /opt/ampache/public/rest/.htaccess.dist /opt/ampache/public/rest/.htaccess +mv /opt/ampache/public/play/.htaccess.dist /opt/ampache/public/play/.htaccess +mv /opt/ampache/public/channel/.htaccess.dist /opt/ampache/public/channel/.htaccess cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php -chmod 664 /opt/ampache/rest/.htaccess /opt/ampache/play/.htaccess /opt/ampache/channel/.htaccess +chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess /opt/ampache/public/channel/.htaccess chown -R www-data:www-data /opt/ampache msg_ok "Set up Ampache" From fd71f94d87f19611662e5a3a0f266a146cea7dae Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:14:59 +0100 Subject: [PATCH 331/378] add qui --- ct/qui.sh | 58 +++++++++++++++++++++++++++++++++++ frontend/public/json/qui.json | 48 +++++++++++++++++++++++++++++ install/qui-install.sh | 42 +++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 ct/qui.sh create mode 100644 frontend/public/json/qui.json create mode 100644 install/qui-install.sh diff --git a/ct/qui.sh b/ct/qui.sh new file mode 100644 index 000000000..4984e1b7a --- /dev/null +++ b/ct/qui.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/autobrr/qui + +APP="Qui" +var_tags="${var_tags:-torrent}" +var_disk="${var_disk:-10}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +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/qui ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "Qui" "autobrr/qui"; then + msg_info "Stopping Service" + systemctl stop qui + msg_ok "Stopped Service" + + fetch_and_deploy_gh_release "qui" "autobrr/qui" "prebuild" "latest" "/tmp/qui" "qui_*_linux_x86_64.tar.gz" + + msg_info "Updating qui" + mv /tmp/qui/qui /usr/local/bin/qui + chmod +x /usr/local/bin/qui + rm -rf /tmp/qui + msg_ok "Updated qui" + + msg_info "Starting Service" + systemctl start qui + 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}:7476${CL}" diff --git a/frontend/public/json/qui.json b/frontend/public/json/qui.json new file mode 100644 index 000000000..bef257e27 --- /dev/null +++ b/frontend/public/json/qui.json @@ -0,0 +1,48 @@ +{ + "name": "Qui", + "slug": "qui", + "categories": [ + 7 + ], + "date_created": "2026-01-13", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 7476, + "documentation": "https://github.com/autobrr/qui", + "website": "https://github.com/autobrr/qui", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/qui.webp", + "config_path": "/root/.config/qui/config.toml", + "description": "Qui is a modern, self-hosted web interface for managing multiple qBittorrent instances with support for 10k+ torrents. It provides a clean and responsive interface for monitoring and controlling your qBittorrent downloads across multiple servers.", + "install_methods": [ + { + "type": "default", + "script": "ct/qui.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 10, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "After installation, create a user account by entering the container and running `qui create-user`.", + "type": "info" + }, + { + "text": "Configuration file is located at /root/.config/qui/config.toml", + "type": "info" + }, + { + "text": "Database is located at /root/.config/qui/qui.db", + "type": "info" + } + ] +} diff --git a/install/qui-install.sh b/install/qui-install.sh new file mode 100644 index 000000000..8e45ed000 --- /dev/null +++ b/install/qui-install.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/autobrr/qui + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "qui" "autobrr/qui" "prebuild" "latest" "/usr/local/bin" "qui_*_linux_x86_64.tar.gz" +chmod +x /usr/local/bin/qui +ln -sf /usr/local/bin/qui /usr/bin/qui +ln -sf /usr/local/bin/qui /opt/qui + +msg_info "Creating Qui Service" +cat </etc/systemd/system/qui.service +[Unit] +Description=Qui - qBittorrent Web UI +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/qui serve +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now qui +msg_ok "Created Qui Service" + +motd_ssh +customize +cleanup_lxc From 4f5e9d893f164c0ceca426c2eebcc6b9207f69a2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:22:29 +0100 Subject: [PATCH 332/378] add missing headers and add windows version of app headers --- .../scripts/generate-app-headers.ps1 | 90 +++++++++++++++++++ ct/headers/almalinux | 1 + ct/headers/alpine | 7 +- ct/headers/alpine-loki | 7 +- ct/headers/alpine-ntfy | 7 +- ct/headers/alpine-valkey | 1 + ct/headers/ampache | 1 + ct/headers/byparr | 1 + ct/headers/centos | 1 + ct/headers/cronmaster | 1 + ct/headers/debian | 7 +- ct/headers/devuan | 1 + ct/headers/docker | 6 -- ct/headers/ente | 7 +- ct/headers/fedora | 1 + ct/headers/forgejo-runner | 1 + ct/headers/freepbx | 7 +- ct/headers/frigate | 7 +- ct/headers/garmin-grafana | 7 +- ct/headers/gentoo | 1 + ct/headers/gitea-mirror | 6 -- ct/headers/gwn-manager | 1 + ct/headers/heimdall-dashboard | 1 + ct/headers/hoodik | 1 + ct/headers/joplin-server | 6 -- ct/headers/kitchenowl | 1 + ct/headers/kutt | 1 + ct/headers/linkwarden | 1 + ct/headers/loki | 6 +- ct/headers/manyfold | 1 + ct/headers/mealie | 6 -- ct/headers/metabase | 6 -- ct/headers/minthcm | 1 + ct/headers/netbird | 1 + ct/headers/nextexplorer | 1 + ct/headers/opencloud | 1 + ct/headers/openeuler | 1 + ct/headers/opensuse | 1 + ct/headers/papra | 1 + ct/headers/petio | 7 +- ct/headers/piler | 1 + ct/headers/pixelfed | 1 + ct/headers/plex | 1 + ct/headers/plex2 | 1 + ct/headers/postgresus | 1 + ct/headers/qdrant | 6 -- ct/headers/qui | 1 + ct/headers/rockylinux | 1 + ct/headers/romm | 7 +- ct/headers/rustypaste | 1 + ct/headers/rybbit | 7 +- ct/headers/snowshare | 6 -- ct/headers/tor-snowflake | 1 + ct/headers/tracearr | 7 +- ct/headers/transmission-openvpn | 7 +- ct/headers/ubuntu | 7 +- ct/headers/viseron | 6 -- ct/headers/wanderer | 6 -- ct/headers/web-check | 6 -- ct/headers/wishlist | 1 + ct/headers/yubal | 1 + tools/headers/_template | 1 + tools/headers/add-iptag | 7 +- tools/headers/code-server | 7 +- tools/headers/container-restore-from-backup | 7 +- tools/headers/copyparty | 7 +- tools/headers/core-restore-from-backup | 7 +- tools/headers/filebrowser | 7 +- tools/headers/filebrowser-quantum | 7 +- tools/headers/glances | 7 +- tools/headers/meilisearch-ui | 7 +- tools/headers/oci-deploy | 1 + tools/headers/phpmyadmin | 7 +- tools/headers/prx-add-ips | 7 +- tools/headers/pve-privilege-converter | 7 +- tools/headers/qbittorrent-exporter | 6 -- tools/headers/saltminion | 7 +- vm/headers/allstarlink-vm | 1 + vm/headers/archlinux-vm | 1 + vm/headers/debian-13-vm | 1 + vm/headers/debian-vm | 1 + vm/headers/debian-vm-test-helper | 1 + vm/headers/docker-vm | 7 +- vm/headers/docker-vm-debug | 1 + vm/headers/haos-vm | 1 + vm/headers/k3s-vm | 1 + vm/headers/mikrotik-routeros | 1 + vm/headers/nextcloud-vm | 1 + vm/headers/openwrt | 1 + vm/headers/opnsense-vm | 1 + vm/headers/owncloud-vm | 1 + vm/headers/pimox-haos-vm | 1 + vm/headers/ubuntu2204-vm | 1 + vm/headers/ubuntu2404-vm | 1 + vm/headers/ubuntu2410-vm | 7 +- vm/headers/umbrel-os-vm | 7 +- vm/headers/unifi-os-server-vm | 1 + 97 files changed, 175 insertions(+), 251 deletions(-) create mode 100644 .github/workflows/scripts/generate-app-headers.ps1 create mode 100644 ct/headers/almalinux create mode 100644 ct/headers/alpine-valkey create mode 100644 ct/headers/ampache create mode 100644 ct/headers/byparr create mode 100644 ct/headers/centos create mode 100644 ct/headers/cronmaster create mode 100644 ct/headers/devuan delete mode 100644 ct/headers/docker create mode 100644 ct/headers/fedora create mode 100644 ct/headers/forgejo-runner create mode 100644 ct/headers/gentoo delete mode 100644 ct/headers/gitea-mirror create mode 100644 ct/headers/gwn-manager create mode 100644 ct/headers/heimdall-dashboard create mode 100644 ct/headers/hoodik delete mode 100644 ct/headers/joplin-server create mode 100644 ct/headers/kitchenowl create mode 100644 ct/headers/kutt create mode 100644 ct/headers/linkwarden create mode 100644 ct/headers/manyfold delete mode 100644 ct/headers/mealie delete mode 100644 ct/headers/metabase create mode 100644 ct/headers/minthcm create mode 100644 ct/headers/netbird create mode 100644 ct/headers/nextexplorer create mode 100644 ct/headers/opencloud create mode 100644 ct/headers/openeuler create mode 100644 ct/headers/opensuse create mode 100644 ct/headers/papra create mode 100644 ct/headers/piler create mode 100644 ct/headers/pixelfed create mode 100644 ct/headers/plex create mode 100644 ct/headers/plex2 create mode 100644 ct/headers/postgresus delete mode 100644 ct/headers/qdrant create mode 100644 ct/headers/qui create mode 100644 ct/headers/rockylinux create mode 100644 ct/headers/rustypaste delete mode 100644 ct/headers/snowshare create mode 100644 ct/headers/tor-snowflake delete mode 100644 ct/headers/viseron delete mode 100644 ct/headers/wanderer delete mode 100644 ct/headers/web-check create mode 100644 ct/headers/wishlist create mode 100644 ct/headers/yubal create mode 100644 tools/headers/_template create mode 100644 tools/headers/oci-deploy delete mode 100644 tools/headers/qbittorrent-exporter create mode 100644 vm/headers/allstarlink-vm create mode 100644 vm/headers/archlinux-vm create mode 100644 vm/headers/debian-13-vm create mode 100644 vm/headers/debian-vm create mode 100644 vm/headers/debian-vm-test-helper create mode 100644 vm/headers/docker-vm-debug create mode 100644 vm/headers/haos-vm create mode 100644 vm/headers/k3s-vm create mode 100644 vm/headers/mikrotik-routeros create mode 100644 vm/headers/nextcloud-vm create mode 100644 vm/headers/openwrt create mode 100644 vm/headers/opnsense-vm create mode 100644 vm/headers/owncloud-vm create mode 100644 vm/headers/pimox-haos-vm create mode 100644 vm/headers/ubuntu2204-vm create mode 100644 vm/headers/ubuntu2404-vm create mode 100644 vm/headers/unifi-os-server-vm diff --git a/.github/workflows/scripts/generate-app-headers.ps1 b/.github/workflows/scripts/generate-app-headers.ps1 new file mode 100644 index 000000000..be2838d19 --- /dev/null +++ b/.github/workflows/scripts/generate-app-headers.ps1 @@ -0,0 +1,90 @@ +#!/usr/bin/env pwsh + +# Function for generating Figlet headers +function Generate-Headers { + param ( + [string]$BaseDir, + [string]$TargetSubdir, + [string]$SearchPattern + ) + + $HeadersDir = Join-Path $BaseDir $TargetSubdir + + # Create headers directory if it doesn't exist + if (-not (Test-Path $HeadersDir)) { + New-Item -ItemType Directory -Path $HeadersDir -Force | Out-Null + } + + # Remove existing header files + Get-ChildItem -Path $HeadersDir -File | Remove-Item -Force + + # Determine search scope (recursive or not) + if ($SearchPattern -eq "**") { + $FileList = Get-ChildItem -Path $BaseDir -Filter "*.sh" -Recurse -File + } + else { + $FileList = Get-ChildItem -Path $BaseDir -Filter "*.sh" -File + } + + foreach ($Script in $FileList) { + # Extract APP name from script + $Content = Get-Content $Script.FullName -Raw + if ($Content -match 'APP="([^"]+)"') { + $AppName = $Matches[1] + + $OutputFile = Join-Path $HeadersDir $Script.BaseName + + # Generate figlet output + try { + $FigletOutput = & figlet -w 500 -f slant $AppName 2>&1 + + if ($LASTEXITCODE -eq 0 -and $FigletOutput) { + $FigletOutput | Out-File -FilePath $OutputFile -Encoding utf8 -NoNewline + Write-Host "Generated: $OutputFile" -ForegroundColor Green + } + else { + Write-Host "Figlet failed for $AppName in $($Script.Name)" -ForegroundColor Yellow + } + } + catch { + Write-Host "Error running figlet for $AppName : $_" -ForegroundColor Red + } + } + else { + Write-Host "No APP name found in $($Script.Name), skipping." -ForegroundColor Gray + } + } +} + +# Check if figlet is available +try { + $null = Get-Command figlet -ErrorAction Stop +} +catch { + Write-Host "Error: figlet is not installed or not in PATH" -ForegroundColor Red + Write-Host "`nInstallation options:" -ForegroundColor Yellow + Write-Host "1. Using Scoop (recommended):" -ForegroundColor Cyan + Write-Host " scoop install figlet" -ForegroundColor White + Write-Host "`n2. Manual download:" -ForegroundColor Cyan + Write-Host " Download from: https://github.com/cmatsuoka/figlet/releases" -ForegroundColor White + Write-Host " Extract and add to PATH" -ForegroundColor White + Write-Host "`n3. Using WSL:" -ForegroundColor Cyan + Write-Host " Run the bash version: bash generate-app-headers.sh" -ForegroundColor White + exit 1 +} + +# Change to script directory +$ScriptDir = Split-Path -Parent $PSCommandPath +$RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $ScriptDir)) +Set-Location $RepoRoot + +Write-Host "Processing ct/ directory..." -ForegroundColor Cyan +Generate-Headers -BaseDir ".\ct" -TargetSubdir "headers" -SearchPattern "*" + +Write-Host "`nProcessing tools/ directory..." -ForegroundColor Cyan +Generate-Headers -BaseDir ".\tools" -TargetSubdir "headers" -SearchPattern "**" + +Write-Host "`nProcessing vm/ directory..." -ForegroundColor Cyan +Generate-Headers -BaseDir ".\vm" -TargetSubdir "headers" -SearchPattern "*" + +Write-Host "`nCompleted processing all sections." -ForegroundColor Green diff --git a/ct/headers/almalinux b/ct/headers/almalinux new file mode 100644 index 000000000..4bcef0f78 --- /dev/null +++ b/ct/headers/almalinux @@ -0,0 +1 @@ + ___ __ __ _ / | / /___ ___ ____ _/ / (_)___ __ ___ __ / /| | / / __ `__ \/ __ `/ / / / __ \/ / / / |/_/ / ___ |/ / / / / / / /_/ / /___/ / / / / /_/ /> < /_/ |_/_/_/ /_/ /_/\__,_/_____/_/_/ /_/\__,_/_/|_| \ No newline at end of file diff --git a/ct/headers/alpine b/ct/headers/alpine index ecf1e0554..0fab98f18 100644 --- a/ct/headers/alpine +++ b/ct/headers/alpine @@ -1,6 +1 @@ - ___ __ _ - / | / /___ (_)___ ___ - / /| | / / __ \/ / __ \/ _ \ - / ___ |/ / /_/ / / / / / __/ -/_/ |_/_/ .___/_/_/ /_/\___/ - /_/ + ___ __ _ / | / /___ (_)___ ___ / /| | / / __ \/ / __ \/ _ \ / ___ |/ / /_/ / / / / / __//_/ |_/_/ .___/_/_/ /_/\___/ /_/ \ No newline at end of file diff --git a/ct/headers/alpine-loki b/ct/headers/alpine-loki index ca154285e..7d026bb23 100644 --- a/ct/headers/alpine-loki +++ b/ct/headers/alpine-loki @@ -1,6 +1 @@ - ___ __ _ __ __ _ - / | / /___ (_)___ ___ / / ____ / /__(_) - / /| | / / __ \/ / __ \/ _ \______/ / / __ \/ //_/ / - / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / ,< / / -/_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/_/|_/_/ - /_/ + ___ __ _ __ __ _ / | / /___ (_)___ ___ / / ____ / /__(_) / /| | / / __ \/ / __ \/ _ \______/ / / __ \/ //_/ / / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / ,< / / /_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/_/|_/_/ /_/ \ No newline at end of file diff --git a/ct/headers/alpine-ntfy b/ct/headers/alpine-ntfy index bc4164342..71e205d5d 100644 --- a/ct/headers/alpine-ntfy +++ b/ct/headers/alpine-ntfy @@ -1,6 +1 @@ - ___ __ _ __ ____ - / | / /___ (_)___ ___ ____ / /_/ __/_ __ - / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / - / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / -/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / - /_/ /____/ + ___ __ _ __ ____ / | / /___ (_)___ ___ ____ / /_/ __/_ __ / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / /_/ /____/ \ No newline at end of file diff --git a/ct/headers/alpine-valkey b/ct/headers/alpine-valkey new file mode 100644 index 000000000..85053df61 --- /dev/null +++ b/ct/headers/alpine-valkey @@ -0,0 +1 @@ + ___ __ _ _ __ ____ / | / /___ (_)___ ___ | | / /___ _/ / /_____ __ __ / /| | / / __ \/ / __ \/ _ \_____| | / / __ `/ / //_/ _ \/ / / / / ___ |/ / /_/ / / / / / __/_____/ |/ / /_/ / / ,< / __/ /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ |___/\__,_/_/_/|_|\___/\__, / /_/ /____/ \ No newline at end of file diff --git a/ct/headers/ampache b/ct/headers/ampache new file mode 100644 index 000000000..599bfac10 --- /dev/null +++ b/ct/headers/ampache @@ -0,0 +1 @@ + ___ __ / | ____ ___ ____ ____ ______/ /_ ___ / /| | / __ `__ \/ __ \/ __ `/ ___/ __ \/ _ \ / ___ |/ / / / / / /_/ / /_/ / /__/ / / / __//_/ |_/_/ /_/ /_/ .___/\__,_/\___/_/ /_/\___/ /_/ \ No newline at end of file diff --git a/ct/headers/byparr b/ct/headers/byparr new file mode 100644 index 000000000..1ed512d4f --- /dev/null +++ b/ct/headers/byparr @@ -0,0 +1 @@ + ____ / __ )__ ______ ____ ___________ / __ / / / / __ \/ __ `/ ___/ ___/ / /_/ / /_/ / /_/ / /_/ / / / / /_____/\__, / .___/\__,_/_/ /_/ /____/_/ \ No newline at end of file diff --git a/ct/headers/centos b/ct/headers/centos new file mode 100644 index 000000000..4c1fef24a --- /dev/null +++ b/ct/headers/centos @@ -0,0 +1 @@ + ______ __ ____ _____ _____ __ / ____/__ ____ / /_/ __ \/ ___/ / ___// /_________ ____ _____ ___ / / / _ \/ __ \/ __/ / / /\__ \ \__ \/ __/ ___/ _ \/ __ `/ __ `__ \/ /___/ __/ / / / /_/ /_/ /___/ / ___/ / /_/ / / __/ /_/ / / / / / /\____/\___/_/ /_/\__/\____//____/ /____/\__/_/ \___/\__,_/_/ /_/ /_/ \ No newline at end of file diff --git a/ct/headers/cronmaster b/ct/headers/cronmaster new file mode 100644 index 000000000..e05167966 --- /dev/null +++ b/ct/headers/cronmaster @@ -0,0 +1 @@ + __________ ____ _ ____ ______ _____________________ / ____/ __ \/ __ \/ | / / |/ / | / ___/_ __/ ____/ __ \ / / / /_/ / / / / |/ / /|_/ / /| | \__ \ / / / __/ / /_/ // /___/ _, _/ /_/ / /| / / / / ___ |___/ // / / /___/ _, _/ \____/_/ |_|\____/_/ |_/_/ /_/_/ |_/____//_/ /_____/_/ |_| \ No newline at end of file diff --git a/ct/headers/debian b/ct/headers/debian index a6f474d46..3fd6acc74 100644 --- a/ct/headers/debian +++ b/ct/headers/debian @@ -1,6 +1 @@ - ____ __ _ - / __ \___ / /_ (_)___ _____ - / / / / _ \/ __ \/ / __ `/ __ \ - / /_/ / __/ /_/ / / /_/ / / / / -/_____/\___/_.___/_/\__,_/_/ /_/ - + ____ __ _ / __ \___ / /_ (_)___ _____ / / / / _ \/ __ \/ / __ `/ __ \ / /_/ / __/ /_/ / / /_/ / / / //_____/\___/_.___/_/\__,_/_/ /_/ \ No newline at end of file diff --git a/ct/headers/devuan b/ct/headers/devuan new file mode 100644 index 000000000..a38d4fd8e --- /dev/null +++ b/ct/headers/devuan @@ -0,0 +1 @@ + ____ / __ \___ _ ____ ______ _____ / / / / _ \ | / / / / / __ `/ __ \ / /_/ / __/ |/ / /_/ / /_/ / / / //_____/\___/|___/\__,_/\__,_/_/ /_/ \ No newline at end of file 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 @@ - ____ __ - / __ \____ _____/ /_____ _____ - / / / / __ \/ ___/ //_/ _ \/ ___/ - / /_/ / /_/ / /__/ ,< / __/ / -/_____/\____/\___/_/|_|\___/_/ - diff --git a/ct/headers/ente b/ct/headers/ente index f700a1f53..46af9e81f 100644 --- a/ct/headers/ente +++ b/ct/headers/ente @@ -1,6 +1 @@ - ______ __ - / ____/___ / /____ - / __/ / __ \/ __/ _ \ - / /___/ / / / /_/ __/ -/_____/_/ /_/\__/\___/ - + ______ __ / ____/___ / /____ / __/ / __ \/ __/ _ \ / /___/ / / / /_/ __//_____/_/ /_/\__/\___/ \ No newline at end of file diff --git a/ct/headers/fedora b/ct/headers/fedora new file mode 100644 index 000000000..8ddeae068 --- /dev/null +++ b/ct/headers/fedora @@ -0,0 +1 @@ + ______ __ / ____/__ ____/ /___ _________ _ / /_ / _ \/ __ / __ \/ ___/ __ `/ / __/ / __/ /_/ / /_/ / / / /_/ / /_/ \___/\__,_/\____/_/ \__,_/ \ No newline at end of file diff --git a/ct/headers/forgejo-runner b/ct/headers/forgejo-runner new file mode 100644 index 000000000..7492d0bf3 --- /dev/null +++ b/ct/headers/forgejo-runner @@ -0,0 +1 @@ + ______ _ ____ / ____/___ _________ ____ (_)___ / __ \__ ______ ____ ___ _____ / /_ / __ \/ ___/ __ `/ _ \ / / __ \ / /_/ / / / / __ \/ __ \/ _ \/ ___/ / __/ / /_/ / / / /_/ / __/ / / /_/ / / _, _/ /_/ / / / / / / / __/ / /_/ \____/_/ \__, /\___/_/ /\____/ /_/ |_|\__,_/_/ /_/_/ /_/\___/_/ /____/ /___/ \ No newline at end of file diff --git a/ct/headers/freepbx b/ct/headers/freepbx index 25541c2ed..c79e3ace8 100644 --- a/ct/headers/freepbx +++ b/ct/headers/freepbx @@ -1,6 +1 @@ - ______ ____ ____ _ __ - / ____/_______ ___ / __ \/ __ ) |/ / - / /_ / ___/ _ \/ _ \/ /_/ / __ | / - / __/ / / / __/ __/ ____/ /_/ / | -/_/ /_/ \___/\___/_/ /_____/_/|_| - + ______ ____ ____ _ __ / ____/_______ ___ / __ \/ __ ) |/ / / /_ / ___/ _ \/ _ \/ /_/ / __ | / / __/ / / / __/ __/ ____/ /_/ / | /_/ /_/ \___/\___/_/ /_____/_/|_| \ No newline at end of file diff --git a/ct/headers/frigate b/ct/headers/frigate index 94ffd3a7d..f7516cd39 100644 --- a/ct/headers/frigate +++ b/ct/headers/frigate @@ -1,6 +1 @@ - ______ _ __ - / ____/____(_)___ _____ _/ /____ - / /_ / ___/ / __ `/ __ `/ __/ _ \ - / __/ / / / / /_/ / /_/ / /_/ __/ -/_/ /_/ /_/\__, /\__,_/\__/\___/ - /____/ + ______ _ __ / ____/____(_)___ _____ _/ /____ / /_ / ___/ / __ `/ __ `/ __/ _ \ / __/ / / / / /_/ / /_/ / /_/ __//_/ /_/ /_/\__, /\__,_/\__/\___/ /____/ \ No newline at end of file diff --git a/ct/headers/garmin-grafana b/ct/headers/garmin-grafana index 5509b8143..91331f066 100644 --- a/ct/headers/garmin-grafana +++ b/ct/headers/garmin-grafana @@ -1,6 +1 @@ - _ ____ - ____ _____ __________ ___ (_)___ ____ __________ _/ __/___ _____ ____ _ - / __ `/ __ `/ ___/ __ `__ \/ / __ \______/ __ `/ ___/ __ `/ /_/ __ `/ __ \/ __ `/ - / /_/ / /_/ / / / / / / / / / / / /_____/ /_/ / / / /_/ / __/ /_/ / / / / /_/ / - \__, /\__,_/_/ /_/ /_/ /_/_/_/ /_/ \__, /_/ \__,_/_/ \__,_/_/ /_/\__,_/ -/____/ /____/ + _ ____ ____ _____ __________ ___ (_)___ ____ __________ _/ __/___ _____ ____ _ / __ `/ __ `/ ___/ __ `__ \/ / __ \______/ __ `/ ___/ __ `/ /_/ __ `/ __ \/ __ `/ / /_/ / /_/ / / / / / / / / / / / /_____/ /_/ / / / /_/ / __/ /_/ / / / / /_/ / \__, /\__,_/_/ /_/ /_/ /_/_/_/ /_/ \__, /_/ \__,_/_/ \__,_/_/ /_/\__,_/ /____/ /____/ \ No newline at end of file diff --git a/ct/headers/gentoo b/ct/headers/gentoo new file mode 100644 index 000000000..31db9fc79 --- /dev/null +++ b/ct/headers/gentoo @@ -0,0 +1 @@ + ______ __ / ____/__ ____ / /_____ ____ / / __/ _ \/ __ \/ __/ __ \/ __ \/ /_/ / __/ / / / /_/ /_/ / /_/ /\____/\___/_/ /_/\__/\____/\____/ \ No newline at end of file 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/gwn-manager b/ct/headers/gwn-manager new file mode 100644 index 000000000..ea785f891 --- /dev/null +++ b/ct/headers/gwn-manager @@ -0,0 +1 @@ + _______ ___ __ __ ___ / ____/ | / / | / / / |/ /___ _____ ____ _____ ____ _____ / / __ | | /| / / |/ /_____/ /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___// /_/ / | |/ |/ / /| /_____/ / / / /_/ / / / / /_/ / /_/ / __/ / \____/ |__/|__/_/ |_/ /_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ /____/ \ No newline at end of file diff --git a/ct/headers/heimdall-dashboard b/ct/headers/heimdall-dashboard new file mode 100644 index 000000000..67b6e60f1 --- /dev/null +++ b/ct/headers/heimdall-dashboard @@ -0,0 +1 @@ + __ __ _ __ ____ ____ __ __ __ / / / /__ (_)___ ___ ____/ /___ _/ / / / __ \____ ______/ /_ / /_ ____ ____ __________/ / / /_/ / _ \/ / __ `__ \/ __ / __ `/ / /_____/ / / / __ `/ ___/ __ \/ __ \/ __ \/ __ `/ ___/ __ / / __ / __/ / / / / / / /_/ / /_/ / / /_____/ /_/ / /_/ (__ ) / / / /_/ / /_/ / /_/ / / / /_/ / /_/ /_/\___/_/_/ /_/ /_/\__,_/\__,_/_/_/ /_____/\__,_/____/_/ /_/_.___/\____/\__,_/_/ \__,_/ \ No newline at end of file diff --git a/ct/headers/hoodik b/ct/headers/hoodik new file mode 100644 index 000000000..4c2b3f211 --- /dev/null +++ b/ct/headers/hoodik @@ -0,0 +1 @@ + __ __ ___ __ / / / /___ ____ ____/ (_) /__ / /_/ / __ \/ __ \/ __ / / //_/ / __ / /_/ / /_/ / /_/ / / ,< /_/ /_/\____/\____/\__,_/_/_/|_| \ No newline at end of file diff --git a/ct/headers/joplin-server b/ct/headers/joplin-server deleted file mode 100644 index 68b32c8fa..000000000 --- a/ct/headers/joplin-server +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ _____ - / /___ ____ / (_)___ / ___/___ ______ _____ _____ - __ / / __ \/ __ \/ / / __ \______\__ \/ _ \/ ___/ | / / _ \/ ___/ -/ /_/ / /_/ / /_/ / / / / / /_____/__/ / __/ / | |/ / __/ / -\____/\____/ .___/_/_/_/ /_/ /____/\___/_/ |___/\___/_/ - /_/ diff --git a/ct/headers/kitchenowl b/ct/headers/kitchenowl new file mode 100644 index 000000000..88e302ede --- /dev/null +++ b/ct/headers/kitchenowl @@ -0,0 +1 @@ + __ __ _ __ __ ____ __ / //_/(_) /______/ /_ ___ ____ / __ \_ __/ / / ,< / / __/ ___/ __ \/ _ \/ __ \/ / / / | /| / / / / /| |/ / /_/ /__/ / / / __/ / / / /_/ /| |/ |/ / / /_/ |_/_/\__/\___/_/ /_/\___/_/ /_/\____/ |__/|__/_/ \ No newline at end of file diff --git a/ct/headers/kutt b/ct/headers/kutt new file mode 100644 index 000000000..81c70e96d --- /dev/null +++ b/ct/headers/kutt @@ -0,0 +1 @@ + __ __ __ __ / //_/_ __/ /_/ /_ / ,< / / / / __/ __/ / /| / /_/ / /_/ /_ /_/ |_\__,_/\__/\__/ \ No newline at end of file diff --git a/ct/headers/linkwarden b/ct/headers/linkwarden new file mode 100644 index 000000000..7f8d0fc99 --- /dev/null +++ b/ct/headers/linkwarden @@ -0,0 +1 @@ + __ _ __ __ / / (_)___ / /___ ______ __________/ /__ ____ / / / / __ \/ //_/ | /| / / __ `/ ___/ __ / _ \/ __ \ / /___/ / / / / ,< | |/ |/ / /_/ / / / /_/ / __/ / / //_____/_/_/ /_/_/|_| |__/|__/\__,_/_/ \__,_/\___/_/ /_/ \ No newline at end of file diff --git a/ct/headers/loki b/ct/headers/loki index 38b3c722b..81516e5db 100644 --- a/ct/headers/loki +++ b/ct/headers/loki @@ -1,5 +1 @@ - __ __ _ - / / ____ / /__(_) - / / / __ \/ //_/ / - / /___/ /_/ / ,< / / -/_____/\____/_/|_/_/ + __ __ _ / / ____ / /__(_) / / / __ \/ //_/ / / /___/ /_/ / ,< / / /_____/\____/_/|_/_/ \ No newline at end of file diff --git a/ct/headers/manyfold b/ct/headers/manyfold new file mode 100644 index 000000000..15e6a8125 --- /dev/null +++ b/ct/headers/manyfold @@ -0,0 +1 @@ + __ ___ ____ __ __ / |/ /___ _____ __ __/ __/___ / /___/ / / /|_/ / __ `/ __ \/ / / / /_/ __ \/ / __ / / / / / /_/ / / / / /_/ / __/ /_/ / / /_/ / /_/ /_/\__,_/_/ /_/\__, /_/ \____/_/\__,_/ /____/ \ No newline at end of file diff --git a/ct/headers/mealie b/ct/headers/mealie deleted file mode 100644 index a5d36d54c..000000000 --- a/ct/headers/mealie +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ ___ - / |/ /__ ____ _/ (_)__ - / /|_/ / _ \/ __ `/ / / _ \ - / / / / __/ /_/ / / / __/ -/_/ /_/\___/\__,_/_/_/\___/ - diff --git a/ct/headers/metabase b/ct/headers/metabase deleted file mode 100644 index a98c3c699..000000000 --- a/ct/headers/metabase +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ __ __ - / |/ /__ / /_____ _/ /_ ____ _________ - / /|_/ / _ \/ __/ __ `/ __ \/ __ `/ ___/ _ \ - / / / / __/ /_/ /_/ / /_/ / /_/ (__ ) __/ -/_/ /_/\___/\__/\__,_/_.___/\__,_/____/\___/ - diff --git a/ct/headers/minthcm b/ct/headers/minthcm new file mode 100644 index 000000000..36b60eb15 --- /dev/null +++ b/ct/headers/minthcm @@ -0,0 +1 @@ + __ ____ __ __ __________ ___ / |/ (_)___ / /_/ / / / ____/ |/ / / /|_/ / / __ \/ __/ /_/ / / / /|_/ / / / / / / / / / /_/ __ / /___/ / / / /_/ /_/_/_/ /_/\__/_/ /_/\____/_/ /_/ \ No newline at end of file diff --git a/ct/headers/netbird b/ct/headers/netbird new file mode 100644 index 000000000..b5fb3e7a0 --- /dev/null +++ b/ct/headers/netbird @@ -0,0 +1 @@ + _ __ __ ____ _ __ / | / /__ / /_/ __ )(_)________/ / / |/ / _ \/ __/ __ / / ___/ __ / / /| / __/ /_/ /_/ / / / / /_/ / /_/ |_/\___/\__/_____/_/_/ \__,_/ \ No newline at end of file diff --git a/ct/headers/nextexplorer b/ct/headers/nextexplorer new file mode 100644 index 000000000..877919951 --- /dev/null +++ b/ct/headers/nextexplorer @@ -0,0 +1 @@ + __ ______ __ ____ ___ _ __/ /_/ ____/ ______ / /___ ________ _____ / __ \/ _ \| |/_/ __/ __/ | |/_/ __ \/ / __ \/ ___/ _ \/ ___/ / / / / __/> < /_/ /_/\___/_/|_| \ No newline at end of file diff --git a/ct/headers/plex2 b/ct/headers/plex2 new file mode 100644 index 000000000..575946fa6 --- /dev/null +++ b/ct/headers/plex2 @@ -0,0 +1 @@ + ____ __ ___ / __ \/ /__ _ _|__ \ / /_/ / / _ \| |/_/_/ / / ____/ / __/> < /_/ |_|\____/\___/_/|_|\__, / /_____/_/_/ /_/\__,_/_/|_| /____/ \ No newline at end of file diff --git a/ct/headers/romm b/ct/headers/romm index 7f214d48e..2e768af82 100644 --- a/ct/headers/romm +++ b/ct/headers/romm @@ -1,6 +1 @@ - ____ __ ___ - / __ \____ ____ ___ / |/ / - / /_/ / __ \/ __ `__ \/ /|_/ / - / _, _/ /_/ / / / / / / / / / -/_/ |_|\____/_/ /_/ /_/_/ /_/ - + ____ __ ___ / __ \____ ____ ___ / |/ / / /_/ / __ \/ __ `__ \/ /|_/ / / _, _/ /_/ / / / / / / / / / /_/ |_|\____/_/ /_/ /_/_/ /_/ \ No newline at end of file diff --git a/ct/headers/rustypaste b/ct/headers/rustypaste new file mode 100644 index 000000000..8b226b558 --- /dev/null +++ b/ct/headers/rustypaste @@ -0,0 +1 @@ + __ __ _______ _______/ /___ ______ ____ ______/ /____ / ___/ / / / ___/ __/ / / / __ \/ __ `/ ___/ __/ _ \ / / / /_/ (__ ) /_/ /_/ / /_/ / /_/ (__ ) /_/ __//_/ \__,_/____/\__/\__, / .___/\__,_/____/\__/\___/ /____/_/ \ No newline at end of file diff --git a/ct/headers/rybbit b/ct/headers/rybbit index f4726262c..eaedef356 100644 --- a/ct/headers/rybbit +++ b/ct/headers/rybbit @@ -1,6 +1 @@ - ____ __ __ _ __ - / __ \__ __/ /_ / /_ (_) /_ - / /_/ / / / / __ \/ __ \/ / __/ - / _, _/ /_/ / /_/ / /_/ / / /_ -/_/ |_|\__, /_.___/_.___/_/\__/ - /____/ + ____ __ __ _ __ / __ \__ __/ /_ / /_ (_) /_ / /_/ / / / / __ \/ __ \/ / __/ / _, _/ /_/ / /_/ / /_/ / / /_ /_/ |_|\__, /_.___/_.___/_/\__/ /____/ \ No newline at end of file diff --git a/ct/headers/snowshare b/ct/headers/snowshare deleted file mode 100644 index 160614e0c..000000000 --- a/ct/headers/snowshare +++ /dev/null @@ -1,6 +0,0 @@ - _____ _____ __ - / ___/____ ____ _ __/ ___// /_ ____ _________ - \__ \/ __ \/ __ \ | /| / /\__ \/ __ \/ __ `/ ___/ _ \ - ___/ / / / / /_/ / |/ |/ /___/ / / / / /_/ / / / __/ -/____/_/ /_/\____/|__/|__//____/_/ /_/\__,_/_/ \___/ - diff --git a/ct/headers/tor-snowflake b/ct/headers/tor-snowflake new file mode 100644 index 000000000..fdcb7e31a --- /dev/null +++ b/ct/headers/tor-snowflake @@ -0,0 +1 @@ + __ ______ __ / /_____ _____ _________ ____ _ __/ __/ /___ _/ /_____ / __/ __ \/ ___/_____/ ___/ __ \/ __ \ | /| / / /_/ / __ `/ //_/ _ \/ /_/ /_/ / / /_____(__ ) / / / /_/ / |/ |/ / __/ / /_/ / ,< / __/\__/\____/_/ /____/_/ /_/\____/|__/|__/_/ /_/\__,_/_/|_|\___/ \ No newline at end of file diff --git a/ct/headers/tracearr b/ct/headers/tracearr index cec64cede..702d16778 100644 --- a/ct/headers/tracearr +++ b/ct/headers/tracearr @@ -1,6 +1 @@ - ______ - /_ __/________ _________ ____ ___________ - / / / ___/ __ `/ ___/ _ \/ __ `/ ___/ ___/ - / / / / / /_/ / /__/ __/ /_/ / / / / -/_/ /_/ \__,_/\___/\___/\__,_/_/ /_/ - + ______ /_ __/________ _________ ____ ___________ / / / ___/ __ `/ ___/ _ \/ __ `/ ___/ ___/ / / / / / /_/ / /__/ __/ /_/ / / / / /_/ /_/ \__,_/\___/\___/\__,_/_/ /_/ \ No newline at end of file diff --git a/ct/headers/transmission-openvpn b/ct/headers/transmission-openvpn index 3c6830b05..636b29841 100644 --- a/ct/headers/transmission-openvpn +++ b/ct/headers/transmission-openvpn @@ -1,6 +1 @@ - __ _ _ - / /__________ _____ _________ ___ (_)_________(_)___ ____ ____ ____ ___ ____ _ ______ ____ - / __/ ___/ __ `/ __ \/ ___/ __ `__ \/ / ___/ ___/ / __ \/ __ \______/ __ \/ __ \/ _ \/ __ \ | / / __ \/ __ \ -/ /_/ / / /_/ / / / (__ ) / / / / / (__ |__ ) / /_/ / / / /_____/ /_/ / /_/ / __/ / / / |/ / /_/ / / / / -\__/_/ \__,_/_/ /_/____/_/ /_/ /_/_/____/____/_/\____/_/ /_/ \____/ .___/\___/_/ /_/|___/ .___/_/ /_/ - /_/ /_/ + __ _ _ / /__________ _____ _________ ___ (_)_________(_)___ ____ ____ ____ ___ ____ _ ______ ____ / __/ ___/ __ `/ __ \/ ___/ __ `__ \/ / ___/ ___/ / __ \/ __ \______/ __ \/ __ \/ _ \/ __ \ | / / __ \/ __ \/ /_/ / / /_/ / / / (__ ) / / / / / (__ |__ ) / /_/ / / / /_____/ /_/ / /_/ / __/ / / / |/ / /_/ / / / /\__/_/ \__,_/_/ /_/____/_/ /_/ /_/_/____/____/_/\____/_/ /_/ \____/ .___/\___/_/ /_/|___/ .___/_/ /_/ /_/ /_/ \ No newline at end of file diff --git a/ct/headers/ubuntu b/ct/headers/ubuntu index a4b0eebf9..8a3923c1b 100644 --- a/ct/headers/ubuntu +++ b/ct/headers/ubuntu @@ -1,6 +1 @@ - __ ____ __ - / / / / /_ __ ______ / /___ __ - / / / / __ \/ / / / __ \/ __/ / / / -/ /_/ / /_/ / /_/ / / / / /_/ /_/ / -\____/_.___/\__,_/_/ /_/\__/\__,_/ - + __ ____ __ / / / / /_ __ ______ / /___ __ / / / / __ \/ / / / __ \/ __/ / / // /_/ / /_/ / /_/ / / / / /_/ /_/ / \____/_.___/\__,_/_/ /_/\__/\__,_/ \ No newline at end of file diff --git a/ct/headers/viseron b/ct/headers/viseron deleted file mode 100644 index 8cb594daf..000000000 --- a/ct/headers/viseron +++ /dev/null @@ -1,6 +0,0 @@ - _ ___ -| | / (_)_______ _________ ____ -| | / / / ___/ _ \/ ___/ __ \/ __ \ -| |/ / (__ ) __/ / / /_/ / / / / -|___/_/____/\___/_/ \____/_/ /_/ - diff --git a/ct/headers/wanderer b/ct/headers/wanderer deleted file mode 100644 index 80a6ccbf2..000000000 --- a/ct/headers/wanderer +++ /dev/null @@ -1,6 +0,0 @@ - _ __ __ -| | / /___ _____ ____/ /__ ________ _____ -| | /| / / __ `/ __ \/ __ / _ \/ ___/ _ \/ ___/ -| |/ |/ / /_/ / / / / /_/ / __/ / / __/ / -|__/|__/\__,_/_/ /_/\__,_/\___/_/ \___/_/ - diff --git a/ct/headers/web-check b/ct/headers/web-check deleted file mode 100644 index 371062cb9..000000000 --- a/ct/headers/web-check +++ /dev/null @@ -1,6 +0,0 @@ - __ __ __ - _ _____ / /_ _____/ /_ ___ _____/ /__ -| | /| / / _ \/ __ \______/ ___/ __ \/ _ \/ ___/ //_/ -| |/ |/ / __/ /_/ /_____/ /__/ / / / __/ /__/ ,< -|__/|__/\___/_.___/ \___/_/ /_/\___/\___/_/|_| - diff --git a/ct/headers/wishlist b/ct/headers/wishlist new file mode 100644 index 000000000..e1f94fc97 --- /dev/null +++ b/ct/headers/wishlist @@ -0,0 +1 @@ + _ ___ __ ___ __ | | / (_)____/ /_ / (_)____/ /_| | /| / / / ___/ __ \/ / / ___/ __/| |/ |/ / (__ ) / / / / (__ ) /_ |__/|__/_/____/_/ /_/_/_/____/\__/ \ No newline at end of file diff --git a/ct/headers/yubal b/ct/headers/yubal new file mode 100644 index 000000000..50cc22340 --- /dev/null +++ b/ct/headers/yubal @@ -0,0 +1 @@ +__ __ __ __\ \/ /_ __/ /_ ____ _/ / \ / / / / __ \/ __ `/ / / / /_/ / /_/ / /_/ / / /_/\__,_/_.___/\__,_/_/ \ No newline at end of file diff --git a/tools/headers/_template b/tools/headers/_template new file mode 100644 index 000000000..e8a6042e2 --- /dev/null +++ b/tools/headers/_template @@ -0,0 +1 @@ + __ ___ ___ __ __ / |/ /_ __/ | ____/ /___/ /___ ____ / /|_/ / / / / /| |/ __ / __ / __ \/ __ \ / / / / /_/ / ___ / /_/ / /_/ / /_/ / / / //_/ /_/\__, /_/ |_\__,_/\__,_/\____/_/ /_/ /____/ \ No newline at end of file diff --git a/tools/headers/add-iptag b/tools/headers/add-iptag index 153ccd369..0266267cc 100644 --- a/tools/headers/add-iptag +++ b/tools/headers/add-iptag @@ -1,6 +1 @@ - ________ ______ - / _/ __ \ /_ __/___ _____ _ - / // /_/ /_____/ / / __ `/ __ `/ - _/ // ____/_____/ / / /_/ / /_/ / -/___/_/ /_/ \__,_/\__, / - /____/ + ________ ______ / _/ __ \ /_ __/___ _____ _ / // /_/ /_____/ / / __ `/ __ `/ _/ // ____/_____/ / / /_/ / /_/ / /___/_/ /_/ \__,_/\__, / /____/ \ No newline at end of file diff --git a/tools/headers/code-server b/tools/headers/code-server index f9bd04768..3bdcf9608 100644 --- a/tools/headers/code-server +++ b/tools/headers/code-server @@ -1,6 +1 @@ - ______ __ _____ - / ____/___ ____/ /__ / ___/___ ______ _____ _____ - / / / __ \/ __ / _ \ \__ \/ _ \/ ___/ | / / _ \/ ___/ -/ /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / -\____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ - + ______ __ _____ / ____/___ ____/ /__ / ___/___ ______ _____ _____ / / / __ \/ __ / _ \ \__ \/ _ \/ ___/ | / / _ \/ ___// /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / \____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ \ No newline at end of file diff --git a/tools/headers/container-restore-from-backup b/tools/headers/container-restore-from-backup index dd7d0def1..5a87e36cd 100644 --- a/tools/headers/container-restore-from-backup +++ b/tools/headers/container-restore-from-backup @@ -1,6 +1 @@ - __ __ ___ _ __ __ ______ __ _ - / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ____ / /_____ _(_)___ ___ _____ - / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/ - / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / / /_/ /_/ / / / / / __/ / -/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ - + __ __ ___ _ __ __ ______ __ _ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ____ / /_____ _(_)___ ___ _____ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / / /_/ /_/ / / / / / __/ / /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ \ No newline at end of file diff --git a/tools/headers/copyparty b/tools/headers/copyparty index 7e072248d..4f526d25c 100644 --- a/tools/headers/copyparty +++ b/tools/headers/copyparty @@ -1,6 +1 @@ - ______ ____ __ - / ____/___ ____ __ __/ __ \____ ______/ /___ __ - / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / / -/ /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / -\____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / - /_/ /____/ /____/ + ______ ____ __ / ____/___ ____ __ __/ __ \____ ______/ /___ __ / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / // /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / \____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / /_/ /____/ /____/ \ No newline at end of file diff --git a/tools/headers/core-restore-from-backup b/tools/headers/core-restore-from-backup index d356da026..e1bd2822a 100644 --- a/tools/headers/core-restore-from-backup +++ b/tools/headers/core-restore-from-backup @@ -1,6 +1 @@ - __ __ ___ _ __ __ ______ - / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ - / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ ___/ _ \ - / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / __/ -/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ - + __ __ ___ _ __ __ ______ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ ___/ _ \ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / __//_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ \ No newline at end of file diff --git a/tools/headers/filebrowser b/tools/headers/filebrowser index 1f4b71a1a..64a18dfbe 100644 --- a/tools/headers/filebrowser +++ b/tools/headers/filebrowser @@ -1,6 +1 @@ - _______ __ ____ - / ____(_) /__ / __ )_________ _ __________ _____ - / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ - / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / -/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ - + _______ __ ____ / ____(_) /__ / __ )_________ _ __________ _____ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / /_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \ No newline at end of file diff --git a/tools/headers/filebrowser-quantum b/tools/headers/filebrowser-quantum index 95c8b4b87..28fc3163c 100644 --- a/tools/headers/filebrowser-quantum +++ b/tools/headers/filebrowser-quantum @@ -1,6 +1 @@ - _______ __ ____ ____ __ - / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ - / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ - / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / -/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ - + _______ __ ____ ____ __ / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / //_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ \ No newline at end of file diff --git a/tools/headers/glances b/tools/headers/glances index 18ac97577..8ed1d92b9 100644 --- a/tools/headers/glances +++ b/tools/headers/glances @@ -1,6 +1 @@ - ________ - / ____/ /___ _____ ________ _____ - / / __/ / __ `/ __ \/ ___/ _ \/ ___/ -/ /_/ / / /_/ / / / / /__/ __(__ ) -\____/_/\__,_/_/ /_/\___/\___/____/ - + ________ / ____/ /___ _____ ________ _____ / / __/ / __ `/ __ \/ ___/ _ \/ ___// /_/ / / /_/ / / / / /__/ __(__ ) \____/_/\__,_/_/ /_/\___/\___/____/ \ No newline at end of file diff --git a/tools/headers/meilisearch-ui b/tools/headers/meilisearch-ui index fe37b6058..e7dc714dd 100644 --- a/tools/headers/meilisearch-ui +++ b/tools/headers/meilisearch-ui @@ -1,6 +1 @@ - __ ___ _ ___ __ __ ______ - / |/ /__ (_) (_)_______ ____ ___________/ /_ / / / / _/ - / /|_/ / _ \/ / / / ___/ _ \/ __ `/ ___/ ___/ __ \ / / / // / - / / / / __/ / / (__ ) __/ /_/ / / / /__/ / / / / /_/ // / -/_/ /_/\___/_/_/_/____/\___/\__,_/_/ \___/_/ /_/ \____/___/ - + __ ___ _ ___ __ __ ______ / |/ /__ (_) (_)_______ ____ ___________/ /_ / / / / _/ / /|_/ / _ \/ / / / ___/ _ \/ __ `/ ___/ ___/ __ \ / / / // / / / / / __/ / / (__ ) __/ /_/ / / / /__/ / / / / /_/ // / /_/ /_/\___/_/_/_/____/\___/\__,_/_/ \___/_/ /_/ \____/___/ \ No newline at end of file diff --git a/tools/headers/oci-deploy b/tools/headers/oci-deploy new file mode 100644 index 000000000..a950269fc --- /dev/null +++ b/tools/headers/oci-deploy @@ -0,0 +1 @@ + ____ __________ ______ __ _ / __ \/ ____/ _/ / ____/___ ____ / /_____ _(_)___ ___ _____ / / / / / / /_____/ / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___// /_/ / /____/ /_____/ /___/ /_/ / / / / /_/ /_/ / / / / / __/ / \____/\____/___/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ \ No newline at end of file diff --git a/tools/headers/phpmyadmin b/tools/headers/phpmyadmin index 8c2a6117b..047f0d0d6 100644 --- a/tools/headers/phpmyadmin +++ b/tools/headers/phpmyadmin @@ -1,6 +1 @@ - __ __ ___ ___ __ _ - ____ / /_ ____ / |/ /_ __/ | ____/ /___ ___ (_)___ - / __ \/ __ \/ __ \/ /|_/ / / / / /| |/ __ / __ `__ \/ / __ \ - / /_/ / / / / /_/ / / / / /_/ / ___ / /_/ / / / / / / / / / / - / .___/_/ /_/ .___/_/ /_/\__, /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ -/_/ /_/ /____/ + __ __ ___ ___ __ _ ____ / /_ ____ / |/ /_ __/ | ____/ /___ ___ (_)___ / __ \/ __ \/ __ \/ /|_/ / / / / /| |/ __ / __ `__ \/ / __ \ / /_/ / / / / /_/ / / / / /_/ / ___ / /_/ / / / / / / / / / / / .___/_/ /_/ .___/_/ /_/\__, /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ /_/ /_/ /____/ \ No newline at end of file diff --git a/tools/headers/prx-add-ips b/tools/headers/prx-add-ips index ea035b532..dd21b719d 100644 --- a/tools/headers/prx-add-ips +++ b/tools/headers/prx-add-ips @@ -1,6 +1 @@ - ____ ___ __ __ ________ - / __ \_________ _ ______ ___ ____ _ __ / | ____/ /___/ / / _/ __ \_____ - / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / /| |/ __ / __ /_____ / // /_/ / ___/ - / ____/ / / /_/ /> < / ___ / /_/ / /_/ /_____// // ____(__ ) -/_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| /_/ |_\__,_/\__,_/ /___/_/ /____/ - + ____ ___ __ __ ________ / __ \_________ _ ______ ___ ____ _ __ / | ____/ /___/ / / _/ __ \_____ / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / /| |/ __ / __ /_____ / // /_/ / ___/ / ____/ / / /_/ /> < / ___ / /_/ / /_/ /_____// // ____(__ ) /_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| /_/ |_\__,_/\__,_/ /___/_/ /____/ \ No newline at end of file diff --git a/tools/headers/pve-privilege-converter b/tools/headers/pve-privilege-converter index 47d0c3657..2d4d82797 100644 --- a/tools/headers/pve-privilege-converter +++ b/tools/headers/pve-privilege-converter @@ -1,6 +1 @@ - ____ _ ________ ____ _ _ __ ______ __ - / __ \ | / / ____/ / __ \_____(_) __(_) /__ ____ ____ / ____/___ ____ _ _____ _____/ /____ _____ - / /_/ / | / / __/______/ /_/ / ___/ / | / / / / _ \/ __ `/ _ \______/ / / __ \/ __ \ | / / _ \/ ___/ __/ _ \/ ___/ - / ____/| |/ / /__/_____/ ____/ / / /| |/ / / / __/ /_/ / __/_____/ /___/ /_/ / / / / |/ / __/ / / /_/ __/ / -/_/ |___/_____/ /_/ /_/ /_/ |___/_/_/\___/\__, /\___/ \____/\____/_/ /_/|___/\___/_/ \__/\___/_/ - /____/ + ____ _ ________ ____ _ _ __ ______ __ / __ \ | / / ____/ / __ \_____(_) __(_) /__ ____ ____ / ____/___ ____ _ _____ _____/ /____ _____ / /_/ / | / / __/______/ /_/ / ___/ / | / / / / _ \/ __ `/ _ \______/ / / __ \/ __ \ | / / _ \/ ___/ __/ _ \/ ___/ / ____/| |/ / /__/_____/ ____/ / / /| |/ / / / __/ /_/ / __/_____/ /___/ /_/ / / / / |/ / __/ / / /_/ __/ / /_/ |___/_____/ /_/ /_/ /_/ |___/_/_/\___/\__, /\___/ \____/\____/_/ /_/|___/\___/_/ \__/\___/_/ /____/ \ No newline at end of file diff --git a/tools/headers/qbittorrent-exporter b/tools/headers/qbittorrent-exporter deleted file mode 100644 index 07677b261..000000000 --- a/tools/headers/qbittorrent-exporter +++ /dev/null @@ -1,6 +0,0 @@ - __ _ __ __ __ __ - ____ _/ /_ (_) /_/ /_____ _____________ ____ / /_ ___ _ ______ ____ _____/ /____ _____ - / __ `/ __ \/ / __/ __/ __ \/ ___/ ___/ _ \/ __ \/ __/_____/ _ \| |/_/ __ \/ __ \/ ___/ __/ _ \/ ___/ -/ /_/ / /_/ / / /_/ /_/ /_/ / / / / / __/ / / / /_/_____/ __/> Date: Tue, 13 Jan 2026 11:24:18 +0100 Subject: [PATCH 333/378] remove channel cp --- ct/ampache.sh | 4 +--- install/ampache-install.sh | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ct/ampache.sh b/ct/ampache.sh index b00467652..044b8e342 100644 --- a/ct/ampache.sh +++ b/ct/ampache.sh @@ -36,7 +36,6 @@ function update_script() { cp /opt/ampache/config/ampache.cfg.php /tmp/ampache.cfg.php.backup cp /opt/ampache/public/rest/.htaccess /tmp/ampache_rest.htaccess.backup cp /opt/ampache/public/play/.htaccess /tmp/ampache_play.htaccess.backup - cp /opt/ampache/public/channel/.htaccess /tmp/ampache_channel.htaccess.backup msg_ok "Backed up Configuration" msg_info "Backup Ampache Folder" @@ -50,8 +49,7 @@ function update_script() { cp /tmp/ampache.cfg.php.backup /opt/ampache/config/ampache.cfg.php cp /tmp/ampache_rest.htaccess.backup /opt/ampache/public/rest/.htaccess cp /tmp/ampache_play.htaccess.backup /opt/ampache/public/play/.htaccess - cp /tmp/ampache_channel.htaccess.backup /opt/ampache/public/channel/.htaccess - chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess /opt/ampache/public/channel/.htaccess + chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess chown -R www-data:www-data /opt/ampache rm -f /tmp/ampache*.backup msg_ok "Restored Configuration" diff --git a/install/ampache-install.sh b/install/ampache-install.sh index f4faac159..d4a9cfdb0 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -38,9 +38,8 @@ rm -rf /var/www/html ln -s /opt/ampache/public /var/www/html mv /opt/ampache/public/rest/.htaccess.dist /opt/ampache/public/rest/.htaccess mv /opt/ampache/public/play/.htaccess.dist /opt/ampache/public/play/.htaccess -mv /opt/ampache/public/channel/.htaccess.dist /opt/ampache/public/channel/.htaccess cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php -chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess /opt/ampache/public/channel/.htaccess +chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess chown -R www-data:www-data /opt/ampache msg_ok "Set up Ampache" From 7d8b30739cf974017791772a86f30fff0a259bd3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:30:18 +0100 Subject: [PATCH 334/378] fix scripts --- .github/workflows/scripts/generate-app-headers.ps1 | 2 +- ct/headers/almalinux | 7 ++++++- ct/headers/alpine | 7 ++++++- ct/headers/alpine-loki | 7 ++++++- ct/headers/alpine-ntfy | 7 ++++++- ct/headers/alpine-valkey | 7 ++++++- ct/headers/ampache | 7 ++++++- ct/headers/byparr | 7 ++++++- ct/headers/centos | 7 ++++++- ct/headers/cronmaster | 7 ++++++- ct/headers/debian | 7 ++++++- ct/headers/devuan | 7 ++++++- ct/headers/ente | 7 ++++++- ct/headers/fedora | 7 ++++++- ct/headers/forgejo-runner | 7 ++++++- ct/headers/freepbx | 7 ++++++- ct/headers/frigate | 7 ++++++- ct/headers/garmin-grafana | 7 ++++++- ct/headers/gentoo | 7 ++++++- ct/headers/gwn-manager | 7 ++++++- ct/headers/heimdall-dashboard | 7 ++++++- ct/headers/hoodik | 7 ++++++- ct/headers/kitchenowl | 7 ++++++- ct/headers/kutt | 7 ++++++- ct/headers/linkwarden | 7 ++++++- ct/headers/loki | 7 ++++++- ct/headers/manyfold | 7 ++++++- ct/headers/minthcm | 7 ++++++- ct/headers/netbird | 7 ++++++- ct/headers/nextexplorer | 7 ++++++- ct/headers/opencloud | 7 ++++++- ct/headers/openeuler | 7 ++++++- ct/headers/opensuse | 7 ++++++- ct/headers/papra | 7 ++++++- ct/headers/petio | 7 ++++++- ct/headers/piler | 7 ++++++- ct/headers/pixelfed | 7 ++++++- ct/headers/plex | 7 ++++++- ct/headers/plex2 | 7 ++++++- ct/headers/postgresus | 7 ++++++- ct/headers/qui | 7 ++++++- ct/headers/rockylinux | 7 ++++++- ct/headers/romm | 7 ++++++- ct/headers/rustypaste | 1 - ct/headers/rybbit | 1 - ct/headers/tor-snowflake | 1 - ct/headers/tracearr | 1 - ct/headers/transmission-openvpn | 1 - ct/headers/ubuntu | 1 - ct/headers/wishlist | 1 - ct/headers/yubal | 1 - 51 files changed, 253 insertions(+), 51 deletions(-) delete mode 100644 ct/headers/rustypaste delete mode 100644 ct/headers/rybbit delete mode 100644 ct/headers/tor-snowflake delete mode 100644 ct/headers/tracearr delete mode 100644 ct/headers/transmission-openvpn delete mode 100644 ct/headers/ubuntu delete mode 100644 ct/headers/wishlist delete mode 100644 ct/headers/yubal diff --git a/.github/workflows/scripts/generate-app-headers.ps1 b/.github/workflows/scripts/generate-app-headers.ps1 index be2838d19..022d9f029 100644 --- a/.github/workflows/scripts/generate-app-headers.ps1 +++ b/.github/workflows/scripts/generate-app-headers.ps1 @@ -39,7 +39,7 @@ function Generate-Headers { $FigletOutput = & figlet -w 500 -f slant $AppName 2>&1 if ($LASTEXITCODE -eq 0 -and $FigletOutput) { - $FigletOutput | Out-File -FilePath $OutputFile -Encoding utf8 -NoNewline + $FigletOutput | Out-File -FilePath $OutputFile -Encoding utf8 Write-Host "Generated: $OutputFile" -ForegroundColor Green } else { diff --git a/ct/headers/almalinux b/ct/headers/almalinux index 4bcef0f78..1ff1dc19b 100644 --- a/ct/headers/almalinux +++ b/ct/headers/almalinux @@ -1 +1,6 @@ - ___ __ __ _ / | / /___ ___ ____ _/ / (_)___ __ ___ __ / /| | / / __ `__ \/ __ `/ / / / __ \/ / / / |/_/ / ___ |/ / / / / / / /_/ / /___/ / / / / /_/ /> < /_/ |_/_/_/ /_/ /_/\__,_/_____/_/_/ /_/\__,_/_/|_| \ No newline at end of file + ___ __ __ _ + / | / /___ ___ ____ _/ / (_)___ __ ___ __ + / /| | / / __ `__ \/ __ `/ / / / __ \/ / / / |/_/ + / ___ |/ / / / / / / /_/ / /___/ / / / / /_/ /> < +/_/ |_/_/_/ /_/ /_/\__,_/_____/_/_/ /_/\__,_/_/|_| + diff --git a/ct/headers/alpine b/ct/headers/alpine index 0fab98f18..2015c7841 100644 --- a/ct/headers/alpine +++ b/ct/headers/alpine @@ -1 +1,6 @@ - ___ __ _ / | / /___ (_)___ ___ / /| | / / __ \/ / __ \/ _ \ / ___ |/ / /_/ / / / / / __//_/ |_/_/ .___/_/_/ /_/\___/ /_/ \ No newline at end of file + ___ __ _ + / | / /___ (_)___ ___ + / /| | / / __ \/ / __ \/ _ \ + / ___ |/ / /_/ / / / / / __/ +/_/ |_/_/ .___/_/_/ /_/\___/ + /_/ diff --git a/ct/headers/alpine-loki b/ct/headers/alpine-loki index 7d026bb23..7e829b1da 100644 --- a/ct/headers/alpine-loki +++ b/ct/headers/alpine-loki @@ -1 +1,6 @@ - ___ __ _ __ __ _ / | / /___ (_)___ ___ / / ____ / /__(_) / /| | / / __ \/ / __ \/ _ \______/ / / __ \/ //_/ / / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / ,< / / /_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/_/|_/_/ /_/ \ No newline at end of file + ___ __ _ __ __ _ + / | / /___ (_)___ ___ / / ____ / /__(_) + / /| | / / __ \/ / __ \/ _ \______/ / / __ \/ //_/ / + / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / ,< / / +/_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/_/|_/_/ + /_/ diff --git a/ct/headers/alpine-ntfy b/ct/headers/alpine-ntfy index 71e205d5d..b97496fc9 100644 --- a/ct/headers/alpine-ntfy +++ b/ct/headers/alpine-ntfy @@ -1 +1,6 @@ - ___ __ _ __ ____ / | / /___ (_)___ ___ ____ / /_/ __/_ __ / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / /_/ /____/ \ No newline at end of file + ___ __ _ __ ____ + / | / /___ (_)___ ___ ____ / /_/ __/_ __ + / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / + / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / + /_/ /____/ diff --git a/ct/headers/alpine-valkey b/ct/headers/alpine-valkey index 85053df61..33e930808 100644 --- a/ct/headers/alpine-valkey +++ b/ct/headers/alpine-valkey @@ -1 +1,6 @@ - ___ __ _ _ __ ____ / | / /___ (_)___ ___ | | / /___ _/ / /_____ __ __ / /| | / / __ \/ / __ \/ _ \_____| | / / __ `/ / //_/ _ \/ / / / / ___ |/ / /_/ / / / / / __/_____/ |/ / /_/ / / ,< / __/ /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ |___/\__,_/_/_/|_|\___/\__, / /_/ /____/ \ No newline at end of file + ___ __ _ _ __ ____ + / | / /___ (_)___ ___ | | / /___ _/ / /_____ __ __ + / /| | / / __ \/ / __ \/ _ \_____| | / / __ `/ / //_/ _ \/ / / / + / ___ |/ / /_/ / / / / / __/_____/ |/ / /_/ / / ,< / __/ /_/ / +/_/ |_/_/ .___/_/_/ /_/\___/ |___/\__,_/_/_/|_|\___/\__, / + /_/ /____/ diff --git a/ct/headers/ampache b/ct/headers/ampache index 599bfac10..68d2c033d 100644 --- a/ct/headers/ampache +++ b/ct/headers/ampache @@ -1 +1,6 @@ - ___ __ / | ____ ___ ____ ____ ______/ /_ ___ / /| | / __ `__ \/ __ \/ __ `/ ___/ __ \/ _ \ / ___ |/ / / / / / /_/ / /_/ / /__/ / / / __//_/ |_/_/ /_/ /_/ .___/\__,_/\___/_/ /_/\___/ /_/ \ No newline at end of file + ___ __ + / | ____ ___ ____ ____ ______/ /_ ___ + / /| | / __ `__ \/ __ \/ __ `/ ___/ __ \/ _ \ + / ___ |/ / / / / / /_/ / /_/ / /__/ / / / __/ +/_/ |_/_/ /_/ /_/ .___/\__,_/\___/_/ /_/\___/ + /_/ diff --git a/ct/headers/byparr b/ct/headers/byparr index 1ed512d4f..0a6f89eca 100644 --- a/ct/headers/byparr +++ b/ct/headers/byparr @@ -1 +1,6 @@ - ____ / __ )__ ______ ____ ___________ / __ / / / / __ \/ __ `/ ___/ ___/ / /_/ / /_/ / /_/ / /_/ / / / / /_____/\__, / .___/\__,_/_/ /_/ /____/_/ \ No newline at end of file + ____ + / __ )__ ______ ____ ___________ + / __ / / / / __ \/ __ `/ ___/ ___/ + / /_/ / /_/ / /_/ / /_/ / / / / +/_____/\__, / .___/\__,_/_/ /_/ + /____/_/ diff --git a/ct/headers/centos b/ct/headers/centos index 4c1fef24a..9a449f2b8 100644 --- a/ct/headers/centos +++ b/ct/headers/centos @@ -1 +1,6 @@ - ______ __ ____ _____ _____ __ / ____/__ ____ / /_/ __ \/ ___/ / ___// /_________ ____ _____ ___ / / / _ \/ __ \/ __/ / / /\__ \ \__ \/ __/ ___/ _ \/ __ `/ __ `__ \/ /___/ __/ / / / /_/ /_/ /___/ / ___/ / /_/ / / __/ /_/ / / / / / /\____/\___/_/ /_/\__/\____//____/ /____/\__/_/ \___/\__,_/_/ /_/ /_/ \ No newline at end of file + ______ __ ____ _____ _____ __ + / ____/__ ____ / /_/ __ \/ ___/ / ___// /_________ ____ _____ ___ + / / / _ \/ __ \/ __/ / / /\__ \ \__ \/ __/ ___/ _ \/ __ `/ __ `__ \ +/ /___/ __/ / / / /_/ /_/ /___/ / ___/ / /_/ / / __/ /_/ / / / / / / +\____/\___/_/ /_/\__/\____//____/ /____/\__/_/ \___/\__,_/_/ /_/ /_/ + diff --git a/ct/headers/cronmaster b/ct/headers/cronmaster index e05167966..e4de81e88 100644 --- a/ct/headers/cronmaster +++ b/ct/headers/cronmaster @@ -1 +1,6 @@ - __________ ____ _ ____ ______ _____________________ / ____/ __ \/ __ \/ | / / |/ / | / ___/_ __/ ____/ __ \ / / / /_/ / / / / |/ / /|_/ / /| | \__ \ / / / __/ / /_/ // /___/ _, _/ /_/ / /| / / / / ___ |___/ // / / /___/ _, _/ \____/_/ |_|\____/_/ |_/_/ /_/_/ |_/____//_/ /_____/_/ |_| \ No newline at end of file + __________ ____ _ ____ ______ _____________________ + / ____/ __ \/ __ \/ | / / |/ / | / ___/_ __/ ____/ __ \ + / / / /_/ / / / / |/ / /|_/ / /| | \__ \ / / / __/ / /_/ / +/ /___/ _, _/ /_/ / /| / / / / ___ |___/ // / / /___/ _, _/ +\____/_/ |_|\____/_/ |_/_/ /_/_/ |_/____//_/ /_____/_/ |_| + diff --git a/ct/headers/debian b/ct/headers/debian index 3fd6acc74..54a409119 100644 --- a/ct/headers/debian +++ b/ct/headers/debian @@ -1 +1,6 @@ - ____ __ _ / __ \___ / /_ (_)___ _____ / / / / _ \/ __ \/ / __ `/ __ \ / /_/ / __/ /_/ / / /_/ / / / //_____/\___/_.___/_/\__,_/_/ /_/ \ No newline at end of file + ____ __ _ + / __ \___ / /_ (_)___ _____ + / / / / _ \/ __ \/ / __ `/ __ \ + / /_/ / __/ /_/ / / /_/ / / / / +/_____/\___/_.___/_/\__,_/_/ /_/ + diff --git a/ct/headers/devuan b/ct/headers/devuan index a38d4fd8e..a5ef3358d 100644 --- a/ct/headers/devuan +++ b/ct/headers/devuan @@ -1 +1,6 @@ - ____ / __ \___ _ ____ ______ _____ / / / / _ \ | / / / / / __ `/ __ \ / /_/ / __/ |/ / /_/ / /_/ / / / //_____/\___/|___/\__,_/\__,_/_/ /_/ \ No newline at end of file + ____ + / __ \___ _ ____ ______ _____ + / / / / _ \ | / / / / / __ `/ __ \ + / /_/ / __/ |/ / /_/ / /_/ / / / / +/_____/\___/|___/\__,_/\__,_/_/ /_/ + diff --git a/ct/headers/ente b/ct/headers/ente index 46af9e81f..271fd8d3b 100644 --- a/ct/headers/ente +++ b/ct/headers/ente @@ -1 +1,6 @@ - ______ __ / ____/___ / /____ / __/ / __ \/ __/ _ \ / /___/ / / / /_/ __//_____/_/ /_/\__/\___/ \ No newline at end of file + ______ __ + / ____/___ / /____ + / __/ / __ \/ __/ _ \ + / /___/ / / / /_/ __/ +/_____/_/ /_/\__/\___/ + diff --git a/ct/headers/fedora b/ct/headers/fedora index 8ddeae068..81e950ee8 100644 --- a/ct/headers/fedora +++ b/ct/headers/fedora @@ -1 +1,6 @@ - ______ __ / ____/__ ____/ /___ _________ _ / /_ / _ \/ __ / __ \/ ___/ __ `/ / __/ / __/ /_/ / /_/ / / / /_/ / /_/ \___/\__,_/\____/_/ \__,_/ \ No newline at end of file + ______ __ + / ____/__ ____/ /___ _________ _ + / /_ / _ \/ __ / __ \/ ___/ __ `/ + / __/ / __/ /_/ / /_/ / / / /_/ / +/_/ \___/\__,_/\____/_/ \__,_/ + diff --git a/ct/headers/forgejo-runner b/ct/headers/forgejo-runner index 7492d0bf3..5e3321610 100644 --- a/ct/headers/forgejo-runner +++ b/ct/headers/forgejo-runner @@ -1 +1,6 @@ - ______ _ ____ / ____/___ _________ ____ (_)___ / __ \__ ______ ____ ___ _____ / /_ / __ \/ ___/ __ `/ _ \ / / __ \ / /_/ / / / / __ \/ __ \/ _ \/ ___/ / __/ / /_/ / / / /_/ / __/ / / /_/ / / _, _/ /_/ / / / / / / / __/ / /_/ \____/_/ \__, /\___/_/ /\____/ /_/ |_|\__,_/_/ /_/_/ /_/\___/_/ /____/ /___/ \ No newline at end of file + ______ _ ____ + / ____/___ _________ ____ (_)___ / __ \__ ______ ____ ___ _____ + / /_ / __ \/ ___/ __ `/ _ \ / / __ \ / /_/ / / / / __ \/ __ \/ _ \/ ___/ + / __/ / /_/ / / / /_/ / __/ / / /_/ / / _, _/ /_/ / / / / / / / __/ / +/_/ \____/_/ \__, /\___/_/ /\____/ /_/ |_|\__,_/_/ /_/_/ /_/\___/_/ + /____/ /___/ diff --git a/ct/headers/freepbx b/ct/headers/freepbx index c79e3ace8..15dc15e4a 100644 --- a/ct/headers/freepbx +++ b/ct/headers/freepbx @@ -1 +1,6 @@ - ______ ____ ____ _ __ / ____/_______ ___ / __ \/ __ ) |/ / / /_ / ___/ _ \/ _ \/ /_/ / __ | / / __/ / / / __/ __/ ____/ /_/ / | /_/ /_/ \___/\___/_/ /_____/_/|_| \ No newline at end of file + ______ ____ ____ _ __ + / ____/_______ ___ / __ \/ __ ) |/ / + / /_ / ___/ _ \/ _ \/ /_/ / __ | / + / __/ / / / __/ __/ ____/ /_/ / | +/_/ /_/ \___/\___/_/ /_____/_/|_| + diff --git a/ct/headers/frigate b/ct/headers/frigate index f7516cd39..cc1c3c84f 100644 --- a/ct/headers/frigate +++ b/ct/headers/frigate @@ -1 +1,6 @@ - ______ _ __ / ____/____(_)___ _____ _/ /____ / /_ / ___/ / __ `/ __ `/ __/ _ \ / __/ / / / / /_/ / /_/ / /_/ __//_/ /_/ /_/\__, /\__,_/\__/\___/ /____/ \ No newline at end of file + ______ _ __ + / ____/____(_)___ _____ _/ /____ + / /_ / ___/ / __ `/ __ `/ __/ _ \ + / __/ / / / / /_/ / /_/ / /_/ __/ +/_/ /_/ /_/\__, /\__,_/\__/\___/ + /____/ diff --git a/ct/headers/garmin-grafana b/ct/headers/garmin-grafana index 91331f066..270b91452 100644 --- a/ct/headers/garmin-grafana +++ b/ct/headers/garmin-grafana @@ -1 +1,6 @@ - _ ____ ____ _____ __________ ___ (_)___ ____ __________ _/ __/___ _____ ____ _ / __ `/ __ `/ ___/ __ `__ \/ / __ \______/ __ `/ ___/ __ `/ /_/ __ `/ __ \/ __ `/ / /_/ / /_/ / / / / / / / / / / / /_____/ /_/ / / / /_/ / __/ /_/ / / / / /_/ / \__, /\__,_/_/ /_/ /_/ /_/_/_/ /_/ \__, /_/ \__,_/_/ \__,_/_/ /_/\__,_/ /____/ /____/ \ No newline at end of file + _ ____ + ____ _____ __________ ___ (_)___ ____ __________ _/ __/___ _____ ____ _ + / __ `/ __ `/ ___/ __ `__ \/ / __ \______/ __ `/ ___/ __ `/ /_/ __ `/ __ \/ __ `/ + / /_/ / /_/ / / / / / / / / / / / /_____/ /_/ / / / /_/ / __/ /_/ / / / / /_/ / + \__, /\__,_/_/ /_/ /_/ /_/_/_/ /_/ \__, /_/ \__,_/_/ \__,_/_/ /_/\__,_/ +/____/ /____/ diff --git a/ct/headers/gentoo b/ct/headers/gentoo index 31db9fc79..958bee88e 100644 --- a/ct/headers/gentoo +++ b/ct/headers/gentoo @@ -1 +1,6 @@ - ______ __ / ____/__ ____ / /_____ ____ / / __/ _ \/ __ \/ __/ __ \/ __ \/ /_/ / __/ / / / /_/ /_/ / /_/ /\____/\___/_/ /_/\__/\____/\____/ \ No newline at end of file + ______ __ + / ____/__ ____ / /_____ ____ + / / __/ _ \/ __ \/ __/ __ \/ __ \ +/ /_/ / __/ / / / /_/ /_/ / /_/ / +\____/\___/_/ /_/\__/\____/\____/ + diff --git a/ct/headers/gwn-manager b/ct/headers/gwn-manager index ea785f891..6aacb372e 100644 --- a/ct/headers/gwn-manager +++ b/ct/headers/gwn-manager @@ -1 +1,6 @@ - _______ ___ __ __ ___ / ____/ | / / | / / / |/ /___ _____ ____ _____ ____ _____ / / __ | | /| / / |/ /_____/ /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___// /_/ / | |/ |/ / /| /_____/ / / / /_/ / / / / /_/ / /_/ / __/ / \____/ |__/|__/_/ |_/ /_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ /____/ \ No newline at end of file + _______ ___ __ __ ___ + / ____/ | / / | / / / |/ /___ _____ ____ _____ ____ _____ + / / __ | | /| / / |/ /_____/ /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ +/ /_/ / | |/ |/ / /| /_____/ / / / /_/ / / / / /_/ / /_/ / __/ / +\____/ |__/|__/_/ |_/ /_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ + /____/ diff --git a/ct/headers/heimdall-dashboard b/ct/headers/heimdall-dashboard index 67b6e60f1..ced6c869b 100644 --- a/ct/headers/heimdall-dashboard +++ b/ct/headers/heimdall-dashboard @@ -1 +1,6 @@ - __ __ _ __ ____ ____ __ __ __ / / / /__ (_)___ ___ ____/ /___ _/ / / / __ \____ ______/ /_ / /_ ____ ____ __________/ / / /_/ / _ \/ / __ `__ \/ __ / __ `/ / /_____/ / / / __ `/ ___/ __ \/ __ \/ __ \/ __ `/ ___/ __ / / __ / __/ / / / / / / /_/ / /_/ / / /_____/ /_/ / /_/ (__ ) / / / /_/ / /_/ / /_/ / / / /_/ / /_/ /_/\___/_/_/ /_/ /_/\__,_/\__,_/_/_/ /_____/\__,_/____/_/ /_/_.___/\____/\__,_/_/ \__,_/ \ No newline at end of file + __ __ _ __ ____ ____ __ __ __ + / / / /__ (_)___ ___ ____/ /___ _/ / / / __ \____ ______/ /_ / /_ ____ ____ __________/ / + / /_/ / _ \/ / __ `__ \/ __ / __ `/ / /_____/ / / / __ `/ ___/ __ \/ __ \/ __ \/ __ `/ ___/ __ / + / __ / __/ / / / / / / /_/ / /_/ / / /_____/ /_/ / /_/ (__ ) / / / /_/ / /_/ / /_/ / / / /_/ / +/_/ /_/\___/_/_/ /_/ /_/\__,_/\__,_/_/_/ /_____/\__,_/____/_/ /_/_.___/\____/\__,_/_/ \__,_/ + diff --git a/ct/headers/hoodik b/ct/headers/hoodik index 4c2b3f211..2d9b951a1 100644 --- a/ct/headers/hoodik +++ b/ct/headers/hoodik @@ -1 +1,6 @@ - __ __ ___ __ / / / /___ ____ ____/ (_) /__ / /_/ / __ \/ __ \/ __ / / //_/ / __ / /_/ / /_/ / /_/ / / ,< /_/ /_/\____/\____/\__,_/_/_/|_| \ No newline at end of file + __ __ ___ __ + / / / /___ ____ ____/ (_) /__ + / /_/ / __ \/ __ \/ __ / / //_/ + / __ / /_/ / /_/ / /_/ / / ,< +/_/ /_/\____/\____/\__,_/_/_/|_| + diff --git a/ct/headers/kitchenowl b/ct/headers/kitchenowl index 88e302ede..6f20ad457 100644 --- a/ct/headers/kitchenowl +++ b/ct/headers/kitchenowl @@ -1 +1,6 @@ - __ __ _ __ __ ____ __ / //_/(_) /______/ /_ ___ ____ / __ \_ __/ / / ,< / / __/ ___/ __ \/ _ \/ __ \/ / / / | /| / / / / /| |/ / /_/ /__/ / / / __/ / / / /_/ /| |/ |/ / / /_/ |_/_/\__/\___/_/ /_/\___/_/ /_/\____/ |__/|__/_/ \ No newline at end of file + __ __ _ __ __ ____ __ + / //_/(_) /______/ /_ ___ ____ / __ \_ __/ / + / ,< / / __/ ___/ __ \/ _ \/ __ \/ / / / | /| / / / + / /| |/ / /_/ /__/ / / / __/ / / / /_/ /| |/ |/ / / +/_/ |_/_/\__/\___/_/ /_/\___/_/ /_/\____/ |__/|__/_/ + diff --git a/ct/headers/kutt b/ct/headers/kutt index 81c70e96d..a132aed6f 100644 --- a/ct/headers/kutt +++ b/ct/headers/kutt @@ -1 +1,6 @@ - __ __ __ __ / //_/_ __/ /_/ /_ / ,< / / / / __/ __/ / /| / /_/ / /_/ /_ /_/ |_\__,_/\__/\__/ \ No newline at end of file + __ __ __ __ + / //_/_ __/ /_/ /_ + / ,< / / / / __/ __/ + / /| / /_/ / /_/ /_ +/_/ |_\__,_/\__/\__/ + diff --git a/ct/headers/linkwarden b/ct/headers/linkwarden index 7f8d0fc99..d4e605619 100644 --- a/ct/headers/linkwarden +++ b/ct/headers/linkwarden @@ -1 +1,6 @@ - __ _ __ __ / / (_)___ / /___ ______ __________/ /__ ____ / / / / __ \/ //_/ | /| / / __ `/ ___/ __ / _ \/ __ \ / /___/ / / / / ,< | |/ |/ / /_/ / / / /_/ / __/ / / //_____/_/_/ /_/_/|_| |__/|__/\__,_/_/ \__,_/\___/_/ /_/ \ No newline at end of file + __ _ __ __ + / / (_)___ / /___ ______ __________/ /__ ____ + / / / / __ \/ //_/ | /| / / __ `/ ___/ __ / _ \/ __ \ + / /___/ / / / / ,< | |/ |/ / /_/ / / / /_/ / __/ / / / +/_____/_/_/ /_/_/|_| |__/|__/\__,_/_/ \__,_/\___/_/ /_/ + diff --git a/ct/headers/loki b/ct/headers/loki index 81516e5db..a8e58ae7f 100644 --- a/ct/headers/loki +++ b/ct/headers/loki @@ -1 +1,6 @@ - __ __ _ / / ____ / /__(_) / / / __ \/ //_/ / / /___/ /_/ / ,< / / /_____/\____/_/|_/_/ \ No newline at end of file + __ __ _ + / / ____ / /__(_) + / / / __ \/ //_/ / + / /___/ /_/ / ,< / / +/_____/\____/_/|_/_/ + diff --git a/ct/headers/manyfold b/ct/headers/manyfold index 15e6a8125..b27653eb0 100644 --- a/ct/headers/manyfold +++ b/ct/headers/manyfold @@ -1 +1,6 @@ - __ ___ ____ __ __ / |/ /___ _____ __ __/ __/___ / /___/ / / /|_/ / __ `/ __ \/ / / / /_/ __ \/ / __ / / / / / /_/ / / / / /_/ / __/ /_/ / / /_/ / /_/ /_/\__,_/_/ /_/\__, /_/ \____/_/\__,_/ /____/ \ No newline at end of file + __ ___ ____ __ __ + / |/ /___ _____ __ __/ __/___ / /___/ / + / /|_/ / __ `/ __ \/ / / / /_/ __ \/ / __ / + / / / / /_/ / / / / /_/ / __/ /_/ / / /_/ / +/_/ /_/\__,_/_/ /_/\__, /_/ \____/_/\__,_/ + /____/ diff --git a/ct/headers/minthcm b/ct/headers/minthcm index 36b60eb15..27a85fbb5 100644 --- a/ct/headers/minthcm +++ b/ct/headers/minthcm @@ -1 +1,6 @@ - __ ____ __ __ __________ ___ / |/ (_)___ / /_/ / / / ____/ |/ / / /|_/ / / __ \/ __/ /_/ / / / /|_/ / / / / / / / / / /_/ __ / /___/ / / / /_/ /_/_/_/ /_/\__/_/ /_/\____/_/ /_/ \ No newline at end of file + __ ____ __ __ __________ ___ + / |/ (_)___ / /_/ / / / ____/ |/ / + / /|_/ / / __ \/ __/ /_/ / / / /|_/ / + / / / / / / / / /_/ __ / /___/ / / / +/_/ /_/_/_/ /_/\__/_/ /_/\____/_/ /_/ + diff --git a/ct/headers/netbird b/ct/headers/netbird index b5fb3e7a0..22e179ef1 100644 --- a/ct/headers/netbird +++ b/ct/headers/netbird @@ -1 +1,6 @@ - _ __ __ ____ _ __ / | / /__ / /_/ __ )(_)________/ / / |/ / _ \/ __/ __ / / ___/ __ / / /| / __/ /_/ /_/ / / / / /_/ / /_/ |_/\___/\__/_____/_/_/ \__,_/ \ No newline at end of file + _ __ __ ____ _ __ + / | / /__ / /_/ __ )(_)________/ / + / |/ / _ \/ __/ __ / / ___/ __ / + / /| / __/ /_/ /_/ / / / / /_/ / +/_/ |_/\___/\__/_____/_/_/ \__,_/ + diff --git a/ct/headers/nextexplorer b/ct/headers/nextexplorer index 877919951..c260c4832 100644 --- a/ct/headers/nextexplorer +++ b/ct/headers/nextexplorer @@ -1 +1,6 @@ - __ ______ __ ____ ___ _ __/ /_/ ____/ ______ / /___ ________ _____ / __ \/ _ \| |/_/ __/ __/ | |/_/ __ \/ / __ \/ ___/ _ \/ ___/ / / / / __/> < /_/ /_/\___/_/|_| \ No newline at end of file + ____ __ + / __ \/ /__ _ __ + / /_/ / / _ \| |/_/ + / ____/ / __/> < +/_/ /_/\___/_/|_| + diff --git a/ct/headers/plex2 b/ct/headers/plex2 index 575946fa6..5381e8d73 100644 --- a/ct/headers/plex2 +++ b/ct/headers/plex2 @@ -1 +1,6 @@ - ____ __ ___ / __ \/ /__ _ _|__ \ / /_/ / / _ \| |/_/_/ / / ____/ / __/> < /_/ |_|\____/\___/_/|_|\__, / /_____/_/_/ /_/\__,_/_/|_| /____/ \ No newline at end of file + ____ __ __ _ + / __ \____ _____/ /____ __ / / (_)___ __ ___ __ + / /_/ / __ \/ ___/ //_/ / / / / / / / __ \/ / / / |/_/ + / _, _/ /_/ / /__/ ,< / /_/ / / /___/ / / / / /_/ /> < +/_/ |_|\____/\___/_/|_|\__, / /_____/_/_/ /_/\__,_/_/|_| + /____/ diff --git a/ct/headers/romm b/ct/headers/romm index 2e768af82..3e942aefb 100644 --- a/ct/headers/romm +++ b/ct/headers/romm @@ -1 +1,6 @@ - ____ __ ___ / __ \____ ____ ___ / |/ / / /_/ / __ \/ __ `__ \/ /|_/ / / _, _/ /_/ / / / / / / / / / /_/ |_|\____/_/ /_/ /_/_/ /_/ \ No newline at end of file + ____ __ ___ + / __ \____ ____ ___ / |/ / + / /_/ / __ \/ __ `__ \/ /|_/ / + / _, _/ /_/ / / / / / / / / / +/_/ |_|\____/_/ /_/ /_/_/ /_/ + diff --git a/ct/headers/rustypaste b/ct/headers/rustypaste deleted file mode 100644 index 8b226b558..000000000 --- a/ct/headers/rustypaste +++ /dev/null @@ -1 +0,0 @@ - __ __ _______ _______/ /___ ______ ____ ______/ /____ / ___/ / / / ___/ __/ / / / __ \/ __ `/ ___/ __/ _ \ / / / /_/ (__ ) /_/ /_/ / /_/ / /_/ (__ ) /_/ __//_/ \__,_/____/\__/\__, / .___/\__,_/____/\__/\___/ /____/_/ \ No newline at end of file diff --git a/ct/headers/rybbit b/ct/headers/rybbit deleted file mode 100644 index eaedef356..000000000 --- a/ct/headers/rybbit +++ /dev/null @@ -1 +0,0 @@ - ____ __ __ _ __ / __ \__ __/ /_ / /_ (_) /_ / /_/ / / / / __ \/ __ \/ / __/ / _, _/ /_/ / /_/ / /_/ / / /_ /_/ |_|\__, /_.___/_.___/_/\__/ /____/ \ No newline at end of file diff --git a/ct/headers/tor-snowflake b/ct/headers/tor-snowflake deleted file mode 100644 index fdcb7e31a..000000000 --- a/ct/headers/tor-snowflake +++ /dev/null @@ -1 +0,0 @@ - __ ______ __ / /_____ _____ _________ ____ _ __/ __/ /___ _/ /_____ / __/ __ \/ ___/_____/ ___/ __ \/ __ \ | /| / / /_/ / __ `/ //_/ _ \/ /_/ /_/ / / /_____(__ ) / / / /_/ / |/ |/ / __/ / /_/ / ,< / __/\__/\____/_/ /____/_/ /_/\____/|__/|__/_/ /_/\__,_/_/|_|\___/ \ No newline at end of file diff --git a/ct/headers/tracearr b/ct/headers/tracearr deleted file mode 100644 index 702d16778..000000000 --- a/ct/headers/tracearr +++ /dev/null @@ -1 +0,0 @@ - ______ /_ __/________ _________ ____ ___________ / / / ___/ __ `/ ___/ _ \/ __ `/ ___/ ___/ / / / / / /_/ / /__/ __/ /_/ / / / / /_/ /_/ \__,_/\___/\___/\__,_/_/ /_/ \ No newline at end of file diff --git a/ct/headers/transmission-openvpn b/ct/headers/transmission-openvpn deleted file mode 100644 index 636b29841..000000000 --- a/ct/headers/transmission-openvpn +++ /dev/null @@ -1 +0,0 @@ - __ _ _ / /__________ _____ _________ ___ (_)_________(_)___ ____ ____ ____ ___ ____ _ ______ ____ / __/ ___/ __ `/ __ \/ ___/ __ `__ \/ / ___/ ___/ / __ \/ __ \______/ __ \/ __ \/ _ \/ __ \ | / / __ \/ __ \/ /_/ / / /_/ / / / (__ ) / / / / / (__ |__ ) / /_/ / / / /_____/ /_/ / /_/ / __/ / / / |/ / /_/ / / / /\__/_/ \__,_/_/ /_/____/_/ /_/ /_/_/____/____/_/\____/_/ /_/ \____/ .___/\___/_/ /_/|___/ .___/_/ /_/ /_/ /_/ \ No newline at end of file diff --git a/ct/headers/ubuntu b/ct/headers/ubuntu deleted file mode 100644 index 8a3923c1b..000000000 --- a/ct/headers/ubuntu +++ /dev/null @@ -1 +0,0 @@ - __ ____ __ / / / / /_ __ ______ / /___ __ / / / / __ \/ / / / __ \/ __/ / / // /_/ / /_/ / /_/ / / / / /_/ /_/ / \____/_.___/\__,_/_/ /_/\__/\__,_/ \ No newline at end of file diff --git a/ct/headers/wishlist b/ct/headers/wishlist deleted file mode 100644 index e1f94fc97..000000000 --- a/ct/headers/wishlist +++ /dev/null @@ -1 +0,0 @@ - _ ___ __ ___ __ | | / (_)____/ /_ / (_)____/ /_| | /| / / / ___/ __ \/ / / ___/ __/| |/ |/ / (__ ) / / / / (__ ) /_ |__/|__/_/____/_/ /_/_/_/____/\__/ \ No newline at end of file diff --git a/ct/headers/yubal b/ct/headers/yubal deleted file mode 100644 index 50cc22340..000000000 --- a/ct/headers/yubal +++ /dev/null @@ -1 +0,0 @@ -__ __ __ __\ \/ /_ __/ /_ ____ _/ / \ / / / / __ \/ __ `/ / / / /_/ / /_/ / /_/ / / /_/\__,_/_.___/\__,_/_/ \ No newline at end of file From f8e80506344c9c7c097cc43f75c2a04914ac992f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:30:27 +0100 Subject: [PATCH 335/378] fix headers --- ct/headers/rustypaste | 6 ++++++ ct/headers/rybbit | 6 ++++++ ct/headers/tor-snowflake | 6 ++++++ ct/headers/tracearr | 6 ++++++ ct/headers/transmission-openvpn | 6 ++++++ ct/headers/ubuntu | 6 ++++++ ct/headers/wishlist | 6 ++++++ ct/headers/yubal | 6 ++++++ tools/headers/_template | 7 ++++++- tools/headers/add-iptag | 7 ++++++- tools/headers/code-server | 7 ++++++- tools/headers/container-restore-from-backup | 7 ++++++- tools/headers/copyparty | 7 ++++++- tools/headers/core-restore-from-backup | 7 ++++++- tools/headers/filebrowser | 7 ++++++- tools/headers/filebrowser-quantum | 7 ++++++- tools/headers/glances | 7 ++++++- tools/headers/meilisearch-ui | 7 ++++++- tools/headers/oci-deploy | 7 ++++++- tools/headers/phpmyadmin | 7 ++++++- tools/headers/prx-add-ips | 7 ++++++- tools/headers/pve-privilege-converter | 7 ++++++- tools/headers/saltminion | 7 ++++++- vm/headers/allstarlink-vm | 7 ++++++- vm/headers/archlinux-vm | 7 ++++++- vm/headers/debian-13-vm | 7 ++++++- vm/headers/debian-vm | 7 ++++++- vm/headers/debian-vm-test-helper | 7 ++++++- vm/headers/docker-vm | 7 ++++++- vm/headers/docker-vm-debug | 7 ++++++- vm/headers/haos-vm | 7 ++++++- vm/headers/k3s-vm | 7 ++++++- vm/headers/mikrotik-routeros | 7 ++++++- vm/headers/nextcloud-vm | 7 ++++++- vm/headers/openwrt | 7 ++++++- vm/headers/opnsense-vm | 7 ++++++- vm/headers/owncloud-vm | 7 ++++++- vm/headers/pimox-haos-vm | 7 ++++++- vm/headers/ubuntu2204-vm | 7 ++++++- vm/headers/ubuntu2404-vm | 7 ++++++- vm/headers/ubuntu2410-vm | 7 ++++++- vm/headers/umbrel-os-vm | 7 ++++++- vm/headers/unifi-os-server-vm | 7 ++++++- 43 files changed, 258 insertions(+), 35 deletions(-) create mode 100644 ct/headers/rustypaste create mode 100644 ct/headers/rybbit create mode 100644 ct/headers/tor-snowflake create mode 100644 ct/headers/tracearr create mode 100644 ct/headers/transmission-openvpn create mode 100644 ct/headers/ubuntu create mode 100644 ct/headers/wishlist create mode 100644 ct/headers/yubal diff --git a/ct/headers/rustypaste b/ct/headers/rustypaste new file mode 100644 index 000000000..c691b0a30 --- /dev/null +++ b/ct/headers/rustypaste @@ -0,0 +1,6 @@ + __ __ + _______ _______/ /___ ______ ____ ______/ /____ + / ___/ / / / ___/ __/ / / / __ \/ __ `/ ___/ __/ _ \ + / / / /_/ (__ ) /_/ /_/ / /_/ / /_/ (__ ) /_/ __/ +/_/ \__,_/____/\__/\__, / .___/\__,_/____/\__/\___/ + /____/_/ diff --git a/ct/headers/rybbit b/ct/headers/rybbit new file mode 100644 index 000000000..edc36ae6c --- /dev/null +++ b/ct/headers/rybbit @@ -0,0 +1,6 @@ + ____ __ __ _ __ + / __ \__ __/ /_ / /_ (_) /_ + / /_/ / / / / __ \/ __ \/ / __/ + / _, _/ /_/ / /_/ / /_/ / / /_ +/_/ |_|\__, /_.___/_.___/_/\__/ + /____/ diff --git a/ct/headers/tor-snowflake b/ct/headers/tor-snowflake new file mode 100644 index 000000000..c1e8728d2 --- /dev/null +++ b/ct/headers/tor-snowflake @@ -0,0 +1,6 @@ + __ ______ __ + / /_____ _____ _________ ____ _ __/ __/ /___ _/ /_____ + / __/ __ \/ ___/_____/ ___/ __ \/ __ \ | /| / / /_/ / __ `/ //_/ _ \ +/ /_/ /_/ / / /_____(__ ) / / / /_/ / |/ |/ / __/ / /_/ / ,< / __/ +\__/\____/_/ /____/_/ /_/\____/|__/|__/_/ /_/\__,_/_/|_|\___/ + diff --git a/ct/headers/tracearr b/ct/headers/tracearr new file mode 100644 index 000000000..aee118c88 --- /dev/null +++ b/ct/headers/tracearr @@ -0,0 +1,6 @@ + ______ + /_ __/________ _________ ____ ___________ + / / / ___/ __ `/ ___/ _ \/ __ `/ ___/ ___/ + / / / / / /_/ / /__/ __/ /_/ / / / / +/_/ /_/ \__,_/\___/\___/\__,_/_/ /_/ + diff --git a/ct/headers/transmission-openvpn b/ct/headers/transmission-openvpn new file mode 100644 index 000000000..372fb12b4 --- /dev/null +++ b/ct/headers/transmission-openvpn @@ -0,0 +1,6 @@ + __ _ _ + / /__________ _____ _________ ___ (_)_________(_)___ ____ ____ ____ ___ ____ _ ______ ____ + / __/ ___/ __ `/ __ \/ ___/ __ `__ \/ / ___/ ___/ / __ \/ __ \______/ __ \/ __ \/ _ \/ __ \ | / / __ \/ __ \ +/ /_/ / / /_/ / / / (__ ) / / / / / (__ |__ ) / /_/ / / / /_____/ /_/ / /_/ / __/ / / / |/ / /_/ / / / / +\__/_/ \__,_/_/ /_/____/_/ /_/ /_/_/____/____/_/\____/_/ /_/ \____/ .___/\___/_/ /_/|___/ .___/_/ /_/ + /_/ /_/ diff --git a/ct/headers/ubuntu b/ct/headers/ubuntu new file mode 100644 index 000000000..8c8a9f69f --- /dev/null +++ b/ct/headers/ubuntu @@ -0,0 +1,6 @@ + __ ____ __ + / / / / /_ __ ______ / /___ __ + / / / / __ \/ / / / __ \/ __/ / / / +/ /_/ / /_/ / /_/ / / / / /_/ /_/ / +\____/_.___/\__,_/_/ /_/\__/\__,_/ + diff --git a/ct/headers/wishlist b/ct/headers/wishlist new file mode 100644 index 000000000..a55ad71bc --- /dev/null +++ b/ct/headers/wishlist @@ -0,0 +1,6 @@ + _ ___ __ ___ __ +| | / (_)____/ /_ / (_)____/ /_ +| | /| / / / ___/ __ \/ / / ___/ __/ +| |/ |/ / (__ ) / / / / (__ ) /_ +|__/|__/_/____/_/ /_/_/_/____/\__/ + diff --git a/ct/headers/yubal b/ct/headers/yubal new file mode 100644 index 000000000..700a03f72 --- /dev/null +++ b/ct/headers/yubal @@ -0,0 +1,6 @@ +__ __ __ __ +\ \/ /_ __/ /_ ____ _/ / + \ / / / / __ \/ __ `/ / + / / /_/ / /_/ / /_/ / / +/_/\__,_/_.___/\__,_/_/ + diff --git a/tools/headers/_template b/tools/headers/_template index e8a6042e2..dddf1857d 100644 --- a/tools/headers/_template +++ b/tools/headers/_template @@ -1 +1,6 @@ - __ ___ ___ __ __ / |/ /_ __/ | ____/ /___/ /___ ____ / /|_/ / / / / /| |/ __ / __ / __ \/ __ \ / / / / /_/ / ___ / /_/ / /_/ / /_/ / / / //_/ /_/\__, /_/ |_\__,_/\__,_/\____/_/ /_/ /____/ \ No newline at end of file + __ ___ ___ __ __ + / |/ /_ __/ | ____/ /___/ /___ ____ + / /|_/ / / / / /| |/ __ / __ / __ \/ __ \ + / / / / /_/ / ___ / /_/ / /_/ / /_/ / / / / +/_/ /_/\__, /_/ |_\__,_/\__,_/\____/_/ /_/ + /____/ diff --git a/tools/headers/add-iptag b/tools/headers/add-iptag index 0266267cc..f4ed9970c 100644 --- a/tools/headers/add-iptag +++ b/tools/headers/add-iptag @@ -1 +1,6 @@ - ________ ______ / _/ __ \ /_ __/___ _____ _ / // /_/ /_____/ / / __ `/ __ `/ _/ // ____/_____/ / / /_/ / /_/ / /___/_/ /_/ \__,_/\__, / /____/ \ No newline at end of file + ________ ______ + / _/ __ \ /_ __/___ _____ _ + / // /_/ /_____/ / / __ `/ __ `/ + _/ // ____/_____/ / / /_/ / /_/ / +/___/_/ /_/ \__,_/\__, / + /____/ diff --git a/tools/headers/code-server b/tools/headers/code-server index 3bdcf9608..156108c33 100644 --- a/tools/headers/code-server +++ b/tools/headers/code-server @@ -1 +1,6 @@ - ______ __ _____ / ____/___ ____/ /__ / ___/___ ______ _____ _____ / / / __ \/ __ / _ \ \__ \/ _ \/ ___/ | / / _ \/ ___// /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / \____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ \ No newline at end of file + ______ __ _____ + / ____/___ ____/ /__ / ___/___ ______ _____ _____ + / / / __ \/ __ / _ \ \__ \/ _ \/ ___/ | / / _ \/ ___/ +/ /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / +\____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ + diff --git a/tools/headers/container-restore-from-backup b/tools/headers/container-restore-from-backup index 5a87e36cd..9b133c0d1 100644 --- a/tools/headers/container-restore-from-backup +++ b/tools/headers/container-restore-from-backup @@ -1 +1,6 @@ - __ __ ___ _ __ __ ______ __ _ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ____ / /_____ _(_)___ ___ _____ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / / /_/ /_/ / / / / / __/ / /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ \ No newline at end of file + __ __ ___ _ __ __ ______ __ _ + / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ____ / /_____ _(_)___ ___ _____ + / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/ + / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / / /_/ /_/ / / / / / __/ / +/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ + diff --git a/tools/headers/copyparty b/tools/headers/copyparty index 4f526d25c..0511b397f 100644 --- a/tools/headers/copyparty +++ b/tools/headers/copyparty @@ -1 +1,6 @@ - ______ ____ __ / ____/___ ____ __ __/ __ \____ ______/ /___ __ / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / // /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / \____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / /_/ /____/ /____/ \ No newline at end of file + ______ ____ __ + / ____/___ ____ __ __/ __ \____ ______/ /___ __ + / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / / +/ /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / +\____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / + /_/ /____/ /____/ diff --git a/tools/headers/core-restore-from-backup b/tools/headers/core-restore-from-backup index e1bd2822a..afb4bbc9e 100644 --- a/tools/headers/core-restore-from-backup +++ b/tools/headers/core-restore-from-backup @@ -1 +1,6 @@ - __ __ ___ _ __ __ ______ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ ___/ _ \ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / __//_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ \ No newline at end of file + __ __ ___ _ __ __ ______ + / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ + / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ ___/ _ \ + / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / __/ +/_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ + diff --git a/tools/headers/filebrowser b/tools/headers/filebrowser index 64a18dfbe..e65eab18a 100644 --- a/tools/headers/filebrowser +++ b/tools/headers/filebrowser @@ -1 +1,6 @@ - _______ __ ____ / ____(_) /__ / __ )_________ _ __________ _____ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / /_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \ No newline at end of file + _______ __ ____ + / ____(_) /__ / __ )_________ _ __________ _____ + / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ + / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / +/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ + diff --git a/tools/headers/filebrowser-quantum b/tools/headers/filebrowser-quantum index 28fc3163c..e0d4e30e3 100644 --- a/tools/headers/filebrowser-quantum +++ b/tools/headers/filebrowser-quantum @@ -1 +1,6 @@ - _______ __ ____ ____ __ / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / //_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ \ No newline at end of file + _______ __ ____ ____ __ + / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ + / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ + / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / +/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ + diff --git a/tools/headers/glances b/tools/headers/glances index 8ed1d92b9..4e35ca0d9 100644 --- a/tools/headers/glances +++ b/tools/headers/glances @@ -1 +1,6 @@ - ________ / ____/ /___ _____ ________ _____ / / __/ / __ `/ __ \/ ___/ _ \/ ___// /_/ / / /_/ / / / / /__/ __(__ ) \____/_/\__,_/_/ /_/\___/\___/____/ \ No newline at end of file + ________ + / ____/ /___ _____ ________ _____ + / / __/ / __ `/ __ \/ ___/ _ \/ ___/ +/ /_/ / / /_/ / / / / /__/ __(__ ) +\____/_/\__,_/_/ /_/\___/\___/____/ + diff --git a/tools/headers/meilisearch-ui b/tools/headers/meilisearch-ui index e7dc714dd..c90a9e839 100644 --- a/tools/headers/meilisearch-ui +++ b/tools/headers/meilisearch-ui @@ -1 +1,6 @@ - __ ___ _ ___ __ __ ______ / |/ /__ (_) (_)_______ ____ ___________/ /_ / / / / _/ / /|_/ / _ \/ / / / ___/ _ \/ __ `/ ___/ ___/ __ \ / / / // / / / / / __/ / / (__ ) __/ /_/ / / / /__/ / / / / /_/ // / /_/ /_/\___/_/_/_/____/\___/\__,_/_/ \___/_/ /_/ \____/___/ \ No newline at end of file + __ ___ _ ___ __ __ ______ + / |/ /__ (_) (_)_______ ____ ___________/ /_ / / / / _/ + / /|_/ / _ \/ / / / ___/ _ \/ __ `/ ___/ ___/ __ \ / / / // / + / / / / __/ / / (__ ) __/ /_/ / / / /__/ / / / / /_/ // / +/_/ /_/\___/_/_/_/____/\___/\__,_/_/ \___/_/ /_/ \____/___/ + diff --git a/tools/headers/oci-deploy b/tools/headers/oci-deploy index a950269fc..5a809b39b 100644 --- a/tools/headers/oci-deploy +++ b/tools/headers/oci-deploy @@ -1 +1,6 @@ - ____ __________ ______ __ _ / __ \/ ____/ _/ / ____/___ ____ / /_____ _(_)___ ___ _____ / / / / / / /_____/ / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___// /_/ / /____/ /_____/ /___/ /_/ / / / / /_/ /_/ / / / / / __/ / \____/\____/___/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ \ No newline at end of file + ____ __________ ______ __ _ + / __ \/ ____/ _/ / ____/___ ____ / /_____ _(_)___ ___ _____ + / / / / / / /_____/ / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/ +/ /_/ / /____/ /_____/ /___/ /_/ / / / / /_/ /_/ / / / / / __/ / +\____/\____/___/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ + diff --git a/tools/headers/phpmyadmin b/tools/headers/phpmyadmin index 047f0d0d6..5bfc88af8 100644 --- a/tools/headers/phpmyadmin +++ b/tools/headers/phpmyadmin @@ -1 +1,6 @@ - __ __ ___ ___ __ _ ____ / /_ ____ / |/ /_ __/ | ____/ /___ ___ (_)___ / __ \/ __ \/ __ \/ /|_/ / / / / /| |/ __ / __ `__ \/ / __ \ / /_/ / / / / /_/ / / / / /_/ / ___ / /_/ / / / / / / / / / / / .___/_/ /_/ .___/_/ /_/\__, /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ /_/ /_/ /____/ \ No newline at end of file + __ __ ___ ___ __ _ + ____ / /_ ____ / |/ /_ __/ | ____/ /___ ___ (_)___ + / __ \/ __ \/ __ \/ /|_/ / / / / /| |/ __ / __ `__ \/ / __ \ + / /_/ / / / / /_/ / / / / /_/ / ___ / /_/ / / / / / / / / / / + / .___/_/ /_/ .___/_/ /_/\__, /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ +/_/ /_/ /____/ diff --git a/tools/headers/prx-add-ips b/tools/headers/prx-add-ips index dd21b719d..707c586c2 100644 --- a/tools/headers/prx-add-ips +++ b/tools/headers/prx-add-ips @@ -1 +1,6 @@ - ____ ___ __ __ ________ / __ \_________ _ ______ ___ ____ _ __ / | ____/ /___/ / / _/ __ \_____ / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / /| |/ __ / __ /_____ / // /_/ / ___/ / ____/ / / /_/ /> < / ___ / /_/ / /_/ /_____// // ____(__ ) /_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| /_/ |_\__,_/\__,_/ /___/_/ /____/ \ No newline at end of file + ____ ___ __ __ ________ + / __ \_________ _ ______ ___ ____ _ __ / | ____/ /___/ / / _/ __ \_____ + / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / /| |/ __ / __ /_____ / // /_/ / ___/ + / ____/ / / /_/ /> < / ___ / /_/ / /_/ /_____// // ____(__ ) +/_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| /_/ |_\__,_/\__,_/ /___/_/ /____/ + diff --git a/tools/headers/pve-privilege-converter b/tools/headers/pve-privilege-converter index 2d4d82797..621e03056 100644 --- a/tools/headers/pve-privilege-converter +++ b/tools/headers/pve-privilege-converter @@ -1 +1,6 @@ - ____ _ ________ ____ _ _ __ ______ __ / __ \ | / / ____/ / __ \_____(_) __(_) /__ ____ ____ / ____/___ ____ _ _____ _____/ /____ _____ / /_/ / | / / __/______/ /_/ / ___/ / | / / / / _ \/ __ `/ _ \______/ / / __ \/ __ \ | / / _ \/ ___/ __/ _ \/ ___/ / ____/| |/ / /__/_____/ ____/ / / /| |/ / / / __/ /_/ / __/_____/ /___/ /_/ / / / / |/ / __/ / / /_/ __/ / /_/ |___/_____/ /_/ /_/ /_/ |___/_/_/\___/\__, /\___/ \____/\____/_/ /_/|___/\___/_/ \__/\___/_/ /____/ \ No newline at end of file + ____ _ ________ ____ _ _ __ ______ __ + / __ \ | / / ____/ / __ \_____(_) __(_) /__ ____ ____ / ____/___ ____ _ _____ _____/ /____ _____ + / /_/ / | / / __/______/ /_/ / ___/ / | / / / / _ \/ __ `/ _ \______/ / / __ \/ __ \ | / / _ \/ ___/ __/ _ \/ ___/ + / ____/| |/ / /__/_____/ ____/ / / /| |/ / / / __/ /_/ / __/_____/ /___/ /_/ / / / / |/ / __/ / / /_/ __/ / +/_/ |___/_____/ /_/ /_/ /_/ |___/_/_/\___/\__, /\___/ \____/\____/_/ /_/|___/\___/_/ \__/\___/_/ + /____/ diff --git a/tools/headers/saltminion b/tools/headers/saltminion index 05d3a3706..a624df074 100644 --- a/tools/headers/saltminion +++ b/tools/headers/saltminion @@ -1 +1,6 @@ - ____ _ _ _________ _/ / /_____ ___ (_)___ (_)___ ____ / ___/ __ `/ / __/ __ `__ \/ / __ \/ / __ \/ __ \ (__ ) /_/ / / /_/ / / / / / / / / / / /_/ / / / //____/\__,_/_/\__/_/ /_/ /_/_/_/ /_/_/\____/_/ /_/ \ No newline at end of file + ____ _ _ + _________ _/ / /_____ ___ (_)___ (_)___ ____ + / ___/ __ `/ / __/ __ `__ \/ / __ \/ / __ \/ __ \ + (__ ) /_/ / / /_/ / / / / / / / / / / /_/ / / / / +/____/\__,_/_/\__/_/ /_/ /_/_/_/ /_/_/\____/_/ /_/ + diff --git a/vm/headers/allstarlink-vm b/vm/headers/allstarlink-vm index b27f98ac0..ea38cad61 100644 --- a/vm/headers/allstarlink-vm +++ b/vm/headers/allstarlink-vm @@ -1 +1,6 @@ - __ __ _ ______ ____/ /__ / /_ (_)___ _____ < /__ \_ ______ ___ / __ / _ \/ __ \/ / __ `/ __ \/ /__/ / | / / __ `__ \/ /_/ / __/ /_/ / / /_/ / / / / // __/| |/ / / / / / /\__,_/\___/_.___/_/\__,_/_/ /_/_//____/|___/_/ /_/ /_/ \ No newline at end of file + __ __ _ ______ + ____/ /__ / /_ (_)___ _____ < /__ \_ ______ ___ + / __ / _ \/ __ \/ / __ `/ __ \/ /__/ / | / / __ `__ \ +/ /_/ / __/ /_/ / / /_/ / / / / // __/| |/ / / / / / / +\__,_/\___/_.___/_/\__,_/_/ /_/_//____/|___/_/ /_/ /_/ + diff --git a/vm/headers/archlinux-vm b/vm/headers/archlinux-vm index dc34bff7d..21f6e897f 100644 --- a/vm/headers/archlinux-vm +++ b/vm/headers/archlinux-vm @@ -1 +1,6 @@ - __ ___ ____ ___________/ /_ / (_)___ __ ___ __ _ ______ ___ / __ `/ ___/ ___/ __ \______/ / / __ \/ / / / |/_/____| | / / __ `__ \/ /_/ / / / /__/ / / /_____/ / / / / / /_/ /> Date: Tue, 13 Jan 2026 11:33:13 +0100 Subject: [PATCH 336/378] remove channel --- ct/ampache.sh | 4 +--- install/ampache-install.sh | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ct/ampache.sh b/ct/ampache.sh index b00467652..044b8e342 100644 --- a/ct/ampache.sh +++ b/ct/ampache.sh @@ -36,7 +36,6 @@ function update_script() { cp /opt/ampache/config/ampache.cfg.php /tmp/ampache.cfg.php.backup cp /opt/ampache/public/rest/.htaccess /tmp/ampache_rest.htaccess.backup cp /opt/ampache/public/play/.htaccess /tmp/ampache_play.htaccess.backup - cp /opt/ampache/public/channel/.htaccess /tmp/ampache_channel.htaccess.backup msg_ok "Backed up Configuration" msg_info "Backup Ampache Folder" @@ -50,8 +49,7 @@ function update_script() { cp /tmp/ampache.cfg.php.backup /opt/ampache/config/ampache.cfg.php cp /tmp/ampache_rest.htaccess.backup /opt/ampache/public/rest/.htaccess cp /tmp/ampache_play.htaccess.backup /opt/ampache/public/play/.htaccess - cp /tmp/ampache_channel.htaccess.backup /opt/ampache/public/channel/.htaccess - chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess /opt/ampache/public/channel/.htaccess + chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess chown -R www-data:www-data /opt/ampache rm -f /tmp/ampache*.backup msg_ok "Restored Configuration" diff --git a/install/ampache-install.sh b/install/ampache-install.sh index f4faac159..d4a9cfdb0 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -38,9 +38,8 @@ rm -rf /var/www/html ln -s /opt/ampache/public /var/www/html mv /opt/ampache/public/rest/.htaccess.dist /opt/ampache/public/rest/.htaccess mv /opt/ampache/public/play/.htaccess.dist /opt/ampache/public/play/.htaccess -mv /opt/ampache/public/channel/.htaccess.dist /opt/ampache/public/channel/.htaccess cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php -chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess /opt/ampache/public/channel/.htaccess +chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess chown -R www-data:www-data /opt/ampache msg_ok "Set up Ampache" From 6343615d85cb92ae725554007613a5ede55a1d5a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:36:19 +0100 Subject: [PATCH 337/378] json fix --- frontend/public/json/qui.json | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/frontend/public/json/qui.json b/frontend/public/json/qui.json index bef257e27..1c9c8f722 100644 --- a/frontend/public/json/qui.json +++ b/frontend/public/json/qui.json @@ -31,18 +31,5 @@ "username": null, "password": null }, - "notes": [ - { - "text": "After installation, create a user account by entering the container and running `qui create-user`.", - "type": "info" - }, - { - "text": "Configuration file is located at /root/.config/qui/config.toml", - "type": "info" - }, - { - "text": "Database is located at /root/.config/qui/qui.db", - "type": "info" - } - ] + "notes": [] } From b3a586c265141a809d327476623668cee69474f2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:56:15 +0100 Subject: [PATCH 338/378] testing --- install/ampache-install.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/install/ampache-install.sh b/install/ampache-install.sh index d4a9cfdb0..48fa2826a 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -40,9 +40,20 @@ mv /opt/ampache/public/rest/.htaccess.dist /opt/ampache/public/rest/.htaccess mv /opt/ampache/public/play/.htaccess.dist /opt/ampache/public/play/.htaccess cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess -chown -R www-data:www-data /opt/ampache msg_ok "Set up Ampache" +# msg_info "Configuring Database Connection" +# sed -i 's|^database_hostname = .*|database_hostname = "localhost"|' /opt/ampache/config/ampache.cfg.php +# sed -i 's|^database_name = .*|database_name = "ampache"|' /opt/ampache/config/ampache.cfg.php +# sed -i 's|^database_username = .*|database_username = "ampache"|' /opt/ampache/config/ampache.cfg.php +# sed -i "s|^database_password = .*|database_password = \"${MARIADB_DB_PASS}\"|" /opt/ampache/config/ampache.cfg.php +# chown -R www-data:www-data /opt/ampache +# msg_ok "Configured Database Connection" + +# msg_info "Importing Database Schema" +# mariadb -u ampache -p"${MARIADB_DB_PASS}" ampache < /opt/ampache/resources/sql/ampache.sql +# msg_ok "Imported Database Schema" + msg_info "Configuring PHP" sed -i 's/upload_max_filesize = .*/upload_max_filesize = 100M/' /etc/php/8.4/apache2/php.ini sed -i 's/post_max_size = .*/post_max_size = 100M/' /etc/php/8.4/apache2/php.ini From 8ef981a18bf5e7012b35ecd69b82629e11ad3c68 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 12:29:46 +0100 Subject: [PATCH 339/378] db prefill --- install/ampache-install.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/install/ampache-install.sh b/install/ampache-install.sh index 48fa2826a..811548ed5 100644 --- a/install/ampache-install.sh +++ b/install/ampache-install.sh @@ -42,17 +42,17 @@ cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess msg_ok "Set up Ampache" -# msg_info "Configuring Database Connection" -# sed -i 's|^database_hostname = .*|database_hostname = "localhost"|' /opt/ampache/config/ampache.cfg.php -# sed -i 's|^database_name = .*|database_name = "ampache"|' /opt/ampache/config/ampache.cfg.php -# sed -i 's|^database_username = .*|database_username = "ampache"|' /opt/ampache/config/ampache.cfg.php -# sed -i "s|^database_password = .*|database_password = \"${MARIADB_DB_PASS}\"|" /opt/ampache/config/ampache.cfg.php -# chown -R www-data:www-data /opt/ampache -# msg_ok "Configured Database Connection" +msg_info "Configuring Database Connection" +sed -i 's|^database_hostname = .*|database_hostname = "localhost"|' /opt/ampache/config/ampache.cfg.php +sed -i 's|^database_name = .*|database_name = "ampache"|' /opt/ampache/config/ampache.cfg.php +sed -i 's|^database_username = .*|database_username = "ampache"|' /opt/ampache/config/ampache.cfg.php +sed -i "s|^database_password = .*|database_password = \"${MARIADB_DB_PASS}\"|" /opt/ampache/config/ampache.cfg.php +chown -R www-data:www-data /opt/ampache +msg_ok "Configured Database Connection" -# msg_info "Importing Database Schema" -# mariadb -u ampache -p"${MARIADB_DB_PASS}" ampache < /opt/ampache/resources/sql/ampache.sql -# msg_ok "Imported Database Schema" +msg_info "Importing Database Schema" +mariadb -u ampache -p"${MARIADB_DB_PASS}" ampache Date: Tue, 13 Jan 2026 13:51:37 +0100 Subject: [PATCH 340/378] finalize json --- frontend/public/json/ampache.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/ampache.json b/frontend/public/json/ampache.json index 16df69787..d16099011 100644 --- a/frontend/public/json/ampache.json +++ b/frontend/public/json/ampache.json @@ -37,11 +37,11 @@ "type": "info" }, { - "text": "Database credentials are stored in ~/ampache.creds", + "text": "Database credentials are stored in `~/ampache.creds` - use only the MySQL username and password from this file", "type": "info" }, { - "text": "Supports multiple music formats with transcoding capabilities", + "text": "During installation, only check 'Create Tables' - leave 'Create Database' and 'Create Database User' unchecked", "type": "info" } ] From e40099236267db763ba3c2821c84f64c1fd7f889 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 13:56:06 +0100 Subject: [PATCH 341/378] update message --- ct/ampache.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ct/ampache.sh b/ct/ampache.sh index 044b8e342..5bc082f0f 100644 --- a/ct/ampache.sh +++ b/ct/ampache.sh @@ -51,13 +51,17 @@ function update_script() { cp /tmp/ampache_play.htaccess.backup /opt/ampache/public/play/.htaccess chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess chown -R www-data:www-data /opt/ampache - rm -f /tmp/ampache*.backup msg_ok "Restored Configuration" + msg_info "Cleaning up" + rm -f /tmp/ampache*.backup + msg_ok "Cleaned up" + msg_info "Starting Apache" systemctl start apache2 msg_ok "Started Apache" msg_ok "Updated successfully!" + msg_custom "⚠️" "${YW}" "Complete database update by visiting: http://${IP}/update.php" fi exit } From 4710550d8d062be19392898f15fe9deb73723d32 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 13:57:15 +0100 Subject: [PATCH 342/378] local_ip --- ct/ampache.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/ampache.sh b/ct/ampache.sh index 5bc082f0f..ef6bb7903 100644 --- a/ct/ampache.sh +++ b/ct/ampache.sh @@ -23,11 +23,13 @@ function update_script() { header_info check_container_storage check_container_resources + if [[ ! -d /opt/ampache ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Ampache" "ampache/ampache"; then + import_local_ip msg_info "Stopping Apache" systemctl stop apache2 msg_ok "Stopped Apache" @@ -61,7 +63,7 @@ function update_script() { systemctl start apache2 msg_ok "Started Apache" msg_ok "Updated successfully!" - msg_custom "⚠️" "${YW}" "Complete database update by visiting: http://${IP}/update.php" + msg_custom "⚠️" "${YW}" "Complete database update by visiting: http://${LOCAL_IP}/update.php" fi exit } From aef1cf79f3e0a3c7b1f932b565c9abb4b7a4e48e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 13 Jan 2026 14:34:58 +0100 Subject: [PATCH 343/378] nodejs binary method --- misc/tools.func | 181 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 159 insertions(+), 22 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 672af628a..dc9c39a8d 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -344,6 +344,11 @@ remove_old_tool_version() { npm uninstall -g "$module" >/dev/null 2>&1 || true done || true fi + # Remove tarball installation if exists + if [[ -d "/usr/local/lib/nodejs" ]]; then + rm -rf /usr/local/lib/nodejs + rm -f /usr/local/bin/node /usr/local/bin/npm /usr/local/bin/npx + fi cleanup_legacy_install "nodejs" cleanup_tool_keyrings "nodesource" ;; @@ -4092,21 +4097,128 @@ EOF msg_ok "Setup MySQL $MYSQL_VERSION" } +# ------------------------------------------------------------------------------ +# Installs Node.js from official prebuilt binaries (alternative to NodeSource repo). +# +# Description: +# - Downloads official Node.js prebuilt binaries from nodejs.org +# - Extracts to /usr/local and creates symlinks +# - No package manager dependencies, no GPG issues +# +# Arguments: +# $1 - Node.js major version (e.g. 22, 24) +# +# Returns: +# 0 on success, 1 on failure +# ------------------------------------------------------------------------------ + +function install_nodejs_from_binary() { + local NODE_VERSION="$1" + local ARCH + + # Determine system architecture + ARCH=$(dpkg --print-architecture 2>/dev/null || uname -m) + case "$ARCH" in + amd64 | x86_64) ARCH="x64" ;; + arm64 | aarch64) ARCH="arm64" ;; + armv7l | armhf) ARCH="armv7l" ;; + ppc64le) ARCH="ppc64le" ;; + s390x) ARCH="s390x" ;; + *) + msg_error "Unsupported architecture: $ARCH" + return 1 + ;; + esac + + msg_info "Fetching Node.js v${NODE_VERSION}.x binary metadata" + + # Fetch latest version info from nodejs.org + local LATEST_URL="https://nodejs.org/dist/latest-v${NODE_VERSION}.x/" + local VERSION_INFO + VERSION_INFO=$(curl -fsSL "$LATEST_URL" 2>/dev/null | grep -oP "node-v${NODE_VERSION}\.[0-9]+\.[0-9]+-linux-${ARCH}\.tar\.xz" | head -n1) + + if [[ -z "$VERSION_INFO" ]]; then + msg_error "Could not find Node.js v${NODE_VERSION}.x binary for linux-${ARCH}" + return 1 + fi + + local FULL_VERSION + FULL_VERSION=$(echo "$VERSION_INFO" | grep -oP "v${NODE_VERSION}\.[0-9]+\.[0-9]+") + local TARBALL_NAME="node-${FULL_VERSION}-linux-${ARCH}.tar.xz" + local TARBALL_URL="${LATEST_URL}${TARBALL_NAME}" + local CHECKSUM_URL="${LATEST_URL}SHASUMS256.txt" + + msg_info "Downloading Node.js ${FULL_VERSION} for linux-${ARCH}" + + # Download tarball and checksum + local TEMP_DIR + TEMP_DIR=$(mktemp -d) + trap "rm -rf '$TEMP_DIR'" EXIT + + if ! curl -fsSL -o "${TEMP_DIR}/${TARBALL_NAME}" "$TARBALL_URL"; then + msg_error "Failed to download Node.js binary from $TARBALL_URL" + return 1 + fi + + if ! curl -fsSL -o "${TEMP_DIR}/SHASUMS256.txt" "$CHECKSUM_URL"; then + msg_warn "Could not download checksum file - skipping verification" + else + # Verify checksum + msg_info "Verifying checksum" + cd "$TEMP_DIR" || return 1 + if ! grep "$TARBALL_NAME" SHASUMS256.txt | sha256sum -c - >/dev/null 2>&1; then + msg_error "Checksum verification failed for $TARBALL_NAME" + return 1 + fi + msg_ok "Checksum verified" + fi + + # Remove old Node.js installation if exists + if [[ -d "/usr/local/lib/nodejs" ]]; then + msg_info "Removing old Node.js installation" + rm -rf /usr/local/lib/nodejs + fi + + # Extract tarball + msg_info "Installing Node.js ${FULL_VERSION}" + mkdir -p /usr/local/lib/nodejs + tar -xJf "${TEMP_DIR}/${TARBALL_NAME}" -C /usr/local/lib/nodejs --strip-components=1 + + # Create symlinks + ln -sf /usr/local/lib/nodejs/bin/node /usr/local/bin/node + ln -sf /usr/local/lib/nodejs/bin/npm /usr/local/bin/npm + ln -sf /usr/local/lib/nodejs/bin/npx /usr/local/bin/npx + + # Verify installation + if ! /usr/local/bin/node --version >/dev/null 2>&1; then + msg_error "Node.js installation verification failed" + return 1 + fi + + local INSTALLED_VERSION + INSTALLED_VERSION=$(/usr/local/bin/node --version 2>/dev/null) + msg_ok "Installed Node.js ${INSTALLED_VERSION}" + + return 0 +} + # ------------------------------------------------------------------------------ # Installs Node.js and optional global modules. # # Description: -# - Installs specified Node.js version using NodeSource APT repo +# - Installs specified Node.js version using NodeSource APT repo OR official binaries # - Optionally installs or updates global npm modules # # Variables: -# NODE_VERSION - Node.js version to install (default: 24 LTS) -# NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") +# NODE_VERSION - Node.js version to install (default: 24 LTS) +# NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") +# NODE_INSTALL_METHOD - Installation method: "nodesource" (default) or "binary" # ------------------------------------------------------------------------------ function setup_nodejs() { local NODE_VERSION="${NODE_VERSION:-24}" local NODE_MODULE="${NODE_MODULE:-}" + local NODE_INSTALL_METHOD="${NODE_INSTALL_METHOD:-nodesource}" # ALWAYS clean up legacy installations first (nvm, etc.) to prevent conflicts cleanup_legacy_install "nodejs" @@ -4128,7 +4240,17 @@ function setup_nodejs() { if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" == "$NODE_VERSION" ]]; then msg_info "Update Node.js $NODE_VERSION" - ensure_apt_working || return 1 + # For binary method, check if there's a newer patch version available + if [[ "$NODE_INSTALL_METHOD" == "binary" ]]; then + local CURRENT_FULL_VERSION + CURRENT_FULL_VERSION=$(node -v 2>/dev/null) + msg_info "Current version: $CURRENT_FULL_VERSION - checking for updates" + + # Try to install latest patch version + install_nodejs_from_binary "$NODE_VERSION" || { + msg_warn "Failed to check for Node.js updates, keeping current version" + } + fi # Just update npm to latest $STD npm install -g npm@latest 2>/dev/null || true @@ -4141,7 +4263,7 @@ function setup_nodejs() { 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" + msg_info "Setup Node.js $NODE_VERSION (method: $NODE_INSTALL_METHOD)" fi # Remove ALL Debian nodejs packages BEFORE adding NodeSource repo @@ -4155,27 +4277,42 @@ function setup_nodejs() { # Remove any APT pinning (not needed) rm -f /etc/apt/preferences.d/nodesource 2>/dev/null || true - # Prepare repository (cleanup + validation) - prepare_repository_setup "nodesource" || { - msg_error "Failed to prepare Node.js repository" - return 1 - } + # Choose installation method + if [[ "$NODE_INSTALL_METHOD" == "binary" ]]; then + # Method 1: Install from official nodejs.org prebuilt binaries (no repo needed) + msg_info "Installing Node.js from official binaries" + ensure_dependencies curl ca-certificates - # 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 - } + install_nodejs_from_binary "$NODE_VERSION" || { + msg_error "Failed to install Node.js from binary" + return 1 + } + else + # Method 2: Install from NodeSource APT repository (traditional method) + msg_info "Installing Node.js from NodeSource repository" - # Force APT cache refresh after repository setup - $STD apt update + # Prepare repository (cleanup + validation) + prepare_repository_setup "nodesource" || { + msg_error "Failed to prepare Node.js repository" + return 1 + } - ensure_dependencies curl ca-certificates gnupg + # 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 + } - install_packages_with_retry "nodejs" || { - msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" - return 1 - } + # Force APT cache refresh after repository setup + $STD apt update + + ensure_dependencies curl ca-certificates gnupg + + 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 From fed9fd9db87ab10fd9b857c0d1b8629165e0c0b8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 09:27:14 +0100 Subject: [PATCH 344/378] fixes tag name github --- install/papra-install.sh | 6 +++--- misc/tools.func | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index efb8cfff0..7a5134881 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -15,9 +15,9 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - build-essential \ - tesseract-ocr \ - tesseract-ocr-all + build-essential \ + tesseract-ocr \ + tesseract-ocr-all msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs diff --git a/misc/tools.func b/misc/tools.func index dc9c39a8d..52479f658 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1770,7 +1770,9 @@ function fetch_and_deploy_gh_release() { if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then # GitHub API's tarball_url/zipball_url can return HTTP 300 Multiple Choices # when a branch and tag share the same name. Use explicit refs/tags/ URL instead. - local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$tag_name.tar.gz" + # URL-encode @ symbols in tag names + local encoded_tag_name="${tag_name//@/%40}" + local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$encoded_tag_name.tar.gz" filename="${app_lc}-${version}.tar.gz" curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" || { From 17d04e2d1dc78ab28880fe4fe555867a39400206 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 09:33:37 +0100 Subject: [PATCH 345/378] fix space --- misc/tools.func | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 52479f658..f3c288764 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1770,8 +1770,9 @@ function fetch_and_deploy_gh_release() { if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then # GitHub API's tarball_url/zipball_url can return HTTP 300 Multiple Choices # when a branch and tag share the same name. Use explicit refs/tags/ URL instead. - # URL-encode @ symbols in tag names + # URL-encode special characters in tag names (@ and /) local encoded_tag_name="${tag_name//@/%40}" + encoded_tag_name="${encoded_tag_name//\//%2F}" local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$encoded_tag_name.tar.gz" filename="${app_lc}-${version}.tar.gz" From fa295d91945555a85699eadfcb355ab3032bbdd0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:01:56 +0100 Subject: [PATCH 346/378] thingsboard --- ct/thingsboard.sh | 57 +++++++++++++++++++++++++++ frontend/public/json/thingsboard.json | 48 ++++++++++++++++++++++ install/thingsboard-install.sh | 51 ++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 ct/thingsboard.sh create mode 100644 frontend/public/json/thingsboard.json create mode 100644 install/thingsboard-install.sh diff --git a/ct/thingsboard.sh b/ct/thingsboard.sh new file mode 100644 index 000000000..7fadb1892 --- /dev/null +++ b/ct/thingsboard.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/thingsboard/thingsboard + +APP="ThingsBoard" +var_tags="${var_tags:-iot;platform}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +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 /usr/share/thingsboard ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "thingsboard" "thingsboard/thingsboard"; then + msg_info "Stopping Service" + systemctl stop thingsboard + msg_ok "Stopped Service" + + fetch_and_deploy_gh_release "thingsboard" "thingsboard/thingsboard" "binary" "latest" "/tmp" "thingsboard-*.deb" + + msg_info "Running Database Upgrade" + $STD /usr/share/thingsboard/bin/install/upgrade.sh + msg_ok "Database Upgraded" + + msg_info "Starting Service" + systemctl start thingsboard + 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/thingsboard.json b/frontend/public/json/thingsboard.json new file mode 100644 index 000000000..5f38ad3ad --- /dev/null +++ b/frontend/public/json/thingsboard.json @@ -0,0 +1,48 @@ +{ + "name": "ThingsBoard", + "slug": "thingsboard", + "categories": [ + 7 + ], + "date_created": "2026-01-14", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://thingsboard.io/docs/", + "website": "https://thingsboard.io/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/thingsboard.webp", + "config_path": "/etc/thingsboard/conf/thingsboard.conf", + "description": "ThingsBoard is an open-source IoT platform for data collection, processing, visualization, and device management. It enables device connectivity via industry standard IoT protocols - MQTT, CoAP and HTTP and supports both cloud and on-premises deployments.", + "install_methods": [ + { + "type": "default", + "script": "ct/thingsboard.sh", + "resources": { + "cpu": 4, + "ram": 4096, + "hdd": 10, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": "sysadmin@thingsboard.org", + "password": "sysadmin" + }, + "notes": [ + { + "text": "Please allow up to 90 seconds for the Web UI to start", + "type": "info" + }, + { + "text": "Additional demo accounts: tenant@thingsboard.org / tenant and customer@thingsboard.org / customer", + "type": "info" + }, + { + "text": "Change passwords for all accounts in the account profile page after first login", + "type": "warning" + } + ] +} diff --git a/install/thingsboard-install.sh b/install/thingsboard-install.sh new file mode 100644 index 000000000..4745b2fe9 --- /dev/null +++ b/install/thingsboard-install.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/thingsboard/thingsboard + +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 \ + libharfbuzz0b \ + fontconfig \ + fonts-dejavu-core +msg_ok "Installed Dependencies" + +JAVA_VERSION="17" setup_java +PG_VERSION="16" setup_postgresql +PG_DB_NAME="thingsboard_db" PG_DB_USER="thingsboard" setup_postgresql_db +fetch_and_deploy_gh_release "thingsboard" "thingsboard/thingsboard" "binary" "latest" "/tmp" "thingsboard-*.deb" + +msg_info "Configuring ThingsBoard" +cat >/etc/thingsboard/conf/thingsboard.conf < Date: Wed, 14 Jan 2026 13:20:20 +0100 Subject: [PATCH 347/378] piler --- ct/piler.sh | 4 ++-- install/piler-install.sh | 32 +++++++++++--------------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/ct/piler.sh b/ct/piler.sh index bfd2831c0..846db7d96 100644 --- a/ct/piler.sh +++ b/ct/piler.sh @@ -10,8 +10,8 @@ var_tags="${var_tags:-email;archive;smtp}" 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_os="${var_os:-ubuntu}" +var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" diff --git a/install/piler-install.sh b/install/piler-install.sh index 42ce2d065..7ed5fc8e6 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -22,12 +22,12 @@ $STD apt install -y \ poppler-utils \ unrtf \ tnef \ - clamav \ - clamav-daemon \ memcached \ sysstat \ python3 \ - python3-mysqldb + python3-mysqldb \ + ca-certificates \ + gnupg msg_ok "Installed Dependencies" import_local_ip @@ -36,26 +36,17 @@ MARIADB_DB_NAME="piler" MARIADB_DB_USER="piler" setup_mysql_db PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="ldap,gd,memcached,pdo,mysql,curl,zip" setup_php msg_info "Installing Manticore Search" -curl -fsSL https://repo.manticoresearch.com/manticore-repo.noarch.deb -o /tmp/manticore-repo.deb -$STD dpkg -i /tmp/manticore-repo.deb +cd /tmp +wget -q https://repo.manticoresearch.com/manticore-repo.noarch.deb +$STD dpkg -i /tmp/manticore-repo.noarch.deb $STD apt update -$STD apt install -y manticore manticore-extra -rm -f /tmp/manticore-repo.deb +$STD apt install -y manticore manticore-columnar-lib manticore-extra +rm -f /tmp/manticore-repo.noarch.deb +$STD systemctl stop manticore +$STD systemctl disable manticore msg_ok "Installed Manticore Search" -msg_info "Installing Piler" -VERSION="1.4.8" -cd /tmp -curl -fsSL "https://github.com/jsuto/piler/releases/download/piler-${VERSION}/piler_${VERSION}-bookworm_amd64.deb" -o piler.deb -curl -fsSL "https://github.com/jsuto/piler/releases/download/piler-${VERSION}/piler-webui_${VERSION}-bookworm_amd64.deb" -o piler-webui.deb - -$STD dpkg -i piler.deb -$STD apt-get -f install -y -$STD dpkg -i piler-webui.deb -$STD apt-get -f install -y - -rm -f piler.deb piler-webui.deb -msg_ok "Installed Piler v${VERSION}" +fetch_and_deploy_gh_release "piler" "jsuto/piler" "binary" "latest" "/tmp" "piler_*-noble-*_amd64.deb" msg_info "Configuring Piler Database" cd /usr/local/share/piler @@ -64,7 +55,6 @@ msg_ok "Configured Piler Database" msg_info "Configuring Piler" PILER_KEY=$(openssl rand -hex 16) - cat </etc/piler/piler.conf hostid=piler.${LOCAL_IP}.nip.io update_counters_to_memcached=1 From d9639c8584ff85694884cd6679d2ccb759b0085d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 12:42:53 +0000 Subject: [PATCH 348/378] First try --- ct/nodecasttv.sh | 54 +++++++++++++++++++++++++++++++++++ install/nodecasttv-install.sh | 41 ++++++++++++++++++++++++++ misc/build.func | 6 ++-- 3 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 ct/nodecasttv.sh create mode 100644 install/nodecasttv-install.sh diff --git a/ct/nodecasttv.sh b/ct/nodecasttv.sh new file mode 100644 index 000000000..71e2c6b38 --- /dev/null +++ b/ct/nodecasttv.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/nodecast-tv/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: luismco +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/BlessedRebuS/Krawl + +APP="Nodecasttv" +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:-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/nodecast-tv ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "nodecast-tv" "technomancer702/nodecast-tv"; then + msg_info "Stopping Service" + systemctl stop nodecast-tv + msg_ok "Stopped Service" + + fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" + + msg_info "Starting Service" + systemctl start nodecast-tv + 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}" + diff --git a/install/nodecasttv-install.sh b/install/nodecasttv-install.sh new file mode 100644 index 000000000..c295adcb9 --- /dev/null +++ b/install/nodecasttv-install.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: luismco +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ThePhaseless/Byparr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" +setup_nodejs + +msg_info "Creating Service" +cat </etc/systemd/system/nodecast-tv.service +[Unit] +Description=nodecast-tv +After=network.target +Wants=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/nodecast-tv +ExecStart=/bin/bash -c 'exec /bin/npm install && /bin/npm run dev' +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now nodecast-tv +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/misc/build.func b/misc/build.func index 06923e925..6abdb8950 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2828,7 +2828,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - 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/luismco/ProxmoxVED/refs/heads/krawl/misc/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -3419,7 +3419,7 @@ chmod +x /etc/profile.d/term.sh" || true set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - 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/luismco/ProxmoxVED/refs/heads/krawl/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -3506,7 +3506,7 @@ chmod +x /etc/profile.d/term.sh" || true if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func) + source <(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/krawl/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From 3bce94c6ac176b5ce1e0ff770a9fdd813758fa35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 12:47:52 +0000 Subject: [PATCH 349/378] build.func changes --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 6abdb8950..d58aa9ea6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3419,7 +3419,7 @@ chmod +x /etc/profile.d/term.sh" || true set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/krawl/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/nodecast-tv/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling From e266debc3dd82afbd8c17c1547db56e6bbd75e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 13:09:14 +0000 Subject: [PATCH 350/378] Changes to the service --- install/nodecasttv-install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/install/nodecasttv-install.sh b/install/nodecasttv-install.sh index c295adcb9..a378deeb3 100644 --- a/install/nodecasttv-install.sh +++ b/install/nodecasttv-install.sh @@ -15,6 +15,10 @@ update_os fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" setup_nodejs +msg_info "Installing Modules" +cd /opt/nodecast-tv +$STD npm install +msg_ok "Installed Modules" msg_info "Creating Service" cat </etc/systemd/system/nodecast-tv.service @@ -26,7 +30,7 @@ Wants=network.target [Service] Type=simple WorkingDirectory=/opt/nodecast-tv -ExecStart=/bin/bash -c 'exec /bin/npm install && /bin/npm run dev' +ExecStart=/bin/npm run dev Restart=on-failure RestartSec=10 From 4f01c94894c6274840715e24cc177b5033bca9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 13:18:38 +0000 Subject: [PATCH 351/378] update flow updated --- ct/nodecasttv.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ct/nodecasttv.sh b/ct/nodecasttv.sh index 71e2c6b38..ce23800c2 100644 --- a/ct/nodecasttv.sh +++ b/ct/nodecasttv.sh @@ -35,6 +35,11 @@ function update_script() { fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" + msg_info "Updating Modules" + cd /opt/nodecast-tv + $STD npm install + msg_ok "Updated Modules" + msg_info "Starting Service" systemctl start nodecast-tv msg_ok "Started Service" From 86c16e163dcfffe1f1ca8277d7c1da219ee20e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 13:26:35 +0000 Subject: [PATCH 352/378] Correct app name --- ct/nodecasttv.sh | 4 ++-- install/{nodecasttv-install.sh => nodecast-tv-install.sh} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename install/{nodecasttv-install.sh => nodecast-tv-install.sh} (100%) diff --git a/ct/nodecasttv.sh b/ct/nodecasttv.sh index ce23800c2..7ff85400d 100644 --- a/ct/nodecasttv.sh +++ b/ct/nodecasttv.sh @@ -3,9 +3,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/he # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/BlessedRebuS/Krawl +# Source: https://github.com/technomancer702/nodecast-tv -APP="Nodecasttv" +APP="Nodecast-tv" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" diff --git a/install/nodecasttv-install.sh b/install/nodecast-tv-install.sh similarity index 100% rename from install/nodecasttv-install.sh rename to install/nodecast-tv-install.sh From 28016c6978f76282763be966a5b513c06d47ce65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 13:35:02 +0000 Subject: [PATCH 353/378] Final commit --- ct/nodecasttv.sh | 4 +-- frontend/public/json/nodecast-tv.json | 35 +++++++++++++++++++++++++++ install/nodecast-tv-install.sh | 2 +- 3 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 frontend/public/json/nodecast-tv.json diff --git a/ct/nodecasttv.sh b/ct/nodecasttv.sh index 7ff85400d..8d03609e0 100644 --- a/ct/nodecasttv.sh +++ b/ct/nodecasttv.sh @@ -5,8 +5,8 @@ source <(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/he # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/technomancer702/nodecast-tv -APP="Nodecast-tv" -var_tags="${var_tags:-proxy}" +APP="nodecast-tv" +var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" diff --git a/frontend/public/json/nodecast-tv.json b/frontend/public/json/nodecast-tv.json new file mode 100644 index 000000000..8d3f0e1fb --- /dev/null +++ b/frontend/public/json/nodecast-tv.json @@ -0,0 +1,35 @@ +{ + "name": "nodecast-tv", + "slug": "nodecast-tv", + "categories": [ + 13 + ], + "date_created": "2026-01-14", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/technomancer702/nodecast-tv/blob/main/README.md", + "website": "https://github.com/technomancer702/nodecast-tv", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nodecast-tv.webp", + "config_path": "", + "description": "nodecast-tv is a modern, web-based IPTV player featuring Live TV, EPG, Movies (VOD), and Series support. Built with performance and user experience in mind.", + "install_methods": [ + { + "type": "default", + "script": "ct/nodecast-tv.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/nodecast-tv-install.sh b/install/nodecast-tv-install.sh index a378deeb3..cc5a2c133 100644 --- a/install/nodecast-tv-install.sh +++ b/install/nodecast-tv-install.sh @@ -3,7 +3,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/ThePhaseless/Byparr +# Source: https://github.com/technomancer702/nodecast-tv source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color From 30820a0ced9fbdde7db5f97e314afb7f04ece34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 13:36:11 +0000 Subject: [PATCH 354/378] app name corrected --- ct/{nodecasttv.sh => nodecast-tv.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ct/{nodecasttv.sh => nodecast-tv.sh} (100%) diff --git a/ct/nodecasttv.sh b/ct/nodecast-tv.sh similarity index 100% rename from ct/nodecasttv.sh rename to ct/nodecast-tv.sh From 564a39b187151342f2ff15ab9969bf6580063221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Wed, 14 Jan 2026 13:43:16 +0000 Subject: [PATCH 355/378] Ready for PR --- ct/nodecast-tv.sh | 2 +- install/nodecast-tv-install.sh | 1 + misc/build.func | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ct/nodecast-tv.sh b/ct/nodecast-tv.sh index 8d03609e0..b05c6bceb 100644 --- a/ct/nodecast-tv.sh +++ b/ct/nodecast-tv.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/nodecast-tv/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/nodecast-tv-install.sh b/install/nodecast-tv-install.sh index cc5a2c133..018d65ffd 100644 --- a/install/nodecast-tv-install.sh +++ b/install/nodecast-tv-install.sh @@ -15,6 +15,7 @@ update_os fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" setup_nodejs + msg_info "Installing Modules" cd /opt/nodecast-tv $STD npm install diff --git a/misc/build.func b/misc/build.func index d58aa9ea6..06923e925 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2828,7 +2828,7 @@ build_container() { pushd "$TEMP_DIR" >/dev/null # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/krawl/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -3419,7 +3419,7 @@ chmod +x /etc/profile.d/term.sh" || true set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/nodecast-tv/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)" local lxc_exit=$? set -Eeuo pipefail # Re-enable error handling @@ -3506,7 +3506,7 @@ chmod +x /etc/profile.d/term.sh" || true if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://raw.githubusercontent.com/luismco/ProxmoxVED/refs/heads/krawl/misc/install.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) From ad535a36c4929805df1dca4c25bbca9d6939c873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= <48201890+luismco@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:52:37 +0000 Subject: [PATCH 356/378] Update nodecast-tv.sh --- ct/nodecast-tv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/nodecast-tv.sh b/ct/nodecast-tv.sh index b05c6bceb..93d3ade53 100644 --- a/ct/nodecast-tv.sh +++ b/ct/nodecast-tv.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-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 1d14e06df870f082e2b957c9d66194d33cc31b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= <48201890+luismco@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:58:21 +0000 Subject: [PATCH 357/378] Update install/nodecast-tv-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/nodecast-tv-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nodecast-tv-install.sh b/install/nodecast-tv-install.sh index 018d65ffd..a9d2e3a7b 100644 --- a/install/nodecast-tv-install.sh +++ b/install/nodecast-tv-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/technomancer702/nodecast-tv source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From 1b89135867ac81965c3a871465f607fffbccdd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= <48201890+luismco@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:58:28 +0000 Subject: [PATCH 358/378] Update ct/nodecast-tv.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- ct/nodecast-tv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/nodecast-tv.sh b/ct/nodecast-tv.sh index 93d3ade53..202e65ecc 100644 --- a/ct/nodecast-tv.sh +++ b/ct/nodecast-tv.sh @@ -2,7 +2,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/technomancer702/nodecast-tv APP="nodecast-tv" From 94639cc61d19172a031adb4ebc746359f73c66a7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:24:40 +0100 Subject: [PATCH 359/378] optimize piler --- ct/piler.sh | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/ct/piler.sh b/ct/piler.sh index 846db7d96..c549f94fa 100644 --- a/ct/piler.sh +++ b/ct/piler.sh @@ -29,10 +29,7 @@ function update_script() { exit fi - RELEASE_NEW=$(curl -fsSL https://www.mailpiler.org/download.php | grep -oP 'piler-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) - RELEASE_OLD=$(pilerd -v 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown") - - if [[ "${RELEASE_NEW}" != "${RELEASE_OLD}" ]]; then + if check_for_gh_release "piler" "jsuto/piler"; then msg_info "Stopping Piler Services" $STD systemctl stop piler $STD systemctl stop manticore @@ -40,31 +37,22 @@ function update_script() { msg_info "Backing up Configuration" cp /etc/piler/piler.conf /tmp/piler.conf.bak + cp /var/www/piler/config-site.php /tmp/config-site.php.bak 2>/dev/null || true msg_ok "Backed up Configuration" - msg_info "Updating to v${RELEASE_NEW}" + msg_info "Updating ${APP}" cd /tmp - curl -fsSL "https://bitbucket.org/jsuto/piler/downloads/piler-${RELEASE_NEW}.tar.gz" -o piler.tar.gz - tar -xzf piler.tar.gz - cd "piler-${RELEASE_NEW}" - - $STD ./configure \ - --localstatedir=/var \ - --with-database=mysql \ - --sysconfdir=/etc/piler \ - --enable-memcached - - $STD make - $STD make install - $STD ldconfig - - cd /tmp && rm -rf "piler-${RELEASE_NEW}" piler.tar.gz - msg_ok "Updated to v${RELEASE_NEW}" + fetch_and_deploy_gh_release "piler" "jsuto/piler" "binary" "latest" "/tmp" "piler_*-noble-*_amd64.deb" + fetch_and_deploy_gh_release "piler-webui" "jsuto/piler" "binary" "latest" "/tmp" "piler-webui_*-noble-*_amd64.deb" + $STD apt-get -f install -y + msg_ok "Updated ${APP}" msg_info "Restoring Configuration" cp /tmp/piler.conf.bak /etc/piler/piler.conf - rm -f /tmp/piler.conf.bak + [[ -f /tmp/config-site.php.bak ]] && cp /tmp/config-site.php.bak /var/www/piler/config-site.php + rm -f /tmp/piler.conf.bak /tmp/config-site.php.bak chown piler:piler /etc/piler/piler.conf + chown -R piler:piler /var/www/piler 2>/dev/null || true msg_ok "Restored Configuration" msg_info "Starting Services" @@ -72,8 +60,6 @@ function update_script() { $STD systemctl start piler msg_ok "Started Services" msg_ok "Updated successfully!" - else - msg_ok "No update available (current: v${RELEASE_OLD})" fi exit } From d5620def9ed1b9f2f25011d8406de0e277930e39 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:27:54 +0100 Subject: [PATCH 360/378] postgresus > databasus --- ct/{postgresus.sh => databasus.sh} | 36 +++++++++---------- .../json/{postgresus.json => databasus.json} | 18 +++++----- .../{postgresus-install.sh => databasus.sh} | 0 install/piler-install.sh | 2 +- 4 files changed, 28 insertions(+), 28 deletions(-) rename ct/{postgresus.sh => databasus.sh} (65%) rename frontend/public/json/{postgresus.json => databasus.json} (75%) rename install/{postgresus-install.sh => databasus.sh} (100%) diff --git a/ct/postgresus.sh b/ct/databasus.sh similarity index 65% rename from ct/postgresus.sh rename to ct/databasus.sh index f3906c320..d7c2dd0a8 100644 --- a/ct/postgresus.sh +++ b/ct/databasus.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://github.com/RostislavDugin/postgresus -APP="Postgresus" +APP="Databasus" var_tags="${var_tags:-backup;postgresql;database}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" @@ -29,37 +29,37 @@ function update_script() { exit fi - if check_for_gh_release "RostislavDugin" "postgresus"; then - msg_info "Stopping Postgresus" - $STD systemctl stop postgresus - msg_ok "Stopped Postgresus" + if check_for_gh_release "Databasus" "Databasus"; then + msg_info "Stopping Databasus" + $STD systemctl stop databasus + msg_ok "Stopped Databasus" msg_info "Backing up Configuration" - cp /opt/postgresus/.env /tmp/postgresus.env.bak + cp /opt/databasus/.env /tmp/databasus.env.bak msg_ok "Backed up Configuration" - fetch_and_deploy_gh_release "postgresus" "RostislavDugin/postgresus" "tarball" "v${RELEASE}" "/opt/postgresus" + fetch_and_deploy_gh_release "databasus" "databasus/databasus" "tarball" "latest" "/opt/databasus" - msg_info "Updating Postgresus" - cd /opt/postgresus/frontend + msg_info "Updating Databasus" + cd /opt/databasus/frontend $STD npm ci $STD npm run build - cd /opt/postgresus/backend + cd /opt/databasus/backend $STD go mod download - $STD go build -o ../postgresus ./cmd/main.go - cd /opt/postgresus/ + $STD go build -o ../databasus ./cmd/main.go + cd /opt/databasus/ cp -r frontend/dist ui cp -r backend/migrations . - msg_ok "Updated Postgresus" + msg_ok "Updated Databasus" msg_info "Restoring Configuration" - cp /tmp/postgresus.env.bak /opt/postgresus/.env - rm -f /tmp/postgresus.env.bak + cp /tmp/databasus.env.bak /opt/databasus/.env + rm -f /tmp/databasus.env.bak msg_ok "Restored Configuration" - msg_info "Starting Postgresus" - $STD systemctl start postgresus - msg_ok "Started Postgresus" + msg_info "Starting Databasus" + $STD systemctl start databasus + msg_ok "Started Databasus" msg_ok "Updated successfully!" fi exit diff --git a/frontend/public/json/postgresus.json b/frontend/public/json/databasus.json similarity index 75% rename from frontend/public/json/postgresus.json rename to frontend/public/json/databasus.json index e60f642f5..766d88925 100644 --- a/frontend/public/json/postgresus.json +++ b/frontend/public/json/databasus.json @@ -1,23 +1,23 @@ { - "name": "Postgresus", - "slug": "postgresus", + "name": "Databasus", + "slug": "databasus", "categories": [ 7 ], - "date_created": "2025-12-11", + "date_created": "2025-01-14", "type": "ct", "updateable": true, "privileged": false, "interface_port": 80, - "documentation": "https://github.com/RostislavDugin/postgresus", - "website": "https://github.com/RostislavDugin/postgresus", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/postgresus.webp", - "config_path": "/opt/postgresus/.env", + "documentation": "https://github.com/databasus/databasus", + "website": "https://github.com/databasus/databasus", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/databasus.webp", + "config_path": "/opt/databasus/.env", "description": "Free, open source and self-hosted solution for automated PostgreSQL backups. With multiple storage options, notifications, scheduling, and a beautiful web interface for managing database backups across multiple PostgreSQL instances.", "install_methods": [ { "type": "default", - "script": "ct/postgresus.sh", + "script": "ct/databasus.sh", "resources": { "cpu": 2, "ram": 2048, @@ -29,7 +29,7 @@ ], "default_credentials": { "username": "admin@localhost", - "password": "See /root/postgresus.creds" + "password": "See /root/databasus.creds" }, "notes": [ { diff --git a/install/postgresus-install.sh b/install/databasus.sh similarity index 100% rename from install/postgresus-install.sh rename to install/databasus.sh diff --git a/install/piler-install.sh b/install/piler-install.sh index 7ed5fc8e6..6eb3e327f 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -32,7 +32,7 @@ msg_ok "Installed Dependencies" import_local_ip setup_mariadb -MARIADB_DB_NAME="piler" MARIADB_DB_USER="piler" setup_mysql_db +MARIADB_DB_NAME="piler" MARIADB_DB_USER="piler" setup_mariadb_db PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="ldap,gd,memcached,pdo,mysql,curl,zip" setup_php msg_info "Installing Manticore Search" From 7298ad6450209f7d8088b0bc44254409d5eddacb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:31:49 +0100 Subject: [PATCH 361/378] ref --- .../{databasus.sh => databasus-install.sh} | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) rename install/{databasus.sh => databasus-install.sh} (65%) diff --git a/install/databasus.sh b/install/databasus-install.sh similarity index 65% rename from install/databasus.sh rename to install/databasus-install.sh index f5d0cdd4d..65768c03d 100644 --- a/install/databasus.sh +++ b/install/databasus-install.sh @@ -3,7 +3,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/RostislavDugin/postgresus +# Source: https://github.com/databasus/databasus source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -19,34 +19,34 @@ msg_ok "Installed Dependencies" import_local_ip PG_VERSION="17" setup_postgresql -PG_DB_NAME="postgresus" PG_DB_USER="postgresus" setup_postgresql_db +PG_DB_NAME="databasus" PG_DB_USER="databasus" setup_postgresql_db setup_go NODE_VERSION="24" setup_nodejs -fetch_and_deploy_gh_release "postgresus" "RostislavDugin/postgresus" "tarball" "latest" "/opt/postgresus" +fetch_and_deploy_gh_release "databasus" "databasus/databasus" "tarball" "latest" "/opt/databasus" -msg_info "Building Postgresus (Patience)" -cd /opt/postgresus/frontend +msg_info "Building Databasus (Patience)" +cd /opt/databasus/frontend $STD npm ci $STD npm run build -cd /opt/postgresus/backend +cd /opt/databasus/backend $STD go mod tidy $STD go mod download $STD go install github.com/swaggo/swag/cmd/swag@latest $STD /root/go/bin/swag init -g cmd/main.go -o swagger -$STD env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o postgresus ./cmd/main.go -mv /opt/postgresus/backend/postgresus /opt/postgresus/postgresus -mkdir -p /opt/postgresus_data/{data,backups,logs} -mkdir -p /postgresus-data/temp -mkdir -p /opt/postgresus/ui/build -cp -r /opt/postgresus/frontend/dist/* /opt/postgresus/ui/build/ -cp -r /opt/postgresus/backend/migrations /opt/postgresus/ -chown -R postgres:postgres /opt/postgresus -chown -R postgres:postgres /opt/postgresus_data -chown -R postgres:postgres /postgresus-data -msg_ok "Built Postgresus" +$STD env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o databasus ./cmd/main.go +mv /opt/databasus/backend/databasus /opt/databasus/databasus +mkdir -p /opt/databasus_data/{data,backups,logs} +mkdir -p /databasus-data/temp +mkdir -p /opt/databasus/ui/build +cp -r /opt/databasus/frontend/dist/* /opt/databasus/ui/build/ +cp -r /opt/databasus/backend/migrations /opt/databasus/ +chown -R postgres:postgres /opt/databasus +chown -R postgres:postgres /opt/databasus_data +chown -R postgres:postgres /databasus-data +msg_ok "Built Databasus" -msg_info "Configuring Postgresus" +msg_info "Configuring Databasus" ADMIN_PASS=$(openssl rand -base64 12) JWT_SECRET=$(openssl rand -hex 32) @@ -59,7 +59,7 @@ done $STD go install github.com/pressly/goose/v3/cmd/goose@latest ln -sf /root/go/bin/goose /usr/local/bin/goose -cat </opt/postgresus/.env +cat </opt/databasus/.env # Environment ENV_MODE=production @@ -74,7 +74,7 @@ DATABASE_URL=postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} # Migrations GOOSE_DRIVER=postgres GOOSE_DBSTRING=postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}?sslmode=disable -GOOSE_MIGRATION_DIR=/opt/postgresus/migrations +GOOSE_MIGRATION_DIR=/opt/databasus/migrations # Security JWT_SECRET=${JWT_SECRET} @@ -85,23 +85,23 @@ ADMIN_EMAIL=admin@localhost ADMIN_PASSWORD=${ADMIN_PASS} # Paths -DATA_DIR=/opt/postgresus_data/data -BACKUP_DIR=/opt/postgresus_data/backups -LOG_DIR=/opt/postgresus_data/logs +DATA_DIR=/opt/databasus_data/data +BACKUP_DIR=/opt/databasus_data/backups +LOG_DIR=/opt/databasus_data/logs # PostgreSQL Tools (for creating backups) PG_DUMP_PATH=/usr/lib/postgresql/17/bin/pg_dump PG_RESTORE_PATH=/usr/lib/postgresql/17/bin/pg_restore PSQL_PATH=/usr/lib/postgresql/17/bin/psql EOF -chown postgres:postgres /opt/postgresus/.env -chmod 600 /opt/postgresus/.env -msg_ok "Configured Postgresus" +chown postgres:postgres /opt/databasus/.env +chmod 600 /opt/databasus/.env +msg_ok "Configured Databasus" -msg_info "Creating Postgresus Service" -cat </etc/systemd/system/postgresus.service +msg_info "Creating Databasus Service" +cat </etc/systemd/system/databasus.service [Unit] -Description=Postgresus - PostgreSQL Backup Management +Description=Databasus - PostgreSQL Backup Management After=network.target postgresql.service Requires=postgresql.service @@ -109,10 +109,10 @@ Requires=postgresql.service Type=simple User=postgres Group=postgres -WorkingDirectory=/opt/postgresus +WorkingDirectory=/opt/databasus Environment="PATH=/usr/local/bin:/usr/bin:/bin" -EnvironmentFile=/opt/postgresus/.env -ExecStart=/opt/postgresus/postgresus +EnvironmentFile=/opt/databasus/.env +ExecStart=/opt/databasus/databasus Restart=always RestartSec=5 StandardOutput=journal @@ -122,11 +122,11 @@ StandardError=journal WantedBy=multi-user.target EOF $STD systemctl daemon-reload -$STD systemctl enable -q --now postgresus -msg_ok "Created Postgresus Service" +$STD systemctl enable -q --now databasus +msg_ok "Created Databasus Service" msg_info "Configuring Nginx" -cat </etc/nginx/sites-available/postgresus +cat </etc/nginx/sites-available/databasus server { listen 80; server_name _; From 8540bc36273e768e17d40e1cd4d0d7e2af6fc205 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:49:44 +0100 Subject: [PATCH 362/378] fixes --- ct/databasus.sh | 21 +++++++++++++-------- install/databasus-install.sh | 2 +- install/piler-install.sh | 16 ++++++++-------- misc/tools.func | 17 +++++++++++++---- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/ct/databasus.sh b/ct/databasus.sh index d7c2dd0a8..c0eab526b 100644 --- a/ct/databasus.sh +++ b/ct/databasus.sh @@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/RostislavDugin/postgresus +# Source: https://github.com/databasus/databasus APP="Databasus" var_tags="${var_tags:-backup;postgresql;database}" @@ -24,12 +24,12 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f /opt/postgresus/postgresus ]]; then + if [[ ! -f /opt/databasus/databasus ]]; then msg_error "No ${APP} Installation Found!" exit fi - if check_for_gh_release "Databasus" "Databasus"; then + if check_for_gh_release "databasus" "databasus/databasus"; then msg_info "Stopping Databasus" $STD systemctl stop databasus msg_ok "Stopped Databasus" @@ -38,23 +38,28 @@ function update_script() { cp /opt/databasus/.env /tmp/databasus.env.bak msg_ok "Backed up Configuration" + msg_info "Updating Databasus" fetch_and_deploy_gh_release "databasus" "databasus/databasus" "tarball" "latest" "/opt/databasus" - msg_info "Updating Databasus" cd /opt/databasus/frontend $STD npm ci $STD npm run build + cd /opt/databasus/backend $STD go mod download - $STD go build -o ../databasus ./cmd/main.go - cd /opt/databasus/ - cp -r frontend/dist ui - cp -r backend/migrations . + $STD /root/go/bin/swag init -g cmd/main.go -o swagger + $STD env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o databasus ./cmd/main.go + mv /opt/databasus/backend/databasus /opt/databasus/databasus + + cp -r /opt/databasus/frontend/dist/* /opt/databasus/ui/build/ + cp -r /opt/databasus/backend/migrations /opt/databasus/ + chown -R postgres:postgres /opt/databasus msg_ok "Updated Databasus" msg_info "Restoring Configuration" cp /tmp/databasus.env.bak /opt/databasus/.env rm -f /tmp/databasus.env.bak + chown postgres:postgres /opt/databasus/.env msg_ok "Restored Configuration" msg_info "Starting Databasus" diff --git a/install/databasus-install.sh b/install/databasus-install.sh index 65768c03d..fbac56054 100644 --- a/install/databasus-install.sh +++ b/install/databasus-install.sh @@ -147,7 +147,7 @@ server { } } EOF -ln -sf /etc/nginx/sites-available/postgresus /etc/nginx/sites-enabled/postgresus +ln -sf /etc/nginx/sites-available/databasus /etc/nginx/sites-enabled/databasus rm -f /etc/nginx/sites-enabled/default $STD nginx -t $STD systemctl enable -q --now nginx diff --git a/install/piler-install.sh b/install/piler-install.sh index 6eb3e327f..e775b16bf 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -33,7 +33,7 @@ msg_ok "Installed Dependencies" import_local_ip setup_mariadb MARIADB_DB_NAME="piler" MARIADB_DB_USER="piler" setup_mariadb_db -PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="ldap,gd,memcached,pdo,mysql,curl,zip" setup_php +PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="ldap,gd,memcached,pdo,mysql,curl,zip" setup_php msg_info "Installing Manticore Search" cd /tmp @@ -171,12 +171,12 @@ $STD systemctl enable --now piler msg_ok "Created Piler Service" msg_info "Configuring PHP-FPM Pool" -cp /etc/php/8.4/fpm/pool.d/www.conf /etc/php/8.4/fpm/pool.d/piler.conf -sed -i 's/\[www\]/[piler]/' /etc/php/8.4/fpm/pool.d/piler.conf -sed -i 's/^user = www-data/user = piler/' /etc/php/8.4/fpm/pool.d/piler.conf -sed -i 's/^group = www-data/group = piler/' /etc/php/8.4/fpm/pool.d/piler.conf -sed -i 's|^listen = .*|listen = /run/php/php8.4-fpm-piler.sock|' /etc/php/8.4/fpm/pool.d/piler.conf -$STD systemctl restart php8.4-fpm +cp /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/piler.conf +sed -i 's/\[www\]/[piler]/' /etc/php/8.3/fpm/pool.d/piler.conf +sed -i 's/^user = www-data/user = piler/' /etc/php/8.3/fpm/pool.d/piler.conf +sed -i 's/^group = www-data/group = piler/' /etc/php/8.3/fpm/pool.d/piler.conf +sed -i 's|^listen = .*|listen = /run/php/php8.3-fpm-piler.sock|' /etc/php/8.3/fpm/pool.d/piler.conf +$STD systemctl restart php8.3-fpm msg_ok "Configured PHP-FPM Pool" msg_info "Configuring Piler Web GUI" @@ -250,7 +250,7 @@ server { } location ~ \.php$ { - fastcgi_pass unix:/run/php/php8.4-fpm-piler.sock; + fastcgi_pass unix:/run/php/php8.3-fpm-piler.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; include fastcgi_params; diff --git a/misc/tools.func b/misc/tools.func index f3c288764..7e18d475d 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -4488,10 +4488,19 @@ EOF return 1 } - manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { - msg_error "Failed to setup PHP repository" - return 1 - } + # Use different repository based on OS + if [[ "$DISTRO_ID" == "ubuntu" ]]; then + # Ubuntu: Use ondrej/php PPA + msg_info "Adding ondrej/php PPA for Ubuntu" + $STD apt install -y software-properties-common + $STD add-apt-repository -y ppa:ondrej/php + else + # Debian: Use 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 + } + fi ensure_apt_working || return 1 $STD apt update From 59e344f877612b327f0298341c6fc6bde09db24a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:24:58 +0100 Subject: [PATCH 363/378] fixes --- install/piler-install.sh | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/install/piler-install.sh b/install/piler-install.sh index e775b16bf..36aae0de2 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -49,8 +49,7 @@ msg_ok "Installed Manticore Search" fetch_and_deploy_gh_release "piler" "jsuto/piler" "binary" "latest" "/tmp" "piler_*-noble-*_amd64.deb" msg_info "Configuring Piler Database" -cd /usr/local/share/piler -mysql -u root "${MARIADB_DB_NAME}" /dev/null || true msg_ok "Configured Piler Database" msg_info "Configuring Piler" @@ -78,9 +77,6 @@ iv=0123456789ABCDEF memcached_servers=127.0.0.1 -enable_clamav=1 -clamd_socket=/var/run/clamav/clamd.ctl - spam_header_line=X-Spam-Status: Yes verbosity=1 @@ -147,8 +143,8 @@ msg_info "Creating Piler Service" cat </etc/systemd/system/piler.service [Unit] Description=Piler Email Archiving -After=network.target mysql.service manticore.service -Requires=mysql.service manticore.service +After=network.target mysql.service manticore.service memcached.service +Requires=mysql.service [Service] Type=forking @@ -166,7 +162,6 @@ EOF $STD systemctl daemon-reload $STD systemctl enable --now manticore $STD systemctl enable --now memcached -$STD systemctl enable --now clamav-daemon $STD systemctl enable --now piler msg_ok "Created Piler Service" From b9aa3ec7ab10e639642c9576685a3b88e5157b55 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:17:04 +0100 Subject: [PATCH 364/378] Enhance Piler and UniFi OS VM installation scripts Added installation steps for Piler WebUI and improved config file handling in piler-install.sh. Updated unifi-os-server-vm.sh to always prompt for VMID, refactored machine type selection, improved storage validation, added swap file setup for stability, and increased VM IP detection attempts for reliability. --- install/piler-install.sh | 9 +++++--- vm/unifi-os-server-vm.sh | 48 +++++++++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/install/piler-install.sh b/install/piler-install.sh index 36aae0de2..f867e54be 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -46,7 +46,10 @@ $STD systemctl stop manticore $STD systemctl disable manticore msg_ok "Installed Manticore Search" +msg_info "Installing Piler" fetch_and_deploy_gh_release "piler" "jsuto/piler" "binary" "latest" "/tmp" "piler_*-noble-*_amd64.deb" +fetch_and_deploy_gh_release "piler-webui" "jsuto/piler" "binary" "latest" "/tmp" "piler-webui_*-noble-*_amd64.deb" +msg_ok "Installed Piler" msg_info "Configuring Piler Database" $STD mariadb -u root "${MARIADB_DB_NAME}" /dev/null || true @@ -175,9 +178,9 @@ $STD systemctl restart php8.3-fpm msg_ok "Configured PHP-FPM Pool" msg_info "Configuring Piler Web GUI" -cd /var/www/piler - -cat </var/www/piler/config-site.php +# Check if config-site.php already exists (created by .deb package) +if [ ! -f /var/www/piler/config-site.php ]; then + cat </var/www/piler/config-site.php &1 1>&2 2>&3); then if [ -z "$VMID" ]; then @@ -391,22 +391,24 @@ function advanced_settings() { fi done - if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Machine Type" 10 58 2 \ + MACH="q35" + if MACH_RESULT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Machine Type" 10 58 2 \ "q35" "Q35 (Modern, PCIe, UEFI)" ON \ "i440fx" "i440fx (Legacy)" OFF \ 3>&1 1>&2 2>&3); then - if [ "$MACH" = "q35" ]; then - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}" - FORMAT="" - MACHINE=" -machine q35" - else - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx (Legacy)${CL}" - FORMAT=",efitype=4m" - MACHINE="" - fi + MACH="$MACH_RESULT" else exit-script fi + if [ "$MACH" = "q35" ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx (Legacy)${CL}" + FORMAT=",efitype=4m" + MACHINE="" + 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 ' ') @@ -575,13 +577,15 @@ start_script post_to_api_vm 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)}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f 2>/dev/null || echo "N/A" | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-0} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") @@ -771,6 +775,20 @@ if [ "$START_VM" == "yes" ]; then sleep 120 msg_ok "Podman installed" + # Setup swap file (1GB for better stability) + msg_info "Setting up swap file" + send_line_to_vm "fallocate -l 1G /swapfile" + sleep 2 + send_line_to_vm "chmod 600 /swapfile" + sleep 1 + send_line_to_vm "mkswap /swapfile" + sleep 2 + send_line_to_vm "swapon /swapfile" + sleep 1 + send_line_to_vm "echo '/swapfile none swap sw 0 0' >> /etc/fstab" + sleep 1 + msg_ok "Swap file created" + # Step 2: Download UniFi OS Server installer msg_info "Downloading UniFi OS Server ${UOS_VERSION}" send_line_to_vm "cd /opt" @@ -800,12 +818,12 @@ if [ "$START_VM" == "yes" ]; then # Get IP from outside via Guest Agent msg_info "Detecting VM IP address" VM_IP="" - for i in {1..15}; do + for i in {1..30}; do VM_IP=$(qm guest cmd $VMID network-get-interfaces 2>/dev/null | jq -r '.[] | select(.name != "lo") | .["ip-addresses"][]? | select(.["ip-address-type"] == "ipv4") | .["ip-address"]' 2>/dev/null | head -1 || echo "") if [ -n "$VM_IP" ]; then break fi - sleep 2 + sleep 1 done if [ -n "$VM_IP" ]; then From ed67ef9663fe87c1d603eff7c2d67cb5a94b4b9d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:22:22 +0100 Subject: [PATCH 365/378] Update unifi-os-server-vm.sh --- vm/unifi-os-server-vm.sh | 62 +++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/vm/unifi-os-server-vm.sh b/vm/unifi-os-server-vm.sh index d5229bc99..14fd08382 100644 --- a/vm/unifi-os-server-vm.sh +++ b/vm/unifi-os-server-vm.sh @@ -104,7 +104,6 @@ function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null - fi } function send_line_to_vm() { @@ -213,7 +212,6 @@ function check_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. @@ -230,8 +228,6 @@ pve_check() { msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 1 fi - return 0 - fi # Check for Proxmox VE 9.x: allow 9.0–9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then @@ -241,8 +237,6 @@ pve_check() { msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 1 fi - return 0 - fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." @@ -257,7 +251,6 @@ function arch_check() { echo -e "Exiting..." sleep 2 exit - fi } function ssh_check() { @@ -270,7 +263,6 @@ function ssh_check() { exit fi fi - fi } function exit-script() { @@ -302,7 +294,6 @@ function select_os() { #echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}${OS_DISPLAY}${CL}" else exit-script - fi } function select_cloud_init() { @@ -399,7 +390,6 @@ function advanced_settings() { MACH="$MACH_RESULT" else exit-script - fi if [ "$MACH" = "q35" ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}" FORMAT="" @@ -408,7 +398,6 @@ function advanced_settings() { echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx (Legacy)${CL}" FORMAT=",efitype=4m" MACHINE="" - 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 ' ') @@ -423,7 +412,6 @@ function advanced_settings() { 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 \ @@ -438,7 +426,6 @@ function advanced_settings() { fi else exit-script - fi 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 @@ -450,7 +437,6 @@ function advanced_settings() { fi else exit-script - fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose CPU Model" --cancel-button Exit-Script 10 58 2 \ "Host" "Host (Faster, recommended)" ON \ @@ -468,7 +454,6 @@ function advanced_settings() { esac 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 @@ -479,7 +464,6 @@ function advanced_settings() { 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 @@ -490,7 +474,6 @@ function advanced_settings() { 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 @@ -501,7 +484,6 @@ function advanced_settings() { 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 @@ -513,7 +495,6 @@ function advanced_settings() { 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 @@ -526,7 +507,6 @@ function advanced_settings() { 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 @@ -539,7 +519,6 @@ function advanced_settings() { 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}" @@ -547,7 +526,6 @@ function 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 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}" @@ -555,7 +533,6 @@ function advanced_settings() { header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings - fi } function start_script() { @@ -567,15 +544,39 @@ function start_script() { 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 "Checking system resources" +SYSTEM_RAM_GB=$(grep MemTotal /proc/meminfo | awk '{printf "%.0f", $2 / 1024 / 1024}') +SYSTEM_SWAP_GB=$(grep SwapTotal /proc/meminfo | awk '{printf "%.0f", $2 / 1024 / 1024}') +SYSTEM_FREE_DISK_GB=$(df -BG / | awk 'NR==2 {print $4}' | sed 's/G//') +if [[ ${SYSTEM_RAM_GB} -lt 4 ]]; then + msg_error "Warning: Less than 4GB RAM detected (${SYSTEM_RAM_GB}GB). Install may be slow." + sleep 3 +fi +if [[ ${SYSTEM_FREE_DISK_GB} -lt 10 ]]; then + msg_error "Warning: Less than 10GB free disk detected. Install may fail." + sleep 3 +fi +msg_ok "System resources: ${SYSTEM_RAM_GB}GB RAM, ${SYSTEM_FREE_DISK_GB}GB free disk" + +if command -v ufw &>/dev/null; then + if ufw status verbose | grep -q "Status: active"; then + msg_info "Setting up firewall rules for UniFi OS Server ports" + ufw allow 11443/tcp 2>/dev/null + ufw allow 8080/tcp 2>/dev/null + ufw allow 3478/tcp 2>/dev/null + ufw allow 3478/udp 2>/dev/null + msg_ok "Firewall rules configured" +fi + msg_info "Validating Storage" STORAGE_MENU=() MSG_MAX_LENGTH=0 @@ -587,7 +588,6 @@ while read -r line; do OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-0} ]]; 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') @@ -775,9 +775,11 @@ if [ "$START_VM" == "yes" ]; then sleep 120 msg_ok "Podman installed" - # Setup swap file (1GB for better stability) + # Setup dynamic swap file based on available disk space msg_info "Setting up swap file" - send_line_to_vm "fallocate -l 1G /swapfile" + send_line_to_vm "export FREE_DISK_GB=\$(df -BG / | awk 'NR==2 {print \$4}' | sed 's/G//'); if [[ \${FREE_DISK_GB} -ge 20 ]]; then SWAP_SIZE=2048; elif [[ \${FREE_DISK_GB} -ge 10 ]]; then SWAP_SIZE=1024; elif [[ \${FREE_DISK_GB} -ge 5 ]]; then SWAP_SIZE=512; else SWAP_SIZE=256; fi; echo \"Creating swap file: \${SWAP_SIZE}MB\"" + sleep 1 + send_line_to_vm "fallocate -l \${SWAP_SIZE}M /swapfile" sleep 2 send_line_to_vm "chmod 600 /swapfile" sleep 1 @@ -787,7 +789,7 @@ if [ "$START_VM" == "yes" ]; then sleep 1 send_line_to_vm "echo '/swapfile none swap sw 0 0' >> /etc/fstab" sleep 1 - msg_ok "Swap file created" + msg_ok "Swap file created (size based on available disk space)" # Step 2: Download UniFi OS Server installer msg_info "Downloading UniFi OS Server ${UOS_VERSION}" @@ -830,7 +832,6 @@ if [ "$START_VM" == "yes" ]; then msg_ok "VM IP Address: ${VM_IP}" else msg_info "Could not detect IP - check VM console" - fi echo "" echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ UniFi OS Server installation complete!${CL}" @@ -838,7 +839,6 @@ if [ "$START_VM" == "yes" ]; then echo -e "${TAB}${GATEWAY}${BOLD}${GN}✓ Access at: ${BGN}https://${VM_IP}:11443${CL}" else echo -e "${TAB}${INFO}${YW}Access via: ${BGN}https://:11443${CL}" - fi echo -e "${TAB}${INFO}${DGN}Console login - User: ${BGN}root${CL} / Password: ${BGN}${CLOUDINIT_PASSWORD}${CL}" echo -e "${TAB}${INFO}${YW}Note: UniFi OS may take 1-2 more minutes to fully start${CL}" echo "" @@ -846,3 +846,5 @@ fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" + + From aec65d399c468b6b2bff95831c75bf0ebd7fa236 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:56:23 +0000 Subject: [PATCH 366/378] Delete kutt (ct) after migration to ProxmoxVE (#1284) Co-authored-by: github-actions[bot] --- ct/headers/kutt | 6 --- ct/kutt.sh | 74 --------------------------------- frontend/public/json/kutt.json | 40 ------------------ install/kutt-install.sh | 75 ---------------------------------- 4 files changed, 195 deletions(-) delete mode 100644 ct/headers/kutt delete mode 100644 ct/kutt.sh delete mode 100644 frontend/public/json/kutt.json delete mode 100644 install/kutt-install.sh diff --git a/ct/headers/kutt b/ct/headers/kutt deleted file mode 100644 index a132aed6f..000000000 --- a/ct/headers/kutt +++ /dev/null @@ -1,6 +0,0 @@ - __ __ __ __ - / //_/_ __/ /_/ /_ - / ,< / / / / __/ __/ - / /| / /_/ / /_/ /_ -/_/ |_\__,_/\__/\__/ - diff --git a/ct/kutt.sh b/ct/kutt.sh deleted file mode 100644 index 79b1c2671..000000000 --- a/ct/kutt.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-2026 community-scripts ORG -# Author: tomfrenzel -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/thedevs-network/kutt - -APP="Kutt" -var_tags="${var_tags:-sharing}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -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/kutt ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "kutt" "thedevs-network/kutt"; then - msg_info "Stopping services" - systemctl stop kutt - msg_ok "Stopped services" - - msg_info "Backing up data" - mkdir -p /opt/kutt-backup - [ -d /opt/kutt/custom ] && cp -r /opt/kutt/custom /opt/kutt-backup/ - [ -d /opt/kutt/db ] && cp -r /opt/kutt/db /opt/kutt-backup/ - cp /opt/kutt/.env /opt/kutt-backup/ - msg_ok "Backed up data" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" - - msg_info "Restoring data" - [ -d /opt/kutt-backup/custom ] && cp -r /opt/kutt-backup/custom /opt/kutt/ - [ -d /opt/kutt-backup/db ] && cp -r /opt/kutt-backup/db /opt/kutt/ - [ -f /opt/kutt-backup/.env ] && cp /opt/kutt-backup/.env /opt/kutt/ - rm -rf /opt/kutt-backup - msg_ok "Restored data" - - msg_info "Configuring Kutt" - cd /opt/kutt - $STD npm install - $STD npm run migrate - msg_ok "Configured Kutt" - - msg_info "Starting services" - systemctl start kutt - 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}https://${IP} or https://${CL}" diff --git a/frontend/public/json/kutt.json b/frontend/public/json/kutt.json deleted file mode 100644 index 1200e34ae..000000000 --- a/frontend/public/json/kutt.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Kutt", - "slug": "kutt", - "categories": [ - 21 - ], - "date_created": "2025-12-14", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 443, - "documentation": "https://github.com/thedevs-network/kutt/", - "config_path": "/opt/kutt/.env", - "website": "https://kutt.it", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kutt.webp", - "description": "Kutt is a modern URL shortener with support for custom domains. Create and edit links, view statistics, manage users, and more.", - "install_methods": [ - { - "type": "default", - "script": "ct/kutt.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 2, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Kutt needs so be served with an SSL certificate for its login to work. During install, you will be prompted to choose if you want to have Caddy installed for SSL termination or if you want to use your own reverse proxy (in that case point your reverse porxy to port 3000).", - "type": "info" - } - ] -} diff --git a/install/kutt-install.sh b/install/kutt-install.sh deleted file mode 100644 index 3482fbb50..000000000 --- a/install/kutt-install.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: tomfrenzel -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/thedevs-network/kutt - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -echo "${TAB3}How would you like to handle SSL termination?" -echo "${TAB3}[i]-Internal (self-signed SSL Certificate) [e]-External (use your own reverse proxy)" -read -rp "${TAB3}Enter your choice (default: i): " ssl_choice -ssl_choice=${ssl_choice:-i} -case "${ssl_choice,,}" in -i) - import_local_ip - DEFAULT_HOST="$LOCAL_IP" - - msg_info "Configuring Caddy" - $STD apt install -y caddy - cat </etc/caddy/Caddyfile -$LOCAL_IP { - reverse_proxy localhost:3000 -} -EOF - systemctl restart caddy - msg_ok "Configured Caddy" - ;; -e) - read -r -p "${TAB3}Enter the hostname you want to use for Kutt (eg. kutt.example.com): " custom_host - if [[ "$custom_host" ]]; then - DEFAULT_HOST="$custom_host" - fi - ;; -esac - -NODE_VERSION="22" setup_nodejs -fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" - -msg_info "Configuring Kutt" -cd /opt/kutt -cp .example.env ".env" -sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" -sed -i "s|DEFAULT_DOMAIN=.*|DEFAULT_DOMAIN=$DEFAULT_HOST|g" ".env" -$STD npm install -$STD npm run migrate -msg_ok "Configured Kutt" - -msg_info "Creating Services" -cat </etc/systemd/system/kutt.service -[Unit] -Description=Kutt server -After=network-online.target - -[Service] -Type=simple -WorkingDirectory=/opt/kutt -ExecStart=/usr/bin/npm start -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now kutt -msg_ok "Created Services" - -motd_ssh -customize -cleanup_lxc From 9eb59f42e1c3b9f9dd49c138e3bdec49efeb6d0b Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:13:44 +0000 Subject: [PATCH 367/378] hostname value for debian fix --- vm/debian-13-vm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/debian-13-vm.sh b/vm/debian-13-vm.sh index 97ca4de15..8ed60d80b 100644 --- a/vm/debian-13-vm.sh +++ b/vm/debian-13-vm.sh @@ -298,9 +298,9 @@ function advanced_settings() { exit-script fi - 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 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="unifi-os-server" + HN="debian" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') From 778f77ac8052d2942f41e39500a0d694f9b9a0a2 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 15 Jan 2026 08:32:44 +0100 Subject: [PATCH 368/378] joplin test --- ct/joplin-server.sh | 65 +++++++++++++++++++++++++++ install/joplin-server-install.sh | 76 ++++++++++++++++++++++++++++++++ 2 files changed, 141 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..1de7846c9 --- /dev/null +++ b/ct/joplin-server.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 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:-6144}" +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 /opt/joplin-server ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + NODE_VERSION=24 NODE_MODULE="yarn,npm,pm2" setup_nodejs + + if check_for_gh_release "joplin-server" "laurent22/joplin"; then + msg_info "Stopping Services" + systemctl stop joplin-server + msg_ok "Stopped Services" + + cp /opt/joplin-server/.env /opt + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" + mv /opt/.env /opt/joplin-server + + msg_info "Updating Joplin-Server" + 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 Joplin-Server" + + 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/install/joplin-server-install.sh b/install/joplin-server-install.sh new file mode 100644 index 000000000..4a45ea189 --- /dev/null +++ b/install/joplin-server-install.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/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 install -y \ + git \ + rsync +msg_ok "Installed Dependencies" + +PG_VERSION="17" setup_postgresql +PG_DB_NAME="joplin" PG_DB_USER="joplin" setup_postgresql_db +NODE_VERSION=24 NODE_MODULE="yarn,npm,pm2" 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 +fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" +import_local_ip + +msg_info "Setting up Joplin Server (Patience)" +cd /opt/joplin-server/packages/server +sed -i "/onenote-converter/d" /opt/joplin-server/packages/lib/package.json +$STD yarn config set --home enableTelemetry 0 +$STD yarn install +$STD yarn build + +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=$PG_DB_PASS +POSTGRES_DATABASE=$PG_DB_NAME +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 +cleanup_lxc From 2a87538293a433cb1b241be935ccd96caedb31ab Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 09:25:31 +0100 Subject: [PATCH 369/378] Update loki.sh --- ct/loki.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ct/loki.sh b/ct/loki.sh index 707b2fb46..fd2df1f1f 100644 --- a/ct/loki.sh +++ b/ct/loki.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: hoholms # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -44,9 +44,7 @@ function update_script() { systemctl start loki systemctl start promtail msg_ok "Started Loki" - - msg_ok "Update Successful" - + msg_ok "Updated successfully!" exit } From d50666fb70b2f57817bb4fcc05871c448c5a43e1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 09:25:42 +0100 Subject: [PATCH 370/378] Update alpine-loki.sh --- ct/alpine-loki.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-loki.sh b/ct/alpine-loki.sh index 99528206d..49f04ca13 100644 --- a/ct/alpine-loki.sh +++ b/ct/alpine-loki.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: hoholms # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From f556a6e6f40cb84519b1dc432a54175e21031916 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 09:29:13 +0100 Subject: [PATCH 371/378] remove petio --- ct/{ => deferred/maybe_death}/petio.sh | 2 +- frontend/public/json/petio.json | 40 ------------------- .../maybe_death}/petio-install.sh | 0 3 files changed, 1 insertion(+), 41 deletions(-) rename ct/{ => deferred/maybe_death}/petio.sh (96%) delete mode 100644 frontend/public/json/petio.json rename install/{ => deferred/maybe_death}/petio-install.sh (100%) diff --git a/ct/petio.sh b/ct/deferred/maybe_death/petio.sh similarity index 96% rename from ct/petio.sh rename to ct/deferred/maybe_death/petio.sh index e70e5d93f..9771b125f 100644 --- a/ct/petio.sh +++ b/ct/deferred/maybe_death/petio.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-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/frontend/public/json/petio.json b/frontend/public/json/petio.json deleted file mode 100644 index 111ff4921..000000000 --- a/frontend/public/json/petio.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Petio", - "slug": "petio", - "categories": [ - 13 - ], - "date_created": "2025-01-15", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 7777, - "documentation": "https://docs.petio.tv/", - "website": "https://petio.tv/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/petio.webp", - "config_path": "/opt/petio", - "description": "Petio is a third party companion app available to Plex server owners to allow their users to request, review and discover content. It integrates with your existing services, such as Sonarr and Radarr, for seamless content management.", - "install_methods": [ - { - "type": "default", - "script": "ct/petio.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 4, - "os": "ubuntu", - "version": "24.04" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Installation path: /opt/Petio", - "type": "info" - } - ] -} diff --git a/install/petio-install.sh b/install/deferred/maybe_death/petio-install.sh similarity index 100% rename from install/petio-install.sh rename to install/deferred/maybe_death/petio-install.sh From 5e8d2565c417ec02fcf6f517756d36b259a67777 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 09:36:29 +0100 Subject: [PATCH 372/378] cleanup cleanup --- ct/deferred/pixelfed.sh | 45 - ct/{ => deferred}/rybbit.sh | 0 ct/{ => deferred}/transmission-openvpn.sh | 0 ct/gwn-manager.sh | 42 - ct/heimdall-dashboard.sh | 80 -- ct/linkwarden.sh | 75 - ct/plex.sh | 59 - ct/plex2.sh | 59 - .../transmission-openvpn-install.sh | 0 install/piler-install.sh | 1 + install/plex-install.sh | 50 - install/plex2-install.sh | 41 - misc/install.func | 1272 ++++++++--------- 13 files changed, 637 insertions(+), 1087 deletions(-) delete mode 100644 ct/deferred/pixelfed.sh rename ct/{ => deferred}/rybbit.sh (100%) rename ct/{ => deferred}/transmission-openvpn.sh (100%) delete mode 100644 ct/gwn-manager.sh delete mode 100644 ct/heimdall-dashboard.sh delete mode 100644 ct/linkwarden.sh delete mode 100644 ct/plex.sh delete mode 100644 ct/plex2.sh rename install/{ => deferred}/transmission-openvpn-install.sh (100%) delete mode 100644 install/plex-install.sh delete mode 100644 install/plex2-install.sh diff --git a/ct/deferred/pixelfed.sh b/ct/deferred/pixelfed.sh deleted file mode 100644 index f9f6ba2e2..000000000 --- a/ct/deferred/pixelfed.sh +++ /dev/null @@ -1,45 +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-2026 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: - -APP="Pixelfed" -var_tags="${var_tags:-pictures}" -var_disk="${var_disk:-7}" -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 [[ ! -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 -} - -start -build_container -description - -msg_ok "Completed successfully!\n" -echo -e "${APP} Setup should be reachable by going to the following URL. - ${BL}http://${IP}:8000${CL} \n" diff --git a/ct/rybbit.sh b/ct/deferred/rybbit.sh similarity index 100% rename from ct/rybbit.sh rename to ct/deferred/rybbit.sh diff --git a/ct/transmission-openvpn.sh b/ct/deferred/transmission-openvpn.sh similarity index 100% rename from ct/transmission-openvpn.sh rename to ct/deferred/transmission-openvpn.sh diff --git a/ct/gwn-manager.sh b/ct/gwn-manager.sh deleted file mode 100644 index 3c63e2c68..000000000 --- a/ct/gwn-manager.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-2026 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager - -APP="GWN-Manager" -var_tags="${var_tags:-network;management}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-6144}" -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 /gwn ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_ok "Application is updated via the 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}https://${IP}:8443${CL}" diff --git a/ct/heimdall-dashboard.sh b/ct/heimdall-dashboard.sh deleted file mode 100644 index 6fe51c7ba..000000000 --- a/ct/heimdall-dashboard.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-2026 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://heimdall.site/ - -APP="Heimdall-Dashboard" -var_tags="${var_tags:-dashboard}" -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/Heimdall ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "Heimdall" "linuxserver/Heimdall"; then - msg_info "Stopping Service" - systemctl stop heimdall - sleep 1 - msg_ok "Stopped Service" - - msg_info "Backing up Data" - cp -R /opt/Heimdall/database database-backup - cp -R /opt/Heimdall/public public-backup - sleep 1 - msg_ok "Backed up Data" - - setup_composer - fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball" - - msg_info "Updating Heimdall-Dashboard" - cd /opt/Heimdall - export COMPOSER_ALLOW_SUPERUSER=1 - $STD composer dump-autoload - msg_ok "Updated Heimdall-Dashboard" - - msg_info "Restoring Data" - cd ~ - cp -R database-backup/* /opt/Heimdall/database - cp -R public-backup/* /opt/Heimdall/public - sleep 1 - msg_ok "Restored Data" - - msg_info "Cleaning Up" - rm -rf {public-backup,database-backup} - sleep 1 - msg_ok "Cleaned Up" - - msg_info "Starting Service" - systemctl start heimdall.service - sleep 2 - 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}:7990${CL}" diff --git a/ct/linkwarden.sh b/ct/linkwarden.sh deleted file mode 100644 index 8753ce8f4..000000000 --- a/ct/linkwarden.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-2026 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://linkwarden.app/ - -APP="Linkwarden" -var_tags="${var_tags:-bookmark}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-12}" -var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.04}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/linkwarden ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "linkwarden" "linkwarden/linkwarden"; then - NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs - msg_info "Stopping Service" - systemctl stop linkwarden - msg_ok "Stopped Service" - - RUST_CRATES="monolith" setup_rust - - msg_info "Backing up data" - mv /opt/linkwarden/.env /opt/.env - [ -d /opt/linkwarden/data ] && mv /opt/linkwarden/data /opt/data.bak - rm -rf /opt/linkwarden - msg_ok "Backed up data" - - fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" - - msg_info "Updating ${APP}" - cd /opt/linkwarden - $STD yarn - $STD npx playwright install-deps - $STD yarn playwright install - mv /opt/.env /opt/linkwarden/.env - $STD yarn prisma:generate - $STD yarn web:build - $STD yarn prisma:deploy - [ -d /opt/data.bak ] && mv /opt/data.bak /opt/linkwarden/data - rm -rf ~/.cargo/registry ~/.cargo/git ~/.cargo/.package-cache - rm -rf /root/.cache/yarn - rm -rf /opt/linkwarden/.next/cache - msg_ok "Updated ${APP}" - - msg_info "Starting Service" - systemctl start linkwarden - 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}" diff --git a/ct/plex.sh b/ct/plex.sh deleted file mode 100644 index 00590c47c..000000000 --- a/ct/plex.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-2026 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}" -var_gpu="${var_gpu:-yes}" - -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 ]] && - [[ ! -f /etc/apt/sources.list.d/plexmediaserver.sources ]]; 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 Plex LXC" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated Plex LXC" - msg_ok "Updated successfully!" - exit - fi - if [ "$UPD" == "2" ]; then - set +e - bash -c "$(curl -fsSL https://raw.githubusercontent.com/mrworf/plexupdate/master/extras/installer.sh)" - msg_ok "Updated successfully!" - 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/ct/plex2.sh b/ct/plex2.sh deleted file mode 100644 index dff1fdd62..000000000 --- a/ct/plex2.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-2026 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.plex.tv/ - -APP="Plex2" -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}" -var_gpu="${var_gpu:-yes}" - -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 ]] && - [[ ! -f /etc/apt/sources.list.d/plexmediaserver.sources ]]; 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 Plex" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated Plex" - msg_ok "Updated successfully!" - exit - fi - if [ "$UPD" == "2" ]; then - set +e - bash -c "$(curl -fsSL https://raw.githubusercontent.com/mrworf/plexupdate/master/extras/installer.sh)" - msg_ok "Updated successfully!" - 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/install/transmission-openvpn-install.sh b/install/deferred/transmission-openvpn-install.sh similarity index 100% rename from install/transmission-openvpn-install.sh rename to install/deferred/transmission-openvpn-install.sh diff --git a/install/piler-install.sh b/install/piler-install.sh index f867e54be..1eeadff04 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -225,6 +225,7 @@ if [ ! -f /var/www/piler/config-site.php ]; then \$config['HEADER_LINE_TO_HIDE'] = 'X-Envelope-To:'; ?> EOF +fi chown -R piler:piler /var/www/piler chmod 755 /var/www/piler diff --git a/install/plex-install.sh b/install/plex-install.sh deleted file mode 100644 index 8e786f0cc..000000000 --- a/install/plex-install.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 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 [[ -d /dev/dri ]]; 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 "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 -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 -cleanup_lxc diff --git a/install/plex2-install.sh b/install/plex2-install.sh deleted file mode 100644 index bb29392c7..000000000 --- a/install/plex2-install.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 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 - -setup_hwaccel - -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 -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 -cleanup_lxc diff --git a/misc/install.func b/misc/install.func index 41168a0f0..591404363 100644 --- a/misc/install.func +++ b/misc/install.func @@ -49,134 +49,134 @@ INIT_SYSTEM="" # systemd, openrc, sysvinit # OS_TYPE, OS_FAMILY, OS_VERSION, PKG_MANAGER, INIT_SYSTEM # ------------------------------------------------------------------------------ detect_os() { - if [[ -f /etc/os-release ]]; then - # shellcheck disable=SC1091 - . /etc/os-release - OS_TYPE="${ID:-unknown}" - OS_VERSION="${VERSION_ID:-unknown}" - elif [[ -f /etc/alpine-release ]]; then - OS_TYPE="alpine" - OS_VERSION=$(cat /etc/alpine-release) - elif [[ -f /etc/debian_version ]]; then - OS_TYPE="debian" - OS_VERSION=$(cat /etc/debian_version) - elif [[ -f /etc/redhat-release ]]; then - OS_TYPE="centos" - OS_VERSION=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1) - elif [[ -f /etc/arch-release ]]; then - OS_TYPE="arch" - OS_VERSION="rolling" - elif [[ -f /etc/gentoo-release ]]; then - OS_TYPE="gentoo" - OS_VERSION=$(cat /etc/gentoo-release | grep -oE '[0-9.]+') - else - OS_TYPE="unknown" - OS_VERSION="unknown" - fi + if [[ -f /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + OS_TYPE="${ID:-unknown}" + OS_VERSION="${VERSION_ID:-unknown}" + elif [[ -f /etc/alpine-release ]]; then + OS_TYPE="alpine" + OS_VERSION=$(cat /etc/alpine-release) + elif [[ -f /etc/debian_version ]]; then + OS_TYPE="debian" + OS_VERSION=$(cat /etc/debian_version) + elif [[ -f /etc/redhat-release ]]; then + OS_TYPE="centos" + OS_VERSION=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1) + elif [[ -f /etc/arch-release ]]; then + OS_TYPE="arch" + OS_VERSION="rolling" + elif [[ -f /etc/gentoo-release ]]; then + OS_TYPE="gentoo" + OS_VERSION=$(cat /etc/gentoo-release | grep -oE '[0-9.]+') + else + OS_TYPE="unknown" + OS_VERSION="unknown" + fi - # Normalize OS type and determine family - case "$OS_TYPE" in - debian) - OS_FAMILY="debian" - PKG_MANAGER="apt" - ;; - ubuntu) - OS_FAMILY="debian" - PKG_MANAGER="apt" - ;; - devuan) - OS_FAMILY="debian" - PKG_MANAGER="apt" - ;; - alpine) - OS_FAMILY="alpine" - PKG_MANAGER="apk" - ;; - fedora) - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - rocky | rockylinux) - OS_TYPE="rocky" - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - alma | almalinux) - OS_TYPE="alma" - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - centos) - OS_FAMILY="rhel" - # CentOS 7 uses yum, 8+ uses dnf - if [[ "${OS_VERSION%%.*}" -ge 8 ]]; then - PKG_MANAGER="dnf" - else - PKG_MANAGER="yum" - fi - ;; - rhel) - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - openeuler) - OS_FAMILY="rhel" - PKG_MANAGER="dnf" - ;; - opensuse* | sles) - OS_TYPE="opensuse" - OS_FAMILY="suse" - PKG_MANAGER="zypper" - ;; - gentoo) - OS_FAMILY="gentoo" - PKG_MANAGER="emerge" - ;; - *) - OS_FAMILY="unknown" - PKG_MANAGER="unknown" - ;; - esac - - # Detect init system - if command -v systemctl &>/dev/null && [[ -d /run/systemd/system ]]; then - INIT_SYSTEM="systemd" - elif command -v rc-service &>/dev/null || [[ -d /etc/init.d && -f /sbin/openrc ]]; then - INIT_SYSTEM="openrc" - elif [[ -f /etc/inittab ]]; then - INIT_SYSTEM="sysvinit" + # Normalize OS type and determine family + case "$OS_TYPE" in + debian) + OS_FAMILY="debian" + PKG_MANAGER="apt" + ;; + ubuntu) + OS_FAMILY="debian" + PKG_MANAGER="apt" + ;; + devuan) + OS_FAMILY="debian" + PKG_MANAGER="apt" + ;; + alpine) + OS_FAMILY="alpine" + PKG_MANAGER="apk" + ;; + fedora) + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + rocky | rockylinux) + OS_TYPE="rocky" + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + alma | almalinux) + OS_TYPE="alma" + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + centos) + OS_FAMILY="rhel" + # CentOS 7 uses yum, 8+ uses dnf + if [[ "${OS_VERSION%%.*}" -ge 8 ]]; then + PKG_MANAGER="dnf" else - INIT_SYSTEM="unknown" + PKG_MANAGER="yum" fi + ;; + rhel) + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + openeuler) + OS_FAMILY="rhel" + PKG_MANAGER="dnf" + ;; + opensuse* | sles) + OS_TYPE="opensuse" + OS_FAMILY="suse" + PKG_MANAGER="zypper" + ;; + gentoo) + OS_FAMILY="gentoo" + PKG_MANAGER="emerge" + ;; + *) + OS_FAMILY="unknown" + PKG_MANAGER="unknown" + ;; + esac + + # Detect init system + if command -v systemctl &>/dev/null && [[ -d /run/systemd/system ]]; then + INIT_SYSTEM="systemd" + elif command -v rc-service &>/dev/null || [[ -d /etc/init.d && -f /sbin/openrc ]]; then + INIT_SYSTEM="openrc" + elif [[ -f /etc/inittab ]]; then + INIT_SYSTEM="sysvinit" + else + INIT_SYSTEM="unknown" + fi } # ------------------------------------------------------------------------------ # Bootstrap: Ensure curl is available and source core functions # ------------------------------------------------------------------------------ _bootstrap() { - # Minimal bootstrap to get curl installed - if ! command -v curl &>/dev/null; then - printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 - if command -v apt-get &>/dev/null; then - apt-get update &>/dev/null && apt-get install -y curl &>/dev/null - elif command -v apk &>/dev/null; then - apk update &>/dev/null && apk add curl &>/dev/null - elif command -v dnf &>/dev/null; then - dnf install -y curl &>/dev/null - elif command -v yum &>/dev/null; then - yum install -y curl &>/dev/null - elif command -v zypper &>/dev/null; then - zypper install -y curl &>/dev/null - elif command -v emerge &>/dev/null; then - emerge --quiet net-misc/curl &>/dev/null - fi + # Minimal bootstrap to get curl installed + if ! command -v curl &>/dev/null; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + if command -v apt-get &>/dev/null; then + apt-get update &>/dev/null && apt-get install -y curl &>/dev/null + elif command -v apk &>/dev/null; then + apk update &>/dev/null && apk add curl &>/dev/null + elif command -v dnf &>/dev/null; then + dnf install -y curl &>/dev/null + elif command -v yum &>/dev/null; then + yum install -y curl &>/dev/null + elif command -v zypper &>/dev/null; then + zypper install -y curl &>/dev/null + elif command -v emerge &>/dev/null; then + emerge --quiet net-misc/curl &>/dev/null fi + fi - # Source core functions - 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 + # Source core functions + 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 } # Run bootstrap and OS detection @@ -193,30 +193,30 @@ detect_os # Updates package manager cache/database # ------------------------------------------------------------------------------ pkg_update() { - case "$PKG_MANAGER" in - apt) - $STD apt-get update - ;; - apk) - $STD apk update - ;; - dnf) - $STD dnf makecache - ;; - yum) - $STD yum makecache - ;; - zypper) - $STD zypper refresh - ;; - emerge) - $STD emerge --sync - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get update + ;; + apk) + $STD apk update + ;; + dnf) + $STD dnf makecache + ;; + yum) + $STD yum makecache + ;; + zypper) + $STD zypper refresh + ;; + emerge) + $STD emerge --sync + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -225,30 +225,30 @@ pkg_update() { # Upgrades all installed packages # ------------------------------------------------------------------------------ pkg_upgrade() { - case "$PKG_MANAGER" in - apt) - $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade - ;; - apk) - $STD apk -U upgrade - ;; - dnf) - $STD dnf -y upgrade - ;; - yum) - $STD yum -y update - ;; - zypper) - $STD zypper -n update - ;; - emerge) - $STD emerge --quiet --update --deep @world - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + ;; + apk) + $STD apk -U upgrade + ;; + dnf) + $STD dnf -y upgrade + ;; + yum) + $STD yum -y update + ;; + zypper) + $STD zypper -n update + ;; + emerge) + $STD emerge --quiet --update --deep @world + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -259,33 +259,33 @@ pkg_upgrade() { # packages - List of packages to install # ------------------------------------------------------------------------------ pkg_install() { - local packages=("$@") - [[ ${#packages[@]} -eq 0 ]] && return 0 + local packages=("$@") + [[ ${#packages[@]} -eq 0 ]] && return 0 - case "$PKG_MANAGER" in - apt) - $STD apt-get install -y "${packages[@]}" - ;; - apk) - $STD apk add --no-cache "${packages[@]}" - ;; - dnf) - $STD dnf install -y "${packages[@]}" - ;; - yum) - $STD yum install -y "${packages[@]}" - ;; - zypper) - $STD zypper install -y "${packages[@]}" - ;; - emerge) - $STD emerge --quiet "${packages[@]}" - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get install -y "${packages[@]}" + ;; + apk) + $STD apk add --no-cache "${packages[@]}" + ;; + dnf) + $STD dnf install -y "${packages[@]}" + ;; + yum) + $STD yum install -y "${packages[@]}" + ;; + zypper) + $STD zypper install -y "${packages[@]}" + ;; + emerge) + $STD emerge --quiet "${packages[@]}" + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -294,33 +294,33 @@ pkg_install() { # Removes one or more packages # ------------------------------------------------------------------------------ pkg_remove() { - local packages=("$@") - [[ ${#packages[@]} -eq 0 ]] && return 0 + local packages=("$@") + [[ ${#packages[@]} -eq 0 ]] && return 0 - case "$PKG_MANAGER" in - apt) - $STD apt-get remove -y "${packages[@]}" - ;; - apk) - $STD apk del "${packages[@]}" - ;; - dnf) - $STD dnf remove -y "${packages[@]}" - ;; - yum) - $STD yum remove -y "${packages[@]}" - ;; - zypper) - $STD zypper remove -y "${packages[@]}" - ;; - emerge) - $STD emerge --quiet --unmerge "${packages[@]}" - ;; - *) - msg_error "Unknown package manager: $PKG_MANAGER" - return 1 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get remove -y "${packages[@]}" + ;; + apk) + $STD apk del "${packages[@]}" + ;; + dnf) + $STD dnf remove -y "${packages[@]}" + ;; + yum) + $STD yum remove -y "${packages[@]}" + ;; + zypper) + $STD zypper remove -y "${packages[@]}" + ;; + emerge) + $STD emerge --quiet --unmerge "${packages[@]}" + ;; + *) + msg_error "Unknown package manager: $PKG_MANAGER" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -329,31 +329,31 @@ pkg_remove() { # Cleans package manager cache to free space # ------------------------------------------------------------------------------ pkg_clean() { - case "$PKG_MANAGER" in - apt) - $STD apt-get autoremove -y - $STD apt-get autoclean - ;; - apk) - $STD apk cache clean - ;; - dnf) - $STD dnf clean all - $STD dnf autoremove -y - ;; - yum) - $STD yum clean all - ;; - zypper) - $STD zypper clean - ;; - emerge) - $STD emerge --quiet --depclean - ;; - *) - return 0 - ;; - esac + case "$PKG_MANAGER" in + apt) + $STD apt-get autoremove -y + $STD apt-get autoclean + ;; + apk) + $STD apk cache clean + ;; + dnf) + $STD dnf clean all + $STD dnf autoremove -y + ;; + yum) + $STD yum clean all + ;; + zypper) + $STD zypper clean + ;; + emerge) + $STD emerge --quiet --depclean + ;; + *) + return 0 + ;; + esac } # ============================================================================== @@ -366,28 +366,28 @@ pkg_clean() { # Enables a service to start at boot # ------------------------------------------------------------------------------ svc_enable() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl enable "$service" - ;; - openrc) - $STD rc-update add "$service" default - ;; - sysvinit) - if command -v update-rc.d &>/dev/null; then - $STD update-rc.d "$service" defaults - elif command -v chkconfig &>/dev/null; then - $STD chkconfig "$service" on - fi - ;; - *) - msg_warn "Unknown init system, cannot enable $service" - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl enable "$service" + ;; + openrc) + $STD rc-update add "$service" default + ;; + sysvinit) + if command -v update-rc.d &>/dev/null; then + $STD update-rc.d "$service" defaults + elif command -v chkconfig &>/dev/null; then + $STD chkconfig "$service" on + fi + ;; + *) + msg_warn "Unknown init system, cannot enable $service" + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -396,27 +396,27 @@ svc_enable() { # Disables a service from starting at boot # ------------------------------------------------------------------------------ svc_disable() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl disable "$service" - ;; - openrc) - $STD rc-update del "$service" default 2>/dev/null || true - ;; - sysvinit) - if command -v update-rc.d &>/dev/null; then - $STD update-rc.d "$service" remove - elif command -v chkconfig &>/dev/null; then - $STD chkconfig "$service" off - fi - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl disable "$service" + ;; + openrc) + $STD rc-update del "$service" default 2>/dev/null || true + ;; + sysvinit) + if command -v update-rc.d &>/dev/null; then + $STD update-rc.d "$service" remove + elif command -v chkconfig &>/dev/null; then + $STD chkconfig "$service" off + fi + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -425,23 +425,23 @@ svc_disable() { # Starts a service immediately # ------------------------------------------------------------------------------ svc_start() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl start "$service" - ;; - openrc) - $STD rc-service "$service" start - ;; - sysvinit) - $STD /etc/init.d/"$service" start - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl start "$service" + ;; + openrc) + $STD rc-service "$service" start + ;; + sysvinit) + $STD /etc/init.d/"$service" start + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -450,23 +450,23 @@ svc_start() { # Stops a running service # ------------------------------------------------------------------------------ svc_stop() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl stop "$service" - ;; - openrc) - $STD rc-service "$service" stop - ;; - sysvinit) - $STD /etc/init.d/"$service" stop - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl stop "$service" + ;; + openrc) + $STD rc-service "$service" stop + ;; + sysvinit) + $STD /etc/init.d/"$service" stop + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -475,23 +475,23 @@ svc_stop() { # Restarts a service # ------------------------------------------------------------------------------ svc_restart() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - $STD systemctl restart "$service" - ;; - openrc) - $STD rc-service "$service" restart - ;; - sysvinit) - $STD /etc/init.d/"$service" restart - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl restart "$service" + ;; + openrc) + $STD rc-service "$service" restart + ;; + sysvinit) + $STD /etc/init.d/"$service" restart + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -500,23 +500,23 @@ svc_restart() { # Gets service status (returns 0 if running) # ------------------------------------------------------------------------------ svc_status() { - local service="$1" - [[ -z "$service" ]] && return 1 + local service="$1" + [[ -z "$service" ]] && return 1 - case "$INIT_SYSTEM" in - systemd) - systemctl is-active --quiet "$service" - ;; - openrc) - rc-service "$service" status &>/dev/null - ;; - sysvinit) - /etc/init.d/"$service" status &>/dev/null - ;; - *) - return 1 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + systemctl is-active --quiet "$service" + ;; + openrc) + rc-service "$service" status &>/dev/null + ;; + sysvinit) + /etc/init.d/"$service" status &>/dev/null + ;; + *) + return 1 + ;; + esac } # ------------------------------------------------------------------------------ @@ -525,15 +525,15 @@ svc_status() { # Reloads init system daemon configuration (for systemd) # ------------------------------------------------------------------------------ svc_reload_daemon() { - case "$INIT_SYSTEM" in - systemd) - $STD systemctl daemon-reload - ;; - *) - # Other init systems don't need this - return 0 - ;; - esac + case "$INIT_SYSTEM" in + systemd) + $STD systemctl daemon-reload + ;; + *) + # Other init systems don't need this + return 0 + ;; + esac } # ============================================================================== @@ -547,24 +547,24 @@ svc_reload_daemon() { # Returns: IP address string # ------------------------------------------------------------------------------ get_ip() { - local ip="" + local ip="" - # Try hostname -I first (most common) - if command -v hostname &>/dev/null; then - ip=$(hostname -I 2>/dev/null | awk '{print $1}') - fi + # Try hostname -I first (most common) + if command -v hostname &>/dev/null; then + ip=$(hostname -I 2>/dev/null | awk '{print $1}') + fi - # Fallback to ip command - if [[ -z "$ip" ]] && command -v ip &>/dev/null; then - ip=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1) - fi + # Fallback to ip command + if [[ -z "$ip" ]] && command -v ip &>/dev/null; then + ip=$(ip -4 addr show scope global | awk '/inet /{print $2}' | cut -d/ -f1 | head -1) + fi - # Fallback to ifconfig - if [[ -z "$ip" ]] && command -v ifconfig &>/dev/null; then - ip=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -1) - fi + # Fallback to ifconfig + if [[ -z "$ip" ]] && command -v ifconfig &>/dev/null; then + ip=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -1) + fi - echo "$ip" + echo "$ip" } # ------------------------------------------------------------------------------ @@ -574,25 +574,25 @@ get_ip() { # If IPV6_METHOD=disable: disables IPv6 via sysctl # ------------------------------------------------------------------------------ verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE + set_std_mode # Set STD mode based on VERBOSE - if [[ "${IPV6_METHOD:-}" == "disable" ]]; then - msg_info "Disabling IPv6 (this may affect some services)" - mkdir -p /etc/sysctl.d - cat >/etc/sysctl.d/99-disable-ipv6.conf </etc/sysctl.d/99-disable-ipv6.conf </dev/null || true - fi - msg_ok "Disabled IPv6" + # For OpenRC, ensure sysctl runs at boot + if [[ "$INIT_SYSTEM" == "openrc" ]]; then + $STD rc-update add sysctl default 2>/dev/null || true fi + msg_ok "Disabled IPv6" + fi } # ------------------------------------------------------------------------------ @@ -604,36 +604,36 @@ EOF # - Disables network wait services # ------------------------------------------------------------------------------ setting_up_container() { - msg_info "Setting up Container OS" + msg_info "Setting up Container OS" - # Wait for network - local i - for ((i = RETRY_NUM; i > 0; i--)); do - if [[ -n "$(get_ip)" ]]; then - break - fi - echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep "$RETRY_EVERY" - done - - if [[ -z "$(get_ip)" ]]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 + # Wait for network + local i + for ((i = RETRY_NUM; i > 0; i--)); do + if [[ -n "$(get_ip)" ]]; then + break fi + echo 1>&2 -en "${CROSS}${RD} No Network! " + sleep "$RETRY_EVERY" + done - # Remove Python EXTERNALLY-MANAGED restriction (Debian 12+, Ubuntu 23.04+) - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true + if [[ -z "$(get_ip)" ]]; then + echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi - # Disable network wait services for faster boot - case "$INIT_SYSTEM" in - systemd) - systemctl disable -q --now systemd-networkd-wait-online.service 2>/dev/null || true - ;; - esac + # Remove Python EXTERNALLY-MANAGED restriction (Debian 12+, Ubuntu 23.04+) + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true - msg_ok "Set up Container OS" - msg_ok "Network Connected: ${BL}$(get_ip)" + # Disable network wait services for faster boot + case "$INIT_SYSTEM" in + systemd) + systemctl disable -q --now systemd-networkd-wait-online.service 2>/dev/null || true + ;; + esac + + msg_ok "Set up Container OS" + msg_ok "Network Connected: ${BL}$(get_ip)" } # ------------------------------------------------------------------------------ @@ -643,65 +643,65 @@ setting_up_container() { # Tests connectivity to DNS servers and verifies DNS resolution # ------------------------------------------------------------------------------ network_check() { - set +e - trap - ERR - local ipv4_connected=false - local ipv6_connected=false - sleep 1 + set +e + trap - ERR + local ipv4_connected=false + local ipv6_connected=false + sleep 1 - # Check IPv4 connectivity - 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 + # Check IPv4 connectivity + 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 (if ping6 exists) + if command -v ping6 &>/dev/null; then + if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null; then + msg_ok "IPv6 Internet Connected" + ipv6_connected=true else - msg_error "IPv4 Internet Not Connected" + msg_error "IPv6 Internet Not Connected" fi + fi - # Check IPv6 connectivity (if ping6 exists) - if command -v ping6 &>/dev/null; then - if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null; then - msg_ok "IPv6 Internet Connected" - ipv6_connected=true - else - msg_error "IPv6 Internet Not Connected" - fi - fi - - # Prompt if both fail - 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 - local GIT_HOSTS=("github.com" "raw.githubusercontent.com" "git.community-scripts.org") - local GIT_STATUS="Git DNS:" - local DNS_FAILED=false - - for HOST in "${GIT_HOSTS[@]}"; do - local RESOLVEDIP - RESOLVEDIP=$(getent hosts "$HOST" 2>/dev/null | awk '{ print $1 }' | head -n1) - if [[ -z "$RESOLVEDIP" ]]; then - GIT_STATUS+=" $HOST:(${DNSFAIL:-FAIL})" - DNS_FAILED=true - else - GIT_STATUS+=" $HOST:(${DNSOK:-OK})" - fi - done - - if [[ "$DNS_FAILED" == true ]]; then - fatal "$GIT_STATUS" + # Prompt if both fail + 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 - msg_ok "$GIT_STATUS" + echo -e "${NETWORK}Check Network Settings" + exit 1 fi + fi - set -e - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + # DNS resolution checks + local GIT_HOSTS=("github.com" "raw.githubusercontent.com" "git.community-scripts.org") + local GIT_STATUS="Git DNS:" + local DNS_FAILED=false + + for HOST in "${GIT_HOSTS[@]}"; do + local RESOLVEDIP + RESOLVEDIP=$(getent hosts "$HOST" 2>/dev/null | awk '{ print $1 }' | head -n1) + if [[ -z "$RESOLVEDIP" ]]; then + GIT_STATUS+=" $HOST:(${DNSFAIL:-FAIL})" + DNS_FAILED=true + else + GIT_STATUS+=" $HOST:(${DNSOK:-OK})" + 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 } # ============================================================================== @@ -714,12 +714,12 @@ network_check() { # Updates container OS and sources appropriate tools.func # ------------------------------------------------------------------------------ update_os() { - msg_info "Updating Container OS" + msg_info "Updating Container OS" - # Configure APT cacher proxy if enabled (Debian/Ubuntu only) - if [[ "$PKG_MANAGER" == "apt" && "${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 + # Configure APT cacher proxy if enabled (Debian/Ubuntu only) + if [[ "$PKG_MANAGER" == "apt" && "${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" @@ -727,27 +727,27 @@ else echo -n "DIRECT" fi EOF - chmod +x /usr/local/bin/apt-proxy-detect.sh - fi + chmod +x /usr/local/bin/apt-proxy-detect.sh + fi - # Update and upgrade - pkg_update - pkg_upgrade + # Update and upgrade + pkg_update + pkg_upgrade - # Remove Python EXTERNALLY-MANAGED restriction - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true + # Remove Python EXTERNALLY-MANAGED restriction + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true - msg_ok "Updated Container OS" + msg_ok "Updated Container OS" - # Source appropriate tools.func based on OS - case "$OS_FAMILY" in - alpine) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) - ;; - *) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - ;; - esac + # Source appropriate tools.func based on OS + case "$OS_FAMILY" in + alpine) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) + ;; + *) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + ;; + esac } # ============================================================================== @@ -760,79 +760,79 @@ EOF # Configures Message of the Day and SSH settings # ------------------------------------------------------------------------------ motd_ssh() { - # Set terminal to 256-color mode - grep -qxF "export TERM='xterm-256color'" /root/.bashrc 2>/dev/null || echo "export TERM='xterm-256color'" >>/root/.bashrc + # Set terminal to 256-color mode + grep -qxF "export TERM='xterm-256color'" /root/.bashrc 2>/dev/null || echo "export TERM='xterm-256color'" >>/root/.bashrc - # Get OS information - local os_name="$OS_TYPE" - local os_version="$OS_VERSION" + # Get OS information + local os_name="$OS_TYPE" + local os_version="$OS_VERSION" - 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 '"') - 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 '"') + fi - # Create MOTD profile script - local PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - cat >"$PROFILE_FILE" <"$PROFILE_FILE" </dev/null | awk '{print \$1}' || ip -4 addr show scope global | grep -oP '(?<=inet\s)\\d+(\\.\\d+){3}' | head -1)${CL:-}" +echo -e "${YW:-} IP Address: ${GN:-}\$(hostname -I 2>/dev/null | awk '{print \$1}' || ip -4 addr show scope global | awk '/inet /{print \$2}' | cut -d/ -f1 | head -1)${CL:-}" echo -e "${YW:-} Repository: ${GN:-}https://github.com/community-scripts/ProxmoxVED${CL:-}" echo "" EOF - # Disable default MOTD scripts (Debian/Ubuntu) - [[ -d /etc/update-motd.d ]] && chmod -x /etc/update-motd.d/* 2>/dev/null || true + # Disable default MOTD scripts (Debian/Ubuntu) + [[ -d /etc/update-motd.d ]] && chmod -x /etc/update-motd.d/* 2>/dev/null || true - # Configure SSH root access if requested - if [[ "${SSH_ROOT:-}" == "yes" ]]; then - # Ensure SSH server is installed - if [[ ! -f /etc/ssh/sshd_config ]]; then - msg_info "Installing SSH server" - case "$PKG_MANAGER" in - apt) - pkg_install openssh-server - ;; - apk) - pkg_install openssh - rc-update add sshd default 2>/dev/null || true - ;; - dnf | yum) - pkg_install openssh-server - ;; - zypper) - pkg_install openssh - ;; - emerge) - pkg_install net-misc/openssh - ;; - esac - msg_ok "Installed SSH server" - fi - - local sshd_config="/etc/ssh/sshd_config" - if [[ -f "$sshd_config" ]]; then - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" - sed -i "s/PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" - - case "$INIT_SYSTEM" in - systemd) - svc_restart sshd 2>/dev/null || svc_restart ssh 2>/dev/null || true - ;; - openrc) - svc_enable sshd 2>/dev/null || true - svc_start sshd 2>/dev/null || true - ;; - *) - svc_restart sshd 2>/dev/null || true - ;; - esac - fi + # Configure SSH root access if requested + if [[ "${SSH_ROOT:-}" == "yes" ]]; then + # Ensure SSH server is installed + if [[ ! -f /etc/ssh/sshd_config ]]; then + msg_info "Installing SSH server" + case "$PKG_MANAGER" in + apt) + pkg_install openssh-server + ;; + apk) + pkg_install openssh + rc-update add sshd default 2>/dev/null || true + ;; + dnf | yum) + pkg_install openssh-server + ;; + zypper) + pkg_install openssh + ;; + emerge) + pkg_install net-misc/openssh + ;; + esac + msg_ok "Installed SSH server" fi + + local sshd_config="/etc/ssh/sshd_config" + if [[ -f "$sshd_config" ]]; then + sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" + sed -i "s/PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" + + case "$INIT_SYSTEM" in + systemd) + svc_restart sshd 2>/dev/null || svc_restart ssh 2>/dev/null || true + ;; + openrc) + svc_enable sshd 2>/dev/null || true + svc_start sshd 2>/dev/null || true + ;; + *) + svc_restart sshd 2>/dev/null || true + ;; + esac + fi + fi } # ============================================================================== @@ -845,104 +845,104 @@ EOF # Customizes container for passwordless login and creates update script # ------------------------------------------------------------------------------ customize() { - if [[ "${PASSWORD:-}" == "" ]]; then - msg_info "Customizing Container" + if [[ "${PASSWORD:-}" == "" ]]; then + msg_info "Customizing Container" - # Remove root password for auto-login - passwd -d root &>/dev/null || true + # Remove root password for auto-login + passwd -d root &>/dev/null || true - case "$INIT_SYSTEM" in - systemd) - # Mask services that block boot in LXC containers - # systemd-homed-firstboot.service hangs waiting for user input on Fedora - systemctl mask systemd-homed-firstboot.service &>/dev/null || true - systemctl mask systemd-homed.service &>/dev/null || true + case "$INIT_SYSTEM" in + systemd) + # Mask services that block boot in LXC containers + # systemd-homed-firstboot.service hangs waiting for user input on Fedora + systemctl mask systemd-homed-firstboot.service &>/dev/null || true + systemctl mask systemd-homed.service &>/dev/null || true - # Configure console-getty for auto-login in LXC containers - # console-getty.service is THE service that handles /dev/console in LXC - # It's present on all systemd distros but not enabled by default on Fedora/RHEL + # Configure console-getty for auto-login in LXC containers + # console-getty.service is THE service that handles /dev/console in LXC + # It's present on all systemd distros but not enabled by default on Fedora/RHEL - if [[ -f /usr/lib/systemd/system/console-getty.service ]]; then - mkdir -p /etc/systemd/system/console-getty.service.d - cat >/etc/systemd/system/console-getty.service.d/override.conf <<'EOF' + if [[ -f /usr/lib/systemd/system/console-getty.service ]]; then + mkdir -p /etc/systemd/system/console-getty.service.d + cat >/etc/systemd/system/console-getty.service.d/override.conf <<'EOF' [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud 115200,38400,9600 - $TERM EOF - # Enable console-getty for LXC web console (required on Fedora/RHEL) - systemctl enable console-getty.service &>/dev/null || true - fi + # Enable console-getty for LXC web console (required on Fedora/RHEL) + systemctl enable console-getty.service &>/dev/null || true + fi - # Also configure container-getty@1 (Debian/Ubuntu default in LXC) - if [[ -f /usr/lib/systemd/system/container-getty@.service ]]; then - mkdir -p /etc/systemd/system/container-getty@1.service.d - cat >/etc/systemd/system/container-getty@1.service.d/override.conf <<'EOF' + # Also configure container-getty@1 (Debian/Ubuntu default in LXC) + if [[ -f /usr/lib/systemd/system/container-getty@.service ]]; then + mkdir -p /etc/systemd/system/container-getty@1.service.d + cat >/etc/systemd/system/container-getty@1.service.d/override.conf <<'EOF' [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 - $TERM EOF - fi + fi - # Reload systemd and restart getty services to apply auto-login - systemctl daemon-reload - systemctl restart console-getty.service &>/dev/null || true - systemctl restart container-getty@1.service &>/dev/null || true - ;; + # Reload systemd and restart getty services to apply auto-login + systemctl daemon-reload + systemctl restart console-getty.service &>/dev/null || true + systemctl restart container-getty@1.service &>/dev/null || true + ;; - openrc) - # Alpine/Gentoo: modify inittab for auto-login - if [[ -f /etc/inittab ]]; then - sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab - fi - touch /root/.hushlogin - ;; + openrc) + # Alpine/Gentoo: modify inittab for auto-login + if [[ -f /etc/inittab ]]; then + sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab + fi + touch /root/.hushlogin + ;; - sysvinit) - # Devuan/older systems - modify inittab for auto-login - # Devuan 5 (daedalus) uses SysVinit with various inittab formats - # CRITICAL: LXC uses /dev/console, NOT tty1! pct console connects to console device - if [[ -f /etc/inittab ]]; then - # Backup original inittab - cp /etc/inittab /etc/inittab.bak 2>/dev/null || true + sysvinit) + # Devuan/older systems - modify inittab for auto-login + # Devuan 5 (daedalus) uses SysVinit with various inittab formats + # CRITICAL: LXC uses /dev/console, NOT tty1! pct console connects to console device + if [[ -f /etc/inittab ]]; then + # Backup original inittab + cp /etc/inittab /etc/inittab.bak 2>/dev/null || true - # First, enable autologin on tty1 (for direct access) - sed -i 's|^1:[0-9]*:respawn:.*/\(a\?getty\).*|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab + # First, enable autologin on tty1 (for direct access) + sed -i 's|^1:[0-9]*:respawn:.*/\(a\?getty\).*|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab - # CRITICAL: Add console entry for LXC - this is what pct console uses! - # Check if there's already a console getty entry - if ! grep -qE '^[^#].*respawn.*console' /etc/inittab; then - # Add new console entry for LXC - echo "" >>/etc/inittab - echo "# LXC console autologin (added by community-scripts)" >>/etc/inittab - echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux" >>/etc/inittab - else - # Enable autologin on existing console entry - sed -i 's|^[^#]*:[0-9]*:respawn:.*/\(a\?getty\).*console.*|co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux|' /etc/inittab - fi + # CRITICAL: Add console entry for LXC - this is what pct console uses! + # Check if there's already a console getty entry + if ! grep -qE '^[^#].*respawn.*console' /etc/inittab; then + # Add new console entry for LXC + echo "" >>/etc/inittab + echo "# LXC console autologin (added by community-scripts)" >>/etc/inittab + echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux" >>/etc/inittab + else + # Enable autologin on existing console entry + sed -i 's|^[^#]*:[0-9]*:respawn:.*/\(a\?getty\).*console.*|co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux|' /etc/inittab + fi - # Force a reload of inittab - try multiple methods - telinit q &>/dev/null || init q &>/dev/null || kill -1 1 &>/dev/null || true - fi - touch /root/.hushlogin - ;; - esac + # Force a reload of inittab - try multiple methods + telinit q &>/dev/null || init q &>/dev/null || kill -1 1 &>/dev/null || true + fi + touch /root/.hushlogin + ;; + esac - msg_ok "Customized Container" - fi + msg_ok "Customized Container" + fi - # Create update script - # Use var_os for OS-based containers, otherwise use app name - local update_script_name="${var_os:-$app}" - echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update + # Create update script + # Use var_os for OS-based containers, otherwise use app name + local update_script_name="${var_os:-$app}" + echo "bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/${update_script_name}.sh)\"" >/usr/bin/update + chmod +x /usr/bin/update - # Inject SSH authorized keys if provided - 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 + # Inject SSH authorized keys if provided + 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 } # ============================================================================== @@ -956,8 +956,8 @@ EOF # Returns: 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_tz() { - local tz="$1" - [[ -f "/usr/share/zoneinfo/$tz" ]] + local tz="$1" + [[ -f "/usr/share/zoneinfo/$tz" ]] } # ------------------------------------------------------------------------------ @@ -966,21 +966,21 @@ validate_tz() { # Sets container timezone # ------------------------------------------------------------------------------ set_timezone() { - local tz="$1" - if validate_tz "$tz"; then - ln -sf "/usr/share/zoneinfo/$tz" /etc/localtime - echo "$tz" >/etc/timezone 2>/dev/null || true + local tz="$1" + if validate_tz "$tz"; then + ln -sf "/usr/share/zoneinfo/$tz" /etc/localtime + echo "$tz" >/etc/timezone 2>/dev/null || true - # Update tzdata if available - case "$PKG_MANAGER" in - apt) - dpkg-reconfigure -f noninteractive tzdata 2>/dev/null || true - ;; - esac - msg_ok "Timezone set to $tz" - else - msg_warn "Invalid timezone: $tz" - fi + # Update tzdata if available + case "$PKG_MANAGER" in + apt) + dpkg-reconfigure -f noninteractive tzdata 2>/dev/null || true + ;; + esac + msg_ok "Timezone set to $tz" + else + msg_warn "Invalid timezone: $tz" + fi } # ------------------------------------------------------------------------------ @@ -989,9 +989,9 @@ set_timezone() { # Prints detected OS information (for debugging) # ------------------------------------------------------------------------------ os_info() { - echo "OS Type: $OS_TYPE" - echo "OS Family: $OS_FAMILY" - echo "OS Version: $OS_VERSION" - echo "Pkg Manager: $PKG_MANAGER" - echo "Init System: $INIT_SYSTEM" + echo "OS Type: $OS_TYPE" + echo "OS Family: $OS_FAMILY" + echo "OS Version: $OS_VERSION" + echo "Pkg Manager: $PKG_MANAGER" + echo "Init System: $INIT_SYSTEM" } From 82053c5b70e222c1af486b067fecf13972061bef Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:04:43 +0100 Subject: [PATCH 373/378] refactor rustypaste and add alpine-tools --- ct/rustypaste.sh | 62 ++++----- frontend/public/json/rustypaste.json | 10 +- install/alpine-rustypaste-install.sh | 46 +++++++ install/rustypaste-install.sh | 12 +- misc/alpine-tools.func | 196 ++++++++++++++++++++++++++- misc/install.func | 2 +- 6 files changed, 275 insertions(+), 53 deletions(-) create mode 100644 install/alpine-rustypaste-install.sh diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index 504e142dd..a4f1bb6da 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -8,7 +8,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ APP="rustypaste" var_tags="${var_tags:-pastebin;storage}" var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" +var_ram="${var_ram:-1024}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" @@ -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 "/opt/rustypaste/target/release/rustypaste" ]]; then - msg_error "No rustypaste Installation Found!" - exit - fi - - if check_for_gh_release "rustypaste" "orhun/rustypaste"; then - msg_info "Stopping Services" - systemctl stop rustypaste - msg_ok "Stopped Services" - - msg_info "Creating Backup" - tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" "/opt/rustypaste/upload" - msg_ok "Backup Created" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" - - msg_info "Updating rustypaste" - cd /opt/rustypaste - sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml - $STD cargo build --locked --release - msg_ok "Updated rustypaste" - - msg_info "Starting Services" - systemctl start rustypaste - msg_ok "Started Services" - msg_ok "Updated successfully!" - fi + if [[ ! -f "/opt/rustypaste/target/release/rustypaste" ]]; then + msg_error "No rustypaste Installation Found!" exit + fi + + if check_for_gh_release "rustypaste" "orhun/rustypaste"; then + msg_info "Stopping Services" + systemctl stop rustypaste + msg_ok "Stopped Services" + + msg_info "Creating Backup" + tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" "/opt/rustypaste/upload" + msg_ok "Backup Created" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" + + msg_info "Updating rustypaste" + cd /opt/rustypaste + sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml + $STD cargo build --locked --release + msg_ok "Updated rustypaste" + + msg_info "Starting Services" + systemctl start rustypaste + msg_ok "Started Services" + msg_ok "Updated successfully!" + fi + exit } start diff --git a/frontend/public/json/rustypaste.json b/frontend/public/json/rustypaste.json index ac81889d5..e71f72687 100644 --- a/frontend/public/json/rustypaste.json +++ b/frontend/public/json/rustypaste.json @@ -20,7 +20,7 @@ "script": "ct/rustypaste.sh", "resources": { "cpu": 1, - "ram": 512, + "ram": 1024, "hdd": 20, "os": "Debian", "version": "13" @@ -32,9 +32,9 @@ "password": null }, "notes": [ - { - "text": "When updating the script it will backup the whole project including all the uploaded files, make sure to extract it to a safe location or remove", - "type": "info" - } + { + "text": "When updating the script it will backup the whole project including all the uploaded files, make sure to extract it to a safe location or remove", + "type": "info" + } ] } diff --git a/install/alpine-rustypaste-install.sh b/install/alpine-rustypaste-install.sh new file mode 100644 index 000000000..ddc9ec9cf --- /dev/null +++ b/install/alpine-rustypaste-install.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/orhun/rustypaste + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-musl.tar.gz" + +msg_info "Setting up rustypaste" +cd /opt/rustypaste +sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml +msg_ok "Set up rustypaste" + +msg_info "Creating Service" +cat <<'EOF' >/etc/init.d/rustypaste +#!/sbin/openrc-run + +name="rustypaste" +description="rustypaste Service" +directory="/opt/rustypaste" +command="/opt/rustypaste/rustypaste" +command_args="" +pidfile="/run/${RC_SVCNAME}.pid" +command_background="yes" +start_stop_daemon_args="--user root" + +depend() { + need net +} +EOF +chmod +x /etc/init.d/rustypaste +rc-update add rustypaste default +rc-service rustypaste start +msg_ok "Created Service" + +motd_ssh +customize diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index 1777780c0..abb0b88ff 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG -# Author: GoldenSpringness +# Author: GoldenSpringness | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste @@ -13,17 +13,11 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt install -y build-essential -msg_ok "Dependencies Installed Successfully" - -RUST_VERSION="1.92.0" setup_rust -fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" +fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-gnu.tar.gz" msg_info "Setting up rustypaste" cd /opt/rustypaste sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml -$STD cargo build --locked --release msg_ok "Set up rustypaste" msg_info "Creating Service" @@ -34,7 +28,7 @@ After=network.target [Service] WorkingDirectory=/opt/rustypaste -ExecStart=/opt/rustypaste/target/release/rustypaste +ExecStart=/opt/rustypaste/rustypaste Restart=always [Install] diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 663b5f1d3..5a1043b94 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -9,6 +9,145 @@ lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; } has() { command -v "$1" >/dev/null 2>&1; } +# tools.func compatibility helpers (Alpine-safe) +cache_installed_version() { + local app="$1" 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 + return 0 +} + +version_gt() { + # returns 0 if $1 > $2 + # BusyBox-safe version compare + awk -v a="$1" -v b="$2" ' + function splitver(v, arr) { n=split(v, arr, /\./); return n } + BEGIN { + na=splitver(a, A); nb=splitver(b, B); + n=(na>nb?na:nb); + for (i=1;i<=n;i++) { + va=(A[i]==""?0:A[i]); vb=(B[i]==""?0:B[i]); + if (va+0 > vb+0) exit 0; + if (va+0 < vb+0) exit 1; + } + exit 1; + }' +} + +get_system_arch() { + local arch + arch=$(uname -m 2>/dev/null || echo "") + [ "$arch" = "x86_64" ] && arch="amd64" + [ "$arch" = "aarch64" ] && arch="arm64" + echo "$arch" +} + +create_temp_dir() { + mktemp -d +} + +get_os_info() { + local field="${1:-all}" + [ -z "${_OS_ID:-}" ] && _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release 2>/dev/null) + [ -z "${_OS_CODENAME:-}" ] && _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release 2>/dev/null) + [ -z "${_OS_VERSION:-}" ] && _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release 2>/dev/null) + case "$field" in + id) echo "$_OS_ID" ;; + codename) echo "$_OS_CODENAME" ;; + version | version_id) echo "$_OS_VERSION" ;; + all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; + *) echo "$_OS_ID" ;; + esac +} + +is_alpine() { [ "$(get_os_info id)" = "alpine" ]; } + +get_os_version_major() { + local v + v=$(get_os_info version) + echo "${v%%.*}" +} + +ensure_dependencies() { + need_tool "$@" +} + +download_file() { + local url="$1" output="$2" max_retries="${3:-3}" show_progress="${4:-false}" + local i=1 curl_opts="-fsSL" + [ "$show_progress" = "true" ] && curl_opts="-fL#" + while [ $i -le "$max_retries" ]; do + if curl $curl_opts -o "$output" "$url"; then + return 0 + fi + i=$((i + 1)) + [ $i -le "$max_retries" ] && sleep 2 + done + msg_error "Failed to download: $url" + return 1 +} + +github_api_call() { + local url="$1" output_file="${2:-/dev/stdout}" + local max_retries=3 retry_delay=2 attempt=1 + local header="" + [ -n "${GITHUB_TOKEN:-}" ] && header="-H Authorization:Bearer\ ${GITHUB_TOKEN}" + while [ $attempt -le $max_retries ]; do + 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 "$url" 2>/dev/null || echo 000) + case "$http_code" in + 200) return 0 ;; + 403) [ $attempt -lt $max_retries ] && sleep "$retry_delay" || { + msg_error "GitHub API rate limit exceeded" + return 1 + } ;; + 404) + msg_error "GitHub API endpoint not found: $url" + return 1 + ;; + *) [ $attempt -lt $max_retries ] && sleep "$retry_delay" || { + msg_error "GitHub API call failed with HTTP $http_code" + return 1 + } ;; + esac + retry_delay=$((retry_delay * 2)) + attempt=$((attempt + 1)) + done + return 1 +} + +extract_version_from_json() { + local json="$1" field="${2:-tag_name}" strip_v="${3:-true}" version + need_tool jq || return 1 + version=$(printf '%s' "$json" | jq -r ".${field} // empty") + [ -z "$version" ] && return 1 + [ "$strip_v" = "true" ] && printf '%s' "${version#v}" || printf '%s' "$version" +} + +get_latest_github_release() { + local repo="$1" strip_v="${2:-true}" tmp + tmp=$(mktemp) || return 1 + github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$tmp" || { + rm -f "$tmp" + return 1 + } + extract_version_from_json "$(cat "$tmp")" "tag_name" "$strip_v" + rc=$? + rm -f "$tmp" + return $rc +} + need_tool() { # usage: need_tool curl jq unzip ... # setup missing tools via apk @@ -78,7 +217,12 @@ check_for_gh_release() { } need_tool curl jq || return 1 - tag=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name // empty') + github_api_call "https://api.github.com/repos/${source}/releases/latest" "/tmp/${app_lc}-release.json" || { + msg_error "Unable to fetch latest tag for $app" + return 1 + } + tag=$(cat "/tmp/${app_lc}-release.json" | jq -r '.tag_name // empty') + rm -f "/tmp/${app_lc}-release.json" [ -z "$tag" ] && { msg_error "Unable to fetch latest tag for $app" return 1 @@ -115,7 +259,7 @@ check_for_gh_release() { # GitHub: get Release & deployen (Alpine) # modes: tarball | prebuild | singlefile # ------------------------------ -fetch_and_deploy_gh() { +fetch_and_deploy_gh_release() { # $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 @@ -133,20 +277,21 @@ fetch_and_deploy_gh() { tmpd="$(mktemp -d)" || return 1 mkdir -p "$target" - # Release JSON + # Release JSON (with token/rate-limit handling) if [ "$version" = "latest" ]; then - json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/latest")" || { + github_api_call "https://api.github.com/repos/$repo/releases/latest" "$tmpd/release.json" || { msg_error "GitHub API failed" rm -rf "$tmpd" return 1 } else - json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || { + github_api_call "https://api.github.com/repos/$repo/releases/tags/$version" "$tmpd/release.json" || { msg_error "GitHub API failed" rm -rf "$tmpd" return 1 } fi + json="$(cat "$tmpd/release.json")" # correct Version version="$(printf '%s' "$json" | jq -r '.tag_name // empty')" @@ -173,6 +318,7 @@ fetch_and_deploy_gh() { return 1 } unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)" + [ "${CLEAN_INSTALL:-0}" = "1" ] && rm -rf "${target:?}/"* # copy content of unpack to target (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" @@ -180,6 +326,34 @@ fetch_and_deploy_gh() { return 1 } ;; + binary) + [ -n "$pattern" ] || pattern="*.apk" + 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 "binary asset not found for pattern: $pattern" + rm -rf "$tmpd" + return 1 + } + filename="${url##*/}" + download_with_progress "$url" "$tmpd/$filename" || { + rm -rf "$tmpd" + return 1 + } + case "$filename" in + *.apk) + apk add --no-cache --allow-untrusted "$tmpd/$filename" >/dev/null 2>&1 || { + msg_error "apk install failed: $filename" + rm -rf "$tmpd" + return 1 + } + ;; + *) + msg_error "Unsupported binary asset on Alpine: $filename" + rm -rf "$tmpd" + return 1 + ;; + esac + ;; prebuild) [ -n "$pattern" ] || { msg_error "prebuild requires asset pattern" @@ -220,6 +394,7 @@ fetch_and_deploy_gh() { return 1 ;; esac + [ "${CLEAN_INSTALL:-0}" = "1" ] && rm -rf "${target:?}/"* # 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)" @@ -252,11 +427,13 @@ fetch_and_deploy_gh() { return 1 } filename="${url##*/}" - download_with_progress "$url" "$target/$app" || { + local target_file="$app" + [ "${USE_ORIGINAL_FILENAME:-false}" = "true" ] && target_file="$filename" + download_with_progress "$url" "$target/$target_file" || { rm -rf "$tmpd" return 1 } - chmod +x "$target/$app" + chmod +x "$target/$target_file" ;; *) msg_error "Unknown mode: $mode" @@ -271,6 +448,11 @@ fetch_and_deploy_gh() { msg_ok "Deployed $app ($version) → $target" } +# tools.func compatibility alias +fetch_and_deploy_gh() { + fetch_and_deploy_gh_release "$@" +} + # ------------------------------ # yq (mikefarah) – Alpine # ------------------------------ diff --git a/misc/install.func b/misc/install.func index 591404363..c8d6ae7e1 100644 --- a/misc/install.func +++ b/misc/install.func @@ -551,7 +551,7 @@ get_ip() { # Try hostname -I first (most common) if command -v hostname &>/dev/null; then - ip=$(hostname -I 2>/dev/null | awk '{print $1}') + ip=$(hostname -I 2>/dev/null | awk '{print $1}' || true) fi # Fallback to ip command From 24b46ee21c6b0a3d5b89d3d64101875927bd604f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:47:54 +0100 Subject: [PATCH 374/378] testing --- ct/unifi-os-server.sh | 41 +++++++++++++++ install/unifi-os-server-install.sh | 81 ++++++++++++++++++++++++++++++ misc/alpine-tools.func | 14 ++++++ 3 files changed, 136 insertions(+) create mode 100644 ct/unifi-os-server.sh create mode 100644 install/unifi-os-server-install.sh diff --git a/ct/unifi-os-server.sh b/ct/unifi-os-server.sh new file mode 100644 index 000000000..a6da9d366 --- /dev/null +++ b/ct/unifi-os-server.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://ui.com/ + +APP="UniFi OS Server" +var_tags="${var_tags:-network}" +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:-0}" +var_tun="${var_tun:-yes}" +var_nesting="${var_nesting:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /usr/local/sbin/unifi-os-server.bin && ! -d /data/unifi ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_error "UniFi OS Server updates are handled by the UniFi OS app itself." + 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/unifi-os-server-install.sh b/install/unifi-os-server-install.sh new file mode 100644 index 000000000..58dea42cf --- /dev/null +++ b/install/unifi-os-server-install.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://ui.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +if [[ "${CTTYPE:-1}" != "0" ]]; then + msg_error "UniFi OS Server requires a privileged LXC container." + msg_error "Recreate the container with unprivileged=0." + exit 1 +fi + +if [[ ! -e /dev/net/tun ]]; then + msg_error "Missing /dev/net/tun in container." + msg_error "Enable TUN/TAP (var_tun=yes) or add /dev/net/tun passthrough." + exit 1 +fi + +msg_info "Installing dependencies" +$STD apt-get install -y ca-certificates curl jq podman uidmap slirp4netns wget +msg_ok "Installed dependencies" + +msg_info "Installing sysctl wrapper (ignore non-critical errors)" +cat <<'EOF' >/usr/local/sbin/sysctl +#!/bin/sh +/usr/sbin/sysctl "$@" || true +exit 0 +EOF +chmod +x /usr/local/sbin/sysctl +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +msg_ok "Sysctl wrapper installed" + +msg_info "Fetching latest UniFi OS Server" +API_URL="https://fw-update.ui.com/api/firmware-latest" +TEMP_JSON="$(mktemp)" +if ! curl -fsSL "$API_URL" -o "$TEMP_JSON"; then + rm -f "$TEMP_JSON" + msg_error "Failed to fetch data from Ubiquiti API" + exit 1 +fi + +LATEST=$(jq -r ' + ._embedded.firmware + | map(select(.product == "unifi-os-server")) + | map(select(.platform == "linux-x64")) + | sort_by(.version_major, .version_minor, .version_patch) + | last +' "$TEMP_JSON") + +UOS_VERSION=$(echo "$LATEST" | jq -r '.version' | sed 's/^v//') +UOS_URL=$(echo "$LATEST" | jq -r '._links.data.href') +rm -f "$TEMP_JSON" + +if [[ -z "$UOS_URL" || -z "$UOS_VERSION" || "$UOS_URL" == "null" ]]; then + msg_error "Failed to parse UniFi OS Server version or download URL" + exit 1 +fi +msg_ok "Found UniFi OS Server ${UOS_VERSION}" + +msg_info "Downloading UniFi OS Server installer" +mkdir -p /usr/local/sbin +curl -fsSL "$UOS_URL" -o /usr/local/sbin/unifi-os-server.bin +chmod +x /usr/local/sbin/unifi-os-server.bin +msg_ok "Downloaded UniFi OS Server installer" + +msg_info "Installing UniFi OS Server (this takes a few minutes)" +echo y | /usr/local/sbin/unifi-os-server.bin +msg_ok "UniFi OS Server installed" + +motd_ssh +customize +cleanup_lxc diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 5a1043b94..955554216 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -3,6 +3,20 @@ # Erwartet vorhandene msg_* und optional $STD aus deinem Framework. +# Fallbacks, wenn core.func nicht geladen wurde (Alpine/ash-safe) +if ! command -v msg_info >/dev/null 2>&1; then + msg_info() { echo "[INFO] $*"; } +fi +if ! command -v msg_ok >/dev/null 2>&1; then + msg_ok() { echo "[OK] $*"; } +fi +if ! command -v msg_warn >/dev/null 2>&1; then + msg_warn() { echo "[WARN] $*"; } +fi +if ! command -v msg_error >/dev/null 2>&1; then + msg_error() { echo "[ERROR] $*" >&2; } +fi + # ------------------------------ # helpers # ------------------------------ From 0d26fb1a4cbedb650c86702fc91d033f0a0fd02e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:50:56 +0100 Subject: [PATCH 375/378] typo --- ct/unifi-os-server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/unifi-os-server.sh b/ct/unifi-os-server.sh index a6da9d366..bdd505f2f 100644 --- a/ct/unifi-os-server.sh +++ b/ct/unifi-os-server.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ui.com/ -APP="UniFi OS Server" +APP="UniFi-OS-Server" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" From 8df83e7fc60efafd7fa42184b172f7f8f447ad4e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:56:35 +0100 Subject: [PATCH 376/378] deb13 --- ct/unifi-os-server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/unifi-os-server.sh b/ct/unifi-os-server.sh index bdd505f2f..fde4eb9cf 100644 --- a/ct/unifi-os-server.sh +++ b/ct/unifi-os-server.sh @@ -11,7 +11,7 @@ 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_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" var_tun="${var_tun:-yes}" var_nesting="${var_nesting:-1}" From 37eb120cc7ffb1347af9bbaa0793185d3bc3c272 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:03:45 +0100 Subject: [PATCH 377/378] ref unifi --- ct/unifi-os-server.sh | 2 ++ install/loki-install.sh | 2 +- install/unifi-os-server-install.sh | 12 +++++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ct/unifi-os-server.sh b/ct/unifi-os-server.sh index fde4eb9cf..610d155a9 100644 --- a/ct/unifi-os-server.sh +++ b/ct/unifi-os-server.sh @@ -39,3 +39,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}https://${IP}:11443${CL}" diff --git a/install/loki-install.sh b/install/loki-install.sh index 0edc93282..e4aaaa0ea 100644 --- a/install/loki-install.sh +++ b/install/loki-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: hoholms +# Author: bysinka-95 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/grafana/loki diff --git a/install/unifi-os-server-install.sh b/install/unifi-os-server-install.sh index 58dea42cf..c094ee67c 100644 --- a/install/unifi-os-server-install.sh +++ b/install/unifi-os-server-install.sh @@ -26,7 +26,12 @@ if [[ ! -e /dev/net/tun ]]; then fi msg_info "Installing dependencies" -$STD apt-get install -y ca-certificates curl jq podman uidmap slirp4netns wget +$STD apt install -y \ + ca-certificates \ + jq \ + podman \ + uidmap \ + slirp4netns msg_ok "Installed dependencies" msg_info "Installing sysctl wrapper (ignore non-critical errors)" @@ -47,7 +52,6 @@ if ! curl -fsSL "$API_URL" -o "$TEMP_JSON"; then msg_error "Failed to fetch data from Ubiquiti API" exit 1 fi - LATEST=$(jq -r ' ._embedded.firmware | map(select(.product == "unifi-os-server")) @@ -55,11 +59,9 @@ LATEST=$(jq -r ' | sort_by(.version_major, .version_minor, .version_patch) | last ' "$TEMP_JSON") - UOS_VERSION=$(echo "$LATEST" | jq -r '.version' | sed 's/^v//') UOS_URL=$(echo "$LATEST" | jq -r '._links.data.href') rm -f "$TEMP_JSON" - if [[ -z "$UOS_URL" || -z "$UOS_VERSION" || "$UOS_URL" == "null" ]]; then msg_error "Failed to parse UniFi OS Server version or download URL" exit 1 @@ -73,7 +75,7 @@ chmod +x /usr/local/sbin/unifi-os-server.bin msg_ok "Downloaded UniFi OS Server installer" msg_info "Installing UniFi OS Server (this takes a few minutes)" -echo y | /usr/local/sbin/unifi-os-server.bin +$STD /usr/local/sbin/unifi-os-server.bin <<<"y" msg_ok "UniFi OS Server installed" motd_ssh From b511d4e30e254e70206705d28e0e69e3e9bd2927 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:08:45 +0100 Subject: [PATCH 378/378] Rename auto-update-app-headers.yml to auto-update-app-headers.yml.bak --- ...uto-update-app-headers.yml => auto-update-app-headers.yml.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{auto-update-app-headers.yml => auto-update-app-headers.yml.bak} (100%) diff --git a/.github/workflows/auto-update-app-headers.yml b/.github/workflows/auto-update-app-headers.yml.bak similarity index 100% rename from .github/workflows/auto-update-app-headers.yml rename to .github/workflows/auto-update-app-headers.yml.bak