Compare commits

..

15 Commits

Author SHA1 Message Date
MickLesk 0a81e7e5e5 fix env 2026-07-10 20:18:46 +02:00
MickLesk 08c84bd89d Block Pangolin SQLite upgrades
Update the Pangolin CT upgrade path to fail fast when PostgreSQL is not installed. The script now explains that upgrades to Pangolin 1.20.0+ require PostgreSQL and that SQLite data cannot be migrated automatically.
2026-07-10 15:41:28 +02:00
MickLesk d371b5d9bd use create_backup and restore_backup 2026-07-10 15:37:31 +02:00
MickLesk 1b1004ad86 remove old migrations 2026-07-10 15:35:37 +02:00
MickLesk f3836df6ac remove migration paths 2026-07-10 15:35:00 +02:00
MickLesk 5530b426a3 bump pangolin to PSQL 2026-07-10 15:33:49 +02:00
MickLesk e3472681c9 another try... 2026-07-10 15:19:04 +02:00
MickLesk cfa1f504e3 extend migration check... 2026-07-10 15:07:59 +02:00
MickLesk 38423262c8 Pangolin: Bump to 1.20.0 and harden SQLite migrations
Bump the default Pangolin version to 1.20.0 in both CT and install scripts. Update the CT upgrade path to validate required 1.20.0 schema objects, clear stale versionMigrations markers when needed, retry migrations once, and abort with a clear error if the schema is still incomplete to avoid a broken runtime.
2026-07-10 14:44:32 +02:00
community-scripts-pr-app[bot] f4e0111ac0 Update CHANGELOG.md (#15680)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-10 08:57:54 +00:00
push-app-to-main[bot] 2e4558e262 Squid (#15605)
* Add squid (ct)

* Update ct/squid.sh

* Simplify squid.sh by removing proxy user instructions

Removed instructions for adding a proxy user inside the container.

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: Sam Heinz <sam@samheinz.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-07-10 10:57:27 +02:00
community-scripts-pr-app[bot] 11139aede7 Update CHANGELOG.md (#15679)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-10 08:54:13 +00:00
Slaviša Arežina 6716d8de84 Fireshare: Fix for install and upgrade to v1.7.3 (#15673)
* Update CHANGELOG.md (#15631)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Update CHANGELOG.md (#15631)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Update CHANGELOG.md (#15631)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix install and upgrade to v1.7.3

---------

Co-authored-by: community-scripts-pr-app[bot] <189241966+community-scripts-pr-app[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-10 10:53:51 +02:00
community-scripts-pr-app[bot] f1e952005e Update CHANGELOG.md (#15678)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-10 08:53:34 +00:00
Slaviša Arežina 89b671880a Endurain: Fix update procedure (#15674)
* Update CHANGELOG.md (#15631)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Update CHANGELOG.md (#15631)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Update CHANGELOG.md (#15631)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fixed update procedure

* ups

---------

Co-authored-by: community-scripts-pr-app[bot] <189241966+community-scripts-pr-app[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-10 10:53:04 +02:00
10 changed files with 752 additions and 44 deletions
+13
View File
@@ -499,6 +499,19 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-07-10
### 🆕 New Scripts
- Squid ([#15605](https://github.com/community-scripts/ProxmoxVE/pull/15605))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fireshare: Fix for install and upgrade to v1.7.3 [@tremor021](https://github.com/tremor021) ([#15673](https://github.com/community-scripts/ProxmoxVE/pull/15673))
- Endurain: Fix update procedure [@tremor021](https://github.com/tremor021) ([#15674](https://github.com/community-scripts/ProxmoxVE/pull/15674))
## 2026-07-09 ## 2026-07-09
### 🚀 Updated Scripts ### 🚀 Updated Scripts
+4 -13
View File
@@ -34,31 +34,22 @@ function update_script() {
systemctl stop endurain systemctl stop endurain
msg_ok "Stopped Service" msg_ok "Stopped Service"
msg_info "Creating Backup" create_backup /opt/endurain/.env /opt/endurain/frontend/dist/env.js
cp /opt/endurain/.env /opt/endurain.env
cp /opt/endurain/frontend/dist/env.js /opt/endurain.env.js
msg_ok "Created Backup"
CLEAN_INSTALL=1 fetch_and_deploy_codeberg_release "endurain" "endurain-project/endurain" "tarball" "latest" "/opt/endurain" CLEAN_INSTALL=1 fetch_and_deploy_codeberg_release "endurain" "endurain-project/endurain" "tarball" "latest" "/opt/endurain"
msg_info "Preparing Update" msg_info "Preparing Update"
cd /opt/endurain cd /opt/endurain
rm -rf \ rm -rf /opt/endurain/{docs,example.env,screenshot_01.png} /opt/endurain/docker* /opt/endurain/*.yml
/opt/endurain/{docs,example.env,screenshot_01.png} \
/opt/endurain/docker* \
/opt/endurain/*.yml
cp /opt/endurain.env /opt/endurain/.env
rm /opt/endurain.env
msg_ok "Prepared Update" msg_ok "Prepared Update"
msg_info "Updating Frontend" msg_info "Updating Frontend"
cd /opt/endurain/frontend cd /opt/endurain/frontend
$STD npm ci $STD npm ci
$STD npm run build $STD npm run build
cp /opt/endurain.env.js /opt/endurain/frontend/dist/env.js
rm /opt/endurain.env.js
msg_ok "Updated Frontend" msg_ok "Updated Frontend"
restore_backup
msg_info "Updating Backend" msg_info "Updating Backend"
cd /opt/endurain/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=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18")
+14 -7
View File
@@ -35,12 +35,22 @@ function update_script() {
systemctl stop fireshare systemctl stop fireshare
msg_ok "Stopped Service" msg_ok "Stopped Service"
mv /opt/fireshare/fireshare.env /opt create_backup /opt/fireshare/fireshare.env
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "fireshare" "ShaneIsrael/fireshare" "tarball" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "fireshare" "ShaneIsrael/fireshare" "tarball"
mv /opt/fireshare.env /opt/fireshare restore_backup
rm -f /usr/local/bin/fireshare rm -f /usr/local/bin/fireshare
msg_info "Updating Fireshare" if ! grep -q "__FIRESHARE_PORT__" /etc/nginx/nginx.conf; then
cp /opt/fireshare/app/nginx/prod.conf /etc/nginx/nginx.conf
sed -i 's|root /processed/|root /opt/fireshare-processed/|g' /etc/nginx/nginx.conf
sed -i 's/^user[[:space:]]\+nginx;/user root;/' /etc/nginx/nginx.conf
sed -i 's|root[[:space:]]\+/app/build;|root /opt/fireshare/app/client/build;|' /etc/nginx/nginx.conf
sed -i 's/__FIRESHARE_PORT__/80/g' /etc/nginx/nginx.conf
cp /opt/fireshare/app/nginx/error.html /etc/nginx/
cp /opt/fireshare/app/nginx/api_unavailable.html /etc/nginx/
fi
msg_info "Configuring Fireshare"
cd /opt/fireshare cd /opt/fireshare
$STD uv venv --clear $STD uv venv --clear
$STD .venv/bin/python -m ensurepip --upgrade $STD .venv/bin/python -m ensurepip --upgrade
@@ -53,13 +63,10 @@ function update_script() {
export VIDEO_DIRECTORY=/opt/fireshare-videos export VIDEO_DIRECTORY=/opt/fireshare-videos
export PROCESSED_DIRECTORY=/opt/fireshare-processed export PROCESSED_DIRECTORY=/opt/fireshare-processed
$STD uv run flask db upgrade $STD uv run flask db upgrade
msg_info "Building Fireshare Client"
cd /opt/fireshare/app/client cd /opt/fireshare/app/client
$STD npm install $STD npm install
$STD npm run build $STD npm run build
msg_ok "Built Fireshare Client" msg_ok "Configured Fireshare"
msg_ok "Updated Fireshare"
msg_info "Starting Service" msg_info "Starting Service"
systemctl start fireshare systemctl start fireshare
+6
View File
@@ -0,0 +1,6 @@
_____ _ __
/ ___/____ ___ __(_)___/ /
\__ \/ __ `/ / / / / __ /
___/ / /_/ / /_/ / / /_/ /
/____/\__, /\__,_/_/\__,_/
/_/
+14 -19
View File
@@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
# Source: https://pangolin.net/ | Github: https://github.com/fosrl/pangolin # Source: https://pangolin.net/ | Github: https://github.com/fosrl/pangolin
APP="Pangolin" APP="Pangolin"
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.4}" PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.20.0}"
var_tags="${var_tags:-proxy}" var_tags="${var_tags:-proxy}"
var_cpu="${var_cpu:-2}" var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}" var_ram="${var_ram:-4096}"
@@ -33,6 +33,15 @@ function update_script() {
ensure_dependencies build-essential python3 ensure_dependencies build-essential python3
if ! command -v psql &>/dev/null; then
msg_error "This installation uses SQLite and cannot be upgraded to Pangolin ${PANGOLIN_VERSION}."
echo -e "${INFO}${YW}Starting with Pangolin 1.20.0, PostgreSQL is required as the database backend.${CL}"
echo -e "${INFO}${YW}An automatic migration of your existing SQLite data is not supported.${CL}"
echo -e "${INFO}${YW}Please create a new LXC with the Pangolin install script, which sets up PostgreSQL automatically.${CL}"
echo -e "${INFO}${YW}Your current data is preserved in this container and can be manually migrated if needed.${CL}"
exit 1
fi
NODE_VERSION="24" setup_nodejs NODE_VERSION="24" setup_nodejs
if check_for_gh_release "pangolin" "fosrl/pangolin" "$PANGOLIN_VERSION" "Pinned to a tested release because Pangolin's schema changes have repeatedly broken unattended updates. To try a newer version at your own risk, run: 'export PANGOLIN_VERSION=<tag>' and re-run update. If it breaks, please open an issue at https://github.com/community-scripts/ProxmoxVE/issues with the error log."; then if check_for_gh_release "pangolin" "fosrl/pangolin" "$PANGOLIN_VERSION" "Pinned to a tested release because Pangolin's schema changes have repeatedly broken unattended updates. To try a newer version at your own risk, run: 'export PANGOLIN_VERSION=<tag>' and re-run update. If it breaks, please open an issue at https://github.com/community-scripts/ProxmoxVE/issues with the error log."; then
@@ -41,13 +50,7 @@ function update_script() {
systemctl stop gerbil systemctl stop gerbil
msg_info "Service stopped" msg_info "Service stopped"
msg_info "Creating backup" create_backup /opt/pangolin/config
tar -czf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin config
if [[ -f /opt/pangolin/config/db/db.sqlite ]]; then
cp -a /opt/pangolin/config/db/db.sqlite \
"/opt/pangolin/config/db/db.sqlite.pre-${PANGOLIN_VERSION}-$(date +%Y%m%d-%H%M%S).bak"
fi
msg_ok "Created backup"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
@@ -55,7 +58,7 @@ function update_script() {
msg_info "Updating Pangolin" msg_info "Updating Pangolin"
cd /opt/pangolin cd /opt/pangolin
$STD npm ci $STD npm ci
$STD npm run set:sqlite $STD npm run set:pg
$STD npm run set:oss $STD npm run set:oss
rm -rf server/private rm -rf server/private
$STD npm run db:generate $STD npm run db:generate
@@ -68,10 +71,7 @@ function update_script() {
cp server/db/mac_models.json ./dist/mac_models.json cp server/db/mac_models.json ./dist/mac_models.json
msg_ok "Updated Pangolin" msg_ok "Updated Pangolin"
msg_info "Restoring config" restore_backup
tar -xzf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin --overwrite
rm -f /opt/pangolin_config_backup.tar.gz
msg_ok "Restored config"
if ! grep -q '^ExecStartPre=/usr/bin/node dist/migrations.mjs' /etc/systemd/system/pangolin.service 2>/dev/null; then if ! grep -q '^ExecStartPre=/usr/bin/node dist/migrations.mjs' /etc/systemd/system/pangolin.service 2>/dev/null; then
msg_info "Adding migration step to pangolin.service" msg_info "Adding migration step to pangolin.service"
@@ -82,13 +82,8 @@ function update_script() {
msg_info "Running database migrations" msg_info "Running database migrations"
cd /opt/pangolin cd /opt/pangolin
SQLITE_DB="/opt/pangolin/config/db/db.sqlite"
if [[ -f "$SQLITE_DB" ]]; then
if ! sqlite3 "$SQLITE_DB" ".tables" 2>/dev/null | tr ' ' '\n' | grep -qx "statusHistory"; then
sqlite3 "$SQLITE_DB" "DELETE FROM versionMigrations;" 2>/dev/null || true
fi
fi
ENVIRONMENT=prod $STD node dist/migrations.mjs ENVIRONMENT=prod $STD node dist/migrations.mjs
msg_ok "Ran database migrations" msg_ok "Ran database migrations"
msg_info "Updating Badger plugin version" msg_info "Updating Badger plugin version"
+53
View File
@@ -0,0 +1,53 @@
#!/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: 007hacky007
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.squid-cache.org/
APP="Squid"
var_tags="${var_tags:-proxy}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_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 [[ ! -f /etc/squid/squid.conf ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating Squid"
$STD apt update
$STD apt upgrade -y
msg_ok "Updated Squid"
msg_info "Validating Squid Configuration"
$STD squid -k parse
msg_ok "Validated Squid Configuration"
msg_info "Restarting Squid"
systemctl restart squid
msg_ok "Restarted Squid"
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} Proxy endpoint:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}${IP}:3128${CL}"
+3
View File
@@ -141,6 +141,9 @@ cp /opt/fireshare/app/nginx/prod.conf /etc/nginx/nginx.conf
sed -i 's|root /processed/|root /opt/fireshare-processed/|g' /etc/nginx/nginx.conf sed -i 's|root /processed/|root /opt/fireshare-processed/|g' /etc/nginx/nginx.conf
sed -i 's/^user[[:space:]]\+nginx;/user root;/' /etc/nginx/nginx.conf sed -i 's/^user[[:space:]]\+nginx;/user root;/' /etc/nginx/nginx.conf
sed -i 's|root[[:space:]]\+/app/build;|root /opt/fireshare/app/client/build;|' /etc/nginx/nginx.conf sed -i 's|root[[:space:]]\+/app/build;|root /opt/fireshare/app/client/build;|' /etc/nginx/nginx.conf
sed -i 's/__FIRESHARE_PORT__/80/g' /etc/nginx/nginx.conf
cp /opt/fireshare/app/nginx/error.html /etc/nginx/
cp /opt/fireshare/app/nginx/api_unavailable.html /etc/nginx/
systemctl start nginx systemctl start nginx
cat <<EOF >~/fireshare.creds cat <<EOF >~/fireshare.creds
+11 -5
View File
@@ -17,12 +17,13 @@ msg_info "Installing Dependencies"
$STD apt install -y \ $STD apt install -y \
build-essential \ build-essential \
python3 \ python3 \
sqlite3 \
iptables iptables
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
NODE_VERSION="24" setup_nodejs NODE_VERSION="24" setup_nodejs
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.4}" PG_VERSION="17" setup_postgresql
PG_DB_NAME="pangolin" PG_DB_USER="pangolin" setup_postgresql_db
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.20.0}"
fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION" fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION"
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)" fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz" fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz"
@@ -36,7 +37,7 @@ BADGER_VERSION=$(get_latest_github_release "fosrl/badger" "false")
cd /opt/pangolin cd /opt/pangolin
mkdir -p /opt/pangolin/config/{traefik,db,letsencrypt,logs} mkdir -p /opt/pangolin/config/{traefik,db,letsencrypt,logs}
$STD npm ci $STD npm ci
$STD npm run set:sqlite $STD npm run set:pg
$STD npm run set:oss $STD npm run set:oss
rm -rf server/private rm -rf server/private
$STD npm run db:generate $STD npm run db:generate
@@ -74,6 +75,9 @@ flags:
require_email_verification: false require_email_verification: false
disable_signup_without_invite: false disable_signup_without_invite: false
disable_user_create_org: false disable_user_create_org: false
postgres:
connection_string: "postgresql://pangolin:${PG_DB_PASS}@localhost:5432/pangolin"
EOF EOF
cat <<EOF >/opt/pangolin/config/traefik/traefik_config.yml cat <<EOF >/opt/pangolin/config/traefik/traefik_config.yml
@@ -181,7 +185,8 @@ http:
servers: servers:
- url: "http://$LOCAL_IP:3000" - url: "http://$LOCAL_IP:3000"
EOF EOF
$STD npm run db:push export ENVIRONMENT=prod
$STD node dist/migrations.mjs
. /etc/os-release . /etc/os-release
if [ "$VERSION_CODENAME" = "trixie" ]; then if [ "$VERSION_CODENAME" = "trixie" ]; then
@@ -197,7 +202,8 @@ msg_info "Creating Services"
cat <<EOF >/etc/systemd/system/pangolin.service cat <<EOF >/etc/systemd/system/pangolin.service
[Unit] [Unit]
Description=Pangolin Service Description=Pangolin Service
After=network.target After=network.target postgresql.service
Wants=postgresql.service
[Service] [Service]
Type=simple Type=simple
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: 007hacky007
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.squid-cache.org/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Configuring Squid"
mkdir -p /etc/squid
cat <<EOF >/etc/squid/squid.conf
acl localnet src 0.0.0.1-0.255.255.255
acl localnet src 10.0.0.0/8
acl localnet src 100.64.0.0/10
acl localnet src 169.254.0.0/16
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16
acl localnet src fc00::/7
acl localnet src fe80::/10
acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 21
acl Safe_ports port 443
acl Safe_ports port 70
acl Safe_ports port 210
acl Safe_ports port 1025-65535
acl Safe_ports port 280
acl Safe_ports port 488
acl Safe_ports port 591
acl Safe_ports port 777
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwords
auth_param basic realm proxy
acl authenticated proxy_auth REQUIRED
http_access allow authenticated
http_access deny all
http_port 3128
coredump_dir /var/spool/squid
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\\?) 0 0% 0
refresh_pattern . 0 20% 4320
# Privacy / hardening
httpd_suppress_version_string on
visible_hostname $(hostname)
forwarded_for delete
request_header_access X-Forwarded-For deny all
EOF
msg_ok "Configured Squid"
msg_info "Installing Dependencies"
$STD apt install -y \
squid \
apache2-utils
msg_ok "Installed Dependencies"
msg_info "Configuring Squid Authentication"
touch /etc/squid/passwords
chown proxy:proxy /etc/squid/passwords
chmod 640 /etc/squid/passwords
$STD squid -k parse
msg_ok "Configured Squid Authentication"
msg_info "Starting Service"
systemctl enable -q --now squid
msg_ok "Started Service"
motd_ssh
customize
cleanup_lxc
+546
View File
@@ -125,6 +125,67 @@ curl_with_retry() {
fi fi
} }
# ------------------------------------------------------------------------------
# Robust curl wrapper for API calls (returns HTTP code + body)
#
# Usage:
# response=$(curl_api_with_retry "https://api.github.com/repos/owner/repo/releases/latest")
# http_code=$(curl_api_with_retry "https://api.github.com/..." "/tmp/body.json")
#
# Parameters:
# $1 - URL to call
# $2 - (optional) Output file for body (default: stdout)
# $3 - (optional) Additional curl options as string
#
# Returns: HTTP status code, body in file or stdout
# ------------------------------------------------------------------------------
curl_api_with_retry() {
local url="$1"
local body_file="${2:-}"
local extra_opts="${3:-}"
local retries="${CURL_RETRIES:-3}"
local timeout="${CURL_TIMEOUT:-60}"
local connect_timeout="${CURL_CONNECT_TO:-10}"
local attempt=1
local http_code=""
while [[ $attempt -le $retries ]]; do
debug_log "curl API attempt $attempt/$retries: $url"
local curl_cmd="curl -fsSL --connect-timeout $connect_timeout --max-time $timeout -w '%{http_code}'"
[[ -n "$extra_opts" ]] && curl_cmd="$curl_cmd $extra_opts"
if [[ -n "$body_file" ]]; then
http_code=$($curl_cmd -o "$body_file" "$url" 2>/dev/null) || true
else
# Capture body and http_code separately
local tmp_body="/tmp/curl_api_body_$$"
http_code=$($curl_cmd -o "$tmp_body" "$url" 2>/dev/null) || true
if [[ -f "$tmp_body" ]]; then
cat "$tmp_body"
rm -f "$tmp_body"
fi
fi
# Success on 2xx codes
if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then
debug_log "curl API successful: $url (HTTP $http_code)"
echo "$http_code"
return 0
fi
debug_log "curl API attempt $attempt failed (HTTP $http_code, timeout=${timeout}s), waiting ${attempt}s..."
sleep "$attempt"
# Double --max-time on each retry so slow connections can finish
timeout=$((timeout * 2))
((attempt++))
done
debug_log "curl API FAILED after $retries attempts: $url"
echo "$http_code"
return 7
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Download and install GPG key with retry logic and validation # Download and install GPG key with retry logic and validation
@@ -256,6 +317,15 @@ cache_installed_version() {
echo "$version" >"/var/cache/app-versions/${app}_version.txt" echo "$version" >"/var/cache/app-versions/${app}_version.txt"
} }
get_cached_version() {
local app="$1"
mkdir -p /var/cache/app-versions
if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then
cat "/var/cache/app-versions/${app}_version.txt"
return 0
fi
return 1
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Clean up ALL keyring locations for a tool (unified helper) # Clean up ALL keyring locations for a tool (unified helper)
@@ -1572,6 +1642,14 @@ codeberg_api_call() {
return 22 return 22
} }
should_upgrade() {
local current="$1"
local target="$2"
[[ -z "$current" ]] && return 0
version_gt "$target" "$current" && return 0
return 1
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Get OS information (cached for performance) # Get OS information (cached for performance)
@@ -1724,6 +1802,200 @@ get_fallback_suite() {
return 0 return 0
} }
# ------------------------------------------------------------------------------
# Verify package source and version
# ------------------------------------------------------------------------------
verify_package_source() {
local package="$1"
local expected_version="$2"
if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then
return 0
fi
return 1
}
# ------------------------------------------------------------------------------
# Check if running on LTS version
# ------------------------------------------------------------------------------
is_lts_version() {
local os_id=$(get_os_info id)
local codename=$(get_os_info codename)
if [[ "$os_id" == "ubuntu" ]]; then
case "$codename" in
focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04
*) return 1 ;;
esac
elif [[ "$os_id" == "debian" ]]; then
# Debian releases are all "stable"
case "$codename" in
bullseye | bookworm | trixie) return 0 ;;
*) return 1 ;;
esac
fi
return 1
}
# ------------------------------------------------------------------------------
# Get optimal number of parallel jobs (cached)
# Features:
# - CPU count detection
# - Memory-based limiting (1.5GB per job for safety)
# - Current load awareness
# - Container/VM detection for conservative limits
# ------------------------------------------------------------------------------
get_parallel_jobs() {
if [[ -z "${_PARALLEL_JOBS:-}" ]]; then
local cpu_count
cpu_count=$(nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo 2>/dev/null || echo 1)
local mem_mb
mem_mb=$(free -m 2>/dev/null | awk '/^Mem:/{print $2}' || echo 1024)
# Assume 1.5GB per compilation job for safety margin
local max_by_mem=$((mem_mb / 1536))
((max_by_mem < 1)) && max_by_mem=1
# Check current system load - reduce jobs if already loaded
local load_1m
load_1m=$(awk '{print int($1)}' /proc/loadavg 2>/dev/null || echo 0)
local available_cpus=$((cpu_count - load_1m))
((available_cpus < 1)) && available_cpus=1
# Take minimum of: available CPUs, memory-limited, and total CPUs
local max_jobs=$cpu_count
((max_by_mem < max_jobs)) && max_jobs=$max_by_mem
((available_cpus < max_jobs)) && max_jobs=$available_cpus
# Container detection - be more conservative in containers
if [[ -f /.dockerenv ]] || grep -q 'lxc\|docker\|container' /proc/1/cgroup 2>/dev/null; then
# Reduce by 25% in containers to leave headroom
max_jobs=$((max_jobs * 3 / 4))
((max_jobs < 1)) && max_jobs=1
fi
# Final bounds check
((max_jobs < 1)) && max_jobs=1
((max_jobs > cpu_count)) && max_jobs=$cpu_count
export _PARALLEL_JOBS=$max_jobs
debug_log "Parallel jobs: $_PARALLEL_JOBS (CPUs: $cpu_count, mem-limit: $max_by_mem, load: $load_1m)"
fi
echo "$_PARALLEL_JOBS"
}
# ------------------------------------------------------------------------------
# Get default PHP version for OS
# Updated for latest distro releases
# ------------------------------------------------------------------------------
get_default_php_version() {
local os_id
os_id=$(get_os_info id)
local os_version
os_version=$(get_os_version_major)
case "$os_id" in
debian)
case "$os_version" in
14) echo "8.4" ;; # Debian 14 (Forky) - future
13) echo "8.3" ;; # Debian 13 (Trixie)
12) echo "8.2" ;; # Debian 12 (Bookworm)
11) echo "7.4" ;; # Debian 11 (Bullseye)
*) echo "8.3" ;; # Default to latest stable
esac
;;
ubuntu)
case "$os_version" in
26) echo "8.4" ;; # Ubuntu 26.04 - future
24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble)
22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy)
20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal)
*) echo "8.3" ;; # Default to latest stable
esac
;;
*)
echo "8.3"
;;
esac
}
# ------------------------------------------------------------------------------
# Get default Python version for OS
# Updated for latest distro releases
# ------------------------------------------------------------------------------
get_default_python_version() {
local os_id
os_id=$(get_os_info id)
local os_version
os_version=$(get_os_version_major)
case "$os_id" in
debian)
case "$os_version" in
14) echo "3.13" ;; # Debian 14 (Forky) - future
13) echo "3.12" ;; # Debian 13 (Trixie)
12) echo "3.11" ;; # Debian 12 (Bookworm)
11) echo "3.9" ;; # Debian 11 (Bullseye)
*) echo "3.12" ;; # Default to latest stable
esac
;;
ubuntu)
case "$os_version" in
26) echo "3.13" ;; # Ubuntu 26.04 - future
24) echo "3.12" ;; # Ubuntu 24.04 LTS
22) echo "3.10" ;; # Ubuntu 22.04 LTS
20) echo "3.8" ;; # Ubuntu 20.04 LTS
*) echo "3.12" ;; # Default to latest stable
esac
;;
*)
echo "3.12"
;;
esac
}
# ------------------------------------------------------------------------------
# Get default Node.js LTS version
# ------------------------------------------------------------------------------
get_default_nodejs_version() {
# Current LTS as of January 2026 (Node.js 24 LTS)
echo "24"
}
# ------------------------------------------------------------------------------
# Check if package manager is locked
# ------------------------------------------------------------------------------
is_apt_locked() {
if fuser /var/lib/dpkg/lock-frontend &>/dev/null ||
fuser /var/lib/apt/lists/lock &>/dev/null ||
fuser /var/cache/apt/archives/lock &>/dev/null; then
return 0
fi
return 1
}
# ------------------------------------------------------------------------------
# Wait for apt to be available
# ------------------------------------------------------------------------------
wait_for_apt() {
local max_wait="${1:-300}" # 5 minutes default
local waited=0
while is_apt_locked; do
if [[ $waited -ge $max_wait ]]; then
msg_error "Timeout waiting for apt to be available"
msg_error "Hint: Another process (apt, dpkg, unattended-upgrades) may hold a lock. Check: ps aux | grep -E 'apt|dpkg'"
return 100
fi
sleep 5
waited=$((waited + 5))
done
return 0
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Cleanup old repository files (migration helper) # Cleanup old repository files (migration helper)
@@ -1926,6 +2198,22 @@ setup_deb822_repo() {
fi fi
} }
# ------------------------------------------------------------------------------
# Package version hold/unhold helpers
# ------------------------------------------------------------------------------
hold_package_version() {
local package="$1"
$STD apt-mark hold "$package" || {
msg_warn "Failed to hold package version: ${package}"
}
}
unhold_package_version() {
local package="$1"
$STD apt-mark unhold "$package" || {
msg_warn "Failed to unhold package version: ${package}"
}
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Safe service restart with verification # Safe service restart with verification
@@ -1971,6 +2259,41 @@ safe_service_restart() {
return 150 return 150
} }
# ------------------------------------------------------------------------------
# Enable and start service (with error handling)
# ------------------------------------------------------------------------------
enable_and_start_service() {
local service="$1"
if ! systemctl enable "$service" &>/dev/null; then
msg_error "Failed to enable service: $service"
return 150
fi
if ! systemctl start "$service" &>/dev/null; then
msg_error "Failed to start $service"
systemctl status "$service" --no-pager
return 150
fi
return 0
}
# ------------------------------------------------------------------------------
# Check if service is enabled
# ------------------------------------------------------------------------------
is_service_enabled() {
local service="$1"
systemctl is-enabled --quiet "$service" 2>/dev/null
}
# ------------------------------------------------------------------------------
# Check if service is running
# ------------------------------------------------------------------------------
is_service_running() {
local service="$1"
systemctl is-active --quiet "$service" 2>/dev/null
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Extract version from JSON (GitHub releases) # Extract version from JSON (GitHub releases)
@@ -2116,6 +2439,42 @@ get_latest_codeberg_release() {
echo "$version" echo "$version"
} }
# ------------------------------------------------------------------------------
# Debug logging - using main debug_log function (line 40)
# Supports both TOOLS_DEBUG and DEBUG environment variables
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Performance timing helper
# ------------------------------------------------------------------------------
start_timer() {
echo $(date +%s)
}
end_timer() {
local start_time="$1"
local label="${2:-Operation}"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
}
# ------------------------------------------------------------------------------
# GPG key fingerprint verification
# ------------------------------------------------------------------------------
verify_gpg_fingerprint() {
local key_file="$1"
local expected_fingerprint="$2"
local actual_fingerprint
actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10)
if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then
return 0
fi
msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint"
return 65
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Fetches and deploys a GitHub tag-based source tarball. # Fetches and deploys a GitHub tag-based source tarball.
@@ -2642,6 +3001,38 @@ create_self_signed_cert() {
chmod 644 "$CERT_CRT" chmod 644 "$CERT_CRT"
} }
# ------------------------------------------------------------------------------
# Downloads file with optional progress indicator using pv.
#
# Arguments:
# $1 - URL
# $2 - Destination path
# ------------------------------------------------------------------------------
download_with_progress() {
local url="$1"
local output="$2"
if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi
ensure_dependencies pv
set -o pipefail
# Content-Length aus HTTP-Header holen
local content_length
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
msg_error "Download failed: $url"
return 7
fi
else
if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then
msg_error "Download failed: $url"
return 7
fi
fi
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Ensures /usr/local/bin is permanently in system PATH. # Ensures /usr/local/bin is permanently in system PATH.
@@ -5941,6 +6332,117 @@ setup_java() {
msg_ok "Setup Temurin JDK $JAVA_VERSION" msg_ok "Setup Temurin JDK $JAVA_VERSION"
} }
# ------------------------------------------------------------------------------
# Installs a local IP updater script using networkd-dispatcher.
#
# Description:
# - Stores current IP in /run/local-ip.env
# - Automatically runs on network changes
# ------------------------------------------------------------------------------
setup_local_ip_helper() {
local BASE_DIR="/usr/local/community-scripts/ip-management"
local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh"
local IP_FILE="/run/local-ip.env"
local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh"
# Check if already set up
if [[ -f "$SCRIPT_PATH" && -f "$DISPATCHER_SCRIPT" ]]; then
msg_info "Update Local IP Helper"
cache_installed_version "local-ip-helper" "1.0"
msg_ok "Update Local IP Helper"
else
msg_info "Setup Local IP Helper"
fi
mkdir -p "$BASE_DIR"
# Install networkd-dispatcher if not present
if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then
ensure_dependencies networkd-dispatcher || {
msg_error "Failed to install networkd-dispatcher"
return 100
}
fi
# Write update_local_ip.sh
cat <<'EOF' >"$SCRIPT_PATH"
#!/bin/bash
set -euo pipefail
IP_FILE="/run/local-ip.env"
mkdir -p "$(dirname "$IP_FILE")"
get_current_ip() {
local ip
# Try IPv4 targets first
local ipv4_targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default")
for target in "${ipv4_targets[@]}"; do
if [[ "$target" == "default" ]]; then
ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}')
else
ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}')
fi
if [[ -n "$ip" ]]; then
echo "$ip"
return 0
fi
done
# IPv6 fallback: Try direct interface lookup for eth0
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
fi
# IPv6 fallback: Use routing table with IPv6 targets (Google DNS, Cloudflare DNS)
local ipv6_targets=("2001:4860:4860::8888" "2606:4700:4700::1111")
for target in "${ipv6_targets[@]}"; do
ip=$(ip -6 route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}')
if [[ -n "$ip" && "$ip" =~ : ]]; then
echo "$ip"
return 0
fi
done
return 1
}
current_ip="$(get_current_ip)"
if [[ -z "$current_ip" ]]; then
echo "[ERROR] Could not detect local IP" >&2
exit 123
fi
if [[ -f "$IP_FILE" ]]; then
source "$IP_FILE"
[[ "$LOCAL_IP" == "$current_ip" ]] && exit 0
fi
echo "LOCAL_IP=$current_ip" > "$IP_FILE"
echo "[INFO] LOCAL_IP updated to $current_ip"
EOF
chmod +x "$SCRIPT_PATH"
# Install dispatcher hook
mkdir -p "$(dirname "$DISPATCHER_SCRIPT")"
cat <<EOF >"$DISPATCHER_SCRIPT"
#!/bin/bash
$SCRIPT_PATH
EOF
chmod +x "$DISPATCHER_SCRIPT"
systemctl enable -q --now networkd-dispatcher.service || {
msg_warn "Failed to enable networkd-dispatcher service"
}
cache_installed_version "local-ip-helper" "1.0"
msg_ok "Setup Local IP Helper"
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Installs or updates MariaDB. # Installs or updates MariaDB.
@@ -8704,6 +9206,50 @@ EOF
return 0 return 0
} }
# ------------------------------------------------------------------------------
# Get latest GitLab release version.
# Usage: get_latest_gitlab_release "owner/repo" [strip_v]
# ------------------------------------------------------------------------------
get_latest_gitlab_release() {
local repo="$1"
local strip_v="${2:-true}"
local repo_encoded
repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g')
local header=()
[[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN")
local temp_file
temp_file=$(mktemp)
local http_code
http_code=$(curl --connect-timeout 10 --max-time 30 -sSL \
-w "%{http_code}" -o "$temp_file" \
"${header[@]}" \
"https://gitlab.com/api/v4/projects/$repo_encoded/releases?per_page=1&order_by=released_at&sort=desc" 2>/dev/null) || true
if [[ "$http_code" != "200" ]]; then
rm -f "$temp_file"
msg_warn "GitLab API call failed for ${repo} (HTTP ${http_code})"
return 22
fi
local version
version=$(jq -r '.[0].tag_name // empty' "$temp_file")
rm -f "$temp_file"
if [[ -z "$version" ]]; then
msg_error "Could not determine latest version for ${repo}"
return 250
fi
if [[ "$strip_v" == "true" ]]; then
[[ "$version" =~ ^v[0-9] ]] && version="${version:1}"
fi
echo "$version"
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Checks for new GitLab release (latest tag). # Checks for new GitLab release (latest tag).