mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-06-27 17:54:58 +02:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7c1f30373 | |||
| 5b252892e1 | |||
| 8b99d3b1cc | |||
| 039470965b | |||
| 8e4a9829cf | |||
| f1eee5f5ed | |||
| fb14e6ae8b | |||
| 4142c5c2d3 | |||
| d85914530f | |||
| 3d9e67292b | |||
| ae60dc138b | |||
| 14a85a8591 | |||
| 1be64c1994 | |||
| 340695b9bd | |||
| ba31c925e3 | |||
| 868b405082 | |||
| 60266b9c17 | |||
| ea8b87fd7f | |||
| 58145d5bd3 | |||
| 9fbe2de1cb | |||
| c918dee5fe | |||
| 0774772b87 | |||
| dc26b8358e | |||
| 01a6c1ddec | |||
| 3e544b750d | |||
| 681924cb1a | |||
| 6ea04b7602 | |||
| b2d20799d8 | |||
| 324fa33d8c | |||
| 5aed3bdde5 | |||
| b3cef14a35 | |||
| 10c8ad6f28 | |||
| 37bcddd847 | |||
| 075a691198 | |||
| fbd083c9ae | |||
| edf167025f | |||
| 3abbefe3b0 | |||
| c356c3827a | |||
| 6f2d3da61f | |||
| f7acb76e80 | |||
| b2ecd86698 | |||
| df10a5ea53 | |||
| 3a65047ff9 | |||
| 5b01ff81e8 | |||
| 4e6cb56f06 | |||
| 470c0672fb | |||
| 62b7080477 | |||
| e193adad5a | |||
| aeb8dba809 | |||
| 3fc7008bbe |
+73
-2
@@ -486,16 +486,87 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
</details>
|
||||
|
||||
## 2026-06-24
|
||||
## 2026-06-27
|
||||
|
||||
### ❔ Uncategorized
|
||||
|
||||
- fix(endurain): replace Poetry/uv-pip backend setup with uv sync --frozen --no-dev [@Copilot](https://github.com/Copilot) ([#15429](https://github.com/community-scripts/ProxmoxVE/pull/15429))
|
||||
|
||||
## 2026-06-26
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- enabling rewirte module in apache [@d3v3lop3rDE](https://github.com/d3v3lop3rDE) ([#15360](https://github.com/community-scripts/ProxmoxVE/pull/15360))
|
||||
- Termix: Update Nginx configuration file paths [@xyzulu](https://github.com/xyzulu) ([#15397](https://github.com/community-scripts/ProxmoxVE/pull/15397))
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- Docuseal: use real SECRET_KEY_BASE for db:migrate on update [@MickLesk](https://github.com/MickLesk) ([#15411](https://github.com/community-scripts/ProxmoxVE/pull/15411))
|
||||
- bun: correct install for degoog [@MickLesk](https://github.com/MickLesk) ([#15412](https://github.com/community-scripts/ProxmoxVE/pull/15412))
|
||||
- fix databasus update/install errors [@asylumexp](https://github.com/asylumexp) ([#15403](https://github.com/community-scripts/ProxmoxVE/pull/15403))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- tools.func: fix setup_docker - don't abort update on docker pull failure [@MickLesk](https://github.com/MickLesk) ([#15410](https://github.com/community-scripts/ProxmoxVE/pull/15410))
|
||||
- fix(build.func): set /dev/kfd GID in fix_gpu_gids for AMD ROCm [@jamiej](https://github.com/jamiej) ([#15401](https://github.com/community-scripts/ProxmoxVE/pull/15401))
|
||||
- fix alpine mktmp error [@asylumexp](https://github.com/asylumexp) ([#15398](https://github.com/community-scripts/ProxmoxVE/pull/15398))
|
||||
|
||||
### 🧰 Tools
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- Refactor: reduce IP-Tag resource usage and clean up ShellCheck findings [@MickLesk](https://github.com/MickLesk) ([#15418](https://github.com/community-scripts/ProxmoxVE/pull/15418))
|
||||
- QoL: kernel-clean: Validate kernel selection input [@MickLesk](https://github.com/MickLesk) ([#15414](https://github.com/community-scripts/ProxmoxVE/pull/15414))
|
||||
- QoL: clean-lxcs exclude matching and set -e cancel handling [@MickLesk](https://github.com/MickLesk) ([#15413](https://github.com/community-scripts/ProxmoxVE/pull/15413))
|
||||
- QoL: Harden microcode download/install in microcode and pbs-microcode [@MickLesk](https://github.com/MickLesk) ([#15415](https://github.com/community-scripts/ProxmoxVE/pull/15415))
|
||||
- QoL: scaling-governor extend selection and guard missing cpufreq [@MickLesk](https://github.com/MickLesk) ([#15416](https://github.com/community-scripts/ProxmoxVE/pull/15416))
|
||||
|
||||
## 2026-06-25
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- Pinchflat ([#15367](https://github.com/community-scripts/ProxmoxVE/pull/15367))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- persist gramps-web configuration file after update [@operfesium](https://github.com/operfesium) ([#15394](https://github.com/community-scripts/ProxmoxVE/pull/15394))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- VM-Core: Update some Functions [@MickLesk](https://github.com/MickLesk) ([#15113](https://github.com/community-scripts/ProxmoxVE/pull/15113))
|
||||
|
||||
## 2026-06-24
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- SnapOtter ([#15368](https://github.com/community-scripts/ProxmoxVE/pull/15368))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- enabling rewirte module in apache [@d3v3lop3rDE](https://github.com/d3v3lop3rDE) ([#15360](https://github.com/community-scripts/ProxmoxVE/pull/15360))
|
||||
- watcharr: Increase default RAM allocation from 1024 to 2048 [@MickLesk](https://github.com/MickLesk) ([#15370](https://github.com/community-scripts/ProxmoxVE/pull/15370))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- Refactor LibreNMS: replace old install and update variant with tarball approach [@MickLesk](https://github.com/MickLesk) ([#15369](https://github.com/community-scripts/ProxmoxVE/pull/15369))
|
||||
|
||||
### 🗑️ Deleted Scripts
|
||||
|
||||
- delete wger [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15380](https://github.com/community-scripts/ProxmoxVE/pull/15380))
|
||||
- Delete ghost [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15377](https://github.com/community-scripts/ProxmoxVE/pull/15377))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- core: add SDN vnet selection in advanced install [@MickLesk](https://github.com/MickLesk) ([#15366](https://github.com/community-scripts/ProxmoxVE/pull/15366))
|
||||
- core: add var_http_proxy and var_http_no_proxy support [@MickLesk](https://github.com/MickLesk) ([#15225](https://github.com/community-scripts/ProxmoxVE/pull/15225))
|
||||
|
||||
## 2026-06-23
|
||||
|
||||
+3
-3
@@ -52,11 +52,11 @@ function update_script() {
|
||||
[[ "$MONGO_ARCH" == "arm64" ]] && MONGO_DIST="ubuntu2204"
|
||||
fetch_and_deploy_from_url "https://fastdl.mongodb.org/tools/db/mongodb-database-tools-${MONGO_DIST}-${MONGO_ARCH}-100.16.1.deb"
|
||||
fi
|
||||
ensure_dependencies mariadb-client
|
||||
mkdir -p /usr/local/mariadb-{10.6,12.1}/bin /usr/local/mysql-{5.7,8.0,8.4,9}/bin /usr/local/mongodb-database-tools/bin
|
||||
[[ -f /usr/bin/mongodump ]] && ln -sf /usr/bin/mongodump /usr/local/mongodb-database-tools/bin/mongodump
|
||||
[[ -f /usr/bin/mongorestore ]] && ln -sf /usr/bin/mongorestore /usr/local/mongodb-database-tools/bin/mongorestore
|
||||
# Create MariaDB and MySQL client symlinks for compatibility
|
||||
ensure_dependencies mariadb-client
|
||||
mkdir -p /usr/local/mariadb-{10.6,12.1}/bin /usr/local/mysql-{5.7,8.0,8.4,9}/bin /usr/local/mongodb-database-tools/bin
|
||||
for dir in /usr/local/mariadb-{10.6,12.1}/bin; do
|
||||
ln -sf /usr/bin/mariadb-dump "$dir/mariadb-dump"
|
||||
ln -sf /usr/bin/mariadb "$dir/mariadb"
|
||||
@@ -79,7 +79,7 @@ function update_script() {
|
||||
cd /opt/databasus/backend
|
||||
$STD go mod download
|
||||
$STD /root/go/bin/swag init -g cmd/main.go -o swagger
|
||||
$STD env CGO_ENABLED=0 GOOS=linux GOARCH=$(arch_resolve) go build -o databasus ./cmd/main.go
|
||||
$STD env CGO_ENABLED=0 GOOS=linux GOARCH=$(arch_resolve) go build -o databasus ./cmd
|
||||
mv /opt/databasus/backend/databasus /opt/databasus/databasus
|
||||
mkdir -p /opt/databasus/ui/build
|
||||
cp -r /opt/databasus/frontend/dist/* /opt/databasus/ui/build/
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ function update_script() {
|
||||
create_backup /opt/degoog/.env \
|
||||
/opt/degoog/data
|
||||
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
if [[ ! -x /root/.bun/bin/bun ]]; then
|
||||
msg_info "Installing Bun"
|
||||
export BUN_INSTALL="/root/.bun"
|
||||
curl -fsSL https://bun.sh/install | $STD bash
|
||||
@@ -52,7 +52,7 @@ function update_script() {
|
||||
msg_ok "Updated Valkey"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "degoog" "fccview/degoog" "prebuild" "latest" "/opt/degoog" "degoog_*_prebuild.tar.gz"
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "curl-impersonate" "lexiforest/curl-impersonate" "prebuild" "latest" "/usr/local/bin" "curl-impersonate-v*.$(uname -m)-linux-gnu.tar.gz"
|
||||
fetch_and_deploy_gh_release "curl-impersonate" "lexiforest/curl-impersonate" "prebuild" "latest" "/usr/local/bin" "curl-impersonate-v*.$(uname -m)-linux-gnu.tar.gz"
|
||||
|
||||
restore_backup
|
||||
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ function update_script() {
|
||||
eval "$(rbenv init - bash)" 2>/dev/null || true
|
||||
export RAILS_ENV=production
|
||||
export NODE_ENV=production
|
||||
export SECRET_KEY_BASE_DUMMY=1
|
||||
mkdir -p /opt/docuseal/tmp
|
||||
set -a
|
||||
source /opt/docuseal/.env
|
||||
set +a
|
||||
|
||||
+1
-4
@@ -63,10 +63,7 @@ function update_script() {
|
||||
cd /opt/endurain/backend
|
||||
UV_VERSION=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18")
|
||||
UV_VERSION="$UV_VERSION" setup_uv
|
||||
$STD poetry export -f requirements.txt --output requirements.txt --without-hashes
|
||||
$STD uv venv --clear
|
||||
$STD uv pip install -r requirements.txt
|
||||
$STD uv pip install pytz
|
||||
$STD uv sync --frozen --no-dev
|
||||
msg_ok "Backend Updated"
|
||||
|
||||
msg_info "Starting Service"
|
||||
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: fabrice1236
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://ghost.org/ | Github: https://github.com/TryGhost/Ghost
|
||||
|
||||
APP="Ghost"
|
||||
var_tags="${var_tags:-cms;blog}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-5}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_arm64="${var_arm64:-yes}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
setup_mariadb
|
||||
NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs
|
||||
ensure_dependencies git
|
||||
|
||||
msg_info "Updating Ghost"
|
||||
if command -v ghost &>/dev/null; then
|
||||
current_version=$(ghost version | grep 'Ghost-CLI version' | awk '{print $3}')
|
||||
latest_version=$(npm show ghost-cli version)
|
||||
if [ "$current_version" != "$latest_version" ]; then
|
||||
msg_info "Updating ${APP} from version v${current_version} to v${latest_version}"
|
||||
$STD npm install -g ghost-cli@latest
|
||||
msg_ok "Updated successfully!"
|
||||
else
|
||||
msg_ok "${APP} is already at v${current_version}"
|
||||
fi
|
||||
else
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
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 "${GATEWAY}${BGN}http://${IP}:2368${CL}"
|
||||
@@ -85,8 +85,13 @@ function update_script() {
|
||||
cd /opt/gramps-web/frontend
|
||||
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
||||
|
||||
create_backup /opt/gramps-web/frontend/dist/config.js
|
||||
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
|
||||
restore_backup
|
||||
|
||||
msg_ok "Updated Gramps Web Frontend"
|
||||
|
||||
msg_info "Starting Service"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
________ __
|
||||
/ ____/ /_ ____ _____/ /_
|
||||
/ / __/ __ \/ __ \/ ___/ __/
|
||||
/ /_/ / / / / /_/ (__ ) /_
|
||||
\____/_/ /_/\____/____/\__/
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
____ _ __ ______ __
|
||||
/ __ \(_)___ _____/ /_ / __/ /___ _/ /_
|
||||
/ /_/ / / __ \/ ___/ __ \/ /_/ / __ `/ __/
|
||||
/ ____/ / / / / /__/ / / / __/ / /_/ / /_
|
||||
/_/ /_/_/ /_/\___/_/ /_/_/ /_/\__,_/\__/
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
_____ ____ __ __
|
||||
/ ___/____ ____ _____ / __ \/ /_/ /____ _____
|
||||
\__ \/ __ \/ __ `/ __ \/ / / / __/ __/ _ \/ ___/
|
||||
___/ / / / / /_/ / /_/ / /_/ / /_/ /_/ __/ /
|
||||
/____/_/ /_/\__,_/ .___/\____/\__/\__/\___/_/
|
||||
/_/
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
_ ______ ____ _____
|
||||
| | /| / / __ `/ _ \/ ___/
|
||||
| |/ |/ / /_/ / __/ /
|
||||
|__/|__/\__, /\___/_/
|
||||
/____/
|
||||
+25
-17
@@ -24,27 +24,35 @@ function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [ ! -d /opt/librenms ]; then
|
||||
if [[ ! -d /opt/librenms ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
setup_mariadb
|
||||
ensure_dependencies git
|
||||
if [[ ! -d /opt/librenms/.git ]]; then
|
||||
msg_info "Initializing LibreNMS git metadata"
|
||||
LIBRENMS_VERSION=$(cat ~/.librenms 2>/dev/null)
|
||||
cd /opt/librenms
|
||||
git init -q
|
||||
git remote add origin https://github.com/librenms/librenms.git
|
||||
git fetch --depth 1 origin "refs/tags/v${LIBRENMS_VERSION}" 2>/dev/null ||
|
||||
git fetch --depth 1 origin "refs/tags/${LIBRENMS_VERSION}" 2>/dev/null || true
|
||||
git checkout -qf FETCH_HEAD 2>/dev/null || true
|
||||
chown -R librenms:librenms .git
|
||||
msg_ok "Initialized LibreNMS git metadata"
|
||||
if check_for_gh_release "librenms" "librenms/librenms"; then
|
||||
msg_info "Stopping Services"
|
||||
systemctl stop php8.4-fpm librenms-scheduler.timer
|
||||
msg_ok "Stopped Services"
|
||||
|
||||
create_backup /opt/librenms/.env /opt/librenms/config.php /opt/librenms/rrd
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "librenms" "librenms/librenms" "tarball"
|
||||
restore_backup
|
||||
|
||||
msg_info "Updating LibreNMS"
|
||||
mkdir -p /opt/librenms/{rrd,logs,bootstrap/cache,storage}
|
||||
chown -R librenms:librenms /opt/librenms
|
||||
chmod 771 /opt/librenms
|
||||
chmod -R ug=rwX /opt/librenms/bootstrap/cache /opt/librenms/storage /opt/librenms/logs /opt/librenms/rrd
|
||||
$STD su - librenms -s /bin/bash -c "cd /opt/librenms && uv venv --clear .venv && source .venv/bin/activate && uv pip install -r requirements.txt"
|
||||
$STD su - librenms -s /bin/bash -c "cd /opt/librenms && COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev"
|
||||
$STD su - librenms -s /bin/bash -c "cd /opt/librenms && php8.4 artisan optimize:clear"
|
||||
$STD su - librenms -s /bin/bash -c "cd /opt/librenms && php8.4 artisan migrate --force --isolated"
|
||||
msg_ok "Updated LibreNMS"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start php8.4-fpm librenms-scheduler.timer
|
||||
msg_ok "Started Services"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
msg_info "Updating LibreNMS"
|
||||
$STD su - librenms -s /bin/bash -c 'cd /opt/librenms && ./daily.sh'
|
||||
msg_ok "Updated LibreNMS"
|
||||
exit
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: nnsense
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/kieraneglin/pinchflat
|
||||
|
||||
APP="Pinchflat"
|
||||
var_tags="${var_tags:-media;youtube;downloader}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-8}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
var_gpu="${var_gpu:-yes}"
|
||||
var_arm64="${var_arm64:-yes}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/pinchflat/app ]]; then
|
||||
msg_error "No ${APP} installation found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if check_for_gh_release "pinchflat" "kieraneglin/pinchflat"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop pinchflat
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pinchflat" "kieraneglin/pinchflat" "tarball" "latest" "/opt/pinchflat-src"
|
||||
|
||||
msg_info "Building Pinchflat"
|
||||
cd /opt/pinchflat-src
|
||||
export MIX_ENV=prod
|
||||
export ERL_FLAGS="+JPperf true"
|
||||
$STD mix deps.get --only prod
|
||||
$STD mix deps.compile
|
||||
$STD yarn --cwd assets install
|
||||
$STD mix assets.deploy
|
||||
$STD mix compile
|
||||
$STD mix release --overwrite
|
||||
rm -rf /opt/pinchflat/app
|
||||
cp -r _build/prod/rel/pinchflat /opt/pinchflat/app
|
||||
msg_ok "Built Pinchflat"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start pinchflat
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
msg_ok "Completed Successfully!\n"
|
||||
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8945${CL}"
|
||||
@@ -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-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://snapotter.com
|
||||
|
||||
APP="SnapOtter"
|
||||
var_tags="${var_tags:-media;image}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-20}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
var_arm64="${var_arm64:-no}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/snapotter ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "snapotter" "snapotter-hq/SnapOtter"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop snapotter
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "snapotter" "snapotter-hq/SnapOtter" "tarball"
|
||||
|
||||
msg_info "Updating SnapOtter"
|
||||
cd /opt/snapotter
|
||||
$STD npm pkg delete scripts.prepare
|
||||
$STD pnpm install --frozen-lockfile
|
||||
$STD pnpm --filter @snapotter/web build
|
||||
sed -i 's/mediapipe==0.10.21/mediapipe>=0.10.21/' /opt/snapotter/docker/feature-manifest.json
|
||||
msg_ok "Updated SnapOtter"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start snapotter
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed Successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1349${CL}"
|
||||
+1
-1
@@ -200,7 +200,7 @@ EOF
|
||||
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
|
||||
curl -fsSL "https://raw.githubusercontent.com/Termix-SSH/Termix/main/docker/nginx.conf" -o /etc/nginx/nginx.conf
|
||||
sed -i '/^master_process/d' /etc/nginx/nginx.conf
|
||||
sed -i 's|pid /tmp/nginx/nginx.pid;|pid /run/nginx.pid;|' /etc/nginx/nginx.conf
|
||||
sed -i '/^pid \/app\/nginx/d' /etc/nginx/nginx.conf
|
||||
sed -i 's|error_log /tmp/nginx/error.log|error_log /var/log/nginx/error.log|' /etc/nginx/nginx.conf
|
||||
sed -i 's|access_log /tmp/nginx/access.log|access_log /var/log/nginx/access.log|' /etc/nginx/nginx.conf
|
||||
sed -i 's|/app/html|/opt/termix/html|g' /etc/nginx/nginx.conf
|
||||
|
||||
+2
-7
@@ -33,17 +33,12 @@ function update_script() {
|
||||
systemctl stop tunarr
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Creating Backup"
|
||||
if [ -d "/root/.local/share/tunarr" ]; then
|
||||
tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /root/.local/share/tunarr $STD
|
||||
msg_ok "Backup Created"
|
||||
else
|
||||
msg_error "Backup failed: /root/.local/share/tunarr does not exist"
|
||||
fi
|
||||
create_backup /root/.local/share/tunarr
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "prebuild" "latest" "/opt/tunarr" "*linux-$(arch_resolve "x64" "arm64").tar.gz"
|
||||
cd /opt/tunarr
|
||||
mv tunarr* tunarr
|
||||
restore_backup
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start tunarr
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
APP="Watcharr"
|
||||
var_tags="${var_tags:-media}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/wger-project/wger
|
||||
|
||||
APP="wger"
|
||||
var_tags="${var_tags:-management;fitness}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-8}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_arm64="${var_arm64:-yes}"
|
||||
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/wger ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "wger" "wger-project/wger"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop redis-server nginx celery celery-beat wger
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Backing up Data"
|
||||
cp -r /opt/wger/media /opt/wger_media_backup
|
||||
cp /opt/wger/.env /opt/wger_env_backup
|
||||
msg_ok "Backed up Data"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball"
|
||||
|
||||
msg_info "Restoring Data"
|
||||
cp -r /opt/wger_media_backup/. /opt/wger/media
|
||||
cp /opt/wger_env_backup /opt/wger/.env
|
||||
rm -rf /opt/wger_media_backup /opt/wger_env_backup
|
||||
|
||||
msg_ok "Restored Data"
|
||||
|
||||
msg_info "Updating wger"
|
||||
cd /opt/wger
|
||||
set -a && source /opt/wger/.env && set +a
|
||||
export DJANGO_SETTINGS_MODULE=settings.main
|
||||
$STD uv pip install .
|
||||
$STD npm install
|
||||
$STD npm run build:css:sass
|
||||
$STD uv run python manage.py migrate
|
||||
$STD uv run python manage.py collectstatic --no-input
|
||||
msg_ok "Updated wger"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start redis-server nginx celery celery-beat wger
|
||||
msg_ok "Started Services"
|
||||
msg_ok "Updated Successfully"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed Successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW}Access it using the following URL:${CL}"
|
||||
echo -e "${GATEWAY}${BGN}http://${IP}:3000${CL}"
|
||||
@@ -33,7 +33,7 @@ done
|
||||
# Install MongoDB Database Tools via direct .deb (no APT repo for Debian 13)
|
||||
[[ "$(get_os_info id)" == "ubuntu" ]] && MONGO_DIST="ubuntu2204" || MONGO_DIST="debian12"
|
||||
# MongoDB only publishes arm64 builds for Ubuntu
|
||||
[[ "$MONGO_ARCH" == "arm64" ]] && MONGO_DIST="ubuntu2204"
|
||||
[[ "$(arch_resolve "x86_64" "arm64")" == "arm64" ]] && MONGO_DIST="ubuntu2204"
|
||||
MONGO_VERSION=$(get_latest_gh_tag "mongodb/mongo-tools" "100." || echo "100.16.1")
|
||||
fetch_and_deploy_from_url "https://fastdl.mongodb.org/tools/db/mongodb-database-tools-${MONGO_DIST}-$(arch_resolve "x86_64" "arm64")-${MONGO_VERSION}.deb" ""
|
||||
mkdir -p /usr/local/mongodb-database-tools/bin
|
||||
@@ -65,7 +65,7 @@ $STD go mod tidy
|
||||
$STD go mod download
|
||||
$STD go install github.com/swaggo/swag/cmd/swag@latest
|
||||
$STD /root/go/bin/swag init -g cmd/main.go -o swagger
|
||||
$STD env CGO_ENABLED=0 GOOS=linux GOARCH=$(arch_resolve) go build -o databasus ./cmd/main.go
|
||||
$STD env CGO_ENABLED=0 GOOS=linux GOARCH=$(arch_resolve) go build -o databasus ./cmd
|
||||
mv /opt/databasus/backend/databasus /opt/databasus/databasus
|
||||
mkdir -p /databasus-data/{pgdata,temp,backups,data,logs}
|
||||
mkdir -p /opt/databasus/ui/build
|
||||
|
||||
@@ -83,14 +83,7 @@ msg_info "Setting up Backend"
|
||||
cd /opt/endurain/backend
|
||||
UV_VERSION=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18")
|
||||
UV_VERSION="$UV_VERSION" setup_uv
|
||||
$STD uv tool install poetry
|
||||
$STD uv tool update-shell
|
||||
export PATH="/root/.local/bin:$PATH"
|
||||
$STD poetry self add poetry-plugin-export
|
||||
$STD poetry export -f requirements.txt --output requirements.txt --without-hashes
|
||||
$STD uv venv --clear
|
||||
$STD uv pip install -r requirements.txt
|
||||
$STD uv pip install pytz
|
||||
$STD uv sync --frozen --no-dev
|
||||
msg_ok "Setup Backend"
|
||||
|
||||
msg_info "Creating Service"
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: fabrice1236
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://ghost.org/ | Github: https://github.com/TryGhost/Ghost
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
nginx \
|
||||
ca-certificates \
|
||||
libjemalloc2 \
|
||||
git
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
setup_mariadb
|
||||
MARIADB_DB_NAME="ghost" MARIADB_DB_USER="ghostuser" setup_mariadb_db
|
||||
NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs
|
||||
|
||||
msg_info "Installing Ghost CLI"
|
||||
$STD npm install ghost-cli@latest -g
|
||||
msg_ok "Installed Ghost CLI"
|
||||
|
||||
msg_info "Creating Service"
|
||||
$STD adduser --disabled-password --gecos "Ghost user" ghost-user
|
||||
$STD usermod -aG sudo ghost-user
|
||||
echo "ghost-user ALL=(ALL) NOPASSWD:ALL" | tee /etc/sudoers.d/ghost-user
|
||||
mkdir -p /var/www/ghost
|
||||
chown -R ghost-user:ghost-user /var/www/ghost
|
||||
chmod 775 /var/www/ghost
|
||||
$STD sudo -u ghost-user -H sh -c "cd /var/www/ghost && ghost install --db=mysql --dbhost=localhost --dbuser=$MARIADB_DB_USER --dbpass=$MARIADB_DB_PASS --dbname=$MARIADB_DB_NAME --url=http://localhost:2368 --no-prompt --no-setup-nginx --no-setup-ssl --no-setup-mysql --enable --start --ip 0.0.0.0"
|
||||
rm /etc/sudoers.d/ghost-user
|
||||
msg_ok "Creating Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -17,7 +17,6 @@ msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
acl \
|
||||
fping \
|
||||
git \
|
||||
graphviz \
|
||||
imagemagick \
|
||||
mtr-tiny \
|
||||
@@ -65,16 +64,6 @@ EOF
|
||||
chown -R librenms:librenms /opt/librenms
|
||||
chmod 771 /opt/librenms
|
||||
chmod -R ug=rwX /opt/librenms/bootstrap/cache /opt/librenms/storage /opt/librenms/logs /opt/librenms/rrd
|
||||
if [[ ! -d /opt/librenms/.git ]]; then
|
||||
LIBRENMS_VERSION=$(cat ~/.librenms 2>/dev/null)
|
||||
cd /opt/librenms
|
||||
git init -q
|
||||
git remote add origin https://github.com/librenms/librenms.git
|
||||
git fetch --depth 1 origin "refs/tags/v${LIBRENMS_VERSION}" 2>/dev/null ||
|
||||
git fetch --depth 1 origin "refs/tags/${LIBRENMS_VERSION}" 2>/dev/null || true
|
||||
git checkout -qf FETCH_HEAD 2>/dev/null || true
|
||||
chown -R librenms:librenms .git
|
||||
fi
|
||||
msg_ok "Configured LibreNMS"
|
||||
|
||||
msg_info "Configure MariaDB"
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: nnsense
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/kieraneglin/pinchflat
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
build-essential \
|
||||
elixir \
|
||||
erlang-dev \
|
||||
erlang-inets \
|
||||
erlang-os-mon \
|
||||
erlang-runtime-tools \
|
||||
erlang-syntax-tools \
|
||||
erlang-xmerl \
|
||||
git \
|
||||
libsqlite3-dev \
|
||||
locales \
|
||||
openssh-client \
|
||||
openssl \
|
||||
pipx \
|
||||
pkg-config \
|
||||
procps \
|
||||
python3-mutagen \
|
||||
zip
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs
|
||||
FFMPEG_TYPE="binary" setup_ffmpeg
|
||||
setup_hwaccel
|
||||
fetch_and_deploy_gh_release "deno" "denoland/deno" "prebuild" "latest" "/usr/local/bin" "deno-$(arch_resolve "x86_64" "aarch64")-unknown-linux-gnu.zip"
|
||||
fetch_and_deploy_gh_release "yt-dlp" "yt-dlp/yt-dlp" "singlefile" "latest" "/usr/local/bin" "yt-dlp_$(arch_resolve "linux" "linux_aarch64")"
|
||||
|
||||
msg_info "Installing Apprise"
|
||||
export PIPX_HOME=/opt/pipx
|
||||
export PIPX_BIN_DIR=/usr/local/bin
|
||||
$STD pipx install apprise
|
||||
msg_ok "Installed Apprise"
|
||||
|
||||
fetch_and_deploy_gh_release "pinchflat" "kieraneglin/pinchflat" "tarball" "latest" "/opt/pinchflat-src"
|
||||
|
||||
msg_info "Configuring Pinchflat"
|
||||
CONFIG_PATH="/opt/pinchflat/config"
|
||||
DOWNLOADS_PATH="/opt/pinchflat/downloads"
|
||||
mkdir -p \
|
||||
/etc/elixir_tzdata_data \
|
||||
/etc/yt-dlp/plugins \
|
||||
/opt/pinchflat/app \
|
||||
"$CONFIG_PATH/db" \
|
||||
"$CONFIG_PATH/extras" \
|
||||
"$CONFIG_PATH/logs" \
|
||||
"$CONFIG_PATH/metadata" \
|
||||
"$DOWNLOADS_PATH"
|
||||
ln -sfn "$CONFIG_PATH" /config
|
||||
ln -sfn "$DOWNLOADS_PATH" /downloads
|
||||
chmod ugo+rw /etc/elixir_tzdata_data /etc/yt-dlp /etc/yt-dlp/plugins
|
||||
|
||||
cat <<EOF >/opt/pinchflat/.env
|
||||
LANG=en_US.UTF-8
|
||||
LANGUAGE=en_US:en
|
||||
LC_ALL=en_US.UTF-8
|
||||
MIX_ENV=prod
|
||||
PHX_SERVER=true
|
||||
PORT=8945
|
||||
RUN_CONTEXT=selfhosted
|
||||
CONFIG_PATH=${CONFIG_PATH}
|
||||
MEDIA_PATH=${DOWNLOADS_PATH}
|
||||
TZ_DATA_PATH=/etc/elixir_tzdata_data
|
||||
SECRET_KEY_BASE=$(openssl rand -base64 48)
|
||||
EOF
|
||||
msg_ok "Configured Pinchflat"
|
||||
|
||||
msg_info "Building Pinchflat"
|
||||
cd /opt/pinchflat-src
|
||||
export MIX_ENV=prod
|
||||
export ERL_FLAGS="+JPperf true"
|
||||
$STD mix local.hex --force
|
||||
$STD mix local.rebar --force
|
||||
$STD mix deps.get --only prod
|
||||
$STD mix deps.compile
|
||||
$STD yarn --cwd assets install
|
||||
$STD mix assets.deploy
|
||||
$STD mix compile
|
||||
$STD mix release --overwrite
|
||||
rm -rf /opt/pinchflat/app
|
||||
cp -r _build/prod/rel/pinchflat /opt/pinchflat/app
|
||||
msg_ok "Built Pinchflat"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/pinchflat.service
|
||||
[Unit]
|
||||
Description=Pinchflat
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
EnvironmentFile=/opt/pinchflat/.env
|
||||
WorkingDirectory=/opt/pinchflat/app
|
||||
UMask=0022
|
||||
ExecStartPre=/opt/pinchflat/app/bin/check_file_permissions
|
||||
ExecStartPre=/opt/pinchflat/app/bin/migrate
|
||||
ExecStart=/opt/pinchflat/app/bin/pinchflat start
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now pinchflat
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://snapotter.com
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
imagemagick \
|
||||
ghostscript \
|
||||
potrace \
|
||||
libopenjp2-tools \
|
||||
libegl1 \
|
||||
libwayland-client0 \
|
||||
libwayland-cursor0 \
|
||||
libwayland-egl1 \
|
||||
libxkbcommon0 \
|
||||
libxkbcommon-x11-0 \
|
||||
libxcursor1 \
|
||||
python3 \
|
||||
python3-dev \
|
||||
gcc \
|
||||
g++
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
PYTHON_VERSION="3.11" setup_uv
|
||||
NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs
|
||||
fetch_and_deploy_gh_release "caire" "esimov/caire" "prebuild" "latest" "/usr/local/bin" "caire-*-linux-amd64.tar.gz"
|
||||
fetch_and_deploy_gh_release "snapotter" "snapotter-hq/SnapOtter" "prebuild" "latest" "/opt/snapotter" "snapotter-*-linux-amd64.tar.gz"
|
||||
|
||||
msg_info "Setting up Python Environment"
|
||||
mkdir -p /opt/snapotter_data/ai/models/rembg
|
||||
$STD uv python install 3.11
|
||||
$STD uv venv --seed --python 3.11 /opt/snapotter_data/ai/venv
|
||||
#if [[ -f /opt/snapotter/packages/ai/python/requirements.txt ]]; then
|
||||
# $STD uv pip install \
|
||||
# --python /opt/snapotter_data/ai/venv/bin/python \
|
||||
# -r /opt/snapotter/packages/ai/python/requirements.txt
|
||||
#fi
|
||||
ln -sfn /opt/snapotter /app
|
||||
msg_ok "Set up Python Environment"
|
||||
|
||||
msg_info "Configuring SnapOtter"
|
||||
mkdir -p /opt/snapotter_data/files
|
||||
mkdir -p /tmp/snapotter-workspace
|
||||
|
||||
cat <<EOF >/opt/snapotter_data/.env
|
||||
PORT=1349
|
||||
NODE_ENV=production
|
||||
DB_PATH=/opt/snapotter_data/snapotter.db
|
||||
WORKSPACE_PATH=/tmp/snapotter-workspace
|
||||
FILES_STORAGE_PATH=/opt/snapotter_data/files
|
||||
PYTHON_VENV_PATH=/opt/snapotter_data/ai/venv
|
||||
MODELS_PATH=/opt/snapotter_data/ai/models
|
||||
DATA_DIR=/opt/snapotter_data
|
||||
FEATURE_MANIFEST_PATH=/opt/snapotter/docker/feature-manifest.json
|
||||
U2NET_HOME=/opt/snapotter_data/ai/models/rembg
|
||||
AUTH_ENABLED=true
|
||||
DEFAULT_USERNAME=admin
|
||||
DEFAULT_PASSWORD=admin
|
||||
LOG_LEVEL=info
|
||||
TRUST_PROXY=true
|
||||
FILE_MAX_AGE_HOURS=72
|
||||
CLEANUP_INTERVAL_MINUTES=60
|
||||
ANALYTICS_ENABLED=false
|
||||
EOF
|
||||
msg_ok "Configured SnapOtter"
|
||||
|
||||
msg_info "Creating Service"
|
||||
PNPM_BIN="$(command -v pnpm)"
|
||||
cat <<EOF >/etc/systemd/system/snapotter.service
|
||||
[Unit]
|
||||
Description=SnapOtter Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/snapotter
|
||||
EnvironmentFile=/opt/snapotter_data/.env
|
||||
ExecStart=${PNPM_BIN} --filter @snapotter/api run start
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now snapotter
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -1,182 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/wger-project/wger
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
build-essential \
|
||||
nginx \
|
||||
redis-server \
|
||||
libpq-dev
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
NODE_VERSION="22" NODE_MODULE="sass" setup_nodejs
|
||||
setup_uv
|
||||
PG_VERSION="16" setup_postgresql
|
||||
PG_DB_NAME="wger" PG_DB_USER="wger" setup_postgresql_db
|
||||
fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball"
|
||||
|
||||
msg_info "Setting up wger"
|
||||
mkdir -p /opt/wger/{static,media}
|
||||
chmod o+w /opt/wger/media
|
||||
cd /opt/wger
|
||||
|
||||
$STD npm install
|
||||
$STD npm run build:css:sass
|
||||
$STD uv venv
|
||||
$STD uv pip install . --group docker
|
||||
SECRET_KEY=$(openssl rand -base64 40)
|
||||
cat <<EOF >/opt/wger/.env
|
||||
DJANGO_SETTINGS_MODULE=settings.main
|
||||
PYTHONPATH=/opt/wger
|
||||
|
||||
DJANGO_DB_ENGINE=django.db.backends.postgresql
|
||||
DJANGO_DB_DATABASE=${PG_DB_NAME}
|
||||
DJANGO_DB_USER=${PG_DB_USER}
|
||||
DJANGO_DB_PASSWORD=${PG_DB_PASS}
|
||||
DJANGO_DB_HOST=localhost
|
||||
DJANGO_DB_PORT=5432
|
||||
DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}
|
||||
|
||||
DJANGO_MEDIA_ROOT=/opt/wger/media
|
||||
DJANGO_STATIC_ROOT=/opt/wger/static
|
||||
DJANGO_STATIC_URL=/static/
|
||||
|
||||
ALLOWED_HOSTS=${LOCAL_IP},localhost,127.0.0.1
|
||||
CSRF_TRUSTED_ORIGINS=http://${LOCAL_IP}:3000
|
||||
|
||||
USE_X_FORWARDED_HOST=True
|
||||
SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,http
|
||||
|
||||
DJANGO_CACHE_BACKEND=django_redis.cache.RedisCache
|
||||
DJANGO_CACHE_LOCATION=redis://127.0.0.1:6379/1
|
||||
DJANGO_CACHE_TIMEOUT=300
|
||||
DJANGO_CACHE_CLIENT_CLASS=django_redis.client.DefaultClient
|
||||
AXES_CACHE_ALIAS=default
|
||||
|
||||
USE_CELERY=True
|
||||
CELERY_BROKER=redis://127.0.0.1:6379/2
|
||||
CELERY_BACKEND=redis://127.0.0.1:6379/2
|
||||
|
||||
SITE_URL=http://${LOCAL_IP}:3000
|
||||
SECRET_KEY=${SECRET_KEY}
|
||||
EOF
|
||||
set -a && source /opt/wger/.env && set +a
|
||||
$STD uv run wger bootstrap
|
||||
$STD uv run python manage.py collectstatic --no-input
|
||||
cat <<EOF | uv run python manage.py shell
|
||||
from django.contrib.auth import get_user_model
|
||||
User = get_user_model()
|
||||
|
||||
user, created = User.objects.get_or_create(
|
||||
username="admin",
|
||||
defaults={"email": "admin@localhost"},
|
||||
)
|
||||
|
||||
if created:
|
||||
user.set_password("${PG_DB_PASS}")
|
||||
user.is_superuser = True
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
EOF
|
||||
msg_ok "Set up wger"
|
||||
msg_info "Creating Config and Services"
|
||||
cat <<EOF >/etc/systemd/system/wger.service
|
||||
[Unit]
|
||||
Description=wger Gunicorn
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
WorkingDirectory=/opt/wger
|
||||
EnvironmentFile=/opt/wger/.env
|
||||
ExecStart=/opt/wger/.venv/bin/gunicorn \
|
||||
--bind 127.0.0.1:8000 \
|
||||
--workers 3 \
|
||||
--threads 2 \
|
||||
--timeout 120 \
|
||||
wger.wsgi:application
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
cat <<EOF >/etc/systemd/system/celery.service
|
||||
[Unit]
|
||||
Description=wger Celery Worker
|
||||
After=network.target redis-server.service
|
||||
Requires=redis-server.service
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/wger
|
||||
EnvironmentFile=/opt/wger/.env
|
||||
ExecStart=/opt/wger/.venv/bin/celery -A wger worker -l info
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
mkdir -p /var/lib/wger/celery
|
||||
chmod 700 /var/lib/wger/celery
|
||||
cat <<EOF >/etc/systemd/system/celery-beat.service
|
||||
[Unit]
|
||||
Description=wger Celery Beat
|
||||
After=network.target redis-server.service
|
||||
Requires=redis-server.service
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/wger
|
||||
EnvironmentFile=/opt/wger/.env
|
||||
ExecStart=/opt/wger/.venv/bin/celery -A wger beat -l info \
|
||||
--schedule /var/lib/wger/celery/celerybeat-schedule
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
cat <<'EOF' >/etc/nginx/sites-available/wger
|
||||
server {
|
||||
listen 3000;
|
||||
server_name _;
|
||||
|
||||
client_max_body_size 20M;
|
||||
|
||||
location /static/ {
|
||||
alias /opt/wger/static/;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
location /media/ {
|
||||
alias /opt/wger/media/;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
$STD rm -f /etc/nginx/sites-enabled/default
|
||||
$STD ln -sf /etc/nginx/sites-available/wger /etc/nginx/sites-enabled/wger
|
||||
systemctl enable -q --now redis-server nginx wger celery celery-beat
|
||||
systemctl restart nginx
|
||||
msg_ok "Created Config and Services"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
+62
-17
@@ -519,6 +519,19 @@ validate_bridge() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# validate_sdn_vnet()
|
||||
#
|
||||
# - Validates that an SDN vnet exists in the cluster config
|
||||
# ------------------------------------------------------------------------------
|
||||
validate_sdn_vnet() {
|
||||
local vnet="$1"
|
||||
[[ -z "$vnet" ]] && return 1
|
||||
[[ -f /etc/pve/sdn/vnets.cfg ]] && grep -qE "^vnet:[[:space:]]*${vnet}([[:space:]]|$)" /etc/pve/sdn/vnets.cfg && return 0
|
||||
command -v pvesh &>/dev/null && pvesh get "/cluster/sdn/vnets/${vnet}" &>/dev/null && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# validate_gateway_in_subnet()
|
||||
#
|
||||
@@ -964,6 +977,7 @@ base_settings() {
|
||||
HN="$requested_hostname"
|
||||
|
||||
BRG=${var_brg:-"vmbr0"}
|
||||
SDN_VNET=${var_sdn_vnet:-""}
|
||||
NET=${var_net:-"dhcp"}
|
||||
|
||||
# Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24)
|
||||
@@ -1078,7 +1092,7 @@ load_vars_file() {
|
||||
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
|
||||
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
|
||||
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
|
||||
var_post_install
|
||||
var_post_install var_sdn_vnet
|
||||
)
|
||||
|
||||
# Whitelist check helper
|
||||
@@ -1253,6 +1267,12 @@ load_vars_file() {
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
var_sdn_vnet)
|
||||
if [[ -n "$var_val" ]] && ! validate_sdn_vnet "$var_val"; then
|
||||
msg_warn "SDN vnet '$var_val' from $file not found, ignoring"
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
var_http_proxy)
|
||||
if [[ -n "$var_val" ]] && ! [[ "$var_val" =~ ^https?://[^[:space:]]+(:[0-9]+)?/?$ ]]; then
|
||||
msg_warn "Invalid HTTP proxy URL '$var_val' in $file, ignoring"
|
||||
@@ -1308,7 +1328,7 @@ default_var_settings() {
|
||||
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
|
||||
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
|
||||
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
|
||||
var_post_install
|
||||
var_post_install var_sdn_vnet
|
||||
)
|
||||
|
||||
# Snapshot: environment variables (highest precedence)
|
||||
@@ -1491,7 +1511,7 @@ if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then
|
||||
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
|
||||
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
|
||||
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
|
||||
var_post_install
|
||||
var_post_install var_sdn_vnet
|
||||
)
|
||||
fi
|
||||
|
||||
@@ -1705,6 +1725,7 @@ _build_current_app_vars_tmp() {
|
||||
|
||||
[ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")"
|
||||
[ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")"
|
||||
[ -n "${var_sdn_vnet:-}" ] && echo "var_sdn_vnet=$(_sanitize_value "${var_sdn_vnet}")"
|
||||
|
||||
[ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")"
|
||||
[ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")"
|
||||
@@ -1865,6 +1886,7 @@ advanced_settings() {
|
||||
local _core_count="${var_cpu:-1}"
|
||||
local _ram_size="${var_ram:-1024}"
|
||||
local _bridge="${var_brg:-vmbr0}"
|
||||
local _sdn_vnet="${var_sdn_vnet:-}"
|
||||
local _net="${var_net:-dhcp}"
|
||||
local _gate="${var_gateway:-}"
|
||||
local _ipv6_method="${var_ipv6_method:-auto}"
|
||||
@@ -1946,6 +1968,11 @@ advanced_settings() {
|
||||
fi
|
||||
done <<<"$BRIDGES"
|
||||
fi
|
||||
if [[ -f /etc/pve/sdn/vnets.cfg ]]; then
|
||||
while IFS= read -r vnet; do
|
||||
[[ -n "$vnet" ]] && BRIDGE_MENU_OPTIONS+=("sdn:${vnet}" "[SDN] ${vnet}")
|
||||
done < <(awk '/^vnet:/{print $2}' /etc/pve/sdn/vnets.cfg 2>/dev/null)
|
||||
fi
|
||||
}
|
||||
_detect_bridges
|
||||
|
||||
@@ -2178,8 +2205,18 @@ advanced_settings() {
|
||||
if [[ "$bridge_test" == "__other__" || "$bridge_test" == -* ]]; then
|
||||
continue
|
||||
fi
|
||||
if validate_bridge "$bridge_test"; then
|
||||
if [[ "$bridge_test" == sdn:* ]]; then
|
||||
local vnet_test="${bridge_test#sdn:}"
|
||||
if validate_sdn_vnet "$vnet_test"; then
|
||||
_sdn_vnet="$vnet_test"
|
||||
_bridge="${var_brg:-vmbr0}"
|
||||
((STEP++))
|
||||
else
|
||||
whiptail --msgbox "SDN vnet '$vnet_test' is not configured on this cluster." 8 58
|
||||
fi
|
||||
elif validate_bridge "$bridge_test"; then
|
||||
_bridge="$bridge_test"
|
||||
_sdn_vnet=""
|
||||
((STEP++))
|
||||
else
|
||||
whiptail --msgbox "Bridge '$bridge_test' is not available or not active." 8 58
|
||||
@@ -2957,6 +2994,7 @@ Advanced:
|
||||
var_timezone="$_ct_timezone"
|
||||
var_apt_cacher="$_apt_cacher"
|
||||
var_apt_cacher_ip="$_apt_cacher_ip"
|
||||
var_sdn_vnet="$_sdn_vnet"
|
||||
var_http_proxy="$_http_proxy"
|
||||
var_http_no_proxy="$_http_no_proxy"
|
||||
|
||||
@@ -3714,7 +3752,6 @@ run_addon_updates() {
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
runtime_script_status_guard() {
|
||||
local script_slug="${SCRIPT_SLUG:-${NSAPP:-}}"
|
||||
script_slug="$(echo "$script_slug" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')"
|
||||
@@ -3892,6 +3929,9 @@ build_container() {
|
||||
# if [ "$VERBOSE" == "yes" ]; then set -x; fi
|
||||
|
||||
NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}"
|
||||
if [[ -n "${var_sdn_vnet:-${SDN_VNET:-}}" ]]; then
|
||||
NET_STRING="-net0 name=eth0,vnet=${var_sdn_vnet:-$SDN_VNET}"
|
||||
fi
|
||||
|
||||
# MAC
|
||||
if [[ -n "$MAC" ]]; then
|
||||
@@ -5390,6 +5430,7 @@ fix_gpu_gids() {
|
||||
# Update dev entries with correct GIDs
|
||||
sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG"
|
||||
sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG"
|
||||
sed -i -E "s|(dev[0-9]+: /dev/kfd),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG"
|
||||
|
||||
# Restart container
|
||||
pct start "$CTID" >/dev/null 2>&1
|
||||
@@ -5411,6 +5452,10 @@ fix_gpu_gids() {
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ -e /dev/kfd ]; then
|
||||
chgrp ${render_gid} /dev/kfd 2>/dev/null || true
|
||||
chmod 660 /dev/kfd 2>/dev/null || true
|
||||
fi
|
||||
" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
@@ -5596,10 +5641,10 @@ check_storage_health() {
|
||||
|
||||
[[ -z "$total_kb" || "$total_kb" == "0" || -z "$avail_kb" ]] && return 0
|
||||
|
||||
used_pct=$(( (total_kb - avail_kb) * 100 / total_kb ))
|
||||
used_pct=$(((total_kb - avail_kb) * 100 / total_kb))
|
||||
free_gb_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$avail_kb" 2>/dev/null || echo "${avail_kb}KB")
|
||||
|
||||
if (( used_pct >= 95 )); then
|
||||
if ((used_pct >= 95)); then
|
||||
msg_warn "Storage '${storage}' (${storage_type}) is ${used_pct}% full (${free_gb_fmt} free)"
|
||||
if ! is_unattended && command -v whiptail >/dev/null 2>&1 && [[ -t 0 ]]; then
|
||||
if ! whiptail --backtitle "Proxmox VE Helper Scripts" \
|
||||
@@ -5608,11 +5653,11 @@ check_storage_health() {
|
||||
msg_error "Installation cancelled – storage nearly full"
|
||||
exit 214
|
||||
fi
|
||||
elif (( used_pct >= 98 )); then
|
||||
elif ((used_pct >= 98)); then
|
||||
msg_error "Storage '${storage}' is ${used_pct}% full – refusing install in unattended mode"
|
||||
exit 214
|
||||
fi
|
||||
elif (( used_pct >= 85 )); then
|
||||
elif ((used_pct >= 85)); then
|
||||
msg_warn "Storage '${storage}' (${storage_type}) is ${used_pct}% full (${free_gb_fmt} free)"
|
||||
fi
|
||||
|
||||
@@ -5951,17 +5996,17 @@ create_lxc_container() {
|
||||
# Maps OS type + version to the release variant name used by ARM64 template sources.
|
||||
arm64_template_variant() {
|
||||
case "$1:$2" in
|
||||
debian:12) echo "bookworm" ;;
|
||||
debian:13) echo "trixie" ;;
|
||||
debian:) echo "$DEBIAN_DEFAULT_CODENAME" ;;
|
||||
debian:12) echo "bookworm" ;;
|
||||
debian:13) echo "trixie" ;;
|
||||
debian:) echo "$DEBIAN_DEFAULT_CODENAME" ;;
|
||||
|
||||
ubuntu:24.04) echo "noble" ;;
|
||||
ubuntu:26.04) echo "questing" ;;
|
||||
ubuntu:) echo "$UBUNTU_DEFAULT_CODENAME" ;;
|
||||
ubuntu:24.04) echo "noble" ;;
|
||||
ubuntu:26.04) echo "questing" ;;
|
||||
ubuntu:) echo "$UBUNTU_DEFAULT_CODENAME" ;;
|
||||
|
||||
alpine:*) echo "${2:-$ALPINE_DEFAULT_VERSION}" ;;
|
||||
alpine:*) echo "${2:-$ALPINE_DEFAULT_VERSION}" ;;
|
||||
|
||||
*) return 1 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -2607,7 +2607,7 @@ check_for_gh_release() {
|
||||
ensure_dependencies jq
|
||||
|
||||
local gh_check_json
|
||||
gh_check_json=$(mktemp /tmp/tools-gh-check-XXXXXX.json) || return 7
|
||||
gh_check_json=$(mktemp /tmp/tools-gh-check-XXXXXX) || return 73
|
||||
trap 'rm -f "$gh_check_json"' RETURN
|
||||
|
||||
# Build auth header if token is available
|
||||
@@ -3257,7 +3257,7 @@ fetch_and_deploy_codeberg_release() {
|
||||
fi
|
||||
|
||||
local codeberg_rel_json
|
||||
codeberg_rel_json=$(mktemp /tmp/tools-codeberg-rel-XXXXXX.json) || return 7
|
||||
codeberg_rel_json=$(mktemp /tmp/tools-codeberg-rel-XXXXXX) || return 73
|
||||
trap 'rm -f "$codeberg_rel_json"' RETURN
|
||||
|
||||
local attempt=0 success=false resp http_code
|
||||
@@ -3744,7 +3744,7 @@ fetch_and_deploy_gh_release() {
|
||||
rm -f "$TOOLS_GH_REL_JSON"
|
||||
fi
|
||||
local gh_rel_json
|
||||
gh_rel_json=$(mktemp /tmp/tools-gh-rel-XXXXXX.json) || return 7
|
||||
gh_rel_json=$(mktemp /tmp/tools-gh-rel-XXXXXX) || return 73
|
||||
TOOLS_GH_REL_JSON="$gh_rel_json"
|
||||
|
||||
local api_url="https://api.github.com/repos/$repo/releases"
|
||||
@@ -4614,11 +4614,11 @@ EOF
|
||||
local image=$(echo "$container" | awk '{print $2}')
|
||||
local current_digest=$(docker inspect "$name" --format='{{.Image}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12)
|
||||
|
||||
# Pull latest image digest
|
||||
docker pull "$image" >/dev/null 2>&1
|
||||
# Pull latest image digest (ignore failures, e.g. local-only images or registry/permission issues)
|
||||
docker pull "$image" >/dev/null 2>&1 || true
|
||||
local latest_digest=$(docker inspect "$image" --format='{{.Id}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12)
|
||||
|
||||
if [ "$current_digest" != "$latest_digest" ]; then
|
||||
if [ -n "$latest_digest" ] && [ "$current_digest" != "$latest_digest" ]; then
|
||||
containers_with_updates+=("$name")
|
||||
container_info+=("${index}) ${name} (${image})")
|
||||
((index++))
|
||||
@@ -7561,8 +7561,8 @@ setup_nodejs() {
|
||||
}
|
||||
|
||||
# Install global Node modules
|
||||
if [[ -n "$NODE_MODULE" ]] || (( node_major >= 25 )); then
|
||||
if (( node_major >= 25 )) && [[ ",${NODE_MODULE}," != *",corepack,"* ]] && [[ "$NODE_MODULE" != corepack* ]]; then
|
||||
if [[ -n "$NODE_MODULE" ]] || ((node_major >= 25)); then
|
||||
if ((node_major >= 25)) && [[ ",${NODE_MODULE}," != *",corepack,"* ]] && [[ "$NODE_MODULE" != corepack* ]]; then
|
||||
NODE_MODULE="${NODE_MODULE:+$NODE_MODULE,}corepack"
|
||||
fi
|
||||
|
||||
@@ -7624,12 +7624,12 @@ setup_nodejs() {
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if (( failed_modules > 0 )); then
|
||||
if ((failed_modules > 0)); then
|
||||
msg_warn "$failed_modules Node.js module(s) failed: $NODE_MODULE"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$NODE_COREPACK_ENABLE" == "1" ]] && (( wants_corepack )) && command -v corepack >/dev/null 2>&1; then
|
||||
if [[ "$NODE_COREPACK_ENABLE" == "1" ]] && ((wants_corepack)) && command -v corepack >/dev/null 2>&1; then
|
||||
msg_info "Enabling corepack"
|
||||
if $STD corepack enable 2>/dev/null; then
|
||||
msg_ok "Enabled corepack"
|
||||
@@ -9172,7 +9172,7 @@ check_for_gl_release() {
|
||||
ensure_dependencies jq
|
||||
|
||||
local gl_check_json
|
||||
gl_check_json=$(mktemp /tmp/tools-gl-check-XXXXXX.json) || return 7
|
||||
gl_check_json=$(mktemp /tmp/tools-gl-check-XXXXXX) || return 73
|
||||
trap 'rm -f "$gl_check_json"' RETURN
|
||||
|
||||
local repo_encoded
|
||||
@@ -9452,7 +9452,7 @@ fetch_and_deploy_gl_release() {
|
||||
ensure_dependencies jq
|
||||
|
||||
local gl_rel_json
|
||||
gl_rel_json=$(mktemp /tmp/tools-gl-rel-XXXXXX.json) || return 7
|
||||
gl_rel_json=$(mktemp /tmp/tools-gl-rel-XXXXXX) || return 73
|
||||
trap 'rm -f "$gl_rel_json"' RETURN
|
||||
|
||||
local repo_encoded
|
||||
@@ -9902,7 +9902,7 @@ setup_nltk() {
|
||||
fi
|
||||
|
||||
mkdir -p "${target_dir}/${subdir}"
|
||||
tmp_zip=$(mktemp --suffix=.zip)
|
||||
tmp_zip=$(mktemp)
|
||||
|
||||
if CURL_TIMEOUT=120 curl_with_retry "$pkg_url" "$tmp_zip"; then
|
||||
if [[ "$do_unzip" == "1" ]]; then
|
||||
|
||||
+549
-45
@@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/LICENSE
|
||||
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE
|
||||
|
||||
set -euo pipefail
|
||||
SPINNER_PID=""
|
||||
@@ -14,9 +14,18 @@ declare -A MSG_INFO_SHOWN
|
||||
[[ -n "${_CORE_FUNC_LOADED:-}" ]] && return
|
||||
_CORE_FUNC_LOADED=1
|
||||
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main}"
|
||||
|
||||
load_api_functions() {
|
||||
if ! declare -f post_to_api_vm >/dev/null 2>&1; then
|
||||
source /dev/stdin <<<$(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/api.func")
|
||||
fi
|
||||
}
|
||||
|
||||
load_functions() {
|
||||
[[ -n "${__FUNCTIONS_LOADED:-}" ]] && return
|
||||
__FUNCTIONS_LOADED=1
|
||||
load_api_functions
|
||||
color
|
||||
formatting
|
||||
icons
|
||||
@@ -31,18 +40,24 @@ load_functions() {
|
||||
arch_check
|
||||
}
|
||||
|
||||
load_cloud_init_functions() {
|
||||
if ! declare -f setup_cloud_init >/dev/null 2>&1; then
|
||||
source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/cloud-init.func") 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to download & save header files
|
||||
get_header() {
|
||||
local app_name=$(echo "${APP,,}" | tr ' ' '-')
|
||||
local app_type=${APP_TYPE:-vm}
|
||||
local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/${app_type}/headers/${app_name}"
|
||||
local header_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/${app_type}/headers/${app_name}"
|
||||
local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}"
|
||||
|
||||
mkdir -p "$(dirname "$local_header_path")"
|
||||
|
||||
if [ ! -s "$local_header_path" ]; then
|
||||
if ! curl -fsSL "$header_url" -o "$local_header_path"; then
|
||||
return 250
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -98,6 +113,7 @@ icons() {
|
||||
DNSOK="✔️ "
|
||||
DNSFAIL="${TAB}✖️${TAB}"
|
||||
INFO="${TAB}💡${TAB}${CL}"
|
||||
CLOUD="${TAB}☁️${TAB}${CL}"
|
||||
OS="${TAB}🖥️${TAB}${CL}"
|
||||
OSVERSION="${TAB}🌟${TAB}${CL}"
|
||||
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
|
||||
@@ -188,18 +204,32 @@ silent() {
|
||||
trap 'error_handler' ERR
|
||||
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
# Return instead of exit so that callers can use `$STD cmd || true`
|
||||
# When no || is used, set -e + ERR trap catches it via error_handler()
|
||||
export _SILENT_FAILED_RC="$rc"
|
||||
export _SILENT_FAILED_CMD="$cmd"
|
||||
export _SILENT_FAILED_LINE="$caller_line"
|
||||
export _SILENT_FAILED_LOG="$logfile"
|
||||
# Source explain_exit_code if needed
|
||||
if ! declare -f explain_exit_code >/dev/null 2>&1; then
|
||||
source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/error_handler.func") 2>/dev/null || true
|
||||
fi
|
||||
|
||||
return "$rc"
|
||||
local explanation=""
|
||||
if declare -f explain_exit_code >/dev/null 2>&1; then
|
||||
explanation="$(explain_exit_code "$rc")"
|
||||
fi
|
||||
|
||||
printf "\e[?25h"
|
||||
if [[ -n "$explanation" ]]; then
|
||||
msg_error "in line ${caller_line}: exit code ${rc} (${explanation})"
|
||||
else
|
||||
msg_error "in line ${caller_line}: exit code ${rc}"
|
||||
fi
|
||||
msg_custom "→" "${YWB}" "${cmd}"
|
||||
|
||||
if [[ -s "$logfile" ]]; then
|
||||
echo -e "\n${TAB}--- Last 20 lines of log ---"
|
||||
tail -n 20 "$logfile"
|
||||
echo -e "${TAB}----------------------------\n"
|
||||
fi
|
||||
|
||||
exit "$rc"
|
||||
fi
|
||||
|
||||
# Clear stale flags on success
|
||||
unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE _SILENT_FAILED_LOG 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -230,7 +260,7 @@ curl_handler() {
|
||||
|
||||
if [[ -z "$url" ]]; then
|
||||
msg_error "no valid url or option entered for curl_handler"
|
||||
exit 64
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$STD msg_info "Fetching: $url"
|
||||
@@ -259,7 +289,7 @@ curl_handler() {
|
||||
rm -f /tmp/curl_error.log
|
||||
fi
|
||||
__curl_err_handler "$exit_code" "$url" "$curl_stderr"
|
||||
exit "$exit_code"
|
||||
exit 1 # hard exit if exit_code is not 0
|
||||
fi
|
||||
|
||||
$STD printf "\r\033[K${INFO}${YW}Retry $attempt/$max_retries in ${delay}s...${CL}" >&2
|
||||
@@ -302,7 +332,7 @@ __curl_err_handler() {
|
||||
esac
|
||||
|
||||
[[ -n "$curl_msg" ]] && printf "%s\n" "$curl_msg" >&2
|
||||
exit "$exit_code"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -317,7 +347,7 @@ shell_check() {
|
||||
msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell."
|
||||
echo -e "\nExiting..."
|
||||
sleep 2
|
||||
exit 103
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -338,11 +368,11 @@ clear_line() {
|
||||
#
|
||||
# - Determines if script should run in verbose mode
|
||||
# - Checks VERBOSE and var_verbose variables
|
||||
# - Note: Non-TTY (pipe) scenarios are handled separately in msg_info()
|
||||
# - Also returns true if not running in TTY (pipe/redirect scenario)
|
||||
# ------------------------------------------------------------------------------
|
||||
is_verbose_mode() {
|
||||
local verbose="${VERBOSE:-${var_verbose:-no}}"
|
||||
[[ "$verbose" != "no" ]]
|
||||
[[ "$verbose" != "no" || ! -t 2 ]]
|
||||
}
|
||||
|
||||
### dev spinner ###
|
||||
@@ -481,6 +511,20 @@ msg_debug() {
|
||||
fi
|
||||
}
|
||||
|
||||
error_handler() {
|
||||
local exit_code="$?"
|
||||
local line_number="${1:-unknown}"
|
||||
local command="${2:-unknown}"
|
||||
|
||||
if declare -f post_update_to_api >/dev/null 2>&1; then
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Displays error message and immediately terminates script
|
||||
fatal() {
|
||||
msg_error "$1"
|
||||
@@ -516,9 +560,13 @@ cleanup_vmid() {
|
||||
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
stop_spinner
|
||||
if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then
|
||||
popd >/dev/null || true
|
||||
fi
|
||||
if [[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]]; then
|
||||
rm -rf "$TEMP_DIR"
|
||||
fi
|
||||
# Report final telemetry status if post_to_api_vm was called but no update was sent
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if declare -f post_update_to_api >/dev/null 2>&1; then
|
||||
@@ -538,18 +586,37 @@ check_root() {
|
||||
msg_error "Please run this script as root."
|
||||
echo -e "\nExiting..."
|
||||
sleep 2
|
||||
exit 104
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
pve_check() {
|
||||
if ! pveversion | grep -Eq "pve-manager/(8\.[1-4]|9\.[0-2])(\.[0-9]+)*"; then
|
||||
msg_error "This version of Proxmox Virtual Environment is not supported"
|
||||
echo -e "Requires Proxmox Virtual Environment Version 8.1 - 8.4 or 9.0 - 9.2."
|
||||
echo -e "Exiting..."
|
||||
sleep 2
|
||||
exit 105
|
||||
local pve_ver
|
||||
pve_ver="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
|
||||
|
||||
if [[ "$pve_ver" =~ ^8\.([0-9]+) ]]; then
|
||||
local minor="${BASH_REMATCH[1]}"
|
||||
if ((minor < 0 || minor > 9)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 8.0 – 8.9"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$pve_ver" =~ ^9\.([0-9]+) ]]; then
|
||||
local minor="${BASH_REMATCH[1]}"
|
||||
if ((minor < 0 || minor > 2)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported versions: Proxmox VE 8.0 – 8.9 or 9.0 – 9.2"
|
||||
exit 105
|
||||
}
|
||||
|
||||
arch_check() {
|
||||
@@ -558,50 +625,487 @@ arch_check() {
|
||||
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
|
||||
echo -e "Exiting..."
|
||||
sleep 2
|
||||
exit 106
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
ssh_check() {
|
||||
if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:-}" ]; 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
|
||||
:
|
||||
else
|
||||
clear
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
exit_script() {
|
||||
clear
|
||||
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
|
||||
exit 0
|
||||
exit
|
||||
}
|
||||
|
||||
sanitize_vm_hostname() {
|
||||
local hostname="${1,,}"
|
||||
hostname=$(echo "$hostname" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
|
||||
echo "${hostname:0:63}"
|
||||
}
|
||||
|
||||
vm_confirm_new_vm() {
|
||||
local title="$1"
|
||||
local message="$2"
|
||||
local height="${3:-10}"
|
||||
local width="${4:-58}"
|
||||
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "$title" --yesno "$message" "$height" "$width"
|
||||
}
|
||||
|
||||
vm_choose_settings_mode() {
|
||||
local message="${1:-Use Default Settings?}"
|
||||
local height="${2:-10}"
|
||||
local width="${3:-58}"
|
||||
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "$message" --no-button Advanced "$height" "$width"
|
||||
}
|
||||
|
||||
vm_confirm_advanced_settings() {
|
||||
local message="$1"
|
||||
local height="${2:-10}"
|
||||
local width="${3:-58}"
|
||||
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "$message" --no-button Do-Over "$height" "$width"
|
||||
}
|
||||
|
||||
vm_prompt_vmid() {
|
||||
local default_vmid="${1:-$(get_valid_nextid)}"
|
||||
|
||||
while true; do
|
||||
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$default_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
|
||||
}
|
||||
|
||||
vm_apply_machine_type() {
|
||||
local machine_type="${1:-i440fx}"
|
||||
|
||||
if [ "$machine_type" = "q35" ]; then
|
||||
MACHINE_TYPE="q35"
|
||||
FORMAT=""
|
||||
MACHINE=" -machine q35"
|
||||
else
|
||||
MACHINE_TYPE="i440fx"
|
||||
FORMAT=",efitype=4m"
|
||||
MACHINE=""
|
||||
fi
|
||||
}
|
||||
|
||||
vm_machine_type_label() {
|
||||
case "${1:-i440fx}" in
|
||||
q35)
|
||||
echo "Q35 (Modern)"
|
||||
;;
|
||||
*)
|
||||
echo "i440fx"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
vm_prompt_machine_type() {
|
||||
local default_machine="${1:-i440fx}"
|
||||
local i440fx_default="ON"
|
||||
local q35_default="OFF"
|
||||
local machine_choice
|
||||
|
||||
if [ "$default_machine" = "q35" ]; then
|
||||
i440fx_default="OFF"
|
||||
q35_default="ON"
|
||||
fi
|
||||
|
||||
if machine_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
|
||||
"i440fx" "Machine i440fx" "$i440fx_default" \
|
||||
"q35" "Machine q35" "$q35_default" \
|
||||
3>&1 1>&2 2>&3); then
|
||||
vm_apply_machine_type "$machine_choice"
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$(vm_machine_type_label "$MACHINE_TYPE")${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
}
|
||||
|
||||
vm_prompt_cloud_init() {
|
||||
local default_user="${1:-root}"
|
||||
|
||||
USE_CLOUD_INIT="no"
|
||||
load_cloud_init_functions
|
||||
|
||||
if ! declare -f configure_cloud_init_interactive >/dev/null 2>&1; then
|
||||
echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}unavailable${CL}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
configure_cloud_init_interactive "$default_user" || true
|
||||
USE_CLOUD_INIT="${CLOUDINIT_ENABLE:-no}"
|
||||
echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}"
|
||||
|
||||
if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f configure_cloudinit_ssh_keys >/dev/null 2>&1; then
|
||||
configure_cloudinit_ssh_keys || true
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
vm_prompt_disk_size() {
|
||||
local default_size="${1:-8G}"
|
||||
local prompt_message="${2:-Set Disk Size in GiB (e.g., 10, 20)}"
|
||||
|
||||
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "$prompt_message" 8 58 "$default_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
|
||||
}
|
||||
|
||||
vm_prompt_disk_cache() {
|
||||
local default_cache="${1:-none}"
|
||||
local none_default="ON"
|
||||
local write_default="OFF"
|
||||
local cache_choice
|
||||
|
||||
if [ "$default_cache" = "writethrough" ]; then
|
||||
none_default="OFF"
|
||||
write_default="ON"
|
||||
fi
|
||||
|
||||
if cache_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "None (Default)" "$none_default" \
|
||||
"1" "Write Through" "$write_default" \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ "$cache_choice" = "1" ]; then
|
||||
DISK_CACHE="cache=writethrough,"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
|
||||
else
|
||||
DISK_CACHE=""
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
}
|
||||
|
||||
vm_prompt_hostname() {
|
||||
local default_hostname="${1:-vm}"
|
||||
local adjusted_hostname
|
||||
local input_hostname
|
||||
|
||||
if input_hostname=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$default_hostname" --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$input_hostname" ]; then
|
||||
HN="$default_hostname"
|
||||
else
|
||||
adjusted_hostname=$(sanitize_vm_hostname "$input_hostname")
|
||||
HN="${adjusted_hostname:-$default_hostname}"
|
||||
if [ "$HN" != "${input_hostname,,}" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
|
||||
fi
|
||||
fi
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
}
|
||||
|
||||
vm_prompt_cpu_model() {
|
||||
local default_model="${1:-kvm64}"
|
||||
local kvm_default="ON"
|
||||
local host_default="OFF"
|
||||
local cpu_choice
|
||||
|
||||
if [ "$default_model" = "host" ]; then
|
||||
kvm_default="OFF"
|
||||
host_default="ON"
|
||||
fi
|
||||
|
||||
if cpu_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "KVM64 (Default)" "$kvm_default" \
|
||||
"1" "Host" "$host_default" \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ "$cpu_choice" = "1" ]; then
|
||||
CPU_TYPE=" -cpu host"
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
|
||||
else
|
||||
CPU_TYPE=""
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
}
|
||||
|
||||
vm_prompt_cpu_cores() {
|
||||
local default_cores="${1:-2}"
|
||||
|
||||
while true; do
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$default_cores" --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then
|
||||
CORE_COUNT="$default_cores"
|
||||
fi
|
||||
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
vm_prompt_ram() {
|
||||
local default_ram="${1:-2048}"
|
||||
|
||||
while true; do
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$default_ram" --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then
|
||||
RAM_SIZE="$default_ram"
|
||||
fi
|
||||
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
vm_prompt_bridge() {
|
||||
local default_bridge="${1:-vmbr0}"
|
||||
|
||||
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 "$default_bridge" --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$BRG" ]; then
|
||||
BRG="$default_bridge"
|
||||
fi
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
}
|
||||
|
||||
vm_prompt_mac() {
|
||||
local default_mac="${1:-$GEN_MAC}"
|
||||
local input_mac
|
||||
|
||||
while true; do
|
||||
if input_mac=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$default_mac" --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$input_mac" ]; then
|
||||
MAC="$default_mac"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$input_mac" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then
|
||||
MAC="$input_mac"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
vm_prompt_vlan() {
|
||||
local default_vlan="${1:-}"
|
||||
local input_vlan
|
||||
|
||||
while true; do
|
||||
if input_vlan=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan (leave blank for default)" 8 58 "$default_vlan" --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$input_vlan" ]; then
|
||||
VLAN=""
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$input_vlan" =~ ^[0-9]+$ ]] && [ "$input_vlan" -ge 1 ] && [ "$input_vlan" -le 4094 ]; then
|
||||
VLAN=",tag=$input_vlan"
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$input_vlan${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
vm_prompt_mtu() {
|
||||
local default_mtu="${1:-}"
|
||||
local input_mtu
|
||||
|
||||
while true; do
|
||||
if input_mtu=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 "$default_mtu" --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$input_mtu" ]; then
|
||||
MTU=""
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$input_mtu" =~ ^[0-9]+$ ]] && [ "$input_mtu" -ge 576 ] && [ "$input_mtu" -le 65520 ]; then
|
||||
MTU=",mtu=$input_mtu"
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$input_mtu${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
vm_prompt_start_vm() {
|
||||
local default_start="${1:-yes}"
|
||||
local default_flag=()
|
||||
|
||||
if [ "$default_start" = "no" ]; then
|
||||
default_flag=(--defaultno)
|
||||
fi
|
||||
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" "${default_flag[@]}" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58; then
|
||||
START_VM="yes"
|
||||
else
|
||||
START_VM="no"
|
||||
fi
|
||||
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}${START_VM}${CL}"
|
||||
}
|
||||
|
||||
vm_apply_storage_layout() {
|
||||
local storage_type="$1"
|
||||
|
||||
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=""
|
||||
;;
|
||||
*)
|
||||
DISK_EXT=""
|
||||
DISK_REF=""
|
||||
DISK_IMPORT_FORMAT="raw"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
vm_select_storage() {
|
||||
local hostname="${1:-${HN:-vm}}"
|
||||
local storage_menu=()
|
||||
local msg_max_length=0
|
||||
local line tag type free item
|
||||
local offset=2
|
||||
local valid_storage
|
||||
|
||||
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 "
|
||||
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_storage=$(pvesm status -content images | awk 'NR>1')
|
||||
if [ -z "$valid_storage" ]; then
|
||||
msg_error "Unable to detect a valid storage location."
|
||||
exit
|
||||
elif [ $((${#storage_menu[@]} / 3)) -eq 1 ]; then
|
||||
STORAGE=${storage_menu[0]}
|
||||
else
|
||||
if [ -n "${SPINNER_PID:-}" ] && ps -p "$SPINNER_PID" >/dev/null 2>&1; then
|
||||
kill "$SPINNER_PID" >/dev/null 2>&1 || true
|
||||
SPINNER_ACTIVE=0
|
||||
printf "\r\e[2K" >&2
|
||||
fi
|
||||
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 ${hostname}?\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}."
|
||||
|
||||
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
|
||||
vm_apply_storage_layout "$STORAGE_TYPE"
|
||||
}
|
||||
|
||||
vm_define_disk_references() {
|
||||
local disk_count="${1:-2}"
|
||||
local i disk_name
|
||||
|
||||
for ((i = 0; i < disk_count; i++)); do
|
||||
disk_name="vm-${VMID}-disk-${i}${DISK_EXT:-}"
|
||||
printf -v "DISK${i}" '%s' "$disk_name"
|
||||
printf -v "DISK${i}_REF" '%s' "${STORAGE}:${DISK_REF:-}${disk_name}"
|
||||
done
|
||||
}
|
||||
|
||||
check_hostname_conflict() {
|
||||
local hostname="$1"
|
||||
if qm list | awk '{print $2}' | grep -qx "$hostname"; then
|
||||
msg_error "Hostname $hostname already in use by another VM."
|
||||
exit 206
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
set_description() {
|
||||
local app_name script_slug script_url donate_url
|
||||
app_name=$(echo "${APP,,}" | tr ' ' '-')
|
||||
script_slug="${SCRIPT_SLUG:-${app_name}}"
|
||||
script_slug="$(echo "$script_slug" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')"
|
||||
script_url="https://community-scripts.org/scripts/${script_slug}"
|
||||
donate_url="https://community-scripts.org/donate"
|
||||
local description_title="${APP:-${NSAPP} VM}"
|
||||
|
||||
DESCRIPTION=$(
|
||||
cat <<EOF
|
||||
<div align='center'>
|
||||
<a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
|
||||
<a href='https://Helper-Scripts.com' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
|
||||
</a>
|
||||
|
||||
<h2 style='font-size: 24px; margin: 20px 0;'>${NSAPP} VM</h2>
|
||||
<h2 style='font-size: 24px; margin: 20px 0;'>${description_title}</h2>
|
||||
|
||||
<p style='margin: 16px 0;'>
|
||||
<a href='${donate_url}' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/❤️-Sponsoring%20%26%20Donations-FF5E5B' alt='Sponsoring and donations' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p style='margin: 12px 0;'>
|
||||
<a href='${script_url}' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/📦-Open%20Script%20Page-00617f' alt='Open script page' />
|
||||
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
+72
-32
@@ -22,10 +22,10 @@ APP="IP-Tag"
|
||||
hostname=$(hostname)
|
||||
|
||||
# Color variables
|
||||
YW=$(echo "\033[33m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
RD=$(echo "\033[01;31m")
|
||||
CL=$(echo "\033[m")
|
||||
YW="\033[33m"
|
||||
GN="\033[1;92m"
|
||||
RD="\033[01;31m"
|
||||
CL="\033[m"
|
||||
BFR="\\r\\033[K"
|
||||
HOLD=" "
|
||||
CM="${GN}✓${CL} "
|
||||
@@ -127,7 +127,7 @@ update_installation() {
|
||||
echo -e "\n${YW}Configuration file already exists.${CL}"
|
||||
echo -e "${YW}Note: No critical changes were made in this version.${CL}"
|
||||
while true; do
|
||||
read -p "Do you want to replace it with defaults? (y/n): " yn
|
||||
read -rp "Do you want to replace it with defaults? (y/n): " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
interactive_config_setup
|
||||
@@ -176,7 +176,7 @@ export FORCE_SINGLE_RUN=true
|
||||
exec "$SCRIPT_FILE"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/iptag-run
|
||||
msg_ok "Created iptag-run executable - You can execute this manually by entering “iptag-run” in the Proxmox host, so the script is executed by hand."
|
||||
msg_ok "Created iptag-run executable - You can execute this manually by entering 'iptag-run' in the Proxmox host, so the script is executed by hand."
|
||||
|
||||
msg_info "Restarting service"
|
||||
systemctl daemon-reload &>/dev/null
|
||||
@@ -208,7 +208,7 @@ install_command_only() {
|
||||
else
|
||||
stop_spinner
|
||||
echo -e "\n${YW}Configuration file already exists.${CL}"
|
||||
read -p "Do you want to reconfigure tag format? (y/n): " reconfigure
|
||||
read -rp "Do you want to reconfigure tag format? (y/n): " reconfigure
|
||||
case $reconfigure in
|
||||
[Yy]*)
|
||||
interactive_config_setup_command
|
||||
@@ -285,7 +285,7 @@ interactive_config_setup_command() {
|
||||
echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)"
|
||||
|
||||
while true; do
|
||||
read -p "Enter your choice (1-3) [1]: " tag_choice
|
||||
read -rp "Enter your choice (1-3) [1]: " tag_choice
|
||||
case ${tag_choice:-1} in
|
||||
1)
|
||||
TAG_FORMAT="last_two_octets"
|
||||
@@ -323,7 +323,7 @@ interactive_config_setup() {
|
||||
echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)"
|
||||
|
||||
while true; do
|
||||
read -p "Enter your choice (1-3) [1]: " tag_choice
|
||||
read -rp "Enter your choice (1-3) [1]: " tag_choice
|
||||
case ${tag_choice:-1} in
|
||||
1)
|
||||
TAG_FORMAT="last_two_octets"
|
||||
@@ -352,7 +352,7 @@ interactive_config_setup() {
|
||||
echo -e "${YW}Recommended range: 300-3600 seconds${CL}"
|
||||
|
||||
while true; do
|
||||
read -p "Enter interval in seconds [300]: " interval_input
|
||||
read -rp "Enter interval in seconds [300]: " interval_input
|
||||
interval_input=${interval_input:-300}
|
||||
|
||||
if [[ $interval_input =~ ^[0-9]+$ ]] && [ $interval_input -ge 300 ] && [ $interval_input -le 7200 ]; then
|
||||
@@ -563,9 +563,10 @@ get_vm_ips() {
|
||||
|
||||
debug_log "vm $vmid: starting IP detection"
|
||||
|
||||
# Check if VM is running first
|
||||
local vm_status=""
|
||||
if command -v qm >/dev/null 2>&1; then
|
||||
# Check if VM is running first (status comes from the cached `qm list`,
|
||||
# falling back to `qm status` only when called outside the normal cycle).
|
||||
local vm_status="${STATUS_CACHE[vm_${vmid}]:-}"
|
||||
if [[ -z "$vm_status" ]] && command -v qm >/dev/null 2>&1; then
|
||||
vm_status=$(qm status "$vmid" 2>/dev/null | awk '{print $2}')
|
||||
fi
|
||||
|
||||
@@ -578,33 +579,43 @@ get_vm_ips() {
|
||||
local mac_addresses=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | head -3)
|
||||
debug_log "vm $vmid: found MACs: $mac_addresses"
|
||||
|
||||
# Method 1: QM guest agent (most reliable for current IP)
|
||||
if command -v qm >/dev/null 2>&1; then
|
||||
debug_log "vm $vmid: trying qm guest agent first"
|
||||
local qm_ips=$(timeout 8 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -3)
|
||||
# Method 1: QEMU guest agent (most reliable for current IP). Only query it
|
||||
# when the agent is actually enabled in the VM config, otherwise the call
|
||||
# blocks until the timeout on every VM without an agent.
|
||||
local agent_enabled=0
|
||||
if [[ "$(grep -E '^agent:' "$vm_config" 2>/dev/null)" =~ (^agent:[[:space:]]*1|enabled=1) ]]; then
|
||||
agent_enabled=1
|
||||
fi
|
||||
if [[ "$agent_enabled" == "1" ]] && command -v qm >/dev/null 2>&1; then
|
||||
debug_log "vm $vmid: querying guest agent"
|
||||
local qm_ips=$(timeout 5 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -3)
|
||||
for qm_ip in $qm_ips; do
|
||||
if [[ "$qm_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "vm $vmid: found IP $qm_ip via qm guest cmd"
|
||||
ips+="$qm_ip "
|
||||
fi
|
||||
done
|
||||
else
|
||||
debug_log "vm $vmid: guest agent not enabled, skipping qm guest cmd"
|
||||
fi
|
||||
|
||||
# Method 2: Fresh ARP table lookup (force refresh)
|
||||
if [[ -n "$mac_addresses" ]]; then
|
||||
debug_log "vm $vmid: refreshing ARP table and checking"
|
||||
# Try to refresh ARP table by pinging network ranges
|
||||
# Method 2: ARP table lookup (only if the guest agent gave us nothing).
|
||||
if [[ -z "$ips" && -n "$mac_addresses" ]]; then
|
||||
debug_log "vm $vmid: checking ARP table"
|
||||
# Snapshot the neighbor table once instead of per MAC
|
||||
local neigh_table
|
||||
neigh_table=$(ip neighbor show 2>/dev/null)
|
||||
for mac in $mac_addresses; do
|
||||
local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# First check current ARP table
|
||||
local current_ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
# Check current ARP table
|
||||
local current_ip=$(echo "$neigh_table" | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
|
||||
# If found in ARP, verify it's still valid by trying to ping
|
||||
if [[ -n "$current_ip" && "$current_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "vm $vmid: found IP $current_ip in ARP table for MAC $mac_lower, verifying..."
|
||||
# Quick ping test to verify IP is still active
|
||||
if timeout 2 ping -c 1 "$current_ip" >/dev/null 2>&1; then
|
||||
if timeout 1 ping -c 1 -W 1 "$current_ip" >/dev/null 2>&1; then
|
||||
debug_log "vm $vmid: verified IP $current_ip is active via ping"
|
||||
ips+="$current_ip "
|
||||
else
|
||||
@@ -628,7 +639,7 @@ get_vm_ips() {
|
||||
if [[ -n "$dhcp_ip" && "$dhcp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "vm $vmid: found IP $dhcp_ip via DHCP leases"
|
||||
# Verify this IP responds
|
||||
if timeout 2 ping -c 1 "$dhcp_ip" >/dev/null 2>&1; then
|
||||
if timeout 1 ping -c 1 -W 1 "$dhcp_ip" >/dev/null 2>&1; then
|
||||
debug_log "vm $vmid: verified DHCP IP $dhcp_ip is active"
|
||||
ips+="$dhcp_ip "
|
||||
break 2
|
||||
@@ -652,6 +663,9 @@ get_vm_ips() {
|
||||
# Cache for configs to avoid repeated reads
|
||||
declare -A CONFIG_CACHE
|
||||
declare -A IP_CACHE
|
||||
# Status cache populated once per check from `pct list` / `qm list` to avoid
|
||||
# spawning an expensive `pct status` / `qm status` (Perl) per guest each cycle.
|
||||
declare -A STATUS_CACHE
|
||||
|
||||
# Update tags for container or VM
|
||||
update_tags() {
|
||||
@@ -836,7 +850,16 @@ update_all_tags() {
|
||||
|
||||
# Get list of all containers/VMs
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
vmids=($(pct list 2>/dev/null | grep -v VMID | awk '{print $1}'))
|
||||
# A single `pct list` call yields both the VMID list and the running
|
||||
# status, so we never need a per-container `pct status` afterwards.
|
||||
local pct_list_output
|
||||
pct_list_output=$(pct list 2>/dev/null)
|
||||
vmids=($(echo "$pct_list_output" | awk 'NR>1 {print $1}'))
|
||||
local _vmid _status _rest
|
||||
while read -r _vmid _status _rest; do
|
||||
[[ "$_vmid" == "VMID" || -z "$_vmid" ]] && continue
|
||||
STATUS_CACHE["lxc_${_vmid}"]="$_status"
|
||||
done <<<"$pct_list_output"
|
||||
else
|
||||
# More efficient: direct file listing instead of ls+sed
|
||||
vmids=()
|
||||
@@ -845,6 +868,15 @@ update_all_tags() {
|
||||
local basename="${conf##*/}"
|
||||
vmids+=("${basename%.conf}")
|
||||
done
|
||||
# A single `qm list` call yields the status for all VMs, avoiding a
|
||||
# per-VM `qm status`.
|
||||
if command -v qm >/dev/null 2>&1; then
|
||||
local _vmid _name _status _rest
|
||||
while read -r _vmid _name _status _rest; do
|
||||
[[ "$_vmid" == "VMID" || -z "$_vmid" ]] && continue
|
||||
STATUS_CACHE["vm_${_vmid}"]="$_status"
|
||||
done <<<"$(qm list 2>/dev/null)"
|
||||
fi
|
||||
fi
|
||||
|
||||
count=${#vmids[@]}
|
||||
@@ -881,6 +913,7 @@ check() {
|
||||
# Clear caches before each run
|
||||
CONFIG_CACHE=()
|
||||
IP_CACHE=()
|
||||
STATUS_CACHE=()
|
||||
|
||||
# Update LXC containers
|
||||
update_all_tags "lxc"
|
||||
@@ -925,8 +958,12 @@ get_lxc_ips() {
|
||||
|
||||
debug_log "lxc $vmid: starting IP detection"
|
||||
|
||||
# Check if LXC is running
|
||||
local lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}')
|
||||
# Check if LXC is running (status comes from the cached `pct list`,
|
||||
# falling back to `pct status` only when called outside the normal cycle).
|
||||
local lxc_status="${STATUS_CACHE[lxc_${vmid}]:-}"
|
||||
if [[ -z "$lxc_status" ]]; then
|
||||
lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}')
|
||||
fi
|
||||
if [[ "$lxc_status" != "running" ]]; then
|
||||
debug_log "lxc $vmid: not running (status: $lxc_status)"
|
||||
return
|
||||
@@ -952,9 +989,12 @@ get_lxc_ips() {
|
||||
if [[ -z "$ips" && -f "$pve_lxc_config" ]]; then
|
||||
local mac_addrs=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | cut -d'=' -f2)
|
||||
if [[ -n "$mac_addrs" ]]; then
|
||||
# Snapshot the neighbor table once instead of per MAC
|
||||
local neigh_table
|
||||
neigh_table=$(ip neighbor show 2>/dev/null)
|
||||
while IFS= read -r mac_addr; do
|
||||
[[ -z "$mac_addr" ]] && continue
|
||||
local arp_ip=$(ip neighbor show | grep -i "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
local arp_ip=$(echo "$neigh_table" | grep -i "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
if [[ -n "$arp_ip" && "$arp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "lxc $vmid: found IP $arp_ip via ARP table for MAC $mac_addr"
|
||||
ips="${ips}${ips:+ }${arp_ip}"
|
||||
@@ -996,7 +1036,7 @@ echo -e "${GN}3)${CL} Update existing installation"
|
||||
echo -e "${RD}4)${CL} Cancel"
|
||||
|
||||
while true; do
|
||||
read -p "Enter your choice (1-4): " choice
|
||||
read -rp "Enter your choice (1-4): " choice
|
||||
case $choice in
|
||||
1)
|
||||
INSTALL_MODE="service"
|
||||
@@ -1025,7 +1065,7 @@ done
|
||||
|
||||
echo -e "\n${YW}This will install ${APP} on ${hostname} in $INSTALL_MODE mode.${CL}"
|
||||
while true; do
|
||||
read -p "Proceed? (y/n): " yn
|
||||
read -rp "Proceed? (y/n): " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
break
|
||||
@@ -1072,7 +1112,7 @@ if [[ "$INSTALL_MODE" == "service" ]]; then
|
||||
else
|
||||
stop_spinner
|
||||
echo -e "\n${YW}Configuration file already exists.${CL}"
|
||||
read -p "Do you want to reconfigure tag format and loop interval? (y/n): " reconfigure
|
||||
read -rp "Do you want to reconfigure tag format and loop interval? (y/n): " reconfigure
|
||||
case $reconfigure in
|
||||
[Yy]*)
|
||||
interactive_config_setup
|
||||
|
||||
+15
-8
@@ -30,7 +30,7 @@ declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "clean-lxcs" "
|
||||
header_info
|
||||
echo "Loading..."
|
||||
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This will clean logs, cache and update package lists on selected LXC Containers. Proceed?" 10 58
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This will clean logs, cache and update package lists on selected LXC Containers. Proceed?" 10 58 || exit 0
|
||||
|
||||
NODE=$(hostname)
|
||||
EXCLUDE_MENU=()
|
||||
@@ -42,17 +42,17 @@ while read -r TAG ITEM; do
|
||||
EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF")
|
||||
done < <(pct list | awk 'NR>1')
|
||||
|
||||
excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \
|
||||
16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit
|
||||
# Capture the selection; abort cleanly if the user cancels the dialog
|
||||
# (set -e would otherwise terminate on the failing command substitution).
|
||||
if ! excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \
|
||||
16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"'); then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
function run_lxc_clean() {
|
||||
local container=$1
|
||||
header_info
|
||||
name=$(pct exec "$container" hostname)
|
||||
name=$(pct exec "$container" -- hostname)
|
||||
|
||||
pct exec "$container" -- bash -c '
|
||||
BL="\033[36m"; GN="\033[1;92m"; CL="\033[m"
|
||||
@@ -84,7 +84,14 @@ function run_lxc_clean() {
|
||||
}
|
||||
|
||||
for container in $(pct list | awk '{if(NR>1) print $1}'); do
|
||||
if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then
|
||||
excluded=0
|
||||
for ex in $excluded_containers; do
|
||||
if [[ "$ex" == "$container" ]]; then
|
||||
excluded=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$excluded" -eq 1 ]]; then
|
||||
header_info
|
||||
echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}"
|
||||
sleep 1
|
||||
|
||||
@@ -55,12 +55,23 @@ read -r selected
|
||||
selected_indices=()
|
||||
IFS=',' read -r -a tokens <<<"$selected"
|
||||
for token in "${tokens[@]}"; do
|
||||
# Strip surrounding whitespace and skip empty tokens
|
||||
token="${token//[[:space:]]/}"
|
||||
[ -z "$token" ] && continue
|
||||
if [[ "$token" =~ ^([0-9]+)-([0-9]+)$ ]]; then
|
||||
for ((i = BASH_REMATCH[1]; i <= BASH_REMATCH[2]; i++)); do
|
||||
start=${BASH_REMATCH[1]}
|
||||
end=${BASH_REMATCH[2]}
|
||||
if ((start > end)); then
|
||||
echo -e "${RD}Ignoring invalid range '${token}' (start greater than end).${CL}"
|
||||
continue
|
||||
fi
|
||||
for ((i = start; i <= end; i++)); do
|
||||
selected_indices+=("$i")
|
||||
done
|
||||
else
|
||||
elif [[ "$token" =~ ^[0-9]+$ ]]; then
|
||||
selected_indices+=("$token")
|
||||
else
|
||||
echo -e "${RD}Ignoring invalid selection '${token}'.${CL}"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -101,8 +112,7 @@ for kernel in "${kernels_to_remove[@]}"; do
|
||||
remaining=$(dpkg --list |
|
||||
awk '/^ii/ {print $2}' |
|
||||
grep -E "^proxmox-kernel-${minor_version}\." |
|
||||
grep -v "^${kernel}$" |
|
||||
wc -l)
|
||||
grep -cv "^${kernel}$")
|
||||
if [ "$remaining" -eq 0 ]; then
|
||||
pkgs_to_remove+=("$meta")
|
||||
fi
|
||||
|
||||
+11
-11
@@ -16,10 +16,10 @@ function header_info {
|
||||
EOF
|
||||
}
|
||||
|
||||
RD=$(echo "\033[01;31m")
|
||||
YW=$(echo "\033[33m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
CL=$(echo "\033[m")
|
||||
RD="\033[01;31m"
|
||||
YW="\033[33m"
|
||||
GN="\033[1;92m"
|
||||
CL="\033[m"
|
||||
BFR="\\r\\033[K"
|
||||
HOLD="-"
|
||||
CM="${GN}✓${CL}"
|
||||
@@ -47,7 +47,7 @@ intel() {
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
intel_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode//" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
|
||||
intel_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
|
||||
[ -z "$intel_microcode" ] && {
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68
|
||||
msg_info "Exiting"
|
||||
@@ -80,17 +80,17 @@ intel() {
|
||||
msg_ok "Downloaded the Intel Processor Microcode Package $microcode"
|
||||
|
||||
msg_info "Installing $microcode (Patience)"
|
||||
dpkg -i $microcode &>/dev/null
|
||||
dpkg -i "$microcode" &>/dev/null
|
||||
msg_ok "Installed $microcode"
|
||||
|
||||
msg_info "Cleaning up"
|
||||
rm $microcode
|
||||
rm -f "$microcode"
|
||||
msg_ok "Cleaned"
|
||||
echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n"
|
||||
}
|
||||
|
||||
amd() {
|
||||
amd_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode///" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
|
||||
amd_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
|
||||
|
||||
[ -z "$amd_microcode" ] && {
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68
|
||||
@@ -120,15 +120,15 @@ amd() {
|
||||
}
|
||||
|
||||
msg_info "Downloading the AMD Processor Microcode Package $microcode"
|
||||
curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o $(basename "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode")
|
||||
curl -fsSL --proto '=https' "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o "$microcode"
|
||||
msg_ok "Downloaded the AMD Processor Microcode Package $microcode"
|
||||
|
||||
msg_info "Installing $microcode (Patience)"
|
||||
dpkg -i $microcode &>/dev/null
|
||||
dpkg -i "$microcode" &>/dev/null
|
||||
msg_ok "Installed $microcode"
|
||||
|
||||
msg_info "Cleaning up"
|
||||
rm $microcode
|
||||
rm -f "$microcode"
|
||||
msg_ok "Cleaned"
|
||||
echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n"
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ EOF
|
||||
}
|
||||
|
||||
# Color definitions
|
||||
RD=$(echo "\033[01;31m")
|
||||
YW=$(echo "\033[33m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
CL=$(echo "\033[m")
|
||||
RD="\033[01;31m"
|
||||
YW="\033[33m"
|
||||
GN="\033[1;92m"
|
||||
CL="\033[m"
|
||||
BFR="\\r\\033[K"
|
||||
HOLD="-"
|
||||
CM="${GN}✓${CL}"
|
||||
@@ -94,11 +94,11 @@ intel() {
|
||||
msg_ok "Downloaded Intel processor microcode package $microcode"
|
||||
|
||||
msg_info "Installing $microcode (this might take a while)"
|
||||
dpkg -i $microcode &>/dev/null
|
||||
dpkg -i "$microcode" &>/dev/null
|
||||
msg_ok "Installed $microcode"
|
||||
|
||||
msg_info "Cleaning up"
|
||||
rm $microcode
|
||||
rm -f "$microcode"
|
||||
msg_ok "Clean up complete"
|
||||
echo -e "\nA system reboot is required to apply the changes.\n"
|
||||
}
|
||||
@@ -137,15 +137,15 @@ amd() {
|
||||
}
|
||||
|
||||
msg_info "Downloading AMD processor microcode package $microcode"
|
||||
curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o $(basename "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode")
|
||||
curl -fsSL --proto '=https' "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o "$microcode"
|
||||
msg_ok "Downloaded AMD processor microcode package $microcode"
|
||||
|
||||
msg_info "Installing $microcode (this might take a while)"
|
||||
dpkg -i $microcode &>/dev/null
|
||||
dpkg -i "$microcode" &>/dev/null
|
||||
msg_ok "Installed $microcode"
|
||||
|
||||
msg_info "Cleaning up"
|
||||
rm $microcode
|
||||
rm -f "$microcode"
|
||||
msg_ok "Clean up complete"
|
||||
echo -e "\nA system reboot is required to apply the changes.\n"
|
||||
}
|
||||
|
||||
@@ -20,16 +20,26 @@ header_info() {
|
||||
EOF
|
||||
}
|
||||
header_info
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Governors" --yesno "View/Change CPU Scaling Governors. Proceed?" 10 58
|
||||
current_governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Governors" --yesno "View/Change CPU Scaling Governors. Proceed?" 10 58 || exit 0
|
||||
|
||||
GOV_BASE="/sys/devices/system/cpu/cpu0/cpufreq"
|
||||
if [[ ! -r "$GOV_BASE/scaling_governor" || ! -r "$GOV_BASE/scaling_available_governors" ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Not Available" \
|
||||
--msgbox "CPU frequency scaling is not available on this system.\n\nThis is normal when no cpufreq driver is active (e.g. CPU power management handled by the BIOS, or certain virtualized hosts)." 12 70
|
||||
clear
|
||||
exit 0
|
||||
fi
|
||||
|
||||
current_governor=$(cat "$GOV_BASE/scaling_governor")
|
||||
GOVERNORS_MENU=()
|
||||
MSG_MAX_LENGTH=0
|
||||
while read -r TAG ITEM; do
|
||||
OFFSET=2
|
||||
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
|
||||
GOVERNORS_MENU+=("$TAG" "$ITEM " "OFF")
|
||||
done < <(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors | tr ' ' '\n' | grep -v "$current_governor")
|
||||
scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --checklist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
|
||||
done < <(tr ' ' '\n' <"$GOV_BASE/scaling_available_governors" | sed '/^$/d' | grep -vxF "$current_governor")
|
||||
# A radiolist is used on purpose: only a single governor can be active at a time.
|
||||
scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --radiolist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
|
||||
[ -z "$scaling_governor" ] && {
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68
|
||||
clear
|
||||
@@ -49,7 +59,7 @@ yes)
|
||||
EXISTING_CRONTAB=$(crontab -l 2>/dev/null)
|
||||
if [[ -n "$EXISTING_CRONTAB" ]]; then
|
||||
TEMP_CRONTAB_FILE=$(mktemp)
|
||||
echo "$EXISTING_CRONTAB" | grep -v "@reboot (sleep 60 && echo*" >"$TEMP_CRONTAB_FILE"
|
||||
echo "$EXISTING_CRONTAB" | grep -vF "@reboot (sleep 60 && echo" >"$TEMP_CRONTAB_FILE"
|
||||
crontab "$TEMP_CRONTAB_FILE"
|
||||
rm "$TEMP_CRONTAB_FILE"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user