Document the api layer's operational models, and fix two headers that had gone stale against the code
This commit is contained in:
@@ -5,6 +5,13 @@
|
||||
// state, tier activation, handback strikes, covered container status — for the fallback
|
||||
// tab's 30s poll.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// Computed fresh on every request, deliberately uncached. The inputs are small local state
|
||||
// files that fallback.sh rewrites as it moves between states, so assembling them costs about
|
||||
// nothing — and a cached fallback picture is the one kind of stale this tab must never serve.
|
||||
// A page showing NORMAL because the answer was cached before the switch is worse than a page
|
||||
// that took an extra moment to load.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Thin transport. Every judgement about what a state file means lives in
|
||||
// include/fallback.php. This file exists to give the browser a URL.
|
||||
|
||||
+16
-11
@@ -4,12 +4,19 @@
|
||||
// Active media sessions endpoint. Normalised now-playing across every Emby, Jellyfin and
|
||||
// Plex instance configured for this host, for the monitor page's session panel.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// Local is the default and stays the cheap path: one call per media server configured on this
|
||||
// host. Mesh adds one bounded SSH hop per partner and is only requested while the operator is
|
||||
// looking at the mesh view, so a dashboard left open on the default costs exactly what it did
|
||||
// before the scope existed.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Thin transport. Discovery, per-server API dialects and normalisation all live in
|
||||
// include/media.php; this file only sets the content type and encodes the result.
|
||||
//
|
||||
// No parameters. Which servers to ask is derived from conf, not from the request, so the
|
||||
// browser cannot point this endpoint at an arbitrary URL.
|
||||
// The request chooses a scope, never a target. scope= selects local or mesh; which servers
|
||||
// are asked, and which partners the mesh hop reaches, are both derived from conf. The browser
|
||||
// can widen what it asks for, but it cannot point this endpoint at an arbitrary URL.
|
||||
//
|
||||
// OPERATIONAL SAFEGUARDS
|
||||
// Bounded by the library's 3s per-request timeout.
|
||||
@@ -25,18 +32,16 @@
|
||||
// messages a client.
|
||||
//
|
||||
// REQUEST
|
||||
// GET, no parameters
|
||||
//
|
||||
// RESPONSE
|
||||
// vv_media_sessions() verbatim — a flat list of normalised sessions across all servers
|
||||
//
|
||||
// REQUEST
|
||||
// GET this host's sessions
|
||||
// GET ?scope=mesh every node's sessions, each row tagged with the host it is playing on
|
||||
//
|
||||
// Local is the default and stays the cheap path: one call per configured media server here.
|
||||
// Mesh adds one bounded SSH hop per partner and is only requested while the operator is looking
|
||||
// at the mesh view, so a dashboard left open on the default costs exactly what it did before.
|
||||
// RESPONSE
|
||||
// local {"scope":"local","sessions":[…],"server_names":[…],"server_count":N}
|
||||
// mesh {"scope":"mesh","nodes":[…],"sessions":[…],"server_names":[…],"server_count":N}
|
||||
//
|
||||
// sessions is the normalised list; in the mesh scope each row also carries the host it is
|
||||
// playing on. server_count counts media servers, not sessions. There is no error shape — an
|
||||
// unreachable server contributes nothing; see OPERATIONAL SAFEGUARDS.
|
||||
//
|
||||
// DEPENDS ON
|
||||
// include/media.php vv_media_sessions(), vv_media_sessions_mesh()
|
||||
|
||||
@@ -4,12 +4,19 @@
|
||||
// The Partnership tab's mesh chat: read a channel, post to it, forget a message on this
|
||||
// machine, and mark a channel read.
|
||||
//
|
||||
// REQUEST
|
||||
// GET channels + this host's id + unread counts
|
||||
// GET ?channel=<id> that channel's messages
|
||||
// POST action=send channel=<id> text=… [color=#rrggbb] [font=mono|sans|serif]
|
||||
// POST action=delete channel=<id> id=<msgid> local only
|
||||
// POST action=read channel=<id> mark seen up to now
|
||||
// OPERATIONAL MODEL
|
||||
// The method is the routing. Anything that is not a POST is a read — channel list or one
|
||||
// channel's messages — and every POST carries an action. That keeps the CSRF boundary and the
|
||||
// read/write boundary on the same line, so a mutation cannot arrive un-covered by being
|
||||
// spelled as a GET.
|
||||
//
|
||||
// Reads are computed per request, not cached. Unread counts come from walking the last 200
|
||||
// messages of each channel against this host's read mark; the card polls on a slow cycle and
|
||||
// the store is a small append log, so a cache would add a staleness class for no gain.
|
||||
//
|
||||
// Delivery is not part of the response's success. vv_nc_send() writes locally and spools for
|
||||
// any partner that could not be reached, and the reply carries that spool depth as `queued`.
|
||||
// A sleeping partner is a pending message, not a failed one.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Read marks are local and per channel. "Unread" is a fact about this operator at this
|
||||
@@ -28,6 +35,22 @@
|
||||
// locally and spooled for retry; saying "failed" over something that is stored and queued
|
||||
// would be the wrong claim.
|
||||
//
|
||||
// REQUEST
|
||||
// GET channels + this host's id + unread counts
|
||||
// GET ?channel=<id> that channel's messages
|
||||
// POST action=send channel=<id> text=… [color=#rrggbb] [font=mono|sans|serif]
|
||||
// POST action=delete channel=<id> id=<msgid> local only
|
||||
// POST action=read channel=<id> mark seen up to now
|
||||
//
|
||||
// RESPONSE
|
||||
// {"ok":true,"me":<host>,"channels":[{…,"unread":N}],"hostnames":{id:name}} channel list
|
||||
// {"ok":true,"me":<host>,"channel":<id>,"messages":[…],"last_read":<ts>} one channel
|
||||
// {"ok":true,"msg":{…},"queued":N} send; N = spooled
|
||||
// {"ok":true|false} delete
|
||||
// {"ok":true} read
|
||||
// {"ok":false,"error":"Unknown channel"|"Nothing to send"|"Could not store message"
|
||||
// |"No message id"|"Unknown action"}
|
||||
//
|
||||
// DEPENDS ON
|
||||
// include/node_chat.php
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
// reachability, SSH trust, conf-sync state and the shared-service inventory — for the
|
||||
// partnership tab's poll.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// One call, one document. The endpoint holds no logic and adds no cache of its own — it
|
||||
// encodes whatever vv_partnership_all() returns, so any freshness policy is the library's and
|
||||
// there is only one place it can be changed.
|
||||
//
|
||||
// The whole picture is assembled per request rather than exposed as separate endpoints for
|
||||
// hosts, trust and services. Those answers are read together and judged against each other —
|
||||
// a host that is reachable but has lost SSH trust is a different state from either fact alone,
|
||||
// and splitting them would let the tab render a combination that never existed at one moment.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Thin transport. Host enumeration, SSH probing and trust evaluation live in
|
||||
// include/partnership.php; this file only sets the content type and encodes.
|
||||
|
||||
@@ -4,6 +4,19 @@
|
||||
// Script and document reader. Returns the full text of one .sh or .md file inside
|
||||
// SCRIPTS_DIR — the source view behind the scheduler page's script viewer and the docs tab.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// Validate, resolve, read. The id is checked against a character class and an extension list
|
||||
// before it is joined to SCRIPTS_DIR, so nothing reaches the filesystem that did not already
|
||||
// look like a repo-relative path.
|
||||
//
|
||||
// The whole file is returned in one response — no ranges, no pagination. These are scripts and
|
||||
// documents, not logs; the largest is a few hundred kilobytes, and a viewer that had to stitch
|
||||
// pages together would be more machinery than the thing it displays.
|
||||
//
|
||||
// Every failure is a JSON body with ok:false, never an HTTP error code. The scheduler's viewer
|
||||
// and the docs tab both parse the response before looking at anything else, so a 404 would
|
||||
// surface as a parse failure rather than as "that file is not there".
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Two extensions, one endpoint.
|
||||
// Scripts and their READMEs are read the same way because they are read for the same
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
// resource, docker, system, storage, network and stability — together with the thresholds
|
||||
// each one is judging against, for the watchdog tab's poll.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// Served from a 5-minute cache unless ?live is present. Assembling this payload reads every
|
||||
// watchdog's state files and resolves every threshold out of master.conf, which is far more
|
||||
// work than the tab's poll needs — the watchdogs themselves only run every 15 minutes, so a
|
||||
// fresher answer would describe the same cycle.
|
||||
//
|
||||
// The cache is consulted before include/watchdog.php is even loaded, so a cache hit costs one
|
||||
// file read and nothing else. ?live skips the read, recomputes, and writes the result back, so
|
||||
// an explicit refresh also benefits the next visitor rather than being discarded.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Thin transport. State-file parsing and threshold resolution live in
|
||||
// include/watchdog.php; this file only sets the content type and encodes.
|
||||
@@ -15,7 +25,9 @@
|
||||
// against, so the page never has to fetch the two independently and risk mismatching
|
||||
// them across a conf edit.
|
||||
//
|
||||
// No parameters. Which watchdogs exist is fixed by the codebase, not by the request.
|
||||
// The only parameter is a freshness override. ?live decides how old an answer may be, never
|
||||
// what is in it — which watchdogs exist is fixed by the codebase, and no request can select,
|
||||
// filter or widen the set.
|
||||
//
|
||||
// OPERATIONAL SAFEGUARDS
|
||||
// Read-only. Nothing here clears a strike, lifts a skip-list entry, restarts a container,
|
||||
|
||||
Reference in New Issue
Block a user