feat: Enhance recipe import functionality with support for PDF, image, and URL inputs
This commit is contained in:
@@ -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)
|
- **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
|
- **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 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
|
- **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)
|
- **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)
|
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)
|
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
|
### Bygg bara backend eller frontend om behövligt
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -69,10 +72,10 @@ docker compose up -d recipe-frontend
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Hälsokontroll via HTTP (backend måste köra)
|
# Hälsokontroll via HTTP (backend måste köra)
|
||||||
curl http://localhost:8080/health
|
curl http://localhost:8080/api/health
|
||||||
|
|
||||||
# Databasspecifik hälsokontroll
|
# 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:
|
Klistra in:
|
||||||
- **ICA-receptlänk** — systemet skrapar automatiskt receptet och importerar det
|
- **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
|
- ICA.se — Recept skrapas automatiskt
|
||||||
- Andra webbplatser — Generic HTML-parser (JSON-LD först, sedan HTML-fallback)
|
- 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:**
|
**Felmeddelandena vägleder dig:**
|
||||||
- "Länken är inte från ICA.se" — Försöker Generic parser istället
|
- "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:
|
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
|
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)
|
### 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**
|
Navigera till **Lägg till nytt recept → Importera från fil**
|
||||||
|
|
||||||
I denna sektion kan du:
|
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
|
- 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
|
### Receptformat — regler
|
||||||
|
|
||||||
|
|||||||
+42
-24
@@ -25,6 +25,13 @@ Recipe App är en fullstack-applikation för hantering av hemmavaror, recept och
|
|||||||
| Container | Docker | 24+ |
|
| Container | Docker | 24+ |
|
||||||
| Converter | Node.js (TypeScript) | Noll externa beroenden |
|
| 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
|
## Frontend
|
||||||
@@ -68,6 +75,7 @@ Recipe App är en fullstack-applikation för hantering av hemmavaror, recept och
|
|||||||
|
|
||||||
| Route | Metod | Syfte |
|
| 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/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/inventory-history-proxy` | GET | Proxies konsumtionshistorik |
|
||||||
| `/api/recipe-preview-proxy` | GET | Proxies receptförhandsvisning |
|
| `/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/))
|
**Quick-Import API:** 📌 (Även tillgänglig via [Microservice Importer](../microservice-importer/))
|
||||||
- **Endpoint:** `POST /api/quick-import`
|
- **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:**
|
- **Process:**
|
||||||
1. URL-validering (HTTP fetch med User-Agent)
|
1. Typdetektering av URL, PDF eller bild
|
||||||
2. Parser-urval baserat på `canHandle(url)`
|
2. URL-import via site-specifik eller generisk parser
|
||||||
3. HTML-parsing och receptextraktion
|
3. PDF-import via `pdf-parse`
|
||||||
4. Markdown-konvertering med källlänk i footer
|
4. Bildimport via `tesseract.js` OCR (`swe+eng`)
|
||||||
|
5. Normalisering till Markdown-format för vidare receptgranskning
|
||||||
- **Parser-arkitektur:**
|
- **Parser-arkitektur:**
|
||||||
- **Base Parser** (`RecipeParser`): Abstract class med gemensam parseIngredientLine()-logik
|
- **Base Parser** (`RecipeParser`): Abstract class med gemensam parseIngredientLine()-logik
|
||||||
- Hanterar bråkmängder (1 1/2 dl), parentetiska noter, unit-validering
|
- 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.
|
- 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
|
- **ICA Parser** (`IcaRecipeParser`): Prioriterar JSON-LD structured data, fallback HTML
|
||||||
- **Generic Parser** (`GenericRecipeParser`): Försöker alla webbplatser (JSON-LD → 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:**
|
**Inventarie-API:**
|
||||||
- CRUD för inventarieföremål (produktreferens, kvantitet, enhet, plats, märke, bäst före, mm)
|
- 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
|
### 🏥 Health endpoints
|
||||||
```
|
```
|
||||||
GET /health Övergripande hälsakontroll (200/503)
|
GET /api/health Övergripande hälsakontroll (200/503)
|
||||||
GET /health/db Databasspecifik hälsa + responseTime
|
GET /api/health/db Databasspecifik hälsa + responseTime
|
||||||
```
|
```
|
||||||
|
|
||||||
### 📦 Inventarie-endpoints
|
### 📦 Inventarie-endpoints
|
||||||
@@ -230,8 +242,8 @@ GET /api/inventory/:id/consumption-history Konsumtionshistorik
|
|||||||
|
|
||||||
### 🍽️ Recept-endpoints
|
### 🍽️ Recept-endpoints
|
||||||
```
|
```
|
||||||
POST /api/quick-import SNITT: Snabbimport (ICA-skrapning)
|
POST /api/quick-import Snabbimport från URL, PDF eller bild
|
||||||
Body: { input: string (URL eller filsökväg) }
|
Body: { input: string } eller multipart-form med file
|
||||||
POST /api/recipes/parse-markdown Tolka Markdown-recept (matchningslogik)
|
POST /api/recipes/parse-markdown Tolka Markdown-recept (matchningslogik)
|
||||||
GET /api/recipes Lista alla recept
|
GET /api/recipes Lista alla recept
|
||||||
POST /api/recipes Skapa nytt 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)
|
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
|
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.
|
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
|
5. Omdirigera till `/recipes/write` med förifylld Markdown
|
||||||
|
|
||||||
**Backend: `QuickImportService` (ny modul)**
|
**Backend: `QuickImportService` (ny modul)**
|
||||||
- Ansvarig för ICA-skrapning, PDF-tolkning, URL-validering
|
- Ansvarig för URL-import, PDF-tolkning, bild-OCR och Markdown-normalisering
|
||||||
- **Huvudmetod:** `importFromInput(input: string)` — Detekterar input-typ och delegerar
|
- **Huvudmetoder:**
|
||||||
- **ICA-specifik:**
|
- `importFromInput(input: string)` — Detekterar URL eller serverfilsökväg
|
||||||
- Validerar URL (måste vara ICA.se)
|
- `importFromUpload(file)` — Hanterar uppladdad PDF eller bildfil
|
||||||
- Fetchar HTML via `fetch()`
|
- **URL-specifik logik:**
|
||||||
- Parsar HTML med regex för: receptnamn, ingredienser, instruktioner
|
- Validerar URL
|
||||||
- Konverterar till Markdown-format
|
- Fetchar HTML via `fetch()` med User-Agent
|
||||||
- **Felhantering:** Specifika felmeddelanden per scenario
|
- Väljer site-specifik parser eller generisk fallback
|
||||||
- **PDF-support:** Stubben för framtida integration (throwError: "PDF-import är under utveckling")
|
- 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:**
|
- **Error-strategi:**
|
||||||
- `400 Bad Request` — Tomt input, inte URL/fil
|
- `400 Bad Request` — Tomt input eller saknad fil
|
||||||
- `400 Bad Request` — Länken är inte från ICA.se
|
- `400 Bad Request` — Ostödd filtyp eller ingen läsbar text
|
||||||
- `503 Service Unavailable` — Network-fel vid hämtning (HTTP-fel från ICA)
|
- `503 Service Unavailable` — Misslyckad PDF- eller OCR-behandling
|
||||||
- `400 Bad Request` — HTML-parsing misslyckades (receptnamn/ingredienser inte hittade)
|
- `400 Bad Request` — HTML-parsing eller hämtning misslyckades
|
||||||
|
|
||||||
**API-endpoint:**
|
**API-endpoint:**
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user