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:
@@ -1,9 +1,83 @@
|
||||
<?php
|
||||
// API cache writer — runs every minute via Varaverk scheduler.
|
||||
// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
|
||||
// loads can serve instantly from the file instead of making live HTTP calls.
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
// PURPOSE
|
||||
// Background cache writer. Builds the full monitor payload and the arrs payload once a
|
||||
// minute and writes them to the cache, so page loads serve from a file instead of paying
|
||||
// for collection.
|
||||
//
|
||||
// Called by api_cache_writer.sh (bash wrapper required by the scheduler).
|
||||
// OPERATIONAL MODEL
|
||||
// This is what makes the monitor and arrs tabs fast. api/monitor.php and api/arrs.php read
|
||||
// the files this process writes and only fall back to collecting for themselves on a miss.
|
||||
// The expensive work — GraphQL, docker stats, SSH to partners, HTTP to every arr instance —
|
||||
// happens here, on a schedule, off the request path.
|
||||
//
|
||||
// Runs from cron every minute via api_cache_writer.sh, a bash wrapper the scheduler
|
||||
// requires. Not reachable over HTTP, and refuses to run if it ever is.
|
||||
//
|
||||
// The payload assembled here is deliberately identical to api/monitor.php's. The two are
|
||||
// maintained together: a field added there and not here is a field that is only ever served
|
||||
// on a cache miss.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// One API round trip for the whole payload.
|
||||
// vv_api_data() is called once up front and static-cached for the life of the process,
|
||||
// so the API-first collectors below share a single GraphQL query rather than issuing
|
||||
// one each.
|
||||
//
|
||||
// Writes two caches, not one.
|
||||
// monitor and arrs have different consumers and different costs, so they are written
|
||||
// under separate keys and either can be served while the other is stale.
|
||||
//
|
||||
// Every payload carries its own timestamp, so consumers can render age rather than
|
||||
// presenting minute-old numbers as current.
|
||||
//
|
||||
// Reports its own duration on stdout.
|
||||
// The elapsed time goes to the job log, which is the only place a slow collection cycle
|
||||
// becomes visible — this process has no other output and no failure anyone would see.
|
||||
//
|
||||
// OPERATIONAL SAFEGUARDS
|
||||
// Refuses to run under a web server.
|
||||
// PHP_SAPI is checked first and a non-CLI invocation is answered with a 404 and no
|
||||
// output. Otherwise a browser hitting this path would trigger the full expensive
|
||||
// collection — including SSH to every partner — outside any cache or rate limit, once
|
||||
// per request.
|
||||
//
|
||||
// Read-only with respect to the system. Every collector observes; none start, stop, or
|
||||
// change anything. The only writes are the two cache files.
|
||||
//
|
||||
// Cache writes are atomic.
|
||||
// vv_cache_write() writes a .tmp and renames, so a page load landing mid-write reads
|
||||
// the previous complete payload rather than a truncated one.
|
||||
//
|
||||
// Every collector degrades to empty rather than fatal.
|
||||
// The library suppresses its filesystem reads and redirects stderr on every shell call,
|
||||
// so absent hardware yields an empty section. On a payload this wide that property is
|
||||
// what keeps one missing subsystem from failing the whole cycle and leaving both caches
|
||||
// to expire.
|
||||
//
|
||||
// A failed cycle is survivable by design.
|
||||
// Nothing here clears the previous cache before building the new one. A run that dies
|
||||
// partway leaves the last good payload in place, and the readers' own age windows —
|
||||
// 300s for monitor, 300s for arrs — are several cycles wide, so a single missed minute
|
||||
// is invisible.
|
||||
//
|
||||
// OUTPUT
|
||||
// VV_CACHE_DIR/monitor.json consumed by api/monitor.php
|
||||
// VV_CACHE_DIR/arrs.json consumed by api/arrs.php
|
||||
// stdout one timing line, captured into the job log
|
||||
//
|
||||
// DEPENDS ON
|
||||
// include/monitor.php, include/common.php, include/unraid_api.php,
|
||||
// include/vms.php, include/docker_folders.php, include/arrs.php
|
||||
// Tools/api_cache_writer.sh the bash wrapper cron actually invokes
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Collecting this payload means GraphQL, docker stats and SSH to every partner. It must never
|
||||
// be triggerable by an HTTP request.
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(404);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$_base = dirname(__DIR__);
|
||||
require_once $_base . '/include/monitor.php';
|
||||
|
||||
Reference in New Issue
Block a user