Files
recipe-app/TEKNISK_BESKRIVNING.md
T

12 KiB
Raw Blame History

Teknisk beskrivning av Recipe App

Se README.md för användarinformation och kom-igång-guide.

Översikt

Recipe App är en fullstack-applikation för hantering av hemmavaror, recept och inköpsplanering. Systemet är byggt med Next.js (frontend), NestJS (backend), Prisma ORM och MariaDB. Applikationen är containeriserad med Docker och använder Caddy som reverse proxy.


Versionsinformation

Delsystem Teknik Version
Frontend Next.js 16.2
React 19.2
TypeScript 5.4.5
Node 22.x (via @types/node 22.15.29)
Backend NestJS 10.3
Prisma 6.12.0
TypeScript 5.4.5
Node 22.x (via @types/node 22.15.29)
Databas MariaDB 11
Proxy Caddy 2.x
Container Docker 24+

Frontend

  • Framework: Next.js 16.2 (App Router, server + client components)
  • Språk: TypeScript 5.4.5
  • UI: React 19.2, ingen CSS-ramverk (ren CSS-in-JS och inline-stilar)
  • Bygg: Standalone output, körs i Docker-container
  • API-anrop: Fetch mot backend och Next.js API routes
  • Felhantering: Global parseErrorResponse utility, svenska felmeddelanden

Funktioner (frontend)

  • Inventarielista:
    • Sök, filtrera och sortera hemmavaror (namn, plats, bäst före, A–Ö)
    • Lägg till, redigera, konsumera och ta bort varor
    • Konsumtionshistorik med enheter
  • Recept:
    • Lista, skapa, redigera och ta bort recept
    • Jämför recept mot hemmavaror (räcker/saknas/enhetskonflikt)
    • Visar instruktioner och saknade ingredienser för valt recept
    • Sidebar med snabblista över recept
  • Admin: Produkter:
    • Sök och sortera produkter (A–Ö, senast tillagda)
    • Redigera canonical name
    • Merge preview för produktnamn
  • Felhantering:
    • Svenska felmeddelanden, tydliga varningar vid valideringsfel
  • Responsiv design:
    • Fungerar på mobil, surfplatta och desktop

Backend

  • Framework: NestJS 10.3
  • Språk: TypeScript 5.4.5
  • Databas: MariaDB 11 (via Prisma 6.12.0)
  • API: REST, validering med class-validator
  • Felhantering: GlobalExceptionFilter (svenska felmeddelanden)
  • Hälsokontroll: /health endpoint med status, uptime, DB-latens
  • Bygg: Körs i Docker-container, byggs med nest build

Funktioner (backend)

  • Inventarie-API:
    • CRUD för hemmavaror
    • Konsumtionshistorik (med enheter)
    • Sortering och filtrering (plats, bäst före, namn)
  • Recept-API:
    • CRUD för recept och ingredienser
    • Preview mot hemmavaror (räcker/saknas/enhetskonflikt)
  • Produkt-API:
    • CRUD för produkter
    • Merge preview och canonical name
  • Felhantering:
    • Svenska felmeddelanden, 400/503 status
  • Hälsokontroll:
    • /health endpoint (200/503, DB-status, uptime)

Infrastruktur & DevOps

  • Docker Compose: Orkestrerar frontend, backend, databas och proxy
  • Caddy: Reverse proxy, hanterar Next.js API routes och backend
  • Miljövariabler: Hanterar DB-url, ports etc
  • Backup-script: backup_recipe_app.sh

Viktiga filer & mappar

  • frontend/app/ Next.js app directory (pages, komponenter)
  • backend/src/ NestJS API (controllers, services, modules)
  • backend/prisma/schema.prisma Prisma datamodell
  • compose.yml Docker Compose setup
  • Caddyfile Proxyregler

Funktionell översikt

Hemmavaror

  • Lägg till, redigera, ta bort och konsumera varor
  • Sök, filtrera (plats), sortera (bäst före, namn)
  • Konsumtionshistorik med enheter

