Files
recipe-app/README.md
T

12 KiB
Raw Blame History

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.
För förslag på nästa steg i projektet, se NEXT_STEPS.md.


Funktioner

Inventorie (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

Recept

  • Skapa och redigera recept — med namn, beskrivning, portionsantal, ingredienser (kvantitet och enhet) och instruktioner i Markdown-format
  • 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)

Matplanering

  • Veckovy — planera veckans måltider dag för dag med ett enkelt receptval
  • Portionsjustering per dag — välj hur många portioner du vill laga för varje dag; avviker du från receptets grundportioner visas en återställningsknapp
  • 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)

Kvittoimport

  • Fotografera eller ladda upp kvitto — JPEG, PNG, WebP, HEIC och PDF stöds (max 15 MB)
  • AI-tolkning via Mistral — Mistral AI extraherar varunamn och mängder direkt från kvittobilden
  • Alias-matchning — kvittots produktnamn matchas mot kända alias (t.ex. "ICA Kvarg Jordg" → "Kvarg") och mot befintliga produkter
  • Granska och lägg till — se tolkningsresultatet, justera kvantitet och enhet, och lägg till direkt i inventariet

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

Admin: Produkter

  • Redigera produkter — uppdatera visningsnamn, canonical name, kategori (hierarkisk dropdown) och varumärke inline direkt i listan
  • Kategoritilldelning — välj kategori ur ett 3-nivåträd (huvudkategori → underkategori → typ) som laddas dynamiskt från API:et
  • Bulk-kategorisering — filtrera fram okategoriserade produkter, markera flera (eller "välj alla synliga") och sätt kategori på alla markerade på en gång — effektivt för att kategorisera många produkter i ett svep
  • Hitta dubbletter — identifiera produkter med samma normaliserade namn
  • Slå ihop produkter — merge av två produktposter: alla inventarieföremål och receptreferenser flyttas till målprodukten, källan soft-deleteras
  • Förhandsvisning — granska vad som händer (inventarieräkningar, utfall) innan merge genomförs
  • Ta bort och återställ — soft-delete enskilda produkter, återställ med ett klick
  • Återställ all produktdata — rensningsknapp som raderar alla produkter, inventarie, taggar och kvitto-alias (behåller användare och kategorier)

Obs: Destruktiva åtgärder (merge, ta bort, återställ, bulk-uppdatera, återställ all data) kräver admin-roll.

Admin: Användare

  • Lista alla användare — se användarnamn, e-postadress, namn, roll och registreringsdatum
  • Ändra roll — växla en användares roll mellan user och admin med ett klick
  • Skyddad sida/admin/users är enbart åtkomlig för inloggade användare med admin-roll; övriga omdirigeras till startsidan
  • Navigering — länken "👥 Användare" visas bara i huvudmenyn om inloggad användare har admin-roll

Autentisering och roller

  • Rollbaserad åtkomstkontroll — systemet har två roller: user (standard) och admin
  • 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 användarhantering kräver admin-roll; försök utan rätt roll ger 403 Förbjuden

Användarprofil

  • Redigera profilinformation — uppdatera förnamn, efternamn och e-postadress under "Min profil"

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

# 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.

Bygg bara backend eller frontend om behövligt

# 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

# 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 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

  • "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:

# 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 backup_recipe_app.sh

Säkerhetskopierar källkod och Docker-images till konfigurerad backupmapp.