Document the PHP api layer and fix what documenting it exposed

Writing down what each endpoint actually guarantees made the places it
didn't obvious — shell arguments reaching a crontab or a bash -c
unescaped, master.conf written without tmp+rename, and conf edits that
could be saved without ever being parsed.
This commit is contained in:
Gmer4Lfe
2026-08-02 10:11:39 -04:00
parent 6a959fb5e4
commit 987313e7dc
55 changed files with 3972 additions and 95 deletions
+77
View File
@@ -1,4 +1,81 @@
<?php
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// PURPOSE
// Full monitor payload. Every metric the monitor page renders — system, CPU, memory,
// network, GPUs, disks and pools, array, parity, UPS, containers, VMs, transcodes, watchdog
// and rsync summaries, and remote node roll-ups — in one document.
//
// OPERATIONAL MODEL
// Deliberately one large response rather than many small ones. Most of these metrics share
// an underlying source — the Unraid API, /proc, the same emhttp ini files — so collecting
// them together means one pass over each source instead of one per endpoint. The page
// renders from a single consistent snapshot; twenty parallel fetches would render from
// twenty slightly different moments.
//
// Served from a 300s cache written by Tools/api_cache_writer.sh, which runs every minute.
// The page therefore almost never pays for collection — the background writer does. ?live
// forces a fresh build for the refresh button.
//
// DESIGN PRINCIPLES
// The cache check happens before the heavy includes.
// Only include/config.php is loaded to reach vv_cache_read(). monitor.php, vms.php and
// docker_folders.php are required only after a miss, so a cache hit costs one file read
// rather than parsing three libraries.
//
// The API cache is pre-warmed once, on purpose.
// vv_api_data() is called before the payload is assembled so the API-first functions
// below it share a single GraphQL round trip instead of each making their own.
//
// Every field is a named function call, in render order.
// The payload is a flat map of key to collector. Adding a metric is adding a line, and
// nothing in the assembly depends on anything else in it — so one expensive or broken
// collector can be moved or removed without touching the others.
//
// Reports the API's own health alongside the data.
// _api_status travels with the payload, so the page can show that a section degraded to
// its local fallback rather than silently presenting less detail.
//
// OPERATIONAL SAFEGUARDS
// Read-only. Every function here observes; none of them start, stop, or change anything.
//
// Cache miss is distinguished from empty payload.
// vv_cache_read() returns null on a miss, expiry, or unparseable file, and the check is
// an explicit !== null — so a legitimately sparse payload is served from cache instead
// of being mistaken for a miss and forced onto the expensive path on every poll.
//
// Every collector degrades to empty rather than fatal.
// The library suppresses its filesystem reads and redirects stderr on every shell call,
// so absent hardware — no GPU, no UPS, no ZFS, no VMs — yields an empty section and an
// unrendered card. On a payload this wide that property is what keeps one missing
// subsystem from blanking the entire page.
//
// Remote collection degrades per node, so one dark partner costs its own card and nothing
// else.
//
// Known cost: a cache miss on a host with an unreachable partner pays the remote SSH
// timeouts inline. That is why the background writer exists and why the cache window is
// long — the miss path is the exception, not the design.
//
// REQUEST
// GET served from the 300s cache when one is present
// GET ?live bypass the cache and collect everything fresh
//
// RESPONSE
// A flat object of the keys listed in the assembly below, plus _api_status and ts.
//
// DEPENDS ON
// include/config.php vv_cache_read()
// include/common.php raw hardware metrics — system, cpu, mem, net, gpu, disks,
// ups, parity, containers, transcodes, remote roll-ups
// (loaded transitively through include/monitor.php)
// include/monitor.php the "is anything wrong" roll-ups — vv_partner_state(),
// vv_fallback_state(), vv_watchdog_summary(),
// vv_scripts_status(), vv_rsync_status()
// include/unraid_api.php vv_api_data(), vv_api_get_status()
// include/vms.php vv_get_vms()
// include/docker_folders.php vv_get_docker_folders()
// Tools/api_cache_writer.sh writes the cache this endpoint normally serves
// ═══════════════════════════════════════════════════════════════════════════════════════════════
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';