From a2038ffbec23094ca762130da96709d6e2710bf9 Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Tue, 14 Apr 2026 22:48:57 +0200 Subject: [PATCH] feat: Enhance recipe import functionality with support for PDF, image, and URL inputs --- README.md | 24 +++++++++------ TEKNISK_BESKRIVNING.md | 66 +++++++++++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 0519aab8..0c4c2d7e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ En fullstack-applikation för hantering av hemmavaror och recept. Håll koll på - **Skapa och redigera recept** — med ingredienser, kvantiteter, enheter och instruktioner (Markdown-stöd) - **Receptjämförelse mot inventorie** — se direkt vilka ingredienser du har hemma, vad som saknas och enhetskonflikt - **Importera recept från Markdown** — klistra in ett recept i enkelt format, låt systemet matcha ingredienser, granska och spara med ett klick +- **Importera recept från PDF, bild eller länk** — stöd för PDF-textutvinning, OCR av bilder och webbimport via snabbimport - **Intelligenta matchningar** — Levenshtein-baserad likhetsbedömning hittar rätt produkt även på osäker stavning - **Enhetskonvertering** — automatisk konvertering mellan viktenheter (g/kg), volymenheter (ml/dl) och portionsenheter (tsk/msk) @@ -51,6 +52,8 @@ docker compose up -d Frontend är tillgänglig på `http://localhost:3000` (eller via Caddy proxy) Backend API är tillgänglig på `http://localhost:8080` (eller via Caddy proxy) +> Stacken använder lokala Docker-images, hälsokontroller och startordning mellan databasen, API:t och frontend för stabilare uppstarter och Portainer-deployer. + ### Bygg bara backend eller frontend om behövligt ```bash @@ -69,10 +72,10 @@ docker compose up -d recipe-frontend ```bash # Hälsokontroll via HTTP (backend måste köra) -curl http://localhost:8080/health +curl http://localhost:8080/api/health # Databasspecifik hälsokontroll -curl http://localhost:8080/health/db +curl http://localhost:8080/api/health/db ``` --- @@ -90,12 +93,14 @@ Snabbimport: Klistra in länk eller fil Klistra in: - **ICA-receptlänk** — systemet skrapar automatiskt receptet och importerar det -- En länk omdirigeras till "Skriv in recept" med förifylld Markdown +- **Andra receptlänkar** — fallback till generisk HTML/JSON-LD-parser +- En lyckad import omdirigeras till "Skriv in recept" med förifylld Markdown -**Stödda webbplatser:** +**Stödda källor:** - ICA.se — Recept skrapas automatiskt - Andra webbplatser — Generic HTML-parser (JSON-LD först, sedan HTML-fallback) -- (PDF-import under utveckling) +- PDF-filer — textutvinning från uppladdad PDF +- Bildfiler — OCR via Tesseract (`swe+eng`) för PNG, JPG, JPEG, WEBP och BMP **Felmeddelandena vägleder dig:** - "Länken är inte från ICA.se" — Försöker Generic parser istället @@ -138,7 +143,7 @@ Innehåller: Klicka på **Lägg till nytt recept** i receptmenyn. Du får ett val mellan: 1. **Skriv in recept** — Skriv receptet i Markdown-format med ingredienser och instruktioner -2. **Importera från fil** — Ladda upp PDF, länk eller annan receptkälla (under utveckling) +2. **Importera från fil** — Ladda upp PDF, bild eller ange receptlänk för automatisk tolkning ### Skriv in recept (Markdown) @@ -184,11 +189,12 @@ Klicka "Spara recept" — receptet sparas med dina valida ingredienser Navigera till **Lägg till nytt recept → Importera från fil** I denna sektion kan du: -- Ladda upp PDF eller andra receptfiler +- Ladda upp PDF-filer med inbyggd text +- Ladda upp bilder (`png`, `jpg`, `jpeg`, `webp`, `bmp`) som tolkas med OCR - Ange URL till en receptsida eller blogg -- Systemet tolkar filen/länken och föreslår ingredienser +- Låta systemet omvandla resultatet till Markdown och öppna det i redigeringsläget -> **Notering:** Fil- och länk-import är under utveckling. För närvarande kan du använda "Skriv in recept" för att mata in receptet manuellt. +> **Notering:** Importen använder PDF-textutvinning och Tesseract OCR för att ge en första receptversion som sedan kan granskas och sparas. ### Receptformat — regler diff --git a/TEKNISK_BESKRIVNING.md b/TEKNISK_BESKRIVNING.md index 88636754..a7928e11 100644 --- a/TEKNISK_BESKRIVNING.md +++ b/TEKNISK_BESKRIVNING.md @@ -25,6 +25,13 @@ Recipe App är en fullstack-applikation för hantering av hemmavaror, recept och | Container | Docker | 24+ | | Converter | Node.js (TypeScript) | Noll externa beroenden | +### Container- och deployupplägg + +- `compose.yml` bygger lokala images för frontend och backend +- `pull_policy: never` används för appens lokala images för att undvika felaktiga registry-pulls i Portainer +- Health checks finns för databas, API och frontend +- `depends_on` med hälsovillkor används för stabilare startordning i Docker och Portainer + --- ## Frontend @@ -68,6 +75,7 @@ Recipe App är en fullstack-applikation för hantering av hemmavaror, recept och | Route | Metod | Syfte | |-------|-------|-------| +| `/api/quick-import-proxy` | POST | Proxies `POST /api/quick-import` för URL-, PDF- och bildimport | | `/api/parse-markdown-proxy` | POST | Proxies `POST /api/recipes/parse-markdown` (Markdown-tolkning för skriv-in-recept) | | `/api/inventory-history-proxy` | GET | Proxies konsumtionshistorik | | `/api/recipe-preview-proxy` | GET | Proxies receptförhandsvisning | @@ -154,19 +162,23 @@ backend/src/ **Quick-Import API:** 📌 (Även tillgänglig via [Microservice Importer](../microservice-importer/)) - **Endpoint:** `POST /api/quick-import` -- **Input:** URL (t.ex. `https://ica.se/recept/...`) eller filsökväg (PDF) +- **Input:** + - JSON-body med `input` för URL eller servermonterad filsökväg + - `multipart/form-data` med `file` för uppladdad PDF eller bild +- **Stödda format:** PDF, PNG, JPG, JPEG, WEBP, BMP samt receptlänkar - **Process:** - 1. URL-validering (HTTP fetch med User-Agent) - 2. Parser-urval baserat på `canHandle(url)` - 3. HTML-parsing och receptextraktion - 4. Markdown-konvertering med källlänk i footer + 1. Typdetektering av URL, PDF eller bild + 2. URL-import via site-specifik eller generisk parser + 3. PDF-import via `pdf-parse` + 4. Bildimport via `tesseract.js` OCR (`swe+eng`) + 5. Normalisering till Markdown-format för vidare receptgranskning - **Parser-arkitektur:** - **Base Parser** (`RecipeParser`): Abstract class med gemensam parseIngredientLine()-logik - Hanterar bråkmängder (1 1/2 dl), parentetiska noter, unit-validering - Kända enheter: g, kg, hg, mg, ml, dl, l, tl, st, tsk, msk, krm, port, efter smak, förp, klyfta, m.fl. - **ICA Parser** (`IcaRecipeParser`): Prioriterar JSON-LD structured data, fallback HTML - **Generic Parser** (`GenericRecipeParser`): Försöker alla webbplatser (JSON-LD → HTML) -- **Output:** Markdown-format recepttext (namn, beskrivning, ingredienser, instruktioner, källa) +- **Output:** Markdown-format recepttext med `source: 'ica' | 'pdf' | 'image' | 'other'` **Inventarie-API:** - CRUD för inventarieföremål (produktreferens, kvantitet, enhet, plats, märke, bäst före, mm) @@ -213,8 +225,8 @@ backend/src/ ### 🏥 Health endpoints ``` -GET /health Övergripande hälsakontroll (200/503) -GET /health/db Databasspecifik hälsa + responseTime +GET /api/health Övergripande hälsakontroll (200/503) +GET /api/health/db Databasspecifik hälsa + responseTime ``` ### 📦 Inventarie-endpoints @@ -230,8 +242,8 @@ GET /api/inventory/:id/consumption-history Konsumtionshistorik ### 🍽️ Recept-endpoints ``` -POST /api/quick-import SNITT: Snabbimport (ICA-skrapning) - Body: { input: string (URL eller filsökväg) } +POST /api/quick-import Snabbimport från URL, PDF eller bild + Body: { input: string } eller multipart-form med file POST /api/recipes/parse-markdown Tolka Markdown-recept (matchningslogik) GET /api/recipes Lista alla recept POST /api/recipes Skapa nytt recept @@ -364,7 +376,7 @@ Recipe App erbjuder tre vägar för att lägga till recept: 1. **Snabbimport** — Klistra in ICA-länk för automatisk skrapning (ny feature) 2. **Skriv in recept** (`/recipes/write`) — Markdown-baserad inmatning där användaren skriver receptet i enkelt format -3. **Importera från fil** (`/recipes/import`) — Ladda upp PDF, länk eller andra receptkällor (under utveckling) +3. **Importera från fil** (`/recipes/import`) — Ladda upp PDF, bild eller länk och få en första Markdown-version automatiskt Alla vägar möjliggör automatisk matchning av ingredienser mot databasen. @@ -383,20 +395,26 @@ Alla vägar möjliggör automatisk matchning av ingredienser mot databasen. 5. Omdirigera till `/recipes/write` med förifylld Markdown **Backend: `QuickImportService` (ny modul)** -- Ansvarig för ICA-skrapning, PDF-tolkning, URL-validering -- **Huvudmetod:** `importFromInput(input: string)` — Detekterar input-typ och delegerar -- **ICA-specifik:** - - Validerar URL (måste vara ICA.se) - - Fetchar HTML via `fetch()` - - Parsar HTML med regex för: receptnamn, ingredienser, instruktioner - - Konverterar till Markdown-format - - **Felhantering:** Specifika felmeddelanden per scenario -- **PDF-support:** Stubben för framtida integration (throwError: "PDF-import är under utveckling") +- Ansvarig för URL-import, PDF-tolkning, bild-OCR och Markdown-normalisering +- **Huvudmetoder:** + - `importFromInput(input: string)` — Detekterar URL eller serverfilsökväg + - `importFromUpload(file)` — Hanterar uppladdad PDF eller bildfil +- **URL-specifik logik:** + - Validerar URL + - Fetchar HTML via `fetch()` med User-Agent + - Väljer site-specifik parser eller generisk fallback + - Konverterar resultatet till Markdown-format +- **PDF-logik:** + - Extraherar text med `pdf-parse` + - Stoppar om ingen läsbar text hittas +- **Bildlogik:** + - OCR via `tesseract.js` + - Svensk och engelsk språkmodell (`swe+eng`) - **Error-strategi:** - - `400 Bad Request` — Tomt input, inte URL/fil - - `400 Bad Request` — Länken är inte från ICA.se - - `503 Service Unavailable` — Network-fel vid hämtning (HTTP-fel från ICA) - - `400 Bad Request` — HTML-parsing misslyckades (receptnamn/ingredienser inte hittade) + - `400 Bad Request` — Tomt input eller saknad fil + - `400 Bad Request` — Ostödd filtyp eller ingen läsbar text + - `503 Service Unavailable` — Misslyckad PDF- eller OCR-behandling + - `400 Bad Request` — HTML-parsing eller hämtning misslyckades **API-endpoint:** ```