Eine Kampagne gehoert zur Agentur -- auch wenn DogFather ueberall admin ist
Aufgefallen beim Nachweis auf der echten Adresse, nicht beim Bauen: Der Kasten haengt an `bereich === "agentur"` und an der Rolle. DogFather ist aber in BEIDEN Haeusern `admin`. Oeffnet er auf crew.dogfather-universe.com von Hand `bereich.html?b=agentur`, dann stimmt der Bereich, die Rolle stimmt -- und `hausFuerNeuenEintrag` gaebe der Kampagne das Haus "crew". Eine Agentur-Kampagne staende im Rudel, und die Trennung waere an genau der Stelle unterlaufen, an der niemand nachsieht. Bauplan, Teil 11: "Nichts im Haus Team Dogi." ZWEI FRAGEN, ZWEI SCHWELLEN -- und das ist Absicht: /api/ich (ZEIGEN) haus !== "crew" Route (SCHREIBEN) haus === "agentur" Der Unterschied betrifft genau einen Fall: `null`. Den gibt es nur auf einer PRUEFADRESSE -- `sitzungLesen` vergibt dort seit dem 24.09.2026 bewusst kein Haus, und die Begruendung dort nennt den Schaden: Als 127.0.0.1 einmal ein Haus bekam, fiel pruef-chat-kanaele mit 13 Fehlschlaegen um, und die naechsten Pruefungen waeren still gruen geblieben, ohne noch etwas zu messen. Eine echte Anfrage hat immer eine der beiden Waende; eine Sitzung zu einer dritten lehnt `sitzungPasstZurAdresse` ohnehin ab. Beim SCHREIBEN wird trotzdem streng gefragt: Ein Eintrag mit `haus = null` waere in BEIDEN Haeusern sichtbar -- genau das, was Teil 11 ausschliesst. WAS ICH DABEI ZWEIMAL FALSCH VERSUCHT HABE Zuerst sollte das Bildwerkzeug ueber die Agenturwand kommen, damit es den Kasten weiter messen kann. Den Host-Kopf beim Durchreichen zu ueberschreiben wirkt nicht -- gemessen: /api/ich meldete weiter `darf: false`, der Wirt blieb "127.0.0.1:4338". Chromium laesst `Host` nicht ueberschreiben. Dann den Namen wirklich aufloesen (--host-resolver-rules). Das funktioniert, bringt aber die Anmeldemaske der echten Wand mit einer Tuer davor, die ein Bildwerkzeug nicht aufmachen soll. Gemessen werden soll die KACHEL, nicht der Zugang. Deshalb bleibt bild-kampagne auf der Pruefadresse -- und genau deshalb fragt /api/ich `!== "crew"` statt `=== "agentur"`. Beide Zeilen tragen jetzt die Begruendung bei sich, damit sie niemand "vereinheitlicht": Das macht entweder die Pruefungen blind oder laesst eine Kampagne ins Rudel. Zwei Gegenproben gefahren, beide schlagen an: Route nimmt jede Wand an -> 2 rot. /api/ich fragt nicht nach dem Haus -> 1 rot. GEPRUEFT: pruef-kampagne 98 -> 99 gruen · bild-kampagne 20 Messungen gruen · pruef-struktur, pruef-haus-trennung 107, pruef-agentur 62, pruef-eventkarte 94, pruef-alle-wege, pruef-schranke, pruef-zeichen 8 Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -37,7 +37,24 @@ express.response.cookie = function (n, w, o) {
|
|||||||
};
|
};
|
||||||
await import("./index.js");
|
await import("./index.js");
|
||||||
await new Promise((r) => setTimeout(r, 1200));
|
await new Promise((r) => setTimeout(r, 1200));
|
||||||
const BASIS = `http://127.0.0.1:${PORT}`;
|
/* WARUM HIER 127.0.0.1 STEHT UND NICHT DIE AGENTURWAND
|
||||||
|
(gemessen und wieder zurueckgebaut, 07.10.2026).
|
||||||
|
|
||||||
|
Versucht wurde beides. Der Host-Kopf laesst sich in Chromium
|
||||||
|
nicht ueberschreiben (gemessen: /api/ich meldete weiter
|
||||||
|
`darf: false`, der Wirt blieb "127.0.0.1"), und laesst man den
|
||||||
|
Namen wirklich aufloesen, kommt die Anmeldemaske der echten Wand
|
||||||
|
-- mit einer Tuer davor, die dieses Werkzeug nicht aufmachen
|
||||||
|
soll. Gemessen werden soll die KACHEL, nicht der Zugang.
|
||||||
|
|
||||||
|
Deshalb bleibt es bei der Pruefadresse. Dort vergibt
|
||||||
|
`sitzungLesen` bewusst KEIN Haus (24.09.2026), und `/api/ich`
|
||||||
|
fragt genau deshalb `haus !== "crew"` statt `=== "agentur"`:
|
||||||
|
Der Kasten ist hier sichtbar und damit messbar.
|
||||||
|
|
||||||
|
Dass er auf der CREW-Adresse verschwindet, misst
|
||||||
|
pruef-kampagne mit echten Host-Koepfen -- das geht ohne
|
||||||
|
Browser und ist dort besser aufgehoben. */const BASIS = `http://127.0.0.1:${PORT}`;
|
||||||
|
|
||||||
const { DatabaseSync } = await import("node:sqlite");
|
const { DatabaseSync } = await import("node:sqlite");
|
||||||
const { scryptSync, randomBytes } = await import("node:crypto");
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
||||||
@@ -65,11 +82,23 @@ const pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_mo
|
|||||||
const browser = await pw.chromium.launch();
|
const browser = await pw.chromium.launch();
|
||||||
const fehlerAufSeite = [];
|
const fehlerAufSeite = [];
|
||||||
|
|
||||||
|
|
||||||
const anmelden = async (seite, rolle, code) => {
|
const anmelden = async (seite, rolle, code) => {
|
||||||
await seite.goto(BASIS + "/workspace/", { waitUntil: "networkidle" });
|
await seite.goto(BASIS + "/workspace/", { waitUntil: "networkidle" });
|
||||||
await seite.click(`.rolle[data-rolle="${rolle}"]`);
|
await seite.click(`.rolle[data-rolle="${rolle}"]`);
|
||||||
await seite.fill("#code", code);
|
await seite.fill("#code", code);
|
||||||
await seite.click("button[type=submit]");
|
/* DEN KNOPF DIREKT AUSLOESEN, nicht ueber die Maus.
|
||||||
|
|
||||||
|
page.click wartet darauf, dass das Element "visible, enabled and
|
||||||
|
stable" ist -- und auf der echten Agenturwand lief es dort in die
|
||||||
|
30-Sekunden-Frist. Die Anmeldeseite bewegt sich dauerhaft (die
|
||||||
|
Buehne atmet), also wird "stable" nie erreicht. Auf 127.0.0.1 war
|
||||||
|
das nie aufgefallen, weil dort eine andere Szene laeuft.
|
||||||
|
|
||||||
|
Gemessen wird hier die KACHEL, nicht die Anmeldemaske -- der
|
||||||
|
direkte Klick ist deshalb kein Umgehen einer Pruefung, sondern
|
||||||
|
der Weg zum eigentlichen Messgegenstand. */
|
||||||
|
await seite.$eval("button[type=submit]", (b) => b.click());
|
||||||
await seite.waitForURL("**/start.html", { timeout: 25000 });
|
await seite.waitForURL("**/start.html", { timeout: 25000 });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,6 +136,12 @@ async function messen(breite, hoehe, name) {
|
|||||||
await seite.waitForTimeout(1600);
|
await seite.waitForTimeout(1600);
|
||||||
|
|
||||||
console.log(`\n=== ${name} (${breite} px) ===`);
|
console.log(`\n=== ${name} (${breite} px) ===`);
|
||||||
|
const ichSagt = await seite.evaluate(async () => {
|
||||||
|
const a = await fetch("/workspace/api/ich", { credentials: "same-origin" });
|
||||||
|
const j = await a.json().catch(() => ({}));
|
||||||
|
return { rolle: j.rolle, darf: j.darf_kampagne_einlesen, wirt: location.host };
|
||||||
|
});
|
||||||
|
console.log(" /api/ich: " + JSON.stringify(ichSagt));
|
||||||
const mass = await seite.evaluate(() => {
|
const mass = await seite.evaluate(() => {
|
||||||
const k = document.getElementById("kampagneholen");
|
const k = document.getElementById("kampagneholen");
|
||||||
if (!k || k.hidden) return { da: false };
|
if (!k || k.hidden) return { da: false };
|
||||||
|
|||||||
@@ -753,6 +753,15 @@ melde("=== 11. Kampagnen anlegen dürfen DogFather und die Manager ===");
|
|||||||
&& ichLuna.darf_kampagne_einlesen === false,
|
&& ichLuna.darf_kampagne_einlesen === false,
|
||||||
"/api/ich sagt der Oberfläche dasselbe wie die Schranke der Route");
|
"/api/ich sagt der Oberfläche dasselbe wie die Schranke der Route");
|
||||||
|
|
||||||
|
/* UND AUF DER CREW-ADRESSE SAGT SIE NEIN. Das ist der Fall, den
|
||||||
|
es in der Wirklichkeit gibt: DogFather ist dort ebenfalls
|
||||||
|
`admin`, und ohne diese Antwort stuende der Kasten im Rudel. */
|
||||||
|
const ichCrew = JSON.parse((await roh("/workspace/api/ich",
|
||||||
|
{ keks: keksDogiCrew, wirt: CREW_HOST })).text);
|
||||||
|
ok(ichCrew.rolle === "admin" && ichCrew.darf_kampagne_einlesen === false,
|
||||||
|
`auf der Crew-Adresse sagt sie nein (Rolle ${ichCrew.rolle},`
|
||||||
|
+ ` darf ${ichCrew.darf_kampagne_einlesen})`);
|
||||||
|
|
||||||
/* FREMDE HERKUNFT. Die Wache steht in index.js, VOR diesem Router. */
|
/* FREMDE HERKUNFT. Die Wache steht in index.js, VOR diesem Router. */
|
||||||
const fremd = await new Promise((f, s) => {
|
const fremd = await new Promise((f, s) => {
|
||||||
const daten = Buffer.from(JSON.stringify({ text: "x" }));
|
const daten = Buffer.from(JSON.stringify({ text: "x" }));
|
||||||
@@ -795,6 +804,18 @@ melde("=== 12. Team Dogi bleibt unberührt ===");
|
|||||||
ok((wsBrett.eintraege || []).some((e) => e.id === kachelId),
|
ok((wsBrett.eintraege || []).some((e) => e.id === kachelId),
|
||||||
"Gegenprobe: auf der Agentur-Adresse schon");
|
"Gegenprobe: auf der Agentur-Adresse schon");
|
||||||
|
|
||||||
|
/* UND VON DER CREW-ADRESSE AUS LAESST SIE SICH GAR NICHT ANLEGEN.
|
||||||
|
|
||||||
|
DogFather ist in beiden Haeusern `admin` -- die Rollenfrage
|
||||||
|
allein laesst ihn also durch. Ohne diese Schranke bekaeme die
|
||||||
|
Kampagne das Haus "crew", und die Trennung waere an genau der
|
||||||
|
Stelle unterlaufen, an der niemand nachsieht. */
|
||||||
|
const vonCrew = await einlesen(`Klick: ${TIK}/t/gipfel`, keksDogiCrew, CREW_HOST);
|
||||||
|
ok(vonCrew.code === 403,
|
||||||
|
`von der Crew-Adresse: HTTP ${vonCrew.code} — „${vonCrew.j.fehler}“`);
|
||||||
|
ok(/[Aa]gentur/.test(vonCrew.j.fehler || ""),
|
||||||
|
" und die Meldung sagt, wo es hingehoert");
|
||||||
|
|
||||||
const crewNachher = dbLesen("SELECT COUNT(*) AS n FROM eintraege WHERE haus = 'crew'")[0].n;
|
const crewNachher = dbLesen("SELECT COUNT(*) AS n FROM eintraege WHERE haus = 'crew'")[0].n;
|
||||||
ok(crewVorher === crewNachher,
|
ok(crewVorher === crewNachher,
|
||||||
`und die Zahl der Crew-Einträge ist unverändert (${crewVorher} = ${crewNachher})`);
|
`und die Zahl der Crew-Einträge ist unverändert (${crewVorher} = ${crewNachher})`);
|
||||||
|
|||||||
@@ -264,6 +264,36 @@ kampagneRouter.post("/workspace/api/kampagne/einlesen", async (req, res) => {
|
|||||||
`keine Leitung (${person.rolle})`);
|
`keine Leitung (${person.rolle})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Und nur auf der AGENTURADRESSE ---------------------
|
||||||
|
|
||||||
|
Bauplan, Teil 11: "Nichts im Haus Team Dogi."
|
||||||
|
|
||||||
|
WARUM DAS EINE EIGENE ZEILE BRAUCHT: DogFather ist in BEIDEN
|
||||||
|
Haeusern `admin`, und `istLeitung` fragt nur nach der Rolle.
|
||||||
|
Meldet er sich auf crew.dogfather-universe.com an und oeffnet
|
||||||
|
dort `bereich.html?b=agentur`, dann ist er Leitung, der
|
||||||
|
Bereich stimmt -- und `hausFuerNeuenEintrag` gaebe der
|
||||||
|
Kampagne das Haus "crew". Eine Agentur-Kampagne staende
|
||||||
|
damit im Rudel, und die Trennung waere genau an der Stelle
|
||||||
|
unterlaufen, an der sie niemand nachsieht.
|
||||||
|
|
||||||
|
Gefragt wird POSITIV ("ist es die Agentur?") und nicht
|
||||||
|
negativ ("ist es nicht die Crew?"): Ohne Adresse gibt
|
||||||
|
`hausFuerNeuenEintrag` `null` zurueck, und ein Eintrag ohne
|
||||||
|
Haus ist in BEIDEN sichtbar. Fuer die meisten Eintraege ist
|
||||||
|
das die bewusst sichere Richtung -- fuer eine Kampagne waere
|
||||||
|
es das Gegenteil.
|
||||||
|
|
||||||
|
Dasselbe Haus wird unten beim Schreiben benutzt, nicht ein
|
||||||
|
zweites Mal berechnet. */
|
||||||
|
const haus = hausFuerNeuenEintrag(person, "agentur");
|
||||||
|
if (haus !== "agentur") {
|
||||||
|
return schiefgegangen(403,
|
||||||
|
"Kampagnen gehören zur Agentur. Bitte auf "
|
||||||
|
+ "workspace.dogfather-universe.com anmelden.",
|
||||||
|
`falsches Haus: ${haus ?? "ohne Adresse"}`);
|
||||||
|
}
|
||||||
|
|
||||||
const text = String(req.body?.text ?? "");
|
const text = String(req.body?.text ?? "");
|
||||||
if (!text.trim()) {
|
if (!text.trim()) {
|
||||||
return schiefgegangen(400,
|
return schiefgegangen(400,
|
||||||
@@ -440,11 +470,11 @@ kampagneRouter.post("/workspace/api/kampagne/einlesen", async (req, res) => {
|
|||||||
kuerzen(regelnText(gelesen.kuer, hinweise)),
|
kuerzen(regelnText(gelesen.kuer, hinweise)),
|
||||||
dringlichkeit,
|
dringlichkeit,
|
||||||
jetzt, person.id,
|
jetzt, person.id,
|
||||||
/* NICHT von Hand auf "agentur" gesetzt: Das Haus entscheidet
|
/* NICHT von Hand auf "agentur" gesetzt, sondern dieselbe
|
||||||
die Adresse, über die jemand hereinkommt. Wer es hier
|
Angabe, die oben schon ueber die Annahme entschieden
|
||||||
festschreibt, baut die Trennung ein zweites Mal -- und
|
hat. Zweimal zu rechnen waeren zwei Fassungen derselben
|
||||||
zwei Fassungen derselben Regel laufen auseinander. */
|
Regel -- und die laufen auseinander. */
|
||||||
hausFuerNeuenEintrag(person, "agentur"),
|
haus,
|
||||||
nummer, link, jetzt, gelesen.pflicht.zone,
|
nummer, link, jetzt, gelesen.pflicht.zone,
|
||||||
JSON.stringify(gelesen.auszug));
|
JSON.stringify(gelesen.auszug));
|
||||||
eintragId = Number(lastInsertRowid);
|
eintragId = Number(lastInsertRowid);
|
||||||
|
|||||||
+29
-1
@@ -7680,7 +7680,35 @@ workspaceRouter.get("/workspace/api/ich", (req, res) => {
|
|||||||
obwohl der Weg dahinter offen war. Und die umgekehrte Richtung
|
obwohl der Weg dahinter offen war. Und die umgekehrte Richtung
|
||||||
ist schlimmer: Ein Feld, das erscheint und dann 403 bekommt,
|
ist schlimmer: Ein Feld, das erscheint und dann 403 bekommt,
|
||||||
laesst den Menschen davor sich selbst fuer den Fehler halten. */
|
laesst den Menschen davor sich selbst fuer den Fehler halten. */
|
||||||
darf_kampagne_einlesen: istLeitung(person),
|
/* UND NICHT IM HAUS TEAM DOGI (nachgetragen 07.10.2026).
|
||||||
|
|
||||||
|
Hier stand zuerst nur `istLeitung(person)`. DogFather ist
|
||||||
|
aber in BEIDEN Haeusern `admin`: Auf der Crew-Adresse haette
|
||||||
|
die Oberflaeche den Kasten gezeigt, und der Server haette mit
|
||||||
|
403 abgelehnt. Ein Formular, das der Server ablehnt, laesst
|
||||||
|
den Menschen davor sich selbst fuer den Fehler halten.
|
||||||
|
|
||||||
|
ZWEI FRAGEN, ZWEI SCHWELLEN -- und das ist Absicht:
|
||||||
|
|
||||||
|
hier (ZEIGEN) haus !== "crew"
|
||||||
|
Route (SCHREIBEN) haus === "agentur"
|
||||||
|
|
||||||
|
Der Unterschied betrifft genau einen Fall: `null`. Den gibt
|
||||||
|
es nur auf einer PRUEFADRESSE -- `sitzungLesen` vergibt dort
|
||||||
|
bewusst kein Haus (siehe die Begruendung dort, 24.09.2026:
|
||||||
|
Als 127.0.0.1 einmal ein Haus bekam, fiel pruef-chat-kanaele
|
||||||
|
mit 13 Fehlschlaegen um, und die naechsten Pruefungen waeren
|
||||||
|
still gruen geblieben, ohne noch etwas zu messen). Eine
|
||||||
|
echte Anfrage hat immer eine der beiden Waende; eine Sitzung
|
||||||
|
zu einer dritten lehnt `sitzungPasstZurAdresse` ohnehin ab.
|
||||||
|
|
||||||
|
Beim SCHREIBEN wird trotzdem streng gefragt: Ein Eintrag mit
|
||||||
|
`haus = null` waere in BEIDEN Haeusern sichtbar -- genau das,
|
||||||
|
was Teil 11 des Bauplans ausschliesst. Wer die beiden Zeilen
|
||||||
|
einmal "vereinheitlicht", macht entweder die Pruefungen
|
||||||
|
blind oder laesst eine Kampagne ins Rudel. */
|
||||||
|
darf_kampagne_einlesen: istLeitung(person)
|
||||||
|
&& hausFuerNeuenEintrag(person, "agentur") !== "crew",
|
||||||
/* ZWEI LISTEN STATT EINER RECHNUNG IM BROWSER (20.09.2026).
|
/* ZWEI LISTEN STATT EINER RECHNUNG IM BROWSER (20.09.2026).
|
||||||
|
|
||||||
Die Rollenwahl nahm bisher die Knoepfe des Anlege-Formulars --
|
Die Rollenwahl nahm bisher die Knoepfe des Anlege-Formulars --
|
||||||
|
|||||||
Reference in New Issue
Block a user