My-Weekly-ToDo-List/TODO.md
mARTin-B78 53de4017e5
Some checks are pending
Playwright Tests / test (push) Waiting to run
Test and SonarQube / quality (push) Waiting to run
fix: @ts-nocheck aus allen drei Dateien entfernen
Die Dateien schalteten die Typpruefung komplett ab und blieben deshalb auch
von ignoreBuildErrors: false unberuehrt -- eine Luecke im gerade erst
geschlossenen Netz.

- calendar-cache.ts brauchte die Ausnahme gar nicht: ohne sie null Fehler.
- microsoft-todo.ts hatte vier Faelle zirkulaerer Typinferenz
  (url -> data -> response -> url), aufgeloest durch explizite Annotationen.
- outlook-calendar.ts verbarg einen moeglichen Laufzeitfehler: Der Code
  streute response!-Assertions, eine Stelle war vergessen. Statt noch einer
  Assertion prueft er jetzt echt, ob nach drei Wiederholversuchen ueberhaupt
  eine Antwort vorliegt.

Ausserdem Block I in TODO.md: Hervorhebung des aktuellen Tages im
Spaltenkopf und die Symbole in Kalenderterminen nach oben rechts, beide mit
Fundstelle in globals.css.

Verifiziert: Typecheck 0 Fehler, npm run build Exit 0.

v1.118.2

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 13:59:57 +02:00

19 KiB

To-do — Repository-Audit vom 2026-09-06

Abarbeitungsliste aus dem Repository-Audit. Reihenfolge ist bewusst gewählt: schnelle Sicherheitsgewinne zuerst, das große Refactoring zuletzt.

Abhaken mit [x]. Die Nummern in Klammern entsprechen den Punkten aus dem Audit.


Block A — Schnell erledigt, sofortiger Gewinn

A1. Tote Mock-Login-Route entfernen (Audit 3)

  • src/app/api/auth/login/route.ts löschen
  • Prüfen, dass niemand sie aufruft (grep -rn "api/auth/login" src/)
  • Commit + Version bumpen

