r004: Auto-Restart + WebGUI-Restart-Button + dynamische Version

- Auto-Restart: Binary erkennt Update auf Platte (mtime/size) → rc.mua restart
- WebGUI: 'MUA neu starten'-Button (POST /restart über Config-Server)
- POST-INSTALL/UPDATE: Service wird nach Installation/Update automatisch neu gestartet
- WebGUI: Version dynamisch aus /health (kein hartkodiertes rNNN)
- MUA_VERSION in helpers.ts auf r004
- .txz neu gepackt, SHA256 aktualisiert
This commit is contained in:
Mikei386
2026-08-18 11:40:17 +02:00
parent b05cc89a3d
commit aec6ba76ce
9 changed files with 122 additions and 14 deletions
Binary file not shown.
+1 -1
View File
@@ -10,7 +10,7 @@
error_reporting(E_ALL);
ini_set('display_errors', 0); // Fehler werden als JSON-RPC-Error zurückgegeben, nicht als HTML
const MUA_VERSION = '2026.08.18.r003';
const MUA_VERSION = '2026.08.18.r004';
const MUA_SERVER_NAME = 'mua';
/**
+1 -1
View File
@@ -21,7 +21,7 @@ const MUA_PORT = 3002;
const MUA_BIND = '0.0.0.0';
const MUA_SESSION_TTL = 3600; // Sekunden
const MUA_SERVER_NAME = 'mua';
const MUA_VERSION = '2026.08.18.r003';
const MUA_VERSION = '2026.08.18.r004';
// ── Session-Management (in-memory) ───────────────────────────────────────
$sessions = []; // session_id => ['created' => time, 'initialized' => bool]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mua",
"version": "2026.08.18.r003",
"version": "2026.08.18.r004",
"description": "Mikes Unraid Agent - MCP over HTTP (Streamable HTTP) for Unraid",
"type": "module",
"main": "src/index.ts",
+20 -7
View File
@@ -2,13 +2,13 @@
<!DOCTYPE PLUGIN [
<!ENTITY name "mua">
<!ENTITY author "Michael">
<!ENTITY version "2026.08.18.r003">
<!ENTITY version "2026.08.18.r004">
<!ENTITY launch "Settings/MUA">
<!ENTITY pluginURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/plugin/mua.plg">
<!ENTITY pluginLOC "/boot/config/plugins/&name;">
<!ENTITY emhttpLOC "/usr/local/emhttp/plugins/&name;">
<!ENTITY txzURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.18.r003-x86_64-1.txz">
<!ENTITY txzSHA256 "ee6227a5367e57508e7408111041102a85ce260386730860c26dc6284ae5f03e">
<!ENTITY txzURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.18.r004-x86_64-1.txz">
<!ENTITY txzSHA256 "0c56cc645de4f312f7d6ded3872734b1ba8607ba993c6ec1519f3768435eca2f">
]>
<PLUGIN name="&name;"
@@ -23,7 +23,12 @@
>
<CHANGES>
### 2026.08.18.r003
### 2026.08.18.r004
- Auto-Restart: Binary erkennt Update auf Platte (mtime/size) und startet den Service automatisch neu.
- WebGUI: „MUA neu starten"-Button (manueller Restart über Config-Server).
- POST-INSTALL/UPDATE: Service wird nach Installation/Update automatisch neu gestartet.
- WebGUI: Version wird dynamisch aus /health gelesen (kein hartkodiertes rNNN).
### 2026.08.18.r002
- API-Key-Authentifizierung (Bearer-Token) für den MCP-HTTP-Endpunkt (Port 3002). /health bleibt offen.
- Einstellungs-Maske im WebGUI-Tab: API-Key generieren + Tools per Checkbox aktivieren/deaktivieren.
- Config-Server auf 127.0.0.1:3013 (nur localhost) für WebGUI-Integration.
@@ -70,23 +75,31 @@ Das .txz enthält:
install/doinst.sh (läuft nach Installation)
===========================================
-->
<FILE Name="mua-2026.08.18.r003-x86_64-1.txz" Run="upgradepkg --install-new" Mode="755" Min="7.0.0">
<FILE Name="mua-2026.08.18.r004-x86_64-1.txz" Run="upgradepkg --install-new" Mode="755" Min="7.0.0">
<URL>&txzURL;</URL>
<SHA256>&txzSHA256;</SHA256>
</FILE>
<!--
===========================================
POST-INSTALL: Health-Check
POST-INSTALL: Service neu starten + Health-Check
===========================================
Läuft nach Installation UND nach Update.
`restart` stoppt den Service (falls er läuft) und startet ihn neu —
funktioniert sowohl bei Erstinstallation als auch bei Update.
===========================================
-->
<FILE Run="/bin/bash">
<INLINE>
set +e
echo ""
echo " Starte MUA-Service neu..."
/etc/rc.d/rc.mua restart 2>/dev/null || true
echo ""
echo " Führe Health-Check aus..."
sleep 2
sleep 3
if curl -sf "http://127.0.0.1:3002/health" > /dev/null 2&gt;&amp;1; then
echo ""
echo " Health-Check: OK"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "mua",
"author": "Michael",
"version": "2026.08.18.r003",
"version": "2026.08.18.r004",
"minver": "7.0.0",
"pluginDirectory": "/usr/local/emhttp/plugins/mua",
"configDirectory": "/boot/config/plugins/mua",
+36 -1
View File
@@ -49,6 +49,13 @@ $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');
@@ -91,6 +98,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$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, [
@@ -248,7 +276,7 @@ $tool_groups = [
<h2>Details</h2>
<table>
<tr><th>Plugin</th><td>MUA (Mikes Unraid Agent)</td></tr>
<tr><th>Version</th><td>2026.08.18.r003</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>
@@ -268,3 +296,10 @@ $tool_groups = [
<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.&nbsp;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>
+1 -1
View File
@@ -13,7 +13,7 @@ import { createConnection, type Socket } from "net";
// ── Konstanten ──────────────────────────────────────────────────────────
export const MUA_SERVER_NAME = "mua";
export const MUA_VERSION = "2026.08.18.r003";
export const MUA_VERSION = "2026.08.18.r004";
export const MUA_PROTOCOL_VERSION = "2025-03-26";
export const PHP_HELPER = "/usr/local/bin/unraid-docker-mcp-helper.php";
+61 -1
View File
@@ -21,6 +21,8 @@ import {
import { TOOLS, toolByName } from "./tools";
import { checkAuth, isToolEnabled, getApiKey, getEnabledTools, setApiKey, setEnabledTools } from "./auth";
import { randomBytes } from "node:crypto";
import { spawn } from "node:child_process";
import { statSync } from "node:fs";
const PORT = Number(process.env["MUA_PORT"] ?? 3002);
const HOST = process.env["MUA_HOST"] ?? "0.0.0.0";
@@ -312,10 +314,25 @@ try {
const path = url.pathname;
const method = req.method;
if (path !== "/config") {
if (path !== "/config" && path !== "/restart") {
return Response.json({ error: "Not found" }, { status: 404 });
}
// POST /restart: Service neu starten (für WebGUI-Button)
if (path === "/restart" && method === "POST") {
const rcScript = process.env["MUA_RC_SCRIPT"] ?? "/etc/rc.d/rc.mua";
try {
const child = spawn(rcScript, ["restart"], {
detached: true,
stdio: "ignore",
});
child.unref();
return Response.json({ ok: true, message: "Restart initiated" });
} catch (e) {
return Response.json({ error: String(e) }, { status: 500 });
}
}
// GET: Config lesen
if (method === "GET") {
return Response.json({
@@ -368,6 +385,49 @@ console.log(
(configServer ? ` + 127.0.0.1:${CONFIG_PORT} (config)` : " (config: NICHT VERFÜGBAR)"),
);
// ── Auto-Restart bei Binary-Update ──────────────────────────────────────
// Beim Plugin-Update wird das Binary auf Platte ersetzt, aber der laufende
// Prozess hält das alte Binary im Speicher (Linux-Verhalten). Wir erkennen
// den Change (mtime + size) und starten den Service automatisch neu.
const BINARY_PATH = process.env["MUA_BINARY"] ?? "/usr/local/bin/mua";
const RC_SCRIPT = process.env["MUA_RC_SCRIPT"] ?? "/etc/rc.d/rc.mua";
let binarySnapshot: { mtimeMs: number; size: number } | null = null;
try {
const st = statSync(BINARY_PATH);
binarySnapshot = { mtimeMs: st.mtimeMs, size: st.size };
} catch {
// Binary-Pfad nicht lesbar (z.B. lokaler Test) — Auto-Restart deaktiviert
}
function checkBinaryUpdate() {
if (!binarySnapshot) return;
try {
const st = statSync(BINARY_PATH);
if (st.mtimeMs !== binarySnapshot.mtimeMs || st.size !== binarySnapshot.size) {
console.log(
`[MUA] Binary-Update erkannt (${BINARY_PATH} geändert). ` +
`Starte Service neu: ${RC_SCRIPT} restart`,
);
// rc.mua restart detached ausführen, dann sauber beenden
const child = spawn(RC_SCRIPT, ["restart"], {
detached: true,
stdio: "ignore",
});
child.unref();
// Kurze Pause, damit der Log-Flush durchkommt
setTimeout(() => process.exit(0), 500);
}
} catch {
// Binary temporär nicht lesbar — ignorieren
}
}
const updateCheckInterval = setInterval(
checkBinaryUpdate,
Number(process.env["MUA_UPDATE_CHECK_INTERVAL"] ?? 30_000),
);
updateCheckInterval.unref();
// Graceful shutdown
process.on("SIGTERM", () => {
console.log("[MUA] SIGTERM received, shutting down");