Files
Varaverk/Plugin/unraid/Varaverk.page
T
Gmer4Lfe be306cf86c Let one gate decide every AI surface, so switching AI off actually removes all of it
The Tools card adopted the AI scripts on the host check alone, and api/ai.php only
tested AI_ENABLED in front of ask, so a disabled subsystem still had rows to run and
an endpoint that answered.
2026-08-07 09:49:31 -04:00

127 lines
5.8 KiB
Plaintext

Menu="Tasks:95"
Title="Varaverk"
Icon="varaverk.png"
---
<?php
$plugin = 'varaverk';
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
$pluginDir = "$docroot/plugins/$plugin";
require_once "$pluginDir/include/config.php";
?>
<script>
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// CSRF token propagation — must run before any page JS.
//
// Unraid enforces CSRF centrally: /etc/php.ini sets auto_prepend_file to webGui's
// local_prepend.php, which terminates *every* POST that does not carry a valid token, before a
// single line of endpoint code runs. It accepts the token as a `csrf_token` POST field or as an
// X-CSRF-Token header.
//
// The webGUI's own injector is jQuery-only ($.ajaxPrefilter in HeadInlineJS.php). Varaverk's
// pages use native fetch(), which that prefilter does not touch — so without this shim every
// mutating request in the plugin is silently killed by the platform. Silently, because
// csrf_terminate() exits with no body: the fetch resolves, r.json() throws on the empty
// response, and the page's own .catch() swallows it.
//
// Setting the header rather than appending a body field is deliberate. It works identically for
// FormData, URLSearchParams and raw JSON bodies, so no call site has to know about it and a new
// endpoint cannot forget to include it. The header is only ever attached to same-origin
// Varaverk URLs; a cross-origin page cannot set a custom header without a preflight it will
// fail, which is precisely what makes this a CSRF defence rather than a formality.
//
// Inline, and above the tab content, because pages/*.php carry their own inline fetch calls and
// some fire on load — an external script could not be guaranteed to install first.
// ═══════════════════════════════════════════════════════════════════════════════════════════════
(function () {
if (window.__vvCsrfInstalled) return;
window.__vvCsrfInstalled = true;
var nativeFetch = window.fetch.bind(window);
window.fetch = function (input, init) {
var url = (typeof input === 'string') ? input : (input && input.url) || '';
if (url.indexOf('/plugins/varaverk/') !== -1 &&
typeof csrf_token !== 'undefined' && csrf_token) {
init = init || {};
var headers = new Headers(init.headers || (typeof input === 'object' && input.headers) || {});
if (!headers.has('X-CSRF-Token')) headers.set('X-CSRF-Token', csrf_token);
init = Object.assign({}, init, { headers: headers });
}
return nativeFetch(input, init);
};
})();
</script>
<?php
// First-run check — show setup wizard if HOST1 is blank OR local host.conf is missing
$_master = vv_read_conf_raw('master.conf');
preg_match('/^\s*HOST1\s*=\s*"([^"]*)"/m', $_master, $_h1m);
$_host1_blank = empty(trim($_h1m[1] ?? ''));
$_my_hostid = vv_detect_host();
$_conf_missing = $_my_hostid !== 'unknown'
&& !file_exists(CONF_DIR . '/' . $_my_hostid . '.conf');
if ($_host1_blank || $_conf_missing) {
include "$pluginDir/pages/setup.php";
return;
}
unset($_master, $_h1m, $_host1_blank, $_my_hostid, $_conf_missing);
// Determine active tab
$tab = $_GET['tab'] ?? 'monitor';
$validTabs = ['monitor', 'scheduler', 'docker', 'watchdog', 'partnership', 'fallback', 'arrs', 'rsync', 'auth', 'settings'];
// The AI tab exists only on HOST1, and only while AI_ENABLED is true. Appended to $validTabs
// rather than filtered out of it, so the check below rejects ?tab=ai server-side as well —
// omitting the link is presentation, not access control, and api/ai.php refuses every action
// on the same two conditions independently.
//
// The host half is not a preference: HOST1 owns the GPU, the Ollama process and the index, and
// include/ai.php only ever reads the *local* {HOST}_OLLAMA_URL — there is no Tailscale resolver
// in the PHP layer the way there is in the shell. On any other host the tab could only render
// and then fail its own health check.
$_vv_ai = vv_ai_ui_on();
if ($_vv_ai) $validTabs[] = 'ai';
if (!in_array($tab, $validTabs)) $tab = 'monitor';
$tabLabels = ['monitor' => 'Monitor', 'scheduler' => 'Scheduler', 'docker' => 'Docker', 'watchdog' => 'Watchdog', 'partnership' => 'Partnership', 'fallback' => 'FallBack', 'arrs' => 'Arrs', 'rsync' => 'Rsync', 'auth' => 'Auth Stack', 'settings' => 'Settings', 'ai' => 'AI'];
?>
<link rel="stylesheet" href="/plugins/<?=$plugin?>/css/varaverk.css">
<div id="varaverk-wrap" class="unapi">
<!-- Tab bar -->
<div id="vv-tabs">
<?php foreach ($validTabs as $t): ?>
<a href="?tab=<?=$t?>" class="localURL vv-tab<?= $t === $tab ? ' active' : '' ?>">
<?= $tabLabels[$t] ?? ucfirst($t) ?>
</a>
<?php endforeach; ?>
<div style="margin-left:auto;display:flex;align-items:center;gap:2px;">
<a href="https://github.com/FailedProxy/Varaverk" target="_blank"
style="padding:0 10px;font-size:10px;color:#333;text-decoration:none;
display:flex;align-items:center;letter-spacing:.03em;"
title="GitHub — source, issues, changelog">
⎋ GitHub
</a>
<button id="vv-expand-btn" type="button" onclick="vvToggleExpand()" title="Expand">⤢</button>
</div>
</div>
<!-- Tab content -->
<div id="vv-content">
<?php
$page = "$pluginDir/pages/$tab.php";
if (file_exists($page)) include $page;
else echo "<p>Page not found: $tab</p>";
?>
</div>
</div>
<script src="/plugins/<?=$plugin?>/js/varaverk.js"></script>