diff --git a/Plugin/unraid/pages/watchdog.php b/Plugin/unraid/pages/watchdog.php index 28e1194..8223f3a 100644 --- a/Plugin/unraid/pages/watchdog.php +++ b/Plugin/unraid/pages/watchdog.php @@ -5,16 +5,24 @@ // action levels, paused and stopped containers, restart history, OOM and reboot counts. // // DESIGN PRINCIPLES -// Pure view. No PHP logic and no include/ dependency — the whole page is markup plus a -// poll against api/watchdog.php. All parsing lives in include/watchdog.php behind that -// endpoint, so the page cannot hold a second opinion about what a strike means. +// State is a pure view. Everything the watchdogs have recorded arrives from one poll against +// api/watchdog.php, and all parsing lives in include/watchdog.php behind that endpoint, so the +// page cannot hold a second opinion about what a strike means. +// +// Configuration is not, and reads conf directly. The container lists and the settings card are +// rendered server-side from host*.conf at page load rather than from the polled payload: that +// payload is cached for up to 300s and drops the per-entry comments, which are where the reason +// for every number in these lists is written down. // // Shows every node, not just this one. A watchdog page that only covered the host you -// happened to open it on would miss exactly the node in trouble. +// happened to open it on would miss exactly the node in trouble. Editing is local-host only +// though — the partner owns its own host*.conf. // // OPERATIONAL SAFEGUARDS -// Strictly read-only. There is no control on this page that clears a strike, restarts a -// container, or resets a counter — those belong to the watchdog that owns the state. +// Read-only about state. There is no control on this page that clears a strike, restarts a +// container, or resets a counter — those belong to the watchdog that owns the state. Changing +// which containers a watchdog acts on is a conf edit and goes through the guarded write path +// with everyone else's; it is not a way to reach into what a watchdog has already decided. // // That holds for the assistant too, and is why the Why? buttons open troubleshoot rather than // repair. troubleshoot may read the machine and the orchestrator's log; it holds no conf_write @@ -27,11 +35,13 @@ // RENDERS // Per-node cards: resource / docker / system / storage / network watchdog state, // strike pills, restart history, reboot and OOM counts +// Editable container lists for this host: Tier 1 memory limits, the two pressure lists, Required // Assistant card, scoped to the Watchdog tab // Watchdog conf sections, via the shared settings renderer // // DEPENDS ON -// api/watchdog.php polled every 30s → include/watchdog.php +// api/watchdog.php polled every 30s → include/watchdog.php (served from a 300s cache) +// api/confform.php POST → the guarded conf write path, for the list editors // include/ai_chat.php → the shared chat component require_once dirname(__DIR__) . '/include/confui.php'; require_once dirname(__DIR__) . '/include/ai_chat.php'; @@ -67,6 +77,34 @@ require_once dirname(__DIR__) . '/include/ai_chat.php'; .vv-wd-ctr-lim { font-size:11px;color:#555; } .vv-wd-pressure{ grid-column:1/-1;border-color:#3a2000;background:#1a1000; } +/* ── Editable container lists ──────────────────────────────────────────────── + Rows, not a textarea. The same values are reachable through the settings card below as raw + bash array bodies, which is fine for reading and unforgiving for changing one number. */ +.vv-wl-card { background:#161616;border:1px solid #2a2a2a;border-radius:6px;padding:10px;min-width:0; + display:flex;flex-direction:column; } +.vv-wl-h { display:flex;align-items:baseline;gap:8px;margin-bottom:2px; } +.vv-wl-t { font-size:10px;font-weight:bold;color:#666;letter-spacing:.07em;text-transform:uppercase; } +.vv-wl-n { font-size:10px;color:#3a3a3a;margin-left:auto; } +.vv-wl-hint { font-size:10px;color:#3f3f3f;line-height:1.45;margin-bottom:8px; } +.vv-wl-row { display:flex;align-items:center;gap:5px;margin:2px 0; } +.vv-wl-row.gone .vv-wl-name { text-decoration:line-through;opacity:.4; } +.vv-wl-name { flex:1;min-width:0;background:#0d0d0d;border:1px solid #242424;color:#bbb; + font-size:11px;padding:3px 6px;border-radius:3px;font-family:inherit; } +.vv-wl-val { width:74px;flex-shrink:0;background:#0d0d0d;border:1px solid #242424;color:#ddd; + font-size:11px;padding:3px 6px;border-radius:3px;text-align:right; } +.vv-wl-unit { font-size:10px;color:#3a3a3a;flex-shrink:0;width:20px; } +.vv-wl-gb { font-size:10px;color:#4a4a4a;flex-shrink:0;width:48px;text-align:right; } +.vv-wl-x { background:none;border:none;color:#4a4a4a;cursor:pointer;font-size:13px; + line-height:1;padding:0 3px;flex-shrink:0; } +.vv-wl-x:hover{ color:#ef5350; } +.vv-wl-row.gone .vv-wl-x { color:#ef5350; } +.vv-wl-note { font-size:10px;color:#3a3a3a;margin:0 0 4px 6px;line-height:1.4; } +.vv-wl-foot { display:flex;align-items:center;gap:6px;margin-top:auto;padding-top:8px; } +.vv-wl-sum { font-size:10px;color:#3a3a3a;flex:1;min-width:0; } +.vv-wl-sum.ok { color:#4caf50; } +.vv-wl-sum.err{ color:#ef5350; } +.vv-wl-dirty { border-color:#3a4a6a !important; } + /* One host per row — inner grid sizes all cards equally */ .vv-wd-host-row { grid-column: 1 / -1; @@ -89,6 +127,57 @@ require_once dirname(__DIR__) . '/include/ai_chat.php'; + $_vv_wd_me . '_WATCHDOG_CONTAINERS', 'kind' => 'map', + 'title' => 'Memory limits (Tier 1)', 'unit' => 'MB', + 'hint' => 'Restarted when it holds more than this. Tier 1 is per container and acts before ' + . 'any pressure tier does.'], + ['key' => $_vv_wd_me . '_RW_PAUSE_CONTAINERS', 'kind' => 'list', + 'title' => 'Pause at medium pressure', 'unit' => '', + 'hint' => 'Paused — not stopped — while RAM sits below the medium threshold, and resumed on ' + . 'recovery. Safe for anything that can lose a few minutes.'], + ['key' => $_vv_wd_me . '_RW_STOP_CONTAINERS', 'kind' => 'list', + 'title' => 'Stop at hard pressure', 'unit' => '', + 'hint' => 'Stopped outright at the hard threshold, and not restarted automatically. The last ' + . 'step before the memory shutdown.'], + ['key' => $_vv_wd_me . '_WATCHDOG_REQUIRED_CONTAINERS', 'kind' => 'list', + 'title' => 'Required', 'unit' => '', + 'hint' => 'Must be running. Missing is a strike whether or not the container is being ' + . 'watched for anything else.'], +]; +$_vv_wd_fields = []; +foreach (vv_conf_all_groups($_vv_wd_file) as $_g) { + foreach ($_g['fields'] as $_f) $_vv_wd_fields[$_f['key']] = $_f; +} +$_vv_wd_lists = []; +foreach ($_vv_wd_defs as $_d) { + $_f = $_vv_wd_fields[$_d['key']] ?? null; + if (!$_f) continue; // key absent from this host's conf — draw nothing + $_vv_wd_lists[] = $_d + [ + 'file' => $_f['file'] ?? $_vv_wd_file, + 'type' => $_f['type'], // assoc_array | array — the writer splices on this + 'body' => $_f['value'], + ]; +} +?> + +
+ + +