Add comprehensive documentation for Flutter frontend migration and backend review
Test Suite / test (24.15.0) (push) Has been cancelled

- Introduced user guide for Flutter frontend in README.md, detailing user flows and recent improvements.
- Created next steps roadmap for Flutter migration in next_steps_flutter.md, outlining current tasks and priorities.
- Developed technical description for Flutter frontend in teknisk_beskrivning_flutter.md, covering architecture and security status.
- Removed outdated migration documentation for Prisma P3009 and added recovery steps for failed migrations in migrering-MSI.md.
- Established a release checklist for product launches in produktlansering.md, ensuring security and stability measures are met.
- Formulated a systematic backend review and optimization plan in review_backend.md, focusing on reducing complexity and improving performance.
This commit is contained in:
Nils-Johan Gynther
2026-05-10 00:28:59 +02:00
parent 1709bb1dad
commit ca8987d9e4
14 changed files with 10 additions and 10 deletions
@@ -0,0 +1,820 @@
# Session 2026-05-06: Refaktor och user-scoped AI
Denna session har genomfört:
- **User-scoped AI-fallback:** AI-förslag för ingrediens- och kategorimatchning är nu individuellt aktiverbara per användare (premium).
- **Admin-toggles:** Backend och UI har stöd för att admin kan slå på/av AI per användare.
- **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.
- **Lessons learned:**
- Nullable propagation i Prisma och DTO:er kräver noggrannhet.
- Fallback-first AI och tydlig separation av analyskontrakt ger robustare flöden.
- User-scoped features kräver ownerId/userId-filter i all logik.
- Manuella migrationer kan krävas vid DB-problem (se migrering-MSI.md).
Se även:
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk genomgång.
- [AI-FUNKTIONER.md](_archive/microservice-ai/AI-FUNKTIONER.md) för AI-översikt.
# Session 2026-05-09: Simplified Matching Logic (Consolidation Phase 3)
Denna session har genomfört den tredje omarbetningsfasen som konsoliderar receipt-import matching-flödet:
## Genomförda förbättringar
### 1. Unified Matcher (`matchAndEnrichReceiptItem`)
- **Tidigare:** Matching var splittrad mellan `matchProducts()` (alias + word-match) och `enrichWithAiCategories()` (~850 rader).
- **Nu:** En central metod som gör allt i explicit ordning:
1. **Alias lookup** — certifierad match från ReceiptAlias
2. **Word-match** — fuzzy produktmatchning med scoring
3. **Categorization** — regel-baserad → AI (fallback) → guards → hard overrides
### 2. Improved Context Management (`prepareMatchingContext`)
- Alla data (aliases, produkter, unit mappings, categories) hämtas **en gång** per receipt
- Parallell loading med `Promise.all()` — högre performance än tidigare sekventiell loading
- Context passeras till alla items för effektiv återanvändning
### 3. Better Decision Logging (`enrichCategoryForItem`)
- Structured trace med steg-för-steg-loggning:
- ✓ Rule-based hits with path name
- ✓ AI suggestions
- ⚠️ Guard remaps (contradiction resolution)
- ⚠️ Hard overrides (special cases)
- ✅ Final decision with confidence level
- Debug-träd med alla decision points
- Kan aktiveras per receipt via `RECEIPT_TRACE_DECISIONS` env-var
### 4. Simplified parseReceipt Flow
```
Before:
parseReceipt() → matchProducts() → enrichWithAiCategories()
After:
parseReceipt() → prepareContext() → matchAndEnrichReceiptItem() (per item)
```
## Technical Details
**Nya metoder:**
- `matchAndEnrichReceiptItem()` — unified matching pipeline per item
- `prepareMatchingContext()` — one-time context preparation
- `enrichCategoryForItem()` — consolidated categorization logic
- `findWordMatchWithScore()` — refactored word matching with explicit score return
**Datatyper:**
- `MatchDecision` interface för strukturerad result (i `dto/match-decision.ts`)
**Build Status:**
- ✅ Full TypeScript compilation successful
- ✅ No breaking changes to existing API contracts
## Cleanup Pending
De gamla metoderna är fortfarande i koden men kan nu ta bort:
- `private async matchProducts()` — deprecated by unified matcher
- `private async enrichWithAiCategories()` — deprecated by unified matcher
- `private findWordMatch()` — replaced by findWordMatchWithScore()
**Cleanup tasks:**
1. Ta bort `matchProducts()`
2. Ta bort `enrichWithAiCategories()`
3. Ta bort `findWordMatch()` (gammal version)
4. Uppdatera kommentarer/docstrings
5. Kör full test suite för regression detection
Cleanup bör göras i nästa session för att ge tid för monitoring/QA.
# Plan för omarbetning av receptimport
# 2026-05-07: Säkerhets- och deployförbättringar
- **Inventory är nu user-scopad:** Alla inventory-operationer kräver och filtrerar på userId i backend (schema, migration, service, controller, tester).
- **IDOR-skydd för inventory:** Det är nu omöjligt för användare att läsa eller ändra andras inventarieposter. Tester verifierar att åtkomst nekas vid försök till IDOR.
- **.gitignore och deploy-hygien:** backend/dist och backend/tsconfig.tsbuildinfo ignoreras och är ej längre spårade i git. .env och .env.* ignoreras, men .env.example finns och är uppdaterad.
- **CI/CD-härdning:** npm audit och prisma validate körs i pipeline. Alla tester och byggen måste passera.
## Bakgrund
Nuvarande importflöde blandar ihop två olika ansvar:
1. Att importera och spara receptet så troget källan som möjligt.
2. Att matcha receptets ingredienser mot interna produkter.
Det som fungerar bra i dag är:
- webskrapning
- import med text och bild
- presentation av receptbild och text när receptet väl är sparat
Det som fungerar sämre är:
- ingrediensimporten
- tidig produktmatchning i importsteget
- att receptets innehåll förvrängs eller tappar ingredienser om matchning saknas
## Målbild
Importsteget ska bara göra detta:
- hämta receptets titel, bild, beskrivning, instruktioner och ingrediensrader
- strukturera ingrediensraderna så gott det går
- spara receptet även när ingen produktmatchning finns
- låta användaren granska receptet utan att behöva välja produkter
Ett senare analyssteg ska göra detta:
- jämföra receptets ingredienser med inventory och pantry
- avgöra exakt träff, trolig ersättningsvara eller saknad vara
- ge underlag för shoppinglista
- ge underlag för AI-förslag och substitutionsförslag
## Arkitekturprincip
Receptet ska vara källtroget.
Produktmatchning ska vara ett separat lager ovanpå receptet.
Det innebär att vi behöver separera:
- receptets råa ingredienser
- användarens lager och skafferi
- matchnings- och analyslogik
## Föreslagen datamodell
### Nuvarande problem
I dag kräver `RecipeIngredient` i Prisma:
- `productId`
- `quantity`
- `unit`
Det gör att en ingrediens inte kan sparas om vi inte redan vet vilken intern produkt den motsvarar.
### Ny målmodell
Inför två nivåer för ingredienser:
1. Receptets egen ingrediensrad
2. Matchning/analys mot interna produkter
### Förslag A: utöka befintlig `RecipeIngredient`
Det enklaste spåret är att behålla `RecipeIngredient`, men göra den receptcentrisk i stället för produktcentrisk.
Nya eller ändrade fält:
- `rawLine String?`
- `rawName String`
- `productId Int?` i stället för required
- `quantity Decimal?`
- `unit String?`
- `note String?`
- `matchConfidence Float?`
- `matchSource String?` (`heuristic`, `ai`, `manual`)
- `analysisStatus String?` (`unmatched`, `exact`, `substitutable`, `missing`)
Behåll:
- `alternativeProductIds Json?`
- `recipeId`
### Förslag B: dela upp i två tabeller
Mer robust på sikt men större ombyggnad:
- `RecipeIngredient` blir rå ingrediensrad
- `RecipeIngredientMatch` blir separat matchningslager
Första implementationen bör använda Förslag A för lägre risk.
## Rekommenderad genomförandeordning
1. Gör datamodellen tolerant för omatchade ingredienser.
2. Ändra backend så att import och sparning fungerar utan produktmatchning.
3. Förenkla frontendens importgranskning.
4. Lägg till separat analyssteg mot inventory och pantry.
5. Lägg till AI-stöd först när grundflödet är stabilt.
---
# Fil-för-fil-plan
## 1. Databas och Prisma
### Fil: `backend/prisma/schema.prisma`
### Ändringar
- Gör `RecipeIngredient.productId` optional.
- Gör `RecipeIngredient.product` optional relation.
- Gör `quantity` optional.
- Gör `unit` optional.
- Lägg till `rawName`.
- Lägg till `rawLine`.
- Lägg till `matchConfidence`.
- Lägg till `matchSource`.
- Lägg till `analysisStatus`.
### Exempel på målmodell
```prisma
model RecipeIngredient {
id Int @id @default(autoincrement())
recipe Recipe @relation(fields: [recipeId], references: [id])
recipeId Int
product Product? @relation(fields: [productId], references: [id])
productId Int?
rawName String
rawLine String? @db.Text
quantity Decimal? @db.Decimal(10, 2)
unit String?
note String?
alternativeProductIds Json?
matchConfidence Float?
matchSource String?
analysisStatus String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
```
### Leverabler
- Prisma-schema uppdaterat
- ny migration skapad
- Prisma Client regenererad
### Risk
- All kod som i dag antar att `productId`, `quantity` och `unit` alltid finns måste uppdateras
---
## 2. DTO:er för receptskapande
### Fil: `backend/src/recipes/dto/create-recipe.dto.ts`
### Nuvarande problem
DTO:n kräver att varje ingrediens har:
- `productId`
- `quantity`
- `unit`
Det blockerar trogen import.
### Ändringar
- Gör `productId` optional.
- Gör `quantity` optional.
- Gör `unit` optional.
- Lägg till `rawName` som required.
- Lägg till `rawLine` som optional.
- Lägg till `matchConfidence` och `matchSource` som optional om vi vill bevara preliminär heuristik.
### Mål
Backend ska kunna acceptera ett recept där ingrediensen bara är:
- `rawName`
- eventuellt `rawLine`
- eventuellt `quantity`, `unit`, `note`
### Konsekvens
`ArrayMinSize(1)` kan sannolikt vara kvar, men valideringen måste vara råingrediens-baserad i stället för produkt-baserad.
---
## 3. DTO för parse-resultat
### Fil: `backend/src/recipes/dto/parse-markdown.dto.ts`
### Ändringar
Ingen stor strukturändring behövs här om endpointen fortsatt bara tar emot markdown.
### Eventuell komplettering
Om importen senare ska stödja flera källor mer explicit kan ett `sourceType` läggas till:
- `markdown`
- `web`
- `ocr`
Detta är inte nödvändigt i första omgången.
---
## 4. Receptservice: parse ska sluta göra för mycket
### Fil: `backend/src/recipes/recipes.service.ts`
### Nuvarande problem
`parseMarkdown()` gör i dag flera saker:
- anropar importer-api eller lokal parser
- hämtar alla produkter
- kör heuristisk matchning med normalisering och Levenshtein
- returnerar suggestions
Det gör att importsteget implicit blir ett matchningssteg.
### Rekommenderad ändring i etapp 1
Behåll parse men ändra dess ansvar:
- parse ska alltid returnera råingredienser
- suggestions får vara optional och sekundära
- frontend ska inte vara beroende av suggestions för att kunna spara
### Konkret refaktorering
Dela upp i privata metoder:
- `parseRecipeContent(markdown)`
- `buildIngredientSuggestions(parsedIngredients)`
- `createRecipeFromImport(dto, userId)`
### Ändringar i `create()`
- sluta kräva att alla ingredienser har `productId`
- kör `assertProductsActive()` bara för ingredienser som faktiskt har `productId`
- spara `rawName` även när produktmatchning finns
- spara `rawLine` när det finns
### Ny metod att lägga till
- `analyzeRecipeIngredients(recipeId, userId)`
Ansvar för `analyzeRecipeIngredients`:
- ladda receptets råingredienser
- jämför mot inventory
- jämför mot pantry
- returnera analysstatus per ingrediens
- returnera förslag på ersättningsvaror och inköp
### Ny privat logik i service
- `findExactInventoryMatches(...)`
- `findPantryMatches(...)`
- `findSubstituteCandidates(...)`
- `buildShoppingNeeds(...)`
### Viktig designregel
`parseMarkdown()` får gärna returnera suggestions, men `create()` får inte vara beroende av att de finns.
---
## 5. Receptcontroller: separera import från analys
### Fil: `backend/src/recipes/recipes.controller.ts`
### Ändringar
Behåll:
- `POST /recipes/parse-markdown`
- `POST /recipes`
Lägg till:
- `GET /recipes/:id/analysis`
- eventuellt `POST /recipes/:id/rematch`
### Förslag på ansvar
`GET /recipes/:id/analysis`
- returnerar en användarspecifik analys av receptet mot inventory och pantry
`POST /recipes/:id/rematch`
- kör ny matchning om användaren har ändrat inventory/pantry eller om heuristiken förbättrats
### Mål
Importen blir en egen sak.
Analysen blir en egen endpoint.
---
---
## 7. Alias-strategi och matchedVia (2026-05-07)
- Alias-strategin är nu fullt implementerad:
- Backend och Flutter stödjer user-alias och globala alias.
- matchedVia-badge visas i UI (Alias/Ordmatch/AI).
- Användare kan spara egna alias, admins kan spara globala.
- Profilsidan har alias-lista för användare, admin-panelen visar produkt-ID och radstruktur.
- Backend har 3 nya tester för matchedVia, totalt 66 tester gröna.
### Fil: `backend/src/recipes/recipes.module.ts`
### Ändringar
Lägg till eventuella nya providers när analyslogik bryts ut:
- `RecipeAnalysisService`
- eventuellt `RecipeMatchingService`
### Rekommendation
Om analysen växer, bryt ut den från `RecipesService` för att undvika att samma service blir för stor.
---
## 7. Ny analysservice
### Ny fil: `backend/src/recipes/recipe-analysis.service.ts`
### Ansvar
- ta ett sparat recept som indata
- analysera varje ingrediens mot inventory och pantry
- ta fram:
- exakt träff
- täcks av pantry
- möjlig ersättning
- saknas
### Förslag på output
```ts
{
recipeId: 123,
ingredients: [
{
ingredientId: 1,
rawName: "gul lök",
quantity: 1,
unit: "st",
status: "exact_match",
matchedProductId: 456,
matchedProductName: "Gul lök",
source: "inventory"
}
],
summary: {
exactCount: 3,
pantryCount: 2,
substituteCount: 1,
missingCount: 4
},
shoppingListCandidates: []
}
```
### Nytta
Detta blir grunden för:
- "kan jag laga detta?"
- shoppinglista
- AI-förslag
---
## 8. Ny matchningstjänst
### Ny fil: `backend/src/recipes/recipe-matching.service.ts`
### Ansvar
- matcha rå ingrediens mot produktkatalog
- inte spara något själv
- kunna användas av både import och analys
### Matchningsnivåer
1. Exakt match på normaliserat namn
2. Synonym/alias-match
3. Kategori- eller taggmatchning
4. AI-fallback
### Varför separat tjänst
Det gör att heuristik, aliaslogik och AI-stöd inte behöver ligga inbyggt i `parseMarkdown()`.
---
## 9. AI-integration för ingredienstolkning
### Fil: `backend/src/ai/ai.service.ts`
### Nuvarande läge
AI används redan för kategorisering.
### Nytt användningsområde
Lägg till en separat metod, till exempel:
- `suggestIngredientMatches(rawIngredient, candidates)`
- `suggestSubstitutions(rawIngredient, availableProducts)`
### Viktig regel
AI ska inte styra om receptet går att spara.
AI ska bara förbättra analys och förslag efteråt.
### Mål
AI används där osäkerhet är acceptabel:
- substitutionsförslag
- kompletterande matchning
- shoppingförslag
---
## 10. Importskärm i Flutter
### Fil: `flutter/lib/features/recipes/presentation/create_recipe_screen.dart`
### Nuvarande problem
Skärmen gör i dag detta i review-steget:
- checkbox per ingrediens
- produktval via suggestions
- manuellt tillagda ingredienser med produktdropdown
- vid sparning ignoreras ingredienser utan `productId`
Det gör att användaren i praktiken förväntas produktmatcha importen.
### Målbild för skärmen
Importgranskningen ska fokusera på receptet, inte lagerkoppling.
### Ändringar
Ta bort eller dölja i importläget:
- produktdropdownar
- krav på produktmatchning
- manuell ingrediensskapning via produktlista
Behåll eller lägg till:
- justera titel
- justera beskrivning vid behov
- justera servings
- visa importerade ingrediensrader som text
- tillåt enklare rättningar av mängd, enhet, notering och namn
- inkludera/uteslut ingrediensrad
### Ny save-logik
Vid `_save()` ska frontend skicka:
- `rawName`
- `rawLine`
- `quantity`
- `unit`
- `note`
- `productId` bara om en automatisk eller manuell matchning redan finns
### Rekommenderad UI-förändring
Dela upp skärmen i två modes:
- `import review`
- `manual recipe editing`
Import review ska vara avsevärt enklare än manuell receptredigering.
---
## 11. Parsed recipe-domain i Flutter
### Fil: `flutter/lib/features/recipes/domain/parsed_recipe.dart`
### Ändringar
Lägg till fält som tydligare representerar rå importdata:
- `rawLine`
- `matchState`
- `isParsedSafely`
### Rekommendation
`ParsedIngredient` ska behandlas som importdata, inte som nästan färdig `RecipeIngredient`.
### Ny riktning
`suggestions` ska vara optional hjälpdata i UI, inte en förutsättning för att spara.
---
## 12. Flutter-repository för recept
### Fil: `flutter/lib/features/recipes/data/recipe_repository.dart`
### Ändringar
- uppdatera `createRecipe()` så att request body tillåter råingredienser utan `productId`
- lägg till metod:
- `fetchRecipeAnalysis(int id)`
- lägg till eventuell metod:
- `rematchRecipeIngredients(int id)`
### Mål
Frontend ska kunna:
- spara troget importerat recept
- senare hämta analys mot inventory/pantry
---
## 13. API-paths i Flutter
### Fil: `flutter/lib/core/api/api_paths.dart`
### Ändringar
Lägg till:
- `static String analysis(int id) => '/recipes/$id/analysis';`
- eventuellt `static String rematch(int id) => '/recipes/$id/rematch';`
---
## 14. Receptdomän i Flutter
### Fil: `flutter/lib/features/recipes/domain/recipe.dart`
### Nuvarande problem
Nuvarande domän ser sannolikt ingredienser som produktkopplade objekt.
### Ändringar
Gör att `RecipeIngredient` klarar:
- `productId == null`
- `productName == null`
- `rawName` finns alltid
- `quantity` och `unit` kan vara null eller tomma
### Viktigt
UI för receptdetalj ska kunna visa rå ingredienstext även när ingen produktmatchning finns.
---
## 15. Receptdetaljskärm i Flutter
### Fil: `flutter/lib/features/recipes/presentation/recipe_detail_screen.dart`
### Ändringar
Ingredienslistan ska rendera enligt följande prioritet:
1. `rawName` och råa fält
2. matchat produktnamn om det finns
### Exempel
Om produktmatchning saknas ska användaren ändå se:
- `2 msk olivolja`
- `1 gul lök`
- `400 g krossade tomater`
### Lägg till ny sektion
- "Har du hemma?"
Den sektionen ska bygga på `GET /recipes/:id/analysis` i stället för att anta att receptingredienser redan är perfekt produktmatchade.
---
## 16. Ny analysdomän i Flutter
### Ny fil: `flutter/lib/features/recipes/domain/recipe_analysis.dart`
### Ansvar
Representera analysresultat från backend.
### Förslag på typer
- `RecipeAnalysis`
- `RecipeIngredientAnalysis`
- `RecipeIngredientAvailabilityStatus`
### Statusvärden
- `exactMatch`
- `coveredByPantry`
- `substitutable`
- `missing`
---
## 17. Ny provider för analys
### Fil: `flutter/lib/features/recipes/data/recipe_providers.dart`
### Ändringar
Lägg till provider:
- `recipeAnalysisProvider`
### Ansvar
- ladda analys för ett recept
- kunna invalidateras när inventory eller pantry ändras
---
## 18. Manual recipe editing ska vara separat
### Fil: `flutter/lib/features/recipes/presentation/recipe_edit_screen.dart`
### Rekommendation
Behåll denna skärm för riktig redigering av receptets struktur.
### Viktig skillnad mot import
- import review = bevara originalet
- recipe edit = medveten redigering av receptet
### Praktisk åtgärd
Undvik att återanvända fullständig edit-UI direkt i importflödet.
---
## 19. Alias- och produktmatchning
### Relevanta filer
- `backend/src/receipt-import/...`
- `backend/src/products/...`
- `backend/src/recipes/recipes.service.ts`
### Åtgärd
Flytta all generell ingrediensmatchning till en central tjänst i stället för att ha den dold i `parseMarkdown()`.
### Mål
Samma logik ska kunna användas av:
- receptimport
- analys mot inventory/pantry
- AI-förslag
- eventuellt framtida shoppinglista
---
## 20. Migration av befintliga data
### Vad behöver hanteras
Befintliga recept har ingredienser som redan är produktkopplade.
### Strategi
- behåll gamla värden
- fyll `rawName` med bästa tillgängliga namn från produkt eller befintlig text
- sätt `matchSource = 'legacy'` för migrerade rader
- sätt `analysisStatus = null` initialt
### Mål
Gamla recept fortsätter fungera utan att behöva byggas om manuellt.
---
# Implementationsfaser
## Fas 1: Gör receptingredienser omatchningsbara
### Filer
- `backend/prisma/schema.prisma`
- `backend/src/recipes/dto/create-recipe.dto.ts`
- `backend/src/recipes/recipes.service.ts`
- `flutter/lib/features/recipes/data/recipe_repository.dart`
- `flutter/lib/features/recipes/domain/recipe.dart`
### Resultat
Ett recept kan sparas även när inga ingredienser är produktmatchade.
---
## Fas 2: Förenkla importgranskningen
### Filer
- `flutter/lib/features/recipes/presentation/create_recipe_screen.dart`
- `flutter/lib/features/recipes/domain/parsed_recipe.dart`
### Resultat
Användaren granskar receptet som recept, inte som lagerobjekt.
---
## Fas 3: Lägg till analys mot inventory/pantry
### Filer
- `backend/src/recipes/recipe-analysis.service.ts`
- `backend/src/recipes/recipes.controller.ts`
- `backend/src/recipes/recipes.module.ts`
- `flutter/lib/core/api/api_paths.dart`
- `flutter/lib/features/recipes/domain/recipe_analysis.dart`
- `flutter/lib/features/recipes/data/recipe_repository.dart`
- `flutter/lib/features/recipes/data/recipe_providers.dart`
- `flutter/lib/features/recipes/presentation/recipe_detail_screen.dart`
### Resultat
Appen kan svara på:
- vad har jag exakt
- vad täcks av pantry
- vad kan ersättas
- vad saknas
---
## Fas 4: Förbättra matchning och AI
### Filer
- `backend/src/recipes/recipe-matching.service.ts`
- `backend/src/ai/ai.service.ts`
- eventuellt fler UI-filer för ersättningsförslag och shoppinglista
### Resultat
AI blir en förbättrare, inte ett krav för importen.
---
# Beslut som bör tas innan implementation
## 1. Ska `RecipeIngredient` byggas om eller delas upp?
Rekommendation:
- börja med att bygga om `RecipeIngredient`
- dela upp i fler tabeller först om behovet blir tydligt senare
## 2. Ska suggestions visas under import?
Rekommendation:
- ja, men bara som passiv hjälp
- inte som något användaren måste välja för att kunna spara
## 3. Ska användaren kunna redigera ingrediensrader under import?
Rekommendation:
- ja, men bara lätt redigering
- ingen tung produktkoppling i detta steg
## 4. När ska AI användas?
Rekommendation:
- efter att receptet är sparat
- för matchning, substitutioner och shoppingstöd
- inte för att avgöra om receptet får sparas
---
# Definition av klart
Denna omarbetning är klar när följande gäller:
1. Ett importerat recept kan sparas utan att någon ingrediens är kopplad till en intern produkt.
2. Receptets ingredienslista visas troget även utan produktmatchning.
3. Importskärmen kräver inte produktval.
4. Receptdetaljen kan analysera receptet mot inventory och pantry i ett separat steg.
5. Analysen kan särskilja exakt träff, pantry-träff, ersättningsbar och saknad ingrediens.
6. AI används bara som hjälplager ovanpå detta.
---
# Rekommenderat första arbetsblock
Om implementationen ska påbörjas direkt är detta bästa första block:
1. Uppdatera `schema.prisma` för råingrediensstöd.
2. Skapa migration.
3. Uppdatera `create-recipe.dto.ts`.
4. Uppdatera `recipes.service.ts#create()` så att recept kan sparas utan `productId`.
5. Uppdatera `create_recipe_screen.dart` så att `_save()` skickar råingredienser i stället för att kasta bort omatchade rader.
Det blocket ger störst effekt med lägst risk, eftersom det löser kärnproblemet: att importen inte längre förstör receptets innehåll.
## 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.
## 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.
@@ -0,0 +1,240 @@
# 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.
# Sessionlogg: Receipt Import Cleanup & Optimization
Datum: 2026-05-09
## Mål under sessionen
- Rensa bort legacy/deprecated kod i receipt-import.
- Förenkla och optimera kvarvarande kod på ett säkert sätt.
- Säkerställa att kategori-förslag visas för okända varor i import-UI.
## Genomförda förändringar
### 1) Legacy/deprecated kod borttagen (backend)
Fil: `backend/src/receipt-import/receipt-import.service.ts`
- Borttaget: `inferPackageDebugFromRawName()`
- Borttaget: `cachedCategories` + `loadCategories()` + constructor-anrop
- Borttaget: `matchProducts()`
- Borttaget: `findWordMatch()` (gammal variant)
- Borttaget: `enrichWithAiCategories()`
Fil: `backend/src/receipt-import/receipt-import.controller.ts`
- Borttaget endpoint: `POST /receipt-import/refresh-categories` (obsolet efter borttagen cache-metod)
### 2) Tester uppdaterade till unified matcher
Fil: `backend/src/receipt-import/receipt-import.service.spec.ts`
- Tester migrerade från anrop av borttagna `matchProducts()` till `matchAndEnrichReceiptItem()`.
- Tester gröna efter uppdatering.
### 3) Säkra förenklingar/optimeringar (backend)
Fil: `backend/src/receipt-import/receipt-import.service.ts`
- Infört tydliga typer för matchningskontext (`MatchingContext`) och debug-objekt.
- Extraherat helpers för:
- signaltextbyggande
- enhetsmappning
- Reducerad duplicering i kategoriseringsflöde.
- In-memory index i matchningskontext för snabbare uppslag:
- `aliasByReceiptName`
- `unitMappingByKey`
- Bakåtkompatibilitet behållen via fallback när index-fält saknas.
### 4) UI-fix: kategori-förslag visades inte för okända varor
Fil: `flutter/lib/features/import/presentation/receipt_import_tab.dart`
- Fixat så kategori-förslag visas även om rad saknar matchad produkt.
- Edit-state fylls nu även för rader med endast kategori-förslag.
- Label i UI visar `Kategoriförslag` när produkt saknas.
### 5) Diagnostik tillagd (backend)
Fil: `backend/src/receipt-import/receipt-import.service.ts`
- Varningslogg om kategorier inte kunde laddas eller om listan blev tom.
- Syfte: snabbare felsökning när kategori-förslag uteblir.
## Verifiering under sessionen
- Backend build: OK (`npm run build`)
- Backend tests: OK (66/66)
- Flutter analyze (berörda filer): OK
Notering: terminalhistorik innehöll äldre felutskrifter, men senaste verifieringarna var gröna.
## Kvar att göra nästa gång
1. Deploya senaste backend + flutter till servern.
2. Re-testa receipt import med PDF i produktion.
3. Bekräfta att rader utan produkt nu visar `Kategoriförslag` direkt i listan.
4. Vid fortsatt problem: kontrollera nya varningsloggar från `prepareMatchingContext` i backend-loggar.
## Snabb fortsättning (checklista)
- [ ] Deploy backend
- [ ] Deploy flutter
- [ ] Import-test med samma PDF
- [ ] Verifiera kategori-förslag för okända varor
- [ ] Dela loggutdrag om något saknas
---
# Sessionlogg: Produkthantering & UI-optimeringar (samma dag, senare)
## Mål under denna del
- Fixa scroll-issue i kvittoimport-gränssnittet (7 rader men bara 5 synliga).
- Implementera product rename/merge för både admins och regular users.
- Eliminera kodduplicering i backend-service.
- Optimera admin-panel och deploy-process.
## Genomförda förändringar
### 1) Scroll-issue fixat i receipt import UI
Fil: `flutter/lib/features/import/presentation/receipt_import_tab.dart`
- **Problem:** `SizedBox(height: 620px, child: ListView.builder(...))` inuti `SingleChildScrollView` skapade konfligerade scroll-handlers.
- **Lösning:** Tog bort `SizedBox`-begränsningen, använd `shrinkWrap: true` och `physics: NeverScrollableScrollPhysics()` på ListView så parent `SingleChildScrollView` hanterar all scrolling.
- **Resultat:** ✅ Alla 7 rader nu synliga.
### 2) Product rename & merge för admin (backend)
Fil: `backend/src/products/products.controller.ts`
- Två nya endpoints:
- `PATCH /products/:id/canonical-name` — admin kan byta namn på vilken produkt som helst
- `POST /products/merge` — admin kan slå ihop två produkter
- Decorator: `@Roles('admin')`
### 3) Private product endpoints för vanliga users (backend)
Fil: `backend/src/products/products.controller.ts`
- Två nya parallella endpoints för user-owned private products:
- `PATCH /products/private/:id/canonical-name` — user kan byta namn på egen privat produkt
- `POST /products/private/merge` — user kan slå ihop egna privata produkter
- JWT extraction: `const userId = req.user.id` (ingen `@Roles`-behov, user kan bara redigera sin egna data)
- Security: `ForbiddenException` om produkt inte är privat eller inte ägs av user
### 4) Backend service refaktorering — kodduplicering eliminerad
Fil: `backend/src/products/products.service.ts`
- Två nya **private helper-metoder:**
- `_updateCanonicalNameCore(id, canonicalName)` — shared logik för trim + Prisma update
- `_mergeCore(sourceId, targetId)` — shared logik för transaction, inventory transfer, soft-delete
- Alla fyra public metoder (`updateCanonicalName`, `updateCanonicalNamePrivate`, `merge`, `mergePrivate`) använder nu dessa helpers
- **Exception fix:** Bytte `throw new Error(...)` till `throw new ForbiddenException(...)` för authorization-fel (korrekt HTTP 403)
- **Resultat:** ~80 rader kodduplicering eliminerad, bättre underhållbarhet
### 5) API-path konstanter för Flutter (frontend)
Fil: `flutter/lib/core/api/api_paths.dart`
- Lade till två nya constants i `ProductApiPaths`:
- `static const mergePrivate = '/products/private/merge'`
- `static String canonicalNamePrivate(int id) => '/products/private/$id/canonical-name'`
### 6) Admin repository uppdaterad (frontend)
Fil: `flutter/lib/features/admin/data/admin_repository.dart`
- Två nya metoder:
- `updateCanonicalNamePrivate(int productId, String canonicalName)` — user rename
- `mergeProductsPrivate({required sourceId, required targetId})` — user merge
- Komment: "Admin kan uppdatera vilken produkt som helst; users kan bara uppdatera sina egna privata produkter"
### 7) Admin panel optimeringar (frontend)
Fil: `flutter/lib/features/admin/presentation/admin_products_panel.dart`
- **Caching av kategorierna:** `_cachedCategoryOptions` beräknas en gång istället för varje build
- **Enklare `_nameForId()`:** Bytte från `where().toList()` till en enkel for-loop med early return
- **Parallell restore:** `_restoreSelected()` använder `Future.wait()` istället för seriebaserade await
- **Expression switch:** `_sortLabel()` förkortat från 12-radigt switch till en enda rad med expression switch
### 8) Deploy-script förbättring
Fil: `deploy.sh`
- Nya flaggor för selektiv build:
- `--backend` — bygga bara backend
- `--flutter` — bygga bara Flutter
- `--importer` — bygga bara microservice-importer
- `--pull-always` — tvinga Docker att hämta senaste base image (för prod-säkerhet)
- Default: bygger alla tre, använder `--pull=false` för snabbhet (ej prod)
- `--seed` flag för opt-in databaskällning
- Help: `./deploy.sh --help` visar användning
## Verifiering
- ✅ Backend build: OK (`npm run build`)
- ✅ Backend tests: OK
- ✅ Flutter analyze: OK (alla berörda filer)
- ✅ TypeScript-fel: Inga
- ✅ Git diff: Alla 4 filer granskat och godkänt
## Öppna uppgifter (nästa steg)
1. Deploy backend + Flutter med `./deploy.sh --backend --flutter`
2. Testa i produktion:
- Verifiera 7 rader nu synliga i receipt import
- Verifiera admin kan byta namn på produkter
- Verifiera admin kan slå ihop produkter
- Verifiera users kan redigera sina egna privata produkter
3. UI för users: Om private rename/merge ska exponeras i användar-app (backend redan klart, saknas bara UI)
4. Unit/integration tests för private endpoints
---
# Sessionlogg: Aliasstrategi i kvittoimport (samma dag, senare)
## Mål under denna del
- Göra aliasstrategin konsekvent med user-scope som standard och global fallback via admin.
- Sluta lära alias automatiskt vid manuell korrigering och kräva explicit val i UI.
- Härda backend mot brusiga eller ogiltiga alias.
## Genomförda förändringar
### 1) Gemensam aliasnormalisering och guardrails (backend)
Filer:
- `backend/src/common/utils/receipt-alias.ts`
- `backend/src/receipt-alias/receipt-alias.service.ts`
- `backend/src/receipt-import/receipt-import.service.ts`
- Infört gemensam utility för aliasnormalisering (`trim`, lowercase, kollapsad whitespace).
- Infört validering som blockerar tomma alias och brusiga alias som `rabatt`, `summa`, `pant`, `att betala`, `totalt`, m.fl.
- Receipt import och alias-API använder nu samma regler för både lookup och sparande.
### 2) Receipt import lär inte längre alias automatiskt (Flutter)
Filer:
- `flutter/lib/features/import/data/receipt_import_session.dart`
- `flutter/lib/features/import/presentation/edit_dialog.dart`
- `flutter/lib/features/import/presentation/receipt_import_tab.dart`
- Infört explicit `learnAlias`-val i edit-dialogen.
- Alias sparas nu bara om användaren aktivt markerar att kvittonamnet ska läras in.
- Valet persisteras i receipt import-sessionen så att tabbyte inte tappar användarens val.
- Om raden redan matchades via alias visas förklarande text i stället för ny aliasinlärning.
### 3) Aliasöversikter visar scope tydligare (Flutter)
Filer:
- `flutter/lib/features/admin/domain/receipt_alias.dart`
- `flutter/lib/features/profile/presentation/user_aliases_screen.dart`
- `flutter/lib/features/admin/presentation/admin_aliases_panel.dart`
- Aliasmodellen utökad med `ownerId` och `isGlobal`.
- User alias-skärmen visar nu skillnad mellan `Privat alias` och `Global fallback`.
- Delete-knapp visas bara för privata alias i användarvyn, så UI:t matchar backend-behörigheten.
- Adminpanelen visar scope även för aliasposter.
### 4) Tester för aliasflödet
Filer:
- `backend/src/receipt-import/receipt-import.service.spec.ts`
- `backend/src/receipt-alias/receipt-alias.service.spec.ts`
- Tester tillagda för normalisering av whitespace vid alias-lookup.
- Tester tillagda för alias-upsert med normalisering.
- Tester tillagda för blockering av brusalias.
- Tester tillagda för behörighetsregler kring globala alias och borttagning.
## Verifiering
- ✅ Backend tests: 31/31 gröna för berörda aliasspecar
- ✅ Flutter analyze: OK för alla berörda alias/import-filer
## Kvar att göra
1. Manuell test i appen: receipt import med explicit alias-inlärning.
2. Produktionstest: verifiera att privata alias och global fallback beter sig rätt mot riktiga kvitton.
3. Bedöm om aliasöversikterna behöver mer avancerad filtrering eller redigering senare.
## Snabb checklista för nästa session
- [ ] Deploy backend + Flutter
- [ ] Testa scroll-fix i prod
- [ ] Testa admin rename/merge
- [ ] Testa private endpoints (API-test eller manual)
- [ ] Implementera user-UI för private rename/merge (valfritt)
## 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.
## 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.
@@ -0,0 +1,414 @@
# 🔒 Säkerhetshärdningsplan för Recipe-App (Flutter + NestJS + MariaDB)
# Nyheter och förbättringar (2026-05-10)
- **Admin-inventarie:** Endast admin har tillgång till CRUD, merge, filter, sortering och preview för alla användares inventarieposter. Endpoints och UI är skyddade med @Roles('admin') och testade.
- **User-scope och IDOR-skydd:** Inventory och produkter är 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.
**Reviderad:** 2026-05-07 — baserad på faktisk kodgranskning av repo.
**Mål:** Täppa till IDOR, Full Table Dump, och andra kritiska säkerhetshål i **backend (NestJS/Prisma/MariaDB)**, **Flutter-frontend**, och **infrastruktur (Docker/Gitea/Ubuntu)**.
**Prioritet:** CRITICAL → HIGH → MEDIUM
---
## ✅ **Redan implementerat — kräver ingen åtgärd**
Följande är bekräftat implementerat i koden och behöver inte åtgärdas:
| Åtgärd | Var |
|---|---|
| Helmet med HSTS, X-Frame-Options, CORP, COOP, Referrer-Policy | `backend/src/main.ts` |
| CORS begränsat till `ALLOWED_ORIGIN` (env-var) | `backend/src/main.ts` |
| Global `JwtAuthGuard` (alla endpoints kräver JWT om inte `@Public()`) | `backend/src/app.module.ts` |
| Global `RolesGuard` med `@Roles('admin')` decorator | `backend/src/app.module.ts` |
| Global `ThrottlerGuard` (120 anrop/min), 10/min på login/register | `backend/src/app.module.ts`, `auth.controller.ts` |
| `ValidationPipe` med `whitelist` + `forbidNonWhitelisted` (förhindrar mass assignment) | `backend/src/main.ts` |
| bcrypt salt 12 för lösenordshashning | `backend/src/auth/auth.service.ts` |
| IDOR-skydd: `recipes`, `pantry`, `meal-plan`, `receipt-alias` filtrerar på `userId` | Respektive controllers |
| Produkter är user-scoped (`ownerId``Product`) | `backend/prisma/schema.prisma` |
| Hemligheter som env-vars i compose, inga hårdkodade värden | `compose.yml` |
| `.env.*` i `.gitignore` | `.gitignore` |
| Token-lagringsabstraktion i Flutter (`ITokenStorage`) | `flutter/lib/core/platform/token_storage.dart` |
| Premium/admin-guard (`PremiumOrAdminGuard`) | `backend/src/auth/premium-or-admin.guard.ts` |
---
---
## 📌 **0. Förberedelser**
### 0.1. Miljö och verktyg
- **Skapa en `security-audit`-gren:**
```bash
git checkout -b security-audit-$(date +%Y%m%d)
```
---
---
## 🚨 **1. IDOR + Full Table Dump i Inventory — CRITICAL**
**Problem:** `InventoryItem` saknar `userId`-fält i Prisma-schemat och `inventory.controller.ts` använder inte `@CurrentUser()` alls. Alla autentiserade användare kan läsa, modifiera och radera varandras inventarieposter via `GET/POST/PATCH/DELETE /api/inventory`.
**Nuläge i koden:**
- `InventoryItem`-modellen i `schema.prisma` saknar `userId`-kolumn
- `inventory.controller.ts` har noll anrop till `@CurrentUser()`
- `inventory.service.ts` metoder `findAll()`, `findExpiring()`, `update()`, `remove()` tar inte in `userId`
**Lösning — 3 steg:**
### Steg 1: Lägg till `userId` i Prisma-schemat
```prisma
// backend/prisma/schema.prisma
model InventoryItem {
id Int @id @default(autoincrement())
userId Int // NY KOLUMN
productId Int
// ...övriga fält oförändrade...
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
consumptions InventoryConsumption[]
@@index([productId])
@@index([userId]) // NY INDEX
}
// Lägg till i User-modellen:
model User {
// ...
inventoryItems InventoryItem[] // NY RELATION
}
```
Skapa migrerings-SQL i `backend/prisma/migrations/{timestamp}_add_userid_to_inventory/migration.sql`:
```sql
ALTER TABLE `InventoryItem` ADD COLUMN `userId` INT NOT NULL DEFAULT 1;
-- Backfill: sätt userId baserat på produktens ägare
UPDATE `InventoryItem` ii
JOIN `Product` p ON ii.productId = p.id
SET ii.userId = p.ownerId;
ALTER TABLE `InventoryItem` ADD CONSTRAINT `fk_inventory_user`
FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE;
CREATE INDEX `InventoryItem_userId_idx` ON `InventoryItem`(`userId`);
```
> **OBS:** Granska backfill-SQL mot din data innan körning — om produkter saknar ägare kan DEFAULT 1 ge fel.
### Steg 2: Uppdatera service-metoderna
```typescript
// backend/src/inventory/inventory.service.ts
// findAll — lägg till userId-parameter och where-klausul
async findAll(userId: number, query?: InventoryQuery) {
const where: Prisma.InventoryItemWhereInput = { userId };
// ...resten oförändrat...
}
// findExpiring — filtrera på userId
async findExpiring(userId: number) {
return this.prisma.inventoryItem.findMany({
where: { userId, bestBeforeDate: { not: null, gte: new Date() } },
// ...
});
}
// create — spara userId
async create(userId: number, data: CreateInventoryDto) {
return this.prisma.inventoryItem.create({
data: { ...data, userId, quantity: new Prisma.Decimal(data.quantity) },
// ...
});
}
// update — verifiera ägarskap innan uppdatering
async update(id: number, userId: number, data: UpdateInventoryDto) {
const existing = await this.findInventoryItemByIdOrThrow(id);
if (existing.userId !== userId) throw new ForbiddenException('Åtkomst nekad');
// ...resten oförändrat...
}
// remove — verifiera ägarskap innan borttagning
async remove(id: number, userId: number) {
const existing = await this.findInventoryItemByIdOrThrow(id);
if (existing.userId !== userId) throw new ForbiddenException('Åtkomst nekad');
return this.prisma.inventoryItem.delete({ where: { id } });
}
// consume — verifiera ägarskap
async consume(id: number, userId: number, body: ConsumeInventoryDto) {
const existing = await this.findInventoryItemByIdOrThrow(id);
if (existing.userId !== userId) throw new ForbiddenException('Åtkomst nekad');
// ...resten oförändrat...
}
```
### Steg 3: Uppdatera controller
```typescript
// backend/src/inventory/inventory.controller.ts
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { ForbiddenException } from '@nestjs/common';
@Controller('inventory')
export class InventoryController {
@Get()
findAll(
@CurrentUser() user: { userId: number },
@Query('location') location?: string,
@Query('sort') sort?: string,
) {
return this.inventoryService.findAll(user.userId, { location, sort });
}
@Get('expiring')
findExpiring(@CurrentUser() user: { userId: number }) {
return this.inventoryService.findExpiring(user.userId);
}
@Post()
create(
@CurrentUser() user: { userId: number },
@Body() body: CreateInventoryDto,
) {
return this.inventoryService.create(user.userId, body);
}
@Patch(':id')
update(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
@Body() body: UpdateInventoryDto,
) {
return this.inventoryService.update(id, user.userId, body);
}
@Delete(':id')
remove(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
) {
return this.inventoryService.remove(id, user.userId);
}
@Post(':id/consume')
consume(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
@Body() body: ConsumeInventoryDto,
) {
return this.inventoryService.consume(id, user.userId, body);
}
@Get(':id/consumption-history')
findConsumptionHistory(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
) {
// Verifiera ägarskap på item innan historik returneras
return this.inventoryService.findConsumptionHistory(id, user.userId);
}
}
```
### Steg 4: Testa
```typescript
// backend/src/inventory/inventory.service.spec.ts
describe('IDOR-skydd inventory', () => {
it('nekar åtkomst till annan användares post vid update', async () => {
prismaMock.inventoryItem.findUnique.mockResolvedValue({ id: 1, userId: 2 } as any);
await expect(service.update(1, 1, {})).rejects.toThrow(ForbiddenException);
});
it('nekar åtkomst till annan användares post vid remove', async () => {
prismaMock.inventoryItem.findUnique.mockResolvedValue({ id: 1, userId: 2 } as any);
await expect(service.remove(1, 1)).rejects.toThrow(ForbiddenException);
});
});
```
---
---
## **2. dist/ saknas i .gitignore — HIGH**
**Problem:** `backend/dist/` och `backend/tsconfig.tsbuildinfo` är committade i repot och orsakar merge-konflikter vid deploy (bekräftat 2026-05-07).
**Lösning:**
Lägg till i `.gitignore` (rotnivå):
```gitignore
# Kompilerat backend-output — byggs i Docker, ska ej spåras
backend/dist/
backend/tsconfig.tsbuildinfo
```
Rensa sedan från git-historiken (enbart lokal tracking):
```bash
git rm -r --cached backend/dist/ backend/tsconfig.tsbuildinfo
git commit -m "chore: untrack compiled backend dist files"
git push
```
På servern, kör sedan:
```bash
git checkout -- backend/dist/ backend/tsconfig.tsbuildinfo 2>/dev/null; git pull
```
---
---
## 🔑 **3. .gitignore täcker inte bare `.env` — MEDIUM**
**Problem:** `.gitignore` har `.env.*` (täcker `.env.local`, `.env.production` etc.) men INTE ett eventuellt `.env` utan suffix.
**Lösning:** Lägg till i `.gitignore`:
```gitignore
.env
.env.*
!.env.example
```
**Skapa `.env.example`** (finns ej i repot):
```env
DATABASE_URL=mysql://user:password@localhost:3306/recipe_app
JWT_SECRET=minst-32-tecken-slumpad-sträng
MARIADB_ROOT_PASSWORD=
MARIADB_DATABASE=recipe_app
MISTRAL_API_KEY=
NEXT_PUBLIC_APP_URL=https://recept.example.com
ADMIN_NADMIN_PASSWORD=
ADMIN_PADMIN_PASSWORD=
SEED_USER1_PASSWORD=
SEED_USER2_PASSWORD=
ALLOWED_ORIGIN=https://recept.example.com
RECEIPT_TRACE_DECISIONS=0
```
---
---
## 📱 **4. Flutter — tokenlagring på webben — MEDIUM**
**Nuläge:** `WebTokenStorage` använder `SharedPreferences` som mappar till `localStorage` i webbläsaren. JWT i localStorage är åtkomlig via JavaScript och kan stjälas vid XSS.
**Realistisk åtgärd för en Flutter Web-app:** Det finns inget httpOnly-cookie-alternativ direkt i Flutter Web utan backend-stöd. Om XSS-risken bedöms hög, överväg:
1. Backend sätter token som httpOnly cookie vid login — Flutter Web läser aldrig token direkt
2. Alternativt: sessionStorage (rensas vid stängt fönster) via JS-interop
**Nuvarande abstraktionslager är korrekt strukturerat** (`ITokenStorage` → `WebTokenStorage`). Byt ut implementationen vid behov utan att ändra resten av koden.
> ️ `flutter_secure_storage` fungerar **inte** på web — det är korrekt att web-adaptern använder SharedPreferences.
---
---
## 🔌 **5. Webhook-säkerhet — LOW (om Gitea-webhooks används)**
**Nuläge:** Inga webhook-endpoints hittades i koden. Åtgärd krävs endast om Gitea-webhooks kopplas in.
**Om webhooks läggs till** — validera `X-Gitea-Signature` med `crypto.timingSafeEqual`:
```typescript
// src/webhooks/gitea.controller.ts
import { Controller, Post, Headers, Body, RawBodyRequest, Req, UnauthorizedException } from '@nestjs/common';
import * as crypto from 'crypto';
import { Public } from '../auth/decorators/public.decorator';
@Controller('webhooks/gitea')
export class GiteaController {
@Public()
@Post()
async handleWebhook(
@Headers('x-gitea-signature-256') signature: string,
@Req() req: RawBodyRequest<Request>,
) {
const secret = process.env.GITEA_WEBHOOK_SECRET;
if (!secret) throw new Error('GITEA_WEBHOOK_SECRET saknas');
const hmac = crypto.createHmac('sha256', secret);
hmac.update(req.rawBody!);
const expected = `sha256=${hmac.digest('hex')}`;
if (!crypto.timingSafeEqual(Buffer.from(signature ?? ''), Buffer.from(expected))) {
throw new UnauthorizedException('Ogiltig signatur');
}
// Hantera event...
}
}
```
Lägg till `GITEA_WEBHOOK_SECRET` i `.env.example`.
---
---
## 🧪 **6. CI/CD-säkerhetstester — MEDIUM**
> ️ Projektet använder **Gitea** (inte GitHub), så GitHub Actions-workflows i originalplanen fungerar inte.
Nuvarande CI: tester körs via `npm test` på push (pipeline finns i Gitea).
**Utöka befintlig pipeline** med:
- `npm audit --audit-level=high` — kontrollera kända CVE:er i beroenden
- `npx prisma validate` — verifiera schema-integritet
- Lägg till `gitleaks` som pre-commit hook lokalt (körs inte i Docker-build)
```bash
# Installera som lokal pre-commit hook
npm install -g gitleaks
gitleaks protect --staged # kör före varje commit
```
---
---
## ✅ **7. Avslutande Checklista**
| **Åtgärd** | **Status** | **Ansvarsområde** |
| -------------------------------- | ---------- | ----------------------- |
| IDOR-skydd: recipes, pantry, meal-plan, receipt-alias | ✅ Klart | Backend |
| IDOR-skydd + userId-filtrering för **inventory** | ✅ Klart | Backend (NestJS/Prisma) |
| `dist/` tillagd i `.gitignore` | ✅ Klart | Git |
| `.env` (utan suffix) i `.gitignore` + `.env.example` | ✅ Klart | Git |
| Helmet, CORS, ThrottlerGuard, ValidationPipe | ✅ Klart | Backend |
| Flutter: token-abstraktion (`ITokenStorage`) | ✅ Klart | Flutter |
| Flutter: httpOnly cookie-alternativ (om XSS är reell risk) | ⬜ LOW | Flutter + Backend |
| Gitea webhook-signaturvalidering (om webhooks används) | ⬜ LOW | Backend |
| `npm audit` i CI-pipeline | ✅ Klart | CI/CD (Gitea) |
---
---
## 🎯 **8. Prioriterad Ordning för Implementering**
1. **Inventory IDOR + userId-fält** (CRITICAL) — ✅ KLART 2026-05-07
2. **dist/ i .gitignore** (HIGH) — ✅ KLART 2026-05-07
3. **bare .env + .env.example** (MEDIUM) — ✅ KLART 2026-05-07
4. **npm audit i CI** (MEDIUM) — ✅ KLART 2026-05-07
5. **Flutter httpOnly cookies** (LOW — kräver arkitekturförändring)
6. **Gitea webhook-validering** (LOW — bara relevant om webhooks används)
---
## 2026-05-07: Sammanfattning av senaste säkerhetsförbättringar
- **Inventory är nu user-scopad:** Alla inventory-operationer kräver och filtrerar på userId i backend (schema, migration, service, controller, tester).
- **IDOR-skydd för inventory:** Det är nu omöjligt för användare att läsa eller ändra andras inventarieposter. Tester verifierar att åtkomst nekas vid försök till IDOR.
- **.gitignore och deploy-hygien:** backend/dist och backend/tsconfig.tsbuildinfo ignoreras och är ej längre spårade i git. .env och .env.* ignoreras, men .env.example finns och är uppdaterad.
- **CI/CD-härdning:** npm audit och prisma validate körs i pipeline. Alla tester och byggen måste passera.
## 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.
@@ -0,0 +1,236 @@
# Implementeringsplan: "Se kvitto"-Modal för Kvittoimporten
**Mål**: MVP-vägen för split-view UX lägg till modal som visar OCR-text från parsade kvittoraderna.
**Scope**: 2-3 timmar
**Status**: Planering
---
## 1. Ändringar i `receipt_import_tab.dart`
### 1.1 Lägg till knapp "Se kvitto" i header-raden (rad ~745-752)
**Plats**: Höger om "Välj alla/Avmarkera alla"-knappen
```dart
// Innan: Row med bara "Välj alla"-knapp
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${items.length} rader — tryck för att redigera', style: theme.textTheme.titleSmall),
TextButton(...), // "Välj alla/Avmarkera alla"
],
)
// Efter: Lägg till "Se kvitto"-knapp
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${items.length} rader — tryck för att redigera', style: theme.textTheme.titleSmall),
Row(
children: [
TextButton.icon(
onPressed: items.isEmpty ? null : () => _showReceiptPreview(context, items),
icon: const Icon(Icons.description_outlined),
label: const Text('Se kvitto'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () => setState(...), // Befintlig "Välj alla"
child: Text(...),
),
],
),
],
)
```
### 1.2 Implementera `_showReceiptPreview`-metod
Lägg till denna metod i `_ReceiptImportTabState`:
```dart
Future<void> _showReceiptPreview(BuildContext context, List<ParsedReceiptItem> items) async {
if (!context.mounted) return;
await showDialog(
context: context,
builder: (ctx) => _ReceiptPreviewDialog(items: items),
);
}
```
---
## 2. Ny widget: `_ReceiptPreviewDialog`
Lägg till denna widget **i samma fil** (`receipt_import_tab.dart`), efter `_ReceiptImportResultRow`-klassen:
```dart
class _ReceiptPreviewDialog extends StatelessWidget {
final List<ParsedReceiptItem> items;
const _ReceiptPreviewDialog({required this.items});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return AlertDialog(
title: const Text('Kvittotexten i sin helhet'),
content: SizedBox(
width: 600, // Responsiv bredd på desktop
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Här visas all OCR-parsad text från kvittot. En rad per artikel:',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 12),
Container(
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerLowest,
border: Border.all(color: theme.colorScheme.outlineVariant),
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(12),
child: SelectableText.rich(
TextSpan(
children: items.isEmpty
? [TextSpan(text: '(Inga rader)', style: theme.textTheme.bodySmall)]
: items
.asMap()
.entries
.map((entry) {
final item = entry.value;
final lineNumber = entry.key + 1;
final lineText = _formatReceiptLine(item);
return TextSpan(
children: [
TextSpan(
text: '$lineNumber. ',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.outlineVariant,
),
),
TextSpan(
text: lineText,
style: theme.textTheme.bodySmall?.copyWith(
fontFamily: 'monospace',
),
),
const TextSpan(text: '\n'),
],
);
})
.toList(),
),
style: theme.textTheme.bodySmall,
),
),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Stäng'),
),
],
);
}
String _formatReceiptLine(ParsedReceiptItem item) {
final parts = <String>[];
if (item.quantity != null) {
parts.add('${item.quantity}');
}
if (item.unit != null) {
parts.add(item.unit!);
}
parts.add(item.rawName);
if (item.price != null) {
parts.add('${item.price} kr');
}
return parts.join(' ');
}
}
```
---
## 3. Implementeringssteg (steg-för-steg)
1. **Läs receipt_import_tab.dart** och identifiera raden med "Välj alla/Avmarkera alla"-knappen
2. **Refaktorera Row**: Lägg "Se kvitto"-knapp bredvid befintliga knapp
3. **Lägg till `_showReceiptPreview()`-metod** i `_ReceiptImportTabState`
4. **Implementera `_ReceiptPreviewDialog`-widget** på slutet av filen
5. **Testa**:
- Ladda ett kvitto
- Klicka "Se kvitto"-knappen
- Verifiera att texten är lesbar och formaterad
- Testa responsive bredd (dialog behöver minska på mobil)
---
## 4. Responsiv förbättring (optional)
Om dialogen behöver anpassas för mobil:
```dart
// I _ReceiptPreviewDialog.build():
final isWide = MediaQuery.of(context).size.width > 600;
return Dialog(
insetPadding: const EdgeInsets.all(16),
child: SizedBox(
width: isWide ? 600 : double.maxFinite, // Full bredd på mobil
// ...
),
);
```
---
## 5. Långsiktiga förbättringar (Phase 2)
Se `next_steps_flutter.md` för split-view roadmap:
- Horisontell split-view på desktop
- Scroll-synkronisering
- Tab-fallback på mobil
- AI-guiding labels
---
## Ärendemal
**Titel**: "Se kvitto"-modal för kvittoimporten
**Branch**: `feat/receipt-preview-modal`
**Labels**: `enhancement`, `import-ux`, `phase-1-mvp`
**Estimate**: 2-3h
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
+149
View File
@@ -0,0 +1,149 @@
# Flutter Performance Profileringsguide
## Mål
| Mätpunkt | Gränsvärde |
|---|---|
| Frame build-tid (60 Hz) | < 16 ms |
| Frame build-tid (120 Hz) | < 8 ms |
| Scroll jank (tappade frames) | 0 vid normal scroll |
| Minnesfotavtryck (app) | < 200 MB |
---
## 1. Starta i profile-läge
Kör alltid profilmätningar i **profile mode**, inte debug. Debug-läget har JIT-kompilering och extra overhead.
```bash
# Mot fysisk enhet
flutter run --profile
# Mot Chrome (web)
flutter run -d chrome --profile
```
---
## 2. Flutter DevTools Öppna
```bash
flutter pub global activate devtools
flutter pub global run devtools
```
Eller anslut direkt från terminalen när appen körs i profile mode Flutter skriver ut en DevTools-URL.
---
## 3. Timeline Mät frame-tider
1. Öppna **Performance**-fliken i DevTools.
2. Klicka **Record**.
3. Utför den aktion du vill mäta (t.ex. byt vy, scrolla).
4. Klicka **Stop**.
5. Granska:
- **UI thread** (Dart-kod) bör vara < 16 ms per frame.
- **Raster thread** (GPU) bör vara < 16 ms per frame.
- Röda/gula staplar = jank.
### Kritiska mätpunkter i appen
| Scenario | Vad att leta efter |
|---|---|
| Byta vy (NavigationBar) | Frame-tid vid `StatefulShellRoute`-byte; bör vara < 32 ms totalt |
| Scrolla receptlista | Inga röda frames; `GridView.builder` bör recykla element |
| Scrolla adminpaneler | `ListView.builder` i embedded-läge; verifiera att ingen `NeverScrollableScrollPhysics` blockerar |
| Kvittoimport kryssa i rad | Endast den berörda raden bör rebuilda (`ConsumerWidget.select`) |
| Kvittoimport "Välj alla" | Batch-uppdatering via `setSelectedForAll` en enda `state =` |
---
## 4. Widget Rebuild-spårning
Aktivera rebuild-räknare i DevTools under **Inspector → Widget rebuild counts**.
Alternativt: lägg till tillfällig räknare i en widget:
```dart
int _buildCount = 0;
@override
Widget build(BuildContext context, WidgetRef ref) {
debugPrint('${widget.runtimeType} build #${++_buildCount}');
// ...
}
```
### Förväntade rebuild-mönster efter optimeringar
- `_ReceiptImportResultRow` med index X ska bara rebuilda när `selected[X]` ändras, inte när andra rader kryssas.
- `AppShell` ska inte rebuilda vid vy-byte (StatefulShellRoute bevarar grenar).
- Admin-paneler ska inte rebuilda hela listan vid en alias-ändring.
---
## 5. Memory Profiler
1. DevTools → **Memory**-fliken.
2. Klicka **Take snapshot** före och efter en tung operation.
3. Jämför levande objekt leta efter läckor (ackumulerade `StreamSubscription`, `Timer`, `Notifier`).
---
## 6. flutter analyze + dart fix
```bash
flutter analyze
dart fix --apply
```
Åtgärda alla varningar om `const` och onödiga rebuilds.
---
## 7. Identifierade optimeringar (genomförda)
| Område | Åtgärd | Effekt |
|---|---|---|
| Admin-paneler | Tog bort `NeverScrollableScrollPhysics` + `shrinkWrap` | Scroll fungerar, O(n) layout istället för O(n²) |
| Admin alias-lista | `ListView.builder` istället för spread | Virtualiserad lista |
| FABs | Explicita `heroTag` på alla FABs | Eliminerar hero-animation-krasch vid vy-byte |
| Scrollables | `PageStorageKey` på alla listvy | Scrollposition bevaras vid vy-byte |
| Router | `StatefulShellRoute.indexedStack` | Branch-state bevaras; ingen ombyggnad vid tab-byte |
| Kvittoimport resultatlista | `ListView.builder` + `SizedBox` bound height | Virtualiserad; max 620 px synlig |
| Kvittoimport radwidget | `ConsumerWidget` med `provider.select((s) => s?.selected[index])` | Endast ändrad rad rebuildar vid checkbox-toggle |
| Kvittoimport batch-API | `setSelectedForAll`, `setSelectedForIndexes`, `setImportedResult` | En `state =` och en SharedPreferences-skrivning per operation |
---
## 8. Snabbtest Verifiera förbättringar
```bash
# Kör i profile mode och öppna DevTools automatiskt
flutter run --profile --devtools-server-address=http://127.0.0.1:9100
```
Kontrollchecklista:
- [ ] Vy-byte NavigationBar: inga röda frames i Timeline
- [ ] Scroll i receptlista: < 2 tappade frames per 100 frames
- [ ] Scroll i admin-flikar: fungerar utan lock
- [ ] Kvittoimport checkbox-toggle: rebuild-räknare ökar bara för berörd rad
- [ ] Kvittoimport "Välj alla": en burst av rebuilds (en per rad), inga dubbla
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
+53
View File
@@ -0,0 +1,53 @@
# Flutter Frontend - Anvandarguide
Detta dokument ar for anvandare och operativa testare.
Har beskriver vi vad som fungerar i Flutter-klienten och hur den anvands i praktiken.
## Dokumentstatus (2026-05-03)
- Fokus: anvandarflode, inte implementation.
- Teknisk detaljniva finns i `teknisk_beskrivning_flutter.md`.
- Planering och backlog finns i `next_steps_flutter.md`.
## Vad appen ar
Flutter-webben ar en klient for Recipe App som kors i Docker och exponeras via Caddy.
Den anvands parallellt med Next-frontenden under migrering och verifiering.
## Senaste forbattringar
- Kvittoimportens granskningsflode ar klart och stabiliserat.
- Pagande kvittoimport sparas i klientens session och kan atertas efter refresh/navigation.
- Tolkning av antal/forpackning i kvittorader ar forbattrad, inklusive format som `2st`.
- AI-kategoriforslag och produktforslag visas separerat for tydligare val.
## Aktuella anvandarfloden
- Inloggning med anvandarnamn och losenord.
- Recept: lista, skapa, redigera, radera.
- Inventarie och baslager: skapa, redigera, konsumera.
- Matplan: veckovy, portionsjustering, inkopslista.
- Import: receptimport och kvittoimport med granskningssteg.
## Kanda begransningar
- Vissa adminfunktioner och avancerad AI-integration ar planerade men ej fullt migrerade.
- Bildimport forutsatter att containrar ar uppdaterade med senaste kod.
## Felsokning (anvandarniva)
1. Om sidan visar gammalt beteende efter deploy: hard uppdatering eller inkognito.
2. Om inloggning misslyckas: verifiera anvandarnamn/losenord (inte e-post).
3. Om importfloden fastnar: rapportera webblasarkonsolens fel till utvecklingsteamet.
## Relaterade dokument
- `next_steps_flutter.md` - roadmap och prioriteringar.
- `teknisk_beskrivning_flutter.md` - teknisk referens for drift/utveckling.
- `../README.md` - overgripande produktinformation.
## 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.
## 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.
## 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.
@@ -0,0 +1,71 @@
# Nasta steg: Flutter-migrering
Detta dokument ar Flutter-teamets roadmap och prioriteringslista.
All historik och implementationdetaljer finns i `teknisk_beskrivning_flutter.md`.
## Dokumentstatus (2026-05-03)
- Fokus: aktiv planering framat.
- Endast en roadmap for Flutter for att undvika dubbletter.
## Klart senaste sessionerna
- Fas 6b: granskningsflode for kvittoimport (edit, destination, merge, spara).
- Fas 6c: separering av AI-kategorichip och produktforslagschip.
- Fas 6d: klientpersistens for pagande kvittoimport + forbattrad antal/forpackningsinferens.
## Pagande arbete
- Robust bildimport och diagnostik i drift.
- Aliasstrategi i kvittoimport (hybrid user-scope + global fallback via admin).
- Utokad adminfunktionalitet i Flutter-sparet.
## Prioriterade nasta steg
1. **Kvitto-import UX förbättring (Split-view långsiktigt)**
- MVP (kort sikt): Lägg till "Se kvitto"-modal som visar full OCR-text från parsade rader
- Knapp i radlist-header, öppnar dialog med ScrollableText
- Enkelt UI, höga UX-vinster
- Implementering: ~2h
- Långsiktigt (Phase 2): Split-view med scroll-synkronisering
- Desktop: Horisontell split (kvitto-text vänster, radlista höger)
- Tablet/Mobil: Tab-based fallback (radlista standard, "Se kvitto"-tab för kontext)
- Scroll-sync mellan text och rader (om rad 3 är synlig, visa motsvarande text)
- AI-guiding labels ("Denna rad matchade mejeri automatiskt")
- Implementering: ~8h
2. Verifiera bildimport och felhantering end-to-end i testmiljo.
3. Implementera alias-inlarning vid manuell korrigering i importflodet.
4. Forbattra UI/UX i granskningsfloden for kvittoimport.
5. Fortsatt migrering av kvarvarande adminfloden.
6. Lokalisera kvarvarande delar i import- och inventarievyer.
## Viktiga beslut
- Flutter ar separat klient mot befintliga API-kontrakt.
- Ingen klientspecifik speciallogik for datamatchning; backend ar sanningskalla.
- Next-frontenden kor parallellt tills verifierad parity ar uppnadd.
## Relaterade dokument
- `README.md` - anvandarperspektiv.
- `teknisk_beskrivning_flutter.md` - teknisk referens.
- `../NEXT_STEPS.md` - overgripande roadmap for hela produkten.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
@@ -0,0 +1,128 @@
# Teknisk Beskrivning - Flutter Frontend
Detta dokument ar teknisk referens for systemadministratorer och programmerare.
Mallet ar en sammanhallen teknisk sanning utan duplicerade roadmap- eller anvandarsektioner.
## Dokumentstatus (2026-05-03)
- Fokus: arkitektur, drift och kanda gotchas.
- User guide finns i `README.md`.
- Planering finns i `next_steps_flutter.md`.
## Viktigt om miljo
Anvand inte absoluta Windows-sokvagar i kod eller scripts.
Bygg och drift sker pa Linux/Ubuntu i containeriserad miljo.
## Senaste tekniska tillagg
- Sessionpersistens i klienten for pagande kvittoimport.
- Forbattrad inferens for antal/forpackning (inklusive uttryck som `2st`).
- Tydlig separation i UI mellan AI-kategoriforslag och produktforslag.
- Ingen server-side persistens introducerad for kvittosession.
- Receptskapande: review-steg med redigerbara falt for quantity/unit/note per ingrediens.
- Receptskapande: tydlig varning i UI nar ingen produktmatchning hittas for inkluderad ingrediens.
- Receptskapande: stod for alternativa ingredienser i parse-svar (ex. "ris eller couscous") och visning i granskningsvyn.
- Global kopierbar feltext: copybar error-snackbar + SelectionArea pa appniva for battre felsokning/stod.
## Arkitektur
### Lager
- Presentation: `flutter/lib/features/*/presentation`
- State/Application: Riverpod providers/notifiers i `flutter/lib/features/*/data`
- Data/API: `flutter/lib/core/api`
- Platform abstraction: token storage i `flutter/lib/core/platform`
### Routing
- GoRouter i `flutter/lib/core/router/app_router.dart`
- ShellRoute for huvudnavigation
- Fullscreen-routes for skapa/redigera/detalj
### Auth
- Login via `POST /api/auth/login`
- Token-falt: `accessToken`
- Auth-gate i router med redirect logik
- `guardedApiCall()` hanterar logout vid 401
## Sakerhetsstatus (2026-05-07)
Denna sektion sammanfattar sakerhetsfunktioner som ar implementerade i Flutter-klienten och hur de fungerar tekniskt.
### Implementerat i Flutter
- Auth-gating i routing:
- Router-lagret stoppar navigation till skyddade vyer utan token/session.
- Om backend returnerar 401 i ett skyddat API-flode hanterar `guardedApiCall()` detta och triggar logout/omdirigering.
- Token-lagring via plattformsabstraktion:
- `ITokenStorage` definierar kontraktet (`getToken/saveToken/deleteToken`).
- Web-implementation (`WebTokenStorage`) lagrar token i `SharedPreferences` (web: localStorage).
- Arkitekturen gor att mobil implementation kan bytas till `flutter_secure_storage` utan att ovrig appkod andras.
- Minimerad klientside-auktorisering:
- Flutter forlitar sig pa backend som auktoritetskalla for access-kontroll.
- Klienten skickar bearer-token men gor inte egen resurstagarskapslogik som kan divergera fran serverregler.
- Kontraktsstyrd API-hantering:
- API-lagret accepterar 2xx pa importanrop och centraliserar felhantering.
- Minskar risken for ad-hoc felhantering i UI och inkonsekvent beteende vid auth-fel.
### Viktig begransning (web)
- Flutter Web kan inte anvanda `httpOnly` cookies enbart pa klientsidan.
- Nuvarande lagring i localStorage ar en praktisk kompromiss for web och innebar att XSS-hardening pa frontend och strict backend-headers/CSP ar fortsatt viktiga.
## API- och kontraktsprinciper
- Flutter foljer backend-kontrakt, ingen lokal speciallogik for matchning.
- 2xx-svar accepteras generellt i importanrop (inte hardkodning till enbart 200).
- Kvittoimport och receptimport ar separata floden med olika svarstyper.
## Kvittoimport - tekniska noter
- Granskningsflode med radredigering och destination (inventarie/baslager).
- Produktval stoder tradbaserat kategori -> produkt-flode.
- AI-kategorihint ar endast ett forslag; slutanvandaren validerar i granskningssteget.
## Drift och deploy
### Bygg och start (Flutter)
```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
```
### Viktiga verifieringar
- Compose merge: `docker compose -f compose.yml -f compose.flutter.yml config`
- Reachability via proxy/Caddy
- Runtime-loggar for importfloden vid felsokning
## Kanda fallgropar
- PDF-mime kan komma som `application/octet-stream` i Flutter Web.
- Orphan-varning ar normal nar huvudstack och Flutter-stack kors separat.
- Cache/service worker kan visa gammal frontend efter deploy.
- Vid massersattningar i Dart: verifiera parentesbalans i `showSnackBar(...)`-anrop innan build, annars faller web-kompilering med syntaxfel.
## Relaterade dokument
- `README.md` - anvandarguide.
- `next_steps_flutter.md` - aktiv planering.
- `../TEKNISK_BESKRIVNING.md` - backend/systemovergripande teknik.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
+266
View File
@@ -0,0 +1,266 @@
This file has been removed as all relevant information has been migrated.
# Prisma P3009 recovery (MySQL, migrationer)
**Problem:**
Prisma migrationer kan fastna i failed state (P3009) om en migration körts med fel SQL-citering (t.ex. "User" istället för `User` i MySQL) eller om deploy avbryts mitt i en migrering.
**Symptom:**
```
migrate found failed migrations in the target database, new migrations will not be applied. The `20260506144000_add_ai_engine_enabled` migration ... failed
```
**Lösning/playbook:**
1. Rätta migrationsfilen så att den använder backticks (`) för tabell- och kolumnnamn (MySQL-stil).
2. Kör:
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate resolve --rolled-back 20260506144000_add_ai_engine_enabled --schema prisma/schema.prisma"
docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma"
```
3. Om deploy klagar på duplicate column (dvs kolumnen finns redan):
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate resolve --applied 20260506144000_add_ai_engine_enabled --schema prisma/schema.prisma"
docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma"
```
4. Verifiera status:
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate status --schema prisma/schema.prisma"
```
**Lessons learned:**
- Kontrollera alltid SQL-citering i migrationsfiler (MySQL kräver backticks, inte dubbla citattecken).
- Vid P3009: använd `migrate resolve` för att markera migrationen som rolled-back eller applied beroende på DB-läge.
- Kör alltid migrationskommandon i rätt container/miljö för att undvika env- och version-mismatch.
# Session 2026-05-06: Migreringar för user-scoped AI och premium
Denna session:
- Lade till `aiEngineEnabled` på User i Prisma-schema och migrerade databasen (manuell SQL vid behov).
- Implementerade endpoint och logik för admin att toggla AI per användare.
- Säkerställde att alla AI-funktioner är user-scoped och premiumstyrda.
- Lessons learned: Vid DB-connectivity-problem krävs ibland manuell migration och resolve (se driftsekvens i TEKNISK_BESKRIVNING.md).
Se även:
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för drift- och migreringsdetaljer.
# Migrering: Import-funktion → microservice-importer
## Status: ✅ GENOMFÖRD 2026-04-30
## Dokumentstatus (2026-05-03)
### Målgrupp
Detta dokument är för systemadministratörer och utvecklare som ansvarar för integrationen mellan recipe-app och microservice-importer.
### Tillägg efter genomförd migrering
- Kvittoparsningens regelbaserade tolkning har förbättrats för multipack och enheter.
- Brödrelaterade kategoriregler och contradiction guards har utökats för högre träffsäkerhet.
- Klientens kvittosession i Flutter är nu persistent utan att ändra backendkontrakt eller införa serverlagring av sessionen.
- Kategoriträdet i seed-data har utökats med `Korvbröd` under `Fastfoodbröd`.
- **PDF-fix (2026-05-03):** `pdf-parse` använder `require()` istället för ESM-import; `pdfjs-dist/legacy/build/pdf.js` är fallback vid parsningsfel — löser `DOMMatrix is not defined` i Node.js Alpine-miljö.
- **Felkods-forwarding (2026-05-03):** `receipt-import.service.ts` returnerar nu `ServiceUnavailableException` (503) vid 503/429 från importer-api istället för `BadRequestException` (400).
- **Reproducerbart bygge (2026-05-03):** `package-lock.json` spåras i git; Dockerfile för importer-api kör `npm ci`.
- **AI-optimering (2026-05-03):** `looksLikeReceiptProductLine()` filtrerar PDF-rader utan siffra innan Mistral-anrop — minskar onödiga API-anrop vid kvittoimport.
- **Scope:** quick-import, parse-markdown, receipt-import
- **Arkitektur:** Backend-till-backend — recipe-app NestJS-backend anropar microservice-importer internt via HTTP. Frontend ändras inte.
- **OCR:** Implementerat i microservice-importer (tesseract.js + Alpine apk-paket)
- **Infra:** `importer-api`-tjänst i `recipe-app/compose.yml`, port 3001 intern
- **Driftsatt:** 2026-04-30, alla containers Healthy
---
## Fas 1 — Utöka microservice-importer ✅
**1. OCR-stöd och multipart i quick-import** ✅
- `QuickImportService.importFromUpload()` tillagd — hanterar PDF (pdf-parse) och bild (tesseract.js)
- `quick-import.controller.ts` utökat med `FileInterceptor`, `@HttpCode(200)`
- `Dockerfile` uppdaterad: `apk add tesseract-ocr tesseract-ocr-data-swe tesseract-ocr-data-eng`
**2. `imageUrl` i quick-import-svaret** ✅
- `imageUrl?: string` tillagd i `ParsedRecipe`-interfacet (`base.parser.ts`)
- ICA-parsern extraherar nu `recipe.image` (string/array/objekt-varianter)
- `QuickImportResult` utökad med `imageUrl?`, `imageWarning?`, source `'image'`
- `normalizeImageUrl()` hanterar protokollrelativa URL:er (`//cdn.ica.se/...`)
**3. Ny `ReceiptParsingModule`** ✅
- `backend/src/receipt-parsing/receipt-parsing.service.ts` — Mistral AI-parsning av kvitto (bild/PDF)
- `backend/src/receipt-parsing/receipt-parsing.controller.ts` — `POST /api/receipt-import/parse`, `@HttpCode(200)`, tillåter `application/octet-stream`
- `backend/src/receipt-parsing/receipt-parsing.module.ts`
- Registrerad i `app.module.ts`
**4. Health-endpoint** ✅
- `GET /api/health` → `{status: "ok"}` inline i `app.module.ts`
**5. Bugfixar i document-service** ✅
- `document-service.module.ts`: korrigerade importvägar + klassnamn (`DocumentImportModule` → `DocumentServiceModule`)
- `services/document-import.service.ts`: parsersökväg `./parsers/` → `../parsers/`
- Borttagna dubbletter: `services/web-scraping.module.ts`, `services/document-service.module.ts`
---
## Fas 2 — Anpassa recipe-app backend ✅
**5. Refaktorera `QuickImportService`** ✅
- All lokal parsning (ICA, pdf-parse, tesseract) borttagen
- Delegerar URL-import: `POST importer-api:3001/api/quick-import` (JSON)
- Delegerar filuploading: `POST importer-api:3001/api/quick-import` (FormData, `new Uint8Array(file.buffer)`)
- `downloadAndOptimizeImage()` behålls lokalt (körs efter microservice returnerat `imageUrl`)
- `IMPORTER_SERVICE_URL` env-variabel med fallback `http://importer-api:3001`
**6. Refaktorera `ReceiptImportService`** ✅
- AI-parsning (Mistral, pdf-parse) borttagen ur recipe-app
- Delegerar till `POST importer-api:3001/api/receipt-import/parse` (FormData)
- `matchProducts()` och `enrichWithAiCategories()` behålls (DB-krav)
- `RECEIPT_IMPORT_MODEL`-konstanten flyttad till `ai.controller.ts` (lokal konstant)
**7. Refaktorera `RecipesService.parseMarkdown()`** ✅
- Delegerar markdown-parsning till `POST importer-api:3001/api/recipes/parse-markdown`
- Fallback till lokal `parseRecipeMarkdown()` vid driftavbrott
- Levenshtein-produktmatchning behålls lokalt
---
## Fas 3 — Infrastruktur ✅
**8. `importer-api` i `recipe-app/compose.yml`** ✅
- Build-context: `../microservice-importer`, dockerfile `backend/Dockerfile`
- Image: `recipe-importer-api:local`, `pull_policy: never`
- Nätverk: `recipe-internal` (ej exponerad externt)
- Env: `MISTRAL_API_KEY`, `PORT=3001`
- Healthcheck: `wget -qO- http://127.0.0.1:3001/api/health`
- `recipe-api` får `depends_on: importer-api: condition: service_healthy`
---
## Relevanta filer som ändrades
| Fil | Förändring |
|---|---|
| `microservice-importer/backend/src/web-scraping-service/parsers/base.parser.ts` | `imageUrl?` i `ParsedRecipe` |
| `microservice-importer/backend/src/web-scraping-service/parsers/ica.parser.ts` | Extraherar `recipe.image` |
| `microservice-importer/backend/src/web-scraping-service/services/quick-import.service.ts` | Omskriven: OCR, PDF, imageUrl, importFromUpload |
| `microservice-importer/backend/src/web-scraping-service/controllers/quick-import.controller.ts` | FileInterceptor, HttpCode(200) |
| `microservice-importer/backend/src/web-scraping-service/web-scraping.module.ts` | Fixade importvägar + klassnamn |
| `microservice-importer/backend/src/document-service/document-service.module.ts` | Fixade importvägar + klassnamn |
| `microservice-importer/backend/src/document-service/services/document-import.service.ts` | Fixad parsersökväg |
| `microservice-importer/backend/src/receipt-parsing/` | Ny modul (service, controller, module) |
| `microservice-importer/backend/src/app.module.ts` | ReceiptParsingModule + HealthController |
| `microservice-importer/backend/Dockerfile` | apk add tesseract-ocr |
| `recipe-app/backend/src/quick-import/quick-import.service.ts` | Delegerar till importer-api |
| `recipe-app/backend/src/receipt-import/receipt-import.service.ts` | AI-del delegeras, matchning behålls |
| `recipe-app/backend/src/recipes/recipes.service.ts` | parseMarkdown delegeras, matchning behålls |
| `recipe-app/backend/src/ai/ai.controller.ts` | RECEIPT_IMPORT_MODEL lokal konstant |
| `recipe-app/compose.yml` | importer-api-tjänst tillagd |
---
## Avgränsningar (oförändrade)
- **Frontend ändras inte** — samma proxy-routes, samma API-kontrakt
- **Auth stannar i recipe-app backend** — microservice-importer exponeras bara internt
- **Bildoptimering** behålls i recipe-app (`downloadAndOptimizeImage` vid `RecipesService.create()`)
- `matchProducts()` och `enrichWithAiCategories()` stannar i recipe-app (DB-krav)
---
## Fas 1 — Utöka microservice-importer
*Steg 13 är oberoende och kan utföras parallellt.*
**1. Lägg till OCR-stöd (tesseract.js)**
Ny `ImageParser` i `backend/src/web-scraping-service/parsers/`. Controllern
`quick-import.controller.ts` utökas att acceptera `multipart/form-data` för
bilder vid sidan av JSON-body för URL-anrop.
**2. Lägg till `imageUrl` i quick-import-svaret**
`quick-import.service.ts` returnerar idag `{ markdown, source }`. Komplettera
med `imageUrl?` (original-URL från skrapad sida).
**3. Ny `ReceiptParsingModule` stateless kvittoparsning**
Ny modul `backend/src/receipt-parsing/` med endpoint `POST /api/receipt-import/parse`.
- PDF → text via `pdf-parse`; bild → base64
- Anropar Mistral AI med kvitto-prompt
- Returnerar: `[{ rawName, quantity, unit, price, brand, origin }]`
- Ingen databaskoppling — rent stateless
---
## Fas 2 — Anpassa recipe-app backend
*Beror på Fas 1. Steg 57 kan utföras parallellt.*
**4. Lägg till `HttpModule` + `IMPORTER_SERVICE_URL`**
recipe-app backend registrerar NestJS:s `HttpModule` (axios-wrapper).
`IMPORTER_SERVICE_URL` sätts som env-variabel (`http://importer-api:3001` i Docker).
**5. Refaktorera `QuickImportService`**
Ta bort lokal ICA-parsning, pdf-parse och tesseract — anropa istället
microservice-importer `POST /api/quick-import` (eller `POST /api/document-import`
för PDF). `QuickImportModule` behåller sin controller och DTO (API-kontrakt oförändrat).
**6. Refaktorera `ReceiptImportService`**
- AI-parsning → delegeras till `POST $IMPORTER_URL/api/receipt-import/parse`
- Produktmatchning (Levenshtein mot `Product`, `ReceiptAlias`) — behålls i recipe-app (DB-krav)
- Slår ihop och returnerar samma svar som idag till frontend
**7. Refaktorera `RecipesService.parseMarkdown()`**
- Anropar `POST $IMPORTER_URL/api/recipes/parse-markdown` → `{ name, ingredients[], ... }`
- Kör befintlig Levenshtein-produktmatchning mot `Product`-tabellen i recipe-app
- Returnerar sammansatt svar — API-kontraktet mot frontend oförändrat
**8. Ta bort lokala parsningsberoenden**
Ta bort `pdf-parse`, `tesseract.js`, `node-fetch` etc. ur recipe-app backend
`package.json` när steg 57 är verifierade.
---
## Fas 3 — Infrastruktur
*Kan påbörjas parallellt med Fas 1.*
**9. Länka microservice-importer i recipe-app:s Docker Compose**
Lägg till `importer-api`-tjänst i `recipe-app/compose.yml` (byggs från
`../microservice-importer/backend`). Delar `recipe-network` med recipe-app
backend. Sätt `IMPORTER_SERVICE_URL=http://importer-api:3001` i recipe-app
backend-tjänstens env.
---
## Relevanta filer
| Fil | Förändring |
|---|---|
| `microservice-importer/backend/src/web-scraping-service/` | Ny ImageParser, imageUrl i svar |
| `microservice-importer/backend/src/` | Ny `receipt-parsing/` modul |
| `recipe-app/backend/src/quick-import/quick-import.service.ts` | Ersätt lokal parsning med HTTP-anrop |
| `recipe-app/backend/src/receipt-import/receipt-import.service.ts` | AI-del delegeras, matchning behålls |
| `recipe-app/backend/src/recipes/recipes.service.ts` | parseMarkdown delegeras, matchning behålls |
| `recipe-app/backend/src/app.module.ts` | Registrera HttpModule |
| `recipe-app/backend/package.json` | Ta bort pdf-parse, tesseract.js |
| `recipe-app/compose.yml` | Lägg till importer-api tjänst |
| `recipe-app/frontend/` | **Ändras inte** |
---
## Verifiering
1. `POST /api/quick-import` (recipe-app backend) med ICA-URL → samma svar som idag
2. `POST /api/quick-import` med PDF-fil → samma svar
3. `POST /api/recipes/parse-markdown` med markdown → ingredienser med produkt-ID:n
4. `POST /api/receipt-import` med kvittobild → matchade items med DB-produkt-ID:n
5. Autentisering fungerar (hanteras av recipe-app backend som tidigare)
6. `docker compose up` startar microservice-importer som intern tjänst
---
## Avgränsningar
- **Frontend ändras inte** — samma proxy-routes, samma API-kontrakt
- **Auth stannar i recipe-app backend** — microservice-importer exponeras bara internt på Docker-nätverket
- **Bildoptimering vid sparande** behålls i recipe-app (sker vid `RecipesService.create()`, inte vid import)
- `receipt-import` splittad: AI-del → microservice, produktmatchning + DB → recipe-app backend
## 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.
## 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.
+52
View File
@@ -0,0 +1,52 @@
# Plan for produktlansering
Detta dokument ar en releasechecklista.
Det kompletterar `NEXT_STEPS.md` och ska inte duplicera backloggen.
## Dokumentstatus (2026-05-03)
- Malgrupp: produktagare, systemadministratorer, utvecklingsteam.
- Fokus: vad som maste vara verifierat innan release.
## 1. Sakerhet och data
- [ ] Kansliga uppgifter krypterade enligt beslutad modell.
- [ ] Rate limiting aktiv pa relevanta API/AI-endpoints.
- [ ] Secret-hantering verifierad (inga hardkodade hemligheter).
- [ ] Roll- och accesskontroller testade i praktiken.
## 2. DevOps och stabilitet
- [ ] CI/CD for build, test och deploy pa plats.
- [ ] Migreringar + seedning kor konsekvent vid release.
- [ ] Health checks och loggning verifierade.
- [ ] Backup/restore testad for datavolymer.
## 3. Kvalitet och test
- [ ] Kritiska floden har testtackning (auth, import, CRUD, AI).
- [ ] Minst en end-to-end verifiering i testmiljo per release.
- [ ] DTO-validering och felhantering kontrollerad.
## 4. Funktionell releaseklarhet
- [ ] Kvittoimport fungerar end-to-end med granskningssteg.
- [ ] User-scoped produktmodell verifierad med flera testanvandare.
- [ ] Kategoritrad seedat och validerat i aktuell miljo.
- [ ] Bildimport och fallbackfloden fungerar i driftmiljo.
## 5. Riskhantering
- [ ] AI-kostnad, timeout och fallback beteende verifierat.
- [ ] Ingen osynk mellan migrationer och seedskript.
- [ ] Kanda release-risker dokumenterade med ansvarig agare.
## Relaterade dokument
- `NEXT_STEPS.md` - overgripande prioriteringar.
- `TEKNISK_BESKRIVNING.md` - teknisk implementation.
- `flutter/next_steps_flutter.md` - Flutter-specifik leveransplan.
## 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.
## 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.
+104
View File
@@ -0,0 +1,104 @@
# Plan för systematisk backend-review och optimering
## Mål
1. Minska komplexitet och duplicering.
2. Förbättra prestanda och stabilitet.
3. Göra koden enklare att underhålla och vidareutveckla.
4. Införa kvalitetsgrindar så förbättringar håller över tid.
## Arbetssätt
1. Jobba i små, säkra iterationer per modul/domän.
2. Mät före och efter varje förändring.
3. Lås upp förbättringar med tester och CI-gates.
4. Prioritera förändringar med hög effekt och låg risk först.
## Fas 1: Baslinje och kartläggning (1 vecka)
1. Inventera backend per modul:
- Endpoints, tjänster, databasaccess, externa integrationer.
2. Sätt baslinjemätningar:
- Responstider per kritisk endpoint (p50/p95), felgrad, DB-latens.
- Nuvarande testtäckning per modul.
3. Skapa hotspot-lista:
- Långa metoder, hög cyclomatic complexity, duplicerad logik, N+1-frågor.
4. Leverabel:
- Prioriterad backlog med topp 10 förbättringsområden.
## Fas 2: Snabba vinster och kodhygien (1-2 veckor)
1. Standardisera felhantering:
- Enhetlig exception mapping och API-felmodell.
2. Rensa duplicerad kod:
- Flytta gemensam logik till tydliga utilities/domänservices.
3. Förbättra validering:
- Konsekvent DTO/valideringslager in och ut.
4. Inför striktare lint-regler:
- Max function length, complexity-tak, no-dead-code.
5. Leverabel:
- Minskad kodvolym i hotspots och jämnare kodstandard.
## Fas 3: Arkitektur-förenkling (2-3 veckor)
1. Tydlig separering av lager:
- Controller = transport.
- Service = affärslogik.
- Repository/data layer = persistens.
2. Minska beroendekoppling:
- Ta bort korsberoenden mellan moduler.
3. Inför tydliga domängränser:
- En modul ska kunna förstås utan att läsa flera andra.
4. Leverabel:
- Enklare call-flöden och färre starkt kopplade beroenden.
## Fas 4: Databas och prestanda (1-2 veckor)
1. Granska tunga queries:
- N+1, överhämtning, saknade index, ineffektiva joins.
2. Förbättra dataåtkomst:
- Standard för pagination/filtering.
- Selektiv hämtning av fält.
3. Caching där det är motiverat:
- Endast för dyra och frekventa läsningar.
4. Leverabel:
- Mätbar förbättring i p95 och minskad DB-belastning.
## Fas 5: Teststrategi och regressionsskydd (1-2 veckor, löpande)
1. Lägg tester där risk och affärsvärde är högst:
- Kritiska use cases först.
2. Balans i testpyramiden:
- Fler enhetstester för domänlogik.
- Fokuserade integrations- och API-tester för flöden.
3. Kontrakttester för externa integrationer.
4. Leverabel:
- Högre täckning i kritiska moduler och färre regressionsbuggar.
## Fas 6: Säkerhet och driftbarhet (parallellt)
1. Säkerhetsgranskning:
- Input-validering, auth/role-kontroller, secret-hantering.
- Inventory är nu user-scopad och IDOR-skyddad: Alla inventory-operationer kräver och filtrerar på userId i backend (schema, migration, service, controller, tester). Tester verifierar att åtkomst nekas vid försök till IDOR.
2. Driftbarhet:
- Strukturerad loggning, korrelations-id, tydligare metrics.
3. Resiliens:
- Timeout/retry/circuit-breaker för externa beroenden.
4. Leverabel:
- Färre driftincidenter och enklare felsökning.
## Kvalitetsgrindar i CI
1. Build och lint måste passera.
2. Tester måste passera.
3. Miniminivå för täckning i ändrade moduler.
4. Blockera PR vid ökad komplexitet över satt tröskel.
5. Enkel performance-smoke på kritiska endpoints.
## Prioriteringsmodell för varje förbättring
1. Effekt: prestanda, stabilitet, underhållbarhet.
2. Risk: regressionsrisk och driftsrisk.
3. Insats: utvecklingstid.
4. Välj först: hög effekt + låg/medel risk + låg/medel insats.
## Definition of Done
1. Kritiska endpoints har förbättrad p95.
2. Topp-hotspots är refaktorerade eller borttagna.
3. Kodduplicering reducerad i prioriterade moduler.
4. Testskydd finns för alla kritiska flöden.
5. CI-gates förhindrar att kvaliteten glider tillbaka.
## 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.
## 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.