39948-vm/backend/docs/database-schema.md
2026-07-03 16:11:24 +02:00

46 KiB

Database Schema Documentation

This document provides a comprehensive analysis of the Tour Builder Platform database schema, including all models, fields, relationships, constraints, and configurations.

Overview

  • Database: PostgreSQL
  • ORM: Sequelize v6
  • Table Naming: freezeTableName: true - table names match model names exactly
  • Soft Delete: All models use paranoid: true - records are soft-deleted via deletedAt timestamp
  • Timestamps: All models include createdAt, updatedAt, and deletedAt columns
  • Primary Keys: All models use UUID v4 as primary key

Database Configuration

Configuration is environment-based in backend/src/db/db-config.ts:

Environment Database Logging
production DB_* env vars Disabled
development db_tour_builder_platform Console
dev_stage DB_* env vars Console

Migration Settings:

  • Migration storage: sequelize (SequelizeMeta table)
  • Seeder storage: sequelize

Entity-Relationship Overview

┌──────────────┐     ┌─────────────────┐     ┌──────────────────┐
│    users     │────<│project_memberships│>───│    projects     │
└──────────────┘     └─────────────────┘     └──────────────────┘
       │                                              │
       │ app_roleId                                   │ hasMany
       ▼                                              ▼
┌──────────────┐                            ┌──────────────────┐
│    roles     │                            │   tour_pages     │
└──────────────┘                            │ (ui_schema_json) │
       │                                    └──────────────────┘
       │ belongsToMany
       ▼
┌──────────────┐
│ permissions  │
└──────────────┘

┌──────────────┐
│    assets    │────< asset_variants
└──────────────┘

┌────────────────────────┐     snapshotted from     ┌────────────────────────┐
│ project_element_defaults│ ─────────────────────── │ element_type_defaults  │
└────────────────────────┘      (source_element_id) └────────────────────────┘
         │                                                   │
         │ belongsTo                                         │ (global defaults)
         ▼                                                   │
┌──────────────────┐                                         │
│    projects      │<────────────────────────────────────────┘
└──────────────────┘    (templates for new projects)

Note: Page elements, navigation links, and transition videos are stored directly in tour_pages.ui_schema_json rather than in separate tables. This simplifies the data model and avoids ID remapping issues when publishing between environments.


Core Models

1. users

User accounts for authentication and authorization.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
firstName TEXT nullable User's first name
lastName TEXT nullable User's last name
phoneNumber TEXT nullable Contact phone
email TEXT NOT NULL, UNIQUE Email (validated)
disabled BOOLEAN NOT NULL, default: false Account disabled flag
password TEXT NOT NULL Bcrypt hashed password
emailVerified BOOLEAN NOT NULL, default: false Email verification status
emailVerificationToken TEXT nullable Token for email verification
emailVerificationTokenExpiresAt DATE nullable Token expiry
passwordResetToken TEXT nullable Password reset token
passwordResetTokenExpiresAt DATE nullable Reset token expiry
provider TEXT NOT NULL, default: 'local' Auth provider (local, google, microsoft)
importHash STRING(255) UNIQUE, nullable Import deduplication key
app_roleId UUID FK → roles.id User's application role
createdById UUID FK → users.id Record creator
updatedById UUID FK → users.id Last modifier

Indexes:

  • email (unique)
  • app_roleId
  • deletedAt

Associations:

  • belongsTo roles (as app_role)
  • hasMany project_memberships
  • hasMany presigned_url_requests
  • hasMany publish_events
  • hasMany access_logs
  • belongsToMany permissions (as custom_permissions)
  • hasMany file (as avatar, polymorphic)

Hooks:

  • beforeCreate: Trims string fields, auto-generates password for OAuth users, sets emailVerified: true for OAuth
  • beforeUpdate: Trims string fields

2. roles

Application-level roles for RBAC.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
name TEXT NOT NULL, len: 1-100 Role name
role_customization TEXT nullable Custom role settings
importHash STRING(255) UNIQUE, nullable Import deduplication

Associations:

  • hasMany users (as users_app_role)
  • belongsToMany permissions (through rolesPermissionsPermissions)

Seeded Roles:

  • Administrator (full access)
  • Platform Owner
  • Account Manager
  • Tour Designer
  • Content Reviewer
  • Analytics Viewer
  • Public (minimal access)

3. permissions

Individual permission definitions for RBAC.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
name TEXT NOT NULL, UNIQUE, len: 1-100 Permission name
importHash STRING(255) UNIQUE, nullable Import deduplication

