From 6ec244c99c7555f24639c09c274e2d57a968e27d Mon Sep 17 00:00:00 2001 From: pshankinclarke Date: Mon, 1 Dec 2025 16:13:42 -0800 Subject: [PATCH 001/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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/136] 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 02a9716ba25172381fe33a7f4033da08a4fc4f56 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:09:34 +0100 Subject: [PATCH 019/136] Update redis directory permissions to 744 Changed permissions of the redis directory from 755 to 744. --- install/homarr-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/homarr-install.sh b/install/homarr-install.sh index e103494b6..990b8be94 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -47,7 +47,7 @@ msg_ok "Installed Homarr" msg_info "Copying config files" mkdir -p /appdata/redis chown -R redis:redis /appdata/redis -chmod 755 /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 From 1f82b1dfc6b44d5f9ca7cf41aee98ba42c67289f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 00:01:40 +0100 Subject: [PATCH 020/136] Heimdall test --- ct/heimdall-dashboard.sh | 80 +++++++++++++++++++++++++++ install/heimdall-dashboard-install.sh | 60 ++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 ct/heimdall-dashboard.sh create mode 100644 install/heimdall-dashboard-install.sh diff --git a/ct/heimdall-dashboard.sh b/ct/heimdall-dashboard.sh new file mode 100644 index 000000000..e27f06427 --- /dev/null +++ b/ct/heimdall-dashboard.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://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:-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/Heimdall ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL "https://api.github.com/repos/linuxserver/Heimdall/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; 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" + msg_info "Updating Heimdall Dashboard to ${RELEASE}" + curl -fsSL "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz" -o $(basename "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz") + tar xzf "${RELEASE}".tar.gz + VER=$(curl -fsSL https://api.github.com/repos/linuxserver/Heimdall/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + cp -R Heimdall-"${VER}"/* /opt/Heimdall + cd /opt/Heimdall + $STD apt-get install -y composer + export COMPOSER_ALLOW_SUPERUSER=1 + $STD composer dump-autoload + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated Heimdall Dashboard to ${RELEASE}" + 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 "Cleanup" + rm -rf {"${RELEASE}".tar.gz,Heimdall-"${VER}",public-backup,database-backup,Heimdall} + sleep 1 + msg_ok "Cleaned" + msg_info "Starting Service" + systemctl start heimdall.service + sleep 2 + 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 URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7990${CL}" diff --git a/install/heimdall-dashboard-install.sh b/install/heimdall-dashboard-install.sh new file mode 100644 index 000000000..a2e0ed1fe --- /dev/null +++ b/install/heimdall-dashboard-install.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://heimdall.site/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y apt-transport-https +$STD apt-get install -y composer +$STD apt-get install -y php8.2-{bz2,curl,sqlite3,zip,xml} +msg_ok "Installed Dependencies" + +RELEASE=$(curl -fsSL "https://api.github.com/repos/linuxserver/Heimdall/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]') +echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt +msg_info "Installing Heimdall Dashboard ${RELEASE}" +curl -fsSL "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz" -o "${RELEASE}".tar.gz +tar xzf "${RELEASE}".tar.gz +VER=$(curl -fsSL https://api.github.com/repos/linuxserver/Heimdall/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') +rm -rf "${RELEASE}".tar.gz +mv Heimdall-"${VER}" /opt/Heimdall +cd /opt/Heimdall +cp .env.example .env +$STD php artisan key:generate +msg_ok "Installed Heimdall Dashboard ${RELEASE}" + +msg_info "Creating Service" +service_path="/etc/systemd/system/heimdall.service" +echo "[Unit] +Description=Heimdall +After=network.target + +[Service] +Restart=always +RestartSec=5 +Type=simple +User=root +WorkingDirectory=/opt/Heimdall +ExecStart="/usr/bin/php" artisan serve --port 7990 --host 0.0.0.0 +TimeoutStopSec=30 + +[Install] +WantedBy=multi-user.target" >$service_path +systemctl enable -q --now heimdall +cd /opt/Heimdall +COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload &>/dev/null +systemctl restart heimdall.service +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 638982a1603405e653f4563bea166a1577034272 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 00:18:38 +0100 Subject: [PATCH 021/136] Heimdall test --- ct/heimdall-dashboard.sh | 32 +++++++++++++-------------- install/heimdall-dashboard-install.sh | 26 +++++++++------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/ct/heimdall-dashboard.sh b/ct/heimdall-dashboard.sh index e27f06427..0a01b757c 100644 --- a/ct/heimdall-dashboard.sh +++ b/ct/heimdall-dashboard.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -27,45 +27,45 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -fsSL "https://api.github.com/repos/linuxserver/Heimdall/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + + if check_for_gh_release "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" - msg_info "Updating Heimdall Dashboard to ${RELEASE}" - curl -fsSL "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz" -o $(basename "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz") - tar xzf "${RELEASE}".tar.gz - VER=$(curl -fsSL https://api.github.com/repos/linuxserver/Heimdall/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - cp -R Heimdall-"${VER}"/* /opt/Heimdall + + setup_composer + fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball" + + msg_info "Updating Heimdall-Dashboard" cd /opt/Heimdall - $STD apt-get install -y composer export COMPOSER_ALLOW_SUPERUSER=1 $STD composer dump-autoload - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated Heimdall Dashboard to ${RELEASE}" + 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 "Cleanup" - rm -rf {"${RELEASE}".tar.gz,Heimdall-"${VER}",public-backup,database-backup,Heimdall} + + msg_info "Cleaning Up" + rm -rf {public-backup,database-backup} sleep 1 - msg_ok "Cleaned" + msg_ok "Cleaned Up" + msg_info "Starting Service" systemctl start heimdall.service sleep 2 msg_ok "Started Service" msg_ok "Updated successfully!" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}." fi exit } diff --git a/install/heimdall-dashboard-install.sh b/install/heimdall-dashboard-install.sh index a2e0ed1fe..057bbe798 100644 --- a/install/heimdall-dashboard-install.sh +++ b/install/heimdall-dashboard-install.sh @@ -14,27 +14,22 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y apt-transport-https -$STD apt-get install -y composer -$STD apt-get install -y php8.2-{bz2,curl,sqlite3,zip,xml} +$STD apt install -y apt-transport-https msg_ok "Installed Dependencies" -RELEASE=$(curl -fsSL "https://api.github.com/repos/linuxserver/Heimdall/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]') -echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt -msg_info "Installing Heimdall Dashboard ${RELEASE}" -curl -fsSL "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz" -o "${RELEASE}".tar.gz -tar xzf "${RELEASE}".tar.gz -VER=$(curl -fsSL https://api.github.com/repos/linuxserver/Heimdall/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -rm -rf "${RELEASE}".tar.gz -mv Heimdall-"${VER}" /opt/Heimdall +PHP_VERSION="8.4" PHP_MODULE="bz2,sqlite3" PHP_FPM="YES" setup_php +setup_composer +fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball" + +msg_info "Setting up Heimdall-Dashboard" cd /opt/Heimdall cp .env.example .env $STD php artisan key:generate msg_ok "Installed Heimdall Dashboard ${RELEASE}" msg_info "Creating Service" -service_path="/etc/systemd/system/heimdall.service" -echo "[Unit] +cat </etc/systemd/system/heimdall.service +[Unit] Description=Heimdall After=network.target @@ -44,11 +39,12 @@ RestartSec=5 Type=simple User=root WorkingDirectory=/opt/Heimdall -ExecStart="/usr/bin/php" artisan serve --port 7990 --host 0.0.0.0 +ExecStart=/usr/bin/php artisan serve --port 7990 --host 0.0.0.0 TimeoutStopSec=30 [Install] -WantedBy=multi-user.target" >$service_path +WantedBy=multi-user.target" +EOF systemctl enable -q --now heimdall cd /opt/Heimdall COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload &>/dev/null From dab7a31c16766d534e25f83779222159c343269b Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 00:24:16 +0100 Subject: [PATCH 022/136] Heimdall test --- install/heimdall-dashboard-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/heimdall-dashboard-install.sh b/install/heimdall-dashboard-install.sh index 057bbe798..01cd5c601 100644 --- a/install/heimdall-dashboard-install.sh +++ b/install/heimdall-dashboard-install.sh @@ -25,7 +25,7 @@ msg_info "Setting up Heimdall-Dashboard" cd /opt/Heimdall cp .env.example .env $STD php artisan key:generate -msg_ok "Installed Heimdall Dashboard ${RELEASE}" +msg_ok "Setup Heimdall-Dashboard" msg_info "Creating Service" cat </etc/systemd/system/heimdall.service From 3be34c50a455781b469ebcaa1fcd0f5595ff4797 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 00:32:51 +0100 Subject: [PATCH 023/136] heimdall update fix --- ct/heimdall-dashboard.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/heimdall-dashboard.sh b/ct/heimdall-dashboard.sh index 0a01b757c..c4fc9349a 100644 --- a/ct/heimdall-dashboard.sh +++ b/ct/heimdall-dashboard.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - if check_for_gh_release "linuxserver/Heimdall"; then + if check_for_gh_release "Heimdall" "linuxserver/Heimdall"; then msg_info "Stopping Service" systemctl stop heimdall sleep 1 From 4af2c412040e19fd9113cd9b06625836367e5baf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 00:34:37 +0100 Subject: [PATCH 024/136] heimdall works now --- install/heimdall-dashboard-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/heimdall-dashboard-install.sh b/install/heimdall-dashboard-install.sh index 01cd5c601..9346ea4fd 100644 --- a/install/heimdall-dashboard-install.sh +++ b/install/heimdall-dashboard-install.sh @@ -47,7 +47,8 @@ WantedBy=multi-user.target" EOF systemctl enable -q --now heimdall cd /opt/Heimdall -COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload &>/dev/null +export COMPOSER_ALLOW_SUPERUSER=1 +$STD composer dump-autoload systemctl restart heimdall.service msg_ok "Created Service" From a3ba39b9decab84de915660a34512e33d32d560c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:26:46 +0100 Subject: [PATCH 025/136] Add Piler email archiving support and update scripts Introduces Piler email archiving with new install and container scripts, and adds metadata for frontend integration. Updates Pixelfed and Postgresus install scripts to improve PHP-FPM pool configuration and streamline build steps. --- ct/piler.sh | 88 ++++++++++ frontend/public/json/piler.json | 23 +++ install/piler-install.sh | 298 ++++++++++++++++++++++++++++++++ install/pixelfed-install.sh | 8 +- install/postgresus-install.sh | 5 +- 5 files changed, 414 insertions(+), 8 deletions(-) create mode 100644 ct/piler.sh create mode 100644 frontend/public/json/piler.json create mode 100644 install/piler-install.sh diff --git a/ct/piler.sh b/ct/piler.sh new file mode 100644 index 000000000..e592f2e3f --- /dev/null +++ b/ct/piler.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.mailpiler.org/ + +APP="Piler" +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_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /etc/piler/piler.conf ]]; then + msg_error "No ${APP} Installation Found!" + 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 + msg_info "Stopping Piler Services" + $STD systemctl stop piler + $STD systemctl stop manticore + msg_ok "Stopped Piler Services" + + msg_info "Backing up Configuration" + cp /etc/piler/piler.conf /tmp/piler.conf.bak + msg_ok "Backed up Configuration" + + msg_info "Updating to v${RELEASE_NEW}" + 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}" + + msg_info "Restoring Configuration" + cp /tmp/piler.conf.bak /etc/piler/piler.conf + rm -f /tmp/piler.conf.bak + chown piler:piler /etc/piler/piler.conf + msg_ok "Restored Configuration" + + msg_info "Starting Piler Services" + $STD systemctl start manticore + $STD systemctl start piler + msg_ok "Started Piler Services" + msg_ok "Updated Successfully to v${RELEASE_NEW}" + else + msg_ok "No update available (current: v${RELEASE_OLD})" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/piler.json b/frontend/public/json/piler.json new file mode 100644 index 000000000..1b8cb08c3 --- /dev/null +++ b/frontend/public/json/piler.json @@ -0,0 +1,23 @@ +{ + "name": "Piler", + "slug": "piler", + "categories": [ + "Email", + "Archive" + ], + "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" + } + ] +} diff --git a/install/piler-install.sh b/install/piler-install.sh new file mode 100644 index 000000000..18b9374ac --- /dev/null +++ b/install/piler-install.sh @@ -0,0 +1,298 @@ +#!/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://www.mailpiler.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 \ + nginx \ + openssl \ + libtre5 \ + catdoc \ + poppler-utils \ + unrtf \ + tnef \ + clamav \ + clamav-daemon \ + memcached \ + sysstat \ + python3 \ + python3-mysqldb +msg_ok "Installed Dependencies" + +import_local_ip +setup_mariadb +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 +$STD apt update +$STD apt install -y manticore manticore-extra +rm -f /tmp/manticore-repo.deb +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}" + +msg_info "Configuring Piler Database" +cd /usr/local/share/piler +mysql -u root "${MARIADB_DB_NAME}" /etc/piler/piler.conf +hostid=piler.${LOCAL_IP}.nip.io +update_counters_to_memcached=1 + +mysql_hostname=localhost +mysql_database=${MARIADB_DB_NAME} +mysql_username=${MARIADB_DB_USER} +mysql_password=${MARIADB_DB_PASS} +mysql_socket=/var/run/mysqld/mysqld.sock + +archive_dir=/var/piler/store +data_dir=/var/piler +tmp_dir=/var/piler/tmp + +listen_addr=0.0.0.0 +listen_port=25 + +encrypt_messages=1 +key=${PILER_KEY} +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 +EOF + +chown piler:piler /etc/piler/piler.conf +chmod 640 /etc/piler/piler.conf +chown -R piler:piler /var/piler +chmod 750 /var/piler +msg_ok "Configured Piler" + +msg_info "Configuring Manticore Search" +cat </etc/manticoresearch/manticore.conf +searchd { + listen = 9306:mysql + listen = 9312 + listen = 9308:http + log = /var/log/manticore/searchd.log + query_log = /var/log/manticore/query.log + pid_file = /var/run/manticore/searchd.pid + binlog_path = /var/lib/manticore/data +} + +source piler1 { + type = mysql + sql_host = localhost + sql_user = ${MARIADB_DB_USER} + sql_pass = ${MARIADB_DB_PASS} + sql_db = ${MARIADB_DB_NAME} + sql_port = 3306 + + sql_query = SELECT id, from_addr, to_addr, subject, body, sent FROM metadata + sql_attr_timestamp = sent +} + +index piler1 { + source = piler1 + path = /var/piler/manticore/piler1 + min_word_len = 1 + charset_table = 0..9, A..Z->a..z, a..z, U+00E1, U+00E9 +} + +index tag1 { + type = rt + path = /var/piler/manticore/tag1 + rt_field = tag + rt_attr_uint = uid +} + +index note1 { + type = rt + path = /var/piler/manticore/note1 + rt_field = note + rt_attr_uint = uid +} +EOF + +mkdir -p /var/log/manticore +chown -R manticore:manticore /var/log/manticore +chown -R piler:piler /var/piler/manticore +msg_ok "Configured Manticore Search" + +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 + +[Service] +Type=forking +User=piler +Group=piler +ExecStart=/usr/local/sbin/pilerd -c /etc/piler/piler.conf +PIDFile=/var/piler/pilerd.pid +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +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" + +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 +msg_ok "Configured PHP-FPM Pool" + +msg_info "Configuring Piler Web GUI" +cd /var/www/piler + +cat </var/www/piler/config-site.php + +EOF + +chown -R piler:piler /var/www/piler +chmod 755 /var/www/piler +msg_ok "Installed Piler Web GUI" + +msg_info "Configuring Nginx" +cat </etc/nginx/sites-available/piler +server { + listen 80; + server_name _; + root /var/www/piler; + index index.php; + + access_log /var/log/nginx/piler-access.log; + error_log /var/log/nginx/piler-error.log; + + charset utf-8; + + location / { + try_files \$uri \$uri/ /index.php?\$args; + } + + location ~ \.php$ { + fastcgi_pass unix:/run/php/php8.4-fpm-piler.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; + include fastcgi_params; + } + + location ~* \.(jpg|jpeg|gif|css|png|js|ico|html|woff|woff2)$ { + access_log off; + expires max; + } + + location ~ /\.ht { + deny all; + } +} +EOF + +ln -sf /etc/nginx/sites-available/piler /etc/nginx/sites-enabled/piler +rm -f /etc/nginx/sites-enabled/default +$STD nginx -t +$STD systemctl enable --now nginx +msg_ok "Configured Nginx" + +msg_info "Setting Up Cron Jobs" +cat </etc/cron.d/piler +30 6 * * * piler /usr/local/libexec/piler/indexer.delta.sh +30 7 * * * piler /usr/local/libexec/piler/indexer.main.sh +*/15 * * * * piler /usr/local/bin/pilerstat +30 2 * * * piler /usr/local/bin/pilerpurge +3 * * * * piler /usr/local/bin/pilerconf +EOF +msg_ok "Set Up Cron Jobs" + +motd_ssh +customize +cleanup_lxc diff --git a/install/pixelfed-install.sh b/install/pixelfed-install.sh index dcb4134da..a122fe356 100644 --- a/install/pixelfed-install.sh +++ b/install/pixelfed-install.sh @@ -43,13 +43,12 @@ systemctl restart redis-server msg_ok "Redis configured" msg_info "Configuring PHP-FPM Pool" -mkdir -p /run/php-fpm cp /etc/php/8.4/fpm/pool.d/www.conf /etc/php/8.4/fpm/pool.d/pixelfed.conf sed -i 's/\[www\]/[pixelfed]/' /etc/php/8.4/fpm/pool.d/pixelfed.conf sed -i 's/^user = www-data/user = pixelfed/' /etc/php/8.4/fpm/pool.d/pixelfed.conf sed -i 's/^group = www-data/group = pixelfed/' /etc/php/8.4/fpm/pool.d/pixelfed.conf -sed -i 's|^listen = .*|listen = /run/php-fpm/pixelfed.sock|' /etc/php/8.4/fpm/pool.d/pixelfed.conf -sed -i 's/^listen.owner = .*/listen.owner = pixelfed/' /etc/php/8.4/fpm/pool.d/pixelfed.conf +sed -i 's|^listen = .*|listen = /run/php/php8.4-fpm-pixelfed.sock|' /etc/php/8.4/fpm/pool.d/pixelfed.conf +sed -i 's/^listen.owner = .*/listen.owner = www-data/' /etc/php/8.4/fpm/pool.d/pixelfed.conf sed -i 's/^listen.group = .*/listen.group = www-data/' /etc/php/8.4/fpm/pool.d/pixelfed.conf systemctl restart php8.4-fpm msg_ok "PHP-FPM Pool configured" @@ -75,6 +74,7 @@ sed -i "s|REDIS_PORT=.*|REDIS_PORT=6379|" .env sed -i "s|ACTIVITY_PUB=.*|ACTIVITY_PUB=true|" .env sed -i "s|AP_REMOTE_FOLLOW=.*|AP_REMOTE_FOLLOW=true|" .env sed -i "s|OAUTH_ENABLED=.*|OAUTH_ENABLED=true|" .env +echo "SESSION_SECURE_COOKIE=false" >>.env chown -R pixelfed:pixelfed /opt/pixelfed chmod -R 755 /opt/pixelfed @@ -122,7 +122,7 @@ server { location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass unix:/run/php-fpm/pixelfed.sock; + fastcgi_pass unix:/run/php/php8.4-fpm-pixelfed.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; diff --git a/install/postgresus-install.sh b/install/postgresus-install.sh index 812346647..4de9b8208 100644 --- a/install/postgresus-install.sh +++ b/install/postgresus-install.sh @@ -26,16 +26,13 @@ NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "postgresus" "RostislavDugin/postgresus" "tarball" "latest" "/opt/postgresus" msg_info "Building Postgresus (Patience)" -# Build frontend cd /opt/postgresus/frontend $STD npm ci $STD npm run build - -# Build backend cd /opt/postgresus/backend $STD go mod tidy $STD go mod download -$STD CGO_ENABLED=0 go build -o /opt/postgresus/postgresus ./cmd/main.go +CGO_ENABLED=0 go build -o postgresus . mkdir -p /opt/postgresus/{data,backups,logs} cp -r /opt/postgresus/frontend/dist /opt/postgresus/ui cp -r /opt/postgresus/backend/migrations /opt/postgresus/ From a9e0a1f0a2dcb18a8739417a1908cac89a5bfe96 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:34:29 +0100 Subject: [PATCH 026/136] Update postgresus-install.sh --- install/postgresus-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/postgresus-install.sh b/install/postgresus-install.sh index 4de9b8208..401ae160a 100644 --- a/install/postgresus-install.sh +++ b/install/postgresus-install.sh @@ -32,7 +32,7 @@ $STD npm run build cd /opt/postgresus/backend $STD go mod tidy $STD go mod download -CGO_ENABLED=0 go build -o postgresus . +$STD env CGO_ENABLED=0 go build -o /opt/postgresus/postgresus ./cmd/main.go mkdir -p /opt/postgresus/{data,backups,logs} cp -r /opt/postgresus/frontend/dist /opt/postgresus/ui cp -r /opt/postgresus/backend/migrations /opt/postgresus/ From 868106ca8db6db88dc821db2e76848fa72e35e13 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:55:20 +0100 Subject: [PATCH 027/136] Update postgresus-install.sh --- install/postgresus-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/postgresus-install.sh b/install/postgresus-install.sh index 401ae160a..9948cb75f 100644 --- a/install/postgresus-install.sh +++ b/install/postgresus-install.sh @@ -32,7 +32,9 @@ $STD npm run build cd /opt/postgresus/backend $STD go mod tidy $STD go mod download -$STD env CGO_ENABLED=0 go build -o /opt/postgresus/postgresus ./cmd/main.go +cd /opt/postgresus/backend +$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,backups,logs} cp -r /opt/postgresus/frontend/dist /opt/postgresus/ui cp -r /opt/postgresus/backend/migrations /opt/postgresus/ From 9cda4592ec0482c2bb705fe807853140a1fc78c0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Dec 2025 12:10:18 +0100 Subject: [PATCH 028/136] swagger bullshit... --- install/postgresus-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/postgresus-install.sh b/install/postgresus-install.sh index 9948cb75f..c8d68524d 100644 --- a/install/postgresus-install.sh +++ b/install/postgresus-install.sh @@ -32,7 +32,8 @@ $STD npm run build cd /opt/postgresus/backend $STD go mod tidy $STD go mod download -cd /opt/postgresus/backend +$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,backups,logs} From e52c2674b6ed892c4dcc865493f18c7490114a98 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Dec 2025 12:50:28 +0100 Subject: [PATCH 029/136] Update postgresus-install.sh --- install/postgresus-install.sh | 39 ++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/install/postgresus-install.sh b/install/postgresus-install.sh index c8d68524d..7eaf731d8 100644 --- a/install/postgresus-install.sh +++ b/install/postgresus-install.sh @@ -36,15 +36,29 @@ $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,backups,logs} -cp -r /opt/postgresus/frontend/dist /opt/postgresus/ui +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" msg_info "Configuring Postgresus" ADMIN_PASS=$(openssl rand -base64 12) JWT_SECRET=$(openssl rand -hex 32) + +# Create PostgreSQL version symlinks for compatibility +for v in 12 13 14 15 16 18; do + ln -sf /usr/lib/postgresql/17 /usr/lib/postgresql/$v +done + +# Install goose for migrations +$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 # Environment ENV_MODE=production @@ -54,8 +68,14 @@ SERVER_PORT=4005 SERVER_HOST=0.0.0.0 # Database (Internal PostgreSQL for app data) +DATABASE_DSN=host=localhost user=${PG_DB_USER} password=${PG_DB_PASS} dbname=${PG_DB_NAME} port=5432 sslmode=disable DATABASE_URL=postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}?sslmode=disable +# 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 + # Security JWT_SECRET=${JWT_SECRET} ENCRYPTION_KEY=$(openssl rand -hex 32) @@ -65,15 +85,16 @@ ADMIN_EMAIL=admin@localhost ADMIN_PASSWORD=${ADMIN_PASS} # Paths -DATA_DIR=/opt/postgresus/data -BACKUP_DIR=/opt/postgresus/backups -LOG_DIR=/opt/postgresus/logs +DATA_DIR=/opt/postgresus_data/data +BACKUP_DIR=/opt/postgresus_data/backups +LOG_DIR=/opt/postgresus_data/logs # PostgreSQL Tools (for creating backups) -PG_DUMP_PATH=/usr/bin/pg_dump -PG_RESTORE_PATH=/usr/bin/pg_restore -PSQL_PATH=/usr/bin/psql +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" @@ -89,6 +110,7 @@ Type=simple User=postgres Group=postgres WorkingDirectory=/opt/postgresus +Environment="PATH=/usr/local/bin:/usr/bin:/bin" EnvironmentFile=/opt/postgresus/.env ExecStart=/opt/postgresus/postgresus Restart=always @@ -99,6 +121,7 @@ StandardError=journal [Install] WantedBy=multi-user.target EOF +$STD systemctl daemon-reload $STD systemctl enable -q --now postgresus msg_ok "Created Postgresus Service" From 4a41cfd06a1b584d1e0c41d9f013a8a5a16fbb7c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 12:03:44 +0000 Subject: [PATCH 030/136] Delete koel (ct) after migration to ProxmoxVE (#1213) Co-authored-by: github-actions[bot] --- ct/koel.sh | 82 -------------- frontend/public/json/koel.json | 48 --------- install/koel-install.sh | 189 --------------------------------- 3 files changed, 319 deletions(-) delete mode 100644 ct/koel.sh delete mode 100644 frontend/public/json/koel.json delete mode 100644 install/koel-install.sh diff --git a/ct/koel.sh b/ct/koel.sh deleted file mode 100644 index aa902d9d2..000000000 --- a/ct/koel.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://koel.dev/ - -APP="Koel" -var_tags="${var_tags:-music;streaming}" -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/koel ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "koel" "koel/koel"; then - msg_info "Stopping Services" - systemctl stop nginx php8.4-fpm - msg_ok "Stopped Services" - - msg_info "Creating Backup" - mkdir -p /tmp/koel_backup - cp /opt/koel/.env /tmp/koel_backup/ - cp -r /opt/koel/storage /tmp/koel_backup/ 2>/dev/null || true - cp -r /opt/koel/public/img /tmp/koel_backup/ 2>/dev/null || true - msg_ok "Created Backup" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "koel" "koel/koel" "prebuild" "latest" "/opt/koel" "koel-*.tar.gz" - - msg_info "Restoring Data" - cp /tmp/koel_backup/.env /opt/koel/ - cp -r /tmp/koel_backup/storage/* /opt/koel/storage/ 2>/dev/null || true - cp -r /tmp/koel_backup/img/* /opt/koel/public/img/ 2>/dev/null || true - rm -rf /tmp/koel_backup - msg_ok "Restored Data" - - msg_info "Running Migrations" - cd /opt/koel - export COMPOSER_ALLOW_SUPERUSER=1 - $STD composer install --no-interaction --no-dev --optimize-autoloader - $STD php artisan migrate --force - $STD php artisan config:clear - $STD php artisan cache:clear - $STD php artisan view:clear - $STD php artisan koel:init --no-assets --no-interaction - chown -R www-data:www-data /opt/koel - chmod -R 775 /opt/koel/storage - msg_ok "Ran Migrations" - - msg_info "Starting Services" - systemctl start php8.4-fpm nginx - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/koel.json b/frontend/public/json/koel.json deleted file mode 100644 index 62530a284..000000000 --- a/frontend/public/json/koel.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "Koel", - "slug": "koel", - "categories": [ - 13 - ], - "date_created": "2025-12-10", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://docs.koel.dev/", - "config_path": "/opt/koel/.env", - "website": "https://koel.dev/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/koel-light.webp", - "description": "Koel is a simple web-based personal audio streaming service written in Vue and Laravel. It supports multiple users, audio visualization, smart playlists, YouTube integration, and Last.fm scrobbling.", - "install_methods": [ - { - "type": "default", - "script": "ct/koel.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 8, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": "admin@koel.dev", - "password": "KoelIsCool" - }, - "notes": [ - { - "text": "Media files should be placed in /opt/koel_media", - "type": "info" - }, - { - "text": "Database credentials are stored in ~/koel.creds", - "type": "info" - }, - { - "text": "Music library is scanned hourly via cron job", - "type": "info" - } - ] -} diff --git a/install/koel-install.sh b/install/koel-install.sh deleted file mode 100644 index 487fc593f..000000000 --- a/install/koel-install.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://koel.dev/ - -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 \ - ffmpeg \ - cron \ - locales -msg_ok "Installed Dependencies" - -import_local_ip -PG_VERSION="16" setup_postgresql -PG_DB_NAME="koel" PG_DB_USER="koel" setup_postgresql_db -PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="bz2,exif,imagick,pgsql,sqlite3" setup_php -NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs -setup_composer - -fetch_and_deploy_gh_release "koel" "koel/koel" "prebuild" "latest" "/opt/koel" "koel-*.tar.gz" - -msg_info "Configuring Koel" -mkdir -p /opt/koel_media /opt/koel_sync -cd /opt/koel -cat </opt/koel/.env -APP_NAME=Koel -APP_ENV=production -APP_DEBUG=false -APP_URL=http://${LOCAL_IP} -APP_KEY= - -TRUSTED_HOSTS= - -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} - -STORAGE_DRIVER=local -MEDIA_PATH=/opt/koel_media -ARTIFACTS_PATH= - -IGNORE_DOT_FILES=true -APP_MAX_SCAN_TIME=600 -MEMORY_LIMIT= - -STREAMING_METHOD=php -SCOUT_DRIVER=tntsearch - -USE_MUSICBRAINZ=true -MUSICBRAINZ_USER_AGENT= - -LASTFM_API_KEY= -LASTFM_API_SECRET= - -SPOTIFY_CLIENT_ID= -SPOTIFY_CLIENT_SECRET= - -YOUTUBE_API_KEY= - -CDN_URL= - -TRANSCODE_FLAC=false -FFMPEG_PATH=/usr/bin/ffmpeg -TRANSCODE_BIT_RATE=128 - -ALLOW_DOWNLOAD=true -BACKUP_ON_DELETE=true - -MEDIA_BROWSER_ENABLED=false - -PROXY_AUTH_ENABLED=false - -SYNC_LOG_LEVEL=error -FORCE_HTTPS= - -MAIL_FROM_ADDRESS="noreply@localhost" -MAIL_FROM_NAME="Koel" -MAIL_MAILER=log -MAIL_HOST=null -MAIL_PORT=null -MAIL_USERNAME=null -MAIL_PASSWORD=null -MAIL_ENCRYPTION=null - -BROADCAST_CONNECTION=log -CACHE_DRIVER=file -FILESYSTEM_DISK=local -QUEUE_CONNECTION=sync -SESSION_DRIVER=file -SESSION_LIFETIME=120 -EOF - -mkdir -p /opt/koel/storage/{app/public,framework/{cache/data,sessions,views},logs} -chown -R www-data:www-data /opt/koel /opt/koel_media /opt/koel_sync -chmod -R 775 /opt/koel/storage /opt/koel/bootstrap/cache -msg_ok "Configured Koel" - -msg_info "Installing Koel (Patience)" -export COMPOSER_ALLOW_SUPERUSER=1 -cd /opt/koel -$STD composer install --no-interaction --no-dev --optimize-autoloader -$STD php artisan key:generate --force -$STD php artisan config:clear -$STD php artisan cache:clear -$STD php artisan koel:init --no-assets --no-interaction -chown -R www-data:www-data /opt/koel -msg_ok "Installed Koel" - -msg_info "Tuning PHP-FPM" -PHP_FPM_CONF="/etc/php/8.4/fpm/pool.d/www.conf" -sed -i 's/^pm.max_children = .*/pm.max_children = 15/' "$PHP_FPM_CONF" -sed -i 's/^pm.start_servers = .*/pm.start_servers = 4/' "$PHP_FPM_CONF" -sed -i 's/^pm.min_spare_servers = .*/pm.min_spare_servers = 2/' "$PHP_FPM_CONF" -sed -i 's/^pm.max_spare_servers = .*/pm.max_spare_servers = 8/' "$PHP_FPM_CONF" -$STD systemctl restart php8.4-fpm -msg_ok "Tuned PHP-FPM" - -msg_info "Configuring Nginx" -cat <<'EOF' >/etc/nginx/sites-available/koel -server { - listen 80; - server_name _; - root /opt/koel/public; - index index.php; - - client_max_body_size 50M; - charset utf-8; - - gzip on; - gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json; - gzip_comp_level 9; - - send_timeout 3600; - - location / { - try_files $uri $uri/ /index.php?$args; - } - - location /media/ { - internal; - alias $upstream_http_x_media_root; - } - - location ~ \.php$ { - try_files $uri $uri/ /index.php?$args; - fastcgi_pass unix:/run/php/php8.4-fpm.sock; - fastcgi_index index.php; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_intercept_errors on; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - include fastcgi_params; - } - - location ~ /\.(?!well-known).* { - deny all; - } -} -EOF -rm -f /etc/nginx/sites-enabled/default -ln -sf /etc/nginx/sites-available/koel /etc/nginx/sites-enabled/koel -$STD systemctl reload nginx -msg_ok "Configured Nginx" - -msg_info "Setting up Cron Job" -cat <<'EOF' >/etc/cron.d/koel -0 * * * * www-data cd /opt/koel && /usr/bin/php artisan koel:scan >/dev/null 2>&1 -EOF -chmod 644 /etc/cron.d/koel -msg_ok "Set up Cron Job" - -motd_ssh -customize -cleanup_lxc From 58f0b23ba6980fd43acdc42e2d19b09b719c2a91 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 18:50:56 +0100 Subject: [PATCH 031/136] Add Mail-Archiver script --- ct/mail-archiver.sh | 44 +++++++++++++++++++++ install/mail-archiver-install.sh | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 ct/mail-archiver.sh create mode 100644 install/mail-archiver-install.sh diff --git a/ct/mail-archiver.sh b/ct/mail-archiver.sh new file mode 100644 index 000000000..832100c43 --- /dev/null +++ b/ct/mail-archiver.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://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-build" "s1t5/mail-archiver"; then + 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}:5380${CL}" diff --git a/install/mail-archiver-install.sh b/install/mail-archiver-install.sh new file mode 100644 index 000000000..8c436ba32 --- /dev/null +++ b/install/mail-archiver-install.sh @@ -0,0 +1,68 @@ +#!/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-build" "s1t5/mail-archiver" "tarball" + +msg_info "Setting up Mail-Archiver" +mkdir -p /opt/mail-archiver +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 01df01c5e6ce8423dd3bb7b43d57380decb079b9 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 19:01:02 +0100 Subject: [PATCH 032/136] update Mail-Archiver --- ct/mail-archiver.sh | 2 +- install/mail-archiver-install.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/mail-archiver.sh b/ct/mail-archiver.sh index 832100c43..44d9f5a01 100644 --- a/ct/mail-archiver.sh +++ b/ct/mail-archiver.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - if check_for_gh_release "mail-archiver-build" "s1t5/mail-archiver"; then + if check_for_gh_release "mail-archiver" "s1t5/mail-archiver"; then msg_ok "Updated successfully!" fi exit diff --git a/install/mail-archiver-install.sh b/install/mail-archiver-install.sh index 8c436ba32..b309966fc 100644 --- a/install/mail-archiver-install.sh +++ b/install/mail-archiver-install.sh @@ -27,10 +27,10 @@ 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-build" "s1t5/mail-archiver" "tarball" +fetch_and_deploy_gh_release "mail-archiver" "s1t5/mail-archiver" "tarball" msg_info "Setting up Mail-Archiver" -mkdir -p /opt/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 From b43a29f9a2847fcc3487091be260f653c4e385b8 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 15 Dec 2025 19:33:43 +0100 Subject: [PATCH 033/136] change back to testing --- install/homarr-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 990b8be94..4231434b2 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -22,9 +22,9 @@ $STD apt install -y \ musl-dev msg_ok "Installed Dependencies" -NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') +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" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-amd64.tar.gz" +fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "latest" "/opt/homarr" "source-amd64.tar.gz" msg_info "Installing Homarr" ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 From 13065b9fb3926753b4d51b4459971ecee5d2b8d8 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 15 Dec 2025 19:34:31 +0100 Subject: [PATCH 034/136] change to testing --- ct/homarr.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/homarr.sh b/ct/homarr.sh index 5b69526a0..3045eab72 100644 --- a/ct/homarr.sh +++ b/ct/homarr.sh @@ -29,7 +29,7 @@ function update_script() { exit fi - if check_for_gh_release "homarr" "homarr-labs/homarr"; then + if check_for_gh_release "homarr" "Meierschlumpf/homarr"; then msg_info "Stopping Services (Patience)" systemctl stop homarr systemctl stop redis-server @@ -62,9 +62,9 @@ EOF $STD apt upgrade nodejs -y msg_ok "Updated Nodejs" - NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') + 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" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-amd64.tar.gz" + 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 From 5f2bd570a817bc58a1b11ac01246d1de18012096 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 15 Dec 2025 19:35:36 +0100 Subject: [PATCH 035/136] fix version --- ct/homarr.sh | 2 +- install/homarr-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/homarr.sh b/ct/homarr.sh index 3045eab72..0a09427a3 100644 --- a/ct/homarr.sh +++ b/ct/homarr.sh @@ -64,7 +64,7 @@ EOF 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" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" " v0.9.12" "/opt/homarr" "source-amd64.tar.gz" msg_info "Updating Homarr" cp /opt/homarr/redis.conf /etc/redis/redis.conf diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 4231434b2..23900a27d 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -24,7 +24,7 @@ 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-amd64.tar.gz" +fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" " v0.9.12" "/opt/homarr" "source-amd64.tar.gz" msg_info "Installing Homarr" ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 From 32d6597e7b2a860643b297364baff02c65d91ca7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 19:47:48 +0100 Subject: [PATCH 036/136] Mail-Archiver update --- ct/mail-archiver.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ct/mail-archiver.sh b/ct/mail-archiver.sh index 44d9f5a01..b56a4fcee 100644 --- a/ct/mail-archiver.sh +++ b/ct/mail-archiver.sh @@ -29,6 +29,25 @@ function update_script() { fi if check_for_gh_release "mail-archiver" "s1t5/mail-archiver"; then + 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_ok "Updated successfully!" fi exit From 4bf17abc5573c50469f6de0ab1d475e90b186c3d Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 15 Dec 2025 19:49:15 +0100 Subject: [PATCH 037/136] fix url --- ct/homarr.sh | 2 +- install/homarr-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/homarr.sh b/ct/homarr.sh index 0a09427a3..60f85f5d5 100644 --- a/ct/homarr.sh +++ b/ct/homarr.sh @@ -64,7 +64,7 @@ EOF 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" " v0.9.12" "/opt/homarr" "source-amd64.tar.gz" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "v0.9.12" "/opt/homarr" "source-amd64.tar.gz" msg_info "Updating Homarr" cp /opt/homarr/redis.conf /etc/redis/redis.conf diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 23900a27d..34b041401 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -24,7 +24,7 @@ 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" " v0.9.12" "/opt/homarr" "source-amd64.tar.gz" +fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "v0.9.12" "/opt/homarr" "source-amd64.tar.gz" msg_info "Installing Homarr" ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 From 7f68ee22f072dd796d981cdb04d15fc435f62f00 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 20:01:13 +0100 Subject: [PATCH 038/136] Update Mail-Archiver --- ct/mail-archiver.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/mail-archiver.sh b/ct/mail-archiver.sh index b56a4fcee..f016d17b7 100644 --- a/ct/mail-archiver.sh +++ b/ct/mail-archiver.sh @@ -60,4 +60,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}:5380${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" From ee2117a1b31f84e8dc935f14ccf786bf65894870 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 20:03:44 +0100 Subject: [PATCH 039/136] Mail-Archiver update fixes --- ct/mail-archiver.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ct/mail-archiver.sh b/ct/mail-archiver.sh index f016d17b7..2205f301f 100644 --- a/ct/mail-archiver.sh +++ b/ct/mail-archiver.sh @@ -29,6 +29,10 @@ function update_script() { 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 @@ -48,6 +52,10 @@ function update_script() { 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 From 7268657048688a5bd9ac815c558f1ab7f32821b7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Dec 2025 20:09:36 +0100 Subject: [PATCH 040/136] Mail-Archiver: add json --- frontend/public/json/mail-archiver.json | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/mail-archiver.json diff --git a/frontend/public/json/mail-archiver.json b/frontend/public/json/mail-archiver.json new file mode 100644 index 000000000..006cae8ca --- /dev/null +++ b/frontend/public/json/mail-archiver.json @@ -0,0 +1,35 @@ +{ + "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": [] +} From d6d000c512f3bc3e740732eeac2f02364e75017d Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 15 Dec 2025 21:45:19 +0100 Subject: [PATCH 041/136] update --- ct/homarr.sh | 7 +++++-- install/homarr-install.sh | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ct/homarr.sh b/ct/homarr.sh index 60f85f5d5..770074cfb 100644 --- a/ct/homarr.sh +++ b/ct/homarr.sh @@ -39,6 +39,10 @@ function update_script() { 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" $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 @@ -51,7 +55,6 @@ function update_script() { [Service] ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis EOF - # TODO: change in json systemctl daemon-reload rm /opt/run_homarr.sh msg_ok "Fixed old structure" @@ -64,7 +67,7 @@ EOF 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" "v0.9.12" "/opt/homarr" "source-amd64.tar.gz" + 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 diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 34b041401..4231434b2 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -24,7 +24,7 @@ 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" "v0.9.12" "/opt/homarr" "source-amd64.tar.gz" +fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "latest" "/opt/homarr" "source-amd64.tar.gz" msg_info "Installing Homarr" ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 From 0f97d192ea04a43b00e606a51c615a29b4e91e90 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 15 Dec 2025 21:57:57 +0100 Subject: [PATCH 042/136] fix nginx --- ct/homarr.sh | 1 + install/homarr-install.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/ct/homarr.sh b/ct/homarr.sh index 770074cfb..151823f0b 100644 --- a/ct/homarr.sh +++ b/ct/homarr.sh @@ -38,6 +38,7 @@ function update_script() { 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 { diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 4231434b2..f2a63f698 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -80,6 +80,7 @@ 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 From 5869e25c38df7ff66b0d27872192c9489550eab7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:14:58 +0100 Subject: [PATCH 043/136] Update fetch_and_deploy_gh_release for Debian package --- install/homarr-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/homarr-install.sh b/install/homarr-install.sh index f2a63f698..02d962800 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -24,7 +24,7 @@ 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-amd64.tar.gz" +fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "latest" "/opt/homarr" "source-debian-amd64.tar.gz" msg_info "Installing Homarr" ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 From 76f095e4eb789bbdd9b48368e0f6b4ae3db544e3 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:15:55 +0100 Subject: [PATCH 044/136] rm musl hack --- install/homarr-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 02d962800..0a5c4cc57 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -18,8 +18,7 @@ $STD apt install -y \ redis-server \ nginx \ gettext \ - openssl \ - musl-dev + 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]') @@ -27,7 +26,6 @@ setup_nodejs fetch_and_deploy_gh_release "homarr" "Meierschlumpf/homarr" "prebuild" "latest" "/opt/homarr" "source-debian-amd64.tar.gz" msg_info "Installing Homarr" -ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 mkdir -p /opt/homarr_db touch /opt/homarr_db/db.sqlite SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)" From c3330474e954d765fdcd0636afdf055d60b69366 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:50:44 +0100 Subject: [PATCH 045/136] update --- install/homarr-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 0a5c4cc57..85051c2f1 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -62,6 +62,8 @@ ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis - EOF cat </etc/systemd/system/homarr.service [Unit] +Requires=redis-server.service +After=redis-server.service Description=Homarr Service After=network.target From a1829ac9c39de335c7b42313053404fe1f2b05a2 Mon Sep 17 00:00:00 2001 From: CrazyWolf13 Date: Tue, 16 Dec 2025 14:34:38 +0100 Subject: [PATCH 046/136] add port --- tools/addon/pihole-exporter.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/addon/pihole-exporter.sh b/tools/addon/pihole-exporter.sh index e79713588..13f8c0164 100644 --- a/tools/addon/pihole-exporter.sh +++ b/tools/addon/pihole-exporter.sh @@ -186,7 +186,7 @@ UPDATEEOF echo "" msg_ok "Pihole-Exporter installed successfully" - msg_ok "Metrics: ${BL}http://${CURRENT_IP}:${DEFAULT_PORT}/metrics${CL}" + msg_ok "Metrics: ${BL}http://${CURRENT_IP}:9617/metrics${CL}" msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" } From 6d2f6183a08d23a38d552286ae943161d12c4b27 Mon Sep 17 00:00:00 2001 From: CrazyWolf13 Date: Tue, 16 Dec 2025 14:39:26 +0100 Subject: [PATCH 047/136] add hostname default --- tools/addon/pihole-exporter.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/addon/pihole-exporter.sh b/tools/addon/pihole-exporter.sh index 13f8c0164..a76d2ec00 100644 --- a/tools/addon/pihole-exporter.sh +++ b/tools/addon/pihole-exporter.sh @@ -118,7 +118,7 @@ function install() { cat <"$CONFIG_PATH" # https://github.com/eko/pihole-exporter/?tab=readme-ov-file#available-cli-options PIHOLE_PASSWORD="${pihole_PASSWORD}" -PIHOLE_HOSTNAME="${pihole_HOSTNAME}" +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}" From 7ec74ce70a1ac1d0e9190ca5ac8adee74f7bd71d Mon Sep 17 00:00:00 2001 From: CrazyWolf13 Date: Tue, 16 Dec 2025 14:47:08 +0100 Subject: [PATCH 048/136] fix std --- tools/addon/pihole-exporter.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/addon/pihole-exporter.sh b/tools/addon/pihole-exporter.sh index a76d2ec00..f9c60bf3d 100644 --- a/tools/addon/pihole-exporter.sh +++ b/tools/addon/pihole-exporter.sh @@ -83,7 +83,7 @@ function update() { msg_info "Starting service" if [[ "$OS" == "Alpine" ]]; then - rc-service pihole-exporter start + rc-service pihole-exporter start &>/dev/null else systemctl start pihole-exporter fi @@ -169,8 +169,8 @@ start_pre() { } EOF chmod +x "$SERVICE_PATH" - rc-update add pihole-exporter default - rc-service pihole-exporter start + $STD rc-update add pihole-exporter default + $STD rc-service pihole-exporter start fi msg_ok "Created and started service" From 699a3b3a8c8e1b7c90835c5ebdf65159ea7dceb7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:04:54 +0100 Subject: [PATCH 049/136] merge from VE --- misc/tools.func | 333 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 239 insertions(+), 94 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 716a74d8a..a122317f3 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -72,15 +72,19 @@ stop_all_services() { local service_patterns=("$@") for pattern in "${service_patterns[@]}"; do - # Find all matching services - systemctl list-units --type=service --all 2>/dev/null | - grep -oE "${pattern}[^ ]*\.service" | - sort -u | + # Find all matching services (grep || true to handle no matches) + local services + services=$(systemctl list-units --type=service --all 2>/dev/null | + grep -oE "${pattern}[^ ]*\.service" 2>/dev/null | sort -u) || true + + if [[ -n "$services" ]]; then while read -r service; do $STD systemctl stop "$service" 2>/dev/null || true $STD systemctl disable "$service" 2>/dev/null || true - done + done <<<"$services" + fi done + } # ------------------------------------------------------------------------------ @@ -188,6 +192,8 @@ install_packages_with_retry() { if [[ $retry -le $max_retries ]]; then msg_warn "Package installation failed, retrying ($retry/$max_retries)..." sleep 2 + # Fix any interrupted dpkg operations before retry + $STD dpkg --configure -a 2>/dev/null || true $STD apt update 2>/dev/null || true fi done @@ -213,6 +219,8 @@ upgrade_packages_with_retry() { if [[ $retry -le $max_retries ]]; then msg_warn "Package upgrade failed, retrying ($retry/$max_retries)..." sleep 2 + # Fix any interrupted dpkg operations before retry + $STD dpkg --configure -a 2>/dev/null || true $STD apt update 2>/dev/null || true fi done @@ -1178,6 +1186,12 @@ cleanup_orphaned_sources() { # This should be called at the start of any setup function # ------------------------------------------------------------------------------ ensure_apt_working() { + # Fix interrupted dpkg operations first + # This can happen if a previous installation was interrupted (e.g., by script error) + if [[ -f /var/lib/dpkg/lock-frontend ]] || dpkg --audit 2>&1 | grep -q "interrupted"; then + $STD dpkg --configure -a 2>/dev/null || true + fi + # Clean up orphaned sources first cleanup_orphaned_sources @@ -1208,6 +1222,7 @@ setup_deb822_repo() { local suite="$4" local component="${5:-main}" local architectures="${6-}" # optional + local enabled="${7-}" # optional: "true" or "false" # Validate required parameters if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then @@ -1235,9 +1250,13 @@ setup_deb822_repo() { echo "Types: deb" echo "URIs: $repo_url" echo "Suites: $suite" - echo "Components: $component" + # Flat repositories (suite="./" or absolute path) must not have Components + if [[ "$suite" != "./" && -n "$component" ]]; then + echo "Components: $component" + fi [[ -n "$architectures" ]] && echo "Architectures: $architectures" echo "Signed-By: /etc/apt/keyrings/${name}.gpg" + [[ -n "$enabled" ]] && echo "Enabled: $enabled" } >/etc/apt/sources.list.d/${name}.sources $STD apt update @@ -1439,15 +1458,32 @@ check_for_gh_release() { ensure_dependencies jq - # Fetch releases and exclude drafts/prereleases - local releases_json - releases_json=$(curl -fsSL --max-time 20 \ - -H 'Accept: application/vnd.github+json' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - "https://api.github.com/repos/${source}/releases") || { - msg_error "Unable to fetch releases for ${app}" - return 1 - } + # Try /latest endpoint for non-pinned versions (most efficient) + local releases_json="" + + if [[ -z "$pinned_version_in" ]]; then + releases_json=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/releases/latest" 2>/dev/null) + + if [[ $? -eq 0 ]] && [[ -n "$releases_json" ]]; then + # Wrap single release in array for consistent processing + releases_json="[$releases_json]" + fi + fi + + # If no releases yet (pinned version OR /latest failed), fetch up to 100 + if [[ -z "$releases_json" ]]; then + # Fetch releases and exclude drafts/prereleases + releases_json=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/releases?per_page=100") || { + msg_error "Unable to fetch releases for ${app}" + return 1 + } + fi mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") if ((${#raw_tags[@]} == 0)); then @@ -1721,12 +1757,13 @@ function fetch_and_deploy_gh_release() { ### Tarball Mode ### if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then - url=$(echo "$json" | jq -r '.tarball_url // empty') - [[ -z "$url" ]] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" + # 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" filename="${app_lc}-${version}.tar.gz" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url" || { - msg_error "Download failed: $url" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" || { + msg_error "Download failed: $direct_tarball_url" rm -rf "$tmpdir" return 1 } @@ -2548,93 +2585,200 @@ function setup_hwaccel() { fi # Detect GPU vendor (Intel, AMD, NVIDIA) - local gpu_vendor - gpu_vendor=$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1 || echo "") + 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_error "No GPU or CPU vendor detected (missing lspci/lscpu output)" - return 1 + msg_warn "No GPU or CPU vendor detected - skipping hardware acceleration setup" + msg_ok "Setup Hardware Acceleration (skipped - no GPU detected)" + return 0 fi # Detect OS with fallbacks - local os_id os_codename - os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^ID=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "debian") - os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^VERSION_CODENAME=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "unknown") + 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 "") - # Validate os_id - if [[ -z "$os_id" ]]; then - os_id="debian" - fi + [[ -z "$os_id" ]] && os_id="debian" - # Determine if we are on a VM or LXC + # Determine if we are in a privileged LXC container local in_ct="${CTTYPE:-0}" case "$gpu_vendor" in Intel) - if [[ "$os_id" == "ubuntu" ]]; then - $STD apt -y install intel-opencl-icd || { - msg_error "Failed to install intel-opencl-icd" - return 1 - } - else - # For Debian: fetch Intel GPU drivers from GitHub - fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || { - msg_warn "Failed to deploy Intel IGC core 2" - } - fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || { - msg_warn "Failed to deploy Intel IGC OpenCL 2" - } - fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || { - msg_warn "Failed to deploy Intel GDGMM12" - } - fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || { - msg_warn "Failed to deploy Intel OpenCL ICD" - } + # Detect Intel GPU generation for driver selection + # Gen 9+ (Skylake and newer) benefit from non-free drivers + local intel_gen="" + local needs_nonfree=false + + # 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+" fi - $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || { - msg_error "Failed to install Intel GPU dependencies" - return 1 - } + 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 \ + 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 + + 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 +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 \ + 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 +Types: deb +URIs: http://deb.debian.org/debian +Suites: trixie trixie-updates +Components: non-free non-free-firmware + +Types: deb +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 \ + 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 vainfo clinfo || { + $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 } - # For AMD CPUs without discrete GPU (APUs) - if [[ "$cpu_vendor" == "AuthenticAMD" && -n "$gpu_vendor" ]]; then - $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true + # 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 - skip for now - msg_info "NVIDIA GPU detected - manual driver setup required" + # 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 ;; + *) - # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) + # No discrete GPU detected - check for AMD APU if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then - $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || { - msg_error "Failed to install Mesa OpenCL stack" - return 1 - } + $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 acceleration" + msg_warn "No supported GPU vendor detected - skipping GPU driver installation" fi ;; esac - if [[ -d /dev/dri ]]; then + # Set permissions for /dev/dri (only in privileged containers and if /dev/dri exists) + 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 - $STD adduser "$(id -u -n)" render + $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 + fi fi cache_installed_version "hwaccel" "1.0" @@ -2780,12 +2924,19 @@ function setup_java() { INSTALLED_VERSION=$(dpkg -l 2>/dev/null | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+' | head -n1 || echo "") fi - # Validate INSTALLED_VERSION is not empty if matched + # Validate INSTALLED_VERSION is not empty if JDK package found local JDK_COUNT=0 JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || true) if [[ -z "$INSTALLED_VERSION" && "${JDK_COUNT:-0}" -gt 0 ]]; then - msg_warn "Found Temurin JDK but cannot determine version" - INSTALLED_VERSION="0" + msg_warn "Found Temurin JDK but cannot determine version - attempting reinstall" + # Try to get actual package name for purge + local OLD_PACKAGE + OLD_PACKAGE=$(dpkg -l 2>/dev/null | awk '/temurin-.*-jdk/{print $2}' | head -n1 || echo "") + if [[ -n "$OLD_PACKAGE" ]]; then + msg_info "Removing existing package: $OLD_PACKAGE" + $STD apt purge -y "$OLD_PACKAGE" || true + fi + INSTALLED_VERSION="" # Reset to trigger fresh install fi # Scenario 1: Already at correct version @@ -3234,7 +3385,6 @@ function setup_mongodb() { return 1 } - # Verify MongoDB was installed correctly if ! command -v mongod >/dev/null 2>&1; then msg_error "MongoDB binary not found after installation" return 1 @@ -3410,12 +3560,12 @@ EOF # - Optionally installs or updates global npm modules # # Variables: -# NODE_VERSION - Node.js version to install (default: 22) +# 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") # ------------------------------------------------------------------------------ function setup_nodejs() { - local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_VERSION="${NODE_VERSION:-24}" local NODE_MODULE="${NODE_MODULE:-}" # ALWAYS clean up legacy installations first (nvm, etc.) to prevent conflicts @@ -3477,14 +3627,11 @@ function setup_nodejs() { return 1 } - # CRITICAL: Force APT cache refresh AFTER repository setup - # This ensures NodeSource is the only nodejs source in APT cache + # Force APT cache refresh after repository setup $STD apt update - # Install dependencies (NodeSource is now the only nodejs source) ensure_dependencies curl ca-certificates gnupg - # Install Node.js from NodeSource install_packages_with_retry "nodejs" || { msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 @@ -3635,7 +3782,7 @@ function setup_php() { local CURRENT_PHP="" CURRENT_PHP=$(is_tool_installed "php" 2>/dev/null) || true - # CRITICAL: If wrong version is installed, remove it FIRST before any pinning + # Remove conflicting PHP version before pinning if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then msg_info "Removing conflicting PHP ${CURRENT_PHP} (need ${PHP_VERSION})" stop_all_services "php.*-fpm" @@ -3782,7 +3929,6 @@ EOF local INSTALLED_VERSION=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - # Critical: if major.minor doesn't match, fail and cleanup if [[ "$INSTALLED_VERSION" != "$PHP_VERSION" ]]; then msg_error "PHP version mismatch: requested ${PHP_VERSION} but got ${INSTALLED_VERSION}" msg_error "This indicates a critical package installation issue" @@ -3862,11 +4008,14 @@ function setup_postgresql() { local SUITE case "$DISTRO_CODENAME" in trixie | forky | sid) + if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then SUITE="trixie-pgdg" + else SUITE="bookworm-pgdg" fi + ;; *) SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") @@ -4387,7 +4536,7 @@ function setup_rust() { # Get currently installed version local CURRENT_VERSION="" if command -v rustc &>/dev/null; then - CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true fi # Scenario 1: Rustup not installed - fresh install @@ -4406,7 +4555,8 @@ function setup_rust() { return 1 fi - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + local RUST_VERSION + RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true if [[ -z "$RUST_VERSION" ]]; then msg_error "Failed to determine Rust version" return 1 @@ -4437,7 +4587,8 @@ function setup_rust() { # Ensure PATH is updated for current shell session export PATH="$CARGO_BIN:$PATH" - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + local RUST_VERSION + RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true if [[ -z "$RUST_VERSION" ]]; then msg_error "Failed to determine Rust version after update" return 1 @@ -4647,6 +4798,7 @@ function setup_uv() { if [[ -d /usr/share/zsh/site-functions ]]; then $STD uv generate-shell-completion zsh >/usr/share/zsh/site-functions/_uv 2>/dev/null || true fi + # Optional: Install specific Python version if requested if [[ -n "${PYTHON_VERSION:-}" ]]; then msg_info "Installing Python $PYTHON_VERSION via uv" @@ -4805,6 +4957,7 @@ function setup_docker() { # Cleanup old repository configurations if [ -f /etc/apt/sources.list.d/docker.list ]; then + msg_info "Migrating from old Docker repository format" rm -f /etc/apt/sources.list.d/docker.list rm -f /etc/apt/keyrings/docker.asc fi @@ -4818,7 +4971,6 @@ function setup_docker() { "$(get_os_info codename)" \ "stable" \ "$(dpkg --print-architecture)" - msg_ok "Set up Docker Repository" # Install or upgrade Docker if [ "$docker_installed" = true ]; then @@ -4826,8 +4978,7 @@ function setup_docker() { DOCKER_LATEST_VERSION=$(apt-cache policy docker-ce | grep Candidate | awk '{print $2}' | cut -d':' -f2 | cut -d'-' -f1) if [ "$DOCKER_CURRENT_VERSION" != "$DOCKER_LATEST_VERSION" ]; then - msg_ok "Docker update available ($DOCKER_CURRENT_VERSION → $DOCKER_LATEST_VERSION)" - msg_info "Updating Docker" + msg_info "Updating Docker $DOCKER_CURRENT_VERSION → $DOCKER_LATEST_VERSION" $STD apt install -y --only-upgrade \ docker-ce \ docker-ce-cli \ @@ -4873,8 +5024,7 @@ EOF PORTAINER_LATEST=$(curl -fsSL https://registry.hub.docker.com/v2/repositories/portainer/portainer-ce/tags?page_size=100 | grep -oP '"name":"\K[0-9]+\.[0-9]+\.[0-9]+"' | head -1 | tr -d '"') if [ "$PORTAINER_CURRENT" != "$PORTAINER_LATEST" ]; then - msg_ok "Portainer update available ($PORTAINER_CURRENT → $PORTAINER_LATEST)" - read -r -p "${TAB3}Update Portainer? " prompt + read -r -p "${TAB3}Update Portainer $PORTAINER_CURRENT → $PORTAINER_LATEST? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Updating Portainer" docker stop portainer @@ -4889,8 +5039,6 @@ EOF -v portainer_data:/data \ portainer/portainer-ce:latest msg_ok "Updated Portainer to $PORTAINER_LATEST" - else - msg_ok "Skipped Portainer update" fi else msg_ok "Portainer is up-to-date ($PORTAINER_CURRENT)" @@ -4938,7 +5086,6 @@ EOF done < <(docker ps --format '{{.Names}} {{.Image}}') if [ ${#containers_with_updates[@]} -gt 0 ]; then - msg_ok "Found ${#containers_with_updates[@]} container(s) with updates" echo "" echo "${TAB3}Container updates available:" for info in "${container_info[@]}"; do @@ -4967,8 +5114,6 @@ EOF msg_ok "Stopped and removed $container (please recreate with updated image)" fi done - else - msg_ok "Skipped container updates" fi else msg_ok "All containers are up-to-date" From c84485d5f4f221f524d2291ea7cd3c0c64150acb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:06:27 +0100 Subject: [PATCH 050/136] Update debian-install.sh --- install/debian-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/debian-install.sh b/install/debian-install.sh index aedf72fc0..99453e489 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -13,6 +13,8 @@ setting_up_container network_check update_os +setup_hwaccel + msg_info "Installing Base Dependencies" $STD apt-get install -y curl wget ca-certificates msg_ok "Installed Base Dependencies" From 601250feeedfe06a8ce828260f62ca73dd6595e1 Mon Sep 17 00:00:00 2001 From: CrazyWolf13 Date: Wed, 17 Dec 2025 20:32:44 +0100 Subject: [PATCH 051/136] 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 052/136] 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 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 053/136] 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 054/136] 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 055/136] 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 056/136] 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 057/136] 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 058/136] 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 059/136] 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 060/136] 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 061/136] 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 062/136] 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 063/136] 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 064/136] 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 065/136] 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 066/136] 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 067/136] 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 068/136] 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 069/136] 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 070/136] 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 071/136] 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 072/136] 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 073/136] 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 074/136] 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 075/136] 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 076/136] 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 077/136] 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 078/136] 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 079/136] 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 080/136] 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 081/136] 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 082/136] 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 083/136] 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 084/136] 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 085/136] 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 086/136] 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 087/136] 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 088/136] 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 089/136] 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 090/136] 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 091/136] 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 092/136] 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 093/136] 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 094/136] 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 095/136] 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 096/136] 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 097/136] 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 098/136] 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 099/136] 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 100/136] 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 101/136] 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 102/136] 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 103/136] 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 104/136] 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 105/136] 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 106/136] 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 107/136] 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 108/136] 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 109/136] 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 110/136] 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 111/136] 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 112/136] 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 113/136] 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 114/136] 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 115/136] 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 116/136] 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 117/136] 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 118/136] 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 b00eb4a8897e0194d513dae2f5883193db26fd88 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Dec 2025 09:51:49 +0100 Subject: [PATCH 119/136] 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 120/136] 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 121/136] 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 122/136] 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 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 123/136] 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 c11ddd0e2b8ffafe1a41d35a3c3acf7f5d3727a8 Mon Sep 17 00:00:00 2001 From: Casey Jones Date: Sun, 28 Dec 2025 10:48:56 -0600 Subject: [PATCH 124/136] 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 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 125/136] 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 a38a15df5b54d63dd1011d9b143bdbc7b47ec0f3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Dec 2025 10:41:34 +0100 Subject: [PATCH 126/136] 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 127/136] 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 128/136] 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 129/136] 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 862070cf47bc82d4d2c21fbeb71a329748cc9d52 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Dec 2025 14:47:36 +0100 Subject: [PATCH 130/136] 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 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 131/136] 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 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 132/136] 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 133/136] 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 134/136] 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 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 135/136] 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 136/136] 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"