From 1bb0eedd4fd815e3a889922d4a1ad48beb3a0b4e Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Fri, 1 May 2026 00:27:30 +0200 Subject: [PATCH] Update to .md-files --- README.md | 392 ++++++++++----------------------------- next_steps_MSImporter.md | 54 +++++- 2 files changed, 149 insertions(+), 297 deletions(-) diff --git a/README.md b/README.md index c279761..00f6fb8 100644 --- a/README.md +++ b/README.md @@ -1,361 +1,161 @@ # Microservice Importer -Standalone-tjänst för import och konvertering av dokument till Markdown-format. Primärt fokus på PDF-filer — textbaserad extraction samt OCR för skannade dokument. Webb-skrapning finns som sekundär funktion för referens och framtida integration. - -Kan användas helt oberoende som fristående microservice. +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. ## 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 -### Dokument-Import via PDF -- **Textbaserad extraction** — Snabb och precis för digitalt skapade PDFs -- **OCR-detektering** — Identifierar skannade bild-PDFs och rapporterar tydligt (OCR-stöd under utveckling) -- **Automatisk konvertering:** - - Titeln extraheras från filnamnet - - Texten struktureras till Markdown-stycken - - Metadata sparas (antal sidor, teckenantal, producent, skapelsedatum) -- **Filstorlek:** Max 50 MB per fil -- **Format:** Multipart/form-data uppladdning +### 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`). -### Webb-skrapning (sekundär funktion) -- **Skrapa från ICA.se** — JSON-LD structured data + HTML-parsing -- **Generisk parser** — Fallback för andra webbplatser -- **Automatisk extraction:** Namn, beskrivning, ingredienser, instruktioner +### Parse-Markdown (`POST /api/recipes/parse-markdown`) +Tolkar Markdown-recept till strukturerat JSON utan databas. -### Parse-Markdown endpoint -Tolka Markdown-format utan databaskomplikationer. Användbar för API-integration utan lokal DB. +### Kvittoparsning (`POST /api/receipt-import/parse`) +- Bild (JPEG/PNG/WebP/HEIC/HEIF) eller PDF +- Mistral AI vision med retry-logik (3 försök vid 503/429) +- Returnerar `ParsedReceiptItem[]` + +### 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.3, TypeScript 5.4.5) -**Port:** 3001 +### Backend (NestJS 10, TypeScript 5, Node.js 22-alpine) +**Port:** 3001 (intern, ej exponerad till host) ``` src/ -├── app.module.ts # Root module -├── main.ts # Startpunkt +├── app.module.ts # Root module + HealthController (GET /api/health) +├── main.ts ├── common/ -│ ├── filters/ -│ │ └── global-exception.filter.ts # Centraliserad felhantering (svenska meddelanden) -│ └── utils/ -│ └── normalize-name.ts # Namnormalisering (åäö-handling) -├── document-import/ # PDF-import & konvertering (primär funktion) -│ ├── document-import.controller.ts # POST /api/document-import -│ ├── document-import.service.ts # Filvalidering, parser-routing -│ ├── document-import.module.ts +│ ├── 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/ -│ ├── document.parser.ts # Abstrakt bas-klass -│ └── pdf.parser.ts # PDF-extraction via pdf-parse -├── quick-import/ # Webb-skrapning (sekundär funktion) -│ ├── quick-import.controller.ts # POST /api/quick-import -│ ├── quick-import.service.ts # Scraping-logik, parser-selection -│ ├── quick-import.module.ts +│ ├── 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/ -│ ├── base.parser.ts # Abstrakt bas-klass -│ ├── ica.parser.ts # ICA.se-specifik (JSON-LD prioritet) -│ └── generic.parser.ts # Fallback för alla webbplatser -└── recipes/ # Markdown-tolkning (utan DB) - ├── recipes.controller.ts # POST /api/recipes/parse-markdown - ├── recipes.service.ts +│ ├── document.parser.ts +│ └── pdf.parser.ts +└── recipes/ # Markdown-tolkning ├── recipes.module.ts - └── dto/ - └── parse-markdown.dto.ts + ├── recipes.controller.ts # POST /api/recipes/parse-markdown + ├── recipes.service.ts + └── dto/parse-markdown.dto.ts ``` **Viktigt:** Backend har _INGEN_ databaskonfiguration — stateless service. -### Frontend (Next.js 16.2, React 19.2, TypeScript 5.4.5) -**Port:** 3000 -``` -app/ -├── layout.tsx # Root layout -├── page.tsx # Home page -├── Navigation.tsx # Minimal nav (Home + Import) -├── import/page.tsx # PRIMARY FEATURE — Drag-and-drop filuppladdning -├── api/ -│ ├── document-import-proxy/route.ts # Proxy: multipart/form-data → backend -│ └── parse-markdown-proxy/route.ts # Proxy: Markdown-tolkning → backend -└── lib/ - ├── api.ts # Centraliserad API-access (fetchJson) - └── error-handler.ts # parseErrorResponse (svenska meddelanden) -``` - ---- - -## Quick-Start - -### Prerequisites -- Node.js 22.x -- Docker & Docker Compose (valfritt) - -### Local Development - -```bash -# 1. Installera backend -cd backend -npm install -npm run start:dev -# Backend kör på http://localhost:3001 - -# 2. (I ny terminal) Installera frontend -cd frontend -npm install -npm run dev -# Frontend kör på http://localhost:3000 -``` - -Öppna http://localhost:3000 → Gå till `/import` → Klistra in URL - -### Docker - -```bash -# Bygg och starta -docker compose up -d - -# Frontend: http://localhost:3000 -# Backend: http://localhost:3001 -``` - -Stoppa: -```bash -docker compose down -``` - ---- - -## API-dokumentation - -### POST /api/document-import - -**Syfte:** Ladda upp en PDF och returnera Markdown-text - -**Request:** `multipart/form-data` med fältet `file` (PDF, max 50 MB) - -```bash -curl -X POST http://localhost:3001/api/document-import \ - -F "file=@dokument.pdf" -``` - -**Response (Success 200):** -```json -{ - "markdown": "# Dokument\n\nInnehåll från PDFen...", - "title": "Dokument", - "documentType": "pdf", - "metadata": { - "pageCount": 5, - "characterCount": 12400, - "producer": "Adobe PDF Library", - "creationDate": "D:20260101120000" - } -} -``` - -**Response (Error 400):** -```json -{ - "statusCode": 400, - "message": "Kunde inte läsa dokumentet: PDFen verkar vara en skannad bild utan textlager.", - "timestamp": "2026-04-12T10:30:00.000Z" -} -``` - -**Error-scenarier:** -- `400` — Ingen fil bifogad -- `400` — Fel filtyp (ej PDF) -- `400` — Filen överstiger 50 MB -- `400` — Lösenordsskyddad PDF -- `400` — Skannad bild-PDF utan textlager (OCR ej implementerat ännu) - ---- - -### POST /api/quick-import - -**Syfte:** Skrapa webbsida och returnera Markdown (sekundär funktion) - -**Request:** -```json -{ - "input": "https://ica.se/recept/kottfarssas-1234" -} -``` - -**Response (Success 200):** -```json -{ - "markdown": "# Köttfärssås\n\nEn klassisk...", - "source": "ica" -} -``` - -**Error-scenarier:** -- `400` — Tomt input, inte en URL -- `400` — HTML-parsing misslyckades -- `503` — Network-fel - ---- - -### POST /api/recipes/parse-markdown - -**Syfte:** Tolka Markdown-format utan databas - -**Request:** -```json -{ - "markdown": "# Titel\n\n## Ingredienser\n- 500 g mjöl\n\n## Tillvägagångssätt\nBlanda..." -} -``` - -**Response (Success 200):** -```json -{ - "name": "Titel", - "description": "", - "instructions": "Blanda...", - "ingredients": [ - { "rawName": "mjöl", "quantity": 500, "unit": "g", "note": null } - ] -} -``` --- ## Parser-arkitektur -### Dokument-parsers (`document-import/parsers/`) +### Dokument-parsers (`document-service/parsers/`) -Abstrakt bas `DocumentParser` som alla dokumenttyp-specifika parsers ärver från: - -```typescript -abstract class DocumentParser { - abstract parse(buffer: Buffer, filename: string): Promise; - - protected textToMarkdown(text: string, title: string): string { - // Slår ihop sammanhängande textrader, bevarar stycken - } -} -``` +Abstrakt bas `DocumentParser` som alla dokumenttyp-specifika parsers ärver från. ### PDF Parser (`pdf.parser.ts`) -Hanterar textbaserade PDFs via `pdf-parse`. +Hanterar textbaserade PDFs via `pdf-parse`. Skannade PDFs varnas. -**Strategi:** -1. Extrahera text med `pdf-parse` -2. Kontrollera att text hittades (annars: skannad PDF-varning) -3. Konvertera text → Markdown via `textToMarkdown()` -4. Returnera titel (från filnamn), innehåll och metadata - -**Felhantering:** -- Lösenordsskyddade PDFs → tydligt felmeddelande -- Skannade bild-PDFs → informativt felmeddelande om OCR - -### Webb-parsers (`quick-import/parsers/`) +### 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 -- **`generic.parser.ts`** — Fallback för alla webbplatser, försöker JSON-LD sedan HTML +- **`ica.parser.ts`** — ICA.se, prioriterar JSON-LD structured data, extraherar `imageUrl` +- **`generic.parser.ts`** — Fallback för alla webbplatser --- ## Deployment -### Docker Compose (Recommended) +`importer-api` byggs och startas via `recipe-app/compose.yml` — **ej via sin egen compose-fil**. -```bash -cd microservice-importer -docker compose up -d +**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 ``` -**Services:** -- `importer-api` — NestJS backend (port 3001) -- `importer-frontend` — Next.js frontend (port 3000) - -**Stoppa:** +**Deploy:** ```bash -docker compose down +# 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 compose logs -f importer-api -docker compose logs -f importer-frontend +docker logs importer-api -f ``` -### Manuell Docker build - +**Hälsokontroll:** ```bash -# Backend -docker build -f backend/Dockerfile -t importer-api:local . -docker run -p 3001:3001 importer-api:local - -# Frontend (ny terminal) -docker build -f frontend/Dockerfile -t importer-frontend:local . -docker run -p 3000:3000 importer-frontend:local +docker exec importer-api wget -qO- http://localhost:3001/api/health +# → {"status":"ok"} ``` -### Environment variables - -Konfigureras i `docker compose` eller `.env`: - -**Backend:** -``` -PORT=3001 -NODE_ENV=production -``` - -**Frontend:** -``` -NEXT_PUBLIC_API_URL_INTERNAL=http://importer-api:3001 -``` - ---- - -## Integration med andra tjänster - -Microservicen kan anropas som extern API från andra applikationer: - -```typescript -const IMPORTER_URL = process.env.MICROSERVICE_IMPORTER_URL || 'http://localhost:3001'; - -// PDF-konvertering -const formData = new FormData(); -formData.append('file', pdfFile); - -const response = await fetch(`${IMPORTER_URL}/api/document-import`, { - method: 'POST', - body: formData, -}); - -const { markdown, title, metadata } = await response.json(); -``` +**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.3 — REST API & modular architecture -- **TypeScript** 5.4.5 — Type safety -- **Node.js** 22.x — Runtime -- **pdf-parse** 1.1.x — PDF text extraction -- **tesseract.js** 5.x — OCR (förberett, ej aktivt ännu) +- **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 - **multer** — Multipart file upload handling -- **class-validator** — DTO validation (svenska felmeddelanden) - **Ingen databas** — Stateless service -### Frontend Stack -- **Next.js** 16.2 — React framework (App Router) -- **React** 19.2 — UI components -- **TypeScript** 5.4.5 -- **Inline CSS** — Minimal styling, inga framework-beroenden +### Systempaket (Alpine) +Installerade i Dockerfile runner-stage: +``` +tesseract-ocr +tesseract-ocr-data-swe +tesseract-ocr-data-eng +``` ### Error Handling - Centraliserad `GlobalExceptionFilter` (svenska meddelanden) @@ -367,13 +167,13 @@ const { markdown, title, metadata } = await response.json(); ## Framtida utbyggnader - [x] PDF-import — textbaserad extraction -- [ ] OCR för skannade bild-PDFs (Tesseract.js förberett) +- [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 -- [ ] Batch-processing (flera filer samtidigt) -- [ ] Strukturerad data-extraction (tabeller, listor) -- [ ] Stöd för fler webbplatser i webb-skraparen (mat.se, kokaihop.se, etc.) -- [ ] Caching av konverterade dokument -- [ ] Rate limiting +- [ ] Swagger/OpenAPI-dokumentation +- [ ] Rate limiting / Caching --- diff --git a/next_steps_MSImporter.md b/next_steps_MSImporter.md index d0c04b2..fc84338 100644 --- a/next_steps_MSImporter.md +++ b/next_steps_MSImporter.md @@ -1,6 +1,58 @@ # Plan för vidareutveckling av Microservice Importer -För att utveckla **Microservice Importer** vidare kan vi fokusera på att förbättra befintliga funktioner, lägga till nya funktioner, optimera arkitekturen och säkerställa skalbarhet. Nedan är en strukturerad plan för projektets fortsatta utveckling. +## Status (2026-04-30) — Integrerad med recipe-app + +`microservice-importer` är nu driftsatt som intern tjänst (`importer-api`) i `recipe-app/compose.yml`. Alla tre importflöden är delegerade: + +| Endpoint | Funktion | Status | +|---|---|---| +| `POST /api/quick-import` | URL-skrapning (ICA, generisk), PDF, OCR-bild | ✅ Driftsatt | +| `POST /api/recipes/parse-markdown` | Markdown → ingrediensstruktur (utan DB) | ✅ Driftsatt | +| `POST /api/receipt-import/parse` | Kvittobild/PDF → `ParsedReceiptItem[]` via Mistral AI | ✅ Driftsatt | +| `GET /api/health` | Hälsokontroll (används av Docker healthcheck) | ✅ Driftsatt | + +**Serverstruktur:** +``` +/opt/containers/ + microservice-importer/ ← klonas och pullas separat + recipe-app/ + compose.yml ← bygger importer-api från ../microservice-importer + deploy.sh +``` + +**Deploy:** +```bash +cd /opt/containers/microservice-importer && git pull +cd /opt/containers/recipe-app && git pull && ./deploy.sh +``` + +--- + +## Återstående / Möjliga nästa steg + +### Hög prioritet +- **Kvittoimport Fas 6b** — Granskningssteg och bulk-spara i Flutter-klienten (logiken är klar i backend) + +### Medel prioritet +- **Fler webbplats-parsers** — Lägg till specifika parsers för t.ex. Arla, Tasteline, Köket.se +- **Swagger/OpenAPI** — Automatisk API-dokumentation via `@nestjs/swagger` +- **Testtäckning** — Enhetstester för parsers och receipt-parsing-service + +### Låg prioritet / Framtida +- **Caching** — Cacha skrapade sidor för att minska belastning på externa webbplatser +- **Puppeteer** — Hantera JavaScript-renderade sidor +- **Word-dokument** — Stöd för `.docx`-import +- **Centraliserad loggning** — Prometheus/Grafana eller liknande + +--- + +## Arkitektur-noteringar + +- Tjänsten är **helt stateless** — ingen databas, ingen session +- Kommunicerar **aldrig direkt** med internet-klienter — exponeras bara på `recipe-internal`-nätverket +- `MISTRAL_API_KEY` injiceras via env (samma nyckel som recipe-api använder) +- Alpine Docker-image: systempaket `tesseract-ocr`, `tesseract-ocr-data-swe`, `tesseract-ocr-data-eng` installerade via `apk` + ---