diff --git a/.gitattributes b/.gitattributes index 713f73309..d2573f2f2 100644 Binary files a/.gitattributes and b/.gitattributes differ diff --git a/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md b/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md index 158a5c6ce..17a1ff4a4 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md +++ b/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md @@ -5,10 +5,10 @@ 1) [adguard.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/adguard.sh): This script collects system parameters. (Also holds the function to update the application.) 2) [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func): Adds user settings and integrates collected information. -3) [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/create_lxc.sh): Constructs the LXC container. +3) [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/create_lxc.sh): Constructs the LXC container. 4) [adguard-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/install/adguard-install.sh): Executes functions from [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), and installs the application. 5) [adguard.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/adguard.sh) (again): To display the completion message. -The installation process uses reusable scripts: [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func), [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/create_lxc.sh), and [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), which are not specific to any particular application. +The installation process uses reusable scripts: [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func), [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/create_lxc.sh), and [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), which are not specific to any particular application. To gain a better understanding, focus on reviewing [adguard-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/install/adguard-install.sh). This script contains the commands and configurations for installing and configuring AdGuard Home within the LXC container. diff --git a/CHANGELOG.md b/CHANGELOG.md index 777f6f0b5..aadf3f0dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,151 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-22 + +## 2025-06-21 + +## 2025-06-20 + +### πŸš€ Updated Scripts + + - #### 🐞 Bug Fixes + + - Immich: remove unneeded tmp_file [@MickLesk](https://github.com/MickLesk) ([#5332](https://github.com/community-scripts/ProxmoxVE/pull/5332)) + - Huntarr: Fix duplicate update status messages [@tremor021](https://github.com/tremor021) ([#5336](https://github.com/community-scripts/ProxmoxVE/pull/5336)) + - fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) + - PLANKA: Better DB password generate [@tremor021](https://github.com/tremor021) ([#5313](https://github.com/community-scripts/ProxmoxVE/pull/5313)) + - Immich: Hotfix for #5299 [@vhsdream](https://github.com/vhsdream) ([#5300](https://github.com/community-scripts/ProxmoxVE/pull/5300)) + - changedetection: add msedge as Browser dependency [@Niklas04](https://github.com/Niklas04) ([#5301](https://github.com/community-scripts/ProxmoxVE/pull/5301)) + + - #### ✨ New Features + + - (turnkey) Add OpenLDAP as a TurnKey option [@mhaligowski](https://github.com/mhaligowski) ([#5305](https://github.com/community-scripts/ProxmoxVE/pull/5305)) + + - #### πŸ”§ Refactor + + - [core]: unify misc/*.func scripts with centralized logic from core.func [@MickLesk](https://github.com/MickLesk) ([#5316](https://github.com/community-scripts/ProxmoxVE/pull/5316)) + - Refactor: migrate AdventureLog update to uv and GitHub release logic [@MickLesk](https://github.com/MickLesk) ([#5318](https://github.com/community-scripts/ProxmoxVE/pull/5318)) + - Refactor: migrate Jupyter Notebook to uv-based installation with update support [@MickLesk](https://github.com/MickLesk) ([#5320](https://github.com/community-scripts/ProxmoxVE/pull/5320)) + +### 🌐 Website + + - #### πŸ“ Script Information + + - Argus: fix wrong port on website [@MickLesk](https://github.com/MickLesk) ([#5322](https://github.com/community-scripts/ProxmoxVE/pull/5322)) + +## 2025-06-19 + +### πŸ†• New Scripts + + - Ubuntu 25.04 VM [@MickLesk](https://github.com/MickLesk) ([#5284](https://github.com/community-scripts/ProxmoxVE/pull/5284)) +- PLANKA ([#5277](https://github.com/community-scripts/ProxmoxVE/pull/5277)) +- Wizarr ([#5273](https://github.com/community-scripts/ProxmoxVE/pull/5273)) + +### πŸš€ Updated Scripts + + - #### 🐞 Bug Fixes + + - immich-install.sh: restore pgvector module install [@vhsdream](https://github.com/vhsdream) ([#5286](https://github.com/community-scripts/ProxmoxVE/pull/5286)) + - Immich: prepare for v1.135.0 [@vhsdream](https://github.com/vhsdream) ([#5025](https://github.com/community-scripts/ProxmoxVE/pull/5025)) + +### 🧰 Maintenance + + - #### ✨ New Features + + - [core]: Feature - Check Quorum Status in create_lxc to prevent issues [@MickLesk](https://github.com/MickLesk) ([#5278](https://github.com/community-scripts/ProxmoxVE/pull/5278)) + - [core]: add validation and replace recursion for invalid inputs in adv. settings [@MickLesk](https://github.com/MickLesk) ([#5291](https://github.com/community-scripts/ProxmoxVE/pull/5291)) + +### 🌐 Website + + - #### πŸ“ Script Information + + - cloudflare-ddns: fix typo in info-text [@LukaZagar](https://github.com/LukaZagar) ([#5263](https://github.com/community-scripts/ProxmoxVE/pull/5263)) + +## 2025-06-18 + +### πŸ†• New Scripts + + - FileBrowser Quantum [@MickLesk](https://github.com/MickLesk) ([#5248](https://github.com/community-scripts/ProxmoxVE/pull/5248)) +- Huntarr ([#5249](https://github.com/community-scripts/ProxmoxVE/pull/5249)) + +### πŸš€ Updated Scripts + + - tools.func: Standardized and Renamed Setup Functions [@MickLesk](https://github.com/MickLesk) ([#5241](https://github.com/community-scripts/ProxmoxVE/pull/5241)) + + - #### 🐞 Bug Fixes + + - Immich: fix prompt clobber issue [@vhsdream](https://github.com/vhsdream) ([#5231](https://github.com/community-scripts/ProxmoxVE/pull/5231)) + + - #### πŸ”§ Refactor + + - Refactor all VM's to same logic & functions [@MickLesk](https://github.com/MickLesk) ([#5254](https://github.com/community-scripts/ProxmoxVE/pull/5254)) + - upgrade old Scriptcalls to new tools.func calls [@MickLesk](https://github.com/MickLesk) ([#5242](https://github.com/community-scripts/ProxmoxVE/pull/5242)) + +## 2025-06-17 + +### πŸš€ Updated Scripts + + - #### 🐞 Bug Fixes + + - gitea-mirror: increase build ressources [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5235](https://github.com/community-scripts/ProxmoxVE/pull/5235)) + - Immich: ensure in proper working dir when updating [@vhsdream](https://github.com/vhsdream) ([#5227](https://github.com/community-scripts/ProxmoxVE/pull/5227)) + - Update IP-Tag [@DesertGamer](https://github.com/DesertGamer) ([#5226](https://github.com/community-scripts/ProxmoxVE/pull/5226)) + - trilium: fix update function after db changes folder [@tjcomserv](https://github.com/tjcomserv) ([#5207](https://github.com/community-scripts/ProxmoxVE/pull/5207)) + + - #### ✨ New Features + + - LibreTranslate: Add .env for easier configuration [@tremor021](https://github.com/tremor021) ([#5216](https://github.com/community-scripts/ProxmoxVE/pull/5216)) + +### 🌐 Website + + - #### πŸ“ Script Information + + - IPTag: Better explanation [@MickLesk](https://github.com/MickLesk) ([#5213](https://github.com/community-scripts/ProxmoxVE/pull/5213)) + +## 2025-06-16 + +### πŸ†• New Scripts + + - Intel NIC offload Fix by @rcastley [@MickLesk](https://github.com/MickLesk) ([#5155](https://github.com/community-scripts/ProxmoxVE/pull/5155)) + +### πŸš€ Updated Scripts + + - [core] Move install_php() from VED to main [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5182](https://github.com/community-scripts/ProxmoxVE/pull/5182)) +- Firefly: Add Data Importer to LXC [@tremor021](https://github.com/tremor021) ([#5159](https://github.com/community-scripts/ProxmoxVE/pull/5159)) + + - #### 🐞 Bug Fixes + + - Wastebin: Fix missing dependencies [@tremor021](https://github.com/tremor021) ([#5185](https://github.com/community-scripts/ProxmoxVE/pull/5185)) + - Kasm: Storing Creds Fix [@omiinaya](https://github.com/omiinaya) ([#5162](https://github.com/community-scripts/ProxmoxVE/pull/5162)) + + - #### ✨ New Features + + - add optional Cloud-init support to Debian VM script [@koendiender](https://github.com/koendiender) ([#5137](https://github.com/community-scripts/ProxmoxVE/pull/5137)) + + - #### πŸ”§ Refactor + + - Refactor: 2FAuth [@tremor021](https://github.com/tremor021) ([#5184](https://github.com/community-scripts/ProxmoxVE/pull/5184)) + +### 🌐 Website + + - Refactor layout and component styles for improved responsiveness [@BramSuurdje](https://github.com/BramSuurdje) ([#5195](https://github.com/community-scripts/ProxmoxVE/pull/5195)) + + - #### 🐞 Bug Fixes + + - Refactor ScriptItem and ConfigFile components to conditionally render config file location. Update ConfigFile to accept configPath prop instead of item. [@BramSuurdje](https://github.com/BramSuurdje) ([#5197](https://github.com/community-scripts/ProxmoxVE/pull/5197)) + - Update default image asset in the public directory and update api route to only search for files that end with .json [@BramSuurdje](https://github.com/BramSuurdje) ([#5179](https://github.com/community-scripts/ProxmoxVE/pull/5179)) + + - #### ✨ New Features + + - Update default image asset in the public directory [@BramSuurdje](https://github.com/BramSuurdje) ([#5189](https://github.com/community-scripts/ProxmoxVE/pull/5189)) + ## 2025-06-15 +### πŸ†• New Scripts + + - LibreTranslate ([#5154](https://github.com/community-scripts/ProxmoxVE/pull/5154)) + ## 2025-06-14 ### πŸš€ Updated Scripts diff --git a/ct/2fauth.sh b/ct/2fauth.sh index 24f8dbef9..b6ca14384 100644 --- a/ct/2fauth.sh +++ b/ct/2fauth.sh @@ -24,21 +24,16 @@ function update_script() { check_container_storage check_container_resources - # Check if installation is present | -f for file, -d for folder if [[ ! -d "/opt/2fauth" ]]; then msg_error "No ${APP} Installation Found!" exit fi - - # Crawling the new version and checking whether an update is required RELEASE=$(curl -fsSL https://api.github.com/repos/Bubka/2FAuth/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/2fauth_version.txt)" ]] || [[ ! -f /opt/2fauth_version.txt ]]; then + if [[ "${RELEASE}" != "$(cat ~/.2fauth 2>/dev/null || cat /opt/2fauth_version.txt 2>/dev/null)" ]]; then msg_info "Updating $APP to ${RELEASE}" - $STD apt-get update $STD apt-get -y upgrade - # Creating Backup msg_info "Creating Backup" mv "/opt/2fauth" "/opt/2fauth-backup" if ! dpkg -l | grep -q 'php8.3'; then @@ -46,37 +41,24 @@ function update_script() { fi msg_ok "Backup Created" - # Upgrade PHP if ! dpkg -l | grep -q 'php8.3'; then $STD apt-get install -y \ lsb-release \ - gpg - curl -fsSL https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/deb.sury.org-php.gpg - echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" >/etc/apt/sources.list.d/php.list - $STD apt-get update - $STD apt-get install -y php8.3-{bcmath,common,ctype,curl,fileinfo,fpm,gd,mbstring,mysql,xml,cli,intl} + gnupg2 + PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" setup_php sed -i 's/php8.2/php8.3/g' /etc/nginx/conf.d/2fauth.conf fi - - # Execute Update - curl -fsSL -o "${RELEASE}.zip" "https://github.com/Bubka/2FAuth/archive/refs/tags/${RELEASE}.zip" - $STD unzip "${RELEASE}.zip" - mv "2FAuth-${RELEASE//v/}/" "/opt/2fauth" + fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" mv "/opt/2fauth-backup/.env" "/opt/2fauth/.env" mv "/opt/2fauth-backup/storage" "/opt/2fauth/storage" cd "/opt/2fauth" || return - chown -R www-data: "/opt/2fauth" chmod -R 755 "/opt/2fauth" - export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev --prefer-source - php artisan 2fauth:install - $STD systemctl restart nginx - # Cleaning up msg_info "Cleaning Up" rm -rf "v${RELEASE}.zip" if dpkg -l | grep -q 'php8.2'; then @@ -86,7 +68,6 @@ function update_script() { $STD apt-get -y autoclean msg_ok "Cleanup Completed" - # Last Action echo "${RELEASE}" >/opt/2fauth_version.txt msg_ok "Updated $APP to ${RELEASE}" else diff --git a/ct/actualbudget.sh b/ct/actualbudget.sh index e00030aa3..a1734bb9f 100644 --- a/ct/actualbudget.sh +++ b/ct/actualbudget.sh @@ -29,7 +29,7 @@ function update_script() { exit fi NODE_VERSION="22" - install_node_and_modules + setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/actualbudget/actual/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [[ -f /opt/actualbudget-data/config.json ]]; then if [[ ! -f /opt/actualbudget_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/actualbudget_version.txt)" ]]; then diff --git a/ct/adventurelog.sh b/ct/adventurelog.sh index ac1ce3611..1548511e9 100644 --- a/ct/adventurelog.sh +++ b/ct/adventurelog.sh @@ -27,43 +27,51 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + RELEASE=$(curl -fsSL https://api.github.com/repos/seanmorley15/AdventureLog/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + if [[ "${RELEASE}" != "$(cat ~/.adventurelog 2>/dev/null)" ]] || [[ ! -f ~/.adventurelog ]]; then msg_info "Stopping Services" systemctl stop adventurelog-backend systemctl stop adventurelog-frontend msg_ok "Services Stopped" - msg_info "Updating ${APP} to ${RELEASE}" - mv /opt/adventurelog/ /opt/adventurelog-backup/ - curl -fsSL -o /opt/v${RELEASE}.zip "https://github.com/seanmorley15/AdventureLog/archive/refs/tags/v${RELEASE}.zip" - $STD unzip /opt/v${RELEASE}.zip -d /opt/ - mv /opt/AdventureLog-${RELEASE} /opt/adventurelog + fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog" + PYTHON_VERSION="3.12" setup_uv + + msg_info "Updating ${APP} to v${RELEASE}" + # Backend Migration + cp /opt/adventurelog-backup/backend/server/.env /opt/adventurelog/backend/server/.env + cp -r /opt/adventurelog-backup/backend/server/media /opt/adventurelog/backend/server/media - mv /opt/adventurelog-backup/backend/server/.env /opt/adventurelog/backend/server/.env - mv /opt/adventurelog-backup/backend/server/media /opt/adventurelog/backend/server/media cd /opt/adventurelog/backend/server - $STD pip install --upgrade pip - $STD pip install -r requirements.txt - $STD python3 manage.py collectstatic --noinput - $STD python3 manage.py migrate + if [[ ! -x .venv/bin/python ]]; then + $STD uv venv .venv + $STD .venv/bin/python -m ensurepip --upgrade + fi - mv /opt/adventurelog-backup/frontend/.env /opt/adventurelog/frontend/.env + $STD .venv/bin/python -m pip install --upgrade pip + $STD .venv/bin/python -m pip install -r requirements.txt + $STD .venv/bin/python -m manage collectstatic --noinput + $STD .venv/bin/python -m manage migrate + + # Frontend Migration + cp /opt/adventurelog-backup/frontend/.env /opt/adventurelog/frontend/.env cd /opt/adventurelog/frontend - $STD pnpm install - $STD pnpm run build - echo "${RELEASE}" >/opt/${APP}_version.txt + $STD pnpm i + $STD pnpm build msg_ok "Updated ${APP}" msg_info "Starting Services" + systemctl daemon-reexec systemctl start adventurelog-backend systemctl start adventurelog-frontend - msg_ok "Started Services" + msg_ok "Services Started" msg_info "Cleaning Up" rm -rf /opt/v${RELEASE}.zip rm -rf /opt/adventurelog-backup msg_ok "Cleaned" + msg_ok "Updated Successfully" else msg_ok "No update required. ${APP} is already at ${RELEASE}" diff --git a/ct/changedetection.sh b/ct/changedetection.sh index 62f05cb9e..8d6ade3d0 100644 --- a/ct/changedetection.sh +++ b/ct/changedetection.sh @@ -52,7 +52,7 @@ function update_script() { $STD /opt/browserless/node_modules/playwright-core/cli.js install --with-deps # Update Chrome separately, as it has to be done with the force option. Otherwise the installation of other browsers will not be done if Chrome is already installed. $STD /opt/browserless/node_modules/playwright-core/cli.js install --force chrome - $STD /opt/browserless/node_modules/playwright-core/cli.js install chromium firefox webkit + $STD /opt/browserless/node_modules/playwright-core/cli.js install chromium firefox webkit msedge $STD npm run build --prefix /opt/browserless $STD npm run build:function --prefix /opt/browserless $STD npm prune production --prefix /opt/browserless diff --git a/ct/configarr.sh b/ct/configarr.sh index 82628bc21..ef38aee77 100644 --- a/ct/configarr.sh +++ b/ct/configarr.sh @@ -28,7 +28,7 @@ function update_script() { exit fi RELEASE=$(curl -fsSL https://api.github.com/repos/raydak-labs/configarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "${RELEASE}" != "$(cat /opt/configarr_version.txt)" ]] || [[ ! -f /opt/configarr_version.txt ]]; then + if [[ "${RELEASE}" != "$(cat ~/.configarr 2>/dev/null || cat /opt/configarr_version.txt 2>/dev/null)" ]]; then msg_info "Stopping $APP" systemctl stop configarr-task.timer msg_ok "Stopped $APP" @@ -37,7 +37,7 @@ function update_script() { mkdir -p /opt/backup/ mv /opt/configarr/{config.yml,secrets.yml,.env} "/opt/backup/" rm -rf /opt/configarr - fetch_and_deploy_gh_release "raydak-labs/configarr" + fetch_and_deploy_gh_release "configarr" "raydak-labs/configarr" mv /opt/backup/* /opt/configarr/ cd /opt/configarr $STD pnpm install diff --git a/ct/create_lxc.sh b/ct/create_lxc.sh deleted file mode 100644 index 03979b719..000000000 --- a/ct/create_lxc.sh +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: MickLesk -# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE - -# This sets verbose mode if the global variable is set to "yes" -# if [ "$VERBOSE" == "yes" ]; then set -x; fi - -# This function sets color variables for formatting output in the terminal -# Colors -YW=$(echo "\033[33m") -YWB=$(echo "\033[93m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -GN=$(echo "\033[1;92m") - -# Formatting -CL=$(echo "\033[m") -UL=$(echo "\033[4m") -BOLD=$(echo "\033[1m") -BFR="\\r\\033[K" -HOLD=" " -TAB=" " - -# Icons -CM="${TAB}βœ”οΈ${TAB}${CL}" -CROSS="${TAB}βœ–οΈ${TAB}${CL}" -INFO="${TAB}πŸ’‘${TAB}${CL}" - -# This sets error handling options and defines the error_handler function to handle errors -set -Eeuo pipefail -trap 'error_handler $LINENO "$BASH_COMMAND"' ERR - -# This function handles errors -function error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" - exit 200 -} - -# This function displays a spinner. -function spinner() { - local frames=('β ‹' 'β ™' 'β Ή' 'β Έ' 'β Ό' 'β ΄' 'β ¦' 'β §' 'β ‡' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -function msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -function msg_warn() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${INFO}${YWB}${msg}${CL}" -} - -# This function displays a success message with a green color. -function msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -function msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - -# This checks for the presence of valid Container Storage and Template Storage locations -msg_info "Validating Storage" -VALIDCT=$(pvesm status -content rootdir | awk 'NR>1') -if [ -z "$VALIDCT" ]; then - msg_error "Unable to detect a valid Container Storage location." - exit 1 -fi -VALIDTMP=$(pvesm status -content vztmpl | awk 'NR>1') -if [ -z "$VALIDTMP" ]; then - msg_error "Unable to detect a valid Template Storage location." - exit 1 -fi - -# This function is used to select the storage class and determine the corresponding storage content type and label. -function select_storage() { - local CLASS=$1 - local CONTENT - local CONTENT_LABEL - case $CLASS in - container) - CONTENT='rootdir' - CONTENT_LABEL='Container' - ;; - template) - CONTENT='vztmpl' - CONTENT_LABEL='Container template' - ;; - *) false || { - msg_error "Invalid storage class." - exit 201 - } ;; - esac - - # This Queries all storage locations - local -a MENU - while read -r line; do - local TAG=$(echo $line | awk '{print $1}') - local TYPE=$(echo $line | awk '{printf "%-10s", $2}') - local FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - local ITEM="Type: $TYPE Free: $FREE " - local OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - local MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - MENU+=("$TAG" "$ITEM" "OFF") - done < <(pvesm status -content $CONTENT | awk 'NR>1') - - # Select storage location - if [ $((${#MENU[@]} / 3)) -eq 1 ]; then - printf ${MENU[0]} - else - local STORAGE - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for the ${CONTENT_LABEL,,}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${MENU[@]}" 3>&1 1>&2 2>&3) || { - msg_error "Menu aborted." - exit 202 - } - if [ $? -ne 0 ]; then - echo -e "${CROSS}${RD} Menu aborted by user.${CL}" - exit 0 - fi - done - printf "%s" "$STORAGE" - fi -} -# Test if required variables are set -[[ "${CTID:-}" ]] || { - msg_error "You need to set 'CTID' variable." - exit 203 -} -[[ "${PCT_OSTYPE:-}" ]] || { - msg_error "You need to set 'PCT_OSTYPE' variable." - exit 204 -} - -# Test if ID is valid -[ "$CTID" -ge "100" ] || { - msg_error "ID cannot be less than 100." - exit 205 -} - -# Check for network connectivity (IPv4 & IPv6) -#function check_network() { -# local CHECK_URLS=("8.8.8.8" "1.1.1.1" "9.9.9.9" "2606:4700:4700::1111" "2001:4860:4860::8888" "2620:fe::fe") -# -# for url in "${CHECK_URLS[@]}"; do -# if ping -c 1 -W 2 "$url" &>/dev/null; then -# return 0 # Success: At least one connection works -# fi -# done -# -# msg_error "No network connection detected. Check your internet connection." -# exit 101 -#} - -# Test if ID is in use -if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then - echo -e "ID '$CTID' is already in use." - unset CTID - msg_error "Cannot use ID that is already in use." - exit 206 -fi - -# Get template storage -TEMPLATE_STORAGE=$(select_storage template) -msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage." - -# Get container storage -CONTAINER_STORAGE=$(select_storage container) -msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage." - -# Get LXC template string -if [ $PCT_OSTYPE = debian ]; then - if [ $PCT_OSVERSION = 11 ]; then - TEMPLATE_VARIENT=bullseye - else - TEMPLATE_VARIENT=bookworm - fi -elif [ $PCT_OSTYPE = alpine ]; then - TEMPLATE_VARIENT=3.19 -else - if [ $PCT_OSVERSION = 20.04 ]; then - TEMPLATE_VARIENT=focal - elif [ $PCT_OSVERSION = 24.04 ]; then - TEMPLATE_VARIENT=noble - elif [ $PCT_OSVERSION = 24.10 ]; then - TEMPLATE_VARIENT=oracular - else - TEMPLATE_VARIENT=jammy - fi -fi - -if [ -d "/var/lib/vz/template/cache" ]; then - TEMPLATE=$PCT_OSTYPE-$TEMPLATE_VARIENT-rootfs.tar.xz - # Download template if needed - if [ ! -f "/var/lib/vz/template/cache/$TEMPLATE" ]; then - if [ $PCT_OSTYPE = debian ]; then - msg_info "Downloading LXC Template" - wget -q $(curl -s https://api.github.com/repos/asylumexp/debian-ifupdown2-lxc/releases/latest | grep download | grep debian-$TEMPLATE_VARIENT-arm64-rootfs.tar.xz | cut -d\" -f4) -O /var/lib/vz/template/cache/$TEMPLATE -q || exit "A problem occured while downloading the LXC template." - msg_ok "Downloaded LXC Template" - else - templateurl="https://jenkins.linuxcontainers.org/job/image-$PCT_OSTYPE/architecture=arm64,release=$TEMPLATE_VARIENT,variant=default/lastStableBuild/artifact/rootfs.tar.xz" - msg_info "Downloading LXC Template" - wget $templateurl -O /var/lib/vz/template/cache/$TEMPLATE -q || exit "A problem occured while downloading the LXC template." - msg_ok "Downloaded LXC Template" - fi - fi -else - # Update LXC template list - msg_info "Updating LXC Template List" - pveam update >/dev/null - msg_ok "Updated LXC Template List" - if [ $PCT_OSTYPE = debian ]; then - msg_error "Debian unsupported with this download method. Exiting." - elif [ $PCT_OSTYPE = alpine]; then - $TEMPLATE_VARIENT = 3.18 - fi - - TEMPLATE="$(pveam available | grep -E "arm64.*$PCT_OSTYPE-$TEMPLATE_VARIENT" | sed 's/arm64[[:space:]]*//')" - - # Download LXC template if needed - if ! pveam list $TEMPLATE_STORAGE | grep -F $TEMPLATE > /dev/null; then - msg_info "Downloading LXC Template" - pveam download $TEMPLATE_STORAGE $TEMPLATE >/dev/null || - exit "A problem occured while downloading the LXC template." - msg_ok "Downloaded LXC Template" - fi -fi -msg_ok "LXC Template is ready to use." - -# Check and fix subuid/subgid -grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid -grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid - -# Combine all options -PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) -[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") - -msg_info "Creating LXC Container" -if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_error "Container creation failed. Checking if template is corrupted." - - if ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then - msg_error "Template appears to be corrupted. Removing and re-downloading." - rm -f "$TEMPLATE_PATH" - - if ! timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then - msg_error "Failed to re-download template." - exit 208 - fi - - msg_ok "Re-downloaded LXC Template" - - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_error "Container creation failed after re-downloading template." - exit 200 - fi - else - msg_error "Container creation failed, but template is not corrupted." - exit 209 - fi -fi -msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." diff --git a/ct/fumadocs.sh b/ct/fumadocs.sh index 0aaaab0e4..6ea6e7862 100644 --- a/ct/fumadocs.sh +++ b/ct/fumadocs.sh @@ -34,7 +34,7 @@ function update_script() { exit 1 fi - NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs PROJECT_NAME=$(=")[1] | split(".")[0]') NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" - install_node_and_modules - + setup_nodejs + rm -rf /opt/homarr - fetch_and_deploy_gh_release "homarr-labs/homarr" + fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" msg_info "Updating and rebuilding ${APP} to v${RELEASE} (Patience)" rm /opt/run_homarr.sh diff --git a/ct/huntarr.sh b/ct/huntarr.sh new file mode 100644 index 000000000..7e290f897 --- /dev/null +++ b/ct/huntarr.sh @@ -0,0 +1,63 @@ +#!/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: BiluliB +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/plexguide/Huntarr.io + +APP="huntarr" +var_tags="${var_tags:-arr}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /opt/huntarr/main.py ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + setup_uv + RELEASE=$(curl -fsSL https://api.github.com/repos/plexguide/Huntarr.io/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') + if [[ -f ~/.huntarr && "${RELEASE}" == "$(cat ~/.huntarr)" ]]; then + msg_ok "No update required. ${APP} is already at ${RELEASE}" + exit + fi + msg_info "Stopping huntarr service" + systemctl stop huntarr + msg_ok "Stopped huntarr service" + + fetch_and_deploy_gh_release "huntarr" "plexguide/Huntarr.io" + msg_info "Configuring $APP" + cd /opt/huntarr + $STD uv pip install -r requirements.txt --python /opt/huntarr/.venv/bin/python + msg_ok "Configured $APP" + + msg_info "Starting $APP" + systemctl start huntarr + msg_ok "Started $APP" + + msg_ok "Updated $APP to v${RELEASE}" + 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}:9705${CL}" diff --git a/ct/immich.sh b/ct/immich.sh index c925c52b0..27e442aef 100644 --- a/ct/immich.sh +++ b/ct/immich.sh @@ -27,9 +27,13 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + + setup_uv + STAGING_DIR=/opt/staging BASE_DIR=${STAGING_DIR}/base-images SOURCE_DIR=${STAGING_DIR}/image-source + cd /root if [[ -f ~/.intel_version ]]; then curl -fsSLO https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/machine-learning/Dockerfile readarray -t INTEL_URLS < <(sed -n "/intel/p" ./Dockerfile | awk '{print $3}') @@ -39,7 +43,7 @@ function update_script() { for url in "${INTEL_URLS[@]}"; do curl -fsSLO "$url" done - $STD dpkg -i ./*.deb + $STD apt install -y ./*.deb rm ./*.deb msg_ok "Intel iGPU dependencies updated" fi @@ -176,54 +180,42 @@ function update_script() { msg_ok "Image-processing libraries compiled" fi fi - RELEASE=$(curl -s https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + RELEASE=$(curl -fsSL https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then msg_info "Stopping ${APP} services" systemctl stop immich-web systemctl stop immich-ml msg_ok "Stopped ${APP}" - if [[ "$(cat /opt/${APP}_version.txt)" < "1.133.0" ]]; then - msg_info "Upgrading to the VectorChord PostgreSQL extension" - NUMBER="$( - sed -n '2p' <( - sudo -u postgres psql -A -d immich < 0 - AND c.relname = 'smart_search'::text - AND f.attname = 'embedding'::text; -EOF - ) - )" - $STD sudo -u postgres psql -d immich < "0.3.0" ]]; then + $STD sudo -u postgres pg_dumpall --clean --if-exists --username=postgres | gzip >/etc/postgresql/immich-db-vchord0.3.0.sql.gz + chown postgres /etc/postgresql/immich-db-vchord0.3.0.sql.gz + $STD sudo -u postgres gunzip --stdout /etc/postgresql/immich-db-vchord0.3.0.sql.gz | + sed -e "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \ + -e "/vchordrq.prewarm_dim/d" | + sudo -u postgres psql + fi + curl -fsSL "https://github.com/tensorchord/vectorchord/releases/download/${VCHORD_RELEASE}/postgresql-16-vchord_${VCHORD_RELEASE}-1_amd64.deb" -o vchord.deb + $STD apt install -y ./vchord.deb + $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;" + systemctl restart postgresql + if [[ ! -f ~/.vchord_version ]] || [[ ! "$(cat ~/.vchord_version)" > "0.3.0" ]]; then + $STD sudo -u postgres psql -d immich -c "REINDEX DATABASE;" + fi + echo "$VCHORD_RELEASE" >~/.vchord_version + rm ./vchord.deb + msg_ok "Updated VectorChord to v${VCHORD_RELEASE}" + fi + cp "$ML_DIR"/ml_start.sh "$INSTALL_DIR" rm -rf "${APP_DIR:?}"/* rm -rf "$SRC_DIR" @@ -234,6 +226,9 @@ EOF mv "$APP-$RELEASE"/ "$SRC_DIR" mkdir -p "$ML_DIR" cd "$SRC_DIR"/server + if [[ "$RELEASE" == "1.135.1" ]]; then + rm ./src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts + fi $STD npm install -g node-gyp node-pre-gyp $STD npm ci $STD npm run build @@ -251,28 +246,21 @@ EOF msg_ok "Updated ${APP} web and microservices" cd "$SRC_DIR"/machine-learning - $STD python3 -m venv "$ML_DIR"/ml-venv + export VIRTUAL_ENV="${ML_DIR}"/ml-venv + $STD /usr/local/bin/uv venv "$VIRTUAL_ENV" if [[ -f ~/.openvino ]]; then msg_info "Updating HW-accelerated machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install -U uv - uv -q sync --extra openvino --no-cache --active - ) - patchelf --clear-execstack "$ML_DIR"/ml-venv/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so + /usr/local/bin/uv -q sync --extra openvino --no-cache --active + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so" msg_ok "Updated HW-accelerated machine-learning" else msg_info "Updating machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install -U uv - uv -q sync --extra cpu --no-cache --active - ) + /usr/local/bin/uv -q sync --extra cpu --no-cache --active msg_ok "Updated machine-learning" fi cd "$SRC_DIR" cp -a machine-learning/{ann,immich_ml} "$ML_DIR" - cp "$INSTALL_DIR"/ml_start.sh "$ML_DIR" + mv "$INSTALL_DIR"/ml_start.sh "$ML_DIR" if [[ -f ~/.openvino ]]; then sed -i "/intra_op/s/int = 0/int = os.cpu_count() or 0/" "$ML_DIR"/immich_ml/config.py fi @@ -291,8 +279,6 @@ EOF $STD npm i -g @immich/cli msg_ok "Updated Immich CLI" - sed -i "s|pgvecto.rs|vectorchord|" /opt/"${APP}"/.env - chown -R immich:immich "$INSTALL_DIR" echo "$RELEASE" >/opt/"${APP}"_version.txt msg_ok "Updated ${APP} to v${RELEASE}" diff --git a/ct/jellyseerr.sh b/ct/jellyseerr.sh index bd1b6941c..2deb53a52 100644 --- a/ct/jellyseerr.sh +++ b/ct/jellyseerr.sh @@ -53,10 +53,10 @@ function update_script() { if [ -z "$pnpm_current" ]; then msg_error "pnpm not found. Installing version $pnpm_desired..." - NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs elif ! node -e "const semver = require('semver'); process.exit(semver.satisfies('$pnpm_current', '$pnpm_desired') ? 0 : 1)"; then msg_error "Updating pnpm from version $pnpm_current to $pnpm_desired..." - NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs else msg_ok "pnpm is already installed and satisfies version $pnpm_desired." fi diff --git a/ct/jupyternotebook.sh b/ct/jupyternotebook.sh index 24b74d43c..54f151a13 100644 --- a/ct/jupyternotebook.sh +++ b/ct/jupyternotebook.sh @@ -20,16 +20,53 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - msg_info "Updating ${APP} LXC" - $STD apt-get update - $STD apt-get install -y upgrade - $STD pip3 install jupyter --upgrade - msg_ok "Updated Successfully" - exit + INSTALL_DIR="/opt/jupyter" + VENV_PYTHON="${INSTALL_DIR}/.venv/bin/python" + VENV_JUPYTER="${INSTALL_DIR}/.venv/bin/jupyter" + SERVICE_FILE="/etc/systemd/system/jupyternotebook.service" + + if [[ ! -x "$VENV_JUPYTER" ]]; then + msg_info "Migrating to uv venv" + PYTHON_VERSION="3.12" setup_uv + mkdir -p "$INSTALL_DIR" + cd "$INSTALL_DIR" + $STD uv venv .venv + $STD "$VENV_PYTHON" -m ensurepip --upgrade + $STD "$VENV_PYTHON" -m pip install --upgrade pip + $STD "$VENV_PYTHON" -m pip install jupyter + msg_ok "Migrated to uv and installed Jupyter" + else + msg_info "Updating Jupyter" + $STD "$VENV_PYTHON" -m pip install --upgrade pip + $STD "$VENV_PYTHON" -m pip install --upgrade jupyter + msg_ok "Jupyter updated" + fi + + if [[ -f "$SERVICE_FILE" && "$(grep ExecStart "$SERVICE_FILE")" != *".venv/bin/jupyter"* ]]; then + msg_info "Updating systemd service to use .venv" + cat <"$SERVICE_FILE" +[Unit] +Description=Jupyter Notebook Server +After=network.target +[Service] +Type=simple +WorkingDirectory=${INSTALL_DIR} +ExecStart=${VENV_JUPYTER} notebook --ip=0.0.0.0 --port=8888 --allow-root +Restart=always +RestartSec=10 +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reexec + systemctl restart jupyternotebook + msg_ok "Service updated and restarted" + fi + + exit } start @@ -39,4 +76,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8888${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8888${CL}" diff --git a/ct/libretranslate.sh b/ct/libretranslate.sh new file mode 100644 index 000000000..12e529af0 --- /dev/null +++ b/ct/libretranslate.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: SlaviΕ‘a AreΕΎina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/LibreTranslate/LibreTranslate + +APP="LibreTranslate" +var_tags="${var_tags:-Arr}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/libretranslate ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -s https://api.github.com/repos/LibreTranslate/LibreTranslate/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ "${RELEASE}" != "$(cat $HOME/.libretranslate)" ]] || [[ ! -f $HOME/.libretranslate ]]; then + msg_info "Stopping $APP" + systemctl stop libretranslate + msg_ok "Stopped $APP" + + msg_info "Updating $APP to ${RELEASE}" + cd /opt/libretranslate + source .venv/bin/activate + $STD pip install -U libretranslate + msg_ok "Updated $APP to ${RELEASE}" + + msg_info "Starting $APP" + systemctl start libretranslate + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" diff --git a/ct/linkwarden.sh b/ct/linkwarden.sh index 7c684e117..92c3b5bfb 100644 --- a/ct/linkwarden.sh +++ b/ct/linkwarden.sh @@ -28,17 +28,17 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/linkwarden/linkwarden/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/linkwarden_version.txt)" ]] || [[ ! -f /opt/linkwarden_version.txt ]]; then - NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Stopping ${APP}" systemctl stop linkwarden msg_ok "Stopped ${APP}" - RUST_CRATES="monolith" install_rust_and_crates + RUST_CRATES="monolith" setup_rust msg_info "Updating ${APP} to ${RELEASE}" mv /opt/linkwarden/.env /opt/.env rm -rf /opt/linkwarden - fetch_and_deploy_gh_release "linkwarden/linkwarden" + fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" cd /opt/linkwarden $STD yarn $STD npx playwright install-deps diff --git a/ct/matterbridge.sh b/ct/matterbridge.sh index f4c130c12..3b03d7383 100644 --- a/ct/matterbridge.sh +++ b/ct/matterbridge.sh @@ -20,19 +20,17 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /root/Matterbridge ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - $STD apt-get update - $STD apt-get upgrade -y - NODE_VERSION="22" - NODE_MODULE="matterbridge" - install_node_and_modules + header_info + check_container_storage + check_container_resources + if [[ ! -d /root/Matterbridge ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + $STD apt-get update + $STD apt-get upgrade -y + NODE_VERSION="22" NODE_MODULE="matterbridge" setup_nodejs + exit } start diff --git a/ct/planka.sh b/ct/planka.sh new file mode 100644 index 000000000..01f817a3b --- /dev/null +++ b/ct/planka.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/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/plankanban/planka + +APP="PLANKA" +var_tags="${var_tags:-Todo,kanban}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /etc/systemd/system/planka.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -s https://api.github.com/repos/plankanban/planka/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.planka 2>/dev/null)" ]] || [[ ! -f ~/.planka ]]; then + msg_info "Stopping $APP" + systemctl stop planka + msg_ok "Stopped $APP" + + msg_info "Updating $APP to ${RELEASE}" + mkdir -p /opt/planka-backup + mkdir -p /opt/planka-backup/favicons + mkdir -p /opt/planka-backup/user-avatars + mkdir -p /opt/planka-backup/background-images + mkdir -p /opt/planka-backup/attachments + mv /opt/planka/planka/.env /opt/planka-backup + mv /opt/planka/planka/public/favicons/* /opt/planka-backup/favicons/ + mv /opt/planka/planka/public/user-avatars/* /opt/planka-backup/user-avatars/ + mv /opt/planka/planka/public/background-images/* /opt/planka-backup/background-images/ + mv /opt/planka/planka/private/attachments/* /opt/planka-backup/attachments/ + rm -rf /opt/planka + fetch_and_deploy_gh_release "planka" "plankanban/planka" "prebuild" "latest" "/opt/planka" "planka-prebuild.zip" + cd /opt/planka/planka + $STD npm install + mv /opt/planka-backup/.env /opt/planka/planka/ + mv /opt/planka-backup/favicons/* /opt/planka/planka/public/favicons/ + mv /opt/planka-backup/user-avatars/* /opt/planka/planka/public/user-avatars/ + mv /opt/planka-backup/background-images/* /opt/planka/planka/public/background-images/ + mv /opt/planka-backup/attachments/* /opt/planka/planka/private/attachments/ + msg_ok "Updated $APP to ${RELEASE}" + + msg_info "Starting $APP" + systemctl start planka + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1337${CL}" diff --git a/ct/seelf.sh b/ct/seelf.sh index f25ecaf55..93e97d93b 100644 --- a/ct/seelf.sh +++ b/ct/seelf.sh @@ -28,7 +28,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - if fetch_and_deploy_gh_release "YuukanOO/seelf"; then + if fetch_and_deploy_gh_release "seelf" "YuukanOO/seelf"; then msg_ok "$APP already at the latest version. No update required." else msg_info "Stopping $APP" diff --git a/ct/streamlink-webui.sh b/ct/streamlink-webui.sh index c0a2b950c..38f8fde96 100644 --- a/ct/streamlink-webui.sh +++ b/ct/streamlink-webui.sh @@ -30,9 +30,9 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/CrazyWolf13/streamlink-webui/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + if [[ "${RELEASE}" != "$(cat ~/.${APP} 2>/dev/null || cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then msg_info "Starting Update" - + msg_info "Stopping $APP" systemctl stop ${APP} msg_ok "Stopped $APP" @@ -40,9 +40,9 @@ function update_script() { rm -rf /opt/${APP} NODE_VERSION="22" NODE_MODULE="npm,yarn" - install_node_and_modules + setup_nodejs setup_uv - fetch_and_deploy_gh_release "CrazyWolf13/streamlink-webui" + fetch_and_deploy_gh_release "streamlink-webui" "CrazyWolf13/streamlink-webui" msg_info "Updating $APP to v${RELEASE}" $STD uv venv /opt/"${APP}"/backend/src/.venv diff --git a/ct/suwayomiserver.sh b/ct/suwayomiserver.sh index a114177e3..0aaf8c0f6 100644 --- a/ct/suwayomiserver.sh +++ b/ct/suwayomiserver.sh @@ -31,7 +31,7 @@ function update_script() { if dpkg -l | grep -q "openjdk-17-jre"; then $STD apt-get remove -y openjdk-17-jre fi - JAVA_VERSION=21 install_java + JAVA_VERSION=21 setup_java RELEASE=$(curl -fsSL https://api.github.com/repos/Suwayomi/Suwayomi-Server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/suwayomi-server_version.txt)" ]] || [[ ! -f /opt/suwayomi-server_version.txt ]]; then msg_info "Updating $APP" diff --git a/ct/trilium.sh b/ct/trilium.sh index 7ec852184..57dccb1e7 100644 --- a/ct/trilium.sh +++ b/ct/trilium.sh @@ -29,38 +29,55 @@ function update_script() { fi if [[ ! -f /opt/${APP}_version.txt ]]; then touch /opt/${APP}_version.txt; fi RELEASE=$(curl -fsSL https://api.github.com/repos/TriliumNext/Notes/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "v${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_info "Stopping ${APP}" - systemctl stop trilium - sleep 1 - msg_ok "Stopped ${APP}" - - msg_info "Updating to ${RELEASE}" - mkdir -p /opt/trilium_backup - mv /opt/trilium/db /opt/trilium_backup/ - rm -rf /opt/trilium - cd /tmp - curl -fsSL "https://github.com/TriliumNext/Notes/releases/download/v${RELEASE}/TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" -o $(basename "https://github.com/TriliumNext/Notes/releases/download/v${RELEASE}/TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz") - tar -xf TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz - mv TriliumNextNotes-Server-$RELEASE-linux-x64 /opt/trilium - cp -r /opt/trilium_backup/db /opt/trilium/ - echo "v${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated to ${RELEASE}" - - msg_info "Cleaning up" - rm -rf /tmp/TriliumNextNotes-Server-${RELEASE}-linux-x64.tar.xz - rm -rf /opt/trilium_backup - msg_ok "Cleaned" - - msg_info "Starting ${APP}" - systemctl start trilium - sleep 1 - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" + if [[ "v${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + + if [[ -d /opt/trilium/db ]]; then + DB_PATH="/opt/trilium/db" + DB_RESTORE_PATH="/opt/trilium/db" + elif [[ -d /opt/trilium/assets/db ]]; then + DB_PATH="/opt/trilium/assets/db" + DB_RESTORE_PATH="/opt/trilium/assets/db" else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_error "Database not found in either /opt/trilium/db or /opt/trilium/assets/db" + exit 1 fi - exit + + msg_info "Stopping ${APP}" + systemctl stop trilium + sleep 1 + msg_ok "Stopped ${APP}" + + msg_info "Updating to ${RELEASE}" + mkdir -p /opt/trilium_backup + cp -r "${DB_PATH}" /opt/trilium_backup/ + rm -rf /opt/trilium + cd /tmp + curl -fsSL "https://github.com/TriliumNext/Notes/releases/download/v${RELEASE}/TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" -o "TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" + tar -xf "TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" + mv "TriliumNextNotes-Server-${RELEASE}-linux-x64" /opt/trilium + + # Restore database + mkdir -p "$(dirname "${DB_RESTORE_PATH}")" + cp -r /opt/trilium_backup/$(basename "${DB_PATH}") "${DB_RESTORE_PATH}" + + echo "v${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated to ${RELEASE}" + + msg_info "Cleaning up" + rm -rf "/tmp/TriliumNextNotes-Server-${RELEASE}-linux-x64.tar.xz" + rm -rf /opt/trilium_backup + msg_ok "Cleaned" + + msg_info "Starting ${APP}" + systemctl start trilium + sleep 1 + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" +else + msg_ok "No update required. ${APP} is already at ${RELEASE}" +fi + +exit } start diff --git a/ct/wizarr.sh b/ct/wizarr.sh new file mode 100644 index 000000000..26c8b66e0 --- /dev/null +++ b/ct/wizarr.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/wizarrrr/wizarr + +APP="Wizarr" +var_tags="${var_tags:-media;arr}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/wizarr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/wizarrrr/wizarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.wizarr 2>/dev/null)" ]] || [[ ! -f ~/.wizarr ]]; then + msg_info "Stopping $APP" + systemctl stop wizarr + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + BACKUP_FILE="/opt/wizarr_backup_$(date +%F).tar.gz" + $STD tar -czf "$BACKUP_FILE" /opt/wizarr/{.env,start.sh} /opt/wizarr/database/ &>/dev/null + msg_ok "Backup Created" + + setup_uv + fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr" + + msg_info "Updating $APP to v${RELEASE}" + cd /opt/wizarr + uv -q sync --locked + $STD uv -q run pybabel compile -d app/translations + $STD npm --prefix app/static install + $STD npm --prefix app/static run build:css + mkdir -p ./.cache + $STD tar -xf "$BACKUP_FILE" --directory=/ + $STD uv -q run flask db upgrade + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start wizarr + msg_ok "Started $APP" + + msg_info "Cleaning Up" + rm -rf "$BACKUP_FILE" + rm /tmp/"$RELEASE".zip + msg_ok "Cleanup Completed" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5690${CL}" diff --git a/frontend/public/defaultimg.png b/frontend/public/defaultimg.png index 4aac77d5c..14d13fdba 100644 Binary files a/frontend/public/defaultimg.png and b/frontend/public/defaultimg.png differ diff --git a/frontend/public/json/add-lxc-iptag.json b/frontend/public/json/add-iptag.json similarity index 59% rename from frontend/public/json/add-lxc-iptag.json rename to frontend/public/json/add-iptag.json index 4f3b148b7..7fd9fd35a 100644 --- a/frontend/public/json/add-lxc-iptag.json +++ b/frontend/public/json/add-iptag.json @@ -1,23 +1,23 @@ { - "name": "Proxmox VE LXC IP-Tag", - "slug": "add-lxc-iptag", + "name": "Proxmox VE LXC Tag", + "slug": "add-iptag", "categories": [ 1 ], - "date_created": "2024-12-16", + "date_created": "2025-06-16", "type": "pve", "updateable": false, "privileged": false, "interface_port": null, "documentation": null, "website": null, - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", - "config_path": "/opt/lxc-iptag/iptag.conf", - "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/proxmox.svg", + "config_path": "/opt/iptag/iptag.conf", + "description": "This script automatically adds IP address as tags to LXC containers or VM's using a systemd service. The service also updates the tags if a LXC/VM IP address is changed.", "install_methods": [ { "type": "default", - "script": "tools/pve/add-lxc-iptag.sh", + "script": "tools/pve/add-iptag.sh", "resources": { "cpu": null, "ram": null, @@ -36,6 +36,10 @@ "text": "Execute within the Proxmox shell", "type": "info" }, + { + "text": "Configuration: `nano /opt/iptag/iptag.conf`. iptag.service must be restarted after change.", + "type": "info" + }, { "text": "The Proxmox Node must contain ipcalc and net-tools. `apt-get install -y ipcalc net-tools`", "type": "warning" diff --git a/frontend/public/json/argus.json b/frontend/public/json/argus.json index 424522c5f..7a0e8cf48 100644 --- a/frontend/public/json/argus.json +++ b/frontend/public/json/argus.json @@ -8,7 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 3000, + "interface_port": 8080, "documentation": "https://release-argus.io/docs/overview/", "website": "https://release-argus.io/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/argus.webp", diff --git a/frontend/public/json/cloudflare-ddns.json b/frontend/public/json/cloudflare-ddns.json index d867a9dbb..54fb39952 100644 --- a/frontend/public/json/cloudflare-ddns.json +++ b/frontend/public/json/cloudflare-ddns.json @@ -37,7 +37,7 @@ "type": "warning" }, { - "text": "To update the configuration edit `/etc/systemd/system/cloudflare-ddns.service`. After edit please restard with `systemctl restart cloudflare-ddns`", + "text": "To update the configuration edit `/etc/systemd/system/cloudflare-ddns.service`. After edit please restart with `systemctl restart cloudflare-ddns`", "type": "info" } ] diff --git a/frontend/public/json/filebrowser-quantum.json b/frontend/public/json/filebrowser-quantum.json new file mode 100644 index 000000000..dae307edb --- /dev/null +++ b/frontend/public/json/filebrowser-quantum.json @@ -0,0 +1,47 @@ +{ + "name": "FileBrowser Quantum", + "slug": "filebrowser-quantum", + "categories": [ + 1, + 11 + ], + "date_created": "2025-06-18", + "type": "addon", + "updateable": false, + "privileged": false, + "interface_port": 8080, + "documentation": "https://github.com/gtsteffaniak/filebrowser/wiki/Getting-Started", + "website": "https://github.com/gtsteffaniak/filebrowser", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/filebrowser-quantum.webp", + "config_path": "/usr/local/community-scripts/fq-config.yaml", + "description": "FileBrowser Quantum provides an easy way to access and manage your files from the web. It has has a web page interface that allows you to create secure shared links, users with their own specific permissions and settings, and offers a great viewing experience for many file types. This version is called Quantum because it packs tons of advanced features into a tiny easy to run file. Unlike the majority of alternative options, FileBrowser Quantum is simple to install and easy to configure.", + "install_methods": [ + { + "type": "default", + "script": "tools/addon/filebrowser-quantum.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + }, + { + "type": "alpine", + "script": "tools/addon/filebrowser-quantum.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": "admin", + "password": "helper-scripts.com" + }, + "notes": [] +} diff --git a/frontend/public/json/firefly.json b/frontend/public/json/firefly.json index 4c8d232f8..cff7e2b9f 100644 --- a/frontend/public/json/firefly.json +++ b/frontend/public/json/firefly.json @@ -32,6 +32,10 @@ "password": null }, "notes": [ + { + "text": "Data Importer is at `http:///dataimporter/`", + "type": "info" + }, { "text": "Database credentials: `cat ~/firefly.creds`", "type": "info" diff --git a/frontend/public/json/gitea-mirror.json b/frontend/public/json/gitea-mirror.json index 387c611b9..5be5cc905 100644 --- a/frontend/public/json/gitea-mirror.json +++ b/frontend/public/json/gitea-mirror.json @@ -19,9 +19,9 @@ "type": "default", "script": "ct/gitea-mirror.sh", "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 5, + "cpu": 2, + "ram": 2048, + "hdd": 6, "os": "Debian", "version": "12" } diff --git a/frontend/public/json/huntarr.json b/frontend/public/json/huntarr.json new file mode 100644 index 000000000..ea450a815 --- /dev/null +++ b/frontend/public/json/huntarr.json @@ -0,0 +1,35 @@ +{ + "name": "Huntarr", + "slug": "huntarr", + "categories": [ + 14 + ], + "date_created": "2025-06-18", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9705, + "documentation": "https://github.com/plexguide/Huntarr.io/wiki", + "config_path": "/opt/huntarr", + "website": "https://github.com/plexguide/Huntarr.io", + "logo": "https://raw.githubusercontent.com/plexguide/Huntarr.io/refs/heads/main/frontend/static/logo/Huntarr.svg", + "description": "Huntarr is a tool that automates the search for missing or low-quality media content in your collection. It works seamlessly with applications like Sonarr, Radarr, Lidarr, Readarr, and Whisparr, enhancing their functionality with continuous background scans to identify and update missed or outdated content. Through a user-friendly web interface accessible on port 9705, Huntarr provides real-time statistics, log views, and extensive configuration options. The software is especially useful for users who want to keep their media library up to date by automatically searching for missing episodes or higher-quality versions. Huntarr is well-suited for self-hosted environments and can easily run in LXC containers or Docker setups.", + "install_methods": [ + { + "type": "default", + "script": "ct/huntarr.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/frontend/public/json/immich.json b/frontend/public/json/immich.json index b29bb2c35..337511778 100644 --- a/frontend/public/json/immich.json +++ b/frontend/public/json/immich.json @@ -43,6 +43,10 @@ { "text": "To change upload location, edit 'IMMICH_MEDIA_LOCATION' in `/opt/immich/.env`, and create the symlink 'upload' in /opt/immich/app & /opt/immich/app/machine-learning to your new upload location", "type": "info" + }, + { + "text": "Logs: `/var/log/immich`", + "type": "info" } ] } diff --git a/frontend/public/json/libretranslate.json b/frontend/public/json/libretranslate.json new file mode 100644 index 000000000..094916f6e --- /dev/null +++ b/frontend/public/json/libretranslate.json @@ -0,0 +1,44 @@ +{ + "name": "LibreTranslate", + "slug": "libretranslate", + "categories": [ + 0 + ], + "date_created": "2025-06-15", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5000, + "documentation": "https://github.com/LibreTranslate/LibreTranslate?tab=readme-ov-file#settings--flags", + "website": "https://libretranslate.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/libretranslate.webp", + "config_path": "/opt/libretranslate/.env", + "description": "Free and Open Source Machine Translation API, entirely self-hosted. Unlike other APIs, it doesn't rely on proprietary providers such as Google or Azure to perform translations. Instead, its translation engine is powered by the open source Argos Translate library.", + "install_methods": [ + { + "type": "default", + "script": "ct/libretranslate.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 20, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "During the installation, application will download language models used for translation. Depending on how fast your internet/host is, this can take 5-10 minutes.", + "type": "info" + }, + { + "text": "At every boot of LXC, application will look for updates for language models installed. This can prolong the startup of the LXC.", + "type": "info" + } + ] +} diff --git a/frontend/public/json/nic-offloading-fix.json b/frontend/public/json/nic-offloading-fix.json new file mode 100644 index 000000000..7d976e29e --- /dev/null +++ b/frontend/public/json/nic-offloading-fix.json @@ -0,0 +1,40 @@ +{ + "name": "NIC Offloading Fix", + "slug": "nic-offloading-fix", + "categories": [ + 1 + ], + "date_created": "2025-05-25", + "type": "pve", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "config_path": "", + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", + "description": "This script automates the process of disabling network interface card (NIC) offloading features specifically for Intel e1000e network interfaces on Linux systems.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/nic-offloading-fix.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell", + "type": "info" + } + ] +} diff --git a/frontend/public/json/planka.json b/frontend/public/json/planka.json new file mode 100644 index 000000000..b8d7a32af --- /dev/null +++ b/frontend/public/json/planka.json @@ -0,0 +1,40 @@ +{ + "name": "PLANKA", + "slug": "planka", + "categories": [ + 12 + ], + "date_created": "2025-06-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 1337, + "documentation": "https://docs.planka.cloud/", + "website": "https://planka.app/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/planka.webp", + "config_path": "/opt/planka/planka/.env", + "description": "Planka is a powerful, project management platform that transforms how teams collaborate. Create projects with multiple boards, organize tasks with intuitive drag-and-drop cards, attach files, write rich markdown descriptions, set due dates, assign team members, and keep conversations flowing with comments and labelsβ€”all with seamless real-time updates and smart notifications.", + "install_methods": [ + { + "type": "default", + "script": "ct/planka.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Type `cat ~/planka.creds` inside LXC to see admin user and database credentials.", + "type": "info" + } + ] +} diff --git a/frontend/public/json/ubuntu2504-vm.json b/frontend/public/json/ubuntu2504-vm.json new file mode 100644 index 000000000..8b6c6838f --- /dev/null +++ b/frontend/public/json/ubuntu2504-vm.json @@ -0,0 +1,40 @@ +{ + "name": "Ubuntu 25.04", + "slug": "ubuntu2504-vm", + "categories": [ + 2 + ], + "date_created": "2025-06-19", + "type": "vm", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": "https://ubuntu.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ubuntu.webp", + "config_path": "", + "description": "Ubuntu is a distribution based on Debian, designed to have regular releases and a consistent user experience.", + "install_methods": [ + { + "type": "default", + "script": "vm/ubuntu2504-vm.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "after installation, checkout: Β΄https://github.com/community-scripts/ProxmoxVE/discussions/272Β΄", + "type": "info" + } + ] +} diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 32d7e8ce4..4a721f2bd 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,8 +1,93 @@ [ { - "name": "advplyr/audiobookshelf", - "version": "v2.25.1", - "date": "2025-06-14T23:32:15Z" + "name": "plexguide/Huntarr.io", + "version": "8.1.7", + "date": "2025-06-21T23:25:45Z" + }, + { + "name": "HabitRPG/habitica", + "version": "v5.37.0", + "date": "2025-06-21T14:05:12Z" + }, + { + "name": "fallenbagel/jellyseerr", + "version": "preview-fix-proxy-auth", + "date": "2025-06-21T13:43:58Z" + }, + { + "name": "rcourtman/Pulse", + "version": "v3.30.0", + "date": "2025-06-17T16:00:01Z" + }, + { + "name": "rogerfar/rdt-client", + "version": "v2.0.114", + "date": "2025-06-21T11:20:21Z" + }, + { + "name": "Luligu/matterbridge", + "version": "3.0.7", + "date": "2025-06-21T09:24:21Z" + }, + { + "name": "theonedev/onedev", + "version": "v11.11.1", + "date": "2025-06-21T09:23:39Z" + }, + { + "name": "pocketbase/pocketbase", + "version": "v0.28.4", + "date": "2025-06-21T08:29:04Z" + }, + { + "name": "dgtlmoon/changedetection.io", + "version": "0.50.4", + "date": "2025-06-21T07:47:02Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2035", + "date": "2025-06-21T05:52:31Z" + }, + { + "name": "coder/code-server", + "version": "v4.101.1", + "date": "2025-06-21T02:47:08Z" + }, + { + "name": "go-gitea/gitea", + "version": "v1.24.2", + "date": "2025-06-20T20:37:55Z" + }, + { + "name": "immich-app/immich", + "version": "v1.135.3", + "date": "2025-06-20T20:19:20Z" + }, + { + "name": "apache/tika", + "version": "3.2.1-rc1", + "date": "2025-06-20T19:41:10Z" + }, + { + "name": "VictoriaMetrics/VictoriaMetrics", + "version": "v1.24.0-victorialogs", + "date": "2025-06-20T19:31:31Z" + }, + { + "name": "homarr-labs/homarr", + "version": "v1.25.0", + "date": "2025-06-20T19:15:43Z" + }, + { + "name": "msgbyte/tianji", + "version": "v1.22.1", + "date": "2025-06-20T18:12:20Z" + }, + { + "name": "mongodb/mongo", + "version": "r8.1.2-rc0", + "date": "2025-06-20T17:35:38Z" }, { "name": "nzbgetcom/nzbget", @@ -10,79 +95,354 @@ "date": "2025-05-12T09:12:04Z" }, { - "name": "hyperion-project/hyperion.ng", - "version": "2.1.1", - "date": "2025-06-14T17:45:06Z" - }, - { - "name": "bunkerity/bunkerweb", - "version": "v1.6.2-rc5", - "date": "2025-06-14T16:44:14Z" - }, - { - "name": "ollama/ollama", - "version": "v0.9.1-rc1", - "date": "2025-06-12T21:18:54Z" - }, - { - "name": "theonedev/onedev", - "version": "v11.11.0", - "date": "2025-06-14T13:23:36Z" - }, - { - "name": "semaphoreui/semaphore", - "version": "v2.15.0", - "date": "2025-06-14T10:48:57Z" - }, - { - "name": "firefly-iii/firefly-iii", - "version": "v6.2.17", - "date": "2025-06-11T12:07:38Z" - }, - { - "name": "Jackett/Jackett", - "version": "v0.22.2016", - "date": "2025-06-14T05:59:28Z" - }, - { - "name": "FlareSolverr/FlareSolverr", - "version": "v3.3.25", - "date": "2025-06-14T02:52:44Z" + "name": "Sonarr/Sonarr", + "version": "v4.0.15.2941", + "date": "2025-06-20T17:20:54Z" }, { "name": "keycloak/keycloak", "version": "26.2.5", "date": "2025-05-28T06:49:43Z" }, + { + "name": "bunkerity/bunkerweb", + "version": "testing", + "date": "2025-06-16T18:10:42Z" + }, + { + "name": "docker/compose", + "version": "v2.37.2", + "date": "2025-06-20T13:25:03Z" + }, + { + "name": "zabbix/zabbix", + "version": "7.2.9", + "date": "2025-06-20T10:58:45Z" + }, + { + "name": "syncthing/syncthing", + "version": "2.0.0-rc.19", + "date": "2025-06-02T17:56:25Z" + }, + { + "name": "benzino77/tasmocompiler", + "version": "v12.7.0", + "date": "2025-06-20T08:31:16Z" + }, + { + "name": "arunavo4/gitea-mirror", + "version": "v2.16.3", + "date": "2025-06-20T05:49:06Z" + }, + { + "name": "firefly-iii/firefly-iii", + "version": "v6.2.18", + "date": "2025-06-20T04:45:37Z" + }, + { + "name": "paperless-ngx/paperless-ngx", + "version": "v2.17.1", + "date": "2025-06-19T19:35:01Z" + }, + { + "name": "pocket-id/pocket-id", + "version": "v1.4.0", + "date": "2025-06-19T18:30:11Z" + }, + { + "name": "rclone/rclone", + "version": "v1.70.1", + "date": "2025-06-19T13:19:02Z" + }, + { + "name": "icereed/paperless-gpt", + "version": "v0.21.0", + "date": "2025-06-19T11:54:59Z" + }, + { + "name": "neo4j/neo4j", + "version": "2025.05.1", + "date": "2025-06-19T11:28:36Z" + }, + { + "name": "mattermost/mattermost", + "version": "v9.11.17", + "date": "2025-06-18T08:12:05Z" + }, + { + "name": "n8n-io/n8n", + "version": "n8n@1.98.2", + "date": "2025-06-18T18:20:16Z" + }, + { + "name": "redis/redis", + "version": "8.2-m01-int2", + "date": "2025-06-12T08:52:10Z" + }, + { + "name": "prometheus-pve/prometheus-pve-exporter", + "version": "v3.5.5", + "date": "2025-06-19T05:43:47Z" + }, + { + "name": "docmost/docmost", + "version": "v0.21.0", + "date": "2025-06-18T21:43:27Z" + }, + { + "name": "ipfs/kubo", + "version": "v0.35.0", + "date": "2025-05-21T18:00:32Z" + }, + { + "name": "pterodactyl/panel", + "version": "v1.11.11", + "date": "2025-06-18T18:04:50Z" + }, + { + "name": "ollama/ollama", + "version": "v0.9.2", + "date": "2025-06-18T14:29:39Z" + }, + { + "name": "NodeBB/NodeBB", + "version": "v3.12.7", + "date": "2025-06-18T14:22:53Z" + }, + { + "name": "clusterzx/paperless-ai", + "version": "v3.0.6", + "date": "2025-06-18T14:18:13Z" + }, + { + "name": "openhab/openhab-core", + "version": "5.0.0.M3", + "date": "2025-06-18T14:18:12Z" + }, + { + "name": "Bubka/2FAuth", + "version": "v5.6.0", + "date": "2025-06-18T12:19:54Z" + }, + { + "name": "zwave-js/zwave-js-ui", + "version": "v10.7.0", + "date": "2025-06-18T11:57:05Z" + }, + { + "name": "esphome/esphome", + "version": "2025.6.0", + "date": "2025-06-18T09:41:11Z" + }, + { + "name": "forgejo/forgejo", + "version": "v11.0.2", + "date": "2025-06-18T09:38:19Z" + }, + { + "name": "evcc-io/evcc", + "version": "0.204.3", + "date": "2025-06-18T08:31:58Z" + }, + { + "name": "silverbulletmd/silverbullet", + "version": "2.0.0-pre3", + "date": "2025-06-18T08:01:24Z" + }, + { + "name": "cross-seed/cross-seed", + "version": "v6.12.7", + "date": "2025-06-18T03:44:24Z" + }, + { + "name": "grafana/grafana", + "version": "v11.5.6", + "date": "2025-06-17T22:00:40Z" + }, + { + "name": "jenkinsci/jenkins", + "version": "jenkins-2.515", + "date": "2025-06-17T19:17:56Z" + }, + { + "name": "project-zot/zot", + "version": "v2.1.5", + "date": "2025-06-17T18:04:11Z" + }, + { + "name": "BookStackApp/BookStack", + "version": "v25.05.1", + "date": "2025-06-17T14:38:04Z" + }, + { + "name": "element-hq/synapse", + "version": "v1.132.0", + "date": "2025-06-17T13:49:30Z" + }, + { + "name": "cloudflare/cloudflared", + "version": "2025.6.1", + "date": "2025-06-17T12:45:39Z" + }, + { + "name": "sabnzbd/sabnzbd", + "version": "4.5.1", + "date": "2025-04-11T09:57:47Z" + }, + { + "name": "crowdsecurity/crowdsec", + "version": "v1.6.9", + "date": "2025-06-17T11:54:50Z" + }, + { + "name": "glpi-project/glpi", + "version": "10.0.18", + "date": "2025-02-12T11:07:02Z" + }, + { + "name": "morpheus65535/bazarr", + "version": "v1.5.2", + "date": "2025-05-11T16:40:55Z" + }, + { + "name": "donaldzou/WGDashboard", + "version": "v4.2.4", + "date": "2025-06-17T05:37:06Z" + }, + { + "name": "webmin/webmin", + "version": "2.402", + "date": "2025-06-17T05:20:42Z" + }, + { + "name": "kimai/kimai", + "version": "2.36.1", + "date": "2025-06-16T19:20:54Z" + }, + { + "name": "goauthentik/authentik", + "version": "version/2025.6.2", + "date": "2025-06-16T17:54:39Z" + }, + { + "name": "runtipi/runtipi", + "version": "nightly", + "date": "2025-06-16T17:35:17Z" + }, + { + "name": "emqx/emqx", + "version": "e5.9.1-alpha.1", + "date": "2025-06-16T15:34:01Z" + }, + { + "name": "fuma-nama/fumadocs", + "version": "fumadocs-openapi@9.0.12", + "date": "2025-06-16T15:09:27Z" + }, + { + "name": "open-webui/open-webui", + "version": "v0.6.15", + "date": "2025-06-16T14:34:42Z" + }, + { + "name": "grokability/snipe-it", + "version": "v8.1.16", + "date": "2025-06-16T13:49:37Z" + }, + { + "name": "Graylog2/graylog2-server", + "version": "6.3.0-rc.1", + "date": "2025-06-16T11:28:08Z" + }, + { + "name": "home-assistant/operating-system", + "version": "15.2", + "date": "2025-04-14T15:37:12Z" + }, + { + "name": "moghtech/komodo", + "version": "v1.18.3", + "date": "2025-06-16T07:03:46Z" + }, + { + "name": "jellyfin/jellyfin", + "version": "v10.10.7", + "date": "2025-04-05T19:14:59Z" + }, + { + "name": "crafty-controller/crafty-4", + "version": "v4.4.11", + "date": "2025-06-15T21:41:02Z" + }, + { + "name": "TriliumNext/Notes", + "version": "v0.95.0", + "date": "2025-06-15T21:12:04Z" + }, + { + "name": "karakeep-app/karakeep", + "version": "cli/v0.25.0", + "date": "2025-06-15T17:48:29Z" + }, + { + "name": "tobychui/zoraxy", + "version": "v3.1.9", + "date": "2025-03-01T02:24:33Z" + }, + { + "name": "Prowlarr/Prowlarr", + "version": "v1.37.0.5076", + "date": "2025-06-04T11:04:53Z" + }, + { + "name": "Readarr/Readarr", + "version": "v2.0.0.4645", + "date": "2017-03-07T18:56:06Z" + }, + { + "name": "Lidarr/Lidarr", + "version": "v2.12.4.4658", + "date": "2025-06-09T17:27:45Z" + }, + { + "name": "Radarr/Radarr", + "version": "v5.26.2.10099", + "date": "2025-06-11T20:10:39Z" + }, + { + "name": "traccar/traccar", + "version": "v6.7.3", + "date": "2025-06-15T05:46:17Z" + }, + { + "name": "hyperion-project/hyperion.ng", + "version": "2.1.1", + "date": "2025-06-14T17:45:06Z" + }, + { + "name": "advplyr/audiobookshelf", + "version": "v2.25.1", + "date": "2025-06-14T23:32:15Z" + }, + { + "name": "semaphoreui/semaphore", + "version": "v2.15.0", + "date": "2025-06-14T10:48:57Z" + }, + { + "name": "FlareSolverr/FlareSolverr", + "version": "v3.3.25", + "date": "2025-06-14T02:52:44Z" + }, { "name": "home-assistant/core", "version": "2025.6.1", "date": "2025-06-13T20:16:18Z" }, { - "name": "homarr-labs/homarr", - "version": "v1.24.0", - "date": "2025-06-13T19:15:36Z" - }, - { - "name": "Luligu/matterbridge", - "version": "3.0.6", - "date": "2025-06-13T15:02:37Z" - }, - { - "name": "jenkinsci/jenkins", - "version": "jenkins-2.514", - "date": "2025-06-10T14:27:57Z" - }, - { - "name": "grafana/grafana", - "version": "v12.0.1+security-01", - "date": "2025-06-13T04:15:18Z" - }, - { - "name": "msgbyte/tianji", - "version": "v1.21.13", - "date": "2025-06-13T04:11:30Z" + "name": "wazuh/wazuh", + "version": "coverity-w25-4.13.0", + "date": "2025-06-13T13:58:23Z" }, { "name": "FlowiseAI/Flowise", @@ -94,116 +454,51 @@ "version": "v2025-06-12", "date": "2025-06-12T20:59:47Z" }, + { + "name": "meilisearch/meilisearch", + "version": "latest", + "date": "2025-06-12T19:09:18Z" + }, { "name": "Checkmk/checkmk", "version": "v2.3.0p34", "date": "2025-06-12T12:15:44Z" }, - { - "name": "n8n-io/n8n", - "version": "n8n@1.95.3", - "date": "2025-06-03T11:09:42Z" - }, - { - "name": "docker/compose", - "version": "v2.37.1", - "date": "2025-06-12T09:00:21Z" - }, - { - "name": "redis/redis", - "version": "8.2-m01-int2", - "date": "2025-06-12T08:52:10Z" - }, { "name": "zitadel/zitadel", "version": "v3.3.0", "date": "2025-06-12T06:54:48Z" }, - { - "name": "morpheus65535/bazarr", - "version": "v1.5.2", - "date": "2025-05-11T16:40:55Z" - }, { "name": "documenso/documenso", "version": "v1.12.0-rc.4", "date": "2025-06-12T00:27:41Z" }, - { - "name": "Radarr/Radarr", - "version": "v5.26.2.10099", - "date": "2025-06-11T20:10:39Z" - }, { "name": "TandoorRecipes/recipes", "version": "2.0.0-alpha-4", "date": "2025-05-14T05:01:45Z" }, - { - "name": "esphome/esphome", - "version": "2025.5.2", - "date": "2025-06-03T08:45:14Z" - }, { "name": "MediaBrowser/Emby.Releases", "version": "4.8.11.0", "date": "2025-03-10T06:39:11Z" }, - { - "name": "crowdsecurity/crowdsec", - "version": "v1.6.8", - "date": "2025-03-25T13:33:10Z" - }, - { - "name": "dgtlmoon/changedetection.io", - "version": "0.50.3", - "date": "2025-06-11T15:19:52Z" - }, - { - "name": "kimai/kimai", - "version": "2.36.0", - "date": "2025-06-11T12:31:07Z" - }, - { - "name": "cloudflare/cloudflared", - "version": "2025.6.0", - "date": "2025-06-11T11:13:21Z" - }, { "name": "autobrr/autobrr", "version": "v1.63.1", "date": "2025-06-11T11:05:42Z" }, { - "name": "fallenbagel/jellyseerr", - "version": "preview-forceipv4-axios", - "date": "2025-06-11T09:16:40Z" - }, - { - "name": "wazuh/wazuh", - "version": "coverity-w24-4.13.0", - "date": "2025-06-11T04:04:55Z" - }, - { - "name": "zabbix/zabbix", - "version": "7.2.8rc1", - "date": "2025-06-11T06:50:19Z" + "name": "steveiliop56/tinyauth", + "version": "v3.4.1", + "date": "2025-06-11T07:53:44Z" }, { "name": "openobserve/openobserve", "version": "v0.15.0-rc2", "date": "2025-06-11T04:29:22Z" }, - { - "name": "mattermost/mattermost", - "version": "server/public/v0.1.15", - "date": "2025-06-11T03:56:25Z" - }, - { - "name": "emqx/emqx", - "version": "e5.10.0", - "date": "2025-06-10T16:03:18Z" - }, { "name": "node-red/node-red", "version": "4.1.0-beta.1", @@ -214,71 +509,21 @@ "version": "v0.107.62", "date": "2025-05-27T12:10:19Z" }, - { - "name": "open-webui/open-webui", - "version": "v0.6.14", - "date": "2025-06-10T14:18:04Z" - }, - { - "name": "element-hq/synapse", - "version": "v1.131.0", - "date": "2025-06-03T14:13:00Z" - }, - { - "name": "Graylog2/graylog2-server", - "version": "6.3.0-beta.5", - "date": "2025-06-10T11:19:42Z" - }, { "name": "OctoPrint/OctoPrint", "version": "1.11.2", "date": "2025-06-10T11:07:14Z" }, - { - "name": "VictoriaMetrics/VictoriaMetrics", - "version": "v1.110.11", - "date": "2025-06-10T10:00:25Z" - }, { "name": "glanceapp/glance", "version": "v0.8.4", "date": "2025-06-10T07:57:14Z" }, - { - "name": "go-gitea/gitea", - "version": "v1.24.0", - "date": "2025-06-10T02:00:38Z" - }, - { - "name": "Sonarr/Sonarr", - "version": "v4.0.14.2939", - "date": "2025-03-17T19:12:37Z" - }, - { - "name": "cross-seed/cross-seed", - "version": "v6.12.6", - "date": "2025-05-28T00:13:19Z" - }, { "name": "tailscale/tailscale", "version": "v1.84.2", "date": "2025-06-09T23:43:27Z" }, - { - "name": "pocket-id/pocket-id", - "version": "v1.3.1", - "date": "2025-06-09T21:07:27Z" - }, - { - "name": "pocketbase/pocketbase", - "version": "v0.28.3", - "date": "2025-06-09T18:11:46Z" - }, - { - "name": "Lidarr/Lidarr", - "version": "v2.12.4.4658", - "date": "2025-06-09T17:27:45Z" - }, { "name": "Brandawg93/PeaNUT", "version": "v5.8.0", @@ -289,81 +534,31 @@ "version": "v3.3.0", "date": "2025-06-09T15:58:04Z" }, - { - "name": "NodeBB/NodeBB", - "version": "v2.8.19", - "date": "2025-06-09T15:32:25Z" - }, { "name": "seanmorley15/AdventureLog", "version": "v0.10.0", "date": "2025-06-09T13:37:07Z" }, - { - "name": "silverbulletmd/silverbullet", - "version": "0.10.4", - "date": "2025-02-25T18:13:42Z" - }, - { - "name": "neo4j/neo4j", - "version": "5.26.8", - "date": "2025-06-08T22:50:58Z" - }, - { - "name": "karakeep-app/karakeep", - "version": "ios/v1.7.0-1", - "date": "2025-06-08T22:02:33Z" - }, { "name": "Forceu/Gokapi", "version": "v2.0.1", "date": "2025-06-08T14:40:24Z" }, - { - "name": "Readarr/Readarr", - "version": "v2.0.0.4645", - "date": "2017-03-07T18:56:06Z" - }, { "name": "matze/wastebin", "version": "3.2.0", "date": "2025-06-07T21:33:22Z" }, - { - "name": "jellyfin/jellyfin", - "version": "v10.10.7", - "date": "2025-04-05T19:14:59Z" - }, { "name": "jordan-dalby/ByteStash", "version": "v1.5.8", "date": "2025-06-07T11:39:10Z" }, - { - "name": "syncthing/syncthing", - "version": "2.0.0-rc.19", - "date": "2025-06-02T17:56:25Z" - }, - { - "name": "evcc-io/evcc", - "version": "0.204.2", - "date": "2025-06-07T11:38:28Z" - }, - { - "name": "TriliumNext/Notes", - "version": "v0.94.1", - "date": "2025-06-07T10:32:38Z" - }, { "name": "homebridge/homebridge", "version": "v1.10.0", "date": "2025-06-07T08:31:48Z" }, - { - "name": "moghtech/komodo", - "version": "v1.18.1", - "date": "2025-06-07T06:25:20Z" - }, { "name": "OliveTin/OliveTin", "version": "2025.6.6", @@ -374,11 +569,6 @@ "version": "v4.1.2", "date": "2025-06-06T17:44:58Z" }, - { - "name": "goauthentik/authentik", - "version": "version/2025.6.1", - "date": "2025-06-06T15:28:21Z" - }, { "name": "ioBroker/ioBroker", "version": "2025-05-31", @@ -394,11 +584,6 @@ "version": "10.1.42", "date": "2025-06-05T22:39:40Z" }, - { - "name": "paperless-ngx/paperless-ngx", - "version": "v2.16.3", - "date": "2025-06-05T21:16:59Z" - }, { "name": "netbox-community/netbox", "version": "v4.3.2", @@ -419,11 +604,6 @@ "version": "mariadb-11.8.2", "date": "2025-06-04T13:35:16Z" }, - { - "name": "donaldzou/WGDashboard", - "version": "v4.2.3", - "date": "2025-05-07T15:35:04Z" - }, { "name": "actualbudget/actual", "version": "v25.6.1", @@ -440,35 +620,15 @@ "date": "2025-06-04T16:41:44Z" }, { - "name": "Prowlarr/Prowlarr", - "version": "v1.37.0.5076", - "date": "2025-06-04T11:04:53Z" - }, - { - "name": "glpi-project/glpi", - "version": "10.0.18", - "date": "2025-02-12T11:07:02Z" + "name": "intri-in/manage-my-damn-life-nextjs", + "version": "v0.7.1", + "date": "2025-06-04T04:39:36Z" }, { "name": "louislam/uptime-kuma", "version": "2.0.0-beta.2-temp", "date": "2025-03-28T08:45:58Z" }, - { - "name": "webmin/webmin", - "version": "2.401", - "date": "2025-06-04T02:53:03Z" - }, - { - "name": "coder/code-server", - "version": "v4.100.3", - "date": "2025-06-03T21:06:41Z" - }, - { - "name": "runtipi/runtipi", - "version": "v4.2.1", - "date": "2025-06-03T20:04:28Z" - }, { "name": "influxdata/influxdb", "version": "v1.12.1rc3", @@ -484,11 +644,6 @@ "version": "1.26.3", "date": "2025-06-02T22:00:14Z" }, - { - "name": "grokability/snipe-it", - "version": "v8.1.15", - "date": "2025-06-02T17:38:24Z" - }, { "name": "inventree/InvenTree", "version": "0.17.13", @@ -499,11 +654,6 @@ "version": "v0.24.4", "date": "2025-06-02T02:49:05Z" }, - { - "name": "traccar/traccar", - "version": "v6.7.2", - "date": "2025-06-01T20:40:00Z" - }, { "name": "gethomepage/homepage", "version": "v1.3.2", @@ -514,31 +664,31 @@ "version": "2.4.0", "date": "2025-06-01T18:08:44Z" }, + { + "name": "TwiN/gatus", + "version": "v5.18.1", + "date": "2025-05-31T23:06:08Z" + }, { "name": "prometheus/prometheus", "version": "v3.4.1", "date": "2025-05-31T13:45:40Z" }, - { - "name": "BookStackApp/BookStack", - "version": "v25.05", - "date": "2025-05-31T13:36:23Z" - }, { "name": "blakeblackshear/frigate", "version": "v0.14.1", "date": "2024-08-29T22:32:51Z" }, - { - "name": "icereed/paperless-gpt", - "version": "v0.20.0", - "date": "2025-05-30T14:39:51Z" - }, { "name": "binwiederhier/ntfy", "version": "v2.12.0", "date": "2025-05-30T00:26:27Z" }, + { + "name": "release-argus/Argus", + "version": "0.26.3", + "date": "2025-05-29T21:18:15Z" + }, { "name": "gristlabs/grist-core", "version": "v1.6.0", @@ -549,21 +699,11 @@ "version": "v0.56.1", "date": "2025-05-29T19:09:16Z" }, - { - "name": "HabitRPG/habitica", - "version": "v5.36.5", - "date": "2025-05-29T17:06:01Z" - }, { "name": "readeck/readeck", "version": "0.19.2", "date": "2025-05-29T14:39:17Z" }, - { - "name": "zwave-js/zwave-js-ui", - "version": "v10.6.1", - "date": "2025-05-29T13:36:09Z" - }, { "name": "duplicati/duplicati", "version": "v2.1.0.119-2.1.0.119_canary_2025-05-29", @@ -574,11 +714,6 @@ "version": "cassandra-4.0.18", "date": "2025-05-28T21:45:55Z" }, - { - "name": "mongodb/mongo", - "version": "r6.0.24", - "date": "2025-05-28T21:25:03Z" - }, { "name": "Athou/commafeed", "version": "5.10.0", @@ -594,11 +729,6 @@ "version": "1.2.34", "date": "2025-05-27T18:18:00Z" }, - { - "name": "immich-app/immich", - "version": "v1.134.0", - "date": "2025-05-27T17:28:27Z" - }, { "name": "traefik/traefik", "version": "v3.4.1", @@ -634,46 +764,21 @@ "version": "RELEASE.2025-05-24T17-08-30Z", "date": "2025-05-24T21:42:19Z" }, - { - "name": "rogerfar/rdt-client", - "version": "v2.0.113", - "date": "2025-05-23T01:47:35Z" - }, { "name": "0xERR0R/blocky", "version": "v0.26.2", "date": "2025-05-22T05:24:42Z" }, { - "name": "tobychui/zoraxy", - "version": "v3.1.9", - "date": "2025-03-01T02:24:33Z" - }, - { - "name": "apache/tika", - "version": "3.2.0-rc2", - "date": "2025-05-21T20:09:07Z" - }, - { - "name": "clusterzx/paperless-ai", - "version": "v3.0.4", - "date": "2025-05-21T19:03:53Z" - }, - { - "name": "ipfs/kubo", - "version": "v0.35.0", - "date": "2025-05-21T18:00:32Z" + "name": "CrazyWolf13/streamlink-webui", + "version": "0.5", + "date": "2025-05-21T20:19:14Z" }, { "name": "Stirling-Tools/Stirling-PDF", "version": "v0.46.2", "date": "2025-05-20T11:21:04Z" }, - { - "name": "crafty-controller/crafty-4", - "version": "v4.4.9", - "date": "2025-05-20T00:08:29Z" - }, { "name": "Part-DB/Part-DB-server", "version": "v1.17.1", @@ -759,6 +864,11 @@ "version": "v1.0.0-beta21", "date": "2025-05-09T23:14:23Z" }, + { + "name": "getumbrel/umbrel", + "version": "1.4.2", + "date": "2025-05-09T08:54:49Z" + }, { "name": "ZoeyVid/NPMplus", "version": "2025-05-07-r1", @@ -769,6 +879,11 @@ "version": "v0.19.0", "date": "2025-05-06T18:05:42Z" }, + { + "name": "garethgeorge/backrest", + "version": "v1.8.1", + "date": "2025-05-06T04:27:00Z" + }, { "name": "linkwarden/linkwarden", "version": "v2.10.2", @@ -785,30 +900,20 @@ "date": "2025-05-05T16:28:24Z" }, { - "name": "forgejo/forgejo", - "version": "v11.0.1", - "date": "2025-05-02T17:10:30Z" + "name": "raydak-labs/configarr", + "version": "v1.13.5", + "date": "2025-05-03T09:48:44Z" }, { "name": "jhuckaby/Cronicle", "version": "v0.9.80", "date": "2025-05-02T16:48:15Z" }, - { - "name": "prometheus-pve/prometheus-pve-exporter", - "version": "v3.5.4", - "date": "2025-05-02T13:42:06Z" - }, { "name": "WordPress/WordPress", "version": "6.8.1", "date": "2025-04-30T16:44:16Z" }, - { - "name": "docmost/docmost", - "version": "v0.20.4", - "date": "2025-04-30T14:15:16Z" - }, { "name": "hivemq/hivemq-community-edition", "version": "2025.3", @@ -829,6 +934,11 @@ "version": "deluge-2.2.0", "date": "2025-04-28T21:31:06Z" }, + { + "name": "oauth2-proxy/oauth2-proxy", + "version": "v7.9.0", + "date": "2025-04-28T16:28:42Z" + }, { "name": "gotify/server", "version": "v2.6.3", @@ -839,11 +949,6 @@ "version": "release-5.1.0", "date": "2025-04-27T08:53:48Z" }, - { - "name": "openhab/openhab-core", - "version": "4.3.5", - "date": "2025-04-26T13:52:07Z" - }, { "name": "photoprism/photoprism", "version": "250426-27ec7a128", @@ -889,11 +994,6 @@ "version": "v2.10.0", "date": "2025-04-18T20:46:28Z" }, - { - "name": "benzino77/tasmocompiler", - "version": "v12.6.1", - "date": "2025-04-17T17:35:02Z" - }, { "name": "IceWhaleTech/CasaOS", "version": "v0.4.15", @@ -904,11 +1004,6 @@ "version": "0.22.5", "date": "2025-04-15T02:52:26Z" }, - { - "name": "home-assistant/operating-system", - "version": "15.2", - "date": "2025-04-14T15:37:12Z" - }, { "name": "Tautulli/Tautulli", "version": "v2.15.2", @@ -919,16 +1014,6 @@ "version": "v0.2.11", "date": "2025-04-12T21:13:08Z" }, - { - "name": "Bubka/2FAuth", - "version": "v5.5.2", - "date": "2025-04-11T22:00:06Z" - }, - { - "name": "sabnzbd/sabnzbd", - "version": "4.5.1", - "date": "2025-04-11T09:57:47Z" - }, { "name": "thomiceli/opengist", "version": "v1.10.0", @@ -989,6 +1074,11 @@ "version": "v1.34.0", "date": "2025-03-26T08:48:34Z" }, + { + "name": "LibreTranslate/LibreTranslate", + "version": "v1.6.5", + "date": "2025-03-25T20:27:29Z" + }, { "name": "nextcloud/nextcloudpi", "version": "v1.55.4", @@ -1054,6 +1144,11 @@ "version": "tc_v0.6.4", "date": "2025-03-05T15:43:40Z" }, + { + "name": "bitmagnet-io/bitmagnet", + "version": "v0.10.0", + "date": "2025-03-02T15:13:47Z" + }, { "name": "heiher/hev-socks5-server", "version": "2.8.0", @@ -1074,6 +1169,11 @@ "version": "v1.1.05", "date": "2025-02-24T11:53:12Z" }, + { + "name": "babybuddy/babybuddy", + "version": "v2.7.1", + "date": "2025-02-22T01:14:41Z" + }, { "name": "typesense/typesense", "version": "v28.0", @@ -1109,6 +1209,11 @@ "version": "1.7.6", "date": "2025-02-01T09:50:52Z" }, + { + "name": "AmruthPillai/Reactive-Resume", + "version": "v4.4.4", + "date": "2025-01-30T16:57:47Z" + }, { "name": "rustdesk/rustdesk-server", "version": "1.1.14", @@ -1119,6 +1224,11 @@ "version": "v0.5.7", "date": "2025-01-17T15:57:17Z" }, + { + "name": "PCJones/UmlautAdaptarr", + "version": "0.7.2", + "date": "2025-01-13T22:17:18Z" + }, { "name": "ErsatzTV/ErsatzTV", "version": "v25.1.0", @@ -1129,6 +1239,11 @@ "version": "v0.24.6", "date": "2024-12-22T20:24:35Z" }, + { + "name": "favonia/cloudflare-ddns", + "version": "v1.15.1", + "date": "2024-12-16T13:00:05Z" + }, { "name": "ArchiveBox/ArchiveBox", "version": "v0.7.3", @@ -1159,11 +1274,6 @@ "version": "0.6.24", "date": "2024-11-16T06:47:56Z" }, - { - "name": "pterodactyl/panel", - "version": "v1.11.10", - "date": "2024-11-15T02:29:18Z" - }, { "name": "sabre-io/Baikal", "version": "0.10.1", @@ -1238,15 +1348,5 @@ "name": "thelounge/thelounge-deb", "version": "v4.4.3", "date": "2024-04-06T12:24:35Z" - }, - { - "name": "deepch/RTSPtoWeb", - "version": "v2.4.3", - "date": "2023-03-29T12:05:02Z" - }, - { - "name": "Shinobi-Systems/Shinobi", - "version": "furrykitten-3", - "date": "2022-07-15T05:20:17Z" } ] diff --git a/frontend/public/json/wizarr.json b/frontend/public/json/wizarr.json new file mode 100644 index 000000000..5cd5ca8e3 --- /dev/null +++ b/frontend/public/json/wizarr.json @@ -0,0 +1,36 @@ +{ + "name": "Wizarr", + "slug": "wizarr", + "categories": [ + 14, + 13 + ], + "date_created": "2025-06-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5690, + "documentation": "https://docs.wizarr.dev/", + "website": "https://docs.wizarr.dev/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/wizarr.webp", + "config_path": "/opt/wizarr/.env", + "description": "Wizarr is an automatic user invitation system for Plex, Jellyfin and Emby. Create a unique link and share it to a user and they will automatically be invited to your media Server", + "install_methods": [ + { + "type": "default", + "script": "ct/wizarr.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/frontend/src/app/api/categories/route.ts b/frontend/src/app/api/categories/route.ts index c7392a1e3..e9bdc8668 100644 --- a/frontend/src/app/api/categories/route.ts +++ b/frontend/src/app/api/categories/route.ts @@ -19,7 +19,11 @@ const getMetadata = async () => { const getScripts = async () => { const filePaths = (await fs.readdir(jsonDir)) - .filter((fileName) => fileName !== metadataFileName && fileName !== versionFileName) + .filter((fileName) => + fileName.endsWith(".json") && + fileName !== metadataFileName && + fileName !== versionFileName + ) .map((fileName) => path.resolve(jsonDir, fileName)); const scripts = await Promise.all( diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 9a341ccfb..07cb6e2a1 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -104,7 +104,7 @@ export default function RootLayout({
-
+
{children} diff --git a/frontend/src/app/scripts/_components/ScriptAccordion.tsx b/frontend/src/app/scripts/_components/ScriptAccordion.tsx index 96a07fc38..1395d2346 100644 --- a/frontend/src/app/scripts/_components/ScriptAccordion.tsx +++ b/frontend/src/app/scripts/_components/ScriptAccordion.tsx @@ -56,7 +56,7 @@ export default function ScriptAccordion({ value={expandedItem} onValueChange={handleAccordionChange} collapsible - className="overflow-y-scroll max-h-[calc(100vh-225px)] overflow-x-hidden mt-3 p-2" + className="overflow-y-scroll max-h-[calc(100vh-225px)] overflow-x-hidden p-2" > {items.map((category) => ( +

Selected Script

@@ -151,7 +151,7 @@ export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
-
+
}> @@ -171,16 +171,18 @@ export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
- -
-

- Location of config file -

-
- -
- -
+ {item.config_path && ( + <> + +
+

Location of config file

+
+ +
+ +
+ + )}
diff --git a/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx b/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx index 0f4f43fb0..d11ed24d2 100644 --- a/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx +++ b/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx @@ -1,10 +1,9 @@ import ConfigCopyButton from "@/components/ui/config-copy-button"; -import { Script } from "@/lib/types"; -export default function ConfigFile({ item }: { item: Script }) { +export default function ConfigFile({ configPath }: { configPath: string }) { return (
- {item.config_path ? item.config_path : "No config path set"} + {configPath}
); } diff --git a/frontend/src/app/scripts/_components/Sidebar.tsx b/frontend/src/app/scripts/_components/Sidebar.tsx index 3680210fa..38b7a3547 100644 --- a/frontend/src/app/scripts/_components/Sidebar.tsx +++ b/frontend/src/app/scripts/_components/Sidebar.tsx @@ -22,7 +22,7 @@ const Sidebar = ({ }, [] as Script[]); return ( -
+

Categories

@@ -40,4 +40,4 @@ const Sidebar = ({ ); }; -export default Sidebar; \ No newline at end of file +export default Sidebar; diff --git a/frontend/src/app/scripts/page.tsx b/frontend/src/app/scripts/page.tsx index 590b67358..32d207bbc 100644 --- a/frontend/src/app/scripts/page.tsx +++ b/frontend/src/app/scripts/page.tsx @@ -47,7 +47,7 @@ function ScriptContent() { setSelectedScript={setSelectedScript} />

-
+
{selectedScript && item ? ( ) : ( diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index efe3a920b..22705d2ac 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -34,7 +34,7 @@ function Navbar() { isScrolled ? "glass border-b bg-background/50" : "" }`} > -
+
/etc/apt/sources.list.d/php.list -$STD apt-get update - -$STD apt-get install -y \ - nginx \ - composer \ - php8.3-{bcmath,common,ctype,curl,fileinfo,fpm,gd,intl,mbstring,mysql,xml,cli} + lsb-release \ + nginx msg_ok "Installed Dependencies" -install_mariadb +PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" setup_php +setup_composer +setup_mariadb msg_info "Setting up Database" DB_NAME=2fauth_db @@ -43,16 +38,12 @@ $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUS } >>~/2FAuth.creds msg_ok "Set up Database" -msg_info "Setup 2FAuth" -RELEASE=$(curl -fsSL https://api.github.com/repos/Bubka/2FAuth/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL "https://github.com/Bubka/2FAuth/archive/refs/tags/${RELEASE}.zip" -o "${RELEASE}.zip" -$STD unzip "${RELEASE}.zip" -mv "2FAuth-${RELEASE//v/}/" /opt/2fauth +fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" -cd "/opt/2fauth" || return +msg_info "Setup 2FAuth" +cd /opt/2fauth cp .env.example .env IPADDRESS=$(hostname -I | awk '{print $1}') - sed -i -e "s|^APP_URL=.*|APP_URL=http://$IPADDRESS|" \ -e "s|^DB_CONNECTION=$|DB_CONNECTION=mysql|" \ -e "s|^DB_DATABASE=$|DB_DATABASE=$DB_NAME|" \ @@ -60,22 +51,16 @@ sed -i -e "s|^APP_URL=.*|APP_URL=http://$IPADDRESS|" \ -e "s|^DB_PORT=$|DB_PORT=3306|" \ -e "s|^DB_USERNAME=$|DB_USERNAME=$DB_USER|" \ -e "s|^DB_PASSWORD=$|DB_PASSWORD=$DB_PASS|" .env - export COMPOSER_ALLOW_SUPERUSER=1 $STD composer update --no-plugins --no-scripts $STD composer install --no-dev --prefer-source --no-plugins --no-scripts - $STD php artisan key:generate --force - $STD php artisan migrate:refresh $STD php artisan passport:install -q -n $STD php artisan storage:link $STD php artisan config:cache - chown -R www-data: /opt/2fauth chmod -R 755 /opt/2fauth - -echo "${RELEASE}" >"/opt/2fauth_version.txt" msg_ok "Setup 2fauth" msg_info "Configure Service" @@ -107,7 +92,6 @@ server { } } EOF - systemctl reload nginx msg_ok "Configured Service" @@ -115,7 +99,6 @@ motd_ssh customize msg_info "Cleaning up" -rm -f "/opt/v${RELEASE}.zip" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" diff --git a/install/actualbudget-install.sh b/install/actualbudget-install.sh index 51843f6c7..59b2d3870 100644 --- a/install/actualbudget-install.sh +++ b/install/actualbudget-install.sh @@ -22,7 +22,7 @@ msg_info "Installing Actual Budget" cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/actualbudget/actual/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') NODE_VERSION="22" -install_node_and_modules +setup_nodejs mkdir -p /opt/actualbudget-data/{server-files,upload,migrate,user-files,migrations,config} chown -R root:root /opt/actualbudget-data chmod -R 755 /opt/actualbudget-data diff --git a/install/adventurelog-install.sh b/install/adventurelog-install.sh index 2fcd5fc1f..407295003 100644 --- a/install/adventurelog-install.sh +++ b/install/adventurelog-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2024 tteck +# Copyright (c) 2021-2025 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -18,12 +18,10 @@ msg_info "Installing Dependencies" $STD apt-get install -y \ gdal-bin \ libgdal-dev \ - git \ - python3-venv \ - python3-pip -rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + git msg_ok "Installed Dependencies" +PYTHON_VERSION="3.12" setup_uv NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules PG_VERSION="16" PG_MODULES="postgis" install_postgresql @@ -47,15 +45,12 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/adventurelog.creds msg_ok "Set up PostgreSQL" +fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog" + msg_info "Installing AdventureLog (Patience)" DJANGO_ADMIN_USER="djangoadmin" DJANGO_ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" LOCAL_IP="$(hostname -I | awk '{print $1}')" -cd /opt -RELEASE=$(curl -fsSL https://api.github.com/repos/seanmorley15/AdventureLog/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -curl -fsSL "https://github.com/seanmorley15/AdventureLog/archive/refs/tags/v${RELEASE}.zip" -o "v${RELEASE}.zip" -$STD unzip v${RELEASE}.zip -mv AdventureLog-${RELEASE} /opt/adventurelog cat </opt/adventurelog/backend/server/.env PGHOST='localhost' PGDATABASE='${DB_NAME}' @@ -80,11 +75,13 @@ DISABLE_REGISTRATION=False EOF cd /opt/adventurelog/backend/server mkdir -p /opt/adventurelog/backend/server/media -$STD pip install --upgrade pip -$STD pip install -r requirements.txt -$STD python3 manage.py collectstatic --noinput -$STD python3 manage.py migrate -$STD python3 manage.py download-countries +$STD uv venv /opt/adventurelog/backend/server/.venv +$STD /opt/adventurelog/backend/server/.venv/bin/python -m ensurepip --upgrade +$STD /opt/adventurelog/backend/server/.venv/bin/python -m pip install --upgrade pip +$STD /opt/adventurelog/backend/server/.venv/bin/python -m pip install -r requirements.txt +$STD /opt/adventurelog/backend/server/.venv/bin/python -m manage collectstatic --noinput +$STD /opt/adventurelog/backend/server/.venv/bin/python -m manage migrate +$STD /opt/adventurelog/backend/server/.venv/bin/python -m manage download-countries cat </opt/adventurelog/frontend/.env PUBLIC_SERVER_URL=http://$LOCAL_IP:8000 BODY_SIZE_LIMIT=Infinity @@ -97,7 +94,8 @@ echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" msg_ok "Installed AdventureLog" msg_info "Setting up Django Admin" -$STD python3 /opt/adventurelog/backend/server/manage.py shell <"/opt/${APPLICATION}_version.txt" msg_ok "Installed Firefly III" @@ -75,7 +83,18 @@ cat </etc/apache2/sites-available/firefly.conf AllowOverride All Require all granted - + + Alias /dataimporter/ /opt/firefly/dataimporter/public/ + + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + + SetHandler application/x-httpd-php + + ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined @@ -93,7 +112,8 @@ motd_ssh customize msg_info "Cleaning up" -rm -rf /opt/FireflyIII-v${RELEASE}.tar.gz +rm -rf "/opt/FireflyIII-v${RELEASE}.tar.gz" +rm -rf "/opt/DataImporter-v${IMPORTER_RELEASE}.tar.gz" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" diff --git a/install/flowiseai-install.sh b/install/flowiseai-install.sh index cf78c6434..5ec7da9cd 100644 --- a/install/flowiseai-install.sh +++ b/install/flowiseai-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Installing FlowiseAI (Patience)" $STD npm install -g flowise \ diff --git a/install/freshrss-install.sh b/install/freshrss-install.sh index dc2894f61..25251d13c 100644 --- a/install/freshrss-install.sh +++ b/install/freshrss-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ libapache2-mod-php msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=freshrss diff --git a/install/frigate-install.sh b/install/frigate-install.sh index f5d63a9c1..6e3ac1b34 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -24,7 +24,7 @@ rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED $STD pip install --upgrade pip msg_ok "Setup Python3" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing go2rtc" mkdir -p /usr/local/go2rtc/bin diff --git a/install/fumadocs-install.sh b/install/fumadocs-install.sh index 84c894786..beef80e3e 100644 --- a/install/fumadocs-install.sh +++ b/install/fumadocs-install.sh @@ -17,7 +17,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Installing Fumadocs" mkdir -p /opt/fumadocs diff --git a/install/gatus-install.sh b/install/gatus-install.sh index 8a2fc7a5c..3b5cd563b 100644 --- a/install/gatus-install.sh +++ b/install/gatus-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ libcap2-bin msg_ok "Installed Dependencies" -install_go +setup_go RELEASE=$(curl -s https://api.github.com/repos/TwiN/gatus/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Setting up gatus v${RELEASE}" diff --git a/install/ghost-install.sh b/install/ghost-install.sh index 14604051d..cee4f3d58 100644 --- a/install/ghost-install.sh +++ b/install/ghost-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Configuring Database" DB_NAME=ghost @@ -37,7 +37,7 @@ $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUS } >>~/ghost.creds msg_ok "Configured MySQL" -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Installing Ghost CLI" $STD npm install ghost-cli@latest -g diff --git a/install/gitea-mirror-install.sh b/install/gitea-mirror-install.sh index 2a44477b9..d1f3dd78f 100644 --- a/install/gitea-mirror-install.sh +++ b/install/gitea-mirror-install.sh @@ -28,7 +28,7 @@ ln -sf /opt/bun/bin/bun /usr/local/bin/bun ln -sf /opt/bun/bin/bun /usr/local/bin/bunx msg_ok "Installed Bun" -fetch_and_deploy_gh_release "arunavo4/gitea-mirror" +fetch_and_deploy_gh_release "gitea-mirror" "arunavo4/gitea-mirror" msg_info "Installing gitea-mirror" cd /opt/gitea-mirror diff --git a/install/glpi-install.sh b/install/glpi-install.sh index 2e9563b6a..25b0d7d7e 100644 --- a/install/glpi-install.sh +++ b/install/glpi-install.sh @@ -22,7 +22,7 @@ $STD apt-get install -y \ libapache2-mod-php msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up database" DB_NAME=glpi_db diff --git a/install/gomft-install.sh b/install/gomft-install.sh index 5b5692d89..2d84ef014 100644 --- a/install/gomft-install.sh +++ b/install/gomft-install.sh @@ -22,8 +22,8 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" -install_go -NODE_VERSION="22" install_node_and_modules +setup_go +NODE_VERSION="22" setup_nodejs msg_info "Setup ${APPLICATION} (Patience)" temp_file=$(mktemp) diff --git a/install/graylog-install.sh b/install/graylog-install.sh index 21bec8777..397d8ebab 100644 --- a/install/graylog-install.sh +++ b/install/graylog-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -MONGO_VERSION="7.0" install_mongodb +MONGO_VERSION="7.0" setup_mongodb msg_info "Setup Graylog Data Node" PASSWORD_SECRET=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) diff --git a/install/grist-install.sh b/install/grist-install.sh index 9649accf4..c69814c70 100644 --- a/install/grist-install.sh +++ b/install/grist-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ python3.11-venv msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Installing Grist" RELEASE=$(curl -fsSL https://api.github.com/repos/gristlabs/grist-core/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') diff --git a/install/habitica-install.sh b/install/habitica-install.sh index 482771e8b..c6b68efd7 100644 --- a/install/habitica-install.sh +++ b/install/habitica-install.sh @@ -22,7 +22,7 @@ curl -fsSL "http://ports.ubuntu.com/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu $STD dpkg -i libssl1.1_1.1.1f-1ubuntu2_arm64.deb msg_ok "Installed Dependencies" -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Setup ${APPLICATION}" temp_file=$(mktemp) diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 1116534bd..0ff212da9 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -28,8 +28,8 @@ 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_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" -install_node_and_modules -fetch_and_deploy_gh_release "homarr-labs/homarr" +setup_nodejs +fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" msg_info "Installing Homarr (Patience)" cd /opt diff --git a/install/homepage-install.sh b/install/homepage-install.sh index 7726b5ea1..303648aa3 100644 --- a/install/homepage-install.sh +++ b/install/homepage-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y jq msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs LOCAL_IP=$(hostname -I | awk '{print $1}') RELEASE=$(curl -fsSL https://api.github.com/repos/gethomepage/homepage/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') diff --git a/install/huntarr-install.sh b/install/huntarr-install.sh new file mode 100644 index 000000000..668ee870b --- /dev/null +++ b/install/huntarr-install.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: BiluliB +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/plexguide/Huntarr.io + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y jq +msg_ok "Installed Dependencies" + +setup_uv +fetch_and_deploy_gh_release "huntarr" "plexguide/Huntarr.io" + +msg_info "Configure Huntarr" +$STD uv venv /opt/huntarr/.venv +$STD uv pip install --python /opt/huntarr/.venv/bin/python -r /opt/huntarr/requirements.txt +msg_ok "Configured Huntrarr" + +msg_info "Creating Service" +cat </etc/systemd/system/huntarr.service +[Unit] +Description=Huntarr Service +After=network.target +[Service] +WorkingDirectory=/opt/huntarr +ExecStart=/opt/huntarr/.venv/bin/python /opt/huntarr/main.py +Restart=always +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now huntarr +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/immich-install.sh b/install/immich-install.sh index be7312767..aa0490b9b 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -13,6 +13,8 @@ setting_up_container network_check update_os +setup_uv + msg_info "Configuring apt and installing dependencies" echo "deb http://deb.debian.org/debian testing main contrib" >/etc/apt/sources.list.d/immich.list cat </etc/apt/preferences.d/immich @@ -27,7 +29,6 @@ $STD apt-get install --no-install-recommends -y \ redis \ autoconf \ build-essential \ - python3-venv \ python3-dev \ cmake \ jq \ @@ -63,6 +64,7 @@ $STD apt-get install --no-install-recommends -y \ mesa-utils \ mesa-va-drivers \ mesa-vulkan-drivers \ + ocl-icd-libopencl1 \ tini \ zlib1g $STD apt-get install -y \ @@ -84,21 +86,17 @@ ln -s /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg ln -s /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe msg_ok "Dependencies Installed" -NODE_VERSION="22" install_node_and_modules -PG_VERSION="16" install_postgresql - -read -r -p "${TAB3}Install OpenVINO dependencies for Intel HW-accelerated machine-learning? " prompt +read -r -p "Install OpenVINO dependencies for Intel HW-accelerated machine-learning? y/N " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing OpenVINO dependencies" touch ~/.openvino - $STD apt-get -y install --no-install-recommends ocl-icd-libopencl1 tmp_dir=$(mktemp -d) $STD pushd "$tmp_dir" - curl -fsSL https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-core_1.0.17384.11_amd64.deb -O - curl -fsSL https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-opencl_1.0.17384.11_amd64.deb -O - curl -fsSL https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/intel-opencl-icd_24.31.30508.7_amd64.deb -O - curl -fsSL https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/libigdgmm12_22.4.1_amd64.deb -O - $STD dpkg -i ./*.deb + curl -fsSLO https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-core_1.0.17384.11_amd64.deb + curl -fsSLO https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-opencl_1.0.17384.11_amd64.deb + curl -fsSLO https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/intel-opencl-icd_24.31.30508.7_amd64.deb + curl -fsSLO https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/libigdgmm12_22.4.1_amd64.deb + $STD apt install -y ./*.deb $STD popd rm -rf "$tmp_dir" dpkg -l | grep "intel-opencl-icd" | awk '{print $3}' >~/.intel_version @@ -112,11 +110,15 @@ if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_ok "Installed OpenVINO dependencies" fi +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql + msg_info "Setting up Postgresql Database" -$STD apt-get install postgresql-16-pgvector -curl -fsSL https://github.com/tensorchord/VectorChord/releases/download/0.3.0/postgresql-16-vchord_0.3.0-1_amd64.deb -o vchord.deb -$STD dpkg -i vchord.deb +VCHORD_RELEASE="$(curl -fsSL https://api.github.com/repos/tensorchord/vectorchord/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')" +curl -fsSL "https://github.com/tensorchord/VectorChord/releases/download/${VCHORD_RELEASE}/postgresql-16-vchord_${VCHORD_RELEASE}-1_amd64.deb" -o vchord.deb +$STD apt install -y ./vchord.deb rm vchord.deb +echo "$VCHORD_RELEASE" >~/.vchord_version DB_NAME="immich" DB_USER="immich" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c18) @@ -271,11 +273,6 @@ rm -rf "$SOURCE"/build } >~/.immich_library_revisions msg_ok "Custom Photo-processing Library Compiled" -msg_info "Installing ${APPLICATION} (more patience please)" -tmp_file=$(mktemp) -RELEASE=$(curl -s https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -curl -fsSL "https://github.com/immich-app/immich/archive/refs/tags/v${RELEASE}.zip" -o "$tmp_file" -unzip -q "$tmp_file" INSTALL_DIR="/opt/${APPLICATION}" UPLOAD_DIR="${INSTALL_DIR}/upload" SRC_DIR="${INSTALL_DIR}/source" @@ -283,9 +280,12 @@ APP_DIR="${INSTALL_DIR}/app" ML_DIR="${APP_DIR}/machine-learning" GEO_DIR="${INSTALL_DIR}/geodata" mkdir -p "$INSTALL_DIR" -mv "$APPLICATION-$RELEASE"/ "$SRC_DIR" mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${ML_DIR}","${INSTALL_DIR}"/cache} +fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v1.135.0" "$SRC_DIR" + +msg_info "Installing ${APPLICATION} (more patience please)" + cd "$SRC_DIR"/server $STD npm install -g node-gyp node-pre-gyp $STD npm ci @@ -304,23 +304,16 @@ cp LICENSE "$APP_DIR" msg_ok "Installed Immich Web Components" cd "$SRC_DIR"/machine-learning -$STD python3 -m venv "$ML_DIR/ml-venv" +export VIRTUAL_ENV="${ML_DIR}/ml-venv" +$STD uv venv "$VIRTUAL_ENV" if [[ -f ~/.openvino ]]; then msg_info "Installing HW-accelerated machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install uv - uv -q sync --extra openvino --no-cache --active - ) - patchelf --clear-execstack "$ML_DIR"/ml-venv/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so + uv -q sync --extra openvino --no-cache --active + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so" msg_ok "Installed HW-accelerated machine-learning" else msg_info "Installing machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install uv - uv -q sync --extra cpu --no-cache --active - ) + uv -q sync --extra cpu --no-cache --active msg_ok "Installed machine-learning" fi cd "$SRC_DIR" @@ -351,7 +344,9 @@ URL_LIST=( https://download.geonames.org/export/dump/cities500.zip https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson ) -echo "${URL_LIST[@]}" | xargs -n1 -P 8 wget -q +for geo in "${URL_LIST[@]}"; do + curl -fsSLO "$geo" +done unzip -q cities500.zip date --iso-8601=seconds | tr -d "\n" >geodata-date.txt rm cities500.zip @@ -361,7 +356,6 @@ msg_ok "Installed GeoNames data" mkdir -p /var/log/immich touch /var/log/immich/{web.log,ml.log} -echo "$RELEASE" >/opt/"${APPLICATION}"_version.txt msg_ok "Installed ${APPLICATION}" msg_info "Creating user, env file, scripts & services" @@ -390,13 +384,13 @@ cat <"${ML_DIR}"/ml_start.sh #!/usr/bin/env bash cd ${ML_DIR} -. ml-venv/bin/activate +. ${VIRTUAL_ENV}/bin/activate set -a . ${INSTALL_DIR}/.env set +a -python -m immich_ml +python3 -m immich_ml EOF chmod +x "$ML_DIR"/ml_start.sh cat </etc/systemd/system/"${APPLICATION}"-web.service @@ -453,7 +447,6 @@ motd_ssh customize msg_info "Cleaning up" -rm -f "$tmp_file" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" diff --git a/install/iobroker-install.sh b/install/iobroker-install.sh index 11674d6a0..7387d5255 100644 --- a/install/iobroker-install.sh +++ b/install/iobroker-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing ioBroker (Patience)" $STD bash <(curl -fsSL https://iobroker.net/install.sh) diff --git a/install/jellyseerr-install.sh b/install/jellyseerr-install.sh index b029e5a99..0618e10f5 100644 --- a/install/jellyseerr-install.sh +++ b/install/jellyseerr-install.sh @@ -21,14 +21,12 @@ $STD apt-get install -y \ gpg msg_ok "Installed Dependencies" - - git clone -q https://github.com/Fallenbagel/jellyseerr.git /opt/jellyseerr cd /opt/jellyseerr $STD git checkout main pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/jellyseerr/package.json) -NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs msg_info "Installing Jellyseerr (Patience)" export CYPRESS_INSTALL_BINARY=0 diff --git a/install/jupyternotebook-install.sh b/install/jupyternotebook-install.sh index 10619776c..a9a2eca8f 100644 --- a/install/jupyternotebook-install.sh +++ b/install/jupyternotebook-install.sh @@ -13,16 +13,19 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - python3 \ - python3-pip -rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED -msg_ok "Installed Dependencies" +PYTHON_VERSION="3.12" setup_uv -msg_info "Setting up Jupyter Notebook" -$STD pip3 install jupyter -msg_ok "Setup Jupyter Notebook" +msg_info "Installing Jupyter" +mkdir -p /opt/jupyter +cd /opt/jupyter +$STD uv venv /opt/jupyter/.venv +$STD /opt/jupyter/.venv/bin/python -m ensurepip --upgrade +$STD /opt/jupyter/.venv/bin/python -m pip install --upgrade pip +$STD /opt/jupyter/.venv/bin/python -m pip install jupyter +ln -s /opt/jupyter/.venv/bin/jupyter /usr/local/bin/jupyter +ln -s /opt/jupyter/.venv/bin/jupyter-lab /usr/local/bin/jupyter-lab +ln -s /opt/jupyter/.venv/bin/jupyter-notebook /usr/local/bin/jupyter-notebook +msg_ok "Installed Jupyter" msg_info "Creating Service" cat </etc/systemd/system/jupyternotebook.service @@ -32,7 +35,8 @@ After=network.target [Service] Type=simple -ExecStart=jupyter notebook --ip=0.0.0.0 --port=8888 --allow-root +WorkingDirectory=/opt/jupyter +ExecStart=/opt/jupyter/.venv/bin/jupyter notebook --ip=0.0.0.0 --port=8888 --allow-root Restart=always RestartSec=10 @@ -40,6 +44,7 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now jupyternotebook +msg_ok "Created Service" motd_ssh customize diff --git a/install/karakeep-install.sh b/install/karakeep-install.sh index fa0d6e4de..2101e44b6 100644 --- a/install/karakeep-install.sh +++ b/install/karakeep-install.sh @@ -48,7 +48,7 @@ sed -i \ /etc/meilisearch.toml msg_ok "Installed Meilisearch" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs $STD npm install -g corepack@0.31.0 msg_info "Installing karakeep" diff --git a/install/kasm-install.sh b/install/kasm-install.sh index b1d7913aa..9dda36c72 100644 --- a/install/kasm-install.sh +++ b/install/kasm-install.sh @@ -16,17 +16,25 @@ update_os msg_info "Installing Kasm Workspaces" KASM_VERSION=$(curl -fsSL 'https://www.kasmweb.com/downloads' | grep -o 'https://kasm-static-content.s3.amazonaws.com/kasm_release_[^"]*\.tar\.gz' | head -n 1 | sed -E 's/.*release_(.*)\.tar\.gz/\1/') curl -fsSL -o "/opt/kasm_release_${KASM_VERSION}.tar.gz" "https://kasm-static-content.s3.amazonaws.com/kasm_release_${KASM_VERSION}.tar.gz" -tar -xf "/opt/kasm_release_${KASM_VERSION}.tar.gz" -C /opt -printf 'y\ny\ny\n4\n' | bash /opt/kasm_release/install.sh -touch ~/kasm-install.output -sed -n '/Kasm UI Login Credentials/,$p' ~/kasm-install.output >~/kasm.creds + +cd /opt +tar -xf "kasm_release_${KASM_VERSION}.tar.gz" +chmod +x /opt/kasm_release/install.sh +printf 'y\ny\ny\n4\n' | bash /opt/kasm_release/install.sh > ~/kasm-install.output 2>&1 +cat ~/kasm-install.output | grep -A 20 -i "credentials\|login\|password\|admin" | sed '1i Kasm-Workspaces-Credentials' >~/kasm.creds + msg_ok "Installed Kasm Workspaces" motd_ssh customize +msg_info "Displaying Kasm Credentials" +cat ~/kasm.creds +msg_ok "Kasm Credentials displayed" + msg_info "Cleaning up" -$STD rm -f "/opt/kasm_release_${KASM_VERSION}.tar.gz" +rm -f /opt/kasm_release_${KASM_VERSION}.tar.gz +rm -f ~/kasm-install.output $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" diff --git a/install/kimai-install.sh b/install/kimai-install.sh index 1df625cba..d4462f4c0 100644 --- a/install/kimai-install.sh +++ b/install/kimai-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ lsb-release msg_ok "Installed Dependencies" -install_mysql +setup_mysql msg_info "Adding PHP8.4 Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/koillection-install.sh b/install/koillection-install.sh index 177dca51d..ca2bddd38 100644 --- a/install/koillection-install.sh +++ b/install/koillection-install.sh @@ -19,8 +19,8 @@ $STD apt-get install -y \ lsb-release msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setup PHP8.4 Repository" $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/libretranslate-install.sh b/install/libretranslate-install.sh new file mode 100644 index 000000000..8c64cc5a9 --- /dev/null +++ b/install/libretranslate-install.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: SlaviΕ‘a AreΕΎina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/LibreTranslate/LibreTranslate + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y --no-install-recommends \ + pkg-config \ + gcc \ + g++ \ + libicu-dev +msg_ok "Installed dependencies" + +msg_info "Setup Python3" +$STD apt-get install -y \ + python3-pip \ + python3-dev \ + python3-icu +msg_ok "Setup Python3" + +setup_uv +fetch_and_deploy_gh_release "libretranslate" "LibreTranslate/LibreTranslate" + +msg_info "Setup LibreTranslate (Patience)" +cd /opt/libretranslate +$STD uv venv .venv +$STD source .venv/bin/activate +$STD uv pip install --upgrade pip setuptools +$STD uv pip install Babel==2.12.1 +$STD .venv/bin/python scripts/compile_locales.py +$STD uv pip install torch==2.2.0 --extra-index-url https://download.pytorch.org/whl/cpu +$STD uv pip install "numpy<2" +$STD uv pip install . +$STD uv pip install libretranslate +$STD .venv/bin/python scripts/install_models.py + +cat </opt/libretranslate/.env +LT_PORT=5000 +EOF +msg_ok "Installed LibreTranslate" + +msg_info "Creating Service" +cat </etc/systemd/system/libretranslate.service +[Unit] +Description=LibreTranslate +After=network.target + +[Service] +User=root +Type=idle +Restart=always +Environment="PATH=/usr/local/lib/python3.11/dist-packages/libretranslate" +EnvironmentFile=/opt/libretranslate/.env +ExecStart=/opt/libretranslate/.venv/bin/python3 /opt/libretranslate/.venv/bin/libretranslate --host * --update-models +ExecReload=/bin/kill -s HUP +KillMode=mixed +TimeoutStopSec=1 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now libretranslate +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/linkwarden-install.sh b/install/linkwarden-install.sh index 42821232f..241c57f4f 100644 --- a/install/linkwarden-install.sh +++ b/install/linkwarden-install.sh @@ -19,9 +19,9 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql -RUST_CRATES="monolith" install_rust_and_crates +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql +RUST_CRATES="monolith" setup_rust msg_info "Setting up PostgreSQL DB" DB_NAME=linkwardendb @@ -48,7 +48,7 @@ if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then fi msg_info "Installing Linkwarden (Patience)" -fetch_and_deploy_gh_release "linkwarden/linkwarden" +fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" cd /opt/linkwarden $STD yarn $STD npx playwright install-deps diff --git a/install/mafl-install.sh b/install/mafl-install.sh index f219d5d42..c429ba917 100644 --- a/install/mafl-install.sh +++ b/install/mafl-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y gcc $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/hywax/mafl/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Installing Mafl v${RELEASE}" diff --git a/install/magicmirror-install.sh b/install/magicmirror-install.sh index f58109361..d4f21a008 100644 --- a/install/magicmirror-install.sh +++ b/install/magicmirror-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Setup MagicMirror" temp_file=$(mktemp) diff --git a/install/managemydamnlife-install.sh b/install/managemydamnlife-install.sh index 41f9f48f3..1c1b1ccc0 100644 --- a/install/managemydamnlife-install.sh +++ b/install/managemydamnlife-install.sh @@ -13,8 +13,8 @@ setting_up_container network_check update_os -NODE_VERSION="20" install_node_and_modules -MYSQL_VERSION="8.0" install_mysql +NODE_VERSION="20" setup_nodejs +MYSQL_VERSION="8.0" setup_mysql msg_info "Setting up Database" DB_NAME="mmdl" diff --git a/install/mariadb-install.sh b/install/mariadb-install.sh index 37c04778a..338a54e15 100644 --- a/install/mariadb-install.sh +++ b/install/mariadb-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -install_mariadb +setup_mariadb msg_info "Setup MariaDB" sed -i 's/^# *\(port *=.*\)/\1/' /etc/mysql/my.cnf diff --git a/install/matterbridge-install.sh b/install/matterbridge-install.sh index fb1aa9124..f1ec5a102 100644 --- a/install/matterbridge-install.sh +++ b/install/matterbridge-install.sh @@ -17,7 +17,7 @@ msg_info "Install Matterbridge" mkdir -p /root/Matterbridge NODE_VERSION="22" NODE_MODULE="matterbridge" -install_node_and_modules +setup_nodejs msg_ok "Installed Matterbridge" msg_info "Creating Service" diff --git a/install/mattermost-install.sh b/install/mattermost-install.sh index 0a2481055..6007e8735 100644 --- a/install/mattermost-install.sh +++ b/install/mattermost-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=mattermost diff --git a/install/meilisearch-install.sh b/install/meilisearch-install.sh index ff76d4f27..ecfc071d9 100644 --- a/install/meilisearch-install.sh +++ b/install/meilisearch-install.sh @@ -35,7 +35,7 @@ msg_ok "Setup ${APPLICATION}" read -r -p "${TAB3}Do you want add meilisearch-ui? [y/n]: " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Setup ${APPLICATION}-ui" tmp_file=$(mktemp) diff --git a/install/memos-install.sh b/install/memos-install.sh index b26ed4ea2..384db4d1a 100644 --- a/install/memos-install.sh +++ b/install/memos-install.sh @@ -21,8 +21,8 @@ $STD apt-get install -y \ tzdata msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules -install_go +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs +setup_go msg_info "Installing Memos (Patience)" mkdir -p /opt/memos_data diff --git a/install/meshcentral-install.sh b/install/meshcentral-install.sh index 8595006f0..e0c9aa187 100644 --- a/install/meshcentral-install.sh +++ b/install/meshcentral-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing MeshCentral" mkdir /opt/meshcentral diff --git a/install/metube-install.sh b/install/metube-install.sh index 32eff7af8..8df8a437d 100644 --- a/install/metube-install.sh +++ b/install/metube-install.sh @@ -37,7 +37,7 @@ $STD apt-get install -y \ rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing MeTube" $STD git clone https://github.com/alexta69/metube /opt/metube diff --git a/install/monica-install.sh b/install/monica-install.sh index 823150342..1f9ab9154 100644 --- a/install/monica-install.sh +++ b/install/monica-install.sh @@ -21,8 +21,8 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -install_mariadb -NODE_VERSION="20" NODE_MODULE="yarn@latest" install_node_and_modules +setup_mariadb +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setting up MariaDB" DB_NAME=monica diff --git a/install/myspeed-install.sh b/install/myspeed-install.sh index 40d1e600d..98b474e84 100644 --- a/install/myspeed-install.sh +++ b/install/myspeed-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing MySpeed" RELEASE=$(curl -fsSL https://github.com/gnmyt/myspeed/releases/latest | grep "title>Release" | cut -d " " -f 5) diff --git a/install/n8n-install.sh b/install/n8n-install.sh index 0417d5fb5..7ce1f2bc2 100644 --- a/install/n8n-install.sh +++ b/install/n8n-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing n8n (Patience)" $STD npm install --global patch-package diff --git a/install/netbox-install.sh b/install/netbox-install.sh index 6a3499c4c..1c9799774 100644 --- a/install/netbox-install.sh +++ b/install/netbox-install.sh @@ -26,7 +26,7 @@ $STD apt-get install -y \ zlib1g-dev msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Installing Python" $STD apt-get install -y \ diff --git a/install/node-red-install.sh b/install/node-red-install.sh index 335d26bfd..69a5965ce 100644 --- a/install/node-red-install.sh +++ b/install/node-red-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Node-Red" $STD npm install -g --unsafe-perm node-red diff --git a/install/nodebb-install.sh b/install/nodebb-install.sh index d4f1093ae..92c54c30d 100644 --- a/install/nodebb-install.sh +++ b/install/nodebb-install.sh @@ -21,8 +21,8 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -install_mongodb -NODE_VERSION="22" install_node_and_modules +setup_mongodb +NODE_VERSION="22" setup_nodejs msg_info "Configure MongoDB" MONGO_ADMIN_USER="admin" diff --git a/install/openwebui-install.sh b/install/openwebui-install.sh index 94e813c95..960c0e773 100644 --- a/install/openwebui-install.sh +++ b/install/openwebui-install.sh @@ -27,7 +27,7 @@ $STD apt-get install -y --no-install-recommends \ rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Open WebUI (Patience)" $STD git clone https://github.com/open-webui/open-webui.git /opt/open-webui diff --git a/install/outline-install.sh b/install/outline-install.sh index d733179f4..5715804b7 100644 --- a/install/outline-install.sh +++ b/install/outline-install.sh @@ -20,8 +20,8 @@ $STD apt-get install -y \ redis msg_ok "Installed Dependencies" -NODE_VERSION="20" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Set up PostgreSQL Database" DB_NAME="outline" diff --git a/install/overseerr-install.sh b/install/overseerr-install.sh index 3bc992b44..010016ae3 100644 --- a/install/overseerr-install.sh +++ b/install/overseerr-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Installing Overseerr (Patience)" git clone -q https://github.com/sct/overseerr.git /opt/overseerr diff --git a/install/pairdrop-install.sh b/install/pairdrop-install.sh index 088050eb9..be5212646 100644 --- a/install/pairdrop-install.sh +++ b/install/pairdrop-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing PairDrop" git clone -q https://github.com/schlagmichdoch/PairDrop.git /opt/pairdrop diff --git a/install/paperless-ai-install.sh b/install/paperless-ai-install.sh index f4972ef46..cb8fc4024 100644 --- a/install/paperless-ai-install.sh +++ b/install/paperless-ai-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ python3-pip msg_ok "Installed Python3" -install_node_and_modules +setup_nodejs msg_info "Setup Paperless-AI" cd /opt diff --git a/install/paperless-gpt-install.sh b/install/paperless-gpt-install.sh index 43b994229..a4e02062b 100644 --- a/install/paperless-gpt-install.sh +++ b/install/paperless-gpt-install.sh @@ -23,8 +23,8 @@ $STD apt-get install -y \ musl-tools msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules -install_go +NODE_VERSION="22" setup_nodejs +setup_go msg_info "Setup Paperless-GPT" temp_file=$(mktemp) diff --git a/install/paperless-ngx-install.sh b/install/paperless-ngx-install.sh index 1f23e9460..87cbff27c 100644 --- a/install/paperless-ngx-install.sh +++ b/install/paperless-ngx-install.sh @@ -35,7 +35,7 @@ $STD apt-get install -y \ libleptonica-dev msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Setup Python3" $STD apt-get install -y \ diff --git a/install/part-db-install.sh b/install/part-db-install.sh index 3fde07809..dae56a5b5 100644 --- a/install/part-db-install.sh +++ b/install/part-db-install.sh @@ -24,8 +24,8 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PHP" PHPVER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "\n";') diff --git a/install/paymenter-install.sh b/install/paymenter-install.sh index c36585341..7c6345870 100644 --- a/install/paymenter-install.sh +++ b/install/paymenter-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ redis-server msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Adding PHP Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/peanut-install.sh b/install/peanut-install.sh index f39c582d6..34aa192cc 100644 --- a/install/peanut-install.sh +++ b/install/peanut-install.sh @@ -14,7 +14,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing NUT" $STD apt-get install -y nut-client diff --git a/install/pelican-panel-install.sh b/install/pelican-panel-install.sh index c78013f25..f82db19f7 100644 --- a/install/pelican-panel-install.sh +++ b/install/pelican-panel-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Adding PHP8.4 Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/pf2etools-install.sh b/install/pf2etools-install.sh index ed1e7bbab..4b2f58e86 100644 --- a/install/pf2etools-install.sh +++ b/install/pf2etools-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Setup Pf2eTools" cd /opt diff --git a/install/phpipam-install.sh b/install/phpipam-install.sh index df903dea3..115a52649 100644 --- a/install/phpipam-install.sh +++ b/install/phpipam-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ php-pear msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up MariaDB" DB_NAME=phpipam diff --git a/install/pingvin-install.sh b/install/pingvin-install.sh index d85ecb859..c89c03060 100644 --- a/install/pingvin-install.sh +++ b/install/pingvin-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pm2" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pm2" setup_nodejs msg_info "Installing Pingvin Share (Patience)" cd /opt diff --git a/install/planka-install.sh b/install/planka-install.sh new file mode 100644 index 000000000..03f8b984e --- /dev/null +++ b/install/planka-install.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: SlaviΕ‘a AreΕΎina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/plankanban/planka + +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 \ + unzip \ + build-essential \ + python3-venv +msg_ok "Installed dependencies" + +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" setup_postgresql + +msg_info "Setting up PostgreSQL Database" +DB_NAME=planka +DB_USER=planka +DB_PASS=$(openssl rand -base64 16 | tr -d '/+=') +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +{ + echo "PLANKA DB Credentials" + echo "PLANKA Database User: $DB_USER" + echo "PLANKA Database Password: $DB_PASS" + echo "PLANKA Database Name: $DB_NAME" +} >>~/planka.creds +msg_ok "Set up PostgreSQL Database" + +fetch_and_deploy_gh_release "planka" "plankanban/planka" "prebuild" "latest" "/opt/planka" "planka-prebuild.zip" + +msg_info "Configuring PLANKA" +LOCAL_IP=$(hostname -I | awk '{print $1}') +SECRET_KEY=$(openssl rand -hex 64) +cd /opt/planka/planka +$STD npm install +cp .env.sample .env +sed -i "s#http://localhost:1337#http://$LOCAL_IP:1337#g" /opt/planka/planka/.env +sed -i "s#postgres@localhost#planka:$DB_PASS@localhost#g" /opt/planka/planka/.env +sed -i "s#notsecretkey#$SECRET_KEY#g" /opt/planka/planka/.env +$STD npm run db:init +msg_ok "Configured PLANKA" + +msg_info "Creating Admin User" +ADMIN_EMAIL="admin@planka.local" +ADMIN_PASSWORD="$(openssl rand -base64 12)" +ADMIN_NAME="Administrator" +ADMIN_USERNAME="admin" +echo "" >>.env +echo "# Temporary admin user creation settings" >>.env +echo "DEFAULT_ADMIN_EMAIL=$ADMIN_EMAIL" >>.env +echo "DEFAULT_ADMIN_PASSWORD=$ADMIN_PASSWORD" >>.env +echo "DEFAULT_ADMIN_NAME=$ADMIN_NAME" >>.env +echo "DEFAULT_ADMIN_USERNAME=$ADMIN_USERNAME" >>.env +$STD npm run db:seed +sed -i '/# Temporary admin user creation settings/,$d' .env +{ + echo "" + echo "PLANKA Admin Credentials" + echo "Admin Email: $ADMIN_EMAIL" + echo "Admin Password: $ADMIN_PASSWORD" + echo "Admin Name: $ADMIN_NAME" + echo "Admin Username: $ADMIN_USERNAME" +} >>~/planka.creds +msg_ok "Created Admin User" + +msg_info "Creating Service" +cat </etc/systemd/system/planka.service +[Unit] +Description=planka Service +After=network.target + +[Service] +WorkingDirectory=/opt/planka/planka +ExecStart=/usr/bin/npm start --prod +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now planka +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/plant-it-install.sh b/install/plant-it-install.sh index 99a012b25..73ebf1440 100644 --- a/install/plant-it-install.sh +++ b/install/plant-it-install.sh @@ -15,11 +15,11 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - redis \ - nginx + redis \ + nginx msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Adoptium Repository" mkdir -p /etc/apt/keyrings @@ -41,10 +41,10 @@ $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "Plant-it Credentials" - echo "Plant-it Database User: $DB_USER" - echo "Plant-it Database Password: $DB_PASS" - echo "Plant-it Database Name: $DB_NAME" + echo "Plant-it Credentials" + echo "Plant-it Database User: $DB_USER" + echo "Plant-it Database Password: $DB_PASS" + echo "Plant-it Database Name: $DB_NAME" } >>~/plant-it.creds msg_ok "Set up MariaDB" diff --git a/install/postgresql-install.sh b/install/postgresql-install.sh index e12462536..332bf7557 100644 --- a/install/postgresql-install.sh +++ b/install/postgresql-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PG_VERSION="17" install_postgresql +PG_VERSION="17" setup_postgresql cat </etc/postgresql/17/main/pg_hba.conf # PostgreSQL Client Authentication Configuration File diff --git a/install/projectsend-install.sh b/install/projectsend-install.sh index 54624323b..4703f460c 100644 --- a/install/projectsend-install.sh +++ b/install/projectsend-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ php8.2-{pdo,mysql,mbstring,gettext,fileinfo,gd,xml,zip} msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up MariaDB" DB_NAME=projectsend diff --git a/install/ps5-mqtt-install.sh b/install/ps5-mqtt-install.sh index 6aa49e2ac..5447fa0ad 100644 --- a/install/ps5-mqtt-install.sh +++ b/install/ps5-mqtt-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="playactor" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="playactor" setup_nodejs msg_info "Installing PS5-MQTT" RELEASE=$(curl -fsSL https://api.github.com/repos/FunkeyFlo/ps5-mqtt/releases/latest | jq -r '.tag_name') diff --git a/install/pterodactyl-panel-install.sh b/install/pterodactyl-panel-install.sh index a0c0608ef..419fd67d9 100644 --- a/install/pterodactyl-panel-install.sh +++ b/install/pterodactyl-panel-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Adding PHP8.4 Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/pulse-install.sh b/install/pulse-install.sh index 5fe6b016c..e9e65273e 100644 --- a/install/pulse-install.sh +++ b/install/pulse-install.sh @@ -28,7 +28,7 @@ else exit 1 fi -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Setup Pulse" RELEASE=$(curl -fsSL https://api.github.com/repos/rcourtman/Pulse/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') @@ -41,7 +41,6 @@ chown pulse:pulse /opt/pulse/.env echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Installed Pulse" - msg_info "Setting permissions for /opt/pulse..." chown -R pulse:pulse "/opt/pulse" find "/opt/pulse" -type d -exec chmod 755 {} \; diff --git a/install/reactive-resume-install.sh b/install/reactive-resume-install.sh index 87ba61cf8..f5a8473ea 100644 --- a/install/reactive-resume-install.sh +++ b/install/reactive-resume-install.sh @@ -19,8 +19,8 @@ curl -fsSL https://dl.min.io/server/minio/release/linux-arm64/minio.deb -o minio $STD dpkg -i minio.deb msg_ok "Installed Dependencies" -PG_VERSION="16" PG_MODULES="common" install_postgresql -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +PG_VERSION="16" PG_MODULES="common" setup_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Setting up Database" DB_USER="rxresume" diff --git a/install/revealjs-install.sh b/install/revealjs-install.sh index 3db939c3b..eb1665608 100644 --- a/install/revealjs-install.sh +++ b/install/revealjs-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Setup ${APPLICATION}" temp_file=$(mktemp) diff --git a/install/seelf-install.sh b/install/seelf-install.sh index 24bf5740c..174aa18ce 100644 --- a/install/seelf-install.sh +++ b/install/seelf-install.sh @@ -19,9 +19,9 @@ $STD apt-get install -y \ gcc msg_ok "Installed Dependencies" -install_go -NODE_VERSION="22" install_node_and_modules -fetch_and_deploy_gh_release "YuukanOO/seelf" +setup_go +NODE_VERSION="22" setup_nodejs +fetch_and_deploy_gh_release "seelf" "YuukanOO/seelf" msg_info "Setting up seelf. Patience" cd /opt/seelf diff --git a/install/sftpgo-install.sh b/install/sftpgo-install.sh index 2d0859fcb..91df27d3f 100644 --- a/install/sftpgo-install.sh +++ b/install/sftpgo-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y sqlite3 msg_ok "Installed Dependencies" -install_go +setup_go msg_info "Installing SFTPGo" curl -fsSL https://ftp.osuosl.org/pub/sftpgo/apt/gpg.key | gpg --dearmor -o /usr/share/keyrings/sftpgo-archive-keyring.gpg diff --git a/install/shinobi-install.sh b/install/shinobi-install.sh index 71d38182f..9b7329e93 100644 --- a/install/shinobi-install.sh +++ b/install/shinobi-install.sh @@ -19,8 +19,8 @@ $STD apt-get install -y gcc g++ cmake $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules -install_mariadb +NODE_VERSION="22" setup_nodejs +setup_mariadb msg_info "Installing FFMPEG" $STD apt-get install -y ffmpeg diff --git a/install/snipeit-install.sh b/install/snipeit-install.sh index 372375a07..98abcdacb 100644 --- a/install/snipeit-install.sh +++ b/install/snipeit-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ php8.2-{bcmath,common,ctype,curl,fileinfo,fpm,gd,iconv,intl,mbstring,mysql,soap,xml,xsl,zip,cli} msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up database" DB_NAME=snipeit_db diff --git a/install/streamlink-webui-install.sh b/install/streamlink-webui-install.sh index 2ab9fe6a2..3c61e36ad 100644 --- a/install/streamlink-webui-install.sh +++ b/install/streamlink-webui-install.sh @@ -16,9 +16,9 @@ update_os NODE_VERSION="22" NODE_MODULE="npm@latest,yarn@latest" -install_node_and_modules +setup_nodejs setup_uv -fetch_and_deploy_gh_release "CrazyWolf13/streamlink-webui" +fetch_and_deploy_gh_release "streamlink-webui" "CrazyWolf13/streamlink-webui" msg_info "Setup ${APPLICATION}" mkdir -p "/opt/${APPLICATION}-download" diff --git a/install/suwayomiserver-install.sh b/install/suwayomiserver-install.sh index 18d12aa43..ffe7485c1 100644 --- a/install/suwayomiserver-install.sh +++ b/install/suwayomiserver-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y libc++-dev msg_ok "Installed Dependencies" -JAVA_VERSION=21 install_java +JAVA_VERSION=21 setup_java msg_info "Settting up Suwayomi-Server" temp_file=$(mktemp) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index a79c187d0..70c11389d 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -42,7 +42,7 @@ $STD apt-get install -y \ rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" -NODE_VERSION="20" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Installing Tandoor (Patience)" $STD git clone https://github.com/TandoorRecipes/recipes -b master /opt/tandoor diff --git a/install/tasmocompiler-install.sh b/install/tasmocompiler-install.sh index 1ba241f0b..128ea9b0f 100644 --- a/install/tasmocompiler-install.sh +++ b/install/tasmocompiler-install.sh @@ -22,7 +22,7 @@ msg_info "Setup Python3" $STD apt-get install -y python3-venv msg_ok "Setup Python3" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setup Platformio" curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py diff --git a/install/the-lounge-install.sh b/install/the-lounge-install.sh index 28d7f0862..6758a30e8 100644 --- a/install/the-lounge-install.sh +++ b/install/the-lounge-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest,node-gyp" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest,node-gyp" setup_nodejs msg_info "Installing The Lounge" cd /opt diff --git a/install/tianji-install.sh b/install/tianji-install.sh index 9fb9c93b2..c08974238 100644 --- a/install/tianji-install.sh +++ b/install/tianji-install.sh @@ -26,8 +26,8 @@ $STD apt-get install -y \ jq msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/msgbyte/tianji/master/package.json | jq -r '.packageManager | split("@")[1]')" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/msgbyte/tianji/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=tianji_db diff --git a/install/umami-install.sh b/install/umami-install.sh index e9fa625f0..d4e9e29ac 100644 --- a/install/umami-install.sh +++ b/install/umami-install.sh @@ -17,8 +17,8 @@ msg_info "Installing Dependencies" $STD apt-get install -y git msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up postgresql" DB_NAME=umamidb diff --git a/install/uptimekuma-install.sh b/install/uptimekuma-install.sh index 84d47aab0..d28844d70 100644 --- a/install/uptimekuma-install.sh +++ b/install/uptimekuma-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y git $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Uptime Kuma" $STD git clone https://github.com/louislam/uptime-kuma.git diff --git a/install/wastebin-install.sh b/install/wastebin-install.sh index adef24d42..1e0419e44 100644 --- a/install/wastebin-install.sh +++ b/install/wastebin-install.sh @@ -13,6 +13,10 @@ setting_up_container network_check update_os +msg_info "Installing dependencies" +$STD apt-get install -y zstd +msg_ok "Installed dependencies" + msg_info "Installing Wastebin" temp_file=$(mktemp) RELEASE=$(curl -fsSL https://api.github.com/repos/matze/wastebin/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') diff --git a/install/watcharr-install.sh b/install/watcharr-install.sh index ae8219211..7e2b4c15d 100644 --- a/install/watcharr-install.sh +++ b/install/watcharr-install.sh @@ -18,8 +18,8 @@ $STD apt-get install -y \ gcc msg_ok "Installed Dependencies" -install_go -NODE_VERSION="22" install_node_and_modules +setup_go +NODE_VERSION="22" setup_nodejs msg_info "Setup Watcharr" temp_file=$(mktemp) diff --git a/install/wavelog-install.sh b/install/wavelog-install.sh index 8e2a36e44..1c65a2143 100644 --- a/install/wavelog-install.sh +++ b/install/wavelog-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ unzip msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Database" DB_NAME=wavelog diff --git a/install/web-check-install.sh b/install/web-check-install.sh index 6ad50f6cf..807ab31f2 100644 --- a/install/web-check-install.sh +++ b/install/web-check-install.sh @@ -35,7 +35,7 @@ $STD apt-get -y install --no-install-recommends \ x11-apps msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setup Python3" $STD apt-get install -y python3 diff --git a/install/wger-install.sh b/install/wger-install.sh index 69eccaabd..0134b0c3d 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -15,9 +15,9 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - git \ - apache2 \ - libapache2-mod-wsgi-py3 + git \ + apache2 \ + libapache2-mod-wsgi-py3 msg_ok "Installed Dependencies" msg_info "Installing Python" @@ -25,7 +25,7 @@ $STD apt-get install -y python3-pip rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Installed Python" -NODE_VERSION="22" NODE_MODULE="yarn@latest,sass" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest,sass" setup_nodejs msg_info "Setting up wger" $STD adduser wger --disabled-password --gecos "" diff --git a/install/wikijs-install.sh b/install/wikijs-install.sh index c718ccb0f..ba43aea21 100644 --- a/install/wikijs-install.sh +++ b/install/wikijs-install.sh @@ -24,8 +24,8 @@ $STD apt-get install -y \ make msg_ok "Installed Dependencies" -NODE_VERSION="20" NODE_MODULE="yarn@latest,node-gyp" install_node_and_modules -PG_VERSION="17" install_postgresql +NODE_VERSION="20" NODE_MODULE="yarn@latest,node-gyp" setup_nodejs +PG_VERSION="17" setup_postgresql msg_info "Set up PostgreSQL" DB_NAME="wiki" diff --git a/install/wizarr-install.sh b/install/wizarr-install.sh new file mode 100644 index 000000000..c9cae2a15 --- /dev/null +++ b/install/wizarr-install.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/wizarrrr/wizarr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y sqlite3 +msg_ok "Installed Dependencies" + +setup_uv +NODE_VERSION="22" setup_nodejs +fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr" + +msg_info "Configure ${APPLICATION}" +cd /opt/wizarr +uv -q sync --locked +$STD uv -q run pybabel compile -d app/translations +$STD npm --prefix app/static install +$STD npm --prefix app/static run build:css +mkdir -p ./.cache +$STD uv -q run flask db upgrade +msg_ok "Configure ${APPLICATION}" + +msg_info "Creating env, start script and service" +LOCAL_IP="$(hostname -I | awk '{print $1}')" +cat </opt/wizarr/.env +APP_URL=http://${LOCAL_IP} +DISABLE_BUILTIN_AUTH=false +LOG_LEVEL=INFO +EOF + +cat </opt/wizarr/start.sh +#!/usr/bin/env bash + +uv run gunicorn \ + --config gunicorn.conf.py \ + --preload \ + --workers 4 \ + --bind 0.0.0.0:5690 \ + --umask 007 \ + run:app +EOF +chmod u+x /opt/wizarr/start.sh + +cat </etc/systemd/system/wizarr.service +[Unit] +Description=${APPLICATION} Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/wizarr +EnvironmentFile=/opt/wizarr/.env +ExecStart=/opt/wizarr/start.sh +Restart=on-abnormal + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now wizarr +msg_ok "Created env, start script and service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/wordpress-install.sh b/install/wordpress-install.sh index dae1f307e..7f6c7aac2 100644 --- a/install/wordpress-install.sh +++ b/install/wordpress-install.sh @@ -22,7 +22,7 @@ $STD apt-get install -y \ mariadb-server msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Database" DB_NAME=wordpress_db diff --git a/install/zigbee2mqtt-install.sh b/install/zigbee2mqtt-install.sh index f2f3070f5..0e6ca49ff 100644 --- a/install/zigbee2mqtt-install.sh +++ b/install/zigbee2mqtt-install.sh @@ -22,7 +22,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Setting up Zigbee2MQTT" cd /opt diff --git a/install/zipline-install.sh b/install/zipline-install.sh index 4e124633b..be27bcf38 100644 --- a/install/zipline-install.sh +++ b/install/zipline-install.sh @@ -14,8 +14,8 @@ setting_up_container network_check update_os -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=ziplinedb diff --git a/install/zitadel-install.sh b/install/zitadel-install.sh index 805109853..20862b9da 100644 --- a/install/zitadel-install.sh +++ b/install/zitadel-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies (Patience)" $STD apt-get install -y ca-certificates msg_ok "Installed Dependecies" -PG_VERSION="17" PG_MODULES="common" install_postgresql +PG_VERSION="17" PG_MODULES="common" setup_postgresql msg_info "Installing Postgresql" DB_NAME="zitadel" diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 5ae3b4457..a2a804bf2 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -1,51 +1,13 @@ -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2025 community-scripts ORG # Author: tteck (tteckster) # License: MIT # https://github.com/asylumexp/Proxmox/raw/main/LICENSE -# This function sets color variables for formatting output in the terminal -color() { - # Colors - YW=$(echo "\033[33m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - GN=$(echo "\033[1;92m") - - # Formatting - CL=$(echo "\033[m") - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - TAB=" " - TAB3=" " - - # System - RETRY_NUM=10 - RETRY_EVERY=3 - i=$RETRY_NUM - - # Icons - CM="${TAB}βœ”οΈ${TAB}${CL}" - CROSS="${TAB}βœ–οΈ${TAB}${CL}" - INFO="${TAB}πŸ’‘${TAB}${CL}" - NETWORK="${TAB}πŸ“‘${TAB}${CL}" - OS="${TAB}πŸ–₯️${TAB}${CL}" - HOSTNAME="${TAB}🏠${TAB}${CL}" - GATEWAY="${TAB}🌐${TAB}${CL}" -} - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} +if ! command -v curl >/dev/null 2>&1; then + apk update && apk add curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +load_functions # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { @@ -73,33 +35,15 @@ error_handler() { echo -e "\n$error_message\n" } -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne " ${TAB}${YW}${msg}" -} - -# This function displays a success message with a green color. -msg_ok() { - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { msg_info "Setting up Container OS" - while [ "$i" -gt 0 ]; do + while [ $i -gt 0 ]; do if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep "$RETRY_EVERY" + sleep $RETRY_EVERY i=$((i - 1)) done @@ -148,10 +92,9 @@ update_os() { # This function modifies the message of the day (motd) and SSH settings motd_ssh() { - # Set terminal to 256-color mode echo "export TERM='xterm-256color'" >>/root/.bashrc IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - # Get OS information + 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 '"') @@ -212,6 +155,6 @@ EOF msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVE/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update } diff --git a/misc/api.func b/misc/api.func index b08c84f9e..c6809c1e1 100644 --- a/misc/api.func +++ b/misc/api.func @@ -4,7 +4,7 @@ post_to_api() { - if ! command -v curl &> /dev/null; then + if ! command -v curl &>/dev/null; then return fi @@ -20,7 +20,8 @@ post_to_api() { local pve_version="not found" pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') - JSON_PAYLOAD=$(cat < /dev/null; then - return - fi + if ! command -v curl &>/dev/null; then + return + fi - if [ "$POST_UPDATE_DONE" = true ]; then - return 0 - fi - local API_URL="http://api.community-scripts.org/upload/updatestatus" - local status="${1:-failed}" - local error="${2:-No error message}" + if [ "$POST_UPDATE_DONE" = true ]; then + return 0 + fi + local API_URL="http://api.community-scripts.org/upload/updatestatus" + local status="${1:-failed}" + local error="${2:-No error message}" - JSON_PAYLOAD=$(cat </dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via wget" +fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeuo pipefail @@ -70,7 +34,6 @@ catch_errors() { # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -80,78 +43,6 @@ error_handler() { echo -e "\n$error_message\n" } -# This function displays an informational message with logging support. -declare -A MSG_INFO_SHOWN -SPINNER_ACTIVE=0 -SPINNER_PID="" -SPINNER_MSG="" - -trap 'stop_spinner' EXIT INT TERM HUP - -start_spinner() { - local msg="$1" - local frames=(β ‹ β ™ β Ή β Έ β Ό β ΄ β ¦ β § β ‡ ⠏) - local spin_i=0 - local interval=0.1 - - SPINNER_MSG="$msg" - printf "\r\e[2K" >&2 - - { - while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do - printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${SPINNER_MSG}${CL}" >&2 - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done - } & - - SPINNER_PID=$! - disown "$SPINNER_PID" -} - -stop_spinner() { - if [[ ${SPINNER_PID+v} && -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then - kill "$SPINNER_PID" 2>/dev/null - sleep 0.1 - kill -0 "$SPINNER_PID" 2>/dev/null && kill -9 "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - fi - SPINNER_ACTIVE=0 - unset SPINNER_PID -} - -spinner_guard() { - if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "$SPINNER_PID" ]]; then - kill "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - SPINNER_ACTIVE=0 - unset SPINNER_PID - fi -} - -msg_info() { - local msg="$1" - [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return - MSG_INFO_SHOWN["$msg"]=1 - - spinner_guard - SPINNER_ACTIVE=1 - start_spinner "$msg" -} - -msg_ok() { - local msg="$1" - stop_spinner - printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2 - unset MSG_INFO_SHOWN["$msg"] -} - -msg_error() { - stop_spinner - local msg="$1" - printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2 -} - # Check if the shell is using bash shell_check() { if [[ "$(basename "$SHELL")" != "bash" ]]; then @@ -272,42 +163,6 @@ update_motd_ip() { fi } -# Function to download & save header files -get_header() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_url="https://raw.githubusercontent.com/asylumexp/Proxmox/main/ct/headers/${app_name}" - local local_header_path="/usr/local/community-scripts/headers/${app_name}" - - mkdir -p "$(dirname "$local_header_path")" - - if [ ! -s "$local_header_path" ]; then - if ! curl -fsSL "$header_url" -o "$local_header_path"; then - echo -e "Failed to download header for ${app_name}. No header will be displayed." - return 1 - fi - fi - - cat "$local_header_path" -} - -# This function sets the APP-Name into an ASCII Header in Slant, figlet needed on proxmox main node. -header_info() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_content - - # Download & save Header-File locally - header_content=$(get_header "$app_name") - if [ $? -ne 0 ]; then - # Fallback: Doesn't show Header - return 0 - fi - - # Show ASCII-Header - term_width=$(tput cols 2>/dev/null || echo 120) - clear - echo "$header_content" -} - # This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. ssh_check() { if [ -n "${SSH_CLIENT:+x}" ]; then @@ -345,8 +200,8 @@ base_settings() { SSH="no" SSH_AUTHORIZED_KEY="" TAGS="community-script;" - ENABLE_FUSE="no" - ENABLE_TUN="no" + ENABLE_FUSE="${1:-no}" + ENABLE_TUN="${1:-no}" # Override default settings with variables from ct script CT_TYPE=${var_unprivileged:-$CT_TYPE} @@ -369,7 +224,7 @@ base_settings() { write_config() { mkdir -p /opt/community-scripts # This function writes the configuration to a file. - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then FILEPATH="/opt/community-scripts/${NSAPP}.conf" if [[ ! -f $FILEPATH ]]; then cat <"$FILEPATH" @@ -402,7 +257,7 @@ EOF echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" else echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then rm -f "$FILEPATH" cat <"$FILEPATH" # ${NSAPP} Configuration File @@ -517,30 +372,38 @@ advanced_settings() { while true; do if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ ! -z "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit_script - fi - fi - else - PW1="Automatic Login" + # Empty = Autologin + if [[ -z "$PW1" ]]; then PW="" + PW1="Automatic Login" echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" break fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi else exit_script fi @@ -557,53 +420,72 @@ advanced_settings() { exit_script fi - if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" + while true; do + if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') + exit_script fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - exit_script - fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then if [ -z "$DISK_SIZE" ]; then DISK_SIZE="$var_disk" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - else - if ! [[ $DISK_SIZE =~ $INTEGER ]]; then - echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" - advanced_settings - fi - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" fi - else - exit_script - fi - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + if [ -z "$CORE_COUNT" ]; then CORE_COUNT="$var_cpu" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - else - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi - else - exit_script - fi - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + if [ -z "$RAM_SIZE" ]; then RAM_SIZE="$var_ram" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - else - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" fi - else - exit_script - fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done BRIDGES="" IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) @@ -778,14 +660,14 @@ advanced_settings() { exit_script fi - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then SSH_AUTHORIZED_KEY="" fi if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then SSH="yes" else SSH="no" @@ -804,11 +686,11 @@ advanced_settings() { echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERB="yes" + VERBOSE="yes" else - VERB="no" + VERBOSE="no" fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}" + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" @@ -832,7 +714,7 @@ diagnostics_check() { DIAGNOSTICS=yes #This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVE/discussions/1836 +#https://github.com/asylumexp/Proxmox/discussions/1836 #Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. #You can review the data at https://community-scripts.github.io/ProxmoxVE/data #If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. @@ -859,7 +741,7 @@ EOF DIAGNOSTICS=no #This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVE/discussions/1836 +#https://github.com/asylumexp/Proxmox/discussions/1836 #Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. #You can review the data at https://community-scripts.github.io/ProxmoxVE/data #If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. @@ -923,18 +805,18 @@ install_script() { 1) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERB="no" + VERBOSE="no" METHOD="default" - base_settings "$VERB" + base_settings "$VERBOSE" echo_default break ;; 2) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERB="yes" + VERBOSE="yes" METHOD="default" - base_settings "$VERB" + base_settings "$VERBOSE" echo_default break ;; @@ -950,7 +832,7 @@ install_script() { header_info echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" METHOD="config_file" - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/config-file.func) + source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/config-file.func) config_file break ;; @@ -1023,23 +905,17 @@ check_container_storage() { } start() { - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) + if ! command -v pveversion >/dev/null 2>&1; then + if [ -f /etc/debian_version ] || [ -f /etc/lsb-release ]; then + apt-get install -y whiptail &>/dev/null + elif [ -f /etc/alpine-release ]; then + apk add --no-cache whiptail &>/dev/null + fi + if command -v pveversion >/dev/null 2>&1; then - if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC" --yesno "This will create a New ${APP} LXC. Proceed?" 10 58); then - clear - exit_script - exit - fi - SPINNER_PID="" install_script - fi - - if ! command -v pveversion >/dev/null 2>&1; then - if [ -f /etc/debian_version ] || [ -f /etc/lsb-release ]; then - apt-get install -y whiptail &>/dev/null - elif [ -f /etc/alpine-release ]; then - apk add --no-cache whiptail &>/dev/null - fi + else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ "Support/Update functions for ${APP} LXC. Choose an option:" \ 12 60 3 \ @@ -1049,11 +925,11 @@ start() { case "$CHOICE" in 1) - VERB="no" + VERBOSE="no" set_std_mode ;; 2) - VERB="yes" + VERBOSE="yes" set_std_mode ;; 3) @@ -1062,15 +938,13 @@ start() { exit ;; esac - - SPINNER_PID="" update_script fi } # This function collects user settings and integrates all the collected information. build_container() { - # if [ "$VERB" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi if [ "$CT_TYPE" == "1" ]; then FEATURES="keyctl=1,nesting=1" @@ -1103,7 +977,7 @@ build_container() { export APPLICATION="$APP" export app="$NSAPP" export PASSWORD="$PW" - export VERBOSE="$VERB" + export VERBOSE="$VERBOSE" export SSH_ROOT="${SSH}" export SSH_AUTHORIZED_KEY export CTID="$CT_ID" @@ -1127,7 +1001,7 @@ build_container() { $PW " # This executes create_lxc.sh and creates the container and .conf file - bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/ct/create_lxc.sh)" $? + bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/create_lxc.sh)" $? LXC_CONFIG=/etc/pve/lxc/${CTID}.conf if [ "$CT_TYPE" == "0" ]; then @@ -1188,16 +1062,30 @@ EOF msg_info "Starting LXC Container" pct start "$CTID" msg_ok "Started LXC Container" + msg_info "Customizing LXC Container" if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' - pct exec "$CTID" -- ash -c "apk add bash >/dev/null" - fi - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/install/"$var_install".sh)" $? + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null" + else + sleep 3 + # Set locale and timezone before update + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + pct exec "$CTID" -- bash -c "echo $tz >/etc/timezone && ln -sf /usr/share/zoneinfo/$tz /etc/localtime" + + # Install curl + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" + fi + msg_ok "Customized LXC Container" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/install/"$var_install".sh)" $? } # This function sets the description of the container. @@ -1246,23 +1134,6 @@ EOF post_update_to_api "done" "none" } -set_std_mode() { - if [ "$VERB" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - if [ "$VERB" = "no" ]; then - "$@" >/dev/null 2>&1 || return 1 - else - "$@" || return 1 - fi -} - api_exit_script() { exit_code=$? # Capture the exit status of the last executed command #200 exit codes indicate error in create_lxc.sh diff --git a/misc/config-file.func b/misc/config-file.func index efaa2de82..401cd73cb 100644 --- a/misc/config-file.func +++ b/misc/config-file.func @@ -53,15 +53,15 @@ config_file() { fi else if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + else + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + fi else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + exit_script fi - else - exit_script - fi fi if [[ -n "${CT_TYPE-}" ]]; then @@ -75,23 +75,22 @@ config_file() { fi echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" else - if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" fi - else - exit_script + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi + else + exit_script + fi fi - if [[ -n "${PW-}" ]]; then if [[ "$PW" == "none" ]]; then PW="" @@ -109,50 +108,50 @@ config_file() { fi else while true; do - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ -n "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi + if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + if [[ -n "$PW1" ]]; then + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 + elif [ ${#PW1} -lt 5 ]; then + whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 else - exit_script + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi fi + else + PW1="Automatic Login" + PW="" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break fi else - PW1="Automatic Login" - PW="" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break + exit_script fi - else - exit_script - fi - done + done fi if [[ -n "${HN-}" ]]; then echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') + exit_script fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - exit_script - fi fi if [[ -n "${DISK_SIZE-}" ]]; then @@ -164,19 +163,19 @@ config_file() { fi else if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - else - if ! [[ $DISK_SIZE =~ $INTEGER ]]; then - echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" - advanced_settings + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + else + if ! [[ $DISK_SIZE =~ $INTEGER ]]; then + echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" + advanced_settings + fi + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" fi - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + else + exit_script fi - else - exit_script - fi fi if [[ -n "${CORE_COUNT-}" ]]; then @@ -188,15 +187,15 @@ config_file() { fi else if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi else - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + exit_script fi - else - exit_script - fi fi if [[ -n "${RAM_SIZE-}" ]]; then @@ -208,15 +207,15 @@ config_file() { fi else if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + fi else - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + exit_script fi - else - exit_script - fi fi IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) @@ -227,20 +226,20 @@ config_file() { for iface_filepath in ${IFACE_FILEPATH_LIST}; do iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - ( grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1 ) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' > "${iface_indexes_tmpfile}" || true + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi - done < "${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" fi done @@ -296,7 +295,7 @@ config_file() { done fi elif [[ "$NET" == *-* ]]; then - IFS="-" read -r ip_start ip_end <<< "$NET" + IFS="-" read -r ip_start ip_end <<<"$NET" if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then msg_error "Invalid IP range format, was $NET should be 0.0.0.0/0-0.0.0.0/0" @@ -309,21 +308,21 @@ config_file() { ip_to_int() { local IFS=. - read -r i1 i2 i3 i4 <<< "$1" - echo $(( (i1 << 24) + (i2 << 16) + (i3 << 8) + i4 )) + read -r i1 i2 i3 i4 <<<"$1" + echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) } int_to_ip() { local ip=$1 - echo "$(( (ip >> 24) & 0xFF )).$(( (ip >> 16) & 0xFF )).$(( (ip >> 8) & 0xFF )).$(( ip & 0xFF ))" + echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" } start_int=$(ip_to_int "$ip1") end_int=$(ip_to_int "$ip2") - for ((ip_int=start_int; ip_int<=end_int; ip_int++)); do + for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do ip=$(int_to_ip $ip_int) - msg_info "Checking IP: $ip" + msg_info "Checking IP: $ip" if ! ping -c 2 -W 1 "$ip" >/dev/null 2>&1; then NET="$ip/$cidr" msg_ok "Using free IP Address: ${BGN}$NET${CL}" @@ -363,37 +362,37 @@ config_file() { fi else while true; do - NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) - exit_status=$? - if [ $exit_status -eq 0 ]; then - if [ "$NET" = "dhcp" ]; then - echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" - break - else - if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) + exit_status=$? + if [ $exit_status -eq 0 ]; then + if [ "$NET" = "dhcp" ]; then echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" break else - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 + if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" + break + else + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 + fi fi + else + exit_script fi - else - exit_script - fi done if [ "$NET" != "dhcp" ]; then - while true; do - GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 - else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done + while true; do + GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done else GATE="" echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}" @@ -406,8 +405,8 @@ config_file() { else if [[ -n "${APT_CACHER_IP-}" ]]; then if [[ ! $APT_CACHER_IP == "none" ]]; then - APT_CACHER="yes" - echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" + APT_CACHER="yes" + echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" else APT_CACHER="" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}" @@ -416,7 +415,7 @@ config_file() { if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then APT_CACHER="${APT_CACHER_IP:+yes}" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - if [[ -n $APT_CACHER_IP ]]; then + if [[ -n $APT_CACHER_IP ]]; then APT_CACHER_IP="none" fi else @@ -506,7 +505,7 @@ config_file() { fi echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" else - exit_script + exit_script fi fi @@ -533,7 +532,7 @@ config_file() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else - exit_script + exit_script fi fi @@ -582,7 +581,7 @@ config_file() { fi echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" else - exit_script + exit_script fi fi @@ -646,7 +645,7 @@ config_file() { exit fi else - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then VERBOSE="yes" else VERBOSE="no" diff --git a/misc/core.func b/misc/core.func index cbabf2f03..1a904f9ab 100644 --- a/misc/core.func +++ b/misc/core.func @@ -46,15 +46,26 @@ load_functions() { on_error() { local exit_code="$1" local lineno="$2" - msg_error "Script failed at line $lineno with exit code $exit_code" - # Optionally log to your API or file here + + stop_spinner + + case "$exit_code" in + 1) msg_error "Generic error occurred (line $lineno)" ;; + 2) msg_error "Shell misuse (line $lineno)" ;; + 126) msg_error "Command cannot execute (line $lineno)" ;; + 127) msg_error "Command not found (line $lineno)" ;; + 128) msg_error "Invalid exit argument (line $lineno)" ;; + 130) msg_error "Script aborted by user (CTRL+C)" ;; + 143) msg_error "Script terminated by SIGTERM" ;; + *) msg_error "Script failed at line $lineno with exit code $exit_code" ;; + esac + exit "$exit_code" } on_exit() { - # Always called on script exit, success or failure - cleanup_temp_files || true - msg_info "Script exited" + cleanup_spinner || true + [[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited" } on_interrupt() { @@ -184,6 +195,7 @@ icons() { CREATING="${TAB}πŸš€${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" FUSE="${TAB}πŸ—‚οΈ${TAB}${CL}" + HOURGLASS="${TAB}⏳${TAB}" } # ------------------------------------------------------------------------------ @@ -429,8 +441,14 @@ msg_info() { local msg="$1" [[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return MSG_INFO_SHOWN["$msg"]=1 + stop_spinner - start_spinner "$msg" + + if [[ "${VERBOSE:-no}" == "no" && -t 2 ]]; then + start_spinner "$msg" + else + printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2 + fi } msg_ok() { diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh new file mode 100644 index 000000000..632e876f2 --- /dev/null +++ b/misc/create_lxc.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# Co-Author: MickLesk +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +# This sets verbose mode if the global variable is set to "yes" +# if [ "$VERBOSE" == "yes" ]; then set -x; fi + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(create-lxc.sh) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(create-lxc.sh) Loaded core.func via wget" +fi + +# This sets error handling options and defines the error_handler function to handle errors +set -Eeuo pipefail +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + +# This function handles errors +function error_handler() { + printf "\e[?25h" + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + echo -e "\n$error_message\n" + exit 200 +} + +# This checks for the presence of valid Container Storage and Template Storage locations +msg_info "Validating Storage" +VALIDCT=$(pvesm status -content rootdir | awk 'NR>1') +if [ -z "$VALIDCT" ]; then + msg_error "Unable to detect a valid Container Storage location." + exit 1 +fi +VALIDTMP=$(pvesm status -content vztmpl | awk 'NR>1') +if [ -z "$VALIDTMP" ]; then + msg_error "Unable to detect a valid Template Storage location." + exit 1 +fi + +# This function is used to select the storage class and determine the corresponding storage content type and label. +function select_storage() { + local CLASS=$1 + local CONTENT + local CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + *) false || { + msg_error "Invalid storage class." + exit 201 + } ;; + esac + + # Collect storage options + local -a MENU + local MSG_MAX_LENGTH=0 + + while read -r TAG TYPE _ _ _ FREE _; do + local TYPE_PADDED + local FREE_FMT + + TYPE_PADDED=$(printf "%-10s" "$TYPE") + FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.2f <<<"$FREE")B + local ITEM="Type: $TYPE_PADDED Free: $FREE_FMT" + + ((${#ITEM} + 2 > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + 2)) + + MENU+=("$TAG" "$ITEM" "OFF") + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + local OPTION_COUNT=$((${#MENU[@]} / 3)) + + # Auto-select if only one option available + if [[ "$OPTION_COUNT" -eq 1 ]]; then + echo "${MENU[0]}" + return 0 + fi + + # Display selection menu + local STORAGE + while [[ -z "${STORAGE:+x}" ]]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Select the storage pool to use for the ${CONTENT_LABEL,,}.\nUse the spacebar to make a selection.\n" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 \ + "${MENU[@]}" 3>&1 1>&2 2>&3) || { + msg_error "Storage selection cancelled." + exit 202 + } + done + + echo "$STORAGE" +} +# Test if required variables are set +[[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 +} +[[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 +} + +# Test if ID is valid +[ "$CTID" -ge "100" ] || { + msg_error "ID cannot be less than 100." + exit 205 +} + +# Test if ID is in use +if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 +fi + +# Get template storage +TEMPLATE_STORAGE=$(select_storage template) +msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage." + +# Get container storage +CONTAINER_STORAGE=$(select_storage container) +msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage." + +# Check free space on selected container storage +STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') +REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) +if [ "$STORAGE_FREE" -lt "$REQUIRED_KB" ]; then + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 +fi +# Check Cluster Quorum if in Cluster +if [ -f /etc/pve/corosync.conf ]; then + msg_info "Checking Proxmox cluster quorum status" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + printf "\e[?25h" + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" +fi + +# Update LXC template list +msg_info "Updating LXC Template List" + +if ! timeout 10 pveam update >/dev/null 2>&1; then + msg_error "Failed to update LXC template list. Please check your Proxmox host's internet connection and DNS resolution." + exit 201 +fi +$STD msg_ok "LXC Template List Updated" + +# Get LXC template string +TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" +mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V) + +if [ ${#TEMPLATES[@]} -eq 0 ]; then + msg_error "No matching LXC template found for '${TEMPLATE_SEARCH}'. Make sure your host can reach the Proxmox template repository." + exit 207 +fi + +TEMPLATE="${TEMPLATES[-1]}" +TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" + +# Check if template exists and is valid +if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then + msg_warn "Template $TEMPLATE not found or appears to be corrupted. Re-downloading." + + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading LXC template..." + + if timeout 120 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 your Proxmox host’s internet access or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 208 + fi + + sleep $((attempt * 5)) + done +fi + +msg_ok "LXC Template '$TEMPLATE' is ready to use." +# Check and fix subuid/subgid +grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid +grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + +# Combine all options +PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) +[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + +# Secure creation of the LXC container with lock and template check +lockfile="/tmp/template.${TEMPLATE}.lock" +exec 9>"$lockfile" +flock -w 60 9 || { + msg_error "Timeout while waiting for template lock" + exit 211 +} +msg_info "Creating LXC Container" +if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then + msg_error "Container creation failed. Checking if template is corrupted or incomplete." + + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_error "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then + msg_error "Template appears to be corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + else + msg_error "Template is valid, but container creation still failed." + exit 209 + fi + + # Retry download + for attempt in {1..3}; do + msg_info "Attempt $attempt: Re-downloading template..." + if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then + msg_ok "Template re-download successful." + break + fi + if [ "$attempt" -eq 3 ]; then + msg_error "Three failed attempts. Aborting." + exit 208 + fi + sleep $((attempt * 5)) + done + + sleep 1 # I/O-Sync-Delay + + msg_ok "Re-downloaded LXC Template" + + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then + msg_error "Container creation failed after re-downloading template." + exit 200 + fi +fi + +if ! pct status "$CTID" &>/dev/null; then + msg_error "Container not found after pct create – assuming failure." + exit 210 +fi + +msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." diff --git a/misc/install.func b/misc/install.func index 4b1ace5cb..cbeb8a473 100644 --- a/misc/install.func +++ b/misc/install.func @@ -3,53 +3,13 @@ # License: MIT # https://github.com/asylumexp/Proxmox/raw/main/LICENSE -# This function sets color variables for formatting output in the terminal -color() { - # Colors - YW=$(echo "\033[33m") - YWB=$(echo "\033[93m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - GN=$(echo "\033[1;92m") - - # Formatting - CL=$(echo "\033[m") - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - HOLD=" " - TAB=" " - TAB3=" " - - # System - RETRY_NUM=10 - RETRY_EVERY=3 - - # Icons - CM="${TAB}βœ”οΈ${TAB}${CL}" - CROSS="${TAB}βœ–οΈ${TAB}${CL}" - INFO="${TAB}πŸ’‘${TAB}${CL}" - NETWORK="${TAB}πŸ“‘${TAB}${CL}" - OS="${TAB}πŸ–₯️${TAB}${CL}" - OSVERSION="${TAB}🌟${TAB}${CL}" - HOSTNAME="${TAB}🏠${TAB}${CL}" - GATEWAY="${TAB}🌐${TAB}${CL}" - DEFAULT="${TAB}βš™οΈ${TAB}${CL}" -} - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} - +if ! command -v curl >/dev/null 2>&1; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +load_functions # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { set_std_mode # Set STD mode based on VERBOSE @@ -69,7 +29,6 @@ catch_errors() { # This function handles errors error_handler() { source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -84,62 +43,15 @@ error_handler() { fi } -# This function displays a spinner. -spinner() { - local frames=('β ‹' 'β ™' 'β Ή' 'β Έ' 'β Ό' 'β ΄' 'β ¦' 'β §' 'β ‡' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -# This function displays a success message with a green color. -msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { msg_info "Setting up Container OS" - sed -i "/$LANG/ s/\(^# \)//" /etc/locale.gen - locale_line=$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print $1}' | head -n 1) - echo "LANG=${locale_line}" >/etc/default/locale - locale-gen >/dev/null - export LANG=${locale_line} - echo "$tz" >/etc/timezone - ln -sf /usr/share/zoneinfo/"$tz" /etc/localtime for ((i = RETRY_NUM; i > 0; i--)); do if [ "$(hostname -I)" != "" ]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep "$RETRY_EVERY" + sleep $RETRY_EVERY done if [ "$(hostname -I)" = "" ]; then echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" @@ -149,7 +61,7 @@ setting_up_container() { rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED systemctl disable -q --now systemd-networkd-wait-online.service msg_ok "Set up Container OS" - msg_ok "Network Connected: ${BL}$(hostname -I)" + msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" } # This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected @@ -177,7 +89,7 @@ network_check() { # If both IPv4 and IPv6 checks fail, prompt the user if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then - read -r -p "No Internet detected,would you like to continue anyway? " prompt + 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 @@ -186,8 +98,26 @@ network_check() { fi fi - RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') - if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi + # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) + GITHUB_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com") + GITHUB_STATUS="GitHub DNS:" + DNS_FAILED=false + + for HOST in "${GITHUB_HOSTS[@]}"; do + RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) + if [[ -z "$RESOLVEDIP" ]]; then + GITHUB_STATUS+="$HOST:($DNSFAIL)" + DNS_FAILED=true + else + GITHUB_STATUS+=" $HOST:($DNSOK)" + fi + done + + if [[ "$DNS_FAILED" == true ]]; then + fatal "$GITHUB_STATUS" + else + msg_ok "$GITHUB_STATUS" + fi set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } @@ -215,8 +145,8 @@ EOF msg_info "Installing core dependencies" $STD apt-get update $STD apt-get install -y sudo curl mc gnupg2 openssh-server wget + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) - msg_ok "Core dependencies installed" } # This function modifies the message of the day (motd) and SSH settings diff --git a/misc/tools.func b/misc/tools.func index f636db10d..fd9eff384 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -12,7 +12,7 @@ # NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") # ------------------------------------------------------------------------------ -install_node_and_modules() { +function setup_nodejs() { local NODE_VERSION="${NODE_VERSION:-22}" local NODE_MODULE="${NODE_MODULE:-}" local CURRENT_NODE_VERSION="" @@ -22,20 +22,17 @@ install_node_and_modules() { if command -v node >/dev/null; then CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Node.js version $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" + msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" NEED_NODE_INSTALL=true - else - msg_ok "Node.js $NODE_VERSION already installed" fi else - msg_info "Node.js not found, installing version $NODE_VERSION" + msg_info "Setup Node.js $NODE_VERSION" NEED_NODE_INSTALL=true fi if ! command -v jq &>/dev/null; then - $STD msg_info "Installing jq..." - $STD apt-get update -qq &>/dev/null - $STD apt-get install -y jq &>/dev/null || { + $STD apt-get update + $STD apt-get install -y jq || { msg_error "Failed to install jq" return 1 } @@ -67,7 +64,7 @@ install_node_and_modules() { exit 1 fi - msg_ok "Installed Node.js ${NODE_VERSION}" + msg_ok "Setup Node.js ${NODE_VERSION}" fi export NODE_OPTIONS="--max-old-space-size=4096" @@ -106,8 +103,6 @@ install_node_and_modules() { msg_error "Failed to update $MODULE_NAME to latest version" exit 1 fi - else - msg_ok "$MODULE_NAME@$MODULE_INSTALLED_VERSION already installed" fi else msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" @@ -117,7 +112,7 @@ install_node_and_modules() { fi fi done - msg_ok "All requested Node modules have been processed" + msg_ok "Installed Node.js modules: $NODE_MODULE" fi } @@ -135,7 +130,7 @@ install_node_and_modules() { # PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) # PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") # ------------------------------------------------------------------------------ -install_postgresql() { +function setup_postgresql() { local PG_VERSION="${PG_VERSION:-16}" local PG_MODULES="${PG_MODULES:-}" local CURRENT_PG_VERSION="" @@ -146,7 +141,7 @@ install_postgresql() { if command -v psql >/dev/null; then CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then - msg_ok "PostgreSQL $PG_VERSION is already installed" + : # PostgreSQL is already at the desired version – no action needed else msg_info "Detected PostgreSQL $CURRENT_PG_VERSION, preparing upgrade to $PG_VERSION" NEED_PG_INSTALL=true @@ -158,39 +153,41 @@ install_postgresql() { if [[ "$NEED_PG_INSTALL" == true ]]; then if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Dumping all PostgreSQL data from version $CURRENT_PG_VERSION" + msg_info "Dumping PostgreSQL $CURRENT_PG_VERSION data" su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + msg_ok "Data dump completed" - msg_info "Stopping PostgreSQL service" systemctl stop postgresql fi - msg_info "Removing pgdg repo and old GPG key" rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg - msg_info "Adding PostgreSQL PGDG repository" + $STD msg_info "Adding PostgreSQL PGDG repository" curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ >/etc/apt/sources.list.d/pgdg.list + $STD msg_ok "Repository added" $STD apt-get update - msg_info "Installing PostgreSQL $PG_VERSION" + msg_info "Setup PostgreSQL $PG_VERSION" $STD apt-get install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" + msg_ok "Setup PostgreSQL $PG_VERSION" if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Removing old PostgreSQL $CURRENT_PG_VERSION packages" $STD apt-get purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true fi - msg_info "Starting PostgreSQL $PG_VERSION" + $STD msg_info "Starting PostgreSQL $PG_VERSION" systemctl enable -q --now postgresql + $STD msg_ok "PostgreSQL $PG_VERSION started" if [[ -n "$CURRENT_PG_VERSION" ]]; then msg_info "Restoring dumped data" su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + msg_ok "Data restored" fi msg_ok "PostgreSQL $PG_VERSION installed" @@ -201,13 +198,13 @@ install_postgresql() { IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do local pkg="postgresql-${PG_VERSION}-${module}" - msg_info "Installing PostgreSQL module: $pkg" + msg_info "Setup PostgreSQL module/s: $pkg" $STD apt-get install -y "$pkg" || { msg_error "Failed to install $pkg" continue } done - msg_ok "All requested PostgreSQL modules installed" + msg_ok "Setup PostgreSQL modules" fi } @@ -223,12 +220,13 @@ install_postgresql() { # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) # ------------------------------------------------------------------------------ -install_mariadb() { +setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" local DISTRO_CODENAME DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" + msg_info "Setting up MariaDB $MARIADB_VERSION" # grab dynamic latest LTS version if [[ "$MARIADB_VERSION" == "latest" ]]; then $STD msg_info "Resolving latest GA MariaDB version" @@ -259,12 +257,12 @@ install_mariadb() { fi if [[ -n "$CURRENT_VERSION" ]]; then - $STD msg_info "Replacing MariaDB $CURRENT_VERSION with $MARIADB_VERSION (data will be preserved)" + $STD msg_info "Upgrading MariaDB $CURRENT_VERSION to $MARIADB_VERSION" $STD systemctl stop mariadb >/dev/null 2>&1 || true $STD apt-get purge -y 'mariadb*' || true rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg else - msg_info "Setup MariaDB $MARIADB_VERSION" + $STD msg_info "Setup MariaDB $MARIADB_VERSION" fi $STD msg_info "Setting up MariaDB Repository" @@ -292,46 +290,47 @@ install_mariadb() { # MYSQL_VERSION - MySQL version to install (e.g. 5.7, 8.0) (default: 8.0) # ------------------------------------------------------------------------------ -install_mysql() { +function setup_mysql() { local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" local CURRENT_VERSION="" local NEED_INSTALL=false + CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" if command -v mysql >/dev/null; then CURRENT_VERSION="$(mysql --version | grep -oP 'Distrib\s+\K[0-9]+\.[0-9]+')" if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "MySQL $CURRENT_VERSION found, replacing with $MYSQL_VERSION" + $STD msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" NEED_INSTALL=true else # Check for patch-level updates if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - msg_info "MySQL $CURRENT_VERSION available for upgrade" + $STD msg_info "MySQL $CURRENT_VERSION available for upgrade" $STD apt-get update $STD apt-get install --only-upgrade -y mysql-server - msg_ok "MySQL upgraded" + $STD msg_ok "MySQL upgraded" fi return fi else - msg_info "Installing MySQL $MYSQL_VERSION" + msg_info "Setup MySQL $MYSQL_VERSION" NEED_INSTALL=true fi if [[ "$NEED_INSTALL" == true ]]; then - $STD systemctl stop mysql >/dev/null 2>&1 || true + $STD systemctl stop mysql || true $STD apt-get purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/trusted.gpg.d/mysql.gpg local DISTRO_CODENAME DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/trusted.gpg.d/mysql.gpg - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/debian/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/${CURRENT_OS}/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ >/etc/apt/sources.list.d/mysql.list export DEBIAN_FRONTEND=noninteractive $STD apt-get update $STD apt-get install -y mysql-server - msg_ok "Installed MySQL $MYSQL_VERSION" + msg_ok "Setup MySQL $MYSQL_VERSION" fi } @@ -354,7 +353,26 @@ install_mysql() { # PHP_MAX_EXECUTION_TIME - (default: 300) # ------------------------------------------------------------------------------ -install_php() { +# ------------------------------------------------------------------------------ +# Installs PHP with selected modules and configures Apache/FPM support. +# +# Description: +# - Adds Sury PHP repo if needed +# - Installs default and user-defined modules +# - Patches php.ini for CLI, Apache, and FPM as needed +# +# Variables: +# PHP_VERSION - PHP version to install (default: 8.4) +# PHP_MODULE - Additional comma-separated modules +# PHP_APACHE - Set YES to enable PHP with Apache +# PHP_FPM - Set YES to enable PHP-FPM +# PHP_MEMORY_LIMIT - (default: 512M) +# PHP_UPLOAD_MAX_FILESIZE - (default: 128M) +# PHP_POST_MAX_SIZE - (default: 128M) +# PHP_MAX_EXECUTION_TIME - (default: 300) +# ------------------------------------------------------------------------------ + +function setup_php() { local PHP_VERSION="${PHP_VERSION:-8.4}" local PHP_MODULE="${PHP_MODULE:-}" local PHP_APACHE="${PHP_APACHE:-NO}" @@ -380,51 +398,50 @@ install_php() { # Deduplicate modules COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - local CURRENT_PHP + local CURRENT_PHP="" if command -v php >/dev/null 2>&1; then CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - else - CURRENT_PHP="" fi if [[ -z "$CURRENT_PHP" ]]; then msg_info "Setup PHP $PHP_VERSION" elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION" - if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb - echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${DISTRO_CODENAME} main" \ - >/etc/apt/sources.list.d/php.list - $STD apt-get update - fi - + msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true fi + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${DISTRO_CODENAME} main" \ + >/etc/apt/sources.list.d/php.list + $STD apt-get update + fi + local MODULE_LIST="php${PHP_VERSION}" IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" for mod in "${MODULES[@]}"; do MODULE_LIST+=" php${PHP_VERSION}-${mod}" done + if [[ "$PHP_FPM" == "YES" ]]; then MODULE_LIST+=" php${PHP_VERSION}-fpm" fi - if [[ "$PHP_APACHE" == "YES" ]]; then - # Optionally disable old Apache PHP module + if [[ "$PHP_APACHE" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then $STD a2dismod php${CURRENT_PHP} || true fi fi - if [[ "$PHP_FPM" == "YES" ]]; then + if [[ "$PHP_FPM" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then $STD systemctl stop php${CURRENT_PHP}-fpm || true $STD systemctl disable php${CURRENT_PHP}-fpm || true fi $STD apt-get install -y $MODULE_LIST - msg_ok "Installed PHP $PHP_VERSION with selected modules" + msg_ok "Setup PHP $PHP_VERSION" if [[ "$PHP_APACHE" == "YES" ]]; then $STD systemctl restart apache2 || true @@ -436,8 +453,7 @@ install_php() { fi # Patch all relevant php.ini files - local PHP_INI_PATHS=() - PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/cli/php.ini") + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") @@ -461,7 +477,7 @@ install_php() { # - Installs to /usr/local/bin/composer # ------------------------------------------------------------------------------ -install_composer() { +function setup_composer() { local COMPOSER_BIN="/usr/local/bin/composer" export COMPOSER_ALLOW_SUPERUSER=1 @@ -469,7 +485,7 @@ install_composer() { if [[ -x "$COMPOSER_BIN" ]]; then local CURRENT_VERSION CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}') - $STD msg_info "Composer $CURRENT_VERSION found, updating to latest" + $STD msg_info "Old Composer $CURRENT_VERSION found, updating to latest" else msg_info "Setup Composer" fi @@ -486,7 +502,6 @@ install_composer() { chmod +x "$COMPOSER_BIN" composer diagnose >/dev/null 2>&1 msg_ok "Setup Composer" - #msg_ok "Installed Composer $($COMPOSER_BIN --version | awk '{print $3}')" } # ------------------------------------------------------------------------------ @@ -500,7 +515,7 @@ install_composer() { # GO_VERSION - Version to install (e.g. 1.22.2 or latest) # ------------------------------------------------------------------------------ -install_go() { +function setup_go() { local ARCH case "$(uname -m)" in x86_64) ARCH="amd64" ;; @@ -518,7 +533,6 @@ install_go() { msg_error "Could not determine latest Go version" return 1 fi - $STD msg_info "Detected latest Go version: $GO_VERSION" fi local GO_BIN="/usr/local/bin/go" @@ -528,14 +542,13 @@ install_go() { local CURRENT_VERSION CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then - $STD msg_ok "Go $GO_VERSION already installed" return 0 else - $STD msg_info "Go $CURRENT_VERSION found, upgrading to $GO_VERSION" + $STD msg_info "Old Go Installation ($CURRENT_VERSION) found, upgrading to $GO_VERSION" rm -rf "$GO_INSTALL_DIR" fi else - msg_info "Installing Go $GO_VERSION" + msg_info "Setup Go $GO_VERSION" fi local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" @@ -552,7 +565,7 @@ install_go() { ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt rm -f "$TMP_TAR" - msg_ok "Installed Go $GO_VERSION" + msg_ok "Setup Go $GO_VERSION" } # ------------------------------------------------------------------------------ @@ -566,7 +579,7 @@ install_go() { # JAVA_VERSION - Temurin JDK version to install (e.g. 17, 21) # ------------------------------------------------------------------------------ -install_java() { +function setup_java() { local JAVA_VERSION="${JAVA_VERSION:-21}" local DISTRO_CODENAME DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) @@ -574,13 +587,13 @@ install_java() { # Add Adoptium repo if missing if [[ ! -f /etc/apt/sources.list.d/adoptium.list ]]; then - msg_info "Setting up Adoptium Repository" + $STD msg_info "Setting up Adoptium Repository" mkdir -p /etc/apt/keyrings curl -fsSL "https://packages.adoptium.net/artifactory/api/gpg/key/public" | gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/adoptium.gpg] https://packages.adoptium.net/artifactory/deb ${DISTRO_CODENAME} main" \ >/etc/apt/sources.list.d/adoptium.list $STD apt-get update - msg_ok "Set up Adoptium Repository" + $STD msg_ok "Set up Adoptium Repository" fi # Detect currently installed temurin version @@ -590,19 +603,19 @@ install_java() { fi if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - msg_info "Temurin JDK $JAVA_VERSION already installed, updating if needed" + $STD msg_info "Upgrading Temurin JDK $JAVA_VERSION" $STD apt-get update $STD apt-get install --only-upgrade -y "$DESIRED_PACKAGE" - msg_ok "Updated Temurin JDK $JAVA_VERSION (if applicable)" + $STD msg_ok "Upgraded Temurin JDK $JAVA_VERSION" else if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Removing Temurin JDK $INSTALLED_VERSION" + $STD msg_info "Removing Temurin JDK $INSTALLED_VERSION" $STD apt-get purge -y "temurin-${INSTALLED_VERSION}-jdk" fi - msg_info "Installing Temurin JDK $JAVA_VERSION" + msg_info "Setup Temurin JDK $JAVA_VERSION" $STD apt-get install -y "$DESIRED_PACKAGE" - msg_ok "Installed Temurin JDK $JAVA_VERSION" + msg_ok "Setup Temurin JDK $JAVA_VERSION" fi } @@ -617,7 +630,7 @@ install_java() { # MONGO_VERSION - MongoDB major version to install (e.g. 7.0, 8.0) # ------------------------------------------------------------------------------ -install_mongodb() { +function setup_mongodb() { local MONGO_VERSION="${MONGO_VERSION:-8.0}" local DISTRO_ID DISTRO_CODENAME MONGO_BASE_URL DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) @@ -640,21 +653,20 @@ install_mongodb() { fi if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - msg_info "MongoDB $MONGO_VERSION already installed, checking for upgrade" + $STD msg_info "Upgrading MongoDB $MONGO_VERSION" $STD apt-get update $STD apt-get install --only-upgrade -y mongodb-org - msg_ok "MongoDB $MONGO_VERSION upgraded if needed" + $STD msg_ok "Upgraded MongoDB $MONGO_VERSION" return 0 fi if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Replacing MongoDB $INSTALLED_VERSION with $MONGO_VERSION (data will be preserved)" $STD systemctl stop mongod || true $STD apt-get purge -y mongodb-org || true rm -f /etc/apt/sources.list.d/mongodb-org-*.list rm -f /etc/apt/trusted.gpg.d/mongodb-*.gpg else - msg_info "Installing MongoDB $MONGO_VERSION" + msg_info "Setup MongoDB $MONGO_VERSION" fi curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg" @@ -673,177 +685,243 @@ install_mongodb() { $STD systemctl enable mongod $STD systemctl start mongod - msg_ok "MongoDB $MONGO_VERSION installed and started" + msg_ok "Setup MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ -# Downloads and deploys latest GitHub release tarball. +# Downloads and deploys latest GitHub release (source, binary, tarball, asset). # # Description: -# - Fetches latest release from GitHub API -# - Detects matching asset by architecture -# - Extracts to /opt/ and saves version +# - Fetches latest release metadata from GitHub API +# - Supports the following modes: +# - tarball: Source code tarball (default if omitted) +# - source: Alias for tarball (same behavior) +# - binary: .deb package install (arch-dependent) +# - prebuild: Prebuilt .tar.gz archive (e.g. Go binaries) +# - singlefile: Standalone binary (no archive, direct chmod +x install) +# - Handles download, extraction/installation and version tracking in ~/. # -# Variables: -# APP - Override default application name (optional) -# GITHUB_TOKEN - (optional) GitHub token for private rate limits +# Parameters: +# $1 APP - Application name (used for install path and version file) +# $2 REPO - GitHub repository in form user/repo +# $3 MODE - Release type: +# tarball β†’ source tarball (.tar.gz) +# binary β†’ .deb file (auto-arch matched) +# prebuild β†’ prebuilt archive (e.g. tar.gz) +# singlefileβ†’ standalone binary (chmod +x) +# $4 VERSION - Optional release tag (default: latest) +# $5 TARGET_DIR - Optional install path (default: /opt/) +# $6 ASSET_FILENAME - Required for: +# - prebuild β†’ archive filename or pattern +# - singlefileβ†’ binary filename or pattern +# +# Optional: +# - Set GITHUB_TOKEN env var to increase API rate limit (recommended for CI/CD). +# +# Examples: +# # 1. Minimal: Fetch and deploy source tarball +# fetch_and_deploy_gh_release "myapp" "myuser/myapp" +# +# # 2. Binary install via .deb asset (architecture auto-detected) +# fetch_and_deploy_gh_release "myapp" "myuser/myapp" "binary" +# +# # 3. Prebuilt archive (.tar.gz) with asset filename match +# fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "prebuild" "latest" "/opt/hanko" "hanko_Linux_x86_64.tar.gz" +# +# # 4. Single binary (chmod +x) like Argus, Promtail etc. +# fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "0.26.3" "/opt/argus" "Argus-.*linux-amd64" # ------------------------------------------------------------------------------ -fetch_and_deploy_gh_release() { - local repo="$1" - local raw_app="${APP:-$APPLICATION}" - local app=$(echo "${raw_app,,}" | tr -d ' ') - local api_url="https://api.github.com/repos/$repo/releases/latest" - local header=() - local attempt=0 - local max_attempts=3 - local api_response tag http_code - local current_version="" +function fetch_and_deploy_gh_release() { + local app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local version="${4:-latest}" + local target="${5:-/opt/$app}" + + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}" local curl_timeout="--connect-timeout 10 --max-time 30" - # Check if the app directory exists and if there's a version file - if [[ -f "/opt/${app}_version.txt" ]]; then - current_version=$(cat "/opt/${app}_version.txt") - $STD msg_info "Current version: $current_version" + + local current_version="" + if [[ -f "$version_file" ]]; then + current_version=$(<"$version_file") fi - # ensure that jq is installed + if ! command -v jq &>/dev/null; then - $STD msg_info "Installing jq..." - $STD apt-get update -qq &>/dev/null - $STD apt-get install -y jq &>/dev/null || { - msg_error "Failed to install jq" - return 1 - } + $STD apt-get install -y jq &>/dev/null fi + + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") - until [[ $attempt -ge $max_attempts ]]; do - ((attempt++)) || true - $STD msg_info "[$attempt/$max_attempts] Fetching GitHub release for $repo...\n" - api_response=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_resp.json "${header[@]}" "$api_url") - http_code="${api_response:(-3)}" - if [[ "$http_code" == "404" ]]; then - msg_error "Repository $repo has no Release candidate (404)" - return 1 - fi - if [[ "$http_code" != "200" ]]; then - $STD msg_info "Request failed with HTTP $http_code, retrying...\n" - sleep $((attempt * 2)) - continue - fi - api_response=$(/dev/null; then - msg_error "Repository not found: $repo" - return 1 - fi - tag=$(echo "$api_response" | jq -r '.tag_name // .name // empty') - [[ "$tag" =~ ^v[0-9] ]] && tag="${tag:1}" - version="${tag#v}" - if [[ -z "$tag" ]]; then - $STD msg_info "Empty tag received, retrying...\n" - sleep $((attempt * 2)) - continue - fi - $STD msg_ok "Found release: $tag for $repo" - break - done - if [[ -z "$tag" ]]; then - msg_error "Failed to fetch release for $repo after $max_attempts attempts." - exit 1 - fi - # Version comparison (if we already have this version, skip) - if [[ "$current_version" == "$tag" ]]; then - $STD msg_info "Already running the latest version ($tag). Skipping update." + + local resp http_code + resp=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") + http_code="${resp:(-3)}" + [[ "$http_code" != "200" ]] && { + msg_error "Failed to fetch release: HTTP $http_code" + return 1 + } + + local json tag_name + json=$(/dev/null; then - arch=$(dpkg --print-architecture) - elif command -v uname &>/dev/null; then - case "$(uname -m)" in - x86_64) arch="amd64" ;; - aarch64) arch="arm64" ;; - armv7l) arch="armv7" ;; - armv6l) arch="armv6" ;; - *) arch="unknown" ;; - esac + local filename="" url="" + + msg_info "Setup $app ($version)" + + if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then + url=$(echo "$json" | jq -r '.tarball_url // empty') + [[ -z "$url" ]] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" + filename="${app_lc}-${version}.tar.gz" + + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url" || { + msg_error "Download failed: $url" + rm -rf "$tmpdir" + return 1 + } + + mkdir -p "$target" + tar -xzf "$tmpdir/$filename" -C "$tmpdir" + local unpack_dir + unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) + + shopt -s dotglob nullglob + cp -r "$unpack_dir"/* "$target/" + shopt -u dotglob nullglob + + elif [[ "$mode" == "binary" ]]; then + local arch + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="x86_64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + + local assets url_match="" + assets=$(echo "$json" | jq -r '.assets[].browser_download_url') + + for u in $assets; do + if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + url_match="$u" + break + fi + done + + if [[ -z "$url_match" ]]; then + for u in $assets; do + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + done + fi + + if [[ -z "$url_match" ]]; then + msg_error "No suitable .deb asset found for $app" + rm -rf "$tmpdir" + return 1 + fi + + filename="${url_match##*/}" + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } + + chmod 644 "$tmpdir/$filename" + $STD apt-get install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" + rm -rf "$tmpdir" + return 1 + } + } + + elif [[ "$mode" == "prebuild" ]]; then + local pattern="$6" + [[ -z "$pattern" ]] && { + msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + [[ "$u" =~ $pattern || "$u" == *"$pattern" ]] && asset_url="$u" && break + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + mkdir -p "$target" + if [[ "$filename" == *.zip ]]; then + if ! command -v unzip &>/dev/null; then + $STD apt-get install -y unzip + fi + $STD unzip "$tmpdir/$filename" -d "$target" + elif [[ "$filename" == *.tar.gz ]]; then + tar -xzf "$tmpdir/$filename" -C "$target" + else + msg_error "Unsupported archive format: $filename" + rm -rf "$tmpdir" + return 1 + fi + + elif [[ "$mode" == "singlefile" ]]; then + local pattern="$6" + [[ -z "$pattern" ]] && { + msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + [[ "$u" =~ $pattern || "$u" == *"$pattern" ]] && asset_url="$u" && break + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + mkdir -p "$target" + curl $curl_timeout -fsSL -o "$target/$app" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + chmod +x "$target/$app" + else - arch="unknown" - fi - $STD msg_info "Detected system architecture: $arch" - # Try to find a matching asset for our architecture - local url="" - for u in $assets; do - if [[ "$u" =~ $arch.*\.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Found matching architecture asset: $url" - break - fi - done - # Fallback to other architectures if our specific one isn't found - if [[ -z "$url" ]]; then - for u in $assets; do - if [[ "$u" =~ (x86_64|amd64|arm64|armv7|armv6).*\.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Architecture-specific asset not found, using: $url" - break - fi - done - fi - # Fallback to any tar.gz - if [[ -z "$url" ]]; then - for u in $assets; do - if [[ "$u" =~ \.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Using generic tarball: $url" - break - fi - done - fi - # Final fallback to GitHub source tarball - if [[ -z "$url" ]]; then - # Use tarball_url directly from API response instead of constructing our own URL - url=$(echo "$api_response" | jq -r '.tarball_url // empty') - - # If tarball_url is empty for some reason, fall back to a constructed URL as before - if [[ -z "$url" ]]; then - url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" - fi - - $STD msg_info "Using GitHub source tarball: $url" - fi - local filename="${url##*/}" - $STD msg_info "Downloading $url" - if ! curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url"; then - msg_error "Failed to download release asset from $url" + msg_error "Unknown mode: $mode" rm -rf "$tmpdir" return 1 fi - mkdir -p "/opt/$app" - tar -xzf "$tmpdir/$filename" -C "$tmpdir" - local content_root - content_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d) - if [[ $(echo "$content_root" | wc -l) -eq 1 ]]; then - shopt -s dotglob nullglob - cp -r "$content_root"/* "/opt/$app/" - shopt -u dotglob nullglob - else - shopt -s dotglob nullglob - cp -r "$tmpdir"/* "/opt/$app/" - shopt -u dotglob nullglob - fi - echo "$version" >"/opt/${app}_version.txt" - $STD msg_ok "Deployed $app v$version to /opt/$app" + + echo "$version" >"$version_file" + msg_ok "Setup $app ($version)" rm -rf "$tmpdir" } @@ -855,7 +933,7 @@ fetch_and_deploy_gh_release() { # - Automatically runs on network changes # ------------------------------------------------------------------------------ -setup_local_ip_helper() { +function setup_local_ip_helper() { local BASE_DIR="/usr/local/community-scripts/ip-management" local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" local IP_FILE="/run/local-ip.env" @@ -865,8 +943,8 @@ setup_local_ip_helper() { # Install networkd-dispatcher if not present if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt-get update -qq - $STD apt-get install -yq networkd-dispatcher + $STD apt-get update + $STD apt-get install -y networkd-dispatcher fi # Write update_local_ip.sh @@ -923,8 +1001,6 @@ EOF chmod +x "$DISPATCHER_SCRIPT" systemctl enable -q --now networkd-dispatcher.service - - $STD msg_ok "LOCAL_IP helper installed using networkd-dispatcher" } # ------------------------------------------------------------------------------ @@ -934,7 +1010,7 @@ EOF # - Loads from /run/local-ip.env or performs runtime lookup # ------------------------------------------------------------------------------ -import_local_ip() { +function import_local_ip() { local IP_FILE="/run/local-ip.env" if [[ -f "$IP_FILE" ]]; then # shellcheck disable=SC1090 @@ -994,7 +1070,6 @@ function download_with_progress() { content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) if [[ -z "$content_length" ]]; then - #msg_warn "Content-Length not available, falling back to plain download" if ! curl -fL# -o "$output" "$url"; then msg_error "Download failed" return 1 @@ -1009,63 +1084,102 @@ function download_with_progress() { # ------------------------------------------------------------------------------ # Installs or upgrades uv (Python package manager) from GitHub releases. -# -# Description: -# - Downloads architecture-specific tarball -# - Places binary in /usr/local/bin +# - Downloads platform-specific tarball (no install.sh!) +# - Extracts uv binary +# - Places it in /usr/local/bin +# - Optionally installs a specific Python version via uv # ------------------------------------------------------------------------------ function setup_uv() { - $STD msg_info "Checking uv installation..." - UV_BIN="/usr/local/bin/uv" + local UV_BIN="/usr/local/bin/uv" + local TMP_DIR TMP_DIR=$(mktemp -d) - ARCH=$(uname -m) - if [[ "$ARCH" == "x86_64" ]]; then - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - elif [[ "$ARCH" == "aarch64" ]]; then - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - else + # Determine system architecture + local ARCH + ARCH=$(uname -m) + local UV_TAR + + case "$ARCH" in + x86_64) UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" ;; + aarch64) UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" ;; + *) msg_error "Unsupported architecture: $ARCH" rm -rf "$TMP_DIR" return 1 - fi + ;; + esac + + # Get latest version from GitHub + local LATEST_VERSION + LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | + grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') - # get current github version - LATEST_VERSION=$(curl -s https://api.github.com/repos/astral-sh/uv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') if [[ -z "$LATEST_VERSION" ]]; then msg_error "Could not fetch latest uv version from GitHub." rm -rf "$TMP_DIR" return 1 fi - # check if uv exists + # Check if uv is already up to date if [[ -x "$UV_BIN" ]]; then + local INSTALLED_VERSION INSTALLED_VERSION=$($UV_BIN -V | awk '{print $2}') if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - $STD msg_ok "uv is already at the latest version ($INSTALLED_VERSION)" rm -rf "$TMP_DIR" - # set path - if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then - export PATH="/usr/local/bin:$PATH" - fi + [[ ":$PATH:" != *":/usr/local/bin:"* ]] && export PATH="/usr/local/bin:$PATH" return 0 else - $STD msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" + msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" fi else - $STD msg_info "uv not found. Installing version $LATEST_VERSION" + msg_info "Setup uv $LATEST_VERSION" fi - # install or update uv - curl -fsSL "https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" -o "$TMP_DIR/uv.tar.gz" - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" - rm -rf "$TMP_DIR" + # Download and install manually + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" + if ! curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz"; then + msg_error "Failed to download $UV_URL" + rm -rf "$TMP_DIR" + return 1 + fi - # set path + if ! tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract uv archive" + rm -rf "$TMP_DIR" + return 1 + fi + + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + msg_error "Failed to install uv binary" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" ensure_usr_local_bin_persist - msg_ok "uv installed/updated to $LATEST_VERSION" + msg_ok "Setup uv $LATEST_VERSION" + + # Optional: install specific Python version + if [[ -n "${PYTHON_VERSION:-}" ]]; then + local VERSION_MATCH + VERSION_MATCH=$(uv python list --only-downloads | + grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | + cut -d'-' -f2 | sort -V | tail -n1) + + if [[ -z "$VERSION_MATCH" ]]; then + msg_error "No matching Python $PYTHON_VERSION.x version found via uv" + return 1 + fi + + if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + if ! $STD uv python install "$VERSION_MATCH"; then + msg_error "Failed to install Python $VERSION_MATCH via uv" + return 1 + fi + msg_ok "Setup Python $VERSION_MATCH via uv" + fi + fi } # ------------------------------------------------------------------------------ @@ -1093,7 +1207,6 @@ function ensure_usr_local_bin_persist() { # ------------------------------------------------------------------------------ function setup_gs() { - msg_info "Setup Ghostscript" mkdir -p /tmp TMP_DIR=$(mktemp -d) CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") @@ -1109,12 +1222,11 @@ function setup_gs() { fi if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED"; then - msg_ok "Ghostscript is already at version $CURRENT_VERSION" rm -rf "$TMP_DIR" return fi - msg_info "Installing/Updating Ghostscript to $LATEST_VERSION_DOTTED" + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then @@ -1140,7 +1252,7 @@ function setup_gs() { rm -rf "$TMP_DIR" if [[ $EXIT_CODE -eq 0 ]]; then - msg_ok "Ghostscript installed/updated to version $LATEST_VERSION_DOTTED" + msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" else msg_error "Ghostscript installation failed" fi @@ -1159,7 +1271,7 @@ function setup_gs() { # RUBY_INSTALL_RAILS - true/false to install Rails (default: true) # ------------------------------------------------------------------------------ -setup_rbenv_stack() { +function setup_ruby() { local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" @@ -1169,9 +1281,8 @@ setup_rbenv_stack() { local TMP_DIR TMP_DIR=$(mktemp -d) - $STD msg_info "Installing rbenv + ruby-build + Ruby $RUBY_VERSION" + msg_info "Setup Ruby $RUBY_VERSION" - # Fetch latest rbenv release tag from GitHub (e.g. v1.3.2 β†’ 1.3.2) local RBENV_RELEASE RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') if [[ -z "$RBENV_RELEASE" ]]; then @@ -1180,14 +1291,12 @@ setup_rbenv_stack() { return 1 fi - # Download and extract rbenv release curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" mkdir -p "$RBENV_DIR" cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - cd "$RBENV_DIR" && src/configure && make -C src + cd "$RBENV_DIR" && src/configure && $STD make -C src - # Fetch latest ruby-build plugin release tag (e.g. v20250507 β†’ 20250507) local RUBY_BUILD_RELEASE RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') if [[ -z "$RUBY_BUILD_RELEASE" ]]; then @@ -1196,44 +1305,35 @@ setup_rbenv_stack() { return 1 fi - # Download and install ruby-build plugin curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" mkdir -p "$RBENV_DIR/plugins/ruby-build" cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" - # Persist rbenv init to user's profile if ! grep -q 'rbenv init' "$PROFILE_FILE"; then echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" fi - # Activate rbenv in current shell export PATH="$RBENV_DIR/bin:$PATH" eval "$("$RBENV_BIN" init - bash)" - # Install Ruby version if not already present - if "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then - msg_ok "Ruby $RUBY_VERSION already installed" - else - $STD msg_info "Installing Ruby $RUBY_VERSION" + if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then $STD "$RBENV_BIN" install "$RUBY_VERSION" fi - # Set Ruby version globally "$RBENV_BIN" global "$RUBY_VERSION" hash -r - # Optionally install Rails via gem if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - $STD msg_info "Installing latest Rails via gem" + msg_info "Setup Rails via gem" gem install rails - msg_ok "Rails $(rails -v) installed" + msg_ok "Setup Rails $(rails -v)" fi rm -rf "$TMP_DIR" - msg_ok "rbenv stack ready (Ruby $RUBY_VERSION)" + msg_ok "Setup Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ @@ -1245,14 +1345,12 @@ setup_rbenv_stack() { # Variables: # APP - Application name (default: $APPLICATION variable) # ------------------------------------------------------------------------------ -create_selfsigned_certs() { +function create_selfsigned_certs() { local app=${APP:-$(echo "${APPLICATION,,}" | tr -d ' ')} - $STD msg_info "Creating Self-Signed Certificate" $STD openssl req -x509 -nodes -days 365 -newkey rsa:4096 \ -keyout /etc/ssl/private/"$app"-selfsigned.key \ -out /etc/ssl/certs/"$app"-selfsigned.crt \ -subj "/C=US/O=$app/OU=Domain Control Validated/CN=localhost" - $STD msg_ok "Created Self-Signed Certificate" } # ------------------------------------------------------------------------------ @@ -1272,18 +1370,18 @@ create_selfsigned_certs() { # RUST_CRATES - Comma-separated list of crates (e.g. "cargo-edit,wasm-pack@0.12.1") # ------------------------------------------------------------------------------ -install_rust_and_crates() { +function setup_rust() { local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" local RUST_CRATES="${RUST_CRATES:-}" local CARGO_BIN="${HOME}/.cargo/bin" # rustup & toolchain if ! command -v rustup &>/dev/null; then - msg_info "Installing rustup" + msg_info "Setup Rust" curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" export PATH="$CARGO_BIN:$PATH" echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" - msg_ok "Installed rustup with $RUST_TOOLCHAIN" + msg_ok "Setup Rust" else $STD rustup install "$RUST_TOOLCHAIN" $STD rustup default "$RUST_TOOLCHAIN" @@ -1308,20 +1406,21 @@ install_rust_and_crates() { if [[ -n "$INSTALLED_VER" ]]; then if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - msg_info "Updating $NAME from $INSTALLED_VER to $VER" + msg_info "Update $NAME: $INSTALLED_VER β†’ $VER" $STD cargo install "$NAME" --version "$VER" --force + msg_ok "Updated $NAME to $VER" elif [[ -z "$VER" ]]; then - msg_info "Updating $NAME to latest" + msg_info "Update $NAME: $INSTALLED_VER β†’ latest" $STD cargo install "$NAME" --force - else - msg_ok "$NAME@$INSTALLED_VER already up to date" + msg_ok "Updated $NAME to latest" fi else - msg_info "Installing $NAME ${VER:+($VER)}" + msg_info "Setup $NAME ${VER:+($VER)}" $STD cargo install "$NAME" ${VER:+--version "$VER"} + msg_ok "Setup $NAME ${VER:-latest}" fi done - msg_ok "All requested Rust crates processed" + msg_ok "Setup Rust" fi } @@ -1333,9 +1432,9 @@ install_rust_and_crates() { # - Supports Alpine and Debian-based systems # ------------------------------------------------------------------------------ -install_adminer() { +function setup_adminer() { if grep -qi alpine /etc/os-release; then - msg_info "Installing Adminer (Alpine)" + msg_info "Setup Adminer (Alpine)" mkdir -p /var/www/localhost/htdocs/adminer if ! curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ -o /var/www/localhost/htdocs/adminer/index.php; then @@ -1344,10 +1443,81 @@ install_adminer() { fi msg_ok "Adminer available at /adminer (Alpine)" else - msg_info "Installing Adminer (Debian/Ubuntu)" + msg_info "Setup Adminer (Debian/Ubuntu)" $STD apt-get install -y adminer $STD a2enconf adminer $STD systemctl reload apache2 msg_ok "Adminer available at /adminer (Debian/Ubuntu)" fi } + +# ------------------------------------------------------------------------------ +# Installs or updates yq (mikefarah/yq - Go version). +# +# Description: +# - Checks if yq is installed and from correct source +# - Compares with latest release on GitHub +# - Updates if outdated or wrong implementation +# ------------------------------------------------------------------------------ + +function setup_yq() { + local TMP_DIR + TMP_DIR=$(mktemp -d) + local CURRENT_VERSION="" + local BINARY_PATH="/usr/local/bin/yq" + local GITHUB_REPO="mikefarah/yq" + + if ! command -v jq &>/dev/null; then + $STD apt-get update + $STD apt-get install -y jq || { + msg_error "Failed to install jq" + rm -rf "$TMP_DIR" + return 1 + } + fi + + if command -v yq &>/dev/null; then + if ! yq --version 2>&1 | grep -q 'mikefarah'; then + rm -f "$(command -v yq)" + else + CURRENT_VERSION=$(yq --version | awk '{print $NF}' | sed 's/^v//') + fi + fi + + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest") + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^v//') + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not determine latest yq version from GitHub." + rm -rf "$TMP_DIR" + return 1 + fi + + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + return + fi + + msg_info "Setup yq ($LATEST_VERSION)" + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" + chmod +x "$TMP_DIR/yq" + mv "$TMP_DIR/yq" "$BINARY_PATH" + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "Failed to install yq to $BINARY_PATH" + rm -rf "$TMP_DIR" + return 1 + fi + + rm -rf "$TMP_DIR" + hash -r + + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}') + if [[ "$FINAL_VERSION" == "v$LATEST_VERSION" ]]; then + msg_ok "Setup yq ($LATEST_VERSION)" + else + msg_error "yq installation incomplete or version mismatch" + fi +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index acd06abd6..000000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "ProxmoxVE", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/tools/addon/filebrowser-quantum.sh b/tools/addon/filebrowser-quantum.sh new file mode 100644 index 000000000..74911a788 --- /dev/null +++ b/tools/addon/filebrowser-quantum.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Author: MickLesk +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info() { + clear + cat <<"EOF" + _______ __ ____ ____ __ + / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ + / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ + / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / +/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ + +EOF +} + +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +BL=$(echo "\033[36m") +CL=$(echo "\033[m") +CM="${GN}βœ”οΈ${CL}" +CROSS="${RD}βœ–οΈ${CL}" +INFO="${BL}ℹ️${CL}" + +APP="FileBrowser Quantum" +INSTALL_PATH="/usr/local/bin/filebrowser" +CONFIG_PATH="/usr/local/community-scripts/fq-config.yaml" +DEFAULT_PORT=8080 +SRC_DIR="/" + +# Get primary IP +IFACE=$(ip -4 route | awk '/default/ {print $5; exit}') +IP=$(ip -4 addr show "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) +[[ -z "$IP" ]] && IP=$(hostname -I | awk '{print $1}') +[[ -z "$IP" ]] && IP="127.0.0.1" + +# OS Detection +if [[ -f "/etc/alpine-release" ]]; then + OS="Alpine" + SERVICE_PATH="/etc/init.d/filebrowser" + PKG_MANAGER="apk add --no-cache" +elif [[ -f "/etc/debian_version" ]]; then + OS="Debian" + SERVICE_PATH="/etc/systemd/system/filebrowser.service" + PKG_MANAGER="apt-get install -y" +else + echo -e "${CROSS} Unsupported OS detected. Exiting." + exit 1 +fi + +header_info + +function msg_info() { + local msg="$1" + echo -e "${INFO} ${YW}${msg}...${CL}" +} + +function msg_ok() { + local msg="$1" + echo -e "${CM} ${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${CROSS} ${RD}${msg}${CL}" +} + +# Detect legacy FileBrowser installation +LEGACY_DB="/usr/local/community-scripts/filebrowser.db" +LEGACY_BIN="/usr/local/bin/filebrowser" +LEGACY_SERVICE_DEB="/etc/systemd/system/filebrowser.service" +LEGACY_SERVICE_ALP="/etc/init.d/filebrowser" + +if [[ -f "$LEGACY_DB" || -f "$LEGACY_BIN" && ! -f "$CONFIG_PATH" ]]; then + echo -e "${YW}⚠️ Detected legacy FileBrowser installation.${CL}" + echo -n "Uninstall legacy FileBrowser and continue with Quantum install? (y/n): " + read -r remove_legacy + if [[ "${remove_legacy,,}" =~ ^(y|yes)$ ]]; then + msg_info "Uninstalling legacy FileBrowser" + if [[ -f "$LEGACY_SERVICE_DEB" ]]; then + systemctl disable --now filebrowser.service &>/dev/null + rm -f "$LEGACY_SERVICE_DEB" + elif [[ -f "$LEGACY_SERVICE_ALP" ]]; then + rc-service filebrowser stop &>/dev/null + rc-update del filebrowser &>/dev/null + rm -f "$LEGACY_SERVICE_ALP" + fi + rm -f "$LEGACY_BIN" "$LEGACY_DB" + msg_ok "Legacy FileBrowser removed" + else + echo -e "${YW}❌ Installation aborted by user.${CL}" + exit 0 + fi +fi + +# Check existing installation +if [[ -f "$INSTALL_PATH" ]]; then + echo -e "${YW}⚠️ ${APP} is already installed.${CL}" + echo -n "Uninstall ${APP}? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + msg_info "Uninstalling ${APP}" + if [[ "$OS" == "Debian" ]]; then + systemctl disable --now filebrowser.service &>/dev/null + rm -f "$SERVICE_PATH" + else + rc-service filebrowser stop &>/dev/null + rc-update del filebrowser &>/dev/null + rm -f "$SERVICE_PATH" + fi + rm -f "$INSTALL_PATH" "$CONFIG_PATH" + msg_ok "${APP} has been uninstalled." + exit 0 + fi + + echo -n "Update ${APP}? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + msg_info "Updating ${APP}" + curl -fsSL https://github.com/gtsteffaniak/filebrowser/releases/latest/download/linux-amd64-filebrowser -o "$INSTALL_PATH" + chmod +x "$INSTALL_PATH" + msg_ok "Updated ${APP}" + exit 0 + else + echo -e "${YW}⚠️ Update skipped. Exiting.${CL}" + exit 0 + fi +fi + +echo -e "${YW}⚠️ ${APP} is not installed.${CL}" +echo -n "Enter port number (Default: ${DEFAULT_PORT}): " +read -r PORT +PORT=${PORT:-$DEFAULT_PORT} + +echo -n "Install ${APP}? (y/n): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + msg_info "Installing ${APP} on ${OS}" + $PKG_MANAGER curl ffmpeg &>/dev/null + curl -fsSL https://github.com/gtsteffaniak/filebrowser/releases/latest/download/linux-amd64-filebrowser -o "$INSTALL_PATH" + chmod +x "$INSTALL_PATH" + msg_ok "Installed ${APP}" + + msg_info "Preparing configuration directory" + mkdir -p /usr/local/community-scripts + chown root:root /usr/local/community-scripts + chmod 755 /usr/local/community-scripts + msg_ok "Directory prepared" + + echo -n "Use No Authentication? (y/N): " + read -r noauth_prompt + + if [[ "${noauth_prompt,,}" =~ ^(y|yes)$ ]]; then + cat <"$CONFIG_PATH" +server: + port: $PORT + sources: + - path: "$SRC_DIR" + config: + disableIndexing: false + indexingIntervalMinutes: 240 + exclude: + folders: + - "/proc" + - "/sys" + - "/dev" + - "/run" + - "/tmp" + - "/lost+found" +auth: + methods: + noauth: true +EOF + msg_ok "Configured with no authentication" + else + cat <"$CONFIG_PATH" +server: + port: $PORT + sources: + - path: "$SRC_DIR" + config: + disableIndexing: false + indexingIntervalMinutes: 240 + exclude: + folders: + - "/proc" + - "/sys" + - "/dev" + - "/run" + - "/tmp" + - "/lost+found" +auth: + adminUsername: admin + adminPassword: helper-scripts.com +EOF + msg_ok "Configured with default admin (admin / helper-scripts.com)" + fi + + msg_info "Creating service" + + if [[ "$OS" == "Debian" ]]; then + cat <"$SERVICE_PATH" +[Unit] +Description=FileBrowser Quantum +After=network.target + +[Service] +User=root +WorkingDirectory=/usr/local/community-scripts +ExecStart=/usr/local/bin/filebrowser -c $CONFIG_PATH +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable --now filebrowser &>/dev/null + else + cat <"$SERVICE_PATH" +#!/sbin/openrc-run + +command="/usr/local/bin/filebrowser" +command_args="-c $CONFIG_PATH" +command_background=true +directory="/usr/local/community-scripts" +pidfile="/usr/local/community-scripts/pidfile" + +depend() { + need net +} +EOF + chmod +x "$SERVICE_PATH" + rc-update add filebrowser default &>/dev/null + rc-service filebrowser start &>/dev/null + fi + + msg_ok "Service created successfully" + echo -e "${CM} ${GN}${APP} is reachable at: ${BL}http://$IP:$PORT${CL}" +else + echo -e "${YW}⚠️ Installation skipped. Exiting.${CL}" + exit 0 +fi diff --git a/tools/headers/add-iptag b/tools/headers/add-iptag new file mode 100644 index 000000000..153ccd369 --- /dev/null +++ b/tools/headers/add-iptag @@ -0,0 +1,6 @@ + ________ ______ + / _/ __ \ /_ __/___ _____ _ + / // /_/ /_____/ / / __ `/ __ `/ + _/ // ____/_____/ / / /_/ / /_/ / +/___/_/ /_/ \__,_/\__, / + /____/ diff --git a/tools/headers/add-lxc-iptag b/tools/headers/add-lxc-iptag deleted file mode 100644 index 766517bf2..000000000 --- a/tools/headers/add-lxc-iptag +++ /dev/null @@ -1,6 +0,0 @@ - __ _ ________ ________ ______ - / / | |/ / ____/ / _/ __ \ /_ __/___ _____ _ - / / | / / / // /_/ /_____/ / / __ `/ __ `/ - / /___/ / /___ _/ // ____/_____/ / / /_/ / /_/ / -/_____/_/|_\____/ /___/_/ /_/ \__,_/\__, / - /____/ diff --git a/tools/headers/filebrowser-quantum b/tools/headers/filebrowser-quantum new file mode 100644 index 000000000..95c8b4b87 --- /dev/null +++ b/tools/headers/filebrowser-quantum @@ -0,0 +1,6 @@ + _______ __ ____ ____ __ + / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ + / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ + / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / +/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ + diff --git a/misc/add-iptag.sh b/tools/pve/add-iptag.sh similarity index 68% rename from misc/add-iptag.sh rename to tools/pve/add-iptag.sh index c3bf88cf9..ba81b838e 100644 --- a/misc/add-iptag.sh +++ b/tools/pve/add-iptag.sh @@ -531,302 +531,260 @@ if [[ ! -f /opt/iptag/iptag ]]; then cat <<'EOF' >/opt/iptag/iptag #!/bin/bash # =============== CONFIGURATION =============== # -CONFIG_FILE="/opt/iptag/iptag.conf" +readonly CONFIG_FILE="/opt/iptag/iptag.conf" +readonly DEFAULT_TAG_FORMAT="full" +readonly DEFAULT_CHECK_INTERVAL=60 # Load the configuration file if it exists if [ -f "$CONFIG_FILE" ]; then - # shellcheck source=./iptag.conf - source "$CONFIG_FILE" + # shellcheck source=./iptag.conf + source "$CONFIG_FILE" fi # Convert IP to integer for comparison ip_to_int() { - local ip="$1" - local a b c d - IFS=. read -r a b c d <<< "${ip}" - echo "$((a << 24 | b << 16 | c << 8 | d))" + local ip="$1" + local a b c d + IFS=. read -r a b c d <<< "${ip}" + echo "$((a << 24 | b << 16 | c << 8 | d))" } # Check if IP is in CIDR ip_in_cidr() { - local ip="$1" - local cidr="$2" + local ip="$1" cidr="$2" + ipcalc -c "$ip" "$cidr" >/dev/null 2>&1 || return 1 - # Use ipcalc with the -c option (check), which returns 0 if the IP is in the network - if ipcalc -c "$ip" "$cidr" >/dev/null 2>&1; then - # Get network address and mask from CIDR - local network prefix + local network prefix ip_parts net_parts network=$(echo "$cidr" | cut -d/ -f1) prefix=$(echo "$cidr" | cut -d/ -f2) + IFS=. read -r -a ip_parts <<< "$ip" + IFS=. read -r -a net_parts <<< "$network" - # Check if IP is in the network - local ip_a ip_b ip_c ip_d net_a net_b net_c net_d - IFS=. read -r ip_a ip_b ip_c ip_d <<< "$ip" - IFS=. read -r net_a net_b net_c net_d <<< "$network" - - # Check octets match based on prefix length - local result=0 - if (( prefix >= 8 )); then - [[ "$ip_a" != "$net_a" ]] && result=1 - fi - if (( prefix >= 16 )); then - [[ "$ip_b" != "$net_b" ]] && result=1 - fi - if (( prefix >= 24 )); then - [[ "$ip_c" != "$net_c" ]] && result=1 - fi - - return $result - fi - - return 1 + case $prefix in + 8) [[ "${ip_parts[0]}" == "${net_parts[0]}" ]] ;; + 16) [[ "${ip_parts[0]}.${ip_parts[1]}" == "${net_parts[0]}.${net_parts[1]}" ]] ;; + 24) [[ "${ip_parts[0]}.${ip_parts[1]}.${ip_parts[2]}" == "${net_parts[0]}.${net_parts[1]}.${net_parts[2]}" ]] ;; + 32) [[ "$ip" == "$network" ]] ;; + *) return 1 ;; + esac } # Format IP address according to the configuration format_ip_tag() { - local ip="$1" - local format="${TAG_FORMAT:-full}" + local ip="$1" + local format="${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}" - case "$format" in - "last_octet") - echo "${ip##*.}" - ;; - "last_two_octets") - echo "${ip#*.*.}" - ;; - *) - echo "$ip" - ;; - esac + case "$format" in + "last_octet") echo "${ip##*.}" ;; + "last_two_octets") echo "${ip#*.*.}" ;; + *) echo "$ip" ;; + esac } # Check if IP is in any CIDRs ip_in_cidrs() { - local ip="$1" - local cidrs="$2" - - # Check that cidrs is not empty - [[ -z "$cidrs" ]] && return 1 - - local IFS=' ' - for cidr in $cidrs; do - ip_in_cidr "$ip" "$cidr" && return 0 - done - return 1 + local ip="$1" cidrs="$2" + [[ -z "$cidrs" ]] && return 1 + local IFS=' ' + for cidr in $cidrs; do + ip_in_cidr "$ip" "$cidr" && return 0 + done + return 1 } # Check if IP is valid is_valid_ipv4() { - local ip="$1" - [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 - local IFS='.' - read -ra parts <<< "$ip" - for part in "${parts[@]}"; do - [[ "$part" =~ ^[0-9]+$ ]] && ((part >= 0 && part <= 255)) || return 1 - done - return 0 -} - -lxc_status_changed() { - current_lxc_status=$(pct list 2>/dev/null) - if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then - return 1 - else - last_lxc_status="${current_lxc_status}" + local ip="$1" + [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 + + local IFS='.' parts + read -ra parts <<< "$ip" + for part in "${parts[@]}"; do + (( part >= 0 && part <= 255 )) || return 1 + done return 0 - fi -} - -vm_status_changed() { - current_vm_status=$(qm list 2>/dev/null) - if [ "${last_vm_status}" == "${current_vm_status}" ]; then - return 1 - else - last_vm_status="${current_vm_status}" - return 0 - fi -} - -fw_net_interface_changed() { - current_net_interface=$(ifconfig | grep "^fw") - if [ "${last_net_interface}" == "${current_net_interface}" ]; then - return 1 - else - last_net_interface="${current_net_interface}" - return 0 - fi } # Get VM IPs using MAC addresses and ARP table get_vm_ips() { - local vmid=$1 - local ips="" + local vmid=$1 ips="" macs found_ip=false + qm status "$vmid" 2>/dev/null | grep -q "status: running" || return - # Check if VM is running - qm status "$vmid" 2>/dev/null | grep -q "status: running" || return + macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -oE '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + [[ -z "$macs" ]] && return - # Get MAC addresses from VM configuration - local macs - macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + for mac in $macs; do + local ip + ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}') + [[ -n "$ip" ]] && { ips+="$ip "; found_ip=true; } + done - # Look up IPs from ARP table using MAC addresses - for mac in $macs; do - local ip - ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}') - if [ -n "$ip" ]; then - ips+="$ip " + if ! $found_ip; then + local agent_ip + agent_ip=$(qm agent "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' || true) + [[ -n "$agent_ip" ]] && ips+="$agent_ip " fi - done - echo "$ips" + echo "${ips% }" } # Update tags for container or VM update_tags() { - local type="$1" - local vmid="$2" - local config_cmd="pct" - [[ "$type" == "vm" ]] && config_cmd="qm" + local type="$1" vmid="$2" config_cmd="pct" + [[ "$type" == "vm" ]] && config_cmd="qm" - # Get current IPs - local current_ips_full - if [[ "$type" == "lxc" ]]; then - # Redirect error output to suppress AppArmor warnings - current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}') - else - current_ips_full=$(get_vm_ips "${vmid}") - fi - - # Parse current tags and get valid IPs - local current_tags=() - local next_tags=() - mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g') - - for tag in "${current_tags[@]}"; do - # Skip tag if it looks like an IP (full or partial) - if ! is_valid_ipv4 "${tag}" && ! [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then - next_tags+=("${tag}") + local current_ips_full + if [[ "$type" == "lxc" ]]; then + current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}') + else + current_ips_full=$(get_vm_ips "${vmid}") fi - done + [[ -z "$current_ips_full" ]] && return - # Add valid IPs to tags - local added_ips=() - local skipped_ips=() + local current_tags=() next_tags=() current_ip_tags=() + mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}"; then - if ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - local formatted_ip=$(format_ip_tag "$ip") - next_tags+=("${formatted_ip}") - added_ips+=("${formatted_ip}") - else - skipped_ips+=("${ip}") - fi + # Separate IP and non-IP tags + for tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${tag}" || [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + current_ip_tags+=("${tag}") + else + next_tags+=("${tag}") + fi + done + + local formatted_ips=() needs_update=false added_ips=() + for ip in ${current_ips_full}; do + if is_valid_ipv4 "$ip" && ip_in_cidrs "$ip" "${CIDR_LIST[*]}"; then + local formatted_ip=$(format_ip_tag "$ip") + formatted_ips+=("$formatted_ip") + if [[ ! " ${current_ip_tags[*]} " =~ " ${formatted_ip} " ]]; then + needs_update=true + added_ips+=("$formatted_ip") + next_tags+=("$formatted_ip") + fi + fi + done + + [[ ${#formatted_ips[@]} -eq 0 ]] && return + + # Add existing IP tags that are still valid + for tag in "${current_ip_tags[@]}"; do + if [[ " ${formatted_ips[*]} " =~ " ${tag} " ]]; then + if [[ ! " ${next_tags[*]} " =~ " ${tag} " ]]; then + next_tags+=("$tag") + fi + fi + done + + if [[ "$needs_update" == true ]]; then + echo "${type^} ${vmid}: adding IP tags: ${added_ips[*]}" + $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null fi - done +} - # Log only if there are changes - if [ ${#added_ips[@]} -gt 0 ]; then - echo "${type^} ${vmid}: added IP tags: ${added_ips[*]}" - fi +# Update all instances of specified type +update_all_tags() { + local type="$1" list_cmd="pct" vmids count=0 + [[ "$type" == "vm" ]] && list_cmd="qm" + + vmids=$($list_cmd list 2>/dev/null | grep -v VMID | awk '{print $1}') + for vmid in $vmids; do ((count++)); done + + echo "Found ${count} running ${type}s" + [[ $count -eq 0 ]] && return - # Update if changed - if [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]]; then - $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null - fi + for vmid in $vmids; do + update_tags "$type" "$vmid" + done } # Check if status changed check_status_changed() { - local type="$1" - local current_status - - case "$type" in - "lxc") - current_status=$(pct list 2>/dev/null | grep -v VMID) - [[ "${last_lxc_status}" == "${current_status}" ]] && return 1 - last_lxc_status="${current_status}" - ;; - "vm") - current_status=$(qm list 2>/dev/null | grep -v VMID) - [[ "${last_vm_status}" == "${current_status}" ]] && return 1 - last_vm_status="${current_status}" - ;; - "fw") - current_status=$(ifconfig 2>/dev/null | grep "^fw") - [[ "${last_net_interface}" == "${current_status}" ]] && return 1 - last_net_interface="${current_status}" - ;; - esac - return 0 + local type="$1" current + case "$type" in + "lxc") current=$(pct list 2>/dev/null | grep -v VMID) ;; + "vm") current=$(qm list 2>/dev/null | grep -v VMID) ;; + "fw") current=$(ifconfig 2>/dev/null | grep "^fw") ;; + esac + local last_var="last_${type}_status" + [[ "${!last_var}" == "$current" ]] && return 1 + eval "$last_var='$current'" + return 0 } +# Main check function check() { - current_time=$(date +%s) + local current_time changes_detected=false + current_time=$(date +%s) - # Check LXC status - time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) - if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking LXC status..." - last_lxc_status_check_time=${current_time} - if check_status_changed "lxc"; then - update_all_tags "lxc" - last_update_lxc_time=${current_time} + # Check LXC status + local time_since_last_lxc_check=$((current_time - last_lxc_status_check_time)) + if [[ "${LXC_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "${time_since_last_lxc_check}" -ge "${LXC_STATUS_CHECK_INTERVAL:-60}" ]]; then + echo "Checking LXC status..." + last_lxc_status_check_time=${current_time} + if check_status_changed "lxc"; then + changes_detected=true + update_all_tags "lxc" + last_update_lxc_time=${current_time} + fi fi - fi - # Check VM status - time_since_last_vm_status_check=$((current_time - last_vm_status_check_time)) - if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking VM status..." - last_vm_status_check_time=${current_time} - if check_status_changed "vm"; then - update_all_tags "vm" - last_update_vm_time=${current_time} + # Check VM status + local time_since_last_vm_check=$((current_time - last_vm_status_check_time)) + if [[ "${VM_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "${time_since_last_vm_check}" -ge "${VM_STATUS_CHECK_INTERVAL:-60}" ]]; then + echo "Checking VM status..." + last_vm_status_check_time=${current_time} + if check_status_changed "vm"; then + changes_detected=true + update_all_tags "vm" + last_update_vm_time=${current_time} + fi fi - fi - # Check network interface changes - time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) - if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then - echo "Checking network interfaces..." - last_fw_net_interface_check_time=${current_time} - if check_status_changed "fw"; then - update_all_tags "lxc" - update_all_tags "vm" - last_update_lxc_time=${current_time} - last_update_vm_time=${current_time} + # Check network interface changes + local time_since_last_fw_check=$((current_time - last_fw_net_interface_check_time)) + if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "${time_since_last_fw_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" ]]; then + echo "Checking network interfaces..." + last_fw_net_interface_check_time=${current_time} + if check_status_changed "fw"; then + changes_detected=true + update_all_tags "lxc" + update_all_tags "vm" + last_update_lxc_time=${current_time} + last_update_vm_time=${current_time} + fi fi - fi - # Force update if needed - for type in "lxc" "vm"; do - local last_update_var="last_update_${type}_time" - local time_since_last_update=$((current_time - ${!last_update_var})) - if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating ${type} tags..." - update_all_tags "$type" - eval "${last_update_var}=${current_time}" - fi - done + # Force update if needed + for type in "lxc" "vm"; do + local last_update_var="last_update_${type}_time" + local time_since_last_update=$((current_time - ${!last_update_var})) + if [[ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL:-1800} ]]; then + echo "Force updating ${type} tags..." + changes_detected=true + update_all_tags "$type" + eval "${last_update_var}=${current_time}" + fi + done + + $changes_detected || echo "No changes detected in system status" } # Initialize time variables -last_lxc_status_check_time=0 -last_vm_status_check_time=0 -last_fw_net_interface_check_time=0 -last_update_lxc_time=0 -last_update_vm_time=0 +declare -g last_lxc_status="" last_vm_status="" last_fw_status="" +declare -g last_lxc_status_check_time=0 last_vm_status_check_time=0 last_fw_net_interface_check_time=0 +declare -g last_update_lxc_time=0 last_update_vm_time=0 -# main: Set the IP tags for all LXC containers and VMs +# Main loop main() { - while true; do - check - sleep "${LOOP_INTERVAL}" - done + while true; do + check + sleep "${LOOP_INTERVAL:-$DEFAULT_CHECK_INTERVAL}" + done } main diff --git a/tools/pve/add-lxc-iptag.sh b/tools/pve/add-lxc-iptag.sh deleted file mode 100644 index 88732be66..000000000 --- a/tools/pve/add-lxc-iptag.sh +++ /dev/null @@ -1,357 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/gitsang/lxc-iptag - -function header_info { - clear - cat <<"EOF" - __ _ ________ ________ ______ - / / | |/ / ____/ / _/ __ \ /_ __/___ _____ _ - / / | / / / // /_/ /_____/ / / __ `/ __ `/ - / /___/ / /___ _/ // ____/_____/ / / /_/ / /_/ / -/_____/_/|_\____/ /___/_/ /_/ \__,_/\__, / - /____/ -EOF -} - -clear -header_info -APP="LXC IP-Tag" -hostname=$(hostname) - -# Farbvariablen -YW=$(echo "\033[33m") -GN=$(echo "\033[1;92m") -RD=$(echo "\033[01;31m") -CL=$(echo "\033[m") -BFR="\\r\\033[K" -HOLD=" " -CM=" βœ”οΈ ${CL}" -CROSS=" βœ–οΈ ${CL}" - -# This function enables error handling in the script by setting options and defining a trap for the ERR signal. -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. -error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" -} - -# This function displays a spinner. -spinner() { - local frames=('β ‹' 'β ™' 'β Ή' 'β Έ' 'β Ό' 'β ΄' 'β ¦' 'β §' 'β ‡' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -# This function displays a success message with a green color. -msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - -while true; do - read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn - case $yn in - [Yy]*) break ;; - [Nn]*) - msg_error "Installation cancelled." - exit - ;; - *) msg_error "Please answer yes or no." ;; - esac -done - -if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0 or later." - msg_error "Exiting..." - sleep 2 - exit -fi - -FILE_PATH="/usr/local/bin/iptag" -if [[ -f "$FILE_PATH" ]]; then - msg_info "The file already exists: '$FILE_PATH'. Skipping installation." - exit 0 -fi - -msg_info "Installing Dependencies" -apt-get update &>/dev/null -apt-get install -y ipcalc net-tools &>/dev/null -msg_ok "Installed Dependencies" - -msg_info "Setting up IP-Tag Scripts" -mkdir -p /opt/lxc-iptag -msg_ok "Setup IP-Tag Scripts" - -msg_info "Setup Default Config" -if [[ ! -f /opt/lxc-iptag/iptag.conf ]]; then - cat </opt/lxc-iptag/iptag.conf -# Configuration file for LXC IP tagging - -# List of allowed CIDRs -CIDR_LIST=( - 192.168.0.0/16 - 172.16.0.0/12 - 10.0.0.0/8 - 100.64.0.0/10 -) - -# Interval settings (in seconds) -LOOP_INTERVAL=60 -FW_NET_INTERFACE_CHECK_INTERVAL=60 -LXC_STATUS_CHECK_INTERVAL=-1 -FORCE_UPDATE_INTERVAL=1800 -EOF - msg_ok "Setup default config" -else - msg_ok "Default config already exists" -fi - -msg_info "Setup Main Function" -if [[ ! -f /opt/lxc-iptag/iptag ]]; then - cat <<'EOF' >/opt/lxc-iptag/iptag -#!/bin/bash - -# =============== CONFIGURATION =============== # - -CONFIG_FILE="/opt/lxc-iptag/iptag.conf" - -# Load the configuration file if it exists -if [ -f "$CONFIG_FILE" ]; then - # shellcheck source=./lxc-iptag.conf - source "$CONFIG_FILE" -fi - -# Convert IP to integer for comparison -ip_to_int() { - local ip="${1}" - local a b c d - - IFS=. read -r a b c d <<< "${ip}" - echo "$((a << 24 | b << 16 | c << 8 | d))" -} - -# Check if IP is in CIDR -ip_in_cidr() { - local ip="${1}" - local cidr="${2}" - - ip_int=$(ip_to_int "${ip}") - netmask_int=$(ip_to_int "$(ipcalc -b "${cidr}" | grep Broadcast | awk '{print $2}')") - masked_ip_int=$(( "${ip_int}" & "${netmask_int}" )) - [[ ${ip_int} -eq ${masked_ip_int} ]] && return 0 || return 1 -} - -# Check if IP is in any CIDRs -ip_in_cidrs() { - local ip="${1}" - local cidrs=() - - mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n') - for cidr in "${cidrs[@]}"; do - ip_in_cidr "${ip}" "${cidr}" && return 0 - done - - return 1 -} - -# Check if IP is valid -is_valid_ipv4() { - local ip=$1 - local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" - - if [[ $ip =~ $regex ]]; then - IFS='.' read -r -a parts <<< "$ip" - for part in "${parts[@]}"; do - if ! [[ $part =~ ^[0-9]+$ ]] || ((part < 0 || part > 255)); then - return 1 - fi - done - return 0 - else - return 1 - fi -} - -lxc_status_changed() { - current_lxc_status=$(pct list 2>/dev/null) - if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then - return 1 - else - last_lxc_status="${current_lxc_status}" - return 0 - fi -} - -fw_net_interface_changed() { - current_net_interface=$(ifconfig | grep "^fw") - if [ "${last_net_interface}" == "${current_net_interface}" ]; then - return 1 - else - last_net_interface="${current_net_interface}" - return 0 - fi -} - -# =============== MAIN =============== # - -update_lxc_iptags() { - vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}') - for vmid in ${vmid_list}; do - last_tagged_ips=() - current_valid_ips=() - next_tags=() - - # Parse current tags - mapfile -t current_tags < <(pct config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for current_tag in "${current_tags[@]}"; do - if is_valid_ipv4 "${current_tag}"; then - last_tagged_ips+=("${current_tag}") - continue - fi - next_tags+=("${current_tag}") - done - - # Get current IPs - current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - current_valid_ips+=("${ip}") - next_tags+=("${ip}") - fi - done - - # Skip if no ip change - if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then - echo "Skipping ${vmid} cause ip no changes" - continue - fi - - # Set tags - echo "Setting ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" - pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" - done -} - -check() { - current_time=$(date +%s) - - time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) - if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_lxc_status_check}" -ge "${STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking lxc status..." - last_lxc_status_check_time=${current_time} - if lxc_status_changed; then - update_lxc_iptags - last_update_time=${current_time} - return - fi - fi - - time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) - if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then - echo "Checking fw net interface..." - last_fw_net_interface_check_time=${current_time} - if fw_net_interface_changed; then - update_lxc_iptags - last_update_time=${current_time} - return - fi - fi - - time_since_last_update=$((current_time - last_update_time)) - if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating lxc iptags..." - update_lxc_iptags - last_update_time=${current_time} - return - fi -} - -# main: Set the IP tags for all LXC containers -main() { - while true; do - check - sleep "${LOOP_INTERVAL}" - done -} - -main -EOF - msg_ok "Setup Main Function" -else - msg_ok "Main Function already exists" -fi -chmod +x /opt/lxc-iptag/iptag - -msg_info "Creating Service" -if [[ ! -f /lib/systemd/system/iptag.service ]]; then - cat </lib/systemd/system/iptag.service -[Unit] -Description=LXC IP-Tag service -After=network.target - -[Service] -Type=simple -ExecStart=/opt/lxc-iptag/iptag -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - msg_ok "Created Service" -else - msg_ok "Service already exists." -fi - -msg_ok "Setup IP-Tag Scripts" - -msg_info "Starting Service" -systemctl daemon-reload &>/dev/null -systemctl enable -q --now iptag.service &>/dev/null -msg_ok "Started Service" -SPINNER_PID="" -echo -e "\n${APP} installation completed successfully! ${CL}\n" diff --git a/tools/pve/nic-offloading-fix.sh b/tools/pve/nic-offloading-fix.sh new file mode 100644 index 000000000..91a97fbbb --- /dev/null +++ b/tools/pve/nic-offloading-fix.sh @@ -0,0 +1,227 @@ +#!/usr/bin/env bash + +# Creates a systemd service to disable NIC offloading features for Intel e1000e interfaces +# Author: rcastley +# License: MIT + +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") +TAB=" " +CM="${TAB}βœ”οΈ${TAB}" +CROSS="${TAB}βœ–οΈ${TAB}" +INFO="${TAB}ℹ️${TAB}${CL}" +WARN="${TAB}⚠️${TAB}${CL}" + +function header_info { + clear + cat <<"EOF" + + _ ____________ ____ __________ ___ ____ _ __ __ + / | / / _/ ____/ / __ \/ __/ __/ /___ ____ _____/ (_)___ ____ _ / __ \(_)________ _/ /_ / /__ _____ + / |/ // // / / / / / /_/ /_/ / __ \/ __ `/ __ / / __ \/ __ `/ / / / / / ___/ __ `/ __ \/ / _ \/ ___/ + / /| // // /___ / /_/ / __/ __/ / /_/ / /_/ / /_/ / / / / / /_/ / / /_/ / (__ ) /_/ / /_/ / / __/ / +/_/ |_/___/\____/ \____/_/ /_/ /_/\____/\__,_/\__,_/_/_/ /_/\__, / /_____/_/____/\__,_/_.___/_/\___/_/ + /____/ + +EOF +} + +header_info + +function msg_info() { echo -e "${INFO} ${YW}${1}...${CL}"; } +function msg_ok() { echo -e "${CM} ${GN}${1}${CL}"; } +function msg_error() { echo -e "${CROSS} ${RD}${1}${CL}"; } +function msg_warn() { echo -e "${WARN} ${YWB}${1}"; } + +# Check for root privileges +if [ "$(id -u)" -ne 0 ]; then + msg_error "Error: This script must be run as root." + exit 1 +fi + +if ! command -v ethtool >/dev/null 2>&1; then + msg_info "Installing ethtool" + apt-get update &>/dev/null + apt-get install -y ethtool &>/dev/null || { msg_error "Failed to install ethtool. Exiting."; exit 1; } + msg_ok "ethtool installed successfully" +fi + +# Get list of network interfaces using Intel e1000e driver +INTERFACES=() +COUNT=0 + +msg_info "Searching for Intel e1000e interfaces" + +for device in /sys/class/net/*; do + interface="$(basename "$device")" # or adjust the rest of the usages below, as mostly you'll use the path anyway + # Skip loopback interface and virtual interfaces + if [[ "$interface" != "lo" ]] && [[ ! "$interface" =~ ^(tap|fwbr|veth|vmbr|bonding_masters) ]]; then + # Check if the interface uses the e1000e driver + driver=$(basename $(readlink -f /sys/class/net/$interface/device/driver 2>/dev/null) 2>/dev/null) + + if [[ "$driver" == "e1000e" ]]; then + # Get MAC address for additional identification + mac=$(cat /sys/class/net/$interface/address 2>/dev/null) + INTERFACES+=("$interface" "Intel e1000e NIC ($mac)") + ((COUNT++)) + fi + fi +done + +# Check if any Intel e1000e interfaces were found +if [ ${#INTERFACES[@]} -eq 0 ]; then + whiptail --title "Error" --msgbox "No Intel e1000e network interfaces found!" 10 60 + msg_error "No Intel e1000e network interfaces found! Exiting." + exit 1 +fi + +msg_ok "Found ${BL}$COUNT${GN} Intel e1000e interfaces" + +# Create a checklist for interface selection with all interfaces initially checked +INTERFACES_CHECKLIST=() +for ((i=0; i<${#INTERFACES[@]}; i+=2)); do + INTERFACES_CHECKLIST+=("${INTERFACES[i]}" "${INTERFACES[i+1]}" "ON") +done + +# Show interface selection checklist +SELECTED_INTERFACES=$(whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --title "Network Interfaces" \ + --separate-output --checklist "Select Intel e1000e network interfaces\n(Space to toggle, Enter to confirm):" 15 80 6 \ + "${INTERFACES_CHECKLIST[@]}" 3>&1 1>&2 2>&3) + +exitstatus=$? +if [ $exitstatus != 0 ]; then + msg_info "User canceled. Exiting." + exit 0 +fi + +# Check if any interfaces were selected +if [ -z "$SELECTED_INTERFACES" ]; then + msg_error "No interfaces selected. Exiting." + exit 0 +fi + +# Convert the selected interfaces into an array +readarray -t INTERFACE_ARRAY <<< "$SELECTED_INTERFACES" + +# Show the number of selected interfaces +INTERFACE_COUNT=${#INTERFACE_ARRAY[@]} + +# Print selected interfaces +for iface in "${INTERFACE_ARRAY[@]}"; do + msg_ok "Selected interface: ${BL}$iface${CL}" +done + +# Ask for confirmation with the list of selected interfaces +CONFIRMATION_MSG="You have selected the following interface(s):\n\n" +for iface in "${INTERFACE_ARRAY[@]}"; do + SPEED=$(cat /sys/class/net/$iface/speed 2>/dev/null) + MAC=$(cat /sys/class/net/$iface/address 2>/dev/null) + CONFIRMATION_MSG+="- $iface (MAC: $MAC, Speed: ${SPEED}Mbps)\n" +done +CONFIRMATION_MSG+="\nThis will create systemd service(s) to disable offloading features.\n\nProceed?" + +if ! whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --title "Confirmation" \ + --yesno "$CONFIRMATION_MSG" 20 80; then + msg_info "User canceled. Exiting." + exit 0 +fi + +# Loop through all selected interfaces and create services for each +for SELECTED_INTERFACE in "${INTERFACE_ARRAY[@]}"; do + # Create service name for this interface + SERVICE_NAME="disable-nic-offload-$SELECTED_INTERFACE.service" + SERVICE_PATH="/etc/systemd/system/$SERVICE_NAME" + + # Create the service file with e1000e specific optimizations + msg_info "Creating systemd service for interface: ${BL}$SELECTED_INTERFACE${YW}" + + # Start with the common part of the service file + cat > "$SERVICE_PATH" << EOF +[Unit] +Description=Disable NIC offloading for Intel e1000e interface $SELECTED_INTERFACE +After=network.target + +[Service] +Type=oneshot +# Disable all offloading features for Intel e1000e +ExecStart=/sbin/ethtool -K $SELECTED_INTERFACE gso off gro off tso off tx off rx off rxvlan off txvlan off sg off +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target +EOF + + # Check if service file was created successfully + if [ ! -f "$SERVICE_PATH" ]; then + whiptail --title "Error" --msgbox "Failed to create service file for $SELECTED_INTERFACE!" 10 50 + msg_error "Failed to create service file for $SELECTED_INTERFACE! Skipping to next interface." + continue + fi + + # Configure this service + { + echo "25"; sleep 0.2 + # Reload systemd to recognize the new service + systemctl daemon-reload + echo "50"; sleep 0.2 + # Start the service + systemctl start "$SERVICE_NAME" + echo "75"; sleep 0.2 + # Enable the service to start on boot + systemctl enable "$SERVICE_NAME" + echo "100"; sleep 0.2 + } | whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --gauge "Configuring service for $SELECTED_INTERFACE..." 10 80 0 + + # Individual service status + if systemctl is-active --quiet "$SERVICE_NAME"; then + SERVICE_STATUS="Active" + else + SERVICE_STATUS="Inactive" + fi + + if systemctl is-enabled --quiet "$SERVICE_NAME"; then + BOOT_STATUS="Enabled" + else + BOOT_STATUS="Disabled" + fi + + # Show individual service results + msg_ok "Service for ${BL}$SELECTED_INTERFACE${GN} created and enabled!" + msg_info "${TAB}Service: ${BL}$SERVICE_NAME${YW}" + msg_info "${TAB}Status: ${BL}$SERVICE_STATUS${YW}" + msg_info "${TAB}Start on boot: ${BL}$BOOT_STATUS${YW}" +done + +# Prepare summary of all interfaces +SUMMARY_MSG="Services created successfully!\n\n" +SUMMARY_MSG+="Configured Interfaces:\n" + +for iface in "${INTERFACE_ARRAY[@]}"; do + SERVICE_NAME="disable-nic-offload-$iface.service" + if systemctl is-active --quiet "$SERVICE_NAME"; then + SVC_STATUS="Active" + else + SVC_STATUS="Inactive" + fi + + if systemctl is-enabled --quiet "$SERVICE_NAME"; then + BOOT_SVC_STATUS="Enabled" + else + BOOT_SVC_STATUS="Disabled" + fi + + SUMMARY_MSG+="- $iface: $SVC_STATUS, Boot: $BOOT_SVC_STATUS\n" +done + +# Show summary results +whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --title "Success" --msgbox "$SUMMARY_MSG" 20 80 + +msg_ok "Intel e1000e optimization complete for ${#INTERFACE_ARRAY[@]} interface(s)!" + +exit 0 diff --git a/turnkey/turnkey.sh b/turnkey/turnkey.sh index 71cfca81d..93337f02e 100644 --- a/turnkey/turnkey.sh +++ b/turnkey/turnkey.sh @@ -79,6 +79,7 @@ mediaserver Media Server nextcloud Nextcloud observium Observium odoo Odoo +openldap OpenLDAP openvpn OpenVPN owncloud ownCloud phpbb phpBB @@ -201,7 +202,7 @@ echo "TurnKey ${turnkey} password: ${PASS}" >>~/turnkey-${turnkey}.creds # file # Start container msg "Starting LXC Container..." pct start "$CTID" -sleep 5 +sleep 10 # Get container IP set +euo pipefail # Turn off error checking diff --git a/vm/archlinux-vm.sh b/vm/archlinux-vm.sh index 223c5796a..30f61e960 100644 --- a/vm/archlinux-vm.sh +++ b/vm/archlinux-vm.sh @@ -19,13 +19,12 @@ EOF } header_info echo -e "\n Loading..." -#API VARIABLES +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="arch-linux-vm" var_os="arch-linux" -var_version=" " -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +var_version="n.d." YW=$(echo "\033[33m") BL=$(echo "\033[36m") @@ -58,6 +57,7 @@ MACADDRESS="${TAB}πŸ”—${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}πŸš€${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e @@ -69,8 +69,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${commad}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -101,6 +101,7 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } diff --git a/vm/debian-vm.sh b/vm/debian-vm.sh index 9b5c3a810..4cdfdc319 100644 --- a/vm/debian-vm.sh +++ b/vm/debian-vm.sh @@ -57,6 +57,7 @@ MACADDRESS="${TAB}πŸ”—${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}πŸš€${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e @@ -191,6 +192,7 @@ function default_settings() { VLAN="" MTU="" START_VM="yes" + CLOUD_INIT="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" @@ -204,6 +206,7 @@ function default_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above default settings${CL}" } @@ -373,6 +376,14 @@ function advanced_settings() { exit-script fi + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" --yesno "Configure the VM with Cloud-init?" --defaultno 10 58); then + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}yes${CL}" + CLOUD_INIT="yes" + else + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" + CLOUD_INIT="no" + fi + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" @@ -439,7 +450,11 @@ fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" -URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-arm64.qcow2 +if [ "$CLOUD_INIT" == "yes" ]; then + URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-arm64.qcow2 +else + URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-arm64.qcow2 +fi sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" @@ -474,11 +489,20 @@ qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 64M 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -boot order=scsi0 \ - -serial0 socket >/dev/null +if [ "$CLOUD_INIT" == "yes" ]; then + qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -scsi1 ${STORAGE}:cloudinit \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +else + qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +fi DESCRIPTION=$( cat < diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 3eab0aac9..bf5e802fe 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -29,7 +29,6 @@ DISK_SIZE="10G" YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") @@ -59,6 +58,8 @@ MACADDRESS="${TAB}πŸ”—${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}πŸš€${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR @@ -69,8 +70,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${command}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -182,7 +183,7 @@ function default_settings() { FORMAT=",efitype=4m" MACHINE="" DISK_CACHE="" - DISK_SIZE="8G" + DISK_SIZE="10G" HN="docker" CPU_TYPE="" CORE_COUNT="2" diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index 5081f94bd..e6850b708 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -2,8 +2,7 @@ # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) @@ -21,8 +20,8 @@ EOF header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -VERSIONS=(stable beta dev) RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +VERSIONS=(stable beta dev) METHOD="" NSAPP="homeassistant-os" var_os="homeassistant" @@ -39,26 +38,43 @@ BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " -CM="${GN}βœ“${CL}" -CROSS="${RD}βœ—${CL}" +TAB=" " + +CM="${TAB}βœ”οΈ${TAB}${CL}" +CROSS="${TAB}βœ–οΈ${TAB}${CL}" +INFO="${TAB}πŸ’‘${TAB}${CL}" +OS="${TAB}πŸ–₯️${TAB}${CL}" +CONTAINERTYPE="${TAB}πŸ“¦${TAB}${CL}" +DISKSIZE="${TAB}πŸ’Ύ${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}πŸ› οΈ${TAB}${CL}" +CONTAINERID="${TAB}πŸ†”${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}πŸŒ‰${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}βš™οΈ${TAB}${CL}" +MACADDRESS="${TAB}πŸ”—${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}πŸš€${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + THIN="discard=on,ssd=1," -SPINNER_PID="" -set -Eeuo pipefail +set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM - function error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${command}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -89,46 +105,31 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOME ASSISTANT OS VM" --yesno "This will create a New Home Assistant OS VM. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Homeassistant OS VM" --yesno "This will create a New Homeassistant OS VM. Proceed?" 10 58; then : else - header_info && echo -e "⚠ User exited script \n" && exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi -function spinner() { - local chars="/-\|" - local spin_i=0 - printf "\e[?25l" - while true; do - printf "\r \e[36m%s\e[0m" "${chars:spin_i++%${#chars}:1}" - sleep 0.1 - done -} - function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg} " - spinner & - SPINNER_PID=$! + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { @@ -143,7 +144,7 @@ function check_root() { function pve_check() { if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 @@ -153,7 +154,8 @@ function pve_check() { function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then - msg_error "This script will not work with PiMox! \n" + 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 @@ -175,7 +177,7 @@ function ssh_check() { function exit-script() { clear - echo -e "⚠ User exited script \n" + echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } @@ -195,26 +197,25 @@ function default_settings() { MTU="" START_VM="yes" METHOD="default" - var_version="${stable}" - echo -e "${DGN}Using HAOS Version: ${BGN}${BRANCH}${CL}" - echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${DGN}Using Machine Type: ${BGN}i440fx${CL}" - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" - echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" - echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" - echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}" - echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}" - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" - echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" - echo -e "${BL}Creating a HAOS VM using the above default settings${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Homeassistant OS VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" - if BRANCH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HAOS VERSION" --radiolist "Choose Version" --cancel-button Exit-Script 10 58 3 \ + if BRANCH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Homeassistant OS Version" --radiolist "Choose Version" --cancel-button Exit-Script 10 58 3 \ "$stable" "Stable " ON \ "$beta" "Beta " OFF \ "$dev" "Dev " OFF \ @@ -229,14 +230,14 @@ function advanced_settings() { while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then - VMID="$VMID" + VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi - echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script @@ -248,11 +249,11 @@ function advanced_settings() { "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -260,15 +261,30 @@ function advanced_settings() { exit-script fi - if DISK_CACHE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None" OFF \ "1" "Write Through (Default)" ON \ 3>&1 1>&2 2>&3); then - if [ $DISK_CACHE1 = "1" ]; then - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else @@ -278,10 +294,10 @@ function advanced_settings() { if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 haos${BRANCH} --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="haos${BRANCH}" - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script @@ -292,10 +308,10 @@ function advanced_settings() { "1" "Host (Default)" ON \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else @@ -305,20 +321,20 @@ function advanced_settings() { if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $CORE_COUNT ]; then CORE_COUNT="2" - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else exit-script fi - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="4096" - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script @@ -327,9 +343,9 @@ function advanced_settings() { if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script @@ -338,10 +354,10 @@ function advanced_settings() { if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" else MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else exit-script @@ -351,10 +367,10 @@ function advanced_settings() { if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script @@ -364,10 +380,10 @@ function advanced_settings() { if [ -z $MTU1 ]; then MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script @@ -381,8 +397,8 @@ function advanced_settings() { START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create HAOS ${BRANCH} VM?" --no-button Do-Over 10 58); then - echo -e "${RD}Creating a HAOS VM using the above advanced settings${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create Homeassistant OS ${BRANCH} VM?" --no-button Do-Over 10 58); then + echo -e "${RD}Creating a Homeassistant OS VM using the above advanced settings${CL}" else header_info echo -e "${RD}Using Advanced Settings${CL}" @@ -440,6 +456,8 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." + +var_version="${BRANCH}" msg_info "Retrieving the URL for Home Assistant ${BRANCH} Disk Image" if [ "$BRANCH" == "$dev" ]; then URL=https://os-artifacts.home-assistant.io/${BRANCH}/haos_generic-aarch64-${BRANCH}.qcow2.xz @@ -452,6 +470,7 @@ curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}haos_ova-${BRANCH}.qcow2.xz${CL}" + msg_info "Extracting KVM Disk Image" unxz $FILE STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') @@ -476,7 +495,8 @@ for i in {0,1}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_ok "Extracted KVM Disk Image" -msg_info "Creating HAOS VM" + +msg_info "Creating Homeassistant OS VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null @@ -484,14 +504,49 @@ qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=32G \ - -boot order=scsi0 \ - -description "
+ -boot order=scsi0 >/dev/null - # Home Assistant OS +DESCRIPTION=$( + cat < + + Logo + - -
" >/dev/null -msg_ok "Created HAOS VM ${CL}${BL}(${HN})" +

Homeassistant OS VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) + +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created Homeassistant OS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Home Assistant OS VM" qm start $VMID diff --git a/vm/headers/owncloud-vm b/vm/headers/owncloud-vm new file mode 100644 index 000000000..54d663d75 --- /dev/null +++ b/vm/headers/owncloud-vm @@ -0,0 +1,6 @@ + ______ __ __ ________ __ _ ____ ___ + /_ __/_ ___________ / //_/__ __ __ ____ _ ______ / ____/ /___ __ ______/ / | | / / |/ / + / / / / / / ___/ __ \/ ,< / _ \/ / / / / __ \ | /| / / __ \/ / / / __ \/ / / / __ / | | / / /|_/ / + / / / /_/ / / / / / / /| / __/ /_/ / / /_/ / |/ |/ / / / / /___/ / /_/ / /_/ / /_/ / | |/ / / / / +/_/ \__,_/_/ /_/ /_/_/ |_\___/\__, / \____/|__/|__/_/ /_/\____/_/\____/\__,_/\__,_/ |___/_/ /_/ + /____/ diff --git a/vm/mikrotik-routeros.sh b/vm/mikrotik-routeros.sh index bf1f77c32..3a4f91d0a 100644 --- a/vm/mikrotik-routeros.sh +++ b/vm/mikrotik-routeros.sh @@ -8,6 +8,7 @@ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { + clear cat <<"EOF" __ ____ __ __ _ __ ____ __ ____ _____ ________ ______ / |/ (_) /___________ / /_(_) /__ / __ \____ __ __/ /____ _____/ __ \/ ___/ / ____/ / / / __ \ @@ -17,7 +18,6 @@ function header_info { EOF } -clear header_info echo -e "Loading..." GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]') @@ -29,33 +29,51 @@ var_version=" " DISK_SIZE="1G" YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}βœ“${CL}" -set -o errexit -set -o errtrace -set -o nounset -set -o pipefail -shopt -s expand_aliases -alias die='EXIT=$? LINE=$LINENO error_exit' -trap die ERR +HOLD=" " +TAB=" " + +CM="${TAB}βœ”οΈ${TAB}${CL}" +CROSS="${TAB}βœ–οΈ${TAB}${CL}" +INFO="${TAB}πŸ’‘${TAB}${CL}" +OS="${TAB}πŸ–₯️${TAB}${CL}" +CONTAINERTYPE="${TAB}πŸ“¦${TAB}${CL}" +DISKSIZE="${TAB}πŸ’Ύ${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}πŸ› οΈ${TAB}${CL}" +CONTAINERID="${TAB}πŸ†”${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}πŸŒ‰${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}βš™οΈ${TAB}${CL}" +MACADDRESS="${TAB}πŸ”—${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}πŸš€${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM -function error_exit() { - trap - ERR - local reason="Unknown failure occurred." - local msg="${1:-$reason}" - local flag="${RD}β€Ό ERROR ${CL}$EXIT@$LINE" - echo -e "$flag $msg" 1>&2 - [ ! -z ${VMID-} ] && cleanup_vmid - exit $EXIT +function error_handler() { + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid } function get_valid_nextid() { @@ -76,162 +94,352 @@ function get_valid_nextid() { } function cleanup_vmid() { - if $(qm status $VMID &>/dev/null); then - if [ "$(qm status $VMID | awk '{print $2}')" == "running" ]; then - qm stop $VMID - fi - qm destroy $VMID + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null fi } + function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } + TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit -fi -if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "Mikrotik RouterOS CHR VM" --yesno "This will create a New Mikrotik RouterOS CHR VM. Proceed?" 10 58); then - echo "User selected Yes" +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Mikrotik RouterOS CHR VM" --yesno "This will create a Mikrotik RouterOS CHR VM. Proceed?" 10 58; then + : else - clear - echo -e "⚠ User exited script \n" - exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } + function msg_ok() { local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +function pve_check() { + if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi +} + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + function default_settings() { - METHOD="default" VMID=$(get_valid_nextid) - echo -e "${DGN}Using Virtual Machine ID: ${BGN}$VMID${CL}" - echo -e "${DGN}Using Hostname: ${BGN}mikrotik-routeros-chr${CL}" - HN=mikrotik-routeros-chr - echo -e "${DGN}Allocated Cores: ${BGN}1${CL}" + FORMAT=",efitype=4m" + MACHINE="" + DISK_SIZE="8G" + DISK_CACHE="" + HN="mikrotik-routeros-chr" + CPU_TYPE="" CORE_COUNT="2" - echo -e "${DGN}Allocated RAM: ${BGN}256${CL}" RAM_SIZE="512" - echo -e "${DGN}Using Bridge: ${BGN}vmbr0${CL}" BRG="vmbr0" - echo -e "${DGN}Using MAC Address: ${BGN}$GEN_MAC${CL}" - MAC=$GEN_MAC - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" + MAC="$GEN_MAC" VLAN="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" MTU="" - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" - START_VM="no" - echo -e "${BL}Creating a Mikrotik RouterOS CHR VM using the above default settings${CL}" + START_VM="yes" + CLOUD_INIT="no" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS VM using the above default settings${CL}" } + +function get_mikrotik_version() { + local mode="$1" + local tree_name + + case "$mode" in + s) tree_name="Stable release tree" ;; + d) tree_name="Development release tree" ;; + l) tree_name="Long-term release tree" ;; + t) tree_name="Testing release tree" ;; + *) return 0 ;; # not an error, just no-op + esac + + local html + html=$(curl -fsSL "https://mikrotik.com/download/changelogs") || return 0 + [ -z "$html" ] && return 0 + + local start_line + start_line=$(echo "$html" | grep -n "$tree_name$" | cut -d: -f1 | head -n1) + [[ "$start_line" =~ ^[0-9]+$ ]] || return 0 + + local line + line=$( (echo "$html" | tail -n +"$start_line" | grep -m 1 "c-\(stable\|longTerm\|testing\|development\)-v") 2>/dev/null || true) + + local version + version=$(echo "$line" | sed -n 's/.*c-[^"]*-v\([0-9_.a-zA-Z-]\+\).*/\1/p' | tr '_' '.') + + [[ "$version" =~ ^[0-9]+\.[0-9]+.*$ ]] && echo "$version" + + return 0 +} + function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) - VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Using Virtual Machine ID: ${BGN}$VMID${CL}" + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_nextid) + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit-script + fi + done + + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" ON \ + "q35" "Machine q35" OFF \ + 3>&1 1>&2 2>&3); then + if [ $MACH = q35 ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT=",efitype=4m" + MACHINE="" + fi else - exit + exit-script fi - VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 mikrotik-routeros-chr --title "HOSTNAME" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi else - exit + exit-script fi - CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON \ + "1" "Write Through" OFF \ + 3>&1 1>&2 2>&3); then + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + DISK_CACHE="cache=writethrough," + else + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + DISK_CACHE="" + fi else - exit + exit-script fi - RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 512 --title "RAM" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 mikrotik-routeros-chr --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="mikrotik-routeros-chr" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + fi else - exit + exit-script fi - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON \ + "1" "Host" OFF \ + 3>&1 1>&2 2>&3); then + if [ $CPU_TYPE1 = "1" ]; then + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi else - exit + exit-script fi - MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi else - exit + exit-script fi - VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + fi + else + exit-script + fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + else + exit-script + fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit-script + fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VLAN1 ]; then - VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + VLAN1="Default" + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi + else + exit-script fi - MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MTU1 ]; then - MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + MTU1="Default" + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi + else + exit-script fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start Mikrotik RouterOS CHR VM when completed?" 10 58); then - echo -e "${DGN}Start Mikrotik RouterOS CHR VM when completed: ${BGN}yes${CL}" + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else - echo -e "${DGN}Start Mikrotik RouterOS CHR VM when completed: ${BGN}no${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create Mikrotik RouterOS VM?" 10 58); then - echo -e "${RD}Creating Mikrotik RouterOS CHR VM using the above advanced settings${CL}" + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Mikrotik RouterOS CHR VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS CHR VM using the above advanced settings${CL}" else - clear header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } + function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then - clear header_info - echo -e "${BL}Using Default Settings${CL}" + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else - clear header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } + +check_root +arch_check +pve_check +ssh_check start_script post_to_api_vm @@ -264,12 +472,22 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Getting URL for Mikrotik RouterOS CHR Disk Image" +msg_info "Getting URL for Latest Mikrotik RouterOS CHR Disk Image" -URL=https://download.mikrotik.com/routeros/7.15.3/chr-7.15.3.img.zip +MIK_VER=$(get_mikrotik_version s) + +if [ -n "$MIK_VER" ]; then + msg_ok "Latest stable version: ${CL}${BL}$MIK_VER${CL}." +else + msg_error "Could not get latest version" + msg_ok "Defaulting to version 7.19" + ver="7.19" +fi + +URL=https://download.mikrotik.com/routeros/$MIK_VER/chr-$MIK_VER.img.zip sleep 2 -msg_ok "${CL}${BL}${URL}${CL}" +msg_ok "Downloading from URL: ${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) @@ -306,13 +524,47 @@ qm create $VMID -tablet 0 -localtime 1 -cores $CORE_COUNT -memory $RAM_SIZE -nam qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -scsi0 "$DISK_REF" \ - -boot order=scsi0 \ - -description "
+ -boot order=scsi0 >/dev/null - # Mikrotik RouterOS CHR +DESCRIPTION=$( + cat < + + Logo + + +

Mikrotik RouterOS CHR

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi - -
" >/dev/null msg_ok "Mikrotik RouterOS CHR VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Mikrotik RouterOS CHR VM" diff --git a/vm/nextcloud-vm.sh b/vm/nextcloud-vm.sh index 1b2703933..8d6c0fe3b 100644 --- a/vm/nextcloud-vm.sh +++ b/vm/nextcloud-vm.sh @@ -19,27 +19,47 @@ EOF } header_info echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="turnkey-nextcloud" var_os="turnkey-nextcloud" -var_version=" " -DISK_SIZE="12G" -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -NAME="TurnKey Nextcloud VM" +var_version="n.d." + YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}βœ“${CL}" -CROSS="${RD}βœ—${CL}" -THIN="discard=on,ssd=1" +HOLD=" " +TAB=" " + +CM="${TAB}βœ”οΈ${TAB}${CL}" +CROSS="${TAB}βœ–οΈ${TAB}${CL}" +INFO="${TAB}πŸ’‘${TAB}${CL}" +OS="${TAB}πŸ–₯️${TAB}${CL}" +CONTAINERTYPE="${TAB}πŸ“¦${TAB}${CL}" +DISKSIZE="${TAB}πŸ’Ύ${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}πŸ› οΈ${TAB}${CL}" +CONTAINERID="${TAB}πŸ†”${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}πŸŒ‰${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}βš™οΈ${TAB}${CL}" +MACADDRESS="${TAB}πŸ”—${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}πŸš€${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT @@ -49,8 +69,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${command}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -81,30 +101,31 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "$NAME" --yesno "This will create a New $NAME. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Nextcloud VM" --yesno "This will create a New Nextcloud VM. Proceed?" 10 58; then : else - header_info && echo -e "⚠ User exited script \n" && exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { @@ -119,7 +140,7 @@ function check_root() { function pve_check() { if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 @@ -129,7 +150,8 @@ function pve_check() { function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then - msg_error "This script will not work with PiMox! \n" + 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 @@ -151,7 +173,7 @@ function ssh_check() { function exit-script() { clear - echo -e "⚠ User exited script \n" + echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } @@ -159,8 +181,9 @@ function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" + DISK_SIZE="10G" DISK_CACHE="" - HN="turnkey-nextcloud-vm" + HN="nextcloud-vm" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" @@ -168,21 +191,22 @@ function default_settings() { MAC="$GEN_MAC" VLAN="" MTU="" - START_VM="no" + START_VM="yes" METHOD="default" - echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${DGN}Using Machine Type: ${BGN}i440fx${CL}" - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" - echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" - echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" - echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}" - echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}" - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" - echo -e "${BL}Creating a $NAME using the above default settings${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above default settings${CL}" } function advanced_settings() { @@ -198,7 +222,7 @@ function advanced_settings() { sleep 2 continue fi - echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script @@ -210,11 +234,11 @@ function advanced_settings() { "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -222,28 +246,43 @@ function advanced_settings() { exit-script fi + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 turnkey-nextcloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 nextcloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="$HN" - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + HN="nextcloud-vm" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script @@ -254,10 +293,10 @@ function advanced_settings() { "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else @@ -267,9 +306,9 @@ function advanced_settings() { if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $CORE_COUNT ]; then CORE_COUNT="2" - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else exit-script @@ -278,9 +317,9 @@ function advanced_settings() { if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script @@ -289,9 +328,9 @@ function advanced_settings() { if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script @@ -300,10 +339,10 @@ function advanced_settings() { if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" else MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else exit-script @@ -313,10 +352,10 @@ function advanced_settings() { if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script @@ -326,28 +365,28 @@ function advanced_settings() { if [ -z $MTU1 ]; then MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then - echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a $NAME?" --no-button Do-Over 10 58); then - echo -e "${RD}Creating a $NAME using the above advanced settings${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Nextcloud VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above advanced settings${CL}" else header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } @@ -441,13 +480,46 @@ qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \ -scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \ - -boot order='scsi1;scsi0' \ - -description "
+ -boot order='scsi1;scsi0' >/dev/null +DESCRIPTION=$( + cat < + + Logo + - # $NAME +

Nextcloud VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi - -
" >/dev/null msg_ok "Created a $NAME ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting $NAME" diff --git a/vm/openwrt.sh b/vm/openwrt.sh index 6d832e5a9..86f6e4e4f 100644 --- a/vm/openwrt.sh +++ b/vm/openwrt.sh @@ -507,13 +507,40 @@ qm set $VMID \ -efidisk0 ${DISK0_REF},efitype=4m,size=4M \ -scsi0 ${DISK1_REF},size=512M \ -boot order=scsi0 \ - -tags community-script \ - -description "
+ -tags community-script >/dev/null - # OpenWrt +DESCRIPTION=$( + cat < + + Logo + + +

OpenWRT VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null - -
" >/dev/null msg_ok "Created OpenWrt VM ${CL}${BL}(${HN})" msg_info "OpenWrt is being started in order to configure the network interfaces." qm start $VMID diff --git a/vm/owncloud-vm.sh b/vm/owncloud-vm.sh index dcc06fdfb..536ee3d82 100644 --- a/vm/owncloud-vm.sh +++ b/vm/owncloud-vm.sh @@ -19,28 +19,48 @@ EOF } header_info echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="turnkey-owncloud-vm" var_os="owncloud" -var_version="12" -DISK_SIZE="12G" -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -NAME="TurnKey ownCloud VM" +var_version="18.0" +APP="TurnKey ownCloud VM" YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}βœ“${CL}" -CROSS="${RD}βœ—${CL}" -THIN="discard=on,ssd=1" +HOLD=" " +TAB=" " + +CM="${TAB}βœ”οΈ${TAB}${CL}" +CROSS="${TAB}βœ–οΈ${TAB}${CL}" +INFO="${TAB}πŸ’‘${TAB}${CL}" +OS="${TAB}πŸ–₯️${TAB}${CL}" +CONTAINERTYPE="${TAB}πŸ“¦${TAB}${CL}" +DISKSIZE="${TAB}πŸ’Ύ${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}πŸ› οΈ${TAB}${CL}" +CONTAINERID="${TAB}πŸ†”${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}πŸŒ‰${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}βš™οΈ${TAB}${CL}" +MACADDRESS="${TAB}πŸ”—${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}πŸš€${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT @@ -50,8 +70,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "$command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -82,30 +102,31 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "$NAME" --yesno "This will create a New $NAME. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Owncloud VM" --yesno "This will create a New Owncloud VM. Proceed?" 10 58; then : else - header_info && echo -e "⚠ User exited script \n" && exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { @@ -120,7 +141,7 @@ function check_root() { function pve_check() { if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 @@ -130,7 +151,8 @@ function pve_check() { function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then - msg_error "This script will not work with PiMox! \n" + 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 @@ -152,7 +174,7 @@ function ssh_check() { function exit-script() { clear - echo -e "⚠ User exited script \n" + echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } @@ -160,8 +182,9 @@ function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" + DISK_SIZE="10G" DISK_CACHE="" - HN="turnkey-owncloud-vm" + HN="owncloud-vm" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" @@ -169,21 +192,22 @@ function default_settings() { MAC="$GEN_MAC" VLAN="" MTU="" - START_VM="no" + START_VM="yes" METHOD="default" - echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${DGN}Using Machine Type: ${BGN}i440fx${CL}" - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" - echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" - echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" - echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}" - echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}" - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" - echo -e "${BL}Creating a $NAME using the above default settings${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Owncloud VM using the above default settings${CL}" } function advanced_settings() { @@ -199,7 +223,7 @@ function advanced_settings() { sleep 2 continue fi - echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script @@ -211,11 +235,11 @@ function advanced_settings() { "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -223,28 +247,43 @@ function advanced_settings() { exit-script fi + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 turnkey-owncloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 owncloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="turnkey-owncloud-vm" - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + HN="owncloud-vm" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script @@ -255,10 +294,10 @@ function advanced_settings() { "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else @@ -268,9 +307,9 @@ function advanced_settings() { if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $CORE_COUNT ]; then CORE_COUNT="2" - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else exit-script @@ -279,9 +318,9 @@ function advanced_settings() { if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script @@ -290,9 +329,9 @@ function advanced_settings() { if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script @@ -301,10 +340,10 @@ function advanced_settings() { if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" else MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else exit-script @@ -314,10 +353,10 @@ function advanced_settings() { if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script @@ -327,28 +366,40 @@ function advanced_settings() { if [ -z $MTU1 ]; then MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then - echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a $NAME?" --no-button Do-Over 10 58); then - echo -e "${RD}Creating a $NAME using the above advanced settings${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Owncloud VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Owncloud VM using the above advanced settings${CL}" else header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } @@ -401,7 +452,7 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Retrieving the URL for the $NAME Disk Image" +msg_info "Retrieving the URL for the $APP Disk Image" URL=http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-owncloud-18.0-bookworm-amd64.iso sleep 2 msg_ok "${CL}${BL}${URL}${CL}" @@ -432,7 +483,7 @@ for i in {0,1,2}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done -msg_info "Creating a $NAME" +msg_info "Creating a $APP" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios seabios${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null @@ -442,18 +493,52 @@ qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \ -scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \ - -boot order='scsi1;scsi0' \ - -description "
+ -boot order='scsi1;scsi0' >/dev/null - # $NAME +DESCRIPTION=$( + cat < + + Logo + - -
" >/dev/null -msg_ok "Created a $NAME ${CL}${BL}(${HN})" +

Owncloud VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created a $APP ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then - msg_info "Starting $NAME" + msg_info "Starting $APP" qm start $VMID - msg_ok "Started $NAME" + msg_ok "Started $APP" fi post_update_to_api "done" "none" msg_ok "Completed Successfully!\n" diff --git a/vm/pimox-haos-vm.sh b/vm/pimox-haos-vm.sh index 8613ab982..c4e328a79 100644 --- a/vm/pimox-haos-vm.sh +++ b/vm/pimox-haos-vm.sh @@ -23,49 +23,65 @@ EOF } clear header_info -echo -e "Loading..." +echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +VERSIONS=(stable beta dev) METHOD="" NSAPP="pimox-haos-vm" var_os="pimox-haos" -var_version=" " DISK_SIZE="32G" -GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]') -USEDID=$(pvesh get /cluster/resources --type vm --output-format yaml | egrep -i 'vmid' | awk '{print substr($2, 1, length($2)-0) }') -STABLE=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/stable.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') -BETA=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/beta.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') -DEV=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/dev.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') + +for version in "${VERSIONS[@]}"; do + eval "$version=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/stable.json | grep '"ova"' | cut -d '"' -f 4)" +done YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}βœ“${CL}" -CROSS="${RD}βœ—${CL}" -set -o errexit -set -o errtrace -set -o nounset -set -o pipefail -shopt -s expand_aliases -alias die='EXIT=$? LINE=$LINENO error_exit' -trap die ERR +HOLD=" " +TAB=" " + +CM="${TAB}βœ”οΈ${TAB}${CL}" +CROSS="${TAB}βœ–οΈ${TAB}${CL}" +INFO="${TAB}πŸ’‘${TAB}${CL}" +OS="${TAB}πŸ–₯️${TAB}${CL}" +CONTAINERTYPE="${TAB}πŸ“¦${TAB}${CL}" +DISKSIZE="${TAB}πŸ’Ύ${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}πŸ› οΈ${TAB}${CL}" +CONTAINERID="${TAB}πŸ†”${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}πŸŒ‰${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}βš™οΈ${TAB}${CL}" +MACADDRESS="${TAB}πŸ”—${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}πŸš€${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM -function error_exit() { - trap - ERR - local reason="Unknown failure occurred." - local msg="${1:-$reason}" - local flag="${RD}β€Ό ERROR ${CL}$EXIT@$LINE" - post_update_to_api "failed" "unknown" - echo -e "$flag $msg" 1>&2 - [ ! -z ${VMID-} ] && cleanup_vmid - exit $EXIT +function error_handler() { + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid } function get_valid_nextid() { @@ -86,51 +102,88 @@ function get_valid_nextid() { } function cleanup_vmid() { - if $(qm status $VMID &>/dev/null); then - if [ "$(qm status $VMID | awk '{print $2}')" == "running" ]; then - qm stop $VMID - fi - qm destroy $VMID + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null fi } + function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } + TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if ! command -v whiptail &>/dev/null; then - echo "Installing whiptail..." - apt-get update &>/dev/null - apt-get install -y whiptail &>/dev/null -fi -if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "PiMox HAOS VM" --yesno "This will create a New PiMox HAOS VM. Proceed?" 10 58); then - echo "User selected Yes" +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Pimox Homeassistant OS VM" --yesno "This will create a New Pimox Homeassistant OS VM. Proceed?" 10 58; then + : else - clear - echo -e "⚠ User exited script \n" - exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi -function ARCH_CHECK() { - ARCH=$(dpkg --print-architecture) - if [[ "$ARCH" == "amd64" ]]; then - echo -e "\n ❌ This script only works with PiMox! \n" + +function msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +function pve_check() { + if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 exit fi } -function msg_info() { - local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi } -function msg_ok() { - local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi } -function msg_error() { - local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit } function default_settings() { @@ -341,13 +394,47 @@ qm set $VMID \ -efidisk0 ${DISK0_REF},efitype=4m,size=64M \ -scsi0 ${DISK1_REF},size=32G >/dev/null qm set $VMID \ - -boot order=scsi0 \ - -description "
+ -boot order=scsi0 >/dev/null - # Home Assistant OS +DESCRIPTION=$( + cat < + + Logo + + +

OpenWRT VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi - -
" >/dev/null msg_ok "Created HAOS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Home Assistant OS VM" diff --git a/vm/ubuntu2504-vm.sh b/vm/ubuntu2504-vm.sh new file mode 100644 index 000000000..39e32d0ee --- /dev/null +++ b/vm/ubuntu2504-vm.sh @@ -0,0 +1,528 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) + +function header_info { + clear + cat <<"EOF" + __ ____ __ ___ ______ ____ __ __ _ ____ ___ + / / / / /_ __ ______ / /___ __ |__ \ / ____// __ \/ // / | | / / |/ / + / / / / __ \/ / / / __ \/ __/ / / / __/ //___ \ / / / / // /_ | | / / /|_/ / +/ /_/ / /_/ / /_/ / / / / /_/ /_/ / / __/____/ // /_/ /__ __/ | |/ / / / / +\____/_.___/\__,_/_/ /_/\__/\__,_/ /____/_____(_)____/ /_/ |___/_/ /_/ (Plucky Puffin) + +EOF +} +header_info +echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" +NSAPP="ubuntu-2504-vm" +var_os="ubuntu" +var_version="2504" + +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") +BFR="\\r\\033[K" +HOLD=" " +TAB=" " + +CM="${TAB}βœ”οΈ${TAB}${CL}" +CROSS="${TAB}βœ–οΈ${TAB}${CL}" +INFO="${TAB}πŸ’‘${TAB}${CL}" +OS="${TAB}πŸ–₯️${TAB}${CL}" +CONTAINERTYPE="${TAB}πŸ“¦${TAB}${CL}" +DISKSIZE="${TAB}πŸ’Ύ${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}πŸ› οΈ${TAB}${CL}" +CONTAINERID="${TAB}πŸ†”${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}πŸŒ‰${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}βš™οΈ${TAB}${CL}" +MACADDRESS="${TAB}πŸ”—${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}πŸš€${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM +function error_handler() { + local exit_code="$?" + local line_number="$1" + local command="$2" + post_update_to_api "failed" "$command" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + echo -e "\n$error_message\n" + cleanup_vmid +} + +function get_valid_nextid() { + local try_id + try_id=$(pvesh get /cluster/nextid) + while true; do + if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then + try_id=$((try_id + 1)) + continue + fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then + try_id=$((try_id + 1)) + continue + fi + break + done + echo "$try_id" +} + +function cleanup_vmid() { + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null + fi +} + +function cleanup() { + popd >/dev/null + rm -rf $TEMP_DIR +} + +TEMP_DIR=$(mktemp -d) +pushd $TEMP_DIR >/dev/null +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Ubuntu 25.04 VM" --yesno "This will create a New Ubuntu 25.04 VM. Proceed?" 10 58; then + : +else + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit +fi + +function msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +function pve_check() { + if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi +} + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +function default_settings() { + VMID=$(get_valid_nextid) + FORMAT=",efitype=4m" + MACHINE="" + DISK_SIZE="7G" + DISK_CACHE="" + HN="ubuntu" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="2048" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 25.04 VM using the above default settings${CL}" +} + +function advanced_settings() { + METHOD="advanced" + [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_nextid) + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit-script + fi + done + + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" ON \ + "q35" "Machine q35" OFF \ + 3>&1 1>&2 2>&3); then + if [ $MACH = q35 ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT=",efitype=4m" + MACHINE="" + fi + else + exit-script + fi + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON \ + "1" "Write Through" OFF \ + 3>&1 1>&2 2>&3); then + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + DISK_CACHE="cache=writethrough," + else + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + DISK_CACHE="" + fi + else + exit-script + fi + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 ubuntu --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="ubuntu" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + fi + else + exit-script + fi + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON \ + "1" "Host" OFF \ + 3>&1 1>&2 2>&3); then + if [ $CPU_TYPE1 = "1" ]; then + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi + else + exit-script + fi + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi + else + exit-script + fi + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + fi + else + exit-script + fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + else + exit-script + fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit-script + fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VLAN1 ]; then + VLAN1="Default" + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + else + VLAN=",tag=$VLAN1" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + fi + else + exit-script + fi + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MTU1 ]; then + MTU1="Default" + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + MTU=",mtu=$MTU1" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + fi + else + exit-script + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + START_VM="yes" + else + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" + START_VM="no" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Ubuntu 25.04 VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 25.04 VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} +check_root +arch_check +pve_check +ssh_check +start_script +post_to_api_vm + +msg_info "Validating Storage" +while read -r line; do + TAG=$(echo $line | awk '{print $1}') + TYPE=$(echo $line | awk '{printf "%-10s", $2}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") +done < <(pvesm status -content images | awk 'NR>1') +VALID=$(pvesm status -content images | awk 'NR>1') +if [ -z "$VALID" ]; then + msg_error "Unable to detect a valid storage location." + exit +elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + STORAGE=${STORAGE_MENU[0]} +else + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + done +fi +msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." +msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." +msg_info "Retrieving the URL for the Ubuntu 25.04 Disk Image" +URL=https://cloud-images.ubuntu.com/plucky/current/plucky-server-cloudimg-amd64.img +sleep 2 +msg_ok "${CL}${BL}${URL}${CL}" +curl -f#SL -o "$(basename "$URL")" "$URL" +echo -en "\e[1A\e[0K" +FILE=$(basename $URL) +msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" + +STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') +case $STORAGE_TYPE in +nfs | dir | cifs) + DISK_EXT=".qcow2" + DISK_REF="$VMID/" + DISK_IMPORT="-format qcow2" + THIN="" + ;; +btrfs) + DISK_EXT=".raw" + DISK_REF="$VMID/" + DISK_IMPORT="-format raw" + FORMAT=",efitype=4m" + THIN="" + ;; +esac +for i in {0,1}; do + disk="DISK$i" + eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} + eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} +done + +msg_info "Creating a Ubuntu 25.04 VM" +qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null +qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null +qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -ide2 ${STORAGE}:cloudinit \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +DESCRIPTION=$( + cat < + + Logo + + +

ubuntu VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created a Ubuntu 25.04 VM ${CL}${BL}(${HN})" +if [ "$START_VM" == "yes" ]; then + msg_info "Starting Ubuntu 25.04 VM" + qm start $VMID + msg_ok "Started Ubuntu 25.04 VM" +fi +post_update_to_api "done" "none" +msg_ok "Completed Successfully!\n" +echo -e "Setup Cloud-Init before starting \n +More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n"