Files
pulse-signage/docs/technical/schema.md
T
lzstealth 49df51d92f
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m13s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 31s
Release 2.16.2
2026-10-21 01:38:22 +01:00

16 KiB

Database Schema

This app creates and maintains its schema at startup through src/db/index.js. The sections below summarize the current tables and their purpose.

Tables generally use a numeric auto-increment id primary key. The relationship table d_announcement_screens intentionally uses the composite (announcement_id, screen_id) primary key instead. Natural and relationship keys remain as unique constraints where needed. Timestamps are stored as created_at and modified_at when a table supports auditing.

Admin

  • a_users - admin user accounts and password hashes.
  • a_roles - named role definitions.
  • a_permissions - permission catalog seeded from the application constants.
  • a_role_permissions - many-to-many mapping between roles and permissions.
  • a_user_roles - many-to-many mapping between users and roles.
  • a_sessions - persisted admin session tokens.
  • a_account_tokens - short-lived account verification and password-reset tokens.
  • a_user_invitations - pending user invitations and assigned role ids.
  • a_login_attempts - login rate-limit and lockout state.

a_users

  • id, name, username, email, email_verified_at, pending_email, pending_email_token_hash, pending_email_expires_at, password_hash, password_salt, password_iterations, must_change_password, account_locked, last_login_at, last_login_ip, created_at, created_by, modified_at, modified_by
  • username is unique.

a_account_tokens

  • id, user_id, token_type, token_hash, expires_at, used_at, created_at
  • token_hash is unique.
  • Foreign key: - user_id -> a_users.id with ON DELETE CASCADE
  • Indexed by (token_type, token_hash, expires_at) and (user_id, token_type).

a_user_invitations

  • id, email, name, role_ids_json, token_hash, expires_at, used_at, created_at, created_by
  • token_hash is unique.
  • Indexed by (email, used_at, expires_at) and (created_by, created_at).

a_login_attempts

  • id, rate_key, failed_count, last_failed_at, locked_until, created_at, modified_at
  • rate_key is unique.

a_roles

  • id, role_key, name, description, created_at, created_by, modified_at, modified_by
  • role_key is unique.
  • name is unique.

a_permissions

  • id, permission_key, name, section_name, description, created_at, modified_at
  • permission_key is unique.

a_role_permissions

  • id, role_id, permission_id, created_at, modified_at
  • Foreign keys: - role_id -> a_roles.id - permission_id -> a_permissions.id
  • Unique key: (role_id, permission_id)

a_user_roles

  • id, user_id, role_id, created_at, modified_at
  • Foreign keys: - user_id -> a_users.id - role_id -> a_roles.id
  • Unique key: (user_id, role_id)

a_sessions

  • id, session_hash, user_id, ip_address, user_agent, expires_at, created_at, created_by, last_used_at, modified_by
  • session_hash is unique.
  • Foreign key: - user_id -> a_users.id

Content

  • c_canvas_sizes - reusable canvas presets for templates.
  • c_playlists - playlist definitions, playback options, and canvas assignment.
  • c_templates - slide templates with canvas and background settings.
  • c_template_regions - template region layout and metadata.
  • c_slides - slide records with template binding, JSON content, and thumbnail path.
  • c_media_assets - reusable uploaded media files referenced by slides and templates.
  • c_playlist_slides - ordered playlist items and timing.
  • c_playlist_slide_schedule_rules - rule rows attached to playlist slides.

c_canvas_sizes

  • id, name, width, height, created_at, created_by, modified_at, modified_by
  • (width, height) is unique.

c_playlists

  • id, name, fade_between_slides, skip_unavailable_rtmp, canvas_id, created_at, created_by, modified_at, modified_by
  • Foreign key: - canvas_id -> c_canvas_sizes.id with ON DELETE SET NULL

