# 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 ``` ### 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'); ```