# Migrering: Import-funktion → microservice-importer ## Status: ✅ GENOMFÖRD 2026-04-30 - **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 1–3 ä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 5–7 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 5–7 ä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