c_templates

  • id, name, canvas_size_id, background_image_path, background_color, background_gradient, created_at, created_by, modified_at, modified_by
  • background_gradient stores normalized linear gradient settings as JSON text, including angle and colour stops.
  • Foreign key: - canvas_size_id -> c_canvas_sizes.id with ON DELETE SET NULL

c_template_regions

  • id, template_id, region_key, region_type, label, font_family, lock_ratio, animation_json, x, y, width, height, z_index, created_at, created_by, modified_at, modified_by
  • Foreign key: - template_id -> c_templates.id with ON DELETE CASCADE

c_slides

  • id, title, template_id, content_json, thumbnail_path, created_at, created_by, modified_at, modified_by
  • Foreign key: - template_id -> c_templates.id with ON DELETE SET NULL

c_media_assets

  • id, media_path, original_name, media_type, mime_type, file_size, is_published, created_at, created_by, modified_at, modified_by
  • media_path is unique.
  • Indexed by media_type and created_at.

c_playlist_slides

  • id, playlist_id, slide_id, position, duration_seconds, use_video_duration, disable_audio, created_at, created_by, modified_at, modified_by
  • Foreign keys: - playlist_id -> c_playlists.id with ON DELETE CASCADE - slide_id -> c_slides.id with ON DELETE CASCADE

c_playlist_slide_schedule_rules

  • id, playlist_slide_id, position, start_datetime, end_datetime, start_time, end_time, schedule_days_json, created_at, created_by, modified_at, modified_by

  • Foreign key: - playlist_slide_id -> c_playlist_slides.id with ON DELETE CASCADE

  • Index: - (playlist_slide_id, position)

  • Each playlist slide can have zero or more schedule rules.

  • Rules are evaluated with OR across rows and with AND across the fields inside a single rule.

Devices

  • d_players - player registry and connection metadata.
  • d_screens - screen records and playlist assignment.
  • d_onboarding_devices - device-to-screen bindings and onboarded client names.

d_players

  • id, identifier, public_base_url, internal_base_url, last_seen_at, created_at, modified_at
  • id is the primary key.
  • identifier is unique and is the stable player identity used by the app.

d_screens

  • id, name, slug, playlist_id, created_at, created_by, modified_at, modified_by

  • slug is unique.

  • Foreign keys: - playlist_id -> c_playlists.id with ON DELETE SET NULL

  • Screens are no longer tied to a player foreign key directly; player registration and live connection metadata are tracked separately in d_players.

d_onboarding_devices

  • id, device_id, client_name, screen_id, created_at, created_by, modified_at, modified_by, last_seen_at
  • device_id is unique.
  • Foreign key: - screen_id -> d_screens.id with ON DELETE SET NULL

Onboarding

  • The onboarding flow uses d_onboarding_devices to bind a device to a screen and persist the client name.

Announcements

  • d_announcements - announcement content and display metadata.
  • d_announcement_screens - announcement-to-screen assignments.

d_announcements

  • id, message, short_label, announcement_type, color_key, icon_key, duration_seconds, expires_at, created_at, created_by, modified_at, modified_by
  • announcement_type defaults to lower-third.

d_announcement_screens

  • announcement_id, screen_id, created_at, created_by, modified_at, modified_by
  • Foreign keys: - announcement_id -> d_announcements.id with ON DELETE CASCADE - screen_id -> d_screens.id with ON DELETE CASCADE
  • Unique key: (announcement_id, screen_id)

Integrations

  • i_rss_feeds - RSS feed definitions and refresh cadence.
  • i_rss_feed_items - cached RSS feed items.
  • i_api_source_groups - API source names and shared authentication settings.
  • i_api_sources - named requests, independent refresh settings, and last response snapshots.
  • i_weather_locations - configured weather locations and last response snapshot.
  • i_timetable_groups - grouped timetable definitions used by the timetable region.
  • i_timetable_entries - dated entries that belong to a timetable group.

