Compare commits

..

13 Commits

Author SHA1 Message Date
MickLesk
a3498644fc fix: harden shell scripts against injection and insecure permissions
Security fixes across multiple files:

- install.func: Quote command substitutions in mkdir/systemctl to prevent
  word splitting and globbing on GETTY_OVERRIDE path
- build.func: Escape sed special chars (& \) in current_os/hostname/ip
  before using them as sed replacement strings in update_motd_ip
- build.func: Escape regex metacharacters (. |) in $LANG before sed use
- build.func: Validate render_gid/video_gid as numeric before sed injection
- build.func: Use HTTPS for Alpine APK repositories instead of HTTP
- tools.func: Verify GPG dearmor output is non-empty (-s check)
- tools.func: Tighten GPU device permissions from 666 to 660 (owner+group)
- tools.func: Add chgrp render for /dev/kfd (AMD ROCm)
- shinobi-install.sh: chmod 777 -> 644 on version.json
- tasmoadmin-install.sh: chmod 777 -> 775 on tmp/data directories
- runtipi.sh: chmod 666 -> 660 on settings.json
2026-03-23 21:22:58 +01:00
community-scripts-pr-app[bot]
b1604ceae0 Update CHANGELOG.md (#13238)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-23 19:44:25 +00:00
community-scripts-pr-app[bot]
89c205d57c Update CHANGELOG.md (#13237)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-23 19:44:06 +00:00
CanbiZ (MickLesk)
5c795395ca Refactor: nginxproxymanager update and OpenResty flow (#13216) 2026-03-23 20:44:04 +01:00
community-scripts-pr-app[bot]
1512711435 Update CHANGELOG.md (#13236)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-23 19:43:58 +00:00
CanbiZ (MickLesk)
a2616ee258 Improve network connectivity and DNS checks (#13222) 2026-03-23 20:43:48 +01:00
community-scripts-pr-app[bot]
4ce6271ec0 Update CHANGELOG.md (#13235)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-23 19:43:43 +00:00
community-scripts-pr-app[bot]
da932e62d0 Update CHANGELOG.md (#13234)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-23 19:43:35 +00:00
CanbiZ (MickLesk)
c2838b69ce Refactor: PartDB (#13229) 2026-03-23 20:43:17 +01:00
community-scripts-pr-app[bot]
676397add0 Update CHANGELOG.md (#13233)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-23 19:43:12 +00:00
CanbiZ (MickLesk)
ec7f2a2e33 Tracearr: modify service restart and modify build ressources (#13230) 2026-03-23 20:42:46 +01:00
community-scripts-pr-app[bot]
e2027a43b4 Update CHANGELOG.md (#13232)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-23 19:42:26 +00:00
CanbiZ (MickLesk)
f29606ae87 fix(build): allow /31 and /32 CIDR with out-of-subnet gateway (#13231) 2026-03-23 20:41:55 +01:00
16 changed files with 658 additions and 734 deletions

View File

@@ -436,10 +436,26 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- NginxProxyManager: build OpenResty from source via GitHub releases [@MickLesk](https://github.com/MickLesk) ([#13134](https://github.com/community-scripts/ProxmoxVE/pull/13134))
- #### 🐞 Bug Fixes
- Tracearr: modify service restart and modify build ressources [@MickLesk](https://github.com/MickLesk) ([#13230](https://github.com/community-scripts/ProxmoxVE/pull/13230))
- #### ✨ New Features
- Kometa: optimize config.yml sed patterns, add Quickstart integration [@MickLesk](https://github.com/MickLesk) ([#13198](https://github.com/community-scripts/ProxmoxVE/pull/13198))
- #### 🔧 Refactor
- Refactor: nginxproxymanager update and OpenResty flow [@MickLesk](https://github.com/MickLesk) ([#13216](https://github.com/community-scripts/ProxmoxVE/pull/13216))
- Refactor: PartDB [@MickLesk](https://github.com/MickLesk) ([#13229](https://github.com/community-scripts/ProxmoxVE/pull/13229))
### 💾 Core
- #### 🔧 Refactor
- core: alpine - Improve network connectivity and DNS checks [@MickLesk](https://github.com/MickLesk) ([#13222](https://github.com/community-scripts/ProxmoxVE/pull/13222))
- core: allow /31 and /32 CIDR with out-of-subnet gateway [@MickLesk](https://github.com/MickLesk) ([#13231](https://github.com/community-scripts/ProxmoxVE/pull/13231))
## 2026-03-22
### 🆕 New Scripts

View File

@@ -49,41 +49,25 @@ function update_script() {
NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs
RELEASE=$(get_latest_github_release "NginxProxyManager/nginx-proxy-manager")
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" "tarball" "v${RELEASE}" "/opt/nginxproxymanager"
msg_info "Stopping Services"
systemctl stop openresty
systemctl stop npm
msg_ok "Stopped Services"
msg_info "Cleaning old files"
$STD rm -rf /app \
/var/www/html \
/etc/nginx \
/var/log/nginx \
/var/lib/nginx \
/var/cache/nginx
msg_ok "Cleaned old files"
msg_info "Migrating to OpenResty from source"
rm -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg /etc/apt/trusted.gpg.d/openresty.gpg
rm -f /etc/apt/sources.list.d/openresty.list /etc/apt/sources.list.d/openresty.sources
if dpkg -l openresty &>/dev/null; then
if dpkg -s openresty &>/dev/null 2>&1; then
msg_info "Migrating from packaged OpenResty to source"
rm -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg /etc/apt/trusted.gpg.d/openresty.gpg
rm -f /etc/apt/sources.list.d/openresty.list /etc/apt/sources.list.d/openresty.sources
$STD apt remove -y openresty
$STD apt autoremove -y
rm -f ~/.openresty
msg_ok "Migrated from packaged OpenResty to source"
fi
local pcre_pkg="libpcre3-dev"
if grep -qE 'VERSION_ID="1[3-9]"' /etc/os-release 2>/dev/null; then
pcre_pkg="libpcre2-dev"
fi
$STD apt install -y build-essential "$pcre_pkg" libssl-dev zlib1g-dev
msg_ok "Migrated to OpenResty from source"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "openresty" "openresty/openresty" "prebuild" "latest" "/opt/openresty" "openresty-*.tar.gz"
if check_for_gh_release "openresty" "openresty/openresty"; then
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "openresty" "openresty/openresty" "prebuild" "${CHECK_UPDATE_RELEASE}" "/opt/openresty" "openresty-*.tar.gz"
if [[ -d /opt/openresty ]]; then
msg_info "Building OpenResty"
cd /opt/openresty
$STD ./configure \
@@ -114,75 +98,100 @@ ExecStart=/usr/local/openresty/nginx/sbin/nginx -g 'daemon off;'
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl restart openresty
msg_ok "Built OpenResty"
fi
msg_info "Setting up Environment"
ln -sf /usr/bin/python3 /usr/bin/python
ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx
ln -sf /usr/local/openresty/nginx/ /etc/nginx
sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json
sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json
sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf
NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf")
for NGINX_CONF in $NGINX_CONFS; do
sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF"
done
mkdir -p /var/www/html /etc/nginx/logs
cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/
cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/
cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini
cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager
ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf
rm -f /etc/nginx/conf.d/dev.conf
mkdir -p /tmp/nginx/body \
/run/nginx \
/data/nginx \
/data/custom_ssl \
/data/logs \
/data/access \
/data/nginx/default_host \
/data/nginx/default_www \
/data/nginx/proxy_host \
/data/nginx/redirection_host \
/data/nginx/stream \
/data/nginx/dead_host \
/data/nginx/temp \
/var/lib/nginx/cache/public \
/var/lib/nginx/cache/private \
/var/cache/nginx/proxy_temp
chmod -R 777 /var/cache/nginx
chown root /tmp/nginx
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf
if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then
$STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem
if [ -d /opt/certbot ]; then
msg_info "Updating Certbot"
$STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel
$STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare
msg_ok "Updated Certbot"
fi
mkdir -p /app/frontend/images
cp -r /opt/nginxproxymanager/backend/* /app
msg_ok "Set up Environment"
if check_for_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager"; then
msg_info "Stopping Services"
systemctl stop openresty
systemctl stop npm
msg_ok "Stopped Services"
msg_info "Building Frontend"
export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider"
cd /opt/nginxproxymanager/frontend
# Replace node-sass with sass in package.json before installation
sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json
$STD yarn install --network-timeout 600000
$STD yarn locale-compile
$STD yarn build
cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend
cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images
msg_ok "Built Frontend"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" "tarball" "${CHECK_UPDATE_RELEASE}" "/opt/nginxproxymanager"
msg_info "Initializing Backend"
rm -rf /app/config/default.json
if [ ! -f /app/config/production.json ]; then
cat <<'EOF' >/app/config/production.json
msg_info "Cleaning old files"
$STD rm -rf /app \
/var/www/html \
/etc/nginx \
/var/log/nginx \
/var/lib/nginx \
/var/cache/nginx
msg_ok "Cleaned old files"
local RELEASE="${CHECK_UPDATE_RELEASE#v}"
msg_info "Setting up Environment"
ln -sf /usr/bin/python3 /usr/bin/python
ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx
ln -sf /usr/local/openresty/nginx/ /etc/nginx
sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json
sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json
sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf
NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf")
for NGINX_CONF in $NGINX_CONFS; do
sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF"
done
mkdir -p /var/www/html /etc/nginx/logs
cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/
cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/
cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini
cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager
ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf
rm -f /etc/nginx/conf.d/dev.conf
mkdir -p /tmp/nginx/body \
/run/nginx \
/data/nginx \
/data/custom_ssl \
/data/logs \
/data/access \
/data/nginx/default_host \
/data/nginx/default_www \
/data/nginx/proxy_host \
/data/nginx/redirection_host \
/data/nginx/stream \
/data/nginx/dead_host \
/data/nginx/temp \
/var/lib/nginx/cache/public \
/var/lib/nginx/cache/private \
/var/cache/nginx/proxy_temp
chmod -R 777 /var/cache/nginx
chown root /tmp/nginx
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf
if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then
$STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem
fi
mkdir -p /app/frontend/images
cp -r /opt/nginxproxymanager/backend/* /app
msg_ok "Set up Environment"
msg_info "Building Frontend"
export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider"
cd /opt/nginxproxymanager/frontend
sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json
$STD yarn install --network-timeout 600000
$STD yarn locale-compile
$STD yarn build
cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend
cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images
msg_ok "Built Frontend"
msg_info "Initializing Backend"
rm -rf /app/config/default.json
if [ ! -f /app/config/production.json ]; then
cat <<'EOF' >/app/config/production.json
{
"database": {
"engine": "knex-native",
@@ -196,28 +205,21 @@ EOF
}
}
EOF
fi
sed -i 's/"client": "sqlite3"/"client": "better-sqlite3"/' /app/config/production.json
cd /app
$STD yarn install --network-timeout 600000
msg_ok "Initialized Backend"
msg_info "Starting Services"
sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf
sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager
systemctl daemon-reload
systemctl enable -q --now openresty
systemctl enable -q --now npm
msg_ok "Started Services"
msg_ok "Updated successfully!"
fi
sed -i 's/"client": "sqlite3"/"client": "better-sqlite3"/' /app/config/production.json
cd /app
$STD yarn install --network-timeout 600000
msg_ok "Initialized Backend"
msg_info "Updating Certbot"
if [ -d /opt/certbot ]; then
$STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel
$STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare
fi
msg_ok "Updated Certbot"
msg_info "Starting Services"
sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf
sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager
systemctl daemon-reload
systemctl enable -q --now openresty
systemctl enable -q --now npm
msg_ok "Started Services"
msg_ok "Updated successfully!"
exit
}

View File

@@ -27,36 +27,27 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
RELEASE=$(get_latest_github_release "Part-DB/Part-DB-server")
if check_for_gh_release "partdb" "Part-DB/Part-DB-server"; then
msg_info "Stopping Service"
systemctl stop apache2
msg_ok "Stopped Service"
msg_info "Updating $APP to v${RELEASE}"
cd /opt
mv /opt/partdb/ /opt/partdb-backup
curl -fsSL "https://github.com/Part-DB/Part-DB-server/archive/refs/tags/v${RELEASE}.zip" -o "/opt/v${RELEASE}.zip"
$STD unzip "v${RELEASE}.zip"
mv /opt/Part-DB-server-${RELEASE}/ /opt/partdb
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "partdb" "Part-DB/Part-DB-server" "prebuild" "latest" "/opt/partdb" "partdb_with_assets.zip"
msg_info "Updating Part-DB"
cd /opt/partdb/
cp -r "/opt/partdb-backup/.env.local" /opt/partdb/
cp -r "/opt/partdb-backup/public/media" /opt/partdb/public/
cp -r "/opt/partdb-backup/config/banner.md" /opt/partdb/config/
cp -r /opt/partdb-backup/.env.local /opt/partdb/
cp -r /opt/partdb-backup/public/media /opt/partdb/public/
cp -r /opt/partdb-backup/config/banner.md /opt/partdb/config/
export COMPOSER_ALLOW_SUPERUSER=1
$STD composer install --no-dev -o --no-interaction
$STD yarn install
$STD yarn build
$STD php bin/console cache:clear
$STD php bin/console doctrine:migrations:migrate -n
chown -R www-data:www-data /opt/partdb
rm -r "/opt/v${RELEASE}.zip"
rm -r /opt/partdb-backup
echo "${RELEASE}" >~/.partdb
msg_ok "Updated $APP to v${RELEASE}"
msg_ok "Updated Part-DB"
msg_info "Starting Service"
systemctl start apache2

View File

@@ -8,7 +8,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
APP="Tracearr"
var_tags="${var_tags:-media}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
var_ram="${var_ram:-8192}"
var_disk="${var_disk:-10}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
@@ -102,7 +102,7 @@ EOF
if check_for_gh_release "tracearr" "connorgallopo/Tracearr"; then
msg_info "Stopping Services"
systemctl stop tracearr postgresql redis
systemctl stop tracearr postgresql redis-server
msg_ok "Stopped Services"
msg_info "Updating pnpm"
@@ -115,6 +115,7 @@ EOF
msg_info "Building Tracearr"
export TZ=$(cat /etc/timezone)
export NODE_OPTIONS="--max-old-space-size=4096"
cd /opt/tracearr.build
$STD pnpm install --frozen-lockfile --force
$STD pnpm turbo telemetry disable
@@ -148,7 +149,7 @@ EOF
msg_ok "Configured Tracearr"
msg_info "Starting services"
systemctl start postgresql redis tracearr
systemctl start postgresql redis-server tracearr
msg_ok "Started services"
msg_ok "Updated successfully!"
else

View File

@@ -13,27 +13,19 @@ setting_up_container
network_check
update_os
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
PG_VERSION="16" setup_postgresql
PG_DB_NAME="partdb" PG_DB_USER="partdb" setup_postgresql_db
PHP_VERSION="8.4" PHP_APACHE="YES" PHP_MODULE="xsl" PHP_POST_MAX_SIZE="100M" PHP_UPLOAD_MAX_FILESIZE="100M" setup_php
setup_composer
msg_info "Installing Part-DB (Patience)"
cd /opt
RELEASE=$(get_latest_github_release "Part-DB/Part-DB-server")
curl -fsSL "https://github.com/Part-DB/Part-DB-server/archive/refs/tags/v${RELEASE}.zip" -o "/opt/v${RELEASE}.zip"
$STD unzip "v${RELEASE}.zip"
mv /opt/Part-DB-server-${RELEASE}/ /opt/partdb
fetch_and_deploy_gh_release "partdb" "Part-DB/Part-DB-server" "prebuild" "latest" "/opt/partdb" "partdb_with_assets.zip"
msg_info "Installing Part-DB"
cd /opt/partdb/
cp .env .env.local
sed -i "s|DATABASE_URL=\"sqlite:///%kernel.project_dir%/var/app.db\"|DATABASE_URL=\"postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}?serverVersion=12.19&charset=utf8\"|" .env.local
export COMPOSER_ALLOW_SUPERUSER=1
$STD composer install --no-dev -o --no-interaction
$STD yarn install
$STD yarn build
$STD php bin/console cache:clear
php bin/console doctrine:migrations:migrate -n >~/database-migration-output
chown -R www-data:www-data /opt/partdb
@@ -44,8 +36,6 @@ ADMIN_PASS=$(grep -oP 'The initial password for the "admin" user is: \K\w+' ~/da
echo "Part-DB Admin Password: $ADMIN_PASS"
} >>~/partdb.creds
rm -rf ~/database-migration-output
rm -rf "/opt/v${RELEASE}.zip"
echo "${RELEASE}" >~/.partdb
msg_ok "Installed Part-DB"
msg_info "Creating Service"

View File

@@ -35,7 +35,7 @@ cd Shinobi
gitVersionNumber=$(git rev-parse HEAD)
theDateRightNow=$(date)
touch version.json
chmod 777 version.json
chmod 644 version.json
echo '{"Product" : "'"Shinobi"'" , "Branch" : "'"master"'" , "Version" : "'"$gitVersionNumber"'" , "Date" : "'"$theDateRightNow"'" , "Repository" : "'"https://gitlab.com/Shinobi-Systems/Shinobi.git"'"}' >version.json
msg_ok "Cloned Shinobi"

View File

@@ -23,7 +23,7 @@ fetch_and_deploy_gh_release "tasmoadmin" "TasmoAdmin/TasmoAdmin" "prebuild" "lat
msg_info "Configuring TasmoAdmin"
rm -rf /etc/php/8.4/apache2/conf.d/10-opcache.ini
chown -R www-data:www-data /var/www/tasmoadmin
chmod 777 /var/www/tasmoadmin/tmp /var/www/tasmoadmin/data
chmod 775 /var/www/tasmoadmin/tmp /var/www/tasmoadmin/data
cat <<EOF >/etc/apache2/sites-available/tasmoadmin.conf
<VirtualHost *:9999>
ServerName tasmoadmin

View File

@@ -62,6 +62,7 @@ fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "lates
msg_info "Building Tracearr"
export TZ=$(cat /etc/timezone)
export NODE_OPTIONS="--max-old-space-size=4096"
cd /opt/tracearr.build
$STD pnpm install --frozen-lockfile --force
$STD pnpm turbo telemetry disable

View File

@@ -67,22 +67,22 @@ EOF
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
setting_up_container() {
msg_info "Setting up Container OS"
local _ip=""
while [ $i -gt 0 ]; do
_ip=$(ip -4 addr show 2>/dev/null | awk '/inet [0-9]/ && !/127\.0\.0\.1/ {sub(/\/.*/, "", $2); print $2; exit}')
[[ -n "$_ip" ]] && break
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then
break
fi
echo 1>&2 -en "${CROSS}${RD} No Network! "
sleep $RETRY_EVERY
i=$((i - 1))
done
if [[ -z "$_ip" ]]; then
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
echo -e "${NETWORK}Check Network Settings"
exit 121
fi
msg_ok "Set up Container OS"
msg_ok "Network Connected: ${BL}${_ip}${CL}"
msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}"
post_progress_to_api
}
@@ -90,11 +90,18 @@ setting_up_container() {
network_check() {
set +e
trap - ERR
ipv4_connected=false
# Check IPv4 connectivity to Cloudflare, Google & Quad9 DNS servers
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
ipv4_status="${GN}${CL} IPv4"
msg_ok "IPv4 Internet Connected"
ipv4_connected=true
else
ipv4_status="${RD}${CL} IPv4"
read -r -p "Internet NOT connected. Continue anyway? <y/N> " prompt
msg_error "IPv4 Internet Not Connected"
fi
if [[ $ipv4_connected == false ]]; then
read -r -p "No Internet detected, would you like to continue anyway? <y/N> " prompt
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
else
@@ -102,12 +109,28 @@ network_check() {
exit 122
fi
fi
RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }')
if [[ -z "$RESOLVEDIP" ]]; then
msg_error "Internet: ${ipv4_status} DNS Failed"
# DNS resolution checks for GitHub-related domains
GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org")
GIT_STATUS="Git DNS:"
DNS_FAILED=false
for HOST in "${GIT_HOSTS[@]}"; do
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
if [[ -z "$RESOLVEDIP" ]]; then
GIT_STATUS+="$HOST:($DNSFAIL)"
DNS_FAILED=true
else
GIT_STATUS+=" $HOST:($DNSOK)"
fi
done
if [[ "$DNS_FAILED" == true ]]; then
fatal "$GIT_STATUS"
else
msg_ok "Internet: ${ipv4_status} DNS: ${BL}${RESOLVEDIP}${CL}"
msg_ok "$GIT_STATUS"
fi
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
}

View File

@@ -53,7 +53,7 @@ download_with_progress() {
# $1 url, $2 dest
local url="$1" out="$2" cl
need_tool curl pv || return 1
cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {gsub(/\r/,""); print $2}')
cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r')
if [ -n "$cl" ]; then
curl -fsSL "$url" | pv -s "$cl" >"$out" || {
msg_error "Download failed: $url"

View File

@@ -348,10 +348,10 @@ explain_exit_code() {
json_escape() {
# Escape a string for safe JSON embedding using awk (handles any input size).
# Pipeline: strip ANSI → remove control chars → escape \ " TAB → join lines with \n
printf '%s' "$1" |
sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' |
tr -d '\000-\010\013\014\016-\037\177\r' |
awk '
printf '%s' "$1" \
| sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' \
| tr -d '\000-\010\013\014\016-\037\177\r' \
| awk '
BEGIN { ORS = "" }
{
gsub(/\\/, "\\\\") # backslash → \\
@@ -401,7 +401,7 @@ get_error_text() {
fi
if [[ -n "$logfile" && -s "$logfile" ]]; then
tail -n 20 "$logfile" 2>/dev/null | sed -E 's/\r$//; s/\x1b\[[0-9;]*[a-zA-Z]//g'
tail -n 20 "$logfile" 2>/dev/null | sed 's/\r$//' | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g'
fi
}
@@ -508,8 +508,7 @@ detect_gpu() {
if [[ -n "$gpu_line" ]]; then
# Extract model: everything after the colon, clean up
GPU_MODEL=$(echo "$gpu_line" | sed -E 's/.*: //; s/ \(rev .*\)$//')
GPU_MODEL="${GPU_MODEL:0:64}"
GPU_MODEL=$(echo "$gpu_line" | sed 's/.*: //' | sed 's/ (rev .*)$//' | cut -c1-64)
# Detect vendor and passthrough type
if echo "$gpu_line" | grep -qi "Intel"; then
@@ -558,8 +557,7 @@ detect_cpu() {
esac
# Extract model name and clean it up
CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed -E 's/^ *//; s/\(R\)//g; s/\(TM\)//g; s/ +/ /g')
CPU_MODEL="${CPU_MODEL:0:64}"
CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ *//' | sed 's/(R)//g' | sed 's/(TM)//g' | sed 's/ */ /g' | cut -c1-64)
fi
export CPU_VENDOR CPU_MODEL
@@ -1327,16 +1325,11 @@ post_addon_to_api() {
error_category=$(categorize_error "$exit_code")
fi
# Detect OS info (single read)
# Detect OS info
local os_type="" os_version=""
if [[ -f /etc/os-release ]]; then
while IFS='=' read -r _k _v; do
_v="${_v//\"/}"
case "$_k" in
ID) os_type="$_v" ;;
VERSION_ID) os_version="$_v" ;;
esac
done </etc/os-release
os_type=$(grep "^ID=" /etc/os-release | cut -d= -f2 | tr -d '"')
os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"')
fi
local JSON_PAYLOAD

File diff suppressed because it is too large Load Diff

View File

@@ -1644,7 +1644,7 @@ function get_lxc_ip() {
local ip
# Try direct interface lookup for eth0 FIRST (most reliable for LXC) - IPv4
ip=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {sub(/\/.*/, "", $2); print $2; exit}')
ip=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1)
if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "$ip"
return 0
@@ -1652,14 +1652,11 @@ function get_lxc_ip() {
# Fallback: Try hostname -I (returns IPv4 first if available)
if command -v hostname >/dev/null 2>&1; then
local -a _ips
read -ra _ips <<<"$(hostname -I 2>/dev/null)"
for ip in "${_ips[@]}"; do
if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "$ip"
return 0
fi
done
ip=$(hostname -I 2>/dev/null | awk '{print $1}')
if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "$ip"
return 0
fi
fi
# Try routing table with IPv4 targets
@@ -1677,7 +1674,7 @@ function get_lxc_ip() {
done
# IPv6 fallback: Try direct interface lookup for eth0
ip=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {sub(/\/.*/, "", $2); print $2; exit}')
ip=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1)
if [[ -n "$ip" && "$ip" =~ : ]]; then
echo "$ip"
return 0
@@ -1685,14 +1682,11 @@ function get_lxc_ip() {
# IPv6 fallback: Try hostname -I for IPv6
if command -v hostname >/dev/null 2>&1; then
local -a _ips6
read -ra _ips6 <<<"$(hostname -I 2>/dev/null)"
for ip in "${_ips6[@]}"; do
[[ "$ip" == *:* ]] && {
echo "$ip"
return 0
}
done
ip=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1)
if [[ -n "$ip" && "$ip" =~ : ]]; then
echo "$ip"
return 0
fi
fi
# IPv6 fallback: Use routing table with IPv6 targets

View File

@@ -116,14 +116,14 @@ setting_up_container() {
(chown root:root / 2>/dev/null) || true
fi
local _host_ip=""
for ((i = RETRY_NUM; i > 0; i--)); do
_host_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
[[ -n "$_host_ip" ]] && break
if [ "$(hostname -I)" != "" ]; then
break
fi
echo 1>&2 -en "${CROSS}${RD} No Network! "
sleep $RETRY_EVERY
done
if [[ -z "$_host_ip" ]]; then
if [ "$(hostname -I)" = "" ]; then
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
echo -e "${NETWORK}Check Network Settings"
exit 121
@@ -131,7 +131,8 @@ setting_up_container() {
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
systemctl disable -q --now systemd-networkd-wait-online.service
msg_ok "Set up Container OS"
msg_ok "Network Connected: ${BL}${_host_ip}"
#msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)"
msg_ok "Network Connected: ${BL}$(hostname -I)"
post_progress_to_api
}
@@ -308,14 +309,14 @@ customize() {
if [[ "$PASSWORD" == "" ]]; then
msg_info "Customizing Container"
GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf"
mkdir -p $(dirname $GETTY_OVERRIDE)
cat <<EOF >$GETTY_OVERRIDE
mkdir -p "$(dirname "$GETTY_OVERRIDE")"
cat <<EOF >"$GETTY_OVERRIDE"
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM
EOF
systemctl daemon-reload
systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//')
systemctl restart "$(basename "$(dirname "$GETTY_OVERRIDE")" | sed 's/\.d//')"
msg_ok "Customized Container"
fi
echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update

View File

@@ -242,7 +242,7 @@ download_gpg_key() {
# Process based on mode
if [[ "$mode" == "dearmor" ]]; then
if gpg --dearmor --yes -o "$output" <"$temp_key" 2>/dev/null; then
if gpg --dearmor --yes -o "$output" <"$temp_key" 2>/dev/null && [[ -s "$output" ]]; then
rm -f "$temp_key"
debug_log "GPG key installed (dearmored): $output"
return 0
@@ -700,7 +700,7 @@ manage_tool_repository() {
local gpg_key_url="${4:-}"
local distro_id repo_component suite
distro_id=$(get_os_info id)
distro_id=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"')
case "$tool_name" in
mariadb)
@@ -714,7 +714,7 @@ manage_tool_repository() {
# Get suite for fallback handling
local distro_codename
distro_codename=$(get_os_info codename)
distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url/$distro_id")
# Setup new repository using deb822 format
@@ -745,7 +745,7 @@ manage_tool_repository() {
# Setup repository
local distro_codename
distro_codename=$(get_os_info codename)
distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
# Suite mapping with fallback for newer releases not yet supported by upstream
if [[ "$distro_id" == "debian" ]]; then
@@ -816,7 +816,7 @@ EOF
# NodeSource uses deb822 format with GPG from repo
local distro_codename
distro_codename=$(get_os_info codename)
distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
# Download GPG key from NodeSource with retry logic
if ! download_gpg_key "$gpg_key_url" "/etc/apt/keyrings/nodesource.gpg" "dearmor"; then
@@ -858,7 +858,7 @@ EOF
# Setup repository
local distro_codename
distro_codename=$(get_os_info codename)
distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
cat <<EOF >/etc/apt/sources.list.d/php.sources
Types: deb
URIs: https://packages.sury.org/php
@@ -886,7 +886,7 @@ EOF
# Setup repository
local distro_codename
distro_codename=$(get_os_info codename)
distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
cat <<EOF >/etc/apt/sources.list.d/postgresql.sources
Types: deb
URIs: http://apt.postgresql.org/pub/repos/apt
@@ -1323,16 +1323,10 @@ get_os_info() {
# Cache OS info to avoid repeated file reads
if [[ -z "${_OS_ID:-}" ]]; then
local _line
while IFS='=' read -r _key _val; do
_val="${_val//\"/}"
case "$_key" in
ID) export _OS_ID="$_val" ;;
VERSION_CODENAME) export _OS_CODENAME="$_val" ;;
VERSION_ID) export _OS_VERSION="$_val" ;;
VERSION) export _OS_VERSION_FULL="$_val" ;;
esac
done </etc/os-release
export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release)
export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release)
export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release)
export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release)
fi
case "$field" in
@@ -2150,8 +2144,8 @@ fetch_and_deploy_gh_tag() {
local repo="$2"
local version="${3:-latest}"
local target="${4:-/opt/$app}"
local app_lc="${app,,}"
app_lc="${app_lc// /}"
local app_lc=""
app_lc="$(echo "${app,,}" | tr -d ' ')"
local version_file="$HOME/.${app_lc}"
if [[ "$version" == "latest" ]]; then
@@ -2227,8 +2221,8 @@ check_for_gh_tag() {
local app="$1"
local repo="$2"
local prefix="${3:-}"
local app_lc="${app,,}"
app_lc="${app_lc// /}"
local app_lc=""
app_lc="$(echo "${app,,}" | tr -d ' ')"
local current_file="$HOME/.${app_lc}"
msg_info "Checking for update: ${app}"
@@ -2278,8 +2272,8 @@ check_for_gh_release() {
local source="$2"
local pinned_version_in="${3:-}" # optional
local pin_reason="${4:-}" # optional reason shown to user
local app_lc="${app,,}"
app_lc="${app_lc// /}"
local app_lc=""
app_lc="$(echo "${app,,}" | tr -d ' ')"
local current_file="$HOME/.${app_lc}"
msg_info "Checking for update: ${app}"
@@ -2606,8 +2600,7 @@ create_self_signed_cert() {
local APP_NAME="${1:-${APPLICATION}}"
local HOSTNAME="$(hostname -f)"
local IP="$(hostname -I | awk '{print $1}')"
local APP_NAME_LC="${APP_NAME,,}"
APP_NAME_LC="${APP_NAME_LC// /}"
local APP_NAME_LC=$(echo "${APP_NAME,,}" | tr -d ' ')
local CERT_DIR="/etc/ssl/${APP_NAME_LC}"
local CERT_KEY="${CERT_DIR}/${APP_NAME_LC}.key"
local CERT_CRT="${CERT_DIR}/${APP_NAME_LC}.crt"
@@ -2654,7 +2647,7 @@ function download_with_progress() {
# Content-Length aus HTTP-Header holen
local content_length
content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {gsub(/\r/,""); print $2}' || true)
content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true)
if [[ -z "$content_length" ]]; then
if ! curl -fL# -o "$output" "$url"; then
@@ -2773,8 +2766,7 @@ function fetch_and_deploy_codeberg_release() {
local target="${5:-/opt/$app}"
local asset_pattern="${6:-}"
local app_lc="${app,,}"
app_lc="${app_lc// /}"
local app_lc=$(echo "${app,,}" | tr -d ' ')
local version_file="$HOME/.${app_lc}"
local api_timeouts=(60 120 240)
@@ -3326,8 +3318,7 @@ function fetch_and_deploy_gh_release() {
fi
fi
local app_lc="${app,,}"
app_lc="${app_lc// /}"
local app_lc=$(echo "${app,,}" | tr -d ' ')
local version_file="$HOME/.${app_lc}"
local api_timeouts=(60 120 240)
@@ -4418,7 +4409,7 @@ function setup_hwaccel() {
# Parse comma-separated numbers
IFS=',' read -ra nums <<<"$selection"
for num in "${nums[@]}"; do
num="${num// /}"
num=$(echo "$num" | tr -d ' ')
if [[ "$num" =~ ^[0-9]+$ ]] && ((num >= 1 && num <= gpu_count)); then
SELECTED_INDICES+=("$((num - 1))")
fi
@@ -4460,9 +4451,9 @@ function setup_hwaccel() {
# OS Detection
# ═══════════════════════════════════════════════════════════════════════════
local os_id os_codename os_version
os_id=$(get_os_info id)
os_codename=$(get_os_info codename)
os_version=$(get_os_info version)
os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "debian")
os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "unknown")
os_version=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "")
[[ -z "$os_id" ]] && os_id="debian"
local in_ct="${CTTYPE:-0}"
@@ -5201,7 +5192,7 @@ _setup_gpu_permissions() {
for nvidia_dev in /dev/nvidia*; do
[[ -e "$nvidia_dev" ]] && {
chgrp video "$nvidia_dev" 2>/dev/null || true
chmod 666 "$nvidia_dev" 2>/dev/null || true
chmod 660 "$nvidia_dev" 2>/dev/null || true
}
done
if [[ -d /dev/nvidia-caps ]]; then
@@ -5209,7 +5200,7 @@ _setup_gpu_permissions() {
for caps_dev in /dev/nvidia-caps/*; do
[[ -e "$caps_dev" ]] && {
chgrp video "$caps_dev" 2>/dev/null || true
chmod 666 "$caps_dev" 2>/dev/null || true
chmod 660 "$caps_dev" 2>/dev/null || true
}
done
fi
@@ -5226,7 +5217,8 @@ _setup_gpu_permissions() {
# /dev/kfd permissions (AMD ROCm)
if [[ -e /dev/kfd ]]; then
chmod 666 /dev/kfd 2>/dev/null || true
chgrp render /dev/kfd 2>/dev/null || true
chmod 660 /dev/kfd 2>/dev/null || true
msg_info "AMD ROCm compute device configured"
fi
@@ -5348,8 +5340,8 @@ function setup_imagemagick() {
function setup_java() {
local JAVA_VERSION="${JAVA_VERSION:-21}"
local DISTRO_ID DISTRO_CODENAME
DISTRO_ID=$(get_os_info id)
DISTRO_CODENAME=$(get_os_info codename)
DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"')
DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)
local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk"
# Prepare repository (cleanup + validation)
@@ -5604,7 +5596,7 @@ EOF
if [[ -n "$CURRENT_VERSION" ]]; then
# Get available distro version
local DISTRO_VERSION=""
DISTRO_VERSION=$(apt-cache policy mariadb-server 2>/dev/null | awk '/Candidate:/ {sub(/^[0-9]+:/, "", $2); print $2}' || echo "")
DISTRO_VERSION=$(apt-cache policy mariadb-server 2>/dev/null | grep -E "Candidate:" | awk '{print $2}' | grep -oP '^\d+:\K\d+\.\d+\.\d+' || echo "")
if [[ -n "$DISTRO_VERSION" ]]; then
# Compare versions - if current is higher, keep it
@@ -6005,8 +5997,8 @@ function setup_mysql() {
local MYSQL_VERSION="${MYSQL_VERSION:-8.0}"
local USE_MYSQL_REPO="${USE_MYSQL_REPO:-true}"
local DISTRO_ID DISTRO_CODENAME
DISTRO_ID=$(get_os_info id)
DISTRO_CODENAME=$(get_os_info codename)
DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"')
DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
# Ensure non-interactive mode for all apt operations
export DEBIAN_FRONTEND=noninteractive
@@ -6352,7 +6344,7 @@ function setup_nodejs() {
# Check if the module is already installed
if $STD npm list -g --depth=0 "$MODULE_NAME" 2>&1 | grep -q "$MODULE_NAME@"; then
MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" 2>&1 | awk -F@ -v mod="$MODULE_NAME" '$0 ~ mod"@" {gsub(/[[:space:]]/, "", $2); print $2}' || echo '')"
MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" 2>&1 | grep "$MODULE_NAME@" | awk -F@ '{print $2}' 2>/dev/null | tr -d '[:space:]' || echo '')"
if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then
msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION"
if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then
@@ -6423,8 +6415,8 @@ function setup_php() {
local PHP_APACHE="${PHP_APACHE:-NO}"
local PHP_FPM="${PHP_FPM:-NO}"
local DISTRO_ID DISTRO_CODENAME
DISTRO_ID=$(get_os_info id)
DISTRO_CODENAME=$(get_os_info codename)
DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"')
DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
# Parse version for compatibility checks
local PHP_MAJOR="${PHP_VERSION%%.*}"
@@ -6471,7 +6463,7 @@ function setup_php() {
local FILTERED_MODULES=""
IFS=',' read -ra ALL_MODULES <<<"$COMBINED_MODULES"
for mod in "${ALL_MODULES[@]}"; do
mod="${mod//[[:space:]]/}"
mod=$(echo "$mod" | tr -d '[:space:]')
[[ -z "$mod" ]] && continue
# Skip if it's a known built-in module
@@ -6488,7 +6480,7 @@ function setup_php() {
done
# Deduplicate
COMBINED_MODULES=$(awk -v RS=',' '!seen[$0]++{printf "%s%s", sep, $0; sep=","}' <<<"$FILTERED_MODULES")
COMBINED_MODULES=$(echo "$FILTERED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -)
# Get current PHP-CLI version
local CURRENT_PHP=""
@@ -6557,7 +6549,7 @@ EOF
IFS=',' read -ra MODULES <<<"$COMBINED_MODULES"
for mod in "${MODULES[@]}"; do
mod="${mod//[[:space:]]/}"
mod=$(echo "$mod" | tr -d '[:space:]')
[[ -z "$mod" ]] && continue
local pkg_name="php${PHP_VERSION}-${mod}"
@@ -6736,8 +6728,8 @@ setup_postgresql() {
local PG_MODULES="${PG_MODULES:-}"
local USE_PGDG_REPO="${USE_PGDG_REPO:-true}"
local DISTRO_ID DISTRO_CODENAME
DISTRO_ID=$(get_os_info id)
DISTRO_CODENAME=$(get_os_info codename)
DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"')
DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
# Ensure non-interactive mode for all apt operations
export DEBIAN_FRONTEND=noninteractive
@@ -7580,8 +7572,8 @@ EOF
function setup_clickhouse() {
local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}"
local DISTRO_ID DISTRO_CODENAME
DISTRO_ID=$(get_os_info id)
DISTRO_CODENAME=$(get_os_info codename)
DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"')
DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
# Ensure non-interactive mode for all apt operations
export DEBIAN_FRONTEND=noninteractive
@@ -7795,7 +7787,7 @@ function setup_rust() {
# Check if already installed
if echo "$CRATE_LIST" | grep -q "^${NAME} "; then
INSTALLED_VER=$(echo "$CRATE_LIST" | grep "^${NAME} " | head -1 | awk '{gsub(/[v:]/, "", $2); print $2}' || echo '')
INSTALLED_VER=$(echo "$CRATE_LIST" | grep "^${NAME} " | head -1 | awk '{print $2}' 2>/dev/null | tr -d 'v:' || echo '')
if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then
msg_info "Upgrading $NAME from v$INSTALLED_VER to v$VER"

View File

@@ -150,7 +150,7 @@ function install() {
curl -fsSL "https://raw.githubusercontent.com/runtipi/runtipi/master/scripts/install.sh" -o "install.sh"
chmod +x install.sh
$STD ./install.sh
chmod 666 /opt/runtipi/state/settings.json 2>/dev/null || true
chmod 660 /opt/runtipi/state/settings.json 2>/dev/null || true
rm -f /opt/install.sh
msg_ok "Installed ${APP}"