272 lines
15 KiB
Markdown
272 lines
15 KiB
Markdown
# Nästa steg
|
||
|
||
> Förslag på vad vi kan ta tag i nästa gång vi öppnar projektet.
|
||
> Se [README.md](README.md) för funktionsöversikt och [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk detaljerinformation.
|
||
> Se [AI-FUNKTIONER.md](AI-FUNKTIONER.md) för planerade AI-funktioner.
|
||
|
||
---
|
||
|
||
## Status — senast genomgånget: 2026-04-21
|
||
|
||
| Funktion | Status |
|
||
|---|---|
|
||
| Inventorie (CRUD, konsumtion, historik) | ✅ Klart |
|
||
| Recept (skapa, visa, importera, matchning) | ✅ Klart |
|
||
| Receptredigering och borttagning (JWT-autentisering) | ✅ Klart |
|
||
| Snabbimport (URL/PDF/bild/ICA) | ✅ Klart |
|
||
| Kvittoimport (Mistral AI, OCR, alias) | ✅ Klart |
|
||
| Matplanering (veckovy, inköpslista) | ✅ Klart |
|
||
| Matplan — portionsjustering per dag | ✅ Klart |
|
||
| Matplan — inventariejämförelse (backend) | ✅ Klart |
|
||
| Matplan — inventariejämförelse (frontend-vy) | ✅ Klart (✅/⚠️/❌ integrerat i inköpslistan) |
|
||
| Baslager (lista, lägg till, ta bort) | ✅ Klart |
|
||
| Admin: Produkter (edit, merge, duplicate, restore, reset) | ✅ Klart |
|
||
| Admin: Bulk-kategorisering | ✅ Klart |
|
||
| Receptredigering (frontend UX) | ✅ Klart |
|
||
| Receptbilder (upload URL) | ✅ Klart |
|
||
| Autentisering (JWT, Auth.js v5, User-modell) | ✅ Klart |
|
||
| Klient-side JWT via useAuthFetch() | ✅ Klart |
|
||
| SessionProvider i root layout | ✅ Klart |
|
||
| Användarprofil (firstName, lastName, email) | ✅ Klart |
|
||
| Produktkategorier — hierarkisk struktur (3 nivåer) | ✅ Klart |
|
||
| Kategori-seed (seed_all.sql, komplett träd, enda sanningskällan) | ✅ Klart |
|
||
| Kategoritilldelning i admin-UI | ✅ Klart |
|
||
| Taggning av produkter | ✅ Klart |
|
||
| Näringsvärden på produkter | ✅ Klart (schema + API) |
|
||
| Seed produktdata med kategoritilldelning | ✅ Klart (seed_all.sql) |
|
||
| Användarspecifika produkter (UserProduct) | ⚠️ Schema klart, UI basic |
|
||
| Användarroller (user / admin) | ✅ Klart |
|
||
| Användarhantering i admin-UI | ✅ Klart |
|
||
| Profilsida med flikar (Min profil / Användare / Databas med undertabbar) | ✅ Klart |
|
||
| Teknisk skuld — oanvända InventoryItem-fält | ✅ Klart (migration 20260418) |
|
||
| Teknisk skuld — redirect-routes städade | ✅ Klart |
|
||
| Premium-plan (isPremium på User, Free/Paid-dropdown) | ✅ Klart |
|
||
| AI-modul (AiService, Mistral-kategorisering, fallback) | ✅ Klart |
|
||
| Admin: AI-kategorisering per produkt ("Fråga AI") | ✅ Klart |
|
||
| Admin: AI-bulk-kategorisering av okategoriserade produkter | ✅ Klart |
|
||
| Produktstatus (pending / active / rejected) | ✅ Klart |
|
||
| Admin: Väntande produktförslag (pending-sida) | ✅ Klart |
|
||
| Kvittoimport — AI-kategorisuggestion för premium-användare | ✅ Klart |
|
||
| Säkerhetshuvuden — Caddy (globala headers) | ✅ Klart |
|
||
| Säkerhetshuvuden — Next.js CSP | ✅ Klart |
|
||
| Säkerhetshuvuden — NestJS Helmet (backup) | ✅ Klart (aktiveras vid nästa rebuild) |
|
||
| Buggfix: "Vad behöver jag köpa" — flimmer och scroll | ✅ Klart |
|
||
| Startsida — produktadmin-länk borttagen | ✅ Klart |
|
||
| Avancerad AI-integration (veckoplanering, receptförslag) | ❌ Planerad |
|
||
| EAN-skanning via Open Food Facts API | ❌ Planerad |
|
||
|
||
---
|
||
|
||
## Notering 2026-04-20
|
||
|
||
## Notering 2026-04-21
|
||
|
||
- Rollskydd i frontend (middleware) och backend (admin på endpoints) är nu heltäckande.
|
||
- Rate limiting, CORS-låsning och JWT_SECRET-krav är på plats.
|
||
- Konsol-loggar borttagna ur backend.
|
||
- Kvar: Kryptering av PII (e-post, namn i databasen), automatisering av seed_all.sql vid fresh install.
|
||
|
||
Den senaste versionen av `db/seeds/seed_all.sql` är pushad till utvecklingsmiljön, men har **ännu inte körts** på databasen. Nästa gång vi vill uppdatera kategoriträdet måste seed-scriptet köras manuellt:
|
||
|
||
```bash
|
||
docker exec -i recipe-db mariadb -uroot -p"$DB_PASS" recipe_app < db/seeds/seed_all.sql
|
||
```
|
||
|
||
Se även TEKNISK_BESKRIVNING.md för detaljer om seed-processen.
|
||
|
||
---
|
||
|
||
## Vägen till produktlansering
|
||
|
||
- [Säkerhet & Data](produktlansering.md#1-säkerhet--data)
|
||
- [DevOps & Stabilitet](produktlansering.md#2-devops--stabilitet)
|
||
- [Testning & Kvalitet](produktlansering.md#3-testning--kvalitet)
|
||
- [Funktionella förbättringar](produktlansering.md#4-funktionella-förbättringar)
|
||
- [Risker & Flaskhalsar](produktlansering.md#5-risker--flaskhalsar)
|
||
|
||
---
|
||
|
||
## Prioriterade förbättringar
|
||
|
||
### 1. ICA-skraparen missar receptbild
|
||
**Problem:** Vid import av ICA-recept via URL hämtas inte receptbilden korrekt.
|
||
|
||
- Felsök parsern i `recipe-document-converter/src/parser.ts` — kontrollera att ICA:s bild-selektor fortfarande matchar sidans HTML-struktur
|
||
- ICA kan ha ändrat klassnamn eller lazy-load-mekanism sedan skraparen skrevs
|
||
- Verifiera med ett konkret ICA-recept-URL och logga vad `imageUrl` returnerar
|
||
|
||
### 2. Seed-data ✅
|
||
**Klart.** `db/seeds/seed_all.sql` är den enda sanningskällan för kategorier och produkter.
|
||
|
||
Filen:
|
||
- TRUNCATEar `Category` och nollställer `Product.categoryId`
|
||
- Bygger hela kategoriträdet (nivå 1–3) utan dubbletter
|
||
- Insertar ~190 produkter (INSERT IGNORE)
|
||
- Kopplar produkterna till rätt kategori via JOIN-subqueries
|
||
|
||
Körs manuellt: `docker exec -i recipe-db mariadb -uroot -p"$DB_PASS" recipe_app < db/seeds/seed_all.sql`
|
||
|
||
### 2. Refactor: seed_all.sql som del av fresh install
|
||
**Mål:** Fresh install ska inte kräva ett manuellt seed-steg.
|
||
|
||
Nuläge:
|
||
- `db/init/001-init.sql` (MariaDB auto-init) skapar bara en bootstrap-check-tabell
|
||
- Prisma-migrationen `20260417310000_add_category_tree` seedar kategorier en gång, men seed_all.sql skriver över dem
|
||
- `seed_all.sql` körs **inte** automatiskt vid deploy eller fresh install
|
||
|
||
Alternativ:
|
||
- **Alt A:** Flytta seed_all.sql-logiken till `db/init/001-init.sql` — körs automatiskt av MariaDB-containern vid första start
|
||
- **Alt B:** Skapa en `prisma/seed.ts` som körs via `prisma db seed` — standard NestJS/Prisma-mönster, kan inkluderas i deploy.sh
|
||
- **Alt C:** Lägg till `seed_all.sql` som ett steg i `deploy.sh` (körs alltid, idempotent p.g.a. TRUNCATE+INSERT IGNORE)
|
||
|
||
Oavsett alternativ bör de gamla kategori-INSERTs i `20260417310000_add_category_tree/migration.sql` tas bort eller kommenteras ur för att undvika förvirring.
|
||
|
||
### 2. Användarroller och full användarhantering ✅
|
||
**Klart.**
|
||
|
||
Systemet har nu fullständig rollbaserad åtkomstkontroll och ett komplett användarhanteringsgränssnitt inbyggt i profilsidan.
|
||
|
||
**Rollsystemet:**
|
||
- **Prisma-migration** (`20260418100000_add_user_role`) — fältet `role String @default("user")` lades till på `User`-modellen
|
||
- **`@Roles('admin')`-dekoratorn** (`auth/decorators/roles.decorator.ts`) — använder `SetMetadata` för att markera endpoints
|
||
- **`RolesGuard`** (`auth/roles.guard.ts`) — registrerad globalt som `APP_GUARD`; läser rollmetadata, kastar 403 om rätt roll saknas
|
||
- **JWT inkluderar nu `role`** — `jwt.strategy.ts` returnerar `{userId, username, role}`, `auth.service.ts` signerar med `role` i payload
|
||
- **Bootstrap-användare** (`users/admin-bootstrap.service.ts`) — `OnApplicationBootstrap` skapar/uppdaterar Nadmin, Padmin, user1 och user2 vid varje uppstart via miljövariabler
|
||
- **Skyddade produkt-endpoints** — `@Roles('admin')` på `merge`, `delete`, `restore`, `reset-all`, `bulk-update`, `backfill-canonical` i `products.controller.ts`
|
||
|
||
**Backend-endpoints för användarhantering (alla kräver admin-roll):**
|
||
- `GET /api/users` — lista alla användare
|
||
- `PATCH /api/users/:id/role` — ändra roll
|
||
- `POST /api/users` — skapa ny användare (validering: unikt användarnamn och e-post)
|
||
- `DELETE /api/users/:id` — ta bort användare (skyddad: kan inte ta bort sig själv)
|
||
- `POST /api/users/:id/reset-password` — genererar tillfälligt lösenord, returnerar meddelandetext + lösenord
|
||
- `PATCH /api/users/:id/email` — uppdatera e-postadress
|
||
|
||
**Profilsidan med flikar (`/profil`):**
|
||
- `?tab=profil` — Min profil (alla användare)
|
||
- `?tab=anvandare` — Användare (enbart admin): skapa, ta bort, rollbyte, e-postbyte, lösenordsåterställning med kopierings-modal
|
||
- `?tab=databas` — Databas (enbart admin): produktadmin, nu med undertabbar:
|
||
- **Varor:** lista och redigera aktiva produkter
|
||
- **Skapa / Slå ihop:** skapa ny produkt, återställ produktdatabas, slå ihop dubbletter
|
||
- **Papperskorg:** visa mjukraderade produkter, återställ eller radera permanent
|
||
- `/admin/users` omdirigerar till `/profil?tab=anvandare`
|
||
- Navigeringslänken "👥 Användare" går direkt till `/profil?tab=anvandare`
|
||
|
||
### 3. Matplan-vy (frontend-polish) ✅
|
||
**Klart.**
|
||
|
||
Inköpslistan och inventariejämförelsen är sammanslagna till en enhetlig vy med tre statusnivåer:
|
||
- ❌ Saknas helt — visar hur mycket som behövs köpas
|
||
- ⚠️ Delvis hemma — visar hur mycket mer som behövs + vad som finns
|
||
- ✅ Finns hemma — markeras nedtonat, ingen köpindikering
|
||
|
||
Listan sorteras automatiskt: saknade ingredienser överst, hemma-ingredienser underst. En sammanfattningsrad visar totalt antal per statuskategori.
|
||
|
||
### 4. Teknisk skuld (underhåll)
|
||
**Mål:** Minska komplexitet och risk för buggar.
|
||
|
||
#### A. CanonicalNameForm och NameForm ✅
|
||
Filerna var redan borttagna — inga aktiva imports hittades. Inget att göra.
|
||
|
||
#### B. Oanvända fält på InventoryItem ✅
|
||
Följande 6 fält togs bort via Prisma-migration (`20260418000000_remove_unused_inventory_fields`):
|
||
`priority`, `shelfNote`, `isOnSale`, `priceLevel`, `proteinType`, `isLeftover`
|
||
- Schema, DTOs (create + update), service och frontend-typen är städade.
|
||
- `opened` och `suitableFor` behölls — de används i UI.
|
||
|
||
#### C. Validering av DTO:er i admin-actions ✅
|
||
Redan implementerat — `trim()` + max 100 tecken på alla fält i `actions.ts`. Inget att göra.
|
||
|
||
#### D. Routing-städning för kvitto och import ✅
|
||
`frontend/app/kvitto/page.tsx` och `frontend/app/recipes/import/page.tsx` är borttagna.
|
||
`/import` täcker båda use-cases via flikar.
|
||
|
||
#### E. Seed-data i versionshantering ✅
|
||
`data/matvaror_sverige.csv` och `data/seed_products.sql` behålls i git för reproducerbarhet. Inga ändringar i `.gitignore`.
|
||
|
||
### 5. Avancerad AI-integration
|
||
**Mål:** Smarta receptförslag och veckoplanering baserat på inventarie och kampanjdata.
|
||
|
||
AI-infrastrukturen är nu på plats (`AiService`, `mistral-small-2603`, premium-plan). Kategorisering för produkter och kvittoimport är implementerat. Nästa steg:
|
||
|
||
- **"Vad ska jag laga idag?"** — Receptförslag baserat på vad som finns i inventariet; kan byggas direkt ovanpå befintliga inventory- och recipe-endpoints
|
||
- **Veckoplanering med AI** — Generera ett veckoschemat baserat på inventarie, preferenser och ev. kampanjpriser (kräver extern datakälla)
|
||
- Kräver: tydlig API-design, kostnadskontroll och modellval per use-case
|
||
|
||
---
|
||
|
||
## AI-loggning och audit
|
||
|
||
- detta behöver ske innan nästa Ai-integration (receptförslag, veckoplanering) implementeras, för att säkerställa att vi har en robust lösning på plats för att spåra och analysera AI-anrop och resultat
|
||
- Utforska och utveckla lösning för loggning och audit av AI-input och output
|
||
- Syfte: möjliggöra spårbarhet, felsökning och kvalitetssäkring av AI-funktioner
|
||
- Implementera loggning på ett sätt som inte lagrar personuppgifter eller känslig data i klartext
|
||
- Utvärdera om loggar ska lagras i separat databas eller fil, och hur de kan användas för analys och förbättring
|
||
|
||
---
|
||
|
||
## Enhetsstöd och konvertering
|
||
|
||
- Se över om vi behöver utöka antalet stödda enhetstyper (t.ex. fler volym- eller portionsenheter)
|
||
- Utveckla och förbättra normaliseringen i `recipeservice.normalizeUnit`
|
||
- Se över och testa konverteringslogiken för enheter (t.ex. edge cases, felhantering)
|
||
- Fundera på om AI kan användas för att tolka, normalisera eller konvertera enheter och mängder smartare
|
||
|
||
---
|
||
|
||
## Enhetstester
|
||
|
||
Jest + ts-jest är uppsatt. Tester finns för:
|
||
- `normalize-name.ts` — 10 tester
|
||
- `base.parser.ts` (`parseIngredientLine`) — 12 tester
|
||
- `recipes.service.ts` (`normalizeUnit`, `convertUnit`) — 17 tester
|
||
|
||
Kör med `npm test` i `backend/`.
|
||
|
||
---
|
||
|
||
## Planerade funktioner
|
||
|
||
### Säkerhet — kryptering av känslig användardata
|
||
**Mål:** Kryptera känsliga fält (t.ex. e-postadresser, personuppgifter) i databasen med en hybridlösning.
|
||
|
||
Förslag på approach:
|
||
- **AES-256-GCM** för symmetrisk kryptering av data i vila (kolumnnivå i MariaDB eller på applikationsnivå i Prisma middleware)
|
||
- **Nyckelhantering** via miljövariabel eller extern nyckelhanteringstjänst (t.ex. HashiCorp Vault)
|
||
- Kräver genomtänkt migrering av befintlig data och påverkar sökbarhet (krypterade fält kan inte indexeras direkt)
|
||
|
||
### EAN-skanning — import via streckkod (Open Food Facts)
|
||
**Mål:** Låt användaren skanna eller skriva in en EAN-streckkod och få produktinformation (namn, kategori, näringsvärden) ifyllt automatiskt.
|
||
|
||
Open Food Facts är ett öppet, gratis API utan API-nyckel för grundläggande användning.
|
||
|
||
**Exempelanrop (EAN 7310960010016):**
|
||
```
|
||
GET https://world.openfoodfacts.org/api/v0/product/{ean}.json
|
||
```
|
||
Svaret innehåller:
|
||
- `product.product_name` — produktnamn
|
||
- `product.categories` — kategoristräng (kommaseparerad)
|
||
- `product.nutriments` — näringsvärden (energi, protein, fett, kolhydrater, m.fl.)
|
||
- `product.code` — EAN-koden bekräftad
|
||
|
||
**Förslag på implementation:**
|
||
|
||
1. **Backend-endpoint** — `GET /api/products/ean/:code` — slår upp i Open Food Facts, returnerar normaliserat produktförslag (namn, kategori, näringsvärden) utan att spara
|
||
2. **Frontend-import** — nytt fält i produktformuläret (eller separat import-flöde): ange EAN → knapp "Hämta" → formuläret förifylls
|
||
3. **Alternativt:** kamerabaserad skanning i mobil webbläsare via `BarcodeDetector` API (eller externt bibliotek som `zxing-js`)
|
||
4. **Kategorimappning** — Open Food Facts-kategorier är på engelska/franska; en mappningstabell eller fuzzy-matchning mot systemets kategorier behövs
|
||
5. **Näringsdata** — om `product.nutriments` finns kan det sparas direkt i Prisma-modellens näringsvärdesfält (redan schema-klara)
|
||
|
||
**Begränsningar att ha i åtanke:**
|
||
- Produkttäckning varierar — svenska dagligvaror är välrepresenterade men inte allt finns
|
||
- Produktnamn är ofta på originalspråk; kan behöva redigeras
|
||
- Rate limiting: för hög användning rekommenderar Open Food Facts att kontakta dem
|
||
|
||
### Byta @Post till @Patch på backfill-canonical
|
||
Konsekvenser: ⚠️ Bryta ändring.
|
||
|
||
Frontend anropar /api/products/backfill-canonical med POST från AdminProductList.
|
||
Caddy och Next.js API-routes kan ha hårdkodade metoder.
|
||
Kräver synkroniserad ändring i backend + alla anropsställen i frontend, annars slutar funktionen fungera.
|