# Tour Builder Platform - Backend

Node.js/Express REST API server with Sequelize ORM for the Tour Builder Platform.

## Tech Stack

- **Runtime**: Node.js 24 LTS
- **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 24 LTS
- PostgreSQL 14+
- npm package manager

## Quick Start

```bash
# Install dependencies
npm install

# Create database (first time only)
npm run db:create

# Start server (runs migrations, seeds, and watches for changes)
npm run start-dev
```

The server runs on **port 3000** by default.

## Checks

```bash
npm run lint
npm run typecheck
npm run build
npm run test
npm run test:integration
npm run check:public-access
```

- `npm run test` runs fast unit tests without requiring PostgreSQL.
- `npm run typecheck` checks the migrated TypeScript scope with `strict: true`.
- `npm run build` compiles the migrated TypeScript scope into `dist/`.
- `npm run test:integration` runs rollback-based PostgreSQL integration tests
  when a valid database configuration is available; otherwise the tests skip.
- `npm run check:public-access` audits stale Public role/user permissions and
  non-Public private production presentation grants. Review its output before
  running `npm run fix:public-access`.

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

```

## Project Structure

```
backend/src/
├── index.ts              # Express app entry point
├── config.ts             # Environment configuration
├── load-env.ts           # Central .env bootstrap for app and DB entrypoints
├── helpers.ts            # Utility functions (wrapAsync)
├── types/                # Shared TypeScript contracts
│
├── auth/                 # Passport.js authentication strategies
│   └── auth.ts           # JWT, Google, Microsoft strategies
│
├── db/
│   ├── db-config.ts      # Typed database connection config
│   ├── umzug.ts          # Typed Umzug runner for migrations and seeders
│   ├── models/           # Sequelize model definitions
│   ├── api/              # Database access layer
│   ├── migrations/       # Applied migration history; do not rewrite
│   └── seeders/          # Typed seed data files
│
├── routes/               # Express route handlers
│   ├── auth.ts           # Authentication endpoints
│   ├── projects.ts       # Project CRUD
│   ├── tour_pages.ts     # Tour page management
│   ├── assets.ts         # Asset management
│   ├── file.ts           # File upload/download, presigned URLs
│   ├── publish.ts        # Publishing workflow
│   ├── search.ts         # Global search
│   └── ...               # Other entity routes
│
├── services/             # Business logic layer
│   ├── auth.ts           # Auth service (JWT, OAuth)
│   ├── publish.ts        # Publishing workflow logic
│   ├── file.ts           # File storage abstraction
│   ├── search.ts         # Global search service
│   ├── email/            # Email templates and sending
│   ├── notifications/    # Error classes and i18n messages
│   └── ...               # Other entity services
│
├── middlewares/
│   ├── check-permissions.ts   # RBAC permission checking
│   ├── runtime-context.ts     # Environment detection from headers
│   ├── runtime-public.ts      # Public runtime access (no auth)
│   ├── upload.ts              # File upload handling (multer)
│   └── rateLimiter.ts         # Rate limiting for API endpoints
│
├── factories/
│   ├── router.factory.ts      # Generate CRUD routes
│   └── service.factory.ts     # Generate service classes
│
└── utils/
    ├── env-validation.ts      # Environment variable validation (Joi)
    ├── errors.ts              # Custom error classes
    ├── logger.ts              # Pino logger configuration
    ├── request-context.ts     # Request-scoped currentUser/runtime/log storage
    └── index.ts               # 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
npm run db:create          # Create database
npm run db:drop            # Drop database
npm run db:migrate         # Run pending migrations
npm run db:migrate:undo    # Undo last migration
npm run db:migrate:undo:all # Undo all migrations
npm run db:migrate:status  # Show migration status
npm run db:seed            # Run all seeders
npm run db:seed:undo       # Undo all seeders
npm run db:reset           # Drop, create, migrate, and seed
npm run start              # Migrate, seed, and start with watch
npm run lint               # Run ESLint
npm run typecheck          # Run strict TypeScript check
npm run build              # Compile migrated TypeScript files
```

## API Documentation

Swagger UI available at: `http://localhost:3000/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.ts` 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');
```