Warum: Altcode aus der Anfangszeit. Vergleicht gegen ein hartcodiertes Fake-Passwort ($2b$10$examplehashedpassword), erzeugt ein bedeutungsloses mock-jwt-token-… und setzt es als Cookie. Weil die Middleware /api/auth/* bewusst durchlässt, ist die Route ohne Login von außen erreichbar und schreibt jede probierte E-Mail per console.log ins Log. Kein Login-Bypass (der Vergleich schlägt immer fehl), aber unnötige Angriffsfläche.

A2. npm test reparieren (Audit 6)

  • Entscheiden: Jest raus oder Jest einrichten
  • package.json: "test" auf das zeigen lassen, was wirklich existiert
  • "test:e2e" für Playwright ergänzen
  • Debug-Skripte in tests/ aussortieren
  • 63 Unit-Tests für die ausgelagerten reinen Funktionen (tests/unit/), laufen in ~170 ms ohne Browser und ohne Datenbank über Nodes eingebauten Test-Runner
  • E2E-Tests mit echtem Login schreiben (braucht laufende App + Datenbank)
  • Testlauf in den nächtlichen Qualitätslauf aufnehmen (scripts/quality-check.sh)

Warum: "test": "jest" — aber es gibt keine Jest-Config und keinen einzigen Jest-Test. Die 15 Dateien in tests/ sind Playwright-Specs, und die Namen verraten den Charakter: login-debug.spec.ts, css-debug-test.spec.ts, login-simple-debug.spec.ts. Wegwerf-Skripte, keine Regressionstests. npm test läuft ins Leere.

Aufräumen: Die reinen Debug-Skripte löschen, die brauchbaren behalten und benennen.

A3. dev-Branch auflösen (Audit 5)

  • Die vier Maßnahmen aus f6c4a21 frisch auf main anwenden (siehe A1, B1, B2)
  • dev auf main zurücksetzen oder löschen
  • CLAUDE.md: Branch-Strategie an die gelebte Praxis anpassen

Warum: Commit f6c4a21 vom 04.07.2026 behebt bereits Rate-Limiting, Token-Hashing, die Mock-Route und die Secret-Datei — wurde aber nie gemerged. dev fehlen inzwischen 70 Commits von main, ein Merge würde alte Dateistände zurückholen. Deshalb: Commit als Vorlage nehmen, Änderungen neu anwenden, dev danach entsorgen.

CLAUDE.md beschreibt einen dev → main-Workflow, der faktisch nicht gelebt wird (alles läuft direkt auf main). Doku an die Realität anpassen ist ehrlicher.


Block B — Sicherheit

B1. Rate-Limiting auf den Auth-Endpunkten (Audit 4)

  • Sliding-Window-Limiter in src/lib/rate-limit.ts anlegen
  • Einbinden in signup
  • Einbinden in resend-verification
  • Einbinden in reset-password
  • Einbinden in verify-email
  • Limits dokumentieren (Vorschlag: 5 Versuche / 15 Minuten je IP)

Warum: Im gesamten src/ ist kein Limiter vorhanden. Signup, Passwort-Reset und Verification-Mails sind unbegrenzt aufrufbar — Einladung zum Missbrauch deines SMTP-Zugangs.

B2. Passwort-Reset-Token nur als SHA-256-Hash speichern (Audit 4)

  • Beim Erzeugen: Klartext per Mail, Hash in die DB
  • Beim Einlösen: eingehenden Token hashen, dann vergleichen
  • Migration für bestehende Tokens (einfachster Weg: alle invalidieren)

Warum: src/app/api/auth/reset-password/route.ts:28 legt passwordResetToken im Klartext ab. Wer Lesezugriff auf die Datenbank hat, kann fremde Konten übernehmen. Der Token selbst kommt aus crypto.randomUUID() — das ist in Ordnung, nur die Ablage nicht.

B3. Secrets aus dem Git entfernen und rotieren (Audit 2)

  • .env.production.save und .env.local gelöscht (mit shred überschrieben)
  • .gitignore auf .env* erweitert (Ausnahme .env.example)
  • NEXTAUTH_SECRET neu erzeugt — 44 Zeichen, geprüft
  • SMTP_PASSWORD neu gesetzt
  • deployment-notes.md und INFRASTRUCTURE_REQUIREMENTS.md aus dem Index entfernt
  • Klartext-Werte in beiden Dateien durch Platzhalter ersetzt
  • Passwort aus tests/debug/db-connection-test.spec.ts entfernt
  • Nach dem Wechsel neu eingeloggt
  • Entscheiden: Historie umschreiben oder belassen (Empfehlung: belassen)
  • Prüfen, ob eines der geleakten DB-Passwörter noch aktiv ist (siehe F1)

Warum: .env.production.save ist eingecheckt und enthält echte Werte — DATABASE_URL (70 Zeichen), NEXTAUTH_SECRET (20 Zeichen), SMTP_PASSWORD (20 Zeichen). .gitignore listet .env.production, die .save-Endung fällt durchs Raster. Drin seit Commit da8d019.

Entwarnung: Das GitHub-Repo ist privat — nicht öffentlich abgreifbar. Die Werte stehen aber in der kompletten Historie, in jedem Klon und auf Gitea.

Anleitung siehe docs/secret-rotation.md.


Block C — Build und Typsicherheit

C1. Build darf Fehler nicht mehr verschlucken (Audit 1)

  • Schritt 1: eslint.ignoreDuringBuilds in next.config.js auf false
  • Gemeldete ESLint-Fehler abarbeiten — es waren nur 2 (prefer-const in SettingsSidebar.tsx), verifiziert: next lint endet jetzt mit Exit 0
  • Schritt 2: npm run type-check — es waren 7 Fehler, alle behoben
  • Schritt 3: typescript.ignoreBuildErrors auf false — Build prüft jetzt Typen
  • tsconfig.json: include von **/* auf src/**/* eingegrenzt. Das war die Ursache der Langsamkeit, nicht die Dateigröße: Der Lauf dauert jetzt 19 Sekunden statt über 30 Minuten. Meine gegenteilige Vermutung war falsch.
  • 3 Dateien mit @ts-nocheck entschärft — im Projekt gibt es jetzt kein einziges @ts-nocheck mehr. calendar-cache.ts brauchte es gar nicht (0 Fehler), in microsoft-todo.ts fehlten Typannotationen, und in outlook-calendar.ts steckte ein möglicher Laufzeitfehler dahinter.
  • any-Stellen reduzieren — ESLint zählt 470 no-explicit-any — fortlaufend, kein Blocker
  • 209 ungenutzte Variablen (no-unused-vars) aufräumen — fortlaufend
  • 11 unvollständige Hook-Abhängigkeiten (react-hooks/exhaustive-deps) prüfen — die sind die gefährlichsten der Warnungen, weil daraus echte Bugs entstehen

