ca8987d9e4
Test Suite / test (24.15.0) (push) Has been cancelled
- Introduced user guide for Flutter frontend in README.md, detailing user flows and recent improvements. - Created next steps roadmap for Flutter migration in next_steps_flutter.md, outlining current tasks and priorities. - Developed technical description for Flutter frontend in teknisk_beskrivning_flutter.md, covering architecture and security status. - Removed outdated migration documentation for Prisma P3009 and added recovery steps for failed migrations in migrering-MSI.md. - Established a release checklist for product launches in produktlansering.md, ensuring security and stability measures are met. - Formulated a systematic backend review and optimization plan in review_backend.md, focusing on reducing complexity and improving performance.
389 lines
18 KiB
Markdown
389 lines
18 KiB
Markdown
|
||
# Nyheter och förbättringar (2026-05-10)
|
||
|
||
- **Admin-inventarie:** Full CRUD, merge, filter, sortering, preview och säkerhet för admin i inventarietabellen. Endast admin kan se och hantera alla användares inventarieposter via nya endpoints och adminpanel i Flutter.
|
||
- **User-scope och IDOR-skydd:** Inventory och produkter är nu strikt user-scopade. Alla operationer kräver och filtrerar på userId. Tester verifierar att åtkomst nekas vid försök till IDOR.
|
||
- **Säkerhetshärdning:** DTO-validering, guard-ordning, logging, throttling, merge abuse-skydd, och rollbaserad access är implementerat och testat.
|
||
- **Optimeringar:** DRY i service-lager, striktare query parsing, preview-cache, API-cleanup, och kodduplication eliminerad.
|
||
- **Testtäckning:** Utökade enhets-, integrations- och säkerhetstester för alla kritiska flöden.
|
||
|
||
# Session 2026-05-06: User-scoped AI-fallback, admin-toggles och premium-funktioner
|
||
|
||
Under denna session har Recipe App fått:
|
||
- **User-scoped AI-fallback:** AI-förslag för ingrediens- och kategorimatchning aktiveras nu per användare (premium). Endast användare med premium-tillgång får AI-hints vid import.
|
||
- **Admin-toggles:** Admin kan slå på/av AI-funktioner per användare via backend och UI.
|
||
- **Premium-scope:** Flutter och backend respekterar premium-flagga och AI-tillgång i alla flöden.
|
||
- **Rematch och manuell produkt:** Flutter har stöd för ommatchning och manuell produkt vid import.
|
||
- **Arkitektur:** Fallback-first AI, strikt separation av analys/import/matchning, user-scoped produktdata.
|
||
|
||
Se [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk genomgång och [NEXT_STEPS.md](NEXT_STEPS.md) för roadmap.
|
||
|
||
# Recipe App
|
||
|
||
En fullstack-applikation för hantering av hemmavaror och recept. Håll koll på vad du har hemma, spara recept och se direkt om du har allt du behöver för att laga en rätt.
|
||
|
||
> För teknisk detaljinformation, se [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md).
|
||
> För förslag på nästa steg i projektet, se [NEXT_STEPS.md](NEXT_STEPS.md).
|
||
> För planerade AI-funktioner, se [_archive/microservice-ai/AI-FUNKTIONER.md](_archive/microservice-ai/AI-FUNKTIONER.md).
|
||
|
||
## Dokumentkarta
|
||
|
||
- [README.md](README.md): användaröversikt och funktioner.
|
||
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md): teknisk referens för drift och utveckling.
|
||
- [NEXT_STEPS.md](NEXT_STEPS.md): gemensam roadmap och prioriteringar.
|
||
- [produktlansering.md](_archive/docs/produktlansering.md): releasechecklista i arkiv.
|
||
- Migrerings- och driftinstruktioner finns nu i [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md). Tidigare migreringsdokument finns i [_archive/docs/migrering-MSI.md](_archive/docs/migrering-MSI.md).
|
||
- [flutter/README.md](_archive/docs/flutter/README.md): Flutter ur användarperspektiv i arkiv.
|
||
- [flutter/teknisk_beskrivning_flutter.md](_archive/docs/flutter/teknisk_beskrivning_flutter.md): Flutter teknisk referens i arkiv.
|
||
- [flutter/next_steps_flutter.md](_archive/docs/flutter/next_steps_flutter.md): Flutter roadmap i arkiv.
|
||
|
||
## Dokumentstatus (2026-05-03)
|
||
|
||
### Målgrupp
|
||
Detta dokument är skrivet för användare och produktägare. Fokus är vad som fungerar i appen och vad som förbättrats i det dagliga flödet.
|
||
|
||
### Nytt sedan senaste sessionerna
|
||
- **Alias-strategi för kvittoimport:**
|
||
- Backend och Flutter har nu stöd för användarspecifika alias (user-alias) och globala alias (admin).
|
||
- Varje rad i kvittoimporten visar nu en badge som anger hur matchningen skedde: **Alias**, **Ordmatch** eller **AI-kategori**.
|
||
- Alla användare kan spara egna alias direkt vid import ("Spara som alias"), admins kan spara globala alias.
|
||
- Ny profilsida: användare kan se och radera sina alias.
|
||
- Admin-panelen för alias visar nu produkt-ID och tydligare radstruktur.
|
||
- Kvittoimporten har blivit mer träffsäker för förpackningar och mängder (t.ex. rader med multipack och "2st").
|
||
- Kategorisering av brödrelaterade produkter har förbättrats så att färre varor hamnar fel.
|
||
- Ny underkategori i kategoriträdet: `Bröd & Kakor > Bröd > Fastfoodbröd > Korvbröd`.
|
||
- Importerat kvitto i Flutter ligger kvar i klientens session vid navigering/refresh, så användaren kan fortsätta utan att börja om.
|
||
- Produkter är user-scopade (personliga per användare), vilket minskar felmatchningar mellan olika användare.
|
||
- Ingen ny serverlagring infördes för kvitto-sessionen; persistensen sker i klienten.
|
||
|
||
---
|
||
|
||
### Funktioner
|
||
|
||
### Kvittoimport och alias
|
||
- **Alias-badge:** Varje rad i kvittoimporten visar nu en badge som anger om matchningen skedde via Alias, Ordmatch eller AI-kategori.
|
||
- **Spara som alias:** Alla användare kan spara egna alias direkt vid import. Admins kan spara globala alias.
|
||
- **Alias-hantering:** Användare kan se och radera sina alias på profilsidan. Admins har en förbättrad panel med produkt-ID och tydligare radstruktur.
|
||
- **Prioritering:** Systemet prioriterar användarens egna alias före globala alias och ordmatchning.
|
||
|
||
### Inventarie (Hemmavaror)
|
||
- **Lägg till, redigera och ta bort varor** — hantera produkt, kvantitet, enhet, plats, märke och bäst före-datum
|
||
- **Filtrera och sortera** — efter plats (kyl, frys, skafferi), bäst före-datum, och namn (A–Ö)
|
||
- **Konsumera varor** — registrera förbrukad mängd med eventuell kommentar
|
||
- **Konsumtionshistorik** — spåra vad som använts när och i vilken mängd
|
||
- **Utförlig information** — stöd för varumärke, lagringsnot, tillkomsttid och mer
|
||
|
||
### Baslager
|
||
- **Ständigt lager** — markera produkter du alltid räknar med att ha hemma
|
||
- **Grupperat per kategori** — produkterna i baslagret visas grupperade under kategorirubrik
|
||
- **Lägg till och ta bort** — välj från produktlistan via sökbar dropdown, ta bort med ett klick
|
||
|
||
----
|
||
|
||
### 📌 Så använder du Inventarie och Baslager
|
||
|
||
Dessa två funktioner fyller olika syften och kompletterar varandra:
|
||
|
||
#### Inventarie — ”vad du faktiskt har hemma”
|
||
Inventariet är en **aktiv förrådsbok** över varor du just nu har hemma. Här registrerar du:
|
||
- Exakt mängd (t.ex. 500 g pasta, 1,5 liter mjolk)
|
||
- Var varan förvaras (kyl, frys, skafferi)
|
||
- Bäst före-datum
|
||
- Om förpackningen är öppnad
|
||
|
||
#### Praktiskt flöde
|
||
| Du vill se vad du behöver köpa | **Matplanen** — inköpslistan jämför mot båda |
|
||
| Du tog slut på mjolk | Ta bort från inventariet (baslagret påverkas inte) |
|
||
- **Portionsjustering** — ange antal portioner vid skapandet; matplanen räknar automatiskt om ingrediensmängder om du lagar fler eller färre portioner
|
||
- **Receptjämförelse mot inventorie** — se direkt vilka ingredienser du har hemma, vad som saknas och om enheter är inkompatibla
|
||
- **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 vid osäker stavning
|
||
- **Enhetskonvertering** — automatisk konvertering mellan viktenheter (g/kg), volymenheter (ml/dl) och portionsenheter (tsk/msk)
|
||
|
||
- **Inköpslista** — genereras automatiskt utifrån veckans planerade recept; ingrediensmängder skalas proportionellt om portioner justerats
|
||
- **Inventariejämförelse** — jämför inköpslistans ingredienser mot vad du faktiskt har hemma (aggregerat per vecka)
|
||
> Obs: Destruktiva åtgärder (merge, ta bort, återställ, bulk-uppdatera, återställ all data) kräver admin-roll.
|
||
**Admin: Produkter (fliken Databas i /profil)**
|
||
|
||
Produktadmin är nu uppdelad i tre undertabbar:
|
||
- **🗑️ Papperskorg** — visa mjukraderade produkter, återställ eller radera permanent
|
||
- Redigera produkter — uppdatera namn, canonical name, kategori (hierarkisk dropdown) och varumärke inline
|
||
- Bulk-kategorisering — filtrera fram okategoriserade produkter, markera flera och sätt kategori på alla markerade
|
||
- AI-kategorisering per produkt och bulk — "✨ Fråga AI" för kategori
|
||
- Hitta dubbletter och slå ihop produkter (merge)
|
||
- Förhandsvisning av merge
|
||
- Ta bort (mjukradera) och återställ produkter
|
||
- **Produktförslags-kö** — produkter med status `pending` samlas på sidan `/admin/products/pending` (länk "⏳ Förslag" i navigeringen)
|
||
- **Godkänn eller avvisa** — admin kan godkänna (status → `active`) eller avvisa (status → `rejected`) varje förslag med ett klick
|
||
|
||
- **Min profil** — redigera förnamn, efternamn och e-postadress
|
||
- **Databas** — hantera inventarie och baslager (se nedan)
|
||
|
||
**Enbart admin:**
|
||
- **Användare** — fullständig användarhantering:
|
||
- Skapa ny användare (användarnamn, e-post, lösenord, roll)
|
||
- Ändra roll via dropdown direkt i tabellen
|
||
- Ändra **plan** (Free / Paid ✨) via dropdown — styr tillgång till premium-AI-funktioner
|
||
- Ändra e-postadress inline
|
||
- Återställ lösenord — genererar ett tillfälligt lösenord och visar ett kopierings-redo meddelande
|
||
- Ta bort användare (skyddad: kan inte ta bort sig själv)
|
||
- **Databas** — produktdatabasen: redigera, merga, bulk-kategorisera och återställa produkter
|
||
|
||
### Autentisering och roller
|
||
- **Rollbaserad åtkomstkontroll** — systemet har två roller: `user` (standard) och `admin`
|
||
- **Premium-plan** — `isPremium`-flagga på användaren styr tillgång till AI-funktioner (kvittoimport AI-hints m.fl.); administreras via Plan-kolumnen i användarhanteringen
|
||
- **Automatisk bootstrap** — fyra användare skapas eller uppdateras automatiskt när backend startar, baserat på miljövariabler:
|
||
- `Nadmin` (admin), `Padmin` (admin), `user1` (user), `user2` (user)
|
||
- **Skyddade admin-endpoints** — destruktiva produkt-endpoints och all användarhantering kräver `admin`-roll; försök utan rätt roll ger 403 Förbjuden
|
||
- **Navigering** — admin-länkarna "⚙️ Admin", "⏳ Förslag" och "👥 Användare" visas enbart för inloggade administratörer
|
||
|
||
---
|
||
|
||
## Kom igång
|
||
|
||
### Förutsättningar
|
||
|
||
- Docker och Docker Compose
|
||
- En extern `proxy`-nätverk i Docker för Caddy (rekommenderat) eller localhost-konfiguration
|
||
|
||
### Starta applikationen
|
||
|
||
```bash
|
||
# Bygg alla images (första gånger)
|
||
docker compose build
|
||
|
||
# Starta alla tjänster i bakgrunden
|
||
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.
|
||
|
||
### Drift-snabbguide
|
||
|
||
Använd dessa kommandon konsekvent beroende på vilken del av systemet som ska vara uppe.
|
||
|
||
**Huvudappen (recept.gynther.se):**
|
||
```bash
|
||
docker compose build recipe-frontend recipe-api
|
||
docker compose up -d recipe-db recipe-api recipe-frontend
|
||
```
|
||
|
||
**Endast backend:**
|
||
```bash
|
||
docker compose build recipe-api
|
||
docker compose up -d recipe-db recipe-api
|
||
```
|
||
|
||
**Flutter-spåret (test.gynther.se):**
|
||
```bash
|
||
docker compose -f compose.yml -f compose.flutter.yml build recipe-flutter
|
||
docker compose -f compose.yml -f compose.flutter.yml up -d --no-deps recipe-flutter
|
||
```
|
||
|
||
**Viktigt:** `docker compose build ...` bygger bara image. Tjänsten startar först efter `docker compose up -d ...`.
|
||
|
||
**Om orphan-varningen:**
|
||
- Varning om orphan-containers är väntad när huvudappen körs med bara `compose.yml` men Flutter tidigare startats med `compose.flutter.yml`.
|
||
- Det är normalt ofarligt.
|
||
- Kör inte `docker compose down --remove-orphans` om du inte avser att även stoppa Flutter-spåret.
|
||
|
||
### Bygg bara backend eller frontend om behövligt
|
||
|
||
```bash
|
||
# Bygg enbart backend (t.ex. efter kodändringar)
|
||
docker compose build recipe-api
|
||
|
||
# Starta bara backend (övriga tjänster fortsätter)
|
||
docker compose up -d recipe-api
|
||
|
||
# Liknande för frontend
|
||
docker compose build recipe-frontend
|
||
docker compose up -d recipe-frontend
|
||
```
|
||
|
||
### Kontrollera hälsa
|
||
|
||
```bash
|
||
# Hälsokontroll via HTTP (backend måste köra)
|
||
curl http://localhost:8080/api/health
|
||
|
||
# Databasspecifik hälsokontroll
|
||
curl http://localhost:8080/api/health/db
|
||
```
|
||
|
||
---
|
||
|
||
## Lägga till recept
|
||
|
||
### Snabbimport
|
||
|
||
På sidan "Lägg till nytt recept" finns ett **snabbimportfält** längst upp:
|
||
|
||
```
|
||
Snabbimport: Klistra in länk eller fil
|
||
[https://www.ica.se/recept/...] [→]
|
||
```
|
||
|
||
Klistra in:
|
||
- **ICA-receptlänk** — systemet skrapar automatiskt receptet och importerar det
|
||
- **Andra receptlänkar** — fallback till generisk HTML/JSON-LD-parser
|
||
- En lyckad import omdirigeras till "Skriv in recept" med förifylld Markdown
|
||
|
||
**Stödda källor:**
|
||
- ICA.se — Recept skrapas automatiskt
|
||
- Andra webbplatser — Generic HTML-parser (JSON-LD först, sedan HTML-fallback)
|
||
- 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
|
||
|
||
> **Notering:** Snabbimport-logiken är också tillgänglig som en **[standalone microservice](../microservice-importer/README.md)** för integrations- eller API-använding.
|
||
|
||
---
|
||
|
||
## Arkitektur: Recipe App + Microservice Importer
|
||
|
||
Recipe App är uppbyggd i två komponenter:
|
||
|
||
### Recipe App (detta repo)
|
||
**Fullstack-applikation:** Frontend (Next.js), Backend (NestJS), Databas (MariaDB)
|
||
|
||
Innehåller:
|
||
- Inventorie-hantering (CRUD, konsumtion, history)
|
||
- Recept-hantering (CRUD, matchning mot inventorie)
|
||
- Produktadmin (merge, duplicates, canonical names)
|
||
- **Quick-import** (ICA-skrapning integrerad i `/recipes/create`)
|
||
|
||
### Microservice Importer (separat repo)
|
||
**Standalone-tjänst:** Frontend (Next.js), Backend (NestJS, INGEN databas)
|
||
|
||
Innehåller:
|
||
- **URL-scraping:** ICA.se + generic HTML-parser
|
||
- **Markdown-parsing:** Samma parser-logik som recipe-app
|
||
- Eget kontrollpanel på `/import`
|
||
|
||
**Användningsfall:**
|
||
- Extern API-integration (POST `http://microservice:3001/api/quick-import`)
|
||
- Oberoende snabbimport-webb-UI
|
||
- Muligt att scalea separat från recipe-app
|
||
|
||
**Repo:** [`microservice-importer`](../microservice-importer/)
|
||
- "Kunde inte hämta recept från ICA: ..." — Länken är bruten eller receptet kunde inte parsas
|
||
- "Du måste ange en URL eller filsökväg" — Fältet var tomt
|
||
|
||
### Välja mellan alternativen
|
||
|
||
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, bild eller ange receptlänk för automatisk tolkning
|
||
|
||
### Skriv in recept (Markdown)
|
||
|
||
Navigera till **Lägg till nytt recept → Skriv in recept**
|
||
|
||
**Steg 1: Skriv receptet**
|
||
Använd detta format:
|
||
|
||
|
||
```markdown
|
||
# Köttfärssås
|
||
|
||
En klassisk köttfärssås med massa smak.
|
||
|
||
## Ingredienser
|
||
- 500 g köttfärs
|
||
- 1 st lök
|
||
- 2 msk tomatpuré
|
||
- 1 dl grädde (vispgrädde)
|
||
- salt och peppar
|
||
|
||
## Tillvägagångssätt
|
||
Hacka löken och stek den mjuk i lite olja. Tillsätt köttfärsen och bräsera tills den är genomstekt. Tillsätt tomatpuré och låt det småkoka ett par minuter innan du tillsätter grädde. Smaka av med salt och peppar.
|
||
```
|
||
|
||
**Steg 2: Granska**
|
||
Systemet:
|
||
- Tolkar receptnamn, beskrivning och instruktioner
|
||
- Försöker matcha varje ingrediens mot databasen (Levenshtein-likhet)
|
||
- Visar förslag för varje ingrediens i prioriteringsordning
|
||
|
||
Du kan:
|
||
- Redigera Namnet, beskrivning och instruktioner
|
||
- Välj rätt produkt från förslagen för varje ingrediens
|
||
- Ta bort ingredienser som inte behövs
|
||
- Ändra kvantiteter och enheter
|
||
|
||
**Steg 3: Spara**
|
||
Klicka "Spara recept" — receptet sparas med dina valida ingredienser
|
||
|
||
### Importera från fil eller länk
|
||
|
||
Navigera till **Lägg till nytt recept → Importera från fil**
|
||
|
||
I denna sektion kan du:
|
||
- 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
|
||
- Låta systemet omvandla resultatet till Markdown och öppna det i redigeringsläget
|
||
|
||
> **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
|
||
|
||
| Sektion | Beskrivning |
|
||
|---------|------------|
|
||
| **H1 (# titel)** | Receptnamn |
|
||
| **Text efter H1, före ## Ingredienser** | Receptbeskrivning (valfritt) |
|
||
| **## Ingredienser** | Rubrik för ingredienslistan |
|
||
| **Ingrediensrader** | Mönster: `- ANTAL ENHET NAMN` eller `- ANTAL NAMN` (standard: st) |
|
||
| **Parentes i ingrediens** | Text i `(parentes)` sparas som ingrediensnot, t.ex. `(vispgrädde)`, `(eller crème fraiche)` |
|
||
| **## Tillvägagångssätt** (eller `## Instruktioner`) | Rubrik för tillagningsinstruktioner |
|
||
| **Text under instruktioner** | Instruktionstexten (kan fortsätta över flera rader) |
|
||
|
||
### Matchningsalgoritm
|
||
|
||
Systemet använder tre metoder för att hitta rätt produkt:
|
||
|
||
1. **Exakt match** (100 poäng)
|
||
- Ingrediensnamn matchar exakt efter normalisering (lowercase, utan skiljetecken)
|
||
|
||
2. **Delsträng-match** (70 poäng)
|
||
- Ingrediensnamn förekommer som del av produktnamnet eller vice versa
|
||
|
||
3. **Levenshtein-likhet** (40–100 poäng)
|
||
- Likhetspoäng baserat på tecknenskillnad
|
||
- Mindre än 40 poäng filtreras bort
|
||
|
||
Systemet visar upp till 5 bästa förslag per ingrediens.
|
||
|
||
---
|
||
|
||
## Projektstruktur
|
||
|
||
```
|
||
recipe-app/
|
||
├── frontend/ # Next.js (App Router)
|
||
├── backend/ # NestJS REST API
|
||
├── recipe-document-converter/ # Markdown-parserbibliotek
|
||
├── db/init/ # SQL-initialiseringsskript
|
||
├── compose.yml # Docker Compose
|
||
└── backup_recipe_app.sh # Backupskript
|
||
```
|
||
|
||
---
|
||
|
||
## Backup
|
||
|
||
```bash
|
||
bash backup_recipe_app.sh
|
||
```
|
||
|
||
Säkerhetskopierar källkod och Docker-images till konfigurerad backupmapp.
|
||
|
||
## 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.
|