docs: update README, NEXT_STEPS, and technical description for category feature and auth
This commit is contained in:
+25
-73
@@ -15,23 +15,31 @@
|
|||||||
| Kvittoimport (Mistral AI, OCR, alias) | ✅ Klart |
|
| Kvittoimport (Mistral AI, OCR, alias) | ✅ Klart |
|
||||||
| Matplanering (veckovy, inköpslista) | ✅ Klart |
|
| Matplanering (veckovy, inköpslista) | ✅ Klart |
|
||||||
| Baslager (lista, lägg till, ta bort) | ✅ Klart |
|
| Baslager (lista, lägg till, ta bort) | ✅ Klart |
|
||||||
| Admin: Produkter (edit, merge, duplicate, restore) | ✅ Klart |
|
| Admin: Produkter (edit, merge, duplicate, restore, reset) | ✅ Klart |
|
||||||
| Receptredigering (frontend UX) | ✅ Klart |
|
| Receptredigering (frontend UX) | ✅ Klart |
|
||||||
| Receptbilder (upload URL) | ✅ Klart |
|
| Receptbilder (upload URL) | ✅ Klart |
|
||||||
|
| Autentisering (JWT, Auth.js v5, User-modell) | ✅ Klart |
|
||||||
|
| Användarprofil (firstName, lastName, email) | ✅ Klart |
|
||||||
|
| Produktkategorier — hierarkisk struktur (3 nivåer) | ✅ Klart |
|
||||||
|
| Taggning av produkter | ✅ Klart |
|
||||||
|
| Näringsvärden på produkter | ✅ Klart (schema + API) |
|
||||||
|
| Kategoritilldelning i admin-UI | ✅ Klart |
|
||||||
| Portionsjustering | ❌ Saknas |
|
| Portionsjustering | ❌ Saknas |
|
||||||
| Produktkategorier — fast lista | ❌ Saknas |
|
|
||||||
| Receptlista — filtrering & kortvy | ✅ Klart |
|
|
||||||
| Matplan — inventariejämförelse | ❌ Saknas |
|
| Matplan — inventariejämförelse | ❌ Saknas |
|
||||||
| Taggning av produkter | ⚠️ Delvis — kräver migration |
|
| Seed produktdata med kategoritilldelning | ❌ Saknas (002-seed-products.sql.disabled) |
|
||||||
| Näringsvärden på produkter | ⚠️ Delvis — kräver migration |
|
| Användarspecifika produkter (UserProduct) | ⚠️ Schema klart, UI basic |
|
||||||
| Autentisering (User-modell) | ❌ Saknas |
|
|
||||||
| Användarspecifika produkter (UserProduct) | ❌ Saknas — kräver auth |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Prioriterade förbättringar
|
## Prioriterade förbättringar
|
||||||
|
|
||||||
### 1. Portionsjustering av recept
|
### 1. Seed produktdata med kategoritilldelning
|
||||||
|
`db/init/002-seed-products.sql` är inaktiverad (`.disabled`) tills den uppdateras med rätt `categoryId` för varje produkt. Utan detta är produktdatabasen tom vid fresh install.
|
||||||
|
- Gå igenom de ~190 produkterna och tilldela rätt kategori-ID från tabellen `Category`
|
||||||
|
- Aktivera filen igen genom att ta bort `.disabled`-suffixet
|
||||||
|
- Alternativt: bygg ett admin-verktyg för bulk-kategorisering
|
||||||
|
|
||||||
|
### 2. Portionsjustering av recept
|
||||||
Recept lagras utan portionsangivelse. Lägg till ett `servings`-fält och låt användaren justera antal portioner i receptvyn — ingrediensmängderna räknas om proportionellt (t.ex. 4 → 6 pers: × 1,5).
|
Recept lagras utan portionsangivelse. Lägg till ett `servings`-fält och låt användaren justera antal portioner i receptvyn — ingrediensmängderna räknas om proportionellt (t.ex. 4 → 6 pers: × 1,5).
|
||||||
- **Databas:** `servings Int?` på `Recipe` i Prisma + migration
|
- **Databas:** `servings Int?` på `Recipe` i Prisma + migration
|
||||||
- **Backend:** `servings` exponeras i `RecipeDto`, sätts vid create/update
|
- **Backend:** `servings` exponeras i `RecipeDto`, sätts vid create/update
|
||||||
@@ -42,67 +50,10 @@ Recept lagras utan portionsangivelse. Lägg till ett `servings`-fält och låt a
|
|||||||
### 3. Matplanering — jämförelse mot inventariet
|
### 3. Matplanering — jämförelse mot inventariet
|
||||||
Veckovy och inköpslista fungerar. Nästa steg är att visa vilka ingredienser på inköpslistan som redan finns hemma och i vilken mängd — liknande receptvyns inventory-preview. Implementeras via `GET /api/recipes/:id/inventory-preview` per recept, aggregerat på veckonivå.
|
Veckovy och inköpslista fungerar. Nästa steg är att visa vilka ingredienser på inköpslistan som redan finns hemma och i vilken mängd — liknande receptvyns inventory-preview. Implementeras via `GET /api/recipes/:id/inventory-preview` per recept, aggregerat på veckonivå.
|
||||||
|
|
||||||
### 4. Produktkategorier — definiera en fast lista
|
### 4. Bulk-kategorisering av produkter i admin
|
||||||
Kategorier skrivs in som fritext i admin. Byt till en dropdown med fördefinierade kategorier (t.ex. "Mejeri, ost & ägg", "Kött, chark & fågel", "Frukt & Grönt") för konsistent data och bättre gruppering i baslagervyn.
|
Admin-UI:t tillåter idag att sätta kategori per produkt. För att effektivt kategorisera hundratals produkter behövs:
|
||||||
|
- Filtervy för okategoriserade produkter
|
||||||
### 5. Utökad databas med taggning
|
- Möjlighet att sätta kategori på flera produkter samtidigt (bulk-select)
|
||||||
Lägg till stöd för taggar, underkategorier och varumärke direkt på produkter. Möjliggör filtrering, sökning och rekommendationer baserade på taggar.
|
|
||||||
|
|
||||||
**Schemaändringar (Prisma):**
|
|
||||||
- **`Product`** — lägg till `subcategory String?` och `brand String?` (behåll `canonicalName`)
|
|
||||||
- **`Tag`** — ny modell: `id`, `name @unique`
|
|
||||||
- **`ProductTag`** — ny relationstabell (many-to-many: `Product ↔ Tag`)
|
|
||||||
|
|
||||||
**Implementeringssteg:**
|
|
||||||
1. Uppdatera `backend/prisma/schema.prisma` med nya modeller och relationer
|
|
||||||
2. Kör migration: `docker exec recipe-api npm exec prisma migrate dev --name add_tags_subcategory_brand`
|
|
||||||
3. Skapa seed-fil (`data/seed_tags.sql`) med taggar och kopplingar
|
|
||||||
4. Kör seed-filen mot databasen
|
|
||||||
5. Exponera `tags`, `subcategory`, `brand` i produkt-DTOs och `GET /api/products` (lägg till `?tag=` och `?subcategory=` som filterparametrar)
|
|
||||||
6. Admin: lägg till tagg-hantering och underkategori-fält
|
|
||||||
7. Baslager/produktlista: filtrera per tagg eller underkategori
|
|
||||||
|
|
||||||
**Rekommenderade taggar:** `ekologisk`, `svensk`, `laktosfri`, `glutenfri`, `vegan`, `nötfri`, `säsong`, `rökt`, `premium`, `lamm`, `korv`, `färs`, m.fl.
|
|
||||||
|
|
||||||
### 6. Näringsvärden på produkter
|
|
||||||
Lägg till en `Nutrition`-modell kopplad till `Product` (one-to-one) med näringsvärden per 100g: kalorier, protein, fett, kolhydrater, salt, socker, fiber. Kan implementeras oberoende av autentisering.
|
|
||||||
|
|
||||||
**Schemaändring:**
|
|
||||||
```prisma
|
|
||||||
model Nutrition {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
calories Float?
|
|
||||||
protein Float?
|
|
||||||
fat Float?
|
|
||||||
carbohydrates Float?
|
|
||||||
salt Float?
|
|
||||||
sugar Float?
|
|
||||||
fiber Float?
|
|
||||||
product Product @relation(fields: [productId], references: [id])
|
|
||||||
productId Int @unique
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **Backend:** CRUD via produktendpoints, exponeras i `ProductDto`
|
|
||||||
- **Frontend:** Visa näringsvärden i produktdetalj och eventuellt i receptvyn (summerat per portion)
|
|
||||||
|
|
||||||
### 7. Autentisering — User-modell
|
|
||||||
Förutsättning för användarspecifika produkter (punkt 10). Idag saknar hela appen autentisering — alla kan CRUD allt.
|
|
||||||
|
|
||||||
**Scope:** JWT-baserad auth med `User`-modell (id, name, email, passwordHash). Berör:
|
|
||||||
- Backend: AuthModule med NestJS Guards, JWT-strategi, skyddade routes
|
|
||||||
- Frontend: Inloggningsflöde, token-hantering i API-anrop
|
|
||||||
- Databas: `User`-tabell + migration
|
|
||||||
|
|
||||||
> ⚠️ Detta är ett stort projekt i sig. Överväg om appen verkligen behöver fler användare eller om enkel HTTP Basic Auth räcker som skydd.
|
|
||||||
|
|
||||||
### 8. Användarspecifika produkter (UserProduct)
|
|
||||||
Låter en användare spara egna produktvarianter med eget namn (t.ex. "Mormors Prästost") kopplade till en standardprodukt — eller fristående utan koppling. Kräver att punkt 9 (auth) är på plats.
|
|
||||||
|
|
||||||
> ⚠️ **Överlapp med InventoryItem:** `InventoryItem` lagrar redan productId, quantity, unit, brand, bestBeforeDate och är i princip en "användarens produkt i lager". Klargör skillnaden:
|
|
||||||
> - `InventoryItem` = vad som finns hemma just nu (lager)
|
|
||||||
> - `UserProduct` = ett eget produktkort/favorit som kan återanvändas utan att vara lager
|
|
||||||
>
|
|
||||||
> Om distinktionen inte är tydlig, riskerar `UserProduct` att duplicera `InventoryItem`-logiken.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -137,7 +88,8 @@ Frontend-server-actions saknar validering på inkommande fält (tom sträng, fö
|
|||||||
|
|
||||||
## Produktdatabasen
|
## Produktdatabasen
|
||||||
|
|
||||||
193 svenska produkter är inseedad. Nästa naturliga steg:
|
Produktdatabasen är just nu tom — seedfilen `db/init/002-seed-products.sql.disabled` innehåller ~190 svenska baslivsmedel men är inaktiverad tills produkterna har tilldelats rätt `categoryId`. Nästa naturliga steg:
|
||||||
- Lägg till fler saknade produkter som dyker upp vid receptimport
|
- Gå igenom produkterna och tilldela kategorier via admin-UI eller uppdatera seed-filen direkt
|
||||||
- Gå igenom produkter utan `canonicalName` i admin och fyll i dem
|
- Aktivera seed-filen igen (`002-seed-products.sql`) för reproducerbarhet vid fresh install
|
||||||
- Kontrollera att `category` är ifyllt för alla produkter (för bättre gruppering i baslager)
|
- Lägg till fler produkter som dyker upp vid receptimport
|
||||||
|
- Kontrollera att `canonicalName` är ifyllt för alla produkter
|
||||||
|
|||||||
@@ -30,12 +30,14 @@ En fullstack-applikation för hantering av hemmavaror och recept. Håll koll på
|
|||||||
- **Lägg till och ta bort** — välj från produktlistan via dropdown, ta bort med ett klick
|
- **Lägg till och ta bort** — välj från produktlistan via dropdown, ta bort med ett klick
|
||||||
|
|
||||||
### Admin: Produkter
|
### Admin: Produkter
|
||||||
- **Redigera produkter** — uppdatera visningsnamn (name), canonical name och kategori inline direkt i listan
|
- **Redigera produkter** — uppdatera visningsnamn (name), 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
|
||||||
- **Ta bort produkter** — soft-delete enskilda produkter
|
- **Ta bort produkter** — soft-delete enskilda produkter
|
||||||
- **Hitta dubbletter** — identifiera produkter med samma normaliserade namn
|
- **Hitta dubbletter** — identifiera produkter med samma normaliserade namn
|
||||||
- **Slå ihop produkter** — merge två produktposter, flytta alla inventarieföremål till målprodukten (källan soft-deleteras)
|
- **Slå ihop produkter** — merge två produktposter, flytta alla inventarieföremål till målprodukten (källan soft-deleteras)
|
||||||
- **Förhandsvisning** — granska vad som kommer att hända innan merge genomförs
|
- **Förhandsvisning** — granska vad som kommer att hända innan merge genomförs
|
||||||
- **Återställ produkter** — restore tidigare raderade produkter
|
- **Återställ produkter** — restore tidigare raderade produkter
|
||||||
|
- **Återställ all produktdata** — rensningsknapp som raderar alla produkter, inventorie, taggar och kvitto-alias (behåller användare och kategorier)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+116
-25
@@ -53,16 +53,22 @@ docker exec recipe-db mariadb -uroot -p"LÖSENORD" recipe_app -e "SHOW TABLES;"
|
|||||||
- **Framework:** Next.js 16.2 (App Router, server + client components)
|
- **Framework:** Next.js 16.2 (App Router, server + client components)
|
||||||
- **Språk:** TypeScript 5.4.5
|
- **Språk:** TypeScript 5.4.5
|
||||||
- **UI:** React 19.2, ingen CSS-ramverk (ren CSS-in-JS och inline-stilar)
|
- **UI:** React 19.2, ingen CSS-ramverk (ren CSS-in-JS och inline-stilar)
|
||||||
|
- **Autentisering:** Auth.js v5 (next-auth beta), JWT-session, `auth()` i server components
|
||||||
- **Bygg:** Standalone output, körs i Docker-container
|
- **Bygg:** Standalone output, körs i Docker-container
|
||||||
- **API-anrop:** Fetch mot backend och Next.js API routes
|
- **API-anrop:** `fetchJson` (server-side med auth-headers) + Next.js API route-proxies (client-side)
|
||||||
- **Felhantering:** Global parseErrorResponse utility, svenska felmeddelanden
|
- **Felhantering:** Global parseErrorResponse utility, svenska felmeddelanden
|
||||||
|
|
||||||
|
> **Viktigt:** `Navigation.tsx` är en async server component som anropar `auth()`. Den får aldrig importeras av client components — rendera den alltid i `page.tsx` (server component).
|
||||||
|
|
||||||
### Frontend-sidor och komponenter
|
### Frontend-sidor och komponenter
|
||||||
|
|
||||||
| Sida | Fil | Funktionalitet |
|
| Sida | Fil | Funktionalitet |
|
||||||
|------|-----|---|
|
|------|-----|---|
|
||||||
| **Hem** | `app/page.tsx` | Startsida |
|
| **Hem** | `app/page.tsx` | Startsida |
|
||||||
| **Navigering** | `app/Navigation.tsx` | Huvudmeny |
|
| **Navigering** | `app/Navigation.tsx` | Huvudmeny, inloggad användare, länk till profil |
|
||||||
|
| **Inloggning** | `app/login/page.tsx` | Inloggningssida med Auth.js Credentials |
|
||||||
|
| **Profil** | `app/profil/page.tsx` | Redigera firstName, lastName, email |
|
||||||
|
| | `app/profil/ProfileClient.tsx` | Klientkomponent för profilformulär |
|
||||||
| **Inventorie** | `app/inventory/page.tsx` | Lista, filtrera, sortera varor |
|
| **Inventorie** | `app/inventory/page.tsx` | Lista, filtrera, sortera varor |
|
||||||
| | `InventoryList.tsx` | Ritning av inventarieföremål |
|
| | `InventoryList.tsx` | Ritning av inventarieföremål |
|
||||||
| | `InventoryForm.tsx` | Skapa nytt inventarieföremål |
|
| | `InventoryForm.tsx` | Skapa nytt inventarieföremål |
|
||||||
@@ -73,17 +79,21 @@ docker exec recipe-db mariadb -uroot -p"LÖSENORD" recipe_app -e "SHOW TABLES;"
|
|||||||
| | `actions.ts` | Server actions för inventarie |
|
| | `actions.ts` | Server actions för inventarie |
|
||||||
| **Recept** | `app/recipes/page.tsx` | Lista recept |
|
| **Recept** | `app/recipes/page.tsx` | Lista recept |
|
||||||
| | `RecipePreview.tsx` | Receptförhandsvisning med inventariestatus |
|
| | `RecipePreview.tsx` | Receptförhandsvisning med inventariestatus |
|
||||||
| **Lägg till recept** | `app/recipes/create/page.tsx` | Meny för receptskaping (val mellan två vägar) |
|
| **Lägg till recept** | `app/recipes/create/page.tsx` | Server component med Navigation |
|
||||||
| **Skriv in recept** | `app/recipes/write/page.tsx` | Startpunkt för Markdown-inmatning |
|
| | `app/recipes/create/CreateRecipeClient.tsx` | Klientkomponent: snabbimport + metodval |
|
||||||
| | `app/recipes/write/WriteRecipePage.tsx` | Komponenter för receptskapande (Markdown-baserat, 3-steg) |
|
| **Skriv in recept** | `app/recipes/write/page.tsx` | Server component med Navigation |
|
||||||
|
| | `app/recipes/write/WriteRecipePage.tsx` | Markdown-baserat receptskapande (3-steg) |
|
||||||
| **Importera från fil** | `app/recipes/import/page.tsx` | Startpunkt för fil/länk-import |
|
| **Importera från fil** | `app/recipes/import/page.tsx` | Startpunkt för fil/länk-import |
|
||||||
| | `app/recipes/import/ImportFilePage.tsx` | Komponenter för fil-/länk-import (PDF, URL, etc) |
|
| | `app/recipes/import/ImportFilePage.tsx` | Fil-/länk-import (PDF, URL, etc) |
|
||||||
|
| **Import (flikar)** | `app/import/page.tsx` | Server component med Navigation + flikvy |
|
||||||
|
| | `app/import/ImportTabsClient.tsx` | Klientkomponent: kvitto/recept-flikar |
|
||||||
| **Recipe detail** | `app/recipes/[id]/` | Enskilt recept (detaljer, redigering) |
|
| **Recipe detail** | `app/recipes/[id]/` | Enskilt recept (detaljer, redigering) |
|
||||||
| **Admin: Produkter** | `app/admin/products/page.tsx` | Produktadmin-panel |
|
| **Admin: Produkter** | `app/admin/products/page.tsx` | Produktadmin-panel |
|
||||||
| | `AdminProductList.tsx` | Lista produkter, sök, sortera |
|
| | `AdminProductList.tsx` | Lista produkter, sök, sortera |
|
||||||
| | `EditProductForm.tsx` | Inline redigering av name, canonicalName, category + soft-delete |
|
| | `EditProductForm.tsx` | Inline redigering: name, canonicalName, kategori (hierarkisk dropdown), brand, taggar |
|
||||||
|
| | `ResetProductsButton.tsx` | Knapp för att rensa all produktdata |
|
||||||
| | `MergePreviewForm.tsx` | Förhandsgranska merge |
|
| | `MergePreviewForm.tsx` | Förhandsgranska merge |
|
||||||
| | `actions.ts` | Server actions: updateProduct, deleteProduct |
|
| | `actions.ts` | Server actions: updateProduct, deleteProduct, resetAllProducts |
|
||||||
| **Baslager** | `app/baslager/page.tsx` | Visa och hantera baslager (server component) |
|
| **Baslager** | `app/baslager/page.tsx` | Visa och hantera baslager (server component) |
|
||||||
| | `AddToPantryForm.tsx` | Lägg till produkt i baslager (dropdown) |
|
| | `AddToPantryForm.tsx` | Lägg till produkt i baslager (dropdown) |
|
||||||
| | `PantryList.tsx` | Visa baslager grupperat per kategori |
|
| | `PantryList.tsx` | Visa baslager grupperat per kategori |
|
||||||
@@ -91,15 +101,29 @@ docker exec recipe-db mariadb -uroot -p"LÖSENORD" recipe_app -e "SHOW TABLES;"
|
|||||||
|
|
||||||
### API-proxy routes (Next.js)
|
### API-proxy routes (Next.js)
|
||||||
|
|
||||||
|
Alla proxy-routes läser auth-token via `auth()` (Auth.js v5) och vidarebefordrar `Authorization: Bearer <token>` till backend.
|
||||||
|
|
||||||
| Route | Metod | Syfte |
|
| Route | Metod | Syfte |
|
||||||
|-------|-------|-------|
|
|-------|-------|-------|
|
||||||
| `/api/quick-import-proxy` | POST | Proxies `POST /api/quick-import` för URL-, PDF- och bildimport |
|
| `/api/auth/[...nextauth]` | GET, POST | Auth.js handlers (login, logout, session) |
|
||||||
| `/api/parse-markdown-proxy` | POST | Proxies `POST /api/recipes/parse-markdown` (Markdown-tolkning för skriv-in-recept) |
|
| `/api/products` | GET | Produktlista (auth-wrappat med `auth(req)`) |
|
||||||
| `/api/inventory-history-proxy` | GET | Proxies konsumtionshistorik |
|
| `/api/categories` | GET | Kategorihierarki (publik, proxies `/api/categories/tree`) |
|
||||||
| `/api/recipe-preview-proxy` | GET | Proxies receptförhandsvisning |
|
| `/api/profile` | GET, PATCH | Hämta/uppdatera användarprofil |
|
||||||
| `/api/admin/merge-preview-proxy` | GET | Proxies produktmerge-preview |
|
| `/api/recipes` | GET, POST | Lista recept + spara nytt |
|
||||||
| `/api/products` | GET | Lista/proxies produkter |
|
| `/api/quick-import-proxy` | POST | URL-, PDF- och bildimport |
|
||||||
| `/api/recipes` | GET, POST | Lista recept + spara nytt recept (proxy till backend) |
|
| `/api/parse-markdown-proxy` | POST | Markdown-tolkning för skriv-in-recept |
|
||||||
|
| `/api/inventory-history-proxy` | GET | Konsumtionshistorik |
|
||||||
|
| `/api/recipe-preview-proxy` | GET | Receptförhandsvisning |
|
||||||
|
| `/api/admin/merge-preview-proxy` | GET | Produktmerge-preview |
|
||||||
|
| `/api/receipt-import-proxy` | POST | Kvittoimport via Mistral AI |
|
||||||
|
| `/api/user-products` | GET, POST, DELETE | Användarspecifika produkter |
|
||||||
|
|
||||||
|
### Autentisering (Auth.js v5)
|
||||||
|
|
||||||
|
- `auth.ts` — NextAuth-konfiguration med Credentials provider
|
||||||
|
- `middleware.ts` — Skyddar alla routes utom `/login`, `/register` och `/api/auth`
|
||||||
|
- `lib/auth-headers.ts` — `getAuthHeaders()` hämtar Bearer-token från session (server-side)
|
||||||
|
- `lib/api.ts` — `fetchJson()` lägger automatiskt till auth-headers server-side, redirectar till `/login` vid 401
|
||||||
|
|
||||||
### Frontend utbyggbarhet
|
### Frontend utbyggbarhet
|
||||||
|
|
||||||
@@ -115,24 +139,41 @@ docker exec recipe-db mariadb -uroot -p"LÖSENORD" recipe_app -e "SHOW TABLES;"
|
|||||||
- **Språk:** TypeScript 5.4.5
|
- **Språk:** TypeScript 5.4.5
|
||||||
- **Databas:** MariaDB 11 (via Prisma 6.12.0 ORM)
|
- **Databas:** MariaDB 11 (via Prisma 6.12.0 ORM)
|
||||||
- **API:** REST, validering med class-validator
|
- **API:** REST, validering med class-validator
|
||||||
|
- **Autentisering:** JWT (7 dagars token), JwtAuthGuard skyddar alla routes, `@Public()` dekorator för öppna endpoints
|
||||||
- **Felhantering:** GlobalExceptionFilter (svenska felmeddelanden)
|
- **Felhantering:** GlobalExceptionFilter (svenska felmeddelanden)
|
||||||
- **Hälsokontroll:** /health endpoints
|
- **Hälsokontroll:** /health endpoints
|
||||||
- **Bygg:** `nest build`, körs i Docker-container
|
- **Bygg:** `nest build`, körs i Docker-container
|
||||||
|
|
||||||
### Backend-moduler och strukturen läsa
|
### Backend-moduler och strukturen
|
||||||
|
|
||||||
```
|
```
|
||||||
backend/src/
|
backend/src/
|
||||||
├── app.module.ts # Root module
|
├── app.module.ts # Root module
|
||||||
├── main.ts # Startpunkt (port 8080)
|
├── main.ts # Startpunkt (port 8080, global prefix "api")
|
||||||
|
├── auth/
|
||||||
|
│ ├── auth.controller.ts # POST /api/auth/login
|
||||||
|
│ ├── auth.service.ts # validateUser, login (JWT-signering)
|
||||||
|
│ ├── auth.module.ts
|
||||||
|
│ ├── jwt.strategy.ts # Passport JWT-strategi
|
||||||
|
│ ├── jwt-auth.guard.ts # Global guard (skyddar allt utom @Public)
|
||||||
|
│ └── decorators/
|
||||||
|
│ └── public.decorator.ts # @Public() – markerar öppen endpoint
|
||||||
|
├── users/
|
||||||
|
│ ├── users.controller.ts # GET/PATCH /api/users/me
|
||||||
|
│ ├── users.service.ts # findByUsername, create, updateProfile
|
||||||
|
│ └── users.module.ts
|
||||||
|
├── categories/
|
||||||
|
│ ├── categories.controller.ts # GET /api/categories, GET /api/categories/tree (@Public)
|
||||||
|
│ ├── categories.service.ts # findAll (flat), findTree (hierarkisk)
|
||||||
|
│ └── categories.module.ts
|
||||||
├── common/
|
├── common/
|
||||||
│ ├── filters/
|
│ ├── filters/
|
||||||
│ │ └── global-exception.filter.ts # Centraliserad felhantering
|
│ │ └── global-exception.filter.ts # Centraliserad felhantering
|
||||||
│ └── utils/
|
│ └── utils/
|
||||||
│ └── normalize-name.ts # Namnormalisering
|
│ └── normalize-name.ts # Namnormalisering
|
||||||
├── health/
|
├── health/
|
||||||
│ ├── health.controller.ts # GET /health, /health/db
|
│ ├── health.controller.ts # GET /health, /health/db (@Public)
|
||||||
│ ├── health.service.ts # Hälsotillstånd-logik
|
│ ├── health.service.ts
|
||||||
│ └── health.module.ts
|
│ └── health.module.ts
|
||||||
├── inventory/
|
├── inventory/
|
||||||
│ ├── inventory.controller.ts # CRUD endpoints
|
│ ├── inventory.controller.ts # CRUD endpoints
|
||||||
@@ -146,8 +187,8 @@ backend/src/
|
|||||||
│ ├── prisma.service.ts # PrismaClient wrapper
|
│ ├── prisma.service.ts # PrismaClient wrapper
|
||||||
│ └── prisma.module.ts
|
│ └── prisma.module.ts
|
||||||
├── products/
|
├── products/
|
||||||
│ ├── products.controller.ts # CRUD, merge, duplicates
|
│ ├── products.controller.ts # CRUD, merge, duplicates, reset-all
|
||||||
│ ├── products.service.ts # Produktlogik
|
│ ├── products.service.ts # Produktlogik inkl. resetAll()
|
||||||
│ ├── products.module.ts
|
│ ├── products.module.ts
|
||||||
│ └── dto/
|
│ └── dto/
|
||||||
│ ├── create-product.dto.ts
|
│ ├── create-product.dto.ts
|
||||||
@@ -291,9 +332,23 @@ GET /api/products/merge-preview Förhandsgranska merge
|
|||||||
POST /api/products/merge Slå ihop två produkter
|
POST /api/products/merge Slå ihop två produkter
|
||||||
PATCH /api/products/:id/canonical-name Uppdatera canonical name
|
PATCH /api/products/:id/canonical-name Uppdatera canonical name
|
||||||
POST /api/products/backfill-canonical Backfill canonical names (admin)
|
POST /api/products/backfill-canonical Backfill canonical names (admin)
|
||||||
|
POST /api/products/reset-all Rensa all produktdata (admin)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🛀 Baslager-endpoints
|
### Kategori-endpoints
|
||||||
|
```
|
||||||
|
GET /api/categories Flat lista av alla kategorier (@Public)
|
||||||
|
GET /api/categories/tree Hierarkiskt träd (@Public)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Användar-endpoints
|
||||||
|
```
|
||||||
|
POST /api/auth/login Logga in, returnerar JWT (@Public)
|
||||||
|
GET /api/users/me Hämta inloggad användares profil
|
||||||
|
PATCH /api/users/me Uppdatera firstName, lastName, email
|
||||||
|
```
|
||||||
|
|
||||||
|
### Baslager-endpoints
|
||||||
```
|
```
|
||||||
GET /api/pantry Lista alla baslagerartiklar (inkl. produktinfo)
|
GET /api/pantry Lista alla baslagerartiklar (inkl. produktinfo)
|
||||||
POST /api/pantry Lägg till produkt i baslagret
|
POST /api/pantry Lägg till produkt i baslagret
|
||||||
@@ -304,6 +359,36 @@ DELETE /api/pantry/:id Ta bort produkt från baslagret
|
|||||||
|
|
||||||
## Datamodell (Prisma ORM)
|
## Datamodell (Prisma ORM)
|
||||||
|
|
||||||
|
### User
|
||||||
|
```prisma
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
username String @unique
|
||||||
|
email String @unique
|
||||||
|
firstName String?
|
||||||
|
lastName String?
|
||||||
|
passwordHash String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Category
|
||||||
|
```prisma
|
||||||
|
model Category {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
parentId Int?
|
||||||
|
parent Category? @relation("CategoryTree", ...) # Förälder (null = toppnivå)
|
||||||
|
children Category[] @relation("CategoryTree") # Underkategorier
|
||||||
|
products Product[]
|
||||||
|
|
||||||
|
@@unique([name, parentId])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Hierarkin har 3 nivåer: **Huvudkategori → Underkategori → Typ**
|
||||||
|
Exempelträd: `Mejeri, ost & ägg → Mjölk → Laktosfri mjölk`
|
||||||
|
|
||||||
### Product
|
### Product
|
||||||
```prisma
|
```prisma
|
||||||
model Product {
|
model Product {
|
||||||
@@ -311,14 +396,20 @@ model Product {
|
|||||||
name String # Visningsnamn
|
name String # Visningsnamn
|
||||||
normalizedName String @unique # Normaliserat namn (lowercase, utan skiljetecken)
|
normalizedName String @unique # Normaliserat namn (lowercase, utan skiljetecken)
|
||||||
canonicalName String? # Canonical namn för receptmatchning
|
canonicalName String? # Canonical namn för receptmatchning
|
||||||
category String? # Produktkategori
|
category String? # Fritext-kategori (äldre fält, ersätts av categoryRef)
|
||||||
isActive Boolean @default(true) # Soft-delete flag
|
subcategory String? # Fritext-underkategori (äldre fält)
|
||||||
deletedAt DateTime? # Tidpunkt för soft-delete
|
brand String? # Varumärke
|
||||||
|
categoryId Int? # FK till Category (ny hierarki)
|
||||||
|
categoryRef Category? # Relation till Category
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
deletedAt DateTime?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
inventoryItems InventoryItem[]
|
inventoryItems InventoryItem[]
|
||||||
recipeIngredients RecipeIngredient[]
|
recipeIngredients RecipeIngredient[]
|
||||||
|
tags ProductTag[]
|
||||||
|
nutrition Nutrition?
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user