Add multi-player and remote bridge support
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
# Docker Compose Setup
|
||||
|
||||
This folder contains the public Docker Compose definitions for Pulse Signage and the remote player stack.
|
||||
|
||||
## Files
|
||||
|
||||
- [local.yml](local.yml) - full public stack with web, player, player bridge, and MySQL.
|
||||
- [remote.yml](remote.yml) - remote player-only stack for machines that sit behind the player bridge.
|
||||
- [local.env.example](local.env.example) - sample environment values for the public stack.
|
||||
- [remote.env.example](remote.env.example) - sample environment values for the remote stack.
|
||||
|
||||
## Stack Overview
|
||||
|
||||
### Public stack
|
||||
|
||||
The main compose stack is the most complete setup. It runs:
|
||||
|
||||
- `web` - the dashboard and admin app.
|
||||
- `player` - the local screen player.
|
||||
- `player-bridge` - the bridge service that proxies dashboard commands and player communication.
|
||||
- `mysql` - the database used by the web, player, and bridge services.
|
||||
|
||||
This is the stack to use when you want the full app running on one machine.
|
||||
|
||||
### Remote player
|
||||
|
||||
The remote stack runs only the `player` service.
|
||||
|
||||
Use it when the player is installed on a remote device and connects back through the player bridge instead of running the full app separately.
|
||||
|
||||
## Services
|
||||
|
||||
### `web`
|
||||
|
||||
The dashboard and admin application.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- serves the web UI on port `8080`
|
||||
- reads and writes application data from MySQL
|
||||
- forwards player actions through the configured bridge base URL
|
||||
- renders connected clients, dashboard pages, and admin workflows
|
||||
|
||||
Key configuration:
|
||||
|
||||
- `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`
|
||||
- `PULSE_SIGNAGE_SHARED_SECRET`
|
||||
- `SESSION_MAX_AGE_DAYS`
|
||||
- `DEFAULT_ADMIN_USERNAME`
|
||||
- `DEFAULT_ADMIN_NAME`
|
||||
- `DEFAULT_ADMIN_PASSWORD`
|
||||
- `PASSWORD_HASH_ITERATIONS`
|
||||
|
||||
### `player`
|
||||
|
||||
The screen runtime that renders playlists and receives commands.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- serves the player UI on port `8081`
|
||||
- connects to MySQL in local mode
|
||||
- connects to the bridge in remote mode through `THIN_CLIENT_BASE_URL`
|
||||
- registers live connections and accepts control commands
|
||||
|
||||
Key configuration:
|
||||
|
||||
- `PLAYER_PUBLIC_BASE_URL`
|
||||
- `PLAYER_INTERNAL_BASE_URL`
|
||||
- `PLAYER_IDENTIFIER`
|
||||
- `THIN_CLIENT_BASE_URL` in remote mode
|
||||
- `PULSE_SIGNAGE_SHARED_SECRET`
|
||||
- database settings in local mode
|
||||
|
||||
### `player-bridge`
|
||||
|
||||
The bridge layer that connects the dashboard to the player network.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- serves the bridge API on port `8090`
|
||||
- forwards authenticated dashboard commands to registered players
|
||||
- exposes screen connection snapshots and player registration data
|
||||
- proxies command traffic between the web app and remote players
|
||||
|
||||
Key configuration:
|
||||
|
||||
- `PULSE_SIGNAGE_SHARED_SECRET`
|
||||
- `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`
|
||||
|
||||
### `mysql`
|
||||
|
||||
The MySQL 8.4 database used by the public stack.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- stores application data, screen state, onboarding state, and registry records
|
||||
- provides persistent storage through `mysql_data`
|
||||
|
||||
Key configuration:
|
||||
|
||||
- `MYSQL_DATABASE`
|
||||
- `MYSQL_USER`
|
||||
- `MYSQL_PASSWORD`
|
||||
- `MYSQL_ROOT_PASSWORD`
|
||||
|
||||
## Environment Files
|
||||
|
||||
### `local.env.example`
|
||||
|
||||
Use this file as a starting point for the public compose stack.
|
||||
|
||||
Important values:
|
||||
|
||||
- `PULSE_SIGNAGE_IMAGE` - image to run for all app services
|
||||
- `PULSE_SIGNAGE_SHARED_SECRET` - shared secret used for request authentication
|
||||
- `PLAYER_IDENTIFIER` - unique local player identifier
|
||||
- `DB_*` - MySQL credentials and database name for the stack
|
||||
- `PLAYER_PUBLIC_BASE_URL` - public URL the player advertises
|
||||
- `PLAYER_INTERNAL_BASE_URL` - internal URL the web app uses for local player calls
|
||||
- `SESSION_MAX_AGE_DAYS` - dashboard session lifetime
|
||||
- `DEFAULT_ADMIN_*` - bootstrap admin account values
|
||||
- `PASSWORD_HASH_ITERATIONS` - password hashing cost
|
||||
|
||||
### `remote.env.example`
|
||||
|
||||
Use this file on a remote player device.
|
||||
|
||||
Important values:
|
||||
|
||||
- `PULSE_SIGNAGE_IMAGE` - image to run on the device
|
||||
- `PULSE_SIGNAGE_SHARED_SECRET` - must match the public stack
|
||||
- `PLAYER_IDENTIFIER` - unique remote player identifier
|
||||
- `PLAYER_PUBLIC_BASE_URL` - public URL for the remote player
|
||||
- `THIN_CLIENT_BASE_URL` - bridge URL the player connects back to
|
||||
- `PLAYER_AGENT_RECONNECT_DELAY_MS` - reconnect delay for the player agent
|
||||
|
||||
## Main Configuration Variables
|
||||
|
||||
| Variable | Used By | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `PULSE_SIGNAGE_IMAGE` | all services | Docker image to run for the app services. |
|
||||
| `PULSE_SIGNAGE_SHARED_SECRET` | web, player, bridge | Shared secret for authenticated requests between services. |
|
||||
| `DB_HOST` | web, player, bridge | Database host name. |
|
||||
| `DB_PORT` | web, player, bridge | Database port. |
|
||||
| `DB_NAME` | web, player, bridge | Database name. |
|
||||
| `DB_USER` | web, player, bridge | Database user. |
|
||||
| `DB_PASSWORD` | web, player, bridge | Database password. |
|
||||
| `MYSQL_ROOT_PASSWORD` | mysql | Root password for the local MySQL container. |
|
||||
| `PLAYER_PUBLIC_BASE_URL` | player | Public URL advertised by the player. |
|
||||
| `PLAYER_INTERNAL_BASE_URL` | web, player | Internal player URL used by the dashboard and player runtime. |
|
||||
| `PLAYER_IDENTIFIER` | player | Stable player identifier. |
|
||||
| `THIN_CLIENT_BASE_URL` | player, remote player | URL of the bridge service. |
|
||||
| `SESSION_MAX_AGE_DAYS` | web | Session cookie lifetime. |
|
||||
| `DEFAULT_ADMIN_USERNAME` | web | Bootstrap admin username. |
|
||||
| `DEFAULT_ADMIN_NAME` | web | Bootstrap admin display name. |
|
||||
| `DEFAULT_ADMIN_PASSWORD` | web | Bootstrap admin password. |
|
||||
| `PASSWORD_HASH_ITERATIONS` | web | Password hashing cost. |
|
||||
| `PLAYER_AGENT_RECONNECT_DELAY_MS` | remote player | Delay before reconnecting to the bridge. |
|
||||
|
||||
## Ports
|
||||
|
||||
Public stack ports:
|
||||
|
||||
- `8080` - web dashboard
|
||||
- `8081` - player
|
||||
- `8090` - player bridge
|
||||
- `3306` - MySQL
|
||||
|
||||
Remote stack ports:
|
||||
|
||||
- `8081` - player only
|
||||
|
||||
## Volumes
|
||||
|
||||
### Public stack
|
||||
|
||||
- `mysql_data` - persistent MySQL data.
|
||||
- `pulse-signage` - shared media and cache volume for the app services.
|
||||
|
||||
### Remote stack
|
||||
|
||||
- `pulse-signage` - shared media and cache volume for the remote player.
|
||||
|
||||
## Networks
|
||||
|
||||
Each compose file creates its own named network:
|
||||
|
||||
- `pulse-signage` for the public stack
|
||||
- `pulse-signage-remote` for remote player deployment.
|
||||
|
||||
## Notes
|
||||
|
||||
- The public stack expects the app services and MySQL to share the same `PULSE_SIGNAGE_SHARED_SECRET`.
|
||||
- The bridge service is the dashboard-facing command path for connected remote players.
|
||||
- The remote player should point `THIN_CLIENT_BASE_URL` at the bridge, not at the public web endpoint.
|
||||
- The `PULSE_SIGNAGE_IMAGE` tag defaults to the published image, but it can be overridden for local builds or custom releases.
|
||||
|
||||
## Recommended Setup
|
||||
|
||||
1. Copy `local.env.example` to a local `.env` file for the public stack.
|
||||
2. Copy `remote.env.example` to a device-specific `.env` file for the remote player.
|
||||
3. Make sure `PULSE_SIGNAGE_SHARED_SECRET` matches everywhere.
|
||||
4. Start the public stack first, then start the remote player after the bridge is reachable.
|
||||
5. Verify that the player appears in Connected clients before testing screen commands.
|
||||
@@ -0,0 +1,19 @@
|
||||
PULSE_SIGNAGE_IMAGE=git.lzstealth.com/lzstealth/pulse-signage:latest
|
||||
PULSE_SIGNAGE_SHARED_SECRET=
|
||||
PLAYER_IDENTIFIER=player-local
|
||||
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
DB_NAME=signage
|
||||
DB_USER=signage_user
|
||||
DB_PASSWORD=signage_password
|
||||
MYSQL_ROOT_PASSWORD=root_password
|
||||
|
||||
PLAYER_PUBLIC_BASE_URL=http://localhost:8081
|
||||
PLAYER_INTERNAL_BASE_URL=http://player:8081
|
||||
|
||||
SESSION_MAX_AGE_DAYS=14
|
||||
DEFAULT_ADMIN_USERNAME=admin
|
||||
DEFAULT_ADMIN_NAME=Admin
|
||||
DEFAULT_ADMIN_PASSWORD=admin
|
||||
PASSWORD_HASH_ITERATIONS=310000
|
||||
@@ -0,0 +1,103 @@
|
||||
name: pulse-signage
|
||||
|
||||
services:
|
||||
web:
|
||||
image: ${PULSE_SIGNAGE_IMAGE:-git.lzstealth.com/lzstealth/pulse-signage:latest}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
DB_HOST: ${DB_HOST:-mysql}
|
||||
DB_PORT: ${DB_PORT:-3306}
|
||||
DB_NAME: ${DB_NAME:-signage}
|
||||
DB_USER: ${DB_USER:-signage_user}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-signage_password}
|
||||
PULSE_SIGNAGE_SHARED_SECRET: ${PULSE_SIGNAGE_SHARED_SECRET:-}
|
||||
SESSION_MAX_AGE_DAYS: ${SESSION_MAX_AGE_DAYS:-14}
|
||||
DEFAULT_ADMIN_USERNAME: ${DEFAULT_ADMIN_USERNAME:-admin}
|
||||
DEFAULT_ADMIN_NAME: ${DEFAULT_ADMIN_NAME:-Admin}
|
||||
DEFAULT_ADMIN_PASSWORD: ${DEFAULT_ADMIN_PASSWORD:-admin}
|
||||
PASSWORD_HASH_ITERATIONS: ${PASSWORD_HASH_ITERATIONS:-310000}
|
||||
volumes:
|
||||
- pulse-signage:/app/media
|
||||
command: ["node", "src/web.js"]
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- pulse_signage
|
||||
|
||||
player:
|
||||
image: ${PULSE_SIGNAGE_IMAGE:-git.lzstealth.com/lzstealth/pulse-signage:latest}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8081:8081"
|
||||
environment:
|
||||
PLAYER_PUBLIC_BASE_URL: ${PLAYER_PUBLIC_BASE_URL:-http://localhost:8081}
|
||||
PLAYER_INTERNAL_BASE_URL: ${PLAYER_INTERNAL_BASE_URL:-http://player:8081}
|
||||
PLAYER_IDENTIFIER: ${PLAYER_IDENTIFIER:-player-local}
|
||||
PULSE_SIGNAGE_SHARED_SECRET: ${PULSE_SIGNAGE_SHARED_SECRET:-}
|
||||
DB_HOST: ${DB_HOST:-mysql}
|
||||
DB_PORT: ${DB_PORT:-3306}
|
||||
DB_NAME: ${DB_NAME:-signage}
|
||||
DB_USER: ${DB_USER:-signage_user}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-signage_password}
|
||||
volumes:
|
||||
- pulse-signage:/app/media
|
||||
command: ["node", "src/player.js"]
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- pulse_signage
|
||||
|
||||
player-bridge:
|
||||
image: ${PULSE_SIGNAGE_IMAGE:-git.lzstealth.com/lzstealth/pulse-signage:latest}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8090:8090"
|
||||
environment:
|
||||
PULSE_SIGNAGE_SHARED_SECRET: ${PULSE_SIGNAGE_SHARED_SECRET:-}
|
||||
DB_HOST: ${DB_HOST:-mysql}
|
||||
DB_PORT: ${DB_PORT:-3306}
|
||||
DB_NAME: ${DB_NAME:-signage}
|
||||
DB_USER: ${DB_USER:-signage_user}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-signage_password}
|
||||
command: ["node", "src/player-bridge/index.js"]
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- pulse_signage
|
||||
|
||||
mysql:
|
||||
image: mysql:8.4
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_DATABASE: ${DB_NAME:-signage}
|
||||
MYSQL_USER: ${DB_USER:-signage_user}
|
||||
MYSQL_PASSWORD: ${DB_PASSWORD:-signage_password}
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root_password}
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$$MYSQL_ROOT_PASSWORD"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
- pulse_signage
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
pulse-signage:
|
||||
|
||||
networks:
|
||||
pulse_signage:
|
||||
name: pulse-signage
|
||||
external: false
|
||||
@@ -0,0 +1,8 @@
|
||||
PULSE_SIGNAGE_IMAGE=git.lzstealth.com/lzstealth/pulse-signage:latest
|
||||
PULSE_SIGNAGE_SHARED_SECRET=
|
||||
PLAYER_IDENTIFIER=player-remote
|
||||
|
||||
PLAYER_PUBLIC_BASE_URL=http://localhost:8081
|
||||
THIN_CLIENT_BASE_URL=http://192.168.0.80:8090
|
||||
|
||||
PLAYER_AGENT_RECONNECT_DELAY_MS=2000
|
||||
@@ -0,0 +1,28 @@
|
||||
name: pulse-signage-remote
|
||||
|
||||
services:
|
||||
|
||||
player:
|
||||
image: ${PULSE_SIGNAGE_IMAGE:-git.lzstealth.com/lzstealth/pulse-signage:latest}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8081:8081"
|
||||
environment:
|
||||
PLAYER_PUBLIC_BASE_URL: ${PLAYER_PUBLIC_BASE_URL:-http://localhost:8081}
|
||||
THIN_CLIENT_BASE_URL: ${THIN_CLIENT_BASE_URL:-}
|
||||
PULSE_SIGNAGE_SHARED_SECRET: ${PULSE_SIGNAGE_SHARED_SECRET:-}
|
||||
volumes:
|
||||
- pulse-signage:/app/media
|
||||
command: ["node", "src/player.js"]
|
||||
networks:
|
||||
- pulse_signage
|
||||
|
||||
|
||||
|
||||
volumes:
|
||||
pulse-signage:
|
||||
|
||||
networks:
|
||||
pulse_signage:
|
||||
name: pulse-signage-remote
|
||||
external: false
|
||||
Reference in New Issue
Block a user