Permission Naming Convention:

  • CREATE_<ENTITY> - Create records
  • READ_<ENTITY> - Read records
  • UPDATE_<ENTITY> - Modify records
  • DELETE_<ENTITY> - Delete records
  • READ_API_DOCS - Access API documentation
  • CREATE_SEARCH - Perform searches

Seeded Entities with CRUD Permissions: users, roles, permissions, projects, project_memberships, assets, asset_variants, presigned_url_requests, tour_pages, project_audio_tracks, publish_events, pwa_caches, access_logs, element_type_defaults, project_element_defaults

Private production presentation access is intentionally not part of the generic CRUD permission seed set. It is represented by project visibility plus explicit customer grants in production_presentation_access.


4. projects

Virtual tour projects - the main organizational unit.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
name TEXT NOT NULL, len: 1-255 Project name
slug TEXT NOT NULL, UNIQUE, regex: ^[a-z0-9_-]+$/i, len: 1-255 URL-safe identifier
description TEXT nullable Project description
logo_url TEXT nullable Project logo URL
favicon_url TEXT nullable Favicon URL
og_image_url TEXT nullable Open Graph image URL
production_presentation_visibility ENUM NOT NULL, default: 'public' Production runtime visibility: public, private
design_width INTEGER nullable, default: 1920 Design canvas width (px)
design_height INTEGER nullable, default: 1080 Design canvas height (px)
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • slug (unique)
  • deletedAt

Associations:

  • hasMany project_memberships
  • hasMany assets
  • hasMany presigned_url_requests
  • hasMany tour_pages
  • hasMany project_audio_tracks
  • hasMany project_element_defaults
  • hasMany publish_events
  • hasMany pwa_caches
  • hasMany access_logs
  • hasMany production_presentation_access
  • hasMany project_ui_control_settings

Cascade Behavior: All child records are deleted when project is deleted.

Auto-Snapshot on Create: When a project is created, all element_type_defaults records are automatically snapshotted to project_element_defaults for the new project.

Global UI Controls

Global UI controls configure the system-owned fullscreen, sound, and offline buttons. Defaults live in global_ui_control_defaults, project/environment overrides live in project_ui_control_settings, and page overrides live in tour_pages.global_ui_controls_settings_json.

Sizes are stored as canvas-width-relative percentage fields (buttonSizePercent, iconSizePercent, borderRadiusPercent). Positions are stored as canvas-relative percentages (xPercent, yPercent) for stable placement across devices.

Icons, background colors, and border colors are state-specific: defaultIconUrl/activeIconUrl, defaultBackgroundColor/activeBackgroundColor, and defaultBorderColor/activeBorderColor.

global_ui_control_defaults

Singleton table for platform-wide defaults.

Field Type Constraints Description
id UUID PK, default UUIDv4 Primary identifier
settings_json JSON NOT NULL Defaults for offline, fullscreen, sound
createdById UUID nullable FK users Creator
updatedById UUID nullable FK users Last updater
createdAt, updatedAt, deletedAt DATE paranoid timestamps Lifecycle fields

project_ui_control_settings

Project/environment-level overrides.

Field Type Constraints Description
id UUID PK, default UUIDv4 Primary identifier
projectId UUID NOT NULL, FK projects, cascade delete Owning project
environment ENUM NOT NULL: dev, stage, production Content environment
source_key TEXT nullable Snapshot/publish/clone provenance
settings_json JSON NOT NULL Project-level UI-control overrides
importHash STRING(255) nullable unique Import deduplication
createdById, updatedById UUID nullable FK users Audit users
createdAt, updatedAt, deletedAt DATE paranoid timestamps Lifecycle fields

Indexes:

  • unique partial index on ("projectId", environment) where deletedAt IS NULL
  • index on deletedAt

5. production_presentation_access

Explicit customer access grants for private production presentations.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
projectId UUID FK → projects.id, NOT NULL Private production presentation project
userId UUID FK → users.id, NOT NULL Customer user allowed to view the presentation
createdById UUID FK → users.id Record creator
updatedById UUID FK → users.id Last modifier
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • projectId
  • userId
  • projectId, userId (unique for active rows where deletedAt IS NULL)

Access Rules:

  • Public production projects do not require rows in this table.
  • Staff users with any RBAC permission can view every private production presentation.
  • Public-role customer users with no RBAC permissions need an active row for each private production presentation.

6. project_memberships

