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,11 +1,64 @@
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
// PURPOSE
|
||||
// Remote node metrics endpoint. The monitor page's partner cards — CPU, memory, storage,
|
||||
// uptime and container counts for every host other than this one.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// Split from monitor.php on cost, not on subject. Local metrics are cheap file reads;
|
||||
// remote metrics are SSH round trips to every partner. Keeping them on separate URLs lets
|
||||
// the page poll local stats often and remote stats rarely, and lets a dark partner slow
|
||||
// only its own request. Served from a 1-hour cache by default; ?live bypasses it for the
|
||||
// page's explicit refresh button.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// The cache is the default and the live call is the exception.
|
||||
// An hour is deliberately long. Partner hardware stats do not move fast enough to
|
||||
// justify paying SSH latency on every page load, and the refresh button exists for the
|
||||
// moment someone actually needs current numbers.
|
||||
//
|
||||
// Every payload carries its own timestamp.
|
||||
// ts is written into the cached document, so the page can render the age rather than
|
||||
// presenting hour-old numbers as current.
|
||||
//
|
||||
// The live path writes the cache too.
|
||||
// A manual refresh benefits every subsequent visitor instead of being discarded.
|
||||
//
|
||||
// OPERATIONAL SAFEGUARDS
|
||||
// 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. A legitimately empty result — the single-host case, where there
|
||||
// are no remote hosts at all — is served from cache rather than being mistaken for a
|
||||
// miss and forced onto the SSH path on every single poll.
|
||||
//
|
||||
// Read-only over SSH. The remote commands are stat collection only; nothing is started,
|
||||
// stopped, or written on a partner.
|
||||
//
|
||||
// Unreachable partners degrade per node inside vv_remote_hosts_stats(), so one dark host
|
||||
// cannot empty the other cards.
|
||||
//
|
||||
// HTTP caching is disabled even though the payload is cached server-side.
|
||||
// The two are not the same lever. The server-side cache has an age the page can see and
|
||||
// a bypass it can trigger; a browser or proxy cache has neither, and would defeat ?live
|
||||
// entirely.
|
||||
//
|
||||
// REQUEST
|
||||
// GET served from the 3600s cache when one is present
|
||||
// GET ?live bypass the cache, collect fresh, and rewrite it
|
||||
//
|
||||
// RESPONSE
|
||||
// {"remote_hosts":{…},"ts":epoch}
|
||||
//
|
||||
// DEPENDS ON
|
||||
// include/monitor.php vv_remote_hosts_stats(), vv_cache_read(), vv_cache_write()
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
header('Content-Type: application/json');
|
||||
header('Cache-Control: no-cache, no-store');
|
||||
require_once dirname(__DIR__) . '/include/monitor.php';
|
||||
|
||||
if (!isset($_GET['live'])) {
|
||||
$cached = vv_cache_read('monitor_remote', 3600);
|
||||
if ($cached) { echo json_encode($cached); exit; }
|
||||
if ($cached !== null) { echo json_encode($cached); exit; }
|
||||
}
|
||||
|
||||
$data = ['remote_hosts' => vv_remote_hosts_stats(), 'ts' => time()];
|
||||
|
||||
Reference in New Issue
Block a user