diff --git a/README.md b/README.md index 4019dbd..877e984 100644 --- a/README.md +++ b/README.md @@ -1,218 +1,43 @@ # Microservice Importer -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. +Intern import-tjänst (`importer-api`) för [recipe-app](../recipe-app). Den hanterar URL-skrapning, OCR, PDF-parsning och AI-kvittoparsning utan databas. Tjänsten körs som Docker-tjänst på det interna `recipe-internal`-nätverket och exponeras inte externt. -## Dokumentstatus (2026-05-03) +## Dokumentstatus (2026-05-10) -### Målgrupp -Detta dokument är för systemadministratörer och utvecklare som driftar eller vidareutvecklar importtjänsten. +Det här dokumentet är skrivet för systemadministratörer och utvecklare som driftar eller vidareutvecklar importtjänsten. För arkitektur, drift och tekniska detaljer, se [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md). För roadmap och prioriteringar, se [next_steps_MSImporter.md](next_steps_MSImporter.md). -### 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. -- Kvittokategorisering: nya regler för pasta, grädde, ägg, juice, godis, och potatis samt justerad AI-guardrail. -- Testinfrastruktur: parametriserade enhetstester för kvittoimport (18 testfall) och CI/CD-pipeline med automatiserad testkörning på push.- **PDF-fix:** `pdf-parse` importeras nu med `require()` (CJS-kompatibilitet). `pdfjs-dist/legacy/build/pdf.js` används som fallback för att undvika `DOMMatrix is not defined` i Node.js-miljö. -- **Retry-förbättring:** Mistral 429/503 → väntar `3000 * attempt` ms innan nytt försök. -- **Reproducerbart bygge:** `package-lock.json` spåras i git; Dockerfile kör `npm ci`. -- **AI-skippning för icke-produktrader:** `looksLikeReceiptProductLine()` filtrerar PDF-rader utan siffra (header/footer/butiksinformation) — dessa skickas inte till Mistral. -## 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 +## Vad tjänsten gör ---- +- Tar emot URL:er, filer och markdown för importflöden +- Skrapar receptsidor och extraherar `imageUrl` när det finns +- Kör OCR för bilder och skannade dokument +- Tolkar kvitton via Mistral AI +- Returnerar strukturerad data till recipe-app utan att lagra någon session eller databaspost -## Features +## Flöden -### 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`). +- `POST /api/quick-import` för URL-skrapning, bild-OCR och PDF-import +- `POST /api/recipes/parse-markdown` för markdown till strukturerat recept +- `POST /api/receipt-import/parse` för kvittobild eller PDF till `ParsedReceiptItem[]` +- `GET /api/health` för Docker healthcheck -### Parse-Markdown (`POST /api/recipes/parse-markdown`) -Tolkar Markdown-recept till strukturerat JSON utan databas. +## Viktigt -### Kvittoparsning (`POST /api/receipt-import/parse`) -- Bild (JPEG/PNG/WebP/HEIC/HEIF) eller PDF -- **Bild:** `mistral-small-2603` (vision-kapabel) med retry-logik (3 försök, `3000 * attempt` ms fördröjning vid 429/503) -- **PDF:** `pdf-parse` → fallback `pdfjs-dist/legacy` → regelbaserad parsning → AI enbart för rader med namntext + siffra -- Returnerar `ParsedReceiptItem[]` med fälten `rawName`, `quantity`, `unit`, `price`, `brand`, `origin` -- Inbyggda regler i AI-prompten styr tolkning av `quantity`/`unit` (se nedan) +- Inga absoluta Windows-sökvägar ska användas i kod eller scripts +- Tjänsten är stateless +- Ingen databas är konfigurerad i tjänsten +- Host-port 3001 används av `wetty` på servern och får därför inte exponeras av importtjänsten -### Health (`GET /api/health`) -Används av Docker-healthcheck i `recipe-app/compose.yml`. Returnerar `{ status: "ok" }`. +## Kort faktadel ---- - -## 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 3X120G` → `quantity=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.yml` — **ej 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:** -```bash -# 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 logs importer-api -f -``` - -**Hälsokontroll:** -```bash -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 - -- [x] PDF-import — textbaserad extraction -- [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 -- [ ] Swagger/OpenAPI-dokumentation -- [ ] Rate limiting / Caching - ---- - -## Licens - -MIT - ---- +- Runtime: Node.js 22-alpine +- Ramverk: NestJS 10 + TypeScript 5 +- OCR: `tesseract.js` +- PDF: `pdf-parse` med `pdfjs-dist/legacy` fallback +- AI: `@mistralai/mistralai` +- Upload: `multer` +- Alpine-paket: `tesseract-ocr`, `tesseract-ocr-data-swe`, `tesseract-ocr-data-eng` ## Support -- **Git Repo** — Gitea på `192.168.50.2:2222/nilsjohan/microservice-importer` +- Git repo: Gitea på `192.168.50.2:2222/nilsjohan/microservice-importer` diff --git a/TEKNISK_BESKRIVNING.md b/TEKNISK_BESKRIVNING.md new file mode 100644 index 0000000..a9c150a --- /dev/null +++ b/TEKNISK_BESKRIVNING.md @@ -0,0 +1,167 @@ +# Teknisk beskrivning av Microservice Importer + +## Dokumentstatus (2026-05-10) + +Detta dokument riktar sig till utvecklare och driftansvariga för microservice-importer. Det beskriver arkitektur, drift och tekniska beslut för den interna importtjänsten. + +## Roll och ansvar + +`importer-api` är en stateless intern tjänst för [recipe-app](../recipe-app). Den hanterar URL-skrapning, OCR, PDF-parsning, markdown-parsning och AI-kvittoparsning utan databas eller användarsessioner. + +## Arkitektur + +### Körmiljö + +- NestJS 10 +- TypeScript 5 +- Node.js 22-alpine +- Port `3001` internt +- Exponeras bara på `recipe-internal`-nätverket + +### Moduler + +```text +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/ +│ ├── 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/ +│ ├── receipt-parsing.module.ts +│ ├── receipt-parsing.controller.ts # POST /api/receipt-import/parse +│ └── receipt-parsing.service.ts +├── document-service/ +│ ├── document-service.module.ts +│ ├── controllers/document-import.controller.ts +│ ├── services/document-import.service.ts +│ └── parsers/ +│ ├── document.parser.ts +│ └── pdf.parser.ts +└── recipes/ + ├── recipes.module.ts + ├── recipes.controller.ts # POST /api/recipes/parse-markdown + ├── recipes.service.ts + └── dto/parse-markdown.dto.ts +``` + +## Endpoints + +| Endpoint | Funktion | +|---|---| +| `POST /api/quick-import` | URL-skrapning, bild-OCR och PDF-import | +| `POST /api/recipes/parse-markdown` | Markdown till strukturerat recept utan databas | +| `POST /api/receipt-import/parse` | Kvittobild/PDF till `ParsedReceiptItem[]` via Mistral AI | +| `GET /api/health` | Hälsokontroll för Docker healthcheck | + +## Kvittoparsning + +### Modell och pipeline + +- Vision-input använder `mistral-small-2603` +- PDF-flödet kör `pdf-parse` eller `pdfjs-dist/legacy/build/pdf.js` som fallback +- Regelbaserad parsning körs före AI när det är möjligt +- `looksLikeReceiptProductLine()` filtrerar bort rader utan siffra så att AI bara används för sannolika produktrader + +### Mängd- och enhetsregler + +Följande regler är inbyggda i kvitto-prompten: + +| Typ | Regel | Exempel | +|---|---|---| +| Lösvikt | `quantity` = faktisk vikt, `unit` = `kg`/`g` | `BLANDFÄRS 20%` 0.997 kg | +| Förpackad vara med storlek i namn | `quantity` = antal förpackningar, `unit` = `förp` | `MJÖLK 1,5L` × 3 | +| Multipack | `quantity=1`, `unit=förp` | `BACON 3X120G` | +| Förpackat innehåll | `quantity` = antal förpackningar, `unit` = `förp` | `BRIOCHE SESAM` × 2 | +| Lösa styckvaror | `quantity` = antal, `unit` = `st` | `BANAN` × 1 | + +Tillåtna enheter: `st`, `kg`, `g`, `l`, `dl`, `cl`, `ml`, `förp`, `pak`, `burk`, `flaska`. + +### Retry och stabilitet + +- Mistral 429/503 backas av med `3000 * attempt` ms +- PDF-flödet använder fallback för CJS- och Node Alpine-kompatibilitet +- `GlobalExceptionFilter` ger konsekventa felobjekt + +## Deployment + +`importer-api` byggs och startas via [recipe-app/compose.yml](../recipe-app/compose.yml) och inte via egen compose-fil. + +### Serverlayout + +```text +/opt/containers/ + microservice-importer/ + recipe-app/ + compose.yml + deploy.sh +``` + +### Driftsekvens + +```bash +cd /opt/containers/microservice-importer && git pull +cd /opt/containers/recipe-app && git pull && ./deploy.sh +``` + +### Hälsokontroll + +```bash +docker exec importer-api wget -qO- http://localhost:3001/api/health +``` + +## Tekniska detaljer + +### Byggberoenden + +- `pdf-parse` +- `tesseract.js` +- `@mistralai/mistralai` +- `multer` + +### Alpine-paket + +- `tesseract-ocr` +- `tesseract-ocr-data-swe` +- `tesseract-ocr-data-eng` + +### Viktiga tekniska beslut + +- Tjänsten är stateless och saknar databaskonfiguration +- Importer exponeras aldrig externt, bara internt via Docker-nätverket +- Host-port 3001 är upptagen av `wetty` och får därför inte användas av tjänsten + +## Parser-arkitektur + +### Dokument-parsers + +Abstrakt bas `DocumentParser` används för dokumenttypsspecifik parsing. + +### PDF Parser + +`pdf.parser.ts` hanterar textbaserade PDFs. Skannade PDFs varnas och kan falla tillbaka på OCR-vägen. + +### Webb-parsers + +- `ica.parser.ts` prioriterar JSON-LD och extraherar `imageUrl` +- `generic.parser.ts` är fallback för webbplatser utan specialparser + +## Framtida utbyggnader + +- Fler webbplats-parsers som Arla, Tasteline och Köket.se +- Word/import av `.docx` +- Swagger/OpenAPI-dokumentation +- Caching av skrapade sidor om belastningen mot externa webbplatser blir ett problem + +## Referenser + +- [README.md](README.md) +- [NEXT_STEPS.md](next_steps_MSImporter.md) diff --git a/next_steps_MSImporter.md b/next_steps_MSImporter.md index eb8b569..c9c3bbd 100644 --- a/next_steps_MSImporter.md +++ b/next_steps_MSImporter.md @@ -1,21 +1,21 @@ # Plan för vidareutveckling av Microservice Importer -## Dokumentstatus (2026-05-03) +## Dokumentstatus (2026-05-10) -Detta dokument riktar sig till utvecklare och driftansvariga för microservice-importer. +Detta dokument riktar sig till utvecklare och driftansvariga för microservice-importer. Teknisk referens finns i [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md). ### Senast avklarat i angränsande flöden -- Regelbaserad kvittotolkning har stärkts för multipack/enheter och svårare radformat. -- Bröd-/rostbrödklassning har utökade guardrails för att minska felaktig kategorisering. +- Regelbaserad kvittotolkning har stärkts för multipack, enheter och svårare radformat. +- Bröd- och rostbrödklassning har utökade guardrails för att minska felaktig kategorisering. - Klientens granskningsflöde och sessionpersistens i Flutter är implementerat, vilket minskar avbrott mellan parse och spara. -- Kvittokategorisering: nya regler för pasta, grädde, ägg, juice, godis, och potatis samt justerad AI-guardrail. -- Testinfrastruktur: parametriserade enhetstester för kvittoimport (18 testfall) och CI/CD-pipeline med automatiserad testkörning på push. -- **PDF-parsning stabiliserad:** `pdf-parse` använder nu `require()` för CJS-kompatibilitet; `pdfjs-dist/legacy/build/pdf.js` används som fallback för att undvika `DOMMatrix`-fel i Node.js-miljö. -- **Retry-logik förbättrad:** Mistral-anrop vid 429/503 väntar nu `3000 * attempt` ms (3s, 6s, 9s) i stället för fast 1s. -- **Reproducerbart bygge:** `package-lock.json` är nu spårat i repot; Dockerfile använder `npm ci`. -- **AI-optimering implementerad:** `looksLikeReceiptProductLine()` filtrerar bort PDF-rader utan siffra (header/footer/butiksinformation) innan Mistral-anrop. Minskar drastiskt antal onödiga AI-anrop vid kvittoimport. +- Kvittokategorisering: nya regler för pasta, grädde, ägg, juice, godis och potatis samt justerad AI-guardrail. +- Testinfrastruktur: parametriserade enhetstester för kvittoimport och CI/CD-pipeline med automatiserad testkörning på push. +- PDF-parsning stabiliserad med `require()` och `pdfjs-dist/legacy/build/pdf.js` som fallback. +- Retry-logik förbättrad för 429/503. +- Reproducerbart bygge via `package-lock.json` och `npm ci`. +- `looksLikeReceiptProductLine()` filtrerar bort PDF-rader utan siffra innan Mistral-anrop. -## Status (2026-05-03) — Driftsatt och integrerad med recipe-app +## Status (2026-05-10) — Driftsatt och integrerad med recipe-app `microservice-importer` körs som intern tjänst (`importer-api`) i `recipe-app/compose.yml`. Alla importflöden är delegerade och driftsatta. @@ -51,7 +51,7 @@ cd /opt/containers/recipe-app && git pull && ./deploy.sh ### Medel prioritet - **Fler webbplats-parsers** — Specifika parsers för t.ex. Tasteline, Köket.se, Arla - **Swagger/OpenAPI** — Automatisk API-dokumentation via `@nestjs/swagger` -- **Testtäckning** — Utökad enhetstesttäckning för parsers och `receipt-parsing.service.ts` (18 testfall för kvittoimport) +- **Testtäckning** — Utökad enhetstesttäckning för parsers och `receipt-parsing.service.ts` ### Låg prioritet / Framtida - **Caching** — Cacha skrapade sidor för att minska belastning på externa webbplatser @@ -62,77 +62,52 @@ cd /opt/containers/recipe-app && git pull && ./deploy.sh ## AI-optimering: Mistral-modell och pipeline -**Nuläge:** `mistral-small-2603` används för bildinput (vision). För PDF-flödet extraheras text först via `pdf-parse`/`pdfjs-dist`; sedan regelbaserad parsning; sedan AI enbart för återstående rader. +Nuläget är att `mistral-small-2603` används för bildinput (vision). För PDF-flödet extraheras text först via `pdf-parse` eller `pdfjs-dist`; därefter körs regelbaserad parsning och AI bara för kvarvarande rader. ### Implementerad optimering: AI sist i pipeline (PDF) Kvittopipelinen för PDF ser nu ut: -``` +```text PDF → pdf-parse / pdfjs-dist → preprocessPdfLines → isIgnoredReceiptLine → ruleBasedParseLine → looksLikeReceiptProductLine → AI ``` -**`looksLikeReceiptProductLine(line)`** filtrerar bort rader som saknar siffra (butiksnamn, datum, välkomsttext m.m.) innan Mistral-anropet. Enbart rader med namnliknande text OCH minst ett tal skickas till AI. - -**Fördelar:** -- Regelbaserad parsning hanterar standardfall gratis och snabbt (t.ex. "MJÖLK 1,5L", "BLANDFÄRS 997G") -- AI anropas bara för rader som regelverket inte kan tolka entydigt -- Möjlighet att använda en mindre/billigare modell för enklare tolkningsuppgifter +`looksLikeReceiptProductLine(line)` filtrerar bort rader som saknar siffra innan Mistral-anropet. Enbart rader med namnliknande text och minst ett tal skickas till AI. ### Modellval för olika deluppgifter | Uppgift | Rekommenderad modell | Motivering | |---|---|---| -| Kvittoparsning (hela bilden, nuläge) | `mistral-small-2603` | Vision-förmåga krävs för bild-input | -| Tolka OCR-text (textbaserad input) | `mistral-small-latest` eller mindre | Enklare uppgift när text redan extraherats | -| Kategorisering av enskild produktrad | `open-mistral-nemo` (7B) | Klassificering, ej vision — kan vara mycket liten | +| Kvittoparsning (hela bilden) | `mistral-small-2603` | Vision-förmåga krävs för bild-input | +| Tolka OCR-text | `mistral-small-latest` eller mindre | Enklare uppgift när text redan extraherats | +| Kategorisering av enskild produktrad | `open-mistral-nemo` (7B) | Klassificering utan vision | -**OBS:** För bild-input (JPEG/PNG/HEIC/WebP) krävs alltid en vision-kapabel modell. Optimering med mindre modell är bara möjlig när Tesseract/pdf-parse redan har extraherat text. +För bild-input krävs alltid en vision-kapabel modell. --- ## Framtida förbättringar -### Schemalagd Uppdatering av Kategorier -- **Mål:** Implementera en schemalagd uppdatering av kategorierna en gång i veckan för att säkerställa att cachen alltid är uppdaterad. -- **Metod:** Använda `cron` för att schemalägga ett anrop till `POST /receipt-import/refresh-categories` en gång i veckan. +- Schemalagd uppdatering av kategorier om det visar sig nödvändigt igen. ---- +## Nuvarande implementering -## Nuvarande Implementering - -### Manuell Uppdatering av Kategorier -- **Mål:** Låta användaren manuellt uppdatera kategorierna via Flutter-UI. -- **Implementering:** - - En knapp i Flutter-UI:n som låter användaren trigga uppdateringen. - - Anropa `POST /receipt-import/refresh-categories` från Flutter-UI:n när användaren klickar på knappen. - -```dart -// Exempel på hur du kan anropa endpointen från Flutter -Future refreshCategories() async { - final response = await http.post( - Uri.parse('http://YOUR_API_URL/receipt-import/refresh-categories'), - headers: {'Authorization': 'Bearer YOUR_JWT_TOKEN'}, - ); - - if (response.statusCode == 200) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Kategorier har uppdaterats.')), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Misslyckades med att uppdatera kategorier.')), - ); - } -} -``` +- Ingen separat manuell kategoriuppdatering via Flutter-UI finns längre. +- Kvittoparsningen använder i stället nuvarande regler, retry-logik och filtrering i backend. --- ## 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` -- Host-port 3001 är upptagen av `wetty` på servern — `importer-api` exponeras aldrig till host +- Tjänsten är helt stateless och saknar databas +- Den exponeras bara på `recipe-internal`-nätverket +- `MISTRAL_API_KEY` injiceras via env +- Alpine Docker-image använder `tesseract-ocr`, `tesseract-ocr-data-swe`, `tesseract-ocr-data-eng` +- Host-port 3001 är upptagen av `wetty`, så `importer-api` exponeras aldrig till host + +## Referenser + +- [README.md](README.md) +- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) + +## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.