My-Weekly-ToDo-List/CHANGELOG.md
mARTin-B78 158051bb47
Some checks are pending
Playwright Tests / test (push) Waiting to run
Test and SonarQube / quality (push) Waiting to run
feat: Kalender-Zugangsdaten verschluesselt speichern (AES-256-GCM)
L1 aus dem TODO-Block Sicherheitsdurchgang: Apple-/Synology-Kalenderpasswoerter lagen
im Klartext in CalendarConnection.accessToken ("email:passwort"). Bei einem
Datenbank-Leak waeren das nicht nur App-Daten, sondern die echten Zugangsdaten der
Nutzer bei Apple bzw. auf ihrem NAS.

- src/lib/credential-crypto.ts: AES-256-GCM, Schluessel aus CREDENTIAL_ENCRYPTION_KEY.
  enc1:-Praefix unterscheidet neu verschluesselte Werte von Alt-Klartext -- Klartext
  bleibt beim Entschluesseln unveraendert lesbar statt zu brechen.
- src/lib/prisma.ts: Prisma-Client-Extension ver-/entschluesselt accessToken/refreshToken
  transparent fuer alle rund ein Dutzend Aufrufstellen (Google/Outlook/Notion-Tokens
  laufen mit, sind ebenso sensibel). Kein Aenderungsbedarf an den Aufrufstellen selbst.
- src/lib/auth.ts: PrismaAdapter braucht einen expliziten Typ-Cast fuer den erweiterten
  Client (kein $on/$use mehr in dessen TS-Form, zur Laufzeit unveraendert).

Bestehende Zeilen brauchen keine Migration: Alt-Klartext bleibt lesbar und wird beim
naechsten Schreiben (Token-Refresh, Neuverbinden) automatisch verschluesselt.

ACHTUNG -- NICHT DEPLOYEN, bevor CREDENTIAL_ENCRYPTION_KEY in der Produktions-.env
steht: ohne den Schluessel wirft jeder Kalender-Request eine Exception. Konnte nicht
selbst gesetzt werden (Remote-Schreibzugriff per SSH von der Sandbox verweigert).

Verifiziert: Typecheck, Lint, 85 Unit-Tests (6 neu), Produktions-Build -- alle gruen,
mit einem Test-Schluessel gebaut.

v1.124.0

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 16:53:33 +02:00