Warum: next.config.js hat ignoreDuringBuilds: true und ignoreBuildErrors: true. tsconfig.json sagt zwar strict: true, das ist aber wirkungslos, weil der Build die Fehler gar nicht meldet. Ein Typfehler fällt nicht beim Bauen auf, sondern erst als kaputte Seite im Browser. Ein erfolgreicher Build ist derzeit kein Beweis, dass der Code korrekt ist.

Achtung Reihenfolge: Erst ESLint, dann TypeScript. Beides gleichzeitig abzuschalten blockiert jeden Deploy, bis alles behoben ist.


Block D — Struktur

D1. Dateien über 4000 Zeilen aufteilen (Audit 7)

Betroffen sind genau zwei Dateien:

Datei Zeilen
src/components/WeeklyView.tsx 11.556 (war 11.906)
src/components/SettingsSidebar.tsx 5.445

Voraussetzung: C1 muss laufen — ohne Typecheck ist ein Refactoring dieser Größe Blindflug. Ebenso hilfreich: A2, damit es überhaupt Tests gibt.

WeeklyView.tsx → Zielstruktur

  • Typen nach src/lib/weekly/types.ts (7 Typen, 97 Zeilen) — Build verifiziert
  • src/components/weekly/ anlegen
  • Datums-/Zeitraster-Helfer nach src/lib/weekly/date-utils.ts (13 Funktionen, 202 Zeilen)
  • Cookie-/Geräteeinstellungen nach src/lib/weekly/device-settings.ts
  • Formatierungs-Helfer nach src/lib/weekly/format.ts
  • getWeekNumber existiert dreimal im Projekt (WeeklyView, WeekPrintModal.tsx, api/user/export-week-view/route.ts) — Duplikate gegen date-utils.ts ersetzen, vorher die drei Implementierungen auf Gleichheit prüfen
  • calendar-cache.ts hat mit getWeekStart eine vierte eigene Wochenlogik — ebenfalls gegen date-utils.ts ersetzen
  • Drag & Drop in einen Hook useTaskDragDrop.ts
  • Someday-Listen in SomedayLists.tsx
  • All-Day-Leiste in AllDayBar.tsx
  • Zeitraster + Tagesspalten in TimeGrid.tsx / DayColumn.tsx
  • Einstellungs-Flyouts in QuickSettingsPanel.tsx
  • Daten-Laden in useWeeklyData.ts
  • WeeklyView.tsx bleibt als Orchestrator übrig

SettingsSidebar.tsx → Zielstruktur

  • src/components/settings/ anlegen
  • Ein Panel pro Themenbereich als eigene Datei
  • Gemeinsame Bausteine (Farbwähler, Schalterzeile) in settings/controls/

Vorgehen: In kleinen Schritten, jeder Schritt ein eigener Commit, nach jedem Schritt npm run build. Kein "großer Wurf" — die Datei ist zu zentral.

