Workspace: Wissens-Bibliothek -- 20 Kategorien, Suche, Filter, Pflege

Wichtigster Befund vorab: Es gab noch KEINEN PDF-Bereich. Weder auf der
Website (28 Seiten, keine davon Anleitungen) noch auf dem Server (null
PDFs). Es wurde also nichts umstrukturiert, sondern neu gebaut -- und
damit war die erste Frage nicht "wie sieht es aus", sondern "wie kommen
die PDFs spaeter rein". Eine feste Liste im Code waere nach drei Monaten
unbrauchbar gewesen.

Filipe hat entschieden: eigener Pflegebereich, und alles nur fuer das
Team. Die Bibliothek liegt deshalb komplett im Workspace hinter dem
Login, nicht auf der oeffentlichen Seite.

GLIEDERUNG
Zwanzig Kategorien woertlich nach Vorgabe, gebuendelt in sechs Welten:
TikTok, Streaming einrichten, Bild & Ton, Uebertragung & Aussehen,
Inhalt & Menschen, Hilfe & Schnellstart. Zwanzig gleich aussehende
Kacheln untereinander findet niemand -- gebuendelt sucht man erst die
Welt, und darin stehen nur noch drei bis vier. Jede Welt hat ihre eigene
Farbe, jede Kategorie ihr eigenes Symbol (20 verschiedene, keins
doppelt). Die Kategorietexte sind Filipes Beschreibungen -- sie
beantworten beim Einsortieren die einzige Frage, die man wirklich hat.

Die Gliederung steht im Code, nicht in der Datenbank: Sie ist eine
bewusste Ordnung, keine Nutzdaten.

JEDE PDF HAT GENAU EINE HAUPTKATEGORIE, alles Weitere laeuft ueber Tags
-- so gibt es jede Anleitung nur einmal, sie ist aber ueber mehrere
Begriffe auffindbar. Genau wie gefordert.

SUCHE UND FILTER
Suche ueber Titel, Beschreibung, Tags und Dateiname. Filter nach Stufe
(Einsteiger / Fortgeschritten / Profi), Geraet und Tag. Zwei Details:
- "PC" zeigt auch die Anleitungen, die fuer BEIDE Geraete gelten --
  sonst filtert man sich versehentlich die Haelfte weg.
- Der Tag-Filter sucht mit Kommas drumherum, sonst wuerde "pc" auch bei
  "pc-spiele" anschlagen.
Sobald gesucht oder gefiltert wird, verschwinden die Kacheln und es
erscheinen Treffer. Wer sucht, will nicht erst noch klicken.

Der Zustand steht in der Adresse (?k=obs&tag=...). Damit funktionieren
Zurueck-Taste, Neuladen und Weiterschicken. Eine Kategorie, die man
niemandem verlinken kann, ist keine Seite. Geprueft.

SICHERHEIT
Hochgeladen wird nur, was WIRKLICH eine PDF ist -- geprueft an den
ersten Bytes (%PDF-), nicht an der Dateiendung. Die kann jeder
umbenennen. Geprueft mit einer als .pdf getarnten HTML-Datei mit Skript:
abgewiesen. Nur weil diese Pruefung existiert, darf die Datei ueberhaupt
im Browser angezeigt werden statt bloss heruntergeladen -- und selbst
dann mit strenger CSP und nosniff.

Lesen darf jeder Angemeldete, pflegen nur DogFather. Geprueft: Creator
und Scout bekommen 403 beim Hochladen und sehen keine Bearbeiten-Knoepfe.
Ohne Anmeldung ist auch die Datei selbst nicht erreichbar (401).

Bricht das Anlegen nach dem Schreiben ab, wird die Datei wieder
geloescht -- sonst laege sie fuer immer verwaist auf der Platte.

Beim Bearbeiten wird die Datei bewusst NICHT ersetzt. Wer eine neue
Fassung hat, stellt sie neu ein. So bleibt nachvollziehbar, was wann galt.

Geprueft: 20 Kacheln, 20 verschiedene Symbole, Kategorie oeffnen, Suche,
Tag-Klick, Browser-Zurueck, Handy ohne Ueberlauf, 0 Konsolenfehler.
This commit is contained in:
2026-08-28 12:40:49 +02:00
parent 18a8d46a04
commit 9b2e09be58
7 changed files with 1351 additions and 0 deletions
+27
View File
@@ -237,6 +237,32 @@ export function db() {
);
CREATE INDEX IF NOT EXISTS idx_eintraege_bereich ON eintraege (bereich, creator_id);
/* Wissens-Bibliothek: Anleitungen als PDF, hinter dem Login.
Jede PDF hat GENAU EINE Hauptkategorie -- so gibt es sie nur
einmal. Zusaetzliche Themen laufen ueber Tags, damit dieselbe
Anleitung trotzdem ueber mehrere Suchbegriffe auffindbar ist.
Die Kategorien selbst stehen im Code (workspace-wissen.js),
nicht hier: Sie sind eine bewusste Gliederung, keine Nutzdaten. */
CREATE TABLE IF NOT EXISTS wissen (
id INTEGER PRIMARY KEY AUTOINCREMENT,
titel TEXT NOT NULL,
beschreibung TEXT,
kategorie TEXT NOT NULL,
stufe TEXT NOT NULL DEFAULT 'einsteiger'
CHECK (stufe IN ('einsteiger','fortgeschritten','profi')),
geraet TEXT NOT NULL DEFAULT 'egal'
CHECK (geraet IN ('pc','handy','beide','egal')),
tags TEXT,
name_original TEXT NOT NULL,
name_datei TEXT NOT NULL,
groesse INTEGER NOT NULL,
wichtig INTEGER NOT NULL DEFAULT 0,
veroeffentlicht TEXT NOT NULL,
aktualisiert TEXT,
erstellt_von INTEGER REFERENCES personen(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_wissen_kat ON wissen (kategorie);
/* Start-Check / Erstanalyse (Konzept, Seite 5). Eine Zeile je
geprueftem Punkt -- ungeprueft heisst: gar keine Zeile. So sieht
man am Bestand sofort, wie weit die Analyse ist, ohne leere
@@ -462,6 +488,7 @@ const GESCHUETZT = {
"/workspace/scouting.html": ["admin", "scout"],
"/workspace/calls.html": null,
"/workspace/startcheck.html": null,
"/workspace/wissen.html": null,
};
workspaceRouter.use((req, res, next) => {