Junction table linking users to projects with access levels.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
access_level ENUM NOT NULL, default: 'viewer' Access level: owner, editor, reviewer, viewer
is_active BOOLEAN NOT NULL, default: false Membership active status
invited_at DATE nullable Invitation timestamp
accepted_at DATE nullable Acceptance timestamp
projectId UUID FK → projects.id Associated project
userId UUID FK → users.id Associated user
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • projectId
  • userId
  • projectId, userId (unique composite)
  • is_active
  • deletedAt

Cascade Behavior: Deleted when associated project or user is deleted.


Tour Content Models

7. tour_pages

Individual pages/scenes within a tour.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
environment ENUM NOT NULL, default: 'dev' Environment: dev, stage, production
source_key TEXT nullable Reference to source version when published
name TEXT NOT NULL, len: 1-255 Page display name
slug TEXT NOT NULL, regex: ^[a-z0-9_-]+$/i, len: 1-255 URL-safe identifier
sort_order INTEGER NOT NULL, default: 0 Presentation display order; first sorted page is the entry page
background_image_url TEXT nullable Background image URL
background_video_url TEXT nullable Background video URL
background_embed_url TEXT nullable Background 360/embed URL
background_audio_url TEXT nullable Background audio URL
background_loop BOOLEAN NOT NULL, default: false Loop background media
background_video_autoplay BOOLEAN NOT NULL, default: true Autoplay background video
background_video_loop BOOLEAN NOT NULL, default: true Loop background video
background_video_muted BOOLEAN NOT NULL, default: true Mute background video
background_video_start_time DECIMAL(10,1) nullable Background video start time (seconds)
background_video_end_time DECIMAL(10,1) nullable Background video end time (seconds)
background_video_play_once BOOLEAN NOT NULL, default: false Play video only once per session (show last frame on revisit)
background_audio_autoplay BOOLEAN NOT NULL, default: true Autoplay background audio
background_audio_loop BOOLEAN NOT NULL, default: true Loop background audio
background_audio_start_time DECIMAL(10,1) nullable Background audio start time (seconds)
background_audio_end_time DECIMAL(10,1) nullable Background audio end time (seconds)
design_width INTEGER nullable, default: null Design canvas width (px) - copied from project on save
design_height INTEGER nullable, default: null Design canvas height (px) - copied from project on save
requires_auth BOOLEAN NOT NULL, default: false Requires authentication
ui_schema_json JSON nullable UI element schema
projectId UUID FK → projects.id Parent project
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • projectId
  • projectId, environment, slug (unique composite)
  • projectId, environment, sort_order
  • deletedAt

Ordering semantics:

  • Constructor page reordering updates sort_order for dev pages only.
  • Constructor page duplication creates a new dev row with a unique slug, fresh page ID, fresh inline element IDs in ui_schema_json, and sort_order = max(project dev sort_order) + 1.
  • Constructor page deletion removes the dev row through the standard DELETE /api/tour_pages/:id endpoint; stage and production keep their previous copies until Save to Stage and Publish run.
  • Runtime loaders sort by sort_order; the first page after sorting is the presentation entry page.
  • Save to Stage copies dev sort_order to stage. Publish copies stage sort_order to production.

Associations:

  • belongsTo projects (as project)

UI Schema JSON Structure: The ui_schema_json field contains all page elements and navigation configuration:

{
  "elements": [{
    "id": "unique-element-id",
    "type": "navigation_next",
    "name": "Next Page Button",
    "xPercent": 90,
    "yPercent": 85,
    "widthPercent": 8,
    "heightPercent": 10,
    "targetPageSlug": "page-2",
    "transitionVideoUrl": "assets/.../transition.mp4",
    "iconUrl": "assets/.../icon.png",
    "styleJson": { ... }
  }]
}

Element Types:

  • navigation_next - Forward navigation button
  • navigation_prev - Back navigation button
  • spot - Hotspot/clickable area
  • description - Text description
  • tooltip - Hover tooltip
  • gallery - Image gallery
  • carousel - Image carousel
  • logo - Logo element
  • video_player - Video player
  • audio_player - Audio player
  • popup - Popup/modal

Asset Management Models

7. assets

Media files (images, videos, audio, documents) used in tours.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
name TEXT nullable, len: 0-255 Asset display name
asset_type ENUM NOT NULL Media type: image, video, audio, file
type ENUM NOT NULL, default: 'general' Usage type (see values below)
cdn_url TEXT nullable Public CDN URL
storage_key TEXT nullable S3/storage key
mime_type TEXT nullable, validated MIME type (e.g., image/png)
size_mb DECIMAL nullable File size in MB
width_px INTEGER nullable Width in pixels
height_px INTEGER nullable Height in pixels
duration_sec DECIMAL nullable Duration for audio/video
frame_rate DECIMAL nullable Video FPS from backend ffprobe
checksum TEXT nullable File checksum
is_public BOOLEAN NOT NULL, default: false Publicly accessible
projectId UUID FK → projects.id Parent project
importHash STRING(255) UNIQUE, nullable Import deduplication

