security: Add proper authentication, RBAC, and tenant isolation
- Add password hashing with bcrypt - Add AuthService with proper login - Add password strength validation - Add RBAC middleware (AdminOnly, ManagerOrAdmin) - Add tenant isolation middleware - Update CRM handler with tenant filtering - Add JWT fallback for development mode - Add user context helpers - Build successful
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
# quiXzoom — Smart Scan Architecture
|
||||
*Beslutad: 2026-06-17. Ersätter "fotoflöde"-tänket.*
|
||||
|
||||
---
|
||||
|
||||
## Paradigmskiftet
|
||||
|
||||
| Fotoflöde (v1/v2) | Smart Scan |
|
||||
|-------------------|------------|
|
||||
| Zoomern tar bilder | Zoomern skannar ett objekt |
|
||||
| AI bedömer varje bild | AI bygger upp ett bevispaket |
|
||||
| Kvalitetsansvar: Zoomern | Kvalitetsansvar: systemet |
|
||||
| Input: foto | Input: kontinuerlig videoström |
|
||||
| Output: godkänd/nekad bild | Output: verifierad kontrollpunkt |
|
||||
| Känsla: formulär | Känsla: Face ID / 3D-skanner |
|
||||
|
||||
**Kärninsikt:** Zoomern förstår inte uppdraget. AI vet redan vilka datapunkter som krävs, vilka vinklar som behövs, vilka detaljer som måste vara synliga, och när bevisningen är tillräcklig.
|
||||
|
||||
Zoomerns enda uppgift: *"Peka kameran mot objektet och följ instruktionerna."*
|
||||
|
||||
---
|
||||
|
||||
## Arkitektur
|
||||
|
||||
### Evidenspaketet (ersätter "foto")
|
||||
|
||||
Varje kontrollpunkt definierar ett `evidence_requirements`-objekt:
|
||||
|
||||
```json
|
||||
{
|
||||
"control_point": "electricity_meter",
|
||||
"evidence_requirements": [
|
||||
{ "id": "e1", "label": "Översikt", "type": "visual_coverage", "min_coverage_pct": 85 },
|
||||
{ "id": "e2", "label": "Serienummer", "type": "ocr", "target": "serial_number" },
|
||||
{ "id": "e3", "label": "Mätarställning","type": "ocr", "target": "meter_reading" },
|
||||
{ "id": "e4", "label": "Plombering", "type": "presence_check", "target": "seal" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Kontrollpunkten är **inte klar** förrän alla `evidence_requirements` är uppfyllda.
|
||||
Det spelar ingen roll hur många frames det tar.
|
||||
|
||||
---
|
||||
|
||||
### Scanloopen (on-device, kontinuerlig)
|
||||
|
||||
```
|
||||
Videoström
|
||||
│
|
||||
▼
|
||||
Frame-sampler (~5fps)
|
||||
│
|
||||
▼
|
||||
Evidence Matcher
|
||||
├─ e1 täckt? → ja (frame 142)
|
||||
├─ e2 läsbar? → ja (frame 198)
|
||||
├─ e3 läsbar? → nej → direktiv: "Visa displayen"
|
||||
└─ e4 synlig? → nej → direktiv: "Visa plomberingen"
|
||||
│
|
||||
▼
|
||||
En direktiv åt gången → AR-overlay
|
||||
│
|
||||
▼ (när alla ej uppfyllda)
|
||||
Bästa frame per requirement väljs ut
|
||||
│
|
||||
▼
|
||||
All clear → "Kontrollpunkt klar ✓"
|
||||
```
|
||||
|
||||
**Systemet väljer frames** — Zoomern fattar aldrig ett fotograferingsbeslut.
|
||||
|
||||
---
|
||||
|
||||
### Frame-selektion
|
||||
|
||||
När ett requirement är uppfyllt sparas den bästa frame:
|
||||
|
||||
```json
|
||||
{
|
||||
"requirement_id": "e2",
|
||||
"frame_number": 198,
|
||||
"timestamp_ms": 4820,
|
||||
"confidence": 0.94,
|
||||
"extracted_value": "SE-4821-9938-01",
|
||||
"selected": true
|
||||
}
|
||||
```
|
||||
|
||||
Systemet väljer den frame med högst confidence inom ett kvalitetsfönster (skärpa, ljus, täckning). Aldrig den sista — den bästa.
|
||||
|
||||
---
|
||||
|
||||
### Evidenspaketet som skickas till server
|
||||
|
||||
Inte ett foto. Ett paket:
|
||||
|
||||
```json
|
||||
{
|
||||
"control_point": "electricity_meter",
|
||||
"session_id": "sess_abc123",
|
||||
"zoomer_id": "z_9981",
|
||||
"geo": { "lat": 59.334, "lng": 18.063, "accuracy_m": 4 },
|
||||
"device_id": "dev_ios_xyz",
|
||||
"scan_duration_ms": 12400,
|
||||
"evidence": [
|
||||
{ "requirement_id": "e1", "frame": "frame_142.jpg", "confidence": 0.97 },
|
||||
{ "requirement_id": "e2", "frame": "frame_198.jpg", "confidence": 0.94, "value": "SE-4821-9938-01" },
|
||||
{ "requirement_id": "e3", "frame": "frame_265.jpg", "confidence": 0.91, "value": "04821.3" },
|
||||
{ "requirement_id": "e4", "frame": "frame_302.jpg", "confidence": 0.88 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Server-AVO verifierar paketet. Inte enskilda bilder.
|
||||
|
||||
---
|
||||
|
||||
### Server-AVO på evidenspaketet
|
||||
|
||||
```json
|
||||
{
|
||||
"phase": "evidence_review",
|
||||
"control_point": "electricity_meter",
|
||||
"decision": "approved",
|
||||
"confidence": 0.942,
|
||||
"evidence_summary": {
|
||||
"e1": { "status": "verified", "confidence": 0.97 },
|
||||
"e2": { "status": "verified", "confidence": 0.94, "value": "SE-4821-9938-01" },
|
||||
"e3": { "status": "verified", "confidence": 0.91, "value": "04821.3" },
|
||||
"e4": { "status": "verified", "confidence": 0.88 }
|
||||
},
|
||||
"deviations": [],
|
||||
"summary": "🟢 Elmätare verifierad. Alla 4 datapunkter insamlade."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## reject_class i Smart Scan
|
||||
|
||||
reject_class kvarstår men beter sig annorlunda:
|
||||
|
||||
| reject_class | Situation | Åtgärd |
|
||||
|-------------|-----------|--------|
|
||||
| `capture` | Requirement kan inte uppfyllas pga scan-kvalitet (för mörkt, för ostadigt) | Direktiv, fortsätt scanning |
|
||||
| `content` | Requirement kan aldrig uppfyllas (skylt förstörd, mätare plomberad på fel sätt) | Stoppa, registrera avvikelse, eskalera |
|
||||
| `evidence_incomplete` | Scanning avbröts innan alla krav uppfylldes | Återuppta scanning |
|
||||
|
||||
---
|
||||
|
||||
## UX-känslan
|
||||
|
||||
### Traditionellt fotoflöde
|
||||
```
|
||||
Öppna kamera → ta bild → vänta → underkänt → ta om
|
||||
```
|
||||
|
||||
### Smart Scan
|
||||
```
|
||||
Öppna kontrollpunkt → kamera startar → följ guidning →
|
||||
"Kontrollpunkt klar ✓" → nästa
|
||||
```
|
||||
|
||||
Det närmaste analogin är **Face ID** — inte "ta ett foto av ditt ansikte", bara "titta mot telefonen".
|
||||
|
||||
Eller en **3D-skanner** — inte "ta 12 foton från dessa vinklar", bara "rör kameran runt objektet".
|
||||
|
||||
---
|
||||
|
||||
## Vad det innebär för skala
|
||||
|
||||
**Gammalt:** Uppdragsgivaren definierar bildkrav → Zoomer tolkar → kvalitetsvariation
|
||||
**Nytt:** Uppdragsgivaren definierar datakrav → AI samlar in → konsekvent kvalitet
|
||||
|
||||
Uppdragsgivaren behöver inte längre tänka i bilder. De anger:
|
||||
- Vilka värden de behöver (serienummer, mätarställning, skadetyp)
|
||||
- Vilka element som måste vara synliga (plombering, skylt, fasad)
|
||||
|
||||
AI:n avgör hur det samlas in.
|
||||
|
||||
---
|
||||
|
||||
## Teknisk implementation
|
||||
|
||||
| Komponent | Ansvar |
|
||||
|-----------|--------|
|
||||
| Frame-sampler | ~5fps från videoström, on-device |
|
||||
| Evidence Matcher | Per-frame check mot requirements, TF Lite / CoreML |
|
||||
| AR Directive Layer | En instruktion åt gången, uppdateras per frame |
|
||||
| Frame Store | Temporär ring-buffer, behåller kandidat-frames |
|
||||
| Frame Selector | Väljer bästa frame per requirement vid completion |
|
||||
| Evidence Packager | Bygger JSON-paketet för server-upload |
|
||||
| Server AVO | Full verifiering, OCR-validering, avvikelseanalys |
|
||||
| Audit Trail | `image_hash + geo + timestamp + device_id` per frame |
|
||||
|
||||
---
|
||||
|
||||
## Öppna frågor (uppdaterade)
|
||||
|
||||
1. **Liveness / anti-spoofing** — hur verifierar vi att videoströmmen är levande och inte en inspelning eller skärmdump? Device attestation (iOS App Attest / Android Play Integrity)?
|
||||
2. **Frame-kvalitetströskel** — vilken minsta confidence krävs för att en frame ska väljas? Konfigurerbart per requirement?
|
||||
3. **Offline scanning** — frames buffras lokalt, paket skickas vid uppkoppling. Max buffer-storlek?
|
||||
4. **GDPR / ansikten** — scanning av fasader och miljöer fångar troligen ansikten. Face blurring before upload?
|
||||
5. **Scan-timeout** — max tid per kontrollpunkt? (förslag: 120 sek → eskalera)
|
||||
6. **Multi-objekt** — kan en scanning-session samla bevis för flera kontrollpunkter parallellt?
|
||||
|
||||
---
|
||||
|
||||
*Ersätter QUIXZOOM_CAPTURE_FLOW.md och AVO-masterprompt v1/v2 som konceptuellt underlag.*
|
||||
*AVO-masternprompt v2 gäller fortfarande som tekniskt kontrakt för fas B (server-AVO).*
|
||||
Reference in New Issue
Block a user