368 lines
11 KiB
Markdown
368 lines
11 KiB
Markdown
# Tour Builder Platform - Backend
|
|
|
|
Node.js/Express REST API server with Sequelize ORM for the Tour Builder Platform.
|
|
|
|
## Tech Stack
|
|
|
|
- **Runtime**: Node.js 18+
|
|
- **Framework**: Express 4.x
|
|
- **Database**: PostgreSQL with Sequelize ORM
|
|
- **Authentication**: Passport.js (JWT, Google OAuth, Microsoft OAuth)
|
|
- **File Storage**: AWS S3 / Google Cloud Storage / Local filesystem
|
|
- **Email**: Nodemailer with AWS SES
|
|
- **API Docs**: Swagger/OpenAPI
|
|
|
|
## Prerequisites
|
|
|
|
- Node.js 18+
|
|
- PostgreSQL 14+
|
|
- Yarn package manager
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Install dependencies
|
|
yarn install
|
|
|
|
# Create database (first time only)
|
|
yarn db:create
|
|
|
|
# Start server (runs migrations, seeds, and watches for changes)
|
|
export $(cat .env | xargs) && NODE_ENV=production yarn start
|
|
```
|
|
|
|
The server runs on **port 8080** by default.
|
|
|
|
## Environment Variables
|
|
|
|
Create a `.env` file in the backend directory:
|
|
|
|
```env
|
|
# Database (required)
|
|
DB_HOST=localhost
|
|
DB_PORT=5432
|
|
DB_NAME=app_39215
|
|
DB_USER=app_39215
|
|
DB_PASSWORD=your_password
|
|
|
|
# JWT Secret (required)
|
|
SECRET_KEY=your-secret-key
|
|
|
|
# Admin credentials (for seeding)
|
|
ADMIN_EMAIL=admin@example.com
|
|
ADMIN_PASS=admin_password
|
|
USER_PASS=user_password
|
|
|
|
# AWS S3 (optional - for file storage)
|
|
AWS_S3_BUCKET=your-bucket
|
|
AWS_S3_REGION=us-east-1
|
|
AWS_ACCESS_KEY_ID=your-access-key
|
|
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
AWS_S3_PREFIX=your-prefix
|
|
|
|
# Google OAuth (optional)
|
|
GOOGLE_CLIENT_ID=your-client-id
|
|
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
|
|
# Microsoft OAuth (optional)
|
|
MS_CLIENT_ID=your-client-id
|
|
MS_CLIENT_SECRET=your-client-secret
|
|
|
|
# Email - AWS SES (optional)
|
|
EMAIL_USER=ses-smtp-user
|
|
EMAIL_PASS=ses-smtp-password
|
|
|
|
# OpenAI (optional)
|
|
GPT_KEY=your-openai-key
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
backend/src/
|
|
├── index.js # Express app entry point
|
|
├── config.js # Environment configuration
|
|
├── helpers.js # Utility functions (wrapAsync)
|
|
│
|
|
├── auth/ # Passport.js authentication strategies
|
|
│ └── auth.js # JWT, Google, Microsoft strategies
|
|
│
|
|
├── db/
|
|
│ ├── db.config.js # Database connection config (per environment)
|
|
│ ├── models/ # Sequelize model definitions (16 models)
|
|
│ ├── api/ # Database access layer (CRUD per model)
|
|
│ ├── migrations/ # Database migrations
|
|
│ └── seeders/ # Seed data (admin users, permissions, roles)
|
|
│
|
|
├── routes/ # Express route handlers (22 routes)
|
|
│ ├── auth.js # Authentication endpoints
|
|
│ ├── projects.js # Project CRUD
|
|
│ ├── tour_pages.js # Tour page management
|
|
│ ├── assets.js # Asset management
|
|
│ ├── file.js # File upload/download, presigned URLs
|
|
│ ├── publish.js # Publishing workflow
|
|
│ ├── search.js # Global search
|
|
│ └── ... # Other entity routes
|
|
│
|
|
├── services/ # Business logic layer (21 services)
|
|
│ ├── auth.js # Auth service (JWT, OAuth)
|
|
│ ├── publish.js # Publishing workflow logic
|
|
│ ├── file.js # File storage abstraction
|
|
│ ├── search.js # Global search service
|
|
│ ├── email/ # Email templates and sending
|
|
│ ├── notifications/ # Error classes and i18n messages
|
|
│ └── ... # Other entity services
|
|
│
|
|
├── middlewares/
|
|
│ ├── check-permissions.js # RBAC permission checking
|
|
│ ├── runtime-context.js # Environment detection from headers
|
|
│ ├── runtime-public.js # Public runtime access (no auth)
|
|
│ ├── upload.js # File upload handling (multer)
|
|
│ └── rateLimiter.js # Rate limiting for API endpoints
|
|
│
|
|
├── factories/
|
|
│ ├── router.factory.js # Generate CRUD routes
|
|
│ └── service.factory.js # Generate service classes
|
|
│
|
|
└── utils/
|
|
├── env-validation.js # Environment variable validation (Joi)
|
|
├── errors.js # Custom error classes
|
|
├── logger.js # Pino logger configuration
|
|
└── index.js # Utils barrel export
|
|
```
|
|
|
|
## Database Setup
|
|
|
|
### Create Database User and Database
|
|
|
|
```bash
|
|
# Connect to PostgreSQL
|
|
psql postgres -U postgres
|
|
|
|
# Create user
|
|
CREATE ROLE app_39215 WITH LOGIN PASSWORD 'your-password';
|
|
ALTER ROLE app_39215 CREATEDB;
|
|
|
|
# Create database
|
|
CREATE DATABASE app_39215 OWNER app_39215;
|
|
GRANT ALL PRIVILEGES ON DATABASE app_39215 TO app_39215;
|
|
|
|
\q
|
|
```
|
|
|
|
### Available Commands
|
|
|
|
```bash
|
|
yarn db:create # Create database
|
|
yarn db:drop # Drop database
|
|
yarn db:migrate # Run pending migrations
|
|
yarn db:migrate:undo # Undo last migration
|
|
yarn db:migrate:undo:all # Undo all migrations
|
|
yarn db:migrate:status # Show migration status
|
|
yarn db:seed # Run all seeders
|
|
yarn db:seed:undo # Undo all seeders
|
|
yarn db:reset # Drop, create, migrate, and seed
|
|
yarn start # Migrate, seed, and start with watch
|
|
yarn lint # Run ESLint
|
|
```
|
|
|
|
## API Documentation
|
|
|
|
Swagger UI available at: `http://localhost:8080/api-docs`
|
|
|
|
### Core Endpoints
|
|
|
|
| Endpoint | Description |
|
|
|----------|-------------|
|
|
| `POST /api/auth/signin/local` | Email/password login |
|
|
| `POST /api/auth/signup` | User registration |
|
|
| `GET /api/auth/me` | Current user info (JWT required) |
|
|
| `GET /api/auth/signin/google` | Google OAuth login |
|
|
| `GET /api/auth/signin/microsoft` | Microsoft OAuth login |
|
|
|
|
### Entity CRUD Pattern
|
|
|
|
All entities follow standard REST patterns:
|
|
|
|
```
|
|
GET /api/{entity} # List with pagination & filters
|
|
GET /api/{entity}/:id # Get single record
|
|
POST /api/{entity} # Create record
|
|
PUT /api/{entity}/:id # Update record
|
|
DELETE /api/{entity}/:id # Soft delete record
|
|
```
|
|
|
|
### Main Entities
|
|
|
|
| Entity | Description |
|
|
|--------|-------------|
|
|
| `projects` | Virtual tour projects |
|
|
| `tour_pages` | Pages within a tour (elements, navigation, transitions stored in ui_schema_json) |
|
|
| `assets` | Uploaded media files |
|
|
| `asset_variants` | Resized/optimized asset versions |
|
|
| `element_type_defaults` | Global element default settings |
|
|
| `project_element_defaults` | Project-specific element settings |
|
|
| `project_audio_tracks` | Background audio for projects |
|
|
| `publish_events` | Publishing history and status tracking |
|
|
| `pwa_caches` | PWA cache manifests for offline support |
|
|
| `presigned_url_requests` | S3 presigned URL request tracking |
|
|
| `access_logs` | User access audit trail |
|
|
| `users` | User accounts |
|
|
| `roles` | User roles |
|
|
| `permissions` | Granular permissions |
|
|
| `project_memberships` | Team access per project |
|
|
|
|
### Element Defaults Hierarchy
|
|
|
|
UI elements use a three-tier defaults system:
|
|
|
|
```
|
|
element_type_defaults (Global)
|
|
│
|
|
│ auto-snapshot on project creation
|
|
▼
|
|
project_element_defaults (Project)
|
|
│
|
|
│ applied when creating elements
|
|
▼
|
|
tour_pages.ui_schema_json (Instance)
|
|
```
|
|
|
|
1. **Global** (`element_type_defaults`) - Platform-wide defaults for 11 element types (navigation, tooltip, gallery, etc.). Auto-seeded on first API access.
|
|
|
|
2. **Project** (`project_element_defaults`) - Per-project overrides. Automatically snapshotted from global when a project is created. Can be customized independently.
|
|
|
|
3. **Instance** (`tour_pages.ui_schema_json`) - Page-specific elements with their settings stored inline. Created in constructor with project defaults applied.
|
|
|
|
**Additional Endpoints:**
|
|
- `POST /api/project-element-defaults/:id/reset` - Reset to current global default
|
|
- `GET /api/project-element-defaults/:id/diff` - Compare with global default
|
|
|
|
### Publishing Workflow
|
|
|
|
Three-tier environment model for content: `dev` → `stage` → `production`
|
|
|
|
```
|
|
POST /api/publish/save-to-stage # Copy dev content to stage (body: { projectId })
|
|
POST /api/publish # Copy stage content to production (body: { projectId })
|
|
```
|
|
|
|
Pages have an `environment` field (`dev`, `stage`, or `production`) that determines visibility:
|
|
- **Constructor** (`/constructor?projectId=`) - Always shows `dev` environment
|
|
- **Stage preview** (`/p/[slug]/stage`) - Shows `stage` environment
|
|
- **Public runtime** (`/p/[slug]`) - Shows `production` environment
|
|
|
|
## Authentication
|
|
|
|
### JWT Authentication
|
|
|
|
Protected routes require JWT token in Authorization header:
|
|
|
|
```
|
|
Authorization: Bearer <jwt-token>
|
|
```
|
|
|
|
### OAuth Providers
|
|
|
|
- **Google**: `/api/auth/signin/google`
|
|
- **Microsoft**: `/api/auth/signin/microsoft`
|
|
|
|
## File Storage
|
|
|
|
Storage provider is auto-detected based on available credentials:
|
|
|
|
1. **AWS S3** - If `AWS_S3_BUCKET` is configured
|
|
2. **Google Cloud Storage** - If GCS credentials are available
|
|
3. **Local filesystem** - Fallback (files stored in system temp directory)
|
|
|
|
### Upload Flow (Presigned URLs)
|
|
|
|
```
|
|
POST /api/file/presigned-url # Get upload URL (authenticated)
|
|
PUT {presigned-url} # Upload directly to S3
|
|
POST /api/assets # Register asset in database
|
|
```
|
|
|
|
### Download Flow (Direct S3 Access)
|
|
|
|
For runtime asset preloading, the frontend can request presigned download URLs:
|
|
|
|
```
|
|
POST /api/file/presign # Get download URLs (public endpoint)
|
|
Request: { urls: ["assets/img1.jpg", "assets/video.mp4", ...] }
|
|
Response: { presignedUrls: { "assets/img1.jpg": "https://s3...", ... } }
|
|
```
|
|
|
|
- **Max URLs per request**: 50
|
|
- **URL expiry**: 1 hour
|
|
- **Public endpoint**: No authentication required (for runtime playback)
|
|
|
|
This allows the frontend to download assets directly from S3, bypassing the backend for better performance.
|
|
|
|
## RBAC (Role-Based Access Control)
|
|
|
|
### Permission Format
|
|
|
|
```
|
|
{ACTION}_{ENTITY}
|
|
```
|
|
|
|
Actions: `CREATE`, `READ`, `UPDATE`, `DELETE`
|
|
|
|
Example: `CREATE_PROJECTS`, `READ_TOUR_PAGES`, `UPDATE_ASSETS`
|
|
|
|
### Default Roles
|
|
|
|
| Role | Description |
|
|
|------|-------------|
|
|
| Administrator | Full access to all features (user/role/permission management) |
|
|
| Platform Owner | Full project access, user management |
|
|
| Account Manager | Project and asset management |
|
|
| Tour Designer | Create and edit tours, assets, pages |
|
|
| Content Reviewer | Review and update content (read/update access) |
|
|
| Analytics Viewer | Read-only access for viewing data |
|
|
| Public | Minimal access for public users |
|
|
|
|
## Environment Detection
|
|
|
|
### Server Environment (NODE_ENV)
|
|
|
|
The backend uses `NODE_ENV` to determine database configuration:
|
|
|
|
| Value | Database | Description |
|
|
|-------|----------|-------------|
|
|
| `production` | Production config | Live environment |
|
|
| `dev_stage` | Staging config | Staging environment |
|
|
| (other) | Development config | Local development |
|
|
|
|
### Content Environment (tour_pages.environment)
|
|
|
|
Separate from server environment, tour pages have a content environment field:
|
|
|
|
| Value | Access | Description |
|
|
|-------|--------|-------------|
|
|
| `dev` | Constructor only | Editing/draft content |
|
|
| `stage` | Stage preview | Pre-production review |
|
|
| `production` | Public runtime | Published content |
|
|
|
|
The `X-Runtime-Environment` header (set by frontend) determines which content environment to query. The `runtime-context.js` middleware resolves this for API requests.
|
|
|
|
## Docker
|
|
|
|
See `docker/` directory for Docker Compose setup:
|
|
|
|
```bash
|
|
cd docker
|
|
docker-compose up
|
|
```
|
|
|
|
## Logging
|
|
|
|
Uses Pino logger with pretty printing in development:
|
|
|
|
```javascript
|
|
const logger = require('pino')();
|
|
logger.info('Server started');
|
|
logger.error({ err }, 'Error occurred');
|
|
```
|