πŸ“š Documentation

# Property Valuation Portal - Architecture Overview ## System Architecture Diagram ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CLIENT LAYER β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Desktop Browser β”‚ β”‚ Mobile Browser (PWA) β”‚ β”‚ β”‚ β”‚ - Chrome, Firefox, β”‚ β”‚ - Android, iOS β”‚ β”‚ β”‚ β”‚ Safari, Edge β”‚ β”‚ - Offline Support β”‚ β”‚ β”‚ β”‚ - React SPA β”‚ β”‚ - Service Worker β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ HTTPS β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CDN / CLOUDFLARE LAYER β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ - Static Assets Delivery (JS, CSS, Images) β”‚ β”‚ - SSL/TLS Termination β”‚ β”‚ - DDoS Protection β”‚ β”‚ - Caching β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ API GATEWAY LAYER β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ - Rate Limiting β”‚ β”‚ - JWT Token Validation β”‚ β”‚ - Request Logging β”‚ β”‚ - Load Balancing β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ APPLICATION LAYER β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Fastify Backend (Node.js) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Auth β”‚ β”‚ Valuation β”‚ β”‚ Document β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Photo β”‚ β”‚ Workflow β”‚ β”‚ Report β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ User β”‚ β”‚ Admin β”‚ β”‚ Notificationβ”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ DATA LAYER β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ PostgreSQL 18+ DB β”‚ β”‚ Object Storage β”‚ β”‚ β”‚ β”‚ + PostGIS Extension β”‚ β”‚ (S3-compatible) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Valuations β”‚ β”‚ - Documents (PDF, DOC) β”‚ β”‚ β”‚ β”‚ - Properties β”‚ β”‚ - Photos (JPG, PNG) β”‚ β”‚ β”‚ β”‚ - Users & Roles β”‚ β”‚ - Generated Reports β”‚ β”‚ β”‚ β”‚ - Workflow States β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Audit Logs β”‚ β”‚ Signed URLs β”‚ β”‚ β”‚ β”‚ - GeoSpatial Data β”‚ β”‚ CDN Delivery β”‚ β”‚ β”‚ β”‚ (Boundaries, Points) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ EXTERNAL INTEGRATIONS β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Google Maps β”‚ β”‚ Email/SMS β”‚ β”‚ Bank APIs β”‚ β”‚ β”‚ β”‚ API β”‚ β”‚ Gateway β”‚ β”‚ (Future) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## Component Architecture ### Frontend Architecture (React SPA) ``` src/ β”œβ”€β”€ components/ # Reusable UI components β”‚ β”œβ”€β”€ common/ # Common components (Button, Input, Card) β”‚ β”œβ”€β”€ layout/ # Layout components (Header, Sidebar, Footer) β”‚ β”œβ”€β”€ forms/ # Form components (ValuationForm, etc.) β”‚ β”œβ”€β”€ maps/ # Map components (MapView, BoundaryDrawer) β”‚ └── charts/ # Chart components (Dashboard charts) β”‚ β”œβ”€β”€ pages/ # Page components (one per screen) β”‚ β”œβ”€β”€ Dashboard/ β”‚ β”œβ”€β”€ Valuations/ β”‚ β”‚ β”œβ”€β”€ ValuationList.tsx β”‚ β”‚ β”œβ”€β”€ CreateValuation.tsx β”‚ β”‚ β”œβ”€β”€ BasicInformation.tsx β”‚ β”‚ β”œβ”€β”€ PropertyDetails.tsx β”‚ β”‚ β”œβ”€β”€ SiteMap.tsx β”‚ β”‚ └── ValuationCalculation.tsx β”‚ β”œβ”€β”€ Documents/ β”‚ β”œβ”€β”€ Photos/ β”‚ β”œβ”€β”€ Review/ β”‚ β”œβ”€β”€ Approval/ β”‚ β”œβ”€β”€ Reports/ β”‚ └── Admin/ β”‚ β”œβ”€β”€ hooks/ # Custom React hooks β”‚ β”œβ”€β”€ useAuth.ts β”‚ β”œβ”€β”€ useValuation.ts β”‚ β”œβ”€β”€ useWorkflow.ts β”‚ └── useMap.ts β”‚ β”œβ”€β”€ services/ # API service layer β”‚ β”œβ”€β”€ api.ts # Base API client (Axios/Fetch) β”‚ β”œβ”€β”€ authService.ts β”‚ β”œβ”€β”€ valuationService.ts β”‚ β”œβ”€β”€ documentService.ts β”‚ β”œβ”€β”€ photoService.ts β”‚ └── adminService.ts β”‚ β”œβ”€β”€ stores/ # State management (Context API or Zustand) β”‚ β”œβ”€β”€ authStore.ts β”‚ β”œβ”€β”€ valuationStore.ts β”‚ └── uiStore.ts β”‚ β”œβ”€β”€ types/ # TypeScript type definitions β”‚ β”œβ”€β”€ valuation.types.ts β”‚ β”œβ”€β”€ user.types.ts β”‚ β”œβ”€β”€ workflow.types.ts β”‚ └── api.types.ts β”‚ β”œβ”€β”€ utils/ # Utility functions β”‚ β”œβ”€β”€ validators.ts # Form validation functions β”‚ β”œβ”€β”€ formatters.ts # Data formatters β”‚ β”œβ”€β”€ calculations.ts # Valuation calculations β”‚ └── helpers.ts # Helper functions β”‚ β”œβ”€β”€ constants/ # Constants and enums β”‚ β”œβ”€β”€ workflow.constants.ts β”‚ β”œβ”€β”€ validation.constants.ts β”‚ └── routes.constants.ts β”‚ └── App.tsx # Main app component ``` --- ### Backend Architecture (Fastify) ``` src/ β”œβ”€β”€ modules/ # Feature modules β”‚ β”œβ”€β”€ auth/ β”‚ β”‚ β”œβ”€β”€ auth.controller.ts β”‚ β”‚ β”œβ”€β”€ auth.service.ts β”‚ β”‚ β”œβ”€β”€ auth.routes.ts β”‚ β”‚ └── auth.schema.ts # Zod validation schemas β”‚ β”‚ β”‚ β”œβ”€β”€ valuations/ β”‚ β”‚ β”œβ”€β”€ valuations.controller.ts β”‚ β”‚ β”œβ”€β”€ valuations.service.ts β”‚ β”‚ β”œβ”€β”€ valuations.routes.ts β”‚ β”‚ └── valuations.schema.ts β”‚ β”‚ β”‚ β”œβ”€β”€ documents/ β”‚ β”œβ”€β”€ photos/ β”‚ β”œβ”€β”€ workflow/ β”‚ β”œβ”€β”€ reports/ β”‚ β”œβ”€β”€ users/ β”‚ └── admin/ β”‚ β”œβ”€β”€ database/ # Database layer β”‚ β”œβ”€β”€ client.ts # Database client (Prisma/Drizzle) β”‚ β”œβ”€β”€ migrations/ # Database migrations β”‚ β”œβ”€β”€ seeds/ # Seed data β”‚ └── schema/ # Database schema definitions β”‚ β”œβ”€β”€ middleware/ # Middleware functions β”‚ β”œβ”€β”€ auth.middleware.ts # JWT validation β”‚ β”œβ”€β”€ rbac.middleware.ts # Role-based access control β”‚ β”œβ”€β”€ logging.middleware.ts # Request logging β”‚ └── error.middleware.ts # Error handling β”‚ β”œβ”€β”€ services/ # Shared services β”‚ β”œβ”€β”€ storage.service.ts # File storage (S3) β”‚ β”œβ”€β”€ email.service.ts # Email notifications β”‚ β”œβ”€β”€ sms.service.ts # SMS notifications β”‚ └── maps.service.ts # Google Maps integration β”‚ β”œβ”€β”€ utils/ # Utility functions β”‚ β”œβ”€β”€ validators.ts β”‚ β”œβ”€β”€ calculations.ts β”‚ β”œβ”€β”€ formatters.ts β”‚ └── helpers.ts β”‚ β”œβ”€β”€ types/ # TypeScript type definitions β”‚ └── index.ts β”‚ β”œβ”€β”€ config/ # Configuration β”‚ β”œβ”€β”€ database.config.ts β”‚ β”œβ”€β”€ storage.config.ts β”‚ └── env.config.ts β”‚ └── index.ts # Application entry point ``` --- ## Database Schema Overview ### Core Tables ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ VALUATIONS (Main entity) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ valuation_number (Text, Unique, Indexed) β”‚ β”‚ bank_id (FK β†’ banks) β”‚ β”‚ property_type (Enum: plot/independent_house/apartment) β”‚ β”‚ status (Enum: draft/survey/submitted/review/approval/approved) β”‚ β”‚ priority (Enum: high/medium/low) β”‚ β”‚ created_by (FK β†’ users) β”‚ β”‚ assigned_surveyor (FK β†’ users) β”‚ β”‚ assigned_keyin (FK β†’ users) β”‚ β”‚ assigned_reviewer (FK β†’ users) β”‚ β”‚ assigned_approver (FK β†’ users) β”‚ β”‚ created_at (Timestamp) β”‚ β”‚ updated_at (Timestamp) β”‚ β”‚ submitted_at (Timestamp) β”‚ β”‚ approved_at (Timestamp) β”‚ β”‚ ... (workflow timestamps) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”œβ”€β”€β”€ 1:1 ───┐ β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PROPERTIES β”‚ β”‚ APPLICANTS β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ id (UUID, PK) β”‚ β”‚ valuation_id (FK) β”‚ β”‚ valuation_id (FK) β”‚ β”‚ address β”‚ β”‚ applicant_name β”‚ β”‚ state_id (FK) β”‚ β”‚ co_applicant_name β”‚ β”‚ district_id (FK) β”‚ β”‚ owner_name β”‚ β”‚ mandal_id (FK) β”‚ β”‚ contact_mobile β”‚ β”‚ village_id (FK) β”‚ β”‚ contact_email β”‚ β”‚ pincode β”‚ β”‚ representative_name β”‚ β”‚ latitude β”‚ β”‚ ... β”‚ β”‚ longitude β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ survey_number β”‚ β”‚ plot_number β”‚ β”‚ house_number β”‚ β”‚ ... (location data) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”œβ”€β”€β”€ 1:1 ───┐ β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PROPERTY_DETAILS β”‚ β”‚ BOUNDARIES β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ id (UUID, PK) β”‚ β”‚ property_id (FK) β”‚ β”‚ property_id (FK) β”‚ β”‚ shape_of_site β”‚ β”‚ boundary_polygon β”‚ β”‚ level β”‚ β”‚ (PostGIS Geometry)β”‚ β”‚ development_status β”‚ β”‚ boundary_geojson β”‚ β”‚ infrastructure β”‚ β”‚ (JSON) β”‚ β”‚ locality_class β”‚ β”‚ north_as_per_doc β”‚ β”‚ locality_type β”‚ β”‚ south_as_per_doc β”‚ β”‚ ... β”‚ β”‚ east_as_per_doc β”‚ β”‚ (site characteristics) β”‚ west_as_per_doc β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ north_actual β”‚ β”‚ south_actual β”‚ β”‚ east_actual β”‚ β”‚ west_actual β”‚ β”‚ ... β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ VALUATION_DATA β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ valuation_id (FK β†’ valuations) β”‚ β”‚ valuation_method (Enum: land_and_building/composite) β”‚ β”‚ land_unit (Enum: acres_guntas/acres_cents/sqmtrs/sqyds/sqft) β”‚ β”‚ land_area_document (Decimal) β”‚ β”‚ land_area_plan (Decimal) β”‚ β”‚ land_area_actual (Decimal) β”‚ β”‚ land_area_adopted (Decimal) β”‚ β”‚ road_effected_area (Decimal) β”‚ β”‚ nala_effected_area (Decimal) β”‚ β”‚ splay_area (Decimal) β”‚ β”‚ buffer_area (Decimal) β”‚ β”‚ net_land_area (Decimal) β”‚ β”‚ land_rate_type (Enum: guideline/market) β”‚ β”‚ land_rate (Decimal) β”‚ β”‚ land_value (Decimal) -- calculated β”‚ β”‚ building_area_plan (Decimal) β”‚ β”‚ building_area_actual (Decimal) β”‚ β”‚ building_area_adopted (Decimal) β”‚ β”‚ building_rate (Decimal) β”‚ β”‚ building_age (Integer) β”‚ β”‚ depreciation_percentage (Decimal) β”‚ β”‚ building_value (Decimal) -- calculated β”‚ β”‚ total_property_value (Decimal) -- calculated β”‚ β”‚ distress_sale_value (Decimal) -- calculated β”‚ β”‚ forced_sale_value (Decimal) -- calculated β”‚ β”‚ realizable_value (Decimal) -- calculated β”‚ β”‚ created_at (Timestamp) β”‚ β”‚ updated_at (Timestamp) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Supporting Tables ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ DOCUMENTS β”‚ β”‚ PHOTOS β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ id (UUID, PK) β”‚ β”‚ valuation_id (FK) β”‚ β”‚ valuation_id (FK) β”‚ β”‚ document_type_id(FK)β”‚ β”‚ category β”‚ β”‚ document_name β”‚ β”‚ caption β”‚ β”‚ file_name β”‚ β”‚ file_name β”‚ β”‚ file_path (S3 URL) β”‚ β”‚ file_path (S3 URL) β”‚ β”‚ file_size β”‚ β”‚ thumbnail_path β”‚ β”‚ version_number β”‚ β”‚ latitude β”‚ β”‚ parent_document_id β”‚ β”‚ longitude β”‚ β”‚ uploaded_by (FK) β”‚ β”‚ gps_accuracy β”‚ β”‚ uploaded_at β”‚ β”‚ exif_data (JSON) β”‚ β”‚ status β”‚ β”‚ annotations (JSON) β”‚ β”‚ ... β”‚ β”‚ uploaded_by (FK) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ uploaded_at β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ COMMENTS β”‚ β”‚ ACTIVITY_LOGS β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ id (UUID, PK) β”‚ β”‚ valuation_id (FK) β”‚ β”‚ valuation_id (FK) β”‚ β”‚ parent_comment_id β”‚ β”‚ user_id (FK) β”‚ β”‚ comment_text β”‚ β”‚ action_type β”‚ β”‚ comment_type β”‚ β”‚ action_description β”‚ β”‚ created_by (FK) β”‚ β”‚ entity_type β”‚ β”‚ created_at β”‚ β”‚ entity_id β”‚ β”‚ ... β”‚ β”‚ old_value (JSON) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ new_value (JSON) β”‚ β”‚ ip_address β”‚ β”‚ user_agent β”‚ β”‚ created_at β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Reference Tables ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ BANKS β”‚ β”‚ USERS β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ id (UUID, PK) β”‚ β”‚ bank_name β”‚ β”‚ employee_id (Unique)β”‚ β”‚ bank_code (Unique) β”‚ β”‚ email (Unique) β”‚ β”‚ logo_url β”‚ β”‚ password_hash β”‚ β”‚ sla_days β”‚ β”‚ first_name β”‚ β”‚ multi_level_approvalβ”‚ β”‚ last_name β”‚ β”‚ contact_person β”‚ β”‚ mobile β”‚ β”‚ email β”‚ β”‚ primary_role_id(FK) β”‚ β”‚ status β”‚ β”‚ team_id (FK) β”‚ β”‚ ... β”‚ β”‚ status β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ last_login_at β”‚ β”‚ ... β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ BANK_CONFIGS β”‚ β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ M:M β”‚ id (UUID, PK) β”‚ β–Ό β”‚ bank_id (FK) β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ config_type β”‚ β”‚ USER_ROLES β”‚ β”‚ config_data (JSON) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ ... β”‚ β”‚ user_id (FK) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ role_id (FK) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ LOCATIONS β”‚ β–Ό β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ id (UUID, PK) β”‚ β”‚ ROLES β”‚ β”‚ location_type β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ parent_id (FK) β”‚ β”‚ id (UUID, PK) β”‚ β”‚ location_name β”‚ β”‚ role_name β”‚ β”‚ location_code β”‚ β”‚ role_description β”‚ β”‚ status β”‚ β”‚ permissions (JSON) β”‚ β”‚ ... β”‚ β”‚ ... β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ RATES β”‚ β”‚ LOOKUP_VALUES β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ id (UUID, PK) β”‚ β”‚ id (UUID, PK) β”‚ β”‚ location_id (FK) β”‚ β”‚ category β”‚ β”‚ rate_type β”‚ β”‚ value β”‚ β”‚ rate_per_unit β”‚ β”‚ display_order β”‚ β”‚ unit β”‚ β”‚ parent_id (FK) β”‚ β”‚ effective_from β”‚ β”‚ status β”‚ β”‚ effective_to β”‚ β”‚ ... β”‚ β”‚ ... β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## API Endpoints Overview ### Authentication ``` POST /api/v1/auth/login # User login POST /api/v1/auth/logout # User logout POST /api/v1/auth/refresh # Refresh JWT token POST /api/v1/auth/forgot-password # Forgot password POST /api/v1/auth/reset-password # Reset password GET /api/v1/auth/me # Get current user info ``` ### Valuations ``` GET /api/v1/valuations # List valuations (filtered) POST /api/v1/valuations # Create new valuation GET /api/v1/valuations/:id # Get valuation details PUT /api/v1/valuations/:id # Update valuation DELETE /api/v1/valuations/:id # Delete valuation (soft) POST /api/v1/valuations/:id/submit # Submit for next stage POST /api/v1/valuations/:id/clone # Clone valuation GET /api/v1/valuations/:id/history # Get change history GET /api/v1/valuations/my-queue # Get user's work queue ``` ### Property Details ``` GET /api/v1/valuations/:id/property # Get property details PUT /api/v1/valuations/:id/property # Update property details GET /api/v1/valuations/:id/boundaries # Get boundary data PUT /api/v1/valuations/:id/boundaries # Update boundary data POST /api/v1/valuations/:id/calculate # Calculate valuation ``` ### Documents ``` GET /api/v1/valuations/:id/documents # List documents POST /api/v1/valuations/:id/documents # Upload document GET /api/v1/documents/:docId # Get document details GET /api/v1/documents/:docId/download # Download document PUT /api/v1/documents/:docId # Update document metadata DELETE /api/v1/documents/:docId # Delete document GET /api/v1/documents/:docId/versions # Get version history ``` ### Photos ``` GET /api/v1/valuations/:id/photos # List photos POST /api/v1/valuations/:id/photos # Upload photo GET /api/v1/photos/:photoId # Get photo details PUT /api/v1/photos/:photoId # Update photo metadata DELETE /api/v1/photos/:photoId # Delete photo PUT /api/v1/photos/:photoId/annotations # Save annotations ``` ### Workflow ``` GET /api/v1/workflow/stages # Get workflow stages POST /api/v1/valuations/:id/review # Submit review POST /api/v1/valuations/:id/approve # Approve valuation POST /api/v1/valuations/:id/reject # Reject valuation POST /api/v1/valuations/:id/query # Raise query POST /api/v1/valuations/:id/reassign # Reassign valuation GET /api/v1/workflow/pending-reviews # Get pending reviews GET /api/v1/workflow/pending-approvals # Get pending approvals ``` ### Reports ``` GET /api/v1/reports/dashboard # Get dashboard data POST /api/v1/reports/generate # Generate custom report GET /api/v1/reports/templates # List report templates GET /api/v1/reports/saved # List saved reports POST /api/v1/reports/export # Export report (PDF/Excel) ``` ### Users & Admin ``` GET /api/v1/users # List users POST /api/v1/users # Create user GET /api/v1/users/:id # Get user details PUT /api/v1/users/:id # Update user DELETE /api/v1/users/:id # Deactivate user GET /api/v1/roles # List roles GET /api/v1/roles/:id/permissions # Get role permissions PUT /api/v1/roles/:id/permissions # Update permissions ``` ### Reference Data ``` GET /api/v1/banks # List banks GET /api/v1/locations # List locations (hierarchical) GET /api/v1/rates # List rates GET /api/v1/lookups/:category # Get lookup values GET /api/v1/config/system # Get system config PUT /api/v1/config/system # Update system config ``` --- ## Security Architecture ### Authentication Flow ``` 1. User Login β”œβ”€ POST /api/v1/auth/login {email, password} β”œβ”€ Backend validates credentials β”œβ”€ Generate JWT token (access + refresh) └─ Return tokens + user info 2. Authenticated Request β”œβ”€ Client adds JWT token in Authorization header β”œβ”€ API Gateway validates token β”œβ”€ Decode token β†’ Extract user ID + role β”œβ”€ RBAC middleware checks permissions └─ Allow/Deny request 3. Token Refresh β”œβ”€ Access token expires (30 min) β”œβ”€ Client uses refresh token β”œβ”€ POST /api/v1/auth/refresh {refresh_token} └─ Backend issues new access token 4. Two-Factor Authentication (Optional) β”œβ”€ After login, require 2FA code β”œβ”€ Send OTP via SMS/Email/Authenticator β”œβ”€ Validate OTP └─ Issue final JWT tokens ``` ### Role-Based Access Control (RBAC) ``` Permission Check: β”œβ”€ Extract user role from JWT token β”œβ”€ Check route permissions from roles table β”œβ”€ Check resource ownership (if applicable) β”‚ Example: Can user edit this valuation? β”‚ β”œβ”€ Is user Creator AND status = Draft? βœ“ Allow β”‚ β”œβ”€ Is user Surveyor AND status = Survey? βœ“ Allow β”‚ └─ Otherwise? βœ— Deny └─ Return 403 Forbidden if unauthorized ``` --- ## Deployment Architecture ### Production Deployment ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CLOUDFLARE / DNS β”‚ β”‚ - Domain: valuations.jr-solutions.com β”‚ β”‚ - SSL/TLS Certificate β”‚ β”‚ - CDN for static assets β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Frontend Server β”‚ β”‚ API Server β”‚ β”‚ (Cloudflare β”‚ β”‚ (Cloud VM/ β”‚ β”‚ Pages) β”‚ β”‚ Container) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - React SPA β”‚ β”‚ - Fastify β”‚ β”‚ - Static Assets β”‚ β”‚ - Node.js 20+ β”‚ β”‚ - Service Workerβ”‚ β”‚ - PM2 Process β”‚ β”‚ β”‚ β”‚ Manager β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PostgreSQL DB β”‚ β”‚ Object Storage β”‚ β”‚ + PostGIS β”‚ β”‚ (S3/R2) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Primary DB β”‚ β”‚ - Documents β”‚ β”‚ - Replica (RO) β”‚ β”‚ - Photos β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Development Environment ``` Local Machine: β”œβ”€ Frontend: npm run dev (Vite dev server on :5173) β”œβ”€ Backend: npm run dev (Fastify on :3000) β”œβ”€ Database: PostgreSQL Docker container on :5432 └─ Storage: MinIO Docker container on :9000 (S3-compatible) ``` --- ## Performance Optimization Strategies ### Frontend - **Code Splitting**: Lazy load routes and components - **Tree Shaking**: Remove unused code - **Image Optimization**: WebP format, lazy loading, responsive images - **Caching**: Service Worker for offline support, Cache API - **Bundle Size**: < 500 KB gzipped for initial load - **Virtual Scrolling**: For large lists (1000+ items) - **Debounced Search**: 300ms delay to reduce API calls ### Backend - **Database Indexing**: Index frequently queried columns - **Query Optimization**: Use EXPLAIN ANALYZE, avoid N+1 queries - **Caching Layer**: Redis for session/lookup data (future) - **Connection Pooling**: Reuse database connections - **Pagination**: Limit query results to 25-100 items - **Async Operations**: Background jobs for heavy tasks (report generation) ### Database - **PostGIS Spatial Indexing**: GIST indexes on geometry columns - **Partial Indexes**: Index only active records - **Materialized Views**: For complex report queries - **Table Partitioning**: Partition by date (if data grows large) --- ## Scalability Considerations ### Horizontal Scaling - **API Servers**: Load balance across multiple instances - **Database**: Read replicas for read-heavy operations - **File Storage**: Object storage auto-scales ### Vertical Scaling - **Database**: Increase CPU/RAM as data grows - **API Server**: Increase resources per instance ### Caching Strategy (Future) - **Redis**: Session storage, lookup data, frequently accessed data - **CDN**: Static assets, photos, documents --- **END OF ARCHITECTURE OVERVIEW**