i_rss_feeds

  • id, name, feed_url, update_interval_value, update_interval_unit, item_limit, enabled, last_pulled_at, created_at, created_by, modified_at, modified_by

i_rss_feed_items

  • id, rss_feed_id, position, item_json, created_at, created_by, modified_at, modified_by
  • Foreign key: - rss_feed_id -> i_rss_feeds.id with ON DELETE CASCADE
  • Unique key: - (rss_feed_id, position)

i_api_source_groups

  • id, name, enabled, auth_method, auth_username, auth_password, auth_bearer_token, auth_header_name, auth_header_value, token_url, token_request_body_json, token_response_path, token_refresh_url, token_refresh_request_body_json, token_refresh_response_path, token_header_name, token_header_prefix, created_at, created_by, modified_at, modified_by
  • Unique group name; authentication applies to every child request. auth_method = 'none' supports public endpoints. Token caches are keyed by group and authentication configuration.
  • enabled is the source-wide refresh gate, added by migration 2.16.2 with a default of 1. Disabling a group leaves child enabled flags unchanged. Refresh lookups combine group/request flags, and queued work checks effective enabled state before making an outbound request.

i_api_sources

  • id, auth_source_id, name, api_url, request_method, request_body_json, items_path, update_interval_value, update_interval_unit, enabled, last_pulled_at, last_pull_error, last_response_status, last_response_content_type, last_response_json, created_at, created_by, modified_at, modified_by
  • auth_source_id references i_api_source_groups.id with restricted parent deletion. The web application requires at least one request per group and unique request names within each group.
  • Request IDs remain the source_id stored in slide regions and the keys used by scheduled refreshes. Removing a request used by a slide is blocked.
  • The 2.16.1 migration copies each existing source's authentication into a same-ID parent group and links the original request without changing its ID or response snapshot. Legacy authentication columns remain on request rows for schema compatibility but runtime authentication is loaded from the parent.
  • Editor/player request lists exclude authentication credentials. Refreshes resolve current parent authentication at execution time.

i_weather_locations

  • id, name, location_label, latitude, longitude, timezone, provider, temperature_unit, wind_unit, precipitation_unit, update_interval_value, update_interval_unit, enabled, last_pulled_at, last_pull_error, last_response_json, created_at, created_by, modified_at, modified_by
  • Stores configured weather locations and the most recent provider response used for forecast previews and weather regions.

i_timetable_groups

  • id, name, short_description, timezone, created_at, created_by, modified_at, modified_by
  • timezone defaults to Europe/London.

i_timetable_entries

  • id, schedule_group_id, title, short_description, start_datetime, end_datetime, created_at, created_by, modified_at, modified_by
  • Foreign key: - schedule_group_id -> i_timetable_groups.id with ON DELETE CASCADE
  • Index: - (schedule_group_id, start_datetime)

Operations

  • o_background_tasks - queue and history for background jobs.
  • o_app_state - generic app state and version markers stored as key/value pairs.
  • o_app_settings - administrator-configurable application settings stored by key.
  • o_audit_events - retained audit events for administrator activity and system changes.

o_background_tasks

  • id, task_key, task_type, title, category, status, payload_json, metadata_json, attempts, created_at, created_by, started_at, finished_at, error_message
  • Indexed by status, task_key, and task_type.

o_app_state

  • id, state_key, state_value, created_at, modified_at
  • state_key is unique.
  • schema_version is stored here so startup can detect the previously recorded schema version before deciding whether migrations need to run.

o_app_settings

  • id, setting_key, setting_value, created_at, created_by, modified_at, modified_by
  • setting_key is unique.
  • Values are stored as JSON and validated against the application setting definitions in src/data/app-settings.js.

o_audit_events

  • id, occurred_at, category, event_type, actor_user_id, target_type, target_id, target_label, ip_address, user_agent, details_json
  • Indexed by occurrence time, category and event type, actor, and target.

