# Nyheter och förbättringar (2026-05-19) - **Flyerimport-sessioner i backend:** Nya endpoints `GET /api/flyer-import/sessions/latest` och `GET /api/flyer-import/sessions/:sessionId` för att återhämta senaste eller specifik importsession per användare. - **Persistens i Flutter för flyerimport:** `flyer_import_session.dart` sparar nu endast `sessionId`, `fileName` och valda rader i `SharedPreferences` för lättviktig cache. - **Hydrering vid återöppning:** Flutter-tabben återläser lokalt tillstånd och hämtar därefter full session från backend, med fallback till senaste session. - **Robustare felsemantik:** Backend returnerar `NotFoundException` (404) för saknade sessioner i stället för `BadRequestException`. - **Verifiering:** Backend typecheck och tjänstetester (`flyer-import.service.spec.ts`, 3/3) passerar. # Nyheter och förbättringar (2026-05-18) - **CI: ESLint för backend:** ESLint är infört i backend (`backend/eslint.config.mjs`) och körs i GitHub Actions (`.github/workflows/test.yml`) via steget `Lint backend`. - **CI: Dart lints aktiverade:** `flutter/analysis_options.yaml` är tillagd med `include: package:flutter_lints/flutter.yaml`, så `flutter analyze` använder explicita lint-regler. - **Prisma query logging i test/staging:** Backend stödjer nu env-styrd query-loggning via `PRISMA_LOG_QUERIES` i `backend/src/prisma/prisma.service.ts`. - **Compose-stöd för loggning:** `compose.yml` har `PRISMA_LOG_QUERIES: "${PRISMA_LOG_QUERIES:-0}"` för säker default av. - **Testfix receipt-import:** Säkerhetstestet för saknat användar-id i `upsertUnitMapping` är uppdaterat till `UnauthorizedException`, i linje med controllerns beteende. # 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** (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.