diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 0752fe4..111044b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -52,7 +52,7 @@ src/ 1. User klickt auf Web-Hit → `/preview?url=...` 2. `/api/recipes/preview` → `importer.ts` lädt HTML, `parseHTML` von linkedom, `json-ld-recipe.ts` extrahiert `Recipe`-Objekt mit **externer** Bild-URL 3. Preview-Seite rendert das `Recipe` via `RecipeView.svelte` (erkennt externe URL und lädt direkt vom Original-CDN) -4. User klickt „Speichern" → `/api/recipes/import` → Importer lädt Bild (`images/downloader.ts`), SHA256-Hash-Dedup, speichert lokal, INSERT in `recipe` + `recipe_ingredient` + `recipe_step` + `recipe_tag` +4. User klickt „Speichern" → `/api/recipes/import` → Importer lädt Bild (`images/downloader.ts`), SHA256-Hash-Dedup, speichert lokal, INSERT in `recipe` + `ingredient` + `step` + `recipe_tag` 5. Redirect zu `/recipes/[id]` ### Web-Suche diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 0adffba..e856c5c 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -133,7 +133,7 @@ Die App hat ein eingebautes Backup unter `/admin` (ZIP-Export mit DB + Bildern). | `SEARXNG_URL` | `http://localhost:8888` | SearXNG-Endpoint, im Compose auf `http://searxng:8080` | | `KOCHWAS_THUMB_TTL_DAYS` | `30` | TTL für Thumbnail-Cache in der SQLite | | `DATABASE_PATH` | `data/kochwas.db` | Pfad zur SQLite, relativ oder absolut | -| `IMAGES_PATH` | `data/images` | Pfad für lokale Bild-Dateien | +| `IMAGE_DIR` | `data/images` | Pfad für lokale Bild-Dateien | | `PORT` | `3000` | Node-HTTP-Port (adapter-node) | Siehe `.env.example` im Repo. diff --git a/docs/superpowers/plans/2026-04-18-review-fixes.md b/docs/superpowers/plans/2026-04-18-review-fixes.md new file mode 100644 index 0000000..cfc9660 --- /dev/null +++ b/docs/superpowers/plans/2026-04-18-review-fixes.md @@ -0,0 +1,153 @@ +# Review-Fixes 2026-04-18 — Implementation Plan + +> **Quelle:** `docs/superpowers/review/REVIEW-2026-04-18.md` + Sub-Reports. +> **Branch:** `review-fixes-2026-04-18` +> **Goal:** Alle HIGH/MEDIUM Findings aus dem Code-Review adressieren, bewusst verschobene Items dokumentieren. +> **Architecture:** Inkrementelle Refactors, jeder atomar committed + gepusht, Tests nach jedem Wave grün. +> **Tech-Stack:** SvelteKit, TypeScript-strict, Zod, Vitest, better-sqlite3, Service-Worker. + +--- + +## Was wird angegangen (must-do) + +| # | Wave | Zeit | Begründung | +|---|------|------|------------| +| 1 | Doku-Fixes (ARCHITECTURE/OPERATIONS/handoff) | 5 min | Hoher Wert, trivialer Aufwand | +| 2 | constants.ts + Image-Endpoint EN + interne Types | 30 min | Alle "Quick-Wins" aus REVIEW | +| 3 | api-helpers.ts (parsePositiveIntParam + validateBody) | 1-2 h | Refactor A — 9+11 Call-Sites | +| 4 | requireProfile() + asyncFetch Wrapper | 1 h | Profile-Guard 4× + fetch-Pattern 5× | +| 5 | Cleanup (yauzl-Doku, baseRecipe-Fixture, Console-Logs) | 30 min | Restliche LOW-Findings | +| 6 | Ingredient-Parser Edge-Cases (Refactor D) | 2-3 h | Locale-Komma, Unicode-Brüche, Bounds | +| 7 | Verifikation (test/check/build, Docker-Smoke) | 30 min | Baseline gegen Regressionen | +| 8 | Re-Review + OPEN-ISSUES-NEXT.md | 1 h | Beweis + Ausblick | + +## Was bewusst NICHT angegangen wird (Begründung in OPEN-ISSUES-NEXT.md) + +- **Refactor B** (Search-State-Store, halber Tag): Touch von 808-Zeilen-Page + 678-Zeilen-Layout, bricht riskant Frontend ohne UAT. Eigene Phase planen. +- **Refactor C** (RecipeEditor zerlegen): Review sagt explizit "keine Eile, solange niemand sonst drin arbeitet". +- **SearXNG Rate-Limit Recovery**: Größeres Feature, eigene Phase. +- **SW-Zombie-Cleanup Unit-Tests**: Bereits 6 pwa-store-Tests vorhanden, Erweiterung wäre Bonus. +- **JSON-LD Parser Edge-Cases** (Locales): Weniger Käse als Ingredient-Parser-Issues, eigene Iteration. + +--- + +## Wave 1 — Doku-Fixes + +**Files:** `docs/ARCHITECTURE.md:55`, `docs/OPERATIONS.md:135`, `docs/superpowers/session-handoff-2026-04-17.md:46` + +- [ ] ARCHITECTURE.md: `recipe_ingredient` + `recipe_step` → `ingredient` + `step` +- [ ] OPERATIONS.md: `IMAGES_PATH` → `IMAGE_DIR` +- [ ] session-handoff: `/api/recipes/[id]/image` (POST/DELETE) ergänzen +- [ ] Commit `docs(review): Doku-Mismatches korrigiert` + +## Wave 2 — Konstanten + Cleanup + +**Files:** `src/lib/constants.ts` (neu), `src/routes/+page.svelte`, `src/lib/client/pwa.svelte.ts`, `src/routes/api/recipes/[id]/image/+server.ts`, `src/lib/sw/cache-strategy.ts`, `src/lib/sw/diff-manifest.ts` + +- [ ] `src/lib/constants.ts` mit `SW_VERSION_QUERY_TIMEOUT_MS = 1500`, `SW_UPDATE_POLL_INTERVAL_MS = 30 * 60_000` +- [ ] Image-Endpoint: deutsche Fehlermeldungen → englisch (Konsistenz) +- [ ] `RequestShape` / `ManifestDiff`: `export` weg wenn rein intern +- [ ] Test + check, Commit + +## Wave 3 — api-helpers.ts (TDD) + +**Files:** `src/lib/server/api-helpers.ts` (neu), `tests/unit/api-helpers.test.ts` (neu), `src/lib/types.ts` (ErrorResponse) + +### 3a Helper bauen +- [ ] Test: `parsePositiveIntParam("42", "id")` → 42 +- [ ] Test: `parsePositiveIntParam("0", "id")` wirft 400 +- [ ] Test: `parsePositiveIntParam("abc", "id")` wirft 400 +- [ ] Test: `parsePositiveIntParam(null, "id")` wirft 400 +- [ ] Test: `validateBody(invalid, schema)` wirft 400 mit issues +- [ ] Test: `validateBody(valid, schema)` returns parsed +- [ ] Implement helpers +- [ ] Tests grün, Commit + +### 3b Migration parseId → parsePositiveIntParam (9 Sites) +Files (jeder Endpoint): +- `src/routes/api/recipes/[id]/+server.ts` +- `src/routes/api/recipes/[id]/favorite/+server.ts` +- `src/routes/api/recipes/[id]/rating/+server.ts` +- `src/routes/api/recipes/[id]/cooked/+server.ts` +- `src/routes/api/recipes/[id]/comments/+server.ts` +- `src/routes/api/recipes/[id]/image/+server.ts` +- `src/routes/api/profiles/[id]/+server.ts` +- `src/routes/api/domains/[id]/+server.ts` +- `src/routes/api/wishlist/[recipe_id]/+server.ts` + +- [ ] Pro Endpoint: lokales parseId entfernen, Helper importieren +- [ ] Tests grün +- [ ] Commit + +### 3c Migration safeParse → validateBody +Files: alle `+server.ts` mit `safeParse`. ErrorResponse-Shape standardisieren. + +- [ ] Pro Endpoint umstellen +- [ ] Tests grün +- [ ] Commit + +## Wave 4 — Client-Helpers + +### 4a requireProfile() +- [ ] Helper in `src/lib/client/profile.svelte.ts` ergänzen +- [ ] 4 Sites in `src/routes/recipes/[id]/+page.svelte` ersetzen +- [ ] Test + Commit + +### 4b asyncFetch Wrapper +- [ ] `src/lib/client/api-fetch-wrapper.ts` mit `asyncFetch(url, init, actionTitle)` +- [ ] 5 Sites umstellen: `recipes/[id]/+page.svelte` (2×), `admin/domains/+page.svelte` (2×), `admin/profiles/+page.svelte` +- [ ] Test + Commit + +## Wave 5 — Cleanup + +- [ ] yauzl: Inline-Kommentar in package.json: "Reserved for Phase 5b ZIP-Backup-Import" +- [ ] baseRecipe Fixture nach `tests/fixtures/recipe.ts` (wenn dupliziert) +- [ ] Console-Logs: per `if (import.meta.env.DEV)` wrappen oder absichtlich-Kommentar +- [ ] Commit + +## Wave 6 — Ingredient-Parser Edge-Cases + +**Files:** `src/lib/server/parsers/ingredient.ts`, `tests/unit/ingredient.test.ts` + +### Tests zuerst (red) +- [ ] Locale-Komma: `"1,5 kg Mehl"` → qty 1.5 +- [ ] Unicode-½: `"½ TL Salz"` → qty 0.5 +- [ ] Unicode-⅓: `"⅓ Tasse Wasser"` → qty 1/3 +- [ ] Unicode-¼: `"¼ kg Zucker"` → qty 0.25 +- [ ] Negativ: `"-1 EL Öl"` → wirft / qty=null +- [ ] Null: `"0 g Mehl"` → wirft / qty=null +- [ ] Führende Null: `"0.5 kg"` → 0.5 +- [ ] Wissenschaftliche Notation: `"1e3 g"` → wirft / qty=null + +### Parser fixen +- [ ] Unicode-Brüche-Map +- [ ] Locale-Komma-Handling (sicher: "1,5" wenn nur 1 Komma + Ziffern drumrum) +- [ ] Bounds: 0 < qty <= 10000 (Zod refinement oder Pre-Check) +- [ ] Tests grün, Commit + +## Wave 7 — Verifikation + +- [ ] `npm test` — 158+ Tests grün +- [ ] `npm run check` — 0 Errors +- [ ] `npm run build` — erfolgreich +- [ ] Optional: Docker-Smoke `docker compose -f docker-compose.prod.yml up --build` +- [ ] Push aller Commits + +## Wave 8 — Re-Review + OPEN-ISSUES-NEXT.md + +- [ ] Parallele Explore-Agenten: dead-code, redundancy, structure, docs-vs-code +- [ ] Befunde in `docs/superpowers/review/OPEN-ISSUES-NEXT.md` +- [ ] Bewusst verschobene Items mit Begründung +- [ ] Neue Findings (falls vorhanden) +- [ ] Commit + Push + +--- + +## Erfolgs-Kriterien + +1. Tests grün (158+) +2. svelte-check: 0 Errors, 0 Warnings (oder ≤ Baseline) +3. Build erfolgreich +4. Alle 8 Quick-Wins + Refactor A + Refactor D umgesetzt +5. OPEN-ISSUES-NEXT.md vorhanden mit klarer Trennung "verschoben (warum)" vs "neu entdeckt" +6. Branch ready zum Mergen / PR diff --git a/docs/superpowers/session-handoff-2026-04-17.md b/docs/superpowers/session-handoff-2026-04-17.md index c23c51e..6bb9475 100644 --- a/docs/superpowers/session-handoff-2026-04-17.md +++ b/docs/superpowers/session-handoff-2026-04-17.md @@ -43,7 +43,7 @@ docker compose -f docker-compose.prod.yml up -d ### Server-Seite - **DB:** SQLite mit FTS5, Migrationen (`./migrations/*.sql`) werden von Vite gebündelt und beim ersten DB-Zugriff angewendet. Auto-mkdir für `data/` und `data/images/`. - **Module:** `parsers/` (iso8601, ingredient, json-ld-recipe), `recipes/` (scaler + repository + actions + importer + search-local), `domains/` (repository + whitelist), `profiles/`, `images/image-downloader`, `search/searxng`, `backup/export`, `http`. -- **Routes:** `/api/health`, `/api/profiles`, `/api/profiles/[id]`, `/api/domains`, `/api/domains/[id]`, `/api/recipes/search`, `/api/recipes/search/web`, `/api/recipes/preview`, `/api/recipes/import`, `/api/recipes/[id]`, `/api/recipes/[id]/rating`, `/api/recipes/[id]/favorite`, `/api/recipes/[id]/cooked`, `/api/recipes/[id]/comments`, `/api/admin/backup`, `/images/[filename]`. +- **Routes:** `/api/health`, `/api/profiles`, `/api/profiles/[id]`, `/api/domains`, `/api/domains/[id]`, `/api/recipes/search`, `/api/recipes/search/web`, `/api/recipes/preview`, `/api/recipes/import`, `/api/recipes/[id]`, `/api/recipes/[id]/rating`, `/api/recipes/[id]/favorite`, `/api/recipes/[id]/cooked`, `/api/recipes/[id]/comments`, `/api/recipes/[id]/image` (POST/DELETE), `/api/admin/backup`, `/images/[filename]`. ### Client-Seite (Svelte 5 Runes) - **Layout** mit Profil-Chip und Zahnrad zu Admin.