mirror of
https://github.com/unraid/api.git
synced 2026-05-12 18:50:26 -05:00
feat: api plugin system & offline versioned dependency vendoring (#1252)
- **New Features** - Created a dynamic plugin system for the API to enable community augmentation of GraphQL, CLI, and Cron functionalities capabilities. - Included an example plugin under `packages/unraid-api-plugin-health` that adds a new graphql query for API health checks. - Added `rc.unraid-api` commands for backing up, restoring, and installing production dependencies, streamlining maintenance and deployment. - Improved dependency vendoring by bundling a versioned pnpm store (instead of `node_modules`). Versioning will allow users to add plugins to a specific api release without requiring an internet connection on subsequent reboots. - **Chores** - Upgraded build workflows and versioning processes to ensure more reliable artifact handling and production packaging.
This commit is contained in:
@@ -8,7 +8,9 @@ flash="/boot/config/plugins/dynamix.my.servers"
|
||||
[[ ! -d "${flash}" ]] && echo "Please reinstall the Unraid Connect plugin" && exit 1
|
||||
[[ ! -f "${flash}/env" ]] && echo 'env=production' >"${flash}/env"
|
||||
unraid_binary_path="/usr/local/bin/unraid-api"
|
||||
pnpm_store_dir="/usr/.pnpm-store"
|
||||
|
||||
# Placeholder functions for plugin installation/uninstallation
|
||||
install() {
|
||||
true;
|
||||
}
|
||||
@@ -16,6 +18,115 @@ uninstall() {
|
||||
true;
|
||||
}
|
||||
|
||||
# Creates a backup of the global pnpm store directory
|
||||
# Args:
|
||||
# $1 - Path to the backup file (tar.xz format)
|
||||
# Returns:
|
||||
# 0 on success, 1 on failure
|
||||
backup_pnpm_store() {
|
||||
# Check if backup file path is provided
|
||||
if [ -z "$1" ]; then
|
||||
echo "Error: Backup file path is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local backup_file="$1"
|
||||
|
||||
# Check if pnpm command exists
|
||||
if ! command -v pnpm >/dev/null 2>&1; then
|
||||
echo "pnpm is not installed. Skipping backup."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Determine the global pnpm store directory
|
||||
mkdir -p "$pnpm_store_dir"
|
||||
|
||||
echo "Backing up pnpm store from '$pnpm_store_dir' to '$backup_file'"
|
||||
|
||||
# Create a tar.gz archive of the global pnpm store
|
||||
if tar -cJf "$backup_file" -C "$(dirname "$pnpm_store_dir")" "$(basename "$pnpm_store_dir")"; then
|
||||
echo "pnpm store backup completed successfully."
|
||||
else
|
||||
echo "Error: Failed to create pnpm store backup."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Restores the pnpm store from a backup file
|
||||
# Args:
|
||||
# $1 - Path to the backup file (tar.xz format)
|
||||
# Returns:
|
||||
# 0 on success, 1 on failure
|
||||
# Note: Requires 1.5x the backup size in free space for safe extraction
|
||||
restore_pnpm_store() {
|
||||
# Check if pnpm command exists
|
||||
if ! command -v pnpm >/dev/null 2>&1; then
|
||||
echo "pnpm is not installed. Cannot restore store."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local backup_file="$1"
|
||||
# Check if backup file exists
|
||||
if [ ! -f "$backup_file" ]; then
|
||||
echo "Backup file not found at '$backup_file'. Skipping restore."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check available disk space in destination
|
||||
local backup_size
|
||||
backup_size=$(stat -c%s "$backup_file")
|
||||
local dest_space
|
||||
dest_space=$(df --output=avail "$(dirname "$pnpm_store_dir")" | tail -n1)
|
||||
dest_space=$((dest_space * 1024)) # Convert KB to bytes
|
||||
|
||||
# Require 1.5x the backup size for safe extraction
|
||||
local required_space=$((backup_size + (backup_size / 2)))
|
||||
|
||||
if [ "$dest_space" -lt "$required_space" ]; then
|
||||
echo "Error: Insufficient disk space in destination. Need at least $((required_space / 1024 / 1024))MB, have $((dest_space / 1024 / 1024))MB"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Restoring pnpm store from '$backup_file' to '$pnpm_store_dir'"
|
||||
# Remove existing store directory if it exists and ensure its parent directory exists
|
||||
rm -rf "$pnpm_store_dir"
|
||||
mkdir -p "$(dirname "$pnpm_store_dir")"
|
||||
|
||||
# Extract directly to final location
|
||||
if ! tar -xJf "$backup_file" -C "$(dirname "$pnpm_store_dir")" --preserve-permissions; then
|
||||
echo "Error: Failed to extract backup to final location."
|
||||
rm -rf "$pnpm_store_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "pnpm store restored successfully."
|
||||
}
|
||||
|
||||
# Installs production dependencies for the unraid-api using pnpm. Prefers offline mode.
|
||||
# Uses the api_base_directory variable or defaults to /usr/local/unraid-api
|
||||
# Returns:
|
||||
# 0 on success, 1 on failure
|
||||
pnpm_install_unraid_api() {
|
||||
# Check if pnpm command exists
|
||||
if ! command -v pnpm >/dev/null 2>&1; then
|
||||
echo "Error: pnpm command not found. Cannot install dependencies."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Use the api_base_directory variable if set, otherwise default to /usr/local/unraid-api
|
||||
local unraid_api_dir="${api_base_directory:-/usr/local/unraid-api}"
|
||||
if [ ! -d "$unraid_api_dir" ]; then
|
||||
echo "Error: unraid API directory '$unraid_api_dir' does not exist."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Executing 'pnpm install' in $unraid_api_dir"
|
||||
rm -rf /usr/local/unraid-api/node_modules
|
||||
# Run pnpm install in a subshell to prevent changing the current working directory of the script
|
||||
( cd "$unraid_api_dir" && pnpm install --prod --prefer-offline )
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
'install')
|
||||
install "$2"
|
||||
@@ -26,6 +137,15 @@ case "$1" in
|
||||
'uninstall')
|
||||
uninstall
|
||||
;;
|
||||
'pnpm-install')
|
||||
pnpm_install_unraid_api
|
||||
;;
|
||||
'backup-dependencies')
|
||||
backup_pnpm_store "$2"
|
||||
;;
|
||||
'restore-dependencies')
|
||||
restore_pnpm_store "$2"
|
||||
;;
|
||||
*)
|
||||
# Pass all other commands to unraid-api
|
||||
"${unraid_binary_path}" "$@"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user