307 lines
13 KiB
Plaintext
307 lines
13 KiB
Plaintext
---
|
|
Menu="OtherSettings"
|
|
Type="xmenu"
|
|
Title="MUA"
|
|
Icon="cubes"
|
|
---
|
|
<?php
|
|
/**
|
|
* MUA - Mikes Unraid Agent
|
|
* WebGUI-Tab (mua.page)
|
|
* Unraid-Standard .page-Format
|
|
*
|
|
* Einstellungen:
|
|
* A) API-Key generieren (POST /config {generate:true})
|
|
* B) Tools per Checkbox aktivieren/deaktivieren (POST /config {enabledTools:[...]})
|
|
*
|
|
* Config-Server: 127.0.0.1:3013 (nur localhost, kein Auth nötig —
|
|
* WebGUI läuft auf demselben Host und ist selbst authifiziert).
|
|
*/
|
|
|
|
$plugin_dir = '/usr/local/emhttp/plugins/mua';
|
|
$port = 3002;
|
|
$config_port = 3013;
|
|
$pidfile = '/var/run/mua.pid';
|
|
$binary = '/usr/local/bin/mua';
|
|
$endpoint = 'http://' . ($_SERVER['SERVER_ADDR'] ?? '127.0.0.1') . ':' . $port . '/mcp';
|
|
|
|
// ── Status bestimmen ────────────────────────────────────────────────────
|
|
$running = false;
|
|
$pid = null;
|
|
if (file_exists($pidfile)) {
|
|
$pid = trim(file_get_contents($pidfile));
|
|
if ($pid && is_numeric($pid) && @posix_kill((int)$pid, 0)) {
|
|
$running = true;
|
|
}
|
|
}
|
|
|
|
// ── Health-Check (MCP-Server, Port 3002) ────────────────────────────────
|
|
$health = null;
|
|
$health_code = 0;
|
|
$ch = curl_init('http://127.0.0.1:' . $port . '/health');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 3,
|
|
CURLOPT_CONNECTTIMEOUT => 2,
|
|
]);
|
|
$health = curl_exec($ch);
|
|
$health_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$health_ok = ($health_code === 200);
|
|
|
|
// Version aus /health lesen (dynamisch, kein hartkodiertes rNNN)
|
|
$running_version = '';
|
|
if ($health_ok && $health !== false) {
|
|
$h = json_decode($health, true);
|
|
$running_version = $h['version'] ?? '';
|
|
}
|
|
|
|
// ── Config vom Config-Server laden (Port 3003) ──────────────────────────
|
|
$config = null;
|
|
$ch = curl_init('http://127.0.0.1:' . $config_port . '/config');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 3,
|
|
CURLOPT_CONNECTTIMEOUT => 2,
|
|
]);
|
|
$config_raw = curl_exec($ch);
|
|
$config_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
if ($config_code === 200 && $config_raw !== false) {
|
|
$config = json_decode($config_raw, true);
|
|
}
|
|
|
|
$api_key = $config['apiKey'] ?? '';
|
|
$enabled_tools = $config['enabledTools'] ?? [];
|
|
$all_tools = $config['allTools'] ?? [];
|
|
$config_ok = ($config !== null);
|
|
|
|
// ── POST-Handling (Einstellungen speichern) ─────────────────────────────
|
|
$flash_msg = '';
|
|
$flash_type = '';
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$payload = [];
|
|
|
|
// A) API-Key generieren
|
|
if (isset($_POST['action']) && $_POST['action'] === 'generate_key') {
|
|
$payload['generate'] = true;
|
|
}
|
|
|
|
// B) Tool-Checkboxen
|
|
if (isset($_POST['action']) && $_POST['action'] === 'save_tools') {
|
|
$selected = [];
|
|
foreach ($all_tools as $tool) {
|
|
if (isset($_POST['tool_' . $tool])) {
|
|
$selected[] = $tool;
|
|
}
|
|
}
|
|
$payload['enabledTools'] = $selected;
|
|
}
|
|
|
|
// C) Service neu starten
|
|
if (isset($_POST['action']) && $_POST['action'] === 'restart') {
|
|
$ch = curl_init('http://127.0.0.1:' . $config_port . '/restart');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 5,
|
|
CURLOPT_CONNECTTIMEOUT => 3,
|
|
CURLOPT_POST => true,
|
|
]);
|
|
$resp_raw = curl_exec($ch);
|
|
$resp_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
if ($resp_code === 200) {
|
|
$flash_msg = 'MUA wird neu gestartet (Service restart).';
|
|
$flash_type = 'ok';
|
|
} else {
|
|
$flash_msg = 'Fehler beim Neustart (HTTP ' . $resp_code . ')';
|
|
$flash_type = 'err';
|
|
}
|
|
}
|
|
|
|
if (!empty($payload)) {
|
|
$ch = curl_init('http://127.0.0.1:' . $config_port . '/config');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 5,
|
|
CURLOPT_CONNECTTIMEOUT => 3,
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => json_encode($payload),
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
|
]);
|
|
$resp_raw = curl_exec($ch);
|
|
$resp_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$resp = json_decode($resp_raw, true);
|
|
|
|
if ($resp_code === 200 && isset($resp['ok'])) {
|
|
// Config neu laden
|
|
$ch = curl_init('http://127.0.0.1:' . $config_port . '/config');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 3,
|
|
CURLOPT_CONNECTTIMEOUT => 2,
|
|
]);
|
|
$config_raw = curl_exec($ch);
|
|
curl_close($ch);
|
|
$config = json_decode($config_raw, true);
|
|
$api_key = $config['apiKey'] ?? '';
|
|
$enabled_tools = $config['enabledTools'] ?? [];
|
|
|
|
if ($payload['generate'] ?? false) {
|
|
$flash_msg = 'Neuer API-Key generiert.';
|
|
$flash_type = 'ok';
|
|
} else {
|
|
$flash_msg = 'Tool-Einstellungen gespeichert (' . count($selected) . ' von ' . count($all_tools) . ' aktiv).';
|
|
$flash_type = 'ok';
|
|
}
|
|
} else {
|
|
$flash_msg = 'Fehler beim Speichern (HTTP ' . $resp_code . ')';
|
|
$flash_type = 'err';
|
|
}
|
|
}
|
|
}
|
|
|
|
$binary_exists = file_exists($binary);
|
|
$host_ip = $_SERVER['SERVER_ADDR'] ?? '127.0.0.1';
|
|
|
|
// Tool-Gruppierung für die Checkbox-Liste
|
|
$tool_groups = [
|
|
'Docker' => [
|
|
'unraid_docker_list', 'unraid_docker_inspect', 'unraid_docker_logs',
|
|
'unraid_docker_analyze_logs', 'unraid_docker_processes', 'unraid_docker_stats',
|
|
'unraid_docker_info', 'unraid_docker_start', 'unraid_docker_stop',
|
|
'unraid_docker_restart', 'unraid_docker_create', 'unraid_docker_modify',
|
|
'unraid_docker_update', 'unraid_docker_rebuild',
|
|
],
|
|
'Netzwerk' => [
|
|
'unraid_network_inventory', 'unraid_network_list', 'unraid_network_inspect',
|
|
'unraid_network_host_state', 'unraid_network_audit_tcp', 'unraid_network_lan_probe',
|
|
],
|
|
'System' => [
|
|
'unraid_system_connection_test',
|
|
'unraid_system_shell',
|
|
],
|
|
];
|
|
?>
|
|
<style>
|
|
.mua-status { padding: 12px 16px; border-radius: 6px; margin: 12px 0; font-size: 0.95em; }
|
|
.mua-status.ok { background: #e8f5e9; border: 1px solid #4caf50; color: #2e7d32; }
|
|
.mua-status.err { background: #ffebee; border: 1px solid #f44336; color: #c62828; }
|
|
.mua-status.warn { background: #fff3e0; border: 1px solid #ff9800; color: #e65100; }
|
|
.mua-endpoint { font-size: 1.1em; color: #4a90d9; font-weight: 600; }
|
|
.mua-key { font-size: 1.05em; word-break: break-all; background: #f5f5f5; padding: 8px 12px; border-radius: 4px; border: 1px solid #ddd; }
|
|
.mua-tool-group { margin: 12px 0; }
|
|
.mua-tool-group h3 { margin: 8px 0 4px 0; font-size: 1em; color: #555; }
|
|
.mua-tool-item { display: inline-block; margin: 4px 12px 4px 0; font-size: 0.9em; }
|
|
.mua-tool-item code { font-size: 0.85em; }
|
|
.mua-btn { padding: 6px 16px; border-radius: 4px; border: 1px solid #ccc; background: #f5f5f5; cursor: pointer; font-size: 0.9em; }
|
|
.mua-btn:hover { background: #e0e0e0; }
|
|
.mua-btn-primary { background: #4a90d9; color: white; border-color: #3a7bc0; }
|
|
.mua-btn-primary:hover { background: #3a7bc0; }
|
|
</style>
|
|
|
|
<?php if ($running): ?>
|
|
<div class="mua-status ok">✅ <strong>MCP-Server läuft</strong> (PID <?= $pid ?>, Port <?= $port ?>)</div>
|
|
<?php else: ?>
|
|
<div class="mua-status err">❌ <strong>MCP-Server läuft nicht</strong> — starte mit <code>/etc/rc.d/rc.mua start</code></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($health_ok): ?>
|
|
<div class="mua-status ok">✅ Health-Check: OK</div>
|
|
<?php elseif ($health !== false): ?>
|
|
<div class="mua-status warn">⚠️ Health-Check: HTTP <?= $health_code ?></div>
|
|
<?php else: ?>
|
|
<div class="mua-status warn">⚠️ Health-Check: nicht erreichbar</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($flash_msg): ?>
|
|
<div class="mua-status <?= $flash_type ?>"><?= htmlspecialchars($flash_msg) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<h2>MCP-Endpunkt</h2>
|
|
<p class="mua-endpoint"><?= $endpoint ?></p>
|
|
<p>Transport: <code>Streamable HTTP</code> (POST-only) · JSON-RPC 2.0</p>
|
|
|
|
<h2>API-Key (Authentifizierung)</h2>
|
|
<?php if ($config_ok && $api_key): ?>
|
|
<div class="mua-status ok">✅ API-Key aktiv — Clients müssen diesen als Bearer-Token senden</div>
|
|
<p><strong>API-Key:*** <code class="mua-key"><?= htmlspecialchars($api_key) ?></code></p>
|
|
<p><strong>Authorization-Header:</strong> <code>Authorization: Bearer *** htmlspecialchars($api_key) ?></code></p>
|
|
<p style="color:#666; font-size:0.9em;">
|
|
Beispiel (curl):<br>
|
|
<code>curl -X POST <?= $endpoint ?> -H "Authorization: Bearer *** htmlspecialchars($api_key) ?>" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'</code>
|
|
</p>
|
|
<form method="post" style="margin-top:8px;">
|
|
<input type="hidden" name="action" value="generate_key">
|
|
<button type="submit" class="mua-btn mua-btn-primary" onclick="return confirm('Neuen API-Key generieren? Der alte Key wird ungültig!')">🔄 Neuen API-Key generieren</button>
|
|
</form>
|
|
<?php elseif ($config_ok): ?>
|
|
<div class="mua-status warn">⚠️ Kein API-Key gesetzt. Generiere einen, um den MCP-Endpunkt zu schützen.</div>
|
|
<form method="post" style="margin-top:8px;">
|
|
<input type="hidden" name="action" value="generate_key">
|
|
<button type="submit" class="mua-btn mua-btn-primary">🔄 API-Key generieren</button>
|
|
</form>
|
|
<?php else: ?>
|
|
<div class="mua-status warn">⚠️ Config-Server nicht erreichbar (Port <?= $config_port ?>). Starte den MCP-Server, um die Einstellungen zu verwalten.</div>
|
|
<?php endif; ?>
|
|
|
|
<h2>Tools (aktivieren / deaktivieren)</h2>
|
|
<?php if ($config_ok && !empty($all_tools)): ?>
|
|
<p style="color:#666; font-size:0.9em;">
|
|
<?= count($enabled_tools) === 0 ? 'Alle Tools aktiv' : count($enabled_tools) . ' von ' . count($all_tools) . ' Tools aktiv' ?>.
|
|
Deaktivierte Tools werden nicht in <code>tools/list</code> angezeigt und bei <code>tools/call</code> abgelehnt.
|
|
</p>
|
|
<form method="post">
|
|
<input type="hidden" name="action" value="save_tools">
|
|
<?php foreach ($tool_groups as $group_name => $group_tools): ?>
|
|
<div class="mua-tool-group">
|
|
<h3><?= htmlspecialchars($group_name) ?> (<?= count($group_tools) ?>)</h3>
|
|
<?php foreach ($group_tools as $tool): ?>
|
|
<?php $checked = (count($enabled_tools) === 0 || in_array($tool, $enabled_tools)) ? 'checked' : ''; ?>
|
|
<label class="mua-tool-item">
|
|
<input type="checkbox" name="tool_<?= htmlspecialchars($tool) ?>" value="1" <?= $checked ?>>
|
|
<code><?= htmlspecialchars($tool) ?></code>
|
|
</label>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<div style="margin-top:12px;">
|
|
<button type="submit" class="mua-btn mua-btn-primary">💾 Einstellungen speichern</button>
|
|
</div>
|
|
</form>
|
|
<?php else: ?>
|
|
<div class="mua-status warn">⚠️ Tool-Liste nicht verfügbar (Config-Server nicht erreichbar).</div>
|
|
<?php endif; ?>
|
|
|
|
<h2>Details</h2>
|
|
<table>
|
|
<tr><th>Plugin</th><td>MUA (Mikes Unraid Agent)</td></tr>
|
|
<tr><th>Version</th><td><?php echo $running_version ? htmlspecialchars($running_version) : '—'; ?></td></tr>
|
|
<tr><th>Runtime</th><td>TypeScript (Bun Runtime, kompiliertes Binary)</td></tr>
|
|
<tr><th>Binary</th><td><code><?= $binary ?></code> <?= $binary_exists ? '✅' : '❌ fehlt' ?></td></tr>
|
|
<tr><th>Port (MCP)</th><td><?= $port ?></td></tr>
|
|
<tr><th>Port (Config)</th><td><?= $config_port ?> (nur localhost)</td></tr>
|
|
<tr><th>Host-IP</th><td><?= $host_ip ?></td></tr>
|
|
<tr><th>Tools</th><td><?= count($all_tools) ?: 22 ?> (Docker: 14, Netzwerk: 6, System: 2)</td></tr>
|
|
<tr><th>Plugin-Verzeichnis</th><td><code><?= $plugin_dir ?></code></td></tr>
|
|
<tr><th>Service</th><td><code>/etc/rc.d/rc.mua</code> (SysVinit)</td></tr>
|
|
</table>
|
|
|
|
<h2>Verwaltung</h2>
|
|
<table>
|
|
<tr><th>Aktion</th><th>Befehl</th></tr>
|
|
<tr><td>Starten</td><td><code>/etc/rc.d/rc.mua start</code></td></tr>
|
|
<tr><td>Stoppen</td><td><code>/etc/rc.d/rc.mua stop</code></td></tr>
|
|
<tr><td>Neu starten</td><td><code>/etc/rc.d/rc.mua restart</code></td></tr>
|
|
<tr><td>Status</td><td><code>/etc/rc.d/rc.mua status</code></td></tr>
|
|
<tr><td>Logs</td><td><code>tail -f /var/log/plugins/mua.log</code></td></tr>
|
|
</table>
|
|
|
|
<h2>Service neu starten</h2>
|
|
<p>Startet den MCP-Service neu (z. B. nach einem Plugin-Update, wenn sich das Binary geändert hat).</p>
|
|
<form method="post" onsubmit="return confirm('MUA-Service neu starten?');">
|
|
<input type="hidden" name="action" value="restart">
|
|
<button type="submit" class="btn btn-primary">🔄 MUA neu starten</button>
|
|
</form>
|