Asset Usage Types:

  • icon - UI icons
  • background_image - Page backgrounds
  • audio - Audio files
  • video - Video files
  • transition - Transition videos
  • logo - Project logos
  • favicon - Favicon images
  • document - PDF/documents
  • general - General assets

Indexes:

  • projectId
  • storage_key partial active-row index (assets_storage_key_active, where deletedAt IS NULL and storage_key IS NOT NULL) for transition/reversed-video lookup by canonical storage key
  • asset_type
  • type
  • is_public
  • deletedAt

Associations:

  • belongsTo projects (as project)
  • hasMany asset_variants (as asset_variants_asset)

8. asset_variants

Processed variants of assets (thumbnails, transcoded videos, reversed videos, etc.).

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
variant_type ENUM nullable Variant type (see values below)
cdn_url TEXT nullable, len: 0-2048, URL validated Variant CDN URL
storage_key TEXT nullable Private storage path (e.g., assets/{assetId}/reversed.mp4)
width_px INTEGER nullable, min: 0 Width in pixels
height_px INTEGER nullable, min: 0 Height in pixels
size_mb DECIMAL nullable, min: 0 File size in MB
assetId UUID FK → assets.id Parent asset
importHash STRING(255) UNIQUE, nullable Import deduplication

Variant Types:

  • thumbnail - Small preview
  • preview - Medium preview
  • webp - WebP format
  • mp4_low - Low-quality MP4
  • mp4_high - High-quality MP4
  • original - Original file
  • reversed - Reversed video for back navigation transitions (generated server-side with FFmpeg)

Cascade Behavior: Deleted when parent asset is deleted.

Indexes:

  • assetId, variant_type partial active-row index (asset_variants_asset_id_variant_type_active, where deletedAt IS NULL) for asset variant joins and reversed-variant lookup

9. presigned_url_requests

Tracks presigned URL requests for secure uploads/downloads.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
purpose ENUM nullable Purpose: upload, download
asset_type ENUM nullable Asset type: image, video, audio, file
requested_key TEXT nullable, len: 0-1024 Requested storage key
mime_type TEXT nullable, len: 0-255, validated Expected MIME type
requested_size_mb DECIMAL nullable, min: 0 Expected file size
expires_at DATE nullable URL expiration time
status TEXT nullable Request status
projectId UUID FK → projects.id Associated project
userId UUID FK → users.id Requesting user
importHash STRING(255) UNIQUE, nullable Import deduplication

Audio & Media Models

10. project_audio_tracks

Background audio tracks for projects.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
environment ENUM NOT NULL, default: 'dev' Environment: dev, stage, production
source_key TEXT nullable Reference to source version when published
name TEXT nullable, len: 0-255 Track name
slug TEXT nullable URL-safe identifier
url TEXT nullable Audio file URL
loop BOOLEAN NOT NULL, default: false Loop playback
volume DECIMAL nullable, min: 0, max: 1 Volume level (0.0-1.0)
sort_order INTEGER nullable Playback order
is_enabled BOOLEAN NOT NULL, default: false Track enabled
projectId UUID FK → projects.id Parent project
importHash STRING(255) UNIQUE, nullable Import deduplication

Note: The environment field is NOT NULL with default 'dev' for consistency with tour_pages.


11. project_transition_settings

Environment-aware project-level transition settings for CSS-based page transitions. Settings cascade: Element → Project → Global → Hardcoded defaults.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
environment ENUM NOT NULL Environment: dev, stage, production
source_key TEXT nullable Reference to source record when published
transition_type TEXT NOT NULL, default: 'fade' CSS transition type (fade, none)
duration_ms INTEGER NOT NULL, default: 700 Transition duration in milliseconds
easing TEXT NOT NULL, default: 'ease-in-out' CSS easing function
overlay_color TEXT NOT NULL, default: '#000000' Transition overlay color
projectId UUID FK → projects.id, NOT NULL Parent project
createdById UUID FK → users.id, nullable Creator user
updatedById UUID FK → users.id, nullable Last updater
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • project_transition_settings_project_env_unique - UNIQUE on (projectId, environment) WHERE deletedAt IS NULL

Associations:

  • belongsTo projects (as project) - CASCADE on delete
  • belongsTo users (as createdBy, updatedBy)

