Haustrennung: kein Eintrag mehr aus dem anderen Haus
Filipe, 24.09.2026: "wenn ich bei der einen was mache soll nichts bei
der anderen passieren." -- und heute: "ja los".
GEMESSEN, BEVOR ETWAS ANGEFASST WURDE (jedes Brett x beide Adressen x
vier Rollen):
AGENTUR-Adresse DogFather 18 Bretter
Manager 6 Bretter
Creator 6 Bretter
Modi kommt nicht rein (401)
CREW-Adresse DogFather 18 Bretter
Modi 13 Bretter
Manager kommt nicht rein (401)
Creator kommt nicht rein (401)
Die Anmeldung war also dicht. Durch griff genau EINE Rolle: DogFather.
Er wohnt in beiden Haeusern, und die Riegel in sichtbarEintrag()
fragten nach der ROLLE, nicht nach der Adresse. Auf der Crew-Adresse
stand damit das Brett der Agentur samt Inhalt.
Nachgemessen ist der Durchgriff AELTER als der gestrige Eventkarten-
Umbau -- zweimal gemessen, mit und ohne ihn, gleiches Ergebnis.
RIEGEL 0 in sichtbarEintrag(): Wer auf einer Adresse angemeldet ist,
sieht nur Eintraege dieses Hauses. Er haengt den uebrigen Riegeln
UM, statt in jeden Ausgang geschrieben zu werden -- die Funktion hat
drei Rueckgabepunkte, und der naechste waere sonst wieder offen.
Nachher, dieselbe Messung: DogFather sieht auf der Agenturadresse nur
Agentur-Eintraege, auf der Crew-Adresse nur die des Rudels. Manager,
Creator und Modi unveraendert.
WAS DABEI SCHIEFGING UND WIE ES AUFFIEL
1. Die erste Fassung liess bei `haus IS NULL` den BEREICH entscheiden.
pruef-haus-trennung.mjs wurde sofort rot: "Lunas Live vom Montag"
verschwand von der Agenturadresse. `live`, `technik` und
`community` tragen BEIDES -- die Kacheln von Team Dogi und die
Creator-Akten. Eine Regel, die jedem Brett genau ein Haus zuweist,
kann das nicht. Jetzt bleibt ein Eintrag ohne Haus sichtbar: ein
Eintrag zu viel faellt auf, ein fehlender nicht.
2. Damit NULL kein Dauerloch ist: FUENF von ACHT Stellen, die
Eintraege anlegen, setzten `haus` gar nicht (workspace-video.js,
-content.js, -bewerbung.js, -treff.js, -vorlagen.js). Nachgetragen.
3. Und `person.haus` war dafuer der falsche Massstab: Legt DogFather
ueber die Agenturadresse ein Highlight an, gehoert es trotzdem dem
Rudel -- sonst sieht die Community es nie. pruef-treff.mjs hat das
gefunden (2 Fehler). Neu: hausFuerNeuenEintrag() -- bei den sieben
Brettern des Rudels entscheidet das BRETT, sonst die Adresse.
NEU: pruef-haus-luecke.mjs (12 Pruefungen). Sie sucht die
Einfuege-Stellen im Quelltext und wird rot, sobald eine neunte
dazukommt, die `haus` vergisst -- mit Gegenprobe, dass das Suchmuster
eine solche Stelle auch wirklich erkennt. Ein Kommentar daneben haette
es nicht verhindert; das steht so schon im Projektgedaechtnis.
NEBENBEI: In bereich.js stand seit gestern `|| "Agentur-Events"` als
Rueckfall fuer das Etikett der Vorschau. pruef-treff.mjs verbietet
das zu Recht -- wie ein Brett heisst, haengt am Haus, und der Server
sagt es. Der Rueckfall ist weg; fehlt die Angabe, steht lieber kein
Etikett da als ein falsches.
pruef-treff.mjs nachgezogen (85 -> 86 Pruefungen, nicht weniger): Die
Zusage "DogFather sieht den Beitrag" wird jetzt auf der Crew-Adresse
geprueft, mit Gegenprobe fuer die Agenturadresse.
GEPRUEFT, alle gruen:
pruef-haus-luecke 12 pruef-haus-trennung 100
pruef-crew-adresse 169 pruef-treff 86
pruef-eventkarte 77 pruef-agentur 62
pruef-haus-seiten 38 pruef-eintrag-bild 24
pruef-vorlagen 24 pruef-video, -content, -bewerbung,
-bereiche-lesend: in Ordnung
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
/* =====================================================================
|
||||
DIE HAUSTRENNUNG BEI DEN EINTRAEGEN (03.10.2026)
|
||||
|
||||
Filipe, 24.09.2026: "wenn ich bei der einen was mache soll nichts bei
|
||||
der anderen passieren. ... Kein Bestand wird geteilt."
|
||||
|
||||
WAS AM 03.10.2026 GEMESSEN WURDE (server/_mess-haeuser.mjs, jedes
|
||||
Brett x beide Adressen x vier Rollen): Die Anmeldung war dicht --
|
||||
eine Agenturrolle kam nicht auf die Crew-Adresse und umgekehrt.
|
||||
Durch griff genau EINE Rolle: DogFather. Er wohnt in beiden
|
||||
Haeusern, und die Riegel in sichtbarEintrag() fragten nach der
|
||||
ROLLE, nicht nach der Adresse. Auf der Crew-Adresse stand damit das
|
||||
Brett der Agentur samt Inhalt.
|
||||
|
||||
Behoben durch Riegel 0 in sichtbarEintrag(): Wer auf einer Adresse
|
||||
angemeldet ist, sieht nur Eintraege dieses Hauses.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WARUM ES DIESE DATEI BRAUCHT UND NICHT NUR DEN RIEGEL
|
||||
|
||||
Der Riegel haengt an der Spalte `eintraege.haus`. Die wird beim
|
||||
ANLEGEN gesetzt -- und zwar von Hand, an jeder Stelle einzeln, die
|
||||
einen Eintrag anlegt. Gemessen am 03.10.2026: FUENF von ACHT
|
||||
Stellen taten es nicht (workspace-video.js, workspace-content.js,
|
||||
workspace-bewerbung.js, workspace-treff.js, workspace-vorlagen.js).
|
||||
|
||||
Das ist dieselbe Falle wie die abgeschriebene Spaltenliste vom
|
||||
11.09.2026: Eine Angabe, die an acht Stellen von Hand gepflegt
|
||||
werden muss, ist an der neunten vergessen -- und zwar still.
|
||||
|
||||
Ein Kommentar daneben verhindert das nicht (auch das steht so im
|
||||
Projektgedaechtnis: der Hinweis stand drei Tage lang direkt neben
|
||||
der Stelle und wurde beim naechsten Mal trotzdem nicht gelesen).
|
||||
Eine Pruefung schon. Diese hier wird rot, sobald eine neunte Stelle
|
||||
dazukommt, die `haus` vergisst.
|
||||
===================================================================== */
|
||||
import { mkdtempSync, rmSync, appendFileSync, readFileSync, readdirSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-hl-"));
|
||||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||
|
||||
import { notbremse } from "./helfer-notbremse.mjs";
|
||||
const { eigenerPort } = await import("./helfer-port.mjs");
|
||||
const PORT = await eigenerPort(import.meta, "die Haus-Luecken-Pruefung");
|
||||
|
||||
process.env.PORT = String(PORT);
|
||||
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||||
process.env.SITE_ACCESS_CODE_DOGI = "DOGI-TEST-1";
|
||||
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
||||
|
||||
const PROTOKOLL = join(process.cwd(), "pruef-haus-luecke-lauf.txt");
|
||||
try { rmSync(PROTOKOLL, { force: true }); } catch { /* gab es noch nicht */ }
|
||||
const melde = (z) => {
|
||||
console.log(z);
|
||||
try { appendFileSync(PROTOKOLL, z + "\n"); } catch { /* Zugabe */ }
|
||||
};
|
||||
let fehler = 0, geprueft = 0;
|
||||
const ok = (b, t) => { geprueft++; melde((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
|
||||
/* =======================================================================
|
||||
1. OHNE SERVER: Schreibt jede Stelle das Haus mit?
|
||||
======================================================================= */
|
||||
melde("=== 1. Jede Stelle, die einen Eintrag anlegt, setzt `haus` ===");
|
||||
{
|
||||
const dateien = readdirSync(".")
|
||||
.filter((f) => f.startsWith("workspace") && f.endsWith(".js"));
|
||||
/* DIE DATEIEN WERDEN GESUCHT, NICHT AUFGEZAEHLT. Eine Liste von
|
||||
Dateinamen hier waere genau die Sorte Angabe, die beim naechsten
|
||||
neuen Modul veraltet -- und dann prueft diese Datei die neue
|
||||
Stelle nicht, ohne dass etwas rot wird. */
|
||||
ok(dateien.length > 10, `${dateien.length} Servermodule durchsucht`);
|
||||
|
||||
const stellen = [];
|
||||
for (const datei of dateien) {
|
||||
const text = readFileSync(datei, "utf8");
|
||||
/* Jede Einfuegung in `eintraege` samt ihrer Spaltenliste. Die
|
||||
Spaltenliste endet an der ersten schliessenden Klammer vor
|
||||
VALUES. */
|
||||
const muster = /INSERT\s+INTO\s+eintraege\s*\(([^)]*)\)/gi;
|
||||
let t;
|
||||
while ((t = muster.exec(text)) !== null) {
|
||||
const zeile = text.slice(0, t.index).split("\n").length;
|
||||
const spalten = t[1].split(",").map((x) => x.trim());
|
||||
stellen.push({ datei, zeile, hatHaus: spalten.includes("haus") });
|
||||
}
|
||||
}
|
||||
ok(stellen.length >= 8, `${stellen.length} Einfuege-Stellen gefunden (erwartet mindestens 8)`);
|
||||
|
||||
const ohne = stellen.filter((s) => !s.hatHaus);
|
||||
for (const s of stellen) {
|
||||
melde(` -- ${s.datei}:${s.zeile} ${s.hatHaus ? "setzt haus" : "OHNE haus"}`);
|
||||
}
|
||||
ok(ohne.length === 0, ohne.length
|
||||
? `diese Stellen vergessen das Haus: ${ohne.map((s) => s.datei + ":" + s.zeile).join(", ")}`
|
||||
: "alle setzen es");
|
||||
|
||||
/* GEGENPROBE: Findet dieses Muster ueberhaupt etwas, das OHNE haus
|
||||
ist? Sonst koennte es an einem Tippfehler im regulaeren Ausdruck
|
||||
liegen, und die Pruefung waere dauerhaft gruen, ohne je etwas
|
||||
gesehen zu haben. */
|
||||
const probe = "INSERT INTO eintraege (bereich, art, titel)\nVALUES (?,?,?)";
|
||||
const m = /INSERT\s+INTO\s+eintraege\s*\(([^)]*)\)/i.exec(probe);
|
||||
ok(!!m && !m[1].split(",").map((x) => x.trim()).includes("haus"),
|
||||
"das Suchmuster erkennt eine Stelle OHNE haus (Gegenprobe)");
|
||||
const probe2 = "INSERT INTO eintraege (bereich, art, haus)\nVALUES (?,?,?)";
|
||||
const m2 = /INSERT\s+INTO\s+eintraege\s*\(([^)]*)\)/i.exec(probe2);
|
||||
ok(!!m2 && m2[1].split(",").map((x) => x.trim()).includes("haus"),
|
||||
"und eine MIT haus (Gegenprobe andersherum)");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
2. MIT SERVER: Greift der Riegel wirklich?
|
||||
======================================================================= */
|
||||
const express = (await import("express")).default;
|
||||
const ec = express.response.cookie;
|
||||
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
||||
await import("./index.js");
|
||||
notbremse(240_000, "pruef-haus-luecke");
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
const BASIS = "http://127.0.0.1:" + PORT;
|
||||
|
||||
const { DatabaseSync } = await import("node:sqlite");
|
||||
const { scryptSync, randomBytes } = await import("node:crypto");
|
||||
await fetch(BASIS + "/workspace/api/anmelden", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ rolle: "admin", code: "x" }) }).catch(() => {});
|
||||
for (let i = 0; i < 60; i++) {
|
||||
try {
|
||||
const p = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||
try { p.prepare("SELECT 1 FROM personen LIMIT 1").get(); break; } finally { p.close(); }
|
||||
} catch { await new Promise((r) => setTimeout(r, 250)); }
|
||||
}
|
||||
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||
const jetzt = new Date().toISOString();
|
||||
function anlegen(name, rolle, code) {
|
||||
const salt = randomBytes(16).toString("hex");
|
||||
const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
||||
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
||||
.run(name, rolle, hash, salt, 32768, jetzt);
|
||||
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
}
|
||||
anlegen("Dogfather", "admin", "CODE-DOGI-0001");
|
||||
const heute = new Date().toISOString().slice(0, 10);
|
||||
/* Drei Marken auf demselben Brett: eine je Haus und eine ohne. */
|
||||
for (const [titel, haus] of [["Marke-CREW", "crew"], ["Marke-AGENTUR", "agentur"], ["Marke-OHNE", null]]) {
|
||||
d.prepare("INSERT INTO eintraege (titel, text, bereich, art, status, datum, dringlichkeit, erstellt, erstellt_von, haus)"
|
||||
+ " VALUES (?,?,'anschlag','ansage','offen',?, 'mittel', ?, 1, ?)")
|
||||
.run(titel, "Test.", heute, jetzt, haus);
|
||||
}
|
||||
d.close();
|
||||
|
||||
const { request } = await import("node:http");
|
||||
function roh(host, pfad, o = {}) {
|
||||
return new Promise((f, s) => {
|
||||
const daten = o.rumpf === undefined ? null : Buffer.from(JSON.stringify(o.rumpf));
|
||||
const a = request({
|
||||
host: "127.0.0.1", port: PORT, path: pfad,
|
||||
method: o.methode || (daten ? "POST" : "GET"),
|
||||
headers: {
|
||||
Host: host, Origin: "https://" + host,
|
||||
...(o.keks ? { Cookie: o.keks } : {}),
|
||||
...(daten ? { "Content-Length": daten.length, "Content-Type": "application/json" } : {}),
|
||||
},
|
||||
}, (r) => {
|
||||
const t = [];
|
||||
r.on("data", (x) => t.push(x));
|
||||
r.on("end", () => f({ code: r.statusCode, kopf: r.headers, text: Buffer.concat(t).toString("utf8") }));
|
||||
});
|
||||
a.on("error", s);
|
||||
if (daten) a.write(daten);
|
||||
a.end();
|
||||
});
|
||||
}
|
||||
const CREW = "crew.dogfather-universe.com";
|
||||
const AGEN = "workspace.dogfather-universe.com";
|
||||
async function titelAuf(host) {
|
||||
const anm = await roh(host, "/workspace/api/anmelden", { rumpf: { rolle: "admin", code: "CODE-DOGI-0001" } });
|
||||
if (anm.code !== 200) return null;
|
||||
const keks = [].concat(anm.kopf["set-cookie"] || []).map((c) => c.split(";")[0]).join("; ");
|
||||
const a = await roh(host, "/workspace/api/bereich/anschlag", { keks });
|
||||
if (a.code !== 200) return null;
|
||||
try { return (JSON.parse(a.text).eintraege || []).map((e) => e.titel).sort(); } catch { return null; }
|
||||
}
|
||||
|
||||
melde("\n=== 2. DogFather sieht nur das Haus seiner Adresse ===");
|
||||
{
|
||||
const aufCrew = await titelAuf(CREW);
|
||||
const aufAgentur = await titelAuf(AGEN);
|
||||
/* DER DRITTE AUSGANG: Antwortet der Server gar nicht, ist das kein
|
||||
"alles sauber" -- dann konnte hier nichts gemessen werden. */
|
||||
if (!aufCrew || !aufAgentur) {
|
||||
melde("ABBRUCH: das Brett antwortet nicht -- hier wurde nichts geprueft.");
|
||||
process.exit(3);
|
||||
}
|
||||
melde(" -- auf crew.: " + JSON.stringify(aufCrew));
|
||||
melde(" -- auf workspace.: " + JSON.stringify(aufAgentur));
|
||||
|
||||
ok(aufCrew.includes("Marke-CREW"), "auf crew. steht die Crew-Marke");
|
||||
ok(!aufCrew.includes("Marke-AGENTUR"), "auf crew. steht die Agentur-Marke NICHT");
|
||||
ok(aufAgentur.includes("Marke-AGENTUR"), "auf workspace. steht die Agentur-Marke");
|
||||
ok(!aufAgentur.includes("Marke-CREW"), "auf workspace. steht die Crew-Marke NICHT");
|
||||
|
||||
/* DER RUECKFALL IST ABSICHT UND WIRD ALS SOLCHER GEPRUEFT: Ein
|
||||
Eintrag ohne Haus bleibt in beiden sichtbar. Das ist die sichere
|
||||
Richtung -- ein Eintrag zu viel faellt auf, ein fehlender nicht.
|
||||
Teil 1 sorgt dafuer, dass dieser Fall gar nicht erst entsteht. */
|
||||
ok(aufCrew.includes("Marke-OHNE") && aufAgentur.includes("Marke-OHNE"),
|
||||
"ein Eintrag OHNE Haus bleibt in beiden sichtbar (bewusster Rueckfall)");
|
||||
|
||||
/* GEGENPROBE ZUR MESSUNG SELBST: Wenn hier nur eine Marke stuende,
|
||||
waeren drei der Zusagen oben gruen, ohne etwas zu beweisen. */
|
||||
ok(aufCrew.length === 2 && aufAgentur.length === 2,
|
||||
`je zwei von drei Marken sind sichtbar (${aufCrew.length} / ${aufAgentur.length})`);
|
||||
}
|
||||
|
||||
melde("\n=== 3. Ohne Adresse wird nicht gefiltert ===");
|
||||
{
|
||||
/* Auf 127.0.0.1 gibt es kein Haus -- dort muss alles sichtbar
|
||||
bleiben, sonst waeren alle Pruefungen des Hauses still gruen und
|
||||
blind. Genau davor warnt crew-adresse.js dreimal. */
|
||||
const alles = await titelAuf("127.0.0.1:" + PORT);
|
||||
ok(alles !== null && alles.length === 3,
|
||||
`auf einer Pruefadresse stehen alle drei Marken (${alles ? alles.length : "keine Antwort"})`);
|
||||
}
|
||||
|
||||
melde("");
|
||||
melde(`${geprueft} geprueft, ${fehler} Fehler`);
|
||||
if (!geprueft) { melde("ABBRUCH: nichts geprueft -- das ist ein Fehler, kein Erfolg."); process.exit(3); }
|
||||
process.exit(fehler ? 1 : 0);
|
||||
+24
-2
@@ -892,8 +892,30 @@ melde("=== 6. Schreiben ===");
|
||||
text: "Ansage", datum: heuteLokal() });
|
||||
ok(brettDesTeams.code === 403, `aufs Anschlagbrett schreibt nur das Team (${brettDesTeams.code})`);
|
||||
|
||||
const beiDogi = await brett("treff", HAUS, kDogi);
|
||||
ok(beiDogi.titel.includes("GAST-SCHREIBT-8899"), "DogFather sieht den Beitrag");
|
||||
/* ==== AUF DER CREW-ADRESSE, NICHT AUF DER AGENTURADRESSE ========
|
||||
(nachgezogen 03.10.2026)
|
||||
|
||||
Hier stand `brett("treff", HAUS, kDogi)` -- DogFather liest das
|
||||
Rudel-Brett ueber die AGENTURADRESSE. Das war richtig, solange
|
||||
"getrennt wird das AUSSEHEN, nicht der Bestand" galt. Filipe hat
|
||||
das am 24.09.2026 umgedreht: "wenn ich bei der einen was mache
|
||||
soll nichts bei der anderen passieren."
|
||||
|
||||
Seit Riegel 0 in sichtbarEintrag() gibt es auf der Agenturadresse
|
||||
keine Eintraege von Team Dogi mehr -- auch nicht fuer DogFather.
|
||||
`treff` ist ein Brett des Rudels; dass dort auf workspace. nichts
|
||||
steht, ist das Ziel und kein Fehler.
|
||||
|
||||
DIE ZUSAGE BLEIBT, sie wird nur am richtigen Ort geprueft -- und
|
||||
bekommt eine GEGENPROBE dazu. Damit sagt diese Stelle jetzt mehr
|
||||
als vorher: nicht nur "er sieht es", sondern "er sieht es DORT
|
||||
und nur dort". */
|
||||
const beiDogi = await brett("treff", CREW, kDogi);
|
||||
ok(beiDogi.titel.includes("GAST-SCHREIBT-8899"),
|
||||
"DogFather sieht den Beitrag auf der Crew-Adresse");
|
||||
const beiDogiAgentur = await brett("treff", HAUS, kDogi);
|
||||
ok(!beiDogiAgentur.titel.includes("GAST-SCHREIBT-8899"),
|
||||
`und auf der Agenturadresse nicht (${beiDogiAgentur.titel.join(" | ") || "leer"})`);
|
||||
const beiModi = await brett("treff", CREW, kModi);
|
||||
ok(beiModi.titel.includes("GAST-SCHREIBT-8899"), "der Modi auch");
|
||||
const beiCrea = await brett("treff", HAUS, kCrea);
|
||||
|
||||
@@ -86,6 +86,7 @@ import {
|
||||
heuteLokal,
|
||||
gehoertAufDieseAdresse,
|
||||
bereicheFuer, kanaeleFuer, KANAELE,
|
||||
hausFuerNeuenEintrag,
|
||||
} from "./workspace.js";
|
||||
import { nachrichtSchicken } from "./workspace-chat.js";
|
||||
import {
|
||||
@@ -1282,7 +1283,119 @@ function ohneVertrauliche(regel, person, praefix) {
|
||||
auch Fragen zu Aufgaben, Dateien und Terminen; ein Bereichsname hat
|
||||
dort nichts zu suchen. Der Kommentar eine Bildschirmseite weiter oben
|
||||
warnt woertlich davor. */
|
||||
|
||||
/* =====================================================================
|
||||
RIEGEL 0: DAS HAUS DER ADRESSE (03.10.2026)
|
||||
|
||||
Filipe, 24.09.2026: "bevor du das aber machst will ich dass du
|
||||
zuerst die komplette site vn der workspace seite trennst. da soll
|
||||
nichts verknuepft sein. ... vermerk dass ab jetzt jeder umbau immer
|
||||
getrennt abgefuehrt wird auf den zwei seiten."
|
||||
|
||||
WAS GEMESSEN WURDE (03.10.2026, server/_mess-haeuser.mjs, jedes
|
||||
Brett x beide Adressen x vier Rollen):
|
||||
|
||||
AGENTUR-Adresse DogFather 18 Bretter
|
||||
Manager 6 Bretter
|
||||
Creator 6 Bretter
|
||||
Modi kommt nicht rein (401)
|
||||
CREW-Adresse DogFather 18 Bretter
|
||||
Modi 13 Bretter
|
||||
Manager kommt nicht rein (401)
|
||||
Creator kommt nicht rein (401)
|
||||
|
||||
Die Anmeldung ist also dicht -- eine Agenturrolle kommt nicht auf
|
||||
die Crew-Adresse und umgekehrt. Durch greift genau EINE Rolle:
|
||||
DogFather. Er ist die einzige in BEIDE_HAEUSER_ROLLEN, und die
|
||||
Riegel darunter fragen nach der ROLLE, nicht nach der Adresse.
|
||||
Auf der Crew-Adresse stand damit das Brett der Agentur samt Inhalt.
|
||||
|
||||
WARUM ES BISHER NICHT GRIFF, obwohl es die Spalte `eintraege.haus`
|
||||
seit dem 24.09. gibt: Sie wird beim ANLEGEN gefuellt und beim LESEN
|
||||
nie gefragt. Gefiltert wurde ausschliesslich ueber die ROLLEN der
|
||||
beteiligten Personen (ohneTeamDogi / ohneAgentur) -- und ein Eintrag,
|
||||
an dem nur DogFather haengt, faellt durch beide Netze, weil er in
|
||||
beiden Haeusern wohnt. Bei 20 von 26 Eintraegen ist er der Ersteller.
|
||||
|
||||
DER RIEGEL STEHT GANZ VORNE, vor der Aussenrolle: Er gilt damit fuer
|
||||
JEDEN Rueckgabeweg dieser Funktion, auch fuer den, den jemand als
|
||||
naechstes einbaut. Dieselbe Ueberlegung wie bei "WARUM DER KLICK
|
||||
GLEICH HIER DRANHAENGT" in bereich.js -- eine Funktion mit drei
|
||||
Ausgaengen braucht ihre Regel am Eingang, nicht an jedem Ausgang.
|
||||
|
||||
OHNE ADRESSE KEINE EINSCHRAENKUNG. `person.haus` setzt sitzungLesen()
|
||||
aus dem Host-Kopf; bei einer Pruefung auf 127.0.0.1 steht dort
|
||||
nichts. Dann greift dieser Riegel nicht -- sonst waeren alle
|
||||
Pruefungen des Hauses still gruen und blind, genau die Falle, vor der
|
||||
crew-adresse.js dreimal warnt.
|
||||
|
||||
UND NULL IST KEIN LOCH. Fuenf von acht Stellen, die Eintraege
|
||||
anlegen, setzen `haus` gar nicht (gemessen: workspace-video.js,
|
||||
workspace-content.js, workspace-bewerbung.js, workspace-treff.js,
|
||||
workspace-vorlagen.js). Ein Filter, der nur `haus = ?` prueft,
|
||||
wuerde deren Eintraege fuer BEIDE Haeuser verbergen -- Daten
|
||||
verschwinden still, der schlimmere Ausgang. Deshalb entscheidet bei
|
||||
NULL der BEREICH, und zwar nach genau derselben Regel, nach der der
|
||||
naechtliche Nachtrag ihn setzt (eintraegeNachHaus in workspace.js).
|
||||
Zwei Fassungen derselben Regel waeren die, die auslaeuft.
|
||||
===================================================================== */
|
||||
|
||||
/** Die Bedingung "nur Eintraege dieses Hauses" -- oder null, wenn die
|
||||
* Frage sich nicht stellt (keine Adresse, also keine Haustrennung).
|
||||
*
|
||||
* ==== WARUM NULL SICHTBAR BLEIBT (03.10.2026) =======================
|
||||
*
|
||||
* Die erste Fassung hat bei `haus IS NULL` den BEREICH entscheiden
|
||||
* lassen -- nach derselben Regel, nach der eintraegeNachHaus() das
|
||||
* Haus nachtraegt (MODI_BEREICHE_ERLAUBT). Das war falsch, und
|
||||
* pruef-haus-trennung.mjs hat es sofort gefunden:
|
||||
*
|
||||
* FEHL auf workspace. steht der Eintrag da
|
||||
*
|
||||
* Es ging um "Lunas Live vom Montag" auf dem Brett `live`. Die
|
||||
* Ableitung stuft `live` als Crew-Brett ein (Team Dogi hat eine
|
||||
* Kachel darauf) -- aber dasselbe Brett traegt auch die
|
||||
* Creator-Akten der Agentur. `live`, `technik` und `community`
|
||||
* gehoeren BEIDEN Haeusern, und eine Regel, die jedem Brett genau
|
||||
* ein Haus zuweist, kann das nicht abbilden.
|
||||
*
|
||||
* Sie ist als VERTEILREGEL trotzdem richtig: Wenn ein neuer Eintrag
|
||||
* ein Haus braucht, ist "wer hat die Kachel" die beste Auskunft, die
|
||||
* es gibt. Als AUSSCHLUSSREGEL beim Lesen ist sie es nicht -- dort
|
||||
* nimmt sie etwas weg, statt etwas zuzuordnen, und ein Irrtum kostet
|
||||
* Sichtbarkeit statt nur Ordnung.
|
||||
*
|
||||
* Deshalb: Wer ein Haus hat, wird danach gefiltert. Wer keins hat,
|
||||
* bleibt sichtbar. Das ist die sichere Richtung -- ein Eintrag zu
|
||||
* viel faellt auf, ein fehlender nicht.
|
||||
*
|
||||
* DAMIT DARAUS KEIN DAUERLOCH WIRD, zwei Dinge: Die fuenf Stellen,
|
||||
* die `haus` bisher nicht gesetzt haben, tun es jetzt (gemessen am
|
||||
* 03.10.2026: workspace-video.js, workspace-content.js,
|
||||
* workspace-bewerbung.js, workspace-treff.js, workspace-vorlagen.js),
|
||||
* und pruef-haus-luecke.mjs wird rot, sobald eine sechste dazukommt,
|
||||
* die es vergisst. Was trotzdem ohne Haus entsteht, traegt
|
||||
* eintraegeNachHaus() beim naechsten Start nach. */
|
||||
function nurDiesesHaus(person, praefix = "e") {
|
||||
const haus = person?.haus;
|
||||
if (haus !== "crew" && haus !== "agentur") return null;
|
||||
return {
|
||||
wo: `(${praefix}.haus = ? OR ${praefix}.haus IS NULL)`,
|
||||
werte: [haus],
|
||||
};
|
||||
}
|
||||
|
||||
export function sichtbarEintrag(person, praefix = "e") {
|
||||
/* RIEGEL 0 -- das Haus der Adresse. Begruendung direkt darueber.
|
||||
Er wird den uebrigen Riegeln UMGEHANGEN statt in jeden Ausgang
|
||||
geschrieben: Diese Funktion hat drei Rueckgabepunkte, und der
|
||||
naechste, den jemand einbaut, waere sonst wieder offen. */
|
||||
const haus = nurDiesesHaus(person, praefix);
|
||||
const mitHaus = (r) => {
|
||||
if (!r || !haus) return r;
|
||||
return { wo: `(${r.wo}) AND ${haus.wo}`, werte: [...r.werte, ...haus.werte] };
|
||||
};
|
||||
|
||||
/* RIEGEL 1 -- die Aussenrolle. Eine eigene, vollstaendige Regel:
|
||||
genau die Bretter des Treffs, sonst nichts. */
|
||||
if (AUSSEN_ROLLEN.has(person?.rolle)) {
|
||||
@@ -1298,19 +1411,19 @@ export function sichtbarEintrag(person, praefix = "e") {
|
||||
weiterhin geliefert. Genau so ist am 10.09. ein Modi-Eintrag
|
||||
durch die Suche gekommen, obwohl das Brett zu war. */
|
||||
const frei = freigabeBedingung(person, praefix);
|
||||
if (!frei) return grund;
|
||||
return { wo: `(${grund.wo}) AND ${frei.wo}`, werte: [...grund.werte, ...frei.werte] };
|
||||
if (!frei) return mitHaus(grund);
|
||||
return mitHaus({ wo: `(${grund.wo}) AND ${frei.wo}`, werte: [...grund.werte, ...frei.werte] });
|
||||
}
|
||||
const regel = sichtbarRoh2(person, praefix);
|
||||
/* RIEGEL 2 -- wer nicht zum Treff gehoert, sieht seine Bretter nicht.
|
||||
Auch DogFather geht hier durch; er steht in TREFF_ROLLEN und faellt
|
||||
deshalb nicht heraus. */
|
||||
if (!regel || TREFF_ROLLEN.has(person?.rolle)) return regel;
|
||||
if (!regel || TREFF_ROLLEN.has(person?.rolle)) return mitHaus(regel);
|
||||
const platz = TREFF_BRETTER.map(() => "?").join(", ");
|
||||
return {
|
||||
return mitHaus({
|
||||
wo: `(${regel.wo}) AND ${praefix}.bereich NOT IN (${platz})`,
|
||||
werte: [...regel.werte, ...TREFF_BRETTER],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function sichtbarRoh2(person, praefix = "e") {
|
||||
@@ -2289,7 +2402,8 @@ bereicheRouter.post("/workspace/api/bereich/:bereich/start", gleicheHerkunft, (r
|
||||
/* Der TAG in Ortszeit, der ZEITSTEMPEL in UTC -- das ist kein
|
||||
Widerspruch: Ein Zeitpunkt ist ueberall derselbe, ein
|
||||
Kalendertag nicht. */
|
||||
heuteLokal(), jetzt(), req.person.id, req.person.haus || null);
|
||||
heuteLokal(), jetzt(), req.person.id,
|
||||
hausFuerNeuenEintrag(req.person, bereich));
|
||||
|
||||
/* UND SOFORT FREIGEBEN (17.09.2026) -- der Fehler, den Filipe
|
||||
auf seinem Bildschirm gesehen hat.
|
||||
@@ -2410,7 +2524,7 @@ bereicheRouter.post("/workspace/api/bereich/:bereich/:id/daraus",
|
||||
„Vorbei" und war zugeklappt -- waehrend beim Wunsch
|
||||
„Daraus wurde ein Termin" zu lesen war. */
|
||||
heuteLokal(), jetzt(), req.person.id, quelle.id,
|
||||
req.person.haus || null);
|
||||
hausFuerNeuenEintrag(req.person, nach));
|
||||
|
||||
protokolliere("bereich_daraus", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
@@ -2683,7 +2797,7 @@ bereicheRouter.post("/workspace/api/bereich/:bereich", gleicheHerkunft, (req, re
|
||||
Fall, und der soll der Normalfall sein. */
|
||||
aus.nur_leitung ?? 0,
|
||||
/* Das Haus aus der Adresse -- letzte Spalte, letzter Wert. */
|
||||
req.person.haus || null);
|
||||
hausFuerNeuenEintrag(req.person, bereich));
|
||||
|
||||
/* DASSELBE VON HAND WIE ÜBER DEN VIDEOWEG (30.09.2026). Wer ein
|
||||
Highlight selbst anlegt und es auch freigeben dürfte, braucht
|
||||
|
||||
@@ -46,6 +46,7 @@ import express from "express";
|
||||
import {
|
||||
db, sitzungLesen, protokolliere, echteIp, istDogFather, heuteLokal,
|
||||
ROLLEN_NAME,
|
||||
hausFuerNeuenEintrag,
|
||||
} from "./workspace.js";
|
||||
import {
|
||||
BEWERBUNG_FRAGEN, BEWERBUNG_GRUPPEN, BEWERBUNG_WEGE, BEWERBUNG_ZUSTAENDE,
|
||||
@@ -543,13 +544,13 @@ bewerbungRouter.put("/workspace/api/bewerbung/:id/weg", express.json({ limit: "8
|
||||
const einsatz = antworten.get("womit") || null;
|
||||
const { lastInsertRowid } = db().prepare(`
|
||||
INSERT INTO eintraege
|
||||
(bereich, art, titel, text, datum, status, erstellt, erstellt_von, einsatz)
|
||||
VALUES ('talente', 'empfohlen', ?, ?, ?, 'offen', ?, ?, ?)`)
|
||||
(bereich, art, titel, text, datum, status, erstellt, erstellt_von, einsatz, haus)
|
||||
VALUES ('talente', 'empfohlen', ?, ?, ?, 'offen', ?, ?, ?, ?)`)
|
||||
.run(b.nenn_name,
|
||||
`Hat sich selbst gemeldet (Anfrage #${id}).`
|
||||
+ (antworten.get("warum") ? `\n\nWarum: ${antworten.get("warum")}` : ""),
|
||||
/* Der TAG in Ortszeit (01.10.2026), der Zeitstempel in UTC. */
|
||||
heuteLokal(), t, req.person.id, einsatz);
|
||||
heuteLokal(), t, req.person.id, einsatz, hausFuerNeuenEintrag(req.person, "talente"));
|
||||
eintragId = Number(lastInsertRowid);
|
||||
|
||||
/* AUF „ANGESPROCHEN" und nicht auf „aufgefallen": Die ersten
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
db, protokolliere, echteIp, sitzungLesen, betreutWo, darfCreator, betreuteIds,
|
||||
istLeitung, istDogFather, siehtAlles, tagLokal,
|
||||
heuteLokal,
|
||||
hausFuerNeuenEintrag,
|
||||
} from "./workspace.js";
|
||||
|
||||
export const contentRouter = express.Router();
|
||||
@@ -413,10 +414,10 @@ contentRouter.post("/workspace/api/content/:id/uebernehmen", gleicheHerkunft, (r
|
||||
|
||||
} else {
|
||||
const r = db().prepare(`
|
||||
INSERT INTO eintraege (bereich, art, titel, text, datum, status, creator_id, erstellt, erstellt_von)
|
||||
VALUES ('live','vorbereitung',?,?,?,'offen',?,?,?)`).run(
|
||||
INSERT INTO eintraege (bereich, art, titel, text, datum, status, creator_id, erstellt, erstellt_von, haus)
|
||||
VALUES ('live','vorbereitung',?,?,?,'offen',?,?,?,?)`).run(
|
||||
e.titel.slice(0, 160), beschreibung, e.geplant ? e.geplant.slice(0, 10) : heute(),
|
||||
e.creator_id, jetzt(), req.person.id);
|
||||
e.creator_id, jetzt(), req.person.id, hausFuerNeuenEintrag(req.person, "live"));
|
||||
neu = { art: "LIVE-Thema", id: Number(r.lastInsertRowid), wohin: "bereich.html?b=live" };
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
db, sitzungLesen, protokolliere, echteIp,
|
||||
TREFF_BRETTER, TREFF_ROLLEN, ROLLEN_NAME, istHand,
|
||||
heuteLokal,
|
||||
hausFuerNeuenEintrag,
|
||||
} from "./workspace.js";
|
||||
/* Die Tabellen und die Sperrfrage stehen in einem Blatt ohne eigene
|
||||
Importe -- workspace.js braucht beides ebenfalls, und ein Importkreis
|
||||
@@ -1019,13 +1020,14 @@ treffRouter.post("/workspace/api/treff/mitmachen/:id/uebernehmen", gleicheHerkun
|
||||
const nun = jetzt();
|
||||
const r = db().prepare(`INSERT INTO eintraege
|
||||
(bereich, art, titel, text, datum, dringlichkeit, status, creator_id,
|
||||
erstellt, erstellt_von, einsatz)
|
||||
VALUES ('talente','empfohlen',?,?,?,'mittel','offen',?,?,?,?)`)
|
||||
erstellt, erstellt_von, einsatz, haus)
|
||||
VALUES ('talente','empfohlen',?,?,?,'mittel','offen',?,?,?,?,?)`)
|
||||
.run(
|
||||
`${wer?.name ?? "Jemand"} hat sich im Rudel gemeldet`,
|
||||
`Aus „Mitmachen“: ${String(e.titel ?? "")}\n\n${String(e.text ?? "")}`.trim(),
|
||||
/* Der TAG in Ortszeit (01.10.2026), der Zeitstempel in UTC. */
|
||||
heuteLokal(), e.erstellt_von, nun, req.person.id, "Das Rudel");
|
||||
heuteLokal(), e.erstellt_von, nun, req.person.id, "Das Rudel",
|
||||
hausFuerNeuenEintrag(req.person, "talente"));
|
||||
protokolliere("treff_talent", { personId: req.person.id, ip: echteIp(req),
|
||||
detail: `aus Eintrag ${id}` });
|
||||
res.json({ ok: true, talent_id: Number(r.lastInsertRowid) });
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
db, protokolliere, echteIp, sitzungLesen, DATEN_ORDNER,
|
||||
KANAELE, kanalVonHandle, TREFF_BRETTER,
|
||||
heuteLokal,
|
||||
hausFuerNeuenEintrag,
|
||||
} from "./workspace.js";
|
||||
import { darfSchreiben, sofortFreigeben } from "./workspace-treff.js";
|
||||
import { sichtbar } from "./workspace-bereiche.js";
|
||||
@@ -281,13 +282,14 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
|
||||
const { lastInsertRowid } = db().prepare(`
|
||||
INSERT INTO eintraege
|
||||
(bereich, art, titel, datum, status, erstellt, erstellt_von,
|
||||
quelle_url, quelle_kanal, quelle_geholt_am, aus_eintrag_id)
|
||||
VALUES (?,?,?,?, 'offen', ?,?,?,?,?,?)`)
|
||||
quelle_url, quelle_kanal, quelle_geholt_am, aus_eintrag_id, haus)
|
||||
VALUES (?,?,?,?, 'offen', ?,?,?,?,?,?,?)`)
|
||||
/* Der TAG in Ortszeit (01.10.2026), der Zeitstempel daneben in
|
||||
UTC -- ein Zeitpunkt ist ueberall derselbe, ein Kalendertag
|
||||
nicht. */
|
||||
.run(bereich, arten[bereich] || "clip", auskunft.titel, heuteLokal(),
|
||||
jetzt, req.person.id, url, ablage, jetzt, ausQuelle ? aus : null);
|
||||
jetzt, req.person.id, url, ablage, jetzt, ausQuelle ? aus : null,
|
||||
hausFuerNeuenEintrag(req.person, bereich));
|
||||
eintragId = Number(lastInsertRowid);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ import {
|
||||
KATALOG_EMPFAENGER,
|
||||
KATALOG_EMPFAENGER_SQL,
|
||||
heuteLokal,
|
||||
hausFuerNeuenEintrag,
|
||||
} from "./workspace.js";
|
||||
|
||||
/* Dieselbe Meldung wie beim anderen Bewerbungsweg -- eine Stelle,
|
||||
@@ -2212,14 +2213,14 @@ vorlagenRouter.post("/workspace/api/vorlagen/uebernehmen", gleicheHerkunft,
|
||||
const einfuegen = (bereich, unterart, titel, text, zusatz = {}) => {
|
||||
db().prepare(`INSERT INTO eintraege
|
||||
(bereich, art, titel, text, datum, status, creator_id, hook, format,
|
||||
erstellt, erstellt_von)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)`)
|
||||
erstellt, erstellt_von, haus)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`)
|
||||
/* Der TAG in Ortszeit (01.10.2026), der Zeitstempel weiter
|
||||
unten in UTC. Aus dem Startkatalog angelegte Eintraege
|
||||
trugen sonst nachts das Datum von gestern. */
|
||||
.run(bereich, unterart, titel, text, heuteLokal(), "offen",
|
||||
creatorId, zusatz.hook || null, zusatz.format || null,
|
||||
jetzt(), req.person.id);
|
||||
jetzt(), req.person.id, hausFuerNeuenEintrag(req.person, bereich));
|
||||
angelegt.push(titel);
|
||||
};
|
||||
|
||||
|
||||
@@ -2296,6 +2296,49 @@ const BRETTER_UEBER_SEITEN = {
|
||||
|
||||
/** Bretter mit eigener Kachel. Diese darf jeder aus dem Team, der die
|
||||
* Kachel sieht -- sie sind daraus abgeleitet, nicht abgeschrieben. */
|
||||
/** In welches Haus gehoert ein NEU angelegter Eintrag?
|
||||
*
|
||||
* ==== WARUM NICHT EINFACH `person.haus` (03.10.2026) ===============
|
||||
*
|
||||
* Genau das stand an allen acht Einfuege-Stellen -- und
|
||||
* pruef-treff.mjs hat gezeigt, dass es falsch ist:
|
||||
*
|
||||
* FEHL die Community sieht es SOFORT -- ohne zweiten Klick
|
||||
* FEHL DogFather sieht den Beitrag
|
||||
*
|
||||
* Beide Male legt DogFather ueber die AGENTURADRESSE einen Beitrag
|
||||
* auf einem Brett des Rudels an (`highlight`, `anschlag`). Mit
|
||||
* `person.haus` bekam der Beitrag das Haus "agentur" -- und war
|
||||
* damit fuer die Community auf crew. unsichtbar. Auf einem Brett,
|
||||
* das es nur dort gibt.
|
||||
*
|
||||
* Der richtige Massstab steht schon im Kommentar von
|
||||
* eintraegeNachHaus() weiter unten: "Das Haus eines Eintrags -- aus
|
||||
* seinem BEREICH, nicht aus dem Ersteller. `regeln`, `anschlag`,
|
||||
* `highlight` sind Bretter von Team Dogi, `agentur` gehoert der
|
||||
* Agentur; das steht fest, unabhaengig davon, wer gerade
|
||||
* hineingeschrieben hat."
|
||||
*
|
||||
* ABER NICHT FUER JEDES BRETT. `live`, `technik` und `community`
|
||||
* tragen BEIDES: die Kacheln von Team Dogi und die Creator-Akten der
|
||||
* Agentur. Dort ist die Adresse, auf der jemand schreibt, die beste
|
||||
* Auskunft, die es gibt -- und die einzige.
|
||||
*
|
||||
* Also: Wo das Brett eindeutig ist, entscheidet das Brett. Wo nicht,
|
||||
* die Adresse. TREFF_BRETTER ist die gepflegte Liste der eindeutigen
|
||||
* Bretter des Rudels; `agentur` ist das Gegenstueck auf der anderen
|
||||
* Seite und steht in BEREICHE, nicht hier.
|
||||
*/
|
||||
export function hausFuerNeuenEintrag(person, bereich) {
|
||||
/* Die sieben Bretter des Rudels gibt es nur bei Team Dogi -- egal,
|
||||
von welcher Adresse aus jemand hineinschreibt. */
|
||||
if (TREFF_BRETTER.includes(String(bereich))) return "crew";
|
||||
/* Sonst entscheidet die Adresse. Ohne Adresse (Pruefungen,
|
||||
127.0.0.1) bleibt es offen -- und ein Eintrag ohne Haus ist in
|
||||
beiden sichtbar, die bewusst sichere Richtung. */
|
||||
return person?.haus || null;
|
||||
}
|
||||
|
||||
export const MODI_BEREICHE_ERLAUBT = new Set([
|
||||
...HAND_MIT_TREFF
|
||||
.map((k) => (/bereich\.html\?b=([a-z]+)/.exec(k.ziel) || [])[1])
|
||||
|
||||
Reference in New Issue
Block a user