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:
Claude
2026-08-31 19:33:08 +00:00
parent 6412dd9524
commit a06bc921ce
11 changed files with 116 additions and 39 deletions
+4 -4
View File
@@ -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",
+15 -4
View File
@@ -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 || ''),