Files
recipe-app/README.md
T
Nils-Johan Gynther 50816a6844
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m2s
Test Suite / flutter-quality (push) Successful in 1m40s
feat: add central help text system, Prisma migration, and Flutter help button
2026-05-13 17:04:44 +02:00

422 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Nyheter och förbättringar (2026-05-13)
- **Centralt hjälptextsystem:** Nytt backend-modul (`HelpTextsModule`) med `GET /api/help-texts/:key` (rollmedveten) och `PUT /api/help-texts/:key/:scope` (admin). Stöd för scopade hjälptexter: `admin`, `user`, `default` med prioritetsordning beroende på användarroll.
- **Prisma-migration:** `20260513150000_add_help_texts` — skapar `HelpText`-tabell och seedar initiala hjälptexter för kvittoimport (standard + admin-variant) på svenska.
- **Flutter — "Läs hjälp"-knapp i kvittoimport:** Nytt `TextButton.icon` med `Icons.help_outline` i headern på receipt_import_tab. Hämtar hjälptext från backend vid klick och visar den i en AlertDialog med titel, innehåll (selekterbart), scope-chip och uppdateringsdatum.
- **Copilot-instruktioner:** `.github/copilot-instructions.md` tillagd i alla tre repos för att alltid använda robust `.env`-läsningsmönster (grep/sed/tr) vid databas-kommandon.
# 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-11)
# Nyheter och förbättringar (2026-05-11)
# Nyheter och förbättringar (2026-05-11)
- **Flutter web build:** Löste dart2js-kompilationsfel i Flutter-webbappen (adminpanel). Fixade runtime-beroende i meny, deklarerade saknade state-fält i formulärdialoger för inventarie och baslager.
- **Git och deploy:** Rensade build-artifacts från versionshantering och dokumenterade rekommenderad .gitignore för Flutter build-mappar.
- **Stabiliserad deploy:** Dokumenterat rutiner för att hantera merge-konflikter orsakade av genererade filer vid serveruppdatering.
- **Seed-data:** Nya kategorier under `Skafferi > Sylt, mos & marmelad`: `Marmelad`, `Sylt`, `Mos` tillagda i seed.
- **Navigation/UI:** Admin-knappen flyttad från sidomenyn till profilmenyn ovanför logout.
- **Deploy och seed:** Förtydligande om att seed-data körs automatiskt vid `./deploy.sh --backend` och att seed-kontroll sker i deploy-scriptet. Hur man verifierar att seed körts och att nya kategorier finns.
- **Kodförenklingar/optimeringar:** Samtliga tre förenklingar/optimeringar från senaste commit är nu implementerade (se SESSION_2026-05-09_RECEIPT_IMPORT.md för detaljer).
### 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
```
### Databas (one-liners via .env)
Anvand dessa kommandon vid drift/felsokning for att alltid lasa MariaDB-credentials direkt fran `.env`.
```bash
# Visa senaste Prisma-migreringar
docker exec -i recipe-db mariadb -uroot -p"$(grep -E '^[[:space:]]*MARIADB_ROOT_PASSWORD[[:space:]]*=' .env | tail -n1 | sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]]+$//; s/^["'\''']|["'\''']$//g' | tr -d '\r')" "$(grep -E '^[[:space:]]*MARIADB_DATABASE[[:space:]]*=' .env | tail -n1 | sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]]+$//; s/^["'\''']|["'\''']$//g' | tr -d '\r')" -e "SELECT migration_name, finished_at FROM _prisma_migrations ORDER BY finished_at DESC LIMIT 10;"
# Kontrollera att HelpText-tabellen finns
docker exec -i recipe-db mariadb -uroot -p"$(grep -E '^[[:space:]]*MARIADB_ROOT_PASSWORD[[:space:]]*=' .env | tail -n1 | sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]]+$//; s/^["'\''']|["'\''']$//g' | tr -d '\r')" "$(grep -E '^[[:space:]]*MARIADB_DATABASE[[:space:]]*=' .env | tail -n1 | sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]]+$//; s/^["'\''']|["'\''']$//g' | tr -d '\r')" -e "SHOW TABLES LIKE 'HelpText';"
```
---
## 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** (40100 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.