From 0cd7eed2763464fb84fa4852ecd3858c8b22ff72 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:46:35 +0000 Subject: [PATCH 001/228] initial commit --- frontend/public/json/truenas-vm.json | 56 +++ vm/truenas-vm.sh | 592 +++++++++++++++++++++++++++ 2 files changed, 648 insertions(+) create mode 100644 frontend/public/json/truenas-vm.json create mode 100644 vm/truenas-vm.sh diff --git a/frontend/public/json/truenas-vm.json b/frontend/public/json/truenas-vm.json new file mode 100644 index 000000000..d4675b55f --- /dev/null +++ b/frontend/public/json/truenas-vm.json @@ -0,0 +1,56 @@ +{ + "name": "TrueNAS Community Edition", + "slug": "truenas-community-edition", + "categories": [ + 2 + ], + "date_created": "2026-01-16", + "type": "vm", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://www.truenas.com/docs/", + "website": "https://www.truenas.com/truenas-community-edition/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/truenas-core.webp", + "config_path": "", + "description": "TrueNAS Community Edition is the world's most deployed storage software. Free, flexible and build on OpenZFS with Docker.", + "install_methods": [ + { + "type": "default", + "script": "vm/truenas-vm.sh", + "resources": { + "cpu": 2, + "ram": 8192, + "hdd": 16, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The default install uses the latest stable release. Please use advanced install for more options.", + "type": "info" + }, + { + "text": "8 GB of RAM is the minimum requirement, but 16 GB+ is recommended for optimal performance.", + "type": "info" + }, + { + "text": "After installation, you will be prompted to unmount the media. To do this: Go to the VM's Hardware tab > select the CD/DVD Drive > Edit > select 'Do not use any media'", + "type": "info" + }, + { + "text": "While you can import onboard disks during install, it is highly recommended to use an HBA to pass through disks for production environments.", + "type": "warning" + }, + { + "text": "ECC RAM is strongly recommended to ensure data integrity, as ZFS checksumming can be compromised by bad data in RAM before the data is written to the pool.", + "type": "warning" + } + ] +} diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh new file mode 100644 index 000000000..ab796ee77 --- /dev/null +++ b/vm/truenas-vm.sh @@ -0,0 +1,592 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: juronja +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) + +function header_info() { + clear + cat <<"EOF" + ______ _ _____ _____ + /_ __/______ _____ / | / / | / ___/ + / / / ___/ / / / _ \/ |/ / /| | \__ \ + / / / / / /_/ / __/ /| / ___ |___/ / +/_/ /_/ \__,_/\___/_/ |_/_/ |_/____/ + (Community Edition) +EOF +} +header_info +echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" + +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") +BFR="\\r\\033[K" +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +ISO="${TAB}📀${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DISK="${TAB}💽${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM +function error_handler() { + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid +} + +function truenas_iso_lookup() { + local BASE_URL="https://download.truenas.com" + local current_year=$(date +%y) + local last_year=$(date -d "1 year ago" +%y) + local year_pattern="${current_year}\.|${last_year}\." + + declare -A latest_stables + local pre_releases=() + + local all_paths=$( + curl -sL "$BASE_URL" | + grep -oE 'href="[^"]+\.iso"' | + sed 's/href="//; s/"$//' | + grep -vE '(nightly|ALPHA)' | + grep -E "$year_pattern" + ) + + while read -r path; do + local filename=$(basename "$path") + local version=$(echo "$filename" | sed -E 's/.*TrueNAS-SCALE-([0-9]{2}\.[0-9]{2}(\.[0-9]+)*(-RC[0-9]|-BETA[0-9])?)\.iso.*/\1/') + if [[ "$version" =~ (RC|BETA) ]]; then + pre_releases+=("$path") + else + local major_version=$(echo "$version" | cut -d'.' -f1,2) + local current_stored_path=${latest_stables["$major_version"]} + if [[ -z "$current_stored_path" ]]; then + latest_stables["$major_version"]="$path" + else + local stored_version=$(basename "$current_stored_path" | sed -E 's/.*TrueNAS-SCALE-([0-9]{2}\.[0-9]{2}(\.[0-9]+)*)\.iso.*/\1/') + if printf '%s\n' "$version" "$stored_version" | sort -V | tail -n 1 | grep -q "$version"; then + latest_stables["$major_version"]="$path" + fi + fi + fi + done <<<"$all_paths" + + for key in "${!latest_stables[@]}"; do + echo "${latest_stables[$key]#/}" + done + + for pre in "${pre_releases[@]}"; do + echo "${pre#/}" + done | sort -V +} + +function get_valid_nextid() { + local try_id + try_id=$(pvesh get /cluster/nextid) + while true; do + if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then + try_id=$((try_id + 1)) + continue + fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then + try_id=$((try_id + 1)) + continue + fi + break + done + echo "$try_id" +} + +function cleanup_vmid() { + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null + fi +} + +function cleanup() { + popd >/dev/null + post_update_to_api "done" "none" + rm -rf $TEMP_DIR +} + +TEMP_DIR=$(mktemp -d) +pushd $TEMP_DIR >/dev/null +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "TrueNAS VM" --yesno "This will create a New TrueNAS VM. Proceed?" 10 58; then + : +else + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit +fi + +function msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. +# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 – 9.1 +pve_check() { + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + # Check for Proxmox VE 8.x: allow 8.0–8.9 + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 0 || MINOR > 9)); then + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported: Proxmox VE version 8.0 – 8.9" + exit 1 + fi + return 0 + fi + + # Check for Proxmox VE 9.x: allow 9.0–9.1 + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 0 || MINOR > 1)); then + msg_error "This version of Proxmox VE is not yet supported." + msg_error "Supported: Proxmox VE version 9.0 – 9.1" + exit 1 + fi + return 0 + fi + + # All other unsupported versions + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" + exit 1 +} + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi +} + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +function default_settings() { + VMID=$(get_valid_nextid) + ISO_DEFAULT="latest stable" + FORMAT="" + MACHINE="q35" + DISK_SIZE="16" + HN="truenas" + CPU_TYPE="host" + CORE_COUNT="2" + RAM_SIZE="8192" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${ISO}${BOLD}${DGN}ISO Chosen: ${BGN}${ISO_DEFAULT}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}${CPU_TYPE}${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a TrueNAS VM using the above default settings${CL}" +} + +function advanced_settings() { + METHOD="advanced" + [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_nextid) + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit-script + fi + done + + # ISO lookup + ISOARRAY=() + while read -r ISOPATH; do + FILENAME=$(basename "$ISOPATH") + ISOARRAY+=("$ISOPATH" "$FILENAME" "OFF") + done < <(truenas_iso_lookup | sort -V) + if [ ${#ISOARRAY[@]} -eq 0 ]; then + echo "No ISOs found." + exit 1 + fi + + if SELECTED_ISO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT ISO TO INSTALL" --notags --radiolist "\nSelect version (BETA/RC + Latest stables):" 20 58 12 "${ISOARRAY[@]}" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + echo -e "${ISO}${BOLD}${DGN}ISO Chosen: ${BGN}$(basename "$SELECTED_ISO")${CL}" + else + exit-script + fi + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10).${CL}" + exit-script + fi + else + exit-script + fi + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 truenas --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="truenas" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + fi + else + exit-script + fi + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose CPU Model" --cancel-button Exit-Script 10 58 2 \ + "KVM64" "Default – safe for migration/compatibility" OFF \ + "Host" "Use host CPU features (faster, no migration)" ON \ + 3>&1 1>&2 2>&3); then + case "$CPU_TYPE1" in + Host) + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE="host" + ;; + *) + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + ;; + esac + else + exit-script + fi + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi + else + exit-script + fi + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 8192 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="8192" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + fi + else + exit-script + fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + else + exit-script + fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit-script + fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VLAN1 ]; then + VLAN1="Default" + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + else + VLAN=",tag=$VLAN1" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + fi + else + exit-script + fi + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MTU1 ]; then + MTU1="Default" + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + MTU=",mtu=$MTU1" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + fi + else + exit-script + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "IMPORT ONBOARD DISKS" --yesno "Would you like to import onboard disks?" 10 58); then + echo -e "${DISK}${BOLD}${DGN}Import onboard disks: ${BGN}yes${CL}" + IMPORT_DISKS="yes" + else + echo -e "${DISK}${BOLD}${DGN}Import onboard disks: ${BGN}no${CL}" + IMPORT_DISKS="no" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + START_VM="yes" + else + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" + START_VM="no" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a TrueNAS VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a TrueNAS VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} +check_root +arch_check +pve_check +ssh_check +start_script +post_to_api_vm + +msg_info "Validating Storage" +while read -r line; do + TAG=$(echo $line | awk '{print $1}') + TYPE=$(echo $line | awk '{printf "%-10s", $2}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") +done < <(pvesm status -content images | awk 'NR>1') +VALID=$(pvesm status -content images | awk 'NR>1') +if [ -z "$VALID" ]; then + msg_error "Unable to detect a valid storage location." + exit +elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + STORAGE=${STORAGE_MENU[0]} +else + while [ -z "${STORAGE:+x}" ]; do + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + printf "\e[?25h" + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + done +fi +msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." +msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." + +if [ -z "${SELECTED_ISO:-}" ]; then + # Fallback: Find the latest stable release only (excluding RC/BETA for safety) + SELECTED_ISO=$(truenas_iso_lookup | grep -vE 'RC|BETA' | sort -V | tail -n 1) + + if [ -z "$SELECTED_ISO" ]; then + msg_error "Could not find a stable ISO for fallback." + exit 1 + fi +fi + +FULL_URL="https://download.truenas.com/${SELECTED_ISO#/}" +ISO_NAME=$(basename "$FULL_URL") +CACHE_DIR="/var/lib/vz/template/iso" +CACHE_FILE="$CACHE_DIR/$ISO_NAME" + +if [[ ! -s "$CACHE_FILE" ]]; then + msg_info "Retrieving the ISO for the TrueNAS Disk Image" + curl -f#SL -o "$CACHE_FILE" "$FULL_URL" + msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}" +else + msg_ok "Using cached image ${CL}${BL}$(basename "$CACHE_FILE")${CL}" +fi + +set -o pipefail +msg_info "Creating TrueNAS VM shell" +qm create "$VMID" -machine q35 -bios ovmf -agent enabled=1 -tablet 0 -localtime 1 -cpu "$CPU_TYPE" \ + -cores "$CORE_COUNT" -memory "$RAM_SIZE" -balloon 0 -name "$HN" -tags community-script \ + -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 \ + -efidisk0 $STORAGE:1,efitype=4m,pre-enrolled-keys=0 -scsi0 $STORAGE:$DISK_SIZE,ssd=1,iothread=on \ + -scsihw virtio-scsi-single -cdrom local:iso/$ISO_NAME -vga virtio >/dev/null +msg_ok "Created VM shell" + +if [ "$IMPORT_DISKS" == "yes" ]; then + msg_info "Importing onboard disks" + DISKARRAY=() + SCSI_NR=0 + + while read -r LSOUTPUT; do + DISKARRAY+=("$LSOUTPUT" "" "OFF") + done < <(ls /dev/disk/by-id | grep -E '^ata-|^nvme-' | grep -v 'part') + + SELECTIONS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT DISKS TO IMPORT" --checklist "\nSelect disk IDs to import. (Use Spacebar to select)\n" --cancel-button "Exit Script" 20 58 10 "${DISKARRAY[@]}" 3>&1 1>&2 2>&3 | tr -d '"') || exit + + for SELECTION in $SELECTIONS; do + ((++SCSI_NR)) + qm set $VMID --scsi$SCSI_NR /dev/disk/by-id/$SELECTION + done + msg_ok "Disks imported successfully" +fi + +DESCRIPTION=$( + cat < + + Logo + + +

TrueNAS VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null + +msg_ok "Created a TrueNAS VM ${CL}${BL}(${HN})" +if [ "$START_VM" == "yes" ]; then + msg_info "Starting TrueNAS VM" + qm start $VMID + msg_ok "Started TrueNAS VM" +fi + +msg_ok "Completed Successfully! Go to VM console and start installation process.\n" From 9872ea39f3824f0f996ab9882c33f23ec6d2b3a8 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:52:19 +0000 Subject: [PATCH 002/228] api func source changed to VED --- vm/truenas-vm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index ab796ee77..5808b070e 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -4,7 +4,7 @@ # Author: juronja # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) +source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) function header_info() { clear @@ -262,7 +262,7 @@ function default_settings() { METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${ISO}${BOLD}${DGN}ISO Chosen: ${BGN}${ISO_DEFAULT}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}${MACHINE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}${CPU_TYPE}${CL}" From e748ad5a6d7d1d9da5d65bf48cf1f79b1737a8b2 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:02:20 +0000 Subject: [PATCH 003/228] added sleep for disk pickup --- vm/truenas-vm.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index 5808b070e..453979eac 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -582,6 +582,8 @@ EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null +sleep 5 + msg_ok "Created a TrueNAS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting TrueNAS VM" From 42b192272db5fe2686db0b894f6c8a75a0004a17 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:19:00 +0000 Subject: [PATCH 004/228] testing with 3 sec sleep --- vm/truenas-vm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index 453979eac..cdbc789b4 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -557,7 +557,7 @@ DESCRIPTION=$( Logo -

TrueNAS VM

+

TrueNAS Community Edition

@@ -582,7 +582,7 @@ EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null -sleep 5 +sleep 3 msg_ok "Created a TrueNAS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then From d979edecf1d7f5bcfc16cbe9ca7111cefc378d68 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:16:21 +0000 Subject: [PATCH 005/228] Fetching iso list msg --- vm/truenas-vm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index cdbc789b4..7f46ce73d 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -296,7 +296,7 @@ function advanced_settings() { fi done - # ISO lookup + msg_info "Fetching iso list from https://download.truenas.com/" ISOARRAY=() while read -r ISOPATH; do FILENAME=$(basename "$ISOPATH") @@ -591,4 +591,4 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started TrueNAS VM" fi -msg_ok "Completed Successfully! Go to VM console and start installation process.\n" +msg_ok "Completed Successfully!\n" From 773d588232229d01c256171b3d10e88b788de2d7 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:26:25 +0000 Subject: [PATCH 006/228] removed info message --- vm/truenas-vm.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index 7f46ce73d..675a4de7a 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -296,7 +296,6 @@ function advanced_settings() { fi done - msg_info "Fetching iso list from https://download.truenas.com/" ISOARRAY=() while read -r ISOPATH; do FILENAME=$(basename "$ISOPATH") From fabeb9f0d6897c35a5fd55ba86f2e98ff1987a67 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:34:48 +0000 Subject: [PATCH 007/228] added comments for custom code specific to truenas --- vm/truenas-vm.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index 675a4de7a..b81525ee2 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -73,6 +73,9 @@ function error_handler() { cleanup_vmid } +# Scrapes the TrueNAS download portal for ISO paths from the current and previous year, +# filtering out nightlies/alphas and returning the latest stable releases for each major +# version along with any beta or RC pre-releases. function truenas_iso_lookup() { local BASE_URL="https://download.truenas.com" local current_year=$(date +%y) @@ -296,6 +299,7 @@ function advanced_settings() { fi done + # Fetching iso list from TrueNAS downloads for whiptail radiolist ISOARRAY=() while read -r ISOPATH; do FILENAME=$(basename "$ISOPATH") @@ -531,6 +535,7 @@ qm create "$VMID" -machine q35 -bios ovmf -agent enabled=1 -tablet 0 -localtime -scsihw virtio-scsi-single -cdrom local:iso/$ISO_NAME -vga virtio >/dev/null msg_ok "Created VM shell" +# Optional step to import onboard disks if [ "$IMPORT_DISKS" == "yes" ]; then msg_info "Importing onboard disks" DISKARRAY=() From f04946cae8e63238db8d195de3cf9a6295c0bd60 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:20:18 +0100 Subject: [PATCH 008/228] Adding source in file header Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- vm/truenas-vm.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index b81525ee2..54b5652f9 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -3,6 +3,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: juronja # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.truenas.com/truenas-community-edition/ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) From 56b6dcd045e1489ad8381f5db583e15bf0f8ddaa Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 04:38:43 +0100 Subject: [PATCH 009/228] feat: Add Nightscout Proxmox CT template, installation scripts, and application metadata. --- ct/nightscout.sh | 50 +++++++++++++++++++ frontend/public/json/nightscout.json | 48 ++++++++++++++++++ install/nightscout-install.sh | 75 ++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 ct/nightscout.sh create mode 100644 frontend/public/json/nightscout.json create mode 100644 install/nightscout-install.sh diff --git a/ct/nightscout.sh b/ct/nightscout.sh new file mode 100644 index 000000000..c88f61861 --- /dev/null +++ b/ct/nightscout.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: aendel +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/nightscout/cgm-remote-monitor + +APP="Nightscout" +var_tags="${var_tags:-arr;health}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +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/nightscout ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating ${APP}" + systemctl stop nightscout + cd /opt/nightscout || exit + git pull + npm install + systemctl start nightscout + msg_ok "Updated ${APP}" + 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}:1337${CL}" +echo -e "${INFO}${YW} Configuration:${CL}" +echo -e "${TAB} Edit /opt/nightscout/my.env to configure your CGM source (Dexcom/CareLink)" +echo -e "${TAB} Then run: ${BGN}systemctl restart nightscout${CL}" diff --git a/frontend/public/json/nightscout.json b/frontend/public/json/nightscout.json new file mode 100644 index 000000000..a2c820352 --- /dev/null +++ b/frontend/public/json/nightscout.json @@ -0,0 +1,48 @@ +{ + "name": "Nightscout", + "slug": "nightscout", + "categories": [ + 0 + ], + "date_created": "2025-01-17", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 1337, + "documentation": "https://nightscout.github.io/", + "website": "http://www.nightscout.info/", + "logo": "https://raw.githubusercontent.com/nightscout/cgm-remote-monitor/master/static/images/large.png", + "config_path": "/opt/nightscout/my.env", + "description": "Nightscout is an open source, DIY project that allows real time access to a CGM data via personal website, smartwatch watchers, or apps and widgets available for smartphones.", + "install_methods": [ + { + "type": "default", + "script": "ct/nightscout.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 10, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Nightscout requires configuring `my.env` with your Mongo connection string and API_SECRET. Default API_SECRET is `yoursecret123`.", + "type": "info" + }, + { + "text": "Official Configuration Guide: [Nightscout Documentation](https://nightscout.github.io/nightscout/setup_variables/)", + "type": "link" + }, + { + "text": "Nightscout requires HTTPS for many features (security, tokens, PWA). Usage of a reverse proxy (e.g. Nginx Proxy Manager) is highly recommended.", + "type": "warning" + } + ] +} diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh new file mode 100644 index 000000000..4d64de7b2 --- /dev/null +++ b/install/nightscout-install.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2026 community-scripts ORG +# Author: aendel +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/nightscout/cgm-remote-monitor + +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 curl build-essential libssl-dev +msg_ok "Installed Dependencies" + +MONGO_VERSION="8.0" setup_mongodb +NODE_VERSION="22" setup_nodejs + +msg_info "Installing Nightscout (Patience)" +cd /opt || exit +git clone https://github.com/nightscout/cgm-remote-monitor.git nightscout +cd nightscout || exit +$STD npm install +msg_ok "Installed Nightscout" + +msg_info "Creating Service" +useradd -s /bin/bash -m nightscout +chown -R nightscout:nightscout /opt/nightscout + +# Create a default my.env file if it doesn't exist, to prevent crash on start if user doesn't configure it immediately? +# Nightscout needs env vars to run. We will create a template. +cat < /opt/nightscout/my.env +# DB connection string. +# MongoDB is installed locally on port 27017. +# You should create a DB and user in Mongo, or trust localhost auth. +# For simplicity in this script we assume localhost with no auth for local binding or the user must configure it. +# However, the installation guide recommends creating a user. +# For now, we point to localhost test DB. +MONGO_CONNECTION=mongodb://127.0.0.1:27017/nightscout +BASE_URL=http://localhost:1337 +API_SECRET=yoursecret123 +DISPLAY_UNITS=mg/dl +ENABLE=careportal boluscalc food bwp cage sage iage iob cob basal ar2 rawbg pushover bgi pump openaps pvb linear custom +# Allow HTTP (avoids redirect loops with reverse proxies) +INSECURE_USE_HTTP=true +EOF +chown nightscout:nightscout /opt/nightscout/my.env + +cat </etc/systemd/system/nightscout.service +[Unit] +Description=Nightscout CGM Service +After=network.target mongodb.service + +[Service] +Type=simple +User=nightscout +WorkingDirectory=/opt/nightscout +EnvironmentFile=/opt/nightscout/my.env +# Some deployments use 'npm start', others 'node server.js'. npm start is safer. +ExecStart=/usr/bin/npm start +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable -q --now nightscout +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From f23c0b72fcd4181d9fc8cb5fcc7ca5748fd787e8 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 06:18:02 +0100 Subject: [PATCH 010/228] fix: Update repository name in URLs from ProxmoxVE to ProxmoxVED. --- ct/nightscout.sh | 4 ++-- install/nightscout-install.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/nightscout.sh b/ct/nightscout.sh index c88f61861..0109fc600 100644 --- a/ct/nightscout.sh +++ b/ct/nightscout.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: aendel -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/nightscout/cgm-remote-monitor APP="Nightscout" diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 4d64de7b2..bf204307e 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: aendel -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/nightscout/cgm-remote-monitor source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From e072924062ce8a7551c4416254433905f0c2bd3d Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 06:21:08 +0100 Subject: [PATCH 011/228] Update install/nightscout-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/nightscout-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index bf204307e..e46f2eb16 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -59,7 +59,7 @@ Type=simple User=nightscout WorkingDirectory=/opt/nightscout EnvironmentFile=/opt/nightscout/my.env -# Some deployments use 'npm start', others 'node server.js'. npm start is safer. +ExecStart=/usr/bin/npm start ExecStart=/usr/bin/npm start Restart=always From c7d2c8fed0f07475ddf6cc46f11a85c5049c0297 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 06:21:32 +0100 Subject: [PATCH 012/228] Update install/nightscout-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/nightscout-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index e46f2eb16..30559445f 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -22,7 +22,7 @@ NODE_VERSION="22" setup_nodejs msg_info "Installing Nightscout (Patience)" cd /opt || exit git clone https://github.com/nightscout/cgm-remote-monitor.git nightscout -cd nightscout || exit +cd nightscout $STD npm install msg_ok "Installed Nightscout" From fb1fbf0a6e19d2f31d96e99a19f02db1bbc23140 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 06:21:48 +0100 Subject: [PATCH 013/228] Update ct/nightscout.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- ct/nightscout.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/nightscout.sh b/ct/nightscout.sh index 0109fc600..105a9fbd8 100644 --- a/ct/nightscout.sh +++ b/ct/nightscout.sh @@ -29,7 +29,7 @@ function update_script() { fi msg_info "Updating ${APP}" systemctl stop nightscout - cd /opt/nightscout || exit + cd /opt/nightscout git pull npm install systemctl start nightscout From f66487cbb818aa57f634bd37d5efe9f9eba05d85 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 06:23:09 +0100 Subject: [PATCH 014/228] Update install/nightscout-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/nightscout-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 30559445f..c231219e5 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -13,7 +13,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y git curl build-essential libssl-dev +$STD apt install -y git build-essential libssl-dev msg_ok "Installed Dependencies" MONGO_VERSION="8.0" setup_mongodb From 61400266fca2211450a75f4628a0eb260611e23a Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 06:23:24 +0100 Subject: [PATCH 015/228] Update install/nightscout-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/nightscout-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index c231219e5..59a7ec211 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -20,7 +20,7 @@ MONGO_VERSION="8.0" setup_mongodb NODE_VERSION="22" setup_nodejs msg_info "Installing Nightscout (Patience)" -cd /opt || exit +cd /opt git clone https://github.com/nightscout/cgm-remote-monitor.git nightscout cd nightscout $STD npm install From ba78a10250d92a0888cae94e6cc88dfc93139ebd Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 06:34:50 +0100 Subject: [PATCH 016/228] refactor: streamline Nightscout installation using a release helper, simplify the default environment file --- install/nightscout-install.sh | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 59a7ec211..0267fcdc4 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -20,36 +20,25 @@ MONGO_VERSION="8.0" setup_mongodb NODE_VERSION="22" setup_nodejs msg_info "Installing Nightscout (Patience)" -cd /opt -git clone https://github.com/nightscout/cgm-remote-monitor.git nightscout -cd nightscout -$STD npm install +fetch_and_deploy_gh_release "nightscout" "nightscout/cgm-remote-monitor" "source" +$STD npm install --prefix /opt/nightscout msg_ok "Installed Nightscout" msg_info "Creating Service" useradd -s /bin/bash -m nightscout chown -R nightscout:nightscout /opt/nightscout -# Create a default my.env file if it doesn't exist, to prevent crash on start if user doesn't configure it immediately? -# Nightscout needs env vars to run. We will create a template. -cat < /opt/nightscout/my.env -# DB connection string. -# MongoDB is installed locally on port 27017. -# You should create a DB and user in Mongo, or trust localhost auth. -# For simplicity in this script we assume localhost with no auth for local binding or the user must configure it. -# However, the installation guide recommends creating a user. -# For now, we point to localhost test DB. +cat > /opt/nightscout/my.env </etc/systemd/system/nightscout.service +cat > /etc/systemd/system/nightscout.service < Date: Sun, 18 Jan 2026 13:28:14 +0100 Subject: [PATCH 017/228] Update install/nightscout-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/nightscout-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 0267fcdc4..111fcb62c 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -54,7 +54,6 @@ Restart=always [Install] WantedBy=multi-user.target EOF - systemctl enable -q --now nightscout msg_ok "Created Service" From f9d3ecec6acd6bf869ad6c68d02b26f7d939f754 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 14:43:15 +0100 Subject: [PATCH 018/228] feat: Refactor Nightscout update logic to use `fetch_and_deploy_gh_release`, streamline dependency installation, and update post-install messages and CT tags. --- ct/nightscout.sh | 46 ++++++++++++++++------------ frontend/public/json/nightscout.json | 8 +++-- install/nightscout-install.sh | 12 +++++--- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/ct/nightscout.sh b/ct/nightscout.sh index 105a9fbd8..d2a934f02 100644 --- a/ct/nightscout.sh +++ b/ct/nightscout.sh @@ -6,7 +6,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ # Source: https://github.com/nightscout/cgm-remote-monitor APP="Nightscout" -var_tags="${var_tags:-arr;health}" +var_tags="${var_tags:-health}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" @@ -20,31 +20,39 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/nightscout ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating ${APP}" - systemctl stop nightscout - cd /opt/nightscout - git pull - npm install - systemctl start nightscout - msg_ok "Updated ${APP}" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/nightscout ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + if check_for_gh_release "nightscout" "nightscout/cgm-remote-monitor"; then + msg_info "Stopping Service" + systemctl stop nightscout + msg_ok "Stopped Service" + + fetch_and_deploy_gh_release "nightscout" "nightscout/cgm-remote-monitor" "source" + + msg_info "Updating ${APP}" + cd /opt/nightscout + $STD npm install + msg_ok "Updated ${APP}" + + msg_info "Starting Service" + systemctl start nightscout + msg_ok "Started Service" + msg_ok "Updated successfully!" + fi + exit } start build_container description -msg_ok "Completed Successfully!\n" +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}:1337${CL}" -echo -e "${INFO}${YW} Configuration:${CL}" -echo -e "${TAB} Edit /opt/nightscout/my.env to configure your CGM source (Dexcom/CareLink)" -echo -e "${TAB} Then run: ${BGN}systemctl restart nightscout${CL}" diff --git a/frontend/public/json/nightscout.json b/frontend/public/json/nightscout.json index a2c820352..a6f72d28e 100644 --- a/frontend/public/json/nightscout.json +++ b/frontend/public/json/nightscout.json @@ -36,13 +36,17 @@ "text": "Nightscout requires configuring `my.env` with your Mongo connection string and API_SECRET. Default API_SECRET is `yoursecret123`.", "type": "info" }, + { + "text": "Edit `/opt/nightscout/my.env` to configure your CGM source (Dexcom/CareLink). Then run: `systemctl restart nightscout`", + "type": "info" + }, { "text": "Official Configuration Guide: [Nightscout Documentation](https://nightscout.github.io/nightscout/setup_variables/)", - "type": "link" + "type": "info" }, { "text": "Nightscout requires HTTPS for many features (security, tokens, PWA). Usage of a reverse proxy (e.g. Nginx Proxy Manager) is highly recommended.", "type": "warning" } ] -} +} \ No newline at end of file diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 111fcb62c..f04dc7593 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -13,14 +13,18 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y git build-essential libssl-dev +$STD apt install -y \ + git \ + build-essential \ + libssl-dev msg_ok "Installed Dependencies" MONGO_VERSION="8.0" setup_mongodb NODE_VERSION="22" setup_nodejs -msg_info "Installing Nightscout (Patience)" fetch_and_deploy_gh_release "nightscout" "nightscout/cgm-remote-monitor" "source" + +msg_info "Installing Nightscout" $STD npm install --prefix /opt/nightscout msg_ok "Installed Nightscout" @@ -28,7 +32,7 @@ msg_info "Creating Service" useradd -s /bin/bash -m nightscout chown -R nightscout:nightscout /opt/nightscout -cat > /opt/nightscout/my.env </opt/nightscout/my.env MONGO_CONNECTION=mongodb://127.0.0.1:27017/nightscout BASE_URL=http://localhost:1337 API_SECRET=yoursecret123 @@ -38,7 +42,7 @@ INSECURE_USE_HTTP=true EOF chown nightscout:nightscout /opt/nightscout/my.env -cat > /etc/systemd/system/nightscout.service </etc/systemd/system/nightscout.service [Unit] Description=Nightscout CGM Service After=network.target mongodb.service From cd06afd1d015a8af9202746db2dd1aa170edade4 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Sun, 18 Jan 2026 15:44:05 +0100 Subject: [PATCH 019/228] docs: update Nightscout reverse proxy recommendations to include Traefik and Caddy. --- frontend/public/json/nightscout.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/nightscout.json b/frontend/public/json/nightscout.json index a6f72d28e..f4030da4a 100644 --- a/frontend/public/json/nightscout.json +++ b/frontend/public/json/nightscout.json @@ -45,7 +45,7 @@ "type": "info" }, { - "text": "Nightscout requires HTTPS for many features (security, tokens, PWA). Usage of a reverse proxy (e.g. Nginx Proxy Manager) is highly recommended.", + "text": "Nightscout requires HTTPS for many features (security, tokens, PWA). Usage of a reverse proxy (e.g. Nginx Proxy Manager, Traefik, Caddy) is highly recommended.", "type": "warning" } ] From f56fefc0a6756c17eb0dd615f1b7e70de679ea16 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Mon, 19 Jan 2026 23:10:42 +0100 Subject: [PATCH 020/228] Update install/nightscout-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/nightscout-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index f04dc7593..250672220 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -21,7 +21,6 @@ msg_ok "Installed Dependencies" MONGO_VERSION="8.0" setup_mongodb NODE_VERSION="22" setup_nodejs - fetch_and_deploy_gh_release "nightscout" "nightscout/cgm-remote-monitor" "source" msg_info "Installing Nightscout" From 3a6790f7c0a179912eb6194d9d249b7d93140349 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Mon, 19 Jan 2026 23:11:53 +0100 Subject: [PATCH 021/228] chore: Update default OS version to 13. --- ct/nightscout.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/nightscout.sh b/ct/nightscout.sh index d2a934f02..96a148118 100644 --- a/ct/nightscout.sh +++ b/ct/nightscout.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From 4051c50e92361ca07f54284e8fbce6b7cda5b064 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Mon, 19 Jan 2026 23:24:51 +0100 Subject: [PATCH 022/228] Update install/nightscout-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/nightscout-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 250672220..8242f9efc 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -30,7 +30,6 @@ msg_ok "Installed Nightscout" msg_info "Creating Service" useradd -s /bin/bash -m nightscout chown -R nightscout:nightscout /opt/nightscout - cat </opt/nightscout/my.env MONGO_CONNECTION=mongodb://127.0.0.1:27017/nightscout BASE_URL=http://localhost:1337 From 24c2fb2613c21f25b8309d896fc9f09453b87ad2 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Mon, 19 Jan 2026 23:24:59 +0100 Subject: [PATCH 023/228] Update install/nightscout-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/nightscout-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 8242f9efc..98ea6c857 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -39,7 +39,6 @@ ENABLE=careportal boluscalc food bwp cage sage iage iob cob basal ar2 rawbg push INSECURE_USE_HTTP=true EOF chown nightscout:nightscout /opt/nightscout/my.env - cat </etc/systemd/system/nightscout.service [Unit] Description=Nightscout CGM Service From 8d9ef8b17dae6db5e30dfed5620d52ccc9a192f9 Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Mon, 19 Jan 2026 23:29:45 +0100 Subject: [PATCH 024/228] feat: Generate a random Nightscout API_SECRET during installation, save it to `~/nightscout.creds`, and update related documentation. --- frontend/public/json/nightscout.json | 4 ++-- install/nightscout-install.sh | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/public/json/nightscout.json b/frontend/public/json/nightscout.json index f4030da4a..05116582e 100644 --- a/frontend/public/json/nightscout.json +++ b/frontend/public/json/nightscout.json @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "Nightscout requires configuring `my.env` with your Mongo connection string and API_SECRET. Default API_SECRET is `yoursecret123`.", + "text": "Nightscout requires configuring `my.env` with your Mongo connection string. API_SECRET has been generated and saved to `~/nightscout.creds`.", "type": "info" }, { @@ -41,7 +41,7 @@ "type": "info" }, { - "text": "Official Configuration Guide: [Nightscout Documentation](https://nightscout.github.io/nightscout/setup_variables/)", + "text": "Official Configuration Guide: https://nightscout.github.io/nightscout/setup_variables/", "type": "info" }, { diff --git a/install/nightscout-install.sh b/install/nightscout-install.sh index 98ea6c857..b8a1baa1c 100644 --- a/install/nightscout-install.sh +++ b/install/nightscout-install.sh @@ -14,9 +14,9 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - git \ build-essential \ - libssl-dev + libssl-dev \ + openssl msg_ok "Installed Dependencies" MONGO_VERSION="8.0" setup_mongodb @@ -30,10 +30,11 @@ msg_ok "Installed Nightscout" msg_info "Creating Service" useradd -s /bin/bash -m nightscout chown -R nightscout:nightscout /opt/nightscout +API_SECRET=$(openssl rand -hex 16) cat </opt/nightscout/my.env MONGO_CONNECTION=mongodb://127.0.0.1:27017/nightscout BASE_URL=http://localhost:1337 -API_SECRET=yoursecret123 +API_SECRET=${API_SECRET} DISPLAY_UNITS=mg/dl ENABLE=careportal boluscalc food bwp cage sage iage iob cob basal ar2 rawbg pushover bgi pump openaps pvb linear custom INSECURE_USE_HTTP=true @@ -58,6 +59,11 @@ EOF systemctl enable -q --now nightscout msg_ok "Created Service" +{ + echo "Nightscout Credentials" + echo "API_SECRET: ${API_SECRET}" +} >> ~/nightscout.creds + motd_ssh customize cleanup_lxc From 5ef45368c48ac57cc21251acb1a0e9095898824e Mon Sep 17 00:00:00 2001 From: Francesco Vattiato Date: Tue, 20 Jan 2026 14:16:09 +0100 Subject: [PATCH 025/228] Update ct/nightscout.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- ct/nightscout.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/nightscout.sh b/ct/nightscout.sh index 96a148118..227342f2c 100644 --- a/ct/nightscout.sh +++ b/ct/nightscout.sh @@ -35,10 +35,10 @@ function update_script() { fetch_and_deploy_gh_release "nightscout" "nightscout/cgm-remote-monitor" "source" - msg_info "Updating ${APP}" + msg_info "Updating Nightscout" cd /opt/nightscout $STD npm install - msg_ok "Updated ${APP}" + msg_ok "Updated Nightscout" msg_info "Starting Service" systemctl start nightscout From c2b890baa6c4875808bdefb190e901bc5bcc7589 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:48:22 +0100 Subject: [PATCH 026/228] Improve validation and robustness in container scripts Enhances timezone handling by mapping 'Etc/*' zones to 'host', adds stricter password validation (removing leading dashes and enforcing minimum length), and improves container ID validation. Adds storage space validation before container creation and when selecting storage. Implements retry logic and stale lock cleanup for template lock files to avoid stuck processes. Improves GitHub release fetching by adding a fallback to codeload.github.com for complex tag names. --- misc/build.func | 135 +++++++++++++++++++++++++++++++++++++++--------- misc/tools.func | 15 ++++-- 2 files changed, 121 insertions(+), 29 deletions(-) diff --git a/misc/build.func b/misc/build.func index 05d54df60..7094e582b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1789,7 +1789,10 @@ advanced_settings() { elif [ -f /etc/timezone ]; then _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") fi + # pct doesn't accept Etc/* zones - map to 'host' instead + [[ "${_host_timezone:-}" == Etc/* ]] && _host_timezone="host" local _ct_timezone="${var_timezone:-$_host_timezone}" + [[ "${_ct_timezone:-}" == Etc/* ]] && _ct_timezone="host" # Helper to show current progress show_progress() { @@ -1882,24 +1885,33 @@ advanced_settings() { ((STEP++)) elif [[ "$PW1" == *" "* ]]; then whiptail --msgbox "Password cannot contain spaces." 8 58 - elif ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 else - # Verify password - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "PASSWORD VERIFICATION" \ - --ok-button "Confirm" --cancel-button "Back" \ - --passwordbox "\nVerify Root Password" 10 58 \ - 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - _pw="-password $PW1" - _pw_display="********" - ((STEP++)) - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi + # Clean up leading dashes from password + local _pw1_clean="$PW1" + while [[ "$_pw1_clean" == -* ]]; do + _pw1_clean="${_pw1_clean#-}" + done + if [[ -z "$_pw1_clean" ]]; then + whiptail --msgbox "Password cannot be only '-' characters." 8 58 + elif ((${#_pw1_clean} < 5)); then + whiptail --msgbox "Password must be at least 5 characters (after removing leading '-')." 8 70 else - ((STEP--)) + # Verify password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "PASSWORD VERIFICATION" \ + --ok-button "Confirm" --cancel-button "Back" \ + --passwordbox "\nVerify Root Password" 10 58 \ + 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + _pw="--password $_pw1_clean" + _pw_display="********" + ((STEP++)) + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + ((STEP--)) + fi fi fi else @@ -1916,8 +1928,23 @@ advanced_settings() { --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ 3>&1 1>&2 2>&3); then - _ct_id="${result:-$NEXTID}" - ((STEP++)) + local input_id="${result:-$NEXTID}" + # Validate container ID is numeric + if ! [[ "$input_id" =~ ^[0-9]+$ ]]; then + whiptail --msgbox "Container ID must be numeric." 8 58 + continue + fi + # Validate container ID is available + if ! validate_container_id "$input_id"; then + if whiptail --yesno "Container/VM ID $input_id is already in use.\n\nWould you like to use the next available ID: $(get_valid_container_id "$input_id")?" 10 58; then + _ct_id=$(get_valid_container_id "$input_id") + ((STEP++)) + fi + # else stay on this step + else + _ct_id="$input_id" + ((STEP++)) + fi else ((STEP--)) fi @@ -2803,6 +2830,8 @@ install_script() { else timezone="UTC" fi + # pct doesn't accept Etc/* zones - map to 'host' instead + [[ "${timezone:-}" == Etc/* ]] && timezone="host" # Show APP Header header_info @@ -3374,10 +3403,37 @@ build_container() { export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" + # Validate storage space before container creation + if [[ -n "$CONTAINER_STORAGE" ]]; then + msg_info "Validating storage space" + if ! validate_storage_space "$CONTAINER_STORAGE" "$DISK_SIZE" "no"; then + local free_space + free_space=$(pvesm status 2>/dev/null | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + local free_fmt + free_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$free_space" 2>/dev/null || echo "${free_space}KB") + msg_error "Not enough space on '$CONTAINER_STORAGE'. Required: ${DISK_SIZE}GB, Available: ${free_fmt}" + exit 214 + fi + msg_ok "Storage space validated" + fi + # Build PCT_OPTIONS as multi-line string - PCT_OPTIONS_STRING=" -features $FEATURES - -hostname $HN + PCT_OPTIONS_STRING="" + + # Add features if set + if [ -n "$FEATURES" ]; then + PCT_OPTIONS_STRING=" -features $FEATURES" + fi + + # Add hostname + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING + -hostname $HN" + + # Add tags if set + if [ -n "$TAGS" ]; then + PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -tags $TAGS" + fi # Add storage if specified if [ -n "$SD" ]; then @@ -4234,6 +4290,11 @@ select_storage() { if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" STORAGE_INFO="${MENU[1]}" + + # Validate storage space for auto-picked container storage + if [[ "$CLASS" == "container" && -n "${DISK_SIZE:-}" ]]; then + validate_storage_space "$STORAGE_RESULT" "$DISK_SIZE" "yes" + fi return 0 fi @@ -4257,6 +4318,11 @@ select_storage() { break fi done + + # Validate storage space for container storage + if [[ "$CLASS" == "container" && -n "${DISK_SIZE:-}" ]]; then + validate_storage_space "$STORAGE_RESULT" "$DISK_SIZE" "yes" + fi return 0 done } @@ -4848,14 +4914,35 @@ create_lxc_container() { # Lock by template file (avoid concurrent downloads/creates) lockfile="/tmp/template.${TEMPLATE}.lock" + + # Cleanup stale lock files (older than 1 hour - likely from crashed processes) + if [[ -f "$lockfile" ]]; then + local lock_age=$(($(date +%s) - $(stat -c %Y "$lockfile" 2>/dev/null || echo 0))) + if [[ $lock_age -gt 3600 ]]; then + msg_warn "Removing stale template lock file (age: ${lock_age}s)" + rm -f "$lockfile" + fi + fi + exec 9>"$lockfile" || { msg_error "Failed to create lock file '$lockfile'." exit 200 } - flock -w 60 9 || { - msg_error "Timeout while waiting for template lock." - exit 211 - } + + # Retry logic for template lock (another container creation may be running) + local lock_attempts=0 + local max_lock_attempts=10 + local lock_wait_time=30 + + while ! flock -w "$lock_wait_time" 9; do + lock_attempts=$((lock_attempts + 1)) + if [[ $lock_attempts -ge $max_lock_attempts ]]; then + msg_error "Timeout while waiting for template lock after ${max_lock_attempts} attempts." + msg_custom "💡" "${YW}" "Another container creation may be stuck. Check running processes or remove: $lockfile" + exit 211 + fi + msg_custom "⏳" "${YW}" "Another container is being created with this template. Waiting... (attempt ${lock_attempts}/${max_lock_attempts})" + done LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" diff --git a/misc/tools.func b/misc/tools.func index 29a95c8aa..2b3e6fc17 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1776,11 +1776,16 @@ function fetch_and_deploy_gh_release() { local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$encoded_tag_name.tar.gz" filename="${app_lc}-${version}.tar.gz" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" || { - msg_error "Download failed: $direct_tarball_url" - rm -rf "$tmpdir" - return 1 - } + # Try primary URL first, fallback to codeload.github.com for complex tag names + if ! curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" 2>/dev/null; then + # Fallback: codeload.github.com handles special chars like @scope/package@version better + local codeload_url="https://codeload.github.com/$repo/tar.gz/refs/tags/$encoded_tag_name" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$codeload_url" || { + msg_error "Download failed: $direct_tarball_url (and fallback $codeload_url)" + rm -rf "$tmpdir" + return 1 + } + fi mkdir -p "$target" if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then From 665907592e748418c9085ffc1c03bd24b9aef959 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:59:06 +0100 Subject: [PATCH 027/228] fix(tools): add codeload.github.com fallback for complex tag names - Primary URL github.com/archive/refs/tags/ tried first - Fallback to codeload.github.com for tags with special chars (@scope/pkg@ver) - Only encode @ in fallback URL, keep / unencoded for codeload compatibility --- misc/tools.func | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 2b3e6fc17..15842c5fb 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1778,8 +1778,9 @@ function fetch_and_deploy_gh_release() { # Try primary URL first, fallback to codeload.github.com for complex tag names if ! curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" 2>/dev/null; then - # Fallback: codeload.github.com handles special chars like @scope/package@version better - local codeload_url="https://codeload.github.com/$repo/tar.gz/refs/tags/$encoded_tag_name" + # Fallback: codeload.github.com - only encode @ not / in tag names + local codeload_encoded="${tag_name//@/%40}" + local codeload_url="https://codeload.github.com/$repo/tar.gz/refs/tags/$codeload_encoded" curl $download_timeout -fsSL -o "$tmpdir/$filename" "$codeload_url" || { msg_error "Download failed: $direct_tarball_url (and fallback $codeload_url)" rm -rf "$tmpdir" From fe9c5d22857b3cf736711f4e6c365c6b47b9a16c Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:09:30 +0100 Subject: [PATCH 028/228] fix(tools): remove partial file before fallback download attempt --- misc/tools.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/tools.func b/misc/tools.func index 15842c5fb..0696c0d9c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1778,6 +1778,8 @@ function fetch_and_deploy_gh_release() { # Try primary URL first, fallback to codeload.github.com for complex tag names if ! curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" 2>/dev/null; then + # Remove partial/corrupt file from failed attempt before retry + rm -f "$tmpdir/$filename" # Fallback: codeload.github.com - only encode @ not / in tag names local codeload_encoded="${tag_name//@/%40}" local codeload_url="https://codeload.github.com/$repo/tar.gz/refs/tags/$codeload_encoded" From fc8433ad8bc20282d1e620be1d853efc496fdf0c Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:19:58 +0100 Subject: [PATCH 029/228] fix(tools): use codeload directly for tags with special chars (@/) Skip failed github.com attempt entirely for scoped tags like @papra/docker@26.0.0 --- misc/tools.func | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 0696c0d9c..f730fd523 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1768,26 +1768,30 @@ function fetch_and_deploy_gh_release() { ### Tarball Mode ### if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then - # GitHub API's tarball_url/zipball_url can return HTTP 300 Multiple Choices - # when a branch and tag share the same name. Use explicit refs/tags/ URL instead. - # URL-encode special characters in tag names (@ and /) - local encoded_tag_name="${tag_name//@/%40}" - encoded_tag_name="${encoded_tag_name//\//%2F}" - local direct_tarball_url="https://github.com/$repo/archive/refs/tags/$encoded_tag_name.tar.gz" filename="${app_lc}-${version}.tar.gz" + local download_success=false - # Try primary URL first, fallback to codeload.github.com for complex tag names - if ! curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url" 2>/dev/null; then - # Remove partial/corrupt file from failed attempt before retry - rm -f "$tmpdir/$filename" - # Fallback: codeload.github.com - only encode @ not / in tag names + # For tags with special characters (@, /), use codeload.github.com directly + # as github.com/archive/refs/tags/ doesn't handle them well + if [[ "$tag_name" =~ [@/] ]]; then + # codeload.github.com - only encode @ not / in tag names local codeload_encoded="${tag_name//@/%40}" local codeload_url="https://codeload.github.com/$repo/tar.gz/refs/tags/$codeload_encoded" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$codeload_url" || { - msg_error "Download failed: $direct_tarball_url (and fallback $codeload_url)" - rm -rf "$tmpdir" - return 1 - } + if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$codeload_url"; then + download_success=true + fi + else + # Standard tags: use github.com/archive/refs/tags/ + local direct_tarball_url="https://github.com/$repo/archive/refs/tags/${tag_name}.tar.gz" + if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url"; then + download_success=true + fi + fi + + if [[ "$download_success" != "true" ]]; then + msg_error "Download failed for $app ($tag_name)" + rm -rf "$tmpdir" + return 1 fi mkdir -p "$target" From 0481d5ccf733ecf74d2fe08724dc2158826308af Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:30:55 +0100 Subject: [PATCH 030/228] fix(tools): sanitize version string for filename Replace @ and / with _ in filename to avoid invalid paths like papra-@papra/docker@26.0.0.tar.gz --- misc/tools.func | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index f730fd523..260fb9a03 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1768,7 +1768,10 @@ function fetch_and_deploy_gh_release() { ### Tarball Mode ### if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then - filename="${app_lc}-${version}.tar.gz" + # Sanitize version for filename (remove/replace special chars) + local safe_version="${version//@/_}" + safe_version="${safe_version//\//_}" + filename="${app_lc}-${safe_version}.tar.gz" local download_success=false # For tags with special characters (@, /), use codeload.github.com directly From 7867b62647fc2afc6c2c2638d615dea90c8ec047 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:41:25 +0100 Subject: [PATCH 031/228] fix(papra): show service logs on startup failure --- install/papra-install.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index 7a5134881..536aff9f1 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -93,9 +93,13 @@ RestartSec=10 WantedBy=multi-user.target EOF -systemctl enable -q --now papra +systemctl enable -q papra +if ! systemctl start papra; then + msg_warn "Service failed to start, checking logs..." + journalctl -u papra --no-pager -n 20 || true +fi echo "${RELEASE}" >/opt/Papra_version.txt -msg_ok "Created and Started Papra Service" +msg_ok "Created Papra Service" motd_ssh customize From d24d9769cb000ae604b9325ba52c6e6661f94633 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:59:20 +0100 Subject: [PATCH 032/228] fix(papra): finalize install script - Add AUTH_SECRET env var (required by app) - Link client dist to server public dir for frontend - Keep BETTER_AUTH_SECRET for compatibility --- install/papra-install.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index 536aff9f1..638a47a0e 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -37,11 +37,14 @@ msg_ok "Set up Papra" msg_info "Configuring Papra" CONTAINER_IP=$(hostname -I | awk '{print $1}') -BETTER_AUTH_SECRET=$(openssl rand -hex 32) +AUTH_SECRET=$(openssl rand -hex 32) mkdir -p /opt/papra/app-data/db mkdir -p /opt/papra/app-data/documents +# Link client build to server public dir +ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public + cat >/opt/papra/.env </opt/Papra_version.txt -msg_ok "Created Papra Service" +msg_ok "Created and Started Papra Service" motd_ssh customize From 8a86fe534536f4e6f4699b1fcf021e2906840f3c Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:59:49 +0100 Subject: [PATCH 033/228] finalize papra --- install/papra-install.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index 638a47a0e..50fb10553 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -36,15 +36,10 @@ $STD pnpm --filter "@papra/app-server..." run build msg_ok "Set up Papra" msg_info "Configuring Papra" -CONTAINER_IP=$(hostname -I | awk '{print $1}') AUTH_SECRET=$(openssl rand -hex 32) - mkdir -p /opt/papra/app-data/db mkdir -p /opt/papra/app-data/documents - -# Link client build to server public dir ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public - cat >/opt/papra/.env </opt/Papra_version.txt msg_ok "Created and Started Papra Service" motd_ssh From aafa60de065859277aee5168ff67b1bb21d84e85 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 17:34:19 +0100 Subject: [PATCH 034/228] fix(papra): correct paths and migration command - Move .env to /opt/papra/apps/papra-server/.env - Create app-data dirs in server folder (relative paths work) - Fix migration command: migration:apply -> migrate:up - Update EnvironmentFile path in service --- install/papra-install.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index 50fb10553..99413e4e1 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -37,10 +37,16 @@ msg_ok "Set up Papra" msg_info "Configuring Papra" AUTH_SECRET=$(openssl rand -hex 32) -mkdir -p /opt/papra/app-data/db -mkdir -p /opt/papra/app-data/documents + +# Create data directories in server folder (where WorkingDirectory points) +mkdir -p /opt/papra/apps/papra-server/app-data/db +mkdir -p /opt/papra/apps/papra-server/app-data/documents +mkdir -p /opt/papra/apps/papra-server/ingestion + +# Link client build to server public dir ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public -cat >/opt/papra/.env </opt/papra/apps/papra-server/.env </opt/papra_version.txt msg_ok "Created and Started Papra Service" motd_ssh From dcc6ab043781dc70a4d295743be7dbbe417d3f59 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 17:34:59 +0100 Subject: [PATCH 035/228] refactor(papra): persist data in /opt/papra_data - Move db, documents, ingestion to /opt/papra_data (survives CLEAN_INSTALL) - Use absolute paths in .env - Preserve AUTH_SECRET across updates (stored in .auth_secret file) --- install/papra-install.sh | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index 99413e4e1..dc2b38a3a 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -36,27 +36,33 @@ $STD pnpm --filter "@papra/app-server..." run build msg_ok "Set up Papra" msg_info "Configuring Papra" -AUTH_SECRET=$(openssl rand -hex 32) +DATA_DIR="/opt/papra_data" -# Create data directories in server folder (where WorkingDirectory points) -mkdir -p /opt/papra/apps/papra-server/app-data/db -mkdir -p /opt/papra/apps/papra-server/app-data/documents -mkdir -p /opt/papra/apps/papra-server/ingestion +# Create persistent data directories (survive updates/CLEAN_INSTALL) +mkdir -p "${DATA_DIR}/db" +mkdir -p "${DATA_DIR}/documents" +mkdir -p "${DATA_DIR}/ingestion" # Link client build to server public dir ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public +# Generate secret only on first install, preserve on updates +if [[ ! -f "${DATA_DIR}/.auth_secret" ]]; then + openssl rand -hex 32 > "${DATA_DIR}/.auth_secret" +fi +AUTH_SECRET=$(cat "${DATA_DIR}/.auth_secret") + cat >/opt/papra/apps/papra-server/.env < Date: Wed, 21 Jan 2026 17:36:12 +0100 Subject: [PATCH 036/228] refactor(papra): cleanup install script community-style - Remove unnecessary vars and comments - Use apt-get instead of apt - Get pnpm version from package.json - Use NODE_MODULE for pnpm setup - Simplify service (node direct, no corepack) - Compact .env format --- install/papra-install.sh | 76 +++++++++++----------------------------- 1 file changed, 21 insertions(+), 55 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index dc2b38a3a..2eb667fa5 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -14,98 +14,64 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y \ +$STD apt-get install -y \ build-essential \ tesseract-ocr \ tesseract-ocr-all msg_ok "Installed Dependencies" -NODE_VERSION="24" setup_nodejs - RELEASE=$(curl -fsSL https://api.github.com/repos/papra-hq/papra/releases | grep -oP '"tag_name":\s*"\K@papra/docker@[^"]+' | head -n1) fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE}" "/opt/papra" -msg_info "Setup Papra" +pnpm_version=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/papra/package.json) +NODE_VERSION="24" NODE_MODULE="pnpm@$pnpm_version" setup_nodejs + +msg_info "Installing Papra (Patience)" cd /opt/papra -export COREPACK_ENABLE_NETWORK=1 -$STD corepack enable -$STD corepack prepare pnpm@10.19.0 --activate $STD pnpm install --frozen-lockfile $STD pnpm --filter "@papra/app-client..." run build $STD pnpm --filter "@papra/app-server..." run build -msg_ok "Set up Papra" +ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public +msg_ok "Installed Papra" msg_info "Configuring Papra" -DATA_DIR="/opt/papra_data" - -# Create persistent data directories (survive updates/CLEAN_INSTALL) -mkdir -p "${DATA_DIR}/db" -mkdir -p "${DATA_DIR}/documents" -mkdir -p "${DATA_DIR}/ingestion" - -# Link client build to server public dir -ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public - -# Generate secret only on first install, preserve on updates -if [[ ! -f "${DATA_DIR}/.auth_secret" ]]; then - openssl rand -hex 32 > "${DATA_DIR}/.auth_secret" -fi -AUTH_SECRET=$(cat "${DATA_DIR}/.auth_secret") - -cat >/opt/papra/apps/papra-server/.env </opt/papra_data/.secret +cat </opt/papra/apps/papra-server/.env NODE_ENV=production SERVER_SERVE_PUBLIC_DIR=true PORT=1221 - -# Database Configuration -DATABASE_URL=file:${DATA_DIR}/db/db.sqlite - -# Storage Configuration -DOCUMENT_STORAGE_FILESYSTEM_ROOT=${DATA_DIR}/documents -PAPRA_CONFIG_DIR=${DATA_DIR} - -# Authentication -AUTH_SECRET=${AUTH_SECRET} -BETTER_AUTH_SECRET=${AUTH_SECRET} +DATABASE_URL=file:/opt/papra_data/db/db.sqlite +DOCUMENT_STORAGE_FILESYSTEM_ROOT=/opt/papra_data/documents +PAPRA_CONFIG_DIR=/opt/papra_data +AUTH_SECRET=$(cat /opt/papra_data/.secret) +BETTER_AUTH_SECRET=$(cat /opt/papra_data/.secret) BETTER_AUTH_TELEMETRY=0 - -# Application Configuration CLIENT_BASE_URL=http://${LOCAL_IP}:1221 - -# Email Configuration (dry-run mode) EMAILS_DRY_RUN=true - -# Ingestion Folder -INGESTION_FOLDER_ROOT=${DATA_DIR}/ingestion +INGESTION_FOLDER_ROOT=/opt/papra_data/ingestion EOF - -chown -R root:root /opt/papra msg_ok "Configured Papra" -msg_info "Creating Papra Service" -cat >/etc/systemd/system/papra.service </etc/systemd/system/papra.service [Unit] Description=Papra Document Management After=network.target [Service] Type=simple -User=root WorkingDirectory=/opt/papra/apps/papra-server EnvironmentFile=/opt/papra/apps/papra-server/.env -Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ExecStartPre=/usr/bin/corepack pnpm --silent run migrate:up -ExecStart=/usr/bin/corepack pnpm --silent run start -Restart=on-failure -RestartSec=10 +ExecStartPre=/usr/bin/pnpm run migrate:up +ExecStart=/usr/bin/node dist/index.js [Install] WantedBy=multi-user.target EOF - systemctl enable -q --now papra echo "${RELEASE}" >/opt/papra_version.txt -msg_ok "Created and Started Papra Service" +msg_ok "Created Service" motd_ssh customize From 9314b497754f14eb82b455e1c612fdec669db887 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 21 Jan 2026 17:45:26 +0100 Subject: [PATCH 037/228] fix(papra): extract pnpm version from packageManager field --- install/papra-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/papra-install.sh b/install/papra-install.sh index 2eb667fa5..1f1be0a74 100644 --- a/install/papra-install.sh +++ b/install/papra-install.sh @@ -23,7 +23,7 @@ msg_ok "Installed Dependencies" RELEASE=$(curl -fsSL https://api.github.com/repos/papra-hq/papra/releases | grep -oP '"tag_name":\s*"\K@papra/docker@[^"]+' | head -n1) fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE}" "/opt/papra" -pnpm_version=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/papra/package.json) +pnpm_version=$(grep -oP '"packageManager":\s*"pnpm@\K[^"]+' /opt/papra/package.json) NODE_VERSION="24" NODE_MODULE="pnpm@$pnpm_version" setup_nodejs msg_info "Installing Papra (Patience)" @@ -70,7 +70,6 @@ ExecStart=/usr/bin/node dist/index.js WantedBy=multi-user.target EOF systemctl enable -q --now papra -echo "${RELEASE}" >/opt/papra_version.txt msg_ok "Created Service" motd_ssh From 7a52d54ab37815ebb7303a997674fede24f388b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:26:31 +0000 Subject: [PATCH 038/228] Delete byparr (ct) after migration to ProxmoxVE (#1326) Co-authored-by: github-actions[bot] --- ct/byparr.sh | 53 -------------------------------- ct/headers/byparr | 6 ---- frontend/public/json/byparr.json | 35 --------------------- install/byparr-install.sh | 50 ------------------------------ 4 files changed, 144 deletions(-) delete mode 100644 ct/byparr.sh delete mode 100644 ct/headers/byparr delete mode 100644 frontend/public/json/byparr.json delete mode 100644 install/byparr-install.sh diff --git a/ct/byparr.sh b/ct/byparr.sh deleted file mode 100644 index 62104bb11..000000000 --- a/ct/byparr.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2026 community-scripts ORG -# Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/ThePhaseless/Byparr - -APP="Byparr" -var_tags="${var_tags:-proxy}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -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/Byparr ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "Byparr" "ThePhaseless/Byparr"; then - msg_info "Stopping Service" - systemctl stop byparr - msg_ok "Stopped Service" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Byparr" "ThePhaseless/Byparr" - - msg_info "Starting Service" - systemctl start byparr - msg_ok "Started Service" - msg_ok "Updated successfully!" - 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}:8191${CL}" diff --git a/ct/headers/byparr b/ct/headers/byparr deleted file mode 100644 index 0a6f89eca..000000000 --- a/ct/headers/byparr +++ /dev/null @@ -1,6 +0,0 @@ - ____ - / __ )__ ______ ____ ___________ - / __ / / / / __ \/ __ `/ ___/ ___/ - / /_/ / /_/ / /_/ / /_/ / / / / -/_____/\__, / .___/\__,_/_/ /_/ - /____/_/ diff --git a/frontend/public/json/byparr.json b/frontend/public/json/byparr.json deleted file mode 100644 index fb572c4f7..000000000 --- a/frontend/public/json/byparr.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Byparr", - "slug": "byparr", - "categories": [ - 14 - ], - "date_created": "2025-12-02", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8191, - "documentation": "https://github.com/ThePhaseless/Byparr/blob/master/README.md", - "website": "https://github.com/ThePhaseless/Byparr", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/byparr.webp", - "config_path": "", - "description": "Byparr is a proxy server to bypass Cloudflare and DDoS-GUARD protection.", - "install_methods": [ - { - "type": "default", - "script": "ct/byparr.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/byparr-install.sh b/install/byparr-install.sh deleted file mode 100644 index 1616b9551..000000000 --- a/install/byparr-install.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/ThePhaseless/Byparr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt -y install \ - xauth \ - xvfb \ - scrot \ - chromium \ - chromium-driver \ - ca-certificates -msg_ok "Installed Dependencies" - -fetch_and_deploy_gh_release "Byparr" "ThePhaseless/Byparr" -setup_uv - -msg_info "Creating Service" -cat </etc/systemd/system/byparr.service -[Unit] -Description=Byparr -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/Byparr -ExecStart=/usr/local/bin/uv run python3 main.py -Restart=on-failure -RestartSec=10 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now byparr -msg_ok "Created Service" - -motd_ssh -customize -cleanup_lxc From 63734bd544933d82ef6ae88d29082d7f9628bf3d Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 21 Jan 2026 19:17:22 -0500 Subject: [PATCH 039/228] restore ensure_profile_loaded to core.func --- misc/core.func | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/misc/core.func b/misc/core.func index 8ee70a3df..4506b101b 100644 --- a/misc/core.func +++ b/misc/core.func @@ -126,6 +126,34 @@ icons() { HOURGLASS="${TAB}⏳${TAB}" } +# ------------------------------------------------------------------------------ +# ensure_profile_loaded() +# +# - Sources /etc/profile.d/*.sh scripts if not already loaded +# - Fixes PATH issues when running via pct enter/exec (non-login shells) +# - Safe to call multiple times (uses guard variable) +# - Should be called in update_script() or any script running inside LXC +# ------------------------------------------------------------------------------ +ensure_profile_loaded() { + # Skip if already loaded or running on Proxmox host + [[ -n "${_PROFILE_LOADED:-}" ]] && return + command -v pveversion &>/dev/null && return + + # Source all profile.d scripts to ensure PATH is complete + if [[ -d /etc/profile.d ]]; then + for script in /etc/profile.d/*.sh; do + [[ -r "$script" ]] && source "$script" + done + fi + + # Also ensure /usr/local/bin is in PATH (common install location) + if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then + export PATH="/usr/local/bin:$PATH" + fi + + export _PROFILE_LOADED=1 +} + # ------------------------------------------------------------------------------ # default_vars() # @@ -838,7 +866,7 @@ cleanup_lxc() { fi # Node.js npm -if command -v npm &>/dev/null; then + if command -v npm &>/dev/null; then rm -rf /root/.npm/_cacache /root/.npm/_logs 2>/dev/null || true fi # Node.js yarn @@ -969,7 +997,6 @@ function get_lxc_ip() { export LOCAL_IP } - # ============================================================================== # SIGNAL TRAPS # ============================================================================== From 58e9c9d6a68d9f63e1eab2911fd9f794f2198ec5 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:34:57 +0100 Subject: [PATCH 040/228] refactor loki --- ct/loki.sh | 16 +++++-- install/alpine-loki-install.sh | 83 +++++++++++++++++++++++++++------- install/loki-install.sh | 61 +++++++++++++++++++++++-- 3 files changed, 133 insertions(+), 27 deletions(-) diff --git a/ct/loki.sh b/ct/loki.sh index fd2df1f1f..108c0ce81 100644 --- a/ct/loki.sh +++ b/ct/loki.sh @@ -31,18 +31,24 @@ function update_script() { msg_info "Stopping Loki" systemctl stop loki - systemctl stop promtail + if systemctl is-active --quiet promtail 2>/dev/null || dpkg -s promtail >/dev/null 2>&1; then + systemctl stop promtail + fi msg_ok "Stopped Loki" msg_info "Updating Loki" - $STD apt update - $STD apt --only-upgrade install -y loki - $STD apt --only-upgrade install -y promtail + $STD apt-get update + $STD apt-get --only-upgrade install -y loki + if dpkg -s promtail >/dev/null 2>&1; then + $STD apt-get --only-upgrade install -y promtail + fi msg_ok "Updated Loki" msg_info "Starting Loki" systemctl start loki - systemctl start promtail + if dpkg -s promtail >/dev/null 2>&1; then + systemctl start promtail + fi msg_ok "Started Loki" msg_ok "Updated successfully!" exit diff --git a/install/alpine-loki-install.sh b/install/alpine-loki-install.sh index cc5dbfdee..6efc9f3a3 100644 --- a/install/alpine-loki-install.sh +++ b/install/alpine-loki-install.sh @@ -16,26 +16,75 @@ update_os msg_info "Installing Loki" $STD apk add loki $STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki -$STD rc-service loki start + +mkdir -p /var/lib/loki/{chunks,boltdb-shipper-active,boltdb-shipper-cache} +chown -R loki:grafana /var/lib/loki +mkdir -p /var/log/loki +chown -R loki:grafana /var/log/loki + +cat </etc/loki/loki-local-config.yaml +auth_enabled: false + +server: + http_listen_port: 3100 + log_level: info + +common: + instance_addr: 127.0.0.1 + path_prefix: /var/lib/loki + storage: + filesystem: + chunks_directory: /var/lib/loki/chunks + rules_directory: /var/lib/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +ruler: + alertmanager_url: http://localhost:9093 +EOF + +chown loki:grafana /etc/loki/loki-local-config.yaml +chmod 644 /etc/loki/loki-local-config.yaml + +echo "output_log=\"\${output_log:-/var/log/loki/output.log}\"" >> /etc/init.d/loki +echo "error_log=\"\${error_log:-/var/log/loki/error.log}\"" >> /etc/init.d/loki +echo "start_stop_daemon_args=\"\${SSD_OPTS} -1 \${output_log} -2 \${error_log}\"" >> /etc/init.d/loki + $STD rc-update add loki default -$STD mkdir /tmp/loki/ -$STD chown -R loki:grafana /tmp/loki/ -$STD mkdir /var/log/loki/ -$STD chown -R loki:grafana /var/log/loki/ -$STD chmod 755 /etc/loki/loki-local-config.yaml -$STD sed -i '/^querier:/,/enable_multi_variant_queries: false/ s/^/#/' /etc/loki/loki-local-config.yaml -$STD echo "output_log=\"\${output_log:-/var/log/loki/output.log}\"" >> /etc/init.d/loki -$STD echo "error_log=\"\${error_log:-/var/log/loki/error.log}\"" >> /etc/init.d/loki -$STD echo "start_stop_daemon_args=\"\${SSD_OPTS} -1 \${output_log} -2 \${error_log}\"" >> /etc/init.d/loki -$STD rc-service loki restart +$STD rc-service loki start msg_ok "Installed Loki" -msg_info "Installing Promtail" -$STD apk add loki-promtail -$STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki -$STD rc-service loki-promtail start -$STD rc-update add loki-promtail default -msg_ok "Installed Promtail" +read -rp "Would you like to install Promtail? (y/N): " INSTALL_PROMTAIL +if [[ "${INSTALL_PROMTAIL,,}" =~ ^(y|yes)$ ]]; then + msg_info "Installing Promtail" + $STD apk add loki-promtail + $STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki-promtail + $STD rc-update add loki-promtail default + $STD rc-service loki-promtail start + msg_ok "Installed Promtail" +fi motd_ssh customize diff --git a/install/loki-install.sh b/install/loki-install.sh index e4aaaa0ea..f2eedff7d 100644 --- a/install/loki-install.sh +++ b/install/loki-install.sh @@ -20,17 +20,68 @@ setup_deb822_repo \ "https://apt.grafana.com" \ "stable" \ "main" -msg_ok "Grafana Repository setup sucessfully" +msg_ok "Grafana Repository setup successfully" msg_info "Installing Loki" $STD apt install -y loki + +mkdir -p /var/lib/loki/{chunks,boltdb-shipper-active,boltdb-shipper-cache} +chown -R loki:loki /var/lib/loki + +cat </etc/loki/config.yml +auth_enabled: false + +server: + http_listen_port: 3100 + log_level: info + +common: + instance_addr: 127.0.0.1 + path_prefix: /var/lib/loki + storage: + filesystem: + chunks_directory: /var/lib/loki/chunks + rules_directory: /var/lib/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +ruler: + alertmanager_url: http://localhost:9093 +EOF + +chown loki:loki /etc/loki/config.yml systemctl enable -q --now loki msg_ok "Installed Loki" -msg_info "Installing Promtail" -$STD apt install -y promtail -systemctl enable -q --now promtail -msg_ok "Installed Promtail" +read -rp "Would you like to install Promtail? (y/N): " INSTALL_PROMTAIL +if [[ "${INSTALL_PROMTAIL,,}" =~ ^(y|yes)$ ]]; then + msg_info "Installing Promtail" + $STD apt install -y promtail + systemctl enable -q --now promtail + msg_ok "Installed Promtail" +fi motd_ssh customize From 0a1366fd8cf6c24d9a3416818f61281e0a4977b9 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:43:06 +0100 Subject: [PATCH 041/228] fix loki --- install/docker-install.sh | 121 -------------------------------------- install/loki-install.sh | 4 +- 2 files changed, 2 insertions(+), 123 deletions(-) delete mode 100644 install/docker-install.sh diff --git a/install/docker-install.sh b/install/docker-install.sh deleted file mode 100644 index d693f69bd..000000000 --- a/install/docker-install.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.docker.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -# Apply AppArmor workaround BEFORE installing Docker -# See: https://github.com/opencontainers/runc/issues/4968 -apply_docker_apparmor_workaround - -get_latest_release() { - curl -fsSL https://api.github.com/repos/"$1"/releases/latest | grep '"tag_name":' | cut -d'"' -f4 -} - -DOCKER_LATEST_VERSION=$(get_latest_release "moby/moby") -PORTAINER_LATEST_VERSION=$(get_latest_release "portainer/portainer") -PORTAINER_AGENT_LATEST_VERSION=$(get_latest_release "portainer/agent") -DOCKER_COMPOSE_LATEST_VERSION=$(get_latest_release "docker/compose") - -msg_info "Installing Docker $DOCKER_LATEST_VERSION" -DOCKER_CONFIG_PATH='/etc/docker/daemon.json' -mkdir -p $(dirname $DOCKER_CONFIG_PATH) -echo -e '{\n "log-driver": "journald"\n}' >/etc/docker/daemon.json -$STD sh <(curl -fsSL https://get.docker.com) -msg_ok "Installed Docker $DOCKER_LATEST_VERSION" - -# Restart Docker to apply AppArmor workaround (if running in LXC) -$STD systemctl restart docker - -read -r -p "${TAB3}Install Docker Compose v2 plugin? " prompt_compose -if [[ ${prompt_compose,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Docker Compose $DOCKER_COMPOSE_LATEST_VERSION" - mkdir -p /usr/local/lib/docker/cli-plugins - curl -fsSL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_LATEST_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \ - -o /usr/local/lib/docker/cli-plugins/docker-compose - chmod +x /usr/local/lib/docker/cli-plugins/docker-compose - msg_ok "Installed Docker Compose $DOCKER_COMPOSE_LATEST_VERSION" -fi - -read -r -p "${TAB3}Would you like to add Portainer (UI)? " prompt -if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Portainer $PORTAINER_LATEST_VERSION" - docker volume create portainer_data >/dev/null - $STD docker run -d \ - -p 8000:8000 \ - -p 9443:9443 \ - --name=portainer \ - --restart=always \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v portainer_data:/data \ - portainer/portainer-ce:latest - msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION" -else - read -r -p "${TAB3}Would you like to install the Portainer Agent (for remote management)? " prompt_agent - if [[ ${prompt_agent,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" - $STD docker run -d \ - -p 9001:9001 \ - --name portainer_agent \ - --restart=always \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /var/lib/docker/volumes:/var/lib/docker/volumes \ - portainer/agent - msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" - fi -fi - -read -r -p "${TAB3}Expose Docker TCP socket (insecure) ? [n = No, l = Local only (127.0.0.1), a = All interfaces (0.0.0.0)] : " socket_choice -case "${socket_choice,,}" in -l) - socket="tcp://127.0.0.1:2375" - ;; -a) - socket="tcp://0.0.0.0:2375" - ;; -*) - socket="" - ;; -esac - -if [[ -n "$socket" ]]; then - msg_info "Enabling Docker TCP socket on $socket" - $STD apt-get install -y jq - - tmpfile=$(mktemp) - jq --arg sock "$socket" '. + { "hosts": ["unix:///var/run/docker.sock", $sock] }' /etc/docker/daemon.json >"$tmpfile" && mv "$tmpfile" /etc/docker/daemon.json - - mkdir -p /etc/systemd/system/docker.service.d - cat </etc/systemd/system/docker.service.d/override.conf -[Service] -ExecStart= -ExecStart=/usr/bin/dockerd -EOF - - $STD systemctl daemon-reexec - $STD systemctl daemon-reload - - if systemctl restart docker; then - msg_ok "Docker TCP socket available on $socket" - else - msg_error "Docker failed to restart. Check journalctl -xeu docker.service" - exit 1 - fi -fi - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/loki-install.sh b/install/loki-install.sh index f2eedff7d..a7228e485 100644 --- a/install/loki-install.sh +++ b/install/loki-install.sh @@ -26,7 +26,7 @@ msg_info "Installing Loki" $STD apt install -y loki mkdir -p /var/lib/loki/{chunks,boltdb-shipper-active,boltdb-shipper-cache} -chown -R loki:loki /var/lib/loki +chown -R loki /var/lib/loki cat </etc/loki/config.yml auth_enabled: false @@ -71,7 +71,7 @@ ruler: alertmanager_url: http://localhost:9093 EOF -chown loki:loki /etc/loki/config.yml +chown loki /etc/loki/config.yml systemctl enable -q --now loki msg_ok "Installed Loki" From 900200ec6d7e942fbcc777c2d88c3abe23c52e04 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:49:47 +0100 Subject: [PATCH 042/228] fix(build): correct storage size formatting bugs - Fix select_storage(): use --from-unit=1024 instead of --from-unit=K - Fix resolve_storage_preselect(): add missing --from-unit=1024 - pvesm outputs KiB (1024-byte blocks), not 1000-byte blocks - Fixes incorrect MB display instead of GB --- misc/build.func | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7094e582b..b584767de 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4155,9 +4155,9 @@ resolve_storage_preselect() { free="$(awk '{print $6}' <<<"$line")" local total_h used_h free_h if command -v numfmt >/dev/null 2>&1; then - total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" - used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" - free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + total_h="$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" STORAGE_INFO="Free: ${free_h} Used: ${used_h}" else STORAGE_INFO="Free: ${free} Used: ${used}" @@ -4274,8 +4274,8 @@ select_storage() { while read -r TAG TYPE _ TOTAL USED FREE _; do [[ -n "$TAG" && -n "$TYPE" ]] || continue local DISPLAY="${TAG} (${TYPE})" - local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") - local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local USED_FMT=$(numfmt --to=iec --from-unit=1024 --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=1024 --format %.1f <<<"$FREE") local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" STORAGE_MAP["$DISPLAY"]="$TAG" MENU+=("$DISPLAY" "$INFO" "OFF") From baa18cb1489d50b97f25ae43419988c40e9775b3 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:51:04 +0100 Subject: [PATCH 043/228] feat(loki): add whiptail menu to update script - Add interactive menu with 3 options - Update Loki & Promtail - Configure listening on 0.0.0.0 (all interfaces) - Configure listening on container IP only - Matches alpine-loki.sh functionality --- ct/loki.sh | 78 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/ct/loki.sh b/ct/loki.sh index 108c0ce81..d04a977c8 100644 --- a/ct/loki.sh +++ b/ct/loki.sh @@ -29,29 +29,65 @@ function update_script() { exit 1 fi - msg_info "Stopping Loki" - systemctl stop loki - if systemctl is-active --quiet promtail 2>/dev/null || dpkg -s promtail >/dev/null 2>&1; then - systemctl stop promtail - fi - msg_ok "Stopped Loki" + LXCIP=$(hostname -I | awk '{print $1}') + while true; do + CHOICE=$( + whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --menu "Select option" 11 58 3 \ + "1" "Update Loki & Promtail" \ + "2" "Allow 0.0.0.0 for listening" \ + "3" "Allow only ${LXCIP} for listening" 3>&2 2>&1 1>&3 + ) + exit_status=$? + if [ $exit_status == 1 ]; then + clear + exit-script + fi + header_info + case $CHOICE in + 1) + msg_info "Stopping Loki" + systemctl stop loki + if systemctl is-active --quiet promtail 2>/dev/null || dpkg -s promtail >/dev/null 2>&1; then + systemctl stop promtail + fi + msg_ok "Stopped Loki" - msg_info "Updating Loki" - $STD apt-get update - $STD apt-get --only-upgrade install -y loki - if dpkg -s promtail >/dev/null 2>&1; then - $STD apt-get --only-upgrade install -y promtail - fi - msg_ok "Updated Loki" + msg_info "Updating Loki" + $STD apt-get update + $STD apt-get --only-upgrade install -y loki + if dpkg -s promtail >/dev/null 2>&1; then + $STD apt-get --only-upgrade install -y promtail + fi + msg_ok "Updated Loki" - msg_info "Starting Loki" - systemctl start loki - if dpkg -s promtail >/dev/null 2>&1; then - systemctl start promtail - fi - msg_ok "Started Loki" - msg_ok "Updated successfully!" - exit + msg_info "Starting Loki" + systemctl start loki + if dpkg -s promtail >/dev/null 2>&1; then + systemctl start promtail + fi + msg_ok "Started Loki" + msg_ok "Updated successfully!" + exit + ;; + 2) + msg_info "Configuring Loki to listen on 0.0.0.0" + sed -i 's/http_listen_address:.*/http_listen_address: 0.0.0.0/' /etc/loki/config.yml + sed -i 's/http_listen_port:.*/http_listen_port: 3100/' /etc/loki/config.yml + systemctl restart loki + msg_ok "Allowed listening on all interfaces!" + exit + ;; + 3) + msg_info "Configuring Loki to listen on ${LXCIP}" + sed -i "s/http_listen_address:.*/http_listen_address: $LXCIP/" /etc/loki/config.yml + sed -i 's/http_listen_port:.*/http_listen_port: 3100/' /etc/loki/config.yml + systemctl restart loki + msg_ok "Allowed listening only on ${LXCIP}!" + exit + ;; + esac + done + exit 0 } start From d01a85c38cc250f981b6a8f31268f76a841e0f87 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:57:31 +0000 Subject: [PATCH 044/228] Delete loki (ct) after migration to ProxmoxVE (#1329) Co-authored-by: github-actions[bot] --- ct/headers/loki | 6 -- ct/loki.sh | 102 --------------------------------- frontend/public/json/loki.json | 44 -------------- install/loki-install.sh | 88 ---------------------------- 4 files changed, 240 deletions(-) delete mode 100644 ct/headers/loki delete mode 100644 ct/loki.sh delete mode 100644 frontend/public/json/loki.json delete mode 100644 install/loki-install.sh diff --git a/ct/headers/loki b/ct/headers/loki deleted file mode 100644 index a8e58ae7f..000000000 --- a/ct/headers/loki +++ /dev/null @@ -1,6 +0,0 @@ - __ __ _ - / / ____ / /__(_) - / / / __ \/ //_/ / - / /___/ /_/ / ,< / / -/_____/\____/_/|_/_/ - diff --git a/ct/loki.sh b/ct/loki.sh deleted file mode 100644 index d04a977c8..000000000 --- a/ct/loki.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/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: hoholms -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/grafana/loki - -APP="Loki" -var_tags="${var_tags:-monitoring;logs}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if ! dpkg -s loki >/dev/null 2>&1; then - msg_error "No ${APP} Installation Found!" - exit 1 - fi - - LXCIP=$(hostname -I | awk '{print $1}') - while true; do - CHOICE=$( - whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --menu "Select option" 11 58 3 \ - "1" "Update Loki & Promtail" \ - "2" "Allow 0.0.0.0 for listening" \ - "3" "Allow only ${LXCIP} for listening" 3>&2 2>&1 1>&3 - ) - exit_status=$? - if [ $exit_status == 1 ]; then - clear - exit-script - fi - header_info - case $CHOICE in - 1) - msg_info "Stopping Loki" - systemctl stop loki - if systemctl is-active --quiet promtail 2>/dev/null || dpkg -s promtail >/dev/null 2>&1; then - systemctl stop promtail - fi - msg_ok "Stopped Loki" - - msg_info "Updating Loki" - $STD apt-get update - $STD apt-get --only-upgrade install -y loki - if dpkg -s promtail >/dev/null 2>&1; then - $STD apt-get --only-upgrade install -y promtail - fi - msg_ok "Updated Loki" - - msg_info "Starting Loki" - systemctl start loki - if dpkg -s promtail >/dev/null 2>&1; then - systemctl start promtail - fi - msg_ok "Started Loki" - msg_ok "Updated successfully!" - exit - ;; - 2) - msg_info "Configuring Loki to listen on 0.0.0.0" - sed -i 's/http_listen_address:.*/http_listen_address: 0.0.0.0/' /etc/loki/config.yml - sed -i 's/http_listen_port:.*/http_listen_port: 3100/' /etc/loki/config.yml - systemctl restart loki - msg_ok "Allowed listening on all interfaces!" - exit - ;; - 3) - msg_info "Configuring Loki to listen on ${LXCIP}" - sed -i "s/http_listen_address:.*/http_listen_address: $LXCIP/" /etc/loki/config.yml - sed -i 's/http_listen_port:.*/http_listen_port: 3100/' /etc/loki/config.yml - systemctl restart loki - msg_ok "Allowed listening only on ${LXCIP}!" - exit - ;; - esac - done - exit 0 -} - -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}:3100${CL}\n" -echo -e "${INFO}${YW} Access promtail using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9080${CL}" diff --git a/frontend/public/json/loki.json b/frontend/public/json/loki.json deleted file mode 100644 index fa3362a86..000000000 --- a/frontend/public/json/loki.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "Loki", - "slug": "loki", - "categories": [9], - "date_created": "2025-12-10", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3100, - "documentation": "https://grafana.com/docs/loki/latest/", - "website": "https://github.com/grafana/loki", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@master/webp/loki.webp", - "config_path": "Debian: /etc/loki/config.yml | Alpine: /etc/loki/loki-local-config.yaml", - "description": "Grafana Loki is a set of open source components that can be composed into a fully featured logging stack. A small index and highly compressed chunks simplifies the operation and significantly lowers the cost of Loki.", - "install_methods": [ - { - "type": "default", - "script": "ct/loki.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "debian", - "version": "13" - } - }, - { - "type": "alpine", - "script": "ct/alpine-loki.sh", - "resources": { - "cpu": 1, - "ram": 256, - "hdd": 1, - "os": "alpine", - "version": "3.22" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/loki-install.sh b/install/loki-install.sh deleted file mode 100644 index a7228e485..000000000 --- a/install/loki-install.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bysinka-95 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/grafana/loki - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Setting up Grafana Repository" -setup_deb822_repo \ - "grafana" \ - "https://apt.grafana.com/gpg.key" \ - "https://apt.grafana.com" \ - "stable" \ - "main" -msg_ok "Grafana Repository setup successfully" - -msg_info "Installing Loki" -$STD apt install -y loki - -mkdir -p /var/lib/loki/{chunks,boltdb-shipper-active,boltdb-shipper-cache} -chown -R loki /var/lib/loki - -cat </etc/loki/config.yml -auth_enabled: false - -server: - http_listen_port: 3100 - log_level: info - -common: - instance_addr: 127.0.0.1 - path_prefix: /var/lib/loki - storage: - filesystem: - chunks_directory: /var/lib/loki/chunks - rules_directory: /var/lib/loki/rules - replication_factor: 1 - ring: - kvstore: - store: inmemory - -schema_config: - configs: - - from: 2020-10-24 - store: tsdb - object_store: filesystem - schema: v13 - index: - prefix: index_ - period: 24h - -query_range: - results_cache: - cache: - embedded_cache: - enabled: true - max_size_mb: 100 - -limits_config: - metric_aggregation_enabled: true - -ruler: - alertmanager_url: http://localhost:9093 -EOF - -chown loki /etc/loki/config.yml -systemctl enable -q --now loki -msg_ok "Installed Loki" - -read -rp "Would you like to install Promtail? (y/N): " INSTALL_PROMTAIL -if [[ "${INSTALL_PROMTAIL,,}" =~ ^(y|yes)$ ]]; then - msg_info "Installing Promtail" - $STD apt install -y promtail - systemctl enable -q --now promtail - msg_ok "Installed Promtail" -fi - -motd_ssh -customize -cleanup_lxc From e7d0f1dbd7f549eecc16f6c2770f99eb0750c88b Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:05:10 +0100 Subject: [PATCH 045/228] add: wger --- ct/wger.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 ct/wger.sh diff --git a/ct/wger.sh b/ct/wger.sh new file mode 100644 index 000000000..1542eabc1 --- /dev/null +++ b/ct/wger.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/wger-project/wger + +APP="wger" +var_tags="${var_tags:-management;fitness}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +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 /home/wger ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/wger-project/wger/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Stopping Service" + systemctl stop wger + msg_ok "Stopped Service" + + msg_info "Updating $APP to v${RELEASE}" + temp_file=$(mktemp) + curl -fsSL "https://github.com/wger-project/wger/archive/refs/tags/$RELEASE.tar.gz" -o "$temp_file" + tar xzf "$temp_file" + cp -rf wger-"$RELEASE"/* /home/wger/src + cd /home/wger/src + $STD pip install -r requirements_prod.txt --ignore-installed + $STD pip install -e . + $STD python3 manage.py migrate + $STD python3 manage.py collectstatic --no-input + $STD yarn install + $STD yarn build:css:sass + rm -rf "$temp_file" + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting Service" + systemctl start wger + msg_ok "Started Service" + msg_ok "Updated successfully!" + 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}" From 6028cce7a30edb32d7e44017b07d4c1bce1c7131 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:07:45 +0100 Subject: [PATCH 046/228] add: wger --- install/wger-install.sh | 107 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 install/wger-install.sh diff --git a/install/wger-install.sh b/install/wger-install.sh new file mode 100644 index 000000000..ddc90a179 --- /dev/null +++ b/install/wger-install.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/wger-project/wger + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + git \ + apache2 \ + libapache2-mod-wsgi-py3 +msg_ok "Installed Dependencies" + +msg_info "Installing Python" +$STD apt install -y python3-pip +rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED +msg_ok "Installed Python" + +NODE_VERSION="22" NODE_MODULE="yarn,sass" setup_nodejs + +msg_info "Setting up wger" +$STD adduser wger --disabled-password --gecos "" +mkdir /home/wger/db +touch /home/wger/db/database.sqlite +chown :www-data -R /home/wger/db +chmod g+w /home/wger/db /home/wger/db/database.sqlite +mkdir /home/wger/{static,media} +chmod o+w /home/wger/media +temp_dir=$(mktemp -d) +cd "$temp_dir" +RELEASE=$(curl -fsSL https://api.github.com/repos/wger-project/wger/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') +curl -fsSL "https://github.com/wger-project/wger/archive/refs/tags/$RELEASE.tar.gz" -o "$RELEASE.tar.gz" +tar xzf "$RELEASE".tar.gz +mv wger-"$RELEASE" /home/wger/src +cd /home/wger/src +$STD pip install -r requirements_prod.txt --ignore-installed +$STD pip install -e . +$STD wger create-settings --database-path /home/wger/db/database.sqlite +sed -i "s#home/wger/src/media#home/wger/media#g" /home/wger/src/settings.py +sed -i "/MEDIA_ROOT = '\/home\/wger\/media'/a STATIC_ROOT = '/home/wger/static'" /home/wger/src/settings.py +$STD wger bootstrap +$STD python3 manage.py collectstatic +rm -rf "$temp_dir" +echo "${RELEASE}" >/opt/wger_version.txt +msg_ok "Finished setting up wger" + +msg_info "Creating Service" +cat </etc/apache2/sites-available/wger.conf + + + Require all granted + + + + + WSGIApplicationGroup %{GLOBAL} + WSGIDaemonProcess wger python-path=/home/wger/src python-home=/home/wger + WSGIProcessGroup wger + WSGIScriptAlias / /home/wger/src/wger/wsgi.py + WSGIPassAuthorization On + + Alias /static/ /home/wger/static/ + + Require all granted + + + Alias /media/ /home/wger/media/ + + Require all granted + + + ErrorLog /var/log/apache2/wger-error.log + CustomLog /var/log/apache2/wger-access.log combined + +EOF +$STD a2dissite 000-default.conf +$STD a2ensite wger +systemctl restart apache2 +cat </etc/systemd/system/wger.service +[Unit] +Description=wger Service +After=network.target + +[Service] +Type=simple +User=root +ExecStart=/usr/local/bin/wger start -a 0.0.0.0 -p 3000 +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now wger +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 2c7a4cf8ba29ec06befd5c0a195eea844b04ca76 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:08:20 +0100 Subject: [PATCH 047/228] VE --> VED --- ct/wger.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/wger.sh b/ct/wger.sh index 1542eabc1..b096937bd 100644 --- a/ct/wger.sh +++ b/ct/wger.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From a58dfb0993dc3ff59f4eb8b3a23bc33792019322 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:38:57 +0100 Subject: [PATCH 048/228] ref. wger --- ct/alpine-loki.sh | 71 ------------------------- ct/wger.sh | 65 ++++++++++++----------- install/alpine-loki-install.sh | 90 -------------------------------- install/wger-install.sh | 95 +++++++++++++++------------------- 4 files changed, 77 insertions(+), 244 deletions(-) delete mode 100644 ct/alpine-loki.sh delete mode 100644 install/alpine-loki-install.sh diff --git a/ct/alpine-loki.sh b/ct/alpine-loki.sh deleted file mode 100644 index 49f04ca13..000000000 --- a/ct/alpine-loki.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/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: hoholms -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/grafana/loki - -APP="Alpine-Loki" -var_tags="${var_tags:-alpine;monitoring}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-256}" -var_disk="${var_disk:-1}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - if ! apk -e info newt >/dev/null 2>&1; then - apk add -q newt - fi - LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - while true; do - CHOICE=$( - whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --menu "Select option" 11 58 3 \ - "1" "Check for Loki Updates" \ - "2" "Allow 0.0.0.0 for listening" \ - "3" "Allow only ${LXCIP} for listening" 3>&2 2>&1 1>&3 - ) - exit_status=$? - if [ $exit_status == 1 ]; then - clear - exit-script - fi - header_info - case $CHOICE in - 1) - $STD apk -U upgrade - msg_ok "Updated successfully!" - exit - ;; - 2) - sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=0.0.0.0/g" /etc/conf.d/loki - service loki restart - msg_ok "Allowed listening on all interfaces!" - exit - ;; - 3) - sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=$LXCIP/g" /etc/conf.d/loki - service loki restart - msg_ok "Allowed listening only on ${LXCIP}!" - exit - ;; - esac - done - exit 0 -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${APP} should be reachable by going to the following URL. - ${BL}http://${IP}:3100${CL} \n" -echo -e "Promtail should be reachable by going to the following URL. - ${BL}http://${IP}:9080${CL} \n" diff --git a/ct/wger.sh b/ct/wger.sh index b096937bd..b58302a30 100644 --- a/ct/wger.sh +++ b/ct/wger.sh @@ -2,14 +2,14 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/wger-project/wger APP="wger" var_tags="${var_tags:-management;fitness}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-6}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" @@ -23,38 +23,45 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /home/wger ]]; then + + if [[ ! -d /opt/wger ]]; then msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/wger-project/wger/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + + if check_for_gh_release "wger" "wger-project/wger"; then msg_info "Stopping Service" - systemctl stop wger + systemctl stop apache2 msg_ok "Stopped Service" - msg_info "Updating $APP to v${RELEASE}" - temp_file=$(mktemp) - curl -fsSL "https://github.com/wger-project/wger/archive/refs/tags/$RELEASE.tar.gz" -o "$temp_file" - tar xzf "$temp_file" - cp -rf wger-"$RELEASE"/* /home/wger/src - cd /home/wger/src - $STD pip install -r requirements_prod.txt --ignore-installed - $STD pip install -e . - $STD python3 manage.py migrate - $STD python3 manage.py collectstatic --no-input - $STD yarn install - $STD yarn build:css:sass - rm -rf "$temp_file" - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated $APP to v${RELEASE}" + msg_info "Backing up Data" + cp -r /opt/wger/db /opt/wger_db_backup + cp -r /opt/wger/media /opt/wger_media_backup + cp -r /opt/wger/settings /opt/wger_settings_backup + msg_ok "Backed up Data" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" "latest" "/opt/wger" + + msg_info "Restoring Data" + cp -r /opt/wger_db_backup/. /opt/wger/db + cp -r /opt/wger_media_backup/. /opt/wger/media + cp -r /opt/wger_settings_backup/. /opt/wger/settings + rm -rf /opt/wger_db_backup /opt/wger_media_backup /opt/wger_settings_backup + msg_ok "Restored Data" + + msg_info "Updating wger" + cd /opt/wger + $STD uv sync --no-dev + export DJANGO_SETTINGS_MODULE=settings.main + export PYTHONPATH=/opt/wger + $STD uv run python manage.py migrate + $STD uv run python manage.py collectstatic --no-input + msg_ok "Updated wger" msg_info "Starting Service" - systemctl start wger + systemctl start apache2 msg_ok "Started Service" - msg_ok "Updated successfully!" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" + msg_ok "Updated Successfully" fi exit } @@ -63,7 +70,7 @@ start build_container description -msg_ok "Completed successfully!\n" +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}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/alpine-loki-install.sh b/install/alpine-loki-install.sh deleted file mode 100644 index 6efc9f3a3..000000000 --- a/install/alpine-loki-install.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: hoholms -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/grafana/loki - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Loki" -$STD apk add loki -$STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki - -mkdir -p /var/lib/loki/{chunks,boltdb-shipper-active,boltdb-shipper-cache} -chown -R loki:grafana /var/lib/loki -mkdir -p /var/log/loki -chown -R loki:grafana /var/log/loki - -cat </etc/loki/loki-local-config.yaml -auth_enabled: false - -server: - http_listen_port: 3100 - log_level: info - -common: - instance_addr: 127.0.0.1 - path_prefix: /var/lib/loki - storage: - filesystem: - chunks_directory: /var/lib/loki/chunks - rules_directory: /var/lib/loki/rules - replication_factor: 1 - ring: - kvstore: - store: inmemory - -schema_config: - configs: - - from: 2020-10-24 - store: tsdb - object_store: filesystem - schema: v13 - index: - prefix: index_ - period: 24h - -query_range: - results_cache: - cache: - embedded_cache: - enabled: true - max_size_mb: 100 - -limits_config: - metric_aggregation_enabled: true - -ruler: - alertmanager_url: http://localhost:9093 -EOF - -chown loki:grafana /etc/loki/loki-local-config.yaml -chmod 644 /etc/loki/loki-local-config.yaml - -echo "output_log=\"\${output_log:-/var/log/loki/output.log}\"" >> /etc/init.d/loki -echo "error_log=\"\${error_log:-/var/log/loki/error.log}\"" >> /etc/init.d/loki -echo "start_stop_daemon_args=\"\${SSD_OPTS} -1 \${output_log} -2 \${error_log}\"" >> /etc/init.d/loki - -$STD rc-update add loki default -$STD rc-service loki start -msg_ok "Installed Loki" - -read -rp "Would you like to install Promtail? (y/N): " INSTALL_PROMTAIL -if [[ "${INSTALL_PROMTAIL,,}" =~ ^(y|yes)$ ]]; then - msg_info "Installing Promtail" - $STD apk add loki-promtail - $STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki-promtail - $STD rc-update add loki-promtail default - $STD rc-service loki-promtail start - msg_ok "Installed Promtail" -fi - -motd_ssh -customize diff --git a/install/wger-install.sh b/install/wger-install.sh index ddc90a179..763a073ae 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/wger-project/wger source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" @@ -14,48 +14,48 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y \ - git \ +$STD apt-get install -y \ apache2 \ libapache2-mod-wsgi-py3 msg_ok "Installed Dependencies" -msg_info "Installing Python" -$STD apt install -y python3-pip -rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED -msg_ok "Installed Python" - NODE_VERSION="22" NODE_MODULE="yarn,sass" setup_nodejs +setup_uv + +fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" "latest" "/opt/wger" msg_info "Setting up wger" -$STD adduser wger --disabled-password --gecos "" -mkdir /home/wger/db -touch /home/wger/db/database.sqlite -chown :www-data -R /home/wger/db -chmod g+w /home/wger/db /home/wger/db/database.sqlite -mkdir /home/wger/{static,media} -chmod o+w /home/wger/media -temp_dir=$(mktemp -d) -cd "$temp_dir" -RELEASE=$(curl -fsSL https://api.github.com/repos/wger-project/wger/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') -curl -fsSL "https://github.com/wger-project/wger/archive/refs/tags/$RELEASE.tar.gz" -o "$RELEASE.tar.gz" -tar xzf "$RELEASE".tar.gz -mv wger-"$RELEASE" /home/wger/src -cd /home/wger/src -$STD pip install -r requirements_prod.txt --ignore-installed -$STD pip install -e . -$STD wger create-settings --database-path /home/wger/db/database.sqlite -sed -i "s#home/wger/src/media#home/wger/media#g" /home/wger/src/settings.py -sed -i "/MEDIA_ROOT = '\/home\/wger\/media'/a STATIC_ROOT = '/home/wger/static'" /home/wger/src/settings.py -$STD wger bootstrap -$STD python3 manage.py collectstatic -rm -rf "$temp_dir" -echo "${RELEASE}" >/opt/wger_version.txt -msg_ok "Finished setting up wger" +mkdir -p /opt/wger/{db,static,media} +touch /opt/wger/db/database.sqlite +chown :www-data -R /opt/wger/db +chmod g+w /opt/wger/db /opt/wger/db/database.sqlite +chmod o+w /opt/wger/media +cd /opt/wger +$STD uv sync --no-dev +mkdir -p /opt/wger/settings +cat </opt/wger/settings/main.py +from wger.settings_global import * + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': '/opt/wger/db/database.sqlite', + } +} + +MEDIA_ROOT = '/opt/wger/media' +STATIC_ROOT = '/opt/wger/static' +EOF +touch /opt/wger/settings/__init__.py +export DJANGO_SETTINGS_MODULE=settings.main +export PYTHONPATH=/opt/wger +$STD uv run python manage.py migrate +$STD uv run python manage.py collectstatic --no-input +msg_ok "Set up wger" msg_info "Creating Service" cat </etc/apache2/sites-available/wger.conf - + Require all granted @@ -63,18 +63,20 @@ cat </etc/apache2/sites-available/wger.conf WSGIApplicationGroup %{GLOBAL} - WSGIDaemonProcess wger python-path=/home/wger/src python-home=/home/wger + WSGIDaemonProcess wger python-path=/opt/wger python-home=/opt/wger/.venv WSGIProcessGroup wger - WSGIScriptAlias / /home/wger/src/wger/wsgi.py + WSGIScriptAlias / /opt/wger/wger/wsgi.py WSGIPassAuthorization On + SetEnv DJANGO_SETTINGS_MODULE settings.main + SetEnv PYTHONPATH /opt/wger - Alias /static/ /home/wger/static/ - + Alias /static/ /opt/wger/static/ + Require all granted - Alias /media/ /home/wger/media/ - + Alias /media/ /opt/wger/media/ + Require all granted @@ -85,21 +87,6 @@ EOF $STD a2dissite 000-default.conf $STD a2ensite wger systemctl restart apache2 -cat </etc/systemd/system/wger.service -[Unit] -Description=wger Service -After=network.target - -[Service] -Type=simple -User=root -ExecStart=/usr/local/bin/wger start -a 0.0.0.0 -p 3000 -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now wger msg_ok "Created Service" motd_ssh From a7e84f87d5fcf9c1616a5367c0ce4b218a8bf2e0 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:43:28 +0100 Subject: [PATCH 049/228] add pip install --- ct/wger.sh | 1 + install/wger-install.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/ct/wger.sh b/ct/wger.sh index b58302a30..b87635bba 100644 --- a/ct/wger.sh +++ b/ct/wger.sh @@ -52,6 +52,7 @@ function update_script() { msg_info "Updating wger" cd /opt/wger $STD uv sync --no-dev + $STD uv pip install -e . export DJANGO_SETTINGS_MODULE=settings.main export PYTHONPATH=/opt/wger $STD uv run python manage.py migrate diff --git a/install/wger-install.sh b/install/wger-install.sh index 763a073ae..3eb392098 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -32,6 +32,7 @@ chmod g+w /opt/wger/db /opt/wger/db/database.sqlite chmod o+w /opt/wger/media cd /opt/wger $STD uv sync --no-dev +$STD uv pip install -e . mkdir -p /opt/wger/settings cat </opt/wger/settings/main.py from wger.settings_global import * From 3d4a95cbd74e8e573d5c7f43e65e0ba342aca759 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:46:29 +0100 Subject: [PATCH 050/228] test jotty --- ct/jotty.sh | 105 +++++++++++++++++++++++++++++++++++++++ install/jotty-install.sh | 85 +++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 ct/jotty.sh create mode 100644 install/jotty-install.sh diff --git a/ct/jotty.sh b/ct/jotty.sh new file mode 100644 index 000000000..9e3c39428 --- /dev/null +++ b/ct/jotty.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/fccview/jotty + +APP="jotty" +var_tags="${var_tags:-tasks;notes}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +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/jotty ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "jotty" "fccview/jotty"; then + msg_info "Stopping Service" + systemctl stop jotty + msg_ok "Stopped Service" + + msg_info "Backing up configuration & data" + cd /opt/jotty + cp ./.env /opt/app.env + $STD tar -cf /opt/data_config.tar ./data ./config + msg_ok "Backed up configuration & data" + + NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" + + msg_info "Updating jotty" + cd /opt/jotty + unset NODE_OPTIONS + export NODE_OPTIONS="--max-old-space-size=3072" + $STD yarn --frozen-lockfile + $STD yarn next telemetry disable + $STD yarn build + + [ -d "public" ] && cp -r public .next/standalone/ + [ -d "howto" ] && cp -r howto .next/standalone/ + mkdir -p .next/standalone/.next + cp -r .next/static .next/standalone/.next/ + + mv .next/standalone /tmp/jotty_standalone + rm -rf ./* .next .git .gitignore .yarn + mv /tmp/jotty_standalone/* . + mv /tmp/jotty_standalone/.[!.]* . 2>/dev/null || true + rm -rf /tmp/jotty_standalone + msg_ok "Updated jotty" + + msg_info "Restoring configuration & data" + mv /opt/app.env /opt/jotty/.env + $STD tar -xf /opt/data_config.tar + msg_ok "Restored configuration & data" + + msg_info "Updating Service" + cat </etc/systemd/system/jotty.service +[Unit] +Description=jotty server +After=network.target + +[Service] +WorkingDirectory=/opt/jotty +EnvironmentFile=/opt/jotty/.env +ExecStart=/usr/bin/node server.js +Restart=on-abnormal + +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reload + msg_ok "Updated Service" + + msg_info "Starting Service" + systemctl start jotty + msg_ok "Started Service" + rm /opt/data_config.tar + msg_ok "Updated successfully!" + 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/install/jotty-install.sh b/install/jotty-install.sh new file mode 100644 index 000000000..1545617c3 --- /dev/null +++ b/install/jotty-install.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/fccview/jotty + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +#fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" +wget -q https://github.com/fccview/jotty/releases/download/untagged-c9147656f5ebbb45b023/jotty-prebuild-develop.tar.gz -O /tmp/jotty.tar.gz +tar -xzf /tmp/jotty.tar.gz -C /opt/jotty --strip-components=1 + +msg_info "Setup jotty" +cd /opt/jotty +unset NODE_OPTIONS +export NODE_OPTIONS="--max-old-space-size=3072" +$STD yarn --frozen-lockfile +$STD yarn next telemetry disable +$STD yarn build + +[ -d "public" ] && cp -r public .next/standalone/ +[ -d "howto" ] && cp -r howto .next/standalone/ +mkdir -p .next/standalone/.next +cp -r .next/static .next/standalone/.next/ + +mv .next/standalone /tmp/jotty_standalone +rm -rf ./* .next .git .gitignore .yarn +mv /tmp/jotty_standalone/* . +mv /tmp/jotty_standalone/.[!.]* . 2>/dev/null || true +rm -rf /tmp/jotty_standalone + +mkdir -p data/{users,checklists,notes} + +cat </opt/jotty/.env +NODE_ENV=production + +# --- Uncomment to enable +# HTTPS=true +# SERVE_PUBLIC_IMAGES=yes +# SERVE_PUBLIC_FILES=yes +# SERVE_PUBLIC_VIDEOS=yes +# STOP_CHECK_UPDATES=yes +# --- For troubleshooting +# DEBUGGER=true + +# --- SSO with OIDC (optional) +# SSO_MODE=oidc +# OIDC_ISSUER= +# OIDC_CLIENT_ID= +# APP_URL= +# SSO_FALLBACK_LOCAL=yes +# OIDC_CLIENT_SECRET=your_client_secret +# OIDC_ADMIN_GROUPS=admins +EOF +msg_ok "Installed ${APPLICATION}" + +msg_info "Creating Service" +cat </etc/systemd/system/jotty.service +[Unit] +Description=jotty server +After=network.target + +[Service] +WorkingDirectory=/opt/jotty +EnvironmentFile=/opt/jotty/.env +ExecStart=/usr/bin/node server.js +Restart=on-abnormal + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now jotty +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 249113c2b91988498e05c9cfcc6bf1d0c24d0206 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:49:39 +0100 Subject: [PATCH 051/228] Update wger install and update scripts for pip usage Replaces 'uv sync --no-dev' and editable pip install with 'uv venv' and standard pip install in both wger.sh and wger-install.sh. This streamlines the installation and update process for wger. --- ct/wger.sh | 3 +-- install/wger-install.sh | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ct/wger.sh b/ct/wger.sh index b87635bba..4da90279d 100644 --- a/ct/wger.sh +++ b/ct/wger.sh @@ -51,8 +51,7 @@ function update_script() { msg_info "Updating wger" cd /opt/wger - $STD uv sync --no-dev - $STD uv pip install -e . + $STD uv pip install . export DJANGO_SETTINGS_MODULE=settings.main export PYTHONPATH=/opt/wger $STD uv run python manage.py migrate diff --git a/install/wger-install.sh b/install/wger-install.sh index 3eb392098..eff990e60 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -31,8 +31,8 @@ chown :www-data -R /opt/wger/db chmod g+w /opt/wger/db /opt/wger/db/database.sqlite chmod o+w /opt/wger/media cd /opt/wger -$STD uv sync --no-dev -$STD uv pip install -e . +$STD uv venv +$STD uv pip install . mkdir -p /opt/wger/settings cat </opt/wger/settings/main.py from wger.settings_global import * From 51a799c7d55011fc5817b4e49212cd931329c7b7 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:58:48 +0100 Subject: [PATCH 052/228] Refactor wger config to use .env and environment variables Replaces the custom settings directory with a .env file for configuration. Updates backup and restore logic to handle .env instead of settings, and sets required Django environment variables explicitly. Simplifies installation and update scripts for better maintainability and security. --- ct/wger.sh | 11 ++++++----- install/wger-install.sh | 30 ++++++++++++++---------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/ct/wger.sh b/ct/wger.sh index 4da90279d..ddfbf3e2c 100644 --- a/ct/wger.sh +++ b/ct/wger.sh @@ -37,7 +37,7 @@ function update_script() { msg_info "Backing up Data" cp -r /opt/wger/db /opt/wger_db_backup cp -r /opt/wger/media /opt/wger_media_backup - cp -r /opt/wger/settings /opt/wger_settings_backup + cp /opt/wger/.env /opt/wger_env_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" "latest" "/opt/wger" @@ -45,15 +45,16 @@ function update_script() { msg_info "Restoring Data" cp -r /opt/wger_db_backup/. /opt/wger/db cp -r /opt/wger_media_backup/. /opt/wger/media - cp -r /opt/wger_settings_backup/. /opt/wger/settings - rm -rf /opt/wger_db_backup /opt/wger_media_backup /opt/wger_settings_backup + cp /opt/wger_env_backup /opt/wger/.env + rm -rf /opt/wger_db_backup /opt/wger_media_backup /opt/wger_env_backup msg_ok "Restored Data" msg_info "Updating wger" cd /opt/wger - $STD uv pip install . + source /opt/wger/.env export DJANGO_SETTINGS_MODULE=settings.main - export PYTHONPATH=/opt/wger + export DJANGO_DB_DATABASE DJANGO_MEDIA_ROOT DJANGO_STATIC_ROOT SECRET_KEY + $STD uv pip install . $STD uv run python manage.py migrate $STD uv run python manage.py collectstatic --no-input msg_ok "Updated wger" diff --git a/install/wger-install.sh b/install/wger-install.sh index eff990e60..dfd0f689f 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -33,23 +33,18 @@ chmod o+w /opt/wger/media cd /opt/wger $STD uv venv $STD uv pip install . -mkdir -p /opt/wger/settings -cat </opt/wger/settings/main.py -from wger.settings_global import * - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': '/opt/wger/db/database.sqlite', - } -} - -MEDIA_ROOT = '/opt/wger/media' -STATIC_ROOT = '/opt/wger/static' +SECRET_KEY=$(openssl rand -base64 40) +cat </opt/wger/.env +DJANGO_DB_DATABASE=/opt/wger/db/database.sqlite +DJANGO_MEDIA_ROOT=/opt/wger/media +DJANGO_STATIC_ROOT=/opt/wger/static +SECRET_KEY=${SECRET_KEY} EOF -touch /opt/wger/settings/__init__.py export DJANGO_SETTINGS_MODULE=settings.main -export PYTHONPATH=/opt/wger +export DJANGO_DB_DATABASE=/opt/wger/db/database.sqlite +export DJANGO_MEDIA_ROOT=/opt/wger/media +export DJANGO_STATIC_ROOT=/opt/wger/static +export SECRET_KEY="${SECRET_KEY}" $STD uv run python manage.py migrate $STD uv run python manage.py collectstatic --no-input msg_ok "Set up wger" @@ -69,7 +64,10 @@ cat </etc/apache2/sites-available/wger.conf WSGIScriptAlias / /opt/wger/wger/wsgi.py WSGIPassAuthorization On SetEnv DJANGO_SETTINGS_MODULE settings.main - SetEnv PYTHONPATH /opt/wger + SetEnv DJANGO_DB_DATABASE /opt/wger/db/database.sqlite + SetEnv DJANGO_MEDIA_ROOT /opt/wger/media + SetEnv DJANGO_STATIC_ROOT /opt/wger/static + SetEnv SECRET_KEY ${SECRET_KEY} Alias /static/ /opt/wger/static/ From 1ffd81531c2b2b204627ddac71e9d0e920ac8754 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:22:30 +0100 Subject: [PATCH 053/228] test jotty --- install/jotty-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index 1545617c3..92944fe93 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -15,7 +15,7 @@ update_os NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs #fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" -wget -q https://github.com/fccview/jotty/releases/download/untagged-c9147656f5ebbb45b023/jotty-prebuild-develop.tar.gz -O /tmp/jotty.tar.gz +wget -q https://github.com/fccview/jotty/releases/download/develop/jotty-prebuild-develop.tar.gz -O /tmp/jotty.tar.gz tar -xzf /tmp/jotty.tar.gz -C /opt/jotty --strip-components=1 msg_info "Setup jotty" From 928beedc9d2a5d39fa7fa154550480633b3865ba Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:27:33 +0100 Subject: [PATCH 054/228] fixes --- ct/wger.sh | 7 ++----- install/jotty-install.sh | 3 ++- install/wger-install.sh | 30 +++++++++++++++++++----------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/ct/wger.sh b/ct/wger.sh index ddfbf3e2c..8629b4de3 100644 --- a/ct/wger.sh +++ b/ct/wger.sh @@ -35,7 +35,6 @@ function update_script() { msg_ok "Stopped Service" msg_info "Backing up Data" - cp -r /opt/wger/db /opt/wger_db_backup cp -r /opt/wger/media /opt/wger_media_backup cp /opt/wger/.env /opt/wger_env_backup msg_ok "Backed up Data" @@ -43,17 +42,15 @@ function update_script() { CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" "latest" "/opt/wger" msg_info "Restoring Data" - cp -r /opt/wger_db_backup/. /opt/wger/db cp -r /opt/wger_media_backup/. /opt/wger/media cp /opt/wger_env_backup /opt/wger/.env - rm -rf /opt/wger_db_backup /opt/wger_media_backup /opt/wger_env_backup + rm -rf /opt/wger_media_backup /opt/wger_env_backup msg_ok "Restored Data" msg_info "Updating wger" cd /opt/wger - source /opt/wger/.env + set -a && source /opt/wger/.env && set +a export DJANGO_SETTINGS_MODULE=settings.main - export DJANGO_DB_DATABASE DJANGO_MEDIA_ROOT DJANGO_STATIC_ROOT SECRET_KEY $STD uv pip install . $STD uv run python manage.py migrate $STD uv run python manage.py collectstatic --no-input diff --git a/install/jotty-install.sh b/install/jotty-install.sh index 92944fe93..ef44d1fdb 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -15,6 +15,7 @@ update_os NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs #fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" +mkdir -p /opt/jotty wget -q https://github.com/fccview/jotty/releases/download/develop/jotty-prebuild-develop.tar.gz -O /tmp/jotty.tar.gz tar -xzf /tmp/jotty.tar.gz -C /opt/jotty --strip-components=1 @@ -22,7 +23,7 @@ msg_info "Setup jotty" cd /opt/jotty unset NODE_OPTIONS export NODE_OPTIONS="--max-old-space-size=3072" -$STD yarn --frozen-lockfile +$STD yarn --frozen-lockfiled $STD yarn next telemetry disable $STD yarn build diff --git a/install/wger-install.sh b/install/wger-install.sh index dfd0f689f..d6b262c44 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -16,35 +16,38 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ apache2 \ - libapache2-mod-wsgi-py3 + libapache2-mod-wsgi-py3 \ + libpq-dev msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn,sass" setup_nodejs setup_uv +PG_VERSION="16" setup_postgresql +PG_DB_NAME="wger" PG_DB_USER="wger" setup_postgresql_db + fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" "latest" "/opt/wger" msg_info "Setting up wger" -mkdir -p /opt/wger/{db,static,media} -touch /opt/wger/db/database.sqlite -chown :www-data -R /opt/wger/db -chmod g+w /opt/wger/db /opt/wger/db/database.sqlite +mkdir -p /opt/wger/{static,media} chmod o+w /opt/wger/media cd /opt/wger $STD uv venv $STD uv pip install . SECRET_KEY=$(openssl rand -base64 40) cat </opt/wger/.env -DJANGO_DB_DATABASE=/opt/wger/db/database.sqlite +DJANGO_DB_ENGINE=django.db.backends.postgresql +DJANGO_DB_DATABASE=${PG_DB_NAME} +DJANGO_DB_USER=${PG_DB_USER} +DJANGO_DB_PASSWORD=${PG_DB_PASS} +DJANGO_DB_HOST=localhost +DJANGO_DB_PORT=5432 DJANGO_MEDIA_ROOT=/opt/wger/media DJANGO_STATIC_ROOT=/opt/wger/static SECRET_KEY=${SECRET_KEY} EOF +set -a && source /opt/wger/.env && set +a export DJANGO_SETTINGS_MODULE=settings.main -export DJANGO_DB_DATABASE=/opt/wger/db/database.sqlite -export DJANGO_MEDIA_ROOT=/opt/wger/media -export DJANGO_STATIC_ROOT=/opt/wger/static -export SECRET_KEY="${SECRET_KEY}" $STD uv run python manage.py migrate $STD uv run python manage.py collectstatic --no-input msg_ok "Set up wger" @@ -64,7 +67,12 @@ cat </etc/apache2/sites-available/wger.conf WSGIScriptAlias / /opt/wger/wger/wsgi.py WSGIPassAuthorization On SetEnv DJANGO_SETTINGS_MODULE settings.main - SetEnv DJANGO_DB_DATABASE /opt/wger/db/database.sqlite + SetEnv DJANGO_DB_ENGINE django.db.backends.postgresql + SetEnv DJANGO_DB_DATABASE ${PG_DB_NAME} + SetEnv DJANGO_DB_USER ${PG_DB_USER} + SetEnv DJANGO_DB_PASSWORD ${PG_DB_PASS} + SetEnv DJANGO_DB_HOST localhost + SetEnv DJANGO_DB_PORT 5432 SetEnv DJANGO_MEDIA_ROOT /opt/wger/media SetEnv DJANGO_STATIC_ROOT /opt/wger/static SetEnv SECRET_KEY ${SECRET_KEY} From ab4270ed83ec346833525b3893fd140bc05d2f0a Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:32:44 +0100 Subject: [PATCH 055/228] remove yarn --- install/jotty-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index ef44d1fdb..df98e70f9 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -23,9 +23,9 @@ msg_info "Setup jotty" cd /opt/jotty unset NODE_OPTIONS export NODE_OPTIONS="--max-old-space-size=3072" -$STD yarn --frozen-lockfiled -$STD yarn next telemetry disable -$STD yarn build +# $STD yarn --frozen-lockfiled +# $STD yarn next telemetry disable +# $STD yarn build [ -d "public" ] && cp -r public .next/standalone/ [ -d "howto" ] && cp -r howto .next/standalone/ From 6a1b5dd4ced84a74c52a47a2bb6859276025cbe0 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:45:21 +0100 Subject: [PATCH 056/228] Update wger-install.sh --- install/wger-install.sh | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/install/wger-install.sh b/install/wger-install.sh index d6b262c44..34d4f6394 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ libpq-dev msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn,sass" setup_nodejs +NODE_VERSION="22" NODE_MODULE="sass" setup_nodejs setup_uv PG_VERSION="16" setup_postgresql @@ -32,6 +32,9 @@ msg_info "Setting up wger" mkdir -p /opt/wger/{static,media} chmod o+w /opt/wger/media cd /opt/wger +$STD corepack enable +$STD npm install +$STD npm run build:css:sass $STD uv venv $STD uv pip install . SECRET_KEY=$(openssl rand -base64 40) @@ -46,6 +49,21 @@ DJANGO_MEDIA_ROOT=/opt/wger/media DJANGO_STATIC_ROOT=/opt/wger/static SECRET_KEY=${SECRET_KEY} EOF +cat <<'WSGI' >/opt/wger/wsgi_wrapper.py +import os +from pathlib import Path + +env_file = Path('/opt/wger/.env') +if env_file.exists(): + for line in env_file.read_text().splitlines(): + if line.strip() and not line.startswith('#') and '=' in line: + key, value = line.split('=', 1) + os.environ.setdefault(key.strip(), value.strip()) + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main') + +from wger.wsgi import application +WSGI set -a && source /opt/wger/.env && set +a export DJANGO_SETTINGS_MODULE=settings.main $STD uv run python manage.py migrate @@ -55,7 +73,7 @@ msg_ok "Set up wger" msg_info "Creating Service" cat </etc/apache2/sites-available/wger.conf - + Require all granted @@ -64,18 +82,8 @@ cat </etc/apache2/sites-available/wger.conf WSGIApplicationGroup %{GLOBAL} WSGIDaemonProcess wger python-path=/opt/wger python-home=/opt/wger/.venv WSGIProcessGroup wger - WSGIScriptAlias / /opt/wger/wger/wsgi.py + WSGIScriptAlias / /opt/wger/wsgi_wrapper.py WSGIPassAuthorization On - SetEnv DJANGO_SETTINGS_MODULE settings.main - SetEnv DJANGO_DB_ENGINE django.db.backends.postgresql - SetEnv DJANGO_DB_DATABASE ${PG_DB_NAME} - SetEnv DJANGO_DB_USER ${PG_DB_USER} - SetEnv DJANGO_DB_PASSWORD ${PG_DB_PASS} - SetEnv DJANGO_DB_HOST localhost - SetEnv DJANGO_DB_PORT 5432 - SetEnv DJANGO_MEDIA_ROOT /opt/wger/media - SetEnv DJANGO_STATIC_ROOT /opt/wger/static - SetEnv SECRET_KEY ${SECRET_KEY} Alias /static/ /opt/wger/static/ From 6b7bbf47d90d2ced7bacee8497b514667cbab212 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:53:29 +0100 Subject: [PATCH 057/228] migrations --- install/wger-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/wger-install.sh b/install/wger-install.sh index 34d4f6394..ac19e32d5 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -67,6 +67,10 @@ WSGI set -a && source /opt/wger/.env && set +a export DJANGO_SETTINGS_MODULE=settings.main $STD uv run python manage.py migrate +$STD uv run python manage.py loaddata languages +$STD uv run python manage.py loaddata gym_config +$STD uv run python manage.py loaddata groups +$STD uv run python manage.py loaddata site $STD uv run python manage.py collectstatic --no-input msg_ok "Set up wger" From 84922f5d461e8ff729b580d58d53ffb08f2520f4 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:07:07 +0100 Subject: [PATCH 058/228] Update wger-install.sh --- install/wger-install.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/install/wger-install.sh b/install/wger-install.sh index ac19e32d5..0e8e9ace7 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -72,6 +72,14 @@ $STD uv run python manage.py loaddata gym_config $STD uv run python manage.py loaddata groups $STD uv run python manage.py loaddata site $STD uv run python manage.py collectstatic --no-input +cat < Date: Thu, 22 Jan 2026 18:16:40 +0100 Subject: [PATCH 059/228] test --- install/jotty-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index df98e70f9..fff981cac 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -17,7 +17,7 @@ NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs #fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" mkdir -p /opt/jotty wget -q https://github.com/fccview/jotty/releases/download/develop/jotty-prebuild-develop.tar.gz -O /tmp/jotty.tar.gz -tar -xzf /tmp/jotty.tar.gz -C /opt/jotty --strip-components=1 +tar -xzf /tmp/jotty.tar.gz -C /opt/jotty msg_info "Setup jotty" cd /opt/jotty From ea78e8f0e8dfa5be974bad638deb5084ad8495d4 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 22 Jan 2026 18:17:02 +0100 Subject: [PATCH 060/228] test jotty --- install/jotty-install.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index fff981cac..fa676597a 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -21,22 +21,22 @@ tar -xzf /tmp/jotty.tar.gz -C /opt/jotty msg_info "Setup jotty" cd /opt/jotty -unset NODE_OPTIONS -export NODE_OPTIONS="--max-old-space-size=3072" -# $STD yarn --frozen-lockfiled -# $STD yarn next telemetry disable -# $STD yarn build +# unset NODE_OPTIONS +# export NODE_OPTIONS="--max-old-space-size=3072" +# # $STD yarn --frozen-lockfiled +# # $STD yarn next telemetry disable +# # $STD yarn build -[ -d "public" ] && cp -r public .next/standalone/ -[ -d "howto" ] && cp -r howto .next/standalone/ -mkdir -p .next/standalone/.next -cp -r .next/static .next/standalone/.next/ +# [ -d "public" ] && cp -r public .next/standalone/ +# [ -d "howto" ] && cp -r howto .next/standalone/ +# mkdir -p .next/standalone/.next +# cp -r .next/static .next/standalone/.next/ -mv .next/standalone /tmp/jotty_standalone -rm -rf ./* .next .git .gitignore .yarn -mv /tmp/jotty_standalone/* . -mv /tmp/jotty_standalone/.[!.]* . 2>/dev/null || true -rm -rf /tmp/jotty_standalone +# mv .next/standalone /tmp/jotty_standalone +# rm -rf ./* .next .git .gitignore .yarn +# mv /tmp/jotty_standalone/* . +# mv /tmp/jotty_standalone/.[!.]* . 2>/dev/null || true +# rm -rf /tmp/jotty_standalone mkdir -p data/{users,checklists,notes} From 84b6afe5dc2555a4834183327a3c43723d5c01e4 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 22 Jan 2026 18:25:52 +0100 Subject: [PATCH 061/228] romm fixes --- install/romm-install.sh | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/install/romm-install.sh b/install/romm-install.sh index 983619eed..a3b9855e0 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -15,7 +15,7 @@ setting_up_container network_check update_os -msg_info "Installing dependencies" +msg_info "Installing Dependencies" $STD apt-get install -y \ acl \ build-essential \ @@ -43,9 +43,9 @@ $STD apt-get install -y \ p7zip-full \ tzdata \ nginx -msg_ok "Installed dependencies" +msg_ok "Installed Dependencies" -UV_VERSION="0.7.19" PYTHON_VERSION="3.13" setup_uv +PYTHON_VERSION="3.13" setup_uv NODE_VERSION="22" setup_nodejs setup_mariadb MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db @@ -120,11 +120,9 @@ CONFIGEOF chmod 644 /var/lib/romm/config/config.yml msg_ok "Created configuration file" -msg_info "Installing RAHasher (RetroAchievements)" -fetch_and_deploy_gh_release "RetroAchievements" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux-*.zip" +fetch_and_deploy_gh_release "RAHasher" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux-*.zip" cp /opt/RALibretro/RAHasher /usr/bin/RAHasher chmod +x /usr/bin/RAHasher -msg_ok "Installed RAHasher" fetch_and_deploy_gh_release "romm" "rommapp/romm" @@ -166,23 +164,24 @@ EOF chmod 600 /opt/romm/.env msg_ok "Created environment file" -msg_info "Setup Romm backend" +msg_info "Setting up RomM Backend" cd /opt/romm export UV_CONCURRENT_DOWNLOADS=1 $STD uv sync --all-extras cd /opt/romm/backend $STD uv run alembic upgrade head -msg_ok "Installed backend" +msg_ok "Set up RomM Backend" -msg_info "Setup Romm frontend" +msg_info "Setting up RomM Frontend" cd /opt/romm/frontend $STD npm install $STD npm run build mkdir -p /opt/romm/frontend/dist/assets/romm ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets -msg_ok "Setup Romm frontend" -msg_info "Configuring nginx" +msg_ok "Set up RomM Frontend" + +msg_info "Configuring Nginx" cat >/etc/nginx/sites-available/romm <<'EOF' upstream romm_backend { server 127.0.0.1:5000; @@ -250,9 +249,9 @@ ln -sf /etc/nginx/sites-available/romm /etc/nginx/sites-enabled/romm $STD nginx -t systemctl restart nginx systemctl enable -q nginx -msg_ok "Configured nginx" +msg_ok "Configured Nginx" -msg_info "Creating services" +msg_info "Creating Services" cat >/etc/systemd/system/romm-backend.service < Date: Thu, 22 Jan 2026 18:30:22 +0100 Subject: [PATCH 062/228] fixes --- install/romm-install.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/install/romm-install.sh b/install/romm-install.sh index a3b9855e0..cc95a84be 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -16,12 +16,10 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ acl \ + git \ build-essential \ - gcc \ - g++ \ - make \ libssl-dev \ libffi-dev \ libmagic-dev \ From 8a80de71a3fc60b2154c036fa214f17851506920 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 22 Jan 2026 18:48:03 +0100 Subject: [PATCH 063/228] little bugfixes --- install/romm-install.sh | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/install/romm-install.sh b/install/romm-install.sh index cc95a84be..1af124cef 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -58,7 +58,7 @@ mkdir -p /opt/romm \ msg_ok "Created directories" msg_info "Creating configuration file" -cat >/var/lib/romm/config/config.yml <<'CONFIGEOF' +cat <<'EOF' >/var/lib/romm/config/config.yml # RomM Configuration File # Documentation: https://docs.romm.app/latest/Getting-Started/Configuration-File/ # Only uncomment the lines you want to use/modify @@ -114,7 +114,7 @@ cat >/var/lib/romm/config/config.yml <<'CONFIGEOF' # emulatorjs: # debug: false # cache_limit: null -CONFIGEOF +EOF chmod 644 /var/lib/romm/config/config.yml msg_ok "Created configuration file" @@ -130,7 +130,7 @@ systemctl restart redis-server systemctl enable -q --now redis-server AUTH_SECRET_KEY=$(openssl rand -hex 32) -cat >/opt/romm/.env </opt/romm/.env ROMM_BASE_PATH=/var/lib/romm ROMM_CONFIG_PATH=/var/lib/romm/config/config.yml WEB_CONCURRENCY=4 @@ -180,7 +180,7 @@ ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets msg_ok "Set up RomM Frontend" msg_info "Configuring Nginx" -cat >/etc/nginx/sites-available/romm <<'EOF' +cat <<'EOF' >/etc/nginx/sites-available/romm upstream romm_backend { server 127.0.0.1:5000; } @@ -244,13 +244,12 @@ EOF rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/romm /etc/nginx/sites-enabled/romm -$STD nginx -t systemctl restart nginx -systemctl enable -q nginx +systemctl enable -q --now nginx msg_ok "Configured Nginx" msg_info "Creating Services" -cat >/etc/systemd/system/romm-backend.service </etc/systemd/system/romm-backend.service [Unit] Description=RomM Backend After=network.target mariadb.service redis-server.service @@ -269,7 +268,7 @@ RestartSec=5 WantedBy=multi-user.target EOF -cat >/etc/systemd/system/romm-worker.service </etc/systemd/system/romm-worker.service [Unit] Description=RomM RQ Worker After=network.target mariadb.service redis-server.service romm-backend.service @@ -288,7 +287,7 @@ RestartSec=5 WantedBy=multi-user.target EOF -cat >/etc/systemd/system/romm-scheduler.service </etc/systemd/system/romm-scheduler.service [Unit] Description=RomM RQ Scheduler After=network.target mariadb.service redis-server.service romm-backend.service @@ -309,7 +308,7 @@ RestartSec=5 WantedBy=multi-user.target EOF -cat >/etc/systemd/system/romm-watcher.service </etc/systemd/system/romm-watcher.service [Unit] Description=RomM Filesystem Watcher After=network.target romm-backend.service From bc1fb085848b4df5d16b15837a1c18e3005ae8e8 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 22 Jan 2026 18:57:35 +0100 Subject: [PATCH 064/228] test logo --- install/romm-install.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/install/romm-install.sh b/install/romm-install.sh index 1af124cef..73f2ada65 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -201,6 +201,14 @@ server { try_files $uri $uri/ /index.html; } + # Static assets + location /assets { + alias /opt/romm/frontend/dist/assets; + try_files $uri $uri/ =404; + expires 1y; + add_header Cache-Control "public, immutable"; + } + # EmulatorJS player - requires COOP/COEP headers for SharedArrayBuffer location ~ ^/rom/.*/ejs$ { add_header Cross-Origin-Embedder-Policy "require-corp"; From b672074d380072052db24ccce2360627bc3c03c0 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 22 Jan 2026 20:06:37 +0100 Subject: [PATCH 065/228] fix --- install/jotty-install.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index fa676597a..ee224b5b6 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -15,11 +15,12 @@ update_os NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs #fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" -mkdir -p /opt/jotty -wget -q https://github.com/fccview/jotty/releases/download/develop/jotty-prebuild-develop.tar.gz -O /tmp/jotty.tar.gz -tar -xzf /tmp/jotty.tar.gz -C /opt/jotty - msg_info "Setup jotty" +mkdir -p /opt/jotty +wget -q https://github.com/fccview/jotty/releases/download/develop/jotty-prebuild-develop.tar.gz -O /opt/jotty.tar.gz +cd /opt/jotty +tar -xzf jotty.tar.gz + cd /opt/jotty # unset NODE_OPTIONS # export NODE_OPTIONS="--max-old-space-size=3072" From 7bf43f007e3b44f99065dc5556559c7a597129d2 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Thu, 22 Jan 2026 20:10:03 +0100 Subject: [PATCH 066/228] path issue --- install/jotty-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index ee224b5b6..a8ce9f634 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -18,7 +18,7 @@ NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs msg_info "Setup jotty" mkdir -p /opt/jotty wget -q https://github.com/fccview/jotty/releases/download/develop/jotty-prebuild-develop.tar.gz -O /opt/jotty.tar.gz -cd /opt/jotty +cd /opt tar -xzf jotty.tar.gz cd /opt/jotty From 7d5123bdd688fba4ebabc98e1d69b491a7adb472 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 22 Jan 2026 22:20:06 +0100 Subject: [PATCH 067/228] add fetch_and_deploy_archive --- misc/tools.func | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/misc/tools.func b/misc/tools.func index 260fb9a03..a86ff9fc6 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -5922,3 +5922,116 @@ EOF msg_ok "Docker setup completed" } + +# ------------------------------------------------------------------------------ +# Fetch and deploy archive from URL +# Downloads an archive (zip or tar.gz) from a URL and extracts it to a directory +# +# Usage: fetch_and_deploy_archive "url" "directory" +# url - URL to the archive (zip or tar.gz) +# directory - Destination path where the archive will be extracted +# +# Examples: +# fetch_and_deploy_archive "https://example.com/app.tar.gz" "/opt/myapp" +# fetch_and_deploy_archive "https://example.com/app.zip" "/opt/myapp" +# ------------------------------------------------------------------------------ +function fetch_and_deploy_archive() { + local url="$1" + local directory="$2" + + if [[ -z "$url" ]]; then + msg_error "URL parameter is required" + return 1 + fi + + if [[ -z "$directory" ]]; then + msg_error "Directory parameter is required" + return 1 + fi + + local filename="${url##*/}" + + local archive_type="zip" + if [[ "$filename" == *.tar.gz || "$filename" == *.tgz ]]; then + archive_type="tar" + fi + + msg_info "Downloading archive from $url" + + local tmpdir + tmpdir=$(mktemp -d) || { + msg_error "Failed to create temporary directory" + return 1 + } + + curl -fsSL -o "$tmpdir/$filename" "$url" || { + msg_error "Download failed: $url" + rm -rf "$tmpdir" + return 1 + } + + msg_info "Extracting archive to $directory" + + mkdir -p "$directory" + + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${directory:?}/"* + fi + + local unpack_tmp + unpack_tmp=$(mktemp -d) + + if [[ "$archive_type" == "zip" ]]; then + ensure_dependencies unzip + unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { + msg_error "Failed to extract ZIP archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + elif [[ "$archive_type" == "tar" ]]; then + tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { + msg_error "Failed to extract TAR archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + fi + + local top_entries + top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) + + if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then + local inner_dir="$top_entries" + shopt -s dotglob nullglob + if compgen -G "$inner_dir/*" >/dev/null; then + cp -r "$inner_dir"/* "$directory/" || { + msg_error "Failed to copy contents from $inner_dir to $directory" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Inner directory is empty: $inner_dir" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + else + shopt -s dotglob nullglob + if compgen -G "$unpack_tmp/*" >/dev/null; then + cp -r "$unpack_tmp"/* "$directory/" || { + msg_error "Failed to copy contents to $directory" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unpacked archive is empty" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + fi + + rm -rf "$tmpdir" "$unpack_tmp" + msg_ok "Successfully deployed archive to $directory" + return 0 +} + From 7cac87858f5be3fc94a7d4f54b65cc912fc6d0a1 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 22 Jan 2026 22:29:53 +0100 Subject: [PATCH 068/228] Fileflows test --- ct/fileflows.sh | 74 ++++++++++++++++++++++++++++++++++++ install/fileflows-install.sh | 47 +++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 ct/fileflows.sh create mode 100644 install/fileflows-install.sh diff --git a/ct/fileflows.sh b/ct/fileflows.sh new file mode 100644 index 000000000..03a0b5e0f --- /dev/null +++ b/ct/fileflows.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: kkroboth +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://fileflows.com/ + +APP="FileFlows" +var_tags="${var_tags:-media;automation}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" +var_gpu="${var_gpu:-yes}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/fileflows ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if ! [[ $(dpkg -s jq 2>/dev/null) ]]; then + $STD apt-get update + $STD apt-get install -y jq + fi + + update_available=$(curl -fsSL -X 'GET' "http://localhost:19200/api/status/update-available" -H 'accept: application/json' | jq .UpdateAvailable) + if [[ "${update_available}" == "true" ]]; then + msg_info "Stopping Service" + systemctl stop fileflows + msg_info "Stopped Service" + + msg_info "Creating Backup" + backup_filename="/opt/${APP}_backup_$(date +%F).tar.gz" + tar -czf "$backup_filename" -C /opt/fileflows Data + msg_ok "Backup Created" + + msg_info "Updating $APP to latest version" + temp_file=$(mktemp) + curl -fsSL https://fileflows.com/downloads/zip -o "$temp_file" + $STD unzip -o -d /opt/fileflows "$temp_file" + rm -rf "$temp_file" + rm -rf "$backup_filename" + msg_ok "Updated $APP to latest version" + + msg_info "Starting Service" + systemctl start fileflows + msg_ok "Started Service" + msg_ok "Updated successfully!" + else + msg_ok "No update required. ${APP} is already at latest version" + 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}:19200${CL}" diff --git a/install/fileflows-install.sh b/install/fileflows-install.sh new file mode 100644 index 000000000..0e04c881d --- /dev/null +++ b/install/fileflows-install.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: kkroboth +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://fileflows.com/ + +# Import Functions und Setup +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 \ + jq \ + imagemagick +msg_ok "Installed Dependencies" + +setup_hwaccel + +msg_info "Installing ASP.NET Core Runtime" +curl -fsSL https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -o packages-microsoft-prod.deb +$STD dpkg -i packages-microsoft-prod.deb +rm -rf packages-microsoft-prod.deb +$STD apt-get update +$STD apt-get install -y aspnetcore-runtime-8.0 +msg_ok "Installed ASP.NET Core Runtime" + +msg_info "Setup ${APPLICATION}" +$STD ln -svf /usr/bin/ffmpeg /usr/local/bin/ffmpeg +$STD ln -svf /usr/bin/ffprobe /usr/local/bin/ffprobe +temp_file=$(mktemp) +curl -fsSL https://fileflows.com/downloads/zip -o "$temp_file" +$STD unzip -d /opt/fileflows "$temp_file" +(cd /opt/fileflows/Server && dotnet FileFlows.Server.dll --systemd install --root true) +systemctl enable -q --now fileflows +rm -f "$temp_file" +msg_ok "Setup ${APPLICATION}" + +motd_ssh +customize +cleanup_lxc From fb99364f101e417bdda47952cbb5f548ca66effa Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 22 Jan 2026 22:33:53 +0100 Subject: [PATCH 069/228] update FileFlows --- ct/fileflows.sh | 2 +- install/fileflows-install.sh | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/ct/fileflows.sh b/ct/fileflows.sh index 03a0b5e0f..45a03faa1 100644 --- a/ct/fileflows.sh +++ b/ct/fileflows.sh @@ -21,7 +21,7 @@ color catch_errors function update_script() { - header_info +header_info check_container_storage check_container_resources diff --git a/install/fileflows-install.sh b/install/fileflows-install.sh index 0e04c881d..81ecfd1ac 100644 --- a/install/fileflows-install.sh +++ b/install/fileflows-install.sh @@ -15,32 +15,30 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ ffmpeg \ - jq \ imagemagick msg_ok "Installed Dependencies" setup_hwaccel +setup_deb822_repo \ + "microsoft" \ + "https://packages.microsoft.com/keys/microsoft-2025.asc" \ + "https://packages.microsoft.com/debian/13/prod/" \ + "trixie" +fetch_and_deploy_archive "https://fileflows.com/downloads/zip" "/opt/fileflows" msg_info "Installing ASP.NET Core Runtime" -curl -fsSL https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -o packages-microsoft-prod.deb -$STD dpkg -i packages-microsoft-prod.deb -rm -rf packages-microsoft-prod.deb -$STD apt-get update -$STD apt-get install -y aspnetcore-runtime-8.0 +$STD apt install -y aspnetcore-runtime-8.0 msg_ok "Installed ASP.NET Core Runtime" -msg_info "Setup ${APPLICATION}" +msg_info "Setup FileFlows" $STD ln -svf /usr/bin/ffmpeg /usr/local/bin/ffmpeg $STD ln -svf /usr/bin/ffprobe /usr/local/bin/ffprobe -temp_file=$(mktemp) -curl -fsSL https://fileflows.com/downloads/zip -o "$temp_file" -$STD unzip -d /opt/fileflows "$temp_file" -(cd /opt/fileflows/Server && dotnet FileFlows.Server.dll --systemd install --root true) +cd /opt/fileflows/Server +$STD dotnet FileFlows.Server.dll --systemd install --root true systemctl enable -q --now fileflows -rm -f "$temp_file" -msg_ok "Setup ${APPLICATION}" +msg_ok "Setup FileFlows" motd_ssh customize From 3606a8d6d0bc27d8c2a0ce616080dcd18945e2b4 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 22 Jan 2026 23:35:26 +0100 Subject: [PATCH 070/228] update FileFlows --- ct/fileflows.sh | 8 +------- install/fileflows-install.sh | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/ct/fileflows.sh b/ct/fileflows.sh index 45a03faa1..27cdfd6fc 100644 --- a/ct/fileflows.sh +++ b/ct/fileflows.sh @@ -45,13 +45,7 @@ header_info tar -czf "$backup_filename" -C /opt/fileflows Data msg_ok "Backup Created" - msg_info "Updating $APP to latest version" - temp_file=$(mktemp) - curl -fsSL https://fileflows.com/downloads/zip -o "$temp_file" - $STD unzip -o -d /opt/fileflows "$temp_file" - rm -rf "$temp_file" - rm -rf "$backup_filename" - msg_ok "Updated $APP to latest version" + fetch_and_deploy_archive "https://fileflows.com/downloads/zip" "/opt/fileflows" msg_info "Starting Service" systemctl start fileflows diff --git a/install/fileflows-install.sh b/install/fileflows-install.sh index 81ecfd1ac..a402560a5 100644 --- a/install/fileflows-install.sh +++ b/install/fileflows-install.sh @@ -32,7 +32,7 @@ msg_info "Installing ASP.NET Core Runtime" $STD apt install -y aspnetcore-runtime-8.0 msg_ok "Installed ASP.NET Core Runtime" -msg_info "Setup FileFlows" +msg_info "Setting up FileFlows" $STD ln -svf /usr/bin/ffmpeg /usr/local/bin/ffmpeg $STD ln -svf /usr/bin/ffprobe /usr/local/bin/ffprobe cd /opt/fileflows/Server From 5cb872d238f95d4dd4570a3322a4d56d0345f3d9 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:42:18 +0100 Subject: [PATCH 071/228] ref manyfold --- install/manyfold-install.sh | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 1c82d07d7..f64a24b75 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ libarchive-dev \ git \ libmariadb-dev \ @@ -24,23 +24,14 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" setup_imagemagick - PG_VERSION="16" setup_postgresql - +PG_DB_NAME="manyfold" PG_DB_USER="manyfold" setup_postgresql_db fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" -msg_info "Configuring manyfold environment" +msg_info "Configuring Manyfold" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') RELEASE=$(get_latest_github_release "manyfold3d/manyfold") -DB_NAME=manyfold -DB_USER=manyfold -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" useradd -m -s /usr/bin/bash manyfold cat </opt/manyfold/.env export APP_VERSION=${RELEASE} @@ -50,9 +41,9 @@ export PUBLIC_PORT=5000 export REDIS_URL=redis://127.0.0.1:6379/1 export DATABASE_ADAPTER=postgresql export DATABASE_HOST=127.0.0.1 -export DATABASE_USER=${DB_USER} -export DATABASE_PASSWORD=${DB_PASS} -export DATABASE_NAME=${DB_NAME} +export DATABASE_USER=${PG_DB_USER} +export DATABASE_PASSWORD=${PG_DB_PASS} +export DATABASE_NAME=${PG_DB_NAME} export DATABASE_CONNECTION_POOL=16 export MULTIUSER=enabled export HTTPS_ONLY=false @@ -79,8 +70,8 @@ bin/rails credentials:edit bin/rails db:migrate bin/rails assets:precompile EOF -$STD mkdir -p /opt/manyfold/data -msg_ok "Configured manyfold environment" +$STD mkdir -p /opt/manyfold_data +msg_ok "Configured Manyfold" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby @@ -92,7 +83,7 @@ chmod +x /opt/manyfold/user_setup.sh npm install --global corepack $STD sudo -u manyfold bash /opt/manyfold/user_setup.sh rm -f /opt/manyfold/user_setup.sh -msg_ok "Installed manyfold" +msg_ok "Installed Manyfold" msg_info "Creating Services" source /opt/manyfold/.env From 5b139000a865560f3dc9fcdd6a8c6e1d03957447 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:38:09 +0100 Subject: [PATCH 072/228] test update --- ct/manyfold.sh | 79 ++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/ct/manyfold.sh b/ct/manyfold.sh index 08ea18617..3b8c74b70 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -30,66 +30,69 @@ function update_script() { fi if check_for_gh_release "manyfold" "manyfold3d/manyfold"; then - msg_info "Stopping Service" + msg_info "Stopping Services" systemctl stop manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 - msg_ok "Stopped Service" + msg_ok "Stopped Services" - msg_info "Backing up data" - source /opt/manyfold/.env - mv /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold/app/config/master.key ~/ - $STD tar -cvzf "/opt/manyfold_${APP_VERSION}_backup.tar.gz" /opt/manyfold/app/ - rm -rf /opt/manyfold/app/ - msg_ok "Backed-up data" + msg_info "Backing up Data" + CURRENT_VERSION=$(grep -oP 'APP_VERSION=\K[^ ]+' /opt/manyfold/.env || echo "unknown") + cp -r /opt/manyfold/app/storage /opt/manyfold_storage_backup 2>/dev/null || true + cp -r /opt/manyfold/app/tmp /opt/manyfold_tmp_backup 2>/dev/null || true + cp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold_credentials.yml.enc 2>/dev/null || true + cp /opt/manyfold/app/config/master.key /opt/manyfold_master.key 2>/dev/null || true + $STD tar -czf "/opt/manyfold_${CURRENT_VERSION}_backup.tar.gz" -C /opt/manyfold app + msg_ok "Backed up Data" - fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" - msg_info "Configuring manyfold environment" + msg_info "Configuring Manyfold" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') RELEASE=$(get_latest_github_release "manyfold3d/manyfold") sed -i "s/^export APP_VERSION=.*/export APP_VERSION=$RELEASE/" "/opt/manyfold/.env" - cat </opt/manyfold/user_setup.sh -#!/bin/bash - -source /opt/manyfold/.env -export PATH="/home/manyfold/.rbenv/bin:\$PATH" -eval "\$(/home/manyfold/.rbenv/bin/rbenv init - bash)" -cd /opt/manyfold/app -rbenv global $RUBY_INSTALL_VERSION -gem install bundler -bundle install -gem install sidekiq -gem install foreman -corepack enable yarn -corepack prepare $YARN_VERSION --activate -corepack use $YARN_VERSION -bin/rails db:migrate -bin/rails assets:precompile -EOF - msg_ok "Configured manyfold environment" + msg_ok "Configured Manyfold" RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby msg_info "Installing Manyfold" chown -R manyfold:manyfold /home/manyfold/.rbenv - rm -rf /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config/credentials.yml.enc - mv ~/storage ~/tmp /opt/manyfold/app/ - mv ~/credentials.yml.enc ~/master.key /opt/manyfold/app/config/ chown -R manyfold:manyfold /opt/manyfold - chmod +x /opt/manyfold/user_setup.sh - $STD sudo -u manyfold bash /opt/manyfold/user_setup.sh - rm -f /opt/manyfold/user_setup.sh - msg_ok "Installed manyfold" - msg_info "Restoring Service" + sudo -u manyfold bash -c ' + source /opt/manyfold/.env + export PATH="/home/manyfold/.rbenv/bin:$PATH" + eval "$(/home/manyfold/.rbenv/bin/rbenv init - bash)" + cd /opt/manyfold/app + gem install bundler sidekiq foreman + bundle install + corepack enable yarn + corepack prepare '"$YARN_VERSION"' --activate + corepack use '"$YARN_VERSION"' + bin/rails db:migrate + bin/rails assets:precompile + ' + msg_ok "Installed Manyfold" + + msg_info "Restoring Data" + rm -rf /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold/app/config/master.key + cp -r /opt/manyfold_storage_backup /opt/manyfold/app/storage 2>/dev/null || true + cp -r /opt/manyfold_tmp_backup /opt/manyfold/app/tmp 2>/dev/null || true + cp /opt/manyfold_credentials.yml.enc /opt/manyfold/app/config/credentials.yml.enc 2>/dev/null || true + cp /opt/manyfold_master.key /opt/manyfold/app/config/master.key 2>/dev/null || true + chown -R manyfold:manyfold /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config + rm -rf /opt/manyfold_storage_backup /opt/manyfold_tmp_backup /opt/manyfold_credentials.yml.enc /opt/manyfold_master.key + msg_ok "Restored Data" + + msg_info "Restarting Services" source /opt/manyfold/.env export PATH="/home/manyfold/.rbenv/shims:/home/manyfold/.rbenv/bin:$PATH" $STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile for f in /etc/systemd/system/manyfold-*.service; do sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" done + systemctl daemon-reload systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 - msg_ok "Restored Service" + msg_ok "Restarted Services" msg_ok "Updated successfully!" fi exit From b58041a70a8d7fa2cb173e7f29f3d455a2e1b5ad Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:45:03 +0100 Subject: [PATCH 073/228] finalize dawarich --- install/dawarich-install.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/install/dawarich-install.sh b/install/dawarich-install.sh index 52dc1d462..0adc17a14 100644 --- a/install/dawarich-install.sh +++ b/install/dawarich-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ build-essential \ git \ libpq-dev \ @@ -32,7 +32,7 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" PG_VERSION="17" PG_MODULES="postgis-3" setup_postgresql -PG_DB_NAME="dawarich_production" PG_DB_USER="dawarich" PG_DB_EXTENSIONS="postgis" setup_postgresql_db +PG_DB_NAME="dawarich_db" PG_DB_USER="dawarich" PG_DB_EXTENSIONS="postgis" setup_postgresql_db fetch_and_deploy_gh_release "dawarich" "Freika/dawarich" "tarball" "latest" "/opt/dawarich/app" @@ -43,7 +43,6 @@ msg_ok "Set up Directories" msg_info "Configuring Environment" SECRET_KEY_BASE=$(openssl rand -hex 64) RELEASE=$(get_latest_github_release "Freika/dawarich") -import_local_ip cat </opt/dawarich/.env RAILS_ENV=production SECRET_KEY_BASE=${SECRET_KEY_BASE} @@ -62,7 +61,6 @@ EOF msg_ok "Configured Environment" NODE_VERSION="22" setup_nodejs - RUBY_VERSION=$(cat /opt/dawarich/app/.ruby-version 2>/dev/null || echo "3.4.6") RUBY_VERSION=${RUBY_VERSION} RUBY_INSTALL_RAILS="false" setup_ruby @@ -71,14 +69,11 @@ cd /opt/dawarich/app source /root/.profile export PATH="/root/.rbenv/shims:/root/.rbenv/bin:$PATH" eval "$(/root/.rbenv/bin/rbenv init - bash)" - set -a && source /opt/dawarich/.env && set +a - $STD gem install bundler $STD bundle config set --local deployment 'true' $STD bundle config set --local without 'development test' $STD bundle install - if [[ -f /opt/dawarich/package.json ]]; then cd /opt/dawarich $STD npm install @@ -86,7 +81,6 @@ if [[ -f /opt/dawarich/package.json ]]; then elif [[ -f /opt/dawarich/app/package.json ]]; then $STD npm install fi - $STD bundle exec rake assets:precompile $STD bundle exec rails db:prepare $STD bundle exec rake data:migrate From 08b8bd0a9fb27810576323324af317893f164a68 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:48:36 +0100 Subject: [PATCH 074/228] finalize tracearr --- install/tracearr-install.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh index 57c71d715..995b9dd04 100644 --- a/install/tracearr-install.sh +++ b/install/tracearr-install.sh @@ -38,10 +38,7 @@ $STD timescaledb-tune -yes -memory "$ram_for_tsdb"MB $STD systemctl restart postgresql msg_ok "Installed TimescaleDB" -msg_info "Creating PostgreSQL Database" -PG_DB_NAME="tracearr" PG_DB_USER="tracearr" PG_DB_EXTENSIONS="timescaledb,timescaledb_toolkit" setup_postgresql_db -msg_ok "Created PostgreSQL Database" - +PG_DB_NAME="tracearr_db" PG_DB_USER="tracearr" PG_DB_EXTENSIONS="timescaledb,timescaledb_toolkit" setup_postgresql_db fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" msg_info "Building Tracearr" @@ -141,9 +138,7 @@ User=tracearr [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now postgresql -systemctl enable -q --now redis-server -systemctl enable -q --now tracearr +systemctl enable -q --now postgresql redis-server tracearr msg_ok "Created Services" motd_ssh From cef934fff083bf701259e5f6e45c10b280759ab1 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:49:34 +0100 Subject: [PATCH 075/228] verbose --- install/manyfold-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index f64a24b75..3fe930eb2 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -80,7 +80,7 @@ msg_info "Installing Manyfold" chown -R manyfold:manyfold /home/manyfold/.rbenv chown -R manyfold:manyfold /opt/manyfold chmod +x /opt/manyfold/user_setup.sh -npm install --global corepack +$STD npm install --global corepack $STD sudo -u manyfold bash /opt/manyfold/user_setup.sh rm -f /opt/manyfold/user_setup.sh msg_ok "Installed Manyfold" From cede4720ee77ebbf2ad42c0e77cddd32bae44756 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:51:48 +0100 Subject: [PATCH 076/228] corepack fixes (sha value) --- ct/tracearr.sh | 8 ++++++-- install/tracearr-install.sh | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ct/tracearr.sh b/ct/tracearr.sh index 050477041..d744db71b 100644 --- a/ct/tracearr.sh +++ b/ct/tracearr.sh @@ -33,8 +33,12 @@ function update_script() { systemctl stop tracearr postgresql redis msg_ok "Stopped Services" - PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')" - NODE_VERSION="22" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs + msg_info "Updating pnpm" + PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]' | cut -d'+' -f1)" + export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 + $STD corepack prepare pnpm@${PNPM_VERSION} --activate + msg_ok "Updated pnpm" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" msg_info "Building Tracearr" diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh index 995b9dd04..6b03d51d0 100644 --- a/install/tracearr-install.sh +++ b/install/tracearr-install.sh @@ -17,10 +17,16 @@ msg_info "Installing Dependencies" $STD apt install -y redis-server msg_ok "Installed Dependencies" -PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')" -NODE_VERSION="22" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs +NODE_VERSION="22" setup_nodejs PG_VERSION="18" setup_postgresql +msg_info "Installing pnpm" +PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]' | cut -d'+' -f1)" +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +$STD corepack enable pnpm +$STD corepack prepare pnpm@${PNPM_VERSION} --activate +msg_ok "Installed pnpm" + msg_info "Installing TimescaleDB" setup_deb822_repo \ "timescaledb" \ From 87477f3c727e841ed5012a1139ec349d93ec1398 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:54:14 +0100 Subject: [PATCH 077/228] add alpine rustypaste --- ct/alpine-rustypaste.sh | 71 ++++++++++++++++++++++++++++ ct/headers/alpine-rustypaste | 6 +++ ct/rustypaste.sh | 23 +++++---- frontend/public/json/rustypaste.json | 11 +++++ install/alpine-rustypaste-install.sh | 6 ++- install/rustypaste-install.sh | 5 +- 6 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 ct/alpine-rustypaste.sh create mode 100644 ct/headers/alpine-rustypaste diff --git a/ct/alpine-rustypaste.sh b/ct/alpine-rustypaste.sh new file mode 100644 index 000000000..27565d852 --- /dev/null +++ b/ct/alpine-rustypaste.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/orhun/rustypaste + +APP="Alpine-RustyPaste" +var_tags="${var_tags:-alpine;pastebin;storage}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +var_disk="${var_disk:-4}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.23}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /opt/rustypaste/rustypaste ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "rustypaste" "orhun/rustypaste"; then + msg_info "Stopping Services" + rc-service rustypaste stop + msg_ok "Stopped Services" + + msg_info "Creating Backup" + tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" /opt/rustypaste/upload 2>/dev/null || true + cp /opt/rustypaste/config.toml /tmp/rustypaste_config.toml.bak + msg_ok "Backup Created" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-musl.tar.gz" + + + msg_info "Updating RustyPaste" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-musl.tar.gz" + mv /tmp/rustypaste_config.toml.bak /opt/rustypaste/config.toml + msg_ok "Updated RustyPaste" + + msg_info "Starting Services" + rc-service rustypaste start + msg_ok "Started Services" + msg_ok "Updated successfully!" + fi + + if check_for_gh_release "rustypaste-cli" "orhun/rustypaste-cli"; then + msg_info "Updating RustyPaste CLI" + fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-musl.tar.gz" + msg_ok "Updated RustyPaste CLI" + 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}:8000${CL}" diff --git a/ct/headers/alpine-rustypaste b/ct/headers/alpine-rustypaste new file mode 100644 index 000000000..ed3b33df2 --- /dev/null +++ b/ct/headers/alpine-rustypaste @@ -0,0 +1,6 @@ + ___ __ _ ____ __ ____ __ + / | / /___ (_)___ ___ / __ \__ _______/ /___ __/ __ \____ ______/ /____ + / /| | / / __ \/ / __ \/ _ \____/ /_/ / / / / ___/ __/ / / / /_/ / __ `/ ___/ __/ _ \ + / ___ |/ / /_/ / / / / / __/____/ _, _/ /_/ (__ ) /_/ /_/ / ____/ /_/ (__ ) /_/ __/ +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_|\__,_/____/\__/\__, /_/ \__,_/____/\__/\___/ + /_/ /____/ diff --git a/ct/rustypaste.sh b/ct/rustypaste.sh index a4f1bb6da..2da17f16e 100644 --- a/ct/rustypaste.sh +++ b/ct/rustypaste.sh @@ -24,8 +24,8 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f "/opt/rustypaste/target/release/rustypaste" ]]; then - msg_error "No rustypaste Installation Found!" + if [[ ! -f /opt/rustypaste/rustypaste ]]; then + msg_error "No ${APP} Installation Found!" exit fi @@ -35,22 +35,27 @@ function update_script() { msg_ok "Stopped Services" msg_info "Creating Backup" - tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" "/opt/rustypaste/upload" + tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" /opt/rustypaste/upload 2>/dev/null || true + cp /opt/rustypaste/config.toml /tmp/rustypaste_config.toml.bak msg_ok "Backup Created" - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "tarball" "latest" "/opt/rustypaste" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-gnu.tar.gz" - msg_info "Updating rustypaste" - cd /opt/rustypaste - sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml - $STD cargo build --locked --release - msg_ok "Updated rustypaste" + msg_info "Restoring Data" + mv /tmp/rustypaste_config.toml.bak /opt/rustypaste/config.toml + tar -xzf "/opt/rustypaste_backup_$(date +%F).tar.gz" -C /opt/rustypaste/upload 2>/dev/null || true + rm -rf /opt/rustypaste_backup_$(date +%F).tar.gz + msg_ok "Restored Data" msg_info "Starting Services" systemctl start rustypaste msg_ok "Started Services" msg_ok "Updated successfully!" fi + + if check_for_gh_release "rustypaste-cli" "orhun/rustypaste-cli"; then + fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-gnu.tar.gz" + fi exit } diff --git a/frontend/public/json/rustypaste.json b/frontend/public/json/rustypaste.json index e71f72687..2ac17c188 100644 --- a/frontend/public/json/rustypaste.json +++ b/frontend/public/json/rustypaste.json @@ -25,6 +25,17 @@ "os": "Debian", "version": "13" } + }, + { + "type": "alpine", + "script": "ct/alpine-rustypaste.sh", + "resources": { + "cpu": 1, + "ram": 256, + "hdd": 4, + "os": "Alpine", + "version": "3.22" + } } ], "default_credentials": { diff --git a/install/alpine-rustypaste-install.sh b/install/alpine-rustypaste-install.sh index ddc9ec9cf..18e806270 100644 --- a/install/alpine-rustypaste-install.sh +++ b/install/alpine-rustypaste-install.sh @@ -14,11 +14,12 @@ network_check update_os fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-musl.tar.gz" +fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-musl.tar.gz" -msg_info "Setting up rustypaste" +msg_info "Setting up RustyPaste" cd /opt/rustypaste sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml -msg_ok "Set up rustypaste" +msg_ok "Set up RustyPaste" msg_info "Creating Service" cat <<'EOF' >/etc/init.d/rustypaste @@ -44,3 +45,4 @@ msg_ok "Created Service" motd_ssh customize +cleanup_lxc diff --git a/install/rustypaste-install.sh b/install/rustypaste-install.sh index abb0b88ff..53e2caf57 100644 --- a/install/rustypaste-install.sh +++ b/install/rustypaste-install.sh @@ -14,11 +14,12 @@ network_check update_os fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-gnu.tar.gz" +fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-gnu.tar.gz" -msg_info "Setting up rustypaste" +msg_info "Setting up RustyPaste" cd /opt/rustypaste sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml -msg_ok "Set up rustypaste" +msg_ok "Set up RustyPaste" msg_info "Creating Service" cat </etc/systemd/system/rustypaste.service From 1623517a02deefb1fe6c47686b856087beeda1ca Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:58:16 +0100 Subject: [PATCH 078/228] alpine --- ct/alpine-rustypaste.sh | 38 +++++++--------------------- install/alpine-rustypaste-install.sh | 34 +++++++------------------ 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/ct/alpine-rustypaste.sh b/ct/alpine-rustypaste.sh index 27565d852..89451084f 100644 --- a/ct/alpine-rustypaste.sh +++ b/ct/alpine-rustypaste.sh @@ -24,40 +24,20 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f /opt/rustypaste/rustypaste ]]; then + if ! apk info -e rustypaste >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit fi - if check_for_gh_release "rustypaste" "orhun/rustypaste"; then - msg_info "Stopping Services" - rc-service rustypaste stop - msg_ok "Stopped Services" + msg_info "Updating RustyPaste" + $STD apk update + $STD apk upgrade rustypaste rustypaste-cli --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community + msg_ok "Updated RustyPaste" - msg_info "Creating Backup" - tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" /opt/rustypaste/upload 2>/dev/null || true - cp /opt/rustypaste/config.toml /tmp/rustypaste_config.toml.bak - msg_ok "Backup Created" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-musl.tar.gz" - - - msg_info "Updating RustyPaste" - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-musl.tar.gz" - mv /tmp/rustypaste_config.toml.bak /opt/rustypaste/config.toml - msg_ok "Updated RustyPaste" - - msg_info "Starting Services" - rc-service rustypaste start - msg_ok "Started Services" - msg_ok "Updated successfully!" - fi - - if check_for_gh_release "rustypaste-cli" "orhun/rustypaste-cli"; then - msg_info "Updating RustyPaste CLI" - fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-musl.tar.gz" - msg_ok "Updated RustyPaste CLI" - fi + msg_info "Restarting Services" + $STD rc-service rustypaste restart + msg_ok "Restarted Services" + msg_ok "Updated successfully!" exit } diff --git a/install/alpine-rustypaste-install.sh b/install/alpine-rustypaste-install.sh index 18e806270..18aeb3cf0 100644 --- a/install/alpine-rustypaste-install.sh +++ b/install/alpine-rustypaste-install.sh @@ -13,34 +13,18 @@ setting_up_container network_check update_os -fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-musl.tar.gz" -fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-musl.tar.gz" +msg_info "Installing RustyPaste" +$STD apk add --no-cache rustypaste rustypaste-openrc rustypaste-cli --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community +msg_ok "Installed RustyPaste" -msg_info "Setting up RustyPaste" -cd /opt/rustypaste -sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml -msg_ok "Set up RustyPaste" +msg_info "Configuring RustyPaste" +mkdir -p /var/lib/rustypaste +sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' /etc/rustypaste/config.toml +msg_ok "Configured RustyPaste" msg_info "Creating Service" -cat <<'EOF' >/etc/init.d/rustypaste -#!/sbin/openrc-run - -name="rustypaste" -description="rustypaste Service" -directory="/opt/rustypaste" -command="/opt/rustypaste/rustypaste" -command_args="" -pidfile="/run/${RC_SVCNAME}.pid" -command_background="yes" -start_stop_daemon_args="--user root" - -depend() { - need net -} -EOF -chmod +x /etc/init.d/rustypaste -rc-update add rustypaste default -rc-service rustypaste start +$STD rc-update add rustypaste default +$STD rc-service rustypaste start msg_ok "Created Service" motd_ssh From ccd518f96be8ff968123cb6a96bcec52f87091c8 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 12:02:18 +0100 Subject: [PATCH 079/228] fixes --- ct/alpine-rustypaste.sh | 2 +- install/alpine-rustypaste-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/alpine-rustypaste.sh b/ct/alpine-rustypaste.sh index 89451084f..893d0d05d 100644 --- a/ct/alpine-rustypaste.sh +++ b/ct/alpine-rustypaste.sh @@ -31,7 +31,7 @@ function update_script() { msg_info "Updating RustyPaste" $STD apk update - $STD apk upgrade rustypaste rustypaste-cli --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community + $STD apk upgrade rustypaste --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community msg_ok "Updated RustyPaste" msg_info "Restarting Services" diff --git a/install/alpine-rustypaste-install.sh b/install/alpine-rustypaste-install.sh index 18aeb3cf0..1f645c71e 100644 --- a/install/alpine-rustypaste-install.sh +++ b/install/alpine-rustypaste-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing RustyPaste" -$STD apk add --no-cache rustypaste rustypaste-openrc rustypaste-cli --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community +$STD apk add --no-cache rustypaste --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community msg_ok "Installed RustyPaste" msg_info "Configuring RustyPaste" From 972e1c18446890efdf6474f6d9be21bc9baf471c Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 23 Jan 2026 13:50:18 +0100 Subject: [PATCH 080/228] Add deb handling to fetch_and_deploy_archive --- misc/tools.func | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index a86ff9fc6..1e1b86b5c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -5925,15 +5925,17 @@ EOF # ------------------------------------------------------------------------------ # Fetch and deploy archive from URL -# Downloads an archive (zip or tar.gz) from a URL and extracts it to a directory +# Downloads an archive (zip, tar.gz, or .deb) from a URL and extracts/installs it # # Usage: fetch_and_deploy_archive "url" "directory" -# url - URL to the archive (zip or tar.gz) +# url - URL to the archive (zip, tar.gz, or .deb) # directory - Destination path where the archive will be extracted +# (not used for .deb packages) # # Examples: # fetch_and_deploy_archive "https://example.com/app.tar.gz" "/opt/myapp" # fetch_and_deploy_archive "https://example.com/app.zip" "/opt/myapp" +# fetch_and_deploy_archive "https://example.com/package.deb" "" # ------------------------------------------------------------------------------ function fetch_and_deploy_archive() { local url="$1" @@ -5944,19 +5946,16 @@ function fetch_and_deploy_archive() { return 1 fi - if [[ -z "$directory" ]]; then - msg_error "Directory parameter is required" - return 1 - fi - local filename="${url##*/}" local archive_type="zip" if [[ "$filename" == *.tar.gz || "$filename" == *.tgz ]]; then archive_type="tar" + elif [[ "$filename" == *.deb ]]; then + archive_type="deb" fi - msg_info "Downloading archive from $url" + msg_info "Downloading from $url" local tmpdir tmpdir=$(mktemp -d) || { @@ -5970,6 +5969,29 @@ function fetch_and_deploy_archive() { return 1 } + if [[ "$archive_type" == "deb" ]]; then + msg_info "Installing .deb package" + + chmod 644 "$tmpdir/$filename" + $STD apt install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" + rm -rf "$tmpdir" + return 1 + } + } + + rm -rf "$tmpdir" + msg_ok "Successfully installed .deb package" + return 0 + fi + + if [[ -z "$directory" ]]; then + msg_error "Directory parameter is required for archive extraction" + rm -rf "$tmpdir" + return 1 + fi + msg_info "Extracting archive to $directory" mkdir -p "$directory" From ea26faffdabfe6c13d034eb4fb0bd730dacfd095 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:07:03 +0000 Subject: [PATCH 081/228] Delete tracearr (ct) after migration to ProxmoxVE (#1331) Co-authored-by: github-actions[bot] --- ct/headers/tracearr | 6 -- ct/tracearr.sh | 90 ----------------- frontend/public/json/tracearr.json | 35 ------- install/tracearr-install.sh | 152 ----------------------------- 4 files changed, 283 deletions(-) delete mode 100644 ct/headers/tracearr delete mode 100644 ct/tracearr.sh delete mode 100644 frontend/public/json/tracearr.json delete mode 100644 install/tracearr-install.sh diff --git a/ct/headers/tracearr b/ct/headers/tracearr deleted file mode 100644 index aee118c88..000000000 --- a/ct/headers/tracearr +++ /dev/null @@ -1,6 +0,0 @@ - ______ - /_ __/________ _________ ____ ___________ - / / / ___/ __ `/ ___/ _ \/ __ `/ ___/ ___/ - / / / / / /_/ / /__/ __/ /_/ / / / / -/_/ /_/ \__,_/\___/\___/\__,_/_/ /_/ - diff --git a/ct/tracearr.sh b/ct/tracearr.sh deleted file mode 100644 index d744db71b..000000000 --- a/ct/tracearr.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/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: durzo -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/connorgallopo/Tracearr - -APP="Tracearr" -var_tags="${var_tags:-media}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-5}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/tracearr.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "tracearr" "connorgallopo/Tracearr"; then - msg_info "Stopping Services" - systemctl stop tracearr postgresql redis - msg_ok "Stopped Services" - - msg_info "Updating pnpm" - PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]' | cut -d'+' -f1)" - export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 - $STD corepack prepare pnpm@${PNPM_VERSION} --activate - msg_ok "Updated pnpm" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" - - msg_info "Building Tracearr" - export TZ=$(cat /etc/timezone) - cd /opt/tracearr.build - $STD pnpm install --frozen-lockfile --force - $STD pnpm turbo telemetry disable - $STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web - rm -rf /opt/tracearr - mkdir -p /opt/tracearr/{packages/shared,apps/server,apps/web,apps/server/src/db} - cp -rf package.json /opt/tracearr/ - cp -rf pnpm-workspace.yaml /opt/tracearr/ - cp -rf pnpm-lock.yaml /opt/tracearr/ - cp -rf apps/server/package.json /opt/tracearr/apps/server/ - cp -rf apps/server/dist /opt/tracearr/apps/server/dist - cp -rf apps/web/dist /opt/tracearr/apps/web/dist - cp -rf packages/shared/package.json /opt/tracearr/packages/shared/ - cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist - cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations - cp -rf data /opt/tracearr/data - mkdir -p /opt/tracearr/data/image-cache - rm -rf /opt/tracearr.build - cd /opt/tracearr - $STD pnpm install --prod --frozen-lockfile --ignore-scripts - $STD chown -R tracearr:tracearr /opt/tracearr - msg_ok "Built Tracearr" - - msg_info "Configuring Tracearr" - sed -i "s/^APP_VERSION=.*/APP_VERSION=$(cat /root/.tracearr)/" /data/tracearr/.env - chmod 600 /data/tracearr/.env - chown -R tracearr:tracearr /data/tracearr - msg_ok "Configured Tracearr" - - msg_info "Starting Services" - systemctl start postgresql redis tracearr - msg_ok "Started Services" - msg_ok "Updated successfully!" - 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/frontend/public/json/tracearr.json b/frontend/public/json/tracearr.json deleted file mode 100644 index 458ec538b..000000000 --- a/frontend/public/json/tracearr.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Tracearr", - "slug": "tracearr", - "categories": [ - 14 - ], - "date_created": "2025-12-28", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/connorgallopo/Tracearr#readme", - "config_path": "", - "website": "https://github.com/connorgallopo/Tracearr", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tracearr.webp", - "description": "Tracearr is a streaming access manager for Plex, Jellyfin and Emby servers. It answers the question every server owner eventually asks: \"Who's actually using my server, and are they sharing their login?\"", - "install_methods": [ - { - "type": "default", - "script": "ct/tracearr.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 5, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/tracearr-install.sh b/install/tracearr-install.sh deleted file mode 100644 index 6b03d51d0..000000000 --- a/install/tracearr-install.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: durzo -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/connorgallopo/Tracearr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y redis-server -msg_ok "Installed Dependencies" - -NODE_VERSION="22" setup_nodejs -PG_VERSION="18" setup_postgresql - -msg_info "Installing pnpm" -PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]' | cut -d'+' -f1)" -export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 -$STD corepack enable pnpm -$STD corepack prepare pnpm@${PNPM_VERSION} --activate -msg_ok "Installed pnpm" - -msg_info "Installing TimescaleDB" -setup_deb822_repo \ - "timescaledb" \ - "https://packagecloud.io/timescale/timescaledb/gpgkey" \ - "https://packagecloud.io/timescale/timescaledb/debian" \ - "$(get_os_info codename)" \ - "main" -$STD apt install -y \ - timescaledb-2-postgresql-18 \ - timescaledb-tools \ - timescaledb-toolkit-postgresql-18 -total_ram_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') -ram_for_tsdb=$((total_ram_kb / 1024 / 2)) -$STD timescaledb-tune -yes -memory "$ram_for_tsdb"MB -$STD systemctl restart postgresql -msg_ok "Installed TimescaleDB" - -PG_DB_NAME="tracearr_db" PG_DB_USER="tracearr" PG_DB_EXTENSIONS="timescaledb,timescaledb_toolkit" setup_postgresql_db -fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" - -msg_info "Building Tracearr" -export TZ=$(cat /etc/timezone) -cd /opt/tracearr.build -$STD pnpm install --frozen-lockfile --force -$STD pnpm turbo telemetry disable -$STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web -mkdir -p /opt/tracearr/{packages/shared,apps/server,apps/web,apps/server/src/db} -cp -rf package.json /opt/tracearr/ -cp -rf pnpm-workspace.yaml /opt/tracearr/ -cp -rf pnpm-lock.yaml /opt/tracearr/ -cp -rf apps/server/package.json /opt/tracearr/apps/server/ -cp -rf apps/server/dist /opt/tracearr/apps/server/dist -cp -rf apps/web/dist /opt/tracearr/apps/web/dist -cp -rf packages/shared/package.json /opt/tracearr/packages/shared/ -cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist -cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations -cp -rf data /opt/tracearr/data -mkdir -p /opt/tracearr/data/image-cache -rm -rf /opt/tracearr.build -cd /opt/tracearr -$STD pnpm install --prod --frozen-lockfile --ignore-scripts -msg_ok "Built Tracearr" - -msg_info "Configuring Tracearr" -$STD useradd -r -s /bin/false -U tracearr -$STD chown -R tracearr:tracearr /opt/tracearr -install -d -m 750 -o tracearr -g tracearr /data/tracearr -export JWT_SECRET=$(openssl rand -hex 32) -export COOKIE_SECRET=$(openssl rand -hex 32) -cat </data/tracearr/.env -DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} -REDIS_URL=redis://127.0.0.1:6379 -PORT=3000 -HOST=0.0.0.0 -NODE_ENV=production -TZ=${TZ} -LOG_LEVEL=info -JWT_SECRET=$JWT_SECRET -COOKIE_SECRET=$COOKIE_SECRET -APP_VERSION=$(cat /root/.tracearr) -#CORS_ORIGIN=http://localhost:5173 -#MOBILE_BETA_MODE=true -EOF -chmod 600 /data/tracearr/.env -chown -R tracearr:tracearr /data/tracearr -msg_ok "Configured Tracearr" - -msg_info "Creating Services" -cat </data/tracearr/prestart.sh -#!/usr/bin/env bash -# ============================================================================= -# Tune PostgreSQL for available resources (runs every startup) -# ============================================================================= -# timescaledb-tune automatically optimizes PostgreSQL settings based on -# available RAM and CPU. Safe to run repeatedly - recalculates if resources change. -if command -v timescaledb-tune &> /dev/null; then - total_ram_kb=\$(grep MemTotal /proc/meminfo | awk '{print \$2}') - ram_for_tsdb=\$((total_ram_kb / 1024 / 2)) - timescaledb-tune -yes -memory "\$ram_for_tsdb"MB --quiet 2>/dev/null \ - || echo "Warning: timescaledb-tune failed (non-fatal)" -fi -# ============================================================================= -# Ensure TimescaleDB decompression limit is set (for existing databases) -# ============================================================================= -# This setting allows migrations to modify compressed hypertable data. -# Without it, bulk UPDATEs on compressed sessions will fail with -# "tuple decompression limit exceeded" errors. -pg_config_file="/etc/postgresql/18/main/postgresql.conf" -if [ -f \$pg_config_file ]; then - if ! grep -q "max_tuples_decompressed_per_dml_transaction" \$pg_config_file; then - echo "" >> \$pg_config_file - echo "# Allow unlimited tuple decompression for migrations on compressed hypertables" >> \$pg_config_file - echo "timescaledb.max_tuples_decompressed_per_dml_transaction = 0" >> \$pg_config_file - fi -fi -systemctl restart postgresql -EOF -chmod +x /data/tracearr/prestart.sh -cat </lib/systemd/system/tracearr.service -[Unit] -Description=Tracearr Web Server -After=network.target postgresql.service redis-server.service - -[Service] -Type=simple -KillMode=control-group -EnvironmentFile=/data/tracearr/.env -WorkingDirectory=/opt/tracearr -ExecStartPre=+/data/tracearr/prestart.sh -ExecStart=node /opt/tracearr/apps/server/dist/index.js -Restart=on-failure -RestartSec=10 -User=tracearr - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now postgresql redis-server tracearr -msg_ok "Created Services" - -motd_ssh -customize -cleanup_lxc From ebf10bf260350ec2602fdf0c103fc843f4c6d998 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 23 Jan 2026 15:58:18 +0100 Subject: [PATCH 082/228] LanguageTool: Add ngrams optional download --- install/languagetool-install.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/install/languagetool-install.sh b/install/languagetool-install.sh index 4ff78e02a..e506fff11 100644 --- a/install/languagetool-install.sh +++ b/install/languagetool-install.sh @@ -26,10 +26,39 @@ unzip -q /tmp/LanguageTool-stable.zip -d /opt mv /opt/LanguageTool-*/ /opt/LanguageTool/ download_file "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin" /opt/lid.176.bin +read -r -p "Enter language code (en, de, es, fr, nl) to download ngrams or press ENTER to skip: " lang_code +ngram_dir="" +if [[ -n "$lang_code" ]]; then + if [[ "$lang_code" =~ ^(en|de|es|fr|nl)$ ]]; then + msg_info "Searching for $lang_code ngrams..." + filename=$(curl -fsSL https://languagetool.org/download/ngram-data/ | grep -oP "ngrams-${lang_code}-[0-9]+\.zip" | sort -uV | tail -n1) + + if [[ -n "$filename" ]]; then + msg_info "Downloading $filename" + download_file "https://languagetool.org/download/ngram-data/${filename}" "/tmp/${filename}" + + mkdir -p /opt/ngrams + msg_info "Extracting $lang_code ngrams to /opt/ngrams" + unzip -q "/tmp/${filename}" -d /opt/ngrams + rm "/tmp/${filename}" + + ngram_dir="/opt/ngrams" + msg_ok "Installed $lang_code ngrams" + else + msg_info "No ngram file found for ${lang_code}" + fi + else + msg_error "Invalid language code: $lang_code" + fi +fi + cat </opt/LanguageTool/server.properties fasttextModel=/opt/lid.176.bin fasttextBinary=/usr/bin/fasttext EOF +if [[ -n "$ngram_dir" ]]; then + echo "languageModel=/opt/ngrams" >> /opt/LanguageTool/server.properties +fi echo "${RELEASE}" >~/.languagetool msg_ok "Setup LanguageTool" From 12c8f5ba025e89dfc4bc6491f1bb598e0fb10128 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 23 Jan 2026 16:03:44 +0100 Subject: [PATCH 083/228] LanguageTool: Add ngrams optional download --- install/languagetool-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/languagetool-install.sh b/install/languagetool-install.sh index e506fff11..6a118e85f 100644 --- a/install/languagetool-install.sh +++ b/install/languagetool-install.sh @@ -26,7 +26,7 @@ unzip -q /tmp/LanguageTool-stable.zip -d /opt mv /opt/LanguageTool-*/ /opt/LanguageTool/ download_file "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin" /opt/lid.176.bin -read -r -p "Enter language code (en, de, es, fr, nl) to download ngrams or press ENTER to skip: " lang_code +read -r -p "${TAB3}Enter language code (en, de, es, fr, nl) to download ngrams or press ENTER to skip: " lang_code ngram_dir="" if [[ -n "$lang_code" ]]; then if [[ "$lang_code" =~ ^(en|de|es|fr|nl)$ ]]; then From 6e147b8adfee3259cf7cf1546082f29911775506 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 24 Jan 2026 13:56:59 -0500 Subject: [PATCH 084/228] Shelfmark --- ct/shelfmark.sh | 68 ++++++++++++++++++++++ frontend/public/json/shelfmark.json | 40 +++++++++++++ install/shelfmark-install.sh | 89 +++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 ct/shelfmark.sh create mode 100644 frontend/public/json/shelfmark.json create mode 100644 install/shelfmark-install.sh diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh new file mode 100644 index 000000000..c76518c08 --- /dev/null +++ b/ct/shelfmark.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/calibrain/shelfmark + +APP="shelfmark" +var_tags="${var_tags:-ebooks}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +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/shelfmark ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + NODE_VERSION="22" setup_nodejs + PYTHON_VERSION="3.12" setup_uv + + if check_for_gh_release "shelfmark" "calibrain/shelfmark"; then + msg_info "Stopping Service" + systemctl stop shelfmark + msg_ok "Stopped Service" + + cp /opt/shelfmark/start.sh /opt/start.sh.bak + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" + + msg_info "Updating Shelfmark" + cd /opt/shelfmark/src/frontend + $STD npm ci + $STD npm run build + cd /opt/shelfmark + $STD uv venv -c ./venv + $STD uv pip install -r requirements-base.txt + mv /opt/start.sh.bak /opt/start.sh + msg_ok "Updated Shelfmark" + + msg_info "Starting Service" + systemctl start shelfmark + msg_ok "Started Service" + msg_ok "Updated successfully!" + 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}:8084${CL}" diff --git a/frontend/public/json/shelfmark.json b/frontend/public/json/shelfmark.json new file mode 100644 index 000000000..f73232a7c --- /dev/null +++ b/frontend/public/json/shelfmark.json @@ -0,0 +1,40 @@ +{ + "name": "Shelfmark", + "slug": "shelfmark", + "categories": [ + 13 + ], + "date_created": "2026-01-24", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8084, + "documentation": "https://github.com/calibrain/shelfmark/tree/main/docs", + "website": "https://github.com/calibrain/shelfmark", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/shelfmark.webp", + "config_path": "/etc/shelfmark", + "description": "Shelfmark is a unified web interface for searching and aggregating books and audiobook downloads from multiple sources - all in one place.", + "install_methods": [ + { + "type": "default", + "script": "ct/shelfmark.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 6, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Check `https://github.com/calibrain/shelfmark/blob/main/docs/environment-variables.md` for more configuration options you can add to `/etc/shelfmark/.env`", + "type": "info" + } + ] +} diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh new file mode 100644 index 000000000..da999c380 --- /dev/null +++ b/install/shelfmark-install.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/calibrain/shelfmark + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + unrar-free +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs +PYTHON_VERSION="3.12" setup_uv + +fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" + +msg_info "Building Shelfmark frontend" +cd /opt/shelfmark/src/frontend +$STD npm ci +$STD npm run build +msg_ok "Built Shelfmark frontend" + +msg_info "Configuring Shelfmark" +cd /opt/shelfmark +$STD uv venv ./venv +$STD source ./venv/bin/activate +$STD uv pip install -r requirements-base.txt +mkdir -p {/var/log/shelfmark,/tmp/shelfmark,/etc/shelfmark} +cat </etc/shelfmark/.env +DOCKERMODE=false +CONFIG_DIR=/etc/shelfmark +TMP_DIR=/tmp/shelfmark +ENABLE_LOGGING=true +FLASK_HOST=0.0.0.0 +FLASK_PORT=8084 +# SESSION_COOKIES_SECURE=true +# CWA_DB_PATH= +USE_CF_BYPASS=true +USING_EXTERNAL_BYPASSER=true +# EXT_BYPASSER_URL= +# EXT_BYPASSER_PATH= +EOF +msg_ok "Configured Shelfmark" + +msg_info "Creating Service and start script" +cat </etc/systemd/system/shelfmark.service +[Unit] +Description=Shelfmark server +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/shelfmark +EnvironmentFile=/etc/shelfmark/.env +ExecStart=/usr/bin/bash /opt/shelfmark/start.sh +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + +cat </opt/shelfmark/start.sh +#!/usr/bin/env bash + +source /opt/shelfmark/venv/bin/activate +set -a +source /opt/shelfmark/.env +set +a + +gunicorn --worker-class geventwebsocket.gunicorn.workers.GeventWebSocketWorker --workers 1 -t 300 -b 0.0.0.0:8084 shelfmark.main:app +EOF +chmod +x /opt/shelfmark/start.sh + +systemctl enable -q --now shelfmark +msg_ok "Created Services and start script" + +motd_ssh +customize +cleanup_lxc From 5c3b273e5319b6b2b01fc16aa90d0656abde516f Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 24 Jan 2026 14:45:30 -0500 Subject: [PATCH 085/228] Shelfmark: edit env; position frontend --- ct/shelfmark.sh | 1 + frontend/public/json/shelfmark.json | 2 +- install/shelfmark-install.sh | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh index c76518c08..f69bca3f0 100644 --- a/ct/shelfmark.sh +++ b/ct/shelfmark.sh @@ -44,6 +44,7 @@ function update_script() { cd /opt/shelfmark/src/frontend $STD npm ci $STD npm run build + mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist cd /opt/shelfmark $STD uv venv -c ./venv $STD uv pip install -r requirements-base.txt diff --git a/frontend/public/json/shelfmark.json b/frontend/public/json/shelfmark.json index f73232a7c..262dcd6f7 100644 --- a/frontend/public/json/shelfmark.json +++ b/frontend/public/json/shelfmark.json @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "Check `https://github.com/calibrain/shelfmark/blob/main/docs/environment-variables.md` for more configuration options you can add to `/etc/shelfmark/.env`", + "text": "The configuration at `/etc/shelfmark/.env` is for bootstrapping the initial install. Customize the configuration via the Shelfmark UI.", "type": "info" } ] diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index da999c380..d38645bae 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -27,6 +27,7 @@ msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend $STD npm ci $STD npm run build +mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist msg_ok "Built Shelfmark frontend" msg_info "Configuring Shelfmark" @@ -44,8 +45,8 @@ FLASK_HOST=0.0.0.0 FLASK_PORT=8084 # SESSION_COOKIES_SECURE=true # CWA_DB_PATH= -USE_CF_BYPASS=true -USING_EXTERNAL_BYPASSER=true +# USE_CF_BYPASS=true +# USING_EXTERNAL_BYPASSER=true # EXT_BYPASSER_URL= # EXT_BYPASSER_PATH= EOF @@ -74,7 +75,7 @@ cat </opt/shelfmark/start.sh source /opt/shelfmark/venv/bin/activate set -a -source /opt/shelfmark/.env +source /etc/shelfmark/.env set +a gunicorn --worker-class geventwebsocket.gunicorn.workers.GeventWebSocketWorker --workers 1 -t 300 -b 0.0.0.0:8084 shelfmark.main:app From a769c90e4859782128e2d9b7ab1904c10a375ef7 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 24 Jan 2026 17:18:44 -0500 Subject: [PATCH 086/228] nextExplorer: add env vars for HW acceleration --- install/nextexplorer-install.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/install/nextexplorer-install.sh b/install/nextexplorer-install.sh index 940cdec67..efd29f343 100644 --- a/install/nextexplorer-install.sh +++ b/install/nextexplorer-install.sh @@ -17,7 +17,11 @@ msg_info "Installing Dependencies" $STD apt install -y \ ripgrep \ imagemagick \ - ffmpeg + ffmpeg \ + libva-drm2 \ + libva2 \ + mesa-va-drivers \ + vainfo msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs @@ -108,6 +112,11 @@ SHOW_VOLUME_USAGE=true # FFMPEG_PATH= # FFPROBE_PATH= +## Hardware acceleration +# FFMPEG_HWACCEL=vaapi +# FFMPEG_HWACCEL_DEVICE=/dev/dri/renderD128 +# FFMPEG_HWACCEL_OUTPUT_FORMAT=nv12 + FAVORITES_DEFAULT_ICON=outline.StarIcon SHARES_ENABLED=true From dfb7fcac102f2fdc37fcad25e377f67b6b2a51dd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 24 Jan 2026 22:50:27 +0000 Subject: [PATCH 087/228] Delete manyfold (ct) after migration to ProxmoxVE (#1335) Co-authored-by: github-actions[bot] --- ct/headers/manyfold | 6 -- ct/manyfold.sh | 108 ---------------------- frontend/public/json/manyfold.json | 40 --------- install/manyfold-install.sh | 139 ----------------------------- 4 files changed, 293 deletions(-) delete mode 100644 ct/headers/manyfold delete mode 100644 ct/manyfold.sh delete mode 100644 frontend/public/json/manyfold.json delete mode 100644 install/manyfold-install.sh diff --git a/ct/headers/manyfold b/ct/headers/manyfold deleted file mode 100644 index b27653eb0..000000000 --- a/ct/headers/manyfold +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ ____ __ __ - / |/ /___ _____ __ __/ __/___ / /___/ / - / /|_/ / __ `/ __ \/ / / / /_/ __ \/ / __ / - / / / / /_/ / / / / /_/ / __/ /_/ / / /_/ / -/_/ /_/\__,_/_/ /_/\__, /_/ \____/_/\__,_/ - /____/ diff --git a/ct/manyfold.sh b/ct/manyfold.sh deleted file mode 100644 index 3b8c74b70..000000000 --- a/ct/manyfold.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2026 community-scripts ORG -# Author: bvdberg01 -# Co-Author: SunFlowerOwl -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/manyfold3d/manyfold - -APP="Manyfold" -var_tags="${var_tags:-3d}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-15}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -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/manyfold ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "manyfold" "manyfold3d/manyfold"; then - msg_info "Stopping Services" - systemctl stop manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 - msg_ok "Stopped Services" - - msg_info "Backing up Data" - CURRENT_VERSION=$(grep -oP 'APP_VERSION=\K[^ ]+' /opt/manyfold/.env || echo "unknown") - cp -r /opt/manyfold/app/storage /opt/manyfold_storage_backup 2>/dev/null || true - cp -r /opt/manyfold/app/tmp /opt/manyfold_tmp_backup 2>/dev/null || true - cp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold_credentials.yml.enc 2>/dev/null || true - cp /opt/manyfold/app/config/master.key /opt/manyfold_master.key 2>/dev/null || true - $STD tar -czf "/opt/manyfold_${CURRENT_VERSION}_backup.tar.gz" -C /opt/manyfold app - msg_ok "Backed up Data" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" - - msg_info "Configuring Manyfold" - RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) - YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') - RELEASE=$(get_latest_github_release "manyfold3d/manyfold") - sed -i "s/^export APP_VERSION=.*/export APP_VERSION=$RELEASE/" "/opt/manyfold/.env" - msg_ok "Configured Manyfold" - - RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby - - msg_info "Installing Manyfold" - chown -R manyfold:manyfold /home/manyfold/.rbenv - chown -R manyfold:manyfold /opt/manyfold - - sudo -u manyfold bash -c ' - source /opt/manyfold/.env - export PATH="/home/manyfold/.rbenv/bin:$PATH" - eval "$(/home/manyfold/.rbenv/bin/rbenv init - bash)" - cd /opt/manyfold/app - gem install bundler sidekiq foreman - bundle install - corepack enable yarn - corepack prepare '"$YARN_VERSION"' --activate - corepack use '"$YARN_VERSION"' - bin/rails db:migrate - bin/rails assets:precompile - ' - msg_ok "Installed Manyfold" - - msg_info "Restoring Data" - rm -rf /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold/app/config/master.key - cp -r /opt/manyfold_storage_backup /opt/manyfold/app/storage 2>/dev/null || true - cp -r /opt/manyfold_tmp_backup /opt/manyfold/app/tmp 2>/dev/null || true - cp /opt/manyfold_credentials.yml.enc /opt/manyfold/app/config/credentials.yml.enc 2>/dev/null || true - cp /opt/manyfold_master.key /opt/manyfold/app/config/master.key 2>/dev/null || true - chown -R manyfold:manyfold /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config - rm -rf /opt/manyfold_storage_backup /opt/manyfold_tmp_backup /opt/manyfold_credentials.yml.enc /opt/manyfold_master.key - msg_ok "Restored Data" - - msg_info "Restarting Services" - source /opt/manyfold/.env - export PATH="/home/manyfold/.rbenv/shims:/home/manyfold/.rbenv/bin:$PATH" - $STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile - for f in /etc/systemd/system/manyfold-*.service; do - sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" - done - systemctl daemon-reload - systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 - msg_ok "Restarted Services" - msg_ok "Updated successfully!" - 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}${CL}" diff --git a/frontend/public/json/manyfold.json b/frontend/public/json/manyfold.json deleted file mode 100644 index 26bfa13d9..000000000 --- a/frontend/public/json/manyfold.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Manyfold", - "slug": "manyfold", - "categories": [ - 24 - ], - "date_created": "2025-03-18", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://manyfold.app/sysadmin/", - "website": "https://manyfold.app/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/manyfold.webp", - "config_path": "/opt/manyfold/.env", - "description": "Manyfold is an open source, self-hosted web application for managing a collection of 3d models, particularly focused on 3d printing.", - "install_methods": [ - { - "type": "default", - "script": "ct/manyfold.sh", - "resources": { - "cpu": 4, - "ram": 4096, - "hdd": 15, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Setup library on first connection in /opt/manyfold/data", - "type": "info" - } - ] -} diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh deleted file mode 100644 index 3fe930eb2..000000000 --- a/install/manyfold-install.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: bvdberg01 -# Co-Author: SunFlowerOwl -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - libarchive-dev \ - git \ - libmariadb-dev \ - redis-server \ - nginx \ - libassimp-dev -msg_ok "Installed Dependencies" - -setup_imagemagick -PG_VERSION="16" setup_postgresql -PG_DB_NAME="manyfold" PG_DB_USER="manyfold" setup_postgresql_db -fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" - -msg_info "Configuring Manyfold" -RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) -YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') -RELEASE=$(get_latest_github_release "manyfold3d/manyfold") -useradd -m -s /usr/bin/bash manyfold -cat </opt/manyfold/.env -export APP_VERSION=${RELEASE} -export GUID=1002 -export PUID=1001 -export PUBLIC_PORT=5000 -export REDIS_URL=redis://127.0.0.1:6379/1 -export DATABASE_ADAPTER=postgresql -export DATABASE_HOST=127.0.0.1 -export DATABASE_USER=${PG_DB_USER} -export DATABASE_PASSWORD=${PG_DB_PASS} -export DATABASE_NAME=${PG_DB_NAME} -export DATABASE_CONNECTION_POOL=16 -export MULTIUSER=enabled -export HTTPS_ONLY=false -export RAILS_ENV=production -EOF -cat </opt/manyfold/user_setup.sh -#!/bin/bash - -source /opt/manyfold/.env -export PATH="/home/manyfold/.rbenv/bin:\$PATH" -eval "\$(/home/manyfold/.rbenv/bin/rbenv init - bash)" -cd /opt/manyfold/app -rbenv global $RUBY_INSTALL_VERSION -gem install bundler -bundle install -gem install sidekiq -gem install foreman -corepack enable yarn -rm -f /opt/manyfold/app/config/credentials.yml.enc -corepack prepare $YARN_VERSION --activate -corepack use $YARN_VERSION -export VISUAL="code --wait" -bin/rails credentials:edit -bin/rails db:migrate -bin/rails assets:precompile -EOF -$STD mkdir -p /opt/manyfold_data -msg_ok "Configured Manyfold" - -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby - -msg_info "Installing Manyfold" -chown -R manyfold:manyfold /home/manyfold/.rbenv -chown -R manyfold:manyfold /opt/manyfold -chmod +x /opt/manyfold/user_setup.sh -$STD npm install --global corepack -$STD sudo -u manyfold bash /opt/manyfold/user_setup.sh -rm -f /opt/manyfold/user_setup.sh -msg_ok "Installed Manyfold" - -msg_info "Creating Services" -source /opt/manyfold/.env -export PATH="/home/manyfold/.rbenv/shims:/home/manyfold/.rbenv/bin:$PATH" -$STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile -for f in /etc/systemd/system/manyfold-*.service; do - sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" -done -systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 -cat </etc/nginx/sites-available/manyfold.conf -server { - listen 80; - server_name manyfold; - root /opt/manyfold/app/public; - - location /cable { - proxy_pass http://127.0.0.1:5000; - proxy_set_header Host \$host; - - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection "Upgrade"; - - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - } - - location / { - try_files \$uri/index.html \$uri @rails; - } - - location @rails { - proxy_pass http://127.0.0.1:5000; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - } -} -EOF -ln -s /etc/nginx/sites-available/manyfold.conf /etc/nginx/sites-enabled/ -rm -f /etc/nginx/sites-enabled/default -$STD systemctl reload nginx -msg_ok "Created Services" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 9f35cc33caed382301996525f3fc4085042edd55 Mon Sep 17 00:00:00 2001 From: Tom Frenzel Date: Sun, 25 Jan 2026 14:43:56 +0100 Subject: [PATCH 088/228] fix: add version variable to shelfmark --- ct/shelfmark.sh | 2 ++ install/shelfmark-install.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh index f69bca3f0..3c1d1dfab 100644 --- a/ct/shelfmark.sh +++ b/ct/shelfmark.sh @@ -39,8 +39,10 @@ function update_script() { cp /opt/shelfmark/start.sh /opt/start.sh.bak CLEAN_INSTALL=1 fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" + RELEASE_VERSION=$(cat "$HOME/.shelfmark") msg_info "Updating Shelfmark" + sed -i "s/^RELEASE_VERSION=.*/RELEASE_VERSION=$RELEASE_VERSION/" /etc/shelfmark/.env cd /opt/shelfmark/src/frontend $STD npm ci $STD npm run build diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index d38645bae..729cf7d7a 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -22,6 +22,7 @@ NODE_VERSION="22" setup_nodejs PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" +RELEASE_VERSION=$(cat "$HOME/.shelfmark") msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend @@ -43,6 +44,7 @@ TMP_DIR=/tmp/shelfmark ENABLE_LOGGING=true FLASK_HOST=0.0.0.0 FLASK_PORT=8084 +RELEASE_VERSION=$RELEASE_VERSION # SESSION_COOKIES_SECURE=true # CWA_DB_PATH= # USE_CF_BYPASS=true From 27693b41a82f57f4ae4a2ded08a2b6b6068c2725 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 13:09:09 +0100 Subject: [PATCH 089/228] test kitchenowl --- install/kitchenowl-install.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh index eb90ba9a8..f60a81698 100644 --- a/install/kitchenowl-install.sh +++ b/install/kitchenowl-install.sh @@ -17,13 +17,28 @@ msg_info "Installing Dependencies" $STD apt install -y \ nginx \ build-essential \ + gfortran \ + pkg-config \ + ninja-build \ + autoconf \ + automake \ libpq-dev \ libffi-dev \ - libssl-dev + libssl-dev \ + libpcre2-dev \ + libre2-dev \ + libxml2-dev \ + libxslt-dev \ + libopenblas-dev \ + liblapack-dev \ + zlib1g-dev \ + libjpeg62-turbo-dev \ + libsqlite3-dev \ + libexpat1-dev \ + libicu-dev msg_ok "Installed Dependencies" PYTHON_VERSION="3.14" setup_uv -import_local_ip fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" rm -rf /opt/kitchenowl/web fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" From 0d382b9b6a1b83f4a773374d95e95b30668163c1 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 26 Jan 2026 13:14:47 +0100 Subject: [PATCH 090/228] ghost test --- ct/ghost.sh | 55 ++++++++++++++++++++++++++++++++++++++++ install/ghost-install.sh | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 ct/ghost.sh create mode 100644 install/ghost-install.sh diff --git a/ct/ghost.sh b/ct/ghost.sh new file mode 100644 index 000000000..31fa44237 --- /dev/null +++ b/ct/ghost.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: fabrice1236 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://ghost.org/ + +APP="Ghost" +var_tags="${var_tags:-cms;blog}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-5}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + setup_mariadb + NODE_VERSION="22" setup_nodejs + + msg_info "Updating Ghost" + if command -v ghost &>/dev/null; then + current_version=$(ghost version | grep 'Ghost-CLI version' | awk '{print $3}') + latest_version=$(npm show ghost-cli version) + if [ "$current_version" != "$latest_version" ]; then + msg_info "Updating ${APP} from version v${current_version} to v${latest_version}" + $STD npm install -g ghost-cli@latest + msg_ok "Updated successfully!" + else + msg_ok "${APP} is already at v${current_version}" + fi + else + msg_error "No ${APP} Installation Found!" + exit + 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}:2368${CL}" diff --git a/install/ghost-install.sh b/install/ghost-install.sh new file mode 100644 index 000000000..d97de4f31 --- /dev/null +++ b/install/ghost-install.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: fabrice1236 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://ghost.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + nginx \ + ca-certificates \ + libjemalloc2 +msg_ok "Installed Dependencies" + +setup_mariadb +MARIADB_DB_NAME="ghost" MARIADB_DB_USER="ghostuser" setup_mariadb_db +NODE_VERSION="22" setup_nodejs + +msg_info "Installing Ghost CLI" +$STD npm install ghost-cli@latest -g +msg_ok "Installed Ghost CLI" + +msg_info "Creating Service" +$STD adduser --disabled-password --gecos "Ghost user" ghost-user +$STD usermod -aG sudo ghost-user +echo "ghost-user ALL=(ALL) NOPASSWD:ALL" | tee /etc/sudoers.d/ghost-user +mkdir -p /var/www/ghost +chown -R ghost-user:ghost-user /var/www/ghost +chmod 775 /var/www/ghost +$STD sudo -u ghost-user -H sh -c "cd /var/www/ghost && ghost install --db=mysql --dbhost=localhost --dbuser=$MARIADB_DB_USER --dbpass=$MARIADB_DB_PASS --dbname=$MARIADB_DB_NAME --url=http://localhost:2368 --no-prompt --no-setup-nginx --no-setup-ssl --no-setup-mysql --enable --start --ip 0.0.0.0" +rm /etc/sudoers.d/ghost-user +msg_ok "Creating Service" + +motd_ssh +customize +cleanup_lxc From ae10fac90b1f4b6364ef8d6514fb58060c5564b3 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 13:20:23 +0100 Subject: [PATCH 091/228] First Clawdbot --- ct/clawdbot.sh | 71 ++++++++++++++++++++++++++++++ frontend/public/json/clawdbot.json | 36 +++++++++++++++ install/clawdbot-install.sh | 65 +++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 ct/clawdbot.sh create mode 100644 frontend/public/json/clawdbot.json create mode 100644 install/clawdbot-install.sh diff --git a/ct/clawdbot.sh b/ct/clawdbot.sh new file mode 100644 index 000000000..09aca1f73 --- /dev/null +++ b/ct/clawdbot.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/clawdbot/clawdbot + +APP="Clawdbot" +var_tags="${var_tags:-ai;assistant}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +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/clawdbot ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "clawdbot" "clawdbot/clawdbot"; then + msg_info "Stopping Service" + systemctl stop clawdbot + msg_ok "Stopped Service" + + msg_info "Backing up Data" + cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true + cp -r /root/.clawdbot /root/.clawdbot_backup 2>/dev/null || true + msg_ok "Backed up Data" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" + + msg_info "Rebuilding Application" + cd /opt/clawdbot + $STD npm install + msg_ok "Rebuilt Application" + + msg_info "Restoring Data" + cp -r /opt/clawdbot_data_backup/. /opt/clawdbot/data 2>/dev/null || true + cp -r /root/.clawdbot_backup/. /root/.clawdbot 2>/dev/null || true + rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup + msg_ok "Restored Data" + + msg_info "Starting Service" + systemctl start clawdbot + msg_ok "Started Service" + msg_ok "Updated successfully!" + 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}:18791${CL}" + diff --git a/frontend/public/json/clawdbot.json b/frontend/public/json/clawdbot.json new file mode 100644 index 000000000..ca21f9043 --- /dev/null +++ b/frontend/public/json/clawdbot.json @@ -0,0 +1,36 @@ +{ + "name": "Clawdbot", + "slug": "clawdbot", + "categories": [ + 20 + ], + "date_created": "2026-01-26", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 18791, + "documentation": "https://docs.clawd.bot/", + "website": "https://clawd.bot/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/clawdbot.webp", + "config_path": "/opt/clawdbot/.env", + "description": "Your own personal AI assistant. Any OS. Any Platform. The lobster way. Clawdbot is a powerful AI agent framework that can be configured to work across multiple platforms and channels.", + "install_methods": [ + { + "type": "default", + "script": "ct/clawdbot.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} + diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh new file mode 100644 index 000000000..10dfb6126 --- /dev/null +++ b/install/clawdbot-install.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/clawdbot/clawdbot + +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 \ + build-essential \ + git +msg_ok "Installed Dependencies" + +NODE_VERSION="24" setup_nodejs +import_local_ip + +fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" + +msg_info "Installing Clawdbot" +cd /opt/clawdbot +$STD npm install +msg_ok "Installed Clawdbot" + +msg_info "Configuring Clawdbot" +mkdir -p /opt/clawdbot/data +cat </opt/clawdbot/.env +NODE_ENV=production +GATEWAY_PORT=18791 +GATEWAY_HOST=0.0.0.0 +EOF +msg_ok "Configured Clawdbot" + +msg_info "Creating Service" +cat </etc/systemd/system/clawdbot.service +[Unit] +Description=Clawdbot Service +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/clawdbot +EnvironmentFile=/opt/clawdbot/.env +ExecStart=/usr/bin/npm start +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now clawdbot +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc + From fa897ddc6736f1affa547df882784c7895d3fb6d Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 13:21:04 +0100 Subject: [PATCH 092/228] test --- install/kitchenowl-install.sh | 4 ++-- install/languagetool-install.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/install/kitchenowl-install.sh b/install/kitchenowl-install.sh index f60a81698..72d88030a 100644 --- a/install/kitchenowl-install.sh +++ b/install/kitchenowl-install.sh @@ -46,10 +46,10 @@ fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" " msg_info "Setting up KitchenOwl" cd /opt/kitchenowl/backend #rm -f uv.lock -$STD uv sync --frozen +$STD uv sync --no-dev sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py mkdir -p /nltk_data -$STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng punkt_tab +$STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng JWT_SECRET=$(openssl rand -hex 32) mkdir -p /opt/kitchenowl/data cat </opt/kitchenowl/kitchenowl.env diff --git a/install/languagetool-install.sh b/install/languagetool-install.sh index 6a118e85f..2797151fc 100644 --- a/install/languagetool-install.sh +++ b/install/languagetool-install.sh @@ -25,6 +25,7 @@ download_file "https://languagetool.org/download/LanguageTool-stable.zip" /tmp/L unzip -q /tmp/LanguageTool-stable.zip -d /opt mv /opt/LanguageTool-*/ /opt/LanguageTool/ download_file "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin" /opt/lid.176.bin +msg_ok "" read -r -p "${TAB3}Enter language code (en, de, es, fr, nl) to download ngrams or press ENTER to skip: " lang_code ngram_dir="" From 327b8b531faa0fdd7303c1501f913ec00ab24184 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 26 Jan 2026 13:27:42 +0100 Subject: [PATCH 093/228] add git dep --- install/ghost-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/ghost-install.sh b/install/ghost-install.sh index d97de4f31..4cee724a6 100644 --- a/install/ghost-install.sh +++ b/install/ghost-install.sh @@ -17,7 +17,8 @@ msg_info "Installing Dependencies" $STD apt install -y \ nginx \ ca-certificates \ - libjemalloc2 + libjemalloc2 \ + git msg_ok "Installed Dependencies" setup_mariadb From ef7d1927c56383d56d0d8116d22311a9f9fa7e5a Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 13:29:32 +0100 Subject: [PATCH 094/228] Updated Install Flow --- ct/clawdbot.sh | 45 +++++++++++++++++-------------------- install/clawdbot-install.sh | 11 ++------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/ct/clawdbot.sh b/ct/clawdbot.sh index 09aca1f73..dadefee2f 100644 --- a/ct/clawdbot.sh +++ b/ct/clawdbot.sh @@ -24,39 +24,34 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -d /opt/clawdbot ]]; then + if ! command -v clawdbot >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit fi - if check_for_gh_release "clawdbot" "clawdbot/clawdbot"; then - msg_info "Stopping Service" - systemctl stop clawdbot - msg_ok "Stopped Service" + msg_info "Stopping Service" + systemctl stop clawdbot + msg_ok "Stopped Service" - msg_info "Backing up Data" - cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true - cp -r /root/.clawdbot /root/.clawdbot_backup 2>/dev/null || true - msg_ok "Backed up Data" + msg_info "Backing up Data" + cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true + cp -r /root/.clawdbot /root/.clawdbot_backup 2>/dev/null || true + msg_ok "Backed up Data" - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" + msg_info "Updating Clawdbot" + $STD npm install -g clawdbot@latest + msg_ok "Updated Clawdbot" - msg_info "Rebuilding Application" - cd /opt/clawdbot - $STD npm install - msg_ok "Rebuilt Application" + msg_info "Restoring Data" + cp -r /opt/clawdbot_data_backup/. /opt/clawdbot/data 2>/dev/null || true + cp -r /root/.clawdbot_backup/. /root/.clawdbot 2>/dev/null || true + rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup + msg_ok "Restored Data" - msg_info "Restoring Data" - cp -r /opt/clawdbot_data_backup/. /opt/clawdbot/data 2>/dev/null || true - cp -r /root/.clawdbot_backup/. /root/.clawdbot 2>/dev/null || true - rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup - msg_ok "Restored Data" - - msg_info "Starting Service" - systemctl start clawdbot - msg_ok "Started Service" - msg_ok "Updated successfully!" - fi + msg_info "Starting Service" + systemctl start clawdbot + msg_ok "Started Service" + msg_ok "Updated successfully!" exit } diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index 10dfb6126..861a8570f 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -19,16 +19,9 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="24" setup_nodejs +NODE_VERSION="24" NODE_MODULE="clawdbot@latest" setup_nodejs import_local_ip -fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" - -msg_info "Installing Clawdbot" -cd /opt/clawdbot -$STD npm install -msg_ok "Installed Clawdbot" - msg_info "Configuring Clawdbot" mkdir -p /opt/clawdbot/data cat </opt/clawdbot/.env @@ -49,7 +42,7 @@ Type=simple User=root WorkingDirectory=/opt/clawdbot EnvironmentFile=/opt/clawdbot/.env -ExecStart=/usr/bin/npm start +ExecStart=/usr/bin/clawdbot Restart=on-failure RestartSec=5 From 9c439c150367c9b8408b7cd02788ed60a9c0c9c7 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 13:31:24 +0100 Subject: [PATCH 095/228] Updated Install Flow --- install/clawdbot-install.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index 861a8570f..374067a77 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -13,12 +13,6 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - build-essential \ - git -msg_ok "Installed Dependencies" - NODE_VERSION="24" NODE_MODULE="clawdbot@latest" setup_nodejs import_local_ip From cb525b9ec2b01f8ae9d022927b0302d5fb14e6e7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 26 Jan 2026 13:32:04 +0100 Subject: [PATCH 096/228] remove ghost --- ct/ghost.sh | 55 ---------------------------------------- install/ghost-install.sh | 45 -------------------------------- 2 files changed, 100 deletions(-) delete mode 100644 ct/ghost.sh delete mode 100644 install/ghost-install.sh diff --git a/ct/ghost.sh b/ct/ghost.sh deleted file mode 100644 index 31fa44237..000000000 --- a/ct/ghost.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2026 community-scripts ORG -# Author: fabrice1236 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://ghost.org/ - -APP="Ghost" -var_tags="${var_tags:-cms;blog}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-5}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - setup_mariadb - NODE_VERSION="22" setup_nodejs - - msg_info "Updating Ghost" - if command -v ghost &>/dev/null; then - current_version=$(ghost version | grep 'Ghost-CLI version' | awk '{print $3}') - latest_version=$(npm show ghost-cli version) - if [ "$current_version" != "$latest_version" ]; then - msg_info "Updating ${APP} from version v${current_version} to v${latest_version}" - $STD npm install -g ghost-cli@latest - msg_ok "Updated successfully!" - else - msg_ok "${APP} is already at v${current_version}" - fi - else - msg_error "No ${APP} Installation Found!" - exit - 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}:2368${CL}" diff --git a/install/ghost-install.sh b/install/ghost-install.sh deleted file mode 100644 index 4cee724a6..000000000 --- a/install/ghost-install.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: fabrice1236 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://ghost.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - nginx \ - ca-certificates \ - libjemalloc2 \ - git -msg_ok "Installed Dependencies" - -setup_mariadb -MARIADB_DB_NAME="ghost" MARIADB_DB_USER="ghostuser" setup_mariadb_db -NODE_VERSION="22" setup_nodejs - -msg_info "Installing Ghost CLI" -$STD npm install ghost-cli@latest -g -msg_ok "Installed Ghost CLI" - -msg_info "Creating Service" -$STD adduser --disabled-password --gecos "Ghost user" ghost-user -$STD usermod -aG sudo ghost-user -echo "ghost-user ALL=(ALL) NOPASSWD:ALL" | tee /etc/sudoers.d/ghost-user -mkdir -p /var/www/ghost -chown -R ghost-user:ghost-user /var/www/ghost -chmod 775 /var/www/ghost -$STD sudo -u ghost-user -H sh -c "cd /var/www/ghost && ghost install --db=mysql --dbhost=localhost --dbuser=$MARIADB_DB_USER --dbpass=$MARIADB_DB_PASS --dbname=$MARIADB_DB_NAME --url=http://localhost:2368 --no-prompt --no-setup-nginx --no-setup-ssl --no-setup-mysql --enable --start --ip 0.0.0.0" -rm /etc/sudoers.d/ghost-user -msg_ok "Creating Service" - -motd_ssh -customize -cleanup_lxc From 602f23f92a9bf82abf1776f3664bb8ba2122d2ce Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 13:43:25 +0100 Subject: [PATCH 097/228] Update install --- install/clawdbot-install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index 374067a77..861a8570f 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -13,6 +13,12 @@ setting_up_container network_check update_os +msg_info "Installing Dependencies" +$STD apt-get install -y \ + build-essential \ + git +msg_ok "Installed Dependencies" + NODE_VERSION="24" NODE_MODULE="clawdbot@latest" setup_nodejs import_local_ip From d9a075139a63538a49846e59b52e36b251e74027 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 14:11:03 +0100 Subject: [PATCH 098/228] Update install --- ct/clawdbot.sh | 2 +- frontend/public/json/clawdbot.json | 10 ++++-- install/clawdbot-install.sh | 58 +++++++++++++++--------------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/ct/clawdbot.sh b/ct/clawdbot.sh index dadefee2f..868da8c38 100644 --- a/ct/clawdbot.sh +++ b/ct/clawdbot.sh @@ -62,5 +62,5 @@ 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}:18791${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" diff --git a/frontend/public/json/clawdbot.json b/frontend/public/json/clawdbot.json index ca21f9043..d444b60ca 100644 --- a/frontend/public/json/clawdbot.json +++ b/frontend/public/json/clawdbot.json @@ -8,7 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 18791, + "interface_port": 80, "documentation": "https://docs.clawd.bot/", "website": "https://clawd.bot/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/clawdbot.webp", @@ -31,6 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "type": "info", + "text": "After install run onboarding: clawdbot onboard --install-daemon" + } + ] } - diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index 861a8570f..ed44b0ef3 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -16,41 +16,41 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ build-essential \ - git + git \ + nginx msg_ok "Installed Dependencies" NODE_VERSION="24" NODE_MODULE="clawdbot@latest" setup_nodejs import_local_ip -msg_info "Configuring Clawdbot" -mkdir -p /opt/clawdbot/data -cat </opt/clawdbot/.env -NODE_ENV=production -GATEWAY_PORT=18791 -GATEWAY_HOST=0.0.0.0 + +msg_info "Configuring Nginx" +cat </etc/nginx/sites-available/clawdbot +server { + listen 80; + server_name _; + + location / { + proxy_pass http://127.0.0.1:18791; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_cache_bypass \$http_upgrade; + proxy_buffering off; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + } +} EOF -msg_ok "Configured Clawdbot" - -msg_info "Creating Service" -cat </etc/systemd/system/clawdbot.service -[Unit] -Description=Clawdbot Service -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/clawdbot -EnvironmentFile=/opt/clawdbot/.env -ExecStart=/usr/bin/clawdbot -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now clawdbot -msg_ok "Created Service" +ln -sf /etc/nginx/sites-available/clawdbot /etc/nginx/sites-enabled/clawdbot +rm -f /etc/nginx/sites-enabled/default +$STD nginx -t +$STD systemctl enable -q --now nginx +msg_ok "Configured Nginx" motd_ssh customize From c1f3b262ff55b15268a79df63bcb3d13552348e8 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:17:12 +0100 Subject: [PATCH 099/228] add gpu for nodecast --- ct/nodecast-tv.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/nodecast-tv.sh b/ct/nodecast-tv.sh index 202e65ecc..53ea93dd1 100644 --- a/ct/nodecast-tv.sh +++ b/ct/nodecast-tv.sh @@ -13,6 +13,7 @@ var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" +var_gpu="${var_gpu:-yes}" header_info "$APP" variables From cb5e1e1524acdc9dd5301f6344f3fb2f54e95469 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:22:43 +0100 Subject: [PATCH 100/228] add ffmpeg --- install/nodecast-tv-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/nodecast-tv-install.sh b/install/nodecast-tv-install.sh index a9d2e3a7b..77a57e8a1 100644 --- a/install/nodecast-tv-install.sh +++ b/install/nodecast-tv-install.sh @@ -16,6 +16,10 @@ update_os fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" setup_nodejs +msg_info "Installing Dependencies" +$STD apt install -y ffmpeg +msg_ok "Installed Dependencies" + msg_info "Installing Modules" cd /opt/nodecast-tv $STD npm install From be9a7e2fb9195bc4bac8504c7e9c7ad6afef67ed Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:28:47 +0100 Subject: [PATCH 101/228] jotty prebuild --- install/jotty-install.sh | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index a8ce9f634..67a37edae 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG -# Author: vhsdream +# Author: vhsdream | MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/fccview/jotty @@ -14,37 +14,16 @@ network_check update_os NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -#fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" +fetch_and_deploy_gh_release "jotty" "fccview/jotty" "prebuild" "/opt/jotty" "jotty_*_prebuild.tar.gz" + msg_info "Setup jotty" -mkdir -p /opt/jotty -wget -q https://github.com/fccview/jotty/releases/download/develop/jotty-prebuild-develop.tar.gz -O /opt/jotty.tar.gz -cd /opt -tar -xzf jotty.tar.gz - -cd /opt/jotty -# unset NODE_OPTIONS -# export NODE_OPTIONS="--max-old-space-size=3072" -# # $STD yarn --frozen-lockfiled -# # $STD yarn next telemetry disable -# # $STD yarn build - -# [ -d "public" ] && cp -r public .next/standalone/ -# [ -d "howto" ] && cp -r howto .next/standalone/ -# mkdir -p .next/standalone/.next -# cp -r .next/static .next/standalone/.next/ - -# mv .next/standalone /tmp/jotty_standalone -# rm -rf ./* .next .git .gitignore .yarn -# mv /tmp/jotty_standalone/* . -# mv /tmp/jotty_standalone/.[!.]* . 2>/dev/null || true -# rm -rf /tmp/jotty_standalone - mkdir -p data/{users,checklists,notes} cat </opt/jotty/.env NODE_ENV=production - # --- Uncomment to enable +# APP_URL=https://your-jotty-domain.com +# INTERNAL_API_URL=http://localhost:3000 # HTTPS=true # SERVE_PUBLIC_IMAGES=yes # SERVE_PUBLIC_FILES=yes @@ -57,12 +36,11 @@ NODE_ENV=production # SSO_MODE=oidc # OIDC_ISSUER= # OIDC_CLIENT_ID= -# APP_URL= # SSO_FALLBACK_LOCAL=yes # OIDC_CLIENT_SECRET=your_client_secret # OIDC_ADMIN_GROUPS=admins EOF -msg_ok "Installed ${APPLICATION}" +msg_ok "Setup jotty" msg_info "Creating Service" cat </etc/systemd/system/jotty.service From 4b1aa41aec87e78cf19207f471f083113e9797fd Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 14:28:07 +0100 Subject: [PATCH 102/228] Update install --- ct/clawdbot.sh | 48 +++++++++++++++++------------- frontend/public/json/clawdbot.json | 2 +- install/clawdbot-install.sh | 41 +++++++------------------ 3 files changed, 39 insertions(+), 52 deletions(-) diff --git a/ct/clawdbot.sh b/ct/clawdbot.sh index 868da8c38..6c82efe1c 100644 --- a/ct/clawdbot.sh +++ b/ct/clawdbot.sh @@ -24,34 +24,40 @@ function update_script() { check_container_storage check_container_resources - if ! command -v clawdbot >/dev/null 2>&1; then + if [[ ! -d /opt/clawdbot ]]; then msg_error "No ${APP} Installation Found!" exit fi - msg_info "Stopping Service" - systemctl stop clawdbot - msg_ok "Stopped Service" + if check_for_gh_release "clawdbot" "clawdbot/clawdbot"; then + msg_info "Stopping Service" + systemctl stop clawdbot + msg_ok "Stopped Service" - msg_info "Backing up Data" - cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true - cp -r /root/.clawdbot /root/.clawdbot_backup 2>/dev/null || true - msg_ok "Backed up Data" + msg_info "Backing up Data" + cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true + cp -r /root/.clawdbot /root/.clawdbot_backup 2>/dev/null || true + msg_ok "Backed up Data" - msg_info "Updating Clawdbot" - $STD npm install -g clawdbot@latest - msg_ok "Updated Clawdbot" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" - msg_info "Restoring Data" - cp -r /opt/clawdbot_data_backup/. /opt/clawdbot/data 2>/dev/null || true - cp -r /root/.clawdbot_backup/. /root/.clawdbot 2>/dev/null || true - rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup - msg_ok "Restored Data" + msg_info "Rebuilding Clawdbot" + cd /opt/clawdbot + $STD pnpm install --frozen-lockfile + $STD pnpm ui:build + msg_ok "Rebuilt Clawdbot" - msg_info "Starting Service" - systemctl start clawdbot - msg_ok "Started Service" - msg_ok "Updated successfully!" + msg_info "Restoring Data" + cp -r /opt/clawdbot_data_backup/. /opt/clawdbot/data 2>/dev/null || true + cp -r /root/.clawdbot_backup/. /root/.clawdbot 2>/dev/null || true + rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup + msg_ok "Restored Data" + + msg_info "Starting Service" + systemctl start clawdbot + msg_ok "Started Service" + msg_ok "Updated successfully!" + fi exit } @@ -62,5 +68,5 @@ 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}:80${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:18791${CL}" diff --git a/frontend/public/json/clawdbot.json b/frontend/public/json/clawdbot.json index d444b60ca..cc5029067 100644 --- a/frontend/public/json/clawdbot.json +++ b/frontend/public/json/clawdbot.json @@ -8,7 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 80, + "interface_port": 18791, "documentation": "https://docs.clawd.bot/", "website": "https://clawd.bot/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/clawdbot.webp", diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index ed44b0ef3..f1a50cd4a 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -16,41 +16,22 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ build-essential \ - git \ - nginx + git msg_ok "Installed Dependencies" -NODE_VERSION="24" NODE_MODULE="clawdbot@latest" setup_nodejs -import_local_ip +fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" +pnpm_version=$(grep -oP '"packageManager":\s*"pnpm@\K[^"]+' /opt/clawdbot/package.json 2>/dev/null || echo "latest") +NODE_VERSION="24" NODE_MODULE="pnpm@${pnpm_version}" setup_nodejs -msg_info "Configuring Nginx" -cat </etc/nginx/sites-available/clawdbot -server { - listen 80; - server_name _; +msg_info "Installing Clawdbot Dependencies" +cd /opt/clawdbot +$STD pnpm install --frozen-lockfile +msg_ok "Installed Dependencies" - location / { - proxy_pass http://127.0.0.1:18791; - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - proxy_cache_bypass \$http_upgrade; - proxy_buffering off; - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; - } -} -EOF -ln -sf /etc/nginx/sites-available/clawdbot /etc/nginx/sites-enabled/clawdbot -rm -f /etc/nginx/sites-enabled/default -$STD nginx -t -$STD systemctl enable -q --now nginx -msg_ok "Configured Nginx" +msg_info "Building Clawdbot UI" +$STD pnpm ui:build +msg_ok "Built Clawdbot UI" motd_ssh customize From 447aaf0ebca6c5425a484a865369c7d2b189933a Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 26 Jan 2026 14:31:27 +0100 Subject: [PATCH 103/228] Cleanup --- install/minthcm-install.sh | 50 +++++++++++++------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/install/minthcm-install.sh b/install/minthcm-install.sh index 090a31135..a0e3b8310 100644 --- a/install/minthcm-install.sh +++ b/install/minthcm-install.sh @@ -12,14 +12,13 @@ catch_errors setting_up_container network_check update_os -PHP_VERSION="8.2" -PHP_APACHE="YES" PHP_MODULE="mysql,cli,redis" PHP_FPM="YES" setup_php -setup_composer -msg_info "Enabling Apache modules (rewrite, headers)" -$STD a2enmod rewrite -$STD a2enmod headers -msg_ok "Enabled Apache modules (rewrite, headers)" +PHP_VERSION="8.2" +PHP_APACHE="YES" PHP_MODULE="mysql,redis" PHP_FPM="YES" setup_php +setup_composer +setup_mariadb +MARIADB_DB_NAME="minthcm" MARIADB_DB_USER="minthcm" setup_mariadb_db +$STD mariadb -u root -e "SET GLOBAL sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'"; fetch_and_deploy_gh_release "MintHCM" "minthcm/minthcm" "tarball" "latest" "/var/www/MintHCM" @@ -37,19 +36,17 @@ mkdir -p /var/www/script cp /var/www/MintHCM/docker/script/generate_config.php /var/www/script/generate_config.php cp /var/www/MintHCM/docker/.env /var/www/script/.env chown -R www-data:www-data /var/www/script -msg_ok "Configured MintHCM" - -msg_info "Restarting Apache2" +$STD a2enmod rewrite +$STD a2enmod headers $STD systemctl restart apache2 -msg_ok "Restarted Apache2" +msg_ok "Configured MintHCM" msg_info "Setting up Elasticsearch" setup_deb822_repo \ "elasticsearch" \ "https://artifacts.elastic.co/GPG-KEY-elasticsearch" \ "https://artifacts.elastic.co/packages/7.x/apt" \ - "stable" \ - "main" + "stable" $STD apt install -y elasticsearch echo "-Xms2g" >>/etc/elasticsearch/jvm.options echo "-Xmx2g" >>/etc/elasticsearch/jvm.options @@ -57,35 +54,22 @@ $STD /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-attachment systemctl enable -q --now elasticsearch msg_ok "Set up Elasticsearch" -setup_mariadb -msg_info "Setting up MariaDB" -$STD mariadb -u root -e "SET GLOBAL sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'"; -msg_ok "Set up MariaDB" - msg_info "Configuring Database" -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -$STD mariadb -u root -e "CREATE USER 'minthcm'@'localhost' IDENTIFIED BY '${DB_PASS}';" -$STD mariadb -u root -e "GRANT ALL ON *.* TO 'minthcm'@'localhost'; FLUSH PRIVILEGES;" -sed -i 's/^DB_HOST=.*/DB_HOST=localhost/' /var/www/script/.env -sed -i 's/^DB_USER=.*/DB_USER=minthcm/' /var/www/script/.env -sed -i "s/^DB_PASS=.*/DB_PASS=${DB_PASS}/" /var/www/script/.env -sed -i 's/^ELASTICSEARCH_HOST=.*/ELASTICSEARCH_HOST=localhost/' /var/www/script/.env -msg_ok "Configured MariaDB" -{ - echo "MintHCM DB Credentials" - echo "MariaDB User: minthcm" - echo "MariaDB Password: $DB_PASS" -} >>~/minthcm.creds +sed -i "s/^DB_HOST=.*/DB_HOST=localhost/" /var/www/script/.env +sed -i "s/^DB_USER=.*/DB_USER=$MARIADB_DB_USER/" /var/www/script/.env +sed -i "s/^DB_PASS=.*/DB_PASS=$MARIADB_DB_PASS/" /var/www/script/.env +sed -i "s/^ELASTICSEARCH_HOST=.*/ELASTICSEARCH_HOST=localhost/" /var/www/script/.env +msg_ok "Configured Database" msg_info "Generating configuration file" set -a source /var/www/script/.env set +a -php /var/www/script/generate_config.php +$STD php /var/www/script/generate_config.php msg_ok "Generated configuration file" msg_info "Installing MintHCM" -cd /var/www/MintHCM && su -s /bin/bash -c 'php /var/www/MintHCM/MintCLI install < /var/www/MintHCM/configMint4' www-data +$STD sudo -u www-data php /var/www/MintHCM/MintCLI install < /var/www/MintHCM/configMint4 printf "* * * * * cd /var/www/MintHCM/legacy; php -f cron.php > /dev/null 2>&1\n" > /var/spool/cron/crontabs/www-data service cron start rm -f /var/www/MintHCM/configMint4 From ca0a11e87adffdd248936a2eebc48a157157a290 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 14:34:21 +0100 Subject: [PATCH 104/228] Update install --- ct/clawdbot.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ct/clawdbot.sh b/ct/clawdbot.sh index 6c82efe1c..14683a3f2 100644 --- a/ct/clawdbot.sh +++ b/ct/clawdbot.sh @@ -30,9 +30,6 @@ function update_script() { fi if check_for_gh_release "clawdbot" "clawdbot/clawdbot"; then - msg_info "Stopping Service" - systemctl stop clawdbot - msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true @@ -53,9 +50,6 @@ function update_script() { rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup msg_ok "Restored Data" - msg_info "Starting Service" - systemctl start clawdbot - msg_ok "Started Service" msg_ok "Updated successfully!" fi exit From c5dfa9d403f3e8c59a0505d82dcf6b9f3823493c Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:40:06 +0100 Subject: [PATCH 105/228] add latest --- install/jotty-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/jotty-install.sh b/install/jotty-install.sh index 67a37edae..e6b88fd59 100644 --- a/install/jotty-install.sh +++ b/install/jotty-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG -# Author: vhsdream | MickLesk +# Author: vhsdream | MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/fccview/jotty @@ -14,7 +14,7 @@ network_check update_os NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -fetch_and_deploy_gh_release "jotty" "fccview/jotty" "prebuild" "/opt/jotty" "jotty_*_prebuild.tar.gz" +fetch_and_deploy_gh_release "jotty" "fccview/jotty" "prebuild" "latest" "/opt/jotty" "jotty_*_prebuild.tar.gz" msg_info "Setup jotty" mkdir -p data/{users,checklists,notes} From 1e4864bb887940a8f7b50e1a13c92dc9274fe34e Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 14:43:57 +0100 Subject: [PATCH 106/228] Update install --- ct/clawdbot.sh | 36 ++++++++++++++---------------------- install/clawdbot-install.sh | 7 +------ 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/ct/clawdbot.sh b/ct/clawdbot.sh index 14683a3f2..7f147dff9 100644 --- a/ct/clawdbot.sh +++ b/ct/clawdbot.sh @@ -24,34 +24,26 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -d /opt/clawdbot ]]; then + if ! command -v clawdbot >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit fi - if check_for_gh_release "clawdbot" "clawdbot/clawdbot"; then + msg_info "Backing up Data" + cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true + cp -r /root/.clawdbot /root/.clawdbot_backup 2>/dev/null || true + msg_ok "Backed up Data" - msg_info "Backing up Data" - cp -r /opt/clawdbot/data /opt/clawdbot_data_backup 2>/dev/null || true - cp -r /root/.clawdbot /root/.clawdbot_backup 2>/dev/null || true - msg_ok "Backed up Data" + msg_info "Updating Clawdbot" + $STD npm install -g clawdbot@latest + msg_ok "Updated Clawdbot" - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" - - msg_info "Rebuilding Clawdbot" - cd /opt/clawdbot - $STD pnpm install --frozen-lockfile - $STD pnpm ui:build - msg_ok "Rebuilt Clawdbot" - - msg_info "Restoring Data" - cp -r /opt/clawdbot_data_backup/. /opt/clawdbot/data 2>/dev/null || true - cp -r /root/.clawdbot_backup/. /root/.clawdbot 2>/dev/null || true - rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup - msg_ok "Restored Data" - - msg_ok "Updated successfully!" - fi + msg_info "Restoring Data" + cp -r /opt/clawdbot_data_backup/. /opt/clawdbot/data 2>/dev/null || true + cp -r /root/.clawdbot_backup/. /root/.clawdbot 2>/dev/null || true + rm -rf /opt/clawdbot_data_backup /root/.clawdbot_backup + msg_ok "Restored Data" + msg_ok "Updated successfully!" exit } diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index f1a50cd4a..6769cc16a 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -22,12 +22,7 @@ msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" pnpm_version=$(grep -oP '"packageManager":\s*"pnpm@\K[^"]+' /opt/clawdbot/package.json 2>/dev/null || echo "latest") -NODE_VERSION="24" NODE_MODULE="pnpm@${pnpm_version}" setup_nodejs - -msg_info "Installing Clawdbot Dependencies" -cd /opt/clawdbot -$STD pnpm install --frozen-lockfile -msg_ok "Installed Dependencies" +NODE_VERSION="24" NODE_MODULE="pnpm@${pnpm_version},clawdbot@latest" setup_nodejs msg_info "Building Clawdbot UI" $STD pnpm ui:build From f9a464bdce09b0eca94eb32a43ad0080402913f5 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 26 Jan 2026 14:45:10 +0100 Subject: [PATCH 107/228] update minth --- install/minthcm-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/minthcm-install.sh b/install/minthcm-install.sh index a0e3b8310..ed6c8ac23 100644 --- a/install/minthcm-install.sh +++ b/install/minthcm-install.sh @@ -69,7 +69,8 @@ $STD php /var/www/script/generate_config.php msg_ok "Generated configuration file" msg_info "Installing MintHCM" -$STD sudo -u www-data php /var/www/MintHCM/MintCLI install < /var/www/MintHCM/configMint4 +cd /var/www/MintHCM +$STD sudo -u www-data php MintCLI install < /var/www/MintHCM/configMint4 printf "* * * * * cd /var/www/MintHCM/legacy; php -f cron.php > /dev/null 2>&1\n" > /var/spool/cron/crontabs/www-data service cron start rm -f /var/www/MintHCM/configMint4 From 6aacad8e30f577c4c409a8d8a1447eecc3221692 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:54:24 +0100 Subject: [PATCH 108/228] fixes --- tools/pve/update-apps.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index b4977e130..c4aa0d5ff 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -64,9 +64,10 @@ END { } header_info -msg_info "Loading all possible LXC containers from Proxmox VE. This may take a few seconds..." + whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit +msg_info "Loading all possible LXC containers from Proxmox VE. This may take a few seconds..." NODE=$(hostname) containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}') From f9fde3a8e0cc4c0df5581f2c3e764836da05ba63 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 26 Jan 2026 14:55:36 +0100 Subject: [PATCH 109/228] update minth --- install/minthcm-install.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/install/minthcm-install.sh b/install/minthcm-install.sh index ed6c8ac23..817c99e5b 100644 --- a/install/minthcm-install.sh +++ b/install/minthcm-install.sh @@ -17,7 +17,6 @@ PHP_VERSION="8.2" PHP_APACHE="YES" PHP_MODULE="mysql,redis" PHP_FPM="YES" setup_php setup_composer setup_mariadb -MARIADB_DB_NAME="minthcm" MARIADB_DB_USER="minthcm" setup_mariadb_db $STD mariadb -u root -e "SET GLOBAL sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'"; fetch_and_deploy_gh_release "MintHCM" "minthcm/minthcm" "tarball" "latest" "/var/www/MintHCM" @@ -55,9 +54,12 @@ systemctl enable -q --now elasticsearch msg_ok "Set up Elasticsearch" msg_info "Configuring Database" +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mariadb -u root -e "CREATE USER 'minthcm'@'localhost' IDENTIFIED BY '${DB_PASS}';" +$STD mariadb -u root -e "GRANT ALL ON *.* TO 'minthcm'@'localhost'; FLUSH PRIVILEGES;" sed -i "s/^DB_HOST=.*/DB_HOST=localhost/" /var/www/script/.env -sed -i "s/^DB_USER=.*/DB_USER=$MARIADB_DB_USER/" /var/www/script/.env -sed -i "s/^DB_PASS=.*/DB_PASS=$MARIADB_DB_PASS/" /var/www/script/.env +sed -i "s/^DB_USER=.*/DB_USER=minthcm/" /var/www/script/.env +sed -i "s/^DB_PASS=.*/DB_PASS=$DB_PASS/" /var/www/script/.env sed -i "s/^ELASTICSEARCH_HOST=.*/ELASTICSEARCH_HOST=localhost/" /var/www/script/.env msg_ok "Configured Database" From 66312f05d2e2a50a0c8a385b576c61726a78f4d6 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:56:55 +0100 Subject: [PATCH 110/228] features --- tools/pve/update-apps.sh | 236 ++++++++++++++++++++++++++++++++++----- 1 file changed, 209 insertions(+), 27 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index c4aa0d5ff..b57e7e822 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -6,6 +6,105 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/core.func) +# ============================================================================= +# CONFIGURATION VARIABLES +# Set these variables to skip interactive prompts (Whiptail dialogs) +# ============================================================================= +# var_backup: Enable/disable backup before update +# Options: "yes" | "no" | "" (empty = interactive prompt) +var_backup="${var_backup:-}" + +# var_backup_storage: Storage location for backups (only used if var_backup=yes) +# Options: Storage name from /etc/pve/storage.cfg (e.g., "local", "nas-backup") +# Leave empty for interactive selection +var_backup_storage="${var_backup_storage:-}" + +# var_container: Which containers to update +# Options: +# - "all" : All containers with community-scripts tags +# - "all_running" : Only running containers with community-scripts tags +# - "all_stopped" : Only stopped containers with community-scripts tags +# - "101,102,109" : Comma-separated list of specific container IDs +# - "" : Interactive selection via Whiptail +var_container="${var_container:-}" + +# var_unattended: Run updates without user interaction inside containers +# Options: "yes" | "no" | "" (empty = interactive prompt) +var_unattended="${var_unattended:-}" + +# var_skip_confirm: Skip initial confirmation dialog +# Options: "yes" | "no" (default: no) +var_skip_confirm="${var_skip_confirm:-no}" + +# var_auto_reboot: Automatically reboot containers that require it after update +# Options: "yes" | "no" | "" (empty = interactive prompt) +var_auto_reboot="${var_auto_reboot:-}" + +# ============================================================================= +# JSON CONFIG EXPORT +# Run with --export-config to output current configuration as JSON +# ============================================================================= + +function export_config_json() { + cat <&2 2>&1 1>&3 | tr -d '"') +# Determine container selection based on var_container +if [[ -n "$var_container" ]]; then + case "$var_container" in + all) + # Select all containers with matching tags + CHOICE="" + for ((i=0; i<${#menu_items[@]}; i+=3)); do + CHOICE="$CHOICE ${menu_items[$i]}" + done + CHOICE=$(echo "$CHOICE" | xargs) + ;; + all_running) + # Select only running containers with matching tags + CHOICE="" + for ((i=0; i<${#menu_items[@]}; i+=3)); do + cid="${menu_items[$i]}" + if pct status "$cid" 2>/dev/null | grep -q "running"; then + CHOICE="$CHOICE $cid" + fi + done + CHOICE=$(echo "$CHOICE" | xargs) + ;; + all_stopped) + # Select only stopped containers with matching tags + CHOICE="" + for ((i=0; i<${#menu_items[@]}; i+=3)); do + cid="${menu_items[$i]}" + if pct status "$cid" 2>/dev/null | grep -q "stopped"; then + CHOICE="$CHOICE $cid" + fi + done + CHOICE=$(echo "$CHOICE" | xargs) + ;; + *) + # Assume comma-separated list of container IDs + CHOICE=$(echo "$var_container" | tr ',' ' ') + ;; + esac -if [ -z "$CHOICE" ]; then - whiptail --title "LXC Container Update" \ - --msgbox "No containers selected!" 10 60 - exit 1 + if [[ -z "$CHOICE" ]]; then + msg_error "No containers matched the selection criteria: $var_container" + exit 1 + fi + msg_ok "Selected containers: $CHOICE" +else + CHOICE=$(whiptail --title "LXC Container Update" \ + --checklist "Select LXC containers to update:" 25 60 13 \ + "${menu_items[@]}" 3>&2 2>&1 1>&3 | tr -d '"') + + if [ -z "$CHOICE" ]; then + whiptail --title "LXC Container Update" \ + --msgbox "No containers selected!" 10 60 + exit 1 + fi fi header_info -BACKUP_CHOICE="no" -if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then - BACKUP_CHOICE="yes" + +# Determine backup choice based on var_backup +if [[ -n "$var_backup" ]]; then + BACKUP_CHOICE="$var_backup" +else + BACKUP_CHOICE="no" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then + BACKUP_CHOICE="yes" + fi fi -UNATTENDED_UPDATE="no" -if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then - UNATTENDED_UPDATE="yes" +# Determine unattended update based on var_unattended +if [[ -n "$var_unattended" ]]; then + UNATTENDED_UPDATE="$var_unattended" +else + UNATTENDED_UPDATE="no" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then + UNATTENDED_UPDATE="yes" + fi fi if [ "$BACKUP_CHOICE" == "yes" ]; then - #STORAGES=$(awk '/^(\S+):/ {storage=$2} /content.*backup/ {print storage}' /etc/pve/storage.cfg) get_backup_storages if [ -z "$STORAGES" ]; then - whiptail --msgbox "No storage with 'backup' found!" 8 40 + msg_error "No storage with 'backup' support found!" exit 1 fi - MENU_ITEMS=() - for STORAGE in $STORAGES; do - MENU_ITEMS+=("$STORAGE" "") - done + # Determine storage based on var_backup_storage + if [[ -n "$var_backup_storage" ]]; then + # Validate that the specified storage exists and supports backups + if echo "$STORAGES" | grep -qw "$var_backup_storage"; then + STORAGE_CHOICE="$var_backup_storage" + msg_ok "Using backup storage: $STORAGE_CHOICE" + else + msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups!" + msg_info "Available storages: $(echo $STORAGES | tr '\n' ' ')" + exit 1 + fi + else + 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) + 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 + if [ -z "$STORAGE_CHOICE" ]; then + msg_error "No storage selected!" + exit 1 + fi fi fi @@ -271,9 +442,20 @@ if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then for container_name in "${containers_needing_reboot[@]}"; do echo "$container_name" done - echo -ne "${INFO} Do you wish to reboot these containers? " - read -r prompt - if [[ ${prompt,,} =~ ^(yes)$ ]]; then + + # Determine reboot choice based on var_auto_reboot + REBOOT_CHOICE="no" + if [[ -n "$var_auto_reboot" ]]; then + REBOOT_CHOICE="$var_auto_reboot" + else + echo -ne "${INFO} Do you wish to reboot these containers? " + read -r prompt + if [[ ${prompt,,} =~ ^(yes)$ ]]; then + REBOOT_CHOICE="yes" + fi + fi + + if [[ "$REBOOT_CHOICE" == "yes" ]]; then echo -e "${CROSS}${HOLD} ${YWB}Rebooting containers.${CL}" for container_name in "${containers_needing_reboot[@]}"; do container=$(echo $container_name | cut -d " " -f 1) From 039121af88d1d2668932b035f5460067c271917c Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:00:53 +0100 Subject: [PATCH 111/228] increase json --- frontend/public/json/update-apps.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/frontend/public/json/update-apps.json b/frontend/public/json/update-apps.json index 0cde58439..c94e9a327 100644 --- a/frontend/public/json/update-apps.json +++ b/frontend/public/json/update-apps.json @@ -51,6 +51,30 @@ { "text": "At the end of the update, containers requiring a reboot will be listed, and you may choose to reboot them directly.", "type": "info" + }, + { + "text": "Use `var_backup=yes|no` to enable/disable backup (skip prompt).", + "type": "info" + }, + { + "text": "Use `var_backup_storage=` to set backup storage location.", + "type": "info" + }, + { + "text": "Use `var_container=all|all_running|all_stopped|101,102,...` to select containers.", + "type": "info" + }, + { + "text": "Use `var_unattended=yes|no` to run updates without interaction.", + "type": "info" + }, + { + "text": "Use `var_skip_confirm=yes` to skip initial confirmation dialog.", + "type": "info" + }, + { + "text": "Use `var_auto_reboot=yes|no` to auto-reboot containers after update.", + "type": "info" } ] } From 870dc3a42f0b14902cbc3a74ff3d19f04278c4a1 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 15:14:07 +0100 Subject: [PATCH 112/228] Update install --- install/clawdbot-install.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index 6769cc16a..1ed3ed574 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -22,7 +22,12 @@ msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" pnpm_version=$(grep -oP '"packageManager":\s*"pnpm@\K[^"]+' /opt/clawdbot/package.json 2>/dev/null || echo "latest") -NODE_VERSION="24" NODE_MODULE="pnpm@${pnpm_version},clawdbot@latest" setup_nodejs +NODE_VERSION="24" NODE_MODULE="pnpm@${pnpm_version}" setup_nodejs + +msg_info "Installing Clawdbot" +$STD pnpm add -g clawdbot@latest +msg_ok "Installed Clawdbot" + msg_info "Building Clawdbot UI" $STD pnpm ui:build From 96e1adbf0112201e02261537340bc8df58b8e1b8 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 15:24:18 +0100 Subject: [PATCH 113/228] Update install --- install/clawdbot-install.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index 1ed3ed574..38839017b 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -24,6 +24,14 @@ fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" pnpm_version=$(grep -oP '"packageManager":\s*"pnpm@\K[^"]+' /opt/clawdbot/package.json 2>/dev/null || echo "latest") NODE_VERSION="24" NODE_MODULE="pnpm@${pnpm_version}" setup_nodejs +msg_info "Setting up pnpm" +$STD pnpm setup +export PNPM_HOME="$HOME/.local/share/pnpm" +export PATH="$PNPM_HOME:$PATH" +echo 'export PNPM_HOME="$HOME/.local/share/pnpm"' >> ~/.bashrc +echo 'export PATH="$PNPM_HOME:$PATH"' >> ~/.bashrc +msg_ok "Set up pnpm" + msg_info "Installing Clawdbot" $STD pnpm add -g clawdbot@latest msg_ok "Installed Clawdbot" From f91ba71b8a3d0e456fed2e1b851a1fa4fc4a8a35 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Mon, 26 Jan 2026 15:31:48 +0100 Subject: [PATCH 114/228] Initial commit for ebusd New script for ebusd --- ct/ebusd.sh | 43 +++++++++++++++++++++++++++ ct/headers/ebusd | 6 ++++ frontend/public/json/ebusd.json | 52 +++++++++++++++++++++++++++++++++ install/ebusd-install.sh | 39 +++++++++++++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 ct/ebusd.sh create mode 100644 ct/headers/ebusd create mode 100644 frontend/public/json/ebusd.json create mode 100644 install/ebusd-install.sh diff --git a/ct/ebusd.sh b/ct/ebusd.sh new file mode 100644 index 000000000..5ce7fd284 --- /dev/null +++ b/ct/ebusd.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/heinemannj/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Joerg Heinemann (heinemannj) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/john30/ebusd + +APP="ebusd" +var_tags="${var_tags:-automation}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/apt/sources.list.d/ebusd.sources ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt update + $STD apt -y upgrade + msg_ok "Updated $APP LXC" + 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}" diff --git a/ct/headers/ebusd b/ct/headers/ebusd new file mode 100644 index 000000000..fec79a3e3 --- /dev/null +++ b/ct/headers/ebusd @@ -0,0 +1,6 @@ + __ __ + ___ / /_ _ ___________/ / + / _ \/ __ \/ / / / ___/ __ / +/ __/ /_/ / /_/ (__ ) /_/ / +\___/_.___/\__,_/____/\__,_/ + diff --git a/frontend/public/json/ebusd.json b/frontend/public/json/ebusd.json new file mode 100644 index 000000000..3db2f8542 --- /dev/null +++ b/frontend/public/json/ebusd.json @@ -0,0 +1,52 @@ +{ + "name": "ebusd", + "slug": "ebusd", + "categories": [ + 16 + ], + "date_created": "2026-01-26", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://github.com/john30/ebusd/wiki", + "website": "https://github.com/john30/ebusd", + "logo": "", + "config_path": "/etc/default/ebusd", + "description": "ebusd is a daemon for handling communication with eBUS devices connected to a 2-wire bus system ("energy bus" used by numerous heating systems).", + "install_methods": [ + { + "type": "default", + "script": "ct/ebusd.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Instructions: \ + 1. Edit `/etc/default/ebusd` if necessary (especially if your device is not `/dev/ttyUSB0`) \ + 2. Start the daemon with `systemctl start ebusd` \ + 3. Check the log file `/var/log/ebusd.log` \ + 4. Make the daemon autostart with `systemctl enable ebusd`", + "type": "info" + }, + { + "text": "Working `/etc/default/ebusd` options for `ebus adapter shield v5`: EBUSD_OPTS='--pidfile=/run/ebusd.pid --latency=100 --scanconfig --configpath=https://ebus.github.io/ --accesslevel=* --pollinterval=30 --device=ens:x.x.x.x:9999 --mqtthost=x.x.x.x --mqttport=1883 --mqttuser=ha-mqtt --mqttpass=xxxxxxxx! --mqttint=/etc/ebusd/mqtt-hassio.cfg --mqttjson --mqttlog --mqttretain --mqtttopic=ebusd --log=all:notice --log=main:notice --log=bus:notice --log=update:notice --log=network:notice --log=other:notice'", + "type": "info" + }, + { + "text": "Only tested with `ebusd-25.1_amd64-bookworm.deb`!", + "type": "warning" + } + ] +} diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh new file mode 100644 index 000000000..32c2eb9a2 --- /dev/null +++ b/install/ebusd-install.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Joerg Heinemann (heinemannj) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/john30/ebusd + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setting up ebusd Repository" +setup_deb822_repo \ + "ebusd" \ + "https://raw.githubusercontent.com/john30/ebusd-debian/master/ebusd.gpg" \ + "https://repo.ebusd.eu/apt/default/bookworm/" \ + "bookworm" \ + "main" +$STD apt update +msg_ok "ebusd Repository setup sucessfully" + +msg_info "Installing ebusd" +$STD apt install -y ebusd +msg_ok "Installed ebusd" + +msg_info "Follow below instructions to make the daemon autostart:" +msg_info "1. Edit '/etc/default/ebusd' if necessary (especially if your device is not '/dev/ttyUSB0')" +msg_info "2. Start the daemon with 'systemctl start ebusd'" +msg_info "3. Check the daemon status with 'systemctl status ebusd'" +msg_info "4. Check the log file '/var/log/ebusd.log'" +msg_info "5. Make the daemon autostart with 'systemctl enable ebusd'" + +motd_ssh +customize +cleanup_lxc From 7f01db5f823f03dbcc51f21b99b70a2485f35f76 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 26 Jan 2026 15:33:30 +0100 Subject: [PATCH 115/228] Update install --- install/clawdbot-install.sh | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index 38839017b..b9ab2ec4f 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -19,27 +19,14 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -fetch_and_deploy_gh_release "clawdbot" "clawdbot/clawdbot" -pnpm_version=$(grep -oP '"packageManager":\s*"pnpm@\K[^"]+' /opt/clawdbot/package.json 2>/dev/null || echo "latest") -NODE_VERSION="24" NODE_MODULE="pnpm@${pnpm_version}" setup_nodejs - -msg_info "Setting up pnpm" -$STD pnpm setup -export PNPM_HOME="$HOME/.local/share/pnpm" -export PATH="$PNPM_HOME:$PATH" -echo 'export PNPM_HOME="$HOME/.local/share/pnpm"' >> ~/.bashrc -echo 'export PATH="$PNPM_HOME:$PATH"' >> ~/.bashrc -msg_ok "Set up pnpm" +NODE_VERSION="24" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Installing Clawdbot" -$STD pnpm add -g clawdbot@latest +curl -fsSL https://clawd.bot/install.sh | bash msg_ok "Installed Clawdbot" -msg_info "Building Clawdbot UI" -$STD pnpm ui:build -msg_ok "Built Clawdbot UI" motd_ssh customize From d62da49b8afa502ae0f754dbf9fe5770137a7659 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Mon, 26 Jan 2026 15:54:25 +0100 Subject: [PATCH 116/228] Update ct/ebusd.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- ct/ebusd.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/ebusd.sh b/ct/ebusd.sh index 5ce7fd284..732bc8179 100644 --- a/ct/ebusd.sh +++ b/ct/ebusd.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/heinemannj/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Joerg Heinemann (heinemannj) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 4888dbca6c47b25d13ddcdc6c3bd9b8c0d9d850a Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Mon, 26 Jan 2026 15:55:17 +0100 Subject: [PATCH 117/228] Update ct/ebusd.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- ct/ebusd.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/ebusd.sh b/ct/ebusd.sh index 732bc8179..2791cb696 100644 --- a/ct/ebusd.sh +++ b/ct/ebusd.sh @@ -2,7 +2,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Joerg Heinemann (heinemannj) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/john30/ebusd APP="ebusd" From b945cf7543467733676c6a40c21cfd1f7415b9f1 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Mon, 26 Jan 2026 15:57:30 +0100 Subject: [PATCH 118/228] Update install/ebusd-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/ebusd-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh index 32c2eb9a2..1abc0b12b 100644 --- a/install/ebusd-install.sh +++ b/install/ebusd-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: Joerg Heinemann (heinemannj) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/john30/ebusd source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From 482681222aceb4541d464444492ae6f9bdbe2ad1 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Mon, 26 Jan 2026 15:58:22 +0100 Subject: [PATCH 119/228] Update install/ebusd-install.sh Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- install/ebusd-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh index 1abc0b12b..17c21b96a 100644 --- a/install/ebusd-install.sh +++ b/install/ebusd-install.sh @@ -21,7 +21,7 @@ setup_deb822_repo \ "bookworm" \ "main" $STD apt update -msg_ok "ebusd Repository setup sucessfully" +msg_ok "ebusd Repository setup successfully" msg_info "Installing ebusd" $STD apt install -y ebusd From 8a06452794e3d0bafe37566c2a4fc217f2c50fc9 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Mon, 26 Jan 2026 15:58:59 +0100 Subject: [PATCH 120/228] Update frontend/public/json/ebusd.json Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- frontend/public/json/ebusd.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/ebusd.json b/frontend/public/json/ebusd.json index 3db2f8542..0be1da11b 100644 --- a/frontend/public/json/ebusd.json +++ b/frontend/public/json/ebusd.json @@ -28,7 +28,7 @@ } ], "default_credentials": { - "username": null, + "username": "root", "password": null }, "notes": [ From f8124fc6a4e91bdae167051cf00053aa4441e311 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 10:03:31 -0500 Subject: [PATCH 121/228] Shelfmark: source virtualenv during update --- ct/shelfmark.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh index 3c1d1dfab..2aad4a5d0 100644 --- a/ct/shelfmark.sh +++ b/ct/shelfmark.sh @@ -49,6 +49,7 @@ function update_script() { mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist cd /opt/shelfmark $STD uv venv -c ./venv + $STD source ./venv/bin/activate $STD uv pip install -r requirements-base.txt mv /opt/start.sh.bak /opt/start.sh msg_ok "Updated Shelfmark" From 78ed40e5bd0c3634edbf51d482bd7e6b0f3fb948 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 10:06:07 -0500 Subject: [PATCH 122/228] Shelfmark: symlink unrar-free to unrar --- install/shelfmark-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index 729cf7d7a..8a780d891 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -16,6 +16,7 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ unrar-free +ln -sf /usr/bin/unrar-free /usr/bin/unrar msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs From 02d6b368ef9b658bba3481ffb7deee8afd7734ed Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 10:23:20 -0500 Subject: [PATCH 123/228] Shelfmark: prompt to install FlareSolverr --- install/shelfmark-install.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index 8a780d891..04ffb8dd0 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -25,6 +25,42 @@ PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" RELEASE_VERSION=$(cat "$HOME/.shelfmark") +read -r -p "${TAB3}Install FlareSolverr? Choose N|n if you have an external instance y/N " fs +if [[ ${fs,,} =~ ^(y|Y|yes)$ ]]; then + fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" + msg_info "Installing FlareSolverr" + $STD apt install -y xvfb + setup_deb822_repo \ + "google-chrome" \ + "https://dl.google.com/linux/linux_signing_key.pub" \ + "https://dl.google.com/linux/chrome/deb/" \ + "stable" + $STD apt update + $STD apt install -y google-chrome-stable + # remove google-chrome.list added by google-chrome-stable + rm /etc/apt/sources.list.d/google-chrome.list + + cat </etc/systemd/system/flaresolverr.service +[Unit] +Description=FlareSolverr +After=network.target +[Service] +SyslogIdentifier=flaresolverr +Restart=always +RestartSec=5 +Type=simple +Environment="LOG_LEVEL=info" +Environment="CAPTCHA_SOLVER=none" +WorkingDirectory=/opt/flaresolverr +ExecStart=/opt/flaresolverr/flaresolverr +TimeoutStopSec=30 +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now flaresolverr + msg_ok "Installed FlareSolverr" +fi + msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend $STD npm ci From 05741195c2e46e6805a6b8cc5182efb4730a55e1 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:14:22 +0100 Subject: [PATCH 124/228] Update clawdbot-install.sh --- install/clawdbot-install.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/install/clawdbot-install.sh b/install/clawdbot-install.sh index b9ab2ec4f..63556fb45 100644 --- a/install/clawdbot-install.sh +++ b/install/clawdbot-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ build-essential \ git msg_ok "Installed Dependencies" @@ -22,10 +22,7 @@ msg_ok "Installed Dependencies" NODE_VERSION="24" NODE_MODULE="pnpm@latest" setup_nodejs -msg_info "Installing Clawdbot" curl -fsSL https://clawd.bot/install.sh | bash -msg_ok "Installed Clawdbot" - motd_ssh From e05e9e1a2407516409a57c146a9fce6feebee9f6 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 12:44:54 -0500 Subject: [PATCH 125/228] Shelfmark: CF Bypass options during install --- ct/shelfmark.sh | 5 +- install/shelfmark-install.sh | 114 +++++++++++++++++++++++++++-------- 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh index 2aad4a5d0..487860af1 100644 --- a/ct/shelfmark.sh +++ b/ct/shelfmark.sh @@ -50,7 +50,10 @@ function update_script() { cd /opt/shelfmark $STD uv venv -c ./venv $STD source ./venv/bin/activate - $STD uv pip install -r requirements-base.txt + $STD uv pip install -r ./requirements-base.txt + if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env) == "false" ]]; then + $STD uv pip install -r ./requirements-shelfmark.txt + fi mv /opt/start.sh.bak /opt/start.sh msg_ok "Updated Shelfmark" diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index 04ffb8dd0..a50539f3c 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -19,16 +19,72 @@ $STD apt install -y \ ln -sf /usr/bin/unrar-free /usr/bin/unrar msg_ok "Installed Dependencies" -NODE_VERSION="22" setup_nodejs -PYTHON_VERSION="3.12" setup_uv +mkdir -p /etc/shelfmark +cat </etc/shelfmark/.env +DOCKERMODE=false +CONFIG_DIR=/etc/shelfmark +TMP_DIR=/tmp/shelfmark +ENABLE_LOGGING=true +FLASK_HOST=0.0.0.0 +FLASK_PORT=8084 +# SESSION_COOKIES_SECURE=true +# CWA_DB_PATH= +USE_CF_BYPASS=true +USING_EXTERNAL_BYPASSER=false +# EXT_BYPASSER_URL= +# EXT_BYPASSER_PATH=/v1 +EOF -fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" -RELEASE_VERSION=$(cat "$HOME/.shelfmark") +echo "" +echo "" +echo -e "${BL}Shelfmark Deployment Type${CL}" +echo "─────────────────────────────────────────" +echo "Please choose your deployment type:" +echo "" +echo " 1) Use Shelfmark's internal captcha bypasser (default)" +echo " 2) Install FlareSolverr in this LXC" +echo " 3) Use an existing Flaresolverr LXC" +echo " 4) Disable captcha bypassing altogether (not recommended)" +echo "" -read -r -p "${TAB3}Install FlareSolverr? Choose N|n if you have an external instance y/N " fs -if [[ ${fs,,} =~ ^(y|Y|yes)$ ]]; then +read -r -p "${TAB3}Select deployment type [1]: " DEPLOYMENT_TYPE +DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-1}" + +case "$DEPLOYMENT_TYPE" in +1) + msg_ok "Using Shelfmark's internal captcha bypasser" + ;; +2) + msg_ok "Proceeding with FlareSolverr installation" + ;; +3) + echo "" + echo -e "${BL}Use existing FlareSolverr LXC${CL}" + echo "─────────────────────────────────────────" + echo "Enter the URL/IP address with port of your Flaresolverr instance" + echo "Example: http://flaresoverr.homelab.lan:8191 or" + echo "http://192.168.10.99:8191" + echo "" + read -r -p "FlareSolverr URL: " FLARESOLVERR_URL + + if [[ -z "$FLARESOLVERR_URL" ]]; then + msg_warn "No Flaresolverr URL provided. Falling back to Shelfmark's internal bypasser." + else + FLARESOLVERR_URL="${FLARESOLVERR_URL%/}" + msg_ok "FlareSolverr URL: ${FLARESOLVERR_URL}" + fi + ;; +4) + msg_warn "Disabling captcha bypass. This may cause the majority of searches and downloads to fail." + ;; +*) + msg_warn "Invalid selection. Reverting to default (internal bypasser)!" + ;; +esac + +if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" - msg_info "Installing FlareSolverr" + msg_info "Installing FlareSolverr (please wait)" $STD apt install -y xvfb setup_deb822_repo \ "google-chrome" \ @@ -39,7 +95,7 @@ if [[ ${fs,,} =~ ^(y|Y|yes)$ ]]; then $STD apt install -y google-chrome-stable # remove google-chrome.list added by google-chrome-stable rm /etc/apt/sources.list.d/google-chrome.list - + sed -i '/BYPASSER=/s/false/true' /etc/shelfmark/.env cat </etc/systemd/system/flaresolverr.service [Unit] Description=FlareSolverr @@ -59,8 +115,29 @@ WantedBy=multi-user.target EOF systemctl enable -q --now flaresolverr msg_ok "Installed FlareSolverr" +elif [[ "$DEPLOYMENT_TYPE" == "3" ]]; then + sed -i -e '/BYPASSER=/s/false/true/' \ + -e '/^# EXT_/EXT_/' \ + -e "s\|_URL=.*|${FLARESOLVERR_URL}|" /etc/shelfmark/.env +elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then + sed -i '/_BYPASS=/s/true/false' /etc/shelfmark/.env +else + msg_info "Installing internal bypasser dependencies" + $STD apt install -y \ + xfvb \ + ffmpeg \ + chromium=143.0.7499.169-1~deb13u1 \ + chromium-driver=143.0.7499.169-1~deb13u1 \ + python3-tk + msg_ok "Installed internal bypasser dependencies" fi +NODE_VERSION="22" setup_nodejs +PYTHON_VERSION="3.12" setup_uv + +fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" +RELEASE_VERSION=$(cat "$HOME/.shelfmark") + msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend $STD npm ci @@ -72,23 +149,10 @@ msg_info "Configuring Shelfmark" cd /opt/shelfmark $STD uv venv ./venv $STD source ./venv/bin/activate -$STD uv pip install -r requirements-base.txt -mkdir -p {/var/log/shelfmark,/tmp/shelfmark,/etc/shelfmark} -cat </etc/shelfmark/.env -DOCKERMODE=false -CONFIG_DIR=/etc/shelfmark -TMP_DIR=/tmp/shelfmark -ENABLE_LOGGING=true -FLASK_HOST=0.0.0.0 -FLASK_PORT=8084 -RELEASE_VERSION=$RELEASE_VERSION -# SESSION_COOKIES_SECURE=true -# CWA_DB_PATH= -# USE_CF_BYPASS=true -# USING_EXTERNAL_BYPASSER=true -# EXT_BYPASSER_URL= -# EXT_BYPASSER_PATH= -EOF +$STD uv pip install -r ./requirements-base.txt +[[ "$DEPLOYMENT_TYPE" == "1" ]] && $STD uv pip install -r ./requirements-shelfmark.txt +mkdir -p {/var/log/shelfmark,/tmp/shelfmark} +echo "$RELEASE_VERSION" >>/etc/shelfmark/.env msg_ok "Configured Shelfmark" msg_info "Creating Service and start script" From f918c81c922999a18b1e4c020874df80983694f6 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 13:06:38 -0500 Subject: [PATCH 126/228] fix start script restore --- ct/shelfmark.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh index 487860af1..f7ba86a0c 100644 --- a/ct/shelfmark.sh +++ b/ct/shelfmark.sh @@ -54,7 +54,7 @@ function update_script() { if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env) == "false" ]]; then $STD uv pip install -r ./requirements-shelfmark.txt fi - mv /opt/start.sh.bak /opt/start.sh + mv /opt/start.sh.bak /opt/shelfmark/start.sh msg_ok "Updated Shelfmark" msg_info "Starting Service" From 57a3d711731568a46c77e03aeadb4d1ec1e1fe54 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 13:32:52 -0500 Subject: [PATCH 127/228] fix chromium deps installation --- install/shelfmark-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index a50539f3c..f3824ec0c 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -123,9 +123,10 @@ elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then sed -i '/_BYPASS=/s/true/false' /etc/shelfmark/.env else msg_info "Installing internal bypasser dependencies" - $STD apt install -y \ + $STD apt install -y --no-install-recommends \ xfvb \ ffmpeg \ + chromium-common=143.0.7499.169-1~deb13u1 \ chromium=143.0.7499.169-1~deb13u1 \ chromium-driver=143.0.7499.169-1~deb13u1 \ python3-tk From 654ffc5a2c6918014790ffa9d2171d08b73cddec Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 13:46:05 -0500 Subject: [PATCH 128/228] fix typo --- install/shelfmark-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index f3824ec0c..775a4db70 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -124,7 +124,7 @@ elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then else msg_info "Installing internal bypasser dependencies" $STD apt install -y --no-install-recommends \ - xfvb \ + xvfb \ ffmpeg \ chromium-common=143.0.7499.169-1~deb13u1 \ chromium=143.0.7499.169-1~deb13u1 \ From 9c03a5dcd636e579f771c9ebc39468694380df34 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:24:54 +0100 Subject: [PATCH 129/228] Update install/ebusd-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/ebusd-install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh index 17c21b96a..c43be4708 100644 --- a/install/ebusd-install.sh +++ b/install/ebusd-install.sh @@ -13,15 +13,12 @@ setting_up_container network_check update_os -msg_info "Setting up ebusd Repository" setup_deb822_repo \ "ebusd" \ "https://raw.githubusercontent.com/john30/ebusd-debian/master/ebusd.gpg" \ "https://repo.ebusd.eu/apt/default/bookworm/" \ "bookworm" \ "main" -$STD apt update -msg_ok "ebusd Repository setup successfully" msg_info "Installing ebusd" $STD apt install -y ebusd From 979057a88835cf7b1563431517ae42ecff185293 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 15:37:31 -0500 Subject: [PATCH 130/228] Shelfmark: add chromium service file --- ct/shelfmark.sh | 4 +++- install/shelfmark-install.sh | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh index f7ba86a0c..be53f2d9a 100644 --- a/ct/shelfmark.sh +++ b/ct/shelfmark.sh @@ -35,6 +35,7 @@ function update_script() { if check_for_gh_release "shelfmark" "calibrain/shelfmark"; then msg_info "Stopping Service" systemctl stop shelfmark + [[ -f /etc/systemd/system/chromium.service ]] && systemctl stop chromium msg_ok "Stopped Service" cp /opt/shelfmark/start.sh /opt/start.sh.bak @@ -51,7 +52,7 @@ function update_script() { $STD uv venv -c ./venv $STD source ./venv/bin/activate $STD uv pip install -r ./requirements-base.txt - if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env) == "false" ]]; then + if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env == "false") ]]; then $STD uv pip install -r ./requirements-shelfmark.txt fi mv /opt/start.sh.bak /opt/shelfmark/start.sh @@ -59,6 +60,7 @@ function update_script() { msg_info "Starting Service" systemctl start shelfmark + [[ -f /etc/systemd/system/chromium.service ]] && systemctl start chromium msg_ok "Started Service" msg_ok "Updated successfully!" fi diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index 775a4db70..c2f3727bb 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -122,6 +122,7 @@ elif [[ "$DEPLOYMENT_TYPE" == "3" ]]; then elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then sed -i '/_BYPASS=/s/true/false' /etc/shelfmark/.env else + DEPLOYMENT_TYPE="1" msg_info "Installing internal bypasser dependencies" $STD apt install -y --no-install-recommends \ xvfb \ @@ -141,6 +142,7 @@ RELEASE_VERSION=$(cat "$HOME/.shelfmark") msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend +echo "$RELEASE_VERSION" >>/etc/shelfmark/.env $STD npm ci $STD npm run build mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist @@ -153,10 +155,9 @@ $STD source ./venv/bin/activate $STD uv pip install -r ./requirements-base.txt [[ "$DEPLOYMENT_TYPE" == "1" ]] && $STD uv pip install -r ./requirements-shelfmark.txt mkdir -p {/var/log/shelfmark,/tmp/shelfmark} -echo "$RELEASE_VERSION" >>/etc/shelfmark/.env msg_ok "Configured Shelfmark" -msg_info "Creating Service and start script" +msg_info "Creating Services and start script" cat </etc/systemd/system/shelfmark.service [Unit] Description=Shelfmark server @@ -174,6 +175,23 @@ RestartSec=10 WantedBy=multi-user.target EOF +if [[ "$DEPLOYMENT_TYPE" == "1" ]]; then + cat </etc/systemd/system/chromium.service +[Unit] +Description=karakeep Headless Browser +After=network.target + +[Service] +User=root +ExecStart=/usr/bin/chromium --headless --no-sandbox --disable-gpu --disable-dev-shm-usage --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --hide-scrollbars +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now chromium +fi + cat </opt/shelfmark/start.sh #!/usr/bin/env bash From d97313ad7884b772d345f258d6ec7046075cd71a Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 16:21:20 -0500 Subject: [PATCH 131/228] Shelfmark: move flaresolverr service file creation --- install/shelfmark-install.sh | 40 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index c2f3727bb..d5db9dc37 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -95,25 +95,7 @@ if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then $STD apt install -y google-chrome-stable # remove google-chrome.list added by google-chrome-stable rm /etc/apt/sources.list.d/google-chrome.list - sed -i '/BYPASSER=/s/false/true' /etc/shelfmark/.env - cat </etc/systemd/system/flaresolverr.service -[Unit] -Description=FlareSolverr -After=network.target -[Service] -SyslogIdentifier=flaresolverr -Restart=always -RestartSec=5 -Type=simple -Environment="LOG_LEVEL=info" -Environment="CAPTCHA_SOLVER=none" -WorkingDirectory=/opt/flaresolverr -ExecStart=/opt/flaresolverr/flaresolverr -TimeoutStopSec=30 -[Install] -WantedBy=multi-user.target -EOF - systemctl enable -q --now flaresolverr + sed -i '/BYPASSER=/s/false/true/' /etc/shelfmark/.env msg_ok "Installed FlareSolverr" elif [[ "$DEPLOYMENT_TYPE" == "3" ]]; then sed -i -e '/BYPASSER=/s/false/true/' \ @@ -191,6 +173,26 @@ WantedBy=multi-user.target EOF systemctl enable -q --now chromium fi +if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then + cat </etc/systemd/system/flaresolverr.service +[Unit] +Description=FlareSolverr +After=network.target +[Service] +SyslogIdentifier=flaresolverr +Restart=always +RestartSec=5 +Type=simple +Environment="LOG_LEVEL=info" +Environment="CAPTCHA_SOLVER=none" +WorkingDirectory=/opt/flaresolverr +ExecStart=/opt/flaresolverr/flaresolverr +TimeoutStopSec=30 +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now flaresolverr +fi cat </opt/shelfmark/start.sh #!/usr/bin/env bash From e68e3894c39c8b61e4cc095b4a06e592cf57738a Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 17:34:20 -0500 Subject: [PATCH 132/228] fix sed --- install/shelfmark-install.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index d5db9dc37..bf4766d6f 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -43,7 +43,7 @@ echo "Please choose your deployment type:" echo "" echo " 1) Use Shelfmark's internal captcha bypasser (default)" echo " 2) Install FlareSolverr in this LXC" -echo " 3) Use an existing Flaresolverr LXC" +echo " 3) Use an existing Flaresolverr/Byparr LXC" echo " 4) Disable captcha bypassing altogether (not recommended)" echo "" @@ -95,14 +95,16 @@ if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then $STD apt install -y google-chrome-stable # remove google-chrome.list added by google-chrome-stable rm /etc/apt/sources.list.d/google-chrome.list - sed -i '/BYPASSER=/s/false/true/' /etc/shelfmark/.env + sed -i -e '/BYPASSER=/s/false/true/' \ + -e 's/^# EXT_/EXT_/' \ + -e "s|_URL=.*|http://localhost:8191|" /etc/shelfmark/.env msg_ok "Installed FlareSolverr" elif [[ "$DEPLOYMENT_TYPE" == "3" ]]; then sed -i -e '/BYPASSER=/s/false/true/' \ - -e '/^# EXT_/EXT_/' \ - -e "s\|_URL=.*|${FLARESOLVERR_URL}|" /etc/shelfmark/.env + -e 's/^# EXT_/EXT_/' \ + -e "s|_URL=.*|${FLARESOLVERR_URL}|" /etc/shelfmark/.env elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then - sed -i '/_BYPASS=/s/true/false' /etc/shelfmark/.env + sed -i '/_BYPASS=/s/true/false/' /etc/shelfmark/.env else DEPLOYMENT_TYPE="1" msg_info "Installing internal bypasser dependencies" From f96c53fc6a32a603523f627115f607471698fa64 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 17:42:22 -0500 Subject: [PATCH 133/228] more sed fixes --- install/shelfmark-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index bf4766d6f..beb825556 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -97,12 +97,12 @@ if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then rm /etc/apt/sources.list.d/google-chrome.list sed -i -e '/BYPASSER=/s/false/true/' \ -e 's/^# EXT_/EXT_/' \ - -e "s|_URL=.*|http://localhost:8191|" /etc/shelfmark/.env + -e "s|_URL=.*|_URL=http://localhost:8191|" /etc/shelfmark/.env msg_ok "Installed FlareSolverr" elif [[ "$DEPLOYMENT_TYPE" == "3" ]]; then sed -i -e '/BYPASSER=/s/false/true/' \ -e 's/^# EXT_/EXT_/' \ - -e "s|_URL=.*|${FLARESOLVERR_URL}|" /etc/shelfmark/.env + -e "s|_URL=.*|_URL=${FLARESOLVERR_URL}|" /etc/shelfmark/.env elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then sed -i '/_BYPASS=/s/true/false/' /etc/shelfmark/.env else @@ -126,7 +126,7 @@ RELEASE_VERSION=$(cat "$HOME/.shelfmark") msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend -echo "$RELEASE_VERSION" >>/etc/shelfmark/.env +echo "RELEASE_VERION=${RELEASE_VERSION}" >>/etc/shelfmark/.env $STD npm ci $STD npm run build mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist From 797c1f4ede8d47a78059105549256529e66705c6 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 18:22:12 -0500 Subject: [PATCH 134/228] typo --- install/shelfmark-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index beb825556..2ac3a0c16 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -126,7 +126,7 @@ RELEASE_VERSION=$(cat "$HOME/.shelfmark") msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend -echo "RELEASE_VERION=${RELEASE_VERSION}" >>/etc/shelfmark/.env +echo "RELEASE_VERSION=${RELEASE_VERSION}" >>/etc/shelfmark/.env $STD npm ci $STD npm run build mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist From 9dac0b03a318bf32746f88d272750db5bd9594df Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 18:38:14 -0500 Subject: [PATCH 135/228] Shelfmark: use `KillMode=mixed` to ensure speedy shutdown/restart --- install/shelfmark-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index 2ac3a0c16..e327caf44 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -154,6 +154,7 @@ EnvironmentFile=/etc/shelfmark/.env ExecStart=/usr/bin/bash /opt/shelfmark/start.sh Restart=always RestartSec=10 +KillMode=mixed [Install] WantedBy=multi-user.target From 7f68c5818195f071177220f8f6b6016df9b15c82 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 26 Jan 2026 19:22:04 -0500 Subject: [PATCH 136/228] Opencloud: bump version to 5.0.0 --- ct/opencloud.sh | 2 +- install/opencloud-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/opencloud.sh b/ct/opencloud.sh index 6c047fa6d..2f1925421 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -29,7 +29,7 @@ function update_script() { exit fi - RELEASE="v4.1.0" + RELEASE="v5.0.0" if check_for_gh_release "opencloud" "opencloud-eu/opencloud" "${RELEASE}"; then msg_info "Stopping services" systemctl stop opencloud opencloud-wopi diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 9159b42c5..39b579429 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -57,7 +57,7 @@ echo "$COOLPASS" >~/.coolpass msg_ok "Installed Collabora Online" # OpenCloud -fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v4.1.0" "/usr/bin" "opencloud-*-linux-amd64" +fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.0" "/usr/bin" "opencloud-*-linux-amd64" msg_info "Configuring OpenCloud" DATA_DIR="/var/lib/opencloud/" From 2371ae3849efa2f9a2d0974424fa3c2de76d9718 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Tue, 27 Jan 2026 06:42:40 +0100 Subject: [PATCH 137/228] Update frontend/public/json/ebusd.json Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- frontend/public/json/ebusd.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/public/json/ebusd.json b/frontend/public/json/ebusd.json index 0be1da11b..96d94b7a8 100644 --- a/frontend/public/json/ebusd.json +++ b/frontend/public/json/ebusd.json @@ -44,9 +44,5 @@ "text": "Working `/etc/default/ebusd` options for `ebus adapter shield v5`: EBUSD_OPTS='--pidfile=/run/ebusd.pid --latency=100 --scanconfig --configpath=https://ebus.github.io/ --accesslevel=* --pollinterval=30 --device=ens:x.x.x.x:9999 --mqtthost=x.x.x.x --mqttport=1883 --mqttuser=ha-mqtt --mqttpass=xxxxxxxx! --mqttint=/etc/ebusd/mqtt-hassio.cfg --mqttjson --mqttlog --mqttretain --mqtttopic=ebusd --log=all:notice --log=main:notice --log=bus:notice --log=update:notice --log=network:notice --log=other:notice'", "type": "info" }, - { - "text": "Only tested with `ebusd-25.1_amd64-bookworm.deb`!", - "type": "warning" - } ] } From dfeb8a8e1fe84f3cfc3bc6b711c38d3cae4136f6 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Tue, 27 Jan 2026 06:47:34 +0100 Subject: [PATCH 138/228] Update ct/ebusd.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- ct/ebusd.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/ebusd.sh b/ct/ebusd.sh index 2791cb696..63f16f512 100644 --- a/ct/ebusd.sh +++ b/ct/ebusd.sh @@ -27,10 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating ebusd" $STD apt update - $STD apt -y upgrade - msg_ok "Updated $APP LXC" + $STD apt -y --upgrade ebusd + msg_ok "Updated ebusd" msg_ok "Updated successfully!" exit } From 0e87e5f76955a31d795d1857d5ae3e1feee41fe9 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Tue, 27 Jan 2026 06:54:24 +0100 Subject: [PATCH 139/228] Update description format in ebusd.json --- frontend/public/json/ebusd.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/ebusd.json b/frontend/public/json/ebusd.json index 96d94b7a8..b902ea7a2 100644 --- a/frontend/public/json/ebusd.json +++ b/frontend/public/json/ebusd.json @@ -13,7 +13,7 @@ "website": "https://github.com/john30/ebusd", "logo": "", "config_path": "/etc/default/ebusd", - "description": "ebusd is a daemon for handling communication with eBUS devices connected to a 2-wire bus system ("energy bus" used by numerous heating systems).", + "description": "ebusd is a daemon for handling communication with eBUS devices connected to a 2-wire `energy bus` used by numerous heating systems.", "install_methods": [ { "type": "default", From 6f9a1965f92dfa73637c37f60adc24ecfd8b7eb8 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:29:22 +0100 Subject: [PATCH 140/228] Merge from VE --- misc/api.func | 155 ++++++++++++++++++++++++++++-------------------- misc/build.func | 135 ++++++++--------------------------------- misc/core.func | 140 ++++++++++++++++++++++++------------------- 3 files changed, 193 insertions(+), 237 deletions(-) diff --git a/misc/api.func b/misc/api.func index f6f284bec..657d786ba 100644 --- a/misc/api.func +++ b/misc/api.func @@ -1,6 +1,6 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner -# License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE +# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE # ============================================================================== # API.FUNC - TELEMETRY & DIAGNOSTICS API @@ -36,14 +36,16 @@ # # - Maps numeric exit codes to human-readable error descriptions # - Supports: -# * Generic/Shell errors (1, 2, 126, 127, 128, 130, 137, 139, 143) -# * Package manager errors (APT, DPKG: 100, 101, 255) -# * Node.js/npm errors (243-249, 254) -# * Python/pip/uv errors (210-212) -# * PostgreSQL errors (231-234) -# * MySQL/MariaDB errors (241-244) -# * MongoDB errors (251-254) +# * Generic/Shell errors (1, 2, 124, 126-130, 134, 137, 139, 141, 143) +# * curl/wget errors (6, 7, 22, 28, 35) +# * Package manager errors (APT, DPKG: 100-102, 255) +# * Systemd/Service errors (150-154) +# * Python/pip/uv errors (160-162) +# * PostgreSQL errors (170-173) +# * MySQL/MariaDB errors (180-183) +# * MongoDB errors (190-193) # * Proxmox custom codes (200-231) +# * Node.js/npm errors (243, 245-249) # - Returns description string for given exit code # - Shared function with error_handler.func for consistency # ------------------------------------------------------------------------------ @@ -53,73 +55,98 @@ explain_exit_code() { # --- Generic / Shell --- 1) echo "General error / Operation not permitted" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; - 126) echo "Command invoked cannot execute (permission problem?)" ;; - 127) echo "Command not found" ;; - 128) echo "Invalid argument to exit" ;; - 130) echo "Terminated by Ctrl+C (SIGINT)" ;; - 137) echo "Killed (SIGKILL / Out of memory?)" ;; - 139) echo "Segmentation fault (core dumped)" ;; - 143) echo "Terminated (SIGTERM)" ;; + + # --- curl / wget errors (commonly seen in downloads) --- + 6) echo "curl: DNS resolution failed (could not resolve host)" ;; + 7) echo "curl: Failed to connect (network unreachable / host down)" ;; + 22) echo "curl: HTTP error returned (404, 429, 500+)" ;; + 28) echo "curl: Operation timeout (network slow or server not responding)" ;; + 35) echo "curl: SSL/TLS handshake failed (certificate error)" ;; # --- Package manager / APT / DPKG --- 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; - 255) echo "DPKG: Fatal internal error" ;; + 102) echo "APT: Lock held by another process (dpkg/apt still running)" ;; - # --- Node.js / npm / pnpm / yarn --- + # --- Common shell/system errors --- + 124) echo "Command timed out (timeout command)" ;; + 126) echo "Command invoked cannot execute (permission problem?)" ;; + 127) echo "Command not found" ;; + 128) echo "Invalid argument to exit" ;; + 130) echo "Terminated by Ctrl+C (SIGINT)" ;; + 134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;; + 137) echo "Killed (SIGKILL / Out of memory?)" ;; + 139) echo "Segmentation fault (core dumped)" ;; + 141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;; + 143) echo "Terminated (SIGTERM)" ;; + + # --- Systemd / Service errors (150-154) --- + 150) echo "Systemd: Service failed to start" ;; + 151) echo "Systemd: Service unit not found" ;; + 152) echo "Permission denied (EACCES)" ;; + 153) echo "Build/compile failed (make/gcc/cmake)" ;; + 154) echo "Node.js: Native addon build failed (node-gyp)" ;; + + # --- Python / pip / uv (160-162) --- + 160) echo "Python: Virtualenv / uv environment missing or broken" ;; + 161) echo "Python: Dependency resolution failed" ;; + 162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; + + # --- PostgreSQL (170-173) --- + 170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; + 171) echo "PostgreSQL: Authentication failed (bad user/password)" ;; + 172) echo "PostgreSQL: Database does not exist" ;; + 173) echo "PostgreSQL: Fatal error in query / syntax" ;; + + # --- MySQL / MariaDB (180-183) --- + 180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; + 181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; + 182) echo "MySQL/MariaDB: Database does not exist" ;; + 183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; + + # --- MongoDB (190-193) --- + 190) echo "MongoDB: Connection failed (server not running)" ;; + 191) echo "MongoDB: Authentication failed (bad user/password)" ;; + 192) echo "MongoDB: Database not found" ;; + 193) echo "MongoDB: Fatal query error" ;; + + # --- Proxmox Custom Codes (200-231) --- + 200) echo "Proxmox: Failed to create lock file" ;; + 203) echo "Proxmox: Missing CTID variable" ;; + 204) echo "Proxmox: Missing PCT_OSTYPE variable" ;; + 205) echo "Proxmox: Invalid CTID (<100)" ;; + 206) echo "Proxmox: CTID already in use" ;; + 207) echo "Proxmox: Password contains unescaped special characters" ;; + 208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;; + 209) echo "Proxmox: Container creation failed" ;; + 210) echo "Proxmox: Cluster not quorate" ;; + 211) echo "Proxmox: Timeout waiting for template lock" ;; + 212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;; + 213) echo "Proxmox: Storage type does not support 'rootdir' content" ;; + 214) echo "Proxmox: Not enough storage space" ;; + 215) echo "Proxmox: Container created but not listed (ghost state)" ;; + 216) echo "Proxmox: RootFS entry missing in config" ;; + 217) echo "Proxmox: Storage not accessible" ;; + 218) echo "Proxmox: Template file corrupted or incomplete" ;; + 219) echo "Proxmox: CephFS does not support containers - use RBD" ;; + 220) echo "Proxmox: Unable to resolve template path" ;; + 221) echo "Proxmox: Template file not readable" ;; + 222) echo "Proxmox: Template download failed" ;; + 223) echo "Proxmox: Template not available after download" ;; + 224) echo "Proxmox: PBS storage is for backups only" ;; + 225) echo "Proxmox: No template available for OS/Version" ;; + 231) echo "Proxmox: LXC stack upgrade failed" ;; + + # --- Node.js / npm / pnpm / yarn (243-249) --- 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; 245) echo "Node.js: Invalid command-line option" ;; 246) echo "Node.js: Internal JavaScript Parse Error" ;; 247) echo "Node.js: Fatal internal error" ;; 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; - 249) echo "Node.js: Inspector error" ;; - 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; + 249) echo "npm/pnpm/yarn: Unknown fatal error" ;; - # --- Python / pip / uv --- - 210) echo "Python: Virtualenv / uv environment missing or broken" ;; - 211) echo "Python: Dependency resolution failed" ;; - 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; - - # --- PostgreSQL --- - 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; - 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; - 233) echo "PostgreSQL: Database does not exist" ;; - 234) echo "PostgreSQL: Fatal error in query / syntax" ;; - - # --- MySQL / MariaDB --- - 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; - 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; - 243) echo "MySQL/MariaDB: Database does not exist" ;; - 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; - - # --- MongoDB --- - 251) echo "MongoDB: Connection failed (server not running)" ;; - 252) echo "MongoDB: Authentication failed (bad user/password)" ;; - 253) echo "MongoDB: Database not found" ;; - 254) echo "MongoDB: Fatal query error" ;; - - # --- Proxmox Custom Codes --- - 200) echo "Custom: Failed to create lock file" ;; - 203) echo "Custom: Missing CTID variable" ;; - 204) echo "Custom: Missing PCT_OSTYPE variable" ;; - 205) echo "Custom: Invalid CTID (<100)" ;; - 206) echo "Custom: CTID already in use (check 'pct list' and /etc/pve/lxc/)" ;; - 207) echo "Custom: Password contains unescaped special characters (-, /, \\, *, etc.)" ;; - 208) echo "Custom: Invalid configuration (DNS/MAC/Network format error)" ;; - 209) echo "Custom: Container creation failed (check logs for pct create output)" ;; - 210) echo "Custom: Cluster not quorate" ;; - 211) echo "Custom: Timeout waiting for template lock (concurrent download in progress)" ;; - 214) echo "Custom: Not enough storage space" ;; - 215) echo "Custom: Container created but not listed (ghost state - check /etc/pve/lxc/)" ;; - 216) echo "Custom: RootFS entry missing in config (incomplete creation)" ;; - 217) echo "Custom: Storage does not support rootdir (check storage capabilities)" ;; - 218) echo "Custom: Template file corrupted or incomplete download (size <1MB or invalid archive)" ;; - 220) echo "Custom: Unable to resolve template path" ;; - 221) echo "Custom: Template file exists but not readable (check file permissions)" ;; - 222) echo "Custom: Template download failed after 3 attempts (network/storage issue)" ;; - 223) echo "Custom: Template not available after download (storage sync issue)" ;; - 225) echo "Custom: No template available for OS/Version (check 'pveam available')" ;; - 231) echo "Custom: LXC stack upgrade/retry failed (outdated pve-container - check https://github.com/community-scripts/ProxmoxVE/discussions/8126)" ;; + # --- DPKG --- + 255) echo "DPKG: Fatal internal error" ;; # --- Default --- *) echo "Unknown error" ;; diff --git a/misc/build.func b/misc/build.func index b584767de..906d64022 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1,8 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE -# Revision: 1 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/branch/main/LICENSE # ============================================================================== # BUILD.FUNC - LXC CONTAINER BUILD & CONFIGURATION @@ -81,109 +80,6 @@ variables() { fi } -# ----------------------------------------------------------------------------- -# Community-Scripts bootstrap loader -# - Always sources build.func from remote -# - Updates local core files only if build.func changed -# - Local cache: /usr/local/community-scripts/core -# ----------------------------------------------------------------------------- - -# FUNC_DIR="/usr/local/community-scripts/core" -# mkdir -p "$FUNC_DIR" - -# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" -# BUILD_REV="$FUNC_DIR/build.rev" -# DEVMODE="${DEVMODE:-no}" - -# # --- Step 1: fetch build.func content once, compute hash --- -# build_content="$(curl -fsSL "$BUILD_URL")" || { -# echo "❌ Failed to fetch build.func" -# exit 1 -# } - -# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') -# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") - -# # --- Step 2: if build.func changed, offer update for core files --- -# if [ "$newhash" != "$oldhash" ]; then -# echo "⚠️ build.func changed!" - -# while true; do -# read -rp "Refresh local core files? [y/N/diff]: " ans -# case "$ans" in -# [Yy]*) -# echo "$newhash" >"$BUILD_REV" - -# update_func_file() { -# local file="$1" -# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" -# local local_path="$FUNC_DIR/$file" - -# echo "⬇️ Downloading $file ..." -# curl -fsSL "$url" -o "$local_path" || { -# echo "❌ Failed to fetch $file" -# exit 1 -# } -# echo "✔️ Updated $file" -# } - -# update_func_file core.func -# update_func_file error_handler.func -# update_func_file tools.func -# break -# ;; -# [Dd]*) -# for file in core.func error_handler.func tools.func; do -# local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" -# remote_tmp="$(mktemp)" - -# curl -fsSL "$url" -o "$remote_tmp" || continue - -# if [ -f "$local_path" ]; then -# echo "🔍 Diff for $file:" -# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" -# else -# echo "📦 New file $file will be installed" -# fi - -# rm -f "$remote_tmp" -# done -# ;; -# *) -# echo "❌ Skipped updating local core files" -# break -# ;; -# esac -# done -# else -# if [ "$DEVMODE" != "yes" ]; then -# echo "✔️ build.func unchanged → using existing local core files" -# fi -# fi - -# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then -# return 0 2>/dev/null || exit 0 -# fi -# _COMMUNITY_SCRIPTS_LOADER=1 - -# # --- Step 3: always source local versions of the core files --- -# source "$FUNC_DIR/core.func" -# source "$FUNC_DIR/error_handler.func" -# source "$FUNC_DIR/tools.func" - -# # --- Step 4: finally, source build.func directly from memory --- -# # (no tmp file needed) -# source <(printf "%s" "$build_content") - -# ------------------------------------------------------------------------------ -# Load core + error handler functions from community-scripts repo -# -# - Prefer curl if available, fallback to wget -# - Load: core.func, error_handler.func, api.func -# - Initialize error traps after loading -# ------------------------------------------------------------------------------ - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then @@ -191,13 +87,11 @@ if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors - #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors - #echo "(build.func) Loaded core.func via wget" fi # ============================================================================== @@ -266,17 +160,29 @@ maxkeys_check() { # # - Returns current container IP depending on OS type # - Debian/Ubuntu: uses `hostname -I` -# - Alpine: parses eth0 via `ip -4 addr` +# - Alpine: parses eth0 via `ip -4 addr` or `ip -6 addr` +# - Supports IPv6-only environments as fallback # - Returns "Unknown" if OS type cannot be determined # ------------------------------------------------------------------------------ get_current_ip() { + CURRENT_IP="" if [ -f /etc/os-release ]; then # Check for Debian/Ubuntu (uses hostname -I) if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - CURRENT_IP=$(hostname -I | awk '{print $1}') + # Try IPv4 first + CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) + # Fallback to IPv6 if no IPv4 + if [[ -z "$CURRENT_IP" ]]; then + CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1) + fi # Check for Alpine (uses ip command) elif grep -q 'ID=alpine' /etc/os-release; then - CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + # Try IPv4 first + CURRENT_IP=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + # Fallback to IPv6 if no IPv4 + if [[ -z "$CURRENT_IP" ]]; then + CURRENT_IP=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n 1) + fi else CURRENT_IP="Unknown" fi @@ -308,6 +214,7 @@ update_motd_ip() { # # - Installs SSH keys into container root account if SSH is enabled # - Uses pct push or direct input to authorized_keys +# - Supports both SSH_KEYS_FILE (from advanced settings) and SSH_AUTHORIZED_KEY (from user defaults) # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { @@ -316,6 +223,13 @@ install_ssh_keys_into_ct() { # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) : "${SSH_KEYS_FILE:=}" + # If SSH_KEYS_FILE doesn't exist but SSH_AUTHORIZED_KEY is set (from user defaults), + # create a temporary SSH_KEYS_FILE with the key + if [[ -z "$SSH_KEYS_FILE" || ! -s "$SSH_KEYS_FILE" ]] && [[ -n "${SSH_AUTHORIZED_KEY:-}" ]]; then + SSH_KEYS_FILE="$(mktemp)" + printf '%s\n' "$SSH_AUTHORIZED_KEY" >"$SSH_KEYS_FILE" + fi + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then msg_info "Installing selected SSH keys into CT ${CTID}" pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { @@ -1025,7 +939,6 @@ base_settings() { ENABLE_NESTING=${var_nesting:-"1"} ENABLE_KEYCTL=${var_keyctl:-"0"} ENABLE_MKNOD=${var_mknod:-"0"} - MOUNT_FS=${var_mount_fs:-""} PROTECT_CT=${var_protection:-"no"} CT_TIMEZONE=${var_timezone:-"$timezone"} [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones diff --git a/misc/core.func b/misc/core.func index 4506b101b..cf564c8a2 100644 --- a/misc/core.func +++ b/misc/core.func @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG -# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE +# License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # ============================================================================== # CORE FUNCTIONS - LXC CONTAINER UTILITIES @@ -123,6 +123,7 @@ icons() { CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" FUSE="${TAB}🗂️${TAB}${CL}" + GPU="${TAB}🎮${TAB}${CL}" HOURGLASS="${TAB}⏳${TAB}" } @@ -551,11 +552,8 @@ msg_info() { if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then declare -gA MSG_INFO_SHOWN=() fi - # Sanitize message for use as associative array key (remove ANSI codes and special chars) - local sanitized_msg - sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') - [[ -n "${MSG_INFO_SHOWN["$sanitized_msg"]+x}" ]] && return - MSG_INFO_SHOWN["$sanitized_msg"]=1 + [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return + MSG_INFO_SHOWN["$msg"]=1 stop_spinner SPINNER_MSG="$msg" @@ -600,7 +598,6 @@ msg_ok() { stop_spinner clear_line echo -e "$CM ${GN}${msg}${CL}" - # Sanitize message for use as associative array key (remove ANSI codes and special chars) local sanitized_msg sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') unset 'MSG_INFO_SHOWN['"$sanitized_msg"']' 2>/dev/null || true @@ -717,7 +714,7 @@ exit_script() { # ------------------------------------------------------------------------------ get_header() { local app_name=$(echo "${APP,,}" | tr -d ' ') - local app_type=${APP_TYPE:-ct} # Default zu 'ct' falls nicht gesetzt + local app_type=${APP_TYPE:-ct} # Default to 'ct' if not set local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/${app_type}/headers/${app_name}" local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}" @@ -820,71 +817,64 @@ is_verbose_mode() { # ------------------------------------------------------------------------------ # cleanup_lxc() # -# - Comprehensive cleanup of package managers, caches, and logs -# - Supports Alpine (apk), Debian/Ubuntu (apt), Fedora/Rocky/CentOS (dnf/yum), -# openSUSE (zypper), Gentoo (emerge), and language package managers -# - Cleans: Python (pip/uv), Node.js (npm/yarn/pnpm), Go, Rust, Ruby, PHP -# - Truncates log files and vacuums systemd journal -# - Run at end of container creation to minimize disk usage +# - Cleans package manager and language caches (safe for installs AND updates) +# - Supports Alpine (apk), Debian/Ubuntu (apt), Python, Node.js, Go, Rust, Ruby, PHP +# - Uses fallback error handling to prevent cleanup failures from breaking installs # ------------------------------------------------------------------------------ cleanup_lxc() { msg_info "Cleaning up" - # OS-specific package manager cleanup + if is_alpine; then - $STD apk cache clean 2>/dev/null || true + $STD apk cache clean || true rm -rf /var/cache/apk/* - elif command -v apt &>/dev/null; then - # Debian/Ubuntu/Devuan - $STD apt -y autoremove 2>/dev/null || true - $STD apt -y autoclean 2>/dev/null || true - $STD apt -y clean 2>/dev/null || true - elif command -v dnf &>/dev/null; then - # Fedora/Rocky/AlmaLinux/CentOS 8+ - $STD dnf clean all 2>/dev/null || true - $STD dnf autoremove -y 2>/dev/null || true - elif command -v yum &>/dev/null; then - # CentOS 7/older RHEL - $STD yum clean all 2>/dev/null || true - elif command -v zypper &>/dev/null; then - # openSUSE - $STD zypper clean --all 2>/dev/null || true - elif command -v emerge &>/dev/null; then - # Gentoo - $STD emerge --quiet --depclean 2>/dev/null || true - $STD eclean-dist -d 2>/dev/null || true - $STD eclean-pkg -d 2>/dev/null || true + else + $STD apt -y autoremove 2>/dev/null || msg_warn "apt autoremove failed (non-critical)" + $STD apt -y autoclean 2>/dev/null || msg_warn "apt autoclean failed (non-critical)" + $STD apt -y clean 2>/dev/null || msg_warn "apt clean failed (non-critical)" fi - # Clear temp artifacts (keep sockets/FIFOs; ignore errors) find /tmp /var/tmp -type f -name 'tmp*' -delete 2>/dev/null || true find /tmp /var/tmp -type f -name 'tempfile*' -delete 2>/dev/null || true - # Truncate writable log files silently (permission errors ignored) - if command -v truncate >/dev/null 2>&1; then - find /var/log -type f -writable -print0 2>/dev/null | - xargs -0 -n1 truncate -s 0 2>/dev/null || true + # Python + if command -v pip &>/dev/null; then + rm -rf /root/.cache/pip 2>/dev/null || true + fi + if command -v uv &>/dev/null; then + rm -rf /root/.cache/uv 2>/dev/null || true fi - # Node.js npm + # Node.js if command -v npm &>/dev/null; then rm -rf /root/.npm/_cacache /root/.npm/_logs 2>/dev/null || true fi - # Node.js yarn - #if command -v yarn &>/dev/null; then $STD yarn cache clean 2>/dev/null || true; fi - # Node.js pnpm - if command -v pnpm &>/dev/null; then $STD pnpm store prune 2>/dev/null || true; fi - # Go - if command -v go &>/dev/null; then $STD go clean -cache -modcache 2>/dev/null || true; fi - # Rust cargo - if command -v cargo &>/dev/null; then $STD cargo clean 2>/dev/null || true; fi - # Ruby gem - if command -v gem &>/dev/null; then $STD gem cleanup 2>/dev/null || true; fi - # Composer (PHP) - if command -v composer &>/dev/null; then $STD composer clear-cache 2>/dev/null || true; fi - - if command -v journalctl &>/dev/null; then - $STD journalctl --vacuum-time=10m 2>/dev/null || true + if command -v yarn &>/dev/null; then + rm -rf /root/.cache/yarn /root/.yarn/cache 2>/dev/null || true fi + if command -v pnpm &>/dev/null; then + pnpm store prune &>/dev/null || true + fi + + # Go (only build cache, not modules) + if command -v go &>/dev/null; then + $STD go clean -cache 2>/dev/null || true + fi + + # Rust (only registry cache, not build artifacts) + if command -v cargo &>/dev/null; then + rm -rf /root/.cargo/registry/cache /root/.cargo/.package-cache 2>/dev/null || true + fi + + # Ruby + if command -v gem &>/dev/null; then + rm -rf /root/.gem/cache 2>/dev/null || true + fi + + # PHP + if command -v composer &>/dev/null; then + rm -rf /root/.composer/cache 2>/dev/null || true + fi + msg_ok "Cleaned" } @@ -954,14 +944,14 @@ function get_lxc_ip() { get_current_ip() { local ip - # Try direct interface lookup for eth0 FIRST (most reliable for LXC) + # Try direct interface lookup for eth0 FIRST (most reliable for LXC) - IPv4 ip=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1) if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "$ip" return 0 fi - # Fallback: Try hostname -I + # Fallback: Try hostname -I (returns IPv4 first if available) if command -v hostname >/dev/null 2>&1; then ip=$(hostname -I 2>/dev/null | awk '{print $1}') if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then @@ -970,9 +960,9 @@ function get_lxc_ip() { fi fi - # Last resort: Use routing table - local targets=("8.8.8.8" "1.1.1.1" "default") - for target in "${targets[@]}"; do + # Try routing table with IPv4 targets + local ipv4_targets=("8.8.8.8" "1.1.1.1" "default") + for target in "${ipv4_targets[@]}"; do if [[ "$target" == "default" ]]; then ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') else @@ -984,6 +974,32 @@ function get_lxc_ip() { fi done + # IPv6 fallback: Try direct interface lookup for eth0 + ip=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + + # IPv6 fallback: Try hostname -I for IPv6 + if command -v hostname >/dev/null 2>&1; then + ip=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1) + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + fi + + # IPv6 fallback: Use routing table with IPv6 targets + local ipv6_targets=("2001:4860:4860::8888" "2606:4700:4700::1111") + for target in "${ipv6_targets[@]}"; do + ip=$(ip -6 route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + done + return 1 } From 3b9ad58ce38eb9ca308750a00a135576b78daf1f Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 27 Jan 2026 10:08:34 +0100 Subject: [PATCH 141/228] merge from Main --- misc/build.func | 1271 ++++++++++++++++++++++++----------------------- 1 file changed, 644 insertions(+), 627 deletions(-) diff --git a/misc/build.func b/misc/build.func index 906d64022..5ea15757a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/branch/main/LICENSE +# License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # ============================================================================== # BUILD.FUNC - LXC CONTAINER BUILD & CONFIGURATION @@ -246,7 +246,7 @@ install_ssh_keys_into_ct() { return 0 fi - # Fallback: nichts ausgewählt + # Fallback msg_warn "No SSH keys to install (skipping)." return 0 } @@ -298,6 +298,53 @@ get_valid_container_id() { echo "$suggested_id" } +# ------------------------------------------------------------------------------ +# validate_container_id() +# +# - Validates if a container ID is available for use +# - Checks if ID is already used by VM or LXC container +# - Checks if ID is used in LVM logical volumes +# - Returns 0 if ID is available, 1 if already in use +# ------------------------------------------------------------------------------ +validate_container_id() { + local ctid="$1" + + # Check if ID is numeric + if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then + return 1 + fi + + # Check if config file exists for VM or LXC + if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then + return 1 + fi + + # Check if ID is used in LVM logical volumes + if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then + return 1 + fi + + return 0 +} + +# ------------------------------------------------------------------------------ +# get_valid_container_id() +# +# - Returns a valid, unused container ID +# - If provided ID is valid, returns it +# - Otherwise increments from suggested ID until a free one is found +# - Calls validate_container_id() to check availability +# ------------------------------------------------------------------------------ +get_valid_container_id() { + local suggested_id="${1:-$(pvesh get /cluster/nextid)}" + + while ! validate_container_id "$suggested_id"; do + suggested_id=$((suggested_id + 1)) + done + + echo "$suggested_id" +} + # ------------------------------------------------------------------------------ # validate_hostname() # @@ -316,7 +363,7 @@ validate_hostname() { # Split by dots and validate each label local IFS='.' - read -ra labels <<<"$hostname" + read -ra labels <<< "$hostname" for label in "${labels[@]}"; do # Each label: 1-63 chars, alphanumeric, hyphens allowed (not at start/end) if [[ -z "$label" ]] || [[ ${#label} -gt 63 ]]; then @@ -420,7 +467,7 @@ validate_ipv6_address() { # Check that no segment exceeds 4 hex chars local IFS=':' local -a segments - read -ra segments <<<"$addr" + read -ra segments <<< "$addr" for seg in "${segments[@]}"; do if [[ ${#seg} -gt 4 ]]; then return 1 @@ -470,14 +517,14 @@ validate_gateway_in_subnet() { # Convert IPs to integers local IFS='.' - read -r i1 i2 i3 i4 <<<"$ip" - read -r g1 g2 g3 g4 <<<"$gateway" + read -r i1 i2 i3 i4 <<< "$ip" + read -r g1 g2 g3 g4 <<< "$gateway" - local ip_int=$(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) - local gw_int=$(((g1 << 24) + (g2 << 16) + (g3 << 8) + g4)) + local ip_int=$(( (i1 << 24) + (i2 << 16) + (i3 << 8) + i4 )) + local gw_int=$(( (g1 << 24) + (g2 << 16) + (g3 << 8) + g4 )) # Check if both are in same network - if (((ip_int & mask) != (gw_int & mask))); then + if (( (ip_int & mask) != (gw_int & mask) )); then return 1 fi @@ -849,8 +896,6 @@ base_settings() { CORE_COUNT="${final_cpu}" RAM_SIZE="${final_ram}" VERBOSE=${var_verbose:-"${1:-no}"} - - # Password sanitization - clean up dashes and format properly PW="" if [[ -n "${var_pw:-}" ]]; then local _pw_raw="${var_pw}" @@ -1012,113 +1057,113 @@ load_vars_file() { # Validate values before setting (skip empty values - they use defaults) if [[ -n "$var_val" ]]; then case "$var_key" in - var_mac) - if ! validate_mac_address "$var_val"; then - msg_warn "Invalid MAC address '$var_val' in $file, ignoring" - continue - fi - ;; - var_vlan) - if ! validate_vlan_tag "$var_val"; then - msg_warn "Invalid VLAN tag '$var_val' in $file (must be 1-4094), ignoring" - continue - fi - ;; - var_mtu) - if ! validate_mtu "$var_val"; then - msg_warn "Invalid MTU '$var_val' in $file (must be 576-65535), ignoring" - continue - fi - ;; - var_tags) - if ! validate_tags "$var_val"; then - msg_warn "Invalid tags '$var_val' in $file (alphanumeric, -, _, ; only), ignoring" - continue - fi - ;; - var_timezone) - if ! validate_timezone "$var_val"; then - msg_warn "Invalid timezone '$var_val' in $file, ignoring" - continue - fi - ;; - var_brg) - if ! validate_bridge "$var_val"; then - msg_warn "Bridge '$var_val' not found in $file, ignoring" - continue - fi - ;; - var_gateway) - if ! validate_gateway_ip "$var_val"; then - msg_warn "Invalid gateway IP '$var_val' in $file, ignoring" - continue - fi - ;; - var_hostname) - if ! validate_hostname "$var_val"; then - msg_warn "Invalid hostname '$var_val' in $file, ignoring" - continue - fi - ;; - var_cpu) - if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1 || var_val > 128)); then - msg_warn "Invalid CPU count '$var_val' in $file (must be 1-128), ignoring" - continue - fi - ;; - var_ram) - if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 256)); then - msg_warn "Invalid RAM '$var_val' in $file (must be >= 256 MiB), ignoring" - continue - fi - ;; - var_disk) - if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1)); then - msg_warn "Invalid disk size '$var_val' in $file (must be >= 1 GB), ignoring" - continue - fi - ;; - var_unprivileged) - if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then - msg_warn "Invalid unprivileged value '$var_val' in $file (must be 0 or 1), ignoring" - continue - fi - ;; - var_nesting) - if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then - msg_warn "Invalid nesting value '$var_val' in $file (must be 0 or 1), ignoring" - continue - fi - ;; - var_keyctl) - if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then - msg_warn "Invalid keyctl value '$var_val' in $file (must be 0 or 1), ignoring" - continue - fi - ;; - var_net) - # var_net can be: dhcp, static IP/CIDR, or IP range - if [[ "$var_val" != "dhcp" ]]; then - if is_ip_range "$var_val"; then - : # IP range is valid, will be resolved at runtime - elif ! validate_ip_address "$var_val"; then - msg_warn "Invalid network '$var_val' in $file (must be dhcp or IP/CIDR), ignoring" + var_mac) + if ! validate_mac_address "$var_val"; then + msg_warn "Invalid MAC address '$var_val' in $file, ignoring" continue fi - fi - ;; - var_fuse | var_tun | var_gpu | var_ssh | var_verbose | var_protection) - if [[ "$var_val" != "yes" && "$var_val" != "no" ]]; then - msg_warn "Invalid boolean '$var_val' for $var_key in $file (must be yes/no), ignoring" - continue - fi - ;; - var_ipv6_method) - if [[ "$var_val" != "auto" && "$var_val" != "dhcp" && "$var_val" != "static" && "$var_val" != "none" ]]; then - msg_warn "Invalid IPv6 method '$var_val' in $file (must be auto/dhcp/static/none), ignoring" - continue - fi - ;; + ;; + var_vlan) + if ! validate_vlan_tag "$var_val"; then + msg_warn "Invalid VLAN tag '$var_val' in $file (must be 1-4094), ignoring" + continue + fi + ;; + var_mtu) + if ! validate_mtu "$var_val"; then + msg_warn "Invalid MTU '$var_val' in $file (must be 576-65535), ignoring" + continue + fi + ;; + var_tags) + if ! validate_tags "$var_val"; then + msg_warn "Invalid tags '$var_val' in $file (alphanumeric, -, _, ; only), ignoring" + continue + fi + ;; + var_timezone) + if ! validate_timezone "$var_val"; then + msg_warn "Invalid timezone '$var_val' in $file, ignoring" + continue + fi + ;; + var_brg) + if ! validate_bridge "$var_val"; then + msg_warn "Bridge '$var_val' not found in $file, ignoring" + continue + fi + ;; + var_gateway) + if ! validate_gateway_ip "$var_val"; then + msg_warn "Invalid gateway IP '$var_val' in $file, ignoring" + continue + fi + ;; + var_hostname) + if ! validate_hostname "$var_val"; then + msg_warn "Invalid hostname '$var_val' in $file, ignoring" + continue + fi + ;; + var_cpu) + if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1 || var_val > 128)); then + msg_warn "Invalid CPU count '$var_val' in $file (must be 1-128), ignoring" + continue + fi + ;; + var_ram) + if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 256)); then + msg_warn "Invalid RAM '$var_val' in $file (must be >= 256 MiB), ignoring" + continue + fi + ;; + var_disk) + if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1)); then + msg_warn "Invalid disk size '$var_val' in $file (must be >= 1 GB), ignoring" + continue + fi + ;; + var_unprivileged) + if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then + msg_warn "Invalid unprivileged value '$var_val' in $file (must be 0 or 1), ignoring" + continue + fi + ;; + var_nesting) + if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then + msg_warn "Invalid nesting value '$var_val' in $file (must be 0 or 1), ignoring" + continue + fi + ;; + var_keyctl) + if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then + msg_warn "Invalid keyctl value '$var_val' in $file (must be 0 or 1), ignoring" + continue + fi + ;; + var_net) + # var_net can be: dhcp, static IP/CIDR, or IP range + if [[ "$var_val" != "dhcp" ]]; then + if is_ip_range "$var_val"; then + : # IP range is valid, will be resolved at runtime + elif ! validate_ip_address "$var_val"; then + msg_warn "Invalid network '$var_val' in $file (must be dhcp or IP/CIDR), ignoring" + continue + fi + fi + ;; + var_fuse|var_tun|var_gpu|var_ssh|var_verbose|var_protection) + if [[ "$var_val" != "yes" && "$var_val" != "no" ]]; then + msg_warn "Invalid boolean '$var_val' for $var_key in $file (must be yes/no), ignoring" + continue + fi + ;; + var_ipv6_method) + if [[ "$var_val" != "auto" && "$var_val" != "dhcp" && "$var_val" != "static" && "$var_val" != "none" ]]; then + msg_warn "Invalid IPv6 method '$var_val' in $file (must be auto/dhcp/static/none), ignoring" + continue + fi + ;; esac fi @@ -1228,18 +1273,12 @@ var_fuse=no var_tun=no # Advanced Settings (Proxmox-official features) -# var_nesting: Allow nesting (required for Docker/LXC in CT) -var_nesting=1 -# var_keyctl: Allow keyctl() - needed for Docker (systemd-networkd workaround) -var_keyctl=0 -# var_mknod: Allow device node creation (requires kernel 5.3+, experimental) -var_mknod=0 -# var_mount_fs: Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) -var_mount_fs= -# var_protection: Prevent accidental deletion of container -var_protection=no -# var_timezone: Container timezone (e.g. Europe/Berlin, leave empty for host timezone) -var_timezone= +var_nesting=1 # Allow nesting (required for Docker/LXC in CT) +var_keyctl=0 # Allow keyctl() - needed for Docker (systemd-networkd workaround) +var_mknod=0 # Allow device node creation (requires kernel 5.3+, experimental) +var_mount_fs= # Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) +var_protection=no # Prevent accidental deletion of container +var_timezone= # Container timezone (e.g. Europe/Berlin, leave empty for host timezone) var_tags=community-script var_verbose=no @@ -1551,7 +1590,7 @@ maybe_offer_save_app_defaults() { # 1) if no file → offer to create if [[ ! -f "$app_vars_path" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + if whiptail --backtitle "Proxmox VE Helper Scripts" \ --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then mkdir -p "$(dirname "$app_vars_path")" install -m 0644 "$new_tmp" "$app_vars_path" @@ -1576,7 +1615,7 @@ maybe_offer_save_app_defaults() { while true; do local sel - sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + sel="$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "APP DEFAULTS – ${APP}" \ --menu "Differences detected. What do you want to do?" 20 78 10 \ "Update Defaults" "Write new values to ${app_vars_file}" \ @@ -1597,7 +1636,7 @@ maybe_offer_save_app_defaults() { break ;; "View Diff") - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Diff – ${APP}" \ --scrolltext --textbox "$diff_tmp" 25 100 ;; @@ -1622,6 +1661,13 @@ ensure_storage_selection_for_vars_file() { if [[ -n "$tpl" && -n "$ct" ]]; then TEMPLATE_STORAGE="$tpl" CONTAINER_STORAGE="$ct" + + # Validate storage space for loaded container storage + if [[ -n "${DISK_SIZE:-}" ]]; then + validate_storage_space "$ct" "$DISK_SIZE" "yes" + # Continue even if validation fails - user was warned + fi + return 0 fi @@ -1702,7 +1748,7 @@ advanced_settings() { elif [ -f /etc/timezone ]; then _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") fi - # pct doesn't accept Etc/* zones - map to 'host' instead + # Map Etc/* timezones to "host" (pct doesn't accept Etc/* zones) [[ "${_host_timezone:-}" == Etc/* ]] && _host_timezone="host" local _ct_timezone="${var_timezone:-$_host_timezone}" [[ "${_ct_timezone:-}" == Etc/* ]] && _ct_timezone="host" @@ -1799,32 +1845,36 @@ advanced_settings() { elif [[ "$PW1" == *" "* ]]; then whiptail --msgbox "Password cannot contain spaces." 8 58 else - # Clean up leading dashes from password local _pw1_clean="$PW1" while [[ "$_pw1_clean" == -* ]]; do _pw1_clean="${_pw1_clean#-}" done if [[ -z "$_pw1_clean" ]]; then whiptail --msgbox "Password cannot be only '-' characters." 8 58 + continue elif ((${#_pw1_clean} < 5)); then whiptail --msgbox "Password must be at least 5 characters (after removing leading '-')." 8 70 - else - # Verify password - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ - --title "PASSWORD VERIFICATION" \ - --ok-button "Confirm" --cancel-button "Back" \ - --passwordbox "\nVerify Root Password" 10 58 \ - 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - _pw="--password $_pw1_clean" - _pw_display="********" - ((STEP++)) - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi + continue + fi + # Verify password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "PASSWORD VERIFICATION" \ + --ok-button "Confirm" --cancel-button "Back" \ + --passwordbox "\nVerify Root Password" 10 58 \ + 3>&1 1>&2 2>&3); then + local _pw2_clean="$PW2" + while [[ "$_pw2_clean" == -* ]]; do + _pw2_clean="${_pw2_clean#-}" + done + if [[ "$_pw1_clean" == "$_pw2_clean" ]]; then + _pw="--password $_pw1_clean" + _pw_display="********" + ((STEP++)) else - ((STEP--)) + whiptail --msgbox "Passwords do not match. Please try again." 8 58 fi + else + ((STEP--)) fi fi else @@ -1842,22 +1892,25 @@ advanced_settings() { --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ 3>&1 1>&2 2>&3); then local input_id="${result:-$NEXTID}" - # Validate container ID is numeric + + # Validate that ID is numeric if ! [[ "$input_id" =~ ^[0-9]+$ ]]; then - whiptail --msgbox "Container ID must be numeric." 8 58 + whiptail --backtitle "Proxmox VE Helper Scripts" --title "Invalid ID" --msgbox "Container ID must be numeric." 8 58 continue fi - # Validate container ID is available + + # Check if ID is already in use if ! validate_container_id "$input_id"; then - if whiptail --yesno "Container/VM ID $input_id is already in use.\n\nWould you like to use the next available ID: $(get_valid_container_id "$input_id")?" 10 58; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ID Already In Use" \ + --yesno "Container/VM ID $input_id is already in use.\n\nWould you like to use the next available ID ($(get_valid_container_id "$input_id"))?" 10 58; then _ct_id=$(get_valid_container_id "$input_id") - ((STEP++)) + else + continue fi - # else stay on this step else _ct_id="$input_id" - ((STEP++)) fi + ((STEP++)) else ((STEP--)) fi @@ -1870,15 +1923,16 @@ advanced_settings() { if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "HOSTNAME" \ --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \ + --inputbox "\nSet Hostname (or FQDN, e.g. host.example.com)" 10 58 "$_hostname" \ 3>&1 1>&2 2>&3); then local hn_test="${result:-$NSAPP}" hn_test=$(echo "${hn_test,,}" | tr -d ' ') - if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + + if validate_hostname "$hn_test"; then _hostname="$hn_test" ((STEP++)) else - whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58 + whiptail --msgbox "Invalid hostname: '$hn_test'\n\nRules:\n- Only lowercase letters, digits, dots and hyphens\n- Labels separated by dots (max 63 chars each)\n- No leading/trailing hyphens or dots\n- No consecutive dots\n- Total max 253 characters" 14 60 fi else ((STEP--)) @@ -1953,8 +2007,14 @@ advanced_settings() { # ═══════════════════════════════════════════════════════════════════════════ 8) if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then - _bridge="vmbr0" - ((STEP++)) + # Validate default bridge exists + if validate_bridge "vmbr0"; then + _bridge="vmbr0" + ((STEP++)) + else + whiptail --msgbox "Default bridge 'vmbr0' not found!\n\nPlease configure a network bridge in Proxmox first." 10 58 + exit 1 + fi else if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "NETWORK BRIDGE" \ @@ -1962,8 +2022,13 @@ advanced_settings() { --menu "\nSelect network bridge:" 16 58 6 \ "${BRIDGE_MENU_OPTIONS[@]}" \ 3>&1 1>&2 2>&3); then - _bridge="${result:-vmbr0}" - ((STEP++)) + local bridge_test="${result:-vmbr0}" + if validate_bridge "$bridge_test"; then + _bridge="$bridge_test" + ((STEP++)) + else + whiptail --msgbox "Bridge '$bridge_test' is not available or not active." 8 58 + fi else ((STEP--)) fi @@ -1977,9 +2042,10 @@ advanced_settings() { if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "IPv4 CONFIGURATION" \ --ok-button "Next" --cancel-button "Back" \ - --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \ + --menu "\nSelect IPv4 Address Assignment:" 16 65 3 \ "dhcp" "Automatic (DHCP, recommended)" \ "static" "Static (manual entry)" \ + "range" "IP Range Scan (find first free IP)" \ 3>&1 1>&2 2>&3); then if [[ "$result" == "static" ]]; then @@ -1990,7 +2056,7 @@ advanced_settings() { --ok-button "Next" --cancel-button "Back" \ --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ 3>&1 1>&2 2>&3); then - if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + if validate_ip_address "$static_ip"; then # Get gateway local gateway_ip if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ @@ -1998,16 +2064,62 @@ advanced_settings() { --ok-button "Next" --cancel-button "Back" \ --inputbox "\nEnter Gateway IP address" 10 58 "" \ 3>&1 1>&2 2>&3); then - if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - _net="$static_ip" - _gate=",gw=$gateway_ip" - ((STEP++)) + if validate_gateway_ip "$gateway_ip"; then + # Validate gateway is in same subnet + if validate_gateway_in_subnet "$static_ip" "$gateway_ip"; then + _net="$static_ip" + _gate=",gw=$gateway_ip" + ((STEP++)) + else + whiptail --msgbox "Gateway is not in the same subnet as the static IP.\n\nStatic IP: $static_ip\nGateway: $gateway_ip" 10 58 + fi else - whiptail --msgbox "Invalid Gateway IP format." 8 58 + whiptail --msgbox "Invalid Gateway IP format.\n\nEach octet must be 0-255.\nExample: 192.168.1.1" 10 58 fi fi else - whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58 + whiptail --msgbox "Invalid IPv4 CIDR format.\n\nEach octet must be 0-255.\nCIDR must be 1-32.\nExample: 192.168.1.100/24" 12 58 + fi + fi + elif [[ "$result" == "range" ]]; then + # IP Range Scan + local ip_range + if ip_range=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "IP RANGE SCAN" \ + --ok-button "Scan" --cancel-button "Back" \ + --inputbox "\nEnter IP range to scan for free address\n(e.g. 192.168.1.100/24-192.168.1.200/24)" 12 65 "" \ + 3>&1 1>&2 2>&3); then + if is_ip_range "$ip_range"; then + # Exit whiptail screen temporarily to show scan progress + clear + header_info + echo -e "${INFO}${BOLD}${DGN}Scanning IP range for free address...${CL}\n" + if resolve_ip_from_range "$ip_range"; then + # Get gateway + local gateway_ip + if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ + --title "GATEWAY IP" \ + --ok-button "Next" --cancel-button "Back" \ + --inputbox "\nFound free IP: $NET_RESOLVED\n\nEnter Gateway IP address" 12 58 "" \ + 3>&1 1>&2 2>&3); then + if validate_gateway_ip "$gateway_ip"; then + # Validate gateway is in same subnet + if validate_gateway_in_subnet "$NET_RESOLVED" "$gateway_ip"; then + _net="$NET_RESOLVED" + _gate=",gw=$gateway_ip" + ((STEP++)) + else + whiptail --msgbox "Gateway is not in the same subnet as the IP.\n\nIP: $NET_RESOLVED\nGateway: $gateway_ip" 10 58 + fi + else + whiptail --msgbox "Invalid Gateway IP format.\n\nEach octet must be 0-255.\nExample: 192.168.1.1" 10 58 + fi + fi + else + whiptail --msgbox "No free IP found in the specified range.\nAll IPs responded to ping." 10 58 + fi + else + whiptail --msgbox "Invalid IP range format.\n\nExample: 192.168.1.100/24-192.168.1.200/24" 10 58 fi fi else @@ -2043,16 +2155,33 @@ advanced_settings() { --title "STATIC IPv6 ADDRESS" \ --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ 3>&1 1>&2 2>&3); then - if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + if validate_ipv6_address "$ipv6_addr"; then _ipv6_addr="$ipv6_addr" - # Optional gateway - _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "IPv6 GATEWAY" \ - --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ - 3>&1 1>&2 2>&3) || true - ((STEP++)) + # Optional gateway - loop until valid or empty + local ipv6_gw_valid=false + while [[ "$ipv6_gw_valid" == "false" ]]; do + local ipv6_gw + ipv6_gw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "IPv6 GATEWAY" \ + --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ + 3>&1 1>&2 2>&3) || true + # Validate gateway if provided + if [[ -n "$ipv6_gw" ]]; then + if validate_ipv6_address "$ipv6_gw"; then + _ipv6_gate="$ipv6_gw" + ipv6_gw_valid=true + ((STEP++)) + else + whiptail --msgbox "Invalid IPv6 gateway format.\n\nExample: 2001:db8::1" 8 58 + fi + else + _ipv6_gate="" + ipv6_gw_valid=true + ((STEP++)) + fi + done else - whiptail --msgbox "Invalid IPv6 CIDR format." 8 58 + whiptail --msgbox "Invalid IPv6 CIDR format.\n\nExample: 2001:db8::1/64\nCIDR must be 1-128." 10 58 fi fi ;; @@ -2061,11 +2190,7 @@ advanced_settings() { _ipv6_gate="" ((STEP++)) ;; - disable) - _ipv6_addr="" - _ipv6_gate="" - ((STEP++)) - ;; + none) _ipv6_addr="none" _ipv6_gate="" @@ -2089,10 +2214,14 @@ advanced_settings() { if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "MTU SIZE" \ --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \ + --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500, common values: 1500, 9000)" 12 62 "" \ 3>&1 1>&2 2>&3); then - _mtu="$result" - ((STEP++)) + if validate_mtu "$result"; then + _mtu="$result" + ((STEP++)) + else + whiptail --msgbox "Invalid MTU size.\n\nMTU must be between 576 and 65535.\nCommon values: 1500 (default), 9000 (jumbo frames)" 10 58 + fi else ((STEP--)) fi @@ -2137,10 +2266,14 @@ advanced_settings() { if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "MAC ADDRESS" \ --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \ + --inputbox "\nSet MAC Address\n(leave blank for auto-generated, format: XX:XX:XX:XX:XX:XX)" 12 62 "" \ 3>&1 1>&2 2>&3); then - _mac="$result" - ((STEP++)) + if validate_mac_address "$result"; then + _mac="$result" + ((STEP++)) + else + whiptail --msgbox "Invalid MAC address format.\n\nRequired format: XX:XX:XX:XX:XX:XX\nExample: 02:00:00:00:00:01" 10 58 + fi else ((STEP--)) fi @@ -2153,10 +2286,14 @@ advanced_settings() { if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "VLAN TAG" \ --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \ + --inputbox "\nSet VLAN Tag (1-4094)\n(leave blank for no VLAN)" 12 58 "" \ 3>&1 1>&2 2>&3); then - _vlan="$result" - ((STEP++)) + if validate_vlan_tag "$result"; then + _vlan="$result" + ((STEP++)) + else + whiptail --msgbox "Invalid VLAN tag.\n\nVLAN must be a number between 1 and 4094." 8 58 + fi else ((STEP--)) fi @@ -2169,11 +2306,16 @@ advanced_settings() { if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER TAGS" \ --ok-button "Next" --cancel-button "Back" \ - --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \ + --inputbox "\nSet Custom Tags (semicolon-separated)\n(alphanumeric, hyphens, underscores only)" 12 58 "$_tags" \ 3>&1 1>&2 2>&3); then - _tags="${result:-;}" - _tags=$(echo "$_tags" | tr -d '[:space:]') - ((STEP++)) + local tags_test="${result:-}" + tags_test=$(echo "$tags_test" | tr -d '[:space:]') + if validate_tags "$tags_test"; then + _tags="$tags_test" + ((STEP++)) + else + whiptail --msgbox "Invalid tag format.\n\nTags can only contain:\n- Letters (a-z, A-Z)\n- Numbers (0-9)\n- Hyphens (-)\n- Underscores (_)\n- Semicolons (;) as separator" 14 58 + fi else ((STEP--)) fi @@ -2352,8 +2494,14 @@ advanced_settings() { --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ 3>&1 1>&2 2>&3); then - _ct_timezone="$result" - ((STEP++)) + local tz_test="$result" + [[ "${tz_test:-}" == Etc/* ]] && tz_test="host" # pct doesn't accept Etc/* zones + if validate_timezone "$tz_test"; then + _ct_timezone="$tz_test" + ((STEP++)) + else + whiptail --msgbox "Invalid timezone: '$result'\n\nTimezone must exist in /usr/share/zoneinfo/\n\nExamples:\n- Europe/Berlin\n- America/New_York\n- Asia/Tokyo\n- UTC" 14 58 + fi else ((STEP--)) fi @@ -2553,10 +2701,9 @@ Advanced: export UDHCPC_FIX export SSH_KEYS_FILE - # Exit alternate screen buffer BEFORE displaying summary - # so the summary is visible in the main terminal + # Exit alternate screen buffer before showing summary (so output remains visible) tput rmcup 2>/dev/null || true - trap - RETURN # Remove the trap since we already called rmcup + trap - RETURN # Display final summary echo -e "\n${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" @@ -2571,14 +2718,14 @@ Advanced: echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" - echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - [[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" - [[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" - echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}" - [[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" - [[ -n "$CT_TIMEZONE" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" - [[ "$APT_CACHER" == "yes" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" + echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}${ENABLE_FUSE:-no}${CL}" + [[ "${ENABLE_TUN:-no}" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "${ENABLE_NESTING:-1}" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" + [[ "${ENABLE_KEYCTL:-0}" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" + echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}${ENABLE_GPU:-no}${CL}" + [[ "${PROTECT_CT:-no}" == "yes" || "${PROTECT_CT:-no}" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" + [[ -n "${CT_TIMEZONE:-}" ]] && echo -e "${INFO}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" + [[ "$APT_CACHER" == "yes" ]] && echo -e "${INFO}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" } @@ -2603,12 +2750,12 @@ diagnostics_check() { fi if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then cat </usr/local/community-scripts/diagnostics DIAGNOSTICS=yes #This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#https://git.community-scripts.org/community-scripts/ProxmoxVED/discussions/1836 #Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. #You can review the data at https://community-scripts.github.io/ProxmoxVE/data #If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. @@ -2633,7 +2780,7 @@ EOF DIAGNOSTICS=no #This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#https://git.community-scripts.org/community-scripts/ProxmoxVED/discussions/1836 #Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. #You can review the data at https://community-scripts.github.io/ProxmoxVE/data #If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. @@ -2662,7 +2809,7 @@ EOF diagnostics_menu() { if [ "${DIAGNOSTICS:-no}" = "yes" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + if whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "DIAGNOSTIC SETTINGS" \ --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ --yes-button "No" --no-button "Back"; then @@ -2671,7 +2818,7 @@ diagnostics_menu() { whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 fi else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + if whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "DIAGNOSTIC SETTINGS" \ --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ --yes-button "Yes" --no-button "Back"; then @@ -2701,8 +2848,8 @@ echo_default() { echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - if [ "${var_gpu:-no}" == "yes" ]; then - echo -e "🎮${BOLD}${DGN} GPU Passthrough: ${BGN}Enabled${CL}" + if [[ -n "${var_gpu:-}" && "${var_gpu}" == "yes" ]]; then + echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}Enabled${CL}" fi if [ "$VERBOSE" == "yes" ]; then echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" @@ -2743,8 +2890,7 @@ install_script() { else timezone="UTC" fi - # pct doesn't accept Etc/* zones - map to 'host' instead - [[ "${timezone:-}" == Etc/* ]] && timezone="host" + [[ "${timezone:-}" == Etc/* ]] && timezone="host" # pct doesn't accept Etc/* zones # Show APP Header header_info @@ -2808,7 +2954,6 @@ install_script() { 2 | advanced | ADVANCED) header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" - echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" METHOD="advanced" base_settings advanced_settings @@ -2829,8 +2974,8 @@ install_script() { header_info echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" METHOD="appdefaults" + load_vars_file "$(get_app_defaults_path)" "yes" # Force override script defaults base_settings - load_vars_file "$(get_app_defaults_path)" echo_default defaults_target="$(get_app_defaults_path)" break @@ -2971,9 +3116,9 @@ ssh_extract_keys_from_file() { tr -d '\r' <"$f" | awk ' /^[[:space:]]*#/ {next} /^[[:space:]]*$/ {next} - # nackt: typ base64 [comment] + # bare format: type base64 [comment] /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} - # mit Optionen: finde ab erstem Key-Typ + # with options: find from first key-type onward { match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) if (RSTART>0) { print substr($0, RSTART) } @@ -3047,8 +3192,8 @@ ssh_discover_default_files() { configure_ssh_settings() { local step_info="${1:-}" - local backtitle="[dev] Proxmox VE Helper Scripts" - [[ -n "$step_info" ]] && backtitle="[dev] Proxmox VE Helper Scripts [${step_info}]" + local backtitle="Proxmox VE Helper Scripts" + [[ -n "$step_info" ]] && backtitle="Proxmox VE Helper Scripts [${step_info}]" SSH_KEYS_FILE="$(mktemp)" : >"$SSH_KEYS_FILE" @@ -3157,7 +3302,7 @@ start() { update_script cleanup_lxc else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ "Support/Update functions for ${APP} LXC. Choose an option:" \ 12 60 3 \ "1" "YES (Silent Mode)" \ @@ -3215,7 +3360,7 @@ build_container() { esac fi - # IP (immer zwingend, Standard dhcp) + # IP (always required, default dhcp) NET_STRING+=",ip=${NET:-dhcp}" # Gateway @@ -3272,14 +3417,14 @@ build_container() { FEATURES="${FEATURES}fuse=1" fi - # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly - # Key insight: Bash cannot export arrays, so we build the options as a string - + # Build PCT_OPTIONS as string for export TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null - - # Unified install.func automatically detects OS type (debian, alpine, fedora, etc.) - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" @@ -3298,11 +3443,11 @@ build_container() { export CTTYPE="$CT_TYPE" export ENABLE_FUSE="$ENABLE_FUSE" export ENABLE_TUN="$ENABLE_TUN" - export ENABLE_GPU="$ENABLE_GPU" - export IPV6_METHOD="$IPV6_METHOD" export PCT_OSTYPE="$var_os" export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" + export IPV6_METHOD="$IPV6_METHOD" + export ENABLE_GPU="$ENABLE_GPU" # DEV_MODE exports (optional, for debugging) export BUILD_LOG="$BUILD_LOG" @@ -3316,38 +3461,21 @@ build_container() { export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" - # Validate storage space before container creation - if [[ -n "$CONTAINER_STORAGE" ]]; then - msg_info "Validating storage space" - if ! validate_storage_space "$CONTAINER_STORAGE" "$DISK_SIZE" "no"; then - local free_space - free_space=$(pvesm status 2>/dev/null | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') - local free_fmt - free_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$free_space" 2>/dev/null || echo "${free_space}KB") - msg_error "Not enough space on '$CONTAINER_STORAGE'. Required: ${DISK_SIZE}GB, Available: ${free_fmt}" - exit 214 - fi - msg_ok "Storage space validated" - fi - # Build PCT_OPTIONS as multi-line string - PCT_OPTIONS_STRING="" + PCT_OPTIONS_STRING=" -hostname $HN" - # Add features if set - if [ -n "$FEATURES" ]; then - PCT_OPTIONS_STRING=" -features $FEATURES" - fi - - # Add hostname - PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING - -hostname $HN" - - # Add tags if set + # Only add -tags if TAGS is not empty if [ -n "$TAGS" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -tags $TAGS" fi + # Only add -features if FEATURES is not empty + if [ -n "$FEATURES" ]; then + PCT_OPTIONS_STRING=" -features $FEATURES +$PCT_OPTIONS_STRING" + fi + # Add storage if specified if [ -n "$SD" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING @@ -3374,10 +3502,12 @@ build_container() { -protection 1" fi - # Timezone flag (if var_timezone was set) + # Timezone (map Etc/* to "host" as pct doesn't accept them) if [ -n "${CT_TIMEZONE:-}" ]; then + local _pct_timezone="$CT_TIMEZONE" + [[ "$_pct_timezone" == Etc/* ]] && _pct_timezone="host" PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING - -timezone $CT_TIMEZONE" + -timezone $_pct_timezone" fi # Password (already formatted) @@ -3391,10 +3521,20 @@ build_container() { export TEMPLATE_STORAGE="${var_template_storage:-}" export CONTAINER_STORAGE="${var_container_storage:-}" - # # DEBUG: Show final PCT_OPTIONS being exported - # echo "[DEBUG] PCT_OPTIONS to be exported:" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Calling create_lxc_container..." + # Validate storage space only if CONTAINER_STORAGE is already set + # (Storage selection happens in create_lxc_container for some modes) + if [[ -n "$CONTAINER_STORAGE" ]]; then + msg_info "Validating storage space" + if ! validate_storage_space "$CONTAINER_STORAGE" "$DISK_SIZE" "no"; then + local free_space + free_space=$(pvesm status 2>/dev/null | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + local free_fmt + free_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$free_space" 2>/dev/null || echo "${free_space}KB") + msg_error "Not enough space on '$CONTAINER_STORAGE'. Required: ${DISK_SIZE}GB, Available: ${free_fmt}" + exit 214 + fi + msg_ok "Storage space validated" + fi create_lxc_container || exit $? @@ -3453,12 +3593,16 @@ build_container() { msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" # Simple passthrough - just bind /dev/nvidia* devices if they exist - # Skip directories like /dev/nvidia-caps (they need special handling) + # Only include character devices (-c), skip directories like /dev/nvidia-caps for d in /dev/nvidia*; do - [[ -e "$d" ]] || continue - [[ -d "$d" ]] && continue # Skip directories - NVIDIA_DEVICES+=("$d") + [[ -c "$d" ]] && NVIDIA_DEVICES+=("$d") done + # Also check for devices inside /dev/nvidia-caps/ directory + if [[ -d /dev/nvidia-caps ]]; then + for d in /dev/nvidia-caps/*; do + [[ -c "$d" ]] && NVIDIA_DEVICES+=("$d") + done + fi if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then msg_custom "🎮" "${GN}" "Found ${#NVIDIA_DEVICES[@]} NVIDIA device(s) for passthrough" @@ -3691,7 +3835,6 @@ EOF msg_ok "Network in LXC is reachable (ping)" fi fi - # Function to get correct GID inside container get_container_gid() { local group="$1" @@ -3713,176 +3856,41 @@ EOF # install_gpu_userland "NVIDIA" # fi - # Continue with standard container setup - install core dependencies based on OS - sleep 3 - - case "$var_os" in - alpine) + # Continue with standard container setup + if [ "$var_os" == "alpine" ]; then + sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" - ;; + else + sleep 3 + LANG=${LANG:-en_US.UTF-8} + pct exec "$CTID" -- bash -c "sed -i \"/$LANG/ s/^# //\" /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" - debian | ubuntu | devuan) - # First install locales package (required for locale-gen on minimal templates) - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null 2>&1 || true" - - # Locale setup for Debian-based - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen 2>/dev/null || true" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen 2>/dev/null | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ - [[ -n \"\$locale_line\" ]] && echo LANG=\$locale_line >/etc/default/locale && \ - locale-gen >/dev/null 2>&1 && \ - export LANG=\$locale_line || true" - - # Timezone setup if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC") fi + [[ "${tz:-}" == Etc/* ]] && tz="UTC" # Normalize Etc/* to UTC for container setup + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + # Set timezone using symlink (Debian 13+ compatible) + # Create /etc/timezone for backwards compatibility with older scripts pct exec "$CTID" -- bash -c "tz='$tz'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" else msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi - # Core dependencies pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { msg_error "apt-get base packages installation failed" exit 1 } - ;; - - fedora | rockylinux | almalinux | centos) - # RHEL-based: Fedora, Rocky, AlmaLinux, CentOS - # Detect OS major version for EL10+ compatibility (DNF 5, different packages) - local rhel_version - rhel_version=$(pct exec "$CTID" -- bash -c "grep -oP '(?<=VERSION_ID=\")[0-9]+' /etc/os-release 2>/dev/null || echo 9") - - # First run makecache to ensure repos are ready (critical for fresh templates) - msg_info "Initializing package manager (this may take a moment)..." - if ! pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1 || yum makecache 2>&1" >/dev/null 2>&1; then - msg_warn "Package cache update had issues, continuing anyway..." - fi - - # Build package list - EL10+ may not have glibc-langpack-en in same form - local rhel_packages="curl sudo mc jq which tar procps-ng ncurses" - if [[ "$rhel_version" -lt 10 ]]; then - rhel_packages="$rhel_packages glibc-langpack-en" - else - # EL10 uses glibc-all-langpacks or langpacks-en - rhel_packages="$rhel_packages langpacks-en glibc-all-langpacks" - fi - - # Install base packages with better error handling - local install_log="/tmp/dnf_install_${CTID}.log" - if ! pct exec "$CTID" -- bash -c "dnf install -y $rhel_packages 2>&1 | tee $install_log; exit \${PIPESTATUS[0]}" >/dev/null 2>&1; then - # Check if it's just missing optional packages - if pct exec "$CTID" -- bash -c "rpm -q curl sudo mc jq which tar procps-ng" >/dev/null 2>&1; then - msg_warn "Some optional packages may have failed, but core packages installed" - else - # Real failure - try minimal install - msg_warn "Full package install failed, trying minimal set..." - if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo jq which tar 2>&1" >/dev/null 2>&1; then - msg_error "dnf/yum base packages installation failed" - pct exec "$CTID" -- bash -c "cat $install_log 2>/dev/null" || true - exit 1 - fi - fi - fi - - # Set locale for RHEL-based systems - pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - - # Timezone setup for RHEL - if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") - fi - [[ "${tz:-}" == Etc/* ]] && tz="UTC" - if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "timedatectl set-timezone '$tz' 2>/dev/null || ln -sf '/usr/share/zoneinfo/$tz' /etc/localtime" || true - fi - ;; - - opensuse) - # openSUSE - special handling for terminal/locale issues - # Use --gpg-auto-import-keys to avoid interactive prompts that cause hangs - msg_info "Initializing package manager for openSUSE..." - pct exec "$CTID" -- bash -c "zypper --gpg-auto-import-keys --non-interactive refresh 2>&1" >/dev/null 2>&1 || true - - # Install packages - ncurses and terminfo are CRITICAL for terminal to work - if ! pct exec "$CTID" -- bash -c "zypper --gpg-auto-import-keys --non-interactive install -y curl sudo mc jq glibc-locale ncurses terminfo-base 2>&1" >/dev/null 2>&1; then - # Try without glibc-locale - if ! pct exec "$CTID" -- bash -c "zypper --gpg-auto-import-keys --non-interactive install -y curl sudo mc jq ncurses terminfo-base 2>&1" >/dev/null 2>&1; then - msg_error "zypper base packages installation failed" - exit 1 - fi - fi - - # Fix 'unknown terminal type' error - set TERM in multiple places - pct exec "$CTID" -- bash -c "localectl set-locale LANG=en_US.UTF-8 2>/dev/null || echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - - # Set TERM globally for all users - pct exec "$CTID" -- bash -c "cat > /etc/profile.d/term.sh << 'EOFTERM' -# Fix terminal type for LXC containers -if [ -z \"\$TERM\" ] || [ \"\$TERM\" = \"dumb\" ] || [ \"\$TERM\" = \"-\" ]; then - export TERM=xterm-256color -fi -EOFTERM -chmod +x /etc/profile.d/term.sh" || true - - # Also set in /etc/environment for non-login shells - pct exec "$CTID" -- bash -c "grep -q '^TERM=' /etc/environment 2>/dev/null || echo 'TERM=xterm-256color' >> /etc/environment" || true - ;; - - gentoo) - # Gentoo - OpenRC based, emerge is slow - # Use emerge-webrsync (faster, uses http instead of rsync) - msg_info "Syncing Gentoo portage via webrsync (faster than rsync)..." - pct exec "$CTID" -- bash -c "emerge-webrsync 2>&1" >/dev/null 2>&1 || { - msg_warn "emerge-webrsync failed, trying emerge --sync..." - pct exec "$CTID" -- bash -c "emerge --sync 2>&1" >/dev/null 2>&1 || true - } - - # Install curl FIRST - it's required for install.func to work - msg_info "Installing essential packages for Gentoo..." - if ! pct exec "$CTID" -- bash -c "emerge --quiet --noreplace net-misc/curl 2>&1" >/dev/null 2>&1; then - msg_error "Failed to install curl on Gentoo - this is required" - exit 1 - fi - - # Install remaining packages - pct exec "$CTID" -- bash -c "emerge --quiet --noreplace app-misc/jq app-misc/mc sys-libs/ncurses 2>&1" >/dev/null 2>&1 || { - msg_warn "Some Gentoo packages may need manual setup" - } - - # Set TERM for Gentoo - pct exec "$CTID" -- bash -c "echo 'export TERM=xterm-256color' >> /etc/profile.d/term.sh && chmod +x /etc/profile.d/term.sh" || true - ;; - - openeuler) - # openEuler (RHEL-compatible, uses DNF) - # Note: Template was patched with /etc/redhat-release in create_container - msg_info "Initializing package manager for openEuler..." - pct exec "$CTID" -- bash -c "dnf makecache --refresh 2>&1" >/dev/null 2>&1 || true - - # openEuler package names may differ from RHEL - local euler_packages="curl sudo mc jq procps-ng ncurses" - if ! pct exec "$CTID" -- bash -c "dnf install -y $euler_packages 2>&1" >/dev/null 2>&1; then - # Try without procps-ng (might be just 'procps' in openEuler) - if ! pct exec "$CTID" -- bash -c "dnf install -y curl sudo mc jq ncurses 2>&1" >/dev/null 2>&1; then - msg_error "dnf base packages installation failed" - exit 1 - fi - fi - # Set locale - pct exec "$CTID" -- bash -c "echo 'LANG=en_US.UTF-8' > /etc/locale.conf" || true - ;; - - *) - msg_warn "Unknown OS '$var_os' - skipping core dependency installation" - ;; - esac + fi msg_ok "Customized LXC Container" @@ -3890,12 +3898,7 @@ chmod +x /etc/profile.d/term.sh" || true install_ssh_keys_into_ct # Run application installer - # NOTE: We disable error handling here because: - # 1. Container errors are caught by error_handler INSIDE container - # 2. Container creates flag file with exit code - # 3. We read flag file and handle cleanup manually below - # 4. We DON'T want host error_handler to fire for lxc-attach command itself - + # Disable error trap - container errors are handled internally via flag file set +Eeuo pipefail # Disable ALL error handling temporarily trap - ERR # Remove ERR trap completely @@ -3965,35 +3968,129 @@ chmod +x /etc/profile.d/term.sh" || true exit $install_exit_code fi - # Report failure to API before container cleanup - post_update_to_api "failed" "$install_exit_code" - # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) echo "" - echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" + + # Detect error type for smart recovery options + local is_oom=false + local error_explanation="" + if declare -f explain_exit_code >/dev/null 2>&1; then + error_explanation="$(explain_exit_code "$install_exit_code")" + fi + + # OOM detection: exit codes 134 (SIGABRT/heap), 137 (SIGKILL/OOM), 243 (Node.js heap) + if [[ $install_exit_code -eq 134 || $install_exit_code -eq 137 || $install_exit_code -eq 243 ]]; then + is_oom=true + fi + + # Show error explanation if available + if [[ -n "$error_explanation" ]]; then + echo -e "${TAB}${RD}Error: ${error_explanation}${CL}" + echo "" + fi + + # Build recovery menu based on error type + echo -e "${YW}What would you like to do?${CL}" + echo "" + echo -e " ${GN}1)${CL} Remove container and exit" + echo -e " ${GN}2)${CL} Keep container for debugging" + echo -e " ${GN}3)${CL} Retry with verbose mode" + if [[ "$is_oom" == true ]]; then + local new_ram=$((RAM_SIZE * 3 / 2)) + local new_cpu=$((CORE_COUNT + 1)) + echo -e " ${GN}4)${CL} Retry with more resources (RAM: ${RAM_SIZE}→${new_ram} MiB, CPU: ${CORE_COUNT}→${new_cpu} cores)" + fi + echo "" + echo -en "${YW}Select option [1-$([[ "$is_oom" == true ]] && echo "4" || echo "3")] (default: 1, auto-remove in 60s): ${CL}" if read -t 60 -r response; then - if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then - # Remove container - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - elif [[ "$response" =~ ^[Nn]$ ]]; then - echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" - - # Dev mode: Setup MOTD/SSH for debugging access to broken container - if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then - echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" - if pct exec "$CTID" -- bash -c " - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func) - declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true - " >/dev/null 2>&1; then - local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" + case "${response:-1}" in + 1) + # Remove container + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + ;; + 2) + echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" + # Dev mode: Setup MOTD/SSH for debugging access to broken container + if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then + echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" + if pct exec "$CTID" -- bash -c " + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func) + declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true + " >/dev/null 2>&1; then + local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" + fi fi - fi - fi + exit $install_exit_code + ;; + 3) + # Retry with verbose mode + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild...${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + echo "" + # Get new container ID + local old_ctid="$CTID" + export CTID=$(get_valid_container_id "$CTID") + export VERBOSE="yes" + export var_verbose="yes" + + # Show rebuild summary + echo -e "${YW}Rebuilding with preserved settings:${CL}" + echo -e " Container ID: ${old_ctid} → ${CTID}" + echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores | Disk: ${DISK_SIZE} GB" + echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" + echo -e " Verbose: ${GN}enabled${CL}" + echo "" + msg_info "Restarting installation..." + # Re-run build_container + build_container + return $? + ;; + 4) + if [[ "$is_oom" == true ]]; then + # Retry with more resources + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with more resources...${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + echo "" + # Get new container ID and increase resources + local old_ctid="$CTID" + local old_ram="$RAM_SIZE" + local old_cpu="$CORE_COUNT" + export CTID=$(get_valid_container_id "$CTID") + export RAM_SIZE=$((RAM_SIZE * 3 / 2)) + export CORE_COUNT=$((CORE_COUNT + 1)) + export var_ram="$RAM_SIZE" + export var_cpu="$CORE_COUNT" + + # Show rebuild summary + echo -e "${YW}Rebuilding with increased resources:${CL}" + echo -e " Container ID: ${old_ctid} → ${CTID}" + echo -e " RAM: ${old_ram} → ${GN}${RAM_SIZE}${CL} MiB (+50%)" + echo -e " CPU: ${old_cpu} → ${GN}${CORE_COUNT}${CL} cores (+1)" + echo -e " Disk: ${DISK_SIZE} GB | Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" + echo "" + msg_info "Restarting installation..." + # Re-run build_container + build_container + return $? + else + echo -e "\n${TAB}${YW}Invalid option. Container ${CTID} kept.${CL}" + exit $install_exit_code + fi + ;; + *) + echo -e "\n${TAB}${YW}Invalid option. Container ${CTID} kept.${CL}" + exit $install_exit_code + ;; + esac else # Timeout - auto-remove echo -e "\n${YW}No response - auto-removing container${CL}" @@ -4013,12 +4110,12 @@ destroy_lxc() { return 1 fi - # Abbruch bei Ctrl-C / Ctrl-D / ESC + # Abort on Ctrl-C / Ctrl-D / ESC trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT local prompt if ! read -rp "Remove this Container? " prompt; then - # read gibt != 0 zurück bei Ctrl-D/ESC + # read returns non-zero on Ctrl-D/ESC msg_error "Aborted input (Ctrl-D/ESC)" return 130 fi @@ -4044,7 +4141,6 @@ destroy_lxc() { # ------------------------------------------------------------------------------ # Storage discovery / selection helpers # ------------------------------------------------------------------------------ -# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== resolve_storage_preselect() { local class="$1" preselect="$2" required_content="" case "$class" in @@ -4120,15 +4216,14 @@ fix_gpu_gids() { # For privileged containers: also fix permissions inside container if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " + pct exec "$CTID" -- sh -c " if [ -d /dev/dri ]; then for dev in /dev/dri/*; do if [ -e \"\$dev\" ]; then - if [[ \"\$dev\" =~ renderD ]]; then - chgrp ${render_gid} \"\$dev\" 2>/dev/null || true - else - chgrp ${video_gid} \"\$dev\" 2>/dev/null || true - fi + case \"\$dev\" in + *renderD*) chgrp ${render_gid} \"\$dev\" 2>/dev/null || true ;; + *) chgrp ${video_gid} \"\$dev\" 2>/dev/null || true ;; + esac chmod 660 \"\$dev\" 2>/dev/null || true fi done @@ -4203,18 +4298,13 @@ select_storage() { if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" STORAGE_INFO="${MENU[1]}" - - # Validate storage space for auto-picked container storage - if [[ "$CLASS" == "container" && -n "${DISK_SIZE:-}" ]]; then - validate_storage_space "$STORAGE_RESULT" "$DISK_SIZE" "yes" - fi return 0 fi local WIDTH=$((COL_WIDTH + 42)) while true; do local DISPLAY_SELECTED - DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Storage Pools" \ --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } @@ -4235,7 +4325,9 @@ select_storage() { # Validate storage space for container storage if [[ "$CLASS" == "container" && -n "${DISK_SIZE:-}" ]]; then validate_storage_space "$STORAGE_RESULT" "$DISK_SIZE" "yes" + # Continue even if validation fails - user was warned fi + return 0 done } @@ -4298,6 +4390,18 @@ validate_storage_space() { return 0 } +# ============================================================================== +# SECTION 8: CONTAINER CREATION +# ============================================================================== + +# ------------------------------------------------------------------------------ +# create_lxc_container() +# +# - Main function for creating LXC containers +# - Handles all phases: validation, template discovery, container creation, +# network config, storage, etc. +# - Extensive error checking with detailed exit codes +# ------------------------------------------------------------------------------ create_lxc_container() { # ------------------------------------------------------------------------------ # Optional verbose mode (debug tracing) @@ -4469,14 +4573,6 @@ create_lxc_container() { fi msg_ok "Template storage '$TEMPLATE_STORAGE' validated" - # Free space check - STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') - REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) - [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { - msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." - exit 214 - } - # Cluster quorum (if cluster) if [[ -f /etc/pve/corosync.conf ]]; then msg_info "Checking cluster quorum" @@ -4489,81 +4585,52 @@ create_lxc_container() { # ------------------------------------------------------------------------------ # Template discovery & validation - # Supported OS types (pveam available): alpine, almalinux, centos, debian, - # devuan, fedora, gentoo, openeuler, opensuse, rockylinux, ubuntu - # Template naming conventions: - # - Debian/Ubuntu/Devuan: --standard__.tar.zst - # - Alpine/Fedora/Rocky/CentOS/AlmaLinux/openEuler: --default__.tar.xz - # - Gentoo: gentoo-current-openrc__.tar.xz (note: underscore before date!) - # - openSUSE: opensuse--default__.tar.xz - # - CentOS: centos--stream-default__.tar.xz (note: stream in name) # ------------------------------------------------------------------------------ TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" case "$PCT_OSTYPE" in - debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rockylinux | almalinux | openeuler) TEMPLATE_PATTERN="-default_" ;; - centos) TEMPLATE_PATTERN="-stream-default_" ;; - gentoo) TEMPLATE_PATTERN="-openrc_" ;; # Pattern: gentoo-current-openrc_ (underscore!) - opensuse) TEMPLATE_PATTERN="-default_" ;; + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; *) TEMPLATE_PATTERN="" ;; esac msg_info "Searching for template '$TEMPLATE_SEARCH'" - # Build regex patterns outside awk/grep for clarity - SEARCH_PATTERN="^${TEMPLATE_SEARCH}" - - #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" - #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" - #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + # Initialize variables + ONLINE_TEMPLATE="" + ONLINE_TEMPLATES=() + # Step 1: Check local templates first (instant) mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) - # Update template catalog with timeout to prevent hangs on slow networks - if command -v timeout &>/dev/null; then - if ! timeout 30 pveam update >/dev/null 2>&1; then - msg_warn "Template catalog update timed out or failed (continuing with cached data)" - fi - else - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - fi - - msg_ok "Template search completed" - - #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" - #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' - - set +u - mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) - #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" - set -u - - ONLINE_TEMPLATE="" - [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - - #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" - #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" - if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - #msg_debug "First 3 online templates:" - count=0 - for idx in "${!ONLINE_TEMPLATES[@]}"; do - #msg_debug " [$idx]: ${ONLINE_TEMPLATES[$idx]}" - ((count++)) - [[ $count -ge 3 ]] && break - done - fi - #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" - + # Step 2: If local template found, use it immediately (skip pveam update) if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${LOCAL_TEMPLATES[-1]}" TEMPLATE_SOURCE="local" + msg_ok "Template search completed" else + # Step 3: No local template - need to check online (this may be slow) + msg_info "No local template found, checking online catalog..." + + # Update catalog with timeout to prevent long hangs + if command -v timeout &>/dev/null; then + if ! timeout 30 pveam update >/dev/null 2>&1; then + msg_warn "Template catalog update timed out (possible network/DNS issue). Run 'pveam update' manually to diagnose." + fi + else + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)" + fi + + ONLINE_TEMPLATES=() + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE="$ONLINE_TEMPLATE" TEMPLATE_SOURCE="online" + msg_ok "Template search completed" fi # If still no template, try to find alternatives @@ -4572,27 +4639,15 @@ create_lxc_container() { echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." # Get all available versions for this OS type - # Special handling for Gentoo which uses 'current' instead of numeric version - if [[ "$PCT_OSTYPE" == "gentoo" ]]; then - mapfile -t AVAILABLE_VERSIONS < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $2}' | - grep "^gentoo-" | - sed -E 's/gentoo-([^-]+)-.*/\1/' | - sort -u 2>/dev/null || sort -u - ) - else - mapfile -t AVAILABLE_VERSIONS < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $2}' | - grep "^${PCT_OSTYPE}-" | - sed -E "s/${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | - grep -E '^[0-9]' | - sort -u -V 2>/dev/null || sort -u - ) - fi + AVAILABLE_VERSIONS=() + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null + ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then echo "" @@ -4606,22 +4661,19 @@ create_lxc_container() { if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" - SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" - - #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + ONLINE_TEMPLATES=() mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk -F'\t' '{print $1}' | - grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + awk '{print $2}' | + grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true ) if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${ONLINE_TEMPLATES[-1]}" TEMPLATE_SOURCE="online" - #echo "[DEBUG] Found alternative: $TEMPLATE" else msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" exit 225 @@ -4636,9 +4688,6 @@ create_lxc_container() { fi fi - #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) @@ -4679,18 +4728,17 @@ create_lxc_container() { # Retry template search with new version TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + awk -v search="${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk -F'\t' '{print $1}' | - grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + awk '{print $2}' | + grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true ) ONLINE_TEMPLATE="" @@ -4804,7 +4852,6 @@ create_lxc_container() { if [[ "$PCT_OSTYPE" == "debian" ]]; then OSVER="$(parse_template_osver "$TEMPLATE")" if [[ -n "$OSVER" ]]; then - # Proactive, aber ohne Abbruch – nur Angebot offer_lxc_stack_upgrade_and_maybe_retry "no" || true fi fi @@ -4825,7 +4872,7 @@ create_lxc_container() { -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" fi - # Lock by template file (avoid concurrent downloads/creates) + # Lock by template file (avoid concurrent template downloads/validation) lockfile="/tmp/template.${TEMPLATE}.lock" # Cleanup stale lock files (older than 1 hour - likely from crashed processes) @@ -4859,84 +4906,54 @@ create_lxc_container() { LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" - # ------------------------------------------------------------------------------ - # openEuler Template Patch: Create /etc/redhat-release inside template - # PVE's post_create_hook expects this file for RHEL-family OS detection - # Without it, container creation fails with "error in setup task" - # ------------------------------------------------------------------------------ - if [[ "${var_os:-}" == "openeuler" ]]; then - msg_info "Patching openEuler template for PVE compatibility..." - local TEMP_EXTRACT_DIR="/tmp/openeuler_template_patch_$$" - local PATCHED_TEMPLATE="${TEMPLATE_PATH%.tar.xz}_patched.tar.xz" - - # Only patch if not already patched - if [[ ! -f "$PATCHED_TEMPLATE" ]]; then - mkdir -p "$TEMP_EXTRACT_DIR" - - # Extract template - if tar -xf "$TEMPLATE_PATH" -C "$TEMP_EXTRACT_DIR" 2>/dev/null; then - # Create /etc/redhat-release if it doesn't exist - if [[ ! -f "$TEMP_EXTRACT_DIR/etc/redhat-release" ]]; then - echo "openEuler release ${var_version:-25.03}" >"$TEMP_EXTRACT_DIR/etc/redhat-release" - fi - - # Repack template - if tar -cJf "$PATCHED_TEMPLATE" -C "$TEMP_EXTRACT_DIR" . 2>/dev/null; then - # Replace original with patched version - mv "$PATCHED_TEMPLATE" "$TEMPLATE_PATH" - msg_ok "openEuler template patched successfully" - else - msg_warn "Failed to repack template, trying without patch..." - fi - else - msg_warn "Failed to extract template for patching, trying without patch..." - fi - - rm -rf "$TEMP_EXTRACT_DIR" + # Validate template before pct create (while holding lock) + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH" 2>/dev/null || echo 0)" -lt 1000000 ]]; then + msg_info "Template file missing or too small – downloading" + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 + msg_ok "Template downloaded" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_info "Template appears corrupted – re-downloading" + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 + msg_ok "Template re-downloaded" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." fi fi - # # DEBUG: Show the actual command that will be executed - # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS =====" - # echo "[DEBUG] CTID: $CTID" - # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" - # echo "[DEBUG] PCT_OPTIONS (will be word-split):" - # echo "$PCT_OPTIONS" | sed 's/^/ /' - # echo "[DEBUG] Full command line:" - # echo " pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" - # echo "[DEBUG] ========================================" + # Release lock after template validation - pct create has its own internal locking + exec 9>&- msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" msg_debug "Logfile: $LOGFILE" # First attempt (PCT_OPTIONS is a multi-line string, use it directly) if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then - msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating template..." + msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Checking error..." - # Validate template file - if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - msg_warn "Template file too small or missing – re-downloading." + # Check if template issue - retry with fresh download + if grep -qiE 'unable to open|corrupt|invalid' "$LOGFILE"; then + msg_info "Template may be corrupted – re-downloading" rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template appears corrupted – re-downloading." - rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - else - msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." - fi + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 + msg_ok "Template re-downloaded" fi # Retry after repair if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then # Fallback to local storage if not already on local if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_info "Retrying container creation with fallback to local storage..." + msg_info "Retrying container creation with fallback to local storage" LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then - msg_info "Downloading template to local..." + msg_ok "Trying local storage fallback" + msg_info "Downloading template to local" pveam download local "$TEMPLATE" >/dev/null 2>&1 + msg_ok "Template downloaded to local" + else + msg_ok "Trying local storage fallback" fi if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then # Local fallback also failed - check for LXC stack version issue @@ -5060,15 +5077,15 @@ description() { - GitHub + Git - Discussions + Discussions - Issues + Issues EOF From 310d0e54a6a491bb03b4867404f35ebeab65eb23 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:26:31 +0100 Subject: [PATCH 142/228] Interactive Prompts --- .../core.func/CORE_FUNCTIONS_REFERENCE.md | 222 +++++++- install/forgejo-runner-install.sh | 27 +- install/garmin-grafana-install.sh | 9 +- misc/build.func | 69 +-- misc/core.func | 519 +++++++++++++++++- 5 files changed, 773 insertions(+), 73 deletions(-) diff --git a/docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md b/docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md index c89942083..8adf62a6f 100644 --- a/docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md +++ b/docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md @@ -634,4 +634,224 @@ silent() - `SILENT_LOGFILE`: Silent execution log file path - `SPINNER_PID`: Spinner process ID - `SPINNER_MSG`: Spinner message text -- `MSG_INFO_SHOWN`: Tracks shown info messages +- `MSG_INFO_SHOWN`: Tracks shown info messages- `PHS_SILENT`: Unattended mode flag (1 = silent) +- `var_unattended`: Unattended mode variable (yes/no) +- `UNATTENDED`: Alternative unattended mode variable + +## Unattended/Interactive Prompt Functions + +These functions provide a unified way to handle user prompts in both interactive and unattended modes. They automatically detect the execution context and either prompt the user (with timeout) or use default values silently. + +### `is_unattended()` +**Purpose**: Detect if script is running in unattended/silent mode +**Parameters**: None +**Returns**: +- `0` (true): Running unattended +- `1` (false): Running interactively +**Side Effects**: None +**Dependencies**: None +**Environment Variables Used**: `PHS_SILENT`, `var_unattended`, `UNATTENDED` + +**Usage Example**: +```bash +if is_unattended; then + echo "Running in unattended mode" +else + echo "Running interactively" +fi +``` + +### `prompt_confirm()` +**Purpose**: Prompt user for yes/no confirmation with timeout and unattended support +**Parameters**: +- `$1` - Prompt message (required) +- `$2` - Default value: "y" or "n" (optional, default: "n") +- `$3` - Timeout in seconds (optional, default: 60) +**Returns**: +- `0`: User confirmed (yes) +- `1`: User declined (no) or timeout with default "n" +**Side Effects**: Displays prompt to terminal +**Dependencies**: `is_unattended()` +**Environment Variables Used**: Color variables (`YW`, `CL`) + +**Behavior**: +- **Unattended mode**: Immediately returns default value +- **Non-TTY**: Immediately returns default value +- **Interactive**: Displays prompt with timeout countdown +- **Timeout**: Auto-applies default value after specified seconds + +**Usage Examples**: +```bash +# Basic confirmation (default: no) +if prompt_confirm "Proceed with installation?"; then + install_package +fi + +# Default to yes, 30 second timeout +if prompt_confirm "Continue?" "y" 30; then + continue_operation +fi + +# In unattended mode (will use default immediately) +var_unattended=yes +prompt_confirm "Delete files?" "n" && echo "Deleting" || echo "Skipped" +``` + +### `prompt_input()` +**Purpose**: Prompt user for text input with timeout and unattended support +**Parameters**: +- `$1` - Prompt message (required) +- `$2` - Default value (optional, default: "") +- `$3` - Timeout in seconds (optional, default: 60) +**Output**: Prints the user input or default value to stdout +**Returns**: `0` always +**Side Effects**: Displays prompt to stderr (keeps stdout clean for value) +**Dependencies**: `is_unattended()` +**Environment Variables Used**: Color variables (`YW`, `CL`) + +**Behavior**: +- **Unattended mode**: Returns default value immediately +- **Non-TTY**: Returns default value immediately +- **Interactive**: Waits for user input with timeout +- **Empty input**: Returns default value +- **Timeout**: Returns default value + +**Usage Examples**: +```bash +# Get username with default +username=$(prompt_input "Enter username:" "admin" 30) +echo "Using username: $username" + +# With validation loop +while true; do + port=$(prompt_input "Enter port:" "8080" 30) + [[ "$port" =~ ^[0-9]+$ ]] && break + echo "Invalid port number" +done + +# Capture value in unattended mode +var_unattended=yes +db_name=$(prompt_input "Database name:" "myapp_db") +``` + +### `prompt_select()` +**Purpose**: Prompt user to select from a list of options with timeout support +**Parameters**: +- `$1` - Prompt message (required) +- `$2` - Default option number, 1-based (optional, default: 1) +- `$3` - Timeout in seconds (optional, default: 60) +- `$4+` - Options to display (required, at least 1) +**Output**: Prints the selected option value to stdout +**Returns**: +- `0`: Success +- `1`: No options provided +**Side Effects**: Displays numbered menu to stderr +**Dependencies**: `is_unattended()` +**Environment Variables Used**: Color variables (`YW`, `GN`, `CL`) + +**Behavior**: +- **Unattended mode**: Returns default selection immediately +- **Non-TTY**: Returns default selection immediately +- **Interactive**: Displays numbered menu with timeout +- **Invalid selection**: Uses default +- **Timeout**: Auto-selects default + +**Usage Examples**: +```bash +# Simple selection +choice=$(prompt_select "Select database:" 1 30 "PostgreSQL" "MySQL" "SQLite") +echo "Selected: $choice" + +# Using array +options=("Production" "Staging" "Development") +env=$(prompt_select "Select environment:" 2 60 "${options[@]}") + +# In automation scripts +var_unattended=yes +db=$(prompt_select "Database:" 1 30 "postgres" "mysql" "sqlite") +# Returns "postgres" immediately without menu +``` + +### `prompt_password()` +**Purpose**: Prompt user for password input with hidden characters and auto-generation +**Parameters**: +- `$1` - Prompt message (required) +- `$2` - Default value or "generate" for auto-generation (optional) +- `$3` - Timeout in seconds (optional, default: 60) +- `$4` - Minimum length for validation (optional, default: 0) +**Output**: Prints the password to stdout +**Returns**: `0` always +**Side Effects**: Displays prompt to stderr with hidden input +**Dependencies**: `is_unattended()`, `openssl` (for generation) +**Environment Variables Used**: Color variables (`YW`, `CL`) + +**Behavior**: +- **"generate" default**: Creates random 16-character password +- **Unattended mode**: Returns default/generated password immediately +- **Non-TTY**: Returns default/generated password immediately +- **Interactive**: Hidden input with timeout +- **Min length validation**: Falls back to default if too short +- **Timeout**: Returns default/generated password + +**Usage Examples**: +```bash +# Auto-generate password if user doesn't provide one +password=$(prompt_password "Enter password:" "generate" 30) +echo "Password has been set" + +# Require minimum length +db_pass=$(prompt_password "Database password:" "" 60 12) + +# With default password +admin_pass=$(prompt_password "Admin password:" "changeme123" 30) + +# In unattended mode with auto-generation +var_unattended=yes +password=$(prompt_password "Password:" "generate") +# Returns randomly generated password +``` + +## Prompt Function Decision Flow + +``` +prompt_confirm() / prompt_input() / prompt_select() / prompt_password() +│ +├── is_unattended()? ─────────────────────┐ +│ └── PHS_SILENT=1? │ +│ └── var_unattended=yes? ├── YES → Return default immediately +│ └── UNATTENDED=yes? │ +│ │ +├── TTY available? ─────────────── NO ────┘ +│ +└── Interactive Mode + ├── Display prompt with timeout hint + ├── read -t $timeout + │ ├── User input received → Validate and return + │ ├── Empty input → Return default + │ └── Timeout → Return default with message + └── Return value +``` + +## Migration Guide: Converting read Commands + +To make existing scripts unattended-compatible, replace `read` commands with the appropriate prompt function: + +### Before (blocking): +```bash +read -rp "Continue? [y/N]: " confirm +[[ "$confirm" =~ ^[Yy]$ ]] && do_something + +read -p "Enter port: " port +port="${port:-8080}" + +read -p "Select (1-3): " choice +``` + +### After (unattended-safe): +```bash +prompt_confirm "Continue?" "n" && do_something + +port=$(prompt_input "Enter port:" "8080") + +choice=$(prompt_select "Select option:" 1 60 "Option 1" "Option 2" "Option 3") +``` diff --git a/install/forgejo-runner-install.sh b/install/forgejo-runner-install.sh index 367b2b76e..78f0a6e5f 100644 --- a/install/forgejo-runner-install.sh +++ b/install/forgejo-runner-install.sh @@ -12,19 +12,19 @@ setting_up_container network_check update_os -if [[ -z "$var_forgejo_instance" ]]; then - read -rp "Forgejo Instance URL (e.g. https://code.forgejo.org): " var_forgejo_instance -fi +# Get required configuration with sensible fallbacks for unattended mode +# These will show a warning if defaults are used +var_forgejo_instance=$(prompt_input_required \ + "Forgejo Instance URL:" \ + "${var_forgejo_instance:-https://codeberg.org}" \ + 120 \ + "var_forgejo_instance") -if [[ -z "$var_forgejo_runner_token" ]]; then - read -rp "Forgejo Runner Registration Token: " var_forgejo_runner_token - echo -fi - -if [[ -z "$var_forgejo_instance" || -z "$var_forgejo_runner_token" ]]; then - echo "❌ Forgejo instance URL and runner token are required." - exit 1 -fi +var_forgejo_runner_token=$(prompt_input_required \ + "Forgejo Runner Registration Token:" \ + "${var_forgejo_runner_token:-REPLACE_WITH_YOUR_TOKEN}" \ + 120 \ + "var_forgejo_runner_token") export FORGEJO_INSTANCE="$var_forgejo_instance" export FORGEJO_RUNNER_TOKEN="$var_forgejo_runner_token" @@ -78,6 +78,9 @@ EOF systemctl enable -q --now forgejo-runner msg_ok "Created Services" +# Show warning if any required values used fallbacks +show_missing_values_warning + motd_ssh customize cleanup_lxc diff --git a/install/garmin-grafana-install.sh b/install/garmin-grafana-install.sh index 46853fae5..12dc4f8d7 100644 --- a/install/garmin-grafana-install.sh +++ b/install/garmin-grafana-install.sh @@ -110,8 +110,7 @@ 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 +if prompt_confirm "Are you using Garmin in mainland China?" "n" 60; then GARMIN_CN="True" else GARMIN_CN="False" @@ -131,9 +130,9 @@ 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 + GARMIN_EMAIL=$(prompt_input "Please enter your Garmin Connect Email:" "" 120) + GARMIN_PASSWORD=$(prompt_password "Please enter your Garmin Connect Password (used to generate token, NOT stored):" "" 120) + GARMIN_MFA=$(prompt_input "Please enter your MFA Code (leave blank if not applicable):" "" 60) # 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 < " prompt; then - # read returns non-zero on Ctrl-D/ESC - msg_error "Aborted input (Ctrl-D/ESC)" - return 130 - fi - - case "${prompt,,}" in - y | yes) + if prompt_confirm "Remove this Container?" "n" 60; then if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then msg_ok "Removed Container $CT_ID" else msg_error "Failed to remove Container $CT_ID" return 1 fi - ;; - "" | n | no) + else msg_custom "ℹ️" "${BL}" "Container was not removed." - ;; - *) - msg_warn "Invalid response. Container was not removed." - ;; - esac + fi } # ------------------------------------------------------------------------------ @@ -4452,9 +4435,7 @@ create_lxc_container() { echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" echo - read -rp "Do you want to upgrade now? [y/N] " _ans - case "${_ans,,}" in - y | yes) + if prompt_confirm "Do you want to upgrade now?" "n" 60; then msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" if $STD apt-get update && $STD apt-get install -y --only-upgrade pve-container lxc-pve; then msg_ok "LXC stack upgraded." @@ -4473,9 +4454,9 @@ create_lxc_container() { msg_error "Upgrade failed. Please check APT output." return 3 fi - ;; - *) return 2 ;; - esac + else + return 2 + fi } # ------------------------------------------------------------------------------ @@ -4650,16 +4631,12 @@ create_lxc_container() { ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then - echo "" - echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" - for i in "${!AVAILABLE_VERSIONS[@]}"; do - echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" - done - echo "" - read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice + # Use prompt_select for version selection (supports unattended mode) + local selected_version + selected_version=$(prompt_select "Select ${PCT_OSTYPE} version:" 1 60 "${AVAILABLE_VERSIONS[@]}") - if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then - PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" + if [[ -n "$selected_version" ]]; then + PCT_OSVERSION="$selected_version" TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" ONLINE_TEMPLATES=() @@ -4713,16 +4690,12 @@ create_lxc_container() { ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then - echo -e "\n${BL}Available versions:${CL}" - for i in "${!AVAILABLE_VERSIONS[@]}"; do - echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" - done + # Use prompt_select for version selection (supports unattended mode) + local selected_version + selected_version=$(prompt_select "Select ${PCT_OSTYPE} version:" 1 60 "${AVAILABLE_VERSIONS[@]}") - echo "" - read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice - - if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then - export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}" + if [[ -n "$selected_version" ]]; then + export var_version="$selected_version" export PCT_OSVERSION="$var_version" msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" @@ -4767,10 +4740,6 @@ create_lxc_container() { msg_error "Template still not found after version change" exit 220 } - else - msg_custom "🚫" "${YW}" "Installation cancelled" - exit 1 - fi else msg_error "No ${PCT_OSTYPE} templates available" exit 220 diff --git a/misc/core.func b/misc/core.func index cf564c8a2..10e40a501 100644 --- a/misc/core.func +++ b/misc/core.func @@ -810,6 +810,517 @@ is_verbose_mode() { [[ "$verbose" != "no" || ! -t 2 ]] } +# ------------------------------------------------------------------------------ +# is_unattended() +# +# - Detects if script is running in unattended/silent mode +# - Checks PHS_SILENT, var_unattended, and UNATTENDED variables +# - Returns 0 (true) if unattended, 1 (false) otherwise +# - Used by prompt functions to auto-apply defaults +# ------------------------------------------------------------------------------ +is_unattended() { + [[ "${PHS_SILENT:-0}" == "1" ]] && return 0 + [[ "${var_unattended:-}" =~ ^(yes|true|1)$ ]] && return 0 + [[ "${UNATTENDED:-}" =~ ^(yes|true|1)$ ]] && return 0 + return 1 +} + +# ------------------------------------------------------------------------------ +# show_missing_values_warning() +# +# - Displays a summary of required values that used fallback defaults +# - Should be called at the end of install scripts +# - Only shows warning if MISSING_REQUIRED_VALUES array has entries +# - Provides clear guidance on what needs manual configuration +# +# Global: +# MISSING_REQUIRED_VALUES - Array of variable names that need configuration +# +# Example: +# # At end of install script: +# show_missing_values_warning +# ------------------------------------------------------------------------------ +show_missing_values_warning() { + if [[ ${#MISSING_REQUIRED_VALUES[@]} -gt 0 ]]; then + echo "" + echo -e "${YW}╔════════════════════════════════════════════════════════════╗${CL}" + echo -e "${YW}║ ⚠️ MANUAL CONFIGURATION REQUIRED ║${CL}" + echo -e "${YW}╠════════════════════════════════════════════════════════════╣${CL}" + echo -e "${YW}║ The following values were not provided and need to be ║${CL}" + echo -e "${YW}║ configured manually for the service to work properly: ║${CL}" + echo -e "${YW}╟────────────────────────────────────────────────────────────╢${CL}" + for val in "${MISSING_REQUIRED_VALUES[@]}"; do + printf "${YW}║${CL} • %-56s ${YW}║${CL}\n" "$val" + done + echo -e "${YW}╟────────────────────────────────────────────────────────────╢${CL}" + echo -e "${YW}║ Check the service configuration files or environment ║${CL}" + echo -e "${YW}║ variables and update the placeholder values. ║${CL}" + echo -e "${YW}╚════════════════════════════════════════════════════════════╝${CL}" + echo "" + return 1 + fi + return 0 +} + +# ------------------------------------------------------------------------------ +# prompt_confirm() +# +# - Prompts user for yes/no confirmation with timeout and unattended support +# - In unattended mode: immediately returns default value +# - In interactive mode: waits for user input with configurable timeout +# - After timeout: auto-applies default value +# +# Arguments: +# $1 - Prompt message (required) +# $2 - Default value: "y" or "n" (optional, default: "n") +# $3 - Timeout in seconds (optional, default: 60) +# +# Returns: +# 0 - User confirmed (yes) +# 1 - User declined (no) or timeout with default "n" +# +# Example: +# if prompt_confirm "Proceed with installation?" "y" 30; then +# echo "Installing..." +# fi +# +# # Unattended: prompt_confirm will use default without waiting +# var_unattended=yes +# prompt_confirm "Delete files?" "n" && echo "Deleting" || echo "Skipped" +# ------------------------------------------------------------------------------ +prompt_confirm() { + local message="${1:-Confirm?}" + local default="${2:-n}" + local timeout="${3:-60}" + local response + + # Normalize default to lowercase + default="${default,,}" + [[ "$default" != "y" ]] && default="n" + + # Build prompt hint + local hint + if [[ "$default" == "y" ]]; then + hint="[Y/n]" + else + hint="[y/N]" + fi + + # Unattended mode: apply default immediately + if is_unattended; then + if [[ "$default" == "y" ]]; then + return 0 + else + return 1 + fi + fi + + # Check if running in a TTY + if [[ ! -t 0 ]]; then + # Not a TTY, use default + if [[ "$default" == "y" ]]; then + return 0 + else + return 1 + fi + fi + + # Interactive prompt with timeout + echo -en "${YW}${message} ${hint} (auto-${default} in ${timeout}s): ${CL}" + + if read -t "$timeout" -r response; then + # User provided input + response="${response,,}" # lowercase + case "$response" in + y|yes) + return 0 + ;; + n|no) + return 1 + ;; + "") + # Empty response, use default + if [[ "$default" == "y" ]]; then + return 0 + else + return 1 + fi + ;; + *) + # Invalid input, use default + echo -e "${YW}Invalid response, using default: ${default}${CL}" + if [[ "$default" == "y" ]]; then + return 0 + else + return 1 + fi + ;; + esac + else + # Timeout occurred + echo "" # Newline after timeout + echo -e "${YW}Timeout - auto-selecting: ${default}${CL}" + if [[ "$default" == "y" ]]; then + return 0 + else + return 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# prompt_input() +# +# - Prompts user for text input with timeout and unattended support +# - In unattended mode: immediately returns default value +# - In interactive mode: waits for user input with configurable timeout +# - After timeout: auto-applies default value +# +# Arguments: +# $1 - Prompt message (required) +# $2 - Default value (optional, default: "") +# $3 - Timeout in seconds (optional, default: 60) +# +# Output: +# Prints the user input or default value to stdout +# +# Example: +# username=$(prompt_input "Enter username:" "admin" 30) +# echo "Using username: $username" +# +# # With validation +# while true; do +# port=$(prompt_input "Enter port:" "8080" 30) +# [[ "$port" =~ ^[0-9]+$ ]] && break +# echo "Invalid port number" +# done +# ------------------------------------------------------------------------------ +prompt_input() { + local message="${1:-Enter value:}" + local default="${2:-}" + local timeout="${3:-60}" + local response + + # Build display default hint + local hint="" + [[ -n "$default" ]] && hint=" (default: ${default})" + + # Unattended mode: return default immediately + if is_unattended; then + echo "$default" + return 0 + fi + + # Check if running in a TTY + if [[ ! -t 0 ]]; then + # Not a TTY, use default + echo "$default" + return 0 + fi + + # Interactive prompt with timeout + echo -en "${YW}${message}${hint} (auto-default in ${timeout}s): ${CL}" >&2 + + if read -t "$timeout" -r response; then + # User provided input (or pressed Enter for empty) + if [[ -n "$response" ]]; then + echo "$response" + else + echo "$default" + fi + else + # Timeout occurred + echo "" >&2 # Newline after timeout + echo -e "${YW}Timeout - using default: ${default}${CL}" >&2 + echo "$default" + fi +} + +# ------------------------------------------------------------------------------ +# prompt_input_required() +# +# - Prompts user for REQUIRED text input with fallback support +# - In unattended mode: Uses fallback value if no env var set (with warning) +# - In interactive mode: loops until user provides non-empty input +# - Tracks missing required values for end-of-script summary +# +# Arguments: +# $1 - Prompt message (required) +# $2 - Fallback/example value for unattended mode (optional) +# $3 - Timeout in seconds (optional, default: 120) +# $4 - Environment variable name hint for error messages (optional) +# +# Output: +# Prints the user input or fallback value to stdout +# +# Returns: +# 0 - Success (value provided or fallback used) +# 1 - Failed (interactive timeout without input) +# +# Global: +# MISSING_REQUIRED_VALUES - Array tracking fields that used fallbacks +# +# Example: +# # With fallback - script continues even in unattended mode +# token=$(prompt_input_required "Enter API Token:" "YOUR_TOKEN_HERE" 60 "var_api_token") +# +# # Check at end of script if any values need manual configuration +# if [[ ${#MISSING_REQUIRED_VALUES[@]} -gt 0 ]]; then +# msg_warn "Please configure: ${MISSING_REQUIRED_VALUES[*]}" +# fi +# ------------------------------------------------------------------------------ +# Global array to track missing required values +declare -g -a MISSING_REQUIRED_VALUES=() + +prompt_input_required() { + local message="${1:-Enter required value:}" + local fallback="${2:-CHANGE_ME}" + local timeout="${3:-120}" + local env_var_hint="${4:-}" + local response="" + + # Check if value is already set via environment variable (if hint provided) + if [[ -n "$env_var_hint" ]]; then + local env_value="${!env_var_hint:-}" + if [[ -n "$env_value" ]]; then + echo "$env_value" + return 0 + fi + fi + + # Unattended mode: use fallback with warning + if is_unattended; then + if [[ -n "$env_var_hint" ]]; then + echo -e "${YW}⚠ Required value '${env_var_hint}' not set - using fallback: ${fallback}${CL}" >&2 + MISSING_REQUIRED_VALUES+=("$env_var_hint") + else + echo -e "${YW}⚠ Required value not provided - using fallback: ${fallback}${CL}" >&2 + MISSING_REQUIRED_VALUES+=("(unnamed)") + fi + echo "$fallback" + return 0 + fi + + # Check if running in a TTY + if [[ ! -t 0 ]]; then + echo -e "${YW}⚠ Not interactive - using fallback: ${fallback}${CL}" >&2 + MISSING_REQUIRED_VALUES+=("${env_var_hint:-unnamed}") + echo "$fallback" + return 0 + fi + + # Interactive prompt - loop until non-empty input or use fallback on timeout + local attempts=0 + while [[ -z "$response" ]]; do + ((attempts++)) + + if [[ $attempts -gt 3 ]]; then + echo -e "${YW}Too many empty inputs - using fallback: ${fallback}${CL}" >&2 + MISSING_REQUIRED_VALUES+=("${env_var_hint:-manual_input}") + echo "$fallback" + return 0 + fi + + echo -en "${YW}${message} (required, timeout ${timeout}s): ${CL}" >&2 + + if read -t "$timeout" -r response; then + if [[ -z "$response" ]]; then + echo -e "${YW}This field is required. Please enter a value. (attempt ${attempts}/3)${CL}" >&2 + fi + else + # Timeout occurred - use fallback + echo "" >&2 + echo -e "${YW}Timeout - using fallback value: ${fallback}${CL}" >&2 + MISSING_REQUIRED_VALUES+=("${env_var_hint:-timeout}") + echo "$fallback" + return 0 + fi + done + + echo "$response" +} + +# ------------------------------------------------------------------------------ +# prompt_select() +# +# - Prompts user to select from a list of options with timeout support +# - In unattended mode: immediately returns default selection +# - In interactive mode: displays numbered menu and waits for choice +# - After timeout: auto-applies default selection +# +# Arguments: +# $1 - Prompt message (required) +# $2 - Default option number, 1-based (optional, default: 1) +# $3 - Timeout in seconds (optional, default: 60) +# $4+ - Options to display (required, at least 2) +# +# Output: +# Prints the selected option value to stdout +# +# Returns: +# 0 - Success +# 1 - No options provided or invalid state +# +# Example: +# choice=$(prompt_select "Select database:" 1 30 "PostgreSQL" "MySQL" "SQLite") +# echo "Selected: $choice" +# +# # With array +# options=("Option A" "Option B" "Option C") +# selected=$(prompt_select "Choose:" 2 60 "${options[@]}") +# ------------------------------------------------------------------------------ +prompt_select() { + local message="${1:-Select option:}" + local default="${2:-1}" + local timeout="${3:-60}" + shift 3 + + local options=("$@") + local num_options=${#options[@]} + + # Validate options + if [[ $num_options -eq 0 ]]; then + echo "" >&2 + return 1 + fi + + # Validate default + if [[ ! "$default" =~ ^[0-9]+$ ]] || [[ "$default" -lt 1 ]] || [[ "$default" -gt "$num_options" ]]; then + default=1 + fi + + # Unattended mode: return default immediately + if is_unattended; then + echo "${options[$((default - 1))]}" + return 0 + fi + + # Check if running in a TTY + if [[ ! -t 0 ]]; then + echo "${options[$((default - 1))]}" + return 0 + fi + + # Display menu + echo -e "${YW}${message}${CL}" >&2 + local i + for i in "${!options[@]}"; do + local num=$((i + 1)) + if [[ $num -eq $default ]]; then + echo -e " ${GN}${num})${CL} ${options[$i]} ${YW}(default)${CL}" >&2 + else + echo -e " ${GN}${num})${CL} ${options[$i]}" >&2 + fi + done + + # Interactive prompt with timeout + echo -en "${YW}Select [1-${num_options}] (auto-select ${default} in ${timeout}s): ${CL}" >&2 + + local response + if read -t "$timeout" -r response; then + if [[ -z "$response" ]]; then + # Empty response, use default + echo "${options[$((default - 1))]}" + elif [[ "$response" =~ ^[0-9]+$ ]] && [[ "$response" -ge 1 ]] && [[ "$response" -le "$num_options" ]]; then + # Valid selection + echo "${options[$((response - 1))]}" + else + # Invalid input, use default + echo -e "${YW}Invalid selection, using default: ${options[$((default - 1))]}${CL}" >&2 + echo "${options[$((default - 1))]}" + fi + else + # Timeout occurred + echo "" >&2 # Newline after timeout + echo -e "${YW}Timeout - auto-selecting: ${options[$((default - 1))]}${CL}" >&2 + echo "${options[$((default - 1))]}" + fi +} + +# ------------------------------------------------------------------------------ +# prompt_password() +# +# - Prompts user for password input with hidden characters +# - In unattended mode: returns default or generates random password +# - Supports auto-generation of secure passwords +# - After timeout: generates random password if allowed +# +# Arguments: +# $1 - Prompt message (required) +# $2 - Default value or "generate" for auto-generation (optional) +# $3 - Timeout in seconds (optional, default: 60) +# $4 - Minimum length for validation (optional, default: 0 = no minimum) +# +# Output: +# Prints the password to stdout +# +# Example: +# password=$(prompt_password "Enter password:" "generate" 30 8) +# echo "Password set" +# +# # Require user input (no default) +# db_pass=$(prompt_password "Database password:" "" 60 12) +# ------------------------------------------------------------------------------ +prompt_password() { + local message="${1:-Enter password:}" + local default="${2:-}" + local timeout="${3:-60}" + local min_length="${4:-0}" + local response + + # Generate random password if requested + local generated="" + if [[ "$default" == "generate" ]]; then + generated=$(openssl rand -base64 16 2>/dev/null | tr -dc 'a-zA-Z0-9' | head -c 16) + [[ -z "$generated" ]] && generated=$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 16) + default="$generated" + fi + + # Unattended mode: return default immediately + if is_unattended; then + echo "$default" + return 0 + fi + + # Check if running in a TTY + if [[ ! -t 0 ]]; then + echo "$default" + return 0 + fi + + # Build hint + local hint="" + if [[ -n "$generated" ]]; then + hint=" (Enter for auto-generated)" + elif [[ -n "$default" ]]; then + hint=" (Enter for default)" + fi + [[ "$min_length" -gt 0 ]] && hint="${hint} [min ${min_length} chars]" + + # Interactive prompt with timeout (silent input) + echo -en "${YW}${message}${hint} (timeout ${timeout}s): ${CL}" >&2 + + if read -t "$timeout" -rs response; then + echo "" >&2 # Newline after hidden input + if [[ -n "$response" ]]; then + # Validate minimum length + if [[ "$min_length" -gt 0 ]] && [[ ${#response} -lt "$min_length" ]]; then + echo -e "${YW}Password too short (min ${min_length}), using default${CL}" >&2 + echo "$default" + else + echo "$response" + fi + else + echo "$default" + fi + else + # Timeout occurred + echo "" >&2 # Newline after timeout + echo -e "${YW}Timeout - using generated password${CL}" >&2 + echo "$default" + fi +} + # ============================================================================== # SECTION 6: CLEANUP & MAINTENANCE # ============================================================================== @@ -898,15 +1409,13 @@ check_or_create_swap() { msg_error "No active swap detected" - read -p "Do you want to create a swap file? [y/N]: " create_swap - create_swap="${create_swap,,}" # to lowercase - - if [[ "$create_swap" != "y" && "$create_swap" != "yes" ]]; then + if ! prompt_confirm "Do you want to create a swap file?" "n" 60; then msg_info "Skipping swap file creation" return 1 fi - read -p "Enter swap size in MB (e.g., 2048 for 2GB): " swap_size_mb + local swap_size_mb + swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60) if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then msg_error "Invalid size input. Aborting." return 1 From a82d04982dd084287d31c824077c66524740d616 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:38:49 +0100 Subject: [PATCH 143/228] Improve unattended mode detection and export MODE Enhanced the is_unattended() function to prioritize the MODE variable for detecting unattended/non-interactive mode, with detailed handling for various modes and legacy fallbacks. Also, export MODE in build_container() to inform install scripts about the current mode. Updated APP name in forgejo-runner.sh for consistency. --- ct/forgejo-runner.sh | 2 +- misc/build.func | 4 ++++ misc/core.func | 49 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/ct/forgejo-runner.sh b/ct/forgejo-runner.sh index 1dfe36ebc..c11dd19a0 100644 --- a/ct/forgejo-runner.sh +++ b/ct/forgejo-runner.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://forgejo.org/ -APP="Forgejo Runner" +APP="Forgejo-Runner" var_tags="${var_tags:-ci}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" diff --git a/misc/build.func b/misc/build.func index cdcb7f2cc..7c431dc99 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3461,6 +3461,10 @@ build_container() { export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" + # MODE export for unattended detection in install scripts + # This tells install scripts whether to prompt for input or use defaults + export MODE="${METHOD:-default}" + # Build PCT_OPTIONS as multi-line string PCT_OPTIONS_STRING=" -hostname $HN" diff --git a/misc/core.func b/misc/core.func index 10e40a501..1aa910c1c 100644 --- a/misc/core.func +++ b/misc/core.func @@ -813,15 +813,60 @@ is_verbose_mode() { # ------------------------------------------------------------------------------ # is_unattended() # -# - Detects if script is running in unattended/silent mode -# - Checks PHS_SILENT, var_unattended, and UNATTENDED variables +# - Detects if script is running in unattended/non-interactive mode +# - Checks MODE variable first (primary method) +# - Falls back to legacy flags (PHS_SILENT, var_unattended) # - Returns 0 (true) if unattended, 1 (false) otherwise # - Used by prompt functions to auto-apply defaults +# +# Modes that are unattended: +# - default (1) : Use script defaults, no prompts +# - mydefaults (3) : Use user's default.vars, no prompts +# - appdefaults (4) : Use app-specific defaults, no prompts +# +# Modes that are interactive: +# - advanced (2) : Full wizard with all options +# +# Note: Even in advanced mode, install scripts run unattended because +# all values are already collected during the wizard phase. # ------------------------------------------------------------------------------ is_unattended() { + # Primary: Check MODE variable (case-insensitive) + local mode="${MODE:-${mode:-}}" + mode="${mode,,}" # lowercase + + case "$mode" in + default|1) + return 0 + ;; + mydefaults|userdefaults|3) + return 0 + ;; + appdefaults|4) + return 0 + ;; + advanced|2) + # Advanced mode is interactive ONLY during wizard + # Inside container (install scripts), it should be unattended + # Check if we're inside a container (no pveversion command) + if ! command -v pveversion &>/dev/null; then + # We're inside the container - all values already collected + return 0 + fi + # On host during wizard - interactive + return 1 + ;; + esac + + # Legacy fallbacks for compatibility [[ "${PHS_SILENT:-0}" == "1" ]] && return 0 [[ "${var_unattended:-}" =~ ^(yes|true|1)$ ]] && return 0 [[ "${UNATTENDED:-}" =~ ^(yes|true|1)$ ]] && return 0 + + # No TTY available = unattended + [[ ! -t 0 ]] && return 0 + + # Default: interactive return 1 } From 1bb32bbc9409d00e250f4848e371812f4c57895d Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:39:57 +0100 Subject: [PATCH 144/228] fix syntax --- misc/build.func | 1 + misc/core.func | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7c431dc99..3863a6d8a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4744,6 +4744,7 @@ create_lxc_container() { msg_error "Template still not found after version change" exit 220 } + fi else msg_error "No ${PCT_OSTYPE} templates available" exit 220 diff --git a/misc/core.func b/misc/core.func index 1aa910c1c..36356ca52 100644 --- a/misc/core.func +++ b/misc/core.func @@ -834,7 +834,7 @@ is_unattended() { # Primary: Check MODE variable (case-insensitive) local mode="${MODE:-${mode:-}}" mode="${mode,,}" # lowercase - + case "$mode" in default|1) return 0 @@ -857,15 +857,15 @@ is_unattended() { return 1 ;; esac - + # Legacy fallbacks for compatibility [[ "${PHS_SILENT:-0}" == "1" ]] && return 0 [[ "${var_unattended:-}" =~ ^(yes|true|1)$ ]] && return 0 [[ "${UNATTENDED:-}" =~ ^(yes|true|1)$ ]] && return 0 - + # No TTY available = unattended [[ ! -t 0 ]] && return 0 - + # Default: interactive return 1 } From a6134095d4fc1862bf582e0e9d8f48571a7aaa14 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:45:38 +0100 Subject: [PATCH 145/228] fix: correct if/fi nesting in template selection blocks --- misc/build.func | 121 +++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/misc/build.func b/misc/build.func index 3863a6d8a..ffdd4223a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4639,29 +4639,25 @@ create_lxc_container() { local selected_version selected_version=$(prompt_select "Select ${PCT_OSTYPE} version:" 1 60 "${AVAILABLE_VERSIONS[@]}") - if [[ -n "$selected_version" ]]; then - PCT_OSVERSION="$selected_version" - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + # prompt_select always returns a value (uses default in unattended mode) + PCT_OSVERSION="$selected_version" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" - ONLINE_TEMPLATES=() - mapfile -t ONLINE_TEMPLATES < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $2}' | - grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V 2>/dev/null || true - ) + ONLINE_TEMPLATES=() + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $2}' | + grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) - if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${ONLINE_TEMPLATES[-1]}" - TEMPLATE_SOURCE="online" - else - msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" - exit 225 - fi + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" else - msg_custom "🚫" "${YW}" "Installation cancelled" - exit 0 + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 fi else msg_error "No ${PCT_OSTYPE} templates available at all" @@ -4698,53 +4694,52 @@ create_lxc_container() { local selected_version selected_version=$(prompt_select "Select ${PCT_OSTYPE} version:" 1 60 "${AVAILABLE_VERSIONS[@]}") - if [[ -n "$selected_version" ]]; then - export var_version="$selected_version" - export PCT_OSVERSION="$var_version" - msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" + # prompt_select always returns a value (uses default in unattended mode) + export var_version="$selected_version" + export PCT_OSVERSION="$var_version" + msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" - # Retry template search with new version - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - mapfile -t LOCAL_TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v search="${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | - sed 's|.*/||' | sort -t - -k 2 -V - ) - mapfile -t ONLINE_TEMPLATES < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $2}' | - grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V 2>/dev/null || true - ) - ONLINE_TEMPLATE="" - [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $2}' | + grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${LOCAL_TEMPLATES[-1]}" - TEMPLATE_SOURCE="local" - else - TEMPLATE="$ONLINE_TEMPLATE" - TEMPLATE_SOURCE="online" - fi - - TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" - if [[ -z "$TEMPLATE_PATH" ]]; then - TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) - [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" - fi - - # If we still don't have a path but have a valid template name, construct it - if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then - TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" - fi - - [[ -n "$TEMPLATE_PATH" ]] || { - msg_error "Template still not found after version change" - exit 220 - } + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } else msg_error "No ${PCT_OSTYPE} templates available" exit 220 From 6965e3e2ecb1cd40c8a8baa999f1f1cf3dc1daf2 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:47:48 +0100 Subject: [PATCH 146/228] fix: use safe arithmetic to avoid exit code 1 from ((attempts++)) when attempts=0 --- misc/core.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/core.func b/misc/core.func index 36356ca52..e14ba3c22 100644 --- a/misc/core.func +++ b/misc/core.func @@ -1157,7 +1157,7 @@ prompt_input_required() { # Interactive prompt - loop until non-empty input or use fallback on timeout local attempts=0 while [[ -z "$response" ]]; do - ((attempts++)) + attempts=$((attempts + 1)) if [[ $attempts -gt 3 ]]; then echo -e "${YW}Too many empty inputs - using fallback: ${fallback}${CL}" >&2 From 90718b440acac48b5b016ee64f8d22abcf4a834a Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Tue, 27 Jan 2026 16:19:14 +0100 Subject: [PATCH 147/228] Clean up installation script by removing autostart instructions Removed instructions for making the ebusd daemon autostart. --- install/ebusd-install.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh index c43be4708..0ede2e9b8 100644 --- a/install/ebusd-install.sh +++ b/install/ebusd-install.sh @@ -22,15 +22,9 @@ setup_deb822_repo \ msg_info "Installing ebusd" $STD apt install -y ebusd +$STD systemctl enable ebusd msg_ok "Installed ebusd" -msg_info "Follow below instructions to make the daemon autostart:" -msg_info "1. Edit '/etc/default/ebusd' if necessary (especially if your device is not '/dev/ttyUSB0')" -msg_info "2. Start the daemon with 'systemctl start ebusd'" -msg_info "3. Check the daemon status with 'systemctl status ebusd'" -msg_info "4. Check the log file '/var/log/ebusd.log'" -msg_info "5. Make the daemon autostart with 'systemctl enable ebusd'" - motd_ssh customize cleanup_lxc From 9d0ed3090c6adea9e72deaf7272092b4af97715c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 27 Jan 2026 10:39:04 -0500 Subject: [PATCH 148/228] Shelfmark: fix misplaced closing bracket --- ct/shelfmark.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/shelfmark.sh b/ct/shelfmark.sh index be53f2d9a..1e2106248 100644 --- a/ct/shelfmark.sh +++ b/ct/shelfmark.sh @@ -52,7 +52,7 @@ function update_script() { $STD uv venv -c ./venv $STD source ./venv/bin/activate $STD uv pip install -r ./requirements-base.txt - if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env == "false") ]]; then + if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env) == "false" ]]; then $STD uv pip install -r ./requirements-shelfmark.txt fi mv /opt/start.sh.bak /opt/shelfmark/start.sh From 6be45a03804cbe3cc5c1774260e4364f7f06e91d Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 27 Jan 2026 10:44:47 -0500 Subject: [PATCH 149/228] Shelfmark: small fixes --- install/shelfmark-install.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/install/shelfmark-install.sh b/install/shelfmark-install.sh index e327caf44..9be897055 100644 --- a/install/shelfmark-install.sh +++ b/install/shelfmark-install.sh @@ -59,19 +59,19 @@ case "$DEPLOYMENT_TYPE" in ;; 3) echo "" - echo -e "${BL}Use existing FlareSolverr LXC${CL}" + echo -e "${BL}Use an existing FlareSolverr/Byparr LXC${CL}" echo "─────────────────────────────────────────" - echo "Enter the URL/IP address with port of your Flaresolverr instance" + echo "Enter the URL/IP address with port of your Flaresolverr/Byparr instance" echo "Example: http://flaresoverr.homelab.lan:8191 or" echo "http://192.168.10.99:8191" echo "" - read -r -p "FlareSolverr URL: " FLARESOLVERR_URL + read -r -p "FlareSolverr/Byparr URL: " BYPASSER_URL - if [[ -z "$FLARESOLVERR_URL" ]]; then - msg_warn "No Flaresolverr URL provided. Falling back to Shelfmark's internal bypasser." + if [[ -z "$BYPASSER_URL" ]]; then + msg_warn "No Flaresolverr/Byparr URL provided. Falling back to Shelfmark's internal bypasser." else - FLARESOLVERR_URL="${FLARESOLVERR_URL%/}" - msg_ok "FlareSolverr URL: ${FLARESOLVERR_URL}" + BYPASSER_URL="${BYPASSER_URL%/}" + msg_ok "FlareSolverr/Byparr URL: ${BYPASSER_URL}" fi ;; 4) @@ -102,7 +102,7 @@ if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then elif [[ "$DEPLOYMENT_TYPE" == "3" ]]; then sed -i -e '/BYPASSER=/s/false/true/' \ -e 's/^# EXT_/EXT_/' \ - -e "s|_URL=.*|_URL=${FLARESOLVERR_URL}|" /etc/shelfmark/.env + -e "s|_URL=.*|_URL=${BYPASSER_URL}|" /etc/shelfmark/.env elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then sed -i '/_BYPASS=/s/true/false/' /etc/shelfmark/.env else From ad48c14cf3330b98f060b451ca44f929800257ce Mon Sep 17 00:00:00 2001 From: Matthew Stern Date: Sun, 25 Jan 2026 18:01:44 -0500 Subject: [PATCH 150/228] feat: add isponsorblocktv --- ct/isponsorblocktv.sh | 41 +++++++++ frontend/public/json/isponsorblocktv.json | 44 ++++++++++ install/isponsorblocktv-install.sh | 102 ++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 ct/isponsorblocktv.sh create mode 100644 frontend/public/json/isponsorblocktv.json create mode 100644 install/isponsorblocktv-install.sh diff --git a/ct/isponsorblocktv.sh b/ct/isponsorblocktv.sh new file mode 100644 index 000000000..c4b717339 --- /dev/null +++ b/ct/isponsorblocktv.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Matthew Stern +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/dmunozv04/iSponsorBlockTV + +APP="iSponsorBlockTV" +var_tags="${var_tags:-media;automation}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +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/isponsorblocktv ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_error "Currently we don't provide an update function for ${APP}." + 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} Run the setup wizard inside the container with:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}iSponsorBlockTV setup${CL}" diff --git a/frontend/public/json/isponsorblocktv.json b/frontend/public/json/isponsorblocktv.json new file mode 100644 index 000000000..71f82fb99 --- /dev/null +++ b/frontend/public/json/isponsorblocktv.json @@ -0,0 +1,44 @@ +{ + "name": "iSponsorBlockTV", + "slug": "isponsorblocktv", + "categories": [ + 13 + ], + "date_created": "2026-01-25", + "type": "ct", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": "https://github.com/dmunozv04/iSponsorBlockTV/wiki", + "website": "https://github.com/dmunozv04/iSponsorBlockTV", + "logo": null, + "config_path": "/var/lib/isponsorblocktv/config.json", + "description": "iSponsorBlockTV connects to YouTube TV clients and automatically skips SponsorBlock segments, mutes ads, and presses the Skip Ad button when available.", + "install_methods": [ + { + "type": "default", + "script": "ct/isponsorblocktv.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "No web UI; run `iSponsorBlockTV setup` inside the container to configure.", + "type": "info" + }, + { + "text": "SSDP auto-discovery requires multicast on your bridge; manual pairing works without it.", + "type": "info" + } + ] +} diff --git a/install/isponsorblocktv-install.sh b/install/isponsorblocktv-install.sh new file mode 100644 index 000000000..17ebe5652 --- /dev/null +++ b/install/isponsorblocktv-install.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Matthew Stern +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/dmunozv04/iSponsorBlockTV + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +INSTALL_DIR="/opt/isponsorblocktv" +DATA_DIR="/var/lib/isponsorblocktv" +SERVICE_USER="isponsorblocktv" + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + python3 \ + python3-venv \ + python3-pip +msg_ok "Installed Dependencies" + +msg_info "Downloading iSponsorBlockTV" +fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" "tarball" "latest" "$INSTALL_DIR" +msg_ok "Downloaded iSponsorBlockTV" + +msg_info "Setting up iSponsorBlockTV" +python3 -m venv "$INSTALL_DIR/venv" +$STD "$INSTALL_DIR/venv/bin/pip" install --upgrade pip +$STD "$INSTALL_DIR/venv/bin/pip" install "$INSTALL_DIR" +msg_ok "Set up iSponsorBlockTV" + +msg_info "Creating service user and data directory" +if ! id "$SERVICE_USER" &>/dev/null; then + useradd --system --home "$DATA_DIR" --create-home "$SERVICE_USER" +fi +install -d -o "$SERVICE_USER" -g "$SERVICE_USER" "$DATA_DIR" +chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR" +msg_ok "Created service user and data directory" + +msg_info "Creating Service" +cat </etc/systemd/system/isponsorblocktv.service +[Unit] +Description=iSponsorBlockTV +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=$SERVICE_USER +Group=$SERVICE_USER +WorkingDirectory=$INSTALL_DIR +Environment=iSPBTV_data_dir=$DATA_DIR +ExecStart=$INSTALL_DIR/venv/bin/iSponsorBlockTV +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOT +systemctl enable -q --now isponsorblocktv +msg_ok "Created Service" + +msg_info "Creating CLI wrapper" +install -d /usr/local/bin +cat <<'EOT' >/usr/local/bin/iSponsorBlockTV +#!/usr/bin/env bash +export iSPBTV_data_dir="/var/lib/isponsorblocktv" + +set +e +/opt/isponsorblocktv/venv/bin/iSponsorBlockTV "$@" +status=$? +set -e + +case "${1:-}" in + setup|setup-cli) + systemctl restart isponsorblocktv >/dev/null 2>&1 || true + ;; +esac + +exit $status +EOT +chmod +x /usr/local/bin/iSponsorBlockTV +ln -sf /usr/local/bin/iSponsorBlockTV /usr/bin/iSponsorBlockTV +msg_ok "Created CLI wrapper" + +msg_info "Setting default data dir for shells" +cat <<'EOT' >/etc/profile.d/isponsorblocktv.sh +export iSPBTV_data_dir="/var/lib/isponsorblocktv" +EOT +if ! grep -q '^iSPBTV_data_dir=' /etc/environment 2>/dev/null; then + echo 'iSPBTV_data_dir=/var/lib/isponsorblocktv' >>/etc/environment +fi +msg_ok "Set default data dir for shells" + +motd_ssh +customize +cleanup_lxc From d25f48f5be7036345a924a5aa6731ca5cf786f0e Mon Sep 17 00:00:00 2001 From: Matthew Stern Date: Sun, 25 Jan 2026 18:46:57 -0500 Subject: [PATCH 151/228] Align to code standards --- ct/isponsorblocktv.sh | 43 ++++++++++++++++++++--- frontend/public/json/isponsorblocktv.json | 6 ++-- install/isponsorblocktv-install.sh | 29 +++++++-------- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/ct/isponsorblocktv.sh b/ct/isponsorblocktv.sh index c4b717339..b6bd4e18a 100644 --- a/ct/isponsorblocktv.sh +++ b/ct/isponsorblocktv.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG -# Author: Matthew Stern -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Author: Matthew Stern (sternma) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/dmunozv04/iSponsorBlockTV APP="iSponsorBlockTV" @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -23,11 +23,44 @@ function update_script() { header_info check_container_storage check_container_resources + if [[ ! -d /opt/isponsorblocktv ]]; then msg_error "No ${APP} Installation Found!" exit fi - msg_error "Currently we don't provide an update function for ${APP}." + + if check_for_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV"; then + msg_info "Stopping Service" + systemctl stop isponsorblocktv + msg_ok "Stopped Service" + + if [[ -d /var/lib/isponsorblocktv ]]; then + msg_info "Backing up Data" + cp -r /var/lib/isponsorblocktv /var/lib/isponsorblocktv_data_backup + msg_ok "Backed up Data" + fi + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" + + msg_info "Setting up iSponsorBlockTV" + $STD python3 -m venv /opt/isponsorblocktv/venv + $STD /opt/isponsorblocktv/venv/bin/pip install --upgrade pip + $STD /opt/isponsorblocktv/venv/bin/pip install /opt/isponsorblocktv + msg_ok "Set up iSponsorBlockTV" + + if [[ -d /var/lib/isponsorblocktv_data_backup ]]; then + msg_info "Restoring Data" + rm -rf /var/lib/isponsorblocktv + cp -r /var/lib/isponsorblocktv_data_backup /var/lib/isponsorblocktv + rm -rf /var/lib/isponsorblocktv_data_backup + msg_ok "Restored Data" + fi + + msg_info "Starting Service" + systemctl start isponsorblocktv + msg_ok "Started Service" + msg_ok "Updated successfully!" + fi exit } diff --git a/frontend/public/json/isponsorblocktv.json b/frontend/public/json/isponsorblocktv.json index 71f82fb99..2b25f17e9 100644 --- a/frontend/public/json/isponsorblocktv.json +++ b/frontend/public/json/isponsorblocktv.json @@ -6,12 +6,12 @@ ], "date_created": "2026-01-25", "type": "ct", - "updateable": false, + "updateable": true, "privileged": false, "interface_port": null, "documentation": "https://github.com/dmunozv04/iSponsorBlockTV/wiki", "website": "https://github.com/dmunozv04/iSponsorBlockTV", - "logo": null, + "logo": "https://raw.githubusercontent.com/ajayyy/SponsorBlock/master/public/icons/IconSponsorBlocker512px.png", "config_path": "/var/lib/isponsorblocktv/config.json", "description": "iSponsorBlockTV connects to YouTube TV clients and automatically skips SponsorBlock segments, mutes ads, and presses the Skip Ad button when available.", "install_methods": [ @@ -23,7 +23,7 @@ "ram": 1024, "hdd": 4, "os": "debian", - "version": "12" + "version": "13" } } ], diff --git a/install/isponsorblocktv-install.sh b/install/isponsorblocktv-install.sh index 17ebe5652..307e4b41e 100644 --- a/install/isponsorblocktv-install.sh +++ b/install/isponsorblocktv-install.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG -# Author: Matthew Stern -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Author: Matthew Stern (sternma) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/dmunozv04/iSponsorBlockTV source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" @@ -15,7 +15,6 @@ update_os INSTALL_DIR="/opt/isponsorblocktv" DATA_DIR="/var/lib/isponsorblocktv" -SERVICE_USER="isponsorblocktv" msg_info "Installing Dependencies" $STD apt-get install -y \ @@ -24,23 +23,17 @@ $STD apt-get install -y \ python3-pip msg_ok "Installed Dependencies" -msg_info "Downloading iSponsorBlockTV" -fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" "tarball" "latest" "$INSTALL_DIR" -msg_ok "Downloaded iSponsorBlockTV" +fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" msg_info "Setting up iSponsorBlockTV" -python3 -m venv "$INSTALL_DIR/venv" +$STD python3 -m venv "$INSTALL_DIR/venv" $STD "$INSTALL_DIR/venv/bin/pip" install --upgrade pip $STD "$INSTALL_DIR/venv/bin/pip" install "$INSTALL_DIR" msg_ok "Set up iSponsorBlockTV" -msg_info "Creating service user and data directory" -if ! id "$SERVICE_USER" &>/dev/null; then - useradd --system --home "$DATA_DIR" --create-home "$SERVICE_USER" -fi -install -d -o "$SERVICE_USER" -g "$SERVICE_USER" "$DATA_DIR" -chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR" -msg_ok "Created service user and data directory" +msg_info "Creating data directory" +install -d "$DATA_DIR" +msg_ok "Created data directory" msg_info "Creating Service" cat </etc/systemd/system/isponsorblocktv.service @@ -51,8 +44,8 @@ Wants=network-online.target [Service] Type=simple -User=$SERVICE_USER -Group=$SERVICE_USER +User=root +Group=root WorkingDirectory=$INSTALL_DIR Environment=iSPBTV_data_dir=$DATA_DIR ExecStart=$INSTALL_DIR/venv/bin/iSponsorBlockTV @@ -93,7 +86,9 @@ cat <<'EOT' >/etc/profile.d/isponsorblocktv.sh export iSPBTV_data_dir="/var/lib/isponsorblocktv" EOT if ! grep -q '^iSPBTV_data_dir=' /etc/environment 2>/dev/null; then - echo 'iSPBTV_data_dir=/var/lib/isponsorblocktv' >>/etc/environment + cat <<'EOT' >>/etc/environment +iSPBTV_data_dir=/var/lib/isponsorblocktv +EOT fi msg_ok "Set default data dir for shells" From 741164de3e41fe5e7a806ff38943e398e0a3faef Mon Sep 17 00:00:00 2001 From: Matthew Stern Date: Sun, 25 Jan 2026 18:56:15 -0500 Subject: [PATCH 152/228] switch from apt-get to apt --- install/isponsorblocktv-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/isponsorblocktv-install.sh b/install/isponsorblocktv-install.sh index 307e4b41e..8cce9c019 100644 --- a/install/isponsorblocktv-install.sh +++ b/install/isponsorblocktv-install.sh @@ -17,7 +17,7 @@ INSTALL_DIR="/opt/isponsorblocktv" DATA_DIR="/var/lib/isponsorblocktv" msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ python3 \ python3-venv \ python3-pip From 1658c13309a54c87b7fb5a976fcba5751ffdb13d Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Wed, 28 Jan 2026 11:09:09 +0100 Subject: [PATCH 153/228] Update logo and configuration instructions in ebusd.json --- frontend/public/json/ebusd.json | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/frontend/public/json/ebusd.json b/frontend/public/json/ebusd.json index b902ea7a2..e946af464 100644 --- a/frontend/public/json/ebusd.json +++ b/frontend/public/json/ebusd.json @@ -11,7 +11,7 @@ "interface_port": null, "documentation": "https://github.com/john30/ebusd/wiki", "website": "https://github.com/john30/ebusd", - "logo": "", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/proxmox-helper-scripts.webp", "config_path": "/etc/default/ebusd", "description": "ebusd is a daemon for handling communication with eBUS devices connected to a 2-wire `energy bus` used by numerous heating systems.", "install_methods": [ @@ -33,16 +33,8 @@ }, "notes": [ { - "text": "Instructions: \ - 1. Edit `/etc/default/ebusd` if necessary (especially if your device is not `/dev/ttyUSB0`) \ - 2. Start the daemon with `systemctl start ebusd` \ - 3. Check the log file `/var/log/ebusd.log` \ - 4. Make the daemon autostart with `systemctl enable ebusd`", + "text": "Show configuration instructions: `cat ~/ebusd-configuation-instructions.txt`", "type": "info" - }, - { - "text": "Working `/etc/default/ebusd` options for `ebus adapter shield v5`: EBUSD_OPTS='--pidfile=/run/ebusd.pid --latency=100 --scanconfig --configpath=https://ebus.github.io/ --accesslevel=* --pollinterval=30 --device=ens:x.x.x.x:9999 --mqtthost=x.x.x.x --mqttport=1883 --mqttuser=ha-mqtt --mqttpass=xxxxxxxx! --mqttint=/etc/ebusd/mqtt-hassio.cfg --mqttjson --mqttlog --mqttretain --mqtttopic=ebusd --log=all:notice --log=main:notice --log=bus:notice --log=update:notice --log=network:notice --log=other:notice'", - "type": "info" - }, + } ] } From f8e6daba1440eb431edafdb0acac01ad2b8553c0 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:14:48 +0100 Subject: [PATCH 154/228] Remove Dawarich app and related files Deleted Dawarich install script, container script, and JSON metadata, effectively removing support for the Dawarich application from the repository. Added a new GitHub Actions workflow to automate updating GitHub version information for remaining apps. --- .github/workflows/update-github-versions.yml | 232 +++++++++++++++++++ ct/dawarich.sh | 95 -------- frontend/public/json/dawarich.json | 40 ---- install/dawarich-install.sh | 173 -------------- 4 files changed, 232 insertions(+), 308 deletions(-) create mode 100644 .github/workflows/update-github-versions.yml delete mode 100644 ct/dawarich.sh delete mode 100644 frontend/public/json/dawarich.json delete mode 100644 install/dawarich-install.sh diff --git a/.github/workflows/update-github-versions.yml b/.github/workflows/update-github-versions.yml new file mode 100644 index 000000000..a31299b84 --- /dev/null +++ b/.github/workflows/update-github-versions.yml @@ -0,0 +1,232 @@ +name: Update GitHub Versions + +on: + workflow_dispatch: + schedule: + # Runs 4x daily: 00:00, 06:00, 12:00, 18:00 UTC + - cron: "0 0,6,12,18 * * *" + +permissions: + contents: write + pull-requests: write + +env: + VERSIONS_FILE: frontend/public/json/github-versions.json + +jobs: + update-github-versions: + if: github.repository == 'community-scripts/ProxmoxVE' + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + ref: main + + - name: Generate GitHub App Token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Extract GitHub versions from install scripts + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + set -euo pipefail + + echo "=========================================" + echo " Extracting GitHub versions from scripts" + echo "=========================================" + + # Initialize versions array + versions_json="[]" + + # Function to add a version entry + add_version() { + local slug="$1" + local repo="$2" + local version="$3" + local pinned="$4" + local date="$5" + + versions_json=$(echo "$versions_json" | jq \ + --arg slug "$slug" \ + --arg repo "$repo" \ + --arg version "$version" \ + --argjson pinned "$pinned" \ + --arg date "$date" \ + '. += [{"slug": $slug, "repo": $repo, "version": $version, "pinned": $pinned, "date": $date}]') + } + + # Get list of slugs from JSON files + echo "" + echo "=== Scanning JSON files for slugs ===" + + for json_file in frontend/public/json/*.json; do + [[ ! -f "$json_file" ]] && continue + + # Skip non-app JSON files + basename_file=$(basename "$json_file") + case "$basename_file" in + metadata.json|versions.json|github-versions.json|dependency-check.json|update-apps.json) + continue + ;; + esac + + # Extract slug from JSON + slug=$(jq -r '.slug // empty' "$json_file" 2>/dev/null) + [[ -z "$slug" ]] && continue + + # Find corresponding install script + install_script="install/${slug}-install.sh" + [[ ! -f "$install_script" ]] && continue + + echo -n "[$slug] " + + # Look for fetch_and_deploy_gh_release calls + # Pattern: fetch_and_deploy_gh_release "app" "owner/repo" ["mode"] ["version"] + while IFS= read -r line; do + # Skip commented lines + [[ "$line" =~ ^[[:space:]]*# ]] && continue + + # Extract repo and version from fetch_and_deploy_gh_release + if [[ "$line" =~ fetch_and_deploy_gh_release[[:space:]]+\"[^\"]*\"[[:space:]]+\"([^\"]+)\"([[:space:]]+\"([^\"]+)\")?([[:space:]]+\"([^\"]+)\")? ]]; then + repo="${BASH_REMATCH[1]}" + mode="${BASH_REMATCH[3]:-tarball}" + pinned_version="${BASH_REMATCH[5]:-latest}" + + # Check if version is pinned (not "latest" and not empty) + is_pinned=false + target_version="" + + if [[ -n "$pinned_version" && "$pinned_version" != "latest" ]]; then + is_pinned=true + target_version="$pinned_version" + echo -n "(pinned: $pinned_version) " + fi + + # Fetch version from GitHub + if [[ "$is_pinned" == "true" ]]; then + # For pinned versions, verify it exists and get date + response=$(gh api "repos/${repo}/releases/tags/${target_version}" 2>/dev/null || echo '{}') + if echo "$response" | jq -e '.tag_name' > /dev/null 2>&1; then + version=$(echo "$response" | jq -r '.tag_name') + date=$(echo "$response" | jq -r '.published_at // empty') + add_version "$slug" "$repo" "$version" "true" "$date" + echo "✓ $version (pinned)" + else + # Pinned version not found, skip + echo "⚠ pinned version $target_version not found" + fi + else + # Fetch latest release + response=$(gh api "repos/${repo}/releases/latest" 2>/dev/null || echo '{}') + if echo "$response" | jq -e '.tag_name' > /dev/null 2>&1; then + version=$(echo "$response" | jq -r '.tag_name') + date=$(echo "$response" | jq -r '.published_at // empty') + add_version "$slug" "$repo" "$version" "false" "$date" + echo "✓ $version" + else + # Try tags as fallback + version=$(gh api "repos/${repo}/tags" --jq '.[0].name // empty' 2>/dev/null || echo "") + if [[ -n "$version" ]]; then + add_version "$slug" "$repo" "$version" "false" "" + echo "✓ $version (from tags)" + else + echo "⚠ no version found" + fi + fi + fi + + break # Only first match per script + fi + done < <(grep 'fetch_and_deploy_gh_release' "$install_script" 2>/dev/null || true) + + done + + # Save versions file + echo "$versions_json" | jq --arg date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + '{generated: $date, versions: (. | sort_by(.slug))}' > "$VERSIONS_FILE" + + total=$(echo "$versions_json" | jq 'length') + echo "" + echo "=========================================" + echo " Total versions extracted: $total" + echo "=========================================" + + - name: Check for changes + id: check-changes + run: | + if git diff --quiet "$VERSIONS_FILE" 2>/dev/null; then + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "No changes detected" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "Changes detected:" + git diff --stat "$VERSIONS_FILE" 2>/dev/null || true + fi + + - name: Create Pull Request + if: steps.check-changes.outputs.changed == 'true' + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + BRANCH_NAME="automated/update-github-versions-$(date +%Y%m%d)" + + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "GitHub Actions[bot]" + + # Check if branch exists and delete it + git push origin --delete "$BRANCH_NAME" 2>/dev/null || true + + git checkout -b "$BRANCH_NAME" + git add "$VERSIONS_FILE" + git commit -m "chore: update github-versions.json + + Total versions: $(jq '.versions | length' "$VERSIONS_FILE") + Pinned versions: $(jq '[.versions[] | select(.pinned == true)] | length' "$VERSIONS_FILE") + Generated: $(jq -r '.generated' "$VERSIONS_FILE")" + + git push origin "$BRANCH_NAME" --force + + # Check if PR already exists + existing_pr=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number // empty') + + if [[ -n "$existing_pr" ]]; then + echo "PR #$existing_pr already exists, updating..." + else + gh pr create \ + --title "[Automated] Update GitHub versions" \ + --body "This PR updates version information from GitHub releases. + + ## How it works + 1. Scans all JSON files in \`frontend/public/json/\` for slugs + 2. Finds corresponding \`install/{slug}-install.sh\` scripts + 3. Extracts \`fetch_and_deploy_gh_release\` calls + 4. Fetches latest (or pinned) version from GitHub + + ## Stats + - Total versions: $(jq '.versions | length' "$VERSIONS_FILE") + - Pinned versions: $(jq '[.versions[] | select(.pinned == true)] | length' "$VERSIONS_FILE") + - Latest versions: $(jq '[.versions[] | select(.pinned == false)] | length' "$VERSIONS_FILE") + + --- + *Automatically generated from install scripts*" \ + --base main \ + --head "$BRANCH_NAME" \ + --label "automated pr" + fi + + - name: Auto-approve PR + if: steps.check-changes.outputs.changed == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + BRANCH_NAME="automated/update-github-versions-$(date +%Y%m%d)" + pr_number=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number') + if [[ -n "$pr_number" ]]; then + gh pr review "$pr_number" --approve + fi diff --git a/ct/dawarich.sh b/ct/dawarich.sh deleted file mode 100644 index e4ba70a28..000000000 --- a/ct/dawarich.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2026 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/Freika/dawarich - -APP="Dawarich" -var_tags="${var_tags:-location;tracking;gps}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-15}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -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/dawarich ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "dawarich" "Freika/dawarich"; then - msg_info "Stopping Services" - systemctl stop dawarich-web dawarich-worker - msg_ok "Stopped Services" - - msg_info "Backing up Data" - cp -r /opt/dawarich/app/storage /opt/dawarich_storage_backup 2>/dev/null || true - cp /opt/dawarich/app/config/master.key /opt/dawarich_master.key 2>/dev/null || true - cp /opt/dawarich/app/config/credentials.yml.enc /opt/dawarich_credentials.yml.enc 2>/dev/null || true - msg_ok "Backed up Data" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "dawarich" "Freika/dawarich" "tarball" "latest" "/opt/dawarich/app" - - RUBY_VERSION=$(cat /opt/dawarich/app/.ruby-version 2>/dev/null || echo "3.4.6") - RUBY_VERSION=${RUBY_VERSION} RUBY_INSTALL_RAILS="false" setup_ruby - - msg_info "Running Migrations" - cd /opt/dawarich/app - source /root/.profile - export PATH="/root/.rbenv/shims:/root/.rbenv/bin:$PATH" - eval "$(/root/.rbenv/bin/rbenv init - bash)" - - set -a && source /opt/dawarich/.env && set +a - - $STD bundle config set --local deployment 'true' - $STD bundle config set --local without 'development test' - $STD bundle install - - if [[ -f /opt/dawarich/package.json ]]; then - cd /opt/dawarich - $STD npm install - cd /opt/dawarich/app - elif [[ -f /opt/dawarich/app/package.json ]]; then - $STD npm install - fi - - $STD bundle exec rake assets:precompile - $STD bundle exec rails db:migrate - $STD bundle exec rake data:migrate - msg_ok "Ran Migrations" - - msg_info "Restoring Data" - cp -r /opt/dawarich_storage_backup/. /opt/dawarich/app/storage/ 2>/dev/null || true - cp /opt/dawarich_master.key /opt/dawarich/app/config/master.key 2>/dev/null || true - cp /opt/dawarich_credentials.yml.enc /opt/dawarich/app/config/credentials.yml.enc 2>/dev/null || true - rm -rf /opt/dawarich_storage_backup /opt/dawarich_master.key /opt/dawarich_credentials.yml.enc - msg_ok "Restored Data" - - msg_info "Starting Services" - systemctl start dawarich-web dawarich-worker - msg_ok "Started Services" - msg_ok "Updated successfully!" - 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/frontend/public/json/dawarich.json b/frontend/public/json/dawarich.json deleted file mode 100644 index 85659bdaa..000000000 --- a/frontend/public/json/dawarich.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Dawarich", - "slug": "dawarich", - "categories": [ - 9 - ], - "date_created": "2026-01-16", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://dawarich.app/docs", - "website": "https://dawarich.app/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/dawarich.webp", - "config_path": "/opt/dawarich/.env", - "description": "Dawarich is a self-hosted alternative to Google Timeline (Google Maps Location History). It allows you to import your location history from Google Maps Timeline and Owntracks, view it on a map, and analyze your location data with statistics and visualizations.", - "install_methods": [ - { - "type": "default", - "script": "ct/dawarich.sh", - "resources": { - "cpu": 4, - "ram": 4096, - "hdd": 15, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": "demo@dawarich.app", - "password": "password" - }, - "notes": [ - { - "text": "Default credentials: demo@dawarich.app / password - Change after first login!", - "type": "warning" - } - ] -} diff --git a/install/dawarich-install.sh b/install/dawarich-install.sh deleted file mode 100644 index 0adc17a14..000000000 --- a/install/dawarich-install.sh +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/Freika/dawarich - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential \ - git \ - libpq-dev \ - libgeos-dev \ - libyaml-dev \ - libffi-dev \ - libssl-dev \ - libjemalloc2 \ - imagemagick \ - libmagickwand-dev \ - libvips-dev \ - cmake \ - redis-server \ - nginx -msg_ok "Installed Dependencies" - -PG_VERSION="17" PG_MODULES="postgis-3" setup_postgresql -PG_DB_NAME="dawarich_db" PG_DB_USER="dawarich" PG_DB_EXTENSIONS="postgis" setup_postgresql_db - -fetch_and_deploy_gh_release "dawarich" "Freika/dawarich" "tarball" "latest" "/opt/dawarich/app" - -msg_info "Setting up Directories" -mkdir -p /opt/dawarich/app/{storage,log,tmp/pids,tmp/cache,tmp/sockets} -msg_ok "Set up Directories" - -msg_info "Configuring Environment" -SECRET_KEY_BASE=$(openssl rand -hex 64) -RELEASE=$(get_latest_github_release "Freika/dawarich") -cat </opt/dawarich/.env -RAILS_ENV=production -SECRET_KEY_BASE=${SECRET_KEY_BASE} -DATABASE_HOST=localhost -DATABASE_USERNAME=${PG_DB_USER} -DATABASE_PASSWORD=${PG_DB_PASS} -DATABASE_NAME=${PG_DB_NAME} -REDIS_URL=redis://127.0.0.1:6379/0 -BACKGROUND_PROCESSING_CONCURRENCY=10 -APPLICATION_HOST=${LOCAL_IP} -APPLICATION_HOSTS=${LOCAL_IP},localhost -TIME_ZONE=UTC -DISABLE_TELEMETRY=true -APP_VERSION=${RELEASE} -EOF -msg_ok "Configured Environment" - -NODE_VERSION="22" setup_nodejs -RUBY_VERSION=$(cat /opt/dawarich/app/.ruby-version 2>/dev/null || echo "3.4.6") -RUBY_VERSION=${RUBY_VERSION} RUBY_INSTALL_RAILS="false" setup_ruby - -msg_info "Installing Dawarich" -cd /opt/dawarich/app -source /root/.profile -export PATH="/root/.rbenv/shims:/root/.rbenv/bin:$PATH" -eval "$(/root/.rbenv/bin/rbenv init - bash)" -set -a && source /opt/dawarich/.env && set +a -$STD gem install bundler -$STD bundle config set --local deployment 'true' -$STD bundle config set --local without 'development test' -$STD bundle install -if [[ -f /opt/dawarich/package.json ]]; then - cd /opt/dawarich - $STD npm install - cd /opt/dawarich/app -elif [[ -f /opt/dawarich/app/package.json ]]; then - $STD npm install -fi -$STD bundle exec rake assets:precompile -$STD bundle exec rails db:prepare -$STD bundle exec rake data:migrate -msg_ok "Installed Dawarich" - -msg_info "Creating Services" -cat </etc/systemd/system/dawarich-web.service -[Unit] -Description=Dawarich Web Server -After=network.target postgresql.service redis-server.service -Requires=postgresql.service redis-server.service - -[Service] -Type=simple -WorkingDirectory=/opt/dawarich/app -EnvironmentFile=/opt/dawarich/.env -ExecStart=/root/.rbenv/shims/bundle exec puma -C config/puma.rb -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/dawarich-worker.service -[Unit] -Description=Dawarich Sidekiq Worker -After=network.target postgresql.service redis-server.service -Requires=postgresql.service redis-server.service - -[Service] -Type=simple -WorkingDirectory=/opt/dawarich/app -EnvironmentFile=/opt/dawarich/.env -ExecStart=/root/.rbenv/shims/bundle exec sidekiq -C config/sidekiq.yml -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now redis-server dawarich-web dawarich-worker -msg_ok "Created Services" - -msg_info "Configuring Nginx" -cat </etc/nginx/sites-available/dawarich.conf -upstream dawarich { - server 127.0.0.1:3000; -} - -server { - listen 80; - server_name _; - - root /opt/dawarich/app/public; - client_max_body_size 100M; - - location ~ ^/(assets|packs)/ { - expires max; - add_header Cache-Control "public, immutable"; - try_files \$uri =404; - } - - location / { - try_files \$uri @rails; - } - - location @rails { - proxy_pass http://dawarich; - proxy_http_version 1.1; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_redirect off; - proxy_buffering off; - } -} -EOF -ln -sf /etc/nginx/sites-available/dawarich.conf /etc/nginx/sites-enabled/ -rm -f /etc/nginx/sites-enabled/default -systemctl enable -q --now nginx -msg_ok "Configured Nginx" - -motd_ssh -customize -cleanup_lxc From 8224544bf0aabcd4537d5318cca7335a20138ec5 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:16:31 +0100 Subject: [PATCH 155/228] Update update-github-versions.yml --- .github/workflows/update-github-versions.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/update-github-versions.yml b/.github/workflows/update-github-versions.yml index a31299b84..be3679f25 100644 --- a/.github/workflows/update-github-versions.yml +++ b/.github/workflows/update-github-versions.yml @@ -24,16 +24,9 @@ jobs: with: ref: main - - name: Generate GitHub App Token - id: generate-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - - name: Extract GitHub versions from install scripts env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail @@ -172,7 +165,7 @@ jobs: - name: Create Pull Request if: steps.check-changes.outputs.changed == 'true' env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | BRANCH_NAME="automated/update-github-versions-$(date +%Y%m%d)" From e1be251201474ad35cef44fbf15a5dce47327d88 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Wed, 28 Jan 2026 13:16:35 +0100 Subject: [PATCH 156/228] Add ebusd configuration instructions to a text file Added configuration instructions for ebusd in a new text file. --- install/ebusd-install.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh index 0ede2e9b8..560bf362a 100644 --- a/install/ebusd-install.sh +++ b/install/ebusd-install.sh @@ -25,6 +25,41 @@ $STD apt install -y ebusd $STD systemctl enable ebusd msg_ok "Installed ebusd" +cat <~/ebusd-configuation-instructions.txt +Configuration instructions: + + 1. Edit "/etc/default/ebusd" if necessary (especially if your device is not "/dev/ttyUSB0") + 2. Start the daemon with "systemctl start ebusd" + 3. Check the log file "/var/log/ebusd.log" + 4. Make the daemon autostart with "systemctl enable ebusd" + +Working "/etc/default/ebusd" options for "ebus adapter shield v5": + +EBUSD_OPTS=" + --pidfile=/run/ebusd.pid + --latency=100 + --scanconfig + --configpath=https://cfg.ebusd.eu/ + --accesslevel=* + --pollinterval=30 + --device=ens:XXX.XXX.XXX.XXX:9999 + --mqtthost=XXX.XXX.XXX.XXX + --mqttport=1883 + --mqttuser=XXXXXX + --mqttpass=XXXXXX + --mqttint=/etc/ebusd/mqtt-hassio.cfg + --mqttjson + --mqttlog + --mqttretain + --mqtttopic=ebusd + --log=all:notice + --log=main:notice + --log=bus:notice + --log=update:notice + --log=network:notice + --log=other:notice" +EOF + motd_ssh customize cleanup_lxc From 416f915a62be2224cdcc591de2f39cc484a27d66 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:17:33 +0100 Subject: [PATCH 157/228] Update update-versions-github.yml --- .github/workflows/update-versions-github.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/update-versions-github.yml b/.github/workflows/update-versions-github.yml index 5c18ef2e5..9e94b9686 100644 --- a/.github/workflows/update-versions-github.yml +++ b/.github/workflows/update-versions-github.yml @@ -25,13 +25,6 @@ jobs: with: ref: main - - name: Generate GitHub App Token - id: generate-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - - name: Extract version sources from install scripts run: | set -euo pipefail @@ -330,7 +323,7 @@ jobs: - name: Fetch versions for all sources env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail @@ -453,7 +446,7 @@ jobs: - name: Create Pull Request if: steps.check-changes.outputs.changed == 'true' env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | BRANCH_NAME="automated/update-versions-$(date +%Y%m%d)" From 498f3f93081175588ecc48ab27b18638d529f277 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:18:29 +0100 Subject: [PATCH 158/228] chore: use GITHUB_TOKEN instead of App Token in version workflows --- .github/workflows/update-github-versions.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-github-versions.yml b/.github/workflows/update-github-versions.yml index be3679f25..924a4c3c1 100644 --- a/.github/workflows/update-github-versions.yml +++ b/.github/workflows/update-github-versions.yml @@ -57,10 +57,10 @@ jobs: # Get list of slugs from JSON files echo "" echo "=== Scanning JSON files for slugs ===" - + for json_file in frontend/public/json/*.json; do [[ ! -f "$json_file" ]] && continue - + # Skip non-app JSON files basename_file=$(basename "$json_file") case "$basename_file" in From a7a4c3457c77bacef7524a56e71e93322d9c4a5e Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:19:39 +0100 Subject: [PATCH 159/228] Update update-github-versions.yml --- .github/workflows/update-github-versions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-github-versions.yml b/.github/workflows/update-github-versions.yml index 924a4c3c1..f907bc679 100644 --- a/.github/workflows/update-github-versions.yml +++ b/.github/workflows/update-github-versions.yml @@ -1,4 +1,4 @@ -name: Update GitHub Versions +name: Update GitHub Versions (New) on: workflow_dispatch: From 6269bc253a86a06f09d29960627ef7389a97b10e Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:20:52 +0100 Subject: [PATCH 160/228] fix: correct repository name in workflow condition --- .github/workflows/update-github-versions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-github-versions.yml b/.github/workflows/update-github-versions.yml index f907bc679..f12fb4157 100644 --- a/.github/workflows/update-github-versions.yml +++ b/.github/workflows/update-github-versions.yml @@ -15,7 +15,7 @@ env: jobs: update-github-versions: - if: github.repository == 'community-scripts/ProxmoxVE' + if: github.repository == 'community-scripts/ProxmoxVED' runs-on: ubuntu-latest steps: From 12fa5e7d121d77d195e372173ab5464197067458 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:22:47 +0100 Subject: [PATCH 161/228] fix: detect new untracked files in change check --- .github/workflows/update-github-versions.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-github-versions.yml b/.github/workflows/update-github-versions.yml index f12fb4157..c7011ab45 100644 --- a/.github/workflows/update-github-versions.yml +++ b/.github/workflows/update-github-versions.yml @@ -153,7 +153,15 @@ jobs: - name: Check for changes id: check-changes run: | - if git diff --quiet "$VERSIONS_FILE" 2>/dev/null; then + # Check if file is new (untracked) or has changes + if [[ ! -f "$VERSIONS_FILE" ]]; then + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "Versions file was not created" + elif ! git ls-files --error-unmatch "$VERSIONS_FILE" &>/dev/null; then + # File exists but is not tracked - it's new + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "New file created: $VERSIONS_FILE" + elif git diff --quiet "$VERSIONS_FILE" 2>/dev/null; then echo "changed=false" >> "$GITHUB_OUTPUT" echo "No changes detected" else From 332eab5fc2a22e7afaf6fcb83432319216df8326 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:28:54 +0100 Subject: [PATCH 162/228] fix: improve output formatting and remove self-approve step --- .github/workflows/update-github-versions.yml | 25 ++++---------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/.github/workflows/update-github-versions.yml b/.github/workflows/update-github-versions.yml index c7011ab45..9f3680a8c 100644 --- a/.github/workflows/update-github-versions.yml +++ b/.github/workflows/update-github-versions.yml @@ -77,8 +77,6 @@ jobs: install_script="install/${slug}-install.sh" [[ ! -f "$install_script" ]] && continue - echo -n "[$slug] " - # Look for fetch_and_deploy_gh_release calls # Pattern: fetch_and_deploy_gh_release "app" "owner/repo" ["mode"] ["version"] while IFS= read -r line; do @@ -98,7 +96,6 @@ jobs: if [[ -n "$pinned_version" && "$pinned_version" != "latest" ]]; then is_pinned=true target_version="$pinned_version" - echo -n "(pinned: $pinned_version) " fi # Fetch version from GitHub @@ -109,10 +106,9 @@ jobs: version=$(echo "$response" | jq -r '.tag_name') date=$(echo "$response" | jq -r '.published_at // empty') add_version "$slug" "$repo" "$version" "true" "$date" - echo "✓ $version (pinned)" + echo "[$slug] ✓ $version (pinned)" else - # Pinned version not found, skip - echo "⚠ pinned version $target_version not found" + echo "[$slug] ⚠ pinned version $target_version not found" fi else # Fetch latest release @@ -121,15 +117,15 @@ jobs: version=$(echo "$response" | jq -r '.tag_name') date=$(echo "$response" | jq -r '.published_at // empty') add_version "$slug" "$repo" "$version" "false" "$date" - echo "✓ $version" + echo "[$slug] ✓ $version" else # Try tags as fallback version=$(gh api "repos/${repo}/tags" --jq '.[0].name // empty' 2>/dev/null || echo "") if [[ -n "$version" ]]; then add_version "$slug" "$repo" "$version" "false" "" - echo "✓ $version (from tags)" + echo "[$slug] ✓ $version (from tags)" else - echo "⚠ no version found" + echo "[$slug] ⚠ no version found" fi fi fi @@ -220,14 +216,3 @@ jobs: --head "$BRANCH_NAME" \ --label "automated pr" fi - - - name: Auto-approve PR - if: steps.check-changes.outputs.changed == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - BRANCH_NAME="automated/update-github-versions-$(date +%Y%m%d)" - pr_number=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number') - if [[ -n "$pr_number" ]]; then - gh pr review "$pr_number" --approve - fi From b858cafad7e3047c1fd729ebe5c1b711dca7d0ec Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Wed, 28 Jan 2026 13:42:39 +0100 Subject: [PATCH 163/228] Update install/ebusd-install.sh Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- install/ebusd-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh index 560bf362a..187cc2265 100644 --- a/install/ebusd-install.sh +++ b/install/ebusd-install.sh @@ -22,7 +22,7 @@ setup_deb822_repo \ msg_info "Installing ebusd" $STD apt install -y ebusd -$STD systemctl enable ebusd +systemctl enable -q --now ebusd msg_ok "Installed ebusd" cat <~/ebusd-configuation-instructions.txt From 4d349d371b8d2cd6f9d131a0bc70e7d7fb3a5200 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:26:56 +0100 Subject: [PATCH 164/228] Create vikunja-install.sh --- install/vikunja-install.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 install/vikunja-install.sh diff --git a/install/vikunja-install.sh b/install/vikunja-install.sh new file mode 100644 index 000000000..e17608e96 --- /dev/null +++ b/install/vikunja-install.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://vikunja.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 install -y make +msg_ok "Installed Dependencies" + +fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" + +msg_info "Setup Vikunja" +sed -i 's|^ timezone: .*| timezone: UTC|' /etc/vikunja/config.yml +sed -i 's|"./vikunja.db"|"/etc/vikunja/vikunja.db"|' /etc/vikunja/config.yml +sed -i 's|./files|/etc/vikunja/files|' /etc/vikunja/config.yml +systemctl enable -q --now vikunja +msg_ok "Setting up Vikunja" + +motd_ssh +customize +cleanup_lxc From ae0f0680c4f0cb27d3f3d97a6b13f6f59dbd3031 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:33:29 +0100 Subject: [PATCH 165/228] Create vikunja.sh --- ct/vikunja.sh | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 ct/vikunja.sh diff --git a/ct/vikunja.sh b/ct/vikunja.sh new file mode 100644 index 000000000..ab5287bbf --- /dev/null +++ b/ct/vikunja.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://vikunja.io/ + +APP="Vikunja" +var_tags="${var_tags:-todo-app}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +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/vikunja ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "vikunja" "go-vikunja/vikunja" "latest"; then + msg_info "Stopping Service" + systemctl stop vikunja + msg_ok "Stopped Service" + + fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" + + msg_info "Starting Service" + systemctl start vikunja + msg_ok "Started Service" + msg_ok "Updated successfully!" + fi + exit 0 +} + +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}:3456${CL}" From 454bd021af79503cce10875fa13e5e18b49a90e9 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:51:47 +0100 Subject: [PATCH 166/228] Update vikunja-install.sh --- install/vikunja-install.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/install/vikunja-install.sh b/install/vikunja-install.sh index e17608e96..a227e028b 100644 --- a/install/vikunja-install.sh +++ b/install/vikunja-install.sh @@ -13,13 +13,10 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -# $STD apt install -y make -msg_ok "Installed Dependencies" - fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" msg_info "Setup Vikunja" +sed -i -E 's/^# cors:/cors:/; s/^[[:space:]]*# enable:[[:space:]]*true/ enable: false/' /etc/vikunja/config.yml sed -i 's|^ timezone: .*| timezone: UTC|' /etc/vikunja/config.yml sed -i 's|"./vikunja.db"|"/etc/vikunja/vikunja.db"|' /etc/vikunja/config.yml sed -i 's|./files|/etc/vikunja/files|' /etc/vikunja/config.yml From dc379e05ed5f1a133cdf04b3756252826ef035c8 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:52:08 +0100 Subject: [PATCH 167/228] Update vikunja-install.sh --- install/vikunja-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/vikunja-install.sh b/install/vikunja-install.sh index a227e028b..5f25288e6 100644 --- a/install/vikunja-install.sh +++ b/install/vikunja-install.sh @@ -15,13 +15,13 @@ update_os fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" -msg_info "Setup Vikunja" +msg_info "Setting up Vikunja" sed -i -E 's/^# cors:/cors:/; s/^[[:space:]]*# enable:[[:space:]]*true/ enable: false/' /etc/vikunja/config.yml sed -i 's|^ timezone: .*| timezone: UTC|' /etc/vikunja/config.yml sed -i 's|"./vikunja.db"|"/etc/vikunja/vikunja.db"|' /etc/vikunja/config.yml sed -i 's|./files|/etc/vikunja/files|' /etc/vikunja/config.yml systemctl enable -q --now vikunja -msg_ok "Setting up Vikunja" +msg_ok "Set up Vikunja" motd_ssh customize From 293a5a8340bc4c6e8f04d3a6dfb326b1c331ebd9 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:59:32 +0100 Subject: [PATCH 168/228] test --- ct/authelia.sh | 48 ++++++++++++++++ install/authelia-install.sh | 107 ++++++++++++++++++++++++++++++++++++ misc/build.func | 18 ++++++ 3 files changed, 173 insertions(+) create mode 100644 ct/authelia.sh create mode 100644 install/authelia-install.sh diff --git a/ct/authelia.sh b/ct/authelia.sh new file mode 100644 index 000000000..0831c4db1 --- /dev/null +++ b/ct/authelia.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: thost96 (thost96) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.authelia.com/ + +APP="Authelia" +var_tags="${var_tags:-authenticator}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +base_settings + +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /etc/authelia/ ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "authelia" "authelia/authelia"; then + $STD apt update + $STD apt -y upgrade + fetch_and_deploy_gh_release "authelia" "authelia/authelia" "binary" + msg_ok "Updated successfully!" + 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}:9091 or https://auth.YOURDOMAIN ${CL}" diff --git a/install/authelia-install.sh b/install/authelia-install.sh new file mode 100644 index 000000000..010be6cab --- /dev/null +++ b/install/authelia-install.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: thost96 (thost96) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.authelia.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "authelia" "authelia/authelia" "binary" + +MAX_ATTEMPTS=3 +attempt=0 +while true; do + attempt=$((attempt + 1)) + read -rp "${TAB3}Enter your domain or IP (ex. example.com or 192.168.1.100): " DOMAIN + if [[ -z "$DOMAIN" ]]; then + if ((attempt >= MAX_ATTEMPTS)); then + DOMAIN="${LOCAL_IP:-localhost}" + msg_warn "Using fallback: $DOMAIN" + break + fi + msg_warn "Domain cannot be empty! (Attempt $attempt/$MAX_ATTEMPTS)" + elif [[ "$DOMAIN" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + valid_ip=true + IFS='.' read -ra octets <<< "$DOMAIN" + for octet in "${octets[@]}"; do + if ((octet > 255)); then + valid_ip=false + break + fi + done + if $valid_ip; then + break + else + msg_warn "Invalid IP address!" + fi + elif [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]; then + break + else + msg_warn "Invalid domain format!" + fi +done +msg_info "Setting Authelia up" +touch /etc/authelia/emails.txt +JWT_SECRET=$(openssl rand -hex 64) +SESSION_SECRET=$(openssl rand -hex 64) +STORAGE_KEY=$(openssl rand -hex 64) + +if [[ "$DOMAIN" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + AUTHELIA_URL="https://${DOMAIN}:9091" +else + AUTHELIA_URL="https://auth.${DOMAIN}" +fi +echo "$AUTHELIA_URL" > /etc/authelia/.authelia_url + +cat </etc/authelia/users.yml +users: + authelia: + disabled: false + displayname: "Authelia Admin" + password: "\$argon2id\$v=19\$m=65536,t=3,p=4\$ZBopMzXrzhHXPEZxRDVT2w\$SxWm96DwhOsZyn34DLocwQEIb4kCDsk632PuiMdZnig" + groups: [] +EOF +cat </etc/authelia/configuration.yml +authentication_backend: + file: + path: /etc/authelia/users.yml +access_control: + default_policy: one_factor +session: + secret: "${SESSION_SECRET}" + name: 'authelia_session' + same_site: 'lax' + inactivity: '5m' + expiration: '1h' + remember_me: '1M' + cookies: + - domain: "${DOMAIN}" + authelia_url: "${AUTHELIA_URL}" +storage: + encryption_key: "${STORAGE_KEY}" + local: + path: /etc/authelia/db.sqlite +identity_validation: + reset_password: + jwt_secret: "${JWT_SECRET}" + jwt_lifespan: '5 minutes' + jwt_algorithm: 'HS256' +notifier: + filesystem: + filename: /etc/authelia/emails.txt +EOF +touch /etc/authelia/emails.txt +chown -R authelia:authelia /etc/authelia +systemctl enable -q --now authelia +msg_ok "Authelia Setup completed" + +motd_ssh +customize +cleanup_lxc diff --git a/misc/build.func b/misc/build.func index ffdd4223a..051c5f4b8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3844,6 +3844,24 @@ EOF fix_gpu_gids + # Fix Debian 13 LXC template bug where / is owned by nobody:nogroup + # This causes systemd-tmpfiles to fail with "unsafe path transition" errors + # We need to fix this from the host before any package installation + if [[ "$var_os" == "debian" && "$var_version" == "13" ]]; then + # Stop container, fix ownership, restart + pct stop "$CTID" >/dev/null 2>&1 || true + sleep 1 + # Get the actual rootfs path from pct mount + local rootfs_path + rootfs_path=$(pct mount "$CTID" 2>/dev/null | grep -oP 'mounted at \K.*' || echo "") + if [[ -n "$rootfs_path" && -d "$rootfs_path" ]]; then + chown root:root "$rootfs_path" 2>/dev/null || true + fi + pct unmount "$CTID" >/dev/null 2>&1 || true + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + fi + # Continue with standard container setup msg_info "Customizing LXC Container" From ae8d5fa9e44ed89f2053d1f48664e5fd22ddc5cb Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 28 Jan 2026 15:02:33 +0100 Subject: [PATCH 169/228] LanguageTool: prevent input of multiple language codes --- install/languagetool-install.sh | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/install/languagetool-install.sh b/install/languagetool-install.sh index 2797151fc..c3285a716 100644 --- a/install/languagetool-install.sh +++ b/install/languagetool-install.sh @@ -27,8 +27,32 @@ mv /opt/LanguageTool-*/ /opt/LanguageTool/ download_file "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin" /opt/lid.176.bin msg_ok "" -read -r -p "${TAB3}Enter language code (en, de, es, fr, nl) to download ngrams or press ENTER to skip: " lang_code ngram_dir="" +lang_code="" +max_attempts=3 +attempt=0 + +while [[ $attempt -lt $max_attempts ]]; do + read -r -p "${TAB3}Enter language code (en, de, es, fr, nl) to download ngrams or press ENTER to skip: " lang_code + + if [[ -z "$lang_code" ]]; then + break + fi + + if [[ "$lang_code" =~ [[:space:]] ]]; then + ((attempt++)) + remaining=$((max_attempts - attempt)) + if [[ $remaining -gt 0 ]]; then + msg_error "Please enter only ONE language code. You have $remaining attempt(s) remaining." + else + msg_error "Maximum attempts reached. Continuing without ngrams." + lang_code="" + fi + continue + fi + break +done + if [[ -n "$lang_code" ]]; then if [[ "$lang_code" =~ ^(en|de|es|fr|nl)$ ]]; then msg_info "Searching for $lang_code ngrams..." From b72bb88f5abd68abd9d009dc6b827add4c9ecadd Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 28 Jan 2026 15:11:50 +0100 Subject: [PATCH 170/228] LanguageTool: fix msg block --- install/languagetool-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/languagetool-install.sh b/install/languagetool-install.sh index c3285a716..d4b486b6e 100644 --- a/install/languagetool-install.sh +++ b/install/languagetool-install.sh @@ -25,7 +25,7 @@ download_file "https://languagetool.org/download/LanguageTool-stable.zip" /tmp/L unzip -q /tmp/LanguageTool-stable.zip -d /opt mv /opt/LanguageTool-*/ /opt/LanguageTool/ download_file "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin" /opt/lid.176.bin -msg_ok "" +msg_ok "Setup LanguageTool" ngram_dir="" lang_code="" From 834e0244a521158082e75753d850f881598d9045 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:17:19 +0100 Subject: [PATCH 171/228] Update vikunja.sh --- ct/vikunja.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index ab5287bbf..7eb1f5cd1 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -28,6 +28,23 @@ function update_script() { exit fi + RELEASE="$(cat /opt/Vikunja_version 2>/dev/null || true)" + if [[ "$RELEASE" == "unstable" ]] || dpkg --compare-versions "$RELEASE" lt "1.0.0"; then + msg_warn "You are upgrading from Vikunja '$RELEASE'." + msg_warn "This requires MANUAL config changes in /etc/vikunja/config.yml." + msg_warn "See: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes" + + read -rp "Continue with update? (y/yes to proceed): " -t 30 CONFIRM1 || exit 1 + [[ "$CONFIRM1" =~ ^([yY]|[yY][eE][sS])$ ]] || exit 0 + + echo + msg_warn "Vikunja may not start after the update until you manually adjust the config." + msg_warn "Details: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes" + + read -rp "Acknowledge and continue? (y/yes): " -t 30 CONFIRM2 || exit 1 + [[ "$CONFIRM2" =~ ^([yY]|[yY][eE][sS])$ ]] || exit 0 + fi + if check_for_gh_release "vikunja" "go-vikunja/vikunja" "latest"; then msg_info "Stopping Service" systemctl stop vikunja From 3e1a942d27df97f14805c34a177ebc90e231e1a5 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:40:51 +0100 Subject: [PATCH 172/228] Update vikunja.sh --- ct/vikunja.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index 7eb1f5cd1..fdd97dce9 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -28,21 +28,21 @@ function update_script() { exit fi - RELEASE="$(cat /opt/Vikunja_version 2>/dev/null || true)" - if [[ "$RELEASE" == "unstable" ]] || dpkg --compare-versions "$RELEASE" lt "1.0.0"; then + RELEASE="$( [[ -f "$HOME/.vikunja" ]] && cat "~/.vikunja" 2>/dev/null || [[ -f /opt/Vikunja_version ]] && cat /opt/Vikunja_version 2>/dev/null)" + if [[ "$RELEASE" == "unstable" ]] || { [[ -n "$RELEASE" ]] && dpkg --compare-versions "$RELEASE" lt "1.0.0"; }; then msg_warn "You are upgrading from Vikunja '$RELEASE'." msg_warn "This requires MANUAL config changes in /etc/vikunja/config.yml." msg_warn "See: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes" - read -rp "Continue with update? (y/yes to proceed): " -t 30 CONFIRM1 || exit 1 - [[ "$CONFIRM1" =~ ^([yY]|[yY][eE][sS])$ ]] || exit 0 + read -rp "Continue with update? (y to proceed): " -t 30 CONFIRM1 || exit 1 + [[ "$CONFIRM1" =~ ^[yY]$ ]] || exit 0 echo msg_warn "Vikunja may not start after the update until you manually adjust the config." msg_warn "Details: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes" - read -rp "Acknowledge and continue? (y/yes): " -t 30 CONFIRM2 || exit 1 - [[ "$CONFIRM2" =~ ^([yY]|[yY][eE][sS])$ ]] || exit 0 + read -rp "Acknowledge and continue? (y): " -t 30 CONFIRM2 || exit 1 + [[ "$CONFIRM2" =~ ^[yY]$ ]] || exit 0 fi if check_for_gh_release "vikunja" "go-vikunja/vikunja" "latest"; then From cc6e705614a3b64fa7d31ccb25e49fe5c8dc5ba5 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:41:51 +0100 Subject: [PATCH 173/228] Update vikunja.sh --- ct/vikunja.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index fdd97dce9..4639ec678 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - RELEASE="$( [[ -f "$HOME/.vikunja" ]] && cat "~/.vikunja" 2>/dev/null || [[ -f /opt/Vikunja_version ]] && cat /opt/Vikunja_version 2>/dev/null)" + RELEASE="$( [[ -f "$HOME/.vikunja" ]] && cat "$HOME/.vikunja" 2>/dev/null || [[ -f /opt/Vikunja_version ]] && cat /opt/Vikunja_version 2>/dev/null)" if [[ "$RELEASE" == "unstable" ]] || { [[ -n "$RELEASE" ]] && dpkg --compare-versions "$RELEASE" lt "1.0.0"; }; then msg_warn "You are upgrading from Vikunja '$RELEASE'." msg_warn "This requires MANUAL config changes in /etc/vikunja/config.yml." From b49ef1899f68cc88a1471a39f1268f6b45610762 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:49:02 +0100 Subject: [PATCH 174/228] Update vikunja.sh --- ct/vikunja.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index 4639ec678..06b8c8613 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - RELEASE="$( [[ -f "$HOME/.vikunja" ]] && cat "$HOME/.vikunja" 2>/dev/null || [[ -f /opt/Vikunja_version ]] && cat /opt/Vikunja_version 2>/dev/null)" + RELEASE="$( [[ -f "$HOME/.vikunja" ]] && cat "$HOME/.vikunja" 2>/dev/null || [[ -f /opt/Vikunja_version ]] && cat /opt/Vikunja_version 2>/dev/null || true)" if [[ "$RELEASE" == "unstable" ]] || { [[ -n "$RELEASE" ]] && dpkg --compare-versions "$RELEASE" lt "1.0.0"; }; then msg_warn "You are upgrading from Vikunja '$RELEASE'." msg_warn "This requires MANUAL config changes in /etc/vikunja/config.yml." From 2f5ed17953bfc5d350eef02b31e860d3423ce488 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:08:12 +0100 Subject: [PATCH 175/228] fix --- ct/vikunja.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index 06b8c8613..4ddbd9a47 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -50,7 +50,7 @@ function update_script() { systemctl stop vikunja msg_ok "Stopped Service" - fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" + fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" msg_info "Starting Service" systemctl start vikunja From e8e98f3c678e390751283e4602d0a1bd11983ae2 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:08:27 +0100 Subject: [PATCH 176/228] Update vikunja-install.sh --- install/vikunja-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/vikunja-install.sh b/install/vikunja-install.sh index 5f25288e6..44fc31999 100644 --- a/install/vikunja-install.sh +++ b/install/vikunja-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" +fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" msg_info "Setting up Vikunja" sed -i -E 's/^# cors:/cors:/; s/^[[:space:]]*# enable:[[:space:]]*true/ enable: false/' /etc/vikunja/config.yml From 6475e513c59d7faea97bde8f1676860c9264c841 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:11:41 +0000 Subject: [PATCH 177/228] Delete nodecast-tv (ct) after migration to ProxmoxVE (#1350) Co-authored-by: github-actions[bot] --- ct/nodecast-tv.sh | 60 --------------------------- frontend/public/json/nodecast-tv.json | 35 ---------------- install/nodecast-tv-install.sh | 50 ---------------------- 3 files changed, 145 deletions(-) delete mode 100644 ct/nodecast-tv.sh delete mode 100644 frontend/public/json/nodecast-tv.json delete mode 100644 install/nodecast-tv-install.sh diff --git a/ct/nodecast-tv.sh b/ct/nodecast-tv.sh deleted file mode 100644 index 53ea93dd1..000000000 --- a/ct/nodecast-tv.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2026 community-scripts ORG -# Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/technomancer702/nodecast-tv - -APP="nodecast-tv" -var_tags="${var_tags:-media}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" -var_gpu="${var_gpu:-yes}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/nodecast-tv ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "nodecast-tv" "technomancer702/nodecast-tv"; then - msg_info "Stopping Service" - systemctl stop nodecast-tv - msg_ok "Stopped Service" - - fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" - - msg_info "Updating Modules" - cd /opt/nodecast-tv - $STD npm install - msg_ok "Updated Modules" - - msg_info "Starting Service" - systemctl start nodecast-tv - msg_ok "Started Service" - msg_ok "Updated successfully!" - 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/frontend/public/json/nodecast-tv.json b/frontend/public/json/nodecast-tv.json deleted file mode 100644 index 8d3f0e1fb..000000000 --- a/frontend/public/json/nodecast-tv.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "nodecast-tv", - "slug": "nodecast-tv", - "categories": [ - 13 - ], - "date_created": "2026-01-14", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/technomancer702/nodecast-tv/blob/main/README.md", - "website": "https://github.com/technomancer702/nodecast-tv", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nodecast-tv.webp", - "config_path": "", - "description": "nodecast-tv is a modern, web-based IPTV player featuring Live TV, EPG, Movies (VOD), and Series support. Built with performance and user experience in mind.", - "install_methods": [ - { - "type": "default", - "script": "ct/nodecast-tv.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/nodecast-tv-install.sh b/install/nodecast-tv-install.sh deleted file mode 100644 index 77a57e8a1..000000000 --- a/install/nodecast-tv-install.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/technomancer702/nodecast-tv - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" -setup_nodejs - -msg_info "Installing Dependencies" -$STD apt install -y ffmpeg -msg_ok "Installed Dependencies" - -msg_info "Installing Modules" -cd /opt/nodecast-tv -$STD npm install -msg_ok "Installed Modules" - -msg_info "Creating Service" -cat </etc/systemd/system/nodecast-tv.service -[Unit] -Description=nodecast-tv -After=network.target -Wants=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/nodecast-tv -ExecStart=/bin/npm run dev -Restart=on-failure -RestartSec=10 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now nodecast-tv -msg_ok "Created Service" - -motd_ssh -customize -cleanup_lxc From e9d3f47336a0a02429413ae6108ee5b3b05cb5a6 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:15:00 +0100 Subject: [PATCH 178/228] Update vikunja.sh --- ct/vikunja.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index 4ddbd9a47..596f45f88 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -45,7 +45,7 @@ function update_script() { [[ "$CONFIRM2" =~ ^[yY]$ ]] || exit 0 fi - if check_for_gh_release "vikunja" "go-vikunja/vikunja" "latest"; then + if check_for_gh_release "vikunja" "go-vikunja/vikunja"; then msg_info "Stopping Service" systemctl stop vikunja msg_ok "Stopped Service" From 6a5f9c8e2b71645624b86ca5b4b3ba36ea2f0334 Mon Sep 17 00:00:00 2001 From: "GitHub Actions[bot]" Date: Wed, 28 Jan 2026 18:48:06 +0000 Subject: [PATCH 179/228] chore: update github-versions.json Total versions: 19 Pinned versions: 1 Generated: 2026-01-28T18:48:06Z --- frontend/public/json/github-versions.json | 138 ++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 frontend/public/json/github-versions.json diff --git a/frontend/public/json/github-versions.json b/frontend/public/json/github-versions.json new file mode 100644 index 000000000..41eab263d --- /dev/null +++ b/frontend/public/json/github-versions.json @@ -0,0 +1,138 @@ +{ + "generated": "2026-01-28T18:48:06Z", + "versions": [ + { + "slug": "affine", + "repo": "toeverything/AFFiNE", + "version": "v0.25.7", + "pinned": false, + "date": "2025-12-09T04:34:14Z" + }, + { + "slug": "ampache", + "repo": "ampache/ampache", + "version": "7.8.0", + "pinned": false, + "date": "2025-12-22T04:23:45Z" + }, + { + "slug": "databasus", + "repo": "databasus/databasus", + "version": "v3.7.0", + "pinned": false, + "date": "2026-01-28T14:46:28Z" + }, + { + "slug": "ente", + "repo": "ente-io/ente", + "version": "photos-v1.3.0", + "pinned": false, + "date": "2026-01-12T06:33:12Z" + }, + { + "slug": "frigate", + "repo": "blakeblackshear/frigate", + "version": "v0.16.3", + "pinned": false, + "date": "2025-12-06T18:15:00Z" + }, + { + "slug": "hoodik", + "repo": "hudikhq/hoodik", + "version": "v1.8.1", + "pinned": false, + "date": "2025-12-22T20:32:27Z" + }, + { + "slug": "isponsorblocktv", + "repo": "dmunozv04/iSponsorBlockTV", + "version": "v2.6.1", + "pinned": false, + "date": "2025-10-19T17:43:10Z" + }, + { + "slug": "kitchenowl", + "repo": "TomBursch/kitchenowl", + "version": "v0.7.6", + "pinned": false, + "date": "2026-01-24T01:21:14Z" + }, + { + "slug": "minthcm", + "repo": "minthcm/minthcm", + "version": "4.2.2", + "pinned": false, + "date": "2025-10-10T09:37:21Z" + }, + { + "slug": "nextexplorer", + "repo": "vikramsoni2/nextExplorer", + "version": "v2.1.1", + "pinned": false, + "date": "2026-01-24T21:22:09Z" + }, + { + "slug": "nightscout", + "repo": "nightscout/cgm-remote-monitor", + "version": "15.0.3", + "pinned": false, + "date": "2025-05-08T22:12:34Z" + }, + { + "slug": "opencloud", + "repo": "opencloud-eu/opencloud", + "version": "v5.0.0", + "pinned": true, + "date": "2026-01-26T15:58:00Z" + }, + { + "slug": "piler", + "repo": "jsuto/piler", + "version": "piler-1.4.8", + "pinned": false, + "date": "2025-09-24T06:51:38Z" + }, + { + "slug": "pixelfed", + "repo": "pixelfed/pixelfed", + "version": "v0.12.6", + "pinned": false, + "date": "2025-09-03T12:12:04Z" + }, + { + "slug": "romm", + "repo": "RetroAchievements/RALibretro", + "version": "1.8.2", + "pinned": false, + "date": "2026-01-23T17:03:31Z" + }, + { + "slug": "rustypaste", + "repo": "orhun/rustypaste", + "version": "v0.16.1", + "pinned": false, + "date": "2025-03-21T20:44:47Z" + }, + { + "slug": "seer", + "repo": "seerr-team/seerr", + "version": "v2.7.3", + "pinned": false, + "date": "2025-08-14T20:43:46Z" + }, + { + "slug": "shelfmark", + "repo": "FlareSolverr/FlareSolverr", + "version": "v3.4.6", + "pinned": false, + "date": "2025-11-29T02:43:00Z" + }, + { + "slug": "wishlist", + "repo": "cmintey/wishlist", + "version": "v0.59.0", + "pinned": false, + "date": "2026-01-19T16:42:14Z" + } + ] +} From 858b6d8931643a508d192c297eafe3acd35b7574 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:24:50 +0100 Subject: [PATCH 180/228] Update vikunja.sh --- ct/vikunja.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index 4ddbd9a47..596f45f88 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -45,7 +45,7 @@ function update_script() { [[ "$CONFIRM2" =~ ^[yY]$ ]] || exit 0 fi - if check_for_gh_release "vikunja" "go-vikunja/vikunja" "latest"; then + if check_for_gh_release "vikunja" "go-vikunja/vikunja"; then msg_info "Stopping Service" systemctl stop vikunja msg_ok "Stopped Service" From 2fa14e30636102bc5d04070271c497fce595132e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 22:05:19 +0100 Subject: [PATCH 181/228] Update vikunja-install.sh --- install/vikunja-install.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/install/vikunja-install.sh b/install/vikunja-install.sh index 44fc31999..3307fd04c 100644 --- a/install/vikunja-install.sh +++ b/install/vikunja-install.sh @@ -16,10 +16,9 @@ update_os fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" msg_info "Setting up Vikunja" -sed -i -E 's/^# cors:/cors:/; s/^[[:space:]]*# enable:[[:space:]]*true/ enable: false/' /etc/vikunja/config.yml -sed -i 's|^ timezone: .*| timezone: UTC|' /etc/vikunja/config.yml -sed -i 's|"./vikunja.db"|"/etc/vikunja/vikunja.db"|' /etc/vikunja/config.yml -sed -i 's|./files|/etc/vikunja/files|' /etc/vikunja/config.yml +sed -i 's|^# \(service:\)|\1|' /etc/vikunja/config.yml +sed -i "s|^ # \(publicurl: \).*| \1\"http://$LOCAL_IP\"|" /etc/vikunja/config.yml +sed -i "0,/^ # \(timezone: \).*/s|| \1${tz}|" /etc/vikunja/config.yml systemctl enable -q --now vikunja msg_ok "Set up Vikunja" From 5aeaf91868fa5f28bf91bf6cbd9b83fa1ce833b2 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 28 Jan 2026 22:19:45 +0100 Subject: [PATCH 182/228] Update vikunja.sh --- ct/vikunja.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/vikunja.sh b/ct/vikunja.sh index 596f45f88..f199e7721 100644 --- a/ct/vikunja.sh +++ b/ct/vikunja.sh @@ -29,7 +29,7 @@ function update_script() { fi RELEASE="$( [[ -f "$HOME/.vikunja" ]] && cat "$HOME/.vikunja" 2>/dev/null || [[ -f /opt/Vikunja_version ]] && cat /opt/Vikunja_version 2>/dev/null || true)" - if [[ "$RELEASE" == "unstable" ]] || { [[ -n "$RELEASE" ]] && dpkg --compare-versions "$RELEASE" lt "1.0.0"; }; then + if [[ -z "$RELEASE" ]] || [[ "$RELEASE" == "unstable" ]] || dpkg --compare-versions "${RELEASE:-0.0.0}" lt "1.0.0"; then msg_warn "You are upgrading from Vikunja '$RELEASE'." msg_warn "This requires MANUAL config changes in /etc/vikunja/config.yml." msg_warn "See: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes" From b1e6e1452cd3d850124e7adce8f9ea340577d92d Mon Sep 17 00:00:00 2001 From: "GitHub Actions[bot]" Date: Thu, 29 Jan 2026 02:41:19 +0000 Subject: [PATCH 183/228] chore: update github-versions.json Total versions: 19 Pinned versions: 1 Generated: 2026-01-29T02:41:18Z --- frontend/public/json/github-versions.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/public/json/github-versions.json b/frontend/public/json/github-versions.json index 41eab263d..7364cdacf 100644 --- a/frontend/public/json/github-versions.json +++ b/frontend/public/json/github-versions.json @@ -1,5 +1,5 @@ { - "generated": "2026-01-28T18:48:06Z", + "generated": "2026-01-29T02:41:18Z", "versions": [ { "slug": "affine", @@ -32,9 +32,9 @@ { "slug": "frigate", "repo": "blakeblackshear/frigate", - "version": "v0.16.3", + "version": "v0.16.4", "pinned": false, - "date": "2025-12-06T18:15:00Z" + "date": "2026-01-29T00:42:14Z" }, { "slug": "hoodik", From 4aeca93c0b54ce9f5b3837ce1f5ae7d9fcccaf76 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 28 Jan 2026 22:42:13 -0500 Subject: [PATCH 184/228] OpenCloud: bump version to 5.0.1 --- ct/opencloud.sh | 2 +- install/opencloud-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/opencloud.sh b/ct/opencloud.sh index 2f1925421..639ee67db 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -29,7 +29,7 @@ function update_script() { exit fi - RELEASE="v5.0.0" + RELEASE="v5.0.1" if check_for_gh_release "opencloud" "opencloud-eu/opencloud" "${RELEASE}"; then msg_info "Stopping services" systemctl stop opencloud opencloud-wopi diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 39b579429..7d4c9a060 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -57,7 +57,7 @@ echo "$COOLPASS" >~/.coolpass msg_ok "Installed Collabora Online" # OpenCloud -fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.0" "/usr/bin" "opencloud-*-linux-amd64" +fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.1" "/usr/bin" "opencloud-*-linux-amd64" msg_info "Configuring OpenCloud" DATA_DIR="/var/lib/opencloud/" From 40124eae6dbb7599445f5e0a5fb3e6c286e8809f Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:39:55 +0100 Subject: [PATCH 185/228] immich dev --- ct/immich.sh | 400 +++++++++++++++++++++++++++++++ install/immich-install.sh | 488 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 888 insertions(+) create mode 100644 ct/immich.sh create mode 100644 install/immich-install.sh diff --git a/ct/immich.sh b/ct/immich.sh new file mode 100644 index 000000000..067c6f0bd --- /dev/null +++ b/ct/immich.sh @@ -0,0 +1,400 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://immich.app + +APP="immich" +var_tags="${var_tags:-photos}" +var_disk="${var_disk:-20}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-6144}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" +var_gpu="${var_gpu:-yes}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/immich ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if [[ -f /etc/apt/sources.list.d/immich.list ]]; then + msg_error "Wrong Debian version detected!" + msg_error "You must upgrade your LXC to Debian Trixie before updating." + msg_error "Please visit https://github.com/community-scripts/ProxmoxVE/discussions/7726 for details." + echo "${TAB3} If you have upgraded your LXC to Trixie and you still see this message, please open an Issue in the Community-Scripts repo." + exit + fi + + setup_uv + PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')" + NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs + + if [[ ! -f /etc/apt/preferences.d/preferences ]]; then + msg_info "Adding Debian Testing repo" + sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources + cat </etc/apt/preferences.d/preferences +Package: * +Pin: release a=unstable +Pin-Priority: 450 + +Package: * +Pin:release a=testing +Pin-Priority: 450 +EOF + if [[ -f /etc/apt/preferences.d/immich ]]; then + rm /etc/apt/preferences.d/immich + fi + $STD apt update + msg_ok "Added Debian Testing repo" + fi + + if ! dpkg -l "libmimalloc3" | grep -q '3.1' || ! dpkg -l "libde265-dev" | grep -q '1.0.16'; then + msg_info "Installing/upgrading Testing repo packages" + $STD apt install -t testing libmimalloc3 libde265-dev -y + msg_ok "Installed/upgraded Testing repo packages" + fi + + if [[ ! -f /etc/apt/sources.list.d/mise.list ]]; then + msg_info "Installing Mise" + curl -fSs https://mise.jdx.dev/gpg-key.pub | tee /etc/apt/keyrings/mise-archive-keyring.pub 1>/dev/null + echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.pub arch=amd64] https://mise.jdx.dev/deb stable main" >/etc/apt/sources.list.d/mise.list + $STD apt update + $STD apt install -y mise + msg_ok "Installed Mise" + fi + + STAGING_DIR=/opt/staging + BASE_DIR=${STAGING_DIR}/base-images + SOURCE_DIR=${STAGING_DIR}/image-source + cd /tmp + if [[ -f ~/.intel_version ]]; then + curl -fsSLO https://raw.githubusercontent.com/immich-app/base-images/refs/heads/main/server/Dockerfile + readarray -t INTEL_URLS < <( + sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $2}' + sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}' + ) + INTEL_RELEASE="$(grep "intel-opencl-icd_" ./Dockerfile | awk -F '_' '{print $2}')" + if [[ "$INTEL_RELEASE" != "$(cat ~/.intel_version)" ]]; then + msg_info "Updating Intel iGPU dependencies" + for url in "${INTEL_URLS[@]}"; do + curl -fsSLO "$url" + done + $STD apt-mark unhold libigdgmm12 + $STD apt install -y ./libigdgmm12*.deb + rm ./libigdgmm12*.deb + $STD apt install -y ./*.deb + rm ./*.deb + $STD apt-mark hold libigdgmm12 + dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version + msg_ok "Intel iGPU dependencies updated" + fi + rm ./Dockerfile + fi + if [[ -f ~/.immich_library_revisions ]]; then + libraries=("libjxl" "libheif" "libraw" "imagemagick" "libvips") + cd "$BASE_DIR" + msg_info "Checking for updates to custom image-processing libraries" + $STD git pull + for library in "${libraries[@]}"; do + compile_"$library" + done + msg_ok "Image-processing libraries up to date" + fi + + RELEASE="2.5.0" + if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then + msg_info "Stopping Services" + systemctl stop immich-web + systemctl stop immich-ml + msg_ok "Stopped Services" + VCHORD_RELEASE="0.5.3" + if [[ ! -f ~/.vchord_version ]] || [[ "$VCHORD_RELEASE" != "$(cat ~/.vchord_version)" ]]; then + msg_info "Upgrading VectorChord" + curl -fsSL "https://github.com/tensorchord/vectorchord/releases/download/${VCHORD_RELEASE}/postgresql-16-vchord_${VCHORD_RELEASE}-1_amd64.deb" -o vchord.deb + $STD apt install -y ./vchord.deb + systemctl restart postgresql + $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vector UPDATE;" + $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;" + $STD sudo -u postgres psql -d immich -c "REINDEX INDEX face_index;" + $STD sudo -u postgres psql -d immich -c "REINDEX INDEX clip_index;" + echo "$VCHORD_RELEASE" >~/.vchord_version + rm ./vchord.deb + msg_ok "Upgraded VectorChord to v${VCHORD_RELEASE}" + fi + if ! dpkg -l | grep -q ccache; then + $STD apt install -yqq ccache + fi + + INSTALL_DIR="/opt/${APP}" + UPLOAD_DIR="$(sed -n '/^IMMICH_MEDIA_LOCATION/s/[^=]*=//p' /opt/immich/.env)" + SRC_DIR="${INSTALL_DIR}/source" + APP_DIR="${INSTALL_DIR}/app" + PLUGIN_DIR="${APP_DIR}/corePlugin" + ML_DIR="${APP_DIR}/machine-learning" + GEO_DIR="${INSTALL_DIR}/geodata" + + cp "$ML_DIR"/ml_start.sh "$INSTALL_DIR" + if grep -qs "set -a" "$APP_DIR"/bin/start.sh; then + cp "$APP_DIR"/bin/start.sh "$INSTALL_DIR" + else + cat <"$INSTALL_DIR"/start.sh +#!/usr/bin/env bash + +set -a +. ${INSTALL_DIR}/.env +set +a + +/usr/bin/node ${APP_DIR}/dist/main.js "\$@" +EOF + chmod +x "$INSTALL_DIR"/start.sh + fi + + ( + shopt -s dotglob + rm -rf "${APP_DIR:?}"/* + ) + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v${RELEASE}" "$SRC_DIR" + + msg_info "Updating Immich web and microservices" + cd "$SRC_DIR"/server + export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 + export CI=1 + corepack enable + + # server build + export SHARP_IGNORE_GLOBAL_LIBVIPS=true + $STD pnpm --filter immich --frozen-lockfile build + unset SHARP_IGNORE_GLOBAL_LIBVIPS + export SHARP_FORCE_GLOBAL_LIBVIPS=true + $STD pnpm --filter immich --frozen-lockfile --prod --no-optional deploy "$APP_DIR" + cp "$APP_DIR"/package.json "$APP_DIR"/bin + sed -i 's|^start|./start|' "$APP_DIR"/bin/immich-admin + + # openapi & web build + cd "$SRC_DIR" + echo "packageImportMethod: hardlink" >>./pnpm-workspace.yaml + $STD pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install + unset SHARP_FORCE_GLOBAL_LIBVIPS + export SHARP_IGNORE_GLOBAL_LIBVIPS=true + $STD pnpm --filter @immich/sdk --filter immich-web build + cp -a web/build "$APP_DIR"/www + cp LICENSE "$APP_DIR" + + # cli build + $STD pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install + $STD pnpm --filter @immich/sdk --filter @immich/cli build + $STD pnpm --filter @immich/cli --prod --no-optional deploy "$APP_DIR"/cli + cd "$APP_DIR" + mv "$INSTALL_DIR"/start.sh "$APP_DIR"/bin + + # plugins + cd "$SRC_DIR" + $STD mise trust --ignore ./mise.toml + $STD mise trust ./plugins/mise.toml + cd plugins + $STD mise install + $STD mise run build + mkdir -p "$PLUGIN_DIR" + cp -r ./dist "$PLUGIN_DIR"/dist + cp ./manifest.json "$PLUGIN_DIR" + msg_ok "Updated Immich server, web, cli and plugins" + + cd "$SRC_DIR"/machine-learning + mkdir -p "$ML_DIR" && chown -R immich:immich "$ML_DIR" + chown immich:immich ./uv.lock + export VIRTUAL_ENV="${ML_DIR}"/ml-venv + if [[ -f ~/.openvino ]]; then + msg_info "Updating HW-accelerated machine-learning" + $STD uv add --no-sync --optional openvino onnxruntime-openvino==1.20.0 --active -n -p python3.12 --managed-python + $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p python3.12 --managed-python + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.12/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-312-x86_64-linux-gnu.so" + msg_ok "Updated HW-accelerated machine-learning" + else + msg_info "Updating machine-learning" + $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p python3.11 --managed-python + msg_ok "Updated machine-learning" + fi + cd "$SRC_DIR" + cp -a machine-learning/{ann,immich_ml} "$ML_DIR" + mv "$INSTALL_DIR"/ml_start.sh "$ML_DIR" + if [[ -f ~/.openvino ]]; then + sed -i "/intra_op/s/int = 0/int = os.cpu_count() or 0/" "$ML_DIR"/immich_ml/config.py + fi + ln -sf "$APP_DIR"/resources "$INSTALL_DIR" + cd "$APP_DIR" + grep -rl /usr/src | xargs -n1 sed -i "s|\/usr/src|$INSTALL_DIR|g" + grep -rlE "'/build'" | xargs -n1 sed -i "s|'/build'|'$APP_DIR'|g" + sed -i "s@\"/cache\"@\"$INSTALL_DIR/cache\"@g" "$ML_DIR"/immich_ml/config.py + ln -s "${UPLOAD_DIR:-/opt/immich/upload}" "$APP_DIR"/upload + ln -s "${UPLOAD_DIR:-/opt/immich/upload}" "$ML_DIR"/upload + ln -s "$GEO_DIR" "$APP_DIR" + + chown -R immich:immich "$INSTALL_DIR" + systemctl restart immich-ml immich-web + msg_ok "Updated successfully!" + fi + exit +} + +function compile_libjxl() { + SOURCE=${SOURCE_DIR}/libjxl + JPEGLI_LIBJPEG_LIBRARY_SOVERSION="62" + JPEGLI_LIBJPEG_LIBRARY_VERSION="62.3.0" + : "${LIBJXL_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libjxl.json)}" + if [[ "$LIBJXL_REVISION" != "$(grep 'libjxl' ~/.immich_library_revisions | awk '{print $2}')" ]]; then + msg_info "Recompiling libjxl" + if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi + $STD git clone https://github.com/libjxl/libjxl.git "$SOURCE" + cd "$SOURCE" + $STD git reset --hard "$LIBJXL_REVISION" + $STD git submodule update --init --recursive --depth 1 --recommend-shallow + $STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-empty-dht-marker.patch + $STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-icc-warning.patch + mkdir build + cd build + $STD cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=OFF \ + -DJPEGXL_ENABLE_DOXYGEN=OFF \ + -DJPEGXL_ENABLE_MANPAGES=OFF \ + -DJPEGXL_ENABLE_PLUGIN_GIMP210=OFF \ + -DJPEGXL_ENABLE_BENCHMARK=OFF \ + -DJPEGXL_ENABLE_EXAMPLES=OFF \ + -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \ + -DJPEGXL_FORCE_SYSTEM_HWY=ON \ + -DJPEGXL_ENABLE_JPEGLI=ON \ + -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=ON \ + -DJPEGXL_INSTALL_JPEGLI_LIBJPEG=ON \ + -DJPEGXL_ENABLE_PLUGINS=ON \ + -DJPEGLI_LIBJPEG_LIBRARY_SOVERSION="$JPEGLI_LIBJPEG_LIBRARY_SOVERSION" \ + -DJPEGLI_LIBJPEG_LIBRARY_VERSION="$JPEGLI_LIBJPEG_LIBRARY_VERSION" \ + -DLIBJPEG_TURBO_VERSION_NUMBER=2001005 \ + .. + $STD cmake --build . -- -j"$(nproc)" + $STD cmake --install . + ldconfig /usr/local/lib + $STD make clean + cd "$STAGING_DIR" + rm -rf "$SOURCE"/{build,third_party} + sed -i "s/libjxl: .*$/libjxl: $LIBJXL_REVISION/" ~/.immich_library_revisions + msg_ok "Recompiled libjxl" + fi +} + +function compile_libheif() { + SOURCE=${SOURCE_DIR}/libheif + if ! dpkg -l | grep -q libaom; then + $STD apt install -y libaom-dev + local update="required" + fi + : "${LIBHEIF_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libheif.json)}" + if [[ "${update:-}" ]] || [[ "$LIBHEIF_REVISION" != "$(grep 'libheif' ~/.immich_library_revisions | awk '{print $2}')" ]]; then + msg_info "Recompiling libheif" + if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi + $STD git clone https://github.com/strukturag/libheif.git "$SOURCE" + cd "$SOURCE" + $STD git reset --hard "$LIBHEIF_REVISION" + mkdir build + cd build + $STD cmake --preset=release-noplugins \ + -DWITH_DAV1D=ON \ + -DENABLE_PARALLEL_TILE_DECODING=ON \ + -DWITH_LIBSHARPYUV=ON \ + -DWITH_LIBDE265=ON \ + -DWITH_AOM_DECODER=OFF \ + -DWITH_AOM_ENCODER=ON \ + -DWITH_X265=OFF \ + -DWITH_EXAMPLES=OFF \ + .. + $STD make install -j "$(nproc)" + ldconfig /usr/local/lib + $STD make clean + cd "$STAGING_DIR" + rm -rf "$SOURCE"/build + sed -i "s/libheif: .*$/libheif: $LIBHEIF_REVISION/" ~/.immich_library_revisions + msg_ok "Recompiled libheif" + fi +} + +function compile_libraw() { + SOURCE=${SOURCE_DIR}/libraw + : "${LIBRAW_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libraw.json)}" + if [[ "$LIBRAW_REVISION" != "$(grep 'libraw' ~/.immich_library_revisions | awk '{print $2}')" ]]; then + msg_info "Recompiling libraw" + if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi + $STD git clone https://github.com/libraw/libraw.git "$SOURCE" + cd "$SOURCE" + $STD git reset --hard "$LIBRAW_REVISION" + $STD autoreconf --install + $STD ./configure --disable-examples + $STD make -j"$(nproc)" + $STD make install + ldconfig /usr/local/lib + $STD make clean + cd "$STAGING_DIR" + sed -i "s/libraw: .*$/libraw: $LIBRAW_REVISION/" ~/.immich_library_revisions + msg_ok "Recompiled libraw" + fi +} + +function compile_imagemagick() { + SOURCE=$SOURCE_DIR/imagemagick + : "${IMAGEMAGICK_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/imagemagick.json)}" + if [[ "$IMAGEMAGICK_REVISION" != "$(grep 'imagemagick' ~/.immich_library_revisions | awk '{print $2}')" ]] || + ! grep -q 'DMAGICK_LIBRAW' /usr/local/lib/ImageMagick-7*/config-Q16HDRI/configure.xml; then + msg_info "Recompiling ImageMagick" + if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi + $STD git clone https://github.com/ImageMagick/ImageMagick.git "$SOURCE" + cd "$SOURCE" + $STD git reset --hard "$IMAGEMAGICK_REVISION" + $STD ./configure --with-modules CPPFLAGS="-DMAGICK_LIBRAW_VERSION_TAIL=202502" + $STD make -j"$(nproc)" + $STD make install + ldconfig /usr/local/lib + $STD make clean + cd "$STAGING_DIR" + sed -i "s/imagemagick: .*$/imagemagick: $IMAGEMAGICK_REVISION/" ~/.immich_library_revisions + msg_ok "Recompiled ImageMagick" + fi +} + +function compile_libvips() { + SOURCE=$SOURCE_DIR/libvips + : "${LIBVIPS_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libvips.json)}" + if [[ "$LIBVIPS_REVISION" != "$(grep 'libvips' ~/.immich_library_revisions | awk '{print $2}')" ]]; then + msg_info "Recompiling libvips" + if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi + $STD git clone https://github.com/libvips/libvips.git "$SOURCE" + cd "$SOURCE" + $STD git reset --hard "$LIBVIPS_REVISION" + $STD meson setup build --buildtype=release --libdir=lib -Dintrospection=disabled -Dtiff=disabled + cd build + $STD ninja install + ldconfig /usr/local/lib + cd "$STAGING_DIR" + rm -rf "$SOURCE"/build + sed -i "s/libvips: .*$/libvips: $LIBVIPS_REVISION/" ~/.immich_library_revisions + msg_ok "Recompiled libvips" + 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}:2283${CL}" diff --git a/install/immich-install.sh b/install/immich-install.sh new file mode 100644 index 000000000..339f79c60 --- /dev/null +++ b/install/immich-install.sh @@ -0,0 +1,488 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://immich.app + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +echo "" +echo "" +echo -e "🤖 ${BL}Immich Machine Learning Options${CL}" +echo "─────────────────────────────────────────" +echo "Please choose your machine-learning type:" +echo "" +echo " 1) CPU only (default)" +echo " 2) Intel OpenVINO (requires GPU passthrough)" +echo "" + +read -r -p "${TAB3}Select machine-learning type [1]: " ML_TYPE +ML_TYPE="${ML_TYPE:-1}" +if [[ "$ML_TYPE" == "2" ]]; then + msg_info "Installing OpenVINO dependencies" + touch ~/.openvino + $STD apt install -y --no-install-recommends patchelf + tmp_dir=$(mktemp -d) + $STD pushd "$tmp_dir" + curl -fsSLO https://raw.githubusercontent.com/immich-app/base-images/refs/heads/main/server/Dockerfile + readarray -t INTEL_URLS < <( + sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $2}' + sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}' + ) + for url in "${INTEL_URLS[@]}"; do + curl -fsSLO "$url" + done + $STD apt install -y ./libigdgmm12*.deb + rm ./libigdgmm12*.deb + $STD apt install -y ./*.deb + $STD apt-mark hold libigdgmm12 + $STD popd + rm -rf "$tmp_dir" + dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version + msg_ok "Installed OpenVINO dependencies" +fi + +setup_uv + +msg_info "Installing dependencies" +$STD apt install --no-install-recommends -y \ + git \ + redis \ + autoconf \ + build-essential \ + python3-dev \ + automake \ + cmake \ + jq \ + libtool \ + libltdl-dev \ + libgdk-pixbuf-2.0-dev \ + libbrotli-dev \ + libexif-dev \ + libexpat1-dev \ + libglib2.0-dev \ + libgsf-1-dev \ + libjpeg62-turbo-dev \ + libspng-dev \ + liblcms2-dev \ + libopenexr-dev \ + libgif-dev \ + librsvg2-dev \ + libexpat1 \ + libgcc-s1 \ + libgomp1 \ + liblqr-1-0 \ + libltdl7 \ + libopenjp2-7 \ + meson \ + ninja-build \ + pkg-config \ + mesa-utils \ + mesa-va-drivers \ + mesa-vulkan-drivers \ + ocl-icd-libopencl1 \ + tini \ + zlib1g \ + libio-compress-brotli-perl \ + libwebp7 \ + libwebpdemux2 \ + libwebpmux3 \ + libhwy1t64 \ + libdav1d-dev \ + libhwy-dev \ + libwebp-dev \ + libaom-dev \ + ccache + +setup_deb822_repo \ + "jellyfin" \ + "https://repo.jellyfin.org/jellyfin_team.gpg.key" \ + "https://repo.jellyfin.org/debian" \ + "$(get_os_info codename)" +$STD apt install -y jellyfin-ffmpeg7 +ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg +ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe + +# Set permissions for /dev/dri (only in privileged containers and if /dev/dri exists) +if [[ "$CTTYPE" == "0" && -d /dev/dri ]]; then + chgrp video /dev/dri 2>/dev/null || true + chmod 755 /dev/dri 2>/dev/null || true + chmod 660 /dev/dri/* 2>/dev/null || true + $STD adduser "$(id -u -n)" video 2>/dev/null || true + $STD adduser "$(id -u -n)" render 2>/dev/null || true +fi +msg_ok "Dependencies Installed" + +msg_info "Installing Mise" +curl -fSs https://mise.jdx.dev/gpg-key.pub | tee /etc/apt/keyrings/mise-archive-keyring.pub 1>/dev/null +echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.pub arch=amd64] https://mise.jdx.dev/deb stable main" >/etc/apt/sources.list.d/mise.list +$STD apt update +$STD apt install -y mise +msg_ok "Installed Mise" + +msg_info "Configuring Debian Testing Repo" +sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources +cat </etc/apt/preferences.d/preferences +Package: * +Pin: release a=unstable +Pin-Priority: 450 + +Package: * +Pin:release a=testing +Pin-Priority: 450 +EOF +$STD apt update +msg_ok "Configured Debian Testing repo" +msg_info "Installing packages from Debian Testing repo" +$STD apt install -t testing --no-install-recommends -yqq libmimalloc3 libde265-dev +msg_ok "Installed packages from Debian Testing repo" + +PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')" +NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs +PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql + +VCHORD_RELEASE="0.5.3" +msg_info "Installing Vectorchord v${VCHORD_RELEASE}" +curl -fsSL "https://github.com/tensorchord/VectorChord/releases/download/${VCHORD_RELEASE}/postgresql-16-vchord_${VCHORD_RELEASE}-1_amd64.deb" -o vchord.deb +$STD apt install -y ./vchord.deb +rm vchord.deb +echo "$VCHORD_RELEASE" >~/.vchord_version +msg_ok "Installed Vectorchord v${VCHORD_RELEASE}" + +sed -i -e "/^#shared_preload/s/^#//;/^shared_preload/s/''/'vchord.so'/" /etc/postgresql/16/main/postgresql.conf +systemctl restart postgresql.service +PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_GRANT_SUPERUSER="true" PG_DB_SKIP_ALTER_ROLE="true" setup_postgresql_db + +msg_info "Compiling Custom Photo-processing Library (extreme patience)" +LD_LIBRARY_PATH=/usr/local/lib +export LD_RUN_PATH=/usr/local/lib +STAGING_DIR=/opt/staging +BASE_REPO="https://github.com/immich-app/base-images" +BASE_DIR=${STAGING_DIR}/base-images +SOURCE_DIR=${STAGING_DIR}/image-source +$STD git clone -b main "$BASE_REPO" "$BASE_DIR" +mkdir -p "$SOURCE_DIR" + +msg_info "(1/5) Compiling libjxl" +cd "$STAGING_DIR" +SOURCE=${SOURCE_DIR}/libjxl +JPEGLI_LIBJPEG_LIBRARY_SOVERSION="62" +JPEGLI_LIBJPEG_LIBRARY_VERSION="62.3.0" +: "${LIBJXL_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/libjxl.json)}" +$STD git clone https://github.com/libjxl/libjxl.git "$SOURCE" +cd "$SOURCE" +$STD git reset --hard "$LIBJXL_REVISION" +$STD git submodule update --init --recursive --depth 1 --recommend-shallow +$STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-empty-dht-marker.patch +$STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-icc-warning.patch +mkdir build +cd build +$STD cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=OFF \ + -DJPEGXL_ENABLE_DOXYGEN=OFF \ + -DJPEGXL_ENABLE_MANPAGES=OFF \ + -DJPEGXL_ENABLE_PLUGIN_GIMP210=OFF \ + -DJPEGXL_ENABLE_BENCHMARK=OFF \ + -DJPEGXL_ENABLE_EXAMPLES=OFF \ + -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \ + -DJPEGXL_FORCE_SYSTEM_HWY=ON \ + -DJPEGXL_ENABLE_JPEGLI=ON \ + -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=ON \ + -DJPEGXL_INSTALL_JPEGLI_LIBJPEG=ON \ + -DJPEGXL_ENABLE_PLUGINS=ON \ + -DJPEGLI_LIBJPEG_LIBRARY_SOVERSION="$JPEGLI_LIBJPEG_LIBRARY_SOVERSION" \ + -DJPEGLI_LIBJPEG_LIBRARY_VERSION="$JPEGLI_LIBJPEG_LIBRARY_VERSION" \ + -DLIBJPEG_TURBO_VERSION_NUMBER=2001005 \ + .. +$STD cmake --build . -- -j"$(nproc)" +$STD cmake --install . +ldconfig /usr/local/lib +$STD make clean +cd "$STAGING_DIR" +rm -rf "$SOURCE"/{build,third_party} +msg_ok "(1/5) Compiled libjxl" + +msg_info "(2/5) Compiling libheif" +SOURCE=${SOURCE_DIR}/libheif +: "${LIBHEIF_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/libheif.json)}" +$STD git clone https://github.com/strukturag/libheif.git "$SOURCE" +cd "$SOURCE" +$STD git reset --hard "$LIBHEIF_REVISION" +mkdir build +cd build +$STD cmake --preset=release-noplugins \ + -DWITH_DAV1D=ON \ + -DENABLE_PARALLEL_TILE_DECODING=ON \ + -DWITH_LIBSHARPYUV=ON \ + -DWITH_LIBDE265=ON \ + -DWITH_AOM_DECODER=OFF \ + -DWITH_AOM_ENCODER=ON \ + -DWITH_X265=OFF \ + -DWITH_EXAMPLES=OFF \ + .. +$STD make install -j "$(nproc)" +ldconfig /usr/local/lib +$STD make clean +cd "$STAGING_DIR" +rm -rf "$SOURCE"/build +msg_ok "(2/5) Compiled libheif" + +msg_info "(3/5) Compiling libraw" +SOURCE=${SOURCE_DIR}/libraw +: "${LIBRAW_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/libraw.json)}" +$STD git clone https://github.com/libraw/libraw.git "$SOURCE" +cd "$SOURCE" +$STD git reset --hard "$LIBRAW_REVISION" +$STD autoreconf --install +$STD ./configure --disable-examples +$STD make -j"$(nproc)" +$STD make install +ldconfig /usr/local/lib +$STD make clean +cd "$STAGING_DIR" +msg_ok "(3/5) Compiled libraw" + +msg_info "(4/5) Compiling imagemagick" +SOURCE=$SOURCE_DIR/imagemagick +: "${IMAGEMAGICK_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/imagemagick.json)}" +$STD git clone https://github.com/ImageMagick/ImageMagick.git "$SOURCE" +cd "$SOURCE" +$STD git reset --hard "$IMAGEMAGICK_REVISION" +$STD ./configure --with-modules CPPFLAGS="-DMAGICK_LIBRAW_VERSION_TAIL=202502" +$STD make -j"$(nproc)" +$STD make install +ldconfig /usr/local/lib +$STD make clean +cd "$STAGING_DIR" +msg_ok "(4/5) Compiled imagemagick" + +msg_info "(5/5) Compiling libvips" +SOURCE=$SOURCE_DIR/libvips +: "${LIBVIPS_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/libvips.json)}" +$STD git clone https://github.com/libvips/libvips.git "$SOURCE" +cd "$SOURCE" +$STD git reset --hard "$LIBVIPS_REVISION" +$STD meson setup build --buildtype=release --libdir=lib -Dintrospection=disabled -Dtiff=disabled +cd build +$STD ninja install +ldconfig /usr/local/lib +cd "$STAGING_DIR" +rm -rf "$SOURCE"/build +msg_ok "(5/5) Compiled libvips" +{ + echo "imagemagick: $IMAGEMAGICK_REVISION" + echo "libheif: $LIBHEIF_REVISION" + echo "libjxl: $LIBJXL_REVISION" + echo "libraw: $LIBRAW_REVISION" + echo "libvips: $LIBVIPS_REVISION" +} >~/.immich_library_revisions +msg_ok "Custom Photo-processing Libraries Compiled Successfully" + +INSTALL_DIR="/opt/${APPLICATION}" +UPLOAD_DIR="${INSTALL_DIR}/upload" +SRC_DIR="${INSTALL_DIR}/source" +APP_DIR="${INSTALL_DIR}/app" +PLUGIN_DIR="${APP_DIR}/corePlugin" +ML_DIR="${APP_DIR}/machine-learning" +GEO_DIR="${INSTALL_DIR}/geodata" +mkdir -p "$INSTALL_DIR" +mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache} + +fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v2.5.0" "$SRC_DIR" + +msg_info "Installing Immich (patience)" + +cd "$SRC_DIR"/server +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +export CI=1 +corepack enable + +# server build +export SHARP_IGNORE_GLOBAL_LIBVIPS=true +$STD pnpm --filter immich --frozen-lockfile build +unset SHARP_IGNORE_GLOBAL_LIBVIPS +export SHARP_FORCE_GLOBAL_LIBVIPS=true +$STD pnpm --filter immich --frozen-lockfile --prod --no-optional deploy "$APP_DIR" +cp "$APP_DIR"/package.json "$APP_DIR"/bin +sed -i 's|^start|./start|' "$APP_DIR"/bin/immich-admin + +# openapi & web build +cd "$SRC_DIR" +echo "packageImportMethod: hardlink" >>./pnpm-workspace.yaml +$STD pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install +unset SHARP_FORCE_GLOBAL_LIBVIPS +export SHARP_IGNORE_GLOBAL_LIBVIPS=true +$STD pnpm --filter @immich/sdk --filter immich-web build +cp -a web/build "$APP_DIR"/www +cp LICENSE "$APP_DIR" + +# cli build +$STD pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install +$STD pnpm --filter @immich/sdk --filter @immich/cli build +$STD pnpm --filter @immich/cli --prod --no-optional deploy "$APP_DIR"/cli + +# plugins +cd "$SRC_DIR" +$STD mise trust --ignore ./mise.toml +$STD mise trust ./plugins/mise.toml +cd plugins +$STD mise install +$STD mise run build +mkdir -p "$PLUGIN_DIR" +cp -r ./dist "$PLUGIN_DIR"/dist +cp ./manifest.json "$PLUGIN_DIR" +msg_ok "Installed Immich Server, Web and Plugin Components" + +cd "$SRC_DIR"/machine-learning +$STD useradd -U -s /usr/sbin/nologin -r -M -d "$INSTALL_DIR" immich +mkdir -p "$ML_DIR" && chown -R immich:immich "$INSTALL_DIR" +export VIRTUAL_ENV="${ML_DIR}/ml-venv" +if [[ -f ~/.openvino ]]; then + msg_info "Installing HW-accelerated machine-learning" + $STD uv add --no-sync --optional openvino onnxruntime-openvino==1.20.0 --active -n -p python3.12 --managed-python + $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p python3.12 --managed-python + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.12/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-312-x86_64-linux-gnu.so" + msg_ok "Installed HW-accelerated machine-learning" +else + msg_info "Installing machine-learning" + $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p python3.11 --managed-python + msg_ok "Installed machine-learning" +fi +cd "$SRC_DIR" +cp -a machine-learning/{ann,immich_ml} "$ML_DIR" +if [[ -f ~/.openvino ]]; then + sed -i "/intra_op/s/int = 0/int = os.cpu_count() or 0/" "$ML_DIR"/immich_ml/config.py +fi +ln -sf "$APP_DIR"/resources "$INSTALL_DIR" + +cd "$APP_DIR" +grep -rl /usr/src | xargs -n1 sed -i "s|\/usr/src|$INSTALL_DIR|g" +grep -rlE "'/build'" | xargs -n1 sed -i "s|'/build'|'$APP_DIR'|g" +sed -i "s@\"/cache\"@\"$INSTALL_DIR/cache\"@g" "$ML_DIR"/immich_ml/config.py +ln -s "$UPLOAD_DIR" "$APP_DIR"/upload +ln -s "$UPLOAD_DIR" "$ML_DIR"/upload + +msg_info "Installing GeoNames data" +cd "$GEO_DIR" +curl -fsSLZ -O "https://download.geonames.org/export/dump/admin1CodesASCII.txt" \ + -O "https://download.geonames.org/export/dump/admin2Codes.txt" \ + -O "https://download.geonames.org/export/dump/cities500.zip" \ + -O "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson" +unzip -q cities500.zip +date --iso-8601=seconds | tr -d "\n" >geodata-date.txt +rm cities500.zip +cd "$INSTALL_DIR" +ln -s "$GEO_DIR" "$APP_DIR" +msg_ok "Installed GeoNames data" + +mkdir -p /var/log/immich +touch /var/log/immich/{web.log,ml.log} +msg_ok "Installed Immich" + +msg_info "Modifying user, creating env file, scripts & services" +usermod -aG video,render immich + +cat <"${INSTALL_DIR}"/.env +TZ=$(cat /etc/timezone) +IMMICH_VERSION=release +NODE_ENV=production +IMMICH_ALLOW_SETUP=true + +DB_HOSTNAME=127.0.0.1 +DB_USERNAME=${PG_DB_USER} +DB_PASSWORD=${PG_DB_PASS} +DB_DATABASE_NAME=${PG_DB_NAME} +DB_VECTOR_EXTENSION=vectorchord + +REDIS_HOSTNAME=127.0.0.1 +IMMICH_MACHINE_LEARNING_URL=http://127.0.0.1:3003 +MACHINE_LEARNING_CACHE_FOLDER=${INSTALL_DIR}/cache +## - For OpenVINO only - uncomment below to increase +## - inference speed while reducing accuracy +## - Default is FP32 +# MACHINE_LEARNING_OPENVINO_PRECISION=FP16 + +IMMICH_MEDIA_LOCATION=${UPLOAD_DIR} +EOF +cat <"${ML_DIR}"/ml_start.sh +#!/usr/bin/env bash + +cd ${ML_DIR} +. ${VIRTUAL_ENV}/bin/activate + +set -a +. ${INSTALL_DIR}/.env +set +a + +python3 -m immich_ml +EOF +cat <"$APP_DIR"/bin/start.sh +#!/usr/bin/env bash + +set -a +. ${INSTALL_DIR}/.env +set +a + +/usr/bin/node ${APP_DIR}/dist/main.js "\$@" +EOF +chmod +x "$ML_DIR"/ml_start.sh "$APP_DIR"/bin/start.sh +cat </etc/systemd/system/"${APPLICATION}"-web.service +[Unit] +Description=${APPLICATION} Web Service +After=network.target +Requires=redis-server.service +Requires=postgresql.service +Requires=immich-ml.service + +[Service] +Type=simple +User=immich +Group=immich +UMask=0077 +WorkingDirectory=${APP_DIR} +EnvironmentFile=${INSTALL_DIR}/.env +ExecStart=/usr/bin/node ${APP_DIR}/dist/main +Restart=on-failure +SyslogIdentifier=immich-web +StandardOutput=append:/var/log/immich/web.log +StandardError=append:/var/log/immich/web.log + +[Install] +WantedBy=multi-user.target +EOF +cat </etc/systemd/system/"${APPLICATION}"-ml.service +[Unit] +Description=${APPLICATION} Machine-Learning +After=network.target + +[Service] +Type=simple +UMask=0077 +User=immich +Group=immich +WorkingDirectory=${APP_DIR} +EnvironmentFile=${INSTALL_DIR}/.env +ExecStart=${ML_DIR}/ml_start.sh +Restart=on-failure +SyslogIdentifier=immich-machine-learning +StandardOutput=append:/var/log/immich/ml.log +StandardError=append:/var/log/immich/ml.log + +[Install] +WantedBy=multi-user.target +EOF +chown -R immich:immich "$INSTALL_DIR" /var/log/immich +systemctl enable -q --now "$APPLICATION"-ml.service "$APPLICATION"-web.service +msg_ok "Modified user, created env file, scripts and services" + +motd_ssh +customize +cleanup_lxc From 36eaaea69b0ecd42dd0551eae29ba8a7a8b5e254 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:50:12 +0100 Subject: [PATCH 186/228] add TAG mode in tools.func fetch_and_deploy --- ct/immich.sh | 6 +- install/immich-install.sh | 2 +- misc/tools.func | 151 +++++++++++++++++++++++++++++++++++++- 3 files changed, 154 insertions(+), 5 deletions(-) diff --git a/ct/immich.sh b/ct/immich.sh index 067c6f0bd..24a663b47 100644 --- a/ct/immich.sh +++ b/ct/immich.sh @@ -112,8 +112,8 @@ EOF msg_ok "Image-processing libraries up to date" fi - RELEASE="2.5.0" - if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then + RELEASE="v2.5.2" + if check_for_gh_tag "immich" "immich-app/immich" "${RELEASE}"; then msg_info "Stopping Services" systemctl stop immich-web systemctl stop immich-ml @@ -165,7 +165,7 @@ EOF rm -rf "${APP_DIR:?}"/* ) - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v${RELEASE}" "$SRC_DIR" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immich" "immich-app/immich" "tag" "${RELEASE}" "$SRC_DIR" msg_info "Updating Immich web and microservices" cd "$SRC_DIR"/server diff --git a/install/immich-install.sh b/install/immich-install.sh index 339f79c60..6482c4c28 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -296,7 +296,7 @@ GEO_DIR="${INSTALL_DIR}/geodata" mkdir -p "$INSTALL_DIR" mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache} -fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v2.5.0" "$SRC_DIR" +fetch_and_deploy_gh_release "immich" "immich-app/immich" "tag" "v2.5.2" "$SRC_DIR" msg_info "Installing Immich (patience)" diff --git a/misc/tools.func b/misc/tools.func index 1e1b86b5c..70e8c3fa9 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1563,6 +1563,77 @@ check_for_gh_release() { return 1 } +# ------------------------------------------------------------------------------ +# Checks if a pinned GitHub tag exists and compares to local version. +# +# Description: +# - For tags that are NOT releases (e.g., hotfix tags) +# - Checks if the specified tag exists via Git refs API +# - Compares to local cached version (~/.) +# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 +# +# Usage: +# if check_for_gh_tag "immich" "immich-app/immich" "v2.5.2"; then +# # trigger update... +# fi +# +# Notes: +# - Requires explicit tag (no 'latest' support - use check_for_gh_release for that) +# - Sets CHECK_UPDATE_RELEASE to the tag name if update is needed +# ------------------------------------------------------------------------------ +check_for_gh_tag() { + local app="$1" + local source="$2" + local pinned_tag="$3" + local app_lc="${app,,}" + local current_file="$HOME/.${app_lc}" + + if [[ -z "$pinned_tag" ]]; then + msg_error "check_for_gh_tag requires a pinned tag version" + return 1 + fi + + msg_info "Checking for update: ${app} (tag: ${pinned_tag})" + + # DNS check + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 + fi + + ensure_dependencies jq + + # Check if tag exists via Git refs API + local tag_check + tag_check=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/git/refs/tags/${pinned_tag}" 2>/dev/null) + + if [[ $? -ne 0 ]] || [[ -z "$tag_check" ]] || echo "$tag_check" | jq -e '.message' &>/dev/null; then + msg_error "Tag ${pinned_tag} not found in ${source}" + return 1 + fi + + local pin_clean="${pinned_tag#v}" + + # Current installed version + local current="" + if [[ -f "$current_file" ]]; then + current="$(<"$current_file")" + fi + current="${current#v}" + + if [[ "$current" != "$pin_clean" ]]; then + CHECK_UPDATE_RELEASE="$pinned_tag" + msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" + return 0 + fi + + msg_ok "No update available: ${app} is already on version (${current})" + return 1 +} + # ------------------------------------------------------------------------------ # Creates and installs self-signed certificates. # @@ -1694,12 +1765,15 @@ function ensure_usr_local_bin_persist() { # # # 4. Single binary (chmod +x) like Argus, Promtail etc. # fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "0.26.3" "/opt/argus" "Argus-.*linux-amd64" +# +# # 5. Git tag (not a release) - bypasses Release API, fetches tarball directly from tag +# fetch_and_deploy_gh_release "immich" "immich-app/immich" "tag" "v2.5.2" "/opt/immich/source" # ------------------------------------------------------------------------------ function fetch_and_deploy_gh_release() { local app="$1" local repo="$2" - local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile | tag local version="${4:-latest}" local target="${5:-/opt/$app}" local asset_pattern="${6:-}" @@ -1715,6 +1789,81 @@ function fetch_and_deploy_gh_release() { ensure_dependencies jq + ### Tag Mode (bypass Release API) ### + if [[ "$mode" == "tag" ]]; then + if [[ "$version" == "latest" ]]; then + msg_error "Mode 'tag' requires explicit version (not 'latest')" + return 1 + fi + + local tag_name="$version" + [[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name" + + if [[ "$current_version" == "$version" ]]; then + $STD msg_ok "$app is already up-to-date (v$version)" + return 0 + fi + + # DNS check + if ! getent hosts "github.com" &>/dev/null; then + msg_error "DNS resolution failed for github.com – check /etc/resolv.conf or networking" + return 1 + fi + + local tmpdir + tmpdir=$(mktemp -d) || return 1 + + msg_info "Fetching GitHub tag: $app ($tag_name)" + + local safe_version="${version//@/_}" + safe_version="${safe_version//\//_}" + local filename="${app_lc}-${safe_version}.tar.gz" + local download_success=false + + # For tags with special characters (@, /), use codeload.github.com + if [[ "$tag_name" =~ [@/] ]]; then + local codeload_encoded="${tag_name//@/%40}" + local codeload_url="https://codeload.github.com/$repo/tar.gz/refs/tags/$codeload_encoded" + if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$codeload_url"; then + download_success=true + fi + else + local direct_tarball_url="https://github.com/$repo/archive/refs/tags/${tag_name}.tar.gz" + if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$direct_tarball_url"; then + download_success=true + fi + fi + + if [[ "$download_success" != "true" ]]; then + msg_error "Download failed for $app ($tag_name)" + rm -rf "$tmpdir" + return 1 + fi + + mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* + fi + + tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { + msg_error "Failed to extract tarball" + rm -rf "$tmpdir" + return 1 + } + + local unpack_dir + unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) + + shopt -s dotglob nullglob + cp -r "$unpack_dir"/* "$target/" + shopt -u dotglob nullglob + + echo "$version" >"$version_file" + msg_ok "Deployed: $app ($version)" + rm -rf "$tmpdir" + return 0 + fi + local api_url="https://api.github.com/repos/$repo/releases" [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" local header=() From 45f1e73ae9d052f3c797411ec8fde06601f74af9 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:13:11 +0100 Subject: [PATCH 187/228] Update OpenVINO install for Python 3.13 and add workaround Switches HW-accelerated machine-learning setup to use Python 3.13 and updates related paths. Adds a workaround for onnxruntime-openvino 1.23.x crash by setting MACHINE_LEARNING_OPENVINO_NUM_THREADS to nproc, referencing Immich PR #11240. --- install/immich-install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/install/immich-install.sh b/install/immich-install.sh index 6482c4c28..d0625e4cf 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -347,9 +347,8 @@ mkdir -p "$ML_DIR" && chown -R immich:immich "$INSTALL_DIR" export VIRTUAL_ENV="${ML_DIR}/ml-venv" if [[ -f ~/.openvino ]]; then msg_info "Installing HW-accelerated machine-learning" - $STD uv add --no-sync --optional openvino onnxruntime-openvino==1.20.0 --active -n -p python3.12 --managed-python - $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p python3.12 --managed-python - patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.12/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-312-x86_64-linux-gnu.so" + $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p python3.13 --managed-python + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-x86_64-linux-gnu.so" msg_ok "Installed HW-accelerated machine-learning" else msg_info "Installing machine-learning" @@ -405,9 +404,10 @@ DB_VECTOR_EXTENSION=vectorchord REDIS_HOSTNAME=127.0.0.1 IMMICH_MACHINE_LEARNING_URL=http://127.0.0.1:3003 MACHINE_LEARNING_CACHE_FOLDER=${INSTALL_DIR}/cache -## - For OpenVINO only - uncomment below to increase -## - inference speed while reducing accuracy -## - Default is FP32 +## - For OpenVINO only - workaround for onnxruntime-openvino 1.23.x crash +## - See: https://github.com/immich-app/immich/pull/11240 +MACHINE_LEARNING_OPENVINO_NUM_THREADS=$(nproc) +## - Uncomment below to increase inference speed while reducing accuracy # MACHINE_LEARNING_OPENVINO_PRECISION=FP16 IMMICH_MEDIA_LOCATION=${UPLOAD_DIR} From 6e294d199e811a5a9a87d76df7c1d7ef9bac04d3 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:15:05 +0100 Subject: [PATCH 188/228] Update OpenVINO sync to use Python 3.13 Changed the OpenVINO sync and related paths from Python 3.12 to Python 3.13 for HW-accelerated machine-learning updates. This ensures compatibility with the newer Python version. --- ct/immich.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ct/immich.sh b/ct/immich.sh index 24a663b47..d96bbd99d 100644 --- a/ct/immich.sh +++ b/ct/immich.sh @@ -217,9 +217,16 @@ EOF export VIRTUAL_ENV="${ML_DIR}"/ml-venv if [[ -f ~/.openvino ]]; then msg_info "Updating HW-accelerated machine-learning" - $STD uv add --no-sync --optional openvino onnxruntime-openvino==1.20.0 --active -n -p python3.12 --managed-python - $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p python3.12 --managed-python - patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.12/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-312-x86_64-linux-gnu.so" + # Remove old venv if Python version changed (3.12 -> 3.13) + if [[ -d "${VIRTUAL_ENV}" ]] && ! "${VIRTUAL_ENV}/bin/python3" --version 2>/dev/null | grep -q "3.13"; then + rm -rf "${VIRTUAL_ENV}" + fi + $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p python3.13 --managed-python + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-x86_64-linux-gnu.so" + # Add workaround for onnxruntime-openvino 1.23.x crash if not present + if ! grep -q "MACHINE_LEARNING_OPENVINO_NUM_THREADS" "$INSTALL_DIR/.env" 2>/dev/null; then + sed -i '/MACHINE_LEARNING_CACHE_FOLDER/a ## - For OpenVINO only - workaround for onnxruntime-openvino 1.23.x crash\n## - See: https://github.com/immich-app/immich/pull/11240\nMACHINE_LEARNING_OPENVINO_NUM_THREADS=$(nproc)' "$INSTALL_DIR/.env" + fi msg_ok "Updated HW-accelerated machine-learning" else msg_info "Updating machine-learning" From 8a1f604080286c4cf6a2a8a4a42408533b552167 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:37:45 +0100 Subject: [PATCH 189/228] Update AI.md --- docs/AI.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/AI.md b/docs/AI.md index 489a4b35d..9efebd689 100644 --- a/docs/AI.md +++ b/docs/AI.md @@ -224,9 +224,9 @@ CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" ### Helper Utilities -| Function | Description | Example | -|----------|-------------|----------| -| `import_local_ip` | Sets `$LOCAL_IP` variable | `import_local_ip` | +| Function/Variable | Description | Example | +|-------------------|-------------|----------| +| `$LOCAL_IP` | Always available - contains the container's IP address | `echo "Access: http://${LOCAL_IP}:3000"` | | `ensure_dependencies` | Checks/installs dependencies | `ensure_dependencies curl jq` | | `install_packages_with_retry` | APT install with retry | `install_packages_with_retry nginx redis` | From 646291e7c1012cb162daed35769fc0a5d30b1260 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:38:07 +0100 Subject: [PATCH 190/228] Add localhost mode and conditional Collabora install Introduces a localhost/IP-based mode for OpenCloud installation, allowing setup without Collabora or TLS when no hostname is provided. Collabora Online and WOPI services are now only installed and configured if a valid hostname is entered, with environment and service files adjusted accordingly. This improves flexibility for local testing and simplifies configuration for non-production environments. --- install/opencloud-install copy.sh | 210 ++++++++++++++++++++++++++++++ install/opencloud-install.sh | 139 ++++++++++++-------- 2 files changed, 298 insertions(+), 51 deletions(-) create mode 100644 install/opencloud-install copy.sh diff --git a/install/opencloud-install copy.sh b/install/opencloud-install copy.sh new file mode 100644 index 000000000..7d4c9a060 --- /dev/null +++ b/install/opencloud-install copy.sh @@ -0,0 +1,210 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://opencloud.eu + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +read -r -p "${TAB3}Enter the hostname of your OpenCloud server (eg cloud.domain.tld): " oc_host +if [[ "$oc_host" ]]; then + OC_HOST="$oc_host" +fi +read -r -p "${TAB3}Enter the hostname of your Collabora server (eg collabora.domain.tld): " collabora_host +if [[ "$collabora_host" ]]; then + COLLABORA_HOST="$collabora_host" +fi +read -r -p "${TAB3}Enter the hostname of your WOPI server (eg wopiserver.domain.tld): " wopi_host +if [[ "$wopi_host" ]]; then + WOPI_HOST="$wopi_host" +fi + +# Collabora online - this is broken because it adds the Component and apt doesn't like that +# setup_deb822_repo \ +# "collaboraonline" \ +# "https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg" \ +# "https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb/Release" \ +# "./" \ +# "main" + +msg_info "Installing Collabora Online" +curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg +cat </etc/apt/sources.list.d/colloboraonline.sources +Types: deb +URIs: https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb +Suites: ./ +Signed-By: /etc/apt/keyrings/collaboraonline-release-keyring.gpg +EOF +$STD apt-get update +$STD apt-get install -y coolwsd code-brand +systemctl stop coolwsd +mkdir -p /etc/systemd/system/coolwsd.service.d +cat </etc/systemd/system/coolwsd.service.d/override.conf +[Unit] +Before=opencloud-wopi.service +EOF +systemctl daemon-reload +COOLPASS="$(openssl rand -base64 36)" +$STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPASS" +echo "$COOLPASS" >~/.coolpass +msg_ok "Installed Collabora Online" + +# OpenCloud +fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.1" "/usr/bin" "opencloud-*-linux-amd64" + +msg_info "Configuring OpenCloud" +DATA_DIR="/var/lib/opencloud/" +CONFIG_DIR="/etc/opencloud" +ENV_FILE="${CONFIG_DIR}/opencloud.env" +mkdir -p "$DATA_DIR" "$CONFIG_DIR"/assets/apps + +curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/csp.yaml -o "$CONFIG_DIR"/csp.yaml +curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak + +cat <"$ENV_FILE" +OC_URL=https://${OC_HOST} +OC_INSECURE=false +IDM_CREATE_DEMO_USERS=false +OC_LOG_LEVEL=warning +OC_CONFIG_DIR=${CONFIG_DIR} +OC_BASE_DATA_PATH=${DATA_DIR} +STORAGE_SYSTEM_OC_ROOT=${DATA_DIR}/storage/metadata + +## Web +WEB_ASSET_CORE_PATH=${CONFIG_DIR}/web/assets +WEB_ASSET_APPS_PATH=${CONFIG_DIR}/web/assets/apps +WEB_UI_CONFIG_FILE=${CONFIG_DIR}/web/config.json +# WEB_ASSET_THEMES_PATH=${CONFIG_DIR}/web/assets/themes +# WEB_UI_THEME_PATH= + +## Frontend +FRONTEND_DISABLE_RADICALE=true +FRONTEND_GROUPWARE_ENABLED=false +GRAPH_INCLUDE_OCM_SHAREES=true + +## Proxy +PROXY_TLS=false +PROXY_CSP_CONFIG_FILE_LOCATION=${CONFIG_DIR}/csp.yaml + +## Collaboration - requires VALID TLS +COLLABORA_DOMAIN=${COLLABORA_HOST} +COLLABORATION_APP_NAME="CollaboraOnline" +COLLABORATION_APP_PRODUCT="Collabora" +COLLABORATION_APP_ADDR=https://${COLLABORA_HOST} +COLLABORATION_APP_INSECURE=false +COLLABORATION_HTTP_ADDR=0.0.0.0:9300 +COLLABORATION_WOPI_SRC=https://${WOPI_HOST} +COLLABORATION_JWT_SECRET= + +## Notifications - Email settings +# NOTIFICATIONS_SMTP_HOST= +# NOTIFICATIONS_SMTP_PORT= +# NOTIFICATIONS_SMTP_SENDER= +# NOTIFICATIONS_SMTP_USERNAME= +# NOTIFICATIONS_SMTP_PASSWORD= +# NOTIFICATIONS_SMTP_AUTHENTICATION=login +## Encryption method. Possible values are 'starttls', 'ssltls' and 'none' +# NOTIFICATIONS_SMTP_ENCRYPTION=starttls +## Allow insecure connections. Defaults to false. +# NOTIFICATIONS_SMTP_INSECURE=false + +## Start additional services at runtime +## Examples: notifications, antivirus etc. +## Do not uncomment unless configured above. +# OC_ADD_RUN_SERVICES="notifications" + +## OpenID - via web browser +## uncomment for OpenID in general +# OC_EXCLUDE_RUN_SERVICES=idp +# OC_OIDC_ISSUER= +# IDP_DOMAIN= +# PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD=none +# PROXY_OIDC_REWRITE_WELLKNOWN=true +# PROXY_USER_OIDC_CLAIM=preferred_username +# PROXY_USER_CS3_CLAIM=username +## automatically create accounts +# PROXY_AUTOPROVISION_ACCOUNTS=true +# WEB_OIDC_SCOPE=openid profile email groups +# GRAPH_ASSIGN_DEFAULT_USER_ROLE=false +# +## uncomment below if using PocketID +# WEB_OIDC_CLIENT_ID= +# WEB_OIDC_METADATA_URL=/.well-known/openid-configuration + +## Full Text Search - Apache Tika +## Requires a separate install of Tika - see https://community-scripts.github.io/ProxmoxVE/scripts?id=apache-tika +# SEARCH_EXTRACTOR_TYPE=tika +# FRONTEND_FULL_TEXT_SEARCH_ENABLED=true +# SEARCH_EXTRACTOR_TIKA_TIKA_URL= + +## External storage test - Only NFS v4.2+ is supported +## User files +# STORAGE_USERS_POSIX_ROOT= +EOF + +cat </etc/systemd/system/opencloud.service +[Unit] +Description=OpenCloud server +After=network-online.target + +[Service] +Type=simple +User=opencloud +Group=opencloud +EnvironmentFile=${ENV_FILE} +ExecStart=/usr/bin/opencloud server +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/opencloud-wopi.service +[Unit] +Description=OpenCloud WOPI Server +Wants=coolwsd.service +After=opencloud.service coolwsd.service + +[Service] +Type=simple +User=opencloud +Group=opencloud +EnvironmentFile=${ENV_FILE} +ExecStartPre=/bin/sleep 10 +ExecStart=/usr/bin/opencloud collaboration server +Restart=always +KillSignal=SIGKILL +KillMode=mixed +TimeoutStopSec=10 + +[Install] +WantedBy=multi-user.target +EOF + +$STD sudo -u cool coolconfig set ssl.enable false +$STD sudo -u cool coolconfig set ssl.termination true +$STD sudo -u cool coolconfig set ssl.ssl_verification true +sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OC_HOST}|" /etc/coolwsd/coolwsd.xml +useradd -r -M -s /usr/sbin/nologin opencloud +chown -R opencloud:opencloud "$CONFIG_DIR" "$DATA_DIR" +sudo -u opencloud opencloud init --config-path "$CONFIG_DIR" --insecure no +OPENCLOUD_SECRET="$(sed -n '/jwt/p' "$CONFIG_DIR"/opencloud.yaml | awk '{print $2}')" +sed -i "s/JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" +msg_ok "Configured OpenCloud" + +msg_info "Starting services" +systemctl enable -q --now coolwsd opencloud +sleep 5 +systemctl enable -q --now opencloud-wopi +msg_ok "Started services" + +motd_ssh +customize +cleanup_lxc diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 7d4c9a060..943578598 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -13,48 +13,48 @@ setting_up_container network_check update_os +echo -e "${TAB3}${INFO}${YW} Leave empty to use IP-based localhost mode (no Collabora)${CL}" read -r -p "${TAB3}Enter the hostname of your OpenCloud server (eg cloud.domain.tld): " oc_host -if [[ "$oc_host" ]]; then + +if [[ -z "$oc_host" ]]; then + # Localhost/IP mode - no TLS, no Collabora + OC_HOST="${LOCAL_IP}" + LOCALHOST_MODE=true + msg_info "Using localhost mode with IP: ${LOCAL_IP}" + msg_warn "Collabora requires TLS and will be skipped in localhost mode" +else OC_HOST="$oc_host" -fi -read -r -p "${TAB3}Enter the hostname of your Collabora server (eg collabora.domain.tld): " collabora_host -if [[ "$collabora_host" ]]; then - COLLABORA_HOST="$collabora_host" -fi -read -r -p "${TAB3}Enter the hostname of your WOPI server (eg wopiserver.domain.tld): " wopi_host -if [[ "$wopi_host" ]]; then - WOPI_HOST="$wopi_host" + LOCALHOST_MODE=false + read -r -p "${TAB3}Enter the hostname of your Collabora server [collabora.${OC_HOST#*.}]: " collabora_host + COLLABORA_HOST="${collabora_host:-collabora.${OC_HOST#*.}}" + read -r -p "${TAB3}Enter the hostname of your WOPI server [wopiserver.${OC_HOST#*.}]: " wopi_host + WOPI_HOST="${wopi_host:-wopiserver.${OC_HOST#*.}}" fi -# Collabora online - this is broken because it adds the Component and apt doesn't like that -# setup_deb822_repo \ -# "collaboraonline" \ -# "https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg" \ -# "https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb/Release" \ -# "./" \ -# "main" - -msg_info "Installing Collabora Online" -curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg -cat </etc/apt/sources.list.d/colloboraonline.sources +# Collabora Online - only install if not in localhost mode (requires TLS) +if [[ "$LOCALHOST_MODE" != true ]]; then + msg_info "Installing Collabora Online" + curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg + cat </etc/apt/sources.list.d/collaboraonline.sources Types: deb URIs: https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb Suites: ./ Signed-By: /etc/apt/keyrings/collaboraonline-release-keyring.gpg EOF -$STD apt-get update -$STD apt-get install -y coolwsd code-brand -systemctl stop coolwsd -mkdir -p /etc/systemd/system/coolwsd.service.d -cat </etc/systemd/system/coolwsd.service.d/override.conf + $STD apt-get update + $STD apt-get install -y coolwsd code-brand + systemctl stop coolwsd + mkdir -p /etc/systemd/system/coolwsd.service.d + cat </etc/systemd/system/coolwsd.service.d/override.conf [Unit] Before=opencloud-wopi.service EOF -systemctl daemon-reload -COOLPASS="$(openssl rand -base64 36)" -$STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPASS" -echo "$COOLPASS" >~/.coolpass -msg_ok "Installed Collabora Online" + systemctl daemon-reload + COOLPASS="$(openssl rand -base64 36)" + $STD runuser -u cool -- coolconfig set-admin-password --user=admin --password="$COOLPASS" + echo "$COOLPASS" >~/.coolpass + msg_ok "Installed Collabora Online" +fi # OpenCloud fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.1" "/usr/bin" "opencloud-*-linux-amd64" @@ -68,9 +68,17 @@ mkdir -p "$DATA_DIR" "$CONFIG_DIR"/assets/apps curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/csp.yaml -o "$CONFIG_DIR"/csp.yaml curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak +if [[ "$LOCALHOST_MODE" == true ]]; then + OC_URL="http://${OC_HOST}:9200" + OC_INSECURE="true" +else + OC_URL="https://${OC_HOST}" + OC_INSECURE="false" +fi + cat <"$ENV_FILE" -OC_URL=https://${OC_HOST} -OC_INSECURE=false +OC_URL=${OC_URL} +OC_INSECURE=${OC_INSECURE} IDM_CREATE_DEMO_USERS=false OC_LOG_LEVEL=warning OC_CONFIG_DIR=${CONFIG_DIR} @@ -93,15 +101,15 @@ GRAPH_INCLUDE_OCM_SHAREES=true PROXY_TLS=false PROXY_CSP_CONFIG_FILE_LOCATION=${CONFIG_DIR}/csp.yaml -## Collaboration - requires VALID TLS -COLLABORA_DOMAIN=${COLLABORA_HOST} -COLLABORATION_APP_NAME="CollaboraOnline" -COLLABORATION_APP_PRODUCT="Collabora" -COLLABORATION_APP_ADDR=https://${COLLABORA_HOST} -COLLABORATION_APP_INSECURE=false -COLLABORATION_HTTP_ADDR=0.0.0.0:9300 -COLLABORATION_WOPI_SRC=https://${WOPI_HOST} -COLLABORATION_JWT_SECRET= +## Collaboration - requires VALID TLS (disabled in localhost mode) +# COLLABORA_DOMAIN= +# COLLABORATION_APP_NAME="CollaboraOnline" +# COLLABORATION_APP_PRODUCT="Collabora" +# COLLABORATION_APP_ADDR= +# COLLABORATION_APP_INSECURE=false +# COLLABORATION_HTTP_ADDR=0.0.0.0:9300 +# COLLABORATION_WOPI_SRC= +# COLLABORATION_JWT_SECRET= ## Notifications - Email settings # NOTIFICATIONS_SMTP_HOST= @@ -166,7 +174,8 @@ Restart=always WantedBy=multi-user.target EOF -cat </etc/systemd/system/opencloud-wopi.service +if [[ "$LOCALHOST_MODE" != true ]]; then + cat </etc/systemd/system/opencloud-wopi.service [Unit] Description=OpenCloud WOPI Server Wants=coolwsd.service @@ -188,21 +197,49 @@ TimeoutStopSec=10 WantedBy=multi-user.target EOF -$STD sudo -u cool coolconfig set ssl.enable false -$STD sudo -u cool coolconfig set ssl.termination true -$STD sudo -u cool coolconfig set ssl.ssl_verification true -sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OC_HOST}|" /etc/coolwsd/coolwsd.xml + # Append active Collabora config to env file + cat <>"$ENV_FILE" + +## Collaboration - active configuration +COLLABORA_DOMAIN=${COLLABORA_HOST} +COLLABORATION_APP_NAME="CollaboraOnline" +COLLABORATION_APP_PRODUCT="Collabora" +COLLABORATION_APP_ADDR=https://${COLLABORA_HOST} +COLLABORATION_APP_INSECURE=false +COLLABORATION_HTTP_ADDR=0.0.0.0:9300 +COLLABORATION_WOPI_SRC=https://${WOPI_HOST} +COLLABORATION_JWT_SECRET= +EOF + + $STD runuser -u cool -- coolconfig set ssl.enable false + $STD runuser -u cool -- coolconfig set ssl.termination true + $STD runuser -u cool -- coolconfig set ssl.ssl_verification true + sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OC_HOST}|" /etc/coolwsd/coolwsd.xml +fi + useradd -r -M -s /usr/sbin/nologin opencloud chown -R opencloud:opencloud "$CONFIG_DIR" "$DATA_DIR" -sudo -u opencloud opencloud init --config-path "$CONFIG_DIR" --insecure no + +if [[ "$LOCALHOST_MODE" == true ]]; then + $STD runuser -u opencloud -- opencloud init --config-path "$CONFIG_DIR" --insecure yes +else + $STD runuser -u opencloud -- opencloud init --config-path "$CONFIG_DIR" --insecure no +fi + OPENCLOUD_SECRET="$(sed -n '/jwt/p' "$CONFIG_DIR"/opencloud.yaml | awk '{print $2}')" -sed -i "s/JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" +if [[ "$LOCALHOST_MODE" != true ]]; then + sed -i "s/COLLABORATION_JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" +fi msg_ok "Configured OpenCloud" msg_info "Starting services" -systemctl enable -q --now coolwsd opencloud -sleep 5 -systemctl enable -q --now opencloud-wopi +if [[ "$LOCALHOST_MODE" == true ]]; then + systemctl enable -q --now opencloud +else + systemctl enable -q --now coolwsd opencloud + sleep 5 + systemctl enable -q --now opencloud-wopi +fi msg_ok "Started services" motd_ssh From a5096a5b622d6e89ab044d32fa25b8c7e96fc0e6 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:41:55 +0100 Subject: [PATCH 191/228] test anytype --- ct/anytype.sh | 66 +++++++++++++++++++++++++++++++ frontend/public/json/anytype.json | 48 ++++++++++++++++++++++ install/anytype-install.sh | 59 +++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 ct/anytype.sh create mode 100644 frontend/public/json/anytype.json create mode 100644 install/anytype-install.sh diff --git a/ct/anytype.sh b/ct/anytype.sh new file mode 100644 index 000000000..b2af170e8 --- /dev/null +++ b/ct/anytype.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-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://anytype.io + +APP="Anytype" +var_tags="${var_tags:-notes;productivity;sync}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /opt/anytype/any-sync-bundle ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "anytype" "grishy/any-sync-bundle"; then + msg_info "Stopping Service" + systemctl stop anytype + msg_ok "Stopped Service" + + msg_info "Backing up Data" + cp -r /opt/anytype/data /opt/anytype_data_backup + msg_ok "Backed up Data" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "anytype" "grishy/any-sync-bundle" "prebuild" "latest" "/opt/anytype" "any-sync-bundle_*_linux_amd64.tar.gz" + chmod +x /opt/anytype/any-sync-bundle + + msg_info "Restoring Data" + cp -r /opt/anytype_data_backup/. /opt/anytype/data + rm -rf /opt/anytype_data_backup + msg_ok "Restored Data" + + msg_info "Starting Service" + systemctl start anytype + msg_ok "Started Service" + msg_ok "Updated successfully!" + 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}:33010${CL}" +echo -e "${INFO}${YW} Client config file:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}/opt/anytype/data/client-config.yml${CL}" diff --git a/frontend/public/json/anytype.json b/frontend/public/json/anytype.json new file mode 100644 index 000000000..88c1abe61 --- /dev/null +++ b/frontend/public/json/anytype.json @@ -0,0 +1,48 @@ +{ + "name": "Anytype", + "slug": "anytype", + "categories": [ + 12 + ], + "date_created": "2026-01-29", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 33010, + "documentation": "https://doc.anytype.io/", + "website": "https://anytype.io/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/anytype.webp", + "config_path": "/opt/anytype/.env", + "description": "Anytype is a local-first, privacy-focused alternative to Notion. This script deploys the any-sync-bundle which provides a self-hosted sync server for Anytype clients with embedded MongoDB and Redis.", + "install_methods": [ + { + "type": "default", + "script": "ct/anytype.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 10, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "After installation, import /opt/anytype/data/client-config.yml into your Anytype apps.", + "type": "info" + }, + { + "text": "This uses the community any-sync-bundle by grishy, not the official Anytype deployment.", + "type": "warning" + }, + { + "text": "Firewall: Open TCP 33010 (DRPC) and UDP 33020 (QUIC) for external access.", + "type": "info" + } + ] +} diff --git a/install/anytype-install.sh b/install/anytype-install.sh new file mode 100644 index 000000000..458e70d3b --- /dev/null +++ b/install/anytype-install.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://anytype.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 install -y ca-certificates +msg_ok "Installed Dependencies" + +fetch_and_deploy_gh_release "anytype" "grishy/any-sync-bundle" "prebuild" "latest" "/opt/anytype" "any-sync-bundle_*_linux_amd64.tar.gz" +chmod +x /opt/anytype/any-sync-bundle + +msg_info "Configuring Anytype" +mkdir -p /opt/anytype/data + +cat </opt/anytype/.env +ANY_SYNC_BUNDLE_CONFIG=/opt/anytype/data/bundle-config.yml +ANY_SYNC_BUNDLE_CLIENT_CONFIG=/opt/anytype/data/client-config.yml +ANY_SYNC_BUNDLE_INIT_STORAGE=/opt/anytype/data/storage/ +ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS=${LOCAL_IP} +ANY_SYNC_BUNDLE_LOG_LEVEL=info +EOF +msg_ok "Configured Anytype" + +msg_info "Creating Service" +cat </etc/systemd/system/anytype.service +[Unit] +Description=Anytype Sync Server (any-sync-bundle) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/anytype +EnvironmentFile=/opt/anytype/.env +ExecStart=/opt/anytype/any-sync-bundle start-all-in-one +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now anytype +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 8c4f1ce5318f9a483bbada392b79f3db85b8e3c0 Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 27 Jan 2026 19:31:02 -0500 Subject: [PATCH 192/228] Make check_container_storage() POSIX compliant --- misc/build.func | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/misc/build.func b/misc/build.func index 051c5f4b8..386b37385 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3086,21 +3086,32 @@ check_container_resources() { # ------------------------------------------------------------------------------ # check_container_storage() # -# - Checks /boot partition usage +# - Checks root (/) partition usage # - Warns if usage >80% and asks user confirmation before proceeding # ------------------------------------------------------------------------------ check_container_storage() { - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then + usage=$(df / -P | awk 'NR==2 {print $5}' | tr -d '%') + + # shellcheck disable=SC2181 + if [ $? -ne 0 ]; then + echo "Error: Failed to check disk usage." + exit 1 + fi + + if [ "$usage" -gt 80 ]; then echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " + printf "Continue anyway? " read -r prompt - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + + case "$prompt" in + [yY][eE][sS] | [yY]) + # User input is "yes" or "y"; continue + ;; + *) echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" exit 1 - fi + ;; + esac fi } From 000a7a270166a005f09a53a0604aeb91c2362f90 Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 27 Jan 2026 20:41:55 -0500 Subject: [PATCH 193/228] Fix Error check and error message style --- misc/build.func | 365 ++++++++++++++++++++++++------------------------ 1 file changed, 181 insertions(+), 184 deletions(-) diff --git a/misc/build.func b/misc/build.func index 386b37385..7e5e0a794 100644 --- a/misc/build.func +++ b/misc/build.func @@ -363,7 +363,7 @@ validate_hostname() { # Split by dots and validate each label local IFS='.' - read -ra labels <<< "$hostname" + read -ra labels <<<"$hostname" for label in "${labels[@]}"; do # Each label: 1-63 chars, alphanumeric, hyphens allowed (not at start/end) if [[ -z "$label" ]] || [[ ${#label} -gt 63 ]]; then @@ -467,7 +467,7 @@ validate_ipv6_address() { # Check that no segment exceeds 4 hex chars local IFS=':' local -a segments - read -ra segments <<< "$addr" + read -ra segments <<<"$addr" for seg in "${segments[@]}"; do if [[ ${#seg} -gt 4 ]]; then return 1 @@ -517,14 +517,14 @@ validate_gateway_in_subnet() { # Convert IPs to integers local IFS='.' - read -r i1 i2 i3 i4 <<< "$ip" - read -r g1 g2 g3 g4 <<< "$gateway" + read -r i1 i2 i3 i4 <<<"$ip" + read -r g1 g2 g3 g4 <<<"$gateway" - local ip_int=$(( (i1 << 24) + (i2 << 16) + (i3 << 8) + i4 )) - local gw_int=$(( (g1 << 24) + (g2 << 16) + (g3 << 8) + g4 )) + local ip_int=$(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) + local gw_int=$(((g1 << 24) + (g2 << 16) + (g3 << 8) + g4)) # Check if both are in same network - if (( (ip_int & mask) != (gw_int & mask) )); then + if (((ip_int & mask) != (gw_int & mask))); then return 1 fi @@ -1057,113 +1057,113 @@ load_vars_file() { # Validate values before setting (skip empty values - they use defaults) if [[ -n "$var_val" ]]; then case "$var_key" in - var_mac) - if ! validate_mac_address "$var_val"; then - msg_warn "Invalid MAC address '$var_val' in $file, ignoring" + var_mac) + if ! validate_mac_address "$var_val"; then + msg_warn "Invalid MAC address '$var_val' in $file, ignoring" + continue + fi + ;; + var_vlan) + if ! validate_vlan_tag "$var_val"; then + msg_warn "Invalid VLAN tag '$var_val' in $file (must be 1-4094), ignoring" + continue + fi + ;; + var_mtu) + if ! validate_mtu "$var_val"; then + msg_warn "Invalid MTU '$var_val' in $file (must be 576-65535), ignoring" + continue + fi + ;; + var_tags) + if ! validate_tags "$var_val"; then + msg_warn "Invalid tags '$var_val' in $file (alphanumeric, -, _, ; only), ignoring" + continue + fi + ;; + var_timezone) + if ! validate_timezone "$var_val"; then + msg_warn "Invalid timezone '$var_val' in $file, ignoring" + continue + fi + ;; + var_brg) + if ! validate_bridge "$var_val"; then + msg_warn "Bridge '$var_val' not found in $file, ignoring" + continue + fi + ;; + var_gateway) + if ! validate_gateway_ip "$var_val"; then + msg_warn "Invalid gateway IP '$var_val' in $file, ignoring" + continue + fi + ;; + var_hostname) + if ! validate_hostname "$var_val"; then + msg_warn "Invalid hostname '$var_val' in $file, ignoring" + continue + fi + ;; + var_cpu) + if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1 || var_val > 128)); then + msg_warn "Invalid CPU count '$var_val' in $file (must be 1-128), ignoring" + continue + fi + ;; + var_ram) + if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 256)); then + msg_warn "Invalid RAM '$var_val' in $file (must be >= 256 MiB), ignoring" + continue + fi + ;; + var_disk) + if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1)); then + msg_warn "Invalid disk size '$var_val' in $file (must be >= 1 GB), ignoring" + continue + fi + ;; + var_unprivileged) + if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then + msg_warn "Invalid unprivileged value '$var_val' in $file (must be 0 or 1), ignoring" + continue + fi + ;; + var_nesting) + if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then + msg_warn "Invalid nesting value '$var_val' in $file (must be 0 or 1), ignoring" + continue + fi + ;; + var_keyctl) + if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then + msg_warn "Invalid keyctl value '$var_val' in $file (must be 0 or 1), ignoring" + continue + fi + ;; + var_net) + # var_net can be: dhcp, static IP/CIDR, or IP range + if [[ "$var_val" != "dhcp" ]]; then + if is_ip_range "$var_val"; then + : # IP range is valid, will be resolved at runtime + elif ! validate_ip_address "$var_val"; then + msg_warn "Invalid network '$var_val' in $file (must be dhcp or IP/CIDR), ignoring" continue fi - ;; - var_vlan) - if ! validate_vlan_tag "$var_val"; then - msg_warn "Invalid VLAN tag '$var_val' in $file (must be 1-4094), ignoring" - continue - fi - ;; - var_mtu) - if ! validate_mtu "$var_val"; then - msg_warn "Invalid MTU '$var_val' in $file (must be 576-65535), ignoring" - continue - fi - ;; - var_tags) - if ! validate_tags "$var_val"; then - msg_warn "Invalid tags '$var_val' in $file (alphanumeric, -, _, ; only), ignoring" - continue - fi - ;; - var_timezone) - if ! validate_timezone "$var_val"; then - msg_warn "Invalid timezone '$var_val' in $file, ignoring" - continue - fi - ;; - var_brg) - if ! validate_bridge "$var_val"; then - msg_warn "Bridge '$var_val' not found in $file, ignoring" - continue - fi - ;; - var_gateway) - if ! validate_gateway_ip "$var_val"; then - msg_warn "Invalid gateway IP '$var_val' in $file, ignoring" - continue - fi - ;; - var_hostname) - if ! validate_hostname "$var_val"; then - msg_warn "Invalid hostname '$var_val' in $file, ignoring" - continue - fi - ;; - var_cpu) - if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1 || var_val > 128)); then - msg_warn "Invalid CPU count '$var_val' in $file (must be 1-128), ignoring" - continue - fi - ;; - var_ram) - if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 256)); then - msg_warn "Invalid RAM '$var_val' in $file (must be >= 256 MiB), ignoring" - continue - fi - ;; - var_disk) - if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1)); then - msg_warn "Invalid disk size '$var_val' in $file (must be >= 1 GB), ignoring" - continue - fi - ;; - var_unprivileged) - if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then - msg_warn "Invalid unprivileged value '$var_val' in $file (must be 0 or 1), ignoring" - continue - fi - ;; - var_nesting) - if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then - msg_warn "Invalid nesting value '$var_val' in $file (must be 0 or 1), ignoring" - continue - fi - ;; - var_keyctl) - if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then - msg_warn "Invalid keyctl value '$var_val' in $file (must be 0 or 1), ignoring" - continue - fi - ;; - var_net) - # var_net can be: dhcp, static IP/CIDR, or IP range - if [[ "$var_val" != "dhcp" ]]; then - if is_ip_range "$var_val"; then - : # IP range is valid, will be resolved at runtime - elif ! validate_ip_address "$var_val"; then - msg_warn "Invalid network '$var_val' in $file (must be dhcp or IP/CIDR), ignoring" - continue - fi - fi - ;; - var_fuse|var_tun|var_gpu|var_ssh|var_verbose|var_protection) - if [[ "$var_val" != "yes" && "$var_val" != "no" ]]; then - msg_warn "Invalid boolean '$var_val' for $var_key in $file (must be yes/no), ignoring" - continue - fi - ;; - var_ipv6_method) - if [[ "$var_val" != "auto" && "$var_val" != "dhcp" && "$var_val" != "static" && "$var_val" != "none" ]]; then - msg_warn "Invalid IPv6 method '$var_val' in $file (must be auto/dhcp/static/none), ignoring" - continue - fi - ;; + fi + ;; + var_fuse | var_tun | var_gpu | var_ssh | var_verbose | var_protection) + if [[ "$var_val" != "yes" && "$var_val" != "no" ]]; then + msg_warn "Invalid boolean '$var_val' for $var_key in $file (must be yes/no), ignoring" + continue + fi + ;; + var_ipv6_method) + if [[ "$var_val" != "auto" && "$var_val" != "dhcp" && "$var_val" != "static" && "$var_val" != "none" ]]; then + msg_warn "Invalid IPv6 method '$var_val' in $file (must be auto/dhcp/static/none), ignoring" + continue + fi + ;; esac fi @@ -3092,21 +3092,18 @@ check_container_resources() { check_container_storage() { usage=$(df / -P | awk 'NR==2 {print $5}' | tr -d '%') - # shellcheck disable=SC2181 - if [ $? -ne 0 ]; then - echo "Error: Failed to check disk usage." + if [ -z "$usage" ] || [ "$usage" -lt 0 ]; then + echo -e "${CROSS}${HOLD}${RD}Error: Failed to check disk usage.${CL}" exit 1 fi if [ "$usage" -gt 80 ]; then - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -e "${INFO}${HOLD}${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" printf "Continue anyway? " read -r prompt case "$prompt" in - [yY][eE][sS] | [yY]) - # User input is "yes" or "y"; continue - ;; + [yY][eE][sS] | [yY]) ;; *) echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" exit 1 @@ -4034,91 +4031,91 @@ EOF' if read -t 60 -r response; then case "${response:-1}" in - 1) - # Remove container - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - ;; - 2) - echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" - # Dev mode: Setup MOTD/SSH for debugging access to broken container - if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then - echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" - if pct exec "$CTID" -- bash -c " + 1) + # Remove container + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + ;; + 2) + echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" + # Dev mode: Setup MOTD/SSH for debugging access to broken container + if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then + echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" + if pct exec "$CTID" -- bash -c " source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then - local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) - echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" - fi + local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) + echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" fi - exit $install_exit_code - ;; - 3) - # Retry with verbose mode - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild...${CL}" + fi + exit $install_exit_code + ;; + 3) + # Retry with verbose mode + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild...${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + echo "" + # Get new container ID + local old_ctid="$CTID" + export CTID=$(get_valid_container_id "$CTID") + export VERBOSE="yes" + export var_verbose="yes" + + # Show rebuild summary + echo -e "${YW}Rebuilding with preserved settings:${CL}" + echo -e " Container ID: ${old_ctid} → ${CTID}" + echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores | Disk: ${DISK_SIZE} GB" + echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" + echo -e " Verbose: ${GN}enabled${CL}" + echo "" + msg_info "Restarting installation..." + # Re-run build_container + build_container + return $? + ;; + 4) + if [[ "$is_oom" == true ]]; then + # Retry with more resources + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with more resources...${CL}" pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" echo "" - # Get new container ID + # Get new container ID and increase resources local old_ctid="$CTID" + local old_ram="$RAM_SIZE" + local old_cpu="$CORE_COUNT" export CTID=$(get_valid_container_id "$CTID") - export VERBOSE="yes" - export var_verbose="yes" + export RAM_SIZE=$((RAM_SIZE * 3 / 2)) + export CORE_COUNT=$((CORE_COUNT + 1)) + export var_ram="$RAM_SIZE" + export var_cpu="$CORE_COUNT" # Show rebuild summary - echo -e "${YW}Rebuilding with preserved settings:${CL}" + echo -e "${YW}Rebuilding with increased resources:${CL}" echo -e " Container ID: ${old_ctid} → ${CTID}" - echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores | Disk: ${DISK_SIZE} GB" - echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" - echo -e " Verbose: ${GN}enabled${CL}" + echo -e " RAM: ${old_ram} → ${GN}${RAM_SIZE}${CL} MiB (+50%)" + echo -e " CPU: ${old_cpu} → ${GN}${CORE_COUNT}${CL} cores (+1)" + echo -e " Disk: ${DISK_SIZE} GB | Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" echo "" msg_info "Restarting installation..." # Re-run build_container build_container return $? - ;; - 4) - if [[ "$is_oom" == true ]]; then - # Retry with more resources - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with more resources...${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - echo "" - # Get new container ID and increase resources - local old_ctid="$CTID" - local old_ram="$RAM_SIZE" - local old_cpu="$CORE_COUNT" - export CTID=$(get_valid_container_id "$CTID") - export RAM_SIZE=$((RAM_SIZE * 3 / 2)) - export CORE_COUNT=$((CORE_COUNT + 1)) - export var_ram="$RAM_SIZE" - export var_cpu="$CORE_COUNT" - - # Show rebuild summary - echo -e "${YW}Rebuilding with increased resources:${CL}" - echo -e " Container ID: ${old_ctid} → ${CTID}" - echo -e " RAM: ${old_ram} → ${GN}${RAM_SIZE}${CL} MiB (+50%)" - echo -e " CPU: ${old_cpu} → ${GN}${CORE_COUNT}${CL} cores (+1)" - echo -e " Disk: ${DISK_SIZE} GB | Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" - echo "" - msg_info "Restarting installation..." - # Re-run build_container - build_container - return $? - else - echo -e "\n${TAB}${YW}Invalid option. Container ${CTID} kept.${CL}" - exit $install_exit_code - fi - ;; - *) + else echo -e "\n${TAB}${YW}Invalid option. Container ${CTID} kept.${CL}" exit $install_exit_code - ;; + fi + ;; + *) + echo -e "\n${TAB}${YW}Invalid option. Container ${CTID} kept.${CL}" + exit $install_exit_code + ;; esac else # Timeout - auto-remove From d7d16543e571e48aef165aeea63d76798d9ebd2c Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 27 Jan 2026 14:16:13 -0500 Subject: [PATCH 194/228] Fix url assignment in fetch_and_deploy_gh_release --- misc/alpine-tools.func | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 955554216..4f798c45a 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -317,9 +317,15 @@ fetch_and_deploy_gh_release() { return 1 } + # The command is the same for all cases other than tarball/source + get_url() { # $1 pattern + printf '%s' "$json" | jq -r '.assets[].browser_download_url' | + awk -v p="$1" 'BEGIN{IGNORECASE=1} $0 ~ p {print; exit}' + } + case "$mode" in tarball | source) - url="$(printf '%s' "$json" | jq -r '.tarball_url // empty')" + url=$(printf '%s' "$json" | jq -r '.tarball_url // empty') [ -z "$url" ] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" filename="${app_lc}-${version}.tar.gz" download_with_progress "$url" "$tmpd/$filename" || { @@ -342,7 +348,7 @@ fetch_and_deploy_gh_release() { ;; binary) [ -n "$pattern" ] || pattern="*.apk" - url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" 'BEGIN{IGNORECASE=1} $0 ~ p {print; exit}')" + url=$(get_url "$pattern") [ -z "$url" ] && { msg_error "binary asset not found for pattern: $pattern" rm -rf "$tmpd" @@ -374,10 +380,7 @@ fetch_and_deploy_gh_release() { rm -rf "$tmpd" return 1 } - url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' - BEGIN{IGNORECASE=1} - $0 ~ p {print; exit} - ')" + url=$(get_url "$pattern") [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" @@ -431,10 +434,7 @@ fetch_and_deploy_gh_release() { rm -rf "$tmpd" return 1 } - url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' - BEGIN{IGNORECASE=1} - $0 ~ p {print; exit} - ')" + url=$(get_url "$pattern") [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" From bf97037ba52d00e25735f2c031b2fbc7f926a95f Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 27 Jan 2026 14:22:39 -0500 Subject: [PATCH 195/228] Remove bad quotes --- misc/alpine-tools.func | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 4f798c45a..6dd06b659 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -277,7 +277,7 @@ fetch_and_deploy_gh_release() { # $1 app, $2 repo, [$3 mode], [$4 version], [$5 target], [$6 asset_pattern local app="$1" repo="$2" mode="${3:-tarball}" version="${4:-latest}" target="${5:-/opt/$1}" pattern="${6:-}" local app_lc - app_lc="$(lower "$app" | tr -d ' ')" + app_lc=$(lower "$app" | tr -d ' ') local vfile="$HOME/.${app_lc}" local json url filename tmpd unpack @@ -288,7 +288,7 @@ fetch_and_deploy_gh_release() { need_tool curl jq tar || return 1 [ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true - tmpd="$(mktemp -d)" || return 1 + tmpd=$(mktemp -d) || return 1 mkdir -p "$target" # Release JSON (with token/rate-limit handling) @@ -305,10 +305,10 @@ fetch_and_deploy_gh_release() { return 1 } fi - json="$(cat "$tmpd/release.json")" + json=$(cat "$tmpd/release.json") # correct Version - version="$(printf '%s' "$json" | jq -r '.tag_name // empty')" + version=$(printf '%s' "$json" | jq -r '.tag_name // empty') version="${version#v}" [ -z "$version" ] && { @@ -337,7 +337,7 @@ fetch_and_deploy_gh_release() { rm -rf "$tmpd" return 1 } - unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)" + unpack=$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1) [ "${CLEAN_INSTALL:-0}" = "1" ] && rm -rf "${target:?}/"* # copy content of unpack to target (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { @@ -414,7 +414,7 @@ fetch_and_deploy_gh_release() { [ "${CLEAN_INSTALL:-0}" = "1" ] && rm -rf "${target:?}/"* # top-level folder strippen if [ "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d | wc -l)" -eq 1 ] && [ -z "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type f | head -n1)" ]; then - unpack="$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d)" + unpack=$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d) (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" From 16d0ffa19a880387eb4118324b21e7675ca68e2b Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 27 Jan 2026 14:42:24 -0500 Subject: [PATCH 196/228] $pattern doesn't need to be a parameter --- misc/alpine-tools.func | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 6dd06b659..9aee5ca56 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -318,9 +318,9 @@ fetch_and_deploy_gh_release() { } # The command is the same for all cases other than tarball/source - get_url() { # $1 pattern + get_url() { printf '%s' "$json" | jq -r '.assets[].browser_download_url' | - awk -v p="$1" 'BEGIN{IGNORECASE=1} $0 ~ p {print; exit}' + awk -v p="$pattern" 'BEGIN{IGNORECASE=1} $0 ~ p {print; exit}' } case "$mode" in @@ -348,7 +348,7 @@ fetch_and_deploy_gh_release() { ;; binary) [ -n "$pattern" ] || pattern="*.apk" - url=$(get_url "$pattern") + url=$(get_url) [ -z "$url" ] && { msg_error "binary asset not found for pattern: $pattern" rm -rf "$tmpd" @@ -380,7 +380,7 @@ fetch_and_deploy_gh_release() { rm -rf "$tmpd" return 1 } - url=$(get_url "$pattern") + url=$(get_url) [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" @@ -434,7 +434,7 @@ fetch_and_deploy_gh_release() { rm -rf "$tmpd" return 1 } - url=$(get_url "$pattern") + url=$(get_url) [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" From f4495280cf8b2abb123f66eb1e36754c60197dfb Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 27 Jan 2026 15:25:05 -0500 Subject: [PATCH 197/228] AWK output needs to be sanitized --- misc/alpine-tools.func | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 9aee5ca56..3ddc7fda8 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -320,7 +320,8 @@ fetch_and_deploy_gh_release() { # The command is the same for all cases other than tarball/source get_url() { printf '%s' "$json" | jq -r '.assets[].browser_download_url' | - awk -v p="$pattern" 'BEGIN{IGNORECASE=1} $0 ~ p {print; exit}' + awk -v p="$pattern" 'BEGIN{IGNORECASE=1} $0 ~ p {print; exit}' | + tr -d '[:cntrl:]' } case "$mode" in From 54bbdc180a22fdd6e43208debf422387ed7703c0 Mon Sep 17 00:00:00 2001 From: justin Date: Wed, 28 Jan 2026 11:00:41 -0500 Subject: [PATCH 198/228] Remove inline comment in misc/alpine-tools.func --- misc/alpine-tools.func | 1 - 1 file changed, 1 deletion(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 3ddc7fda8..6d7ed215a 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -317,7 +317,6 @@ fetch_and_deploy_gh_release() { return 1 } - # The command is the same for all cases other than tarball/source get_url() { printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" 'BEGIN{IGNORECASE=1} $0 ~ p {print; exit}' | From 622fda4b321a1f93ff12793bb1d8d174299b5742 Mon Sep 17 00:00:00 2001 From: justin Date: Mon, 26 Jan 2026 14:32:47 -0500 Subject: [PATCH 199/228] Fix download_with_progress() content_length calc --- misc/alpine-tools.func | 16 ++++++++++++---- misc/tools.func | 9 +++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 6d7ed215a..a830c220c 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -196,11 +196,19 @@ ensure_usr_local_bin_persist() { download_with_progress() { # $1 url, $2 dest - local url="$1" out="$2" cl + local url="$1" out="$2" content_length need_tool curl pv || return 1 - cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r') - if [ -n "$cl" ]; then - curl -fsSL "$url" | pv -s "$cl" >"$out" || { + + content_length=$( + curl -fsSLI "$url" 2>/dev/null | + # May return multiple values on redirect. i.e., 0 and content-length + # Cast $2 to int by adding 0 to it + awk '(tolower($1) ~ /^content-length:/) && ($2 + 0 > 0) {print $2+0}' | + tr -cd '[:digit:]' || true + ) + + if [ -n "$content_length" ]; then + curl -fsSL "$url" | pv -s "$content_length" >"$out" || { msg_error "Download failed: $url" return 1 } diff --git a/misc/tools.func b/misc/tools.func index 70e8c3fa9..44484ae5b 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1692,7 +1692,13 @@ function download_with_progress() { # Content-Length aus HTTP-Header holen local content_length - content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + content_length=$( + curl -fsSLI "$url" 2>/dev/null | + # May return multiple values on redirect. i.e., 0 and content_length + # Add 0 to $2 to cast it to int + awk '(tolower($1) ~ /^content-length:/) && ($2 + 0 > 0) {print $2+0}' | + tr -cd '[:digit:]' || true + ) if [[ -z "$content_length" ]]; then if ! curl -fL# -o "$output" "$url"; then @@ -6205,4 +6211,3 @@ function fetch_and_deploy_archive() { msg_ok "Successfully deployed archive to $directory" return 0 } - From 75898f498697d40e1ff4b90db3f9a3098758fd70 Mon Sep 17 00:00:00 2001 From: justin Date: Tue, 27 Jan 2026 11:30:26 -0500 Subject: [PATCH 200/228] Add tail -1 before tr to use only the last value --- misc/alpine-tools.func | 4 ++-- misc/tools.func | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index a830c220c..035565194 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -201,10 +201,10 @@ download_with_progress() { content_length=$( curl -fsSLI "$url" 2>/dev/null | - # May return multiple values on redirect. i.e., 0 and content-length + # May return multiple values on redirect. i.e., 0 and actual content-length value # Cast $2 to int by adding 0 to it awk '(tolower($1) ~ /^content-length:/) && ($2 + 0 > 0) {print $2+0}' | - tr -cd '[:digit:]' || true + tail -1 | tr -cd '[:digit:]' || true ) if [ -n "$content_length" ]; then diff --git a/misc/tools.func b/misc/tools.func index 44484ae5b..18602dc85 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1694,10 +1694,10 @@ function download_with_progress() { local content_length content_length=$( curl -fsSLI "$url" 2>/dev/null | - # May return multiple values on redirect. i.e., 0 and content_length + # May return multiple values on redirect. i.e., 0 and actual content-length value # Add 0 to $2 to cast it to int awk '(tolower($1) ~ /^content-length:/) && ($2 + 0 > 0) {print $2+0}' | - tr -cd '[:digit:]' || true + tail -1 | tr -cd '[:digit:]' || true ) if [[ -z "$content_length" ]]; then From f2a847ac81ffa5f5a670d30b055d4d4d47f79ed0 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 28 Jan 2026 15:48:21 +0000 Subject: [PATCH 201/228] Remove inline comment in misc/alpine-tools.func Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> --- misc/alpine-tools.func | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 035565194..9386f737a 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -201,8 +201,6 @@ download_with_progress() { content_length=$( curl -fsSLI "$url" 2>/dev/null | - # May return multiple values on redirect. i.e., 0 and actual content-length value - # Cast $2 to int by adding 0 to it awk '(tolower($1) ~ /^content-length:/) && ($2 + 0 > 0) {print $2+0}' | tail -1 | tr -cd '[:digit:]' || true ) From 62f68959c74b193bc6ab497262608f08240ed652 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 28 Jan 2026 15:48:34 +0000 Subject: [PATCH 202/228] Remove inline comment in misc/tools.func Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> --- misc/tools.func | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 18602dc85..1096b4a01 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1694,8 +1694,6 @@ function download_with_progress() { local content_length content_length=$( curl -fsSLI "$url" 2>/dev/null | - # May return multiple values on redirect. i.e., 0 and actual content-length value - # Add 0 to $2 to cast it to int awk '(tolower($1) ~ /^content-length:/) && ($2 + 0 > 0) {print $2+0}' | tail -1 | tr -cd '[:digit:]' || true ) From 210b6080cd9ce40392baa4652c4a1569cc3cd1bc Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 10:36:57 +0100 Subject: [PATCH 203/228] Update Anytype to use external MongoDB and Redis Stack Switches Anytype deployment to use external MongoDB and Redis Stack instead of embedded databases. Updates default resources to 4GB RAM, 16GB disk, Ubuntu 24.04, and configures MongoDB replica set and Redis Stack installation. Adjusts service dependencies and environment variables accordingly. --- ct/anytype.sh | 8 +++---- frontend/public/json/anytype.json | 10 ++++----- install/anytype-install.sh | 37 +++++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/ct/anytype.sh b/ct/anytype.sh index b2af170e8..7525e1eaf 100644 --- a/ct/anytype.sh +++ b/ct/anytype.sh @@ -8,10 +8,10 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Anytype" var_tags="${var_tags:-notes;productivity;sync}" var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-16}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" diff --git a/frontend/public/json/anytype.json b/frontend/public/json/anytype.json index 88c1abe61..a89835cc9 100644 --- a/frontend/public/json/anytype.json +++ b/frontend/public/json/anytype.json @@ -13,17 +13,17 @@ "website": "https://anytype.io/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/anytype.webp", "config_path": "/opt/anytype/.env", - "description": "Anytype is a local-first, privacy-focused alternative to Notion. This script deploys the any-sync-bundle which provides a self-hosted sync server for Anytype clients with embedded MongoDB and Redis.", + "description": "Anytype is a local-first, privacy-focused alternative to Notion. This script deploys the any-sync-bundle which provides a self-hosted sync server for Anytype clients with external MongoDB and Redis Stack.", "install_methods": [ { "type": "default", "script": "ct/anytype.sh", "resources": { "cpu": 2, - "ram": 2048, - "hdd": 10, - "os": "Debian", - "version": "13" + "ram": 4096, + "hdd": 16, + "os": "Ubuntu", + "version": "24.04" } } ], diff --git a/install/anytype-install.sh b/install/anytype-install.sh index 458e70d3b..c6dc1e96b 100644 --- a/install/anytype-install.sh +++ b/install/anytype-install.sh @@ -13,21 +13,43 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt install -y ca-certificates -msg_ok "Installed Dependencies" +setup_mongodb + +msg_info "Configuring MongoDB Replica Set" +cat <>/etc/mongod.conf + +replication: + replSetName: "rs0" +EOF +systemctl restart mongod +sleep 3 +$STD mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "127.0.0.1:27017"}]})' +msg_ok "Configured MongoDB Replica Set" + +msg_info "Installing Redis Stack" +setup_deb822_repo \ + "redis-stack" \ + "https://packages.redis.io/gpg" \ + "https://packages.redis.io/deb" \ + "jammy" \ + "main" +$STD apt-get install -y \ + redis-stack-server +systemctl enable -q --now redis-stack-server +msg_ok "Installed Redis Stack" fetch_and_deploy_gh_release "anytype" "grishy/any-sync-bundle" "prebuild" "latest" "/opt/anytype" "any-sync-bundle_*_linux_amd64.tar.gz" chmod +x /opt/anytype/any-sync-bundle msg_info "Configuring Anytype" -mkdir -p /opt/anytype/data - +mkdir -p /opt/anytype/data/storage cat </opt/anytype/.env ANY_SYNC_BUNDLE_CONFIG=/opt/anytype/data/bundle-config.yml ANY_SYNC_BUNDLE_CLIENT_CONFIG=/opt/anytype/data/client-config.yml ANY_SYNC_BUNDLE_INIT_STORAGE=/opt/anytype/data/storage/ ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS=${LOCAL_IP} +ANY_SYNC_BUNDLE_INIT_MONGO_URI=mongodb://127.0.0.1:27017/ +ANY_SYNC_BUNDLE_INIT_REDIS_URI=redis://127.0.0.1:6379/ ANY_SYNC_BUNDLE_LOG_LEVEL=info EOF msg_ok "Configured Anytype" @@ -36,15 +58,16 @@ msg_info "Creating Service" cat </etc/systemd/system/anytype.service [Unit] Description=Anytype Sync Server (any-sync-bundle) -After=network-online.target +After=network-online.target mongod.service redis-stack-server.service Wants=network-online.target +Requires=mongod.service redis-stack-server.service [Service] Type=simple User=root WorkingDirectory=/opt/anytype EnvironmentFile=/opt/anytype/.env -ExecStart=/opt/anytype/any-sync-bundle start-all-in-one +ExecStart=/opt/anytype/any-sync-bundle start-bundle Restart=on-failure RestartSec=10 From 9d77145a7e02b9f4c9fe852ec12c4baca9fa4623 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 11:22:49 +0100 Subject: [PATCH 204/228] Refactor OpenCloud installer to simplify Collabora handling Reworks opencloud-install.sh to remove localhost mode, always require FQDNs, and streamline Collabora and WOPI configuration. The script now always installs Collabora and WOPI, sets up environment variables accordingly, and simplifies service management. The previous localhost/IP-based mode is preserved in opencloud-install.sh.bak for reference. --- install/opencloud-install.sh | 139 ++++++---------- ...stall copy.sh => opencloud-install.sh.bak} | 155 ++++++++++++------ 2 files changed, 155 insertions(+), 139 deletions(-) rename install/{opencloud-install copy.sh => opencloud-install.sh.bak} (56%) diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 943578598..7d4c9a060 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -13,48 +13,48 @@ setting_up_container network_check update_os -echo -e "${TAB3}${INFO}${YW} Leave empty to use IP-based localhost mode (no Collabora)${CL}" read -r -p "${TAB3}Enter the hostname of your OpenCloud server (eg cloud.domain.tld): " oc_host - -if [[ -z "$oc_host" ]]; then - # Localhost/IP mode - no TLS, no Collabora - OC_HOST="${LOCAL_IP}" - LOCALHOST_MODE=true - msg_info "Using localhost mode with IP: ${LOCAL_IP}" - msg_warn "Collabora requires TLS and will be skipped in localhost mode" -else +if [[ "$oc_host" ]]; then OC_HOST="$oc_host" - LOCALHOST_MODE=false - read -r -p "${TAB3}Enter the hostname of your Collabora server [collabora.${OC_HOST#*.}]: " collabora_host - COLLABORA_HOST="${collabora_host:-collabora.${OC_HOST#*.}}" - read -r -p "${TAB3}Enter the hostname of your WOPI server [wopiserver.${OC_HOST#*.}]: " wopi_host - WOPI_HOST="${wopi_host:-wopiserver.${OC_HOST#*.}}" +fi +read -r -p "${TAB3}Enter the hostname of your Collabora server (eg collabora.domain.tld): " collabora_host +if [[ "$collabora_host" ]]; then + COLLABORA_HOST="$collabora_host" +fi +read -r -p "${TAB3}Enter the hostname of your WOPI server (eg wopiserver.domain.tld): " wopi_host +if [[ "$wopi_host" ]]; then + WOPI_HOST="$wopi_host" fi -# Collabora Online - only install if not in localhost mode (requires TLS) -if [[ "$LOCALHOST_MODE" != true ]]; then - msg_info "Installing Collabora Online" - curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg - cat </etc/apt/sources.list.d/collaboraonline.sources +# Collabora online - this is broken because it adds the Component and apt doesn't like that +# setup_deb822_repo \ +# "collaboraonline" \ +# "https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg" \ +# "https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb/Release" \ +# "./" \ +# "main" + +msg_info "Installing Collabora Online" +curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg +cat </etc/apt/sources.list.d/colloboraonline.sources Types: deb URIs: https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb Suites: ./ Signed-By: /etc/apt/keyrings/collaboraonline-release-keyring.gpg EOF - $STD apt-get update - $STD apt-get install -y coolwsd code-brand - systemctl stop coolwsd - mkdir -p /etc/systemd/system/coolwsd.service.d - cat </etc/systemd/system/coolwsd.service.d/override.conf +$STD apt-get update +$STD apt-get install -y coolwsd code-brand +systemctl stop coolwsd +mkdir -p /etc/systemd/system/coolwsd.service.d +cat </etc/systemd/system/coolwsd.service.d/override.conf [Unit] Before=opencloud-wopi.service EOF - systemctl daemon-reload - COOLPASS="$(openssl rand -base64 36)" - $STD runuser -u cool -- coolconfig set-admin-password --user=admin --password="$COOLPASS" - echo "$COOLPASS" >~/.coolpass - msg_ok "Installed Collabora Online" -fi +systemctl daemon-reload +COOLPASS="$(openssl rand -base64 36)" +$STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPASS" +echo "$COOLPASS" >~/.coolpass +msg_ok "Installed Collabora Online" # OpenCloud fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.1" "/usr/bin" "opencloud-*-linux-amd64" @@ -68,17 +68,9 @@ mkdir -p "$DATA_DIR" "$CONFIG_DIR"/assets/apps curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/csp.yaml -o "$CONFIG_DIR"/csp.yaml curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak -if [[ "$LOCALHOST_MODE" == true ]]; then - OC_URL="http://${OC_HOST}:9200" - OC_INSECURE="true" -else - OC_URL="https://${OC_HOST}" - OC_INSECURE="false" -fi - cat <"$ENV_FILE" -OC_URL=${OC_URL} -OC_INSECURE=${OC_INSECURE} +OC_URL=https://${OC_HOST} +OC_INSECURE=false IDM_CREATE_DEMO_USERS=false OC_LOG_LEVEL=warning OC_CONFIG_DIR=${CONFIG_DIR} @@ -101,15 +93,15 @@ GRAPH_INCLUDE_OCM_SHAREES=true PROXY_TLS=false PROXY_CSP_CONFIG_FILE_LOCATION=${CONFIG_DIR}/csp.yaml -## Collaboration - requires VALID TLS (disabled in localhost mode) -# COLLABORA_DOMAIN= -# COLLABORATION_APP_NAME="CollaboraOnline" -# COLLABORATION_APP_PRODUCT="Collabora" -# COLLABORATION_APP_ADDR= -# COLLABORATION_APP_INSECURE=false -# COLLABORATION_HTTP_ADDR=0.0.0.0:9300 -# COLLABORATION_WOPI_SRC= -# COLLABORATION_JWT_SECRET= +## Collaboration - requires VALID TLS +COLLABORA_DOMAIN=${COLLABORA_HOST} +COLLABORATION_APP_NAME="CollaboraOnline" +COLLABORATION_APP_PRODUCT="Collabora" +COLLABORATION_APP_ADDR=https://${COLLABORA_HOST} +COLLABORATION_APP_INSECURE=false +COLLABORATION_HTTP_ADDR=0.0.0.0:9300 +COLLABORATION_WOPI_SRC=https://${WOPI_HOST} +COLLABORATION_JWT_SECRET= ## Notifications - Email settings # NOTIFICATIONS_SMTP_HOST= @@ -174,8 +166,7 @@ Restart=always WantedBy=multi-user.target EOF -if [[ "$LOCALHOST_MODE" != true ]]; then - cat </etc/systemd/system/opencloud-wopi.service +cat </etc/systemd/system/opencloud-wopi.service [Unit] Description=OpenCloud WOPI Server Wants=coolwsd.service @@ -197,49 +188,21 @@ TimeoutStopSec=10 WantedBy=multi-user.target EOF - # Append active Collabora config to env file - cat <>"$ENV_FILE" - -## Collaboration - active configuration -COLLABORA_DOMAIN=${COLLABORA_HOST} -COLLABORATION_APP_NAME="CollaboraOnline" -COLLABORATION_APP_PRODUCT="Collabora" -COLLABORATION_APP_ADDR=https://${COLLABORA_HOST} -COLLABORATION_APP_INSECURE=false -COLLABORATION_HTTP_ADDR=0.0.0.0:9300 -COLLABORATION_WOPI_SRC=https://${WOPI_HOST} -COLLABORATION_JWT_SECRET= -EOF - - $STD runuser -u cool -- coolconfig set ssl.enable false - $STD runuser -u cool -- coolconfig set ssl.termination true - $STD runuser -u cool -- coolconfig set ssl.ssl_verification true - sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OC_HOST}|" /etc/coolwsd/coolwsd.xml -fi - +$STD sudo -u cool coolconfig set ssl.enable false +$STD sudo -u cool coolconfig set ssl.termination true +$STD sudo -u cool coolconfig set ssl.ssl_verification true +sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OC_HOST}|" /etc/coolwsd/coolwsd.xml useradd -r -M -s /usr/sbin/nologin opencloud chown -R opencloud:opencloud "$CONFIG_DIR" "$DATA_DIR" - -if [[ "$LOCALHOST_MODE" == true ]]; then - $STD runuser -u opencloud -- opencloud init --config-path "$CONFIG_DIR" --insecure yes -else - $STD runuser -u opencloud -- opencloud init --config-path "$CONFIG_DIR" --insecure no -fi - +sudo -u opencloud opencloud init --config-path "$CONFIG_DIR" --insecure no OPENCLOUD_SECRET="$(sed -n '/jwt/p' "$CONFIG_DIR"/opencloud.yaml | awk '{print $2}')" -if [[ "$LOCALHOST_MODE" != true ]]; then - sed -i "s/COLLABORATION_JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" -fi +sed -i "s/JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" msg_ok "Configured OpenCloud" msg_info "Starting services" -if [[ "$LOCALHOST_MODE" == true ]]; then - systemctl enable -q --now opencloud -else - systemctl enable -q --now coolwsd opencloud - sleep 5 - systemctl enable -q --now opencloud-wopi -fi +systemctl enable -q --now coolwsd opencloud +sleep 5 +systemctl enable -q --now opencloud-wopi msg_ok "Started services" motd_ssh diff --git a/install/opencloud-install copy.sh b/install/opencloud-install.sh.bak similarity index 56% rename from install/opencloud-install copy.sh rename to install/opencloud-install.sh.bak index 7d4c9a060..0ed6cc619 100644 --- a/install/opencloud-install copy.sh +++ b/install/opencloud-install.sh.bak @@ -13,48 +13,48 @@ setting_up_container network_check update_os +echo -e "${TAB3}${INFO}${YW} Leave empty to use IP-based localhost mode (no Collabora)${CL}" read -r -p "${TAB3}Enter the hostname of your OpenCloud server (eg cloud.domain.tld): " oc_host -if [[ "$oc_host" ]]; then + +if [[ -z "$oc_host" ]]; then + # Localhost/IP mode - no TLS, no Collabora + OC_HOST="${LOCAL_IP}" + LOCALHOST_MODE=true + msg_info "Using localhost mode with IP: ${LOCAL_IP}" + msg_warn "Collabora requires TLS and will be skipped in localhost mode" +else OC_HOST="$oc_host" -fi -read -r -p "${TAB3}Enter the hostname of your Collabora server (eg collabora.domain.tld): " collabora_host -if [[ "$collabora_host" ]]; then - COLLABORA_HOST="$collabora_host" -fi -read -r -p "${TAB3}Enter the hostname of your WOPI server (eg wopiserver.domain.tld): " wopi_host -if [[ "$wopi_host" ]]; then - WOPI_HOST="$wopi_host" + LOCALHOST_MODE=false + read -r -p "${TAB3}Enter the hostname of your Collabora server [collabora.${OC_HOST#*.}]: " collabora_host + COLLABORA_HOST="${collabora_host:-collabora.${OC_HOST#*.}}" + read -r -p "${TAB3}Enter the hostname of your WOPI server [wopiserver.${OC_HOST#*.}]: " wopi_host + WOPI_HOST="${wopi_host:-wopiserver.${OC_HOST#*.}}" fi -# Collabora online - this is broken because it adds the Component and apt doesn't like that -# setup_deb822_repo \ -# "collaboraonline" \ -# "https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg" \ -# "https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb/Release" \ -# "./" \ -# "main" - -msg_info "Installing Collabora Online" -curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg -cat </etc/apt/sources.list.d/colloboraonline.sources +# Collabora Online - only install if not in localhost mode (requires TLS) +if [[ "$LOCALHOST_MODE" != true ]]; then + msg_info "Installing Collabora Online" + curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg + cat </etc/apt/sources.list.d/collaboraonline.sources Types: deb URIs: https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb Suites: ./ Signed-By: /etc/apt/keyrings/collaboraonline-release-keyring.gpg EOF -$STD apt-get update -$STD apt-get install -y coolwsd code-brand -systemctl stop coolwsd -mkdir -p /etc/systemd/system/coolwsd.service.d -cat </etc/systemd/system/coolwsd.service.d/override.conf + $STD apt-get update + $STD apt-get install -y coolwsd code-brand + systemctl stop coolwsd + mkdir -p /etc/systemd/system/coolwsd.service.d + cat </etc/systemd/system/coolwsd.service.d/override.conf [Unit] Before=opencloud-wopi.service EOF -systemctl daemon-reload -COOLPASS="$(openssl rand -base64 36)" -$STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPASS" -echo "$COOLPASS" >~/.coolpass -msg_ok "Installed Collabora Online" + systemctl daemon-reload + COOLPASS="$(openssl rand -base64 36)" + $STD runuser -u cool -- coolconfig set-admin-password --user=admin --password="$COOLPASS" + echo "$COOLPASS" >~/.coolpass + msg_ok "Installed Collabora Online" +fi # OpenCloud fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.0.1" "/usr/bin" "opencloud-*-linux-amd64" @@ -68,9 +68,33 @@ mkdir -p "$DATA_DIR" "$CONFIG_DIR"/assets/apps curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/csp.yaml -o "$CONFIG_DIR"/csp.yaml curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak +if [[ "$LOCALHOST_MODE" == true ]]; then + OC_URL="http://${OC_HOST}:9200" + OC_INSECURE="true" +else + OC_URL="https://${OC_HOST}" + OC_INSECURE="false" +fi + +# Create web config directory and config.json +mkdir -p "$CONFIG_DIR"/web +cat <"$CONFIG_DIR"/web/config.json +{ + "server": "${OC_URL}", + "theme": "https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/web/themes/opencloud/theme.json", + "openIdConnect": { + "metadata_url": "${OC_URL}/.well-known/openid-configuration", + "authority": "${OC_URL}", + "client_id": "web", + "response_type": "code", + "scope": "openid profile email" + } +} +EOF + cat <"$ENV_FILE" -OC_URL=https://${OC_HOST} -OC_INSECURE=false +OC_URL=${OC_URL} +OC_INSECURE=${OC_INSECURE} IDM_CREATE_DEMO_USERS=false OC_LOG_LEVEL=warning OC_CONFIG_DIR=${CONFIG_DIR} @@ -93,15 +117,15 @@ GRAPH_INCLUDE_OCM_SHAREES=true PROXY_TLS=false PROXY_CSP_CONFIG_FILE_LOCATION=${CONFIG_DIR}/csp.yaml -## Collaboration - requires VALID TLS -COLLABORA_DOMAIN=${COLLABORA_HOST} -COLLABORATION_APP_NAME="CollaboraOnline" -COLLABORATION_APP_PRODUCT="Collabora" -COLLABORATION_APP_ADDR=https://${COLLABORA_HOST} -COLLABORATION_APP_INSECURE=false -COLLABORATION_HTTP_ADDR=0.0.0.0:9300 -COLLABORATION_WOPI_SRC=https://${WOPI_HOST} -COLLABORATION_JWT_SECRET= +## Collaboration - requires VALID TLS (disabled in localhost mode) +# COLLABORA_DOMAIN= +# COLLABORATION_APP_NAME="CollaboraOnline" +# COLLABORATION_APP_PRODUCT="Collabora" +# COLLABORATION_APP_ADDR= +# COLLABORATION_APP_INSECURE=false +# COLLABORATION_HTTP_ADDR=0.0.0.0:9300 +# COLLABORATION_WOPI_SRC= +# COLLABORATION_JWT_SECRET= ## Notifications - Email settings # NOTIFICATIONS_SMTP_HOST= @@ -166,7 +190,8 @@ Restart=always WantedBy=multi-user.target EOF -cat </etc/systemd/system/opencloud-wopi.service +if [[ "$LOCALHOST_MODE" != true ]]; then + cat </etc/systemd/system/opencloud-wopi.service [Unit] Description=OpenCloud WOPI Server Wants=coolwsd.service @@ -188,21 +213,49 @@ TimeoutStopSec=10 WantedBy=multi-user.target EOF -$STD sudo -u cool coolconfig set ssl.enable false -$STD sudo -u cool coolconfig set ssl.termination true -$STD sudo -u cool coolconfig set ssl.ssl_verification true -sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OC_HOST}|" /etc/coolwsd/coolwsd.xml + # Append active Collabora config to env file + cat <>"$ENV_FILE" + +## Collaboration - active configuration +COLLABORA_DOMAIN=${COLLABORA_HOST} +COLLABORATION_APP_NAME="CollaboraOnline" +COLLABORATION_APP_PRODUCT="Collabora" +COLLABORATION_APP_ADDR=https://${COLLABORA_HOST} +COLLABORATION_APP_INSECURE=false +COLLABORATION_HTTP_ADDR=0.0.0.0:9300 +COLLABORATION_WOPI_SRC=https://${WOPI_HOST} +COLLABORATION_JWT_SECRET= +EOF + + $STD runuser -u cool -- coolconfig set ssl.enable false + $STD runuser -u cool -- coolconfig set ssl.termination true + $STD runuser -u cool -- coolconfig set ssl.ssl_verification true + sed -i "s|CSP2\"/>|CSP2\">frame-ancestors https://${OC_HOST}|" /etc/coolwsd/coolwsd.xml +fi + useradd -r -M -s /usr/sbin/nologin opencloud chown -R opencloud:opencloud "$CONFIG_DIR" "$DATA_DIR" -sudo -u opencloud opencloud init --config-path "$CONFIG_DIR" --insecure no + +if [[ "$LOCALHOST_MODE" == true ]]; then + $STD runuser -u opencloud -- opencloud init --config-path "$CONFIG_DIR" --insecure yes +else + $STD runuser -u opencloud -- opencloud init --config-path "$CONFIG_DIR" --insecure no +fi + OPENCLOUD_SECRET="$(sed -n '/jwt/p' "$CONFIG_DIR"/opencloud.yaml | awk '{print $2}')" -sed -i "s/JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" +if [[ "$LOCALHOST_MODE" != true ]]; then + sed -i "s/COLLABORATION_JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" +fi msg_ok "Configured OpenCloud" msg_info "Starting services" -systemctl enable -q --now coolwsd opencloud -sleep 5 -systemctl enable -q --now opencloud-wopi +if [[ "$LOCALHOST_MODE" == true ]]; then + systemctl enable -q --now opencloud +else + systemctl enable -q --now coolwsd opencloud + sleep 5 + systemctl enable -q --now opencloud-wopi +fi msg_ok "Started services" motd_ssh From b4af893e66a47a6a82b9d3186360a16d54651e33 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:25:01 +0100 Subject: [PATCH 205/228] add features to valkey --- install/alpine-valkey-install.sh | 58 ++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index 66501b3eb..5e09e7d1c 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -14,11 +14,63 @@ network_check update_os msg_info "Installing Valkey" -$STD apk add valkey valkey-openrc valkey-cli -$STD sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf +$STD apk add valkey valkey-openrc valkey-cli openssl +sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf + +PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" +echo "requirepass $PASS" >>/etc/valkey/valkey.conf +echo "$PASS" >~/valkey.creds +chmod 600 ~/valkey.creds + +MEMTOTAL_MB=$(free -m | grep ^Mem: | awk '{print $2}') +MAXMEMORY_MB=$((MEMTOTAL_MB * 75 / 100)) + +{ + echo "" + echo "# Memory-optimized settings for small-scale deployments" + echo "maxmemory ${MAXMEMORY_MB}mb" + echo "maxmemory-policy allkeys-lru" + echo "maxmemory-samples 10" +} >>/etc/valkey/valkey.conf +msg_ok "Installed Valkey" + +echo +read -r -p "${TAB3}Enable TLS for Valkey (Sentinel mode not supported)? [y/N]: " prompt +if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + read -r -p "${TAB3}Use TLS-only mode (disable TCP port 6379)? [y/N]: " tls_only + msg_info "Configuring TLS for Valkey..." + + create_self_signed_cert "Valkey" + TLS_DIR="/etc/ssl/valkey" + chown valkey:valkey "$TLS_DIR/valkey.crt" "$TLS_DIR/valkey.key" + + if [[ ${tls_only,,} =~ ^(y|yes)$ ]]; then + { + echo "" + echo "# TLS configuration generated by Proxmox VE Valkey helper-script" + echo "port 0" + echo "tls-port 6379" + echo "tls-cert-file $TLS_DIR/valkey.crt" + echo "tls-key-file $TLS_DIR/valkey.key" + echo "tls-auth-clients no" + } >>/etc/valkey/valkey.conf + msg_ok "Enabled TLS-only mode on port 6379" + else + { + echo "" + echo "# TLS configuration generated by Proxmox VE Valkey helper-script" + echo "tls-port 6380" + echo "tls-cert-file $TLS_DIR/valkey.crt" + echo "tls-key-file $TLS_DIR/valkey.key" + echo "tls-auth-clients no" + } >>/etc/valkey/valkey.conf + msg_ok "Enabled TLS on port 6380 and TCP on 6379" + fi +fi + $STD rc-update add valkey default $STD rc-service valkey start -msg_ok "Installed Valkey" motd_ssh customize +cleanup_lxc From a2bbf6c75f69e3f4b3fa0ee8a43fe69da201b2eb Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:26:20 +0100 Subject: [PATCH 206/228] ssl --- install/alpine-valkey-install.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index 5e09e7d1c..26571cb72 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -40,8 +40,15 @@ if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then read -r -p "${TAB3}Use TLS-only mode (disable TCP port 6379)? [y/N]: " tls_only msg_info "Configuring TLS for Valkey..." - create_self_signed_cert "Valkey" TLS_DIR="/etc/ssl/valkey" + mkdir -p "$TLS_DIR" + $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ + -subj "/CN=Valkey" \ + -addext "subjectAltName=DNS:Valkey" \ + -keyout "$TLS_DIR/valkey.key" \ + -out "$TLS_DIR/valkey.crt" + chmod 600 "$TLS_DIR/valkey.key" + chmod 644 "$TLS_DIR/valkey.crt" chown valkey:valkey "$TLS_DIR/valkey.crt" "$TLS_DIR/valkey.key" if [[ ${tls_only,,} =~ ^(y|yes)$ ]]; then From 8d649bbb8c7e7e52f7af9884ae7100b9fed9255e Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:43:51 +0100 Subject: [PATCH 207/228] clear valkey --- install/alpine-valkey-install.sh | 46 +++----------------------------- 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index 26571cb72..ea2d17451 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -14,10 +14,10 @@ network_check update_os msg_info "Installing Valkey" -$STD apk add valkey valkey-openrc valkey-cli openssl +$STD apk add valkey valkey-openrc valkey-cli sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf -PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" +PASS="$(tr -dc 'a-zA-Z0-9' >/etc/valkey/valkey.conf echo "$PASS" >~/valkey.creds chmod 600 ~/valkey.creds @@ -34,46 +34,8 @@ MAXMEMORY_MB=$((MEMTOTAL_MB * 75 / 100)) } >>/etc/valkey/valkey.conf msg_ok "Installed Valkey" -echo -read -r -p "${TAB3}Enable TLS for Valkey (Sentinel mode not supported)? [y/N]: " prompt -if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - read -r -p "${TAB3}Use TLS-only mode (disable TCP port 6379)? [y/N]: " tls_only - msg_info "Configuring TLS for Valkey..." - - TLS_DIR="/etc/ssl/valkey" - mkdir -p "$TLS_DIR" - $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/CN=Valkey" \ - -addext "subjectAltName=DNS:Valkey" \ - -keyout "$TLS_DIR/valkey.key" \ - -out "$TLS_DIR/valkey.crt" - chmod 600 "$TLS_DIR/valkey.key" - chmod 644 "$TLS_DIR/valkey.crt" - chown valkey:valkey "$TLS_DIR/valkey.crt" "$TLS_DIR/valkey.key" - - if [[ ${tls_only,,} =~ ^(y|yes)$ ]]; then - { - echo "" - echo "# TLS configuration generated by Proxmox VE Valkey helper-script" - echo "port 0" - echo "tls-port 6379" - echo "tls-cert-file $TLS_DIR/valkey.crt" - echo "tls-key-file $TLS_DIR/valkey.key" - echo "tls-auth-clients no" - } >>/etc/valkey/valkey.conf - msg_ok "Enabled TLS-only mode on port 6379" - else - { - echo "" - echo "# TLS configuration generated by Proxmox VE Valkey helper-script" - echo "tls-port 6380" - echo "tls-cert-file $TLS_DIR/valkey.crt" - echo "tls-key-file $TLS_DIR/valkey.key" - echo "tls-auth-clients no" - } >>/etc/valkey/valkey.conf - msg_ok "Enabled TLS on port 6380 and TCP on 6379" - fi -fi +# Note: Alpine's valkey package is compiled without TLS support +# For TLS, use the Debian-based valkey script instead $STD rc-update add valkey default $STD rc-service valkey start From bd5dd98b86df2da8f87f249e0c47748dfb9ba001 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:50:15 +0100 Subject: [PATCH 208/228] Update alpine-valkey-install.sh --- install/alpine-valkey-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh index ea2d17451..5beda4860 100644 --- a/install/alpine-valkey-install.sh +++ b/install/alpine-valkey-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Valkey" $STD apk add valkey valkey-openrc valkey-cli sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf -PASS="$(tr -dc 'a-zA-Z0-9' >/etc/valkey/valkey.conf echo "$PASS" >~/valkey.creds chmod 600 ~/valkey.creds From 669dfa52cf8bbc35faa46bbfda0065eddaca4a97 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:53:19 +0100 Subject: [PATCH 209/228] remove valkey --- install/alpine-valkey-install.sh | 45 -------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 install/alpine-valkey-install.sh diff --git a/install/alpine-valkey-install.sh b/install/alpine-valkey-install.sh deleted file mode 100644 index 5beda4860..000000000 --- a/install/alpine-valkey-install.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2026 community-scripts ORG -# Author: pshankinclarke (lazarillo) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://valkey.io/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Valkey" -$STD apk add valkey valkey-openrc valkey-cli -sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf - -PASS="$(head -c 100 /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c32)" -echo "requirepass $PASS" >>/etc/valkey/valkey.conf -echo "$PASS" >~/valkey.creds -chmod 600 ~/valkey.creds - -MEMTOTAL_MB=$(free -m | grep ^Mem: | awk '{print $2}') -MAXMEMORY_MB=$((MEMTOTAL_MB * 75 / 100)) - -{ - echo "" - echo "# Memory-optimized settings for small-scale deployments" - echo "maxmemory ${MAXMEMORY_MB}mb" - echo "maxmemory-policy allkeys-lru" - echo "maxmemory-samples 10" -} >>/etc/valkey/valkey.conf -msg_ok "Installed Valkey" - -# Note: Alpine's valkey package is compiled without TLS support -# For TLS, use the Debian-based valkey script instead - -$STD rc-update add valkey default -$STD rc-service valkey start - -motd_ssh -customize -cleanup_lxc From 448c1d6cafcf3ba98fdf71879a3f244907932b24 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:53:27 +0100 Subject: [PATCH 210/228] remove valkey --- ct/alpine-valkey.sh | 73 --------------------------------------------- 1 file changed, 73 deletions(-) delete mode 100644 ct/alpine-valkey.sh diff --git a/ct/alpine-valkey.sh b/ct/alpine-valkey.sh deleted file mode 100644 index 2765aff7f..000000000 --- a/ct/alpine-valkey.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2026 community-scripts ORG -# Author: pshankinclarke (lazarillo) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://valkey.io/ - -APP="Alpine-Valkey" -var_tags="${var_tags:-alpine;database}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-256}" -var_disk="${var_disk:-1}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - if ! apk -e info newt >/dev/null 2>&1; then - apk add -q newt - fi - LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - while true; do - CHOICE=$( - whiptail --backtitle "Proxmox VE Helper Scripts" --title "Valkey Management" --menu "Select option" 11 58 3 \ - "1" "Update Valkey" \ - "2" "Allow 0.0.0.0 for listening" \ - "3" "Allow only ${LXCIP} for listening" 3>&2 2>&1 1>&3 - ) - exit_status=$? - if [ $exit_status == 1 ]; then - clear - exit-script - fi - header_info - case $CHOICE in - 1) - msg_info "Updating Valkey" - apk update && apk upgrade valkey - rc-service valkey restart - msg_ok "Updated Valkey" - msg_ok "Updated successfully!" - exit - ;; - 2) - msg_info "Setting Valkey to listen on all interfaces" - sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf - rc-service valkey restart - msg_ok "Valkey now listens on all interfaces!" - exit - ;; - 3) - msg_info "Setting Valkey to listen only on ${LXCIP}" - sed -i "s/^bind .*/bind ${LXCIP}/" /etc/valkey/valkey.conf - rc-service valkey restart - msg_ok "Valkey now listens only on ${LXCIP}!" - exit - ;; - esac - done -} - -start -build_container -description - -msg_ok "Completed successfully!\n" -echo -e "${APP} should be reachable on port 6379. - ${BL}valkey-cli -h ${IP} -p 6379${CL} \n" From ff1cb4e9842d8d981e643c5be77d7b97b7bd11ca Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:43:45 +0100 Subject: [PATCH 211/228] Remove unused import_local_ip calls from install scripts Eliminated redundant or unused calls to import_local_ip across multiple install and update scripts to clean up the codebase. No functional changes were made to the installation or update processes. --- ct/ampache.sh | 2 +- install/affine-install.sh | 2 +- install/deferred/docspell-install.sh | 2 +- install/ente-install.sh | 2 +- install/linkwarden-install.sh | 2 +- install/piler-install.sh | 2 +- install/pixelfed-install.sh | 2 +- install/wishlist-install.sh | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ct/ampache.sh b/ct/ampache.sh index ef6bb7903..1c4d6415e 100644 --- a/ct/ampache.sh +++ b/ct/ampache.sh @@ -29,7 +29,7 @@ function update_script() { exit fi if check_for_gh_release "Ampache" "ampache/ampache"; then - import_local_ip + msg_info "Stopping Apache" systemctl stop apache2 msg_ok "Stopped Apache" diff --git a/install/affine-install.sh b/install/affine-install.sh index ce3cb9fac..5cce12462 100644 --- a/install/affine-install.sh +++ b/install/affine-install.sh @@ -29,7 +29,7 @@ PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql PG_DB_NAME="affine" PG_DB_USER="affine" setup_postgresql_db NODE_VERSION="22" setup_nodejs setup_rust -import_local_ip + fetch_and_deploy_gh_release "affine_app" "toeverything/AFFiNE" "tarball" "latest" "/opt/affine" diff --git a/install/deferred/docspell-install.sh b/install/deferred/docspell-install.sh index 794e4c9bb..3738c816b 100644 --- a/install/deferred/docspell-install.sh +++ b/install/deferred/docspell-install.sh @@ -14,7 +14,7 @@ update_os msg_info "Setup Functions" setup_local_ip_helper -import_local_ip + msg_ok "Setup Functions" msg_info "Installing Dependencies (Patience)" diff --git a/install/ente-install.sh b/install/ente-install.sh index a6028e3cb..e1b9298e7 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -28,7 +28,7 @@ setup_go NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs RUST_CRATES="wasm-pack" setup_rust $STD rustup target add wasm32-unknown-unknown -import_local_ip + ENTE_CLI_VERSION=$(curl -s https://api.github.com/repos/ente-io/ente/releases | jq -r '[.[] | select(.tag_name | startswith("cli-v"))][0].tag_name') fetch_and_deploy_gh_release "ente-server" "ente-io/ente" "tarball" "latest" "/opt/ente" diff --git a/install/linkwarden-install.sh b/install/linkwarden-install.sh index 8f4b569a5..3808c8426 100644 --- a/install/linkwarden-install.sh +++ b/install/linkwarden-install.sh @@ -22,7 +22,7 @@ PG_VERSION="16" setup_postgresql PG_DB_NAME="linkwardendb" PG_DB_USER="linkwarden" setup_postgresql_db RUST_CRATES="monolith" setup_rust fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" -import_local_ip + read -r -p "${TAB3}Would you like to add Adminer? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then diff --git a/install/piler-install.sh b/install/piler-install.sh index 1eeadff04..0f9c92bc7 100644 --- a/install/piler-install.sh +++ b/install/piler-install.sh @@ -30,7 +30,7 @@ $STD apt install -y \ gnupg msg_ok "Installed Dependencies" -import_local_ip + setup_mariadb MARIADB_DB_NAME="piler" MARIADB_DB_USER="piler" setup_mariadb_db PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="ldap,gd,memcached,pdo,mysql,curl,zip" setup_php diff --git a/install/pixelfed-install.sh b/install/pixelfed-install.sh index f2b9ee78e..923068139 100644 --- a/install/pixelfed-install.sh +++ b/install/pixelfed-install.sh @@ -29,7 +29,7 @@ useradd -rU -s /bin/bash pixelfed usermod -aG redis pixelfed msg_ok "Created Pixelfed User" -import_local_ip + PG_VERSION="17" setup_postgresql PG_DB_NAME="pixelfed" PG_DB_USER="pixelfed" setup_postgresql_db PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="bcmath,ctype,exif,imagick,pgsql,redis,tokenizer" PHP_UPLOAD_MAX_FILESIZE="500M" PHP_POST_MAX_SIZE="500M" PHP_MAX_EXECUTION_TIME="600" setup_php diff --git a/install/wishlist-install.sh b/install/wishlist-install.sh index 5203d2731..659439f74 100644 --- a/install/wishlist-install.sh +++ b/install/wishlist-install.sh @@ -24,7 +24,7 @@ msg_ok "Installed dependencies" NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist" false) -import_local_ip + msg_info "Installing Wishlist" cd /opt/wishlist From 4cf56f38358169f0d834ee361d6fb9c9b85012b8 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:45:17 +0100 Subject: [PATCH 212/228] test databasus --- install/databasus-install.sh | 71 ++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/install/databasus-install.sh b/install/databasus-install.sh index fbac56054..2d4f06743 100644 --- a/install/databasus-install.sh +++ b/install/databasus-install.sh @@ -17,12 +17,19 @@ msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" -import_local_ip PG_VERSION="17" setup_postgresql -PG_DB_NAME="databasus" PG_DB_USER="databasus" setup_postgresql_db setup_go NODE_VERSION="24" setup_nodejs +msg_info "Installing Valkey" +setup_deb822_repo "valkey" \ + "https://greensec.github.io/valkey-debian/public.key" \ + "https://greensec.github.io/valkey-debian/repo" \ + "$(lsb_release -cs)" \ + "main" +$STD apt-get install -y valkey +msg_ok "Installed Valkey" + fetch_and_deploy_gh_release "databasus" "databasus/databasus" "tarball" "latest" "/opt/databasus" msg_info "Building Databasus (Patience)" @@ -36,19 +43,17 @@ $STD go install github.com/swaggo/swag/cmd/swag@latest $STD /root/go/bin/swag init -g cmd/main.go -o swagger $STD env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o databasus ./cmd/main.go mv /opt/databasus/backend/databasus /opt/databasus/databasus -mkdir -p /opt/databasus_data/{data,backups,logs} -mkdir -p /databasus-data/temp +mkdir -p /databasus-data/{pgdata,temp,backups,data,logs} mkdir -p /opt/databasus/ui/build +mkdir -p /opt/databasus/migrations cp -r /opt/databasus/frontend/dist/* /opt/databasus/ui/build/ -cp -r /opt/databasus/backend/migrations /opt/databasus/ -chown -R postgres:postgres /opt/databasus -chown -R postgres:postgres /opt/databasus_data +cp -r /opt/databasus/backend/migrations/* /opt/databasus/migrations/ chown -R postgres:postgres /databasus-data msg_ok "Built Databasus" msg_info "Configuring Databasus" -ADMIN_PASS=$(openssl rand -base64 12) JWT_SECRET=$(openssl rand -hex 32) +ENCRYPTION_KEY=$(openssl rand -hex 32) # Create PostgreSQL version symlinks for compatibility for v in 12 13 14 15 16 18; do @@ -67,50 +72,54 @@ ENV_MODE=production SERVER_PORT=4005 SERVER_HOST=0.0.0.0 -# Database (Internal PostgreSQL for app data) -DATABASE_DSN=host=localhost user=${PG_DB_USER} password=${PG_DB_PASS} dbname=${PG_DB_NAME} port=5432 sslmode=disable -DATABASE_URL=postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}?sslmode=disable +# Database +DATABASE_DSN=host=localhost user=postgres password=postgres dbname=databasus port=5432 sslmode=disable +DATABASE_URL=postgres://postgres:postgres@localhost:5432/databasus?sslmode=disable # Migrations GOOSE_DRIVER=postgres -GOOSE_DBSTRING=postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}?sslmode=disable +GOOSE_DBSTRING=postgres://postgres:postgres@localhost:5432/databasus?sslmode=disable GOOSE_MIGRATION_DIR=/opt/databasus/migrations # Security JWT_SECRET=${JWT_SECRET} -ENCRYPTION_KEY=$(openssl rand -hex 32) - -# Admin User -ADMIN_EMAIL=admin@localhost -ADMIN_PASSWORD=${ADMIN_PASS} +ENCRYPTION_KEY=${ENCRYPTION_KEY} # Paths -DATA_DIR=/opt/databasus_data/data -BACKUP_DIR=/opt/databasus_data/backups -LOG_DIR=/opt/databasus_data/logs - -# PostgreSQL Tools (for creating backups) -PG_DUMP_PATH=/usr/lib/postgresql/17/bin/pg_dump -PG_RESTORE_PATH=/usr/lib/postgresql/17/bin/pg_restore -PSQL_PATH=/usr/lib/postgresql/17/bin/psql +DATA_DIR=/databasus-data/data +BACKUP_DIR=/databasus-data/backups +LOG_DIR=/databasus-data/logs EOF chown postgres:postgres /opt/databasus/.env chmod 600 /opt/databasus/.env msg_ok "Configured Databasus" +msg_info "Configuring Valkey" +cat >/etc/valkey/valkey.conf </dev/null || true +msg_ok "Created Database" + msg_info "Creating Databasus Service" cat </etc/systemd/system/databasus.service [Unit] -Description=Databasus - PostgreSQL Backup Management -After=network.target postgresql.service -Requires=postgresql.service +Description=Databasus - Database Backup Management +After=network.target postgresql.service valkey.service +Requires=postgresql.service valkey.service [Service] Type=simple -User=postgres -Group=postgres WorkingDirectory=/opt/databasus -Environment="PATH=/usr/local/bin:/usr/bin:/bin" EnvironmentFile=/opt/databasus/.env ExecStart=/opt/databasus/databasus Restart=always From 84b9763632e29075f75b5d6285ad266a72ad617d Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:26:07 +0100 Subject: [PATCH 213/228] valkey fix --- install/databasus-install.sh | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/install/databasus-install.sh b/install/databasus-install.sh index 2d4f06743..82238035a 100644 --- a/install/databasus-install.sh +++ b/install/databasus-install.sh @@ -14,22 +14,15 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y nginx +$STD apt install -y \ + nginx \ + valkey msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql setup_go NODE_VERSION="24" setup_nodejs -msg_info "Installing Valkey" -setup_deb822_repo "valkey" \ - "https://greensec.github.io/valkey-debian/public.key" \ - "https://greensec.github.io/valkey-debian/repo" \ - "$(lsb_release -cs)" \ - "main" -$STD apt-get install -y valkey -msg_ok "Installed Valkey" - fetch_and_deploy_gh_release "databasus" "databasus/databasus" "tarball" "latest" "/opt/databasus" msg_info "Building Databasus (Patience)" From 670e74b1367089bfb4417ea7c12733f81940dac4 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:44:45 +0100 Subject: [PATCH 214/228] valkey fix --- install/databasus-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/databasus-install.sh b/install/databasus-install.sh index 82238035a..b2026cb1f 100644 --- a/install/databasus-install.sh +++ b/install/databasus-install.sh @@ -96,7 +96,8 @@ save "" maxmemory 256mb maxmemory-policy allkeys-lru EOF -$STD systemctl enable -q --now valkey +systemctl enable -q --now valkey-server +systemctl restart valkey-server msg_ok "Configured Valkey" msg_info "Creating Database" From aac028efda5436f2fce5ba75f11ff1e050e40ce1 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:00:25 +0100 Subject: [PATCH 215/228] Update ebusd.sh --- ct/ebusd.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/ebusd.sh b/ct/ebusd.sh index 63f16f512..710c81c06 100644 --- a/ct/ebusd.sh +++ b/ct/ebusd.sh @@ -27,9 +27,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + msg_info "Updating ebusd" $STD apt update - $STD apt -y --upgrade ebusd + $STD apt --upgrade -y ebusd msg_ok "Updated ebusd" msg_ok "Updated successfully!" exit From 036c17647e6552344f258f15ee121c6cc48c7865 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:53:16 +0100 Subject: [PATCH 216/228] fixes --- install/databasus-install.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/install/databasus-install.sh b/install/databasus-install.sh index b2026cb1f..4ecf2d9fa 100644 --- a/install/databasus-install.sh +++ b/install/databasus-install.sh @@ -74,6 +74,10 @@ GOOSE_DRIVER=postgres GOOSE_DBSTRING=postgres://postgres:postgres@localhost:5432/databasus?sslmode=disable GOOSE_MIGRATION_DIR=/opt/databasus/migrations +# Valkey (Redis-compatible cache) +VALKEY_HOST=localhost +VALKEY_PORT=6379 + # Security JWT_SECRET=${JWT_SECRET} ENCRYPTION_KEY=${ENCRYPTION_KEY} @@ -101,6 +105,13 @@ systemctl restart valkey-server msg_ok "Configured Valkey" msg_info "Creating Database" +# Configure PostgreSQL to allow local password auth for databasus +PG_HBA="/etc/postgresql/17/main/pg_hba.conf" +if ! grep -q "databasus" "$PG_HBA"; then + sed -i '/^local\s*all\s*all/i local databasus postgres trust' "$PG_HBA" + sed -i '/^host\s*all\s*all\s*127/i host databasus postgres 127.0.0.1/32 trust' "$PG_HBA" + systemctl reload postgresql +fi $STD sudo -u postgres psql -c "CREATE DATABASE databasus;" 2>/dev/null || true msg_ok "Created Database" From eae154328f315fed9de4755f31e0730ee8a4d885 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 29 Jan 2026 19:03:41 +0100 Subject: [PATCH 217/228] Add PowerDNS script --- ct/alpine-powerdns.sh | 50 ++++++++++++++++++++++++++++++++++++++ install/alpine-powerdns.sh | 34 ++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 ct/alpine-powerdns.sh create mode 100644 install/alpine-powerdns.sh diff --git a/ct/alpine-powerdns.sh b/ct/alpine-powerdns.sh new file mode 100644 index 000000000..470079396 --- /dev/null +++ b/ct/alpine-powerdns.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/orhun/rustypaste + +APP="Alpine-PowerDNS" +var_tags="${var_tags:-os;alpine;dns}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +var_disk="${var_disk:-4}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.23}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if ! apk info -e pdns >/dev/null 2>&1; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating PowerDNS" + $STD apk -U upgrade + msg_ok "Updated PowerDNS" + + msg_info "Restarting Services" + $STD rc-service pdns restart + msg_ok "Restarted Services" + 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}:53${CL}" diff --git a/install/alpine-powerdns.sh b/install/alpine-powerdns.sh new file mode 100644 index 000000000..de78cf8a5 --- /dev/null +++ b/install/alpine-powerdns.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.powerdns.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing PowerDNS" +$STD apk add --no-cache pdns pdns-backend-sqlite3 pdns-doc +msg_ok "Installed PowerDNS" + +msg_info "Configuring PowerDNS" +sed -i '/^# launch=$/c\launch=gsqlite3\ngsqlite3-database=/var/lib/powerdns/pdns.sqlite3' /etc/pdns/pdns.conf +mkdir /var/lib/powerdns +sqlite3 /var/lib/powerdns/pdns.sqlite3 < /usr/share/doc/pdns/schema.sqlite3.sql +chown -R pdns:pdns /var/lib/powerdns +msg_ok "Configured PowerDNS" + +msg_info "Creating Service" +$STD rc-update add pdns default +$STD rc-service pdns start +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From e3452d7d9ef2952fed80a9043c4b4c3b7649f9b3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 29 Jan 2026 19:26:14 +0100 Subject: [PATCH 218/228] Add PowerDNS script --- install/{alpine-powerdns.sh => alpine-powerdns-install.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename install/{alpine-powerdns.sh => alpine-powerdns-install.sh} (100%) diff --git a/install/alpine-powerdns.sh b/install/alpine-powerdns-install.sh similarity index 100% rename from install/alpine-powerdns.sh rename to install/alpine-powerdns-install.sh From 302ef66520546d79c791f7305217ad484694c6a5 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 29 Jan 2026 19:52:49 +0100 Subject: [PATCH 219/228] Add PowerDNS script --- ct/alpine-powerdns.sh | 6 +++--- install/alpine-powerdns-install.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/alpine-powerdns.sh b/ct/alpine-powerdns.sh index 470079396..21ce7f0b5 100644 --- a/ct/alpine-powerdns.sh +++ b/ct/alpine-powerdns.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/orhun/rustypaste +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.powerdns.com/ APP="Alpine-PowerDNS" var_tags="${var_tags:-os;alpine;dns}" diff --git a/install/alpine-powerdns-install.sh b/install/alpine-powerdns-install.sh index de78cf8a5..b3cbc6d65 100644 --- a/install/alpine-powerdns-install.sh +++ b/install/alpine-powerdns-install.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://www.powerdns.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From d00e1176becfa380c968503faea4fc15c0b9defe Mon Sep 17 00:00:00 2001 From: "GitHub Actions[bot]" Date: Thu, 29 Jan 2026 18:58:55 +0000 Subject: [PATCH 220/228] chore: update github-versions.json Total versions: 20 Pinned versions: 1 Generated: 2026-01-29T18:58:55Z --- frontend/public/json/github-versions.json | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/frontend/public/json/github-versions.json b/frontend/public/json/github-versions.json index 7364cdacf..73deb25d9 100644 --- a/frontend/public/json/github-versions.json +++ b/frontend/public/json/github-versions.json @@ -1,5 +1,5 @@ { - "generated": "2026-01-29T02:41:18Z", + "generated": "2026-01-29T18:58:55Z", "versions": [ { "slug": "affine", @@ -15,6 +15,13 @@ "pinned": false, "date": "2025-12-22T04:23:45Z" }, + { + "slug": "anytype", + "repo": "grishy/any-sync-bundle", + "version": "v1.2.1-2025-12-10", + "pinned": false, + "date": "2025-12-24T20:40:15Z" + }, { "slug": "databasus", "repo": "databasus/databasus", @@ -25,9 +32,9 @@ { "slug": "ente", "repo": "ente-io/ente", - "version": "photos-v1.3.0", + "version": "photos-v1.3.7", "pinned": false, - "date": "2026-01-12T06:33:12Z" + "date": "2026-01-29T17:20:41Z" }, { "slug": "frigate", @@ -81,9 +88,9 @@ { "slug": "opencloud", "repo": "opencloud-eu/opencloud", - "version": "v5.0.0", + "version": "v5.0.1", "pinned": true, - "date": "2026-01-26T15:58:00Z" + "date": "2026-01-28T15:08:23Z" }, { "slug": "piler", From 2c9fb7b6f926212b6c14ef32ca5ea615b31b2fc7 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:01:49 +0000 Subject: [PATCH 221/228] removed comments --- vm/truenas-vm.sh | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index b81525ee2..bc9ab24b1 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -73,9 +73,6 @@ function error_handler() { cleanup_vmid } -# Scrapes the TrueNAS download portal for ISO paths from the current and previous year, -# filtering out nightlies/alphas and returning the latest stable releases for each major -# version along with any beta or RC pre-releases. function truenas_iso_lookup() { local BASE_URL="https://download.truenas.com" local current_year=$(date +%y) @@ -184,13 +181,10 @@ function check_root() { fi } -# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. -# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 – 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then @@ -201,7 +195,6 @@ pve_check() { return 0 fi - # Check for Proxmox VE 9.x: allow 9.0–9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then @@ -212,7 +205,6 @@ pve_check() { return 0 fi - # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 1 @@ -299,7 +291,6 @@ function advanced_settings() { fi done - # Fetching iso list from TrueNAS downloads for whiptail radiolist ISOARRAY=() while read -r ISOPATH; do FILENAME=$(basename "$ISOPATH") @@ -504,7 +495,6 @@ msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." if [ -z "${SELECTED_ISO:-}" ]; then - # Fallback: Find the latest stable release only (excluding RC/BETA for safety) SELECTED_ISO=$(truenas_iso_lookup | grep -vE 'RC|BETA' | sort -V | tail -n 1) if [ -z "$SELECTED_ISO" ]; then @@ -535,7 +525,6 @@ qm create "$VMID" -machine q35 -bios ovmf -agent enabled=1 -tablet 0 -localtime -scsihw virtio-scsi-single -cdrom local:iso/$ISO_NAME -vga virtio >/dev/null msg_ok "Created VM shell" -# Optional step to import onboard disks if [ "$IMPORT_DISKS" == "yes" ]; then msg_info "Importing onboard disks" DISKARRAY=() From bf57d6693995c8b279ab2b99f736833d03388746 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:01:47 +0000 Subject: [PATCH 222/228] Left only one notice with a link to the discussion --- frontend/public/json/truenas-vm.json | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/frontend/public/json/truenas-vm.json b/frontend/public/json/truenas-vm.json index d4675b55f..7f11e7564 100644 --- a/frontend/public/json/truenas-vm.json +++ b/frontend/public/json/truenas-vm.json @@ -33,24 +33,8 @@ }, "notes": [ { - "text": "The default install uses the latest stable release. Please use advanced install for more options.", + "text": "Once the script finishes, proceed with the OS installation via the console. For more details, please refer to this discussion: `https://github.com/community-scripts/ProxmoxVE/discussions/11344`", "type": "info" - }, - { - "text": "8 GB of RAM is the minimum requirement, but 16 GB+ is recommended for optimal performance.", - "type": "info" - }, - { - "text": "After installation, you will be prompted to unmount the media. To do this: Go to the VM's Hardware tab > select the CD/DVD Drive > Edit > select 'Do not use any media'", - "type": "info" - }, - { - "text": "While you can import onboard disks during install, it is highly recommended to use an HBA to pass through disks for production environments.", - "type": "warning" - }, - { - "text": "ECC RAM is strongly recommended to ensure data integrity, as ZFS checksumming can be compromised by bad data in RAM before the data is written to the pool.", - "type": "warning" } ] } From ed1dc2f3635c41de46d6167b66f4fedee5e52f41 Mon Sep 17 00:00:00 2001 From: "GitHub Actions[bot]" Date: Fri, 30 Jan 2026 07:00:55 +0000 Subject: [PATCH 223/228] chore: update github-versions.json Total versions: 20 Pinned versions: 1 Generated: 2026-01-30T07:00:55Z --- frontend/public/json/github-versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/github-versions.json b/frontend/public/json/github-versions.json index 73deb25d9..3be9decfb 100644 --- a/frontend/public/json/github-versions.json +++ b/frontend/public/json/github-versions.json @@ -1,5 +1,5 @@ { - "generated": "2026-01-29T18:58:55Z", + "generated": "2026-01-30T07:00:55Z", "versions": [ { "slug": "affine", From 1ff16293dee578a8bfa6f3b7cc6fd8ce3ca9938e Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Fri, 30 Jan 2026 08:07:54 +0100 Subject: [PATCH 224/228] Remove ebusd configuration instructions Removed configuration instructions for ebusd from the installation script. --- install/ebusd-install.sh | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/install/ebusd-install.sh b/install/ebusd-install.sh index 187cc2265..f7e58010c 100644 --- a/install/ebusd-install.sh +++ b/install/ebusd-install.sh @@ -25,41 +25,6 @@ $STD apt install -y ebusd systemctl enable -q --now ebusd msg_ok "Installed ebusd" -cat <~/ebusd-configuation-instructions.txt -Configuration instructions: - - 1. Edit "/etc/default/ebusd" if necessary (especially if your device is not "/dev/ttyUSB0") - 2. Start the daemon with "systemctl start ebusd" - 3. Check the log file "/var/log/ebusd.log" - 4. Make the daemon autostart with "systemctl enable ebusd" - -Working "/etc/default/ebusd" options for "ebus adapter shield v5": - -EBUSD_OPTS=" - --pidfile=/run/ebusd.pid - --latency=100 - --scanconfig - --configpath=https://cfg.ebusd.eu/ - --accesslevel=* - --pollinterval=30 - --device=ens:XXX.XXX.XXX.XXX:9999 - --mqtthost=XXX.XXX.XXX.XXX - --mqttport=1883 - --mqttuser=XXXXXX - --mqttpass=XXXXXX - --mqttint=/etc/ebusd/mqtt-hassio.cfg - --mqttjson - --mqttlog - --mqttretain - --mqtttopic=ebusd - --log=all:notice - --log=main:notice - --log=bus:notice - --log=update:notice - --log=network:notice - --log=other:notice" -EOF - motd_ssh customize cleanup_lxc From c9da138a6980eaf7bc7738f23b9e5ad94583ec03 Mon Sep 17 00:00:00 2001 From: Joerg Heinemann Date: Fri, 30 Jan 2026 08:13:08 +0100 Subject: [PATCH 225/228] Update notes with new post installation link --- frontend/public/json/ebusd.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/ebusd.json b/frontend/public/json/ebusd.json index e946af464..862d24153 100644 --- a/frontend/public/json/ebusd.json +++ b/frontend/public/json/ebusd.json @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "Show configuration instructions: `cat ~/ebusd-configuation-instructions.txt`", + "text": "For required post installation actions, checkout: `https://github.com/community-scripts/ProxmoxVE/discussions/11352`", "type": "info" } ] From 0dd93a4df029dfb23fbcc826480fd95728341f88 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 30 Jan 2026 09:12:11 +0100 Subject: [PATCH 226/228] Fix CI/CD --- frontend/src/__tests__/public/validate-json.test.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/src/__tests__/public/validate-json.test.ts b/frontend/src/__tests__/public/validate-json.test.ts index 1ab52db68..f0ab7dde1 100644 --- a/frontend/src/__tests__/public/validate-json.test.ts +++ b/frontend/src/__tests__/public/validate-json.test.ts @@ -7,16 +7,17 @@ console.log('Current directory: ' + process.cwd()); const jsonDir = "public/json"; const metadataFileName = "metadata.json"; const versionsFileName = "versions.json"; +const githubVersionsFileName = "github-versions.json"; const encoding = "utf-8"; const fileNames = (await fs.readdir(jsonDir)) - .filter((fileName) => fileName !== metadataFileName && fileName !== versionsFileName); + .filter((fileName) => fileName !== metadataFileName && fileName !== versionsFileName && fileName !== githubVersionsFileName); describe.each(fileNames)("%s", async (fileName) => { let script: Script; beforeAll(async () => { - const filePath = path.resolve(jsonDir, fileName); + const filePath = path.resolve(jsonDir, fileName); const fileContent = await fs.readFile(filePath, encoding) script = JSON.parse(fileContent); }) @@ -40,7 +41,7 @@ describe(`${metadataFileName}`, async () => { let metadata: Metadata; beforeAll(async () => { - const filePath = path.resolve(jsonDir, metadataFileName); + const filePath = path.resolve(jsonDir, metadataFileName); const fileContent = await fs.readFile(filePath, encoding) metadata = JSON.parse(fileContent); }) @@ -48,9 +49,9 @@ describe(`${metadataFileName}`, async () => { // TODO: create zod schema for metadata. Move zod schemas to /lib/types.ts assert(metadata.categories.length > 0); metadata.categories.forEach((category) => { - assert.isString(category.name) - assert.isNumber(category.id) - assert.isNumber(category.sort_order) + assert.isString(category.name) + assert.isNumber(category.id) + assert.isNumber(category.sort_order) }); }); }) From dc5c1d3ef06a0739bff2cb0706585f1a927abed5 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:47:38 +0100 Subject: [PATCH 227/228] Improve locale setup and database user privileges Add command to grant superuser, createrole, and createdb privileges to the postgres user during Databasus installation. Enhance LXC container customization by installing locales for Devuan templates and only configuring locale if locale.gen exists, improving compatibility with minimal templates. --- install/databasus-install.sh | 1 + misc/build.func | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/install/databasus-install.sh b/install/databasus-install.sh index 4ecf2d9fa..0b068d865 100644 --- a/install/databasus-install.sh +++ b/install/databasus-install.sh @@ -113,6 +113,7 @@ if ! grep -q "databasus" "$PG_HBA"; then systemctl reload postgresql fi $STD sudo -u postgres psql -c "CREATE DATABASE databasus;" 2>/dev/null || true +$STD sudo -u postgres psql -c "ALTER USER postgres WITH SUPERUSER CREATEROLE CREATEDB;" 2>/dev/null || true msg_ok "Created Database" msg_info "Creating Databasus Service" diff --git a/misc/build.func b/misc/build.func index 7e5e0a794..20a20e985 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3870,18 +3870,7 @@ EOF sleep 3 fi - # Continue with standard container setup msg_info "Customizing LXC Container" - - # # Install GPU userland if configured - # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - # install_gpu_userland "VAAPI" - # fi - - # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - # install_gpu_userland "NVIDIA" - # fi - # Continue with standard container setup if [ "$var_os" == "alpine" ]; then sleep 3 @@ -3893,11 +3882,20 @@ EOF' else sleep 3 LANG=${LANG:-en_US.UTF-8} - pct exec "$CTID" -- bash -c "sed -i \"/$LANG/ s/^# //\" /etc/locale.gen" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ - echo LANG=\$locale_line >/etc/default/locale && \ - locale-gen >/dev/null && \ - export LANG=\$locale_line" + + # Devuan templates don't include locales package by default - install it first + if [ "$var_os" == "devuan" ]; then + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null" || true + fi + + # Only configure locale if locale.gen exists (some minimal templates don't have it) + if pct exec "$CTID" -- test -f /etc/locale.gen 2>/dev/null; then + pct exec "$CTID" -- bash -c "sed -i \"/$LANG/ s/^# //\" /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + fi if [[ -z "${tz:-}" ]]; then tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC") From edac4a229334458e25ae6190db3de5ac750f9a4b Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:59:11 +0100 Subject: [PATCH 228/228] Update install.func --- misc/install.func | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/misc/install.func b/misc/install.func index 0a26d7d70..1841526a8 100644 --- a/misc/install.func +++ b/misc/install.func @@ -907,23 +907,27 @@ EOF # Backup original inittab cp /etc/inittab /etc/inittab.bak 2>/dev/null || true - # First, enable autologin on tty1 (for direct access) - sed -i 's|^1:[0-9]*:respawn:.*/\(a\?getty\).*|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab + # Enable autologin on tty1 (for direct access) - handle various formats + # Devuan uses format: 1:2345:respawn:/sbin/getty 38400 tty1 + sed -i 's|^\(1:[0-9]*:respawn:\).*getty.*tty1.*|1:2345:respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab - # CRITICAL: Add console entry for LXC - this is what pct console uses! - # Check if there's already a console getty entry - if ! grep -qE '^[^#].*respawn.*console' /etc/inittab; then - # Add new console entry for LXC - echo "" >>/etc/inittab - echo "# LXC console autologin (added by community-scripts)" >>/etc/inittab - echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux" >>/etc/inittab - else - # Enable autologin on existing console entry - sed -i 's|^[^#]*:[0-9]*:respawn:.*/\(a\?getty\).*console.*|co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200,38400,9600 linux|' /etc/inittab - fi + # CRITICAL: Add/replace console entry for LXC - this is what pct console uses! + # Remove any existing console entries first (commented or not) + sed -i '/^[^#]*:.*:respawn:.*getty.*console/d' /etc/inittab + sed -i '/^# LXC console autologin/d' /etc/inittab - # Force a reload of inittab - try multiple methods - telinit q &>/dev/null || init q &>/dev/null || kill -1 1 &>/dev/null || true + # Add new console entry for LXC at the end + echo "" >>/etc/inittab + echo "# LXC console autologin (added by community-scripts)" >>/etc/inittab + echo "co:2345:respawn:/sbin/agetty --autologin root --noclear console 115200 linux" >>/etc/inittab + + # Force a reload of inittab and respawn console getty + # Kill existing getty on console to force respawn with new settings + pkill -9 -f 'getty.*console' &>/dev/null || true + pkill -9 -f 'agetty.*console' &>/dev/null || true + + # Reload inittab - try multiple methods + telinit q &>/dev/null || init q &>/dev/null || kill -HUP 1 &>/dev/null || true fi touch /root/.hushlogin ;;