Files
dashboardAPI/src/nodeClass.js

87 lines
2.9 KiB
JavaScript
Raw Normal View History

'use strict';
// dashboardAPI nodeClass — passive HTTP-emitter adapter.
//
// Does NOT extend BaseNodeAdapter: dashboardAPI has no generalFunctions
// config JSON, no Port-0/1 telemetry stream, no parent registration, no
// tick or status loop. It just listens for `child.register` and emits one
// Grafana upsert HTTP request per dashboard. See OPEN_QUESTIONS.md
// (2026-05-10) for the rationale.
const { configManager, createRegistry } = require('generalFunctions');
const DashboardApi = require('./specificClass');
const commands = require('./commands');
2026-01-13 14:29:43 +01:00
class nodeClass {
constructor(uiConfig, RED, nodeInstance, nameOfNode) {
this.node = nodeInstance;
this.RED = RED;
this.name = nameOfNode;
this.config = this._buildConfig(uiConfig);
this.source = new DashboardApi(this.config);
this.node.source = this.source;
this._commands = createRegistry(commands, { logger: this.source?.logger });
2026-01-13 14:29:43 +01:00
this._attachInputHandler();
this._attachCloseHandler();
}
_buildConfig(uiConfig) {
const cfgMgr = new configManager();
// Credentials block (Node-RED encrypts at rest in flow_cred.json). Legacy
// installs may still carry bearerToken on uiConfig — fall back with a
// one-time deprecation warning so the user knows to re-save.
const credentialToken = this.node?.credentials?.bearerToken || '';
const legacyToken = uiConfig.bearerToken || '';
if (!credentialToken && legacyToken) {
this.RED?.log?.warn?.(
`[${this.name}] bearer token loaded from legacy plain config field. ` +
`Re-open this node in the editor and click Done to migrate to encrypted credentials.`
);
}
const bearerToken = credentialToken || legacyToken;
return cfgMgr.buildConfig(this.name, uiConfig, this.node.id, {
functionality: {
softwareType: this.name.toLowerCase(),
role: 'auto ui generator',
2026-01-13 14:29:43 +01:00
},
grafanaConnector: {
protocol: uiConfig.protocol || 'http',
host: uiConfig.host || 'localhost',
port: Number(uiConfig.port || 3000),
bearerToken,
folderUid: uiConfig.folderUid || '',
2026-01-13 14:29:43 +01:00
},
defaultBucket: uiConfig.defaultBucket || process.env.INFLUXDB_BUCKET || '',
});
2026-01-13 14:29:43 +01:00
}
_attachInputHandler() {
this.node.on('input', async (msg, send, done) => {
try {
await this._commands.dispatch(msg, this.source, {
node: this.node,
RED: this.RED,
send,
logger: this.source?.logger,
});
if (typeof done === 'function') done();
2026-01-13 14:29:43 +01:00
} catch (error) {
this.node.status({ fill: 'red', shape: 'ring', text: 'dashboardapi error' });
this.node.error(error?.message || error, msg);
if (typeof done === 'function') done(error);
2026-01-13 14:29:43 +01:00
}
});
}
_attachCloseHandler() {
2026-02-23 13:16:58 +01:00
this.node.on('close', (done) => {
if (typeof done === 'function') done();
2026-02-23 13:16:58 +01:00
});
2026-01-13 14:29:43 +01:00
}
}
module.exports = nodeClass;