Mission Control v2 – Schritt 1: SoC-Refactor + Design 2.0
Architektur auf Separation of Concerns umgestellt – ohne Build-Schritt,
ohne neues Framework, ohne DB (KISS bleibt). Endpoint-URLs unveraendert,
daher 1:1-kompatibel zum bisherigen Stand.
Backend (Top-Level-Helfer + ein Router je Bereich):
- app.py auf duennen Einstieg reduziert (FastAPI + include_router + static)
- config/auth/jobengine/llamaswap als getrennte Helfer-Module
- Endpoints in routers/{models,jobs,maintenance}.py
Frontend (native ES-Module statt Single-File):
- index.html = Huelle: Sidebar-Nav, Topbar, Alert-Banner, Hash-Routing
- css/{base,components}.css – Tokens + Komponenten
- js/core/{api,ui,nav}.js + js/panels/{overview,models,maintenance,jobs}.js + main.js
- Panel-Vertrag: { id, mount?(), onStatus?(s), onJobs?(jobs) }
- Optik an docs/mission-control-overview.png angelehnt (Hero, KPI-Kacheln,
Listen, Aktivitaets-Stream, getoente Karten)
Doku: CLAUDE.md + README auf die neue Struktur aktualisiert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
// main.js — App-Boot: Panels mounten, Nav starten, Topbar/Alert pflegen, Polling fahren.
|
||||
// Panel-Vertrag: { id, mount?(), onStatus?(s), onJobs?(jobs) }.
|
||||
|
||||
import { api, getToken, setToken } from "./core/api.js";
|
||||
import { $ } from "./core/ui.js";
|
||||
import { initNav } from "./core/nav.js";
|
||||
|
||||
import overview from "./panels/overview.js";
|
||||
import models from "./panels/models.js";
|
||||
import maintenance from "./panels/maintenance.js";
|
||||
import jobs from "./panels/jobs.js";
|
||||
|
||||
const panels = [overview, models, maintenance, jobs];
|
||||
|
||||
let lastJobs = [];
|
||||
|
||||
// ---- Topbar / Alert aus dem Status ableiten ----
|
||||
function applyStatus(s) {
|
||||
const dot = $("#swdot"), label = $("#swlabel"), alert = $("#alert");
|
||||
|
||||
if (!s) {
|
||||
dot.className = "dot off";
|
||||
label.textContent = "Backend nicht erreichbar";
|
||||
$("#top-models").textContent = "–";
|
||||
showAlert("Backend nicht erreichbar — läuft uvicorn?", false);
|
||||
} else {
|
||||
const host = s.swap_url.replace(/^https?:\/\//, "");
|
||||
dot.className = "dot " + (s.swap_ok ? "on" : "off");
|
||||
label.textContent = (s.swap_ok ? "llama-swap online · " : "llama-swap offline · ") + host;
|
||||
$("#top-models").textContent = (s.models || []).length;
|
||||
if (s.swap_ok) hideAlert();
|
||||
else showAlert(`llama-swap nicht erreichbar unter <b>${host}</b> — läuft der Dienst?`, true);
|
||||
}
|
||||
for (const p of panels) p.onStatus?.(s);
|
||||
}
|
||||
|
||||
function applyJobs(jobs) {
|
||||
lastJobs = jobs || [];
|
||||
$("#top-jobs").textContent = lastJobs.filter(j => j.state === "running" || j.state === "queued").length;
|
||||
for (const p of panels) p.onJobs?.(lastJobs);
|
||||
}
|
||||
|
||||
function showAlert(html, warn) {
|
||||
const a = $("#alert");
|
||||
a.className = "alert" + (warn ? " warn" : "");
|
||||
a.innerHTML = `<span class="a-dot"></span><span>${html}</span>`;
|
||||
a.style.display = "flex";
|
||||
}
|
||||
function hideAlert() { $("#alert").style.display = "none"; }
|
||||
|
||||
// ---- Polling ----
|
||||
async function pollStatus() {
|
||||
try { applyStatus(await api("/api/status")); }
|
||||
catch { applyStatus(null); }
|
||||
}
|
||||
async function pollJobs() {
|
||||
try { applyJobs(await api("/api/jobs")); }
|
||||
catch { /* still */ }
|
||||
}
|
||||
|
||||
// ---- Boot ----
|
||||
function bootToken() {
|
||||
const i = $("#token");
|
||||
i.value = getToken();
|
||||
i.addEventListener("change", e => { setToken(e.target.value); pollStatus(); });
|
||||
}
|
||||
function tickClock() {
|
||||
$("#clock").textContent = new Date().toTimeString().slice(0, 5);
|
||||
}
|
||||
|
||||
for (const p of panels) p.mount?.();
|
||||
initNav("overview");
|
||||
bootToken();
|
||||
tickClock();
|
||||
document.addEventListener("mc:refresh", pollStatus);
|
||||
|
||||
pollStatus();
|
||||
pollJobs();
|
||||
setInterval(tickClock, 1000);
|
||||
setInterval(pollStatus, 3000);
|
||||
setInterval(pollJobs, 1500);
|
||||
Reference in New Issue
Block a user