Workspace: Rolle "Management" heisst jetzt "DogFather"

Auf Wunsch von Filipe. Betrifft ausschliesslich die Anzeige.

Der Rollenschluessel bleibt "admin". Er steckt in der CHECK-Regel der
Datenbank, in jeder bestehenden Sitzung und in jeder Rechteabfrage --
ihn umzubenennen haette alle drei gebrochen, und bestehende Anmeldungen
waeren ungueltig geworden. Umbenannt wird nur, was man LIEST.

Der Anzeigename steht jetzt an EINER Stelle (ROLLEN_NAME in
workspace.js) und kommt ueber /workspace/api/ich als rolle_name mit.
Stand er in elf Dateien, waere er beim naechsten Mal in zehn davon
geaendert.

Dabei aufgefallen: In der Kopfleiste stand auf JEDER Seite der rohe
Rollenschluessel -- "Chef · admin". Das war schon vorher unschoen, faellt
aber jetzt erst richtig auf. Alle elf Seiten zeigen jetzt "Chef ·
DogFather". Geprueft: kein rohes "admin" mehr in der Oberflaeche.

Geaendert: Rollenkachel und Untertitel auf der Anmeldeseite, Kopfleiste
aller Seiten, Rollentext auf dem Dashboard, Rollenmarken und Auswahl in
der Personenverwaltung, die Betreuungs-Auswahl ("nur DogFather"), der
Hinweis auf der Dateienseite, die Erklaerung zur internen Notiz im
Profil, die Hinweise fuer Scouts ohne Zuteilung -- und vier
Fehlermeldungen vom Server.

Schreibweise "DogFather" wie von Filipe geschrieben; das ist auch auf
der oeffentlichen Website die haeufigste Form (717 von 1216).

