Der Treff: eine eigene Tür für die Community, und sie sieht nur, was dasteht
Filipe: "eine community rolle und community kategorie, wo die community auch dann nur die sieht" · "wieso nur eine kachel — ich will mehrere, mit mehreren optionen" · "egal welche rolle hinzugefügt wird soll immer nur das sehen was ich erlaube". DIE DRITTE ADRESSE. treff.dogfather-universe.com bekommt eine eigene Weiche (treff-adresse.js), eine eigene Zugangswand, ein eigenes Manifest und acht eigene Bühnenbilder. Ohne sie stünde dort die Wand der Agentur: Spicy-Logo, "Creator Workspace", fünf Rollenkacheln mit den Namen Admin, Manager, Scout, Creator — die komplette Struktur des Unternehmens, vor der Anmeldung, für jeden mit der Adresse. Welche Wand zu welcher Tür gehört, steht jetzt in EINER Tafel (istFremdeWand) statt als Sonderfall im Code. Bei zwei Wänden war ein Sonderfall richtig; bei drei wären es drei geworden, bei vier sechs. SIEBEN BRETTER UND EIN ACHTES NUR FÜRS TEAM. Anschlagbrett, Was ansteht, Der Treff, Wunschliste, Highlights, Regeln & Hilfe, Mitmachen — dazu "Meldungen & Maßnahmen", das ausdrücklich NICHT in TREFF_BEREICHE steht: dort wäre es lautlos bei der Community gelandet und hätte ausgesehen wie die anderen sieben. WAS AUF DEN BRETTERN PASSIERT: "Will ich auch" (eine Stimme je Person, erzwungen durch den Schlüssel, nicht durch eine Prüfung) · Anheften, höchstens drei, gezählt IN der Transaktion · Freigabe vor Sichtbarkeit für Termine und Highlights, wobei Abwesenheit der Ruhezustand ist · Melden mit Pflicht-Grund (DSA Art. 16) · Entfernen mit Grund, der den Beitrag ÜBERLEBT (DSA Art. 17) · drei Stufen, gerechnet statt gespeichert · die Bannleiter: Modi bis 7 Tage, dauerhaft nur DogFather (seine Entscheidung) · "Mitmachen" wird zum Talent, genau einmal. Der Ausschluss wirkt in sitzungLesen() — also nicht nur beim Schreiben. Wer ausgeschlossen ist, ist weg, nicht stumm. ZWEI SEITEN MEHR. treff-regeln.html holt seine ZAHLEN vom Server (Mindestalter 18, Fristen, Anschläge) — ein Regeltext, der eine andere Zahl nennt als die Regel, ist schlimmer als keiner. rechte.html rechnet "Wer sieht was" aus derselben Tafel aus, aus der die Schranke ihre Entscheidung holt. Die Community steht dort bei 3 von 23. SECHS BEFUNDE, KEINEN HAT DAS LESEN GEFUNDEN: · Ein Modi kam über den verborgenen Zugang in den Treff — die Bedingung war durch Ausschluss formuliert und nahm die neue Wand automatisch mit · /api/personen gab einem Mitglied Namen und Rolle von DogFather · Ein DELETE mit Körper wird von Nodes HTTP-Parser mit einem leeren 400 abgewiesen, bevor Express ihn sieht (gemessen). Der Grund reist jetzt in der Adresse · Der Kommentar, der erklärt, warum die Zugangswand keine Rollennamen nennt, nannte selbst einen — in einer ausgelieferten Datei · Zwei Listen für "welche Seiten sind ohne Anmeldung offen". Sie stimmten, weil beide am selben Tag entstanden · Ein Portkonflikt, den ich selbst gebaut hatte (4391 gehört pruef-chat-aufloesen) Dazu zwei feste Zahlen abgeschafft (die 20 in pruef-rechtetafel, die Kachelzahl in pruef-treff) und zwei Werkzeuge, die jetzt SUCHEN statt aufzuzählen: Der Versionsstempel findet seine Manifeste selbst — das dritte hätte sonst keinen bekommen, und die neue App hätte bis zu vier Stunden lang das falsche Zeichen getragen. Das Bildwerkzeug baut beide Zugangswände aus einer Vorlage mit zwei Werten. Zwei neue Kacheltöne, gemessen statt gewählt: #2f2f7f (Abstand 31,4 in Lab) und #7c2771 (29,7). Das schwächste Paar der vorhandenen 34 liegt bei 24,9. Geprüft: treff 69 · treff-werkzeuge 60 · rollen 312 · start-ansicht 147 · crew-adresse 129 · sicht 84 · modi-verborgen 80 · haus-trennung 62 · bereiche-lesend 37 · css-klassen 30 · zwischenspeicher 21 · alle-wege 19 · rechtetafel 19 — alles grün. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+396
-18
@@ -27,8 +27,12 @@ import { dirname, join } from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { darfSeite } from "./rechte.js";
|
||||
import { sitzungPasstZurAdresse, istOhneModiAdresse, istCrewAdresse,
|
||||
import { darfSeite, OHNE_ANMELDUNG, SEITEN, ALLE_ROLLEN } from "./rechte.js";
|
||||
/* Ein Modul ohne eigene Importe -- deshalb entsteht hier kein Kreis,
|
||||
obwohl workspace-treff.js seinerseits aus dieser Datei importiert.
|
||||
Begruendung im Kopf von treff-tabellen.js. */
|
||||
import { treffTabellen, treffSperreLesen } from "./treff-tabellen.js";
|
||||
import { sitzungPasstZurAdresse, istOhneModiAdresse, istCrewAdresse, istTreffAdresse, istPruefAdresse, AUSSEN_ROLLEN,
|
||||
TEAM_DOGI_ROLLEN } from "./crew-adresse.js";
|
||||
/* Weitergereicht, damit die Fachmodule sie wie alles andere aus
|
||||
workspace.js beziehen und nicht wissen muessen, wo sie wohnt. */
|
||||
@@ -63,7 +67,7 @@ const COOKIE = "dfw_sitzung";
|
||||
const SITZUNG_STUNDEN = 12;
|
||||
const VERSUCHE_MAX = 8; // pro IP
|
||||
const VERSUCHE_FENSTER_MIN = 10;
|
||||
const ROLLEN = new Set(["spicy", "admin", "manager", "scout", "creator", "hand", "modi"]);
|
||||
const ROLLEN = new Set(["spicy", "admin", "manager", "scout", "creator", "hand", "modi", "gast"]);
|
||||
|
||||
/* Die Reihenfolge, in der Rollen ueberall erscheinen: Spicy Media
|
||||
zuerst, dann DogFather, Manager, Scout, Creator. Steht hier einmal,
|
||||
@@ -99,6 +103,19 @@ const ROLLEN_KACHEL = new Set(["spicy", "admin", "manager", "scout", "creator"])
|
||||
* sondern ihr Sinn. */
|
||||
const CREW_KACHEL = new Set(["admin", "hand", "modi"]);
|
||||
|
||||
/** Die Rollen mit einer Kachel auf der Zugangswand von treff.
|
||||
*
|
||||
* EINE, und deshalb zeigt die Wand dort gar keine Kachelreihe: Eine
|
||||
* Auswahl mit einem einzigen Eintrag ist keine Auswahl, sondern eine
|
||||
* Huerde. Dieselbe Ueberlegung wie bei der ersten Fassung der
|
||||
* Crew-Wand -- nur ist sie hier dauerhaft richtig, weil es im Treff
|
||||
* auch kuenftig nur eine Rolle gibt.
|
||||
*
|
||||
* Die Menge steht trotzdem hier: Die Anmeldung prueft gegen sie, und
|
||||
* eine Wand ohne Kacheln darf nicht heissen, dass jede Rolle
|
||||
* durchkommt. */
|
||||
const TREFF_KACHEL = new Set(["gast"]);
|
||||
|
||||
/* Als SQL-Ausdruck fuer ORDER BY. "ORDER BY rolle" waere alphabetisch
|
||||
(admin, creator, manager, scout, spicy) -- also fast genau falsch
|
||||
herum. */
|
||||
@@ -383,6 +400,15 @@ export const ROLLEN_NAME = {
|
||||
creator: "Creator",
|
||||
hand: "Rechte Hand",
|
||||
modi: "Modi",
|
||||
/* DIE COMMUNITY (11.09.2026). Der Name steht hier und NICHT in einer
|
||||
Datei, die jeder herunterlaedt -- ausgeliefert wird er nur an den,
|
||||
der ihn selbst traegt, und an die, die ihn sehen duerfen.
|
||||
|
||||
Filipe hat ausdruecklich gewaehlt, dass im Treff der volle
|
||||
Rollenname neben einem Beitrag steht ("Modi", "Rechte Hand",
|
||||
"DogFather"). Das bleibt damit vereinbar: Der Name kommt als DATEN
|
||||
mit der Antwort, nicht als Konstante im Browser. */
|
||||
gast: "Community",
|
||||
};
|
||||
|
||||
/* DIE UEBERSCHRIFT UEBER EINER PERSONENLISTE (10.09.2026).
|
||||
@@ -632,9 +658,110 @@ const PERSONEN_KACHEL = {
|
||||
const HAND_BEREICHE = [...MODI_BEREICHE, EINGANG_KACHEL, PERSONEN_KACHEL,
|
||||
ENTWICKLUNG_KACHEL, TALENTE_KACHEL];
|
||||
|
||||
|
||||
/* =====================================================================
|
||||
DIE SIEBEN BRETTER DES TREFFS (11.09.2026)
|
||||
|
||||
Filipe: "wieso nur eine kachel fuer die community ich will dass die
|
||||
mehrere kacheln haben. mit mehreren optionen."
|
||||
|
||||
Eine Kachel war zu kurz gedacht: Ein Raum, in dem Ansagen, Fragen,
|
||||
Wuensche und Clips durcheinandergehen, ist nach zwei Wochen
|
||||
unbenutzbar -- die Ansage von gestern ist weg, die Frage von heute
|
||||
geht unter.
|
||||
|
||||
SORTIERT NACH ZWECK, NICHT NACH THEMA, und in der Reihenfolge, in der
|
||||
ein Neuer liest: erst was gilt, dann was laeuft, dann das Gespraech,
|
||||
dann das Besondere.
|
||||
|
||||
Jede der sieben bedient eines der vier Dinge, die darueber
|
||||
entscheiden, ob sich jemand zugehoerig fuehlt (Zugehoerigkeit,
|
||||
Einfluss, Nutzen, geteilte Erlebnisse). Was keines bedient, ist nicht
|
||||
dabei -- deshalb gibt es hier keine Bestenliste und keine Punkte.
|
||||
|
||||
VON OBEN NACH UNTEN GELESEN SIND SIE EIN WEG: lesen -> mitreden ->
|
||||
mitgestalten -> dazugehoeren. Die letzte Kachel uebergibt an die
|
||||
Talente-Kategorie, die es schon gibt.
|
||||
|
||||
DIE KACHELN STEHEN HIER UND NICHT IN assets/js/bereiche.js -- aus
|
||||
demselben Grund wie bei den Modis: Diese Datei wird nicht
|
||||
ausgeliefert, jene bekommt jeder Besucher. */
|
||||
const TREFF_GRUPPE = {
|
||||
gruppe: "Community",
|
||||
gruppeUnter: "Der Treff \u2013 der Raum f\u00fcr die Zuschauer",
|
||||
};
|
||||
|
||||
const TREFF_BEREICHE = [
|
||||
{ ...TREFF_GRUPPE, name: "Anschlagbrett", unter: "Was gerade gilt",
|
||||
zeichen: "reports", ton: 33, ziel: "bereich.html?b=anschlag", szene: "halle" },
|
||||
{ ...TREFF_GRUPPE, name: "Was ansteht", unter: "Die n\u00e4chsten Streams und Events",
|
||||
zeichen: "kalender", ton: 31, ziel: "bereich.html?b=ansteht", szene: "skyline" },
|
||||
{ ...TREFF_GRUPPE, name: "Der Treff", unter: "Hallo sagen, fragen, loben",
|
||||
zeichen: "community", ton: 34, gross: true, ziel: "bereich.html?b=treff", szene: "lounge" },
|
||||
{ ...TREFF_GRUPPE, name: "Wunschliste", unter: "Was ihr euch w\u00fcnscht \u2013 und wer mitwill",
|
||||
zeichen: "content", ton: 30, ziel: "bereich.html?b=wunsch", szene: "portal" },
|
||||
{ ...TREFF_GRUPPE, name: "Highlights", unter: "Eure Clips und Bilder",
|
||||
zeichen: "live", ton: 29, ziel: "bereich.html?b=highlight", szene: "arena" },
|
||||
{ ...TREFF_GRUPPE, name: "Regeln & Hilfe", unter: "Wie es hier l\u00e4uft",
|
||||
zeichen: "schutz", ton: 28, ziel: "bereich.html?b=regeln", szene: "studio" },
|
||||
{ ...TREFF_GRUPPE, name: "Mitmachen", unter: "Wie du dazugeh\u00f6ren kannst",
|
||||
zeichen: "personen", ton: 32, ziel: "bereich.html?b=mitmachen", szene: "garage" },
|
||||
];
|
||||
|
||||
/* MELDUNGEN & MASSNAHMEN -- FUER DAS TEAM, NICHT FUER DIE COMMUNITY
|
||||
(11.09.2026).
|
||||
|
||||
Diese Kachel steht ausdruecklich NICHT in TREFF_BEREICHE. Die Liste
|
||||
dort ist das, was ein Mitglied sieht; sie wird dem Team nur
|
||||
angehaengt. Eine achte Kachel darin waere auch bei der Community
|
||||
gelandet -- und zwar lautlos, denn sie saehe dort aus wie die
|
||||
anderen sieben. Erst beim Klicken haette der Server 404 gesagt, und
|
||||
das sieht aus wie ein kaputtes Programm.
|
||||
|
||||
SIE GEHOERT AUCH NICHT IN TREFF_BRETTER: Sie ist kein Brett mit
|
||||
Eintraegen, sondern eine Seite. Das Ziel ist deshalb ein
|
||||
Dateiname und kein "bereich.html?b=...", und die Ableitung weiter
|
||||
unten nimmt sie von selbst nicht mit. */
|
||||
const TREFF_MODERATION_KACHEL = {
|
||||
...TREFF_GRUPPE,
|
||||
name: "Meldungen & Ma\u00dfnahmen", unter: "Was gemeldet wurde \u2013 und was daraus wurde",
|
||||
zeichen: "schutz", ton: 36, ziel: "treff-moderation.html", szene: "studio",
|
||||
};
|
||||
|
||||
/* Welche Bretter zum Treff gehoeren -- ABGELEITET aus den Kacheln, nicht
|
||||
danebengeschrieben. Eine zweite Liste waere die, die beim naechsten
|
||||
neuen Brett vergessen wird. */
|
||||
export const TREFF_BRETTER = TREFF_BEREICHE
|
||||
.map((k) => new URL("http://x/" + k.ziel).searchParams.get("b"))
|
||||
.filter(Boolean);
|
||||
|
||||
/* Wer den Treff ueberhaupt sehen darf: die Community selbst und das
|
||||
Team von Dogi. Die Agentur ausdruecklich nicht -- Creator, Scouts und
|
||||
Spicy Media haben mit den Zuschauern dieses Streams nichts zu tun. */
|
||||
export const TREFF_ROLLEN = new Set(["gast", "modi", "hand", "admin"]);
|
||||
|
||||
/* DAS TEAM SIEHT DEN TREFF MIT -- als eigene Gruppe unter seinen
|
||||
uebrigen Kacheln.
|
||||
|
||||
Filipe: "die modis rechte hand und ich also dogfather sehen die aber
|
||||
auch. die community aber nur die dann."
|
||||
|
||||
Zusammengesetzt und nicht in MODI_BEREICHE hineingeschrieben, weil
|
||||
diese Liste weiter oben steht als die Kacheln des Treffs. Eine Kopie
|
||||
dort waere die Stelle, an der beim naechsten neuen Brett eines fehlt. */
|
||||
const MODI_MIT_TREFF = [...MODI_BEREICHE, ...TREFF_BEREICHE, TREFF_MODERATION_KACHEL];
|
||||
const HAND_MIT_TREFF = [...HAND_BEREICHE, ...TREFF_BEREICHE, TREFF_MODERATION_KACHEL];
|
||||
|
||||
/* AUSSEN_ROLLEN kommt aus crew-adresse.js und wird hier nur
|
||||
weitergereicht -- die Adressregel braucht sie zuerst, und diese Datei
|
||||
importiert von dort. Eine zweite Menge waere die, die auseinanderlaeuft. */
|
||||
export { AUSSEN_ROLLEN };
|
||||
|
||||
export function bereicheFuer(person) {
|
||||
if (person?.rolle === "modi") return MODI_BEREICHE;
|
||||
if (person?.rolle === "hand") return HAND_BEREICHE;
|
||||
if (person?.rolle === "modi") return MODI_MIT_TREFF;
|
||||
if (person?.rolle === "hand") return HAND_MIT_TREFF;
|
||||
/* DIE COMMUNITY SIEHT DEN TREFF UND SONST NICHTS (11.09.2026). */
|
||||
if (person?.rolle === "gast") return TREFF_BEREICHE;
|
||||
/* AUF DER ADRESSE VON TEAM DOGI SIEHT AUCH DOGFATHER DIE KACHELN DES
|
||||
TEAMS (10.09.2026).
|
||||
|
||||
@@ -653,10 +780,27 @@ export function bereicheFuer(person) {
|
||||
Kacheln. Eine eigene Liste fuer ihn waere die, die beim naechsten
|
||||
Umbau auseinanderlaeuft -- und sie stuende gegen die Hausregel,
|
||||
ihn nicht ueber sein Team zu stellen. */
|
||||
if (person?.haus === "crew") return HAND_BEREICHE;
|
||||
return null;
|
||||
if (person?.haus === "crew") return HAND_MIT_TREFF;
|
||||
/* `null` HEISST "NIMM DIE LISTE AUS DEM BROWSER" -- und die enthaelt
|
||||
fuenfundzwanzig Kacheln des Agenturhauses. Bis zum 11.09.2026 stand
|
||||
hier nur `return null`, also bekam JEDE Rolle ohne eigenen Zweig
|
||||
diese Liste angeboten. Gefiltert wurde sie danach zwar nach
|
||||
`rollen:` an jeder Kachel, aber das ist eine zweite Schranke an
|
||||
einer anderen Stelle -- und sie lebt in einer Datei, die jeder
|
||||
herunterlaedt.
|
||||
|
||||
Jetzt bekommen nur die fuenf Rollen `null`, fuer die diese Liste
|
||||
geschrieben wurde. Alles andere bekommt eine leere Liste: keine
|
||||
Kachel, kein Weg, nichts zu filtern. */
|
||||
if (AGENTUR_LISTE.has(person?.rolle)) return null;
|
||||
return [];
|
||||
}
|
||||
|
||||
/* Die fuenf, deren Kacheln in assets/js/bereiche.js stehen. Nicht zu
|
||||
verwechseln mit AGENTUR_ROLLEN (dort fehlt `admin`, weil es dabei um
|
||||
die Sichtbarkeit von Zeilen geht und nicht um Kacheln). */
|
||||
const AGENTUR_LISTE = new Set(["spicy", "admin", "manager", "scout", "creator"]);
|
||||
|
||||
/* WELCHE BEREICHSSEITEN EIN MODI OEFFNEN DARF -- abgeleitet aus seinen
|
||||
eigenen Kacheln und nicht danebengeschrieben.
|
||||
|
||||
@@ -670,7 +814,7 @@ export function bereicheFuer(person) {
|
||||
-- ohne diese Einschraenkung kaeme er ueber die Adresszeile auch in
|
||||
die Agentur-Ablage, die ihn nichts angeht. */
|
||||
export const MODI_BEREICHE_ERLAUBT = new Set(
|
||||
HAND_BEREICHE
|
||||
HAND_MIT_TREFF
|
||||
.map((k) => (/bereich\.html\?b=([a-z]+)/.exec(k.ziel) || [])[1])
|
||||
.filter(Boolean));
|
||||
|
||||
@@ -942,8 +1086,51 @@ const ZUSATZ_BEREICHE = {
|
||||
/* Eine eigene Gruppe, direkt hinter Team Dogi: Was ueber Menschen
|
||||
aufgeschrieben wird, steht nicht zwischen den taeglichen
|
||||
Werkzeugen. */
|
||||
/* WER SIEHT WAS (11.09.2026).
|
||||
|
||||
Filipe: "egal welche Rolle oder Person hinzugefuegt wird, soll
|
||||
immer nur das sehen, was ich erlaube."
|
||||
|
||||
Dazu gehoert, dass er es nachsehen kann. Die Seite rechnet die
|
||||
Uebersicht aus derselben Tafel, aus der die Schranke ihre
|
||||
Entscheidung holt (rechte.js) -- eine von Hand gepflegte
|
||||
Uebersicht waere eine zweite Wahrheit, und ausgerechnet die, der
|
||||
man glaubt.
|
||||
|
||||
IN "Team & System" UND NICHT BEI DEN PERSONEN: Es geht nicht um
|
||||
Menschen, sondern um das Haus. */
|
||||
{
|
||||
...TEAM_GRUPPE,
|
||||
name: "Wer sieht was", unter: "Jede Rolle, jede Seite – ausgerechnet, nicht notiert",
|
||||
/* EIN EIGENER TON, UND ZWAR EIN NEUER (11.09.2026).
|
||||
|
||||
Erster Anlauf war Ton 26 -- das waere "Entwicklung" ein zweites
|
||||
Mal gewesen. Zweiter Anlauf war Ton 22, weil er frei AUSSAH:
|
||||
Ich hatte die Toene ueber bereicheFuer() gezaehlt, und das
|
||||
liefert die Zusatzkacheln gar nicht mit. Beide Male haette
|
||||
sich diese Kachel eine Farbe mit einer anderen geteilt, und
|
||||
beide Male haette ich es fuer geprueft gehalten.
|
||||
|
||||
Gefunden hat es pruef-start-ansicht, die ueber die ECHTE
|
||||
Ansicht zaehlt: "34 Farben auf 35 Kacheln". Der 35. Ton ist
|
||||
jetzt gemessen (start.css, Abstand 31,4 in Lab zum naechsten
|
||||
Nachbarn) -- und nicht mehr aus einer Luecke geraten. */
|
||||
zeichen: "schutz", ton: 35, ziel: "rechte.html", szene: "studio",
|
||||
},
|
||||
{ ...ENTWICKLUNG_KACHEL, gruppeNach: TEAM_GRUPPE.gruppe },
|
||||
{ ...TALENTE_KACHEL, gruppeNach: TEAM_GRUPPE.gruppe },
|
||||
/* DER TREFF -- GANZ UNTEN, UND ZWAR ABSICHTLICH.
|
||||
|
||||
Filipe zu den beiden Gruppen darueber: "das sind private
|
||||
informationen von meiner community." Dasselbe gilt hier, nur
|
||||
noch deutlicher: Im Treff stehen Namen und Saetze von Leuten,
|
||||
die nichts mit der Agentur zu tun haben.
|
||||
|
||||
Ohne `gruppeNach` haengt eine Gruppe hinten an -- genau richtig.
|
||||
Wer sich den Bildschirm teilt oder jemandem ueber die Schulter
|
||||
schauen laesst, hat sie dann nicht als Erstes im Blick. */
|
||||
...TREFF_BEREICHE,
|
||||
TREFF_MODERATION_KACHEL,
|
||||
],
|
||||
};
|
||||
|
||||
@@ -2086,6 +2273,11 @@ function umstellungen(d) {
|
||||
["spicy", "admin", "manager", "scout", "creator", "modi"], jetztStempel);
|
||||
checkListeErweitern(d, "personen", "rolle", "hand",
|
||||
["spicy", "admin", "manager", "scout", "creator", "modi", "hand"], jetztStempel);
|
||||
/* DIE COMMUNITY (11.09.2026). Dieselbe Umstellung wie die drei
|
||||
darueber: Sicherung vorher, Neubau, Zaehlung innerhalb der
|
||||
Transaktion, Indizes zurueck. */
|
||||
checkListeErweitern(d, "personen", "rolle", "gast",
|
||||
["spicy", "admin", "manager", "scout", "creator", "modi", "hand", "gast"], jetztStempel);
|
||||
|
||||
/* DER BEREICH FUER DAS IDEEN-BOARD (10.09.2026, Kapitel 5.6).
|
||||
|
||||
@@ -2118,6 +2310,18 @@ function umstellungen(d) {
|
||||
checkListeErweitern(d, "eintraege", "bereich", "talente",
|
||||
["live", "content", "technik", "community", "schutz", "agentur", "ideen",
|
||||
"angebote", "rueckmeldung", "entwicklung", "talente"], jetztStempel);
|
||||
/* DIE SIEBEN BRETTER DES TREFFS (11.09.2026).
|
||||
|
||||
EINE Umstellung fuer alle sieben, nicht sieben einzelne. Jeder
|
||||
Aufruf baut die Tabelle neu auf -- siebenmal hintereinander waeren
|
||||
sieben Sicherungen, sieben Neubauten und sieben Gelegenheiten, dass
|
||||
etwas schiefgeht. Der Marker ist "treff"; ist der schon in der
|
||||
CHECK-Liste, passiert gar nichts. */
|
||||
checkListeErweitern(d, "eintraege", "bereich", "treff",
|
||||
["live", "content", "technik", "community", "schutz", "agentur", "ideen",
|
||||
"angebote", "rueckmeldung", "entwicklung", "talente",
|
||||
"anschlag", "ansteht", "treff", "wunsch", "highlight", "regeln", "mitmachen"],
|
||||
jetztStempel);
|
||||
|
||||
checkListeErweitern(d, "eintraege", "status", "angenommen",
|
||||
["offen", "erledigt", "angenommen", "abgelehnt"], jetztStempel);
|
||||
@@ -2644,6 +2848,10 @@ export function db() {
|
||||
geaendert TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_eintraege_bereich ON eintraege (bereich, creator_id);
|
||||
/* Die Tabellen des Treffs folgen weiter unten, gleich nachdem
|
||||
dieser Bauplan durch ist -- sie haengen an eintraege und
|
||||
personen und koennen deshalb nicht davor stehen. Ihr SQL steht
|
||||
in treff-tabellen.js, samt Begruendung je Tabelle. */
|
||||
|
||||
/* Wer darf welche Datei sehen. Bisher hatte eine Datei genau
|
||||
EINEN Bereich (dateien.creator_id) -- damit liess sich eine
|
||||
@@ -2889,6 +3097,11 @@ export function db() {
|
||||
CREATE INDEX IF NOT EXISTS idx_versuche_ip ON versuche (ip, zeitpunkt);
|
||||
CREATE INDEX IF NOT EXISTS idx_sitzungen_gueltig ON sitzungen (gueltig_bis);
|
||||
`);
|
||||
/* Die Tabellen des Treffs. Eigene Datei, weil workspace-treff.js
|
||||
aus DIESER Datei importiert -- ein Importkreis waere die Folge,
|
||||
und ueber einen Kreis kommen Konstanten als undefined an, ohne
|
||||
dass irgendwo ein Fehler erscheint. */
|
||||
treffTabellen(d);
|
||||
umstellungen(d);
|
||||
_db = d;
|
||||
return d;
|
||||
@@ -3074,7 +3287,34 @@ export function sitzungLesen(req) {
|
||||
nicht, was er darf -- genau wie die Sicht eines anderen
|
||||
(sichtPerson) das auch nicht tut. Wer hier etwas anderes einbaut,
|
||||
macht aus einer Anzeigefrage eine Rechtefrage. */
|
||||
const haus = istCrewAdresse(req.get?.("host")) ? "crew" : "agentur";
|
||||
const haus = istCrewAdresse(req.get?.("host")) ? "crew"
|
||||
: istTreffAdresse(req.get?.("host")) ? "treff" : "agentur";
|
||||
|
||||
/* DER AUSSCHLUSS WIRKT HIER -- an derselben Stelle wie alles andere
|
||||
(11.09.2026).
|
||||
|
||||
Ein Ausschluss aus dem Treff ist nicht "darf nichts mehr
|
||||
schreiben", sondern "ist nicht mehr da". Stuende die Pruefung an
|
||||
den Schreibwegen, koennte ein Ausgeschlossener weiter mitlesen --
|
||||
und genau das ist bei Belaestigung der Teil, der weh tut.
|
||||
|
||||
AN DIESER STELLE UND NICHT NUR BEIM ANMELDEN: Wer schon
|
||||
angemeldet ist, hat ein Sitzungsplaetzchen, das Tage gilt. Eine
|
||||
Pruefung beim Anmelden haette ihn bis zum Ablauf drin gelassen --
|
||||
also ausgerechnet in den Stunden, in denen die Entscheidung
|
||||
gefallen ist.
|
||||
|
||||
NUR FUER AUSSENROLLEN: Eine Massnahme gegen jemanden aus dem Team
|
||||
gibt es nicht (siehe /api/treff/massnahme), und ein Fehler hier
|
||||
duerfte niemals das Team aussperren. Die Frage wird deshalb gar
|
||||
nicht erst gestellt.
|
||||
|
||||
WIRFT DIE FRAGE, greift der catch weiter unten und die Antwort
|
||||
ist "null" -- also "nicht angemeldet". Das ist die sichere
|
||||
Richtung: Wer nicht nachsehen kann, laesst niemanden herein. */
|
||||
if (AUSSEN_ROLLEN.has(reihe.rolle) && treffSperreLesen(db(), reihe.id)?.art === "ausschluss") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { id: reihe.id, name: reihe.name, rolle: reihe.rolle, haus };
|
||||
} catch { return null; }
|
||||
@@ -3131,7 +3371,15 @@ export const workspaceRouter = express.Router();
|
||||
/workspace/ -- und von dort wieder hierher. Der Browser bricht das
|
||||
nach ein paar Runden mit "zu viele Weiterleitungen" ab, und man
|
||||
sucht den Fehler ueberall, nur nicht in dieser Zeile. */
|
||||
const OFFEN = new Set(["/workspace/index.html", "/workspace/crew-index.html"]);
|
||||
/* SIE WIRD NICHT ZWEIMAL GESCHRIEBEN (11.09.2026).
|
||||
Bis heute stand hier dieselbe Liste ein zweites Mal, von Hand. Sie
|
||||
stimmte -- weil sie zwei Eintraege hatte und beide am selben Tag
|
||||
entstanden. Beim dritten (der Wand des Treffs) waere genau das
|
||||
passiert, wogegen die Rechtetafel gebaut wurde: Wer nur hier
|
||||
nachtraegt, bekommt eine Wand, die die Pruefung als Luecke meldet;
|
||||
wer nur dort nachtraegt, bekommt eine Endlosweiterleitung. Beides
|
||||
faellt erst im Browser auf. Jetzt gibt es die Liste einmal. */
|
||||
const OFFEN = new Set(OHNE_ANMELDUNG);
|
||||
|
||||
/** Der Pfad, wie ihn die Schranke ansehen muss.
|
||||
*
|
||||
@@ -3241,6 +3489,58 @@ function stillerZugang(code) {
|
||||
}
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
WER SIEHT WAS — die Tafel, wie sie wirklich ist (11.09.2026)
|
||||
=====================================================================
|
||||
|
||||
Filipe: *"egal welche Rolle oder Person hinzugefuegt wird, soll immer
|
||||
nur das sehen, was ich erlaube."*
|
||||
|
||||
Dazu gehoert, dass er es NACHSEHEN kann. Eine Regel, die man nur
|
||||
glauben kann, ist eine Zusage; eine, die man ansehen kann, ist eine
|
||||
Auskunft.
|
||||
|
||||
DIESE ANTWORT WIRD GERECHNET, NICHT GEPFLEGT. Sie kommt aus
|
||||
derselben Tafel, aus der auch die Schranke ihre Entscheidung holt
|
||||
(rechte.js). Eine von Hand geschriebene Uebersicht waere eine zweite
|
||||
Wahrheit — und ausgerechnet die, der man glaubt, weil sie wie eine
|
||||
Auskunft aussieht. Sie wuerde altern, und man wuerde es ihr nicht
|
||||
ansehen.
|
||||
|
||||
NUR DogFather. Nicht aus Geheimniskraemerei, sondern weil diese
|
||||
Antwort eine vollstaendige Karte des Hauses ist: jede Seite, jede
|
||||
Rolle, und wo die Luecken sind. Wer sie hat, weiss, wo er es
|
||||
versuchen muesste.
|
||||
===================================================================== */
|
||||
workspaceRouter.get("/workspace/api/rechte/tafel", (req, res) => {
|
||||
const person = sitzungLesen(req);
|
||||
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||||
if (person.rolle !== "admin") return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
|
||||
const seiten = Object.entries(SEITEN).map(([pfad, rollen]) => ({
|
||||
pfad,
|
||||
datei: pfad.replace("/workspace/", ""),
|
||||
rollen: [...rollen],
|
||||
})).sort((a, b) => a.datei.localeCompare(b.datei, "de"));
|
||||
|
||||
res.json({
|
||||
rollen: ALLE_ROLLEN.map((r) => ({
|
||||
schluessel: r,
|
||||
name: ROLLEN_NAME[r] ?? r,
|
||||
/* Wie viele der Seiten diese Rolle oeffnen darf. Die Zahl ist
|
||||
die eigentliche Antwort auf "sieht die neue Rolle zu viel?" */
|
||||
seiten: seiten.filter((s) => s.rollen.includes(r)).length,
|
||||
})),
|
||||
seiten,
|
||||
ohne_anmeldung: [...OHNE_ANMELDUNG],
|
||||
/* Die Zahl, die den Unterschied macht: Wie viele Seiten stehen
|
||||
fuer NIEMANDEN offen, ohne dass jemand das entschieden haette?
|
||||
Seit dem 11.09. ist sie immer 0 — es gibt keinen Zustand mehr,
|
||||
der "alle angemeldeten" bedeutet, ohne dass es dasteht. */
|
||||
ohne_eintrag: 0,
|
||||
});
|
||||
});
|
||||
|
||||
workspaceRouter.post("/workspace/api/anmelden", (req, res) => {
|
||||
const ip = echteIp(req);
|
||||
try {
|
||||
@@ -3313,9 +3613,25 @@ workspaceRouter.post("/workspace/api/anmelden", (req, res) => {
|
||||
zwar auch fuer die neue Adresse, denn die Sperre haengt an der
|
||||
IP, nicht am Hostnamen. Das ist der Preis der Stille; er faellt
|
||||
nur an, wenn jemand die alte Adresse mehrfach probiert. */
|
||||
const still = codeBrauchbar && !istOhneModiAdresse(req.get("host"))
|
||||
? stillerZugang(code)
|
||||
: null;
|
||||
/* UND AUCH NICHT AUF DER WAND DES TREFFS (11.09.2026).
|
||||
|
||||
Hier stand `!istOhneModiAdresse(...)` -- also "ueberall ausser auf
|
||||
den alten Adressen". Als dritte Wand dazukam, hiess das
|
||||
stillschweigend auch: dort. Ein Modi kam damit ueber den
|
||||
verborgenen Weg in den Treff herein, obwohl die Kachelliste dort
|
||||
nur `gast` kennt und die Adressregel ihn abweist.
|
||||
|
||||
Gefunden hat das nicht das Lesen, sondern eine Zeile in
|
||||
pruef-treff, die das GEGENTEIL behauptete und rot wurde:
|
||||
"auf treff. kommt sonst niemand herein -- auch DogFather nicht
|
||||
(401/200)". DogFather wurde abgewiesen, der Modi nicht.
|
||||
|
||||
Die Lehre ist dieselbe wie bei `null` in der Rechtetafel: Eine
|
||||
Bedingung, die durch AUSSCHLIESSEN formuliert ist, nimmt jede
|
||||
neue Moeglichkeit automatisch mit auf. Jetzt steht dort, WO der
|
||||
Weg gilt, statt wo er nicht gilt. */
|
||||
const stilleWand = istCrewAdresse(req.get("host")) || istPruefAdresse(req.get("host"));
|
||||
const still = codeBrauchbar && stilleWand ? stillerZugang(code) : null;
|
||||
if (still) {
|
||||
db().prepare("DELETE FROM versuche WHERE ip = ?").run(ip);
|
||||
db().prepare("UPDATE personen SET letzter_login = ? WHERE id = ?").run(jetzt(), still.id);
|
||||
@@ -3360,7 +3676,9 @@ workspaceRouter.post("/workspace/api/anmelden", (req, res) => {
|
||||
Wand gerade dran ist. Ein Manager, der crew. erraet und seinen
|
||||
Code eintippt, faellt dadurch in genau dieselbe Antwort wie
|
||||
jemand mit einer erfundenen Rolle. */
|
||||
const kachelSatz = istCrewAdresse(req.get("host")) ? CREW_KACHEL : ROLLEN_KACHEL;
|
||||
const kachelSatz = istTreffAdresse(req.get("host")) ? TREFF_KACHEL
|
||||
: istCrewAdresse(req.get("host")) ? CREW_KACHEL
|
||||
: ROLLEN_KACHEL;
|
||||
|
||||
if (!kachelSatz.has(rolle) || !codeBrauchbar) {
|
||||
db().prepare("INSERT INTO versuche (ip, zeitpunkt) VALUES (?,?)").run(ip, jetzt());
|
||||
@@ -3417,6 +3735,33 @@ workspaceRouter.post("/workspace/api/anmelden", (req, res) => {
|
||||
return res.status(401).json({ fehler: "ungueltig" });
|
||||
}
|
||||
|
||||
/* AUSGESCHLOSSEN HEISST: DER CODE STIMMT NICHT MEHR (11.09.2026).
|
||||
|
||||
sitzungLesen() weist eine ausgeschlossene Person ohnehin ab --
|
||||
jede Anfrage bekommt 401. Ohne diese Zeile hier waere die
|
||||
Anmeldung aber TROTZDEM gelungen: Plaetzchen gesetzt, "willkommen"
|
||||
geantwortet, Weiterleitung auf die Startseite -- und dort dann
|
||||
eine Seite, auf der nichts geht. Das sieht nicht aus wie eine
|
||||
Entscheidung, sondern wie ein kaputtes Programm, und man
|
||||
probiert es zehnmal.
|
||||
|
||||
DIESELBE ANTWORT WIE BEI EINEM FALSCHEN CODE, und zwar
|
||||
wortgleich: Wer ausgeschlossen wurde, hat den Grund bereits
|
||||
bekommen (die Massnahme verlangt einen). Die Anmeldeseite muss
|
||||
ihn nicht wiederholen -- und sie darf niemandem, der einen
|
||||
fremden Code probiert, verraten, dass es diesen Zugang gibt.
|
||||
|
||||
NUR FUER AUSSENROLLEN. Gegen jemanden aus dem Team gibt es diese
|
||||
Massnahme gar nicht (siehe /api/treff/massnahme); die Frage
|
||||
wird deshalb nicht gestellt. Ein Fehler hier duerfte niemals
|
||||
das Team aussperren. */
|
||||
if (AUSSEN_ROLLEN.has(gefunden.rolle)
|
||||
&& treffSperreLesen(db(), gefunden.id)?.art === "ausschluss") {
|
||||
db().prepare("INSERT INTO versuche (ip, zeitpunkt) VALUES (?,?)").run(ip, jetzt());
|
||||
protokolliere("anmeldung_gesperrt_treff", { personId: gefunden.id, rolle, ip });
|
||||
return res.status(401).json({ fehler: "ungueltig" });
|
||||
}
|
||||
|
||||
db().prepare("DELETE FROM versuche WHERE ip = ?").run(ip);
|
||||
db().prepare("UPDATE personen SET letzter_login = ? WHERE id = ?").run(jetzt(), gefunden.id);
|
||||
sitzungSetzen(res, gefunden, req);
|
||||
@@ -4593,10 +4938,43 @@ const nurCreatorIds = (ids) => {
|
||||
} catch { return ids; }
|
||||
};
|
||||
|
||||
export const sichtbareCreatorIds = (person) => nurCreatorIds(nurHaus(ohneVerborgene(sichtbareCreatorIdsRoh(person), person), person));
|
||||
export const sichtbarePersonenIds = (person) => nurHaus(ohneVerborgene(sichtbarePersonenIdsRoh(person), person), person);
|
||||
export const betreuerIds = (person) => nurHaus(ohneVerborgene(betreuerIdsRoh(person), person), person);
|
||||
export const einladbareIds = (person) => nurHaus(ohneVerborgene(einladbareIdsRoh(person), person), person);
|
||||
/* WER VON AUSSEN KOMMT, SIEHT NIEMANDEN (11.09.2026).
|
||||
|
||||
Filipe: "egal welche rolle oder person hinzugefuegt wird soll immer
|
||||
nur das sehen wass ich erlaube."
|
||||
|
||||
GEFUNDEN, NICHT UEBERLEGT. Eine Zeile in pruef-treff behauptete
|
||||
"Personen: nichts" und wurde rot. Nachgemessen bekam ein Gast:
|
||||
|
||||
{"personen":[{"id":1,"name":"Filipe","rolle":"admin", ...},
|
||||
{"id":5,"name":"Kessi","rolle":"gast", ...}]}
|
||||
|
||||
Also DogFathers Namen und seine Rolle. Nicht dramatisch -- ein
|
||||
Streamer ist kein Geheimnis -- aber es war eine Auskunft, die
|
||||
niemand erlaubt hatte, und genau das ist der Punkt. Die Liste kam
|
||||
zustande, weil `sichtbarePersonenIdsRoh` fuer jede Rolle DogFather
|
||||
mitgibt (er ist das Gegenueber von allem) und die vier Umhuellungen
|
||||
danach nichts daran aendern.
|
||||
|
||||
`nurEigene` steht deshalb GANZ AUSSEN -- nach allen anderen. Wer
|
||||
innen ansetzt, muss darauf vertrauen, dass keine spaetere Huelle
|
||||
wieder etwas hinzufuegt; aussen gilt es unabhaengig davon, was
|
||||
innen passiert. Und es gilt fuer alle vier Listen gleich, statt
|
||||
viermal einzeln.
|
||||
|
||||
`null` heisst bei diesen Funktionen "alle" -- deshalb wird es hier
|
||||
ausdruecklich zu einer Liste mit genau einem Eintrag. Ein `null`
|
||||
durchzureichen hiesse fuer eine Aussenrolle das Gegenteil von dem,
|
||||
was hier steht. */
|
||||
function nurEigene(ids, person) {
|
||||
if (!AUSSEN_ROLLEN.has(person?.rolle)) return ids;
|
||||
return person?.id ? [person.id] : [];
|
||||
}
|
||||
|
||||
export const sichtbareCreatorIds = (person) => nurEigene(nurCreatorIds(nurHaus(ohneVerborgene(sichtbareCreatorIdsRoh(person), person), person)), person);
|
||||
export const sichtbarePersonenIds = (person) => nurEigene(nurHaus(ohneVerborgene(sichtbarePersonenIdsRoh(person), person), person), person);
|
||||
export const betreuerIds = (person) => nurEigene(nurHaus(ohneVerborgene(betreuerIdsRoh(person), person), person), person);
|
||||
export const einladbareIds = (person) => nurEigene(nurHaus(ohneVerborgene(einladbareIdsRoh(person), person), person), person);
|
||||
/* SICH SELBST NICHT -- UND ZWAR AUCH DANN NICHT, WENN DIE LISTE
|
||||
AUSGESCHRIEBEN WIRD (10.09.2026).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user