# Microservice Importer Intern import-tjänst (`importer-api`) för [recipe-app](../recipe-app). Hanterar URL-skrapning, OCR, PDF-parsning och AI-kvittoparsning utan databas. Körs som Docker-tjänst på det interna `recipe-internal`-nätverket — exponeras ej externt. ## Dokumentstatus (2026-05-03) ### Målgrupp Detta dokument är för systemadministratörer och utvecklare som driftar eller vidareutvecklar importtjänsten. ### Tillägg från senaste sessionerna - Regelbaserad kvittotolkning har förbättrats för multipack, enheter och antaluttryck. - Parserstödet för brödrelaterade produkter och guardrails mot felkategorisering har utökats. - Integrationen med klienten för kvitto-session är fortfarande stateless i denna tjänst; ingen serverlagring av användarsession infördes. ## Viktigt!! Kod- och byggpraxis! Säkerställ att inga absoluta Windows-sökvägar används i koden, för att stödja bygg och drift på Linux/Ubuntu --- ## Features ### Quick-import (`POST /api/quick-import`) - **URL-skrapning** — ICA.se (JSON-LD) och generisk parser. Extraherar `imageUrl` från receptbild. - **OCR (bild)** — tesseract.js, svenska+engelska. Returnerar `source: 'image'`. - **PDF-parsning** — pdf-parse för digitala PDFs, OCR-fallback för skannade. - **Multipart** — Tar emot antingen JSON-body (`{ url }`) eller FormData (`file`). ### Parse-Markdown (`POST /api/recipes/parse-markdown`) Tolkar Markdown-recept till strukturerat JSON utan databas. ### Kvittoparsning (`POST /api/receipt-import/parse`) - Bild (JPEG/PNG/WebP/HEIC/HEIF) eller PDF - **Modell:** `mistral-small-2603` (vision-kapabel) med retry-logik (3 försök vid 503/429) - Returnerar `ParsedReceiptItem[]` med fälten `rawName`, `quantity`, `unit`, `price`, `brand`, `origin` - Inbyggda regler i AI-prompten styr tolkning av `quantity`/`unit` (se nedan) ### Health (`GET /api/health`) Används av Docker-healthcheck i `recipe-app/compose.yml`. Returnerar `{ status: "ok" }`. --- ## Miljövariabler | Variabel | Beskrivning | Standardvärde | |---|---|---| | `MISTRAL_API_KEY` | API-nyckel för Mistral AI | (krävs för kvittoparsning) | | `PORT` | HTTP-port | `3001` | --- ## Arkitektur ### Backend (NestJS 10, TypeScript 5, Node.js 22-alpine) **Port:** 3001 (intern, ej exponerad till host) ``` src/ ├── app.module.ts # Root module + HealthController (GET /api/health) ├── main.ts ├── common/ │ ├── filters/global-exception.filter.ts │ └── utils/normalize-name.ts ├── web-scraping-service/ # Quick-import (URL + fil) │ ├── web-scraping.module.ts │ ├── controllers/quick-import.controller.ts # POST /api/quick-import │ ├── services/quick-import.service.ts # Scraping, OCR, PDF │ └── parsers/ │ ├── base.parser.ts # ParsedRecipe interface │ ├── ica.parser.ts # ICA.se JSON-LD + imageUrl │ └── generic.parser.ts ├── receipt-parsing/ # Kvittoparsning via Mistral AI │ ├── receipt-parsing.module.ts │ ├── receipt-parsing.controller.ts # POST /api/receipt-import/parse │ └── receipt-parsing.service.ts ├── document-service/ # PDF-dokumentimport │ ├── document-service.module.ts │ ├── controllers/document-import.controller.ts │ ├── services/document-import.service.ts │ └── parsers/ │ ├── document.parser.ts │ └── pdf.parser.ts └── recipes/ # Markdown-tolkning ├── recipes.module.ts ├── recipes.controller.ts # POST /api/recipes/parse-markdown ├── recipes.service.ts └── dto/parse-markdown.dto.ts ``` **Viktigt:** Backend har _INGEN_ databaskonfiguration — stateless service. --- ## Kvittoparsning — regler för quantity och unit Följande regler är inbyggda i AI-prompten och styr hur Mistral tolkar mängd och enhet per produkt: | Typ | Regel | Exempel | |---|---|---| | **Lösvikt** (kött, ost, frukt/grönt vägt i kassan) | `quantity` = faktisk vikt från kvittot, `unit` = `kg`/`g` | `BLANDFÄRS 20%` 0.997 kg → `quantity=0.997, unit="kg"` | | **Förpackad vara med storlek i namn** (mejeri, dryck, konserver) | `quantity` = antal förpackningar, `unit` = `"förp"` | `MJÖLK 1,5L` × 3 → `quantity=3, unit="förp"` | | **Multipack** (`NxYg`/`NxYml` i namn) | `quantity=1`, `unit="förp"` — räkna inte upp N | `BACON 3X120G` → `quantity=1, unit="förp"` | | **Förpackat innehåll** (bröd, kex, chips) | `quantity` = antal förpackningar, `unit` = `"förp"` | `BRIOCHE SESAM` × 2 → `quantity=2, unit="förp"` | | **Lösa styckvaror** (enstaka frukt/bröd per st) | `quantity` = antal, `unit` = `"st"` | `BANAN` × 1 → `quantity=1, unit="st"` | Tillåtna enheter: `st`, `kg`, `g`, `l`, `dl`, `cl`, `ml`, `förp`, `pak`, `burk`, `flaska` --- ## Parser-arkitektur ### Dokument-parsers (`document-service/parsers/`) Abstrakt bas `DocumentParser` som alla dokumenttyp-specifika parsers ärver från. ### PDF Parser (`pdf.parser.ts`) Hanterar textbaserade PDFs via `pdf-parse`. Skannade PDFs varnas. ### Webb-parsers (`web-scraping-service/parsers/`) Abstrakt bas `RecipeParser` med `canHandle(url)` + `parse(html)`. Implementationer: - **`ica.parser.ts`** — ICA.se, prioriterar JSON-LD structured data, extraherar `imageUrl` - **`generic.parser.ts`** — Fallback för alla webbplatser --- ## Deployment `importer-api` byggs och startas via `recipe-app/compose.yml` — **ej via sin egen compose-fil**. **Serverstruktur:** ``` /opt/containers/ microservice-importer/ ← klonas separat, pullas vid deploy recipe-app/ compose.yml ← definierar importer-api-tjänsten deploy.sh ← kör docker compose build + up ``` **Deploy:** ```bash # 1. Uppdatera importer (om ändringar gjorts) cd /opt/containers/microservice-importer && git pull # 2. Bygg och starta alla containers cd /opt/containers/recipe-app && git pull && ./deploy.sh ``` **Loggar:** ```bash docker logs importer-api -f ``` **Hälsokontroll:** ```bash docker exec importer-api wget -qO- http://localhost:3001/api/health # → {"status":"ok"} ``` **OBS:** Host-port 3001 används av `wetty` på servern. `importer-api` exponeras **aldrig** utanför Docker-nätverket — anropas via `http://importer-api:3001` från `recipe-api`. --- ## Tekniska detaljer ### Backend Stack - **NestJS** 10 — REST API & modular architecture - **TypeScript** 5 — Type safety - **Node.js** 22-alpine — Runtime (Alpine Linux) - **pdf-parse** — PDF text extraction - **tesseract.js** — OCR (bild och skannade PDFs, svenska + engelska) - **@mistralai/mistralai** — AI-kvittoparsning (`mistral-small-2603`) - **multer** — Multipart file upload handling - **Ingen databas** — Stateless service ### Systempaket (Alpine) Installerade i Dockerfile runner-stage: ``` tesseract-ocr tesseract-ocr-data-swe tesseract-ocr-data-eng ``` ### Error Handling - Centraliserad `GlobalExceptionFilter` (svenska meddelanden) - Konsistent JSON-responsformat: `{ statusCode, message, timestamp, path }` - HTTP status codes: 200, 400, 503 --- ## Framtida utbyggnader - [x] PDF-import — textbaserad extraction - [x] OCR för skannade bild-PDFs (tesseract.js + Alpine-paket) - [x] Kvittoparsning via Mistral AI - [x] ICA-receptbildsextraktion (`imageUrl` i `ParsedRecipe`) - [ ] Fler webbplats-parsers (Arla, Tasteline, Köket.se) - [ ] Word (.docx) import - [ ] Swagger/OpenAPI-dokumentation - [ ] Rate limiting / Caching --- ## Licens MIT --- ## Support - **Git Repo** — Gitea på `192.168.50.2:2222/nilsjohan/microservice-importer`