Finishes the pass: every script now documents its safeguards, and the deliberate absences in the sourced libraries are recorded so they are not "corrected" later.
206 lines
8.2 KiB
Bash
Executable File
206 lines
8.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# ==============================================================================================
|
|
# ============================= Remote Arr Cache Writer ========================================
|
|
# ==============================================================================================
|
|
#
|
|
# PURPOSE
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# SSHes to each remote partner host, calls vv_arrs_local_node() on their PHP
|
|
# stack, and caches the result locally in /tmp/vv_cache/arrs_remote_hostN.json.
|
|
#
|
|
# The arrs page reads these files for instant initial load without hitting the
|
|
# remote arr APIs on every page view. This script runs every 2 hours so remote
|
|
# library counts stay reasonably current without hammering the network.
|
|
#
|
|
# Accepts --host=HOST2 to refresh a single host (used by the UI refresh button).
|
|
#
|
|
# ==============================================================================================
|
|
# DESIGN PRINCIPLES
|
|
# ==============================================================================================
|
|
#
|
|
# Cache-First, Never Live on Page Load
|
|
# Remote arr APIs have non-trivial latency — calling them on every page view
|
|
# would make the arrs page slow and fragile. Writing to /tmp/vv_cache/ on a
|
|
# 2-hour schedule decouples page load time from network availability.
|
|
#
|
|
# Single-Host Refresh for UI
|
|
# The UI refresh button passes --host=HOSTN to update one host without waiting
|
|
# for the full 2-hour cycle. Keeps the cache fresh when a user requests it.
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL MODEL
|
|
# ==============================================================================================
|
|
#
|
|
# Per partner host:
|
|
# 1. Resolve its Tailscale IP — unresolvable skips that host, not the run
|
|
# 2. SSH across and call vv_arrs_local_node() on its own PHP stack
|
|
# 3. Write the JSON to /tmp/vv_cache/arrs_remote_<hostid>.json locally
|
|
#
|
|
# The remote builds its own payload rather than this host querying the remote's arr APIs
|
|
# directly — the partner already has working local URLs and keys for its own arrs, so no
|
|
# cross-host credentials or path mapping are involved.
|
|
#
|
|
# Runs every 2 hours. The arrs page reads these files for an instant first paint and falls
|
|
# back to live calls when a file is missing or stale.
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL SAFEGUARDS
|
|
# ==============================================================================================
|
|
#
|
|
# acquire_lock — prevents concurrent cache write runs
|
|
# detect_hosts() — MY_ID and partner host list
|
|
# SSH reachability — skips a host cleanly if it cannot be reached
|
|
# /tmp/vv_cache/ — auto-created if missing; cleared on reboot (intentional)
|
|
#
|
|
# ==============================================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================================
|
|
#
|
|
# host*.conf
|
|
#
|
|
# SSH_KEY
|
|
# Used to reach each partner. Written by ssh_setup.sh.
|
|
#
|
|
# HOST* — partner hostnames, resolved to Tailscale IPs at runtime
|
|
#
|
|
# Cache output: /tmp/vv_cache/arrs_remote_<hostid>.json
|
|
# tmpfs, cleared on reboot. Regenerable by definition — losing it costs one slow page
|
|
# load, never correctness, which is why nothing here retries hard on failure.
|
|
#
|
|
# No arr credentials are read locally. Each partner uses its own SONARR_*/RADARR_*/LIDARR_*
|
|
# values on its own side, so this host never holds keys for a remote's arrs.
|
|
#
|
|
# ==============================================================================================
|
|
# RUNTIME MODES
|
|
# ==============================================================================================
|
|
#
|
|
# remote_arr_cache_writer.sh — refresh all remote hosts
|
|
# remote_arr_cache_writer.sh --host=HOST2 — refresh one host only
|
|
# remote_arr_cache_writer.sh --dry-run — show what would happen
|
|
# remote_arr_cache_writer.sh --log — verbose output
|
|
#
|
|
# ==============================================================================================
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/../../../load_config.sh"
|
|
|
|
parse_args "$@"
|
|
|
|
# Reads $SSH_KEY from /root/.ssh to reach partner hosts.
|
|
[[ "$EUID" -ne 0 ]] && { error "Must be run as root"; exit 1; }
|
|
|
|
acquire_lock
|
|
detect_hosts
|
|
|
|
# Parse --host= from raw args
|
|
TARGET_HOST=""
|
|
for arg in "$@"; do
|
|
case "$arg" in --host=*) TARGET_HOST="${arg#--host=}" ;; esac
|
|
done
|
|
|
|
mkdir -p "$VV_CACHE_DIR"
|
|
|
|
log "$ICON_GEAR Config: target=${TARGET_HOST:-all hosts} ssh-key=${SSH_KEY}"
|
|
|
|
FETCH_OK=0
|
|
FETCH_FAIL=0
|
|
FETCH_SKIP=0
|
|
|
|
echo ""
|
|
echo "━━━ $ICON_SYNC Remote Arr Cache Writer ━━━"
|
|
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
|
|
[[ -n "$TARGET_HOST" ]] && echo " Target: $TARGET_HOST"
|
|
echo ""
|
|
|
|
for host_var in HOST1 HOST2 HOST3 HOST4 HOST5 HOST6 HOST7 HOST8; do
|
|
[[ "$host_var" == "$MY_ID" ]] && continue
|
|
|
|
hostname="${!host_var:-}"
|
|
[[ -z "$hostname" ]] && continue
|
|
|
|
# If targeting a specific host, skip others
|
|
if [[ -n "$TARGET_HOST" ]]; then
|
|
[[ "${host_var,,}" != "${TARGET_HOST,,}" && "$host_var" != "$TARGET_HOST" ]] && continue
|
|
fi
|
|
|
|
host_id="${host_var,,}" # host1, host2, …
|
|
cache_file="$VV_CACHE_DIR/arrs_remote_${host_id}.json"
|
|
|
|
echo " $host_var ($hostname)…"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn " DRY RUN — would SSH to $hostname and cache arr data"
|
|
(( FETCH_SKIP++ ))
|
|
continue
|
|
fi
|
|
|
|
# Resolve Tailscale IP
|
|
REMOTE_IP=$(resolve_tailscale_ip "$hostname")
|
|
if [[ -z "$REMOTE_IP" ]]; then
|
|
warn " $host_var: cannot resolve Tailscale IP for $hostname — skipping"
|
|
(( FETCH_FAIL++ ))
|
|
continue
|
|
fi
|
|
log " $host_var: resolved $hostname → $REMOTE_IP"
|
|
|
|
# Single SSH connection — fetch arrs + monitor stats together
|
|
RESULT=$(ssh -i "$SSH_KEY" \
|
|
-o ConnectTimeout=10 \
|
|
-o StrictHostKeyChecking=no \
|
|
-o BatchMode=yes \
|
|
"root@${REMOTE_IP}" \
|
|
"php -r \"
|
|
require_once '/usr/local/emhttp/plugins/varaverk/include/arrs.php';
|
|
require_once '/usr/local/emhttp/plugins/varaverk/include/unraid_api.php';
|
|
echo json_encode([
|
|
'arrs' => vv_arrs_local_node(),
|
|
'monitor' => vv_local_host_stats(),
|
|
]);
|
|
\"" 2>/dev/null)
|
|
|
|
if [[ -z "$RESULT" ]]; then
|
|
warn " $host_var: empty SSH response — skipping"
|
|
(( FETCH_FAIL++ ))
|
|
continue
|
|
fi
|
|
|
|
# Validate combined JSON
|
|
if ! echo "$RESULT" | php -r "
|
|
\$d = json_decode(file_get_contents('php://stdin'), true);
|
|
exit(!is_array(\$d) || !isset(\$d['arrs'], \$d['monitor']) ? 1 : 0);
|
|
" 2>/dev/null; then
|
|
warn " $host_var: invalid JSON — skipping"
|
|
log " Response: ${RESULT:0:200}"
|
|
(( FETCH_FAIL++ ))
|
|
continue
|
|
fi
|
|
|
|
# Save arr cache
|
|
echo "$RESULT" | php -r "
|
|
\$d = json_decode(file_get_contents('php://stdin'), true);
|
|
file_put_contents('$cache_file', json_encode(\$d['arrs']));
|
|
" 2>/dev/null
|
|
|
|
# Save monitor cache
|
|
MONITOR_CACHE="$VV_CACHE_DIR/monitor_remote_${host_id}.json"
|
|
echo "$RESULT" | php -r "
|
|
\$d = json_decode(file_get_contents('php://stdin'), true);
|
|
file_put_contents('$MONITOR_CACHE', json_encode(\$d['monitor']));
|
|
" 2>/dev/null
|
|
|
|
CACHED_TYPES=$(echo "$RESULT" | php -r "
|
|
\$d = json_decode(file_get_contents('php://stdin'), true);
|
|
echo implode(', ', array_column(\$d['arrs']['arrs'] ?? [], 'type'));
|
|
" 2>/dev/null)
|
|
echo " $host_var: arrs [${CACHED_TYPES:-none}] + monitor stats cached ✅"
|
|
(( FETCH_OK++ ))
|
|
done
|
|
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY Cache Writer Summary ━━━━━"
|
|
echo " $FETCH_OK updated · $FETCH_FAIL failed · $FETCH_SKIP skipped"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
[[ "$FETCH_FAIL" -gt 0 ]] && exit 1
|
|
exit 0
|