20260607 - login, add scene, add hotspot
This commit is contained in:
+239
@@ -0,0 +1,239 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user