Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67bcd3640a | ||
|
|
095fe66118 | ||
|
|
a3e1282354 | ||
|
|
6417c3aa54 | ||
|
|
c1d9fa0527 |
@@ -44,7 +44,7 @@ title-bar controls). The rules that matter most:
|
|||||||
- `prodcom-listener/` — live ProdCom transcript web app. Zero-dependency Node
|
- `prodcom-listener/` — live ProdCom transcript web app. Zero-dependency Node
|
||||||
`server.js` that serves `public/` and proxies `/prodcom/*` (including the SSE
|
`server.js` that serves `public/` and proxies `/prodcom/*` (including the SSE
|
||||||
transcript stream) to the ProdCom API at `config.json → prodcomUrl`
|
transcript stream) to the ProdCom API at `config.json → prodcomUrl`
|
||||||
(default `http://10.3.11.152:24480`). Good reference for: SSE handling,
|
(set per site; each campus runs its own). Good reference for: SSE handling,
|
||||||
reconnect logic, channel colors/filtering, PWA install.
|
reconnect logic, channel colors/filtering, PWA install.
|
||||||
- `band-lineup-display/` — backstage Electron app (stage lineup photos with
|
- `band-lineup-display/` — backstage Electron app (stage lineup photos with
|
||||||
mic/pack labels) plus ProPresenter Now/Next:
|
mic/pack labels) plus ProPresenter Now/Next:
|
||||||
|
|||||||
@@ -100,10 +100,24 @@ with this machine's shell settings — the same keys as the checked-in
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `port` | Port ProdDash serves on (default `24500`). The macOS launcher writes this one, so the port it is set to holds however ProdDash is started. |
|
| `port` | Port ProdDash serves on (default `24500`). The macOS launcher writes this one, so the port it is set to holds however ProdDash is started. |
|
||||||
| `adminPasscode` | If set, the admin page asks for this shared passcode. Leave `""` for none — it's a plain-HTTP LAN tool. |
|
| `adminPasscode` | If set, the admin page asks for this shared passcode. Leave `""` for none — it's a plain-HTTP LAN tool. |
|
||||||
|
| `forge` | Where updates and module installs are fetched from. Absent or `"github"` uses GitHub; a Gitea or Forgejo base URL such as `"http://10.3.8.90:3000"` uses a server of your own. Set it per machine in the data directory, alongside `repo`. |
|
||||||
| `theme` | The dashboard palette: `booth` (default), `harbor`, `graphite`, `ember` or `daylight`. The admin page's **Theme** section writes this one — see [Themes](#themes). |
|
| `theme` | The dashboard palette: `booth` (default), `harbor`, `graphite`, `ember` or `daylight`. The admin page's **Theme** section writes this one — see [Themes](#themes). |
|
||||||
|
|
||||||
|
**Updating from your own git server.** ProdDash fetches one `tar.gz` of the
|
||||||
|
branch — nothing else — so any Gitea or Forgejo works. Point `forge` at its
|
||||||
|
base URL and `repo` at the owner/repo there:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "forge": "http://10.3.8.90:3000", "repo": "michael/ProdDash", "branch": "main" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Gitea spells an archive URL differently from GitHub, which is the only thing
|
||||||
|
that changes; the tarball itself is identical, commit hash included, so update
|
||||||
|
detection and module installs behave exactly as before. Make the repository
|
||||||
|
readable without signing in, or ProdDash cannot fetch it.
|
||||||
|
|
||||||
Environment variables override both files: `PORT`, `PRODDASH_PASSCODE`, and
|
Environment variables override both files: `PORT`, `PRODDASH_PASSCODE`, and
|
||||||
`PRODDASH_DATA_DIR` to put the data directory somewhere else (a relative path
|
`PRODDASH_FORGE`, and `PRODDASH_DATA_DIR` to put the data directory somewhere else (a relative path
|
||||||
is taken from the app folder; `PRODDASH_DATA_DIR=config` restores the old
|
is taken from the app folder; `PRODDASH_DATA_DIR=config` restores the old
|
||||||
in-repo location). If the default directory can't be created, the server says
|
in-repo location). If the default directory can't be created, the server says
|
||||||
so and falls back to the app's `config/` folder.
|
so and falls back to the app's `config/` folder.
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ modules/<id>/
|
|||||||
"minSize": { "w": 2, "h": 2 },
|
"minSize": { "w": 2, "h": 2 },
|
||||||
"defaultSize": { "w": 4, "h": 3 },
|
"defaultSize": { "w": 4, "h": 3 },
|
||||||
"configSchema": {
|
"configSchema": {
|
||||||
"prodcomUrl": { "type": "string", "label": "ProdCom URL", "default": "http://10.3.11.152:24480" },
|
"prodcomUrl": { "type": "string", "label": "ProdCom URL", "default": "" },
|
||||||
"apiKey": { "type": "password", "label": "API key (if PSK auth enabled)", "default": "" }
|
"apiKey": { "type": "password", "label": "API key (if PSK auth enabled)", "default": "" }
|
||||||
},
|
},
|
||||||
"instanceSchema": {
|
"instanceSchema": {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"port": 24500,
|
"port": 24500,
|
||||||
"adminPasscode": "",
|
"adminPasscode": "",
|
||||||
|
"forge": "github",
|
||||||
"repo": "michaelmcgary2008/ProdDash",
|
"repo": "michaelmcgary2008/ProdDash",
|
||||||
"branch": "main"
|
"branch": "main"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "prodcom-transcript",
|
"id": "prodcom-transcript",
|
||||||
"name": "ProdCom",
|
"name": "ProdCom",
|
||||||
"version": "1.3.0",
|
"version": "1.3.1",
|
||||||
"proddash": ">=1.1.0",
|
"proddash": ">=1.1.0",
|
||||||
"description": "Live ProdCom transcript",
|
"description": "Live ProdCom transcript",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
@@ -26,9 +26,10 @@
|
|||||||
"type": "endpoint",
|
"type": "endpoint",
|
||||||
"label": "ProdCom server",
|
"label": "ProdCom server",
|
||||||
"default": {
|
"default": {
|
||||||
"host": "10.3.11.152",
|
"host": "",
|
||||||
"port": 24480
|
"port": 24480
|
||||||
}
|
},
|
||||||
|
"help": "The ProdCom server at THIS location — every campus has its own. Blank until you set it, so a new install never reads another campus's comms."
|
||||||
},
|
},
|
||||||
"apiKey": {
|
"apiKey": {
|
||||||
"type": "password",
|
"type": "password",
|
||||||
|
|||||||
@@ -269,6 +269,9 @@ module.exports = {
|
|||||||
streams,
|
streams,
|
||||||
health() {
|
health() {
|
||||||
if (status.state === 'ok') return { status: 'ok', message: `Live — ${channels.length} channel${channels.length === 1 ? '' : 's'}, ${entries.size} entries` };
|
if (status.state === 'ok') return { status: 'ok', message: `Live — ${channels.length} channel${channels.length === 1 ? '' : 's'}, ${entries.size} entries` };
|
||||||
|
// Nothing to connect to is a setting that is missing, not a link that
|
||||||
|
// is coming up — it reads red, like the other modules say it.
|
||||||
|
if (!target) return { status: 'error', message: status.message };
|
||||||
return { status: status.state === 'error' ? 'error' : 'connecting', message: status.message };
|
return { status: status.state === 'error' ? 'error' : 'connecting', message: status.message };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Edit [config.json](config.json):
|
|||||||
|
|
||||||
| Key | Meaning |
|
| Key | Meaning |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `prodcomUrl` | Where the ProdCom API lives (default `http://10.3.11.152:24480`) |
|
| `prodcomUrl` | Where the ProdCom API lives — set it per site, e.g. `http://<prodcom-host>:24480` |
|
||||||
| `port` | Port this listener serves on (default `24481`) |
|
| `port` | Port this listener serves on (default `24481`) |
|
||||||
| `apiKey` | Only needed if PSK auth is enabled in ProdCom settings |
|
| `apiKey` | Only needed if PSK auth is enabled in ProdCom settings |
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"prodcomUrl": "http://10.3.11.152:24480",
|
"prodcomUrl": "",
|
||||||
"port": 24481,
|
"port": 24481,
|
||||||
"apiKey": ""
|
"apiKey": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,15 @@ try {
|
|||||||
console.warn('config.json not found or invalid, using defaults/env vars');
|
console.warn('config.json not found or invalid, using defaults/env vars');
|
||||||
}
|
}
|
||||||
|
|
||||||
const PRODCOM_URL = process.env.PRODCOM_URL || cfg.prodcomUrl || 'http://10.3.11.152:24480';
|
const PRODCOM_URL = process.env.PRODCOM_URL || cfg.prodcomUrl || '';
|
||||||
const PORT = Number(process.env.PORT || cfg.port || 24481);
|
const PORT = Number(process.env.PORT || cfg.port || 24481);
|
||||||
const API_KEY = process.env.PRODCOM_API_KEY || cfg.apiKey || '';
|
const API_KEY = process.env.PRODCOM_API_KEY || cfg.apiKey || '';
|
||||||
|
|
||||||
|
if (!PRODCOM_URL) {
|
||||||
|
console.error('No ProdCom API configured. Set prodcomUrl in config.json (or the PRODCOM_URL');
|
||||||
|
console.error('environment variable) to this site\'s ProdCom, e.g. http://<prodcom-host>:24480');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
const target = new URL(PRODCOM_URL);
|
const target = new URL(PRODCOM_URL);
|
||||||
const PUBLIC_DIR = path.join(__dirname, 'public');
|
const PUBLIC_DIR = path.join(__dirname, 'public');
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
* proddash.json in the data directory (this machine's overrides), or env vars:
|
* proddash.json in the data directory (this machine's overrides), or env vars:
|
||||||
* PORT (default: value in proddash.json, else 24500)
|
* PORT (default: value in proddash.json, else 24500)
|
||||||
* PRODDASH_PASSCODE (overrides adminPasscode in proddash.json)
|
* PRODDASH_PASSCODE (overrides adminPasscode in proddash.json)
|
||||||
|
* PRODDASH_FORGE (overrides `forge`: "github", or a Gitea/Forgejo base
|
||||||
|
* URL such as http://10.3.8.90:3000)
|
||||||
* PRODDASH_DATA_DIR (where module config + layouts are kept; see below)
|
* PRODDASH_DATA_DIR (where module config + layouts are kept; see below)
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -765,14 +767,52 @@ function parseRepoSetting(value, fallbackBranch) {
|
|||||||
|
|
||||||
const REPO = parseRepoSetting(process.env.PRODDASH_REPO || shellConfig.repo, shellConfig.branch)
|
const REPO = parseRepoSetting(process.env.PRODDASH_REPO || shellConfig.repo, shellConfig.branch)
|
||||||
|| parseRepoSetting('michaelmcgary2008/ProdDash', 'main');
|
|| parseRepoSetting('michaelmcgary2008/ProdDash', 'main');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which server the code comes from. GitHub unless `forge` names another —
|
||||||
|
* give it a Gitea (or Forgejo) base URL, "http://10.3.8.90:3000", and updates
|
||||||
|
* and module installs come from there instead. The two hosts differ only in
|
||||||
|
* how an archive URL is spelled: the tarball itself is the same, pax header
|
||||||
|
* and commit sha included, so everything downstream is untouched.
|
||||||
|
*/
|
||||||
|
function parseForge(value) {
|
||||||
|
const s = String(value || '').trim().replace(/\/+$/, '');
|
||||||
|
if (!s || s.toLowerCase() === 'github') {
|
||||||
|
return { kind: 'github', base: 'https://github.com', archiveBase: 'https://codeload.github.com' };
|
||||||
|
}
|
||||||
|
if (!/^https?:\/\//i.test(s)) return null;
|
||||||
|
// A GitHub URL still means codeload for archives.
|
||||||
|
if (/^https?:\/\/(www\.)?github\.com$/i.test(s)) {
|
||||||
|
return { kind: 'github', base: s, archiveBase: 'https://codeload.github.com' };
|
||||||
|
}
|
||||||
|
return { kind: 'gitea', base: s, archiveBase: s };
|
||||||
|
}
|
||||||
|
|
||||||
|
const FORGE = parseForge(process.env.PRODDASH_FORGE || shellConfig.forge) || parseForge('github');
|
||||||
/** Where branch archives come from; tests point this at a local mock. */
|
/** Where branch archives come from; tests point this at a local mock. */
|
||||||
const ARCHIVE_BASE = String(process.env.PRODDASH_ARCHIVE_BASE || 'https://codeload.github.com').replace(/\/+$/, '');
|
const ARCHIVE_BASE = String(process.env.PRODDASH_ARCHIVE_BASE || FORGE.archiveBase).replace(/\/+$/, '');
|
||||||
const REMOTE_TTL_MS = 10 * 60 * 1000;
|
const REMOTE_TTL_MS = 10 * 60 * 1000;
|
||||||
const REMOTE_CHECK_MS = 6 * 60 * 60 * 1000;
|
const REMOTE_CHECK_MS = 6 * 60 * 60 * 1000;
|
||||||
const MAX_ARCHIVE_BYTES = 80 * 1024 * 1024;
|
const MAX_ARCHIVE_BYTES = 80 * 1024 * 1024;
|
||||||
|
|
||||||
function tarballUrl() {
|
function tarballUrl() {
|
||||||
return `${ARCHIVE_BASE}/${REPO.owner}/${REPO.repo}/tar.gz/${encodeURIComponent(REPO.branch)}`;
|
const branch = encodeURIComponent(REPO.branch);
|
||||||
|
return FORGE.kind === 'gitea'
|
||||||
|
? `${ARCHIVE_BASE}/${REPO.owner}/${REPO.repo}/archive/${branch}.tar.gz`
|
||||||
|
: `${ARCHIVE_BASE}/${REPO.owner}/${REPO.repo}/tar.gz/${branch}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The page a human should read to see what changed between two commits. */
|
||||||
|
function forgeCompareUrl(from, to) {
|
||||||
|
return `${FORGE.base}/${REPO.owner}/${REPO.repo}/compare/${from}...${to}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The commit list for the branch we follow. */
|
||||||
|
function forgeCommitsUrl() {
|
||||||
|
const branch = encodeURIComponent(REPO.branch);
|
||||||
|
return FORGE.kind === 'gitea'
|
||||||
|
? `${FORGE.base}/${REPO.owner}/${REPO.repo}/commits/branch/${branch}`
|
||||||
|
: `${FORGE.base}/${REPO.owner}/${REPO.repo}/commits/${branch}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function httpError(status, message) {
|
function httpError(status, message) {
|
||||||
@@ -929,11 +969,11 @@ function shellStatus() {
|
|||||||
else if (local.branch !== REPO.branch) blocker = `this checkout is on branch "${local.branch}", not "${REPO.branch}"`;
|
else if (local.branch !== REPO.branch) blocker = `this checkout is on branch "${local.branch}", not "${REPO.branch}"`;
|
||||||
else if (local.dirty) blocker = 'this checkout has local changes — commit or discard them first';
|
else if (local.dirty) blocker = 'this checkout has local changes — commit or discard them first';
|
||||||
}
|
}
|
||||||
const gh = `https://github.com/${REPO.owner}/${REPO.repo}`;
|
|
||||||
return {
|
return {
|
||||||
version: SHELL_VERSION,
|
version: SHELL_VERSION,
|
||||||
repo: `${REPO.owner}/${REPO.repo}`,
|
repo: `${REPO.owner}/${REPO.repo}`,
|
||||||
branch: REPO.branch,
|
branch: REPO.branch,
|
||||||
|
forge: FORGE.base,
|
||||||
local: { isGit: local.isGit, sha: local.sha, branch: local.branch, dirty: local.dirty },
|
local: { isGit: local.isGit, sha: local.sha, branch: local.branch, dirty: local.dirty },
|
||||||
remote: { version: remote.version, sha: remote.sha, checkedAt: remote.checkedAt, error: remote.error },
|
remote: { version: remote.version, sha: remote.sha, checkedAt: remote.checkedAt, error: remote.error },
|
||||||
updateAvailable: versionBehind || commitsBehind,
|
updateAvailable: versionBehind || commitsBehind,
|
||||||
@@ -941,8 +981,8 @@ function shellStatus() {
|
|||||||
method: local.isGit ? 'git' : 'archive',
|
method: local.isGit ? 'git' : 'archive',
|
||||||
updateBlocker: blocker,
|
updateBlocker: blocker,
|
||||||
changesUrl: local.sha && remote.sha
|
changesUrl: local.sha && remote.sha
|
||||||
? `${gh}/compare/${local.sha.slice(0, 12)}...${remote.sha.slice(0, 12)}`
|
? forgeCompareUrl(local.sha.slice(0, 12), remote.sha.slice(0, 12))
|
||||||
: `${gh}/commits/${REPO.branch}`,
|
: forgeCommitsUrl(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1693,7 +1733,7 @@ server.on('listening', () => {
|
|||||||
? ' Modules : ' + ids.map((id) => id + (isEnabled(id) ? '' : ' (disabled)') + (manifests.get(id).incompatible ? ' (needs newer ProdDash)' : '')).join(', ')
|
? ' Modules : ' + ids.map((id) => id + (isEnabled(id) ? '' : ' (disabled)') + (manifests.get(id).incompatible ? ' (needs newer ProdDash)' : '')).join(', ')
|
||||||
: ' Modules : none installed');
|
: ' Modules : none installed');
|
||||||
console.log(' Settings : ' + DATA_DIR + (DATA_DIR === REPO_CONFIG_DIR ? ' (inside the app folder)' : ''));
|
console.log(' Settings : ' + DATA_DIR + (DATA_DIR === REPO_CONFIG_DIR ? ' (inside the app folder)' : ''));
|
||||||
console.log(' Repo : ' + REPO.owner + '/' + REPO.repo + '@' + REPO.branch);
|
console.log(' Repo : ' + REPO.owner + '/' + REPO.repo + '@' + REPO.branch + ' (' + FORGE.base + ')');
|
||||||
});
|
});
|
||||||
|
|
||||||
// A restart hands the port over from the exiting ProdDash: wait for it.
|
// A restart hands the port over from the exiting ProdDash: wait for it.
|
||||||
|
|||||||
Reference in New Issue
Block a user