install_node_and_modules() { local NODE_VERSION="${NODE_VERSION:-22}" local NODE_MODULE="${NODE_MODULE:-}" local CURRENT_NODE_VERSION="" local NEED_NODE_INSTALL=false # Check if Node.js is already installed if command -v node >/dev/null; then CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then msg_info "Node.js version $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" NEED_NODE_INSTALL=true else msg_ok "Node.js $NODE_VERSION already installed" fi else msg_info "Node.js not found, installing version $NODE_VERSION" NEED_NODE_INSTALL=true fi # Install Node.js if required if [[ "$NEED_NODE_INSTALL" == true ]]; then $STD apt-get purge -y nodejs rm -f /etc/apt/sources.list.d/nodesource.list /etc/apt/keyrings/nodesource.gpg mkdir -p /etc/apt/keyrings if ! curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; then msg_error "Failed to download or import NodeSource GPG key" exit 1 fi echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_VERSION}.x nodistro main" \ >/etc/apt/sources.list.d/nodesource.list if ! apt-get update >/dev/null 2>&1; then msg_error "Failed to update APT repositories after adding NodeSource" exit 1 fi if ! apt-get install -y nodejs >/dev/null 2>&1; then msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" exit 1 fi msg_ok "Installed Node.js ${NODE_VERSION}" fi export NODE_OPTIONS="--max_old_space_size=4096" # Install global Node modules if [[ -n "$NODE_MODULE" ]]; then IFS=',' read -ra MODULES <<<"$NODE_MODULE" for mod in "${MODULES[@]}"; do local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION if [[ "$mod" == *"@"* ]]; then MODULE_NAME="${mod%@*}" MODULE_REQ_VERSION="${mod#*@}" else MODULE_NAME="$mod" MODULE_REQ_VERSION="latest" fi # Check if the module is already installed if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" 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}"; then msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" exit 1 fi elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then msg_info "Updating $MODULE_NAME to latest version" if ! $STD npm install -g "${MODULE_NAME}@latest"; then msg_error "Failed to update $MODULE_NAME to latest version" exit 1 fi else msg_ok "$MODULE_NAME@$MODULE_INSTALLED_VERSION already installed" fi else msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" exit 1 fi fi done msg_ok "All requested Node modules have been processed" fi } install_postgresql() { local PG_VERSION="${PG_VERSION:-16}" local CURRENT_PG_VERSION="" local DISTRO local NEED_PG_INSTALL=false DISTRO="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" if command -v psql >/dev/null; then CURRENT_PG_VERSION="$(psql -V | grep -oP '\s\K[0-9]+(?=\.)')" if [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]]; then msg_info "PostgreSQL Version $CURRENT_PG_VERSION found, replacing with $PG_VERSION" NEED_PG_INSTALL=true fi else msg_info "PostgreSQL not found, installing version $PG_VERSION" NEED_PG_INSTALL=true fi if [[ "$NEED_PG_INSTALL" == true ]]; then msg_info "Stopping PostgreSQL if running" systemctl stop postgresql >/dev/null 2>&1 || true msg_info "Removing conflicting PostgreSQL packages" $STD apt-get purge -y "postgresql*" rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg msg_info "Setting up PostgreSQL Repository" curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ >/etc/apt/sources.list.d/pgdg.list $STD apt-get update $STD apt-get install -y "postgresql-${PG_VERSION}" msg_ok "Installed PostgreSQL ${PG_VERSION}" fi } install_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" local DISTRO_CODENAME DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" # grab dynamic latest LTS version if [[ "$MARIADB_VERSION" == "latest" ]]; then msg_info "Resolving latest MariaDB version" MARIADB_VERSION=$(curl -fsSL https://mariadb.org | grep -oP 'MariaDB \K10\.[0-9]+' | head -n1) if [[ -z "$MARIADB_VERSION" ]]; then msg_error "Could not determine latest MariaDB version" return 1 fi msg_ok "Latest MariaDB version is $MARIADB_VERSION" fi local CURRENT_VERSION="" if command -v mariadb >/dev/null; then CURRENT_VERSION="$(mariadb --version | grep -oP 'Ver\s+\K[0-9]+\.[0-9]+')" fi if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then msg_info "MariaDB $MARIADB_VERSION already installed, checking for upgrade" $STD apt-get update $STD apt-get install --only-upgrade -y mariadb-server mariadb-client msg_ok "MariaDB $MARIADB_VERSION upgraded if applicable" return 0 fi if [[ -n "$CURRENT_VERSION" ]]; then msg_info "Replacing MariaDB $CURRENT_VERSION with $MARIADB_VERSION (data will be preserved)" $STD systemctl stop mariadb >/dev/null 2>&1 || true $STD apt-get purge -y 'mariadb*' || true rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg else msg_info "Installing MariaDB $MARIADB_VERSION" fi msg_info "Setting up MariaDB Repository" curl -fsSL "https://mariadb.org/mariadb_release_signing_key.asc" | gpg --dearmor -o /etc/apt/trusted.gpg.d/mariadb.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/mariadb.gpg] http://mirror.mariadb.org/repo/${MARIADB_VERSION}/debian ${DISTRO_CODENAME} main" \ >/etc/apt/sources.list.d/mariadb.list $STD apt-get update $STD apt-get install -y mariadb-server mariadb-client msg_ok "Installed MariaDB $MARIADB_VERSION" } install_mysql() { local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" local CURRENT_VERSION="" local NEED_INSTALL=false if command -v mysql >/dev/null; then CURRENT_VERSION="$(mysql --version | grep -oP 'Distrib\s+\K[0-9]+\.[0-9]+')" if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then msg_info "MySQL $CURRENT_VERSION found, replacing with $MYSQL_VERSION" NEED_INSTALL=true else msg_ok "MySQL $MYSQL_VERSION already installed" fi else msg_info "MySQL not found, installing version $MYSQL_VERSION" NEED_INSTALL=true fi if [[ "$NEED_INSTALL" == true ]]; then msg_info "Removing conflicting MySQL packages" $STD systemctl stop mysql >/dev/null 2>&1 || true $STD apt-get purge -y 'mysql*' rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/trusted.gpg.d/mysql.gpg msg_info "Setting up MySQL APT Repository" DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 | gpg --dearmor -o /etc/apt/trusted.gpg.d/mysql.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/debian/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ >/etc/apt/sources.list.d/mysql.list $STD apt-get update $STD apt-get install -y mysql-server msg_ok "Installed MySQL $MYSQL_VERSION" fi } install_php() { local PHP_VERSION="${PHP_VERSION:-8.4}" local PHP_MODULE="${PHP_MODULE:-}" local PHP_APACHE="${PHP_APACHE:-NO}" local PHP_FPM="${PHP_FPM:-NO}" local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" local COMBINED_MODULES local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" # Merge default + user-defined modules if [[ -n "$PHP_MODULE" ]]; then COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" else COMBINED_MODULES="${DEFAULT_MODULES}" fi # Deduplicate modules COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) local CURRENT_PHP CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) if [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then $STD echo "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION" if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" \ >/etc/apt/sources.list.d/php.list $STD apt-get update fi $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true fi local MODULE_LIST="php${PHP_VERSION}" IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" for mod in "${MODULES[@]}"; do MODULE_LIST+=" php${PHP_VERSION}-${mod}" done if [[ "$PHP_APACHE" == "YES" ]]; then # Optionally disable old Apache PHP module if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then $STD a2dismod php${CURRENT_PHP} || true fi fi if [[ "$PHP_FPM" == "YES" ]]; then $STD systemctl stop php${CURRENT_PHP}-fpm || true $STD systemctl disable php${CURRENT_PHP}-fpm || true fi $STD apt-get install -y $MODULE_LIST msg_ok "Installed PHP $PHP_VERSION with selected modules" if [[ "$PHP_APACHE" == "YES" ]]; then $STD systemctl restart apache2 || true fi if [[ "$PHP_FPM" == "YES" ]]; then $STD systemctl enable php${PHP_VERSION}-fpm $STD systemctl restart php${PHP_VERSION}-fpm fi # Patch all relevant php.ini files local PHP_INI_PATHS=() PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/cli/php.ini") [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") for ini in "${PHP_INI_PATHS[@]}"; do if [[ -f "$ini" ]]; then msg_info "Patching $ini" sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" msg_ok "Patched $ini" fi done } install_composer() { local COMPOSER_BIN="/usr/local/bin/composer" export COMPOSER_ALLOW_SUPERUSER=1 # Check if composer is already installed if [[ -x "$COMPOSER_BIN" ]]; then local CURRENT_VERSION CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}') msg_info "Composer $CURRENT_VERSION found, updating to latest" else msg_info "Composer not found, installing latest version" fi # Download and install latest composer curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 if [[ $? -ne 0 ]]; then msg_error "Failed to install Composer" return 1 fi chmod +x "$COMPOSER_BIN" msg_ok "Installed Composer $($COMPOSER_BIN --version | awk '{print $3}')" } install_go() { local ARCH case "$(uname -m)" in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64" ;; *) msg_error "Unsupported architecture: $(uname -m)" return 1 ;; esac # Determine version if [[ -z "$GO_VERSION" || "$GO_VERSION" == "latest" ]]; then GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') if [[ -z "$GO_VERSION" ]]; then msg_error "Could not determine latest Go version" return 1 fi msg_info "Detected latest Go version: $GO_VERSION" fi local GO_BIN="/usr/local/bin/go" local GO_INSTALL_DIR="/usr/local/go" if [[ -x "$GO_BIN" ]]; then local CURRENT_VERSION CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then msg_ok "Go $GO_VERSION already installed" return 0 else msg_info "Go $CURRENT_VERSION found, upgrading to $GO_VERSION" rm -rf "$GO_INSTALL_DIR" fi else msg_info "Installing Go $GO_VERSION" fi local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" local URL="https://go.dev/dl/${TARBALL}" local TMP_TAR=$(mktemp) curl -fsSL "$URL" -o "$TMP_TAR" || { msg_error "Failed to download $TARBALL" return 1 } tar -C /usr/local -xzf "$TMP_TAR" ln -sf /usr/local/go/bin/go /usr/local/bin/go ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt rm -f "$TMP_TAR" msg_ok "Installed Go $GO_VERSION" } install_java() { local JAVA_VERSION="${JAVA_VERSION:-17}" local DISTRO_CODENAME DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" # Add Adoptium repo if missing if [[ ! -f /etc/apt/sources.list.d/adoptium.list ]]; then msg_info "Setting up Adoptium Repository" mkdir -p /etc/apt/keyrings curl -fsSL "https://packages.adoptium.net/artifactory/api/gpg/key/public" | gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/adoptium.gpg] https://packages.adoptium.net/artifactory/deb ${DISTRO_CODENAME} main" \ >/etc/apt/sources.list.d/adoptium.list $STD apt-get update msg_ok "Set up Adoptium Repository" fi # Detect currently installed temurin version local INSTALLED_VERSION="" if dpkg -l | grep -q "temurin-.*-jdk"; then INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') fi if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then msg_info "Temurin JDK $JAVA_VERSION already installed, updating if needed" $STD apt-get update $STD apt-get install --only-upgrade -y "$DESIRED_PACKAGE" msg_ok "Updated Temurin JDK $JAVA_VERSION (if applicable)" else if [[ -n "$INSTALLED_VERSION" ]]; then msg_info "Removing Temurin JDK $INSTALLED_VERSION" $STD apt-get purge -y "temurin-${INSTALLED_VERSION}-jdk" fi msg_info "Installing Temurin JDK $JAVA_VERSION" $STD apt-get install -y "$DESIRED_PACKAGE" msg_ok "Installed Temurin JDK $JAVA_VERSION" fi } install_mongodb() { local MONGO_VERSION="${MONGO_VERSION:-8.0}" local DISTRO_CODENAME DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) local REPO_LIST="/etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.list" # Aktuell installierte Major-Version ermitteln local INSTALLED_VERSION="" if command -v mongod >/dev/null; then INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) fi if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then msg_info "MongoDB $MONGO_VERSION already installed, checking for upgrade" $STD apt-get update $STD apt-get install --only-upgrade -y mongodb-org msg_ok "MongoDB $MONGO_VERSION upgraded if needed" return 0 fi # Ältere Version entfernen (nur Packages, nicht Daten!) if [[ -n "$INSTALLED_VERSION" ]]; then msg_info "Replacing MongoDB $INSTALLED_VERSION with $MONGO_VERSION (data will be preserved)" $STD systemctl stop mongod || true $STD apt-get purge -y mongodb-org || true rm -f /etc/apt/sources.list.d/mongodb-org-*.list rm -f /etc/apt/trusted.gpg.d/mongodb-*.gpg else msg_info "Installing MongoDB $MONGO_VERSION" fi # MongoDB Repo hinzufügen curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg" echo "deb [signed-by=/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg] https://repo.mongodb.org/apt/debian ${DISTRO_CODENAME}/mongodb-org/${MONGO_VERSION} main" \ >"$REPO_LIST" $STD apt-get update $STD apt-get install -y mongodb-org # Sicherstellen, dass Datenverzeichnis intakt bleibt mkdir -p /var/lib/mongodb chown -R mongodb:mongodb /var/lib/mongodb $STD systemctl enable mongod $STD systemctl start mongod msg_ok "MongoDB $MONGO_VERSION installed and started" } fetch_and_deploy_gh_release() { local repo="$1" local app=$(echo ${APPLICATION,,} | tr -d ' ') local api_url="https://api.github.com/repos/$repo/releases/latest" local header=() local attempt=0 local max_attempts=3 local api_response tag http_code local current_version="" local curl_timeout="--connect-timeout 10 --max-time 30" # Check if the app directory exists and if there's a version file if [[ -f "/opt/${app}_version.txt" ]]; then current_version=$(cat "/opt/${app}_version.txt") $STD msg_info "Current version: $current_version" fi # ensure that jq is installed if ! command -v jq &>/dev/null; then $STD msg_info "Installing jq..." $STD apt-get update -qq &>/dev/null $STD apt-get install -y jq &>/dev/null || { msg_error "Failed to install jq" return 1 } fi [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") until [[ $attempt -ge $max_attempts ]]; do ((attempt++)) || true $STD msg_info "[$attempt/$max_attempts] Fetching GitHub release for $repo...\n" api_response=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_resp.json "${header[@]}" "$api_url") http_code="${api_response:(-3)}" if [[ "$http_code" == "404" ]]; then msg_error "Repository $repo has no Release candidate (404)" return 1 fi if [[ "$http_code" != "200" ]]; then $STD msg_info "Request failed with HTTP $http_code, retrying...\n" sleep $((attempt * 2)) continue fi api_response=$(/dev/null; then msg_error "Repository not found: $repo" return 1 fi tag=$(echo "$api_response" | jq -r '.tag_name // .name // empty') [[ "$tag" =~ ^v[0-9] ]] && tag="${tag:1}" if [[ -z "$tag" ]]; then $STD msg_info "Empty tag received, retrying...\n" sleep $((attempt * 2)) continue fi $STD msg_ok "Found release: $tag for $repo" break done if [[ -z "$tag" ]]; then msg_error "Failed to fetch release for $repo after $max_attempts attempts." exit 1 fi # Version comparison (if we already have this version, skip) if [[ "$current_version" == "$tag" ]]; then $STD msg_info "Already running the latest version ($tag). Skipping update." return 0 fi local version="$tag" local base_url="https://github.com/$repo/releases/download/v$tag" local tmpdir tmpdir=$(mktemp -d) || return 1 # Extract list of assets from the Release API local assets urls assets=$(echo "$api_response" | jq -r '.assets[].browser_download_url') || true # Detect current architecture local arch if command -v dpkg &>/dev/null; then arch=$(dpkg --print-architecture) elif command -v uname &>/dev/null; then case "$(uname -m)" in x86_64) arch="amd64" ;; aarch64) arch="arm64" ;; armv7l) arch="armv7" ;; armv6l) arch="armv6" ;; *) arch="unknown" ;; esac else arch="unknown" fi $STD msg_info "Detected system architecture: $arch" # Try to find a matching asset for our architecture local url="" for u in $assets; do if [[ "$u" =~ $arch.*\.tar\.gz$ ]]; then url="$u" $STD msg_info "Found matching architecture asset: $url" break fi done # Fallback to other architectures if our specific one isn't found if [[ -z "$url" ]]; then for u in $assets; do if [[ "$u" =~ (x86_64|amd64|arm64|armv7|armv6).*\.tar\.gz$ ]]; then url="$u" $STD msg_info "Architecture-specific asset not found, using: $url" break fi done fi # Fallback to any tar.gz if [[ -z "$url" ]]; then for u in $assets; do if [[ "$u" =~ \.tar\.gz$ ]]; then url="$u" $STD msg_info "Using generic tarball: $url" break fi done fi # Final fallback to GitHub source tarball if [[ -z "$url" ]]; then url="https://github.com/$repo/archive/refs/tags/$version.tar.gz" $STD msg_info "Trying GitHub source tarball fallback: $url" fi local filename="${url##*/}" $STD msg_info "Downloading $url" if ! curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url"; then msg_error "Failed to download release asset from $url" rm -rf "$tmpdir" return 1 fi mkdir -p "/opt/$app" tar -xzf "$tmpdir/$filename" -C "$tmpdir" local content_root content_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d) if [[ $(echo "$content_root" | wc -l) -eq 1 ]]; then cp -r "$content_root"/* "/opt/$app/" else cp -r "$tmpdir"/* "/opt/$app/" fi echo "$version" >"/opt/${app}_version.txt" $STD msg_ok "Deployed $app v$version to /opt/$app" rm -rf "$tmpdir" } 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" mkdir -p "$BASE_DIR" # Install networkd-dispatcher if not present if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then $STD apt-get update -qq $STD apt-get install -yq networkd-dispatcher 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 targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") local ip for target in "${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 return 1 } current_ip="$(get_current_ip)" if [[ -z "$current_ip" ]]; then echo "[ERROR] Could not detect local IP" >&2 exit 1 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 <"$DISPATCHER_SCRIPT" #!/bin/bash $SCRIPT_PATH EOF chmod +x "$DISPATCHER_SCRIPT" systemctl enable --now networkd-dispatcher.service $STD msg_ok "LOCAL_IP helper installed using networkd-dispatcher" } import_local_ip() { local IP_FILE="/run/local-ip.env" if [[ -f "$IP_FILE" ]]; then # shellcheck disable=SC1090 source "$IP_FILE" fi if [[ -z "${LOCAL_IP:-}" ]]; then get_current_ip() { local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") local ip for target in "${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 return 1 } LOCAL_IP="$(get_current_ip || true)" if [[ -z "$LOCAL_IP" ]]; then msg_error "Could not determine LOCAL_IP" return 1 fi fi export LOCAL_IP }