Workspace: keine Kachel mehr ohne Ziel -- Automationen bekommen ihren Ort
Filipe: "da sind aber noch zwei kategorien zu". Er hat recht, und es waren zwei VERSCHIEDENE Fehler. 1) "DASHBOARD" war eine Kachel, die man nicht oeffnen kann -- man steht ja schon darauf. Sie stand seit dem ersten Tag als Platzhalter da. Ein Eintrag, der nirgendwohin fuehrt, sieht aus wie etwas Unfertiges. Entfernt. 2) "AUTOMATIONEN" trug noch "Phase 4", obwohl die Automationen laengst liefen -- sie waren nur verteilt (Hinweise auf der Uebersicht, Suche ueberall, KI-Vorschlaege an drei Stellen) und hatten keinen Ort. Jetzt haben sie einen. DIE SEITE FUEHRT BEWUSST NICHTS EIGENES. Was dort steht, kommt aus den Bereichen selbst -- eine Seite, die Automationen doppelt abbildet, waere die naechste Stelle, die irgendwann etwas anderes behauptet als die Wirklichkeit. Vier Abschnitte: - Die lokale KI mit Zustand, Modell, Ort, Kosten und Grenzen -- und dem einzigen Schalter der Seite. Sie ist die einzige Automation, die Rechenzeit kostet, die der Website gehoert. - "Was gerade anliegt": dieselben Hinweise wie auf der Uebersicht, hier vollstaendig statt gekuerzt. - "Laeuft dauerhaft im Hintergrund": sieben Regeln im Klartext. Jede beschreibt etwas, das tatsaechlich im Code steht. - "Zuletzt automatisch passiert": aus dem Protokoll, gefiltert auf das, was ohne Zutun geschah. DER KI-SCHALTER WIRD AN ZWEI STELLEN GEPRUEFT: beim Anzeigen der Knoepfe UND in jedem Endpunkt. Eine Oberflaeche, die etwas versteckt, ist keine Sperre. Geprueft: abgeschaltet liefert der Endpunkt 403, ein Creator darf gar nicht schalten (403), die Einstellung ueberdauert einen Neustart (eigene Tabelle, damit sie dieselbe Sicherung bekommt wie alles andere). NEBENBEI DERSELBE FEHLER WIE FRUEHER: Der Schalter-Baustein lag in wissen.css und fehlte prompt auf der neuen Seite -- dort erschien er als nacktes Kaestchen. Wie damals bei .knopf-still. Liegt jetzt in gate.css. Geprueft: 17 Kacheln, KEINE mehr ohne Ziel. Creator wird von der Seite weggeleitet (302). Handy ohne Ueberlauf, 0 Konsolenfehler.
This commit is contained in:
+34
-3
@@ -27,7 +27,9 @@
|
||||
===================================================================== */
|
||||
|
||||
import express from "express";
|
||||
import { sitzungLesen, protokolliere, echteIp } from "./workspace.js";
|
||||
import {
|
||||
sitzungLesen, protokolliere, echteIp, einstellung, einstellungSetzen, istLeitung,
|
||||
} from "./workspace.js";
|
||||
|
||||
export const kiRouter = express.Router();
|
||||
|
||||
@@ -38,6 +40,11 @@ const TEXT_MAX = 4000;
|
||||
/* Nur eine Anfrage zur Zeit -- siehe Regel 3. */
|
||||
let laeuft = false;
|
||||
|
||||
/* Der Schalter aus der Automationen-Seite. Steht bewusst hier oben, vor
|
||||
der ersten Verwendung -- als const weiter unten waere es zwar zur
|
||||
Laufzeit gutgegangen, aber beim Lesen eine Stolperfalle. */
|
||||
const ANGESCHALTET = () => einstellung("ki_an", "1") !== "0";
|
||||
|
||||
/* Einfache Bremse je Person: fuenf Anfragen in zehn Minuten. Die KI ist
|
||||
billig im Geld, aber teuer in Rechenzeit, die der Website gehoert. */
|
||||
const VERBRAUCH = new Map();
|
||||
@@ -113,6 +120,11 @@ async function frage(anweisung, text, maxWorte, beispiele = []) {
|
||||
/* Gemeinsamer Rahmen: Bremse, Sperre, Fehler abfangen. So steht die
|
||||
Absicherung einmal da und nicht in jedem Endpunkt neu. */
|
||||
async function mitSchutz(req, res, aufgabe, was) {
|
||||
/* Der Schalter wird HIER geprueft, nicht nur beim Anzeigen der
|
||||
Knoepfe. Eine Oberflaeche, die etwas versteckt, ist keine Sperre. */
|
||||
if (!ANGESCHALTET()) {
|
||||
return res.status(403).json({ fehler: "Die KI-Vorschläge sind abgeschaltet." });
|
||||
}
|
||||
if (zuVielGefragt(req.person.id)) {
|
||||
return res.status(429).json({
|
||||
fehler: `Höchstens ${LIMIT} Vorschläge in ${FENSTER_MS / 60000} Minuten – die KI teilt sich den Server mit der Website.`,
|
||||
@@ -150,18 +162,37 @@ async function mitSchutz(req, res, aufgabe, was) {
|
||||
als gar kein Knopf. */
|
||||
|
||||
kiRouter.get("/workspace/api/ki/status", async (req, res) => {
|
||||
/* Abgeschaltet heisst abgeschaltet -- dann wird der Dienst gar nicht
|
||||
erst gefragt, und die Knoepfe erscheinen nirgends. */
|
||||
if (!ANGESCHALTET()) {
|
||||
return res.json({ da: false, angeschaltet: false, erreichbar: null, beschaeftigt: false });
|
||||
}
|
||||
const abbruch = new AbortController();
|
||||
const uhr = setTimeout(() => abbruch.abort(), 2500);
|
||||
try {
|
||||
const a = await fetch(KI_URL + "/health", { signal: abbruch.signal });
|
||||
res.json({ da: a.ok, beschaeftigt: laeuft });
|
||||
res.json({ da: a.ok, angeschaltet: true, erreichbar: a.ok, beschaeftigt: laeuft });
|
||||
} catch {
|
||||
res.json({ da: false, beschaeftigt: false });
|
||||
res.json({ da: false, angeschaltet: true, erreichbar: false, beschaeftigt: false });
|
||||
} finally {
|
||||
clearTimeout(uhr);
|
||||
}
|
||||
});
|
||||
|
||||
/* Ein- und ausschalten. Nur die Leitung -- es ist eine Entscheidung
|
||||
ueber Rechenzeit, die der Website gehoert. */
|
||||
kiRouter.put("/workspace/api/ki/schalter", gleicheHerkunft, express.json(), (req, res) => {
|
||||
try {
|
||||
if (!istLeitung(req.person)) return res.status(403).json({ fehler: "nicht_erlaubt" });
|
||||
const an = req.body?.an === true || req.body?.an === "1";
|
||||
einstellungSetzen("ki_an", an ? "1" : "0", { ...req.person, ip: echteIp(req) });
|
||||
res.json({ ok: true, angeschaltet: an });
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] KI-Schalter:", fehler?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- Aus einem Gespraech To-dos vorschlagen ----------------------- */
|
||||
|
||||
kiRouter.post("/workspace/api/ki/todos", gleicheHerkunft, express.json(), (req, res) => {
|
||||
|
||||
@@ -259,6 +259,18 @@ export function db() {
|
||||
CREATE INDEX IF NOT EXISTS idx_aufgaben_status ON aufgaben (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_aufgaben_creator ON aufgaben (creator_id);
|
||||
|
||||
/* Einstellungen, die sich zur Laufzeit aendern lassen. Bisher nur
|
||||
eine: ob die KI-Vorschlaege angeboten werden. Als Tabelle statt
|
||||
als Datei, damit sie dieselbe Sicherung bekommt wie alles
|
||||
andere -- eine Einstellung, die beim Wiederherstellen fehlt,
|
||||
faellt erst auf, wenn etwas nicht mehr geht. */
|
||||
CREATE TABLE IF NOT EXISTS einstellungen (
|
||||
schluessel TEXT PRIMARY KEY,
|
||||
wert TEXT NOT NULL,
|
||||
geaendert TEXT,
|
||||
von INTEGER REFERENCES personen(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
/* Rueckmeldungen zu einer Aufgabe (Konzept: "Aufgaben & Feedback").
|
||||
Ohne sie endet jede Rueckfrage ausserhalb des Systems -- in
|
||||
WhatsApp, und damit ausserhalb dessen, was spaeter noch
|
||||
@@ -635,6 +647,7 @@ const GESCHUETZT = {
|
||||
"/workspace/calls.html": null,
|
||||
"/workspace/startcheck.html": null,
|
||||
"/workspace/wissen.html": null,
|
||||
"/workspace/automation.html": ["admin", "manager"],
|
||||
};
|
||||
|
||||
workspaceRouter.use((req, res, next) => {
|
||||
@@ -807,6 +820,31 @@ export function betreuungSetzen(creatorId, betreuerId, akteur = null) {
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------- Einstellungen -------------------------------------------------
|
||||
Bewusst mit Vorgabewert und ohne Fehlerfall: Faellt die Datenbank aus,
|
||||
soll eine fehlende Einstellung nicht die Seite mitreissen. */
|
||||
|
||||
export function einstellung(schluessel, vorgabe = null) {
|
||||
try {
|
||||
return db().prepare("SELECT wert FROM einstellungen WHERE schluessel = ?")
|
||||
.get(schluessel)?.wert ?? vorgabe;
|
||||
} catch {
|
||||
return vorgabe;
|
||||
}
|
||||
}
|
||||
|
||||
export function einstellungSetzen(schluessel, wert, akteur = null) {
|
||||
db().prepare(`
|
||||
INSERT INTO einstellungen (schluessel, wert, geaendert, von) VALUES (?,?,?,?)
|
||||
ON CONFLICT(schluessel) DO UPDATE SET
|
||||
wert = excluded.wert, geaendert = excluded.geaendert, von = excluded.von`).run(
|
||||
schluessel, String(wert), new Date().toISOString(), akteur?.id ?? null);
|
||||
protokolliere("einstellung_geaendert", {
|
||||
personId: akteur?.id ?? null, rolle: akteur?.rolle ?? null, ip: akteur?.ip ?? null,
|
||||
detail: `${schluessel} = ${wert}`.slice(0, 120),
|
||||
});
|
||||
}
|
||||
|
||||
export function personAnlegen(name, rolle, akteur = null) {
|
||||
if (!ROLLEN.has(rolle)) throw new Error(`Unbekannte Rolle: ${rolle}`);
|
||||
const code = codeErzeugen();
|
||||
|
||||
Reference in New Issue
Block a user