Unified endpoint config type: one Host/IP : Port control in admin
New configSchema type 'endpoint' declared in the module spec: anything
that points at a device or service on the network must be one endpoint
field, rendered identically for every module in /admin as a joined
Host/IP : Port control and stored/handed to the module server as
{ host, port }. Migrates all three connection modules — ProdCom (was a
full URL field), ProPresenter and Timers (were separate host + port
fields). Legacy stored configs (prodcomUrl / flat host+port) keep
working until the admin form is re-saved, at which point the new shape
replaces them. MODULE-GUIDE documents the type and the presentation
rule: no split host/port fields, and full URLs only for genuine web
addresses.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNYUv9EyrGSFmCnmH7WYKH
This commit is contained in:
@@ -9,10 +9,10 @@
|
||||
"minSize": { "w": 3, "h": 2 },
|
||||
"defaultSize": { "w": 4, "h": 4 },
|
||||
"configSchema": {
|
||||
"prodcomUrl": {
|
||||
"type": "string",
|
||||
"label": "ProdCom URL",
|
||||
"default": "http://10.3.11.152:24480"
|
||||
"prodcom": {
|
||||
"type": "endpoint",
|
||||
"label": "ProdCom server",
|
||||
"default": { "host": "10.3.11.152", "port": 24480 }
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "password",
|
||||
|
||||
@@ -71,11 +71,22 @@ function proxy(req, res, target, apiKey) {
|
||||
req.pipe(preq);
|
||||
}
|
||||
|
||||
/** The ProdCom base URL from the endpoint config. A legacy prodcomUrl (saved
|
||||
before the endpoint field existed) can only still be present until the
|
||||
admin form is re-saved, so when it exists it wins over the endpoint's
|
||||
schema default. */
|
||||
function resolveTarget(config) {
|
||||
if (config.prodcomUrl) return new URL(config.prodcomUrl);
|
||||
const ep = config.prodcom;
|
||||
if (ep && ep.host && Number(ep.port)) return new URL(`http://${ep.host}:${Number(ep.port)}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init({ config }) {
|
||||
health = config.prodcomUrl
|
||||
health = resolveTarget(config)
|
||||
? { status: 'connecting', message: 'No traffic yet' }
|
||||
: { status: 'error', message: 'No ProdCom URL configured' };
|
||||
: { status: 'error', message: 'No ProdCom server configured' };
|
||||
return {
|
||||
stop() { /* nothing persistent to tear down — streams die with their sockets */ },
|
||||
health: () => health,
|
||||
@@ -83,8 +94,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
routes({ config, log }) {
|
||||
// An invalid URL throws here, which surfaces as a mount error in /admin.
|
||||
const target = new URL(config.prodcomUrl || 'http://10.3.11.152:24480');
|
||||
// An invalid host/URL throws here, which surfaces as a mount error in /admin.
|
||||
const target = resolveTarget(config) || new URL('http://10.3.11.152:24480');
|
||||
log(`proxying /prodcom/* → ${target.origin}`);
|
||||
return {
|
||||
'* /prodcom/*': (req, res) => proxy(req, res, target, config.apiKey || ''),
|
||||
|
||||
@@ -9,15 +9,10 @@
|
||||
"minSize": { "w": 3, "h": 3 },
|
||||
"defaultSize": { "w": 4, "h": 5 },
|
||||
"configSchema": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"label": "ProPresenter host / IP",
|
||||
"default": ""
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"label": "ProPresenter network port",
|
||||
"default": 1025
|
||||
"propresenter": {
|
||||
"type": "endpoint",
|
||||
"label": "ProPresenter",
|
||||
"default": { "host": "", "port": 1025 }
|
||||
},
|
||||
"password": {
|
||||
"type": "password",
|
||||
|
||||
@@ -23,7 +23,13 @@ let current = null;
|
||||
module.exports = {
|
||||
init({ config, log }) {
|
||||
const streams = new Set();
|
||||
const enabled = Boolean(config.host && Number(config.port));
|
||||
// Endpoint config. Legacy flat host/port keys (saved before the endpoint
|
||||
// field existed) can only still be present until the admin form is
|
||||
// re-saved, so when they exist they win over the endpoint's default.
|
||||
const ep = config.propresenter && typeof config.propresenter === 'object' ? config.propresenter : {};
|
||||
const host = String(config.host ?? ep.host ?? '');
|
||||
const port = Number(config.port ?? ep.port) || 1025;
|
||||
const enabled = Boolean(host && port);
|
||||
let latest = clearedViewModel();
|
||||
let lastFrame = '';
|
||||
|
||||
@@ -54,14 +60,14 @@ module.exports = {
|
||||
|
||||
client.configure({
|
||||
enabled,
|
||||
host: String(config.host || ''),
|
||||
port: Number(config.port) || 1025,
|
||||
host,
|
||||
port,
|
||||
password: String(config.password || ''),
|
||||
});
|
||||
|
||||
if (enabled) {
|
||||
client.start();
|
||||
log(`following ProPresenter at ${config.host}:${config.port}`);
|
||||
log(`following ProPresenter at ${host}:${port}`);
|
||||
} else {
|
||||
log('no ProPresenter host configured — set one in /admin');
|
||||
}
|
||||
@@ -91,7 +97,7 @@ module.exports = {
|
||||
},
|
||||
health() {
|
||||
if (!enabled) return { status: 'error', message: 'No ProPresenter host configured' };
|
||||
if (latest.reachable) return { status: 'ok', message: `Following ${config.host}:${config.port}` };
|
||||
if (latest.reachable) return { status: 'ok', message: `Following ${host}:${port}` };
|
||||
return {
|
||||
status: 'error',
|
||||
message: latest.lastError
|
||||
|
||||
@@ -9,15 +9,10 @@
|
||||
"minSize": { "w": 2, "h": 1 },
|
||||
"defaultSize": { "w": 4, "h": 2 },
|
||||
"configSchema": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"label": "ProPresenter host / IP",
|
||||
"default": ""
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"label": "ProPresenter network port",
|
||||
"default": 1025
|
||||
"propresenter": {
|
||||
"type": "endpoint",
|
||||
"label": "ProPresenter",
|
||||
"default": { "host": "", "port": 1025 }
|
||||
},
|
||||
"password": {
|
||||
"type": "password",
|
||||
|
||||
@@ -65,8 +65,12 @@ function clearedState(enabled) {
|
||||
module.exports = {
|
||||
init({ config, log }) {
|
||||
const streams = new Set();
|
||||
const host = String(config.host || '');
|
||||
const port = Number(config.port) || 1025;
|
||||
// Endpoint config. Legacy flat host/port keys (saved before the endpoint
|
||||
// field existed) can only still be present until the admin form is
|
||||
// re-saved, so when they exist they win over the endpoint's default.
|
||||
const ep = config.propresenter && typeof config.propresenter === 'object' ? config.propresenter : {};
|
||||
const host = String(config.host ?? ep.host ?? '');
|
||||
const port = Number(config.port ?? ep.port) || 1025;
|
||||
const password = String(config.password || '');
|
||||
const enabled = Boolean(host && port);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user