ProxmoxVED/misc/helper.func
2025-04-10 16:22:12 +02:00

381 lines
12 KiB
Plaintext

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
}
function install_postgresql() {
local PG_VERSION="${PG_VERSION:-15}"
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
}
function install_mariadb() {
local MARIADB_VERSION="${MARIADB_VERSION:-10.11}"
local CURRENT_VERSION=""
local NEED_INSTALL=false
if command -v mariadb >/dev/null; then
CURRENT_VERSION="$(mariadb --version | grep -oP 'Ver\s+\K[0-9]+\.[0-9]+')"
if [[ "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then
msg_info "MariaDB $CURRENT_VERSION found, replacing with $MARIADB_VERSION"
NEED_INSTALL=true
else
msg_ok "MariaDB $MARIADB_VERSION already installed"
fi
else
msg_info "MariaDB not found, installing version $MARIADB_VERSION"
NEED_INSTALL=true
fi
if [[ "$NEED_INSTALL" == true ]]; then
msg_info "Removing conflicting MariaDB packages"
$STD systemctl stop mariadb >/dev/null 2>&1 || true
$STD apt-get purge -y 'mariadb*'
rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg
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
DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)"
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"
fi
}
function 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
}
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..."
apt-get update -qq &>/dev/null
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=$(</tmp/gh_resp.json)
if echo "$api_response" | grep -q "API rate limit exceeded"; then
msg_error "GitHub API rate limit exceeded."
return 1
fi
if echo "$api_response" | jq -e '.message == "Not Found"' &>/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"
}