Files
recipe-app/migrering-MSI.md
T

267 lines
14 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.
This file has been removed as all relevant information has been migrated.
# Prisma P3009 recovery (MySQL, migrationer)
**Problem:**
Prisma migrationer kan fastna i failed state (P3009) om en migration körts med fel SQL-citering (t.ex. "User" istället för `User` i MySQL) eller om deploy avbryts mitt i en migrering.
**Symptom:**
```
migrate found failed migrations in the target database, new migrations will not be applied. The `20260506144000_add_ai_engine_enabled` migration ... failed
```
**Lösning/playbook:**
1. Rätta migrationsfilen så att den använder backticks (`) för tabell- och kolumnnamn (MySQL-stil).
2. Kör:
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate resolve --rolled-back 20260506144000_add_ai_engine_enabled --schema prisma/schema.prisma"
docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma"
```
3. Om deploy klagar på duplicate column (dvs kolumnen finns redan):
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate resolve --applied 20260506144000_add_ai_engine_enabled --schema prisma/schema.prisma"
docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma"
```
4. Verifiera status:
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate status --schema prisma/schema.prisma"
```
**Lessons learned:**
- Kontrollera alltid SQL-citering i migrationsfiler (MySQL kräver backticks, inte dubbla citattecken).
- Vid P3009: använd `migrate resolve` för att markera migrationen som rolled-back eller applied beroende på DB-läge.
- Kör alltid migrationskommandon i rätt container/miljö för att undvika env- och version-mismatch.
# Session 2026-05-06: Migreringar för user-scoped AI och premium
Denna session:
- Lade till `aiEngineEnabled` på User i Prisma-schema och migrerade databasen (manuell SQL vid behov).
- Implementerade endpoint och logik för admin att toggla AI per användare.
- Säkerställde att alla AI-funktioner är user-scoped och premiumstyrda.
- Lessons learned: Vid DB-connectivity-problem krävs ibland manuell migration och resolve (se driftsekvens i TEKNISK_BESKRIVNING.md).
Se även:
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för drift- och migreringsdetaljer.
# Migrering: Import-funktion → microservice-importer
## Status: ✅ GENOMFÖRD 2026-04-30
## Dokumentstatus (2026-05-03)
### Målgrupp
Detta dokument är för systemadministratörer och utvecklare som ansvarar för integrationen mellan recipe-app och microservice-importer.
### Tillägg efter genomförd migrering
- Kvittoparsningens regelbaserade tolkning har förbättrats för multipack och enheter.
- Brödrelaterade kategoriregler och contradiction guards har utökats för högre träffsäkerhet.
- Klientens kvittosession i Flutter är nu persistent utan att ändra backendkontrakt eller införa serverlagring av sessionen.
- Kategoriträdet i seed-data har utökats med `Korvbröd` under `Fastfoodbröd`.
- **PDF-fix (2026-05-03):** `pdf-parse` använder `require()` istället för ESM-import; `pdfjs-dist/legacy/build/pdf.js` är fallback vid parsningsfel — löser `DOMMatrix is not defined` i Node.js Alpine-miljö.
- **Felkods-forwarding (2026-05-03):** `receipt-import.service.ts` returnerar nu `ServiceUnavailableException` (503) vid 503/429 från importer-api istället för `BadRequestException` (400).
- **Reproducerbart bygge (2026-05-03):** `package-lock.json` spåras i git; Dockerfile för importer-api kör `npm ci`.
- **AI-optimering (2026-05-03):** `looksLikeReceiptProductLine()` filtrerar PDF-rader utan siffra innan Mistral-anrop — minskar onödiga API-anrop vid kvittoimport.
- **Scope:** quick-import, parse-markdown, receipt-import
- **Arkitektur:** Backend-till-backend — recipe-app NestJS-backend anropar microservice-importer internt via HTTP. Frontend ändras inte.
- **OCR:** Implementerat i microservice-importer (tesseract.js + Alpine apk-paket)
- **Infra:** `importer-api`-tjänst i `recipe-app/compose.yml`, port 3001 intern
- **Driftsatt:** 2026-04-30, alla containers Healthy
---
## Fas 1 — Utöka microservice-importer ✅
**1. OCR-stöd och multipart i quick-import** ✅
- `QuickImportService.importFromUpload()` tillagd — hanterar PDF (pdf-parse) och bild (tesseract.js)
- `quick-import.controller.ts` utökat med `FileInterceptor`, `@HttpCode(200)`
- `Dockerfile` uppdaterad: `apk add tesseract-ocr tesseract-ocr-data-swe tesseract-ocr-data-eng`
**2. `imageUrl` i quick-import-svaret** ✅
- `imageUrl?: string` tillagd i `ParsedRecipe`-interfacet (`base.parser.ts`)
- ICA-parsern extraherar nu `recipe.image` (string/array/objekt-varianter)
- `QuickImportResult` utökad med `imageUrl?`, `imageWarning?`, source `'image'`
- `normalizeImageUrl()` hanterar protokollrelativa URL:er (`//cdn.ica.se/...`)
**3. Ny `ReceiptParsingModule`** ✅
- `backend/src/receipt-parsing/receipt-parsing.service.ts` — Mistral AI-parsning av kvitto (bild/PDF)
- `backend/src/receipt-parsing/receipt-parsing.controller.ts` — `POST /api/receipt-import/parse`, `@HttpCode(200)`, tillåter `application/octet-stream`
- `backend/src/receipt-parsing/receipt-parsing.module.ts`
- Registrerad i `app.module.ts`
**4. Health-endpoint** ✅
- `GET /api/health` → `{status: "ok"}` inline i `app.module.ts`
**5. Bugfixar i document-service** ✅
- `document-service.module.ts`: korrigerade importvägar + klassnamn (`DocumentImportModule` → `DocumentServiceModule`)
- `services/document-import.service.ts`: parsersökväg `./parsers/` → `../parsers/`
- Borttagna dubbletter: `services/web-scraping.module.ts`, `services/document-service.module.ts`
---
## Fas 2 — Anpassa recipe-app backend ✅
**5. Refaktorera `QuickImportService`** ✅
- All lokal parsning (ICA, pdf-parse, tesseract) borttagen
- Delegerar URL-import: `POST importer-api:3001/api/quick-import` (JSON)
- Delegerar filuploading: `POST importer-api:3001/api/quick-import` (FormData, `new Uint8Array(file.buffer)`)
- `downloadAndOptimizeImage()` behålls lokalt (körs efter microservice returnerat `imageUrl`)
- `IMPORTER_SERVICE_URL` env-variabel med fallback `http://importer-api:3001`
**6. Refaktorera `ReceiptImportService`** ✅
- AI-parsning (Mistral, pdf-parse) borttagen ur recipe-app
- Delegerar till `POST importer-api:3001/api/receipt-import/parse` (FormData)
- `matchProducts()` och `enrichWithAiCategories()` behålls (DB-krav)
- `RECEIPT_IMPORT_MODEL`-konstanten flyttad till `ai.controller.ts` (lokal konstant)
**7. Refaktorera `RecipesService.parseMarkdown()`** ✅
- Delegerar markdown-parsning till `POST importer-api:3001/api/recipes/parse-markdown`
- Fallback till lokal `parseRecipeMarkdown()` vid driftavbrott
- Levenshtein-produktmatchning behålls lokalt
---
## Fas 3 — Infrastruktur ✅
**8. `importer-api` i `recipe-app/compose.yml`** ✅
- Build-context: `../microservice-importer`, dockerfile `backend/Dockerfile`
- Image: `recipe-importer-api:local`, `pull_policy: never`
- Nätverk: `recipe-internal` (ej exponerad externt)
- Env: `MISTRAL_API_KEY`, `PORT=3001`
- Healthcheck: `wget -qO- http://127.0.0.1:3001/api/health`
- `recipe-api` får `depends_on: importer-api: condition: service_healthy`
---
## Relevanta filer som ändrades
| Fil | Förändring |
|---|---|
| `microservice-importer/backend/src/web-scraping-service/parsers/base.parser.ts` | `imageUrl?` i `ParsedRecipe` |
| `microservice-importer/backend/src/web-scraping-service/parsers/ica.parser.ts` | Extraherar `recipe.image` |
| `microservice-importer/backend/src/web-scraping-service/services/quick-import.service.ts` | Omskriven: OCR, PDF, imageUrl, importFromUpload |
| `microservice-importer/backend/src/web-scraping-service/controllers/quick-import.controller.ts` | FileInterceptor, HttpCode(200) |
| `microservice-importer/backend/src/web-scraping-service/web-scraping.module.ts` | Fixade importvägar + klassnamn |
| `microservice-importer/backend/src/document-service/document-service.module.ts` | Fixade importvägar + klassnamn |
| `microservice-importer/backend/src/document-service/services/document-import.service.ts` | Fixad parsersökväg |
| `microservice-importer/backend/src/receipt-parsing/` | Ny modul (service, controller, module) |
| `microservice-importer/backend/src/app.module.ts` | ReceiptParsingModule + HealthController |
| `microservice-importer/backend/Dockerfile` | apk add tesseract-ocr |
| `recipe-app/backend/src/quick-import/quick-import.service.ts` | Delegerar till importer-api |
| `recipe-app/backend/src/receipt-import/receipt-import.service.ts` | AI-del delegeras, matchning behålls |
| `recipe-app/backend/src/recipes/recipes.service.ts` | parseMarkdown delegeras, matchning behålls |
| `recipe-app/backend/src/ai/ai.controller.ts` | RECEIPT_IMPORT_MODEL lokal konstant |
| `recipe-app/compose.yml` | importer-api-tjänst tillagd |
---
## Avgränsningar (oförändrade)
- **Frontend ändras inte** — samma proxy-routes, samma API-kontrakt
- **Auth stannar i recipe-app backend** — microservice-importer exponeras bara internt
- **Bildoptimering** behålls i recipe-app (`downloadAndOptimizeImage` vid `RecipesService.create()`)
- `matchProducts()` och `enrichWithAiCategories()` stannar i recipe-app (DB-krav)
---
## Fas 1 — Utöka microservice-importer
*Steg 13 är oberoende och kan utföras parallellt.*
**1. Lägg till OCR-stöd (tesseract.js)**
Ny `ImageParser` i `backend/src/web-scraping-service/parsers/`. Controllern
`quick-import.controller.ts` utökas att acceptera `multipart/form-data` för
bilder vid sidan av JSON-body för URL-anrop.
**2. Lägg till `imageUrl` i quick-import-svaret**
`quick-import.service.ts` returnerar idag `{ markdown, source }`. Komplettera
med `imageUrl?` (original-URL från skrapad sida).
**3. Ny `ReceiptParsingModule` stateless kvittoparsning**
Ny modul `backend/src/receipt-parsing/` med endpoint `POST /api/receipt-import/parse`.
- PDF → text via `pdf-parse`; bild → base64
- Anropar Mistral AI med kvitto-prompt
- Returnerar: `[{ rawName, quantity, unit, price, brand, origin }]`
- Ingen databaskoppling — rent stateless
---
## Fas 2 — Anpassa recipe-app backend
*Beror på Fas 1. Steg 57 kan utföras parallellt.*
**4. Lägg till `HttpModule` + `IMPORTER_SERVICE_URL`**
recipe-app backend registrerar NestJS:s `HttpModule` (axios-wrapper).
`IMPORTER_SERVICE_URL` sätts som env-variabel (`http://importer-api:3001` i Docker).
**5. Refaktorera `QuickImportService`**
Ta bort lokal ICA-parsning, pdf-parse och tesseract — anropa istället
microservice-importer `POST /api/quick-import` (eller `POST /api/document-import`
för PDF). `QuickImportModule` behåller sin controller och DTO (API-kontrakt oförändrat).
**6. Refaktorera `ReceiptImportService`**
- AI-parsning → delegeras till `POST $IMPORTER_URL/api/receipt-import/parse`
- Produktmatchning (Levenshtein mot `Product`, `ReceiptAlias`) — behålls i recipe-app (DB-krav)
- Slår ihop och returnerar samma svar som idag till frontend
**7. Refaktorera `RecipesService.parseMarkdown()`**
- Anropar `POST $IMPORTER_URL/api/recipes/parse-markdown` → `{ name, ingredients[], ... }`
- Kör befintlig Levenshtein-produktmatchning mot `Product`-tabellen i recipe-app
- Returnerar sammansatt svar — API-kontraktet mot frontend oförändrat
**8. Ta bort lokala parsningsberoenden**
Ta bort `pdf-parse`, `tesseract.js`, `node-fetch` etc. ur recipe-app backend
`package.json` när steg 57 är verifierade.
---
## Fas 3 — Infrastruktur
*Kan påbörjas parallellt med Fas 1.*
**9. Länka microservice-importer i recipe-app:s Docker Compose**
Lägg till `importer-api`-tjänst i `recipe-app/compose.yml` (byggs från
`../microservice-importer/backend`). Delar `recipe-network` med recipe-app
backend. Sätt `IMPORTER_SERVICE_URL=http://importer-api:3001` i recipe-app
backend-tjänstens env.
---
## Relevanta filer
| Fil | Förändring |
|---|---|
| `microservice-importer/backend/src/web-scraping-service/` | Ny ImageParser, imageUrl i svar |
| `microservice-importer/backend/src/` | Ny `receipt-parsing/` modul |
| `recipe-app/backend/src/quick-import/quick-import.service.ts` | Ersätt lokal parsning med HTTP-anrop |
| `recipe-app/backend/src/receipt-import/receipt-import.service.ts` | AI-del delegeras, matchning behålls |
| `recipe-app/backend/src/recipes/recipes.service.ts` | parseMarkdown delegeras, matchning behålls |
| `recipe-app/backend/src/app.module.ts` | Registrera HttpModule |
| `recipe-app/backend/package.json` | Ta bort pdf-parse, tesseract.js |
| `recipe-app/compose.yml` | Lägg till importer-api tjänst |
| `recipe-app/frontend/` | **Ändras inte** |
---
## Verifiering
1. `POST /api/quick-import` (recipe-app backend) med ICA-URL → samma svar som idag
2. `POST /api/quick-import` med PDF-fil → samma svar
3. `POST /api/recipes/parse-markdown` med markdown → ingredienser med produkt-ID:n
4. `POST /api/receipt-import` med kvittobild → matchade items med DB-produkt-ID:n
5. Autentisering fungerar (hanteras av recipe-app backend som tidigare)
6. `docker compose up` startar microservice-importer som intern tjänst
---
## Avgränsningar
- **Frontend ändras inte** — samma proxy-routes, samma API-kontrakt
- **Auth stannar i recipe-app backend** — microservice-importer exponeras bara internt på Docker-nätverket
- **Bildoptimering vid sparande** behålls i recipe-app (sker vid `RecipesService.create()`, inte vid import)
- `receipt-import` splittad: AI-del → microservice, produktmatchning + DB → recipe-app backend
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.