2026-03-31 10:37:41 +04:00
..
2026-03-31 10:37:41 +04:00
2026-03-24 15:40:35 +04:00
2026-03-17 12:06:17 +00:00
2026-03-17 12:06:17 +00:00
2026-03-17 12:06:17 +00:00
2026-03-17 12:06:17 +00:00
2026-03-31 08:56:49 +04:00
2026-03-17 12:06:17 +00:00
2026-03-31 05:58:01 +00:00

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

# 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:

# 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

# 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

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: devstageproduction

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:

cd docker
docker-compose up

Logging

Uses Pino logger with pretty printing in development:

const logger = require('pino')();
logger.info('Server started');
logger.error({ err }, 'Error occurred');