Notes

  • The schema is initialized with CREATE TABLE IF NOT EXISTS, so new installs can start from an empty database.
  • src/db/bootstrap.js seeds the canvas size defaults, default permissions, and the default administrator role.
  • src/db/migrations.js records the current schema version in o_app_state during startup so later launches can tell whether an update is happening.
  • The ER diagram shows declared foreign keys and the logical audit actor association; player registry and JSON-based references are intentionally not shown as foreign-key relationships.
erDiagram
        A_USERS {
        }
        A_ROLES {
        }
        A_PERMISSIONS {
        }
        A_ROLE_PERMISSIONS {
        }
        A_USER_ROLES {
        }
        A_SESSIONS {
        }
        A_ACCOUNT_TOKENS {
        }
        A_USER_INVITATIONS {
        }
        A_LOGIN_ATTEMPTS {
        }
        C_CANVAS_SIZES {
        }
        C_PLAYLISTS {
        }
        C_TEMPLATES {
        }
        C_TEMPLATE_REGIONS {
        }
        C_SLIDES {
        }
        C_MEDIA_ASSETS {
        }
        C_PLAYLIST_SLIDES {
        }
        C_PLAYLIST_SLIDE_SCHEDULE_RULES {
        }
        D_PLAYERS {
        }
        D_SCREENS {
        }
        D_ONBOARDING_DEVICES {
        }
        D_ANNOUNCEMENTS {
        }
        D_ANNOUNCEMENT_SCREENS {
        }
        I_RSS_FEEDS {
        }
        I_RSS_FEED_ITEMS {
        }
        I_API_SOURCES {
        }
        I_API_SOURCE_GROUPS {
        }
        I_WEATHER_LOCATIONS {
        }
        I_TIMETABLE_GROUPS {
        }
        I_TIMETABLE_ENTRIES {
        }
        O_APP_STATE {
        }
        O_APP_SETTINGS {
        }
        O_BACKGROUND_TASKS {
        }
        O_AUDIT_EVENTS {
        }

        A_USERS ||--o{ A_USER_ROLES : has
        A_ROLES ||--o{ A_USER_ROLES : assigned_to
        A_ROLES ||--o{ A_ROLE_PERMISSIONS : has
        A_PERMISSIONS ||--o{ A_ROLE_PERMISSIONS : granted_to
        A_USERS ||--o{ A_SESSIONS : owns
        A_USERS ||--o{ A_ACCOUNT_TOKENS : has
        A_USERS o|--o{ O_AUDIT_EVENTS : acts
        I_API_SOURCE_GROUPS ||--o{ I_API_SOURCES : contains

        C_CANVAS_SIZES o|--o{ C_TEMPLATES : used_by
        C_CANVAS_SIZES o|--o{ C_PLAYLISTS : used_by
        C_TEMPLATES ||--o{ C_TEMPLATE_REGIONS : contains
        C_TEMPLATES o|--o{ C_SLIDES : used_by
        C_MEDIA_ASSETS }o..o{ C_SLIDES : referenced_by
        C_MEDIA_ASSETS o{..o| C_TEMPLATES : background_for
        C_PLAYLISTS ||--o{ C_PLAYLIST_SLIDES : contains
        C_SLIDES ||--o{ C_PLAYLIST_SLIDES : included_in
        C_PLAYLIST_SLIDES ||--o{ C_PLAYLIST_SLIDE_SCHEDULE_RULES : has_rules

        C_PLAYLISTS o|--o{ D_SCREENS : uses
        D_SCREENS o|--o{ D_ONBOARDING_DEVICES : binds
        D_ANNOUNCEMENTS ||--o{ D_ANNOUNCEMENT_SCREENS : targets
        D_SCREENS ||--o{ D_ANNOUNCEMENT_SCREENS : receives

        I_RSS_FEEDS ||--o{ I_RSS_FEED_ITEMS : caches
        I_TIMETABLE_GROUPS ||--o{ I_TIMETABLE_ENTRIES : contains