MC2 Final (D16 c+e): System-Logs mit Fehler-Farben/Filter + Auto-Dedupe

c) System-Logs ausgebaut: neue LogConsole-Komponente färbt Fehler (rot) und
   Warnungen (amber) ein, "Nur Probleme"-Filter mit Zähler, Zeilen-Suche;
   voice-service in die Dienst-Liste aufgenommen (war nur backend-seitig).
e) Mem0-Dubletten automatisch: deterministischer Auto-Dedupe-Loop (täglich,
   apply=True, Schwelle 0.9 > manueller 0.85 da ohne Review) als Backend-
   Hintergrund-Task — kein Memory-Bloat mehr ohne Zutun. Knopf bleibt on-demand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-03 20:11:59 +02:00
parent 5ee3f8f73d
commit 8a2db70f3c
10 changed files with 271 additions and 161 deletions
+12 -57
View File
@@ -1,9 +1,10 @@
import { useEffect, useState, useRef } from "react"
import { X, RefreshCw, Terminal, Cpu, Server, Shield, Power, AlertTriangle, Camera, Bot, Box, Shuffle } from "lucide-react"
import { useEffect, useState } from "react"
import { X, RefreshCw, Cpu, Server, Shield, Power, Camera, Bot, Box, Shuffle } from "lucide-react"
import { api, type Job, type UpdatesResp, type ServicesResp, type UpdateDetails } from "@/lib/api"
import { cn } from "@/lib/utils"
import { CustomDialog } from "./CustomDialog"
import { SERVICES, ServiceRow, UpdateRow, formatBytes } from "./system/rows"
import { LogConsole } from "./system/LogConsole"
import { SettingsTab } from "./system/SettingsTab"
import { UpdateDetailModal, type UpdateDetailState } from "./system/UpdateDetailModal"
@@ -86,7 +87,6 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
}
}, [open, defaultTab])
const logContainerRef = useRef<HTMLPreElement | null>(null)
function loadUpdates() {
api<UpdatesResp>("/api/maintenance/updates")
@@ -124,15 +124,7 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
}
})
.catch((e) => setLogs(`Fehler: ${e.message}`))
.finally(() => {
setLoadingLogs(false)
// Scroll to bottom
setTimeout(() => {
if (logContainerRef.current) {
logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight
}
}, 50)
})
.finally(() => setLoadingLogs(false))
}
// Poll jobs & updates when open
@@ -593,51 +585,14 @@ export function SystemDrawer({ open, onClose, defaultTab = "maintenance" }: Syst
</button>
</div>
{/* Log Console Container */}
<div className="flex-1 flex flex-col min-h-[300px] border border-border/60 bg-black/50 rounded-xl overflow-hidden shadow-inner">
{/* Console Header */}
<div className="flex h-9 items-center justify-between px-4 border-b border-border/40 bg-black/30 shrink-0">
<div className="flex items-center gap-1.5 text-[10px] font-mono text-muted-foreground">
<Terminal className="h-3 w-3 text-primary" />
<span>stdout/stderr - {selectedService}</span>
</div>
<button
onClick={() => loadServiceLogs(selectedService)}
disabled={loadingLogs}
className="text-muted-foreground hover:text-foreground transition-colors"
>
<RefreshCw className={cn("h-3 w-3", loadingLogs && "animate-spin")} />
</button>
</div>
{/* Console Log Area */}
<pre
ref={logContainerRef}
className="flex-1 p-4 overflow-y-auto text-[10px] font-mono text-cyan-300/90 whitespace-pre-wrap select-text leading-relaxed scrollbar-thin"
>
{logStatus === "password_required" || logStatus === "incorrect_password" ? (
<div className="flex flex-col items-center justify-center p-6 text-center h-full space-y-3">
<AlertTriangle className="h-8 w-8 text-amber-400 animate-pulse animate-duration-1000" />
<div className="text-xs font-semibold text-amber-300">
{logStatus === "incorrect_password" ? "Falsches Sudo-Passwort hinterlegt." : "Sudo-Passwort für systemd-Dienste erforderlich."}
</div>
<p className="text-[10px] text-muted-foreground max-w-xs leading-normal">
Für das Auslesen der systemd-Logs von {selectedService} werden Root-Rechte benötigt. Hinterlege dein Passwort in den Einstellungen.
</p>
<button
onClick={() => setActiveTab("settings")}
className="px-3 py-1.5 text-[10px] font-semibold text-primary-foreground bg-primary hover:bg-primary/95 rounded-md transition-colors mt-2"
>
Sudo-Passwort eintragen
</button>
</div>
) : loadingLogs && !logs ? (
<span className="text-muted-foreground">Lade Logs...</span>
) : (
logs || <span className="text-muted-foreground">Keine Logeinträge vorhanden.</span>
)}
</pre>
</div>
<LogConsole
service={selectedService}
text={logs}
loading={loadingLogs}
logStatus={logStatus}
onRefresh={() => loadServiceLogs(selectedService)}
onOpenSettings={() => setActiveTab("settings")}
/>
</div>
)}