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,4 +1,70 @@
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
// PURPOSE
|
||||
// Job log endpoint. Returns the tail of one job's log for the scheduler page's live output
|
||||
// view, and — on POST with clear=1 — truncates it.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// Read and clear share one URL because they share one identifier and one validation path.
|
||||
// The method decides which: GET always reads, POST reads unless clear is set. Splitting
|
||||
// them would duplicate the id validation, which is the only part with teeth.
|
||||
//
|
||||
// The response carries the log's mtime as ts, so the page can tell a log that is still
|
||||
// growing from one that has stopped without diffing the content it already has.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Tail, never the whole file.
|
||||
// The last 200 lines. Orchestrator logs are appended to indefinitely and this is polled
|
||||
// while a job runs, so returning the file would grow the response without bound exactly
|
||||
// when the page is fetching it most often.
|
||||
//
|
||||
// A log that does not exist is a successful empty read.
|
||||
// ok:true with empty content and ts:0. A job that has never run has no log, and that is
|
||||
// a normal state on a fresh install — not a condition the page should report as an
|
||||
// error.
|
||||
//
|
||||
// Clearing truncates, never deletes.
|
||||
// file_put_contents with an empty string keeps the inode, so a running job's open file
|
||||
// handle keeps writing to the same file. Unlinking it would leave the job appending to
|
||||
// a file nothing can read.
|
||||
//
|
||||
// OPERATIONAL SAFEGUARDS
|
||||
// The job id is validated identically on both paths.
|
||||
// ^[a-zA-Z0-9_./\-]+\.sh$ plus an explicit '..' check, applied before the method is
|
||||
// branched on, so the clear path cannot be reached with an id the read path would have
|
||||
// rejected. The slash must be permitted for Category/name.sh ids, which is why
|
||||
// traversal gets its own test rather than being implied by the character class.
|
||||
//
|
||||
// The id is never used as a path directly.
|
||||
// vv_job_log_path() maps it into LOG_DIR and rewrites the .sh suffix to .log, so the
|
||||
// only files this endpoint can name are job logs — the .sh requirement in the pattern
|
||||
// is what makes that rewrite total.
|
||||
//
|
||||
// Clearing requires POST and an explicit flag.
|
||||
// A GET cannot truncate a log, and a POST without clear=1 reads like any other request.
|
||||
// Destroying output needs to be asked for twice, in two different ways.
|
||||
//
|
||||
// Clearing an absent log is success, not an error.
|
||||
// file_exists() is checked first, so clearing a job that has never run reports ok
|
||||
// rather than failing on a file the caller did not expect to exist anyway.
|
||||
//
|
||||
// The tail read degrades to empty.
|
||||
// file() with a ?: [] fallback, so an unreadable or vanishing log yields an empty view
|
||||
// instead of a fatal that would blank the page mid-poll.
|
||||
//
|
||||
// REQUEST
|
||||
// GET ?id=<Category/name.sh> last 200 lines
|
||||
// POST id=<…> same as GET
|
||||
// POST id=<…> clear=1 truncate the log
|
||||
//
|
||||
// RESPONSE
|
||||
// {"ok":true,"content":"…","ts":<mtime>} read — ts is 0 when the log does not exist
|
||||
// {"ok":true} clear
|
||||
// {"ok":false,"error":"Invalid job id"}
|
||||
//
|
||||
// DEPENDS ON
|
||||
// include/scheduler.php vv_job_log_path()
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/scheduler.php';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user