signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
234 lines (233 loc) • 9.19 kB
JavaScript
;
/*
* Copyright 2017 Scott Bender <scott@scottbender.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const config_1 = require("../config/config");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { runDiscovery } = require('../discovery');
const constants_1 = require("../constants");
// 21-bit unique number per NMEA 2000 standard (0x1FFFFF)
const MAX_CANBUS_UNIQUE_NUMBER = 0x1fffff;
function getProviders(source, wasDiscovered) {
return source.map((provider) => {
const type = provider.pipeElements[0].type;
let providerRes;
if (type === 'providers/simple' && provider.pipeElements.length === 1) {
const options = provider.pipeElements[0].options;
providerRes = {
...JSON.parse(JSON.stringify(options)),
id: provider.id,
enabled: provider.enabled,
options: options.subOptions,
editable: true
};
delete providerRes.subOptions;
}
else {
providerRes = {
id: provider.id,
enabled: provider.enabled,
json: JSON.stringify(provider, null, 2),
type: `${type}`,
editable: false
};
}
if (wasDiscovered) {
providerRes.isNew = true;
providerRes.wasDiscovered = true;
}
return providerRes;
});
}
function applyProviderSettings(target, source, res) {
if (source.type === 'Unknown') {
res.status(401).send('Can not update an Unknown type');
return false;
}
const options = target.pipeElements[0].options;
target.id = source.id;
target.enabled = source.enabled;
options.logging = source.logging;
options.type = source.type;
if (!options.subOptions || options.subOptions.type !== source.options.type) {
options.subOptions = {};
}
Object.assign(options.subOptions, source.options);
// Keys that mean "the client owns the entire value, including emptiness".
// Object.assign cannot delete keys the client omitted, so a delete-all
// edit on a field like talkerGroups would keep the previous saved value.
// Replace these explicitly: if the client sent the key, write it; if not,
// drop it from the saved options.
const REPLACE_KEYS = ['talkerGroups'];
for (const key of REPLACE_KEYS) {
if (key in source.options) {
;
options.subOptions[key] = source.options[key];
}
else {
delete options.subOptions[key];
}
}
return true;
}
function isValidProviderBody(body) {
if (!body || typeof body !== 'object' || Array.isArray(body))
return false;
const b = body;
if (b.id !== undefined && typeof b.id !== 'string')
return false;
if (b.options !== undefined &&
(typeof b.options !== 'object' || Array.isArray(b.options)))
return false;
return true;
}
module.exports = function (app) {
app.on('discovered', (provider) => {
const p = provider;
if (p.enabled === undefined) {
p.enabled = true;
}
// mDNS browsers fire 'update' repeatedly for the same service, so
// the list would otherwise accumulate dozens of rows per remote.
if (app.discoveredProviders.some((existing) => existing.id === p.id)) {
return;
}
app.discoveredProviders.push(p);
app.emit('serverevent', {
type: 'DISCOVERY_CHANGED',
from: 'discovery',
data: getProviders(app.discoveredProviders, true)
});
});
app.get(`${constants_1.SERVERROUTESPREFIX}/providers`, (_req, res) => {
res.json(getProviders(app.config.settings.pipedProviders));
});
app.put(`${constants_1.SERVERROUTESPREFIX}/runDiscovery`, (_req, res) => {
app.discoveredProviders = [];
runDiscovery(app);
res.json('Discovery started');
});
app.put(`${constants_1.SERVERROUTESPREFIX}/providers/:id`, (req, res) => {
if (!isValidProviderBody(req.body)) {
res.status(400).send('Invalid provider: id and options object required');
return;
}
updateProvider(req.params.id, req.body, res);
});
app.post(`${constants_1.SERVERROUTESPREFIX}/providers`, (req, res) => {
if (!isValidProviderBody(req.body)) {
res.status(400).send('Invalid provider: id and options object required');
return;
}
updateProvider(null, req.body, res);
});
app.delete(`${constants_1.SERVERROUTESPREFIX}/providers/:id`, (req, res) => {
const idx = app.config.settings.pipedProviders.findIndex((p) => p.id === req.params.id);
if (idx === -1) {
res.status(401).send(`Connection with name ${req.params.id} not found`);
return;
}
app.config.settings.pipedProviders.splice(idx, 1);
(0, config_1.writeSettingsFile)(app, app.config.settings, (err) => {
if (err) {
console.error(err);
res.status(500).send('Unable to save to settings file');
}
else {
app.pipedProviders.stopProvider(req.params.id);
res.type('text/plain');
res.send('Connection deleted');
}
});
});
function updateProvider(idToUpdate, provider, res) {
const isNew = idToUpdate === undefined || idToUpdate === null;
const existing = app.config.settings.pipedProviders.find((p) => p.id === (isNew ? provider.id : idToUpdate));
if (isNew && existing) {
res.status(401).send(`Connection with ID '${provider.id}' already exists`);
return;
}
else if (!isNew && idToUpdate !== provider.id) {
if (app.config.settings.pipedProviders.find((p) => p.id === provider.id)) {
res
.status(401)
.send(`Connection with ID '${provider.id}' already exists`);
return;
}
}
if (!provider.id || provider.id.length === 0) {
res.status(401).send('Please enter a provider ID');
return;
}
if (provider.wasDiscovered) {
const idx = app.discoveredProviders.findIndex((p) => p.id === provider.originalId);
if (idx !== -1) {
app.discoveredProviders.splice(idx, 1);
}
app.emit('serverevent', {
type: 'DISCOVERY_CHANGED',
from: 'discovery',
data: getProviders(app.discoveredProviders)
});
}
const updatedProvider = existing || {
id: '',
enabled: true,
pipeElements: [
{
type: 'providers/simple',
options: {}
}
]
};
if (provider.options.type === 'canbus-canboatjs') {
const uniqueNumber = parseInt(String(provider.options.uniqueNumber), 10);
if (!isNaN(uniqueNumber)) {
provider.options.uniqueNumber = uniqueNumber;
}
else {
provider.options.uniqueNumber = Math.floor(Math.random() * MAX_CANBUS_UNIQUE_NUMBER);
}
const mfgCode = parseInt(String(provider.options.mfgCode), 10);
if (!isNaN(mfgCode)) {
provider.options.mfgCode = mfgCode;
}
else {
if (provider.options.mfgCode !== '')
delete provider.options.mfgCode;
}
}
if (applyProviderSettings(updatedProvider, provider, res)) {
if (isNew) {
app.config.settings.pipedProviders.push(updatedProvider);
}
(0, config_1.writeSettingsFile)(app, app.config.settings, (err) => {
if (err) {
console.error(err);
res.status(500).send('Unable to save to settings file');
}
else {
if (!isNew && idToUpdate !== provider.id) {
app.pipedProviders.stopProvider(idToUpdate);
}
app.pipedProviders.restartProvider(provider.id);
res.type('text/plain');
res.send('Connection ' + (isNew ? 'added' : 'updated'));
}
});
}
}
};
//# sourceMappingURL=providers.js.map