Workspace: Creator-Profile (Onboarding aus dem Konzept)

/workspace/profil.html -- Stammdaten, Ziele und 90-Tage-Plan, genau nach
Seite 5 des Konzepts. Management waehlt oben den Creator aus, ein Creator
sieht nur sein eigenes Profil.

Sicherheitskern ist das Feld admin_notiz. Das Konzept fordert "private
Admin-Notizen separat". Die Notiz wird deshalb nicht im Browser
ausgeblendet, sondern gar nicht erst gesendet: Die Spaltenliste der
Abfrage haengt an der Rolle (FELDER_OFFEN / FELDER_ADMIN). Dasselbe gilt
fuer Plan-Start und Review-Termin.

Geprueft:
- In der kompletten Rohantwort an den Creator kommt der Inhalt der
  internen Notiz 0-mal vor
- Creator auf fremdes Profil: 404 (lesend wie schreibend)
- Scout auf ein Profil: 404, profil.html leitet ihn weg
- Creator setzt admin_notiz selbst: wird stillschweigend ignoriert,
  der Inhalt bleibt unveraendert
- Profil einer Nicht-Creator-Person: 404

Dabei ist ein aelterer Fehler aufgefallen: /api/ich lieferte nur Name und
Rolle, nicht die eigene Nummer. Dadurch rief die Profilseite eines
Creators /api/profil/undefined auf und blieb leer. Derselbe Fehler machte
in der Personenverwaltung den Selbstvergleich unwirksam -- beim eigenen
Eintrag erschien ein "Sperren"-Knopf, den der Server dann ablehnte.
/api/ich liefert jetzt zusaetzlich die id.

Im Protokoll landen nur die Feldnamen, nie die Inhalte: Im Profil stehen
persoenliche Angaben, die nicht zusaetzlich im Audit-Log auftauchen
sollen.
This commit is contained in:
2026-08-28 00:17:53 +02:00
parent 232a2003dd
commit 8bfd2ae166
7 changed files with 534 additions and 3 deletions
+30 -1
View File
@@ -125,6 +125,30 @@ export function db() {
CREATE INDEX IF NOT EXISTS idx_aufgaben_status ON aufgaben (status);
CREATE INDEX IF NOT EXISTS idx_aufgaben_creator ON aufgaben (creator_id);
/* Creator-Profil (Onboarding aus dem Konzept). Eine Zeile je
Creator, entsteht erst beim ersten Speichern.
admin_notiz ist bewusst Teil dieser Tabelle, wird aber nur an
das Management ausgeliefert -- siehe workspace-profil.js. */
CREATE TABLE IF NOT EXISTS profile (
person_id INTEGER PRIMARY KEY REFERENCES personen(id) ON DELETE CASCADE,
handles TEXT,
nische TEXT,
live_zeiten TEXT,
technik TEXT,
ziel_live TEXT,
ziel_content TEXT,
ziel_community TEXT,
ziel_technik TEXT,
plan_start TEXT,
plan_prio1 TEXT,
plan_prio2 TEXT,
plan_prio3 TEXT,
naechster_review TEXT,
admin_notiz TEXT,
geaendert TEXT,
geaendert_von INTEGER REFERENCES personen(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS versuche (
ip TEXT NOT NULL,
zeitpunkt TEXT NOT NULL
@@ -257,6 +281,7 @@ const GESCHUETZT = {
"/workspace/start.html": null,
"/workspace/aufgaben.html": null,
"/workspace/personen.html": ["admin"],
"/workspace/profil.html": ["admin", "creator"],
};
workspaceRouter.use((req, res, next) => {
@@ -335,7 +360,11 @@ workspaceRouter.post("/workspace/api/abmelden", (req, res) => {
workspaceRouter.get("/workspace/api/ich", (req, res) => {
const person = sitzungLesen(req);
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
res.json({ name: person.name, rolle: person.rolle });
/* Die eigene Nummer gehört mit dazu: Ohne sie kann die Oberfläche nicht
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 });
});
/* ---------- Verwaltung (nur über die Kommandozeile) --------------------