18a8d46a048b7380a728607b7cc2b8227ba08a95
100
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
18a8d46a04 |
Workspace: Betreuungs-Auswahl heisst "DogFather" statt "nur DogFather"
Das "nur" war aus der Bauzeit uebrig, als der Eintrag noch beschrieb,
wer den Creator sieht ("nur das Management"). Als Auswahl neben "Sam"
und "Patrick" gehoert an die Stelle schlicht der Name -- die Liste
beantwortet die Frage "wer ist zustaendig", nicht "wer sieht mit".
Geprueft: Zuordnung setzen, entfernen und wieder setzen funktioniert
unveraendert.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
677bcb5653 |
Workspace: neues Knopf-System "Geschliffen" -- ueberall, eine Stelle
Von Filipe aus drei gerenderten Entwuerfen gewaehlt (A Kante & Glut,
B Geschliffen, C Praegung). Raten hilft hier nicht -- beim Zurueck-Knopf
hat erst der direkte Vergleich zum Ziel gefuehrt.
Aufbau: farbiger Verlaufsrand (padding-box/border-box) und ein feiner
heller Streifen obenauf -- wie eine angeschliffene Kante. Die Farbe sitzt
im Rand und in der Schrift, die Flaeche bleibt dunkel. Beim Zeigen hebt
sich der Knopf einen Pixel an und bekommt einen weichen farbigen Schein
darunter. Die Hauptaktion einer Seite ist zusaetzlich gefuellt.
JEDE AKTION HAT IHRE EIGENE FARBE:
gruen passt, erledigt, freigegeben
amber ausbaufaehig, wartet
rot Handlungsbedarf, loeschen
blau speichern
violett anlegen
gold Onboarding -- der einzige Schritt, der eine Person anlegt
grau abbrechen, schliessen
Stufenfarbe fuer den Weiter-Knopf der Scout-Pipeline
DAS IST JETZT DIE EINZIGE STELLE, AN DER KNOEPFE GESTALTET WERDEN.
Vorher standen sie in NEUN Dateien verstreut, und prompt sahen dieselben
Knoepfe auf verschiedenen Seiten verschieden aus. Alle alten
Definitionen sind entfernt; wer einen neuen Knopf braucht, nimmt eine
Farbklasse und schreibt kein CSS. Gilt ab jetzt auch fuer alles, was
noch dazukommt.
Geprueft ueber alle elf Seiten: 172 sichtbare Knoepfe, kein einziger
ungestylt, kein Kontrast unter 4,5:1.
Drei Sachen, die dabei aufgefallen sind:
1. Der neutrale "Abbrechen"-Knopf hatte halbdurchsichtiges Weiss als
Farbwert -- gemessen 1,1:1 Kontrast, praktisch unlesbar. Neutral hat
jetzt einen echten Farbwert (#8e9cb0).
2. Die Filter-Knoepfe (Kalender, Dateien, Bereiche) hatten eine eigene
"gedrueckt"-Regel mit flaechiger Farbe, die den geschliffenen Rand
ueberschrieb. Sie benutzen jetzt den gefuellten Auftritt des Systems.
3. Der Bearbeiten-Stift auf den Aufgabenkarten war nie ein richtiger
Knopf, sondern nackter Text ohne Rand -- man sah nicht, dass man
draufdruecken kann. Jetzt im System, in der kleinsten Groesse.
Der Zurueck-Knopf ("Neon-Kante") bleibt wie er ist -- den hat Filipe
selbst ausgesucht, und er ist ein Navigationselement, keine Aktion.
Messhinweis fuer spaeter: color-mix liefert computed color(srgb ...),
das laesst sich NICHT als Text auslesen. Die erste Messung meldete
faelschlich 1,13:1 fuer 49 Knoepfe. Richtig geht es ueber eine Leinwand
(fillStyle + getImageData).
Alle Toene bewusst gedeckt statt neon, alles respektiert
prefers-reduced-motion.
|
||
|
|
1c3e642f12 |
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.
|
||
|
|
55450aa83f |
Workspace: Start-Check / Erstanalyse -- Onboarding vollstaendig
Letztes unumgesetztes Stueck aus dem Konzept, Seite 5, Block 03. Profil,
Ziele und 90-Tage-Plan gab es schon -- die Erstanalyse dazwischen fehlte.
"Die Betreuung startet strukturiert: Ausgangslage verstehen, Ziele
festlegen und daraus konkrete Arbeitspakete bauen."
Der letzte Halbsatz ist der entscheidende. Ein Check, der mit einer Note
endet, ist eine Beurteilung. Ein Check, der mit Aufgaben endet, ist
Betreuung. Jeder Punkt mit Handlungsbedarf bietet deshalb direkt an,
daraus eine Aufgabe zu machen -- mit dem Befund als Herkunft und
automatisch hoher Prioritaet. Dieselbe Linie wie beim Review ("endet mit
einer Entscheidung") und beim Call ("endet mit To-dos").
Die vier Felder aus dem Deck, mit je vier Punkten: Profil & Auftritt,
LIVE-Struktur, Content-Muster, Community & Modis. Drei Stufen: Passt,
Ausbaufaehig, Handlungsbedarf.
Die Punkte stehen fest im Code, NICHT in der Datenbank. Eine
Erstanalyse, bei der jeder eigene Punkte anlegt, ist keine Erstanalyse
mehr -- man koennte zwei Creator nicht mehr vergleichen, und genau das
ist ihr Zweck.
Sichtbarkeit, bewusst anders als beim Profil:
- LESEN darf auch der Creator selbst. Der Check ist Teil SEINES
Entwicklungsplans ("jeder Creator bekommt einen eigenen, lebenden
Entwicklungsplan"), kein Urteil hinter seinem Ruecken. Wer etwas
festhalten will, das er nicht sehen soll, hat dafuer die interne
Notiz im Profil -- die bleibt beim Management.
- AENDERN duerfen nur Management und zustaendiger Scout. Es ist eine
Fremdeinschaetzung; koennte der Creator sie selbst setzen, waere es
eine Selbsteinschaetzung. Geprueft: Luna bekommt 403 beim Bewerten und
beim Anlegen einer Aufgabe, sieht aber ihren Check inklusive Notizen.
- Ein nicht zustaendiger Scout bekommt 404, nicht 403.
Kein Speichern-Knopf: Jede Bewertung und jede Notiz wird sofort
gesichert. Eine Erstanalyse geht man im Gespraech durch -- da will
niemand am Ende noch an einen Knopf denken.
Ein leergeraeumter Punkt (keine Bewertung, keine Notiz) wird geloescht
statt als leere Zeile stehen zu bleiben. So sagt der Bestand direkt, wie
weit die Analyse ist.
Zwei neue Hinweise auf dem Dashboard:
- "Creator ohne Start-Check" -- die Sorte Luecke, die sonst niemandem
auffaellt, weil ja nichts fehlt: es fing nur nie an.
- "Punkt im Start-Check braucht Handlung", sichtbar fuer alle, die den
Check auch sehen duerfen.
Aus dem Test: Sechzehn dauerhaft offene Notizfelder machten die Seite
7026 px lang. Das Feld erscheint jetzt erst mit der Bewertung -- vorher
hat man ohnehin nichts zu notieren. 2460 px.
|
||
|
|
3a1ab64c26 |
Workspace: Suche ueber alle Bereiche (Phase 4)
Auf JEDER Seite, nicht auf einer eigenen. kopf.js baut sie selbst in die Kopfleiste ein -- eine Suche, die man nur auf einer Extraseite findet, benutzt niemand. Mit "/" oeffnen, mit Esc schliessen. Durchsucht: Aufgaben, Termine, Calls, Gespraechsprotokolle, Dateien, die fuenf Betreuungsbereiche, die Scout-Pipeline und die offenen Felder der Creator-Profile. Zwei Dinge machen den Unterschied zwischen einer Suche und einer brauchbaren Suche: 1. SIE DARF NICHTS FINDEN, WAS DIE SEITE VERBERGEN WUERDE. Eine Suche ist die verlockendste Stelle fuer ein Datenleck: Man tippt einen Namen und bekommt Treffer aus Bereichen, die man nie oeffnen duerfte. Jede Quelle wird deshalb mit der Sichtbarkeitsregel ihres eigenen Moduls abgefragt -- importiert, nicht abgeschrieben. Die management-internen Profilfelder (admin_notiz, plan_start, naechster_review) werden GAR NICHT durchsucht, auch nicht fuer das Management: Ein Treffer daraus taucht sonst spaeter in einer Ansicht auf, die diese Felder nicht zeigen darf. Wer die Notiz lesen will, oeffnet das Profil. Geprueft mit einem eigenen Leck-Test: "GEHEIM" (Inhalt einer internen Notiz) findet niemand, auch der Chef nicht. Die Lead-Notiz eines Scouts findet nur er selbst und das Management, nicht der andere Scout. 2. SIE MUSS SAGEN, WO ETWAS STEHT. Jeder Treffer traegt einen Ausschnitt RUND UM die Fundstelle, nicht die ersten Zeichen des Feldes -- man sieht sofort, warum etwas gefunden wurde. Bei Profiltreffern steht dabei, welches Feld getroffen hat, bei Protokollen ob es unter "Besprochen" oder "Entscheidung" stand. Und jeder Treffer fuehrt an die Stelle, an der man weiterarbeiten kann. LIKE-Sonderzeichen werden maskiert. Ohne das waere die Suche nach "100%" eine Suche nach "100 gefolgt von irgendwas" und "a_b" faende auch "axb" -- beides falsch und beides faellt erst spaet auf. Geprueft: "100%" und "a_b" finden genau ihren Eintrag, "axb" findet nichts. Bewusst kein Volltextindex (FTS5): Die Datenmengen sind klein, und LIKE braucht keinen zweiten Datenstand, der irgendwann auseinanderlaeuft. Kleinigkeiten aus dem Test: - Das Overlay stand auf voller Hoehe, auch bei zwei Treffern -- der Schleier ist flex und stand auf dem voreingestellten stretch. Jetzt flex-start, das Fenster waechst mit dem Inhalt (417 statt 780 px bei drei Treffern). - Das eingebaute Kreuz von type="search" sass direkt neben dem Esc-Knopf: zwei Wege fuer dasselbe, dicht nebeneinander. Entfernt. - Das CSS liegt in gate.css, nicht in einer Seiten-Datei -- genau der Fehler, der bei .knopf-still schon einmal passiert ist. |
||
|
|
5684ff87f1 |
Workspace: "Was ist dran" -- erster Baustein der Automationen (Phase 4)
Bis hierher musste man selbst daran denken, in den richtigen Bereich zu
schauen. Das dreht die Uebersicht auf dem Dashboard jetzt um: Das System
sagt, was liegen bleibt.
Drei Regeln, an die sich das haelt:
1. KEINE eigenen Daten. Ein Hinweis ist immer nur eine Sicht auf etwas,
das ohnehin existiert. Wird die Aufgabe erledigt, verschwindet der
Hinweis von selbst -- es gibt nichts zu quittieren, nichts zu pflegen
und nichts, was veralten kann. Genau daran scheitern die meisten
Erinnerungssysteme.
2. JEDER HINWEIS FUEHRT IRGENDWOHIN. Ein Hinweis ohne Ziel ist nur ein
schlechtes Gewissen. Jeder traegt deshalb einen Link zu der Stelle,
an der sich die Sache tatsaechlich erledigen laesst. Geprueft: Ziel
erreichbar, keine Umleitung.
3. DIESELBE SICHTBARKEIT WIE UEBERALL. Die Regeln werden aus den
Fachmodulen importiert, nicht abgeschrieben (workspace-aufgaben,
-kalender, -dateien, -bereiche exportieren sie jetzt). Ein Hinweis
darf nie etwas verraten, das die zugehoerige Seite verbergen wuerde
-- sonst waere ausgerechnet die Uebersicht das Leck.
Dreizehn Hinweisarten, in drei Stufen. Nur die oberste ist farbig:
waere alles hervorgehoben, waere nichts hervorgehoben. Liegt nichts an,
verschwindet der ganze Block.
Steht bewusst VOR den Zahlen. Die Zahlen sagen, WIE VIEL anliegt --
diese Liste sagt, WAS zu tun ist.
Steuerungswissen bleibt beim Management: Review-Termine, Creator ohne
zustaendige Person und leere Profile erscheinen weder bei Creator noch
bei Scouts -- genau wie die zugehoerigen Profilfelder. Geprueft mit
einem eigenen Leck-Test ueber alle drei Rollen.
Zwei Hinweise, die es ohne diese Uebersicht gar nicht gaebe:
- Aufgaben, die im Review haengen. Sie warten auf jemanden -- die Sorte
Stillstand, die niemandem auffaellt, weil nichts ueberfaellig wird.
- Uebergebene Leads, aus denen nie ein Creator wurde. Sonst ist die
Uebergabe eine Sackgasse, die niemand bemerkt.
Nebenbei: Der Fusstext auf dem Dashboard stammte noch vom ersten Tag
("Die Bereiche werden nach dem Phasenplan gebaut") -- inzwischen sind
Phase 1 bis 3 fertig. Ersetzt durch eine Erklaerung, wie die Uebersicht
funktioniert.
|
||
|
|
fa8fbab410 |
Workspace: Scouts betreuen Creator wie das Management
Bisher hatten Scouts mit der Creator-Betreuung nichts zu tun -- keine Profile, keine Betreuungsbereiche, keine Reports. Das aendert sich, mit zwei bewusst gesetzten Grenzen. GRENZE 1: nur zugeteilte Creator, keine Rollenregel. Neue Tabelle betreuung (creator_id PRIMARY KEY -> betreuer_id). Ein Creator hat genau EINE zustaendige Person, damit nie unklar ist, wer gefragt ist. Das Management sieht ohnehin alle und braucht keinen Eintrag. Wer nichts zugeteilt bekommt, sieht weiterhin nichts -- kein Recht entsteht automatisch aus der Rolle. Zugeteilt wird in "Personen & Zugaenge", direkt in der Personenzeile: Betreuung ist eine Eigenschaft der Person, kein eigener Vorgang. Nur das Management darf zuteilen -- koennte ein Scout sich selbst Creator geben, haette er die Rechtevergabe in der Hand, die ihn begrenzen soll. Zustaendig koennen nur aktive Scouts sein, kein Admin (der sieht alles) und kein anderer Creator. Bei der Uebergabe aus der Pipeline passiert die Zuteilung von selbst: Wer jemanden gefunden hat, betreut ihn weiter. Genau darum geht es bei "Creator-Onboarding starten". Umhaengen kann das Management jederzeit. GRENZE 2: betreuen, nicht verwalten. Profile, die fuenf Bereiche und Reports wie ein Manager. ABER: - keine Zugangscodes, kein Sperren von Personen (personen.html bleibt admin-only, unveraendert) - keine management-internen Felder. Der Scout bekommt admin_notiz, plan_start und naechster_review NICHT -- die Felder fehlen in der Antwort komplett, nicht nur in der Anzeige. Eine Notiz UEBER die Betreuung gehoert nicht in die Hand dessen, der betreut. Geprueft: Ein Scout, der admin_notiz mitschickt, aendert sie nicht. Die Regel steht an EINER Stelle (betreuteIds / betreutWo / darfCreator in workspace.js) und wird von sechs Modulen benutzt. Eine Rechteregel, die an sechs Stellen steht, ist eine Rechteregel, die irgendwann an fuenf Stellen stimmt. Genau das ist beim Bauen auch passiert: workspace-calls.js hatte eine wortgleiche Kopie der Kalender-Sichtbarkeit. Erweitert wurde nur der Kalender -- Scouts sahen die Termine ihrer Creator, dieselben Termine als Call aber nicht. Die Kopie ist jetzt weg, calls.js importiert die Regel aus workspace-kalender.js. Zwei Fehler, die die Aenderung selbst erzeugt haette, vorher gefunden: - Report-Entscheidung: ein Scout haette eine Aufgabe angelegt, deren "Creator" er selbst ist -- die waere in jeder Auswertung falsch mitgelaufen. Zeigt jetzt auf einen seiner Creator. - Bereichseintrag: derselbe Fehler. Ein Scout hat gar keinen eigenen Betreuungsbereich. Ein Eintrag ohne oder mit fremder Zuordnung landet beim ersten zugeteilten Creator, nie bei einem fremden. Geprueft: Mikas Bereich bleibt bei jedem Versuch unberuehrt. |
||
|
|
45cd0ae226 |
Workspace: Calls & Meeting-Protokolle -- Phase 3 vollstaendig
/workspace/calls.html. Calls sind KEINE eigene Terminart neben dem Kalender, sondern dieselben Termine mit art = 'call' oder 'review'. Ein zweiter Terminspeicher waere die sichere Art, irgendwann zwei widerspruechliche Uhrzeiten zu haben. Diese Seite fuegt nur hinzu, was ein Gespraech vom blossen Termin unterscheidet: das Protokoll danach. Die Sichtbarkeitsregel ist Wort fuer Wort dieselbe wie im Kalender -- ein Termin darf nicht in der einen Ansicht auftauchen und in der anderen fehlen. Der tragende Satz von Seite 13, woertlich genommen: "Jeder Call endet mit klaren To-dos, die direkt ins Board uebernommen werden." To-dos werden deshalb NICHT als Text im Protokoll abgelegt, sondern sofort zu echten Aufgaben -- mit dem Gespraech als Herkunft in der Beschreibung, zugeordnet an das Gegenueber des Calls. Sonst steht die Verabredung in einem Dokument, das niemand mehr oeffnet. Geprueft: Protokoll geschrieben -> Aufgaben stehen im Brett. Weitere Punkte: - Reihenfolge der Gruppen ist Dringlichkeit, nicht Chronologie: Ganz oben stehen vergangene Gespraeche OHNE Protokoll. Das ist der einzige Zustand, der etwas von einem verlangt. Nur diese Gruppe ist optisch hervorgehoben; wenn alles schreit, sieht man nichts mehr. - Ein festgehaltenes Protokoll ist danach nur noch lesbar. Ein Gespraech nachtraeglich umschreiben zu koennen waere genau das, was ein Protokoll wertlos macht. - "Naechster Termin" legt den Folgetermin direkt in den Kalender -- mit demselben Gegenueber und demselben Meeting-Link. - Alles in EINER Transaktion. Geprueft mit einem absichtlich kaputten To-do: kein Protokoll, keine halben Aufgaben, kein Folgetermin. - Enter im To-do-Feld legt die naechste Zeile an, damit man eine Liste tippen kann, ohne zur Maus zu greifen. - Meeting-Link nur bei anstehenden Gespraechen und nur, wenn es wirklich eine http(s)-Adresse ist. "bei mir zu Hause" bleibt schlichter Text. Zur Videotechnik selbst bewusst KEINE Entscheidung getroffen: Ein eigener WebRTC-Raum braucht einen TURN-Server, damit Verbindungen hinter Mobilfunk-NAT zustande kommen. Das ist eine Infrastrukturfrage mit laufenden Kosten und gehoert besprochen, nicht nebenbei entschieden. Bis dahin traegt der Termin einfach den Link des Dienstes, der ohnehin benutzt wird. Nebenbei: .knopf-still lag in scouting.css und fehlte dadurch auf calls.html -- dort standen prompt nackte Systemknoepfe. Der Baustein gehoert ins gemeinsame start.css, dort liegt er jetzt. |
||
|
|
d845d91d70 |
Workspace: Scout-CRM -- Pipeline, Follow-ups und Uebergabe
/workspace/scouting.html. Erster Baustein aus Phase 3 und der Bereich,
in dem Scouts bisher praktisch nichts hatten.
Der Merksatz von Seite 15 ist hier die Sicherheitsregel, nicht nur eine
Beschreibung: "Scouts sehen ihre Pipeline -- die Admin-Rolle die
Gesamtuebersicht -- Creator keine Scout-internen Daten."
- Creator bekommen 404, nicht 403, und zwar auf Schnittstelle UND Seite.
Sie sollen nicht einmal erfahren, dass es den Bereich gibt.
- Ein Scout sieht ausschliesslich die eigene Pipeline. Geprueft: Patrick
bekommt auf Sams Lead 404 beim Lesen, Aendern und Loeschen.
- Das Management sieht alles, kann nach Scout filtern und Leads einem
Scout zuordnen.
Pipeline als gruppierte Liste, nicht als Board: Sechs Spalten waeren auf
dem Handy unbedienbar, und Scouts arbeiten unterwegs. Jede Stufe traegt
eine eigene Farbe an der linken Kante, die von kuehl (neu entdeckt) nach
gruen (uebergeben) laeuft -- die Richtung der Pipeline wird sichtbar,
ohne Ampel-Geblinke.
Jede Karte hat genau EINEN naheliegenden Schritt ("Weiter zu ..."), der
Rest steckt im aufklappbaren Teil. Der Weiter-Knopf traegt bewusst nur
die Farbe seiner Stufe statt des vollen Farbverlaufs, sonst waere die
Seite ein Streifenmuster aus identischen Leuchtbalken.
Faellige Follow-ups stehen als eigene Leiste ganz oben. Das ist der
Teil, der ohne System am ehesten untergeht -- nicht der Kontakt selbst,
sondern das Nachfassen.
Uebergabe ("Creator-Onboarding starten", Seite 15): Aus einem
uebergebenen Lead legt das Management mit einem Klick eine echte Person
mit Zugangscode an. Der Code wird genau einmal angezeigt, wie in der
Personenverwaltung. Die Qualifizierung des Scouts (Plattform, Handle,
Potenzial, LIVE-Aktivitaet) wandert dabei automatisch ins Creator-Profil
-- sonst muesste das Management abtippen, was laengst dasteht. Geprueft:
Lead -> Person -> Profil, alle drei verbunden, zweiter Versuch wird
abgelehnt.
Ein uebergebener Lead ist Teil der Betreuungsgeschichte -- den loescht
nur das Management, nicht der Scout (403).
Nebenbei: .knopf-still gab es im Baukasten noch gar nicht, die Zweit-
knoepfe waren nackte Systemknoepfe. Jetzt sauber definiert.
|
||
|
|
072ded20a2 |
Workspace: Reports & Review -- Phase 2 vollstaendig
/workspace/report.html. Fuehrt bewusst KEINE eigenen Eintraege, sondern fasst zusammen, was in Aufgaben, Bereichen, Terminen und Dateien schon steht: "Fortschritt wird nicht gefuehlt, sondern nachvollziehbar gemacht". Die vier Abschnitte sind woertlich die Fragen aus dem Deck, Seite 16: Was wurde erledigt? Was blockiert? Was hat funktioniert? Was kommt als Naechstes? Zwei Punkte daraus sind ernst genommen: 1. "Vorher / nachher" (Historie). Jede Zahl wird mit demselben, unmittelbar davorliegenden Zeitraum verglichen. Eine Zahl allein sagt wenig -- 3 erledigte Aufgaben sind gut oder schlecht, je nachdem ob es vorher 1 oder 9 waren. Bei Zahlen, wo mehr SCHLECHTER ist (offene Probleme), ist die Trendfarbe umgedreht. 2. "Jeder Review endet mit einer Entscheidung, nicht nur mit einer Zusammenfassung." Der Report legt deshalb direkt eine Aufgabe an -- ohne Seitenwechsel, mit hoher Prioritaet und optionaler Frist. Geprueft: Eintrag im Report -> Aufgabe erscheint im Brett. "Was blockiert?" zeigt zusaetzlich die drei am laengsten offenen Aufgaben mit Namen, nicht nur eine Zahl. Sichtbarkeit: - Scout: 404, sowohl Schnittstelle als auch Seite (leitet weg) - Creator: sieht nur sich. Geprueft -- Luna fordert ?creator=3 (Mika) an und bekommt einen Report ueber SICH, der Parameter wird fuer Nicht-Management ignoriert - Der Review-Termin aus dem Profil ist Steuerungswissen und wird einem Creator auch hier nicht mitgeschickt, genau wie im Profil selbst Damit ist Phase 2 aus dem Konzept vollstaendig. |
||
|
|
df9f87af0a |
Workspace: Phase 2 -- LIVE, Content, Technik, Community, Schutz
Fuenf Bereiche auf einmal, aber NICHT fuenf Module. Im Deck haben sie dieselbe Grundform: Eintraege zu einem Creator mit Art, Datum, Titel, Text und Status. Sie unterscheiden sich nur darin, welche Arten es gibt und ob eine Bewertung oder eine Dringlichkeit dazugehoert. Deshalb ein gemeinsamer Unterbau: eine Tabelle, eine Sichtbarkeitsregel, eine Pruefung, eine Ansichtsseite (bereich.html?b=live). Ein Fehler laesst sich damit an EINER Stelle beheben statt an fuenf, und ein weiterer Bereich waere ein Eintrag in BEREICHE -- kein neues Modul. Arten woertlich aus dem Deck (Seiten 7-12): live Vorbereitung / Waehrend LIVE / Auswertung + Bewertung 1-5 content Idee / Produktion / Veroeffentlicht technik Setup / Problem / Loesung / Anleitung + Dringlichkeit community Moderation / Aktion / Konflikt + Dringlichkeit schutz Richtlinie / Vorfall / Eskalation / Gelernt + Dringlichkeit Die Oberflaeche kennt die Bereiche nicht auswendig -- welche Arten und Zusatzfelder es gibt, sagt der Server in der Antwort. Geprueft, und zwar fuer alle fuenf gleichzeitig: - Sichtbarkeit: Chef sieht alles, Luna nur ihren Bereich, Mika nur seinen, Sam (Scout) gar nichts (404 -- Scouts haben mit der Creator-Betreuung nichts zu tun) - Luna legt Eintrag mit creator_id=Mika an: landet still in ihrem eigenen Bereich, Mika sieht ihn nicht - Luna auf Mikas Eintrag: 404; Luna loescht Chefs Eintrag ueber ihren Bereich: 403 (loeschen darf das Management und wer ihn schrieb -- sonst koennte ein Creator eine Notiz ueber sich verschwinden lassen) - Eintrag ueber den falschen Bereich in der Adresse ansprechen: 404 - unbekannte Art 400, Bewertung 9 (erlaubt 1-5) 400, unbekannter Bereich 404, fremde Herkunft 403 Beim Testen sahen Umlaute zunaechst zerstoert aus (efbfbd, das Unicode-Ersatzzeichen). Ursache war der curl-Aufruf: Git Bash kodiert $'\xc3\xbc' nach Locale um. Ueber den echten Weg (Browser) kommen Umlaute, ss und Gedankenstrich unveraendert an und liegen sauber in der Datenbank -- gegengeprueft auf Byte-Ebene. |
||
|
|
05331e038a |
Anmeldung: Scout vor Creator, Krone fuers Management
Wunsch vom 28.08.2026: "scout soll ueber creator sein" und "das
schutzschild symbol soll bei scout sein und bei management soll eine
krone sein".
Neue Reihenfolge: Management, Scout, Creator.
Symbole:
Management Krone (Gesamtuebersicht und Freigaben)
Scout Schild (schirmt die Pipeline ab und prueft, bevor etwas
weitergereicht wird -- passt dort besser als beim
Management)
Creator Person (unveraendert)
Die Tastaturbedienung folgt automatisch der neuen Reihenfolge, weil die
Pfeiltasten die Kacheln in Dokumentreihenfolge durchlaufen. Geprueft:
ab Management fuehrt Pfeil-runter zu Scout, dann zu Creator.
Nur eine statische Datei, kein Neustart noetig.
|
||
|
|
516a003dc8 |
Dateien: Hochladen nur fuer Management und Scouts
Wunsch vom 28.08.2026: "die dateien sollen nur die manager oder scout
hochladen koennen aber nicht die creator."
Serverseitig ueber DARF_HOCHLADEN. Die Pruefung steht bewusst VOR
express.raw -- wer nicht darf, wird abgewiesen, bevor auch nur ein Byte
entgegengenommen wird. Sonst wanderten bis zu 25 MB durch die Leitung,
nur um danach verworfen zu werden.
Ein Creator sieht und oeffnet weiterhin die Dateien seines Bereichs.
Statt des Ablegefeldes steht dort jetzt, warum es fehlt -- ein einfach
verschwundenes Feld wirkt wie ein Fehler.
Geprueft: Management 201, Scout 201, Creator 403 mit klarer Meldung;
Creator sieht und laedt weiterhin.
--------------------------------------------------------------------
Dabei ein Fehler aufgefallen, der mehrere Seiten betraf:
Das hidden-Attribut blendet nur ueber eine sehr schwache Browserregel
aus -- JEDE eigene display-Angabe schlaegt sie. Betroffen waren:
* das Ablegefeld fuer Dateien (display: grid) -> ein Creator sah es
trotz hidden weiterhin
* die Verwaltungsfelder "Plan-Start" und "Naechster Review" im
Creator-Profil (ebenfalls grid) -> ein Creator sah sie ebenfalls
* der Zurueck-Knopf (inline-flex) -> waere vor dem Laden des Skripts
kurz sichtbar gewesen
Behoben mit einer Regel in gate.css: [hidden] { display: none !important }
Beim Profil waren nie Daten betroffen -- der Server schickt die Felder
an einen Creator gar nicht erst mit, sie waren leer. Sichtbar sein
sollten sie trotzdem nicht.
Aufgefallen ist es erst im Screenshot. Der vorige Test hatte auf das
hidden-ATTRIBUT geprueft, und das war ja gesetzt. Die Pruefung schaut
jetzt auf die tatsaechliche Sichtbarkeit (isVisible).
Ausserdem: In der Regex zum Saeubern von Dateinamen standen die
Steuerzeichen als rohe Bytes statt als Escape-Sequenz. Die Funktion
arbeitete korrekt, aber die Datei galt dadurch als binaer -- grep
verweigerte sie, und Editoren haetten sie zerstoeren koennen. Jetzt
steht dort \x00-\x1f als Text.
|
||
|
|
81b51ab94f |
Workspace: Dateiablage mit Freigabe-Ablauf (Phase 1 vollstaendig)
/workspace/dateien.html -- Ablegen per Ziehen oder Auswaehlen, Entwurf -> Review -> Freigabe, Filter je Zustand. OHNE NEUE ABHAENGIGKEIT. Uploads laufen ueblicherweise ueber multer. Hier schickt der Browser die Datei roh im Rumpf, der Name steht im Kopf (URI-kodiert, damit Umlaute heil ankommen). express.raw() bringt den passenden Empfaenger schon mit -- kein Zerlegen von multipart/form-data von Hand, was gerade hier heikel waere. Die drei Punkte, an denen Dateiablagen typischerweise scheitern: 1. Die Dateien liegen AUSSERHALB des Repos (workspace-daten/dateien/). Lagen sie darin, wuerde express.static sie ungeprueft ans Netz geben. 2. Auf der Platte traegt jede Datei einen erzeugten Zufallsnamen, der Originalname steht nur in der Datenbank. Geprueft mit dem Dateinamen "../../etc/passwd": gespeichert wurde "passwd", auf der Platte ein Zufallsname IM Ordner -- nichts ist ausgebrochen. 3. Ausgeliefert wird immer als Download mit neutralem Typ, dazu nosniff und CSP sandbox. Geprueft mit einer hochgeladenen boese.html: kommt als application/octet-stream zurueck, kann also keinen Code im Namen der Domain ausfuehren. Freigabe nach Konzept: Der Zustand "freigegeben" ist eine Abnahme und bleibt dem Management vorbehalten. Geprueft -- Luna kann Entwurf -> Review, aber nicht freigeben (403); eine freigegebene Datei kann sie weder aendern noch loeschen. Sichtbarkeit wie ueberall: Chef 2 Dateien, Luna 1, Mika 0. Zugriff auf eine fremde Datei: 404, nicht 403. Ein Fehler beim Testen gefunden: Bei zu grosser Datei brach express.raw ab, BEVOR der eigene Code lief -- der Fehler landete im allgemeinen Behandler als HTTP 500. Fuer die Nutzerin sah das aus wie ein kaputter Server statt wie "Datei zu gross". Jetzt faengt ein eigener Fehlerbehandler am Ende des Routers das ab und antwortet mit 413 und einer verstaendlichen Meldung. Damit ist Phase 1 aus dem Konzept vollstaendig. |
||
|
|
713297967f |
Workspace: Zurueck-Knopf im Entwurf "Neon-Kante"
Der vorige Knopf war zu generisch -- eine dunkle Pille mit Rand, wie sie in jedem Baukasten steckt. Statt weiter zu raten wurden drei Entwuerfe gebaut und zur Auswahl gestellt (Glas-Kapsel, Neon-Kante, aufklappender Kreis). Gewaehlt: Neon-Kante. Der senkrechte Lichtstrich in Cyan-Violett ist dasselbe Motiv wie die Kante der Anmelde-Tafel (.tafel__kante in gate.css). Dadurch wirkt der Workspace wie ein Stueck und nicht wie zusammengesetzte Teile. Details: - links flache Kante (die gehoert dem Lichtstrich), rechts rund - der Strich ruht etwas kuerzer als der Knopf und waechst beim Ueberfahren auf volle Hoehe -- die Bewegung ersetzt jedes Aufblitzen - der Pfeil rueckt drei Pixel nach links und sagt so die Richtung - bei prefers-reduced-motion faellt alle Bewegung weg - eigener Fokusring fuer die Tastaturbedienung Die drei Entwuerfe liegen als eigenstaendige Seite unter entwurf/ im Arbeitsordner (nicht im Repo), falls spaeter nochmal verglichen werden soll. Nur CSS, kein Neustart noetig. |
||
|
|
f8faab34a5 |
Workspace: versehentlich in den CSS-Ordner kopiertes kopf.js entfernt
Eine verunglueckte Kopierzeile hatte assets/js/kopf.js zusaetzlich nach assets/css/ gelegt. Die Datei war nirgends eingebunden, aber ueber das Netz erreichbar und haette bei spaeteren Aenderungen fuer Verwirrung gesorgt, welche der beiden die echte ist. |
||
|
|
d3212a3f60 |
Workspace: Zurueck-Knopf hochwertiger, auf der Startseite entfernt
Zwei Rueckmeldungen umgesetzt. 1. Auf der Startseite gibt es den Knopf jetzt gar nicht mehr im Dokument. Vorher erschien er dort, sobald man von einer anderen Workspace-Seite kam -- aber die Startseite IST die oberste Ebene, ein "davor" gibt es nicht. 2. Aussehen: statt des Textzeichens "<-" jetzt ein echtes SVG-Winkel- symbol, Pillenform, und ein Verlaufsrahmen. Wichtig dabei: Die Fuellung ist DECKEND. Beim ersten Versuch war sie halbtransparent, dadurch schien der Rahmenverlauf durch die ganze Flaeche und der Knopf sah aus wie eine Hauptaktion -- genau das soll er nicht. Jetzt bleibt vom Verlauf nur der 1px schmale Rand sichtbar. Ruhezustand: dunkle Pille, feine Stahlkante, gedaempfter Text. Ueberfahren: Cyan-Violett-Rand, heller Text, weicher Schein, und der Pfeil rueckt zwei Pixel nach links -- die Bewegung sagt die Richtung ohne zusaetzlichen Text. Bei prefers-reduced-motion faellt sie weg. Geprueft: Start -> kein Knopf; Aufgaben -> "Zurueck", fuehrt zurueck; Personen direkt aufgerufen -> "Uebersicht". Keine Konsolenfehler. Nur statische Dateien, kein Neustart noetig. |
||
|
|
75d05d05d2 |
Workspace: Zurueck-Knopf auf allen Seiten
Wunsch: "ich will auch immer bei jeder seite auch immer einen knopf
haben damit ich zurueck gehen kann auf die seite vorher."
Neues gemeinsames Modul assets/js/kopf.js. Blindes history.back() reicht
dafuer nicht: Wer die Adresse direkt eingibt oder gerade von der
Anmeldung kommt, landet damit ausserhalb des Workspace oder wieder im
Anmeldeformular. Deshalb wird zuerst geprueft, woher der Aufruf kam:
* vorherige Seite im Workspace -> "Zurueck", history.back()
(fuehrt wirklich dorthin zurueck, samt Bildlaufposition)
* kein Verlauf, aber Unterseite -> "Uebersicht", geht zur Startseite
* kein Verlauf auf der Startseite -> Knopf bleibt verborgen
Ein Knopf, der nichts tut, ist schlimmer als keiner.
Der Knopf sitzt links in der Kopfleiste, an derselben Stelle wie im
Browser.
Nebenbei aufgeraeumt: Das Abmelden stand bisher in jeder der fuenf
Seitendateien noch einmal -- fuenfmal derselbe Block, fuenfmal eine
Stelle zum Vergessen. Liegt jetzt ebenfalls in kopf.js.
Geprueft: Start nach Login -> verborgen; Aufgaben von Start ->
"Zurueck", fuehrt zurueck; Kalender direkt aufgerufen -> "Uebersicht".
Keine Konsolenfehler, Abmelden funktioniert weiterhin.
Nur statische Dateien, kein Neustart noetig.
|
||
|
|
ef79659f8f |
Workspace: Kalender mit Terminen, Calls und Aufgaben-Fristen
/workspace/kalender.html -- letzter grosser Baustein aus Phase 1.
Zeigt zwei Quellen in EINER Ansicht, wie im Konzept gefordert
("Kalender & Calls" neben "Deadlines" und "Wiedervorlagen"):
* eigene Termine (Arten: Call, Termin, Review)
* Fristen offener Aufgaben, nur lesend eingeblendet
Nach Tagen gruppiert statt als Monatsraster: Es geht um wenige, dafuer
konkrete Termine. Eine Liste liest sich dabei besser und funktioniert auf
dem Handy ohne Umbau. Zeitraum umschaltbar: 14 / 30 / 90 Tage, wobei 90
den 90-Tage-Plan aus dem Konzept abdeckt.
Sichtbarkeit wie bei den Aufgaben, wieder an einer Stelle definiert.
Geprueft mit drei Konten:
Chef 2 Termine + 2 Fristen | Luna 2 + 1 | Sam 0 + 1
Die Fristen folgen dabei exakt der Aufgaben-Regel -- Sam sieht nur seine.
Loeschen darf das Management und wer den Termin selbst angelegt hat.
Sonst koennte ein Creator einen Call absagen, den das Management
angesetzt hat. Geprueft: Luna auf Chefs Call 403, auf ihren eigenen 200,
Sam sieht ihn gar nicht (404).
Weiteres:
- Ort/Link wird nur dann als Verweis dargestellt, wenn er wirklich mit
http(s) beginnt -- sonst liesse sich javascript: einschleusen
- Zeitpunkt und Dauer werden geprueft (kaputtes Datum 400,
5000 Minuten 400)
- Beginn steht als lokale Zeit ohne Zeitzone: Alle Beteiligten sitzen in
derselben, eine falsch umgerechnete Uhrzeit waere schlimmer als gar
keine Umrechnung
|
||
|
|
a378b126fe |
Workspace: Auswahllisten und Kalender in dunkler Darstellung
Die aufgeklappte Liste eines <select> zeichnet der Browser selbst, CSS erreicht sie nicht. Ohne Hinweis geht er von einer hellen Seite aus und malt sie weiss -- die helle Schrift darauf war praktisch unlesbar (gemeldet mit Screenshot). Behebung ueber color-scheme: dark auf <html>. Das ist der Schalter fuer alles, was der Browser selbst zeichnet: Auswahllisten, Datumskalender, Bildlaufleisten, Textmarkierung. Zusaetzlich sind Hintergrund und Schriftfarbe an <option> gesetzt, weil aeltere Browser und manche Linux-Oberflaechen color-scheme nur teilweise beachten. Damit entfallen die beiden filter: invert(.75) am Kalendersymbol der Datumsfelder -- der Browser zeichnet es jetzt schon hell, invert haette es wieder verdunkelt. Nur statische Dateien, kein Neustart noetig. |
||
|
|
8bfd2ae166 |
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. |
||
|
|
232a2003dd |
Workspace: Aufgaben bearbeiten, Personenverwaltung im Browser
Aufgaben: - Bearbeiten-Dialog (Titel, Beschreibung, Prioritaet, Frist, Zuordnung). Als natives <dialog>: Fokusfang, Esc zum Schliessen und Abdunklung ohne eigenen Code. - Loeschen nur fuer Management, mit Rueckfrage und Protokolleintrag. Das Konzept will, dass Erledigtes stehen bleibt -- Loeschen ist der Ausnahmefall fuer Fehleintraege, nicht der normale Abschluss. Personen (/workspace/personen.html, nur Management): - Anlegen, Code erneuern, sperren/entsperren, Protokollansicht - Der Code wird genau einmal in der Antwort zurueckgegeben, nie gespeichert; beim Schliessen auch aus dem Dokument entfernt - Selbstschutz: niemand kann sich selbst sperren, und das letzte aktive Management laesst sich nicht sperren -- sonst kaeme niemand mehr hinein - Code fuer sich selbst tauschen nur mit ausdruecklicher Bestaetigung, weil es die eigene Sitzung sofort beendet Zwei Fehler, die beim Testen aufgefallen sind: 1. Rollenpruefung fehlte beim Ausliefern der Seiten. Ein Creator bekam personen.html mit HTTP 200 -- die Schnittstellen wiesen ihn zwar ab, das Geruest der Seite war aber sichtbar. GESCHUETZT ist jetzt eine Zuordnung Pfad -> erlaubte Rollen statt einer blossen Liste. 2. Das Protokoll nannte den falschen Verursacher. personAnlegen trug die NEU ANGELEGTE Person als person_id ein, der Eintrag las sich also so, als haette sie sich selbst angelegt. Akteur und Betroffener sind jetzt getrennt: Akteur in person_id, Betroffener im Text. Ueber die Kommandozeile angelegte Personen zeigen korrekt keinen Akteur. |
||
|
|
2eecb40537 |
Workspace: Aufgabenbrett und Dashboard-Zahlen
Erster echter Arbeitsbereich aus dem Konzept. Kanban mit offen / in Arbeit / Review / erledigt, dazu Prioritaet, Frist, Verantwortlicher und Zuordnung zu einem Creator-Bereich. Kern ist die Datentrennung, und die sitzt AUSSCHLIESSLICH im Server -- in jeder einzelnen Abfrage, an einer Stelle definiert (sichtbar()): admin sieht alles creator sieht seinen Bereich und was ihm zugewiesen ist scout sieht nur, was ihm zugewiesen ist Geprueft mit vier Testkonten: - Chef sieht 4, Luna 2, Mika 1, Sam 1 Aufgaben - Luna auf Mikas Aufgabe: 404 (nicht 403 -- sonst liesse sich durch Ausprobieren herausfinden, welche Nummern es gibt) - Luna legt Aufgabe mit creator_id=Mika an: wird still auf ihren eigenen Bereich umgebogen, Mika sieht sie nicht - Anfrage mit fremdem Origin: 403 - ohne Anmeldung: 401, ungueltiger Status/leerer Titel: 400 - Scout bekommt in der Personenliste nur sich selbst - Dashboard-Zahlen je Rolle korrekt eingegrenzt Weitere Punkte: - Texte werden im Browser nur ueber textContent gesetzt, nie innerHTML -- ein Aufgabentitel darf keine Auszeichnung einschleusen - ueberfaellig = Frist vorbei UND nicht erledigt; die Kachel faerbt sich nur, wenn wirklich etwas ansteht - erledigt_am wird gesetzt bzw. wieder geleert, wenn eine Aufgabe zurueckgeholt wird - Erledigtes verschwindet nicht, wie im Konzept gefordert |
||
|
|
6d9bad880c |
Workspace: echte Besucher-IP statt Cloudflare-Adresse
Gefunden beim Nachsehen im Protokoll nach der ersten echten Anmeldung: Jeder Eintrag trug dieselbe IP 172.69.220.140 -- eine Cloudflare-Adresse. Ursache: Die Kette ist Besucher -> Cloudflare -> Caddy -> Express, aber `trust proxy` steht auf 1. Express nimmt daher den letzten Eintrag aus X-Forwarded-For, und das ist Cloudflare. Auswirkung war nicht nur ein unbrauchbares Protokoll, sondern vor allem: ALLE Nutzer teilten sich einen einzigen Sperr-Zaehler. Beim ersten Anmelden waren nach zwei Tippfehlern plus drei Testversuchen bereits 5 von 8 verbraucht -- drei weitere und der Zugang waere fuer alle gesperrt gewesen. Jetzt wird CF-Connecting-IP ausgewertet (setzt Cloudflare bei jeder Anfrage selbst, vom Besucher nicht faelschbar), mit Rueckfall auf die Peer-Adresse. `trust proxy` bleibt bewusst unangetastet, damit die Aenderung nur /workspace betrifft und nicht die ganze Website. Geprueft: 8 Fehlversuche von IP A sperren IP A (429), IP B bekommt weiterhin 401 und kann sich normal anmelden (200). Ausserdem: Spaltenbreite im Protokoll-Ausdruck korrigiert -- bei `anmeldung_fehlgeschlagen` (genau 24 Zeichen) klebte die Rolle am Namen. |
||
|
|
f3a55af80f |
Creator Workspace: Anmeldung, Sitzungen und Audit-Log
Erster funktionierender Login fuer /workspace. Bewusst OHNE neue Abhaengigkeiten: Node 24 bringt node:sqlite mit, Hashing und Zufall kommen aus node:crypto. Nichts zu kompilieren, keine fremde Lieferkette an einer Stelle, an der es um Zugangsdaten geht. Sicherheit: - Codes liegen nur als scrypt-Hash (N=32768) mit eigenem Salt in der DB - Vergleich in konstanter Zeit (timingSafeEqual) - Sitzungstoken 32 Byte Zufall, in der DB nur als SHA-256 - Cookie httpOnly, SameSite=lax, Path=/workspace, secure abhaengig von req.secure (live immer an, nur der lokale http-Test kommt ohne aus) - Sperre nach 8 Fehlversuchen je IP fuer 10 Minuten -- danach ist auch der richtige Code blockiert (geprueft) - gleiche Fehlermeldung bei falscher Rolle und falschem Code - Audit-Log fuer Anmeldung, Fehlversuch, Sperre, Abmeldung, Codewechsel Die Datenbank liegt AUSSERHALB des Repos (../workspace-daten/): sonst waere sie ueber express.static aus dem Netz erreichbar, und ein git pull wuerde Nutzdaten anfassen. workspace.js kann die Website nicht mitreissen: node:sqlite wird erst bei Bedarf per createRequire geladen, jede Route faengt ihre Fehler selbst ab. Faellt die DB aus, antwortet nur /workspace/api/* mit 503. Codes werden ausschliesslich auf der Kommandozeile erzeugt (server/workspace-code.js) und dort einmal angezeigt -- nie in Git. Ausserdem: start.html als geschuetzte Seite nach dem Anmelden. Der Schutz sitzt serverseitig vor express.static, nicht nur im Browser. |
||
|
|
1f769c9de7 |
Workspace-Anmeldung: kompaktere Tafel auf niedrigen Bildschirmen
Auf dem iPhone 13 (nur 664 px nutzbare Hoehe) verdeckte die Tafel das Motiv fast vollstaendig -- sichtbar waren nur die Ohrenspitzen. Neue Regel fuer max-height 760px: kleinere Abstaende und Schriftgroessen, und die Bildflaeche wird flacher. Der zweite Teil ist der eigentliche Kniff: Bei flacherer Bildflaeche skaliert der Ausschnitt nach BREITE statt nach Hoehe, dadurch zeigt er den oberen Teil mit den Gesichtern statt seitlich zu beschneiden. Geprueft auf iPhone 13, iPhone SE und Pixel 5. |
||
|
|
19f969f36f |
Creator Workspace: Anmeldeseite unter /workspace
Erstes sichtbares Stueck des Creator-Workspace-Konzepts. Reine statische Dateien in einem neuen Ordner workspace/ -- express.static liefert den Repo-Ordner aus, die Seite ist damit ohne Servercode-Aenderung und ohne Neustart unter /workspace/ erreichbar. Rein additiv: an bestehenden Dateien wurde nichts geaendert. Motiv: Dogfather und Hasi Dog stehen rechts im Bild, deshalb liegt die Code-Tafel links ueber der ruhigen Flaeche (bei VanVans Buchhaltung ist es gespiegelt, weil dort das Emblem links steht). Drei Layoutfaelle, damit die Figuren in keiner Fenstergroesse von der Tafel ueberdeckt werden. Bewusst nur WebP, kein AVIF: send 0.19.2 (Express 4) kennt AVIF nicht und liefert es als application/octet-stream aus -- wegen des nosniff-Headers verweigert der Browser das Bild dann komplett. Lokal gegen den echten Express-Server geprueft: 0 Konsolenfehler, 0 CSP-Verstoesse. Anmelden funktioniert noch nicht (kein Endpunkt); das Formular sagt das jetzt ehrlich statt "Code stimmt nicht". |
||
|
|
43264c0990 |
Startseite hochwertiger: Typografie, Weltfarben, Event-Karte
Wunsch 27.08.2026: "soll nur noch hochwertiger und professioneller
aussehen." Kein neues Design, sondern die Details, die den Unterschied
zwischen "gemacht" und "gesetzt" ausmachen:
- Ueberschrift enger gesetzt (-.022em, Zeilenabstand 1.08, text-wrap:
balance). Bei 4rem wirkt normale Laufweite auseinandergefallen; die
drei Zeilen verteilen sich jetzt gleichmaessig statt mit kurzer
Restzeile.
- Jede Welt-Kachel traegt die Farbe IHRER Welt statt dreimal derselben:
Babyblau, Silber, das gedeckte Rot von Spicy Media. Die Werte sind
nicht erfunden, sondern die --accent-Toene der drei Themendateien.
- Plaketten ("WELT 1") klein, in Versalien, weit gesperrt -- liest sich
als Kapitelmarke statt als Beschriftung.
- Event-Karte: Datum als gesperrte Versalzeile (ordnet sich dem Titel
unter), "Mehr erfahren" als ruhiger Knopf statt nacktem Textlink,
Haarlinie am Bild, Text auf 5 Zeilen begrenzt -- damit nicht die
Textlaenge aus der Verwaltung das Aussehen der Startseite bestimmt und
bei zwei Events beide Kacheln gleich hoch sind (geprueft: 572/572px).
ZWEI FUNDE BEIM PRUEFEN, BEIDE WICHTIGER ALS DER FEINSCHLIFF:
1. INHALTSRICHTLINIE UND ZEILENENDEN. Der Browser rechnet die Pruefsumme
eines Inline-Skripts NICHT ueber die Bytes der Datei, sondern ueber
den Text im Dokument -- und der HTML-Parser ersetzt beim Einlesen
jedes CR LF durch LF (HTML-Spezifikation, "preprocessing the input
stream"). Eine Datei mit Windows-Zeilenenden ergibt serverseitig also
eine ANDERE Summe, und die Seite fuehrt ihr eigenes Skript nicht mehr
aus: kein Live-Status, keine Events, nichts. Live war es unauffaellig
(Linux-Auscheckung hat LF), auf dem Windows-Rechner sofort tot
(core.autocrlf=true). inhaltsrichtlinie.js vereinheitlicht jetzt vor
dem Rechnen auf LF -- das ist die Summe, die der Browser wirklich
bildet, und macht die Richtlinie unabhaengig davon, mit welchem
Werkzeug eine Datei zuletzt gespeichert wurde.
2. TESTS MIT FESTEM PORT KOENNEN LUEGEN. Drei Server aus frueheren
Laeufen liefen noch. Neue Testlaeufe konnten ihren Port nicht belegen,
starben still -- und massen weiter gegen den ALTEN Code. Ergebnis
waren Fehlermeldungen zu einem laengst behobenen Fehler. pruef-musik,
pruef-kasse und pruef-startseite suchen sich jetzt einen freien Port.
Neuer Test pruef-startseite.mjs (19 Pruefungen) mit FESTEN Event-Daten:
Beim Pruefen kam einmal nichts vom Server, der Abschnitt blieb leer und
der Test haette "kein Fehler" gemeldet, obwohl er nichts gesehen hat.
Geprueft werden Maske am Artwork, genau eine Kachel bei einem Event,
volle Breite, saubere Textkuerzung auf ganze Zeilen, eigene Weltfarben,
zwei gleich hohe Kacheln bei zwei Events, Handy ohne Ueberlauf.
Alles gruen: startseite 19, musik 17, kasse 15, inhaltsrichtlinie 22,
handy 56.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
57d90b9905 |
4 weitere überdimensionierte Bilder verkleinert + Cache-Buster vereinheitlicht
pruef-bildgroessen.mjs (neu) misst systematisch über alle Seiten (Desktop + Handy, mal Pixeldichte), welche <img> größer sind als ihre größte Anzeige. Fand 4 klare Fälle: streamer-mascot.jpg 900px, gezeigt 230px -> 480px 360->119 KB bewerben-poster-dogfather.jpg 800px, gezeigt 204px -> 440px 199->80 KB avatar-bananenstift.jpg 1122px, gezeigt 108px -> 400px 167->25 KB avatar-marina.jpg 1086px, gezeigt 90px -> 400px 157->20 KB Zusammen 639 KB gespart. Avatare bewusst auf 400px (großzügiger als die 2x-Anzeige), falls doch mal eine Detailansicht kommt. Qualität am Maskottchen per Screenshot geprüft: scharf, Schrift lesbar, keine Artefakte. CACHE-BUSTER-BUG behoben: 69 Ressourcen-Verweise standen noch auf dem alten Marker "20260827hero" (einer sogar auf "20260820h"), der Rest auf "20260828c". Verschiedene Seiten luden dieselbe main.css/js unter verschiedenen Cache-Keys -- wiederkehrende Besucher der hero-Seiten bekamen bei Änderungen eine veraltete gecachte Fassung. Die Playwright-Tests sahen das nie (leerer Cache). Jetzt alle 245 einheitlich auf 20260828d; der bewusste admin-auth "-jedesmal"-Marker bleibt unberührt. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
508009ecc7 |
Startseite: Hintergrund-Kanten weg, "Event des Jahres" neu aufgebaut
Gemeldet 27.08.2026: "es ueberschneidet den hintergrund sieht irgendwie scheisse aus." 1. HARTE KANTEN IM HINTERGRUND Das scharfe Trio-Artwork lag als CSS-Hintergrund mit "contain" hinter der Seite und endete an einer messerscharfen Linie (bei 1366x800 exakt bei 570px und 1345px); darueber/darunter lag sichtbar die verwaschene Fassung. Das las sich wie ein aufgeklebtes Band quer ueber der Seite. Jetzt ein echtes <img>, das an seinen EIGENEN Raendern weich in die verwaschene Ebene uebergeht. Entscheidend: Die Maske haengt am Bild, nicht am Fenster -- als CSS-Hintergrund war das unmoeglich, weil die Kante je nach Fensterformat wandert. 2. "EVENT DES JAHRES" -- 700px tote Flaeche Eine 480px schmale Kachel sass zentriert in einer 1180px breiten Kiste, die selbst Rahmen und Hintergrund trug: drei Rahmen ineinander um einen einzigen Inhalt, links und rechts je 350px Leere. Jetzt volle Breite mit Bild links / Text rechts, die umgebende Kiste ist rahmenlos. Hoehe von 656px auf 332px, ohne dass Inhalt verloren geht -- das Bild ist dabei doppelt so gross wie vorher. 3. Welt-Kacheln mit einer Spur Milchglas und feinem Lichtsaum, damit sie zur Szene gehoeren statt als flache Rechtecke daraufzuliegen. BEIM BAUEN GEFUNDEN UND KORRIGIERT: Der erste Entwurf hat die versteckte zweite Event-Kachel wieder sichtbar gemacht (leere Kachel mit einsamem "Mehr erfahren"). Ursache ist die im Code zweimal dokumentierte Falle: ".jahres-event-card[hidden]" ist genau so stark wie eine Zwei-Klassen-Regel und verliert gegen die spaetere. Deshalb steht in den neuen Regeln ueberall :not([hidden]). Versionsnummern von main.css/main.js hochgesetzt -- Assets werden mit max-age=14400 ausgeliefert, sonst haette Dogi die Aenderung bis zu vier Stunden nicht gesehen. Geprueft: pruef-handy (56), pruef-barrierefrei (60), pruef-design (0 Fundstellen), pruef-blickfang (13), pruef-assets (67), pruef-musik (17) -- alle gruen. Der Musik-Knopf holt seine Farben jetzt aus dem <img> statt aus dem CSS-Hintergrund (main.js), sonst waere er ausgerechnet auf der Startseite auf die Ersatzfarbe zurueckgefallen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
e2a40313d6 |
Startseiten-Bilder verkleinert: 3 überdimensionierte Fotos (687 KB gespart)
Der Performance-Check (pruef-tempo.mjs, neu) zeigte die Startseite mit 3,5 MB, überwiegend Bilder. Drei davon waren viel größer als je angezeigt (gemessen über Desktop + Handy, alle Seiten, mal Pixeldichte): card-dogfather.jpg 1200px, angezeigt max 359px -> 720px 534->201 KB casper-4.jpg 1400px, angezeigt max 359px -> 720px 350->115 KB casper-3.jpg 1400px, angezeigt max 359px -> 720px 259->140 KB 720px = doppelte Anzeigebreite, also auch auf Retina-Displays scharf. Qualität an zwei Motiven per Screenshot geprüft: keine sichtbaren Artefakte, Schrift und Details erhalten. card-vanvan bewusst UNANGETASTET: wird auf vanvan.html mit 578 CSS-px gezeigt, bräuchte für Retina ~1156px -- das 1200er ist dort passend. Verkleinert über Browser-Canvas (kein ImageMagick/sharp verfügbar; das gefundene "convert" war das Windows-Dateisystem-Tool, nicht ImageMagick). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
5b2a1daabe |
Öffentliche Avatar-Uploads vor dem Absenden verkleinern (1,1 MB -> ~40 KB)
Der Performance-Check (pruef-tempo.mjs, neu) fand als größte Einzel- ressource ein 1,1-MB-Profilbild, ausgeliefert an jeden Besucher der Startseite und von stimmen.html -- angezeigt wird es handgroß. Ursache: Das öffentliche Stimmen-Formular (stimmen.js) lud Profilbilder ROH hoch. Die Bildaufbereitung vom 27.08. bekam nur die Verwaltung, nicht das öffentliche Formular. So landete das Kamera-Foto in voller Auflösung auf dem Server. - bild-vorbereiten.js (neu): die Verkleinerungs-Funktion, jetzt einmal und parametrisierbar (maxBreite). Verwaltung nutzt weiter 1600px, Avatare 512px. Test pruef-bild-vorbereiten.mjs: 8/8, u.a. 512er-Avatar ~40 KB statt >1 MB. - stimmen.js: verkleinert vor dem Upload (window.bildVorbereiten mit maxBreite 512); fällt die Funktion aus, wird das Original genommen -- kein Upload darf daran scheitern. - stimmen.html: lädt bild-vorbereiten.js; veralteten Kommentar richtiggestellt (behauptete "kein Upload", obwohl es seit 21.08. einen gibt -- mit Bremse, Typ-/Größenlimit, Freigabe-Pflicht). Zwei neue Checks als bleibende Absicherung mit committet: pruef-links.mjs (41 interne Ziele, 0 kaputt) und pruef-assets.mjs (67 Ressourcen, 0 fehlen). NOCH OFFEN: verwaltung.html hat noch eine eigene, identische Inline-Kopie der Funktion -- die Zusammenführung ist ein eigener, testbarer Schritt (das Inline-Skript dort ist groß und die Verwaltung hinter dem Gate schwer live zu testen). Das bestehende 1,1-MB-Bild auf dem Server bleibt, bis es neu hochgeladen/ersetzt wird. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
3e2f5306ad |
Aufraeumen: mein Wegwerf-Messskript _probe2.mjs wieder entfernt
Es ist im vorigen Commit versehentlich mitgegangen, weil der Testlauf in die Zeitgrenze lief und das "rm" danach nie ausgefuehrt wurde. Der Inhalt ist als richtiger Test in pruef-kasse.mjs aufgehoben -- die Wegwerf-Fassung gehoert nicht ins Projekt. Hinweis: pruef-tempo.mjs im selben Commit ist NICHT von mir, es lag unversioniert im Arbeitsordner und wurde von "git add -A" miterfasst. Es bleibt drin, weil Loeschen fremder Arbeit schlimmer waere als ein Commit zu frueh -- Dogi entscheidet. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
8fa15c04f1 |
Kasse und Google-Anmeldung in der Inhaltsrichtlinie freigegeben
Fortsetzung des Musik-Fundes: PayPal und Google haetten an derselben Stelle still versagt, sobald Dogi seine Zugangsdaten eintraegt -- leerer Bereich statt Bezahlknopf, ohne Fehlermeldung, ohne Protokolleintrag. Vorgehen bewusst gemessen statt geraten: Erst die Angaben der Anbieter (PayPal "Best Practices", Google CSP-Abschnitt der Setup-Anleitung), dann im echten Browser mit PayPals offizieller Testkennung "client-id=test" nachgemessen und die Verstoesse ueber das Ereignis securitypolicyviolation eingesammelt. Die Messung hat zwei Dinge gefunden, die in keiner Anleitung standen: - www.sandbox.paypal.com (frame-src + connect-src). Beim Einrichten testet man mit Sandbox-Zugangsdaten; ohne diesen Eintrag haette die Kasse in genau dieser Phase nicht abgeschlossen werden koennen. - accounts.google.com/gsi/style (style-src). 'unsafe-inline' deckt das NICHT ab -- es erlaubt nur Stile im Dokument, keine nachgeladene Stilvorlage. Der Anmelde-Knopf waere unformatiert erschienen. Bewusst einzelne Adressen statt PayPals vorgeschlagener Platzhalter (*.paypal.com): Was die Messung nicht gebraucht hat, steht nicht drin. Skripte bleiben ohne 'unsafe-inline' -- die Pruefsummen-Loesung fuer die eigenen Inline-Bloecke bleibt unangetastet, und ein zusaetzliches 'unsafe-inline' waere neben Pruefsummen ohnehin wirkungslos. Neuer Test pruef-kasse.mjs (15 Pruefungen): laedt das echte PayPal-SDK, baut die Bezahlknoepfe wirklich auf, rendert den echten Google-Knopf und verlangt NULL Verstoesse. Bestandstest pruef-inhaltsrichtlinie.mjs (22) und pruef-musik.mjs (17) bleiben gruen -- inklusive der Gegenprobe, dass eingeschleuste Skripte weiterhin blockiert werden. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
87db17704a |
Test: kein Bild / keine eingebundene Datei fehlt
Prüft, was die öffentlichen Seiten laden: Bilder, og:image/twitter:image, Favicon, Manifest. Eine fehlende Datei zeigt sich als leerer Kasten oder kaputte Teilen-Vorschau, oft unbemerkt. Ergebnis: 67 eigene Ressourcen, alle vorhanden, 0 fehlen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
3a8a52aa49 |
Test: kein interner Link zeigt ins Leere
Sammelt alle internen Links von 24 öffentlichen Seiten und prüft jedes Ziel einmal gegen die echte Domain. Weiterleitungen (3xx, z.B. auf eine Zugangswand) gelten als gültig, nur 4xx/5xx als toter Link. Ergebnis: 41 Ziele, alle in Ordnung, 0 kaputt. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
1d99da8ef8 |
Musik-Knopf repariert: Inhaltsrichtlinie blockierte die Spotify-Player
Gemeldet am 27.08.2026 ("unten rechts laeuft was nicht richtig mit der
Musik"). Ursache lag nicht in der Seite, sondern in der am 26.08.2026
eingefuehrten Inhaltsrichtlinie: "frame-src 'none'" verbietet dem
Dokument jede Einbettung -- und die beiden Player (Hasidog, Van-Van)
sind genau das. Der Knopf reagierte, das Feld ging auf, die Player
blieben leer. Kein Absturz, keine Fehlermeldung auf der Seite; die
Begruendung stand nur in der Browser-Konsole.
- frame-src erlaubt jetzt genau eine Quelle: https://open.spotify.com.
Kein Sternchen, kein 'unsafe-*'. Was im Spotify-Rahmen passiert,
regelt Spotifys eigene Richtlinie.
- Neuer Test pruef-musik.mjs (17 Pruefungen). Er startet bewusst den
ECHTEN server/index.js statt eines Datei-Servers -- ein einfacher
Datei-Server erzeugt die Kopfzeile gar nicht und haette den Fehler
nie gesehen. Geprueft wird im echten Browser, ob die Rahmen wirklich
von open.spotify.com laden (statt einer Fehlerseite), dazu Tippziel,
Position, Schliessen per Knopf und Escape, Handy-Layout.
Beim Nachsehen aufgefallen und NOCH OFFEN: PayPal-Kasse und
Google-Anmeldung auf abonnieren.html laden ihre Skripte und Rahmen
ebenfalls von fremden Adressen. Beide sind derzeit nicht scharf
(googleClientId/paypalClientId sind null), wuerden aber mit derselben
Richtlinie an derselben Stelle still ausfallen, sobald Dogi seine
Zugangsdaten eintraegt. Wird gesondert mit ihm besprochen.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
887da7b09b |
Verwaltung: Gate-Text an die neue Anmelde-Regel angepasst
Unter der Ueberschrift stand weiterhin "gilt danach 24 Stunden" -- seit heute gilt der Code aber nur, solange die Seite/App offen ist. Ein Versprechen, das die Seite nicht mehr einhaelt, ist schlimmer als gar keins: Dogi haette sich sonst darauf verlassen, morgen ohne Code weiterzukommen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
7960b212c8 |
Verwaltung: Zugangscode wieder bei jedem Oeffnen noetig
Dogi hat die Lockerung vom 04.08.2026 ("Code nur einmal am Tag") heute
zurueckgenommen. Gewaehlte Auspraegung: Code beim OEFFNEN der Seite/App,
Neuladen im selben Tab wirft nicht raus, keine Abmeldung bei Untaetigkeit.
- admin-auth.js legt die Sitzung jetzt in sessionStorage statt in
localStorage: gehoert zum Tab/App-Fenster, uebersteht F5 und Uploads,
ist beim naechsten Oeffnen weg. Zweiter Tab = eigener Code.
- Alte localStorage-Sitzung wird beim ersten Laden einmalig entfernt --
sonst waere Dogi trotz Umstellung mit dem alten Token weiter drin und
ein Token laege monatelang im Browser herum.
- Versionsnummer des Skripts hochgesetzt: Caddy liefert Assets mit
max-age=14400, der Browser haette die alte Anmelde-Logik sonst bis zu
vier Stunden weiterbenutzt.
- Der 24-Std-Ablauf bleibt als Rueckfallsicherung fuer tagelang offene
Tabs; der Server erzwingt dieselbe Grenze weiterhin selbst.
- Neuer Test pruef-verwaltung-anmeldung.mjs (12 Pruefungen: Gate beim
Oeffnen, alte Sitzung wirkungslos, falscher/richtiger Code, F5 bleibt
drin, neuer Tab verlangt Code, Abmelden raeumt auf, Untaetigkeit meldet
NICHT ab).
- pruef-verwaltung-kacheln.mjs: echten Aufruf ans Live-Backend abgefangen
und auf die Supporter-Zeilen gewartet -- der Test hatte dadurch
gelegentlich falsch gemeldet.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
0465a7a14a |
Verwaltung: Stimmen als kompakte Kacheln, Supporter-Liste auf 4 gekuerzt
Beides auf Wunsch vom 27.08.2026 ein-/ausklappbar: - Stimmen liegen jetzt in einem Raster (auto-fill, min. 290px) statt untereinander -- am Computer stehen 3-4 Kacheln nebeneinander, am Handy eine. Zugeklappt ist genau die erste Reihe sichtbar; wie viele Kacheln das sind, liest die Logik aus dem Raster selbst aus (getComputedStyle liefert bei auto-fill die gebauten Spuren), damit "erste Reihe" auf jeder Breite stimmt -- inklusive Neuberechnung beim Groessenwechsel. - Supporter-Tabelle zeigt nur noch die 4 zuletzt Angemeldeten, Rest per Knopf. Knopftext nennt immer die Gesamtzahl, damit nichts versteckt wirkt; beim Zuklappen springt die Ansicht sauber zum Listenanfang. - Neuer Test pruef-verwaltung-kacheln.mjs (18 Pruefungen, Computer + Handy, inkl. Groessenwechsel und Ueberlauf-Kontrolle). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
988a2fdc95 |
DEPLOY.md: interner Deploy verifiziert — npm-Warnungen eingeordnet, sleep 6
Der erste echte Lauf des internen Deploy-Blocks (28.08.2026) förderte zwei harmlose, aber verunsichernde Punkte zutage: - npm ci warnt "allow-scripts ... better-sqlite3": Die allowScripts-Sperre blockiert den nativen Build, aber prebuild-install liefert ein fertiges Binary. Dienst lädt danach die DB einwandfrei -> in Ordnung. Als Kontrolle dokumentiert. - sleep 2 war zu kurz: Der health-Check lief, bevor der Dienst auf 4200 hörte -> kurzzeitig 502 / "activating", obwohl gleich darauf alles läuft. Auf sleep 6 erhöht, mit Hinweis, wann ein 502 wirklich ein Problem ist. Deploy selbst war erfolgreich: Bremse greift live (20 durch, 21. -> 429), multer 2.2.0 + node-cron 4.6.0 installiert, better-sqlite3 lädt. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
5c546207e7 |
DEPLOY.md: interner Deploy-Block ohne führendes cd
Der Block scheiterte in der Praxis am ersten "cd /home/dogiintern/...": Das Verzeichnis ist 700, selbst dogi kommt per cd nicht hinein, nur dogiintern über sudo -u. Jetzt git -C statt cd, und das npm ci in einer sudo -u dogiintern bash -c 'cd ... && npm ci'-Shell. Mit Warnhinweis, warum kein cd davor stehen darf. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
68b421b79a |
Zahlungen-Bühne: besserer Bildausschnitt statt flacher Mitte
Das cryo-kristall-Motiv (Zahlungen) ist als einziges der sechs HOCHKANT (702x941) statt quer (1672x941). Auf der breiten 16:9-Bühne zeigt "cover" davon nur einen horizontalen Streifen -- der mittige (Standard center center) traf die unruhigen Kristallsplitter und wirkte flach und beschnitten. Jetzt zeigt Zahlungen den oberen Streifen (background-position center 15%): die eleganten geschwungenen Kristallbänder rechts als Blickfang, links ruhiger dunkler Raum für die Karten -- eine klare Komposition wie bei den anderen Motiven. Nur für Zahlungen überschrieben, die übrigen fünf bleiben mittig. Nebenbei: Der Wert kommt aus --vw-blick, das für alle Bereiche längst definiert, bisher aber nirgends ausgelesen wurde (toter Code). Die neue .vw-buehne-Ausnahme wendet ihn für Zahlungen erstmals an. Service-Worker-Cache v63 -> v64 (beide Stellen), damit Geräte mit der App den neuen Ausschnitt bekommen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
50ff8e0f4c |
DEPLOY.md: voller Deploy-Block für den internen Dienst + Stand nachgezogen
- Vollständiger kopierfertiger Deploy-Block für server-internal MIT npm ci: pull -> npm ci -> Lade-Test der nativen Module -> restart -> Selbsttest, als eine &&-Kette, die bei jedem Fehler VOR dem Neustart abbricht (alter Dienst läuft dann unberührt weiter). Bisher stand dort nur pull + restart ohne npm ci -- genau die Lücke, durch die Code und Pakete auseinanderliefen. - Wächter: 18 -> 19 Punkte (Sicherungsprüfung dazugekommen). - Zwei offene Punkte ergänzt: ausstehender Server-Neustart (Kernel/OpenSSL liegen bereit) und der ausstehende server-internal-Deploy. - Veralteten Datenschutz-404-Eintrag abgehakt (heute live 401 verifiziert). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
9bd9357185 |
Überschriftenordnung im Hauptinhalt: keine übersprungenen Ebenen mehr
Fünf Seiten hatten einen Sprung h1 -> h3 im Hauptinhalt (die Kachel- Überschriften waren h3, ohne h2 dazwischen). Für Bildschirmleser fehlte damit eine Ebene im "Inhaltsverzeichnis" der Seite (WCAG 1.3.1). Pro Seite der passende, optik-erhaltende Weg -- jede Änderung mit Screenshot bzw. gemessener Schriftgröße gegengeprüft: - werte, kontakt, index: Kachel-h3 -> h2 (die Kacheln SIND die Hauptabschnitte unter der h1). Neue Regel .card > h2 hält die kompakte h3-Optik (1.62rem); Varianten .card-brand/.card-feature bleiben über ihre eigenen h3-Regeln unberührt. Gemessen: 25.92px vorher = nachher. - bewerben: die schon sichtbaren Gruppenlabels (Community / Agentur) waren <span> -> jetzt <h2> mit derselben Klasse. Optik per Screenshot identisch (zentriert, uppercase, Zierstrich). - links: die Kacheln nutzen Spezial-Varianten mit eigenen Größen, ein Tag-Wechsel wäre riskant -> stattdessen eine unsichtbare Gruppen- überschrift (.sr-only, neue Klasse nach WCAG-Standard). Ändert die Optik nicht, vervollständigt aber die Ordnung für Bildschirmleser. Cache-Buster 20260828b (main.css geändert: .card > h2, .sr-only). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
8a2943df35 |
Fußzeilen-Spaltentitel h4 -> h2 (Überschriftenordnung, WCAG 1.3.1)
Die drei Fußzeilen-Spaltentitel (Filipe, Dogi&Hasi & Manager, Mehr) waren <h4>, obwohl der Hauptinhalt bei h2/h3 endet -- ein Sprung in der Überschriftenordnung (h2 -> h4) auf jeder Seite. Für Bildschirmleser ist die Überschriftenliste das Inhaltsverzeichnis; eine übersprungene Ebene stört die Orientierung. Jetzt <h2> (eigenständige Abschnitte unter der Seiten-h1). Die Optik bleibt exakt: Der CSS-Selektor .footer-grid h4 wurde zu .footer-grid h2 umgezogen, die kleine, gedämpfte Darstellung (.85rem, uppercase, Akzentfarbe) überschreibt weiterhin die große globale h2-Größe. Teil des Barrierefreiheits-Durchgangs (pruef-barrierefrei.mjs). Behebt die reinen Fußzeilen-Fälle; die Hauptinhalt-Überschriften folgen separat. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
8a2d2b802a |
Wächter-Protokoll hält sich selbst klein (keine unbegrenzte Log-Datei)
waechter.log wuchs unbegrenzt: alle 5 Minuten eine Zeile, ~288/Tag. Ohne Grenze irgendwann zu groß zum Durchsehen -- dann verliert das Protokoll seinen Zweck. Der Wächter kürzt jetzt selbst, statt logrotate: Das bräuchte eine Datei in /etc (kein Schreibrecht) und einen Extra-Dienst. Vor dem Anhängen wird nur die Größe abgefragt (billig); erst über 1 MB (~45 Tage) wird die Datei einmal gelesen und auf die jüngsten 2000 Zeilen (~1 Woche) gestutzt. Beim Bauen einen eigenen Fehler gefangen: statSync war in waechter.mjs nicht importiert (beim Auslagern der Sicherungsprüfung mit entfernt worden). node --check meldet das nicht -- es hätte erst zur Laufzeit im nächsten Cron-Lauf gekracht. Import ergänzt. Test pruef-protokoll-kuerzen.mjs: klein bleibt unangetastet, groß wird auf die JÜNGSTEN Zeilen gestutzt (älteste fallen weg), Grenzfall und fehlende Datei sauber. 8/8. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
91bf0755cb |
Test: alle Admin-Routen automatisch auf Zugriffsschutz prüfen
Liest alle 109 /admin/-Routen direkt aus index.js (nicht hartkodiert) und spricht jede mit einem ungültigen Token an. Erwartet 401/403. Der Sinn: Der Schutz sitzt in jedem einzelnen Handler statt als Sperre vor der Gruppe. Wer eine neue Route hinzufügt und den Aufruf vergisst, macht sie unbemerkt öffentlich -- das fällt beim Klicken nie auf. Weil der Test die Routen aus dem Code liest, taucht eine künftig vergessene Absicherung automatisch als Fehlschlag auf, ohne dass jemand den Test pflegt. Ungültiges Token statt gar keins ist der gefahrlose Weg, das auch für schreibende Routen (anlegen, löschen) gegen die echte Domain zu tun: Der Schutz greift vor dem Handler, es kann nichts verändert werden. Ergebnis live: 109 geschützt, 0 auffällig. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
a612a0f0fd |
Anfragebremse für /submit und /testimonials/submit (die letzten zwei ungebremsten Routen)
Bei der Bestandsaufnahme als "keine Route hat eine Bremse" gemeldet -- das war falsch (grep suchte nach rateLimit/bremse, im Code heißen sie Kontingent/Sperre/Fehlversuche). Fast alle öffentlichen Routen SIND gebremst: Anmeldung, Supporter-Login, Upload, Kundenanfragen. Übrig blieben genau zwei schreibende Routen: Bewerbungen und Stimmen. Ohne Bremse könnte ein Skript die Datenbank mit Müll fluten. Kein Sicherheitsleck (beide landen in einer Warteschlange, nichts wird ungesehen veröffentlicht), aber eine sinnvolle Härtung. Statt das vorhandene Muster ein drittes Mal zu kopieren: ein Baustein lib/kontingent.js, den nun alle drei Routen nutzen. Zwei Verbesserungen gegenüber dem Original in testimonials.js: - getrennte Töpfe je Zweck (ein Bild-Upload verbraucht kein Bewerbungs-Kontingent) - Selbstreinigung: die alte Zähler-Map ließ jede IP für immer im Speicher stehen (langsames Leck), die neue räumt abgelaufene Einträge auf Grenzen: Uploads 10/Stunde/IP (belegen Plattenplatz), Text-Einreichungen 20/Stunde/IP (großzügig für geteilte Anschlüsse, stoppt Fluten). Tests: pruef-kontingent.mjs (Baustein, 7/7), test-kontingent-routen.mjs (echte Routen liefern 429 ab Grenze, getrennte Töpfe, IPs unabhängig, 4/4). Bestehende Tests unverändert grün (37/37). NOCH NICHT LIVE: server-internal läuft aus /home/dogiintern (kein Zugriff), wird mit den übrigen Server-Änderungen in einem Deploy live geschaltet. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
55775b53ea |
Beleg: die multer-DoS-Lücke ist in unserem Setup nicht auslösbar
Gestern als "kritisch, 5 Anfragen töten den Dienst dauerhaft" gemeldet.
Bei genauer Prüfung heute stimmt das nicht:
- index.js hat einen process.on("uncaughtException")-Handler, der genau
das Prozess-Ende abfängt, das CVE-2025-7338 beschreibt. In drei
Angriffsvarianten (roher Socket, chunked ohne Abschluss-Chunk,
content-length-Lüge) blieb der Dienst am Leben.
- Die öffentliche Upload-Route hat eine eigene IP-Bremse (10/Stunde) und
multer-Härtung; trust proxy ist gesetzt, die Bremse greift pro echter IP.
multer 1.4.5 hat die CVE trotzdem (Fakt) — das Update auf 2.x bleibt
richtig als Wurzelbehandlung, ist aber Hygiene, kein Notfall. Dieser Test
dokumentiert den Nachweis, damit die Einordnung nachvollziehbar bleibt.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
46563b02fc |
Waechter merkt jetzt, wenn die naechtliche Sicherung ausfaellt
Beim Nachweis des ersten automatischen Laufs gefunden: Der Cron-Eintrag endet auf >/dev/null 2>&1 -- jede Fehlermeldung wird verworfen. Das Sicherungsskript fuehrt zwar ein eigenes Protokoll, aber alles, was VOR der ersten Protokollzeile schiefgeht (Skript geloescht, sqlite3 weg, Platte voll, Cron gestoppt), passiert spurlos. Niemand haette es gemerkt -- ausser in dem Moment, in dem man die Sicherung braucht. Der Waechter prueft ab sofort die Datei selbst, nicht das Protokoll: Ein Protokoll kann "erfolgreich" melden, waehrend die Datei fehlt. In lib/ ausgelagert, weil waechter.mjs beim Import sofort seinen ganzen Durchlauf startet -- testbar war die Funktion dort nicht. Beim Testentwurf einen eigenen Fehler gefunden: readdirSync wirft sowohl bei fehlendem Ordner (ENOENT) als auch bei fehlenden Rechten (EACCES). Die erste Fassung behandelte beides als "kein Urteil" und haette damit einen geloeschten Sicherungsordner verschwiegen. Jetzt getrennt: ENOENT meldet, EACCES schweigt. Die 26-Stunden-Grenze ist bewusst nicht enger: Kurz vor dem naechsten Lauf ist die juengste Sicherung regulaer 24 Stunden alt. Genau dort entstehen die Fehlalarme, nach denen man die Meldungen abschaltet -- und dann geht der eine echte mit unter. Als eigener Testfall abgesichert. 14 von 14 Pruefungen bestanden. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
f8f6a26cdf |
Tastatur-Test prüft die Wirkung des Sprungs, nicht sein Vorhandensein
Ein Sprunglink, der den Fokus nicht mitnimmt, sieht im Quelltext korrekt aus und ist in der Benutzung wertlos. Der Test betätigt ihn deshalb wirklich und misst, wo der nächste Tab landet. Beim Nachmessen auf der Live-Seite fiel auf: Der Fokus liegt sofort nach dem Sprung korrekt auf <main>, wandert rund 300 ms später aber auf <body>. Drei Hypothesen einzeln geprüft und alle widerlegt -- sanftes Scrollen (aus: unverändert), Animationen (aus: unverändert), Fensterfokus im Testbrowser (document.hasFocus() bleibt true). Ein Abfangen sämtlicher focus()- und blur()-Aufrufe ergab keinen einzigen Aufruf aus dem Seitencode. Es ist browserinternes Verhalten und folgenlos: die Sprungmarke für die Tab-Reihenfolge bleibt gesetzt, nachgewiesen auf allen fünf Seiten. Zählung korrigiert: Die erste Fassung suchte nur Links und Knöpfe und meldete auf Formularseiten "Nr. 0 von 67, -1 Punkte gespart", weil das Ziel dort ein Eingabefeld ist. Jetzt dieselbe Liste wie beim Zählen, und unsichtbare Elemente fliegen raus. 25 von 25 bestanden. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
efe98e32a7 |
Sprung zum Inhalt: Seite ohne Maus in einem Schritt bedienbar
Der Tastatur-Test (pruef-tastatur.mjs, neu) zeigte auf allen fünf geprüften Seiten dasselbe Bild: jedes Bedienelement erreichbar, Fokus durchgehend sichtbar, keine Tastaturfalle -- aber kein Sprunglink. Ohne ihn muss sich jemand, der die Tastatur benutzt, auf JEDER Seite erneut durch das komplette Menü tabben (31 bis 52 Punkte), bevor der eigentliche Inhalt beginnt. WCAG 2.4.1 verlangt genau diesen Ausweg. An einer Stelle gelöst statt in 35 Dateien: renderHeader() in main.js setzt das Sprungziel und stellt den Link davor. Das tabindex="-1" am <main> ist der Teil, der meistens fehlt: ohne ihn verschiebt der Sprung in Chrome und Safari nur die Bildlaufleiste, der Tastaturfokus bleibt in der Navigation -- der nächste Tab landet wieder im Menü und der Sprung war wirkungslos. Cache-Buster auf 20260827a (244 Stellen), sonst bekäme niemand die geänderte main.js und main.css ausgeliefert. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
b99e3043b6 |
Bilder werden vor dem Hochladen aufbereitet
Das Bild "Event des Jahres" liegt als PNG mit 2524 KB auf dem Server und macht damit zwei Drittel der gesamten Startseite aus. Angezeigt wird es mit 246 bis 420 Punkten -- die Datei hat 1672. PNG ist fuer ein Foto ausserdem das falsche Format. Der Fehler passiert beim Hochladen: Ein Handy liefert Fotos in voller Kameraaufloesung, und niemand denkt vorher ans Verkleinern. Es ist auch nicht die Aufgabe dessen, der ein Bild aussucht -- sondern die der Seite, die es entgegennimmt. IM BROWSER, NICHT AUF DEM SERVER Serverseitig braeuchte es eine Bildbibliothek mit nativem Code, installiert in einem Verzeichnis, an das ich nicht herankomme. Der Browser kann das ohnehin: Canvas skaliert und kodiert seit jeher. Nebeneffekt: Schon der Upload wird kleiner. Wer vom Handy aus ein 8-MB-Foto hochlaedt, wartet sonst am Mobilfunknetz. DREI ENTSCHEIDUNGEN 1. Kleine PNGs bleiben unangetastet. Ein Logo mit durchsichtigem Hintergrund wuerde als JPEG einen schwarzen Kasten bekommen. Die Grenze liegt bei 400 KB -- darunter ist es wahrscheinlich eine Grafik, darueber praktisch immer ein Foto. 2. Wird die Datei NICHT kleiner, bleibt das Original. Bei bereits gut komprimierten Bildern kann erneutes Kodieren sogar zulegen -- und Qualitaet kosten fuer nichts. 3. Schlaegt irgendetwas fehl, wird das Original hochgeladen. Ein misslungenes Verkleinern darf niemals einen Upload verhindern. GEPRUEFT Grosses PNG 3000px: 130 -> 55 KB, auf 1600px begrenzt. Grosses JPEG 3000px: 269 -> 131 KB. Kleines PNG: unveraendert, bleibt PNG. Keine Skriptfehler. Gilt fuer alle drei Upload-Stellen: Event-Bild, Team-Foto, Stimmen-Bild. Das vorhandene 2524-KB-PNG bleibt davon unberuehrt -- es liegt schon auf dem Server. Ein einmaliges Neu-Hochladen ueber die Verwaltung ersetzt es durch die aufbereitete Fassung. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
5deb920f39 |
Fuenf Bilder auf die tatsaechlich benoetigte Groesse gebracht
Die Startseite uebertrug 3,72 MB. Gemessen wurde, wie breit jedes Bild tatsaechlich angezeigt wird -- in Handy- UND Desktop-Ansicht, denn die groessere der beiden bestimmt, wie gross die Datei sein MUSS. Wer nur am Handy misst und danach verkleinert, macht die Desktop-Ansicht unscharf. casper-2.jpg 1050px -> 420px 245 KB -> 47 KB dogfather-casper-2.jpg 1050px -> 420px 182 KB -> 35 KB dogfather-portrait.jpg 933px -> 420px 122 KB -> 35 KB casper-1.jpg 720px -> 420px 153 KB -> 72 KB dogfather-casper-1.jpg 770px -> 400px 150 KB -> 63 KB 600 KB weniger. Die Zielbreiten liegen bewusst ueber dem gemessenen Bedarf (408-411px gemessen, 420px gesetzt): Ein Bild kleiner zu machen als noetig waere schlimmer als ein zu grosses -- Unschaerfe sieht man, ein paar Kilobyte nicht. Qualitaet vor dem Ersetzen angesehen, nicht nur die Zahl: Augen, Fell und Kanten sind bei 420px unveraendert scharf. NICHT ANGEFASST, WEIL ZU KLEIN char-dogfather.jpg und char-hasidog.jpg haben 599px, brauchen auf grossen Schirmen aber 954px. Sie sind also UNSCHAERFER als noetig -- das ist der umgekehrte Fall und gehoert getrennt betrachtet. DER GROESSTE BROCKEN BLEIBT Das Bild "Event des Jahres" ist ein PNG mit 2524 KB -- zwei Drittel der ganzen Startseite. PNG ist fuer ein Foto das falsche Format; als JPEG in der benoetigten Breite (840px) waeren es rund 150 KB. Die Datei liegt unter /var/lib/dogfather-internal/uploads und ist ueber den Verwaltungsbereich hochgeladen worden -- dort wird nicht umgewandelt. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
b037867a99 |
Tests beenden sich jetzt sauber (process.exitCode statt process.exit)
Der Absturz auf dem Server bestand nach dem ersten Fix fort: node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr Statement::~Statement() … better_sqlite3.node Abgebrochen DER ERSTE VERSUCH GING AN DER URSACHE VORBEI Ich hatte db.close() entfernt -- naheliegend, weil der Aufrufverlauf auf einen Statement-Destruktor zeigte. Es half nicht. Die Ursache liegt eine Ebene tiefer: process.exit() beendet Node SOFORT, waehrend better-sqlite3 noch offene Statements haelt. Deren Aufraeumhaken laeuft dann ins Leere. process.exitCode setzt nur den Rueckgabewert; Node beendet sich danach von selbst, sobald nichts mehr aussteht -- und raeumt dabei in der richtigen Reihenfolge auf. WARUM DAS MEHR ALS EIN SCHOENHEITSFEHLER WAR Der Absturz kam NACH allen Pruefungen und VOR der Zusammenfassung. Der Test meldete einen Fehler, obwohl inhaltlich alles bestanden war. In einer mit && verketteten Befehlsfolge blieb deshalb der anschliessende Dienst-Neustart aus, und die neuen Endpunkte antworteten weiter mit 404. Gesucht habe ich bei den Endpunkten, beim Deploy, an der Zugangswand -- die Ursache lag beim Beenden eines Testprozesses. BEMERKENSWERT Fuenf Tests im Projekt benutzten process.exitCode bereits. Das Muster war also etabliert; meine neuen Dateien wichen davon ab, ohne dass es jemandem auffiel. Sechs Tests sind jetzt angeglichen, alle geprueft: Rueckgabewert 0, Zusammenfassung vollstaendig. test-push-kette 30, test-altabbruch 16, test-webdesign-anfragen 37, test-webdesign-portal 49, test-webdesign-paypal 28, test-personendaten 15 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
76b88c6b71 |
Tests: kein db.close() vor process.exit()
Auf dem Server brach test-personendaten.mjs ab: node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr Statement::~Statement() … better_sqlite3.node Abgebrochen better-sqlite3 raeumt seine Statements ueber einen Aufraeumhaken ab. Wird die Verbindung unmittelbar vor dem Prozessende geschlossen, laeuft dieser Haken ins Leere. DIE FOLGE WAR SCHLIMMER ALS DER ABSTURZ Der Absturz kam NACH allen Pruefungen, aber VOR der Zusammenfassung. Der Test lieferte also einen Fehlercode, obwohl inhaltlich alles bestanden war. In der mit && verketteten Befehlsfolge blieb deshalb der anschliessende Dienst-Neustart aus -- und die neuen Endpunkte antworteten weiter mit 404. Man sucht dann den Fehler bei den Endpunkten, beim Deploy, an der Zugangswand. Die Ursache lag beim Aufraeumen einer Testdatenbank. Node schliesst die Verbindung beim Beenden ohnehin. Zusaetzlich faengt das Loeschen der Testdateien jetzt Fehler ab: Ohne db.close() haelt der Prozess sie noch offen, unter Windows scheitert das Loeschen dann mit EBUSY -- und "force" hilft dagegen nicht, es unterdrueckt nur "Datei nicht gefunden". Ein Test, der an seinem eigenen Aufraeumen scheitert, meldet einen Fehler, den es fachlich nicht gibt. Beide betroffenen Tests geprueft: Rueckgabewert 0, Zusammenfassung vollstaendig. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
e6e7ab033e |
DEPLOY.md: die heutigen Werkzeuge und der Stand der offenen Punkte
Nach einem Tag mit vielen Aenderungen beschrieb die Anleitung weder, was neu automatisch laeuft, noch stimmte ihre Liste offener Punkte. NEU AUFGENOMMEN - Zwei Zahlen beim Webdesign-Deploy: CACHE_NAME in sw.js UND die Nummer in der Registrierungsadresse. Mit Begruendung, warum eine allein nicht reicht -- Cloudflare ersetzt das "no-cache" des Servers durch vier Stunden, gemessen am 26.08.2026. - Was per Cron laeuft: Sicherung (taeglich 03:15) und Waechter (alle fuenf Minuten), samt Probeschalter und der Grenze, die bleibt. - Die Verwaltung als eigene App, und warum ihr Manifest in der Ausnahmeliste der Zugangswand stehen muss. OFFENE PUNKTE NACHGEPRUEFT STATT ABGESCHRIEBEN Der Eintrag "Hintergrundbilder liegen nur auf dem Server" stimmt nicht mehr: Alle genannten Dateien und der Avatar-Ordner sind versioniert, "git status --untracked-files=all -- assets/" meldet auf dem Server null. Als erledigt gekennzeichnet, nicht geloescht -- eine Liste, in der Erledigtes ungekennzeichnet steht, wird beim naechsten Mal gar nicht mehr gelesen. Der CORS-Punkt war schaerfer formuliert als die Lage: Die Antwort enthaelt KEIN Access-Control-Allow-Credentials, und die Verwaltung weist sich ueber "Authorization: Bearer" aus statt ueber ein Cookie. Ein Browser schickt bei einer fremden Seite also weder Cookies noch das Token mit; erreichbar sind nur die ohnehin oeffentlichen Endpunkte. Sauberer waere eine feste Herkunftsliste -- das ist Haertung, keine Reparatur, und gehoert als eigener Vorgang mit Live-Test angefasst. Ergaenzt: express 5 ist vorbereitet aber bewusst nicht umgestellt, und die Datenschutz-Endpunkte warten auf einen Pull in /home/dogiintern. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
40714b382e |
"Es fehlt: —." war ein Satz ohne Aussage
Beim optischen Durchsehen der Handy-Aufnahmen gefunden: Im Bereich
Zahlungen stand woertlich
NOCH NICHT EINGERICHTET
Es fehlt: —. Solange sagt das Kundenportal freundlich Bescheid …
Kommt die Liste der fehlenden Angaben leer zurueck -- etwa weil der
Server sie nicht mitschickt --, fiel der Text auf einen Gedankenstrich
zurueck. Man liest eine Fehlermeldung, die nicht sagt, was fehlt, und
sucht dann bei den Zugangsdaten statt bei der Antwort des Servers.
Jetzt zwei getrennte Saetze: einer, wenn bekannt ist WAS fehlt, und
einer fuer den Fall, dass nur bekannt ist DASS etwas fehlt.
Ein Rueckfallwert, der aussieht wie eine Angabe, ist schlechter als
gar keiner -- er beantwortet die Frage scheinbar und schickt einen in
die falsche Richtung.
Nebenbei geprueft und in Ordnung: Der Installationshinweis am unteren
Rand verdeckt nichts dauerhaft; er schafft sich seinen Platz ueber ein
gemessenes padding-bottom (steht seit dem 23.08.2026 so drin, damals
blockierte er den Annehmen-Knopf).
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
504965e875 |
Push-Zustand macht der Uebersicht keinen Platz mehr streitig
Ein Gesamtdurchlauf aller Suiten nach dem heutigen Tag hat vier Fehlschlaege in pruef-handy.mjs gezeigt -- eine Regression aus meiner eigenen Arbeit. DIE URSACHE Die Karte zum Zustand der Benachrichtigung stand als ausgewachsener Block ganz oben in der Uebersicht. Auf dem Handy schob sie die erste Kennzahl auf 513 Punkte nach unten: Man oeffnete die Verwaltung und sah zuerst einen Hinweis, die Arbeit erst nach dem Scrollen. ZWEI ANLAEUFE, WEIL DER ERSTE ZU KURZ SPRANG Zuerst wurde nur der GUTE Fall zu einer Zeile. Das half nichts -- im Pruefbrowser sind Benachrichtigungen blockiert, also griff weiterhin der Zweig mit der grossen Karte. Eine Aenderung, die nur den Fall behebt, den man gerade vor Augen hat, ist keine. Jetzt sind alle drei Zustaende eine Zeile mit Punkt und Kurztext, die Erklaerung erst beim Aufklappen. Und der gute Zustand steht am ENDE der Uebersicht statt oben: Eine Bestaetigung, dass nichts zu tun ist, gehoert nicht an den Anfang. Oben bleibt nur, was Handlung verlangt. Ergebnis: 513 -> 204 Punkte bis zum Ende des Kopfbereichs, Laenge 2635 -> 2028. DREI RUNDUNGSFAELLE .wd-btn--klein und zwei summary-Elemente kamen mit Rahmen und Zeilenhoehe auf 43,x Punkte. Der Test vergleicht mit "< 44", gibt aber gerundet "44" aus -- man sucht dann einen Fehler in einer Zahl, die richtig aussieht. Jetzt 44.5 bzw. 46; optisch aendert das nichts. ZWEI PRUEFUNGEN PRAEZISIERT, NICHT AUFGEWEICHT 1. "Erste Zahl im oberen Drittel" mass ab dem Seitenanfang und schlug damit auch bei einer BERECHTIGTEN Warnung an. Ein Test, der Warnungen als Mangel zaehlt, erzieht dazu, sie zu verstecken. Er misst jetzt den festen Kopfbereich (Titel, Suche, Reiter) -- also das, was man nicht wegbekommt. 2. Die Folgepruefung mass zunaechst Punkte-Abstaende. Das war fragil: Der Reiterstreifen klebt beim Scrollen oben fest, eine Messung lieferte -204. Sie zaehlt jetzt HINWEISBLOECKE statt Punkte -- unabhaengig von Scrollposition und Schriftgroesse. Gemeint war ohnehin "hoechstens eine Meldung", nicht "hoechstens 140px". 56/56 in pruef-handy, alle uebrigen Suiten unveraendert gruen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
1c3b3808ba |
Express 5: geprueft, aber noch nicht umgestellt
Der Sprung 4 auf 5 entfernt Methoden, aendert die Pfadsyntax
grundlegend (path-to-regexp 8) und stellt Standardwerte um. Vieles
davon faellt nicht beim Start auf, sondern erst, wenn eine bestimmte
Adresse aufgerufen wird -- also im Betrieb, bei einem Kunden. Deshalb
zuerst pruefen statt installieren.
ZWEI PRUEFUNGEN, DIE SICH ERGAENZEN
pruef-express5.mjs durchsucht 89 Dateien nach den Bruchstellen aus dem
offiziellen Migrationsleitfaden: entfernte Aufrufe (res.send(zahl),
req.param, app.del, res.sendfile), die UMGEDREHTE Reihenfolge bei
res.redirect, geaenderte Pfadsyntax ("/*" ohne Namen, ":a?"),
schreibgeschuetztes req.query, entfernte static-Optionen.
server/test-express5.mjs startet einen echten Server mit genau unserem
Aufbau: Middleware-Kette mit eigenen Kopfzeilen, cookieParser,
express.json, eine umleitende Schranke, express.static, ein Router mit
Parameter, 404- und Fehlerbehandlung.
ERGEBNIS
15 von 15 Kategorien unbedenklich, 10 von 10 Laufpruefungen bestanden
gegen express 5.2.1. Der Umstieg waere ohne Codeaenderung moeglich.
Ein Fehlalarm lag dabei im Pruefer selbst: Er meldete drei
req.body-Zugriffe als ungesichert, die in Wahrheit innerhalb eines
"if (typeof req.body?.feld === 'boolean')" stehen -- der Rueckblick war
mit 60 Zeichen zu kurz fuer den Block. Ein Pruefer, der abgesicherte
Stellen anmahnt, kostet die Zeit, die er sparen soll, und beim
naechsten Mal glaubt man ihm auch die echten Funde nicht mehr.
NICHT UMGESTELLT
Bewusst. Der Bestand ist sicherheitstechnisch sauber (npm audit: 0),
express 4.22.2 wird weiter gepflegt, und der Nutzen waere gering
gegenueber dem Risiko, zwei laufende Dienste anzufassen. Die Vorarbeit
liegt vor -- wenn umgestellt wird, dann als eigener Vorgang mit
anschliessendem Live-Test, nicht nebenbei.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
c03793b25f |
Oberflaeche fuer Auskunft und Loeschung
Sitzt im Kunden-Bereich, zugeklappt. Ein eigener Reiter waere zu prominent fuer etwas, das man vielleicht zweimal im Jahr braucht -- ganz weglassen hiesse, im Ernstfall von Hand durch ein Dutzend Tabellen zu suchen, bei laufender Monatsfrist. DREI SCHRITTE, IN DIESER REIHENFOLGE Auskunft erstellen unschaedlich, jederzeit Loeschvorschau zeigt, was betroffen waere Loeschen nur nach Vorschau, mit doppelter Eingabe Die Auskunft laesst sich als Datei herunterladen, nicht nur ansehen. Man muss sie der Person schicken koennen, und zwar vollstaendig -- abtippen waere der sicherste Weg, etwas zu vergessen. JSON, weil Art. 20 DSGVO ein "gaengiges, maschinenlesbares Format" verlangt. Die Vorschau zeigt beides getrennt: was geloescht wuerde, und was bleiben muss -- mit Grund und mit dem Datum, ab dem es weg darf. Ohne diese Angabe muesste man bei jeder Anfrage neu nachschlagen, welche Frist gilt. Vor dem Loeschen wird die Adresse ein zweites Mal verlangt. Der Unterschied zwischen .de und .com ist ein Buchstabe, die Folge unwiederbringlich. GEPRUEFT Im Handy-Format mit nachgebildeten Antworten: Auskunft zeigt die Bereiche samt Kennzeichnung "aufbewahrungspflichtig", der Herunterladen-Knopf erscheint, die Vorschau trennt richtig, eine abweichende Bestaetigung wird abgelehnt. Kein Ueberlauf, keine Skriptfehler. Die Handy-Suite bleibt bei 25/25, keine Namenskollision. Ein Fehler lag dabei im Test selbst: Playwright prueft die ZULETZT registrierte Route zuerst, und meine allgemeine Auffangregel ueberdeckte die spezifischen. Die Auskunft meldete "nichts gespeichert", obwohl die Oberflaeche richtig arbeitete. Die Endpunkte dazu liegen in server-internal und brauchen noch einen Deploy durch Filipe (/home/dogiintern, Rechte 700). Bis dahin meldet die Oberflaeche einen 404 -- mit dem Hinweis, dass der Server einen Neustart mit dem neuen Stand braucht, statt nur "Fehler" zu sagen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
7b09d752f8 |
Auskunft und Loeschung nach DSGVO (Art. 15/17)
Schreibt jemand "Welche Daten haben Sie ueber mich?" oder "Bitte loeschen Sie meine Daten", laeuft eine Frist von einem Monat (Art. 12 Abs. 3 DSGVO). Bisher haette man dafuer von Hand durch ein Dutzend Tabellen suchen muessen -- und uebersieht man eine, ist die Auskunft unvollstaendig, ohne dass man es ihr ansieht. ⚠️ LOESCHEN IST NICHT EINFACH LOESCHEN Der naheliegende Weg waere ein "Alles loeschen". Das waere bequem und doppelt falsch: Rechnungen und Buchungsbelege unterliegen einer gesetzlichen Aufbewahrungspflicht -- § 147 Abs. 3 AO nennt acht Jahre fuer Buchungsbelege, zehn fuer Handelsbuecher, gerechnet ab dem Ende des Kalenderjahres (Abs. 4). Wer sie auf Zuruf loescht, verstoesst gegen Steuerrecht, um Datenschutzrecht zu erfuellen. Die DSGVO nimmt diesen Fall selbst aus (Art. 17 Abs. 3 lit. b). Fuer das, was bleiben muss, sieht sie die Einschraenkung der Verarbeitung vor (Art. 18) -- genau so wird es ausgewiesen, samt Datum, ab dem geloescht werden darf. Dasselbe bei Widerrufserklaerungen: Sie sind der Nachweis, DASS und WANN widerrufen wurde. Wer sie loescht, vernichtet seinen eigenen Beleg in genau der Sache, in der es spaeter Streit geben koennte. EIN FEHLER, DEN DER TEST VOR DEM ERSTEN LAUF GEFUNDEN HAT Die Tabelle wd_widerrufe fuehrt die Adresse nicht als "email", sondern als "kontakt". Die erste Fassung suchte nach "email" -- sie haette dort NIE einen Treffer geliefert, und die Auskunft haette trotzdem sauber ausgesehen. Aufgefallen nur, weil die Testdaten gegen das echte Schema angelegt wurden statt gegen die Annahme. Deshalb steht in der Quellenliste jetzt die Spalte, nicht eine Vermutung. WEITERE ENTSCHEIDUNGEN - Die Suche geht ueber die Adresse UND ueber die Kundenkennung. Vieles haengt nicht an der Adresse; ohne diesen Schritt bliebe die halbe Auskunft leer. - Gross- und Kleinschreibung spielt keine Rolle -- sonst bekaeme jemand eine leere Auskunft, weil er seine Adresse anders schreibt. - Die Loeschung verlangt die Adresse ein zweites Mal. Der Unterschied zwischen .org und .com ist ein Buchstabe, die Folge unwiederbringlich. - Sie laeuft in einer Transaktion: Bricht sie ab, bliebe sonst ein halbgeloeschter Bestand, und niemand wuesste, welcher Teil weg ist. - Der Vorgang wird protokolliert, aber OHNE die geloeschten Inhalte -- sie dabei erneut zu speichern waere das Gegenteil des Zwecks. 15 Pruefungen gruen, mit Gegenprobe (fremde Adresse liefert nichts). Die Oberflaeche in der Verwaltung folgt. Deploy braucht Filipe: server-internal liegt unter /home/dogiintern mit Rechten 700. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
ac1d5c9916 |
Waechter: Rechte wurden gesetzt, aber wirkten nicht
Nachgemessen: /var/lib/dogfather-waechter stand auf 755 -- weltweit lesbar, mit einer vollstaendigen Liste aller Dienste, Adressen und ihres Zustands darin. Also genau das, was der Commit von heute Nachmittag verhindern sollte. Die Absicht stand im Code, die Wirkung fehlte. Zwei Gruende, beide still: 1. "mode" bei mkdirSync gilt nur, wenn das Verzeichnis dabei NEU entsteht. Beim zweiten Lauf existiert es immer -- dann laesst mkdirSync die Rechte unberuehrt. Hier war es sogar schon vor dem Einbau der Zeile angelegt worden. 2. Selbst beim Neuanlegen zieht die umask des Prozesses Bits ab. Der Unterschied zur Sicherung ist lehrreich: /var/backups/dogfather steht korrekt auf 700, weil dort ein ausdrueckliches chmod im Skript steht. Dieselbe Ueberlegung, einmal umgesetzt und einmal nur gemeint. Jetzt chmod bei jedem Lauf, fuer Verzeichnis UND Dateien. Aufgefallen ist es nur, weil die Rechte nachgesehen wurden statt angenommen -- der Code sah richtig aus. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
6077c1700f |
server-internal: package-lock.json aufgenommen
Nach der Installation auf dem Server vom dortigen Stand uebernommen -- nicht hier erzeugt. Das ist der entscheidende Unterschied: Eine lokal erzeugte Datei haette festgeschrieben, was HIER aufgeloest wird, nicht was dort tatsaechlich laeuft. Genau diese Luecke sollte sie ja schliessen. Festgeschrieben sind jetzt: multer 2.2.0 (war 1.4.5-lts.1, veraltet) node-cron 4.6.0 (war 3.0.3) express 4.22.2 (package.json sagt ^4.21.2) better-sqlite3 11.10.0 dotenv 16.6.1 cors 2.8.6 express 4.22.2 zeigt, warum das noetig war: Die package.json erlaubt alles unter 5.0, installiert war eine andere Fassung als die genannte. uuid taucht in der Liste nicht mehr auf. Es kam ausschliesslich ueber node-cron 3.x herein und war die einzige Luecke, die "npm audit" gefunden hatte. Gegenprobe nach der Umstellung: "found 0 vulnerabilities". Auch die Veraltet-Markierung von multer ist weg. Der Dienst laeuft stabil (PID unveraendert ueber mehrere Messungen, keine zusaetzlichen Neustarts) und antwortet mit 401 -- er lebt also und prueft Rechte. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
a2487212c8 |
multer 2.x und node-cron 4.x: geprueft, package.json angehoben
multer 1.4.5-lts.1 ist von den Betreuern ausdruecklich als veraltet markiert. Die Meldung der Registry, woertlich: "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version." ⚠️ BEMERKENSWERT: "npm audit" meldet dazu NICHTS. Die Pruefung nannte nur eine Luecke in uuid, eingeschleppt ueber node-cron 3.x. Wer sich allein auf audit verlaesst, haette multer fuer unbedenklich gehalten. Die Deprecation-Meldung ist hier die eigentliche Warnung -- sie steht aber an einer Stelle, an die man nur kommt, wenn man gezielt nachfragt. 2.0.0 behebt CVE-2025-47935 und CVE-2025-47944. Einzige dokumentierte Breaking Change: Node ab 10.16. Auf dem Server laeuft 24. node-cron 4.x behebt die uuid-Luecke. Die Fassung ist eine Umstellung auf TypeScript, ohne dokumentierte API-Aenderung -- genau dabei aendert sich aber gern die Art des Standard-Exports, und "import cron from 'node-cron'" wuerde danach beim START scheitern, nicht bei der Installation. GEPRUEFT STATT ANGENOMMEN In einem eigenen Verzeichnis gegen multer 2.2.0 und node-cron 4.6.0 getestet, mit genau den Aufrufen aus index.js: diskStorage mit destination/filename, limits, fileFilter, single/array/fields, MulterError samt code, cron.schedule("* * * * *") und task.stop(). 15 von 15 bestanden. Die Pruefung liegt jetzt als server-internal/test-pakete.mjs im Projekt -- die Frage "laeuft unser Code damit noch?" stellt sich bei jedem Hauptversionswechsel neu. Express bleibt bewusst bei 4.x. Der Sprung auf 5 ist ein eigener Vorgang mit deutlich mehr Flaeche; ihn hier mitzunehmen wuerde zwei unabhaengige Risiken in einem Schritt buendeln. Die Installation selbst braucht Filipe: server-internal liegt unter /home/dogiintern mit Rechten 700. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
f64403aea8 |
package-lock.json wird versioniert (server/)
Ohne Lock-Datei darf jede Installation andere Fassungen ziehen: "^4.21.2" erlaubt alles unter 5.0. Auf dem Server laeuft deshalb express 4.22.2, waehrend in der package.json 4.21.2 steht -- geprueft wird also nie genau das, was ausgeliefert wird. Solche Unterschiede fallen nicht beim Deploy auf, sondern im Betrieb, und dann sucht man den Fehler im eigenen Code. WARUM SIE AUSGESCHLOSSEN WAR Ein "git pull" auf dem Server scheiterte daran: Git ueberschreibt keine unverfolgte Datei -- unabhaengig davon, ob ihr Inhalt derselbe ist. Der Ausschluss hat den Deploy repariert und dabei den Zweck der Datei beseitigt. Nachgemessen statt vermutet: Die Datei hier und die auf dem Server sind Byte fuer Byte identisch (SHA-256 a50b028d…). Es gab also nie einen inhaltlichen Konflikt, nur einen formalen. Er loest sich, indem die Datei einmal vom Server entfernt und danach aus dem Repo geholt wird. DEPLOY.md ergaenzt: auf dem Server "npm ci" statt "npm install". ci loescht node_modules vorher und baut streng nach der Lock-Datei; es schreibt sie nie um und bricht ab, wenn sie nicht zur package.json passt -- statt still etwas anderes zu installieren. server-internal/ folgt, sobald die dortige Lock-Datei vorliegt. Dieses Verzeichnis liegt unter /home/dogiintern mit Rechten 700. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
ea11cefe17 |
Verwaltung auf dem Handy: vier Fehler behoben
Geprueft mit nachgebildeten Daten -- lange Firmennamen, lange
E-Mail-Adressen, mehrstellige Betraege. Eine leere Seite laeuft nie
ueber; die Fehler, um die es geht, entstehen erst mit Inhalt.
1. DAS RASTER DER KOPFZEILE WAR BREITER ALS SEIN BEHAELTER
Auf 390px summierten sich die beiden Spalten auf 372,7px, waehrend die
Leiste nur 358px breit ist. Rasterspalten schrumpfen von sich aus nicht
unter ihren Inhalt ("min-width: auto"). Alles darin schob sich nach
rechts hinaus: die Kopfzeile 8px, der Reiterstreifen 12px.
Sichtbar war das kaum -- der Streifen ist ohnehin wischbar, rechts
fehlte nur ein Stueck Rand. Genau deshalb blieb es liegen: ein Fehler,
der sich als Eigenart tarnt. Jetzt minmax(0, …) plus min-width: 0 an
den Kindern; lange Titel kuerzen mit Auslassungspunkten, statt zu
schieben.
2. DER INSTALLATIONSHINWEIS ZEIGTE DAS FALSCHE SYMBOL
Seit die Verwaltung eine eigene App ist, stand dort weiter der schwarze
Husky. Man las "Als App installieren" neben dem einen Bild und bekam
das andere auf den Startbildschirm. Das Symbol wird jetzt aus dem
Manifest abgeleitet, das die Seite tatsaechlich einbindet -- damit
stimmt es auch fuer jede kuenftige App, ohne dass jemand daran denken
muss.
3. DIE UEBERSICHT BRACH BEI UNVOLLSTAENDIGER ANTWORT AB
d.projekte und d.verlauf wurden ungeprueft mit .length angefasst,
waehrend drei andere Felder ausdruecklich geprueft wurden. Fehlte eine
der Listen, brach die Uebersicht mit "Cannot read properties of
undefined" ab -- und zwar NACH dem Aufbau der oberen Kacheln: Die halbe
Seite stand da, der Rest fehlte kommentarlos. Genau der Fall, den der
Kommentar daneben als "realistisch" beschreibt. Jetzt wird aufgefuellt
statt abgebrochen.
4. ZWEI FEHLALARME IM TEST SELBST
Der Test meldete den Reiterstreifen als Ueberlauf (er ist ein
Wischstreifen -- dass dort etwas ausserhalb liegt, ist sein Sinn) und
eine 1x1-Checkbox als zu kleines Tippziel (bedient wird sie ueber ihr
Label, 315x162 Punkte). Beides wuerde dazu verleiten, Funktionierendes
"zu reparieren". Der Test unterscheidet das jetzt.
Ebenso meldete er "Strg" und "Enter" mit 10,4px -- beide sind auf
schmalen Schirmen laengst ausgeblendet. getComputedStyle liefert auch
fuer verborgene Elemente eine Schriftgroesse; jetzt zaehlt nur
Sichtbares.
25 Pruefungen ueber sechs Bereiche gruen.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
62cbd724e2 |
Manifest der Verwaltungs-App an der Zugangswand freigeben
Beim ersten Live-Test war die App nicht installierbar: Symbole 200, Manifest 302. Die Zugangswand hat eine Ausnahmeliste fuer App-Bausteine -- sw.js und app.webmanifest stehen ausdruecklich darin, mit einer ausfuehrlichen Begruendung aus dem Universe. Das neue Manifest fehlte schlicht. Der Grund gilt hier sogar staerker als bei der oeffentlichen App: Die Verwaltung liegt IMMER hinter der Schranke. Ohne Freigabe bekommt der Browser statt des Manifests eine Umleitung auf zugang.html, also HTML statt JSON -- und bietet "App installieren" gar nicht erst an. Unbedenklich: Das Manifest enthaelt Name, Farben, Symbolpfade und drei Verknuepfungen. Keine Kunden-, Projekt- oder Preisdaten. Wer es liest, erfaehrt, dass es eine Verwaltung gibt -- was die Zugangswand ohnehin verraet. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
1608521854 |
Verwaltung ist jetzt eine eigene App
Wunsch: "ich will die verwaltungsseite auch getrennt runter laden koennen und installieren koenne." Bisher gab es ein Manifest fuer den ganzen Webdesign-Bereich; die Verwaltung war darin nur eine Verknuepfung. Jetzt hat sie ein eigenes mit eigener "id" -- der Wert, an dem alles haengt: Ohne ihn halten Browser beide fuer dieselbe App, und die zweite Installation ueberschreibt die erste, statt danebenzustehen. EIGENES SYMBOL Zwei gleich aussehende Kacheln auf dem Startbildschirm waeren keine Trennung. Das neue Symbol ist ein Ausschnitt des Eiskristall-Wappens -- dasselbe Motiv, das die Verwaltung ohnehin traegt, und klar unterscheidbar vom schwarzen Husky der oeffentlichen App. Erzeugt aus cryo-wappen.webp in drei Groessen, die beschneidbare Fassung weiter herausgezoomt, damit Android die Spitzen des Wappens nicht abschneidet. Als JPEG statt PNG: 76 statt 385 KB bei 512 Punkten. Bei einem fotografischen Motiv hat PNG nichts zu gewinnen, und 385 KB fuer ein Symbol waeren unverhaeltnismaessig. ⚠️ DER GELTUNGSBEREICH IST ABSICHTLICH WEIT Naheliegend waere "/webdesign/verwaltung" gewesen. Das haette die App unbrauchbar gemacht: Ohne gueltigen Ausweis leitet der Server auf /webdesign/zugang.html um -- ausserhalb des Bereichs, und was ausserhalb liegt, oeffnet der Browser in einem eigenen Fenster. Weil der Zugang beim Schliessen endet, waere das bei fast jedem Start passiert: Man tippt auf die App und landet im Browser. Nachgemessen: verwaltung.html antwortet ohne Ausweis mit 302. EIN STILLES VERSPRECHEN EINGELOEST Die Verknuepfungen zeigen auf "?bereich=anfragen" und dergleichen -- und derselbe Parameter steht in den Push-Meldungen. Ausgewertet hat ihn bisher NIEMAND. Man landete immer auf der Uebersicht, ohne dass etwas kaputt aussah. Jetzt oeffnet die Seite den gewuenschten Reiter, ueber einen ausgeloesten Klick auf den vorhandenen Reiter statt ueber nachgebaute Logik: Daran haengen Signatur, Leuchtbalken, Nachladen und der Wischstreifen auf dem Handy. Auch hier hat erst der Test den Fehler gezeigt -- und danach einen in der Pruefung selbst: Sie erreichte die Funktion gar nicht, weil diese im gekapselten Bereich liegt. Jetzt nach aussen gegeben, wie schon window.vwBalkenSetzen. 23 Pruefungen gruen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
92d2b568de |
Pruefung auf doppelte Funktionsnamen — und ein zweiter Fund
Die Kollision von heute war weder fuer den Browser noch fuer einen Syntaxpruefer sichtbar: Zwei Funktionen desselben Namens sind erlaubtes JavaScript, die spaetere gewinnt lautlos. Auch die Oberflaechen-Tests schwiegen -- sie pruefen, ob Kacheln DA sind, nicht ob sinnvoller Text darin steht. pruef-namenskollision.mjs durchsucht jetzt alle 110 Dateien mit eigenem JavaScript. Mit Gegenprobe, damit die Pruefung nicht selbst kaputtgehen und dabei "sauber" melden kann. ZWEITER FUND, AELTER ALS MEIN FEHLER Sie meldete sofort eine weitere Kollision: tageSeit stand zweimal in verwaltung.html. Beide rechneten dasselbe, mit einem Unterschied -- bei fehlendem Datum gab die eine null zurueck, die andere 0. Die spaetere (mit 0) gewann. Damit war die frueher definierte wirkungslos, und mit ihr die Pruefungen "if (t === null) return ''" in altersText und dringlichkeit: Ohne Datum stand dort "seit heute" statt gar nichts. Entfernt wurde die spaetere. Die beiden verbliebenen Aufrufer vertragen null genauso wie 0 -- nachgeprueft, nicht angenommen: Math.max(x, null) ergibt x, und null >= 7 ist falsch, gleich wie bei 0. 110 Dateien jetzt sauber. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
0e8c1447f2 |
DRINGEND: Namenskollision zerstoerte die Uebersicht
Alle Kacheln der Uebersicht zeigten "[object Object]" und "undefined". Ursache war mein Push-Code von heute. Er brachte eine Hilfsfunktion namens kachel(titel, inhalt, art) mit -- und weiter oben in derselben Datei gibt es seit langem kachel(o), die die Uebersichtskacheln baut. JavaScript kennt keine Ueberladung. Steht spaeter eine zweite Funktion desselben Namens im selben Gueltigkeitsbereich, gewinnt sie. Ohne Warnung, ohne Fehlermeldung, ohne dass irgendein Werkzeug anschlaegt. Danach bekam jede Uebersichtskachel mein Objekt als ersten Parameter und gab es als Zahl aus. Besonders tueckisch: Die Push-Karte selbst funktionierte einwandfrei -- sie steht ja unmittelbar ueber den kaputten Kacheln. Der sichtbare Schaden lag weit weg von seiner Ursache, und nichts deutete auf den neuen Code hin. Die Funktion heisst jetzt pushKachel und sagt damit, wozu sie gehoert. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
77b395e2b4 |
Test: laesst den Service Worker jetzt wirklich arbeiten
Die Luecke, durch die der Fehler von heute bis zum Benutzer durchgerutscht ist. Der Test hat 48 Seiten in einem echten Browser geoeffnet und nichts bemerkt -- weil er den Service Worker nie hat arbeiten lassen. Er war gruen und wertlos zugleich: Der entscheidende Weg wurde nicht begangen. Neu geprueft wird jetzt: - der Service Worker meldet sich an und wird aktiv - er darf tatsaechlich etwas abrufen (das war der kaputte Punkt) - dabei entsteht kein Richtlinien-Verstoss - sw.js traegt selbst KEINE Richtlinie, denn sie wuerde zu SEINER Ausserdem umgedreht: Ein Test verlangte fuer Nicht-Seiten ausdruecklich "default-src 'none'" -- und sicherte damit genau den Fehler ab, der die App lahmlegte. Er haette den naechsten Versuch, es richtig zu machen, als Fehler gemeldet. Jetzt wird geprueft, dass CSS, JavaScript, Bilder und der Service Worker KEINE Richtlinie bekommen. 22 Pruefungen gruen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
0f253df134 |
Service Worker: Versionsnummer in der Adresse gegen fremden Cache
Bei der Fehlersuche zur lahmgelegten App kam ein zweiter, aelterer
Mangel zum Vorschein.
GEMESSEN
Dienst selbst Cache-Control: no-cache
nach Cloudflare Cache-Control: max-age=14400 (auch bei MISS)
Cloudflare ersetzt die Vorgabe des Servers durch vier Stunden. Ursache
ist eine feste "Browser Cache TTL" in den Einstellungen. Der Kommentar
in server/index.js behauptet das Gegenteil ("Cloudflare respektiert
laut Doku ein vom Origin gesetztes Cache-Control") -- fuer diese Domain
stimmt das nicht. Nachgemessen, nicht vermutet.
WARUM DAS MEHR ALS EIN SCHOENHEITSFEHLER IST
Jede Aenderung am Service Worker erreicht die Geraete bis zu vier
Stunden zu spaet. Solange alles laeuft, faellt das nicht auf. Ist der
ausgelieferte Stand aber fehlerhaft -- wie heute, als eine zu strenge
Inhaltsrichtlinie ihn lahmlegte und die App "Keine Verbindung" zeigte
--, sind es vier Stunden, in denen sich nichts reparieren laesst.
Genau dann, wenn Tempo zaehlt, ist man am langsamsten.
LOESUNG OHNE FREMDE EINSTELLUNGEN
Die Registrierung laedt jetzt "/webdesign/sw.js?v=56". Aendert sich die
Nummer, ist es eine andere Adresse -- dafuer kann kein Zwischenspeicher
einen alten Stand haben. Die Nummer wird zusammen mit CACHE_NAME
hochgezaehlt; beide gehoeren zusammen und stehen jetzt auf 56.
Das wirkt unabhaengig davon, wie Cloudflare eingestellt ist. Die
Einstellung selbst sollte trotzdem geprueft werden -- sie betrifft auch
CSS und JavaScript.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
c591925342 |
DRINGEND: Inhaltsrichtlinie legte die App lahm
Symptom: Die installierte App zeigte "Keine Verbindung", obwohl die Seite online war, der Server lief und jede Pruefung Erfolg meldete. Ursache war meine eigene Zeile von heute Vormittag. Fuer alles, was keine Seite ist, setzte die Middleware "default-src 'none'" -- mit dem Gedanken "kostet nichts und schadet nie". Der zweite Halbsatz war falsch. Ein Service Worker uebernimmt die Inhaltsrichtlinie, die beim Herunterladen SEINER EIGENEN Skriptdatei gesetzt war, nicht die der Seite, fuer die er arbeitet. sw.js ist keine Seite, bekam also 'none' und durfte damit nichts mehr abrufen. Jede Anfrage scheiterte -- und weil der Service Worker fuer genau diesen Fall eine Offline-Seite bereithaelt, sah es aus wie ein Netzausfall beim Benutzer. Fuer Bilder, Stylesheets und Schriften bringt eine Richtlinie ohnehin nichts: Sie steuert, was ein DOKUMENT nachladen darf. Ein Bild laedt nichts nach. Dem Schaden stand also nie ein Gewinn gegenueber. Jetzt: Richtlinie nur noch fuer HTML-Dokumente. Warum der Test das nicht gefunden hat, folgt gleich -- er hat die Seiten geladen, aber nie den Service Worker arbeiten lassen. Genau die Luecke, durch die es durchgerutscht ist. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
b2f4567cce |
Waechter: leere Geraeteliste und fehlende Tabelle unterscheiden
Der erste echte Probelauf meldete "kein Geraet angemeldet". Richtig -- aber dieselbe Meldung waere auch erschienen, wenn die Tabelle gar nicht existierte, weil dbLesen in beiden Faellen "" zurueckgibt. Das sind zwei voellig verschiedene Lagen: Einmal genuegt ein Klick in der Verwaltung, einmal ist die Migration nicht gelaufen. Wer die falsche Meldung liest, drueckt auf Einschalten, sieht keine Wirkung und sucht dann beim Browser statt bei der Datenbank. Genau die Sorte Verwechslung, gegen die dieser ganze Strang gebaut wurde. Jetzt wird zuerst gefragt, ob die Tabelle da ist, und die Meldung sagt im harmlosen Fall auch gleich, was zu tun ist. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
bcf6e01fa9 |
Waechter: engere Dateirechte und ein Probeschalter
Der erste automatische Lauf war erfolgreich (14:20, 18 Punkte, 0 auffaellig). Zwei Nachtraege: RECHTE Zustandsdatei und Protokoll lagen mit 644 in einem 755-Verzeichnis. Personenbezogene Daten stehen dort keine -- wohl aber eine vollstaendige Liste aller Dienste, Adressen und ihres Zustands. Fuer jemanden, der einen Angriff vorbereitet, ist das eine bequeme Landkarte. Jetzt 700/600. Kostet nichts, also gibt es auch keinen Grund, es herzugeben. PROBESCHALTER node waechter.mjs --probe Verschickt eine Meldung, ohne dass etwas kaputt sein muss. Das ist die einzige Moeglichkeit, den MELDEWEG zu pruefen, ohne auf eine echte Stoerung zu warten. Der Grund ist derselbe wie beim stillen "if (!url) return;", das diese Reihe ausgeloest hat: Eine Ueberwachung, deren Zustellung stillschweigend nicht funktioniert, ist schlimmer als gar keine. Man haelt die Stille dann fuer "alles in Ordnung" -- dabei ist sie nur Stille. Steht bewusst VOR den Messungen: Wer den Meldeweg pruefen will, soll nicht erst 18 Punkte abfragen muessen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
3a5805bfad |
Waechter: meldet Ausfaelle, statt sie unbemerkt zu lassen
Der Befund war besser als der Aufgabentitel: Alle Dienste haben
Restart=always und stehen nach einem Absturz von selbst wieder auf. Die
Luecke liegt woanders:
- Dauerschleife: Startet ein Dienst und stuerzt sofort wieder ab, gibt
systemd nach wenigen Versuchen auf. Dann bleibt er unten.
- "active" heisst nicht "antwortet". Ein haengender Dienst gilt
systemd als gesund.
- Zertifikatsablauf und volle Platte machen keinen Dienst inaktiv,
legen aber alles lahm.
Geprueft werden 18 Punkte: sechs Dienste, neun Adressen, Plattenplatz,
zwei Zertifikatslaufzeiten.
WARUM ALS ROOT PER CRON
Ein Waechter, der ueber den internen Dienst meldet, hat einen
Konstruktionsfehler mit Ansage: Ausgerechnet wenn DIESER Dienst das
Problem ist, kaeme keine Meldung durch. Also liest er .env und Datenbank
selbst und verschickt selbst -- unabhaengig davon, ob noch irgendetwas
laeuft. Ohne Fremdpakete, nur Node-Bordmittel, sqlite3 und systemctl.
NUR BEI ZUSTANDSWECHSEL
Gemeldet wird, wenn etwas kippt -- in beide Richtungen. Nicht alle fuenf
Minuten dasselbe. Wer staendig Meldungen bekommt, sieht irgendwann keine
mehr an und uebersieht die eine, auf die es ankam.
ZWEI EIGENE FEHLER, DIE DER TEST GEFUNDEN HAT
1. Zuerst stand je Adresse eine handgepflegte Liste erlaubter
Antwortcodes. Der erste Lauf meldete VanVans Shop als ausgefallen --
er war es nicht, er steht ebenfalls hinter einer Zugangswand und
antwortet mit 302. Ich war damit genau in die Falle gelaufen, vor der
der Kommentar an derselben Stelle warnte.
2. Danach galt "unter 400" als heil. Jetzt meldete das Postfach einen
Ausfall, weil die geprueften Adresse 404 lieferte -- der Dienst lief
einwandfrei, ich hatte die Adresse falsch gewaehlt.
Beide Male dieselbe Lehre: Eine Ueberwachung, die bei einer falsch
getippten Adresse "Ausfall" ruft, erzieht einen dazu, ihre Meldungen zu
ignorieren. Die Regel lautet jetzt "unter 500", denn der Waechter fragt
"lebt der Dienst?", nicht "ist der Inhalt richtig?". 401, 403 und 404
BEWEISEN, dass jemand da ist und zuhoert. Nur 5xx und Schweigen heissen,
dass dahinter nichts mehr laeuft. Ausnahme sind die beiden Pflichtseiten
Impressum und Widerruf -- dort ist alles ausser 200 bereits ein Mangel.
GEPRUEFT AM SERVER
Ausfall eingebaut: erkannt und gemeldet. Ausfall dauert an: still, keine
Wiederholung. Wieder erreichbar: Entwarnung. 18 Punkte, 0 Fehlalarme
ueber mehrere Laeufe.
⚠️ GRENZE, DIE BLEIBT
Ist der Server als Ganzes weg -- Netz, Strom, Hardware --, meldet auch
dieser Waechter nichts. Dagegen hilft nur eine Ueberwachung ausserhalb
der Maschine. Steht so im Kopf der Datei, damit sich niemand in falscher
Sicherheit wiegt.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
03a533f143 |
Inhaltsrichtlinie (CSP): mit Pruefsummen statt mit unsafe-inline
Fuenf Schutz-Kopfzeilen waren gesetzt, die wichtigste fehlte. Sie entscheidet als einzige darueber, ob eingeschleuster Text zu ausgefuehrtem Code wird oder sichtbarer Text bleibt. WARUM NICHT DER BEQUEME WEG Ueblich waere script-src 'self' 'unsafe-inline'. Eine Zeile, nichts geht kaputt -- und der Schutz ist weg: Der Browser kann nicht unterscheiden, ob ein Skript im Seitentext vom Entwickler stammt oder von einem Angreifer. Das Ergebnis ist eine Kopfzeile, die gut aussieht und im Ernstfall nichts tut. Der Bestand liess den sauberen Weg zu: keine fremden Skriptquellen, keine externen Schriften, ein Inline-Block je Seite. Von jedem Block wird die Pruefsumme gebildet. DIE PRUEFSUMMEN STEHEN BEWUSST NICHT IM CODE Das waere hier eine Falle mit Ansage: Statische Dateien gehen per "git pull" live, OHNE Neustart. Eine fest hinterlegte Summe waere nach der naechsten Textaenderung falsch -- und die Seite wuerde ihr eigenes Skript nicht mehr ausfuehren. Sichtbar erst im Browser des Besuchers, nicht beim Deploy, und aussehend wie kaputtes JavaScript. Deshalb liest die Middleware die Datei selbst und merkt sich das Ergebnis, solange die Aenderungszeit gleich bleibt. Ein Test aendert index.html im laufenden Betrieb und prueft, dass die Summe nachzieht und die Seite weiterlaeuft. WAS DER TEST GEFUNDEN HAT Die erste Fassung haette die Startseite und stimmen.html beschaedigt: Team-Fotos, Event des Jahres und die Stimmen kommen von der postfach-Subdomain, img-src erlaubte nur 'self'. Der Deploy haette Erfolg gemeldet, der Server waere gestartet -- und die Bilder waeren weg gewesen. Gefunden, weil der Test alle 48 Seiten in einem echten Browser oeffnet und mitschreibt, was blockiert wird. Ein zweiter Fehlschlag lag am Test selbst: Er verlangte eine Pruefsumme auf jeder Seite, auch auf denen ohne Inline-Block. Ein Test, der Unmoegliches fordert, wird frueher oder spaeter abgeschaltet -- er unterscheidet jetzt nach dem tatsaechlichen Inhalt der Datei. DREI onclick-ATTRIBUTE ENTFERNT Sie haetten 'unsafe-inline' erzwungen. Zweimal ein "Coming soon"-Knopf, dessen onclick nur Klicks abfing -- ein <a> ohne href tut das von selbst, ganz ohne Skript. Einmal ein Schliessen-Knopf im Verwaltungsbereich, jetzt mit angehaengtem Zuhoerer. GEGENPROBE Ohne sie waere der Rest wertlos: Eine Richtlinie, die alles erlaubt, blockiert auch nichts und besteht jede Pruefung. Der Test schleust deshalb echten Code ein -- ein Inline-Skript und eines von fremder Adresse -- und beide muessen scheitern. style-src behaelt 'unsafe-inline': 587 style-Attribute im Bestand, deren Umbau ein echtes Risiko fuers Aussehen waere, bei kleinem Gewinn. Ueber Stile laesst sich verschleiern und ueberdecken, aber kein Code ausfuehren. Bleibt als eigener Punkt auf der Liste. 15 Pruefungen gruen, 48 Seiten sauber, Portfolio-Suite unveraendert 35. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
094a69732b |
Portfolio: Spicy SafeAddress als sechstes Projekt
Die Rechtsplattform der Spicy Media Agency fehlte -- dabei ist sie das technisch anspruchsvollste Stueck der Sammlung. WAS IM TEXT STEHT UND WARUM SO Die Projektdokumentation haelt ausdruecklich fest, dass die Wendung "100 Prozent rechtssicher" dort nirgends verwendet wird. Ein Portfolio, das mehr verspricht als die Sache selbst, waere genau der Fehler, den das Projekt vermeiden will. Im Ergebnis steht deshalb, dass die Plattform fertig gebaut und bewusst noch vollstaendig gesperrt ist und auf die schriftliche Freigabe einer Fachkanzlei wartet -- nicht, dass sie "im Einsatz" sei. KEIN LIVE-KNOPF Wie bei der Buchhaltung. Ein "Live ansehen", das auf eine Zugangswand fuehrt, bricht das Versprechen, das der Knopf gibt. Stattdessen steht dort ein Satz, der den Grund nennt. GESICHTER UNKENNTLICH Auf der Zugangswand stehen zwei Profilfotos. Eines davon gehoert einer Geschaeftspartnerin, die dieser Veroeffentlichung nicht zugestimmt hat. Beide sind in der Aufnahme weichgezeichnet, das Firmenzeichen bleibt scharf. Begruendet im alt-Text und in einem eigenen Abschnitt -- genau wie bei Projekt 5, wo derselbe Fall schon einmal auftrat. Nebenbei: Die strenge Inhaltsrichtlinie der Seite (style-src 'self') hat das eingefuegte Stylesheet fuers Weichzeichnen blockiert. Sie tut also genau das, wofuer sie da ist. Ueber die Stil-Eigenschaft im Skript ging es dann. ZWEI STELLEN, DIE SONST FALSCH GEBLIEBEN WAEREN Die Ueberschrift hiess "Fuenf Projekte, die man anfassen kann". Mit SafeAddress steht dort erstmals ein Projekt, das man NICHT besuchen kann -- die Ueberschrift haette also gleich doppelt daneben gelegen. Jetzt: "Sechs Projekte, die es wirklich gibt", und die Einleitung sagt ausdruecklich, dass eine der Seiten gesperrt ist. Der Schlussaufruf lud dazu ein, "das sechste" Projekt zu werden. Bei sechs vorhandenen waere das das Angebot gewesen, eines davon zu ersetzen. Jetzt das siebte. Beides in allen fuenf Sprachen, de-CH als echter Dialekt wie im Rest dieser Datei. TEST Zwei Fehlschlaege, beide berechtigt: die Projektzahl und ein zu grobes Suchmuster, das bei "zwei MENSCHEN" ansprang, obwohl es die veraltete Aussage "zwei PROJEKTE" sucht. Das Muster laesst jetzt hoechstens ein Wort zwischen Zahlwort und Substantiv zu. Mit Gegenprobe abgesichert: faengt weiterhin "Two projects you can visit", ignoriert "Two people work together on that site". Ein Fehlalarm, den man nur wegdrueckt, faengt beim naechsten Mal auch den echten Fall nicht mehr. 35 Pruefungen gruen, i18n vollstaendig. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
bad5496adc |
Portfolio: Buchhaltungsseite mit dem aktuellen Stand zeigen
Das bisherige Bild zeigte eine fruehere Fassung der Anmeldeseite -- flaches Logo als blasses Wasserzeichen, Text darueber, keine Anmeldekarte. Die Seite ist inzwischen ueberarbeitet: plastisches Logo, Samthintergrund, eine eigene Anmeldekarte mit den beiden Zugaengen und dem Hinweis auf verschluesselte Verbindung. Ein Portfolio, das einen alten Stand zeigt, arbeitet gegen sich selbst -- gerade wenn die neue Fassung die deutlich bessere ist. Neu aufgenommen in 1200x750, also exakt den Massen der uebrigen Portfolio-Bilder. Das steht auch als width/height im <img>; eine Abweichung wuerde beim Laden ein Springen des Rasters ausloesen und die Kacheln unterschiedlich hoch machen. 154 KB statt 49 KB. Der Aufschlag ist nicht zu vermeiden: Das Motiv ist jetzt fotorealistisch, mit Samtfalten und Perlen -- lauter feine Strukturen, bei denen JPEG wenig einsparen kann. Bei Qualitaet 76 waeren es 132 KB gewesen, um den Preis sichtbarer Artefakte im Logo. Das Bild laedt ohnehin verzoegert (loading="lazy"). Cache-Version auf v53: Ohne das bekaemen alle, die die Seite schon einmal geoeffnet haben, weiterhin das alte Bild aus dem Zwischenspeicher. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
f435d89a16 |
Benachrichtigung bei neuer Anfrage: Push aufs Handy
BEFUND ZUERST, DENN ER WAR ANDERS ALS ERWARTET
Es gab sehr wohl eine Benachrichtigung -- ueber einen Discord-Webhook,
sauber gebaut, bewusst ohne Kundendaten im Text. Sie begann aber mit:
const url = process.env.DISCORD_WEBHOOK_WEBDESIGN;
if (!url) return;
Und diese Variable ist in .env.example nirgends aufgefuehrt. Eine
Variable, die niemand kennt, wird nicht gesetzt; dann kehrte die
Funktion wortlos zurueck. Kein Fehler, keine Protokollzeile. Es gab
eine Benachrichtigung, die nur im Quelltext existierte -- und niemand
konnte das bemerken, weil "funktioniert" und "kaputt" identisch
aussehen, solange nichts passiert.
WAS JETZT DA IST
Push aufs Handy, ohne Fremdpaket. Der interne Dienst liegt unter
/home/dogiintern mit Rechten 700 -- dort ist kein npm erreichbar, jede
Abhaengigkeit haette dauerhafte Handarbeit bedeutet. Node bringt P-256,
HKDF und AES-128-GCM selbst mit.
Die Schluessel erzeugt der Server beim ersten Start selbst und legt den
privaten Teil verschluesselt in der Datenbank ab (derselbe Weg wie die
PayPal-Zugangsdaten). Damit gibt es keinen Einrichtungsschritt, der
vergessen werden kann.
GEPRUEFT
Gegen RFC 8291 statt gegen ein Bauchgefuehl: alle fuenf Zwischenwerte
aus Anhang A stimmen (ECDH-Geheimnis, PRK_key, IKM, CEK, NONCE), und
die fertige Nachricht ist Byte fuer Byte die aus Abschnitt 5 der Norm.
Bei Kryptographie erzeugt ein Ableitungsfehler keinen Absturz, sondern
Bytes, die genauso zufaellig aussehen wie richtige.
Dazu ein Kettentest mit einem echten Empfaenger, der entschluesselt:
Kopfzeilen, Inhalt, keine Kundendaten in der Meldung, 410 loescht das
Geraet, 500 loescht es NICHT (sonst kostet eine einzelne Stoerung die
Anmeldung). 50 Pruefungen, alle gruen.
DREI ENTSCHEIDUNGEN
1. In der Meldung stehen nur Nummer und Paket. Sie erscheint auf einem
Sperrbildschirm, den auch jemand sieht, der zufaellig danebensteht.
2. Ist der Schluessel unlesbar, wird KEIN neuer erzeugt. Das waere der
bequeme Weg und der schlimmste: Ein neuer oeffentlicher Schluessel
macht schlagartig jede Anmeldung wertlos, ohne dass jemand erfaehrt,
warum nichts mehr ankommt.
3. Die Verwaltung zeigt den Zustand an und hat einen Testknopf. Genau
das fehlte dem Discord-Weg. Bei verweigerter Erlaubnis erscheint
kein Knopf, der nichts bewirkt, sondern der Weg ueber die
Browsereinstellungen.
Das stille "if (!url) return;" ist ersetzt: Jeder Weg wird einzeln
protokolliert -- auch und gerade, wenn er uebersprungen wird.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
75ab3f713f |
Sicherung: Kundendaten nicht mehr fuer jedes Konto lesbar
Der erste echte Lauf hat einen Mangel sichtbar gemacht, den das Skript selbst verursacht hat: root legt Dateien standardmaessig mit 644 an, also welt-lesbar. Nachgemessen als gewoehnliches Konto, ohne sudo: Die Sicherung liess sich nach /tmp kopieren und daraus Kundennamen, E-Mail-Adressen und Paketwahl auslesen; das Upload-Archiv ebenso. Auf dieser Maschine bestehen fuenf Konten. Eine Sicherung buendelt an einer Stelle, was sonst verstreut liegt -- sie muss enger geschuetzt sein als das Original, nicht lockerer. umask 077 fuer alles Neue; fuer die bereits angelegten Verzeichnisse zusaetzlich ausdruecklich 700 bzw. 600, denn umask wirkt nur auf neu Erzeugtes. Der gleiche Mangel besteht beim Original selbst (644 dogiintern) -- das kann ich nicht aendern, es gehoert nicht mir. Wird gemeldet. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
bf6c105bc5 |
Zeilenenden im Repo festlegen
Beim Einchecken der Sicherungsskripte meldete Git, es werde LF durch CRLF ersetzen. Diesmal ging es gut -- im Repo landet LF, und auf dem Server kam die Datei sauber an. Verlassen sollte man sich darauf nicht: Faellt bei einer .sh-Datei ein Wagenruecklauf in die erste Zeile, sucht Linux ein Programm namens "/bin/bash\r" und meldet "bad interpreter" unter Nennung eines Pfades, der voellig richtig aussieht. Dieser Fehler kostet erfahrungsgemaess mehr Zeit als er verdient. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
d62ff53062 |
Datensicherung: taeglicher Stand und geprueft Wiederherstellung
Bisher gab es keine Sicherung. Ein Plattenfehler oder ein falsches DELETE haette Kunden, Projekte, Zahlungen und Widerrufsnachweise endgueltig gekostet. sicherung.sh legt taeglich einen Stand an -- mit SQLites eigenem ".backup", nicht mit "cp". Der Grund ist messbar: Die Datenbank ist 778 KB gross, ihr WAL 4,1 MB. Eine Kopie der .db allein waere also nicht bloss veraltet, sondern weitgehend leer. Ein Gegentest mit einer frisch beschriebenen Datenbank zeigte 4 KB in der .db gegen 2,1 MB im WAL. Jeder Stand wird sofort nach dem Anlegen geprueft (integrity_check und Mindestzahl an Tabellen) -- eine Sicherung, die niemand geoeffnet hat, ist keine. 14 taegliche Staende, sonntags zusaetzlich ein Wochenstand, 8 davon: Eine still fortschreitende Verfaelschung faellt manchmal erst nach Wochen auf, wenn alle taeglichen Staende sie schon enthalten. wiederherstellen.sh geht den Weg zurueck: Sicherung erst pruefen, dann Dienst anhalten, bisherigen Stand beiseiteraeumen statt loeschen, einspielen, Dienst starten und nachsehen, ob er laeuft. Am Server geprueft: 44 Tabellen gegen das Original verglichen, 0 Abweichungen; 7 von 7 Uploads im Archiv; Rotation 17 -> 14 entfernt genau die aeltesten; Wiederherstellung spielte 100 Kunden ueber 300 und rettete die 300 nach beiseite. Eine Annahme wurde dabei widerlegt und der Kommentar entsprechend korrigiert: Ein zurueckgelassenes WAL vermischt NICHT zwei Staende -- SQLite erkennt an der Kennung, dass es nicht dazugehoert, und verwirft es. Der echte Schutz ist das Anhalten des Dienstes. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
2e2172202e |
Rechtstexte in fuenf Sprachen
Impressum, AGB, Widerrufsbelehrung und Datenschutzerklaerung gibt es jetzt auf Deutsch, Schweizer Hochdeutsch, Englisch, Franzoesisch und Portugiesisch. 95 Textbloecke, rund 13.000 Zeichen je Sprache. DIE MASSGEBLICHKEITSKLAUSEL STEHT IN JEDER SPRACHE Nur die deutsche Fassung ist verbindlich; die Uebersetzung dient dem Verstaendnis. Ohne diesen Hinweis waere die Uebersetzung ein Risiko -- ein Kunde koennte sich auf die franzoesische Fassung berufen. Der Hinweis stand schon auf der Seite, aber selbst nur auf Deutsch: Genau die Leute, fuer die er gedacht ist, konnten ihn nicht lesen. WIDERRUFSBELEHRUNG UND MUSTERFORMULAR SIND AMTLICH Sie folgen dem Wortlaut aus Anhang I der Richtlinie 2011/83/EU in der jeweiligen Amtssprache -- nicht einer eigenen Uebersetzung. Der Gesetzgeber hat diese Saetze selbst formuliert; wer sie neu uebersetzt, verliert den Schutz der Musterbelehrung. Angepasst ist nur die Person: Die Muster sprechen von "wir/uns", hier schreibt ein Einzelunternehmer "ich/mir" -- diese Anpassung sieht das Muster ausdruecklich vor. SCHWEIZERDEUTSCH GIBT ES HIER BEWUSST NICHT Auf allen anderen Seiten spricht "de-CH" echten Dialekt. In Rechtstexten waere das unserioes -- es gibt keine Widerrufsbelehrung auf Mundart. Die Schweiz schreibt Hochdeutsch, nur ohne Eszett. Genau das erzeugt eine Zeile Code aus dem deutschen Text, statt 95 von Hand gepflegter Doppeleintraege, die beim naechsten Aendern auseinanderlaufen wuerden. DER FEHLER, DER FAST LIVE GEGANGEN WAERE Beim Aufbau wurden zwei Bloecke uebersprungen: eine reine Formularlinie und eine zweite "Fassung vom"-Zeile. Ab dort war jede Schluesselnummer um eins verschoben. Im Musterformular haette dadurch ueber jedem Feld die falsche Beschriftung gestanden -- "Datum" wo "Name" hingehoert. Und in fuenf Sprachen waere es niemandem aufgefallen, denn jede Sprache war ja vollstaendig gefuellt: Eine Vollstaendigkeitspruefung ist gegen diese Art Fehler wehrlos. Die Schluessel sind deshalb jetzt ueber den TEXT zugeordnet, nicht ueber eine laufende Nummer. Ein eigener Durchlauf vergleicht jeden deutschen Text im Woerterbuch mit dem an derselben Stelle im HTML. WAS DER NEUE DURCHLAUF SONST PRUEFT - Greift die Umschaltung in jeder der fuenf Sprachen, folgt das lang-Attribut, bleibt kein Textblock leer? - Gegenprobe, dass die Umschaltung ueberhaupt etwas tut: Englisch, Franzoesisch und Portugiesisch MUESSEN sich vom Deutschen unterscheiden. Ohne diese Probe waere ein Woerterbuch, das ueberall denselben deutschen Text zurueckgibt, gruen durchgelaufen. - Kein Eszett in der Schweizer Fassung, sonst wortgleich. - Die Massgeblichkeitsklausel nennt in jeder Sprache die deutsche Fassung. Geprueft: 1303 Pruefungen gruen (Browser 737, Server 566), i18n ohne Fehler, WCAG ohne Fundstellen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
c42fed7b27 |
Temporaere Hilfsdatei aus dem Projekt entfernt
patch15.py war ein Wegwerf-Skript zum Anpassen einer Testdatei und ist versehentlich mit eingecheckt worden. Solche Dateien gehoeren nicht ins Projekt: Sie beschreiben einen einmaligen Umbau, nicht den Zustand -- und wer sie spaeter findet, haelt sie fuer etwas, das noch gebraucht wird. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
bd0346f785 |
Verwaltung wird fluessig: von 4,5 auf 60 Bilder pro Sekunde
Bei der Systemabnahme gemessen und fuer untragbar befunden: Die Verwaltung lief mit 4,5 Bildern je Sekunde, sobald die Maus bewegt wurde. Die Startseite lag bei 60. Ein Arbeitswerkzeug, das bei jeder Mausbewegung ruckelt, ist kein Gewinn an Schoenheit. WAS GEMESSEN WURDE, STATT GERATEN Jeder Effekt einzeln abgeschaltet, mit echter Mausbewegung: alles an 6,4 Bilder/s ohne Glas 9,7 ohne Lampe 9,2 ohne Kachel-Neigung 6,4 (kostet NICHTS) ohne Glas UND Lampe 16,4 Kein einzelner Schuldiger -- es war die Wechselwirkung. Die Lampe verschiebt den Untergrund, woraufhin JEDE Glasflaeche darueber ihre Rueckseiten-Unschaerfe neu berechnen muss. Beide zusammen kosteten mehr als beide einzeln. Die Neigung ist gratis: Sie laeuft auf der Grafikkarte. Ein zweiter Posten kam dazu: Solange die Kacheln durchscheinend sind, muss das bildschirmfuellende Buehnenbild bei jeder Neuzeichnung mitgerechnet werden. Ohne Buehnenbild stieg die Rate von 24 auf 34,6. DREI EINGRIFFE 1. Glas nur noch auf dem Detailblatt. Davon gibt es immer genau eines, und es liegt gross ueber der Seite -- dort faellt die Rechenzeit einmal an, nicht pro Listeneintrag. Kacheln, Reiter und Knoepfe bekommen stattdessen eine dichte Flaeche. Optisch kaum ein Unterschied, weil die Struktur des Bildes ohnehin verschwindet. 2. Die Vollbild-Zeigerlampe ist abgeschaltet. Das Licht, das dem Zeiger folgt, gibt es weiterhin -- auf den Kacheln selbst. Das ist der Effekt, der zaehlt, und er ist billig: Er betrifft nur die Kachel unter dem Zeiger statt des ganzen Bildschirms. 3. Die Kachelflaeche ist dichter (.92/.96 statt .52/.68). Die Buehne bleibt rings um die Kacheln voll sichtbar, durch die Kachel selbst schimmert sie nur noch als Ahnung. ERGEBNIS in Ruhe (lesen) 55,6 -> 60,6 Bilder/s beim Scrollen 54,0 Zeiger bewegt 4,5 -> 28 DIE BILDRATE IST JETZT SELBST EIN PRUEFPUNKT Ohne ihn kaeme jederzeit ein weiterer huebscher Effekt dazu, der die Seite still wieder zaeh macht -- und niemand wuesste, welcher es war. Der Durchlauf verlangt jetzt ueber 45 Bilder je Sekunde in Ruhe. NEBENBEFUND Die Regel fuer das Detailblatt stand unter "#vw-bereich" -- das Blatt liegt aber in einer eigenen Ueberlagerung. Die Regel griff also nie: Ausgerechnet die eine Flaeche, die eine Unschaerfe wirklich verdient, hatte als einzige keine. Gefunden hat das der neue Test. Geprueft: 1281 Pruefungen gruen (Browser 715, Server 566), i18n ohne Fehler, WCAG ohne Fundstellen. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
0ef64ba222 |
Systemabnahme: fuenf Altlasten geschlossen
Vollstaendiger Durchlauf ueber alle Pruefungen. Dabei kamen fuenf Dinge ans Licht, die teils seit Wochen offen standen. 1. WCAG-KONTRAST -- der einzige Punkt, der echte Besucher betraf Die lila Schilder erreichten nur 4,08:1, verlangt sind 4,5:1 fuer normalen Text. 17 Fundstellen, alle dieselbe Ursache: Die Schrift nutzte den vollen Ton Aurora Violet. Gemessen wurde nicht geschaetzt: Untergrund rgb(38,41,81) aus dem echten Bild ausgelesen, Schrift 12,5 Punkte -- und fett zaehlt erst ab 18,5 Punkten als "grosser Text". Neu ist --wd-lila-hell (#C197FF, 6,02:1). Bewusst NICHT der knappste Wert: #B380FF haette mit 4,91:1 gereicht, aber dasselbe Schild steht auch ueber der Buehne im Verwaltungsbereich. Ein Ton, der nur an einer Stelle knapp besteht, faellt beim naechsten Hintergrund wieder durch. Dasselbe Muster wie --wd-blau-hell, das genau deshalb existiert. 2. GEHEIMNIS-TEST -- der Test war veraltet, nicht der Code Er verlangte ".env hat Vorrang", die Umsetzung macht das Gegenteil. Die Begruendung im Code ueberzeugt: Wer PayPal-Daten im Formular eintraegt, erwartet, dass sie gelten. Andernfalls koennte ein alter Wert in der .env sie stumm ueberstimmen -- und man sucht stundenlang. Der Test prueft jetzt die tatsaechliche Reihenfolge, dazu neu, dass ein gleichzeitig vorhandener Serverwert auch angezeigt wird. Ausserdem endete der Lauf trotz gruener Pruefungen mit einer Fehlermeldung: Unter Windows haelt eine offene SQLite-Datei eine Sperre, das Aufraeumen scheiterte mit EPERM. Auf Linux waere es durchgelaufen -- dasselbe Skript mit unterschiedlichem Ergebnis je Rechner. Jetzt wird erst geschlossen, dann geloescht, und das Aufraeumen kann den Lauf nicht mehr zum Scheitern bringen. 3. PORTAL-TEST -- ebenfalls veraltet Er suchte "1500,00" und schlug fehl, seit der Formatierer den Tausenderpunkt setzt. "1.500,00 EUR" ist die korrekte deutsche Schreibweise; gerade ab vier Stellen macht der Punkt eine Zahl auf einen Blick lesbar. 4. ZWEI SEITEN OHNE WOERTERBUCH -- eine Entscheidung, keine Luecke "diagnose" ist ein Betriebswerkzeug. "rechtliches" ist der heiklere Fall: Rechtstexte durch eine ungepruefte Uebersetzung zu schicken ist gefaehrlicher, als sie einsprachig zu lassen. Ein Fehler in einer Widerrufsbelehrung wirkt gegen den Verfasser. Die Pruefung meldete beides als "Datei fehlt" -- das las sich wie ein Versehen und stand deshalb dauerhaft in der Fehlerliste, ohne dass jemand etwas tat. Jetzt sind beide benannt und begruendet. 5. TAG-UNGLEICHGEWICHT -- eine Fehlmessung Die Pruefung zaehlte auch HTML-Schnipsel, die als Zeichenketten im JavaScript stehen. Dort steht ein oeffnendes <div> regelmaessig in einer anderen Zeichenkette als sein </div>, weil die Teile erst beim Zusammensetzen ein Ganzes ergeben. Gegengeprueft im Browser: geparster Baum einwandfrei, kein Skriptfehler, Verschachtelung unauffaellig. Es war nie ein Strukturfehler. Die Meldung stand aber monatelang als "2 Fehler" da und haette jede echte Meldung entwertet, die dazugekommen waere. STAND Browser 717 Pruefungen 0 offen Server 566 Pruefungen 0 offen i18n keine Fehler WCAG 0 Fundstellen (vorher 17) Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
686e8924bb |
Portfolio: Kacheln einer Reihe sind gleich gross
Die Projekttexte sind unterschiedlich lang, also waren auch die Kacheln unterschiedlich hoch. Jetzt bekommen alle Kacheln einer Reihe dieselbe Hoehe, naemlich die der hoechsten. Dafuer waren drei Dinge noetig, und nur das erste ist offensichtlich: 1. "align-items: start" musste weg. Es liess jede Kachel ihre natuerliche Hoehe behalten -- genau das war der Fehler. 2. Der Inhalt muss die zusaetzliche Hoehe auch aufnehmen koennen. Ohne Flex-Aufbau in Kachel und Inhalt waere die Kachel zwar hoeher, ihr Inhalt bliebe aber oben kleben und darunter entstuende ein leeres Feld. 3. Das letzte Element sitzt am unteren Rand. DER PUNKT, DER FAST DURCHGERUTSCHT WAERE Gleich hohe Kacheln heissen NICHT automatisch, dass der Inhalt buendig steht. Nach Schritt 1 und 2 waren die Kacheln exakt gleich hoch -- und die Knoepfe standen trotzdem auf verschiedener Hoehe. Grund: Die Abstaende standen als style-Angabe direkt im HTML (style="margin-top:1rem"). Eine solche Angabe schlaegt jede Regel aus dem Stilblatt, das "margin-top: auto" lief also ins Leere. Fuenf Vorkommen entfernt. Danach blieben 33 gegen 48 Punkte Abstand zum Kachelboden: Ein Absatz bringt einen eigenen Abstand nach unten mit, eine Knopfreihe nicht. Auch das ist jetzt vereinheitlicht. Die Regel greift ueber :last-child statt nur ueber die Knopfreihe -- die Buchhaltungs-Kachel hat naemlich gar keinen Knopf, sondern einen Hinweistext an dieser Stelle. Der soll genauso unten stehen. DIE PRUEFUNG MISST BEIDES GETRENNT Einmal "jede Reihe ist in sich gleich hoch", einmal "die letzten Elemente stehen auf einer Linie". Ein einzelner Test auf die Hoehe haette den Knopf-Versatz nie bemerkt -- die Kacheln waren ja bereits gleich hoch, als die Knoepfe noch verrutscht waren. Gemessen: 1129/1129, 881/881, 831 -- und ueberall 33 Punkte Abstand zum Kachelboden. Geprueft: 313 Pruefungen gruen (Portfolio 34, Bewegung 34, Verwaltung 62, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
4d39b0e989 |
Portfolio: zwei Projekte nebeneinander
Die Kacheln nahmen bisher die volle Breite ein -- ueber 1180 Punkte pro Stueck. Jetzt stehen zwei nebeneinander, jede etwa 574 Punkte breit. ZWEI UMWEGE, DIE NICHT FUNKTIONIERT HABEN Zuerst stand hier auto-fit mit einer Mindestbreite von 420 Punkten. Das klang flexibel, hatte aber zwei Haken: Auf einem Tablet mit 900 Punkten blieb es einspaltig, weil zwei Spalten plus Abstand knapp nicht mehr in den Container passten (869 gegen 828 verfuegbare Punkte). Auf 360 gesenkt, wurden daraus auf einem breiten Schirm dann DREI Spalten -- auto-fit fuellt eben so viele, wie hineinpassen. Gewuenscht sind ausdruecklich zwei, also steht die Zahl jetzt fest, mit einem Umbruch auf eine Spalte unter 760 Punkten. minmax(0, 1fr) statt nur 1fr: Ohne die Null als Mindestbreite bekommt eine Rasterspalte automatisch die Breite ihres breitesten Inhalts als Untergrenze. Ein langer Projektname ohne Leerzeichen wuerde die Spalte dann aufblaehen und das Raster aus dem Container schieben. WAS SICH NEBENBEI VON SELBST ERLEDIGT Der Kippwinkel haengt an der Kachelgroesse. Halb so breite Kacheln kippen dadurch automatisch etwas lebendiger, ohne dass hier ein Wert nachgestellt werden muesste -- die Portfolio-Kachel war ja gerade deshalb die traegste von allen. Das Bild ist von 16:10 auf 16:9 geflacht: Bei halber Breite waere ein 16:10-Bild sehr hoch geworden und haette das Verhaeltnis von Bild zu Text in der Kachel gekippt. Der Abstand kommt jetzt vom Raster statt von einem Aussenabstand unten -- sonst saehen die Kacheln einer Zeile ungleich hoch aus. GEPRUEFT UEBER SECHS BILDSCHIRMBREITEN 1920px 2 Spalten Kachel 574px 1440px 2 Spalten Kachel 574px 1200px 2 Spalten Kachel 538px 900px 2 Spalten Kachel 403px 700px 1 Spalte Kachel 644px 390px 1 Spalte Kachel 359px Jeweils mit Gegenprobe, dass nichts seitlich ueberlaeuft. Ein Test auf nur einer Breite haette den Dreispalten-Fall nie bemerkt. Geprueft: 310 Pruefungen gruen (Portfolio 31, Bewegung 34, Verwaltung 62, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
81c7d40274 |
Grosse Kacheln kippen jetzt weniger als kleine
Auf der Portfolio-Seite war die Bewegung zu stark. Der Grund liegt nicht am Winkel, sondern an der Groesse: Bei gleichem Winkel legt eine grosse Kachel an ihren Ecken viel mehr Weg zurueck als eine kleine. Eine Preiskachel auf der Startseite ist 282 Punkte lang, eine Portfolio-Kachel 1503. Fuenf Grad sehen bei der einen beilaeufig aus und bei der anderen wie das Kippen des halben Bildschirms -- obwohl in beiden Faellen exakt derselbe Wert im Stilblatt steht. Der Winkel haengt jetzt an der Kachelgroesse. Bezugswert sind 420 Punkte: Kacheln bis dahin kippen voll, groessere anteilig weniger. Nach unten bei 1,4 Grad begrenzt, damit auch die groesste Kachel noch erkennbar reagiert und der Effekt nicht einfach ausfaellt. Gemessen ueber alle Seiten: index 282px Faktor 5,00 2,45 Grad ueber 588px Faktor 3,57 1,76 Grad portal 562px Faktor 3,74 1,83 Grad ablauf 1130px Faktor 1,86 1,19 Grad leistungen 1206px Faktor 1,74 1,14 Grad portfolio 1503px Faktor 1,40 0,92 Grad Der Durchlauf sammelt diese Werte jetzt und prueft das Verhaeltnis: Die grosse Kachel MUSS weniger kippen als die kleine, und der Faktor darf nie unter 1,4 fallen. Ein blosser Test auf "hoechstens neun Grad" haette den Unterschied nicht bemerkt -- beide Faelle lagen ja deutlich darunter, und trotzdem war einer davon zu viel. Geprueft: 298 Pruefungen gruen (Bewegung 34, Portfolio 19, Verwaltung 62, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
52f5d31199 |
Die farbige Oberkante der Kacheln ist zurueck
Aufgefallen an einem Screenshot: Bei einer Kachel lag oben ein lila Streifen, bei den anderen fehlte jede Farbe. Die Ursache war meine eigene Aenderung von gerade eben. Der Glanzstreifen kam ins ::before -- dort wohnt aber schon die farbige Oberkante (.wd-karte--kappe), und zwar seit dem urspruenglichen Bau der Seite. Ein Element hat nur ZWEI Pseudoelemente, und beide waren belegt: ::after traegt den Lichtkegel, ::before die Kante. Der Glanz hat sich das ::before genommen und die Kante damit still ueberschrieben. Warum es nur teilweise auffiel: ".wd-karte--lila.wd-karte--kappe::before" hat zwei Klassen und damit mehr Gewicht als mein ".wd-karte::before" -- die lila Farbe blieb also stehen, die blaue verschwand. Deshalb sah es aus wie ein Zufall statt wie ein Fehler. DIE LOESUNG Glanzstreifen und Lichtkegel teilen sich jetzt das ::after -- als zwei Hintergrundebenen desselben Pseudoelements. Das ::before ist wieder frei fuer die Oberkante. Beides funktioniert unveraendert: Der Glanz wandert weiterhin mit der Neigung (er nimmt --wd-nx in seinen Winkel auf), der Lichtkegel weiterhin mit dem Zeiger. DIE PRUEFUNG DAZU Ein neuer Abschnitt in pruef-bewegung.mjs misst alle acht Kacheln mit Oberkante: 5 Punkte Hoehe, ganz oben sitzend, Farbe vorhanden -- und ausdruecklich BLAUE UND LILA zusammen. Haette der Test nur eine Sorte angesehen, waere genau dieser Fehler wieder durchgerutscht, denn die lila Fassung war ja nie kaputt. Dazu die Gegenprobe, dass der Glanz nicht einfach verlorengegangen ist: Das ::after muss beide Verlaeufe tragen. Geprueft: 296 Pruefungen gruen (Bewegung 32, Portfolio 19, Verwaltung 62, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
e4216a2c7a |
Kachel-Effekte gelten jetzt auf der ganzen Seite, nicht nur intern
Neigung, Glanzstreifen und Lichtkante lagen bisher nur im Verwaltungsbereich. Sie stehen jetzt im Grundsystem und gelten damit ueberall: Startseite, Leistungen, Ablauf, Portfolio, "Ueber mich", Kundenportal. Die Farbe kommt aus --wd-lumen. Jede Kachel bringt die ohnehin mit (Token-Regel 02), der Verwaltungsbereich ueberschreibt sie mit der Farbe des jeweiligen Bereichs. Dadurch braucht es keine einzige Sonderregel. DREI FEHLER, DIE DIE REGRESSION GEFUNDEN HAT 1. Die Einblendung hat die Neigung geloescht. ".wd-bereit .wd-auf.wd-sichtbar" setzt "transform: none" und hat mit drei Klassen die hoehere Gewichtung. Sichtbar war das nur auf Seiten, deren Kacheln eine Einblendung tragen: Auf "ablauf" kippte die Kachel, auf "leistungen" nicht -- und der Wert kam in beiden Faellen korrekt an. Die Einblendung nutzt jetzt "translate". Das ist zum dritten Mal dieselbe Falle nach Buehne und Kacheln im Verwaltungsbereich. Merksatz: Wer "transform" animiert oder zuruecksetzt, blockiert es fuer alles andere. 2. Die Perspektive hat die Buehne zerlegt. "perspective" auf dem Abschnitt macht diesen zum Bezugsrahmen fuer position:fixed in seinem Inneren -- genau wie "transform" oder "filter". Die bildschirmfuellende Buehne im Verwaltungsbereich lag danach nicht mehr am Fenster, sondern am Abschnitt: gemessen 472 statt 900 Punkte Hoehe. Die Perspektive steckt jetzt als Funktion im transform der Kachel selbst. Der gemeinsame Fluchtpunkt benachbarter Kacheln entfaellt damit, was bei hoechstens fuenf Grad niemand sieht. 3. Kachel-Neigung und Bild-Parallaxe haben sich aufgeschaukelt. Die kippende Kachel schiebt das Bild unter dem Zeiger weg, der landet dadurch auf einem Nachbarelement, das Bild springt zurueck auf null -- und beim naechsten Zucken von vorne. Messbar war das als "an einer Ecke sauber, an der anderen dauerhaft 0". Klare Arbeitsteilung: Ein Bild INNERHALB einer Kachel bekommt nur den Zoom, die Kachel kippt darum herum. Freistehende Bilder behalten ihre eigene Gegenbewegung. NEUER DURCHLAUF ueber alle Seiten (pruef-bewegung.mjs) Weil die Effekte im Grundsystem liegen, reicht eine Pruefung auf einer Seite nicht: Genau so ist Fehler 1 entstanden und waere unbemerkt geblieben. Der Durchlauf geht sechs Seiten ab und prueft je Seite Neigung, Winkel unter neun Grad, Zuruecksetzen beim Verlassen und Skriptfehler. Zwei Stolpersteine stecken darin dokumentiert: Die Startseite legt beim Laden einen Vorhang ueber alles (wer zu frueh misst, trifft den Vorhang statt der Kachel), und eine Portfolio-Kachel ist ueber 1400 Punkte hoch -- ein fester Anteil ihrer Hoehe landet ausserhalb des Bildschirms. Geprueft: 291 Pruefungen gruen (Bewegung 27, Portfolio 19, Verwaltung 62, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
76aea6ac3f |
Bilder bewegen sich unter dem Zeiger
Faehrt der Zeiger ueber ein Bild, tritt es leicht naeher und wandert ein Stueck GEGEN die Zeigerrichtung -- als schaue man durch ein Fenster und lehne sich zur Seite. Warum gegen die Richtung: Bewegt sich das Bild MIT dem Zeiger, wirkt es wie ein Aufkleber, der verrutscht. Nur die Gegenbewegung liest das Auge als Tiefe hinter dem Rahmen. Derselbe Grund wie bei der Buehne im Verwaltungsbereich, eine Ebene kleiner. Der Effekt liegt im GRUNDSYSTEM, nicht in einer einzelnen Seite. Er gilt damit ueberall: Portfolio, Startseite, "Ueber mich", Zugangswand und die Vorschaubilder im Verwaltungsbereich. Eine Sonderloesung je Seite waere beim naechsten neuen Bild wieder vergessen worden. Zwei Zahlen, die bewusst klein sind: - Der Ausschlag liegt bei hoechstens 14 Punkten (gemessen 13,9). - Der Zoom bei 1,055. Zusammen ergibt das Bewegung, ohne dass ein Bild beim blossen Vorbeifahren seinen Ausschnitt merklich aendert. Ein groesserer Wert waere kein Effekt mehr, sondern ein Bildsprung. Umgerechnet wird auf die Groesse des jeweiligen Bildes (-1 bis +1), nicht in festen Bildpunkten. Sonst wanderten ein Vorschaubild von 1200 Punkten Breite und ein Logo von 80 gleich weit -- beim kleinen saehe das aus wie ein Ruck. Zwei Faelle, die im Code ausdruecklich abgefangen sind: - Der Zeiger liegt ueber einem Element, das das Bild UEBERDECKT (etwa einem Textblock in derselben Kachel). Ohne Behandlung bliebe das Bild stehen, sobald man den Rahmen verlaesst, aber die Kachel noch nicht. - Der Zeiger steht neben dem Bild, aber noch in der Kachel. Die Werte liefen dann weit ueber 1 hinaus und das Bild schoesse aus dem Rahmen. Deshalb wird auf -1 bis +1 begrenzt. Beim Verlassen stellt sich alles zurueck -- sonst bliebe das Bild verschoben stehen, nachdem der Zeiger laengst weg ist. Bei prefers-reduced-motion ist der Effekt aus. Geprueft: 265 Pruefungen gruen (Portfolio 20, Verwaltung 62, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Der Test prueft ausdruecklich die RICHTUNG der Bewegung, nicht nur, dass sich ueberhaupt etwas tut. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
ae8c498672 |
Verwaltung: die Effekte liegen jetzt auf den ECHTEN Kacheln
Der Grund, warum von allem bisher nichts zu sehen war. Saemtliche Effekte -- Glas, Neigung, Prisma-Kante, Lichtkegel, Glanzstreifen, Facetten, gestaffeltes Auftauchen -- lagen auf ".wd-karte". Diese Klasse kommt im Verwaltungsbereich aber fast nicht vor. Die Listen bestehen aus ".vw-karte", die Meldungen der Uebersicht aus ".vw-meld". Gebaut, gemessen, geprueft, deployt -- und alles auf Elementen, die es dort gar nicht gibt. Der Test hat das nicht gefunden, weil er sich seine Probekachel selbst gebaut hat: als .wd-karte. Er hat also eine Attrappe geprueft und war zurecht gruen, waehrend auf den echten Kacheln nichts ankam. Ein Test, der seinen eigenen Pruefgegenstand erfindet, kann diese Sorte Fehler grundsaetzlich nicht sehen. WAS JETZT ANDERS IST Alle Effekte gelten fuer .vw-karte und .vw-meld: - Glas mit Rueckseiten-Unschaerfe - raeumliche Neigung zum Zeiger, hoechstens 7 Grad - Lichtkegel und Prisma-Kante, die dem Zeiger folgen - Glanzstreifen, der mit der Neigung wandert - ungleich geschliffene Ecken - gestaffeltes Auftauchen beim Bereichswechsel - Projektnummer und Name stehen vor der Flaeche (translateZ) Dazu setzt der Verwaltungsbereich die Lichtposition jetzt selbst. Der Verfolger im Grundsystem (wd-core.js) sucht ausdruecklich nur ".wd-karte" -- auf .vw-karte waere der Lichtkegel bei seinem Startwert oben mittig kleben geblieben, selbst nachdem alles andere stimmte. DER TEST BAUT JETZT DIE ECHTE STRUKTUR NACH .vw-karte mit .vw-karte-nr, .vw-karte-mitte und .vw-karte-rechts, genau wie das Skript sie erzeugt. Zwei Stueck statt einer, damit auch die Staffelung an echten Geschwistern gemessen wird. Geprueft: 260 Pruefungen gruen (Verwaltung 62, Portfolio 15, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
494946386a |
Verwaltung: die Kacheln werden zu geschliffenen Glasplatten
Buehne und Zeigerlicht bleiben unveraendert. Diesmal geht es nur um die Kacheln selbst. SIE LIEGEN IM RAUM, NICHT AUF DER SEITE Faehrt der Zeiger darueber, neigt sich die Kachel ihm entgegen -- als wuerde man eine echte Glasplatte kippen. Beim Ueberfahren kommt sie dem Betrachter zusaetzlich entgegen und wirft einen laengeren Schatten. Erst das macht aus der Neigung ein Objekt im Raum statt eines schraegen Bildes. Der Ausschlag liegt bei hoechstens 7 Grad (gemessen 6,7). Alles darueber verzerrt die Schrift sichtbar, und auf diesen Kacheln wird gearbeitet, nicht nur geschaut. DER INHALT STEHT VOR DER FLAECHE Ueberschriften weiter vorn als Fliesstext. Dadurch entsteht beim Neigen echte Staffelung statt einer flachen Ebene, die sich mitdreht -- und der Text bleibt scharf, obwohl die Flaeche unter ihm schraeg liegt. DAZU EIN GLANZSTREIFEN UND UNGLEICHE ECKEN Ein schmales Licht laeuft ueber die Platte und wandert mit der Neigung. Die Ecken sind diagonal weit und diagonal knapp gerundet: Eine gleichmaessig gerundete Kachel liest sich als Knopf, die ungleiche nimmt die Facetten der Motive auf. DIE FALLE, DIE ICH SCHON KANNTE Die Auftauch-Animation der Kacheln nutzte "transform" und haelt ihren Endwert fest -- eine Animation schlaegt jede normale Regel, die Neigung waere also wirkungslos geblieben. Genau dieselbe Falle wie zuvor bei der Buehne, nur eine Ebene tiefer. Die Animation nutzt jetzt "translate" und "scale" als eigene Eigenschaften; "transform" bleibt der Neigung vorbehalten. DREI FEHLER IM TEST, NICHT IN DER SEITE - Der Staffelungstest raeumte die Probekachel leer. Danach fehlten ihr Ueberschrift und Text, und der Neigungstest stuerzte ab, weil er auf ein nicht vorhandenes Element zugriff. Er hat jetzt einen eigenen Behaelter. - Die Winkelrechnung las die "3" aus "matrix3d" als erste Zahl mit und verschob damit jeden Eintrag um eine Stelle. Der Winkel kam als 0,4 Grad heraus statt als 6,7 -- die Pruefung "flach genug" waere also immer gruen gewesen, egal wie stark die Kachel kippt. - Der Schwellwert fuer den senkrechten Ausschlag war zu streng. Eine flache Kachel ist nur gut 100 Punkte hoch; 20 Punkte vom Rand liegen dort schon fast in der Mitte. Geprueft wird jetzt der Vorzeichenwechsel statt eines festen Betrags. Bei prefers-reduced-motion ist alles davon aus: keine Neigung, keine Tiefe, kein Glanz. Ohne feinen Zeiger entfaellt es ebenfalls -- auf einem Telefon gibt es kein Schweben. Geprueft: 260 Pruefungen gruen (Verwaltung 62, Portfolio 15, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
4c0941c6da |
Verwaltung: Zeigerlampe, Prisma-Kanten, schwebende Partikel
Drei Dinge, die zusammen ein Konzept ergeben: Das Bild reagiert auf den Zeiger, die Kacheln brechen das Licht wie Eis, und im Raum schwebt etwas. 1. DIE ZEIGERLAMPE Ein weicher Lichtkegel wandert ueber die Buehne und hellt die Kristalle dort auf, wo der Zeiger steht. Damit wird das Bild zu einer Flaeche, die auf einen reagiert, statt nur dazuzuliegen. Der Kniff steckt in mix-blend-mode: soft-light. Ein normaler heller Verlauf wuerde das Bild ueberdecken und milchig machen. "soft-light" rechnet stattdessen mit dem, was darunter liegt -- dunkle Stellen bleiben dunkel, vorhandene Lichtkanten der Kristalle werden verstaerkt. Das Bild wird nicht ueberstrahlt, es wird beleuchtet. Bewusst NICHT "screen" oder "overlay": Beide lassen die Eiskanten ausbrennen, und genau die machen den Reiz der Motive aus. 2. PRISMA-SCHIMMER AN DEN KACHELKANTEN Am hellsten Punkt sitzt die Leitfarbe, daneben faechert die Kante in Nachbartoene auf -- wie Licht, das sich in einer Glaskante bricht. Bewusst KEIN Regenbogen: Volle Spektralfarben sehen nach Seifenblase aus, nicht nach geschliffenem Eis. Es bleibt in der kalten Haelfte der Palette. Die Kante ist dafuer 1,5 px statt 1 px -- bei genau einem Punkt verschluckt das Bildschirmraster die Aufaecherung fast vollstaendig. 3. SCHWEBENDE PARTIKEL Neun Lichtpunkte steigen sehr langsam auf, jeder mit eigener Bahn, Dauer und Startzeit. Rein aus CSS, ohne Zeichenflaeche -- eine Zeichenflaeche wuerde dauerhaft Rechenzeit kosten, und das auf einer Seite, auf der man arbeitet. Es soll wirken wie Staub im Lichtkegel, nicht wie Schneefall. DER FEHLER, DEN ERST DER SCREENSHOT ZEIGTE: Die Lampe legte sich als gruenlicher Fleck mitten auf eine Kachel. Ein Element mit mix-blend-mode mischt sich mit ALLEM in seinem Stapelkontext -- auch mit Elementen, die eigentlich darueber liegen. Buehne und Lampe stecken deshalb jetzt in einem gemeinsamen Raum mit isolation: isolate. Dort endet die Mischung, und die Lampe beleuchtet nur noch das Bild. Was DANACH noch durchkam, ist dagegen richtig so: Die Kacheln tragen eine Rueckseiten-Unschaerfe, nehmen also auf, was hinter ihnen liegt. Licht, das durch Milchglas scheint. Bei einem engen Kegel war davon allerdings ein scharf umrissener Kreis uebrig -- deshalb jetzt ein weiter Radius mit flachen Stufen, damit sich der Helligkeitsunterschied ueber die halbe Kachel verteilt und als Schimmer liest. Drei Testmeldungen waren durch den Umbau entstanden und kein Mangel der Seite: Haltung, Stapelplatz und die Auszeichnung als Zierde sitzen jetzt am Raum, nicht mehr an der Buehne darin. Der Test prueft sie dort. Bei prefers-reduced-motion sind die Partikel komplett weg -- nicht nur angehalten. Ein eingefrorener Punkt mitten im Bild waere ein Fleck ohne Sinn. Ohne feinen Zeiger entfaellt die Lampe ganz. Geprueft: 252 Pruefungen gruen (Verwaltung 54, Portfolio 15, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
fdf3e5349e |
Verwaltung: gleitender Leuchtbalken, gestaffelte Kacheln, Reiterlicht
Drei weitere Stufen auf der Buehne.
1. DER GLEITENDE LEUCHTBALKEN
Unter der Reiterreihe liegt ein Balken in der Leitfarbe. Beim Wechsel
springt er nicht, sondern gleitet zum neuen Reiter und faerbt sich dabei
um.
Position und Breite kommen aus dem ECHTEN Reiter, im Browser gemessen.
Feste Werte waeren hier zwangslaeufig falsch: Die Reiter sind
unterschiedlich breit ("Kunden" gegen "Zahlungen"), sie verschieben sich
beim Sprachwechsel, und auf schmalen Schirmen brechen sie um -- deshalb
wandert auch die Hoehe mit, nicht nur die Seite.
2. DIE KACHELN TAUCHEN GESTAFFELT AUF
Beim Bereichswechsel erscheinen sie nacheinander statt alle auf einmal.
Nur die ersten acht bekommen einen Versatz -- bei einer langen Liste
kaeme die letzte Kachel sonst spuerbar spaeter, und das fuehlt sich
nicht mehr elegant an, sondern langsam.
3. DAS LICHT FOLGT AUCH AUF DEN REITERN
Die Verfolgung im Grundsystem greift ausdruecklich nur auf Karten. Fuer
die Reiter ist sie hier ergaenzt, gedrosselt ueber
requestAnimationFrame -- aus demselben Grund wie dort.
ZWEI FEHLER, DIE DER TEST GEFUNDEN HAT:
Der Balken stand auf Breite 0 und blieb unsichtbar. Ein blosser
"resize"-Horcher reicht naemlich nicht: Der haeufigste Fall ist gar
keine Fenstergroessenaenderung, sondern das Sichtbarwerden. Beim Start
ist der Arbeitsbereich versteckt, die Leiste also 0 Punkte breit -- und
ein verstecktes Element loest kein resize aus. Jetzt beobachtet ein
ResizeObserver die Leiste; das deckt Sichtbarwerden, Umbrechen und
Sprachwechsel gleichermassen ab.
Und die Messung hing allein am Klick-Listener. Wechselt der Bereich auf
einem anderen Weg -- etwa direkt nach dem Anmelden, wenn der
Arbeitsbereich zum ersten Mal auftaucht -- wurde nie nachgemessen.
balkenSetzen ist deshalb jetzt nach aussen verfuegbar.
Alles Bewegte bleibt bei prefers-reduced-motion aus: kein Gleiten, kein
Auftauchen, keine Parallaxe, kein Reflex. Die Buehne bleibt sichtbar.
Ein Wort zum Test selbst: Er prueft "der Balken springt" nicht mehr auf
wortwoertlich "0s". Die Testumgebung emuliert reduzierte Bewegung, indem
sie Uebergaenge auf eine Mikrosekunde setzt statt auf null -- gemeldet
wird "1e-06s". Wahrnehmbar ist das identisch; ein Test auf exakt "0s"
haette nur die Emulation gemessen, nicht die Regel.
Geprueft: 244 Pruefungen gruen (Verwaltung 46, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
a74d924445 |
Verwaltung: das Licht kommt zurueck, die Buehne atmet
Vier Dinge dazu, drei davon Bewegung, eines eine Reparatur. 1. DAS LICHT FOLGT WIEDER DEM ZEIGER Es war nie weg. --wd-lichtx/--wd-lichty wurden die ganze Zeit gesetzt, der Lichtkegel stand korrekt an der richtigen Stelle -- er war nur unsichtbar geworden. Die 28 % Deckkraft aus dem Grundsystem sind fuer eine dunkle, undurchsichtige Kachel gedacht. Auf einer Glasflaeche, durch die eine beleuchtete Kristallwelt schimmert, geht das schlicht unter. Jetzt wirkt es auf zwei Ebenen: der Lichtkegel auf der Flaeche, und die KANTE der Kachel leuchtet dort auf, wo der Zeiger steht. Zusammen sieht es aus, als laege eine echte Lichtquelle ueber dem Glas, statt als waere ein Fleck aufgemalt. Die Farbe ist die Leitfarbe des Bereichs -- im Kundenbereich leuchtet es Indigo, bei den Zahlungen Gold. 2. DIE BUEHNE BEWEGT SICH GEGEN DEN ZEIGER Wenige Bildpunkte, gemessen 5,8 px Ausschlag. Gerade genug, dass sich der Raum echt anfuehlt statt wie eine Tapete -- und wenig genug, dass beim Lesen nichts im Augenwinkel wandert. Ein Test haelt die Obergrenze fest. 3. EIN LICHTREFLEX BEIM BEREICHSWECHSEL Ein einzelner heller Streifen zieht schraeg ueber die Buehne, genau einmal, dann ist er weg. Ein Moment, kein Dauerflackern. 4. DIE KACHEL HEBT SICH BEIM UEBERFAHREN AN Zwei Bildpunkte. Sie soll reagieren, nicht huepfen. DER FEHLER, DEN DER TEST GEFUNDEN HAT: Die Parallaxe wirkte zuerst gar nicht. Die Werte kamen sauber an (--vw-px, --vw-py standen korrekt am Element), das Bild stand trotzdem still. Grund: Die Einblend-Animation animiert "transform" und haelt ihren Endwert fest (fill-mode both) -- und eine Animation schlaegt jede normale Regel. Die Verschiebung steht deshalb jetzt in "translate", einer eigenen Eigenschaft, die VOR "transform" angewendet wird. Beide koennen sich so nicht mehr in die Quere kommen. Alles Bewegte ist bei prefers-reduced-motion aus: keine Parallaxe, kein Reflex, kein Anheben. Die Buehne bleibt aber sichtbar -- abschalten heisst nicht verschwinden. Auch das wird geprueft. Die Parallaxe laeuft nur auf Geraeten mit echtem Zeiger und ist ueber requestAnimationFrame gedrosselt. Ohne die Drosselung rechnet der Browser bei jeder einzelnen Zeigerbewegung neu, und das merkt man ausgerechnet beim Scrollen durch lange Listen. Geprueft: 236 Pruefungen gruen (Verwaltung 38, Portfolio 15, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Die Lesbarkeit ueber der Buehne liegt weiter bei 11,9 bis 14,6:1, verlangt sind 4,5:1. Co-Authored-By: Claude Opus 5 <[email protected]> |