Zwischenstand nach drei Schnitten: 11.906 → 11.556 Zeilen, also rund 3 %. Damit sind die reinen Funktionen ohne Komponenten-State weitgehend heraus — das war der leichte Teil.

Die verbleibenden ~11.500 Zeilen sind die Komponente selbst: JSX und Event-Handler, die auf gemeinsamen State zugreifen. Jeder weitere Schnitt braucht echtes Props-Design statt Verschieben. Realistisch pro Schnitt ein eigener Durchgang, und erst mit Tests (A2) oder zumindest laufendem Typecheck sinnvoll — ein Fehler beim Herauslösen von Drag & Drop fällt sonst wieder erst im Browser auf.

Warum: WeeklyView.tsx ist ein Viertel des gesamten Quellcodes. Der Weekday-Bug von Anfang September saß genau dort und war nur deshalb schwer zu finden.


Block E — Dauerhafte Absicherung

E1. Regelmäßige automatische Prüfung (Audit 8)

  • SonarQube-Analyse gegen https://sq.martin-bierschenk.de einrichten
  • Regelmäßigen Lauf aufsetzen — nightly 03:00, todo-sonar.timer (systemd user unit)
  • npm audit in denselben Lauf aufnehmen (scripts/quality-check.sh)
  • todo-sonar.timer auf scripts/quality-check.sh umstellen statt nur todo-sonar-scan
  • Playwright-Tests in den nächtlichen Lauf aufnehmen (braucht laufende App — erst nach A2)
  • Quality Gate im SonarQube definieren
  • Ergebnis irgendwohin melden, wo es auffällt (bisher nur Konsolenausgabe)
  • Entscheiden: .github/workflows/sonarqube.yml löschen oder behalten

Stand: Die Analyse läuft bereits — nächtlich um 03:00 auf dem Spark, per systemd-Timer, Ergebnis auf https://sq.martin-bierschenk.de. Was fehlte, war npm audit (Sicherheitslücken in Abhängigkeiten findet SonarQube nicht) und ein Bündel-Skript. Beides ist jetzt da.

Zur Entscheidung: GitHub Actions wurde am 02.09. bewusst verworfen — das Repo ist privat, Actions-Minuten kosten, und der SonarQube-Server läuft ohnehin selbst gehostet. Trotzdem liegt seit dem 03.09. ein Workflow im Repo, der bei jedem Push auf main anspringt. Er ruft npm run type-check und npm run lint auf — beides schlägt derzeit fehl (siehe C1), der Workflow wäre also dauerhaft rot. Sein Projekt-Key war zudem falsch und hätte in ein zweites, leeres SonarQube-Projekt geschrieben; das ist korrigiert. Entweder löschen (passt zur Entscheidung vom 02.09.) oder bewusst behalten und C1 vorziehen.

Warum: Bisher fallen Regressionen erst in Produktion auf. In Kombination mit C1 (Build meldet Fehler) und A2 (Tests laufen) entsteht daraus ein echtes Netz.

E2. Abhängigkeiten auf Sicherheitslücken prüfen

  • npm audit auf dem Container ausführen
  • Kritische Funde beheben

Warum: Konnte im Audit nicht geprüft werden — node_modules ist auf dem CIFS-Share nicht installierbar (keine Symlinks). Zu bekannten Lücken in den Abhängigkeiten liegt daher keine Aussage vor. Muss nachgeholt werden.


Block F — Zurückgestellt

F1. Datenbank-Passwort rotieren (bewusst nach hinten geschoben)

  • Prüfen, ob eines der geleakten Passwörter noch aktiv ist
  • Falls ja: rotieren nach docs/secret-rotation.md, Schritt 2
  • Falls nein: nichts zu tun, Haken dran

Warum zurückgestellt: Von den drei Zugangsdaten ist dieses das am wenigsten dringende. Die Datenbank hört auf einer privaten LAN-Adresse, nicht am Internet. Wer das Passwort aus dem Git holen will, braucht Zugriff auf das private Repository — und ist damit ohnehin schon im Netz. NEXTAUTH_SECRET und SMTP_PASSWORD waren die kritischen, beide sind erledigt.

