# 3D Virtual Tour Map - Architecture Diagram ## Technology Stack ### Backend - **Runtime**: Node.js - **Framework**: Express.js - **Database**: MongoDB (Mongoose ODM) - **Authentication**: JWT (jsonwebtoken) + bcrypt - **Image Processing**: Sharp (resize), exifr (read EXIF), piexifjs (write EXIF) - **File Upload**: Multer - **Security**: CORS, referer verification, no-cache headers ### Frontend - **Map**: Leaflet.js (OpenStreetMap tiles) - **3D Viewer**: Pannellum.js (360° panorama viewer) - **UI**: Vanilla HTML/CSS/JavaScript - **State**: localStorage for JWT tokens ## Architecture Diagram ```mermaid graph TB subgraph "Client (Browser)" UI[HTML/CSS UI] MAP[Leaflet Map] VIEWER[Pannellum 3D Viewer] AUTH[Auth Panel] MODAL[Scene Creation Modal] end subgraph "Backend (Node.js/Express)" SERVER[server.js] ROUTES[API Routes] MIDDLEWARES[Security & Auth Middlewares] UTILS[Image & EXIF Utils] MODELS[Mongoose Models] end subgraph "Database (MongoDB)" USERS[Users Collection] SCENES[Scenes Collection] ASSETS[Assets Collection] end subgraph "File System" UPLOADS[uploads/ Directory] TEMP[temp/ Directory] end UI --> MAP UI --> AUTH UI --> MODAL UI --> VIEWER MAP -->|Right-click| MODAL MAP -->|Load Scenes| ROUTES MAP -->|Click Marker| VIEWER AUTH -->|Login/Register| ROUTES AUTH -->|Store JWT| UI MODAL -->|Upload Image| ROUTES VIEWER -->|Request Image| ROUTES ROUTES --> MIDDLEWARES MIDDLEWARES -->|Verify JWT| AUTH MIDDLEWARES -->|Verify Referer| UI MIDDLEWARES -->|Privacy Check| SCENES ROUTES --> MODELS MODELS --> USERS MODELS --> SCENES MODELS --> ASSETS ROUTES --> UTILS UTILS -->|Resize| UPLOADS UTILS -->|Read/Write EXIF| UPLOADS UTILS -->|Temp Storage| TEMP SCENES --> ASSETS ASSETS --> UPLOADS SERVER --> ROUTES SERVER -->|Serve Static| UI ``` ## Data Flow ### 1. User Registration/Login ``` Client → POST /api/auth/register or /api/auth/login → authRoutes.js → User model (bcrypt hash/compare) → JWT generation → Response with token → Client stores token in localStorage ``` ### 2. Scene Creation (Upload 360° Image) ``` Client → Right-click on map → Open modal with lat/lng → POST /api/scenes (with multipart/form-data) → authMiddleware.protect (verify JWT) → Multer saves to temp/ → imageHelper.resizeTo8K (resize to 8192x4096) → exifHelper.getGPSCoordinates (read original GPS) → exifHelper.injectGPSCoordinates (inject map lat/lng) → Asset model saved to DB → Scene model saved to DB (with privacy settings) → Delete temp file → Response with scene data ``` ### 3. Load Scenes on Map ``` Client → GET /api/scenes (with optional JWT) → authMiddleware.optionalAuth → Scene.find() with privacy filter: - Guests: public + shared scenes - Logged-in: public + member + owned + shared-with-me → Populate owner and asset data → Response with scene list → Client adds markers to Leaflet map ``` ### 4. View 3D Panorama ``` Client → Click marker → GET /api/scenes/:id (with token if shared) → authMiddleware.optionalAuth → Privacy verification → Response with scene details → Client constructs secure image URL: /api/assets/view/:assetId?token=... → GET /api/assets/view/:assetId → securityMiddleware.verifyReferer (anti-hotlinking) → securityMiddleware.setNoCacheHeaders → Privacy verification again → Stream image file from disk → Pannellum viewer displays 360° panorama → Client-side security: block right-click, drag, keyboard shortcuts ``` ## Security Layers ### Backend Security 1. **JWT Authentication**: Required for creating scenes, optional for viewing 2. **Privacy Model**: Four levels (public, private, member, shared) 3. **Referer Verification**: Prevents direct URL access to images 4. **No-Cache Headers**: Prevents browser caching of protected images 5. **Share Tokens**: For shared scenes, token required for access ### Frontend Security 1. **Right-click Blocking**: Prevents image saving in viewer 2. **Drag Prevention**: Blocks drag-and-drop of images 3. **Keyboard Restrictions**: Blocks F12, Ctrl+S, Ctrl+U, Ctrl+Shift+I 4. **Token-based Access**: Share tokens passed in URLs for shared content ## Database Schema ### User Model ```javascript { username: String (unique, required), password: String (bcrypt hashed), role: Enum ['Chủ sở hữu', 'Thành viên'], timestamps: true } ``` ### Scene Model ```javascript { title: String (required), assetId: ObjectId (ref: Asset), lat: Number (required), lng: Number (required), owner: ObjectId (ref: User), privacy: Enum ['public', 'private', 'shared', 'member'], shareToken: String (unique, sparse), sharedWith: [ObjectId] (ref: User), timestamps: true } ``` ### Asset Model ```javascript { filePath: String (required), uploadedBy: ObjectId (ref: User), coordinates: { lat: Number, lng: Number }, timestamps: true } ``` ## File Structure ``` 3dtours/ ├── backend/ │ ├── config/ │ │ └── db.js # MongoDB connection │ ├── middlewares/ │ │ ├── authMiddleware.js # JWT verification │ │ └── securityMiddleware.js # Referer check, cache control │ ├── models/ │ │ ├── User.js # User schema │ │ ├── Scene.js # Scene schema │ │ └── Asset.js # Asset schema │ ├── routes/ │ │ ├── authRoutes.js # Login/register endpoints │ │ └── apiRoutes.js # Scenes/assets endpoints │ ├── utils/ │ │ ├── imageHelper.js # Sharp resize to 8K │ │ └── exifHelper.js # GPS read/write │ ├── uploads/ # Processed images │ │ └── temp/ # Temporary upload storage │ ├── server.js # Express app entry point │ ├── package.json │ └── .env # Environment variables └── frontend/ ├── css/ │ └── style.css # UI styling ├── js/ │ ├── main_map.js # Map logic, auth, scene loading │ └── viewer360.js # Pannellum viewer + security └── index.html # Main UI ``` ## Key Features 1. **Interactive Map**: Leaflet-based map with scene markers 2. **3D Panorama Viewer**: Pannellum for 360° image viewing 3. **User Authentication**: Registration, login with role-based access 4. **Privacy Controls**: Public, private, member-only, and shared scenes 5. **Image Processing**: Automatic resize to 8K (8192x4096) for consistency 6. **GPS Handling**: Extract original EXIF GPS, inject map coordinates 7. **Security**: Multi-layer protection against unauthorized access 8. **Share Links**: Token-based sharing for restricted content