Files
microservice-importer/README.md
T
2026-05-03 17:03:25 +02:00

7.9 KiB
Raw Blame History

Microservice Importer

Intern import-tjänst (importer-api) för 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 3X120Gquantity=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.ymlej 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:

# 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:

docker logs importer-api -f

Hälsokontroll:

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

  • PDF-import — textbaserad extraction
  • OCR för skannade bild-PDFs (tesseract.js + Alpine-paket)
  • Kvittoparsning via Mistral AI
  • 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