Recept

  • Skapa, redigera, ta bort recept
  • Jämför mot hemmavaror (räcker/saknas/enhetskonflikt)
  • Visar instruktioner och saknade ingredienser
  • Importera recept från Markdown (se nedan)

Produkter (Admin)

  • Sök, sortera, redigera canonical name
  • Merge preview

Hälsa & Fel

  • /health endpoint (status, uptime, DB)
  • Svenska felmeddelanden i hela systemet

Exempel på API-endpoints

  • GET /api/inventory Lista hemmavaror
  • POST /api/inventory Lägg till vara
  • PATCH /api/inventory/:id Uppdatera vara
  • DELETE /api/inventory/:id Ta bort vara
  • GET /api/recipes Lista recept
  • POST /api/recipes Skapa recept
  • PATCH /api/recipes/:id Uppdatera recept
  • DELETE /api/recipes/:id Ta bort recept
  • POST /api/recipes/parse-markdown Tolka Markdown-recept (se nedan)
  • GET /api/products Lista produkter
  • PATCH /api/products/:id Uppdatera produkt
  • GET /health Hälsokontroll


Receptimport via Markdown

Syfte

Användaren kan importera ett recept skrivet i ett enkelt Markdown-format istället för att fylla i formularet manuellt. Systemet tolkar texten, föreslår matchande produkter från databasen och låter användaren granska och bekräfta innan receptet sparas.

Markdown-format

# Receptnamn

Valfri beskrivning av receptet.

## Ingredienser
- 500 g köttfärs
- 1 st lök
- 2 msk tomatpuré
- 1 dl grädde (vispgrädde)

## Tillvägagångssätt
Stek löken i lite smör. Tillsats köttfärsen...

Regler:

  • Rad med # tolkas som receptnamn
  • Text mellan #-rubriken och ## Ingredienser tolkas som beskrivning
  • Rader under ## Ingredienser med mönstret - ANTAL ENHET NAMN tolkas som ingredienser
  • Text i parentes efter ingrediensnamnet ((vispgrädde)) sparas som anteckning
  • Text under ## Tillvägagångssätt (eller ## Instruktioner) tolkas som instruktioner

Arkitektur

Användaren         Frontend           Backend           Bibliotek
(klistrar in MD) → /recipes/import → POST /api/        → recipe-document-
                   ImportRecipePage    recipes/           converter/
                                       parse-markdown     parseRecipeMarkdown()
                        ↑
                   Granskar förslag
                   Väljer produkter
                        ↓
                   POST /api/recipes  (befintlig endpoint)

Komponenterna

recipe-document-converter/ (fristående TypeScript-bibliotek)

Ett eget npm-paket som inte har externa beroenden. Det enda som exporteras är:

parseRecipeMarkdown(markdown: string): ParsedRecipe

Returnerar:

type ParsedRecipe = {
  name: string;
  description?: string;
  instructions?: string;
  ingredients: Array<{
    rawName: string;   // fråntext, t.ex. "köttfärs"
    quantity: number;  // t.ex. 500
    unit: string;      // t.ex. "g"
    note?: string;     // text i parentes, t.ex. "nötfärs"
  }>;
};

Biblioteket kompileras i ett separat Docker-byggsteg och länkas till backend via "recipe-document-converter": "file:../recipe-document-converter" i backend/package.json.

Backend — POST /api/recipes/parse-markdown

Endpoint som tar emot { markdown: string } och returnerar det tolkade receptet åtsamman med produktmatchförslag för varje ingrediens.

Matchningslogik:

  1. Anropar parseRecipeMarkdown() från biblioteket
  2. Hämtar alla aktiva produkter ur databasen
  3. Jämför varje ingrediensnamn mot product.canonicalName / product.normalizedName med tre metoder i ordning:
    • Exakt match (efter normalisering) → 100 poäng
    • Delsträngsmatch → 70 poäng
    • Levenshtein-likhet → 0100 poäng (filtreras under 40)
  4. Returnerar upp till 5 förslag per ingrediens, sorterade efter poäng

