2025-06-04 20:39:50 +02:00
#!/bin/bash
# ==========================================================
# ProxMenu - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.1
# Last Updated: 04/06/2025
# ==========================================================
# Description:
# This script provides a simple and efficient way to access and execute Proxmox VE scripts
# from the Community Scripts project (https://community-scripts.github.io/ProxmoxVE/).
#
# It serves as a convenient tool to run key automation scripts that simplify system management,
# continuing the great work and legacy of tteck in making Proxmox VE more accessible.
# A streamlined solution for executing must-have tools in Proxmox VE.
# ==========================================================
# Configuration ============================================
REPO_URL = "https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR = "/usr/local/share/proxmenux"
UTILS_FILE = " $BASE_DIR /utils.sh "
VENV_PATH = "/opt/googletrans-env"
if [ [ -f " $UTILS_FILE " ] ] ; then
source " $UTILS_FILE "
fi
load_language
initialize_cache
# ==========================================================
HELPERS_JSON_URL = "https://raw.githubusercontent.com/MacRimi/ProxMenux/refs/heads/main/json/helpers_cache.json"
METADATA_URL = "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/frontend/public/json/metadata.json"
for cmd in curl jq dialog; do
if ! command -v " $cmd " >/dev/null; then
echo " Missing required command: $cmd "
exit 1
fi
done
CACHE_JSON = $( curl -s " $HELPERS_JSON_URL " )
META_JSON = $( curl -s " $METADATA_URL " )
declare -A CATEGORY_NAMES
while read -r id name; do
CATEGORY_NAMES[ $id ] = " $name "
done < <( echo " $META_JSON " | jq -r '.categories[] | "\(.id)\t\(.name)"' )
declare -A CATEGORY_COUNT
for id in $( echo " $CACHE_JSON " | jq -r '.[].categories[]' ) ; do
( ( CATEGORY_COUNT[ $id ] ++) )
done
get_type_label( ) {
local type = " $1 "
case " $type " in
ct) echo $'\Z1LXC\Zn' ; ;
vm) echo $'\Z4VM\Zn' ; ;
pve) echo $'\Z3PVE\Zn' ; ;
addon) echo $'\Z2ADDON\Zn' ; ;
*) echo $'\Z7GEN\Zn' ; ;
esac
}
download_script( ) {
local url = " $1 "
local fallback_pve = " ${ url /misc \/ tools \/ pve } "
local fallback_addon = " ${ url /misc \/ tools \/ addon } "
local fallback_copydata = " ${ url /misc \/ tools \/ copy -data } "
if curl --silent --head --fail " $url " >/dev/null; then
bash <( curl -s " $url " )
elif curl --silent --head --fail " $fallback_pve " >/dev/null; then
bash <( curl -s " $fallback_pve " )
elif curl --silent --head --fail " $fallback_addon " >/dev/null; then
bash <( curl -s " $fallback_addon " )
elif curl --silent --head --fail " $fallback_copydata " >/dev/null; then
bash <( curl -s " $fallback_copydata " )
else
dialog --title "Helper Scripts" --msgbox "Error: Failed to download the script." 12 70
fi
}
RETURN_TO_MAIN = false
run_script_by_slug( ) {
local slug = " $1 "
local script_info
script_info = $( echo " $CACHE_JSON " | jq -r --arg slug " $slug " '.[] | select(.slug == $slug) | @base64' )
decode( ) {
echo " $1 " | base64 --decode | jq -r " $2 "
}
local name desc script_url notes
name = $( decode " $script_info " ".name" )
desc = $( decode " $script_info " ".desc" )
script_url = $( decode " $script_info " ".script_url" )
notes = $( decode " $script_info " ".notes | join(\"\n• \" )" )
local msg = " \Zb\Z1 $name \Zn\n\n $desc "
[ [ -n " $notes " ] ] && msg += " \n\n\ZbNotes:\Zn\n• $notes "
dialog --clear --colors --backtitle "ProxMenux" --title " $name " --yesno " $msg \n\nExecute this script? " 20 80
if [ [ $? -eq 0 ] ] ; then
download_script " $script_url "
echo
echo
msg_success "Press Enter to return to the main menu..."
read -r
RETURN_TO_MAIN = true
fi
}
search_and_filter_scripts( ) {
local search_term = ""
while true; do
search_term = $( dialog --inputbox "Enter search term (leave empty to show all scripts):" \
8 65 " $search_term " 3>& 1 1>& 2 2>& 3)
[ [ $? -ne 0 ] ] && return
local filtered_json
if [ [ -z " $search_term " ] ] ; then
filtered_json = " $CACHE_JSON "
else
local search_lower
search_lower = $( echo " $search_term " | tr '[:upper:]' '[:lower:]' )
filtered_json = $( echo " $CACHE_JSON " | jq --arg term " $search_lower " '
[ .[ ] | select (
( .name | ascii_downcase | contains( $term ) ) or
( .desc | ascii_downcase | contains( $term ) )
) ] ' )
fi
local count
count = $( echo " $filtered_json " | jq length)
if [ [ $count -eq 0 ] ] ; then
dialog --msgbox " No scripts found for: ' $search_term '\n\nTry a different search term. " 8 50
continue
fi
while true; do
declare -A index_to_slug
local menu_items = ( )
local i = 1
while IFS = $'\t' read -r slug name type; do
index_to_slug[ $i ] = " $slug "
local label
label = $( get_type_label " $type " )
local padded_name
padded_name = $( printf "%-42s" " $name " )
local entry = " $padded_name $label "
menu_items += ( " $i " " $entry " )
( ( i++) )
done < <( echo " $filtered_json " | jq -r '
sort_by( .name) [ ] | [ .slug, .name, .type] | @tsv' )
menu_items += ( "" "" )
menu_items += ( "new_search" "New Search" )
menu_items += ( "show_all" "Show All Scripts" )
local title = "Search Results"
if [ [ -n " $search_term " ] ] ; then
title = " Search Results for: ' $search_term ' ( $count found) "
else
title = " All Available Scripts ( $count total) "
fi
local selected
selected = $( dialog --colors --backtitle "ProxMenux" \
--title " $title " \
--menu "Select a script or action:" \
22 75 15 " ${ menu_items [@] } " 3>& 1 1>& 2 2>& 3)
if [ [ $? -ne 0 ] ] ; then
return
fi
case " $selected " in
"new_search" )
break
; ;
"show_all" )
search_term = ""
filtered_json = " $CACHE_JSON "
count = $( echo " $filtered_json " | jq length)
continue
; ;
"back" | "" )
return
; ;
*)
if [ [ -n " ${ index_to_slug [ $selected ] } " ] ] ; then
run_script_by_slug " ${ index_to_slug [ $selected ] } "
[ [ " $RETURN_TO_MAIN " = = true ] ] && { RETURN_TO_MAIN = false; return ; }
fi
; ;
esac
done
done
}
while true; do
MENU_ITEMS = ( )
MENU_ITEMS += ( "search" "Search/Filter Scripts" )
MENU_ITEMS += ( "" "" )
for id in $( printf "%s\n" " ${ !CATEGORY_COUNT[@] } " | sort -n) ; do
name = " ${ CATEGORY_NAMES [ $id ] :- Category $id } "
count = " ${ CATEGORY_COUNT [ $id ] } "
padded_name = $( printf "%-35s" " $name " )
padded_count = $( printf "(%2d)" " $count " )
MENU_ITEMS += ( " $id " " $padded_name $padded_count " )
done
SELECTED = $( dialog --backtitle "ProxMenux" --title "Proxmox VE Helper-Scripts" --menu \
"Select a category or search for scripts:" 20 70 14 \
" ${ MENU_ITEMS [@] } " 3>& 1 1>& 2 2>& 3) || {
dialog --title "Proxmox VE Helper-Scripts" \
--msgbox " \n\n $( translate "Visit the website to discover more scripts, stay updated with the latest updates, and support the project:" ) \n\nhttps://community-scripts.github.io/ProxmoxVE " 15 70
2025-06-04 20:50:01 +02:00
2025-06-04 20:47:11 +02:00
clear
2025-06-04 20:50:01 +02:00
break
2025-06-04 20:39:50 +02:00
}
if [ [ " $SELECTED " = = "search" ] ] ; then
search_and_filter_scripts
continue
fi
while true; do
declare -A INDEX_TO_SLUG
SCRIPTS = ( )
i = 1
while IFS = $'\t' read -r slug name type; do
INDEX_TO_SLUG[ $i ] = " $slug "
label = $( get_type_label " $type " )
padded_name = $( printf "%-42s" " $name " )
entry = " $padded_name $label "
SCRIPTS += ( " $i " " $entry " )
( ( i++) )
done < <( echo " $CACHE_JSON " | jq -r --argjson id " $SELECTED " \
'[.[] | select(.categories | index($id)) | {slug, name, type}] | sort_by(.name)[] | [.slug, .name, .type] | @tsv' )
SCRIPT_INDEX = $( dialog --colors --backtitle "ProxMenux" --title " Scripts in ${ CATEGORY_NAMES [ $SELECTED ] } " --menu \
"Choose a script to execute:" 20 70 14 \
" ${ SCRIPTS [@] } " 3>& 1 1>& 2 2>& 3) || break
SCRIPT_SELECTED = " ${ INDEX_TO_SLUG [ $SCRIPT_INDEX ] } "
run_script_by_slug " $SCRIPT_SELECTED "
[ [ " $RETURN_TO_MAIN " = = true ] ] && { RETURN_TO_MAIN = false; break; }
done
done