fix: Apple-Sync-Route absichern und Passwort-Logging entfernen
Zwei Befunde aus dem Sicherheitsdurchgang zum CodeScene-Auftrag: - /api/calendar/apple/sync war ohne Anmeldung erreichbar und hat beliebige Apple-Zugangsdaten aus dem Request gegen Apple CalDAV geschickt. Damit liess sich die Instanz als Relay zum Durchprobieren fremder Zugangsdaten nutzen. Die Route verlangt jetzt dieselbe Session-Pruefung wie /connect. - AuthForm.tsx hat E-Mail und Passwort im Klartext in die Browser-Konsole geschrieben. Die Debug-Ausgaben sind entfernt. Keine Aenderung an Payloads oder Antwortformaten. v1.121.1 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d52e780967
commit
00010059a2
431
TODO.md
431
TODO.md
@ -1,431 +0,0 @@
|
||||
# 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)
|
||||
- [x] `src/app/api/auth/login/route.ts` löschen
|
||||
- [x] 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)
|
||||
- [x] Entscheiden: Jest raus oder Jest einrichten
|
||||
- [x] `package.json`: `"test"` auf das zeigen lassen, was wirklich existiert
|
||||
- [x] `"test:e2e"` für Playwright ergänzen
|
||||
- [x] Debug-Skripte in `tests/` aussortieren
|
||||
- [x] **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)
|
||||
- [x] Sliding-Window-Limiter in `src/lib/rate-limit.ts` anlegen
|
||||
- [x] Einbinden in `signup`
|
||||
- [x] Einbinden in `resend-verification`
|
||||
- [x] Einbinden in `reset-password`
|
||||
- [x] Einbinden in `verify-email`
|
||||
- [x] 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)
|
||||
- [x] Beim Erzeugen: Klartext per Mail, **Hash** in die DB
|
||||
- [x] 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)
|
||||
- [x] `.env.production.save` und `.env.local` gelöscht (mit `shred` überschrieben)
|
||||
- [x] `.gitignore` auf `.env*` erweitert (Ausnahme `.env.example`)
|
||||
- [x] `NEXTAUTH_SECRET` neu erzeugt — 44 Zeichen, geprüft
|
||||
- [x] `SMTP_PASSWORD` neu gesetzt
|
||||
- [x] `deployment-notes.md` und `INFRASTRUCTURE_REQUIREMENTS.md` aus dem Index entfernt
|
||||
- [x] Klartext-Werte in beiden Dateien durch Platzhalter ersetzt
|
||||
- [x] Passwort aus `tests/debug/db-connection-test.spec.ts` entfernt
|
||||
- [x] 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`](docs/secret-rotation.md).
|
||||
|
||||
---
|
||||
|
||||
## Block C — Build und Typsicherheit
|
||||
|
||||
### C1. Build darf Fehler nicht mehr verschlucken (Audit 1)
|
||||
- [x] **Schritt 1:** `eslint.ignoreDuringBuilds` in `next.config.js` auf `false`
|
||||
- [x] Gemeldete ESLint-Fehler abarbeiten — es waren nur **2** (`prefer-const` in
|
||||
`SettingsSidebar.tsx`), verifiziert: `next lint` endet jetzt mit Exit 0
|
||||
- [x] **Schritt 2:** `npm run type-check` — es waren **7 Fehler**, alle behoben
|
||||
- [x] **Schritt 3:** `typescript.ignoreBuildErrors` auf `false` — Build prüft jetzt Typen
|
||||
- [x] `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.
|
||||
- [x] **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` | **9.711** *(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
|
||||
- [x] Typen nach `src/lib/weekly/types.ts` (7 Typen, 97 Zeilen) — Build verifiziert
|
||||
- [x] `src/components/weekly/` angelegt
|
||||
- [x] **Entdeckung:** In `WeeklyView.tsx` steckten **acht eigenständige Komponenten**, die
|
||||
gar nichts mit der Wochenansicht zu tun haben — zusammen rund 1.870 Zeilen. Drei davon
|
||||
sind heraus: `ProjectIcon`, `ProjectsSidebar`, `NotesSidebar`.
|
||||
- [x] Restliche fünf herausgelöst: `TaskInput`, `SomedayAddTask`, `TaskItem`,
|
||||
`ListVisualsPopover`, `TabVisualsPopover` — dazu die Prioritäts-Abzeichen, die
|
||||
`GridTaskBlock` von `WeeklyView` importierte
|
||||
- [ ] Someday-Listen aus dem Haupt-JSX herauslösen (jetzt echtes Props-Design nötig)
|
||||
- [ ] All-Day-Leiste herauslösen
|
||||
- [ ] Zeitraster und Tagesspalten herauslösen
|
||||
- [ ] Einstellungs-Flyouts herauslösen
|
||||
- [ ] Drag & Drop in einen Hook (~15 Abhängigkeiten — erst sinnvoll, wenn der State
|
||||
geordneter ist; ein Hook mit 15 Parametern wäre keine Verbesserung)
|
||||
- [x] Datums-/Zeitraster-Helfer nach `src/lib/weekly/date-utils.ts` (13 Funktionen, 202 Zeilen)
|
||||
- [x] Cookie-/Geräteeinstellungen nach `src/lib/weekly/device-settings.ts`
|
||||
- [x] 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
|
||||
- [ ] 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)
|
||||
- [x] SonarQube-Analyse gegen https://sq.martin-bierschenk.de einrichten
|
||||
- [x] Regelmäßigen Lauf aufsetzen — nightly 03:00, `todo-sonar.timer` (systemd user unit)
|
||||
- [x] `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`](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:
|
||||
|
||||
```bash
|
||||
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_id`s. Das sind keine
|
||||
`data_source_id`s** — betroffene Nutzer müssen Notion einmal neu verbinden.
|
||||
|
||||
---
|
||||
|
||||
## Block K — Deploy-Sicherheit ✅
|
||||
|
||||
### K1. Build zerstört nicht mehr die laufende App ✅ *(v1.120.0)*
|
||||
- [x] Build läuft nach `.next-build`, das laufende `.next` bleibt unberührt
|
||||
- [x] Smoke-Test vor dem Umschalten — der neue Build muss einmal geantwortet haben
|
||||
- [x] Umschalten erst danach, alter Stand bleibt als `.next-previous` liegen
|
||||
- [x] Beide Fälle getestet: Fehlschlag lässt `.next` unangetastet, Erfolg schaltet sauber um
|
||||
- [x] Build-Erkennung an den gebauten Commit gekoppelt (`.next/BUILD_COMMIT`) statt an
|
||||
`HEAD` vs. `origin/main` — sonst überspringt das Skript den Build, wenn jemand vorher
|
||||
von Hand `git pull` ausführt
|
||||
- [x] Smoke-Test-Server wird zuverlässig beendet (`exec`, sonst überlebt `node` als Waise
|
||||
und blockiert Port 3999 für alle weiteren Deploys)
|
||||
|
||||
**Warum:** `next build` lief direkt auf `.next` — also auf dem Verzeichnis, aus dem die
|
||||
laufende App bedient wird. Scheiterte der Build, war nicht nur das Update weg, sondern die
|
||||
laufende Instanz gleich mit: PM2 fand `server.js` nicht mehr
|
||||
(`MODULE_NOT_FOUND`) und geriet in eine Neustartschleife mit über 790 Versuchen.
|
||||
Am 07.09. war die Seite dadurch rund eine Stunde unbrauchbar.
|
||||
|
||||
**Zurückrollen** (falls ein Deploy doch mal Mist ausliefert):
|
||||
|
||||
```bash
|
||||
pm2 stop my-weekly-todo
|
||||
cd /root/My-Weekly-ToDo-List
|
||||
rm -rf .next && mv .next-previous .next
|
||||
pm2 restart my-weekly-todo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Block J — Build-Speicher auf dem Container
|
||||
|
||||
### J1. Container-RAM erhöhen *(empfohlen)*
|
||||
- [ ] LXC 202 von 3 GB auf 6 GB anheben (Proxmox → Container → Resources → Memory)
|
||||
|
||||
**Warum:** `next build` braucht **rund 2,6 GB Spitzenspeicher** (gemessen). Bei 3 GB
|
||||
Container-RAM bleibt fast nichts übrig — der laufende PM2-Prozess und das System teilen
|
||||
sich den Rest. Der Deploy vom 07.09. starb deshalb mit
|
||||
`FATAL ERROR: Ineffective mark-compacts near heap limit`.
|
||||
|
||||
`scripts/update-app.sh` setzt jetzt `NODE_OPTIONS="--max-old-space-size=2048"`, weil Nodes
|
||||
Standard-Heap auf diesem Container bei nur rund 1,5 GB liegt. Das ist die Sofortmaßnahme,
|
||||
aber bei 2,6 GB Bedarf bleibt es knapp. **Mehr RAM ist die eigentliche Lösung** und in
|
||||
Proxmox ein Klick.
|
||||
|
||||
**Nicht die Ursache — geprüft:** ESLint im Build. Gemessener Spitzenspeicher mit ESLint
|
||||
2.590 MB, ohne 2.595 MB. Kein Unterschied.
|
||||
|
||||
### J2. Alternative: woanders bauen
|
||||
- [ ] Erwägen, den Build auf dem Spark laufen zu lassen und nur `.next/standalone` zu
|
||||
übertragen
|
||||
|
||||
**Warum:** Dann braucht der Produktionscontainer überhaupt keinen Build-Speicher mehr —
|
||||
er würde nur noch fertige Dateien erhalten und PM2 neu starten. Grösserer Umbau des
|
||||
Deploy-Wegs, aber die sauberste Trennung.
|
||||
|
||||
---
|
||||
|
||||
## Block I — UI-Wünsche
|
||||
|
||||
### I1. Aktuellen Tag auch im Spaltenkopf hervorheben ✅ *(vom Nutzer bestätigt)*
|
||||
- [x] Hervorhebungsfarbe zusätzlich hinter Wochentagsname und Datum gelegt
|
||||
|
||||
**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](src/components/WeeklyView.tsx#L7089)) 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 ✅ *(vom Nutzer bestätigt, Freiraum reicht)*
|
||||
- [x] `.event-icons-row` nach oben rechts verschoben, Titel bekommt rechts Freiraum
|
||||
|
||||
**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)
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-weekly-todo-list",
|
||||
"version": "1.121.0",
|
||||
"version": "1.121.1",
|
||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -1,8 +1,18 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getServerSession } from 'next-auth';
|
||||
import { getUpcomingEvents } from '@/lib/apple-calendar';
|
||||
import { authOptions } from "@/lib/auth";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
// Ohne Anmeldung war diese Route ein offener Relay: beliebige Apple-Zugangsdaten
|
||||
// liessen sich hier gegen Apple durchprobieren. Gleiche Pruefung wie in
|
||||
// /api/calendar/apple/connect.
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user?.email) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await req.json();
|
||||
const { email, password, calendarId, timeMin, timeMax } = body;
|
||||
|
||||
|
||||
@ -14,13 +14,9 @@ const AuthForm: React.FC<AuthFormProps> = ({ type, onSubmit, isLoading, error })
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
console.log('📝 AuthForm handleSubmit called with:', { email, password });
|
||||
console.log('📝 onSubmit function is:', typeof onSubmit);
|
||||
// Kein Logging der Formulardaten: hier stand das Passwort im Klartext in der Browser-Konsole.
|
||||
if (typeof onSubmit === 'function') {
|
||||
console.log('🚀 Calling onSubmit function...');
|
||||
onSubmit({ email, password });
|
||||
} else {
|
||||
console.error('❌ onSubmit is not a function!', onSubmit);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user