Stufe 8 aus dem Plan zur Perfektion: "Loeschkonzept je Datenart ·
IP-Adressen im Protokoll nach 30 Tagen weg. Je mehr Daten, desto teurer
das Nachholen."
WARUM NICHT ALS NOTIZ
Ein Loeschkonzept als Dokument ist nach dem ersten neuen Feld falsch,
und niemand merkt es. workspace-aufbewahrung.js ist beides: die
Uebersicht, was wie lange aufgehoben wird -- UND das Programm, das es
tut. Was dort nicht steht, wird nicht geraeumt; was dort steht, wird
geraeumt.
GEMESSEN, BEVOR ETWAS GEBAUT WURDE
42 Tabellen durchgesehen. Drei halten IP-Adressen:
versuche.ip wird beim Anmelden im Fenster geraeumt ok
sitzungen.ip nur beim Abmelden -- ABGELAUFENE Sitzungen
standen ewig da, samt IP und Browser Luecke
protokoll.ip wurde nirgends geraeumt, wuchs ewig Luecke
Zwei echte Luecken also, nicht fuenf vermutete.
DREI SORTEN EINTRAG, UND DIE DRITTE IST DIE WICHTIGSTE
raeumen Diese Datei loescht selbst.
fremd Eine andere Stelle loescht -- hier wird nur GEZAEHLT.
Bricht die andere Stelle, steht die Zahl trotzdem da.
bleibt Wird absichtlich aufgehoben, mit Begruendung.
Ein Loeschkonzept, das nur Loeschungen auffuehrt, ist ein halbes: Die
begruendete Aufbewahrung ist der Teil, nach dem gefragt wird. Jede
Zeile nennt Frist, Zweck, Rechtsgrundlage und Wirkung.
SICHTBAR, NICHT NUR WIRKSAM
Auf automation.html -- dort steht alles, was ohne Zutun laeuft, und
genau das ist es. Bei jeder Zeile die Zahl, wie viele Datensaetze sie
gerade betrifft, und ein Knopf "Jetzt aufraeumen", damit es kein
schwarzer Kasten ist. Die Sorte steht als WORT daneben, nicht nur als
Farbe.
BEI ETWAS, DAS LOESCHT, IST DIE WICHTIGERE HAELFTE: WAS ES NICHT LOESCHT
Zu jeder Loeschung eine Gegenprobe:
IP nach 30 Tagen weg -> und die von vor 29 Tagen bleibt
IP weg -> und die ZEILE bleibt (nur das Feld)
abgelaufene Sitzung weg -> und die gueltige bleibt
abgelaufene Sitzung weg -> und ich bin danach noch angemeldet
Die letzte ist die unbequemste und die wichtigste: Ein Aufraeumlauf,
der die eigene Anmeldung mitnimmt, wirft im Betrieb alle hinaus -- und
zwar einmal taeglich. Dazu: ein zweiter Lauf muss NICHTS mehr finden
(sonst waere "aufgeraeumt" nicht von "nichts getan" zu unterscheiden),
und eine Zaehlung ueber eine fehlende Tabelle ergibt `null`, nicht 0.
NEBENBEI: EINE EIGENE DOPPLUNG VON GESTERN
Drei der Seitenerklaerungen sagten in "Wofuer" und "Du tust" fast
dasselbe (report 80 %, treff-regeln 67 %, startcheck 50 %). Gestern
hatte ich nur den Fall geprueft, an dem ich mich verbrannt hatte --
"Wofuer" gegen die Unterzeile -- und die naheliegendere Dopplung
uebersehen. Jetzt gemessen, schlechtester Wert 25 %.
PRUEFUNGEN: pruef-aufbewahrung neu mit 41, davon 7 im Browser
(der Knopf muss eine Zahl wirklich fallen lassen, und die IP muss
danach in der Datenbank weg sein). pruef-erklaerung 44 -> 47.
Co-Authored-By: Claude Opus 5 <[email protected]>
230 lines
11 KiB
JavaScript
230 lines
11 KiB
JavaScript
/* =====================================================================
|
||
Die kurze Erklärung auf jeder Seite (14.09.2026)
|
||
|
||
Filipe: *„ich will in jeder seite auch eine kleine detaillierte aber
|
||
schnelle erklärung kurz und knapps noch zu jeder seite."*
|
||
|
||
---------------------------------------------------------------------
|
||
DREI ZEILEN, UND DIE DRITTE RECHNET SICH SELBST AUS
|
||
|
||
WOFÜR wozu es diese Seite gibt
|
||
DU TUST was man hier konkret macht — nicht was man könnte
|
||
SIEHT wer diese Seite überhaupt öffnen kann
|
||
|
||
Die ersten beiden sind geschriebener Text. Die dritte ist es NICHT:
|
||
Sie wird aus der Rechtetafel gerechnet, aus derselben, aus der auch
|
||
die Schranke ihre Entscheidung holt. Das ist der ganze Grund, warum
|
||
diese Zeile hier stehen darf.
|
||
|
||
Hätte ich sie danebengeschrieben, wäre sie beim ersten Umstellen in
|
||
„Wer sieht was" falsch geworden — und zwar STILL falsch, in einem
|
||
Satz, der behauptet, wer etwas sieht. Genau diese Sorte Zweitschrift
|
||
hat im Projekt schon dreimal Daten oder Vertrauen gekostet (die
|
||
abgeschriebene Spaltenliste, die zweite Rollenreihenfolge im
|
||
Browser, das fest eingetragene Wort „Agentur"). Eine Angabe über
|
||
Sichtbarkeit ist der schlechteste Ort, um es ein viertes Mal zu tun.
|
||
|
||
---------------------------------------------------------------------
|
||
WARUM DIE ERKLÄRUNG HINTER DERSELBEN SCHRANKE STEHT WIE DIE SEITE
|
||
|
||
Wer eine Seite nicht öffnen darf, bekommt auch ihre Erklärung nicht
|
||
— mit 404, nicht mit 403. Sonst wäre dieser Weg eine Landkarte des
|
||
Hauses: Ein Modi könnte abfragen, dass es „Personen & Zugänge" gibt,
|
||
wozu die Seite dient und wer sie sieht. Die Seite selbst antwortet
|
||
ihm mit „gibt es nicht"; die Erklärung dazu muss dasselbe sagen.
|
||
|
||
---------------------------------------------------------------------
|
||
WAS HIER BEWUSST NICHT STEHT
|
||
|
||
Keine Anleitung in Schritten („klicke oben rechts auf …"). Solche
|
||
Texte altern mit jedem Umbau der Oberfläche und werden dann zu
|
||
Anweisungen, die ins Leere zeigen. Was hier steht, ist der ZWECK —
|
||
der ändert sich, wenn die Seite sich ändert, und nicht vorher.
|
||
===================================================================== */
|
||
|
||
import express from "express";
|
||
import { sitzungLesen, ROLLEN_NAME, ROLLEN_REIHE } from "./workspace.js";
|
||
import { darfSeite, tafelJetzt } from "./rechte.js";
|
||
|
||
export const erklaerungRouter = express.Router();
|
||
|
||
/** Der Text zu jeder Seite.
|
||
*
|
||
* `wofuer` beantwortet „wozu gibt es das?", `tun` beantwortet „was
|
||
* mache ich hier?". `merke` ist der eine Satz, der am häufigsten
|
||
* missverstanden wird — er steht nur dort, wo es ihn wirklich gibt.
|
||
* Ein Feld, das auf jeder Seite gefüllt ist, wird nicht mehr gelesen.
|
||
*/
|
||
export const ERKLAERUNGEN = {
|
||
"start.html": {
|
||
wofuer: "Deine Startseite. Jede Kachel führt zu einem Bereich, und du siehst nur die, die dir offenstehen.",
|
||
tun: "Aussuchen, wo du hinwillst – oder oben auf die Glocke schauen, wenn etwas auf dich wartet.",
|
||
},
|
||
"uebersicht.html": {
|
||
wofuer: "Alle betreuten Creator nebeneinander, mit Zahlen, offenen Aufgaben und dem Datum des letzten Kontakts.",
|
||
tun: "Die oberste Karte zuerst ansehen – sortiert wird nach dem, was Handlung verlangt, nicht nach Namen.",
|
||
},
|
||
"aufgaben.html": {
|
||
wofuer: "Alle Aufgaben des Teams in vier Spalten, mit Frist, Zuständigkeit und Rückmeldungen.",
|
||
tun: "Eine Aufgabe weiterschieben, sobald sich ihr Stand ändert – nicht sammeln und später nachtragen.",
|
||
merke: "Was hier nicht steht, passiert nicht. Aufgaben aus einem Call landen automatisch hier.",
|
||
},
|
||
"kalender.html": {
|
||
wofuer: "Alle Termine des Teams an einer Stelle, mit Serien und Feiertagen.",
|
||
tun: "Termin anlegen und unten auswählen, wer ihn im eigenen Kalender sehen soll.",
|
||
merke: "Wen du nicht auswählst, sieht den Termin nicht – auch nicht, dass es ihn gibt.",
|
||
},
|
||
"chat.html": {
|
||
wofuer: "Nachrichten an einzelne Menschen oder an einen Kanal.",
|
||
tun: "Schreiben. Auf eine Nachricht kannst du auch nur reagieren, statt zu antworten.",
|
||
},
|
||
"calls.html": {
|
||
wofuer: "Alle Gespräche mit Datum, Teilnehmern und Protokoll.",
|
||
tun: "Nach dem Call das Protokoll schreiben; die To-dos daraus landen direkt im Aufgabenbrett.",
|
||
merke: "Ein Call ohne Protokoll bleibt auf der Startseite stehen, bis eines da ist.",
|
||
},
|
||
"dateien.html": {
|
||
wofuer: "Alle Dateien zu einem Creator, jede mit Stand und Versionsverlauf.",
|
||
tun: "Hochladen und den Stand setzen – die aktuelle Fassung ist dadurch immer erkennbar.",
|
||
},
|
||
"content.html": {
|
||
wofuer: "Alle Videoideen mit Säule, Hook, Format und geplantem Datum.",
|
||
tun: "Eine Idee eintragen und ihr eine Säule geben; die Strecke danach ist immer dieselbe.",
|
||
},
|
||
"bereich.html": {
|
||
wofuer: "Die Einträge dieses Bretts, je nach Brett mit Art, Datum, Dringlichkeit oder Bewertung.",
|
||
tun: "Eintrag anlegen, Art wählen, und bei „Gilt für“ entscheiden, ob er dir gehört oder allen.",
|
||
},
|
||
"wissen.html": {
|
||
wofuer: "Anleitungen, PDFs und Kurzfassungen – durchsuchbar, nach Themen sortiert.",
|
||
tun: "Nachschlagen, bevor du fragst – und selbst etwas dazulegen, wenn du es erklärt hast.",
|
||
},
|
||
"report.html": {
|
||
wofuer: "Je Creator und Zeitraum: was lief, was nicht, was als Nächstes kommt.",
|
||
tun: "Zeitraum wählen und den Rückblick schreiben, solange die Zahlen dazu danebenstehen.",
|
||
},
|
||
"leistung.html": {
|
||
wofuer: "Livestunden, Zuschauer und Einnahmen je Tag – aus Backstage oder von Hand.",
|
||
tun: "Zeitraum importieren oder einen Tag von Hand eintragen, dann mit den Wochenzielen vergleichen.",
|
||
},
|
||
"scouting.html": {
|
||
wofuer: "Alle Kontakte mit Stufe, Kanal, Reichweite und dem, was zuletzt passiert ist.",
|
||
tun: "Jeden Kontakt eine Stufe weiterschieben – oder bewusst beenden, statt ihn liegen zu lassen.",
|
||
},
|
||
"startcheck.html": {
|
||
wofuer: "Ein Fragebogen je neuem Creator und die Arbeitspakete, die daraus entstehen.",
|
||
tun: "Gemeinsam mit ihr ausfüllen, nicht über sie – die Antworten sind der Anfang eurer Zusammenarbeit.",
|
||
},
|
||
"profil.html": {
|
||
wofuer: "Zahlen, Aufgaben, Dateien, Termine und Verlauf zu einer Creatorin an einer Stelle.",
|
||
tun: "Nachsehen, bevor du mit ihr oder ihm sprichst.",
|
||
},
|
||
"steckbrief.html": {
|
||
wofuer: "Dein Bild, ein paar Worte über dich und deine Kanäle.",
|
||
tun: "Ausfüllen. Dein Bild steht danach überall dort, wo dein Name auftaucht.",
|
||
merke: "Das hier gehört dir. Niemand außer dir ändert daran etwas.",
|
||
},
|
||
"personen.html": {
|
||
wofuer: "Zugänge: wer hereinkommt, mit welcher Rolle, und wer wen betreut.",
|
||
tun: "Zugang anlegen und den Code persönlich weitergeben.",
|
||
merke: "Ein Zugangscode wird genau einmal angezeigt. Danach gibt es nur einen neuen, keinen alten.",
|
||
},
|
||
"automation.html": {
|
||
wofuer: "Jede Automatik des Hauses mit Zustand, letzter Ausführung und Schalter.",
|
||
tun: "Nachsehen, was gerade greift, bevor du dich über etwas wunderst, das du nicht angestoßen hast.",
|
||
},
|
||
"team.html": {
|
||
wofuer: "Die Arbeitslast von Managern und Scouts nebeneinander.",
|
||
tun: "Stellen suchen, an denen jemand zu viel hat – und dort umverteilen.",
|
||
merke: "Das ist keine Kontrolle. Die Seite gibt es, um fehlende Unterstützung zu finden.",
|
||
},
|
||
"teamlage.html": {
|
||
wofuer: "Was in deinem Team gerade offen ist – und bei wem.",
|
||
tun: "Die offenen Punkte durchgehen und ansprechen.",
|
||
merke: "„Offen“ heißt: darüber habt ihr noch nicht geredet. Es ist keine Note über jemanden.",
|
||
},
|
||
"entwicklung.html": {
|
||
wofuer: "34 Beobachtungen je Person, gesetzt von zwei Augenpaaren unabhängig voneinander.",
|
||
tun: "Pro Person die Punkte setzen, die du wirklich beobachtet hast. Den Rest lässt du offen.",
|
||
merke: "Keine Noten, keine Punktzahl. Das Ergebnis ist die Abweichung zwischen euch beiden – ein Gesprächsanlass.",
|
||
},
|
||
"talente.html": {
|
||
wofuer: "Kandidaten für dein Mod-Team in fünf Stufen – mit 21 Merkmalen und 6 Warnzeichen je Karte.",
|
||
tun: "Pro Karte den einen nächsten Schritt machen, der dort steht.",
|
||
merke: "Hier stehen Namen von Menschen, die nichts davon wissen. Nichts davon geht nach außen.",
|
||
},
|
||
"rechte.html": {
|
||
wofuer: "Jede Rolle mal jede Seite als Tafel – und jedes Feld lässt sich umstellen.",
|
||
tun: "Ein Feld umstellen und speichern. Die Änderung gilt sofort, auch für alle, die gerade angemeldet sind.",
|
||
merke: "Drei Felder lassen sich nicht abschalten – sonst könntest du dich selbst aussperren.",
|
||
},
|
||
"treff-regeln.html": {
|
||
wofuer: "Die Regeln des Treffs und was passiert, wenn sie jemand nicht einhält.",
|
||
tun: "Einmal lesen. Danach weißt du, was hier gilt und an wen du dich wendest, wenn etwas schiefläuft.",
|
||
},
|
||
"treff-moderation.html": {
|
||
wofuer: "Offene Meldungen, erledigte Fälle und jede Maßnahme mit Begründung.",
|
||
tun: "Eine Meldung ansehen und entscheiden: Hinweis, Pause oder Ausschluss – immer mit Begründung.",
|
||
merke: "Jede Maßnahme steht nachher mit Grund da. Das schützt auch dich.",
|
||
},
|
||
};
|
||
|
||
/** Die Rollen, die diese Seite gerade öffnen dürfen — als Namen.
|
||
*
|
||
* AUS DER TAFEL GERECHNET, NICHT DANEBENGESCHRIEBEN. Wer in „Wer
|
||
* sieht was" ein Feld umstellt, ändert damit auch diesen Satz; eine
|
||
* zweite Liste hier wäre spätestens dann eine Behauptung.
|
||
*
|
||
* In der Reihenfolge des Hauses (ROLLEN_REIHE), damit die rechte Hand
|
||
* auch hier direkt hinter DogFather steht. */
|
||
function sichtbarFuer(pfad) {
|
||
const erlaubt = tafelJetzt()[pfad] || [];
|
||
const bekannt = ROLLEN_REIHE.filter((r) => erlaubt.includes(r));
|
||
/* Rollen, die (noch) nicht in der Rangfolge stehen -- heute nur
|
||
'gast'. Sie faellt ans Ende, statt stillschweigend zu fehlen. */
|
||
const rest = erlaubt.filter((r) => !bekannt.includes(r));
|
||
return [...bekannt, ...rest].map((r) => ROLLEN_NAME[r] || r);
|
||
}
|
||
|
||
erklaerungRouter.get("/workspace/api/erklaerung", (req, res) => {
|
||
try {
|
||
const person = sitzungLesen(req);
|
||
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||
|
||
/* Nur der Dateiname, und nur aus dem eigenen Verzeichnis. Ohne das
|
||
waere `?seite=../../etwas` eine Frage, die dieser Weg gar nicht
|
||
beantworten koennen soll. */
|
||
const datei = String(req.query?.seite || "").trim();
|
||
if (!/^[a-z0-9-]+\.html$/.test(datei)) {
|
||
return res.status(400).json({ fehler: "ungueltig" });
|
||
}
|
||
const pfad = "/workspace/" + datei;
|
||
|
||
/* DIESELBE SCHRANKE WIE FUER DIE SEITE SELBST -- und dieselbe
|
||
Antwort, wenn sie zu ist: 404, nicht 403. Wer "kein Zugriff"
|
||
liest, weiss, dass es die Seite gibt. */
|
||
if (!darfSeite(person, pfad)) {
|
||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||
}
|
||
|
||
const text = ERKLAERUNGEN[datei];
|
||
/* Eine Seite ohne Text ist KEIN Fehler, aber auch keine leere
|
||
Erklaerung: Der Browser bekommt `hat_text: false` und zeichnet
|
||
gar nichts. Ein Kasten mit drei leeren Zeilen waere schlechter
|
||
als keiner. */
|
||
if (!text) return res.json({ hat_text: false, seite: datei });
|
||
|
||
res.json({
|
||
hat_text: true,
|
||
seite: datei,
|
||
wofuer: text.wofuer,
|
||
tun: text.tun,
|
||
merke: text.merke || null,
|
||
sichtbar: sichtbarFuer(pfad),
|
||
});
|
||
} catch (fehler) {
|
||
console.error("[erklaerung]", fehler?.message);
|
||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||
}
|
||
});
|