# 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. --- ## 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 ### 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 endpoint Tolka Markdown-format utan databaskomplikationer. Användbar för API-integration utan lokal DB. --- ## Arkitektur ### Backend (NestJS 10.3, TypeScript 5.4.5) **Port:** 3001 ``` src/ ├── app.module.ts # Root module ├── main.ts # Startpunkt ├── 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 │ └── 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 │ └── 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 ├── recipes.module.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/`) 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 } } ``` ### PDF Parser (`pdf.parser.ts`) Hanterar textbaserade PDFs via `pdf-parse`. **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/`) 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 --- ## Deployment ### Docker Compose (Recommended) ```bash cd microservice-importer docker compose up -d ``` **Services:** - `importer-api` — NestJS backend (port 3001) - `importer-frontend` — Next.js frontend (port 3000) **Stoppa:** ```bash docker compose down ``` **Loggar:** ```bash docker compose logs -f importer-api docker compose logs -f importer-frontend ``` ### Manuell Docker build ```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 ``` ### 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(); ``` --- ## 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) - **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 ### 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 - [ ] OCR för skannade bild-PDFs (Tesseract.js förberett) - [ ] 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 --- ## Licens MIT --- ## Support - **Git Repo** — Gitea på `192.168.50.2:2222/nilsjohan/microservice-importer`