Und es ist riskant: Supabase läuft selbst gehostet per Docker. Dasselbe Passwort steckt in supabase/docker/.env und in Postgres. Ein halb durchgeführter Wechsel legt die Datenbank und damit die App lahm. Das gehört an einen ruhigen Abend, nicht nebenbei.

Zwei verschiedene Passwörter liegen im Git (Fingerabdrücke, keine Klartexte):

Fingerabdruck Wo Stand
59fb583eaae9 ehem. deployment-notes.md, INFRASTRUCTURE_REQUIREMENTS.md altes Docker-Compose-Setup (@db:5432); Dateien bereinigt
9dbb40b14c4d ehem. .env.production.save Supabase-Zugang, Status offen — Datei gelöscht, Wert steht noch in der Historie

Vom Nutzer bestätigt: das alte NEXTAUTH_SECRET (20 Zeichen) und das alte SMTP_PASSWORD sind nicht mehr in Verwendung, beide wurden rotiert. Offen ist allein das Datenbank-Passwort.

Prüfen lässt sich das auf dem Container, ohne einen Wert auszugeben:

cd /root/My-Weekly-ToDo-List
grep '^DATABASE_URL=' .env | sed -E 's|.*://[^:]+:([^@]+)@.*|\1|' | tr -d '\n' | sha256sum | cut -c1-12

Ergibt der Befehl 9dbb40b14c4d, ist das geleakte Passwort noch in Benutzung — dann lohnt F1. Kommt etwas anderes heraus, wurde es längst getauscht und du kannst F1 abhaken.


Block H — Aus dem scharfen Typecheck

H1. Notion-Anbindung war defekt (migriert, aber ungetestet)

  • Migration praktisch prüfen (braucht Notion-Zugang)
  • Betroffene Nutzer informieren: Notion-Verbindung muss neu eingerichtet werden

Warum: Der Code rief notion.databases.query() auf. Die Methode gibt es in @notionhq/client v5 nicht mehr (nur noch retrieve/create/update) — der Aufruf lief zur Laufzeit in ein "is not a function". Die Notion-Anbindung war damit schlicht kaputt, und das ist niemandem aufgefallen, weil der Build Typfehler verschluckte.

Umgestellt auf dataSources.query(); die Suche filtert jetzt auf data_source statt database. Das ist in sich konsistent, aber mangels Notion-Zugang nicht erprobt.

⚠️ Gespeicherte Verbindungen enthalten alte database_ids. Das sind keine data_source_ids — betroffene Nutzer müssen Notion einmal neu verbinden.


Block I — UI-Wünsche

I1. Aktuellen Tag auch im Spaltenkopf hervorheben

  • Hervorhebungsfarbe zusätzlich hinter Wochentagsname und Datum legen

Wunsch: Der laufende Tag soll deutlicher auffallen. Aktuell ist nur der Spaltenkörper eingefärbt, der Kopfbereich mit "SEP 7 / MONDAY" bleibt weiß.

Wo: globals.css:666 färbt .weekly-day-column.is-today mit var(--weekly-today-highlight). Der Kopf ist ein eigenes <header class="weekly-day-header"> (WeeklyView.tsx:7089) und übermalt die Farbe offenbar mit eigenem Hintergrund. Der Wochentagsname hat bereits eine eigene Regel (globals.css:708, .weekly-day-name.is-today setzt nur die Schriftfarbe).

Zu klären: Der Kopf ist vermutlich sticky und braucht deshalb einen deckenden Hintergrund — die Farbe muss also gesetzt, nicht nur geerbt werden.

I2. Symbole in Kalenderterminen nach oben rechts

  • .event-icons-row von unten rechts nach oben rechts verschieben

Wunsch: Die kleinen Symbole (Wiederholung, Anbieter) sitzen unten rechts im Termin und sollen nach oben rechts.

