diff --git a/ct/alpine-homarr.sh b/ct/alpine-homarr.sh new file mode 100644 index 0000000..fc87c90 --- /dev/null +++ b/ct/alpine-homarr.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) | Co-Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://homarr.dev/ + +APP="alpine-homarr" +var_tags="${var_tags:-arr;dashboard}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-8}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.21}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" + +variables +color +catch_errors + +function update_script() { + header_info + RELEASE=$(curl -fsSL https://api.github.com/repos/homarr-labs/homarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + + msg_info "Stopping Services (Patience)" + systemctl stop homarr + msg_ok "Services Stopped" + + msg_info "Backup Data" + mkdir -p /opt/homarr-data-backup + cp /opt/homarr/.env /opt/homarr-data-backup/.env + msg_ok "Backup Data" + + msg_info "Updating and rebuilding ${APP} to v${RELEASE} (Patience)" + rm /opt/run_homarr.sh + cat <<'EOF' >/opt/run_homarr.sh +#!/bin/bash +set -a +source /opt/homarr/.env +set +a +export DB_DIALECT='sqlite' +export AUTH_SECRET=$(openssl rand -base64 32) +node /opt/homarr_db/migrations/$DB_DIALECT/migrate.cjs /opt/homarr_db/migrations/$DB_DIALECT +for dir in $(find /opt/homarr_db/migrations/migrations -mindepth 1 -maxdepth 1 -type d); do + dirname=$(basename "$dir") + mkdir -p "/opt/homarr_db/migrations/$dirname" + cp -r "$dir"/* "/opt/homarr_db/migrations/$dirname/" 2>/dev/null || true +done +export HOSTNAME=$(ip route get 1.1.1.1 | grep -oP 'src \K[^ ]+') +envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf +nginx -g 'daemon off;' & +redis-server /opt/homarr/packages/redis/redis.conf & +node apps/tasks/tasks.cjs & +node apps/websocket/wssServer.cjs & +node apps/nextjs/server.js & PID=$! +wait $PID +EOF + chmod +x /opt/run_homarr.sh + NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') + NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" + install_node_and_modules + rm -rf /opt/homarr + fetch_and_deploy_gh_release "homarr-labs/homarr" + mv /opt/homarr-data-backup/.env /opt/homarr/.env + cd /opt/homarr + echo "test2" + export NODE_ENV="" + $STD pnpm install --recursive --frozen-lockfile --shamefully-hoist + $STD pnpm build + cp /opt/homarr/apps/nextjs/next.config.ts . + cp /opt/homarr/apps/nextjs/package.json . + cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations + cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr + mkdir -p /appdata/redis + cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf + rm /etc/nginx/nginx.conf + mkdir -p /etc/nginx/templates + cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf + + mkdir -p /opt/homarr/apps/cli + cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs + echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr + chmod +x /usr/bin/homarr + + mkdir /opt/homarr/build + cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated ${APP}" + + msg_info "Starting Services" + systemctl start homarr + msg_ok "Started Services" + msg_ok "Updated Successfully" + read -p "It's recommended to reboot the LXC after an update, would you like to reboot the LXC now ? (y/n): " choice + if [[ "$choice" =~ ^[Yy]$ ]]; then + reboot + fi + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7575${CL}" diff --git a/ct/argus.sh b/ct/argus.sh new file mode 100644 index 0000000..b7032fe --- /dev/null +++ b/ct/argus.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://release-argus.io/ + +APP="Argus" +var_tags="${var_tags:-os}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-3}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/argus ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/release-argus/Argus/releases/latest | jq -r .tag_name | sed 's/^v//') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Updating $APP to ${RELEASE}" + curl -fsSL "https://github.com/release-argus/Argus/releases/download/${RELEASE}/Argus-${RELEASE}.linux-amd64" -o /opt/argus/Argus + chmod +x /opt/argus/Argus + systemctl restart argus + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated $APP to ${RELEASE}" + else + msg_ok "$APP is already up to date (${RELEASE})" + fi +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/ct/configarr.sh b/ct/configarr.sh new file mode 100644 index 0000000..02ca312 --- /dev/null +++ b/ct/configarr.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: finkerle +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/raydak-labs/configarr + +APP="Configarr" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/configarr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/raydak-labs/configarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ "${RELEASE}" != "$(cat /opt/configarr_version.txt)" ]] || [[ ! -f /opt/configarr_version.txt ]]; then + msg_info "Stopping $APP" + systemctl stop configarr-task.timer + msg_ok "Stopped $APP" + + msg_info "Updating $APP to v${RELEASE}" + mkdir -p /opt/backup/ + mv /opt/configarr/{config.yml,secrets.yml,.env} "/opt/backup/" + rm -rf /opt/configarr + fetch_and_deploy_gh_release "raydak-labs/configarr" + mv /opt/backup/* /opt/configarr/ + cd /opt/configarr + pnpm install + pnpm run build + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start configarr-task.timer + msg_ok "Started configarr" + + rm -rf /opt/backup + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:[PORT]${CL}" diff --git a/ct/debian.sh b/ct/debian.sh index 187457d..2a28d61 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -13,6 +13,8 @@ var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" +var_fuse="${var_fuse:-1}" +var_tun="${var_tun:-1}" header_info "$APP" variables @@ -20,18 +22,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit } start @@ -42,10 +44,10 @@ msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" read -p "Remove this Container? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - pct stop "$CTID" - pct destroy "$CTID" - msg_ok "Removed this script" - else - msg_warn "Did not remove this script" - fi +if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + pct stop "$CTID" + pct destroy "$CTID" + msg_ok "Removed this script" +else + msg_warn "Did not remove this script" +fi diff --git a/ct/garmin-grafana.sh b/ct/garmin-grafana.sh new file mode 100644 index 0000000..fb0a5af --- /dev/null +++ b/ct/garmin-grafana.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/raw/main/garmin-grafana/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: aliaksei135 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/arpanghosh8453/garmin-grafana + +APP="garmin-grafana" +var_tags="${var_tags:-sports;visualization}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +# this only updates garmin-grafana, not influxdb or grafana, which are upgraded with apt +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/garmin-grafana/ ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/arpanghosh8453/garmin-grafana/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -d /opt/garmin-grafana/ ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Stopping $APP" + systemctl stop garmin-grafana + systemctl stop grafana-server + systemctl stop influxdb + msg_ok "Stopped $APP" + + if [[ ! -f /opt/garmin-grafana/.env ]]; then + msg_error "No .env file found in /opt/garmin-grafana/.env" + exit + fi + source /opt/garmin-grafana/.env + if [[ -z "${INFLUXDB_USER}" || -z "${INFLUXDB_PASSWORD}" || -z "${INFLUXDB_NAME}" ]]; then + msg_error "INFLUXDB_USER, INFLUXDB_PASSWORD, or INFLUXDB_NAME not set in .env file" + exit + fi + + msg_info "Creating Backup" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/garmin-grafana/.garminconnect /opt/garmin-grafana/.env + mv /opt/garmin-grafana/ /opt/garmin-grafana-backup/ + msg_ok "Backup Created" + + msg_info "Updating $APP to v${RELEASE}" + curl -fsSL -o "${RELEASE}.zip" "https://github.com/arpanghosh8453/garmin-grafana/archive/refs/tags/${RELEASE}.zip" + unzip -q "${RELEASE}.zip" + mv "garmin-grafana-${RELEASE}/" "/opt/garmin-grafana" + rm -f "${RELEASE}.zip" + $STD uv sync --locked --project /opt/garmin-grafana/ + # shellcheck disable=SC2016 + sed -i 's/\${DS_GARMIN_STATS}/garmin_influxdb/g' /opt/garmin-grafana/Grafana_Dashboard/Garmin-Grafana-Dashboard.json + sed -i 's/influxdb:8086/localhost:8086/' /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + sed -i "s/influxdb_user/${INFLUXDB_USER}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + sed -i "s/influxdb_secret_password/${INFLUXDB_PASSWORD}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + sed -i "s/GarminStats/${INFLUXDB_NAME}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + # Copy across grafana data + cp -r /opt/garmin-grafana/Grafana_Datasource/* /etc/grafana/provisioning/datasources + cp -r /opt/garmin-grafana/Grafana_Dashboard/* /etc/grafana/provisioning/dashboards + # Copy back the env and token files + cp /opt/garmin-grafana-backup/.env /opt/garmin-grafana/.env + cp -r /opt/garmin-grafana-backup/.garminconnect /opt/garmin-grafana/.garminconnect + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start garmin-grafana + systemctl start grafana-server + systemctl start influxdb + msg_ok "Started $APP" + + msg_info "Cleaning Up" + rm -rf /opt/garmin-grafana-backup + msg_ok "Cleanup Completed" + + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/headers/alpine-homarr b/ct/headers/alpine-homarr new file mode 100644 index 0000000..86ef993 --- /dev/null +++ b/ct/headers/alpine-homarr @@ -0,0 +1,6 @@ + __ _ __ + ____ _/ /___ (_)___ ___ / /_ ____ ____ ___ ____ ___________ + / __ `/ / __ \/ / __ \/ _ \______/ __ \/ __ \/ __ `__ \/ __ `/ ___/ ___/ +/ /_/ / / /_/ / / / / / __/_____/ / / / /_/ / / / / / / /_/ / / / / +\__,_/_/ .___/_/_/ /_/\___/ /_/ /_/\____/_/ /_/ /_/\__,_/_/ /_/ + /_/ diff --git a/ct/headers/argus b/ct/headers/argus new file mode 100644 index 0000000..47434a3 --- /dev/null +++ b/ct/headers/argus @@ -0,0 +1,6 @@ + ___ + / | _________ ___ _______ + / /| | / ___/ __ `/ / / / ___/ + / ___ |/ / / /_/ / /_/ (__ ) +/_/ |_/_/ \__, /\__,_/____/ + /____/ diff --git a/ct/headers/configarr b/ct/headers/configarr new file mode 100644 index 0000000..cf84ff3 --- /dev/null +++ b/ct/headers/configarr @@ -0,0 +1,6 @@ + ______ _____ + / ____/___ ____ / __(_)___ _____ ___________ + / / / __ \/ __ \/ /_/ / __ `/ __ `/ ___/ ___/ +/ /___/ /_/ / / / / __/ / /_/ / /_/ / / / / +\____/\____/_/ /_/_/ /_/\__, /\__,_/_/ /_/ + /____/ diff --git a/ct/headers/garmin-grafana b/ct/headers/garmin-grafana new file mode 100644 index 0000000..5509b81 --- /dev/null +++ b/ct/headers/garmin-grafana @@ -0,0 +1,6 @@ + _ ____ + ____ _____ __________ ___ (_)___ ____ __________ _/ __/___ _____ ____ _ + / __ `/ __ `/ ___/ __ `__ \/ / __ \______/ __ `/ ___/ __ `/ /_/ __ `/ __ \/ __ `/ + / /_/ / /_/ / / / / / / / / / / / /_____/ /_/ / / / /_/ / __/ /_/ / / / / /_/ / + \__, /\__,_/_/ /_/ /_/ /_/_/_/ /_/ \__, /_/ \__,_/_/ \__,_/_/ /_/\__,_/ +/____/ /____/ diff --git a/ct/headers/openwebui b/ct/headers/openwebui new file mode 100644 index 0000000..0097a27 --- /dev/null +++ b/ct/headers/openwebui @@ -0,0 +1,6 @@ + ____ _ __ __ __ ______ + / __ \____ ___ ____ | | / /__ / /_ / / / / _/ + / / / / __ \/ _ \/ __ \ | | /| / / _ \/ __ \/ / / // / +/ /_/ / /_/ / __/ / / / | |/ |/ / __/ /_/ / /_/ // / +\____/ .___/\___/_/ /_/ |__/|__/\___/_.___/\____/___/ + /_/ diff --git a/ct/headers/pulse b/ct/headers/pulse new file mode 100644 index 0000000..3d3b702 --- /dev/null +++ b/ct/headers/pulse @@ -0,0 +1,6 @@ + ____ __ + / __ \__ __/ /_______ + / /_/ / / / / / ___/ _ \ + / ____/ /_/ / (__ ) __/ +/_/ \__,_/_/____/\___/ + diff --git a/ct/openwebui.sh b/ct/openwebui.sh new file mode 100644 index 0000000..96647a9 --- /dev/null +++ b/ct/openwebui.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: havardthom +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://openwebui.com/ + +APP="Open WebUI" +var_tags="${var_tags:-ai;interface}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-25}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/open-webui ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if [ -x "/usr/bin/ollama" ]; then + msg_info "Updating Ollama" + OLLAMA_VERSION=$(ollama -v | awk '{print $NF}') + RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') + if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then + curl -fsSLO https://ollama.com/download/ollama-linux-amd64.tgz + tar -C /usr -xzf ollama-linux-amd64.tgz + rm -rf ollama-linux-amd64.tgz + msg_ok "Ollama updated to version $RELEASE" + else + msg_ok "Ollama is already up to date." + fi + fi + + msg_info "Updating ${APP} (Patience)" + systemctl stop open-webui.service + mkdir -p /opt/openwebui-backup + cp -rf /opt/openwebui/backend/data /opt/openwebui-backup + cp /opt/openwebui/.env /opt + rm -rf /opt/openwebui + fetch_and_deploy_gh_release "open-webui/open-webui" + cd /opt/openwebui + $STD npm install + export NODE_OPTIONS="--max-old-space-size=3584" + sed -i "s/git rev-parse HEAD/openssl rand -hex 20/g" /opt/openwebui/svelte.config.js + $STD npm run build + cd ./backend + $STD pip install -r requirements.txt -U + cp -rf /opt/openwebui-backup/* /opt/openwebui/backend + mv /opt/.env /opt/openwebui/ + systemctl start open-webui.service + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/ct/pulse.sh b/ct/pulse.sh new file mode 100644 index 0000000..3b16d49 --- /dev/null +++ b/ct/pulse.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: rcourtman +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/rcourtman/Pulse + +APP="Pulse" +var_tags="monitoring;nodejs" +var_cpu="1" +var_ram="1024" +var_disk="4" +var_os="debian" +var_version="12" +var_unprivileged="1" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if ! command -v jq &>/dev/null; then + msg_info "jq is not installed. Installing..." + $STD apt-get update >/dev/null + $STD apt-get install -y jq >/dev/null + if ! command -v jq &>/dev/null; then + msg_error "Failed to install jq. Cannot proceed with update check." + exit 1 + fi + msg_ok "jq installed." + fi + + msg_info "Checking for ${APP} updates..." + LATEST_RELEASE=$(curl -s https://api.github.com/repos/rcourtman/Pulse/releases/latest | jq -r '.tag_name') + msg_ok "Latest available version: ${LATEST_RELEASE}" + + CURRENT_VERSION="" + if [[ -f /opt/${APP}_version.txt ]]; then + CURRENT_VERSION=$(cat /opt/${APP}_version.txt) + fi + + if [[ "${LATEST_RELEASE}" != "$CURRENT_VERSION" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Updating ${APP} to ${LATEST_RELEASE}..." + + msg_info "Stopping ${APP} service..." + systemctl stop pulse-monitor.service + msg_ok "Stopped ${APP} service." + + msg_info "Fetching and checking out ${LATEST_RELEASE}..." + cd /opt/pulse-proxmox || { + msg_error "Failed to cd into /opt/pulse-proxmox" + exit 1 + } + + msg_info "Configuring git safe directory..." + set -x + git config --global --add safe.directory /opt/pulse-proxmox + local git_config_exit_code=$? + set +x + if [ $git_config_exit_code -ne 0 ]; then + msg_error "git config safe.directory failed with exit code $git_config_exit_code" + exit 1 + fi + msg_ok "Configured git safe directory." + + silent git fetch origin --tags --force || { + msg_error "Failed to fetch from git remote." + exit 1 + } + echo "DEBUG: Attempting checkout command: git checkout ${LATEST_RELEASE}" + git checkout "${LATEST_RELEASE}" || { + msg_error "Failed to checkout tag ${LATEST_RELEASE}." + exit 1 + } + silent git reset --hard "${LATEST_RELEASE}" || { + msg_error "Failed to reset to tag ${LATEST_RELEASE}." + exit 1 + } + silent git clean -fd || { msg_warning "Failed to clean untracked files."; } + msg_ok "Fetched and checked out ${LATEST_RELEASE}." + + msg_info "Setting ownership and permissions before npm install..." + chown -R pulse:pulse /opt/pulse-proxmox || { + msg_error "Failed to chown /opt/pulse-proxmox" + exit 1 + } + chmod -R u+rwX,go+rX,go-w /opt/pulse-proxmox || { + msg_error "Failed to chmod /opt/pulse-proxmox" + exit 1 + } + if [ -d "/opt/pulse-proxmox/node_modules/.bin" ]; then + chmod +x /opt/pulse-proxmox/node_modules/.bin/* || msg_warning "Failed to chmod +x on node_modules/.bin" + fi + msg_ok "Ownership and permissions set." + + msg_info "Installing Node.js dependencies..." + silent sudo -iu pulse sh -c 'cd /opt/pulse-proxmox && npm install --unsafe-perm' || { + msg_error "Failed to install root npm dependencies." + exit 1 + } + silent sudo -iu pulse sh -c 'cd /opt/pulse-proxmox/server && npm install --unsafe-perm' || { + msg_error "Failed to install server npm dependencies." + exit 1 + } + msg_ok "Node.js dependencies installed." + + msg_info "Building CSS assets..." + TAILWIND_PATH="/opt/pulse-proxmox/node_modules/.bin/tailwindcss" + TAILWIND_ARGS="-c ./src/tailwind.config.js -i ./src/index.css -o ./src/public/output.css" + if ! sudo -iu pulse sh -c "cd /opt/pulse-proxmox && $TAILWIND_PATH $TAILWIND_ARGS"; then + echo -e "${TAB}${YW}⚠️ Failed to build CSS assets (See errors above). Proceeding anyway.${CL}" + else + msg_ok "CSS assets built." + fi + + msg_info "Setting permissions..." + chown -R pulse:pulse /opt/pulse-proxmox || msg_warning "Final chown failed." + msg_ok "Permissions set." + + msg_info "Starting ${APP} service..." + systemctl start pulse-monitor.service + msg_ok "Started ${APP} service." + + echo "${LATEST_RELEASE}" >/opt/${APP}_version.txt + msg_ok "Update Successful to ${LATEST_RELEASE}" + else + msg_ok "No update required. ${APP} is already at ${LATEST_RELEASE}." + fi + exit 0 +} + +start +build_container +description + +PULSE_PORT=7655 +if [ -f "/opt/pulse-proxmox/.env" ] && grep -q '^PORT=' "/opt/pulse-proxmox/.env"; then + PULSE_PORT=$(grep '^PORT=' "/opt/pulse-proxmox/.env" | cut -d'=' -f2 | tr -d '[:space:]') + if ! [[ "$PULSE_PORT" =~ ^[0-9]+$ ]]; then + PULSE_PORT=7655 + fi +fi + +msg_ok "Completed Successfully! +" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:${PULSE_PORT}${CL}" diff --git a/frontend/public/json/argus.json b/frontend/public/json/argus.json new file mode 100644 index 0000000..c1368ff --- /dev/null +++ b/frontend/public/json/argus.json @@ -0,0 +1,35 @@ +{ + "name": "Argus", + "slug": "argus", + "categories": [ + 11 + ], + "date_created": "2025-05-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://release-argus.io/docs/overview/", + "website": "https://release-argus.io/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/argus.webp", + "config_path": "/opt/argus/config.yml", + "description": "Argus will query websites at a user defined interval for new software releases and then trigger Gotify/Slack/Other notification(s) and/or WebHook(s) when one has been found. For example, you could set it to monitor the Argus repo (release-argus/argus). This will query the GitHub API and track the tag_name variable. When this variable changes from what it was on a previous query, a GitHub-style WebHook could be sent that triggers something (like AWX) to update Argus on your server.", + "install_methods": [ + { + "type": "default", + "script": "ct/argus.sh", + "resources": { + "cpu": 1, + "ram": 256, + "hdd": 3, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/frontend/public/json/configarr.json b/frontend/public/json/configarr.json new file mode 100644 index 0000000..964c974 --- /dev/null +++ b/frontend/public/json/configarr.json @@ -0,0 +1,40 @@ +{ + "name": "Configarr", + "slug": "configarr", + "categories": [ + 14 + ], + "date_created": "2025-05-06", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://configarr.raydak.de/docs/intro", + "config_path": "/opt/configarr/config.yml", + "website": "https://configarr.raydak.de/", + "logo": "https://github.com/raydak-labs/configarr/blob/main/docs/static/img/logo.webp", + "description": "Configarr is an open-source tool designed to simplify configuration and synchronization for Sonarr and Radarr (and other experimental). It integrates with TRaSH Guides to automate updates of custom formats, quality profiles, and other settings, while also supporting user-defined configurations.", + "install_methods": [ + { + "type": "default", + "script": "ct/configarr.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "change secrets file /opt/configarr/secrets.yml", + "type": "info" + } + ] +} diff --git a/frontend/public/json/garmin-grafana.json b/frontend/public/json/garmin-grafana.json new file mode 100644 index 0000000..d9a9283 --- /dev/null +++ b/frontend/public/json/garmin-grafana.json @@ -0,0 +1,44 @@ +{ + "name": "garmin-grafana", + "slug": "garmin-grafana", + "categories": [ + 24 + ], + "date_created": "2025-05-08", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/arpanghosh8453/garmin-grafana", + "config_path": "", + "website": "https://github.com/arpanghosh8453/garmin-grafana", + "logo": "https://github.com/arpanghosh8453/garmin-grafana/raw/refs/heads/main/Extra/Garmin-Grafana-Logo.svg", + "description": "A docker container to fetch data from Garmin servers and store the data in a local influxdb database for appealing visualization with Grafana.", + "install_methods": [ + { + "type": "default", + "script": "ct/garmin-grafana.sh", + "resources": { + "cpu": 2, + "ram": 2, + "hdd": 8, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Show login and database credentials: `cat ~/.garmin-grafana.creds`", + "type": "info" + }, + { + "text": "`garmin-grafana` only imports the past 7 days by default. To import historical data, use the `~/bulk-import.sh` script after installation.", + "type": "info" + } + ] +} diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index dcdddf2..f65a399 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,9 +1,114 @@ [ + { + "name": "Stirling-Tools/Stirling-PDF", + "version": "v0.46.2", + "date": "2025-05-20T11:21:04Z" + }, + { + "name": "zabbix/zabbix", + "version": "7.2.7", + "date": "2025-05-20T11:00:56Z" + }, + { + "name": "mattermost/mattermost", + "version": "server/public/v0.1.13", + "date": "2025-05-16T03:44:25Z" + }, + { + "name": "Luligu/matterbridge", + "version": "3.0.3", + "date": "2025-05-20T06:51:42Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.1930", + "date": "2025-05-20T06:38:17Z" + }, + { + "name": "crafty-controller/crafty-4", + "version": "v4.4.9", + "date": "2025-05-20T00:08:29Z" + }, + { + "name": "HabitRPG/habitica", + "version": "v5.36.4", + "date": "2025-05-19T22:28:11Z" + }, + { + "name": "apache/cassandra", + "version": "cassandra-4.1.9", + "date": "2025-05-19T21:37:07Z" + }, + { + "name": "paperless-ngx/paperless-ngx", + "version": "v2.16.1", + "date": "2025-05-19T21:26:44Z" + }, + { + "name": "glanceapp/glance", + "version": "v0.8.3", + "date": "2025-05-19T20:45:10Z" + }, + { + "name": "keycloak/keycloak", + "version": "26.0.12", + "date": "2025-05-15T14:06:52Z" + }, + { + "name": "bunkerity/bunkerweb", + "version": "v1.6.1", + "date": "2025-03-15T17:29:17Z" + }, + { + "name": "Forceu/Gokapi", + "version": "v1.9.6", + "date": "2024-12-18T14:35:37Z" + }, + { + "name": "Checkmk/checkmk", + "version": "v2.4.0p2", + "date": "2025-05-19T17:06:15Z" + }, + { + "name": "firefly-iii/firefly-iii", + "version": "v6.2.12", + "date": "2025-04-20T19:22:17Z" + }, + { + "name": "esphome/esphome", + "version": "2025.4.2", + "date": "2025-05-11T22:18:43Z" + }, + { + "name": "n8n-io/n8n", + "version": "n8n@1.91.3", + "date": "2025-05-08T12:25:10Z" + }, + { + "name": "docker/compose", + "version": "v2.36.1", + "date": "2025-05-19T12:26:41Z" + }, + { + "name": "Graylog2/graylog2-server", + "version": "6.3.0-beta.1", + "date": "2025-05-19T11:23:27Z" + }, + { + "name": "documenso/documenso", + "version": "v1.11.0", + "date": "2025-05-19T06:21:18Z" + }, { "name": "MediaBrowser/Emby.Releases", "version": "4.8.11.0", "date": "2025-03-10T06:39:11Z" }, + { + "name": "open-webui/open-webui", + "version": "v0.6.10", + "date": "2025-05-19T01:34:37Z" + }, { "name": "Part-DB/Part-DB-server", "version": "v1.17.1", @@ -36,8 +141,8 @@ }, { "name": "runtipi/runtipi", - "version": "nightly", - "date": "2025-05-18T13:00:01Z" + "version": "v4.1.1", + "date": "2025-05-16T17:37:30Z" }, { "name": "hansmi/prometheus-paperless-exporter", @@ -49,11 +154,6 @@ "version": "v1.35.1.5034", "date": "2025-04-30T11:02:36Z" }, - { - "name": "Jackett/Jackett", - "version": "v0.22.1917", - "date": "2025-05-18T05:59:41Z" - }, { "name": "theonedev/onedev", "version": "v11.9.8", @@ -64,11 +164,6 @@ "version": "0.17.12", "date": "2025-05-17T19:16:00Z" }, - { - "name": "crafty-controller/crafty-4", - "version": "v4.4.8", - "date": "2025-05-17T18:47:36Z" - }, { "name": "Radarr/Radarr", "version": "v5.23.3.9987", @@ -139,11 +234,6 @@ "version": "2.0.4", "date": "2025-05-16T15:09:53Z" }, - { - "name": "keycloak/keycloak", - "version": "26.0.12", - "date": "2025-05-15T14:06:52Z" - }, { "name": "emqx/emqx", "version": "e5.10.0-alpha.1", @@ -159,11 +249,6 @@ "version": "1.21.3", "date": "2025-05-16T04:31:05Z" }, - { - "name": "mattermost/mattermost", - "version": "server/public/v0.1.13", - "date": "2025-05-16T03:44:25Z" - }, { "name": "ipfs/kubo", "version": "v0.34.1", @@ -184,11 +269,6 @@ "version": "2025.5.0", "date": "2025-05-15T17:09:50Z" }, - { - "name": "apache/cassandra", - "version": "4.1.9-tentative", - "date": "2025-05-15T15:52:53Z" - }, { "name": "VictoriaMetrics/VictoriaMetrics", "version": "pmm-6401-v1.117.1", @@ -199,16 +279,6 @@ "version": "v3.1.1", "date": "2025-05-15T15:17:57Z" }, - { - "name": "bunkerity/bunkerweb", - "version": "v1.6.1", - "date": "2025-03-15T17:29:17Z" - }, - { - "name": "Checkmk/checkmk", - "version": "v2.4.0p1", - "date": "2025-05-15T12:41:12Z" - }, { "name": "zwave-js/zwave-js-ui", "version": "v10.5.1", @@ -229,26 +299,11 @@ "version": "v0.7.0", "date": "2025-05-14T23:42:30Z" }, - { - "name": "glanceapp/glance", - "version": "v0.8.2", - "date": "2025-05-14T21:34:41Z" - }, { "name": "Ombi-app/Ombi", "version": "v4.47.1", "date": "2025-01-05T21:14:23Z" }, - { - "name": "Luligu/matterbridge", - "version": "3.0.2", - "date": "2025-05-14T20:38:06Z" - }, - { - "name": "esphome/esphome", - "version": "2025.4.2", - "date": "2025-05-11T22:18:43Z" - }, { "name": "Athou/commafeed", "version": "5.9.0", @@ -269,11 +324,6 @@ "version": "0.42.1", "date": "2020-06-07T07:27:04Z" }, - { - "name": "firefly-iii/firefly-iii", - "version": "v6.2.12", - "date": "2025-04-20T19:22:17Z" - }, { "name": "jenkinsci/jenkins", "version": "jenkins-2.510", @@ -319,16 +369,6 @@ "version": "v1.129.0", "date": "2025-05-06T12:28:54Z" }, - { - "name": "zabbix/zabbix", - "version": "7.2.7rc1", - "date": "2025-05-13T11:55:32Z" - }, - { - "name": "Graylog2/graylog2-server", - "version": "6.3.0-alpha.4", - "date": "2025-05-13T11:18:29Z" - }, { "name": "zitadel/zitadel", "version": "v2.65.9", @@ -359,11 +399,6 @@ "version": "v0.24.3", "date": "2025-05-12T15:23:21Z" }, - { - "name": "n8n-io/n8n", - "version": "n8n@1.91.3", - "date": "2025-05-08T12:25:10Z" - }, { "name": "apache/tika", "version": "3.2.0-rc1", @@ -404,16 +439,6 @@ "version": "v0.2.3", "date": "2025-05-10T21:14:45Z" }, - { - "name": "open-webui/open-webui", - "version": "v0.6.9", - "date": "2025-05-10T19:05:02Z" - }, - { - "name": "Stirling-Tools/Stirling-PDF", - "version": "v0.46.1", - "date": "2025-05-10T15:39:10Z" - }, { "name": "pelican-dev/wings", "version": "v1.0.0-beta13", @@ -443,30 +468,5 @@ "name": "semaphoreui/semaphore", "version": "v2.14.10", "date": "2025-05-07T20:23:29Z" - }, - { - "name": "readeck/readeck", - "version": "0.18.2", - "date": "2025-05-07T19:22:22Z" - }, - { - "name": "HabitRPG/habitica", - "version": "v5.36.3", - "date": "2025-05-07T17:22:07Z" - }, - { - "name": "donaldzou/WGDashboard", - "version": "v4.2.3", - "date": "2025-05-07T15:35:04Z" - }, - { - "name": "stonith404/pingvin-share", - "version": "v1.12.0", - "date": "2025-05-07T14:12:11Z" - }, - { - "name": "Brandawg93/PeaNUT", - "version": "v5.7.5", - "date": "2025-05-07T14:01:45Z" } ] diff --git a/install/alpine-homarr-install.sh b/install/alpine-homarr-install.sh new file mode 100644 index 0000000..69cabf2 --- /dev/null +++ b/install/alpine-homarr-install.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/homarr-labs/homarr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apk add --no-cache \ + redis \ + nginx \ + ca-certificates \ + openssl \ + jq \ + make \ + g++ \ + gettext \ + libstdc++ \ + libgcc \ + python3 \ + py3-pip +msg_ok "Installed Dependencies" + +NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') +NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" +install_node_and_modules +fetch_and_deploy_gh_release "homarr-labs/homarr" + +msg_info "Installing Homarr" +mkdir -p /opt/homarr_db +touch /opt/homarr_db/db.sqlite +SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)" +cd /opt/homarr +cat </opt/homarr/.env +DB_DRIVER='better-sqlite3' +DB_DIALECT='sqlite' +SECRET_ENCRYPTION_KEY='${SECRET_ENCRYPTION_KEY}' +DB_URL='/opt/homarr_db/db.sqlite' +TURBO_TELEMETRY_DISABLED=1 +AUTH_PROVIDERS='credentials' +NODE_ENV='production' +EOF + +$STD pnpm install +$STD pnpm build +msg_ok "Installed Homarr" + +msg_info "Copying build and config files" +cp /opt/homarr/apps/nextjs/next.config.ts . +cp /opt/homarr/apps/nextjs/package.json . +cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations +cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr +mkdir -p /appdata/redis +cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf +mkdir -p /etc/nginx/templates +cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf +mkdir -p /opt/homarr/apps/cli +cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs +echo -e '#!/bin/sh\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr +chmod +x /usr/bin/homarr +mkdir -p /opt/homarr/build +cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node +echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" +msg_ok "Finished copying" + +msg_info "Creating run script" +cat <<'EOF' >/opt/run_homarr.sh +#!/bin/sh +set -a +. /opt/homarr/.env +set +a +export DB_DIALECT='sqlite' +export AUTH_SECRET=$(openssl rand -base64 32) +node /opt/homarr_db/migrations/$DB_DIALECT/migrate.cjs /opt/homarr_db/migrations/$DB_DIALECT +for dir in $(find /opt/homarr_db/migrations/migrations -mindepth 1 -maxdepth 1 -type d); do + dirname=$(basename "$dir") + mkdir -p "/opt/homarr_db/migrations/$dirname" + cp -r "$dir"/* "/opt/homarr_db/migrations/$dirname/" 2>/dev/null || true +done +export HOSTNAME=$(ip route get 1.1.1.1 | awk '/src/ { print $7 }') +envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf +nginx -g 'daemon off;' & +redis-server /opt/homarr/redis.conf & +node apps/tasks/tasks.cjs & +node apps/websocket/wssServer.cjs & +node apps/nextjs/server.js & PID=$! +wait $PID +EOF +chmod +x /opt/run_homarr.sh +msg_ok "Created run script" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -rf /opt/v${RELEASE}.zip +msg_ok "Cleaned" diff --git a/install/argus-install.sh b/install/argus-install.sh new file mode 100644 index 0000000..b196b93 --- /dev/null +++ b/install/argus-install.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://release-argus.io/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + jq +msg_ok "Installed Dependencies" + +msg_info "Setup Argus" +RELEASE=$(curl -fsSL https://api.github.com/repos/release-argus/Argus/releases/latest | jq -r .tag_name | sed 's/^v//') +mkdir -p /opt/argus +curl -fsSL "https://github.com/release-argus/Argus/releases/download/${RELEASE}/Argus-${RELEASE}.linux-amd64" -o /opt/argus/Argus +chmod +x /opt/argus/Argus +msg_ok "Setup Argus" + +msg_info "Setup Argus Config" +cat </opt/argus/config.yml +settings: + log: + level: INFO + timestamps: false + data: + database_file: data/argus.db + web: + listen_host: 0.0.0.0 + listen_port: 8080 + route_prefix: / + +defaults: + service: + options: + interval: 30m + semantic_versioning: true + latest_version: + allow_invalid_certs: false + use_prerelease: false + dashboard: + auto_approve: true + webhook: + desired_status_code: 201 + +service: + release-argus/argus: + latest_version: + type: github + url: release-argus/argus + dashboard: + icon: https://raw.githubusercontent.com/release-argus/Argus/master/web/ui/react-app/public/favicon.svg + icon_link_to: https://release-argus.io + web_url: https://github.com/release-argus/Argus/blob/master/CHANGELOG.md + + community-scripts/ProxmoxVE: + latest_version: + type: github + url: community-scripts/ProxmoxVE + use_prerelease: false + dashboard: + icon: https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/images/logo.png + icon_link_to: https://helper-scripts.com/ + web_url: https://github.com/community-scripts/ProxmoxVE/releases +EOF +echo "${RELEASE}" >/opt/argus_version.txt +msg_ok "Setup Config" + +msg_info "Creating Service" +cat </etc/systemd/system/argus.service +[Unit] +Description=Argus +After=network.target +[Service] +Type=simple +WorkingDirectory=/opt/argus +ExecStart=/opt/argus/Argus +Restart=on-failure +RestartSec=5 +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now argus +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/configarr-install.sh b/install/configarr-install.sh new file mode 100644 index 0000000..d915ab5 --- /dev/null +++ b/install/configarr-install.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: finkerle +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/raydak-labs/configarr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git +msg_ok "Installed Dependencies" + +NODE_MODULE="pnpm@latest" install_node_and_modules +fetch_and_deploy_gh_release "raydak-labs/configarr" + +msg_info "Setup ${APPLICATION}" +cat </opt/configarr/.env +ROOT_PATH=/opt/configarr +CUSTOM_REPO_ROOT=/opt/configarr/repos +CONFIG_LOCATION=/opt/configarr/config.yml +SECRETS_LOCATION=/opt/configarr/secrets.yml +EOF +mv /opt/configarr/secrets.yml.template /opt/configarr/secrets.yml +sed 's|#localConfigTemplatesPath: /app/templates|#localConfigTemplatesPath: /opt/configarr/templates|' /opt/configarr/config.yml.template >/opt/configarr/config.yml +cd /opt/configarr +pnpm install +pnpm run build +msg_ok "Setup ${APPLICATION}" + +msg_info "Creating Service" +cat </etc/systemd/system/configarr-task.service +[Unit] +Description=Run Configarr Task + +[Service] +Type=oneshot +WorkingDirectory=/opt/configarr +ExecStart=/usr/bin/node /opt/configarr/bundle.cjs + +EOF +cat </etc/systemd/system/configarr-task.timer +[Unit] +Description=Run Configarr every 5 minutes + +[Timer] +OnBootSec=2min +OnUnitActiveSec=5min +Persistent=true + +[Install] +WantedBy=timers.target +EOF +systemctl enable -q --now configarr-task.timer +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/garmin-grafana-install.sh b/install/garmin-grafana-install.sh new file mode 100644 index 0000000..c0d85a6 --- /dev/null +++ b/install/garmin-grafana-install.sh @@ -0,0 +1,213 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: aliaksei135 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/arpanghosh8453/garmin-grafana + +# Import Functions und Setup +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +# Installing Dependencies +msg_info "Installing Dependencies" +$STD apt-get install -y \ + gnupg \ + apt-transport-https \ + software-properties-common \ + lsb-base \ + lsb-release \ + gnupg2 \ + python3 \ + python3-requests \ + python3-dotenv +setup_uv +msg_ok "Installed Dependencies" + +msg_info "Setting up InfluxDB Repository" +curl -fsSL "https://repos.influxdata.com/influxdata-archive_compat.key" | gpg --dearmor >/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg +echo "deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg] https://repos.influxdata.com/debian stable main" >/etc/apt/sources.list.d/influxdata.list +msg_ok "Set up InfluxDB Repository" + +# garmin-grafana recommends influxdb v1 +# this install chronograf, which is the UI for influxdb. this might be overkill? +msg_info "Installing InfluxDB" +$STD apt-get update +$STD apt-get install -y influxdb +curl -fsSL "https://dl.influxdata.com/chronograf/releases/chronograf_1.10.7_amd64.deb" -o "$(basename "https://dl.influxdata.com/chronograf/releases/chronograf_1.10.7_amd64.deb")" +$STD dpkg -i chronograf_1.10.7_amd64.deb +msg_ok "Installed InfluxDB" + +msg_info "Setting up InfluxDB" +$STD sed -i 's/# index-version = "inmem"/index-version = "tsi1"/' /etc/influxdb/influxdb.conf + +INFLUXDB_USER="garmin_grafana_user" +INFLUXDB_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) +INFLUXDB_NAME="GarminStats" +$STD influx -execute "CREATE DATABASE ${INFLUXDB_NAME}" +$STD influx -execute "CREATE USER ${INFLUXDB_USER} WITH PASSWORD '${INFLUXDB_PASSWORD}'" +$STD influx -execute "GRANT ALL ON ${INFLUXDB_NAME} TO ${INFLUXDB_USER}" +# Start the service +$STD systemctl enable --now influxdb +msg_ok "Set up InfluxDB" + +msg_info "Setting up Grafana Repository" +curl -fsSL "https://apt.grafana.com/gpg.key" -o "/usr/share/keyrings/grafana.key" +sh -c 'echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" > /etc/apt/sources.list.d/grafana.list' +msg_ok "Set up Grafana Repository" + +msg_info "Installing Grafana" +$STD apt-get update +$STD apt-get install -y grafana +systemctl start grafana-server +systemctl daemon-reload +systemctl enable --now -q grafana-server.service +# This avoids the "database is locked" error when running the grafana-cli +sleep 20 +msg_ok "Installed Grafana" + +msg_info "Setting up Grafana" +GRAFANA_USER="admin" +GRAFANA_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) +$STD grafana-cli admin reset-admin-password "${GRAFANA_PASS}" +$STD grafana-cli plugins install marcusolsson-hourly-heatmap-panel +$STD systemctl restart grafana-server +# Output credentials to file +{ + echo "Grafana Credentials" + echo "Grafana User: ${GRAFANA_USER}" + echo "Grafana Password: ${GRAFANA_PASS}" +} >>~/garmin-grafana.creds +msg_ok "Set up Grafana" + +# Setup App +msg_info "Installing garmin-grafana" +RELEASE=$(curl -fsSL https://api.github.com/repos/arpanghosh8453/garmin-grafana/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +curl -fsSL -o "${RELEASE}.zip" "https://github.com/arpanghosh8453/garmin-grafana/archive/refs/tags/${RELEASE}.zip" +unzip -q "${RELEASE}.zip" +# Remove the v prefix to RELEASE if it exists +if [[ "${RELEASE}" == v* ]]; then + RELEASE="${RELEASE:1}" +fi +mv "garmin-grafana-${RELEASE}/" "/opt/garmin-grafana" +mkdir -p /opt/garmin-grafana/.garminconnect +$STD uv sync --locked --project /opt/garmin-grafana/ +# Setup grafana provisioning configs +# shellcheck disable=SC2016 +sed -i 's/\${DS_GARMIN_STATS}/garmin_influxdb/g' /opt/garmin-grafana/Grafana_Dashboard/Garmin-Grafana-Dashboard.json +sed -i 's/influxdb:8086/localhost:8086/' /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml +sed -i "s/influxdb_user/${INFLUXDB_USER}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml +sed -i "s/influxdb_secret_password/${INFLUXDB_PASSWORD}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml +sed -i "s/GarminStats/${INFLUXDB_NAME}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml +# Copy across grafana data +cp -r /opt/garmin-grafana/Grafana_Datasource/* /etc/grafana/provisioning/datasources +cp -r /opt/garmin-grafana/Grafana_Dashboard/* /etc/grafana/provisioning/dashboards +echo "${RELEASE}" >"/opt/garmin-grafana_version.txt" +msg_ok "Installed garmin-grafana" + +msg_info "Setting up garmin-grafana" +# Check if using Chinese garmin servers +read -rp "Are you using Garmin in mainland China? (y/N): " prompt +if [[ "${prompt,,}" =~ ^(y|yes|Y)$ ]]; then + GARMIN_CN="True" +else + GARMIN_CN="False" +fi + +cat </opt/garmin-grafana/.env +INFLUXDB_HOST=localhost +INFLUXDB_PORT=8086 +INFLUXDB_ENDPOINT_IS_HTTP=True +INFLUXDB_USERNAME=${INFLUXDB_USER} +INFLUXDB_PASSWORD=${INFLUXDB_PASSWORD} +INFLUXDB_DATABASE=${INFLUXDB_NAME} +GARMIN_IS_CN=${GARMIN_CN} +TOKEN_DIR=/opt/garmin-grafana/.garminconnect +EOF + +# garmin-grafana usually prompts the user for email and password (and MFA) on first run, +# then stores a refreshable token. We try to avoid storing user credentials in the env vars +if [ -z "$(ls -A /opt/garmin-grafana/.garminconnect)" ]; then + read -r -p "Please enter your Garmin Connect Email: " GARMIN_EMAIL + read -r -p "Please enter your Garmin Connect Password (this is used to generate a token and NOT stored): " GARMIN_PASSWORD + read -r -p "Please enter your MFA Code (if applicable, leave blank if not): " GARMIN_MFA + # Run the script once to prompt for credential + msg_info "Creating Garmin credentials, this will timeout in 60 seconds" + timeout 60s uv run --env-file /opt/garmin-grafana/.env --project /opt/garmin-grafana/ /opt/garmin-grafana/src/garmin_grafana/garmin_fetch.py <~/bulk-import.sh +#!/usr/bin/env bash +if [[ -z \$1 ]]; then + echo "Usage: \$0 " + echo "Example: \$0 2023-01-01 2023-01-31" + echo "Date format: YYYY-MM-DD" + echo "This will import data from the start_date to the end_date (inclusive)" + exit 1 +fi + +START_DATE="\$1" +if [[ -z \$2 ]]; then + END_DATE="\$(date +%Y-%m-%d)" + echo "No end date provided, using today as end date: \${END_DATE}" +else + END_DATE="\$2" +fi + +# Stop the service if running +systemctl stop garmin-grafana + +MANUAL_START_DATE="\${START_DATE}" MANUAL_END_DATE="\${END_DATE}" uv run --env-file /opt/garmin-grafana/.env --project /opt/garmin-grafana/ /opt/garmin-grafana/src/garmin_grafana/garmin_fetch.py + +# Restart the service +systemctl start garmin-grafana +EOF +chmod +x ~/bulk-import.sh +msg_ok "Set up garmin-grafana" + +msg_info "Creating Service" +cat </etc/systemd/system/garmin-grafana.service +[Unit] +Description=garmin-grafana Service +After=network.target + +[Service] +ExecStart=uv run --project /opt/garmin-grafana/ /opt/garmin-grafana/src/garmin_grafana/garmin_fetch.py +Restart=always +EnvironmentFile=/opt/garmin-grafana/.env + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now garmin-grafana +msg_ok "Created Service" + +motd_ssh +customize + +# Cleanup +msg_info "Cleaning up" +rm -f "${RELEASE}".zip +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/openwebui-install.sh b/install/openwebui-install.sh new file mode 100644 index 0000000..aebb130 --- /dev/null +++ b/install/openwebui-install.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck +# Co-Author: havardthom +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://openwebui.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + ffmpeg +msg_ok "Installed Dependencies" + +msg_info "Setup Python3" +$STD apt-get install -y --no-install-recommends \ + python3 \ + python3-pip +msg_ok "Setup Python3" + +install_node_and_modules + +msg_info "Installing Open WebUI (Patience)" +fetch_and_deploy_gh_release "open-webui/open-webui" +cd /opt/openwebui/backend +$STD pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu +$STD pip3 install -r requirements.txt -U +cd /opt/openwebui +cat </opt/openwebui/.env +# Ollama URL for the backend to connect +# The path '/ollama' will be redirected to the specified backend URL +OLLAMA_BASE_URL=http://0.0.0.0:11434 +OPENAI_API_BASE_URL='' +OPENAI_API_KEY='' +# AUTOMATIC1111_BASE_URL="http://localhost:7860" +# DO NOT TRACK +SCARF_NO_ANALYTICS=true +DO_NOT_TRACK=true +ANONYMIZED_TELEMETRY=false +ENV=prod +ENABLE_OLLAMA_API=false +EOF +$STD npm install +export NODE_OPTIONS="--max-old-space-size=3584" +sed -i "s/git rev-parse HEAD/openssl rand -hex 20/g" /opt/openwebui/svelte.config.js +$STD npm run build +msg_ok "Installed Open WebUI" + +read -r -p "${TAB3}Would you like to add Ollama? " prompt +if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + msg_info "Installing Ollama" + curl -fsSLO https://ollama.com/download/ollama-linux-amd64.tgz + tar -C /usr -xzf ollama-linux-amd64.tgz + rm -rf ollama-linux-amd64.tgz + cat </etc/systemd/system/ollama.service +[Unit] +Description=Ollama Service +After=network-online.target + +[Service] +Type=exec +ExecStart=/usr/bin/ollama serve +Environment=HOME=$HOME +Environment=OLLAMA_HOST=0.0.0.0 +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now ollama + sed -i 's/ENABLE_OLLAMA_API=false/ENABLE_OLLAMA_API=true/g' /opt/openwebui/.env + msg_ok "Installed Ollama" +fi + +msg_info "Creating Service" +cat </etc/systemd/system/open-webui.service +[Unit] +Description=Open WebUI Service +After=network.target + +[Service] +Type=exec +WorkingDirectory=/opt/openwebui +EnvironmentFile=/opt/openwebui/.env +ExecStart=/opt/openwebui/backend/start.sh + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now open-webui +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/install/pulse-install.sh b/install/pulse-install.sh new file mode 100644 index 0000000..7acbd4a --- /dev/null +++ b/install/pulse-install.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: rcourtman +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/rcourtman/Pulse + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" + +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +APP="Pulse" +APP_DIR="/opt/pulse-proxmox" +PULSE_USER="pulse" +SERVICE_NAME="pulse-monitor.service" + +msg_info "Creating dedicated user pulse..." +if id pulse &>/dev/null; then + msg_warning "User '${PULSE_USER}' already exists. Skipping creation." +else + useradd -r -m -d /opt/pulse-home -s /bin/bash "$PULSE_USER" + if useradd -r -m -d /opt/pulse-home -s /bin/bash "$PULSE_USER"; then + msg_ok "User '${PULSE_USER}' created successfully." + else + msg_error "Failed to create user '${PULSE_USER}'." + exit 1 + fi +fi + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git \ + jq \ + diffutils +msg_ok "Installed Core Dependencies" + +NODE_VERSION="20" NODE_MODULE="yarn@latest" install_node_and_modules + +msg_info "Setup ${APP}" +$STD git clone https://github.com/rcourtman/Pulse.git /opt/pulse-proxmox +cd /opt/pulse-proxmox +$STD npm install --unsafe-perm +cd /opt/pulse-proxmox/server +$STD npm install --unsafe-perm +cd /opt/pulse-proxmox +$STD npm run build:css +ENV_EXAMPLE="/opt/pulse-proxmox/.env.example" +ENV_FILE="${/opt/pulse-proxmox}/.env" +if [ -f "$ENV_EXAMPLE" ]; then + if [ ! -s "$ENV_FILE" ]; then + cp "$ENV_EXAMPLE" "$ENV_FILE" + msg_info "Created ${ENV_FILE} from example." + sed -i 's|^PROXMOX_HOST=.*|PROXMOX_HOST=https://proxmox_host:8006|' "$ENV_FILE" + sed -i 's|^PROXMOX_TOKEN_ID=.*|PROXMOX_TOKEN_ID=user@pam!tokenid|' "$ENV_FILE" + sed -i 's|^PROXMOX_TOKEN_SECRET=.*|PROXMOX_TOKEN_SECRET=YOUR_API_SECRET_HERE|' "$ENV_FILE" + sed -i 's|^PROXMOX_ALLOW_SELF_SIGNED_CERTS=.*|PROXMOX_ALLOW_SELF_SIGNED_CERTS=true|' "$ENV_FILE" + sed -i 's|^PORT=.*|PORT=7655|' "$ENV_FILE" + msg_warning "${ENV_FILE} created with placeholder values. Please edit it with your Proxmox details!" + else + msg_warning "${ENV_FILE} already exists. Skipping default configuration." + fi + chmod 600 "$ENV_FILE" +else + msg_warning "${ENV_EXAMPLE} not found. Skipping environment configuration." +fi + +msg_info "Setting permissions for /opt/pulse-proxmox..." +chown -R ${PULSE_USER}:${PULSE_USER} "/opt/pulse-proxmox" +find "/opt/pulse-proxmox" -type d -exec chmod 755 {} \; +find "/opt/pulse-proxmox" -type f -exec chmod 644 {} \; +chmod 600 "$ENV_FILE" +msg_ok "Set permissions." + +msg_info "Saving installed version information..." +VERSION_TO_SAVE="${LATEST_RELEASE:-$(git rev-parse --short HEAD)}" +echo "${VERSION_TO_SAVE}" >/opt/${APP}_version.txt +msg_ok "Saved version info (${VERSION_TO_SAVE})." + +msg_info "Creating Service" +cat </etc/systemd/system/pulse-monitor.service +[Unit] +Description=Pulse Monitoring Application +After=network.target + +[Service] +Type=simple +User=pulse +Group=pulse +WorkingDirectory=/opt/pulse-proxmox +EnvironmentFile=/opt/pulse-proxmox/.env +ExecStart=/usr/bin/npm run start +Restart=on-failure +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now pulse-monitor.service +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up apt cache..." +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned up." diff --git a/json/pulse.json b/json/pulse.json new file mode 100644 index 0000000..554e6a9 --- /dev/null +++ b/json/pulse.json @@ -0,0 +1,34 @@ +{ + "name": "Pulse", + "slug": "pulse", + "categories": [ + 9 + ], + "date_created": "2024-07-26", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 7655, + "documentation": null, + "website": "https://github.com/rcourtman/Pulse", + "logo": "https://raw.githubusercontent.com/rcourtman/Pulse/main/src/public/logos/pulse-logo-256x256.png", + "description": "A lightweight monitoring application for Proxmox VE that displays real-time status for VMs and containers via a simple web interface.", + "install_methods": [ + { + "type": "default", + "script": "ct/pulse.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} \ No newline at end of file diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 8d19309..c1a01eb 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -99,7 +99,6 @@ update_os() { $STD apk update $STD apk upgrade msg_ok "Updated Container OS" - detect_os } # This function modifies the message of the day (motd) and SSH settings diff --git a/misc/build.func b/misc/build.func index 5842081..431eaf0 100644 --- a/misc/build.func +++ b/misc/build.func @@ -46,8 +46,8 @@ error_handler() { if [[ -n "$CT_ID" ]]; then read -p "Remove this Container? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - pct stop "$CT_ID" &> /dev/null - pct destroy "$CT_ID" &> /dev/null + pct stop "$CT_ID" &>/dev/null + pct destroy "$CT_ID" &>/dev/null msg_ok "Removed this Container" fi fi @@ -244,6 +244,8 @@ base_settings() { SSH_AUTHORIZED_KEY="" TAGS="community-script;" UDHCPC_FIX="" + ENABLE_FUSE=="0" + ENABLE_TUN="0" # Override default settings with variables from ct script CT_TYPE=${var_unprivileged:-$CT_TYPE} @@ -252,6 +254,8 @@ base_settings() { RAM_SIZE=${var_ram:-$RAM_SIZE} VERBOSE=${var_verbose:-$VERBOSE} TAGS="${TAGS}${var_tags:-}" + ENABLE_FUSE="${var_fuse:-$ENABLE_FUSE}" + ENABLE_TUN="${var_tun:-$ENABLE_TUN}" # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts if [ -z "$var_os" ]; then @@ -316,7 +320,7 @@ VERBOSE="${VERBOSE}" TAGS="${TAGS:-none}" VLAN="${VLAN:-none}" MTU="${MTU:-1500}" -GATE="${GATE:-none}" +GATE="${GATE:-}" SD="${SD:-none}" MAC="${MAC:-none}" NS="${NS:-none}" @@ -327,8 +331,8 @@ EOF else echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then - rm -f "$FILEPATH" - cat <"$FILEPATH" + rm -f "$FILEPATH" + cat <"$FILEPATH" # ${NSAPP} Configuration File # Generated on $(date) @@ -347,7 +351,7 @@ VERBOSE="${VERBOSE}" TAGS="${TAGS:-none}" VLAN="${VLAN:-none}" MTU="${MTU:-1500}" -GATE="${GATE:-none}" +GATE="${GATE:-}" SD="${SD:-none}" MAC="${MAC:-none}" NS="${NS:-none}" @@ -498,7 +502,6 @@ advanced_settings() { exit_script fi - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) BRIDGES="" OLD_IFS=$IFS @@ -506,21 +509,21 @@ advanced_settings() { for iface_filepath in ${IFACE_FILEPATH_LIST}; do iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - ( grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1 ) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' > "${iface_indexes_tmpfile}" || true + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi - done < "${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" fi done @@ -779,7 +782,6 @@ EOF } - install_script() { pve_check shell_check @@ -960,6 +962,10 @@ build_container() { FEATURES="nesting=1" fi + if [ "$ENABLE_FUSE" == "1" ]; then + FEATURES="$FEATURES,fuse=1" + fi + if [[ $DIAGNOSTICS == "yes" ]]; then post_to_api fi @@ -984,6 +990,8 @@ build_container() { export SSH_AUTHORIZED_KEY export CTID="$CT_ID" export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" export PCT_OSTYPE="$var_os" export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" @@ -1051,6 +1059,13 @@ EOF fi fi + if [ "$ENABLE_TUN" == "1" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + # This starts the container and executes -install.sh msg_info "Starting LXC Container" pct start "$CTID" @@ -1124,8 +1139,6 @@ EOF systemctl start ping-instances.service fi - - post_update_to_api "done" "none" } diff --git a/misc/config-file.func b/misc/config-file.func index 2f4fe16..d7d2683 100644 --- a/misc/config-file.func +++ b/misc/config-file.func @@ -279,13 +279,86 @@ config_file() { else msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was ${GATE}" exit + fi + + else + while true; do + GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + fi + elif [[ "$NET" == *-* ]]; then + IFS="-" read -r ip_start ip_end <<< "$NET" + + if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then + msg_error "Invalid IP range format, was $NET should be 0.0.0.0/0-0.0.0.0/0" + exit 1 + fi + + ip1="${ip_start%%/*}" + ip2="${ip_end%%/*}" + cidr="${ip_start##*/}" + + ip_to_int() { + local IFS=. + read -r i1 i2 i3 i4 <<< "$1" + echo $(( (i1 << 24) + (i2 << 16) + (i3 << 8) + i4 )) + } + + int_to_ip() { + local ip=$1 + echo "$(( (ip >> 24) & 0xFF )).$(( (ip >> 16) & 0xFF )).$(( (ip >> 8) & 0xFF )).$(( ip & 0xFF ))" + } + + start_int=$(ip_to_int "$ip1") + end_int=$(ip_to_int "$ip2") + + for ((ip_int=start_int; ip_int<=end_int; ip_int++)); do + ip=$(int_to_ip $ip_int) + msg_info "Checking IP: $ip" + if ! ping -c 2 -W 1 "$ip" >/dev/null 2>&1; then + NET="$ip/$cidr" + msg_ok "Using free IP Address: ${BGN}$NET${CL}" + sleep 3 + break + fi + done + if [[ "$NET" == *-* ]]; then + msg_error "No free IP found in range" + exit 1 + fi + if [ -n "$GATE" ]; then + if [[ "$GATE" =~ $ip_regex ]]; then + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE${CL}" + GATE=",gw=$GATE" + else + msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was ${GATE}" + exit + fi + else + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done fi else - msg_error "Gateway IP Address cannot be empty" - exit - fi - else - msg_error "Invalid IP Address format. Needs to be 0.0.0.0/0, was ${NET}" + msg_error "Invalid IP Address format. Needs to be 0.0.0.0/0 or a range like 10.0.0.1/24-10.0.0.10/24, was ${NET}" exit fi else @@ -333,13 +406,8 @@ config_file() { else if [[ -n "${APT_CACHER_IP-}" ]]; then if [[ ! $APT_CACHER_IP == "none" ]]; then - if [[ "$APT_CACHER_IP" =~ $ip_regex ]]; then APT_CACHER="yes" echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" - else - msg_error "Invalid IP Address format for APT-Cacher. Needs to be 0.0.0.0, was ${APT_CACHER_IP}" - exit - fi else APT_CACHER="" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}" @@ -497,7 +565,12 @@ config_file() { fi if [[ -n "${TAGS-}" ]]; then - echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + if [[ "$TAGS" == *"DEFAULT"* ]]; then + TAGS="${TAGS//DEFAULT/}" + TAGS="${TAGS//;/}" + TAGS="$TAGS;${var_tags:-}" + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + fi else TAGS="community-scripts;" if ADV_TAGS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then @@ -565,7 +638,6 @@ config_file() { if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS WITH CONFIG FILE COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above settings${CL}" - write_config else clear header_info diff --git a/misc/core.func b/misc/core.func index fb5937c..ee75e23 100644 --- a/misc/core.func +++ b/misc/core.func @@ -35,10 +35,70 @@ load_functions() { icons default_vars set_std_mode - detect_os # add more } +setup_trap_abort_handling() { + trap '__handle_signal_abort SIGINT' SIGINT + trap '__handle_signal_abort SIGTERM' SIGTERM + trap '__handle_unexpected_error $?' ERR +} + +__handle_signal_abort() { + local signal="$1" + echo + [ -n "${SPINNER_PID:-}" ] && kill "$SPINNER_PID" 2>/dev/null && wait "$SPINNER_PID" 2>/dev/null + + case "$signal" in + SIGINT) + msg_error "Script aborted by user (CTRL+C)" + exit 130 + ;; + SIGTERM) + msg_error "Script terminated (SIGTERM)" + exit 143 + ;; + *) + msg_error "Script interrupted (unknown signal: $signal)" + exit 1 + ;; + esac +} + +__handle_unexpected_error() { + local exit_code="$1" + echo + [ -n "${SPINNER_PID:-}" ] && kill "$SPINNER_PID" 2>/dev/null && wait "$SPINNER_PID" 2>/dev/null + + case "$exit_code" in + 1) + msg_error "Generic error occurred (exit code 1)" + ;; + 2) + msg_error "Misuse of shell builtins (exit code 2)" + ;; + 126) + msg_error "Command invoked cannot execute (exit code 126)" + ;; + 127) + msg_error "Command not found (exit code 127)" + ;; + 128) + msg_error "Invalid exit argument (exit code 128)" + ;; + 130) + msg_error "Script aborted by user (CTRL+C)" + ;; + 143) + msg_error "Script terminated by SIGTERM" + ;; + *) + msg_error "Unexpected error occurred (exit code $exit_code)" + ;; + esac + exit "$exit_code" +} + # ------------------------------------------------------------------------------ # Sets ANSI color codes used for styled terminal output. # ------------------------------------------------------------------------------ @@ -233,14 +293,6 @@ __curl_err_handler() { exit 1 } -detect_os() { - case "$PCT_OSTYPE" in - alpine) CORE_OS="alpine" ;; - debian | ubuntu) CORE_OS="debian" ;; - *) CORE_OS="unknown" ;; - esac -} - fatal() { msg_error "$1" kill -INT $$ @@ -378,3 +430,14 @@ msg_progress() { printf "\n" >&2 fi } + +run_container_safe() { + local ct="$1" + shift + local cmd="$*" + + lxc-attach -n "$ct" -- bash -euo pipefail -c " + trap 'echo Aborted in container; exit 130' SIGINT SIGTERM + $cmd + " || __handle_general_error "lxc-attach to CT $ct" +} diff --git a/misc/install.func b/misc/install.func index 0f23430..8855689 100644 --- a/misc/install.func +++ b/misc/install.func @@ -168,7 +168,6 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - detect_os source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) } diff --git a/misc/tools.func b/misc/tools.func index 4380f29..0534750 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -790,9 +790,13 @@ fetch_and_deploy_gh_release() { local content_root content_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d) if [[ $(echo "$content_root" | wc -l) -eq 1 ]]; then + shopt -s dotglob nullglob cp -r "$content_root"/* "/opt/$app/" + shopt -u dotglob nullglob else + shopt -s dotglob nullglob cp -r "$tmpdir"/* "/opt/$app/" + shopt -u dotglob nullglob fi echo "$version" >"/opt/${app}_version.txt" $STD msg_ok "Deployed $app v$version to /opt/$app" diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh new file mode 100644 index 0000000..e7712e4 --- /dev/null +++ b/tools/pve/update-apps.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: BvdBerg01 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info { + clear + cat <<"EOF" + __ _ ________ __ __ __ __ + / / | |/ / ____/ / / / /___ ____/ /___ _/ /____ + / / | / / / / / / __ \/ __ / __ `/ __/ _ \ + / /___/ / /___ / /_/ / /_/ / /_/ / /_/ / /_/ __/ +/_____/_/|_\____/ \____/ .___/\__,_/\__,_/\__/\___/ + /_/ +EOF +} + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + +header_info +echo "Loading..." +whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit + +NODE=$(hostname) +containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}') + +if [ -z "$containers" ]; then + whiptail --title "LXC Container Update" --msgbox "No LXC containers available!" 10 60 + exit 1 +fi + +menu_items=() +FORMAT="%-10s %-15s %-10s" + +while read -r container; do + container_id=$(echo $container | awk '{print $1}') + container_name=$(echo $container | awk '{print $2}') + container_status=$(echo $container | awk '{print $3}') + formatted_line=$(printf "$FORMAT" "$container_name" "$container_status") + IS_HELPERSCRIPT_LXC=$(pct exec $container_id -- [ -e /usr/bin/update ] && echo true || echo false) + if [ "$IS_HELPERSCRIPT_LXC" = true ]; then + menu_items+=("$container_id" "$formatted_line" "OFF") + fi +done <<< "$containers" + +CHOICE=$(whiptail --title "LXC Container Update" \ + --radiolist "Select LXC container to update:" 25 60 13 \ + "${menu_items[@]}" 3>&2 2>&1 1>&3) + +if [ -z "$CHOICE" ]; then + whiptail --title "LXC Container Update" \ + --msgbox "No containers selected!" 10 60 + exit 1 +fi + +header_info +if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to create a backup from your container?" 10 58); then + + STORAGES=$(awk '/^(\S+):/ {storage=$2} /content.*backup/ {print storage}' /etc/pve/storage.cfg) + + if [ -z "$STORAGES" ]; then + whiptail --msgbox "Geen opslag met 'backup' gevonden!" 8 40 + exit 1 + fi + + MENU_ITEMS=() + for STORAGE in $STORAGES; do + MENU_ITEMS+=("$STORAGE" "") + done + + STORAGE_CHOICE=$(whiptail --title "Select storage device" --menu "Select a storage device (Only storage devices with 'backup' support are listed):" 15 50 5 "${MENU_ITEMS[@]}" 3>&1 1>&2 2>&3) + + if [ -z "$STORAGE_CHOICE" ]; then + msg_error "No storage selected!" + exit 1 + fi + + msg_info "Creating backup" + vzdump $CHOICE --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" > /dev/null 2>&1 + status=$? + + if [ $status -eq 0 ]; then + msg_ok "Backup created" + pct exec $CHOICE -- update --from-pve + exit_code=$? + else + msg_error "Backup failed" + fi + +else + pct exec $CHOICE -- update --from-pve + exit_code=$? +fi + +if [ $exit_code -eq 0 ]; then + msg_ok "Update completed" +else + msg_info "Restoring LXC from backup" + pct stop $CHOICE + LXC_STORAGE=$(pct config $CHOICE | awk -F '[:,]' '/rootfs/ {print $2}') + pct restore $CHOICE /var/lib/vz/dump/vzdump-lxc-$CHOICE-*.tar.zst --storage $LXC_STORAGE --force > /dev/null 2>&1 + pct start $CHOICE + restorestatus=$? + if [ $restorestatus -eq 0 ]; then + msg_ok "Restored LXC from backup" + else + msg_error "Restored LXC from backup failed" + fi + +fi