B9: In einen Kategorie-Kanal gehoert Team Dogi -- und alle davon
VanVans Befund, von Filipe weitergegeben:
* "Patrick und BananaStift (stifti) aus der Erwaehnen-Liste
entfernen -- sie stehen dort als SCOUT."
* "Die Modis zum Erwaehnen hinzufuegen."
* "Die Modis sehen den Chat Moderation auch gar nicht."
AN DER LIVE-DATENBANK NACHGEMESSEN, bevor etwas gebaut wurde:
Raum 3 kanal Chat-Moderation admin, hand, SCOUT, SCOUT, linke
Raum 7 kanal Der Treff admin, hand, 4x modi, linke, 3x gast
Raum 8 gruppe Dogi und Modis admin, hand, 4x modi, linke
Raum 9 gruppe Abmeldungen admin, hand, 4x modi, linke
Damit waren alle drei Punkte EIN Befund. Die Erwaehnen-Liste im
Browser zeigt genau `offen.teilnehmer`, also die Teilnehmer des Raums
(teilnehmerVon) -- eine zweite Quelle gibt es nicht. Standen dort zwei
Scouts und kein Modi, dann schlug sie Scouts vor, und die Modis sahen
den Kanal gar nicht. An der Erwaehnung selbst war nichts kaputt.
DIE REGEL
Ein Kategorie-Kanal gehoert Team Dogi: admin + TEAM_DOGI_ROLLEN
(hand, linke, modi), abgeleitet aus der einen Liste des Hauses. Der
Abgleich beim Blick in die Gespraechsliste heilt jetzt in BEIDE
Richtungen:
hinein jeder aktive Mensch aus Team Dogi, der im Raum NOCH NIE eine
Zeile hatte
hinaus jeder aktive Teilnehmer, dessen Rolle nicht dazugehoert
(raus_am, kein DELETE -- der Verlauf bleibt lesbar)
"NOCH NIE EINE ZEILE" ist der wichtige Teil: Wer ueber die
Mitglieder-Route bewusst herausgenommen wurde, hat eine Zeile mit
raus_am und wird NICHT zurueckgeholt. Ein Abgleich, der eine
Entscheidung von Hand beim naechsten Seitenaufruf ueberschreibt,
macht die Route wertlos -- dieselbe Ueberlegung wie bei
treffAngleichen(). Der Treff bleibt ausgenommen: Dort gehoert die
Community dazu.
Dazu dieselbe Schranke an beiden Schreibwegen (Kanal anlegen und
umbesetzen). `darfSchreibenMit` beantwortet eine ANDERE Frage -- "darf
ich diesen Menschen ueberhaupt anschreiben" -- und sagt bei einem
Scout zu Recht ja. Genau diese Luecke hat die zwei Scouts in den
Moderations-Kanal gebracht.
WARUM NICHT "NUR DIE ZUSTAENDIGEN"
Weil es die Zuordnung nicht gibt: `kategorienFuer` gibt JEDEM aus Team
Dogi ALLE Kategorien, eine Tabelle Person -> Kategorie existiert
nirgends. Eine Regel "nur die Zustaendigen" waere eine Liste, die
niemand pflegt. Wer einen engeren Kanal will, nimmt jemanden heraus --
und das haelt.
PRUEFUNGEN
pruef-kanal-besetzung.mjs (NEU, 16/0): baut die gemeldete Lage nach
(Scout drin, kein Modi), laesst den Abgleich darueberlaufen und
misst beide Richtungen. Vier Gegenproben: der von Hand Entfernte
bleibt draussen; ein Scout kommt auch ueber die Mitglieder-Route
nicht hinein; der Gast bleibt im Treff; und ein wieder
hereingeschmuggelter Scout fliegt beim naechsten Abgleich erneut
hinaus -- die Regel wirkt dauerhaft, nicht einmalig.
pruef-chat-kanaele.mjs (79 mit 3 Fehlern -> 81/0): Die Zeile "wer
nicht drin ist, sieht ihn nicht" hat den alten Zuschnitt
festgeschrieben -- sie prueft jetzt die schaerfere Grenze: wer
HERAUSGENOMMEN wurde, bleibt draussen, auch nach dem naechsten
Abgleich. Dieser Weg war bis heute ungeprueft.
Mitgelaufen und gruen: pruef-chat, pruef-treffchat (110/0).
Vor dem Ausliefern gesichert: workspace-vor-kanalregel-20260922-231409.db
(946 KB, integrity_check ok, 17 Personen, 37 Teilnehmerzeilen,
111 Nachrichten).
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+125
-12
@@ -44,7 +44,7 @@ import { mkdirSync, writeFileSync, createReadStream, statSync, unlinkSync } from
|
||||
import {
|
||||
db, protokolliere, echteIp, sitzungLesen, istDogFather,
|
||||
schreibbareIds, darfSchreibenMit, ROLLEN_SORTIERUNG, DATEN_ORDNER,
|
||||
fuehrtTeamDogi, MODI_KATEGORIEN, ROLLEN_GRUPPE,
|
||||
fuehrtTeamDogi, MODI_KATEGORIEN, ROLLEN_GRUPPE, TEAM_DOGI_ROLLEN,
|
||||
} from "./workspace.js";
|
||||
|
||||
import { erwaehnteIds } from "./chat-erwaehnung.js";
|
||||
@@ -548,22 +548,104 @@ function darfAnheften(person, raumId) {
|
||||
* dazukommt, hat die vierhundert Nachrichten von vorher nicht
|
||||
* verpasst -- eine rote Vierhundert waere keine Auskunft, sondern
|
||||
* Rauschen, und man schaltet sie ab statt sie zu lesen. */
|
||||
/* =====================================================================
|
||||
WER IN EINEN KATEGORIE-KANAL GEHOERT (22.09.2026)
|
||||
=====================================================================
|
||||
|
||||
VanVans Befund, von Filipe weitergegeben:
|
||||
|
||||
* "Patrick und BananaStift (stifti) aus der Erwaehnen-Liste
|
||||
entfernen -- sie stehen dort als SCOUT und gehoeren nicht in
|
||||
diesen Chat."
|
||||
* "Die Modis zum Erwaehnen hinzufuegen."
|
||||
* "Die Modis sehen den Chat Moderation auch gar nicht."
|
||||
|
||||
AN DER LIVE-DATENBANK NACHGEMESSEN, bevor irgendetwas gebaut wurde:
|
||||
|
||||
Raum 3 kanal Chat-Moderation admin, hand, SCOUT, SCOUT, linke
|
||||
Raum 7 kanal Der Treff admin, hand, 4x modi, linke, 3x gast
|
||||
Raum 8 gruppe Dogi und Modis admin, hand, 4x modi, linke
|
||||
Raum 9 gruppe Abmeldungen admin, hand, 4x modi, linke
|
||||
|
||||
Damit waren alle drei Punkte EIN Befund: Die Erwaehnen-Liste zeigt
|
||||
genau die Teilnehmer des Raums (teilnehmerVon). Im
|
||||
Moderations-Kanal standen zwei Scouts und kein einziger Modi --
|
||||
also schlug die Liste Scouts vor, und die Modis sahen den Kanal gar
|
||||
nicht. Nichts davon war ein Fehler der Erwaehnung.
|
||||
|
||||
DIE REGEL: Ein Kategorie-Kanal gehoert Team Dogi. Scouts, Manager
|
||||
und Creator arbeiten im anderen Haus; sie haben in einem Kanal
|
||||
ueber Chat-Moderation nichts verloren -- und dass sie dort standen,
|
||||
war kein Rechtefehler, sondern eine Besetzung von Hand, die nie
|
||||
jemand nachgeprueft hat.
|
||||
|
||||
SELBSTHEILEND, IN BEIDE RICHTUNGEN -- und das ist der Unterschied
|
||||
zum alten Abgleich, der nur die Teamfuehrung nachtrug:
|
||||
|
||||
hinein jeder aktive Mensch aus Team Dogi, der in diesem Raum
|
||||
NOCH NIE eine Zeile hatte
|
||||
hinaus jeder aktive Teilnehmer, dessen Rolle nicht dazugehoert
|
||||
(raus_am, kein DELETE -- der Verlauf bleibt lesbar)
|
||||
|
||||
"NOCH NIE EINE ZEILE" IST DER WICHTIGE TEIL. Wer ueber die
|
||||
Mitglieder-Route bewusst herausgenommen wurde, hat eine Zeile mit
|
||||
`raus_am` -- und wird deshalb NICHT wieder hineingezogen. Ein
|
||||
Abgleich, der eine Entscheidung von Hand beim naechsten Seitenaufruf
|
||||
ueberschreibt, macht die Mitglieder-Route wertlos; dieselbe
|
||||
Ueberlegung steht schon bei treffAngleichen().
|
||||
|
||||
DER TREFF BLEIBT AUSSEN VOR. Er ist zwar technisch ein 'kanal',
|
||||
aber kein Thema des Teams -- dort gehoert die Community dazu, und
|
||||
TREFF_ROLLEN sagt das an seiner eigenen Stelle.
|
||||
===================================================================== */
|
||||
|
||||
/** Wer in einen Kategorie-Kanal gehoert.
|
||||
*
|
||||
* ABGELEITET, NICHT ABGESCHRIEBEN: TEAM_DOGI_ROLLEN ist die eine
|
||||
* Liste des Hauses (hand, linke, modi); DogFather kommt dazu, weil er
|
||||
* zum Team gehoert -- er steht nicht darueber, er ist dabei. Kaeme
|
||||
* morgen eine weitere Team-Rolle, waere sie hier von selbst dabei. */
|
||||
export const KANAL_ROLLEN = ["admin", ...TEAM_DOGI_ROLLEN];
|
||||
|
||||
function kanaeleAngleichen(person) {
|
||||
if (!fuehrtTeamDogi(person)) return 0;
|
||||
try {
|
||||
const d = db();
|
||||
const offen = d.prepare(`
|
||||
SELECT r.id FROM chat_raeume r
|
||||
WHERE r.art = 'kanal'
|
||||
AND NOT EXISTS (SELECT 1 FROM chat_teilnehmer t
|
||||
WHERE t.raum_id = r.id AND t.person_id = ?)`).all(person.id);
|
||||
if (!offen.length) return 0;
|
||||
const rein = d.prepare(`
|
||||
INSERT INTO chat_teilnehmer (raum_id, person_id, seit, gelesen_bis, leitung)
|
||||
SELECT ?, ?, ?, COALESCE((SELECT MAX(id) FROM chat_nachrichten WHERE raum_id = ?), 0), 1`);
|
||||
const n = jetzt();
|
||||
for (const r of offen) rein.run(r.id, person.id, n, r.id);
|
||||
return offen.length;
|
||||
const platz = KANAL_ROLLEN.map(() => "?").join(", ");
|
||||
|
||||
/* 1. WER FEHLT, KOMMT HINEIN. `gelesen_bis` auf den letzten Stand:
|
||||
Wer heute dazukommt, hat nichts verpasst -- sonst staende beim
|
||||
ersten Oeffnen eine rote Zahl ueber allem, was das Team je
|
||||
geschrieben hat. Dieselbe Ueberlegung wie in der
|
||||
Mitglieder-Route. */
|
||||
const dazu = d.prepare(`
|
||||
INSERT INTO chat_teilnehmer (raum_id, person_id, seit, gelesen_bis, leitung)
|
||||
SELECT r.id, p.id, ?,
|
||||
COALESCE((SELECT MAX(id) FROM chat_nachrichten WHERE raum_id = r.id), 0),
|
||||
CASE WHEN p.rolle IN ('admin','hand') THEN 1 ELSE 0 END
|
||||
FROM chat_raeume r
|
||||
JOIN personen p ON p.aktiv = 1 AND p.rolle IN (${platz})
|
||||
WHERE r.art = 'kanal' AND COALESCE(r.kategorie, '') <> ?
|
||||
AND NOT EXISTS (SELECT 1 FROM chat_teilnehmer t
|
||||
WHERE t.raum_id = r.id AND t.person_id = p.id)`)
|
||||
.run(n, ...KANAL_ROLLEN, TREFF_KATEGORIE).changes;
|
||||
|
||||
/* 2. WER NICHT DAZUGEHOERT, GEHT HINAUS. Nicht geloescht: Was er
|
||||
geschrieben hat, bleibt lesbar, und niemand verschwindet
|
||||
rueckwirkend aus der Geschichte. */
|
||||
const raus = d.prepare(`
|
||||
UPDATE chat_teilnehmer SET raus_am = ?
|
||||
WHERE raus_am IS NULL
|
||||
AND raum_id IN (SELECT id FROM chat_raeume
|
||||
WHERE art = 'kanal' AND COALESCE(kategorie, '') <> ?)
|
||||
AND person_id IN (SELECT id FROM personen WHERE rolle NOT IN (${platz}))`)
|
||||
.run(n, TREFF_KATEGORIE, ...KANAL_ROLLEN).changes;
|
||||
|
||||
if (dazu || raus) {
|
||||
console.log(`[chat] Kanal-Abgleich: +${dazu} / -${raus}`);
|
||||
}
|
||||
return dazu + raus;
|
||||
} catch (fehler) {
|
||||
/* Ein Fehler hier darf die Gespraechsliste nicht kosten. Der
|
||||
Zugriff selbst haengt ohnehin nicht daran -- istDrin() laesst die
|
||||
@@ -573,6 +655,19 @@ function kanaeleAngleichen(person) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gehoert diese Person in einen Kategorie-Kanal?
|
||||
*
|
||||
* STEHT AN EINER STELLE, weil drei danach fragen: der Abgleich oben,
|
||||
* das Anlegen eines Kanals und das Umbesetzen. Drei Fassungen waeren
|
||||
* die, bei der die dritte beim naechsten Umbau vergessen wird -- und
|
||||
* genau diese dritte Luecke hat VanVan gefunden. */
|
||||
function gehoertInKanal(id) {
|
||||
try {
|
||||
const r = db().prepare("SELECT rolle FROM personen WHERE id = ? AND aktiv = 1").get(id);
|
||||
return !!r && KANAL_ROLLEN.includes(r.rolle);
|
||||
} catch { return false; }
|
||||
}
|
||||
|
||||
function raumName(raum, teilnehmer, ichId) {
|
||||
if (raum.art === "kanal") return raum.name || kanalName(raum.kategorie);
|
||||
if (raum.art === "gruppe") return raum.name || "Gruppe";
|
||||
@@ -934,6 +1029,16 @@ chatRouter.post("/workspace/api/chat/kanal", gleicheHerkunft, express.json({ lim
|
||||
if (mit.some((id) => !darfSchreibenMit(req.person, id))) {
|
||||
return res.status(403).json({ fehler: "Mit mindestens einer Person kannst du nicht schreiben." });
|
||||
}
|
||||
/* UND SIE MUESSEN ZU TEAM DOGI GEHOEREN (22.09.2026, VanVans
|
||||
Befund). `darfSchreibenMit` beantwortet eine andere Frage --
|
||||
"darf ich diesen Menschen ueberhaupt anschreiben" --, und die
|
||||
sagt bei einem Scout zu Recht ja. Ein Kanal ueber
|
||||
Chat-Moderation ist trotzdem kein Ort fuer ihn. Genau diese
|
||||
Luecke hat die zwei Scouts in den Moderations-Kanal gebracht. */
|
||||
if (mit.some((id) => !gehoertInKanal(id))) {
|
||||
return res.status(403).json({
|
||||
fehler: "In einen Kanal gehören nur Leute aus Team Dogi." });
|
||||
}
|
||||
|
||||
const n = jetzt();
|
||||
d.prepare(`INSERT INTO chat_raeume (art, name, kategorie, erstellt, erstellt_von)
|
||||
@@ -1004,6 +1109,14 @@ chatRouter.put("/workspace/api/chat/kanal/:id/mitglieder", gleicheHerkunft,
|
||||
if ([...soll].some((id) => !darfSchreibenMit(req.person, id))) {
|
||||
return res.status(403).json({ fehler: "Mit mindestens einer Person kannst du nicht schreiben." });
|
||||
}
|
||||
/* DIESELBE SCHRANKE WIE BEIM ANLEGEN. Ohne sie waere die Regel
|
||||
eine Tuer mit zwei Eingaengen, von denen nur einer ein Schloss
|
||||
hat -- und der ohne Schloss ist der, den man haeufiger
|
||||
benutzt. */
|
||||
if ([...soll].some((id) => !gehoertInKanal(id))) {
|
||||
return res.status(403).json({
|
||||
fehler: "In einen Kanal gehören nur Leute aus Team Dogi." });
|
||||
}
|
||||
|
||||
/* Die Teamfuehrung bleibt unangetastet -- sie hat laut Kapitel 7.2
|
||||
Zugriff auf alle Kanaele. Sie aus der Liste entfernen zu koennen
|
||||
|
||||
Reference in New Issue
Block a user