Publishing Integration: Copied between environments during Save to Stage (dev → stage) and Publish (stage → production). The source_key tracks lineage.

Cascade Resolution: When determining transition settings:

  1. Element-level settings (from ui_schema_json)
  2. Project-level settings (this table, environment-specific)
  3. Global defaults (global_transition_defaults)
  4. Hardcoded fallback (fade, 700ms, ease-in-out, #000000)

Publishing & Caching Models

12. publish_events

Records of content publishing between environments.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
title STRING nullable, len: 0-255 Event title
description TEXT nullable, len: 0-5000 Event description
from_environment ENUM NOT NULL Source: dev, stage, production
to_environment ENUM NOT NULL Target: dev, stage, production
started_at DATE nullable Start timestamp
finished_at DATE nullable Completion timestamp
status ENUM NOT NULL, default: 'queued' Status: queued, running, success, failed
error_message TEXT nullable Error details
pages_copied INTEGER nullable, min: 0 Pages copied count
audios_copied INTEGER nullable, min: 0 Audio tracks copied count
projectId UUID FK → projects.id Published project
userId UUID FK → users.id Publishing user
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • projectId
  • userId
  • status
  • started_at

Cascade Behavior: userId set to NULL when user is deleted (preserves audit trail).


13. pwa_caches

PWA cache configurations for offline support.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
environment ENUM nullable Environment: dev, stage, production
cache_version TEXT nullable, len: 0-255 Cache version string
manifest_json JSON nullable PWA manifest configuration
asset_list_json JSON nullable List of cached assets
generated_at DATE nullable Generation timestamp
is_active BOOLEAN NOT NULL, default: false Cache active status
projectId UUID FK → projects.id Parent project
importHash STRING(255) UNIQUE, nullable Import deduplication

Audit & Logging Models

14. access_logs

Audit trail for tour access.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
environment ENUM NOT NULL Access context: admin, stage, production
path TEXT nullable, len: 0-2048 Accessed path
ip_address TEXT nullable, len: 0-45 Client IP (IPv4/IPv6)
user_agent TEXT nullable, len: 0-1024 Browser user agent
accessed_at DATE NOT NULL, default: NOW Access timestamp
projectId UUID FK → projects.id Accessed project
userId UUID FK → users.id Accessing user (if authenticated)
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • projectId
  • environment
  • userId
  • accessed_at

Cascade Behavior: userId set to NULL when user is deleted (preserves audit trail).


Element Default Settings Models

These models implement a two-tier settings hierarchy for UI elements:

  1. Global defaults (element_type_defaults) - Platform-wide default settings per element type
  2. Project defaults (project_element_defaults) - Project-specific overrides, snapshotted from global on project creation

Instance element settings are stored directly in tour_pages.ui_schema_json as part of each element's configuration.

15. element_type_defaults

Global platform-wide default settings for each element type. These serve as templates that are snapshotted to new projects.

Note: This table was renamed from ui_elements to element_type_defaults for clarity.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
element_type TEXT NOT NULL, UNIQUE, len: 1-100 Element type identifier
name TEXT NOT NULL, len: 1-255 Display name
sort_order INTEGER NOT NULL, default: 0 Display order in UI
is_active VIRTUAL getter: true Virtual active field
settings_json TEXT nullable Default settings JSON
importHash STRING(255) UNIQUE, nullable Import deduplication

Field Aliasing: The model exposes default_settings_json as a property name, but it maps to the settings_json column in the database via field: 'settings_json'. This provides a clearer API name while maintaining backward compatibility with the database schema.

Virtual Field: is_active is a VIRTUAL field that always returns true (computed, not stored in database).

Indexes:

  • element_type (unique)
  • sort_order
  • deletedAt

Associations:

  • hasMany project_element_defaults (as project_defaults)

Auto-Initialization: The API includes an ensureInitialized() method that automatically seeds default records if the table is empty. This runs before any CRUD operation.

Seeded Element Types (11 types auto-seeded):

  • navigation_next - Forward navigation button (sort_order: 1)
  • navigation_prev - Back navigation button (sort_order: 2)
  • tooltip - Hover tooltip (sort_order: 3)
  • description - Text description (sort_order: 4)
  • gallery - Image gallery (sort_order: 5)
  • carousel - Image carousel (sort_order: 6)
  • video_player - Video player (sort_order: 7)
  • audio_player - Audio player (sort_order: 8)
  • spot - Hotspot/clickable area (sort_order: 9)
  • logo - Logo element (sort_order: 10)
  • popup - Popup/modal (sort_order: 11)

16. project_element_defaults

Project-specific element default settings. Created automatically when a project is created by snapshotting all global element_type_defaults. Can be customized per-project without affecting global defaults or other projects.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
element_type TEXT NOT NULL, len: 1-100 Element type identifier
name TEXT nullable, len: 0-255 Custom display name
sort_order INTEGER NOT NULL, default: 0 Display order in UI
settings_json TEXT nullable Project-specific settings JSON
source_element_id UUID FK → element_type_defaults.id, nullable Reference to global default (for reset/diff)
snapshot_version INTEGER NOT NULL, default: 1 Version counter (incremented on reset)
projectId UUID FK → projects.id, NOT NULL Parent project
importHash STRING(255) UNIQUE, nullable Import deduplication

Indexes:

  • projectId
  • projectId, element_type (unique composite)
  • element_type
  • source_element_id
  • deletedAt

Associations:

  • belongsTo projects (as project) - CASCADE on delete
  • belongsTo element_type_defaults (as source_element) - SET NULL on delete

Custom findAll with Project Filtering: The API implements a custom findAll() method that supports filtering by project:

  • Query params: ?projectId=<uuid> or ?project=<uuid|name>
  • Supports multiple values: ?projectId=uuid1|uuid2
  • Can filter by project UUID or project name (case-insensitive)
  • Includes related project and source_element associations
  • Default ordering: sort_order ASC

Auto-Snapshot Behavior: When a new project is created, all records from element_type_defaults are automatically copied to project_element_defaults for that project. This ensures each project starts with consistent defaults while allowing customization.

Reset Functionality: Projects can reset their element defaults back to the current global defaults via the /api/project-element-defaults/:id/reset endpoint. This increments snapshot_version and copies current global settings.

Diff Functionality: The /api/project-element-defaults/:id/diff endpoint compares project defaults with global defaults to show customizations.


16. file (table: files)

Polymorphic file attachments (e.g., user avatars).

Note: This model does NOT have freezeTableName: true, so the table name is pluralized to files.

Field Type Constraints Description
id UUID PK, default: UUIDv4 Primary identifier
belongsTo STRING(255) nullable Parent table name
belongsToId UUID nullable Parent record ID
belongsToColumn STRING(255) nullable Column name
name STRING(2083) NOT NULL File name
sizeInBytes INTEGER nullable File size
privateUrl STRING(2083) nullable Private storage URL
publicUrl STRING(2083) NOT NULL Public access URL

Polymorphic Usage: Files are attached to records via belongsTo/belongsToId/belongsToColumn columns. Example: User avatar has belongsTo: 'users', belongsToId: <user_id>, belongsToColumn: 'avatar'


Junction Tables

rolesPermissionsPermissions

Many-to-many relationship between roles and permissions.

Field Type Constraints
roles_permissionsId UUID PK, FK → roles.id
permissionId UUID PK, FK → permissions.id
createdAt TIMESTAMP WITH TIME ZONE NOT NULL
updatedAt TIMESTAMP WITH TIME ZONE NOT NULL

Indexes:

  • permissionId

usersCustom_permissionsPermissions

Many-to-many relationship for user custom permissions.

Field Type Constraints
users_custom_permissionsId UUID FK → users.id
permissionId UUID FK → permissions.id

Database Indexes Summary

Table Index Fields Type
users email email unique
users app_roleId app_roleId -
users deletedAt deletedAt -
projects slug slug unique
projects deletedAt deletedAt -
production_presentation_access projectId projectId -
production_presentation_access userId userId -
production_presentation_access composite projectId, userId unique active rows
project_memberships composite projectId, userId unique
tour_pages composite projectId, environment, slug unique
tour_pages sort projectId, environment, sort_order -
assets projectId projectId -
assets asset_type asset_type -
assets type type -
assets is_public is_public -
element_type_defaults element_type element_type unique
element_type_defaults sort_order sort_order -
element_type_defaults deletedAt deletedAt -
project_element_defaults projectId projectId -
project_element_defaults composite projectId, element_type unique
project_element_defaults element_type element_type -
project_element_defaults source_element_id source_element_id -
project_element_defaults deletedAt deletedAt -
publish_events status status -
publish_events started_at started_at -
access_logs accessed_at accessed_at -

Foreign Key Constraints

All foreign key constraints are enforced at the database level via migration 20260319000001-add-foreign-key-constraints.js.

Child Table Column Parent Table On Delete On Update
asset_variants assetId assets CASCADE CASCADE
assets projectId projects CASCADE CASCADE
tour_pages projectId projects CASCADE CASCADE
project_memberships projectId projects CASCADE CASCADE
project_memberships userId users CASCADE CASCADE
production_presentation_access projectId projects CASCADE CASCADE
production_presentation_access userId users CASCADE CASCADE
production_presentation_access createdById users SET NULL CASCADE
production_presentation_access updatedById users SET NULL CASCADE
presigned_url_requests projectId projects CASCADE CASCADE
presigned_url_requests userId users CASCADE CASCADE
project_audio_tracks projectId projects CASCADE CASCADE
project_element_defaults projectId projects CASCADE CASCADE
project_element_defaults source_element_id element_type_defaults SET NULL CASCADE
publish_events projectId projects CASCADE CASCADE
publish_events userId users SET NULL CASCADE
pwa_caches projectId projects CASCADE CASCADE
access_logs projectId projects CASCADE CASCADE
access_logs userId users SET NULL CASCADE
users app_roleId roles SET NULL CASCADE

Migration History

Migration Description
20260319000001-add-foreign-key-constraints.js Adds all FK constraints to enforce referential integrity
20260319000002-remove-redundant-deletion-columns.js Removes deprecated is_deleted and deleted_at_time columns from assets and projects
20260326000001-rename-ui-elements-to-element-type-defaults.js Renames ui_elements table to element_type_defaults for clarity
20260326000002-convert-element-type-enum-to-text.js Converts element type from ENUM to TEXT for flexibility
20260326000003-create-project-element-defaults.js Creates project_element_defaults table for project-specific element settings
20260326000004-backfill-project-element-defaults.js Backfills project_element_defaults for existing projects by snapshotting global defaults
20260326000005-fix-project-audio-tracks-environment.js Fixes project_audio_tracks.environment to NOT NULL with default 'dev'
20260326000006-copy-dev-to-stage.js Copies existing dev content to stage environment for all projects (initializes dev→stage workflow)
20260326043002-enforce-environment-not-null.js Enforces NOT NULL constraint on environment columns in tour_pages and transitions
20260326050442-remove-project-phase-column.js Removes redundant phase column from projects table (environment is on tour_pages)
20260326054410-remove-entry-page-slug-column.js Removes entry_page_slug from projects (entry page is first by sort_order)
20260326060000-convert-targetpageid-to-slug.js Converts targetPageId to targetPageSlug in ui_schema_json for environment-safe navigation
20260326060001-drop-page-elements-table.js Drops unused page_elements table (data stored in ui_schema_json)
20260326060002-drop-page-links-table.js Drops unused page_links table (navigation stored in ui_schema_json)
20260326060003-drop-transitions-table.js Drops unused transitions table (transitionVideoUrl stored in ui_schema_json)
20260326171017-add-missing-element-type-defaults.js Adds missing element types (spot, logo, popup) to element_type_defaults and backfills project_element_defaults
20260327000001-sync-all-element-type-defaults.js Syncs all 11 element types with correct sort_order and backfills missing project_element_defaults for all projects
20260331024423-remove-unused-theme-columns-from-projects.js Removes unused theme_config_json, custom_css_json, cdn_base_url columns from projects table
20260331054340-remove-duplicate-element-type-defaults.js Removes duplicate element_type_defaults records created during earlier migrations
20260331063424-cleanup-invalid-element-type-defaults.js Cleans up invalid element_type_defaults entries and ensures data integrity
20260403000001-add-background-video-settings.js Adds background video playback settings to tour_pages (autoplay, loop, muted, start_time, end_time)
20260409000001-add-design-dimensions-to-projects.js Adds design_width and design_height columns to projects table for canvas scaling
20260409111309-add-design-dimensions-to-tour-pages.js Adds design_width and design_height columns to tour_pages table for presentation isolation
20260422000001-add-background-video-play-once.js Adds background_video_play_once column to tour_pages for session-scoped single playback
20260605000001-add-background-audio-settings.js Adds background audio playback settings to tour_pages (autoplay, loop, start_time, end_time)
20260613000001-add-background-embed-url-to-tour-pages.js Adds background_embed_url to tour_pages for 360/embed page backgrounds
20260626000001-add-private-production-presentation-access.js Adds project production visibility and customer access grants for private production presentations
20260626000002-grant-account-manager-create-users.js Grants CREATE_USERS to Account Manager for customer viewer creation

Seeders

Seeder Description
20200430130759-admin-user.js Creates initial admin and test users
20200430130760-user-roles.js Creates roles, permissions, and role-permission assignments
20231127130745-sample-data.js Creates sample projects, pages, assets, and other demo data

Initial Users:

  • Admin: admin@flatlogic.com (admin password from config)
  • John Doe: john@doe.com (user password from config)
  • Client: client@hello.com (user password from config)

Base DB API Patterns

All entity DB APIs extend GenericDBApi which provides:

Configurable Properties

  • MODEL - Sequelize model reference (required, must be defined in subclass)
  • TABLE_NAME - Derived from MODEL.getTableName()
  • SEARCHABLE_FIELDS - Text search fields (ILIKE)
  • RANGE_FIELDS - Date/number range filters
  • ENUM_FIELDS - Exact match filters
  • RELATION_FILTERS - Related entity filters
  • CSV_FIELDS - Export field list (default: ['id', 'createdAt'])
  • AUTOCOMPLETE_FIELD - Field for autocomplete (default: 'name')
  • ASSOCIATIONS - Many-to-many relationships
  • FIND_BY_INCLUDES - Eager loading for findBy
  • FIND_ALL_INCLUDES - Eager loading for findAll
  • getFieldMapping(data) - Transform input data before save (default: returns data unchanged)

Standard Methods

  • create(data, options) - Create record with associations
  • bulkImport(data, options) - Bulk create records
  • update({ id, data, currentUser, transaction, runtimeContext }) - Update record
  • deleteByIds({ ids, currentUser, transaction, runtimeContext }) - Soft delete multiple records
  • remove({ id, currentUser, transaction, runtimeContext }) - Soft delete single record
  • findBy(where, options) - Find single record
  • findAll(filter, options) - Paginated list with filters
  • findAllAutocomplete({ query, limit, offset }, options) - Autocomplete search
  • toCSV(rows) - Export to CSV

Environment-Based Content

The platform uses an environment-based content model for publishing workflow:

  1. dev - Development environment (editable in constructor)
  2. stage - Staging/preview environment (for review before production)
  3. production - Live production environment (public-facing)

Content Flow

┌─────────┐    Save to Stage    ┌─────────┐    Publish    ┌────────────┐
│   dev   │ ─────────────────── │  stage  │ ───────────── │ production │
└─────────┘                     └─────────┘               └────────────┘
     ▲
     │
 Constructor
   edits

Tables with Environment Column

  • tour_pages - Pages with environment column
  • project_audio_tracks - Audio tracks with environment column
  • project_transition_settings - CSS transition settings with environment column

Source Key Tracking

When content is copied between environments, the source_key field stores the ID of the source record. This enables:

  • Tracking content lineage
  • Identifying which stage records came from dev
  • Rolling back changes if needed

Environment Access Control

Access to content is controlled by environment with strict isolation:

Environment Authentication Access Use Case
dev Required (JWT) Admin/Constructor only Editing in constructor
stage Required (JWT) Authenticated users Review workspace before publish
production Public (no auth) Anyone Published public tours

Security Layers:

  1. Backend Middleware (requireRuntimeReadOrAuth): Only production environment allows unauthenticated GET requests. Stage and dev require JWT authentication.

  2. Runtime Context API (getRuntimeEnvironment): Blocks dev environment from being accessed via X-Runtime-Environment header. Only production and stage are allowed.

  3. Database Filtering (applyRuntimeEnvironment): Applies environment filter to all queries when runtime context is present.

  4. Frontend Routes:

    • /p/[slug] → production (LayoutGuest)
    • /p/[slug]/stage → stage (LayoutAuthenticated)
    • /constructor → dev (LayoutAuthenticated)

Navigation uses targetPageSlug (not targetPageId) in ui_schema_json to ensure navigation works correctly across environments since page IDs differ between dev, stage, and production.


Data Types Reference

Sequelize Type PostgreSQL Type Usage
UUID uuid Primary keys, foreign keys
TEXT text Long strings (unlimited)
STRING(n) varchar(n) Limited strings
INTEGER integer Whole numbers
DECIMAL numeric Precise decimals
BOOLEAN boolean True/false
DATE timestamp with time zone Dates and times
JSON jsonb Structured data
ENUM enum type Fixed value sets
VIRTUAL (none) Computed fields

Common Model Options

Most models share these Sequelize options:

{
  timestamps: true,      // Adds createdAt, updatedAt
  paranoid: true,        // Soft delete via deletedAt
  freezeTableName: true, // Table name = model name
}

Exception: The file model does not set freezeTableName: true, so its table name is files (pluralized by Sequelize default).

All records include audit fields:

  • createdAt - Creation timestamp
  • updatedAt - Last modification timestamp
  • deletedAt - Soft deletion timestamp (null if not deleted)
  • createdById - Creating user's ID
  • updatedById - Last modifying user's ID