In den Code-Kommentaren der Fachmodule heisst die Rolle weiterhin "das
Management". Das bleibt bewusst so -- eine Massenaenderung an vierzig
Kommentaren waere reines Risiko ohne sichtbaren Nutzen. Ein Hinweis an
der ROLLEN_NAME-Zuordnung erklaert den Zusammenhang.
This commit is contained in:
2026-08-28 11:55:05 +02:00
parent 55450aa83f
commit 1c3e642f12
20 changed files with 54 additions and 36 deletions
+4 -4
View File
@@ -129,7 +129,7 @@ function nameAusKopf(req) {
function darfHochladen(req, res, next) {
if (!DARF_HOCHLADEN.has(req.person.rolle)) {
return res.status(403).json({
fehler: "Dateien legen das Management und die Scouts ab.",
fehler: "Dateien legen DogFather und die Scouts ab.",
});
}
next();
@@ -263,10 +263,10 @@ dateienRouter.patch("/workspace/api/dateien/:id", gleicheHerkunft, (req, res) =>
/* "freigegeben" ist eine Abnahme -- die trifft laut Konzept das
Management, nicht der, der die Datei hochgeladen hat. */
if (req.body.status === "freigegeben" && req.person.rolle !== "admin") {
return res.status(403).json({ fehler: "Freigeben darf nur das Management." });
return res.status(403).json({ fehler: "Freigeben darf nur DogFather." });
}
if (datei.status === "freigegeben" && req.person.rolle !== "admin") {
return res.status(403).json({ fehler: "Freigegebene Dateien ändert nur das Management." });
return res.status(403).json({ fehler: "Freigegebene Dateien ändert nur DogFather." });
}
setz.push("status = ?"); daten.push(req.body.status);
}
@@ -309,7 +309,7 @@ dateienRouter.delete("/workspace/api/dateien/:id", gleicheHerkunft, (req, res) =
return res.status(403).json({ fehler: "nicht_erlaubt" });
}
if (datei.status === "freigegeben") {
return res.status(403).json({ fehler: "Freigegebene Dateien löscht nur das Management." });
return res.status(403).json({ fehler: "Freigegebene Dateien löscht nur DogFather." });
}
}
+1 -1
View File
@@ -180,7 +180,7 @@ personenRouter.patch("/workspace/api/verwaltung/personen/:id", gleicheHerkunft,
if (!aktiv && person.rolle === "admin") {
const { n } = db().prepare(
"SELECT COUNT(*) AS n FROM personen WHERE rolle = 'admin' AND aktiv = 1").get();
if (n <= 1) return res.status(400).json({ fehler: "Das ist das letzte aktive Management." });
if (n <= 1) return res.status(400).json({ fehler: "Das ist der letzte aktive DogFather-Zugang." });
}
personSperren(id, aktiv, { ...req.person, ip: echteIp(req) });
}
+1 -1
View File
@@ -227,7 +227,7 @@ scoutRouter.delete("/workspace/api/leads/:id", gleicheHerkunft, (req, res) => {
/* Ein übergebener Lead ist Teil der Betreuungsgeschichte -- den
räumt nur das Management weg. */
if (req.person.rolle !== "admin" && lead.status === "uebergeben") {
return res.status(403).json({ fehler: "Übergebene Leads löscht nur das Management." });
return res.status(403).json({ fehler: "Übergebene Leads löscht nur DogFather." });
}
db().prepare("DELETE FROM leads WHERE id = ?").run(id);
protokolliere("lead_geloescht", {
+19 -1
View File
@@ -54,6 +54,21 @@ const VERSUCHE_MAX = 8; // pro IP
const VERSUCHE_FENSTER_MIN = 10;
const ROLLEN = new Set(["admin", "creator", "scout"]);
/* Der Rollenschluessel bleibt "admin" -- er steckt in der CHECK-Regel der
Datenbank, in jeder Sitzung und in jeder Rechteabfrage. Umbenannt wird
nur, was man LIEST. Diese Zuordnung ist die einzige Stelle dafuer:
Stand der Anzeigename in elf Dateien, waere er beim naechsten Mal in
zehn davon geaendert.
Hinweis fuer spaeter: In den Kommentaren der Fachmodule heisst die
Rolle "admin" weiterhin "das Management". Gemeint ist dieselbe Rolle,
die in der Oberflaeche "DogFather" heisst. */
export const ROLLEN_NAME = {
admin: "DogFather",
creator: "Creator",
scout: "Scout",
};
/* scrypt-Parameter. N=2^15 braucht auf diesem Server rund 150 ms — spürbar
genug, um Rateversuche auszubremsen, und unauffällig für einen echten
Anmeldevorgang. Die Werte werden je Datensatz mitgespeichert, damit sie
@@ -529,7 +544,10 @@ workspaceRouter.get("/workspace/api/ich", (req, res) => {
erkennen, welcher Eintrag der eigene ist (etwa "das bin ich" in der
Personenliste), und das eigene Profil liesse sich gar nicht aufrufen.
Ein Geheimnis ist sie nicht -- sie beschreibt nur den Angemeldeten. */
res.json({ id: person.id, name: person.name, rolle: person.rolle });
res.json({
id: person.id, name: person.name, rolle: person.rolle,
rolle_name: ROLLEN_NAME[person.rolle] ?? person.rolle,
});
});
/* ---------- Verwaltung (nur über die Kommandozeile) --------------------
+1 -1
View File
@@ -260,7 +260,7 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe
ich = await antwort.json();
} catch { return; }
$('wer').textContent = `${ich.name} · ${ich.rolle}`;
$('wer').textContent = `${ich.name} · ${ich.rolle_name || ich.rolle}`;
if (ich.rolle === 'admin') {
$('feld-creator').hidden = false;
+1 -1
View File
@@ -200,7 +200,7 @@
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
let daten;
try {
+1 -1
View File
@@ -328,7 +328,7 @@
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
await laden();
})();
})();
+1 -1
View File
@@ -214,7 +214,7 @@
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
/* Creator laden nichts hoch (Wunsch 28.08.2026). Das Ablegefeld
verschwindet, stattdessen steht dort, warum -- ein einfach
+1 -1
View File
@@ -15,7 +15,7 @@
const knopf = $('knopf');
const fuer = $('fuer');
const NAMEN = { admin: 'Management', creator: 'Creator', scout: 'Scout' };
const NAMEN = { admin: 'DogFather', creator: 'Creator', scout: 'Scout' };
const SPEICHER = 'dfw.rolle';
let gewaehlt = null;
+1 -1
View File
@@ -242,7 +242,7 @@
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
if (ich.rolle === 'admin') {
$('feld-teilnehmer').hidden = false;
+4 -4
View File
@@ -1,12 +1,12 @@
/* ===================================================================
Personen und Zugangscodes verwalten. Nur für Management -- der Server
Personen und Zugangscodes verwalten. Nur für DogFather -- der Server
liefert allen anderen ohnehin 404 auf diese Schnittstellen.
=================================================================== */
(() => {
'use strict';
const $ = (id) => document.getElementById(id);
const ROLLENNAME = { admin: 'Management', creator: 'Creator', scout: 'Scout' };
const ROLLENNAME = { admin: 'DogFather', creator: 'Creator', scout: 'Scout' };
let ich = null;
const el = (tag, klasse, text) => {
@@ -88,7 +88,7 @@
const w = el('select', 'betreuung__wahl');
w.id = 'betreuer-' + p.id;
const leer = document.createElement('option');
leer.value = ''; leer.textContent = 'nur Management';
leer.value = ''; leer.textContent = 'nur DogFather';
w.append(leer);
for (const s2 of betreuerListe) {
const o = document.createElement('option');
@@ -230,7 +230,7 @@
ich = await a.json();
} catch { return; }
if (ich.rolle !== 'admin') { location.assign('/workspace/start.html'); return; }
$('wer').textContent = `${ich.name} · ${ich.rolle}`;
$('wer').textContent = `${ich.name} · ${ich.rolle_name || ich.rolle}`;
await laden();
})();
})();
+3 -3
View File
@@ -97,9 +97,9 @@
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
/* Scouts betreuen ihre zugeteilten Creator wie das Management --
/* Scouts betreuen ihre zugeteilten Creator wie DogFather --
sie sehen aber nur die management-internen Felder nicht. */
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
let liste;
try { liste = await (await hole('/workspace/api/profil')).json(); } catch { return; }
@@ -107,7 +107,7 @@
if (ich.rolle === 'admin' || ich.rolle === 'scout') {
if (!liste.creator.length) {
$('unterzeile').textContent = ich.rolle === 'scout'
? 'Dir ist noch kein Creator zugeteilt. Das Management legt fest, wen du betreust.'
? 'Dir ist noch kein Creator zugeteilt. DogFather legt fest, wen du betreust.'
: 'Es gibt noch keinen Creator. Lege zuerst unter Personen & Zugänge einen an.';
return;
}
+2 -2
View File
@@ -200,7 +200,7 @@
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
try {
const { creator } = await (await hole('/workspace/api/report/creator')).json();
@@ -214,7 +214,7 @@
fuer ihn nicht, das waere die Gesamtuebersicht. */
if (!creator.length) {
$('unterzeile').textContent =
'Dir ist noch kein Creator zugeteilt. Das Management legt fest, wen du betreust.';
'Dir ist noch kein Creator zugeteilt. DogFather legt fest, wen du betreust.';
return;
}
$('creator-block').hidden = false;
+2 -2
View File
@@ -340,7 +340,7 @@
ich = await a.json();
} catch { return; }
if (ich.rolle === 'creator') { location.assign('/workspace/start.html'); return; }
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
if (ich.rolle === 'admin') {
$('unterzeile').textContent = 'Gesamtübersicht über alle Scouts.';
@@ -358,7 +358,7 @@
} catch { return; }
$('f-scout').addEventListener('change', zeichne);
} else {
$('unterzeile').textContent = 'Deine Pipeline – nur du und das Management sehen sie.';
$('unterzeile').textContent = 'Deine Pipeline – nur du und DogFather sehen sie.';
}
await laden();
+2 -2
View File
@@ -7,7 +7,7 @@
'use strict';
const ROLLENTEXT = {
admin: 'Management · Gesamtübersicht und Freigaben',
admin: 'DogFather · Gesamtübersicht und Freigaben',
creator: 'Creator · dein eigener Bereich',
scout: 'Scout · deine Pipeline und die Creator, die du betreust',
};
@@ -48,7 +48,7 @@
return;
}
$('wer').textContent = `${ich.name} · ${ich.rolle}`;
$('wer').textContent = `${ich.name} · ${ich.rolle_name || ich.rolle}`;
$('gruss').textContent = `Willkommen, ${ich.name}`;
$('rollentext').textContent = ROLLENTEXT[ich.rolle] || ich.rolle;
+2 -2
View File
@@ -254,7 +254,7 @@
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + ich.rolle;
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
let liste;
try { liste = await (await hole('/workspace/api/startcheck/creator')).json(); } catch { return; }
@@ -262,7 +262,7 @@
if (!liste.creator.length) {
$('felder').textContent = '';
$('felder').append(el('p', 'leer-hinweis', ich.rolle === 'scout'
? 'Dir ist noch kein Creator zugeteilt. Das Management legt fest, wen du betreust.'
? 'Dir ist noch kein Creator zugeteilt. DogFather legt fest, wen du betreust.'
: 'Es gibt noch keinen Creator.'));
return;
}
+1 -1
View File
@@ -44,7 +44,7 @@
Feld wirkt wie ein Fehler. -->
<p class="nur-lesen" id="nur-lesen" hidden>
Hier siehst du die Dateien deines Bereichs. <strong>Abgelegt werden sie
vom Management oder von Scouts</strong> – du kannst sie öffnen, aber nichts hochladen.
von DogFather oder von Scouts</strong> – du kannst sie öffnen, aber nichts hochladen.
</p>
<!-- Ablegefeld. Klick öffnet die Dateiauswahl, Ziehen funktioniert
+4 -4
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Dogfather Universe · Creator Workspace · Anmeldung</title>
<meta name="description" content="Privater Arbeitsbereich für Creator, Scouts und Management." />
<meta name="description" content="Privater Arbeitsbereich für Creator, Scouts und DogFather." />
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
@@ -53,13 +53,13 @@
<header class="kopf">
<p class="marke">Dogfather&nbsp;Universe</p>
<h1 class="titel">Creator&nbsp;Workspace</h1>
<p class="unterzeile">Privater Bereich für Creator, Scouts und Management.</p>
<p class="unterzeile">Privater Bereich für Creator, Scouts und DogFather.</p>
</header>
<div class="rollen" role="radiogroup" aria-labelledby="rollen-schild">
<p class="feldschild" id="rollen-schild">Womit meldest du dich an?</p>
<!-- Krone für das Management: steht für die Gesamtübersicht und die
<!-- Krone für DogFather: steht für die Gesamtübersicht und die
Freigaben. Das Schild ist zum Scout gewandert -- dort passt es
besser, weil ein Scout die Pipeline abschirmt und prüft, bevor
etwas weitergereicht wird. -->
@@ -69,7 +69,7 @@
<path d="M6 20.1h12"/>
</svg>
<span class="rolle__text">
<span class="rolle__name">Management</span>
<span class="rolle__name">DogFather</span>
<span class="rolle__zusatz">Gesamtübersicht &amp; Freigaben</span>
</span>
</button>
+1 -1
View File
@@ -50,7 +50,7 @@
<select id="f-rolle" name="rolle">
<option value="creator" selected>Creator</option>
<option value="scout">Scout</option>
<option value="admin">Management</option>
<option value="admin">DogFather</option>
</select>
</div>
</div>
+2 -2
View File
@@ -108,12 +108,12 @@
</div>
</fieldset>
<!-- Nur fürs Management. Der Server schickt dieses Feld an Creator
<!-- Nur für DogFather. Der Server schickt dieses Feld an Creator
gar nicht erst mit. -->
<fieldset class="gruppe gruppe--intern" id="gruppe-intern" hidden>
<legend class="gruppe__titel">Interne Notiz</legend>
<p class="gruppe__text">
Sieht <strong>nur das Management</strong>. Der Creator bekommt dieses Feld
Sieht <strong>nur DogFather</strong>. Der Creator bekommt dieses Feld
weder angezeigt noch übertragen.
</p>
<textarea id="admin_notiz" rows="4" maxlength="2000"></textarea>