Wo: globals.css:3183 — .event-icons-row hat bottom: 2px; right: 4px;. Für oben rechts wird daraus top: 2px; right: 4px;.

Achtung: Der Titel beginnt oben links; bei kurzen Terminen könnten sich Symbole und Titel überlappen. Vermutlich braucht die Titelzeile rechts etwas Freiraum (padding-right), damit nichts verdeckt wird.


Block G — Infrastruktur (Supabase-LXC 201)

G1. Container läuft privilegiert (Sicherheitsthema)

  • Entscheiden, ob unprivileged: 0 so bleiben soll
  • Falls nicht: Container neu aufsetzen und Daten übernehmen

Warum: pct config 201 meldet unprivileged: 0. Wer aus diesem Container ausbricht, ist root auf dem NUC — bei einem Container, der die Datenbank betreibt, die relevantere Baustelle als ein fehlendes Root-Passwort. Meist wird das umgestellt, weil Docker im unprivilegierten Container zickt; nachvollziehbar, aber ein bewusster Kompromiss.

G2. Besitzverhältnisse im Dateisystem sind schief

  • Kurzfix, falls SSH-Zugang zum Container gebraucht wird: pct exec 201 -- chown 0:0 /etc/passwd und pct exec 201 -- chown 0:42 /etc/shadow
  • Langfristig: Container sauber neu aufsetzen

Warum: Das Dateisystem stammt aus einem unprivilegierten Container (alles liegt mit UID 100000 auf der Platte), läuft aber jetzt privilegiert, also ohne UID-Mapping. root besitzt dadurch praktisch keine Datei im System. Zeitstempel belegen es: alles vom 26.02.2026 mit UID 100000, nur /etc/hostname vom 06.09. mit 0:0 — das schreibt Proxmox von außen.

Symptom war passwd: Authentication token manipulation error. Das System läuft trotzdem, weil root per CAP_DAC_OVERRIDE die Rechteprüfung überspringt; Werkzeuge wie passwd, die strenger prüfen, fallen darüber. Weitere solche Merkwürdigkeiten sind zu erwarten.

⚠️ Kein chown -R über das ganze Dateisystem — nicht am laufenden Container und schon gar nicht mit Docker darin. Postgres und die Supabase-Container haben eigene UIDs (999, 70, …), die dabei zerschossen würden.

Kein Blocker: pct enter 201 gibt jederzeit eine Root-Shell ohne Passwort. Ein Root-Passwort wird nur für direkten SSH-Zugang gebraucht.

G3. Supabase Studio ist nicht erreichbar

  • Auf 201 prüfen: docker compose ps — laufen die Container?
  • Port klären: Studio hängt hinter Kong auf 8000, nicht 8001
  • scripts/supabase-setup.sh korrigieren: falsche Port-Ausgabe und Shebang #!/bash

Warum: http://<IP>:8001 antwortet nicht. Die Zahl stammt aus einer echo-Zeile in scripts/supabase-setup.sh, die nirgends eine Portfreigabe setzt — beim offiziellen supabase/docker-Stack ist Studio nur über Kong auf 8000 erreichbar. Zusätzlich hat das Skript #!/bash statt #!/bin/bash und ist so gar nicht direkt ausführbar.


Kleinkram (ohne Eile)

  • 114 console.log aus dem Produktionscode entfernen (die 275 console.error sind in Ordnung)
  • NEXTAUTH_SECRET auf 32 Byte verlängern (erledigt sich mit B3)
  • API-Routen: JSON-401 statt HTML-Redirect für nicht angemeldete Aufrufe
  • deploy.sh und docker-compose.yml löschen oder als veraltet markieren — sie beschreiben ein Portainer-Setup, das nicht mehr verwendet wird
  • Eigener Auth-Check in apple/sync und weather/geocode (aktuell nur durch die Middleware geschützt — zusätzliche Absicherung schadet nicht)