Svar:

{
  "name": "Köttfärssås",
  "description": "En klassisk...",
  "instructions": "Stek löken...",
  "ingredients": [
    {
      "rawName": "köttfärs",
      "quantity": 500,
      "unit": "g",
      "suggestions": [
        { "productId": 12, "productName": "Köttfärs", "score": 100 },
        { "productId": 34, "productName": "Blandfärs", "score": 55 }
      ]
    }
  ]
}

Frontend — /recipes/import

En 3-stegsvy (client component):

Steg Innehåll
1. Klistra in Textarea för Markdown + "Tolka recept"-knapp
2. Granska Redigerbara fält för namn/beskrivning/instruktioner; varje ingrediens har en dropdown med föreslagna produkter överst, sedan alla produkter
3. Spara Knapp som POSTar till befintlig POST /api/recipes

Ingrediensräder med ingen matchning markeras visuellt (gul ram) så att användaren ser att de behöver väljas manuellt. Receptet sparas inte förrän minst en ingrediens har en vald produkt.

Flöde i Next.js:

/recipes/import
  └─ ImportRecipePage.tsx          (client component, 3-stegsflödet)

/api/parse-markdown-proxy
  └─ route.ts                       (POST-proxy till backend, omgår CORS)

Docker-bygget

Backend-Dockerfilen använder projektets rot (.) som byggkontext. Bygget sker i tre steg:

  1. converter-build — Kompilerar recipe-document-converter till JavaScript + typdeklarationer
  2. builder — Installerar backend-beroenden, kopierar in den kompilerade convertern till node_modules/, genererar Prisma-klient och bygger NestJS-appen
  3. runner — Minimal produktionsimage med enbart dist/, node_modules/ och prisma/
# Stage 1: Bygg converter-biblioteket
FROM node:22-alpine AS converter-build
WORKDIR /converter
COPY recipe-document-converter/package.json ./
RUN npm install
COPY recipe-document-converter/src ./src
COPY recipe-document-converter/tsconfig.json ./
RUN npm run build

# Stage 2: Bygg applikationen
FROM node:22-alpine AS builder
WORKDIR /app
COPY backend/package.json ./
COPY backend/prisma ./prisma
COPY backend/src ./src
COPY backend/tsconfig.json ./
COPY backend/nest-cli.json ./
RUN npm install
# Kopiera in det kompilerade converter-biblioteket efter npm install
COPY --from=converter-build /converter ./node_modules/recipe-document-converter
RUN npx prisma generate
RUN npm run build

# Stage 3: Kör applikationen
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/dist ./dist
EXPOSE 8080
CMD ["node", "dist/main"]

OBS: backend/package.json har "recipe-document-converter": "file:../recipe-document-converter" för lokal utveckling. I Docker-bygget ignoreras den file-referensen — convertern kopieras in manuellt från converter-build-steget.

Bygga om backend efter ändringar:

docker compose build recipe-api

Relevanta filer

Fil Syfte
recipe-document-converter/src/parser.ts Markdown-parser
recipe-document-converter/src/index.ts Biblioteksexport
backend/src/recipes/dto/parse-markdown.dto.ts Inkommande DTO
backend/src/recipes/recipes.controller.ts Nytt endpoint
backend/src/recipes/recipes.service.ts Matchningslogik
frontend/app/recipes/import/ImportRecipePage.tsx 3-stegsvy
frontend/app/api/parse-markdown-proxy/route.ts Proxy-route

Säkerhet

  • Ingen auth i grundutförande (kan enkelt byggas på)
  • Validering av all input (class-validator)
  • Felmeddelanden på svenska

Utbyggbarhet

  • Lätt att lägga till fler fält, filter och funktioner
  • Kan utökas med auth, shoppinglistor, delning m.m.

Kontakt

För frågor, kontakta utvecklaren.