634 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Changelog
All notable changes to this project are documented in this file.
## [1.124.0] - noch nicht deployed
**Achtung: dieser Commit braucht `CREDENTIAL_ENCRYPTION_KEY` in der Produktions-`.env`,
bevor er deployed wird — siehe unten.**
### Security
- **Apple-/Synology-Kalenderpasswörter und OAuth-Tokens werden jetzt verschlüsselt
gespeichert.** `CalendarConnection.accessToken`/`refreshToken` enthielten bei Apple- und
Synology-Verbindungen das echte Passwort im Klartext (`"email:passwort"`). Neue
Prisma-Client-Extension (`src/lib/prisma.ts`) ver-/entschlüsselt diese Felder transparent
für alle bestehenden Aufrufstellen (AES-256-GCM, `src/lib/credential-crypto.ts`).
Bestehende Klartextzeilen bleiben lesbar (`enc1:`-Präfix unterscheidet altes von neuem
Format) und werden beim nächsten Schreiben automatisch verschlüsselt — keine Migration,
keine erzwungene Neuverbindung nötig.
### Erforderlich vor dem Deploy
`CREDENTIAL_ENCRYPTION_KEY` (32 Byte, base64) muss in der Produktions-`.env` stehen. Ohne
diese Variable wirft jeder Kalender-Request eine Exception. Erzeugen mit:
```bash
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
```
Verifiziert: Typecheck, Lint, 85 Unit-Tests (6 neu für `credential-crypto`),
Produktions-Build — alle grün, mit einem Test-Schlüssel gebaut.
## [1.123.2] - 2026-09-12
Erste drei Punkte aus dem TODO-Block L (Sicherheitsdurchgang vom 12.09.) — sichere, in
sich abgeschlossene Änderungen ohne Datenmodell-Migration.
### Security
- **Kein automatischer `db push --accept-data-loss`-Fallback mehr in
`scripts/update-app.sh`.** Eine fehlgeschlagene `prisma migrate deploy` bricht das
Deployment jetzt sauber ab statt unbeaufsichtigt das Schema zu überschreiben — genau
dieser Fallback ist am 11.09. beim Boot-Lauf tatsächlich ausgelöst worden.
- **14 API-Routen gaben rohe Fehlermeldungen (`error.message`) direkt an den Client
zurück** (Kalender-Connect/-Sync, Task-Import/-Sync, Someday-Listen, Datenimport). Neuer
Helper `src/lib/api-error.ts` (`safeErrorMessage`): generische Meldung in Produktion,
echte Meldung nur noch lokal beim Entwickeln. Volles Logging bleibt unverändert in
`console.error`.
- SMTP-Zertifikatsprüfung wieder aktiv: `tls: { rejectUnauthorized: false }` in
`src/lib/email.ts` entfernt — stand unbegründet seit dem allerersten Commit der Datei und
öffnete Tür für Man-in-the-Middle auf Passwort-Reset-/Verifizierungs-Mails.
Verifiziert: Typecheck, Lint, 79 Unit-Tests, Produktions-Build alle grün.
## [1.123.1] - 2026-09-12
### Fixed
- **Erster Deploy nach dem next@15-Upgrade schlug am Smoke-Test fehl**, weil
Next.js den Standalone-Output um einen Ordner tiefer verschachtelt hat als
erwartet: `.next-build/standalone/My-Weekly-ToDo-List/server.js` statt
`.next-build/standalone/server.js`. Ursache: Next sucht per Lockfile-Suche
nach oben einen "Workspace-Root" fuer das File-Tracing und fand dabei eine
verwaiste, leere `package-lock.json` in `/root` auf dem Produktionshost --
hielt `/root` faelschlich fuer die Projektwurzel und spiegelte deren
Ordnerstruktur in den Output. `scripts/update-app.sh` erwartet den flachen
Pfad fest. `outputFileTracingRoot: __dirname` in `next.config.js` verankert
die Wurzel jetzt explizit -- unabhaengig davon, was sonst noch im
Dateisystem oberhalb des Projekts liegt.
- Die laufende App war davon nicht betroffen: der Smoke-Test hat den kaputten
Build wie vorgesehen abgefangen, bevor umgeschaltet wurde.
## [1.123.0] - 2026-09-12
### Security
- **`next` 14.2.35 → 15.5.24.** Patcht mehrere kritische Advisories, darunter zwei
unauthentifizierte RCE-Klassen (Windows-Hosts, AVIF-Bildoptimierung — beide auf
diesem Linux-Host ohnehin nicht direkt erreichbar, aber jetzt geschlossen) sowie
diverse DoS-, SSRF- und Cache-Poisoning-Advisories. 15.5.24 patcht bereits alles,
was sonst erst next@16 fordern würde — ein Major-Sprung statt zwei.
- **`next-auth` 4.24.13 → 4.24.15**, plus `@auth/core` per `overrides` auf 0.41.3
erzwungen (next-auth pinnt es sonst exakt auf die verwundbare 0.34.3 — als
optionalen Peer, den dieses Projekt nirgends importiert). Behebt einen
E-Mail-Normalisierungs-Bypass, einen Absturz bei fehlerhaften Bearer-Headern und
eine fehlende Provider-Bindung bei OAuth-State/Nonce/PKCE-Cookies.
- **`nodemailer` 7 → 10** (`overrides`, da next-auth einen ungenutzten optionalen
Peer auf 7.x haelt). Behebt SMTP-Command-Injection, CRLF-Header-Injection, eine
SSRF/Arbitrary-File-Read-Luecke ueber den `raw`-Nachrichtenmodus und eine
IDN/Punycode-Domain-Bypass beim Empfaenger-Check.
- `postcss` (auch die von `next` selbst intern gebündelte Kopie, per `overrides`)
auf 8.5.28. Behebt XSS in der CSS-Ausgabe und mehrfache Pfad-Traversal-/
Arbitrary-File-Read-Lücken über `sourceMappingURL`.
- Ungenutztes `@auth/prisma-adapter` (v5-Adapter, nirgends importiert — das
Projekt nutzt `@next-auth/prisma-adapter` für v4) entfernt.
- **`npm audit`: 24 → 0 gemeldete Schwachstellen.**
### Fixed
- `google-auth-library` war ein undeklarierter, nur über `googleapis` transitiv
gehofftes Hoisting verfügbarer Import in `google-tasks.ts`. Ein frischer
`package-lock.json` (Nebeneffekt der obigen Audit-Fixes) hob ihn nicht mehr
passend, wodurch zwei strukturell inkompatible Kopien nebeneinander existierten
und der Typecheck brach. Jetzt eine direkte Abhängigkeit plus `overrides`-Eintrag,
damit immer genau eine Instanz installiert wird.
- `nodemailer.Transporter` gibt es als Namespace-Property seit `@types/nodemailer`
8 nicht mehr — `Transporter` ist jetzt ein benannter Type-Export. Einzige
betroffene Stelle: `src/lib/email.ts`.
- Next.js 15: dynamische Routen-Parameter (`{ params }`) sind jetzt ein Promise.
Einzige betroffene Route: `PATCH`/`DELETE` unter
`/api/calendar/cross-sync/rules/[id]`.
Verifiziert: Typecheck, Lint und 79 Unit-Tests grün; Produktions-Build erfolgreich;
Login-Seite, Sicherheitsheader und die Auth-Middleware (Redirect bei fehlendem
Token) im gebauten Standalone-Server manuell geprüft. Kein Playwright-E2E-Lauf,
da dafür eine echte Datenbank und OAuth-Zugangsdaten nötig sind.
## [1.122.0] - 2026-09-12
### Security
- **Login-Endpunkt hatte kein Rate-Limit.** signup/reset-password/verify-email/
resend-verification waren bereits gedrosselt, der eigentliche Login
(`authorize()` in `src/lib/auth.ts`) nicht -- ein Bruteforce gegen ein
bekanntes Konto lief unbegrenzt durch. Jetzt maximal 8 Versuche pro E-Mail
in 15 Minuten, mit eigener Fehlermeldung auf der Login-Seite.
- Fehlende Standard-Sicherheitsheader ergaenzt (`X-Content-Type-Options`,
`X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`) und
`X-Powered-By: Next.js` abgeschaltet. Bewusst ohne Content-Security-Policy --
die braucht eine sorgfaeltige Whitelist fuer OAuth, Google Fonts und die
FontAwesome-CDN und ist ein eigener Schritt.
- `npm audit fix` (ohne `--force`) eingespielt: 24 auf 10 gemeldete
Schwachstellen reduziert, alles ohne Breaking Changes.
### Fixed
- `scripts/update-app.sh` setzt jetzt selbst `PATH=/usr/local/bin:$PATH`.
Ein Lauf ohne Login-Shell (z. B. `ssh host 'scripts/update-app.sh'`) fand
`pm2` sonst nicht und brach direkt nach einem erfolgreichen Build beim
Neustart ab -- am 12.09. live aufgetreten, siehe Betriebsnotiz.
- `tsconfig.json` nimmt `.next-build/types/**/*.ts` mit auf. Next.js traegt
diesen Pfad beim Bauen mit `NEXT_DIST_DIR=.next-build` automatisch ein;
ohne den committeten Eintrag stand das auf dem Produktionshost als
staendige, nie eingecheckte Abweichung in `git status`.
### Known issues
- **`next` (14.2.35) und `next-auth`/`@auth/core` (4.24.x) bleiben mit
bekannten kritischen CVEs** (u. a. ein E-Mail-Normalisierungs-Bypass in
Auth.js und mehrere Next.js-Advisories bis hin zu RCE-Klassen). Der Fix
ist ein Major-Upgrade auf next@16 und next-auth v5 -- ein eigenes,
sorgfaeltig zu testendes Vorhaben, kein Patch. `npm audit` zeigt den
aktuellen Stand.
## [1.121.2] - 2026-09-12
### Changed
- Die Spaltenaufteilung sich überlappender Termine im Tagesraster ist aus
`WeeklyView.tsx` in `src/lib/weekly/event-layout.ts` ausgelagert
(`layoutDaySegments`/`computeEventOverlapLayout`), mit eigenen Tests
abgesichert. Erster Schnitt der CodeScene-getriebenen Sanierung; der
Algorithmus selbst ist unverändert, kein sichtbares Verhalten geändert.
## [1.121.1] - 2026-09-12
### Fixed
- **`/api/calendar/apple/sync` war ohne Anmeldung erreichbar** und hat beliebige
Apple-Zugangsdaten aus dem Request gegen Apple CalDAV geschickt. Die Instanz
liess sich damit als Relay zum Durchprobieren fremder Zugangsdaten
missbrauchen. Die Route verlangt jetzt dieselbe Session-Prüfung wie
`/api/calendar/apple/connect`.
- `AuthForm.tsx` schrieb E-Mail-Adresse und Passwort im Klartext in die
Browser-Konsole. Die Debug-Ausgaben sind entfernt.
## [1.121.0] - 2026-09-12
### Fixed
- **Termine über Mitternacht verlängerten den Starttag**, statt am Folgetag
weiterzulaufen. Ein Termin von 22:00 bis 02:00 erschien bisher als ein
überlanger Block im Starttag; jetzt wird er an der Tagesgrenze geteilt:
22:00–24:00 am Starttag, 00:00–02:00 am Folgetag — so, wie es Kalender-Apps
zeigen. Die Aufteilungsregel (`splitEventByDay` in
`src/lib/weekly/date-utils.ts`) ist mit Unit-Tests abgesichert. Klick, Drag
und Resize wirken weiterhin auf den ungeteilten Termin; Payloads und API
sind unverändert.
## [1.120.1] - 2026-09-07
### Fixed
- **Das Skript übersprang den Build, wenn vorher von Hand `git pull` ausgeführt wurde.**
Die Entscheidung hing an `HEAD` vs. `origin/main` — nach einem manuellen Pull sind beide
gleich, und das Skript sah keinen Grund zu bauen, obwohl der vorhandene Build aus einem
älteren Commit stammte. Es lieferte weiter den alten Stand aus und meldete dabei
„Nothing to do". Massgeblich ist jetzt `.next/BUILD_COMMIT`: aus welchem Commit der
vorhandene Build tatsächlich stammt.
- **Der Smoke-Test-Server überlebte das Beenden.** `( … ) &` startet eine Subshell, `$!`
ist deren PID — `node` darin lief als Waise weiter und hielt Port 3999 belegt, womit
jeder weitere Deploy am Smoke-Test gescheitert wäre. Mit `exec` ersetzt `node` die
Subshell, `kill` greift, und ein `kill -9` fasst nach. Zusätzlich prüft das Skript vorab,
ob der Port frei ist, und der ERR-Trap beendet den Server ebenfalls.
Beide Fehler kamen bei einem Testlauf ans Licht, nicht in Produktion.
## [1.120.0] - 2026-09-07
### Changed
- **Ein fehlgeschlagener Build reisst die laufende App nicht mehr mit.** `next build` lief
bisher direkt auf `.next` — also auf dem Verzeichnis, aus dem die laufende Instanz bedient
wird. Brach der Build ab, fand PM2 `server.js` nicht mehr (`MODULE_NOT_FOUND`) und geriet
in eine Neustartschleife; am 07.09. war die Seite deshalb rund eine Stunde unbrauchbar.
Der neue Ablauf in `scripts/update-app.sh`:
1. Build nach `.next-build` — `.next` bleibt dabei unangetastet
2. Statische Dateien und `.env` in den neuen Baum kopieren
3. **Smoke-Test:** Der neue Build wird auf Port 3999 gestartet und muss auf
`/auth/login` antworten. Ein Build kann durchlaufen und trotzdem nicht starten.
4. Erst danach umschalten; der alte Stand bleibt als `.next-previous` liegen
5. PM2 neu starten
Scheitert irgendetwas davon, wird `.next-build` weggeräumt und die laufende App bleibt
unverändert. Zum Zurückrollen genügt `mv .next-previous .next` plus PM2-Neustart.
- `next.config.js`: `distDir` kommt jetzt aus `NEXT_DIST_DIR` und fällt auf `.next` zurück.
Beide Fälle sind praktisch getestet: Ein abgebrochener Lauf liess `.next` samt Markierung
unberührt und räumte `.next-build` weg; ein erfolgreicher schaltete sauber um, legte
`.next-previous` an und bestand den Smoke-Test.
## [1.119.4] - 2026-09-07
### Fixed
- Heap-Limit für den Build von 2048 auf 4096 MB angehoben. Der Wert aus v1.119.3 stammte
aus der Zeit, als der Container 3 GB hatte, und war schlicht zu niedrig: Der Build starb
punktgenau bei 2045 MB — also an der gesetzten Grenze. Gemessener Bedarf sind rund 2,6 GB.
## [1.119.3] - 2026-09-07
### Fixed
- `scripts/update-app.sh` setzt beim Build `NODE_OPTIONS="--max-old-space-size=2048"`.
Nodes Standard-Heap richtet sich nach dem Arbeitsspeicher und landet auf dem
Produktionscontainer bei rund 1,5 GB — zu wenig für `next build`, der Deploy starb mit
`FATAL ERROR: Ineffective mark-compacts near heap limit`.
### Notes
- **Gemessen, nicht vermutet:** ESLint im Build ist *nicht* die Ursache. Spitzenspeicher
mit ESLint 2.590 MB, ohne 2.595 MB. `eslint.ignoreDuringBuilds` bleibt daher auf `false`
— eine zwischenzeitliche Rücknahme beruhte auf einer falschen Annahme.
- Der Build braucht rund **2,6 GB**. Bei 3 GB Container-RAM bleibt das knapp; mehr Speicher
für LXC 202 steht als Block J in `TODO.md`.
## [1.119.2] - 2026-09-07
### Changed
- Die restlichen fünf Fremdkomponenten aus `WeeklyView.tsx` liegen jetzt unter
`src/components/weekly/`: `TaskInput`, `SomedayAddTask`, `TaskItem` (mit über 900 Zeilen
die grösste), `ListVisualsPopover` und `TabVisualsPopover`.
- Die Prioritäts-Abzeichen sind nach `weekly/priority-badge.tsx` gewandert. `GridTaskBlock`
importierte `getPriorityBadge` aus `WeeklyView.tsx` und zog sich damit die gesamte
Wochenansicht in seinen Abhängigkeitsgraphen; `WeeklyView` re-exportiert die Funktion,
bestehende Importe bleiben gültig.
- **`WeeklyView.tsx`: 11.148 → 9.711 Zeilen** — erstmals unter 10.000. Gegenüber dem Stand
vor dem Audit (11.906) sind das 2.195 Zeilen oder 18 Prozent.
Verifiziert: Typecheck 0 Fehler, `npm run build` Exit 0, 63 Unit-Tests grün.
## [1.119.1] - 2026-09-07
### Changed
- `ProjectIcon`, `ProjectsSidebar` und `NotesSidebar` liegen jetzt als eigene Dateien unter
`src/components/weekly/`. Sie standen in `WeeklyView.tsx`, hatten dort aber bereits eigene
Props und nichts mit der Wochenansicht zu tun. Die Symbolliste `projectIconsGlobal` ist zu
`ProjectIcon` gewandert, wo sie als einzige gebraucht wird.
- `WeeklyView.tsx`: 11.556 → 11.148 Zeilen.
In `WeeklyView.tsx` stecken insgesamt **acht** solcher eigenständigen Komponenten mit
zusammen rund 1.870 Zeilen — fünf davon warten noch (siehe TODO.md D1).
Verifiziert: Typecheck 0 Fehler, `npm run build` Exit 0, 63 Unit-Tests grün.
## [1.119.0] - 2026-09-07
### Changed
- Der laufende Tag ist jetzt auch im Spaltenkopf hervorgehoben, nicht mehr nur im
Spaltenkörper. Der Kopf ist `position: sticky` und deckte mit `--weekly-bg` ab, weshalb
die Färbung erst unterhalb von Wochentag und Datum begann.
- Die Symbole in Kalenderterminen (Wiederholung, Anbieter) sitzen jetzt oben rechts statt
unten rechts. Titel und Uhrzeit bekommen dort Freiraum, damit sich nichts überlappt —
über `:has(.event-icons-row)` nur bei Terminen, die überhaupt Symbole zeigen. Umgesetzt
für beide Darstellungen: Zeitraster und Liste.
## [1.118.2] - 2026-09-07
### Fixed
- **Kein `@ts-nocheck` mehr im Projekt.** Drei Dateien hatten die Typprüfung komplett
abgeschaltet und blieben deshalb auch von `ignoreBuildErrors: false` unberührt:
- `calendar-cache.ts` brauchte die Ausnahme überhaupt nicht — ohne sie: null Fehler.
- `microsoft-todo.ts`: vier Fälle zirkulärer Typinferenz (`url` → `data` → `response`
→ `url`), aufgelöst durch explizite Annotationen.
- `outlook-calendar.ts`: Hinter der Ausnahme steckte ein möglicher Laufzeitfehler. Der
Code streute `response!`-Assertions, eine Stelle war vergessen. Statt einer weiteren
Assertion prüft der Code jetzt echt, ob nach den drei Wiederholversuchen überhaupt
eine Antwort vorliegt.
### Added
- `TODO.md`: Block I mit zwei UI-Wünschen — Hervorhebung des aktuellen Tages auch im
Spaltenkopf, und die Symbole in Kalenderterminen nach oben rechts. Beide mit den
konkreten Fundstellen in `globals.css`.
## [1.118.1] - 2026-09-07
### Fixed
- **Der Build prüft wieder Typen.** `typescript.ignoreBuildErrors` steht auf `false`.
Damit ist ein erfolgreicher Build endlich wieder ein Beleg dafür, dass der Code
übersetzbar ist — bisher war er es nicht.
- **Notion-Anbindung war defekt.** Der Code rief `notion.databases.query()` auf; in
`@notionhq/client` v5 gibt es diese Methode nicht mehr, der Aufruf lief zur Laufzeit in
ein "is not a function". Umgestellt auf `dataSources.query()`, die Suche filtert jetzt
auf `data_source`. **Ungetestet** (kein Notion-Zugang), und gespeicherte Verbindungen
enthalten alte `database_id`s — betroffene Nutzer müssen Notion neu verbinden.
- `WeeklyView.tsx` importierte `isAllDayEvent` weiterhin aus `format`, obwohl die Funktion
in v1.118.0 nach `date-utils` gewandert war. Wäre zur Laufzeit gescheitert.
- Export-Helfer (`EXPORT_FIELDS`, `isoWeek`, `weekLabel`, `csvCell` …) liegen jetzt in
`src/lib/export-fields.ts`. Next.js erlaubt in einer `route.ts` nur Route-Handler als
Exporte; zudem importierte `SettingsSidebar` sie direkt aus der Route und zog damit
einen Server-Handler in den Client-Graphen.
- `push-notifications.ts`: `applicationServerKey` braucht seit TypeScript 5.7 einen Cast
auf `BufferSource`.
- Typdeklaration für `web-push` in `src/types/web-push.d.ts` ergänzt — als lokale Datei
statt als Abhängigkeit, weil `npm install` im Projektordner nicht läuft.
### Changed
- `tsconfig.json`: `include` von `**/*` auf `src/**/*` eingegrenzt. **Das war die Ursache
dafür, dass der Typecheck über 30 Minuten lief** — nicht die Größe von `WeeklyView.tsx`,
wie zuvor vermutet. Der vollständige Lauf dauert jetzt **19 Sekunden**.
## [1.118.0] - 2026-09-07
### Added
- **63 Unit-Tests** in `tests/unit/` für die ausgelagerten reinen Funktionen. Sie laufen in
rund 170 ms über Nodes eingebauten Test-Runner — ohne Browser, ohne Datenbank, ohne neue
Abhängigkeit. `npm test` führt sie aus, `npm run test:e2e` startet weiterhin Playwright.
Geprüft werden ISO-Kalenderwochen, Wochenstart-Berechnung, das Zeitraster, die
12/24-Stunden-Anzeige, der Round-Trip zwischen `formatDateToISO` und `parseCalendarDate`
sowie Farbinvertierung und Spaltenbegrenzung.
### Fixed
- `isAllDayEvent` rief `parseCalendarDate` auf, ohne es zu importieren — beim Auslagern aus
`WeeklyView.tsx` in v1.117.7 übersehen. Der Build meldete nichts, weil
`typescript.ignoreBuildErrors` noch aktiv ist; zur Laufzeit hätte der erste ganztägige
Termin mit Uhrzeit einen `ReferenceError` ausgelöst. Die Funktion liegt jetzt in
`date-utils.ts`, wo sie thematisch hingehört.
- `resolveViewDays` nutzte `clampViewDays` ohne Import — derselbe Fehler beim selben Schnitt.
`clampViewDays` und `MAX_VIEW_DAYS` sind nach `device-settings.ts` gewandert; `DaysSelector`
re-exportiert sie, bestehende Importe bleiben gültig.
Beide Fehler sind genau die Sorte, die `typescript.ignoreBuildErrors` verdeckt — und beide
wären von den neuen Tests sofort gefunden worden.
## [1.117.7] - 2026-09-07
### Changed
- Geräteabhängige Einstellungen nach `src/lib/weekly/device-settings.ts` (Cookie-Helfer und
die vier Schlüssellisten, die bewusst nur im Cookie und nicht in der Datenbank landen).
- Darstellungs-Helfer nach `src/lib/weekly/format.ts` (`stripHtml`, `invertColor`,
`isAllDayEvent`, `getSomedaySlotCount`).
- `WeeklyView.tsx`: 11.655 → 11.556 Zeilen. Mit `npm run build` verifiziert (Exit 0).
Damit sind die reinen Funktionen ohne Komponenten-State heraus — insgesamt 11.906 → 11.556
Zeilen (rund 3 %). Der Rest ist die Komponente selbst und braucht echtes Props-Design statt
Verschieben.
## [1.117.6] - 2026-09-07
### Changed
- Datums-, Wochen- und Zeitraster-Helfer nach `src/lib/weekly/date-utils.ts` verschoben
(13 reine Funktionen ohne Komponenten-State: `getStartOfWeek`, `getDayName`,
`getTimeSlots`, `formatHour`, `getWeekNumber`, `formatCustomHeader` und weitere).
`WeeklyView.tsx`: 11.830 → 11.655 Zeilen. Mit `npm run build` verifiziert (Exit 0).
### Notes
- `getWeekNumber` existiert im Projekt **dreimal** — hier, in `WeekPrintModal.tsx` und in
`app/api/user/export-week-view/route.ts`. Die Duplikate zu ersetzen steht in TODO.md;
vorher müssen die drei Implementierungen auf Gleichheit geprüft werden.
## [1.117.5] - 2026-09-07
### Changed
- Die gemeinsamen Datentypen der Wochenansicht liegen jetzt in `src/lib/weekly/types.ts`
(`Task`, `CalendarEvent`, `SomedayList`, `ViewStyle`, `KanbanStage`, `CellDuration`,
`RecurringTaskException`). `SettingsSidebar` und `GridTaskBlock` importierten sie bisher
aus `WeeklyView.tsx` und zogen sich damit die gesamte 11.900-Zeilen-Komponente in ihren
Abhängigkeitsgraphen. `WeeklyView.tsx` re-exportiert die Typen weiterhin, bestehende
Importe funktionieren also unverändert.
- Erster Schritt der Aufteilung von `WeeklyView.tsx`: 11.906 → 11.830 Zeilen.
Mit `npm run build` verifiziert (Exit 0, 36 Seiten, `/tasks` unverändert bei 265 kB).
## [1.117.4] - 2026-09-07
### Fixed
- Der Build meldet wieder ESLint-Fehler: `eslint.ignoreDuringBuilds` steht auf `false`.
Es waren nur **zwei** echte Errors im gesamten Projekt (`let json` statt `const json` in
`SettingsSidebar.tsx`), beide behoben. `next lint` endet jetzt mit Exit 0.
Die 695 Warnungen — 470-mal `no-explicit-any`, 209 ungenutzte Variablen, 11 unvollständige
Hook-Abhängigkeiten — blockieren den Build nicht und werden fortlaufend abgebaut.
## [1.117.3] - 2026-09-06
### Security
- `.env.production.save` und `.env.local` aus dem Arbeitsverzeichnis gelöscht — mit `shred`
überschrieben, damit die Werte nicht aus freiem Speicher rekonstruierbar bleiben. Beide
waren identische Kopien derselben Zugangsdaten.
- Klartext-Werte in `deployment-notes.md` und `INFRASTRUCTURE_REQUIREMENTS.md` durch
Platzhalter ersetzt (je drei Vorkommen: DB-Passwort, `NEXTAUTH_SECRET`, SMTP-Passwort).
Die Dateien bleiben als Dokumentation lesbar.
- Damit liegt außerhalb der Git-Historie kein Zugangsdatum mehr im Projekt.
## [1.117.2] - 2026-09-06
### Security
- Weitere Dateien mit echten Zugangsdaten aus dem Git-Index genommen (lokal bleiben sie
erhalten): `deployment-notes.md` und `INFRASTRUCTURE_REQUIREMENTS.md` — letztere führt das
Datenbank-Passwort sogar unverschleiert als `**Password**: …` auf. Ebenfalls entfernt:
`.env.production.save`, der ursprüngliche Fund.
- Passwort aus `tests/debug/db-connection-test.spec.ts` entfernt; dort stand eine vollständige
Connection-URL samt echter IP.
- `.gitignore` greift jetzt auf `.env*` statt auf eine Liste fester Namen. Die alte Regel
listete `.env.production`, weshalb `.env.production.save` durchs Raster fiel.
### Changed
- `TODO.md`: Die Rotation des Datenbank-Passworts ist in einen neuen Block F verschoben —
bewusst zurückgestellt. Die Datenbank hört auf einer privaten LAN-Adresse, und bei
selbst gehostetem Supabase steckt dasselbe Passwort an mehreren Stellen, was einen
halben Wechsel gefährlich macht. Enthält jetzt einen Prüfbefehl, mit dem sich ohne
Klartext-Ausgabe feststellen lässt, ob eines der geleakten Passwörter noch aktiv ist.
## [1.117.1] - 2026-09-06
### Fixed
- `docs/secret-rotation.md`, Schritt 2: Die Anleitung ging von *Supabase Cloud* mit
Dashboard und "Reset database password"-Knopf aus. Tatsächlich läuft Supabase
**selbst gehostet per Docker auf einem eigenen LXC** (siehe `scripts/supabase-setup.sh`) —
es gibt kein Cloud-Dashboard. Korrigiert samt Ports (6543 Pooler, 5432 direkt,
8000 API, 8001 Studio) und dem tatsächlichen Weg über `POSTGRES_PASSWORD` plus
`ALTER USER`, inklusive Backup-Schritt.
## [1.117.0] - 2026-09-06
### Security
- Rate-Limiting für alle Auth-Endpunkte: `signup` (5/15 Min), `resend-verification`
(3/15 Min), `verify-email` (10/15 Min) und `reset-password` (5/15 Min), je Client-IP.
Bisher waren sie unbegrenzt aufrufbar — beliebig viele Mails über den eigenen
SMTP-Zugang, beliebig viele Rateversuche auf Verifizierungscodes.
- Passwort-Reset-Token wird nur noch als SHA-256-Hash gespeichert. Bisher lag er im
Klartext in der Datenbank: Lesezugriff auf die DB genügte, um fremde Konten zu übernehmen.
- Tote Mock-Login-Route `src/app/api/auth/login/route.ts` entfernt. Sie verglich gegen ein
hartcodiertes Fake-Passwort, erzeugte ein bedeutungsloses `mock-jwt-token-…` und war
über die Middleware-Ausnahme für `/api/auth/*` ohne Anmeldung erreichbar. Kein
Login-Bypass, aber sie protokollierte jede probierte E-Mail-Adresse.
- Passwort-Reset nutzt jetzt bcrypt-Kosten 12 statt 10 — gleicher Wert wie beim Signup.
### Fixed
- `npm test` lief ins Leere: Das Skript rief `jest` auf, obwohl es weder eine Jest-Config
noch einen einzigen Jest-Test gibt. Zeigt jetzt auf Playwright; `jest`, `ts-jest` und
`@types/jest` sind entfernt (letzteres kollidiert mit den Playwright-Globals).
- Falscher `sonar.projectKey` im GitHub-Workflow — er hätte in ein zweites, leeres
SonarQube-Projekt geschrieben statt in das bestehende.
### Changed
- Die zwölf Wegwerf-Skripte aus der Fehlersuche (`login-debug`, `css-debug-test`, …) liegen
jetzt unter `tests/debug/` und sind per `testIgnore` aus dem normalen Testlauf genommen.
Aktiv bleiben die drei echten Testdateien. Erreichbar über `npm run test:debug`.
### Added
- `TODO.md`: abhakbare Liste aus dem Repository-Audit.
- `docs/secret-rotation.md`: Anleitung zum Austausch der Zugangsdaten, ohne dass ein Wert
auf dem Bildschirm erscheint.
- `scripts/quality-check.sh`: bündelt `npm audit`, Typecheck, Lint und den SonarQube-Scan
für den nächtlichen Lauf. `npm audit` fehlte bisher komplett.
## [1.116.4] - 2026-09-06
### Fixed
- Improved calendar event text contrast while retaining the calendar-colored background.
## [1.116.3] - 2026-09-05
### Added
- Calendar events can now be reassigned to another editable calendar from the event editor.
- Moving an event updates the source and target calendar caches.
### Changed
- Calendar event backgrounds now use the calendar color with a lighter, readable contrast.
- Google event-specific colors no longer override the selected calendar's color.
## [1.116.2] - 2026-09-05
### Fixed
- The remove action now applies to saved local location suggestions only.
OpenStreetMap search results remain available, while removed saved locations
are deleted from the user's stored location list.
## [1.116.1] - 2026-09-05
### Fixed
- Display preferences such as the number of visible days now remain device-specific.
Automatic profile saving no longer sends browser-only settings back to the shared account.
- An explicitly selected day count now survives mobile reloads and orientation changes.
- Cancelled or superseded location autocomplete requests no longer update the event form.
- Photon autocomplete requests now have a five-second timeout and input length limit.
## [1.116.0] - 2026-09-04
### Added
- Added a two-day reminder preset for calendar events.
- Added optional address and business autocomplete using Photon/OpenStreetMap,
with debounced server-side search and OpenStreetMap attribution.
- Users can keep their original location text or explicitly select a suggested result.
## [1.115.3] - 2026-09-03
### Fixed
- Drag & drop in views wider than 7 days: the drop target was keyed by weekday
(`date.getDay()`), which is only unique inside a 7-day window. In a 21-day view every
column sharing the weekday of the hovered one lit up as a drop target — three mirrored
highlights, one per Tuesday. Now keyed by the local date.
- Same bug, worse half: `handleDrop` resolved the target date with
`visibleDays.find(d => d.getDay() === dayOfWeek)`, so dropping a task on the second or
third Tuesday of a wide view silently scheduled it on the *first* Tuesday. The drop now
uses the column's own date. Tasks dragged in a >7-day view before this fix may sit on the
wrong date.
- The inline "click an empty slot to add a task" input had the same weekday key, so clicking
one slot opened an input in every column with that weekday.
## [1.115.2] - 2026-09-03
### Changed
- `scripts/update-app.sh` is now the single update path, used both for a manual deploy and
for the boot run; `my-weekly-todo-update.service` points at it. There were two
near-identical scripts and they drifted — the `npm ci` fix from 1.114.1 only ever landed
in update-app.sh, so the boot path kept running `npm install`. `scripts/boot-update.sh`
is now a shim that calls it, kept only so a not-yet-recopied unit file still works.
- The update log moved from `/var/log/app-boot-update.log` to
`/var/log/my-weekly-todo-update.log`.
### Added
- `update-app.sh` skips the rebuild when `HEAD` already matches `origin/main` and
`.next/standalone/server.js` exists, so a reboot no longer costs a full rebuild. Pass
`--force` to rebuild anyway.
- `update-app.sh` takes a `flock` single-instance lock, so a boot run and a manual run can
no longer install into the same tree at once — which is how node_modules ended up in a
state npm could not clean up.
- `update-app.sh` runs `prisma migrate deploy` and `prisma generate` (previously only the
boot script did), and copies `.env` into the standalone output.
### Fixed
- `update-app.sh` no longer fails at boot trying to restart a PM2 process that does not
exist yet. It restarts only when the PM2 daemon is actually running, and otherwise leaves
the start to `pm2-root.service`, which is ordered after this unit. It deliberately does
not probe with `pm2 describe`, since any pm2 command spawns a daemon that would race the
real service.
## [1.115.1] - 2026-09-03
### Fixed
- `scripts/boot-update.sh` (the "update on reboot" unit): every command was piped into `tee`,
so `set -e` only ever saw tee's exit status. A failed `npm install` or `npm run build` did
not stop the script and did not fail the systemd unit — it logged "Boot update finished"
and left a half-built tree. Now uses `set -euo pipefail`, logs through a single `exec`
redirect, and traps ERR.
- `scripts/boot-update.sh`: switched from `npm install` to `npm ci`. Same fix update-app.sh
got in 1.114.1 — `npm install` rewrites package-lock.json, leaving the tree dirty so the
*next* `git pull` aborts.
- `scripts/boot-update.sh`: body wrapped in a function so bash parses it in one go. The
script lives inside the checkout it pulls, so `git pull` rewriting it mid-run made bash
resume at a stale file offset.
- `scripts/boot-update.sh`: set `GIT_TERMINAL_PROMPT=0` so a git credential prompt fails
fast instead of hanging the unit at boot, where there is no tty.
- `scripts/my-weekly-todo-update.service`: raised `TimeoutStartSec` from 300 to 1800.
`npm ci` + `prisma generate` + `next build` takes far longer than 5 minutes in the
container; systemd SIGTERMed the oneshot unit mid-install, which left a `node_modules`
tree npm could no longer remove (`ENOTEMPTY: rmdir node_modules/date-fns/isEqual`).
- `package-lock.json`: version resynced with `package.json` (missed in the 1.115.0 commit).
## [1.115.0] - 2026-09-03
### Added
- Day columns: a "Custom" option next to the 1/2/3/5/7 presets, where any number of days from
1 to 31 can be typed (e.g. 14, 21, 28). The chosen value shows up as its own chip beside the
presets and is available in every place the day count can be set — sidebar, both settings
flyouts, the desktop toolbar and the header overflow menu.
### Fixed
- Day grid and all-day grid only had CSS for `cols-1/2/3/5/7`, so any other day count silently
fell back to a 5-column layout. Both grids now derive `grid-template-columns` from the actual
day count.
## [1.114.2] - 2026-09-01
### Fixed
- Deploy script: copy `public/` and `.next/static/` into `.next/standalone/` after the build.
`next.config.js` sets `output: 'standalone'`, and Next.js does not copy those itself. Without
the copy every deploy left the old chunk hashes in the standalone tree, so the browser's
requests for the new chunks 404'd, React never hydrated, and the app hung on "Loading
session..." — while the API kept returning 200, which made it look like an auth problem.
## [1.114.1] - 2026-09-01
### Added
- `scripts/update-app.sh`: the production deploy script, now version-controlled instead of
living only on the server. Hardened with `set -euo pipefail` (a failed `git pull` used to
still trigger a rebuild and PM2 restart on unchanged code) and switched from `npm install`
to `npm ci` (npm install rewrote package-lock.json, leaving the tree dirty so the next
pull aborted).
## [1.114.0] - 2026-09-01
### Added
- Focus Mode: "SKIP" button to move on to the next task of the day without completing the
current one. Skipped tasks are remembered until Focus Mode is closed, and the timer
resets for each new task. The button is disabled when no other task is left for today.
### Changed
- Styling settings: removed the standalone "Element Colors" and "Weekend Colors" sections —
those controls now live only in the CSS Variables panel (added there in 1.113.1).
- Styling settings: larger, borderless color swatches and bigger labels in the CSS Variables
panel.
## [1.113.1] - 2026-07-08
### Changed
- CSS Variables panel: added the existing "Today Highlight", "Past Days", "Saturday" and
"Sunday" color controls as their own groups (bound to the same profile fields as the
standalone Element/Weekend Colors sections, so both stay in sync).
- CSS Variables panel: every row now shows the color swatch before its label.
## [1.113.0] - 2026-07-08
### Added
- Styling settings: "CSS Variables" panel with a color picker and description for every
customizable color token in `globals.css` (base colors, settings sidebar, design token
aliases, event colors, all-day chips).
- Styling settings: "Custom CSS" textarea to inject a personal stylesheet that loads after
the app's default styles and can override any rule.