UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

423 lines (422 loc) 15.6 kB
/* eslint-disable @typescript-eslint/no-require-imports */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl> * * 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. */ 'use strict'; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.load = load; exports.readDefaultsFile = readDefaultsFile; exports.sendBaseDeltas = sendBaseDeltas; exports.writeDefaultsFile = writeDefaultsFile; exports.writeBaseDeltasFileSync = writeBaseDeltasFileSync; exports.writeBaseDeltasFile = writeBaseDeltasFile; exports.writeSettingsFile = writeSettingsFile; const fs_1 = __importDefault(require("fs")); const node_os_1 = __importDefault(require("node:os")); const lodash_1 = __importDefault(require("lodash")); const path_1 = __importDefault(require("path")); const semver_1 = __importDefault(require("semver")); const uuid_1 = require("uuid"); const debug_1 = require("../debug"); const deltaeditor_1 = __importDefault(require("../deltaeditor")); const ports_1 = require("../ports"); const debug = (0, debug_1.createDebug)('signalk-server:config'); let disableWriteSettings = false; // use dynamic path so that ts compiler does not detect this // json file, as ts compile needs to copy all (other) used // json files under /dist // tslint:disable-next-line const packageJson = require('../../' + 'package.json'); function load(app) { app.argv = require('minimist')(process.argv.slice(2)); const config = (app.config = app.config || {}); const env = (app.env = process.env); config.getExternalHostname = getExternalHostname.bind(config, config); config.getExternalPort = ports_1.getExternalPort.bind(config, app); config.appPath = config.appPath || path_1.default.normalize(__dirname + '/../../'); debug('appPath:' + config.appPath); try { config.name = packageJson.name; config.author = packageJson.author; config.contributors = packageJson.contributors; config.version = packageJson.version; config.description = packageJson.description; //if dependencies are installed from tarballs like in //master docker build the version will be like //file:signalk-server-admin-ui-1.44.1.tgz if (!process.env.SKIP_ADMINUI_VERSION_CHECK) { checkPackageVersion('@signalk/server-admin-ui', packageJson, app.config.appPath); } } catch (err) { console.error('error parsing package.json', err); process.exit(1); } setConfigDirectory(app); app.config.baseDeltaEditor = new deltaeditor_1.default(); if (lodash_1.default.isObject(app.config.settings)) { debug('Using settings from constructor call, not reading defaults'); disableWriteSettings = true; if (config.defaults) { convertOldDefaultsToDeltas(app.config.baseDeltaEditor, config.defaults); } } else { readSettingsFile(app); if (!setBaseDeltas(app)) { const defaults = getFullDefaults(app); if (defaults) { convertOldDefaultsToDeltas(app.config.baseDeltaEditor, defaults); if (typeof app.config.settings.useBaseDeltas === 'undefined' || app.config.settings.useBaseDeltas) { writeBaseDeltasFileSync(app); } else { app.config.hasOldDefaults = true; } } } } setSelfSettings(app); if (app.argv['sample-nmea0183-data']) { const sample = path_1.default.join(app.config.appPath, 'samples/plaka.log'); console.log(`Using sample data from ${sample}`); app.config.settings.pipedProviders.push({ id: 'nmea0183-sample-data', pipeElements: [ { type: 'providers/simple', options: { logging: false, type: 'FileStream', subOptions: { dataType: 'NMEA0183', filename: sample } } } ], enabled: true }); } if (app.argv['sample-n2k-data']) { const sample = path_1.default.join(app.config.appPath, 'samples/aava-n2k.data'); console.log(`Using sample data from ${sample}`); app.config.settings.pipedProviders.push({ id: 'n2k-sample-data', pipeElements: [ { type: 'providers/simple', options: { logging: false, type: 'FileStream', subOptions: { dataType: 'NMEA2000JS', filename: sample } } } ], enabled: true }); } if (app.argv['override-timestamps']) { app.config.overrideTimestampWithNow = true; } if (app.argv.securityenabled && !app.config.security) { app.config.settings.security = { strategy: './tokensecurity' }; } if (env.SSLPORT) { config.settings.ssl = true; } if (!lodash_1.default.isUndefined(app.env.WSCOMPRESSION)) { config.settings.wsCompression = app.env.WSCOMPRESSION.toLowerCase() === 'true'; } if (config.settings.landingPage && config.settings.landingPage.charAt(0) !== '/') { console.error(`invalid rootUri: ${config.settings.landingPage}`); process.exit(1); } require('./development')(app); require('./production')(app); } function checkPackageVersion(name, pkg, appPath) { const expected = pkg.dependencies[name]; let modulePackageJsonPath = path_1.default.join(appPath, 'node_modules', name, 'package.json'); if (!fs_1.default.existsSync(modulePackageJsonPath)) { modulePackageJsonPath = path_1.default.join(appPath, '..', name, 'package.json'); } const installed = require(modulePackageJsonPath); if (!semver_1.default.satisfies(installed.version, expected)) { console.error(`invalid version of the ${name} package is installed ${installed.version} !== ${expected}`); process.exit(1); } } // Establish what the config directory path is. function getConfigDirectory({ argv, config, env }) { // Possible paths in order of priority. const configPaths = [ env.SIGNALK_NODE_CONDFIG_DIR, env.SIGNALK_NODE_CONFIG_DIR, config.configPath, argv.c, argv.s && config.appPath, env.HOME && path_1.default.join(env.HOME, '.signalk'), config.appPath ]; // Find first config directory path that has a truthy value. const configPath = path_1.default.resolve(lodash_1.default.find(configPaths)); debug('configDirPath: ' + configPath); return configPath; } // Create directories and set app.config.configPath. function setConfigDirectory(app) { app.config.configPath = getConfigDirectory(app); if (app.config.configPath !== app.config.appPath) { if (!fs_1.default.existsSync(app.config.configPath)) { fs_1.default.mkdirSync(app.config.configPath); debug(`configDir Created: ${app.config.configPath}`); } const configPackage = path_1.default.join(app.config.configPath, 'package.json'); if (!fs_1.default.existsSync(configPackage)) { fs_1.default.writeFileSync(configPackage, JSON.stringify(pluginsPackageJsonTemplate, null, 2)); } const npmrcPath = path_1.default.join(app.config.configPath, '.npmrc'); if (!fs_1.default.existsSync(npmrcPath)) { fs_1.default.writeFileSync(npmrcPath, 'package-lock=false\n'); } else { const contents = fs_1.default.readFileSync(npmrcPath); if (contents.indexOf('package-lock=') === -1) { fs_1.default.appendFileSync(npmrcPath, '\npackage-lock=false\n'); } } } } function getDefaultsPath(app) { const defaultsFile = app.config.configPath !== app.config.appPath ? 'defaults.json' : 'settings/defaults.json'; return path_1.default.join(app.config.configPath, defaultsFile); } function getBaseDeltasPath(app) { const defaultsFile = app.config.configPath !== app.config.appPath ? 'baseDeltas.json' : 'settings/baseDeltas.json'; return path_1.default.join(app.config.configPath, defaultsFile); } function readDefaultsFile(app) { const defaultsPath = getDefaultsPath(app); const data = fs_1.default.readFileSync(defaultsPath); return JSON.parse(data.toString()); } function getFullDefaults(app) { const defaultsPath = getDefaultsPath(app); try { const defaults = readDefaultsFile(app); debug(`Found defaults at ${defaultsPath.toString()}`); return defaults; } catch (e) { if (e?.code === 'ENOENT') { return undefined; } else { console.error(`unable to parse ${defaultsPath.toString()}`); console.error(e); process.exit(1); } } return undefined; } function setBaseDeltas(app) { const defaultsPath = getBaseDeltasPath(app); try { app.config.baseDeltaEditor.load(defaultsPath); debug(`Found default deltas at ${defaultsPath.toString()}`); } catch (e) { if (e?.code === 'ENOENT') { debug(`No default deltas found at ${defaultsPath.toString()}`); return; } else { console.log(e); } } return true; } function sendBaseDeltas(app) { const copy = JSON.parse(JSON.stringify(app.config.baseDeltaEditor.deltas)); copy.forEach((delta) => { app.handleMessage('defaults', delta); }); } function writeDefaultsFile(app, defaults, cb) { fs_1.default.writeFile(getDefaultsPath(app), JSON.stringify(defaults, null, 2), cb); } function writeBaseDeltasFileSync(app) { app.config.baseDeltaEditor.saveSync(getBaseDeltasPath(app)); } function writeBaseDeltasFile(app) { return app.config.baseDeltaEditor.save(getBaseDeltasPath(app)); } function setSelfSettings(app) { const name = app.config.baseDeltaEditor.getSelfValue('name'); const mmsi = app.config.baseDeltaEditor.getSelfValue('mmsi'); let uuid = app.config.baseDeltaEditor.getSelfValue('uuid'); if (mmsi && !lodash_1.default.isString(mmsi)) { throw new Error(`invalid mmsi: ${mmsi}`); } if (uuid && !lodash_1.default.isString(uuid)) { throw new Error(`invalid uuid: ${uuid}`); } if (mmsi === null && uuid === null) { uuid = 'urn:mrn:signalk:uuid:' + (0, uuid_1.v4)(); app.config.baseDeltaEditor.setSelfValue('uuid', uuid); } app.config.vesselName = name; if (mmsi) { app.selfType = 'mmsi'; app.selfId = 'urn:mrn:imo:mmsi:' + mmsi; app.config.vesselMMSI = mmsi; } else if (uuid) { app.selfType = 'uuid'; app.selfId = uuid; app.config.vesselUUID = uuid; } if (app.selfType) { debug(app.selfType.toUpperCase() + ': ' + app.selfId); } app.selfContext = 'vessels.' + app.selfId; } function readSettingsFile(app) { const settings = getSettingsFilename(app); if (!app.argv.s && !fs_1.default.existsSync(settings)) { console.log('Settings file does not exist, using empty settings'); app.config.settings = { pipedProviders: [] }; } else { debug('Using settings file: ' + settings); try { app.config.settings = require(settings); } catch (_e) { console.error(`Error reading settings file ${settings}, using empty settings`); app.config.settings = { pipedProviders: [] }; } } if (lodash_1.default.isUndefined(app.config.settings.pipedProviders)) { app.config.settings.pipedProviders = []; } if (lodash_1.default.isUndefined(app.config.settings.interfaces)) { app.config.settings.interfaces = {}; } } function writeSettingsFile(app, settings, cb) { if (!disableWriteSettings) { const settingsPath = getSettingsFilename(app); fs_1.default.writeFile(settingsPath, JSON.stringify(settings, null, 2), cb); } else { cb(); } } function getSettingsFilename(app) { if (process.env.SIGNALK_NODE_SETTINGS) { debug('Settings filename was set in environment SIGNALK_NODE_SETTINGS, overriding all other options'); return path_1.default.resolve(process.env.SIGNALK_NODE_SETTINGS); } const settingsFile = app.argv.s || 'settings.json'; return path_1.default.join(app.config.configPath, settingsFile); } function getExternalHostname(config) { if (process.env.EXTERNALHOST) { return process.env.EXTERNALHOST; } if (config.settings.proxy_host) { return config.settings.proxy_host; } else if (config.settings.hostname) { return config.settings.hostname; } try { return node_os_1.default.hostname(); } catch (_ex) { return 'hostname_not_available'; } } function scanDefaults(deltaEditor, vpath, item) { lodash_1.default.keys(item).forEach((key) => { const value = item[key]; if (key === 'meta') { deltaEditor.setMeta('vessels.self', vpath, value); } else if (key === 'value') { deltaEditor.setSelfValue(vpath, value); } else if (lodash_1.default.isObject(value)) { const childPath = vpath.length > 0 ? `${vpath}.${key}` : key; scanDefaults(deltaEditor, childPath, value); } }); } function convertOldDefaultsToDeltas(deltaEditor, defaults) { const deltas = []; const self = lodash_1.default.get(defaults, 'vessels.self'); if (self) { lodash_1.default.keys(self).forEach((key) => { const value = self[key]; if (!lodash_1.default.isString(value)) { scanDefaults(deltaEditor, key, value); } else { deltaEditor.setSelfValue(key, value); } }); if (self.communication) { deltaEditor.setSelfValue('communication', self.communication); } } return deltas; } const pluginsPackageJsonTemplate = { name: 'signalk-server-config', version: '0.0.1', description: 'This file is here to track your plugin and webapp installs.', repository: {}, license: 'Apache-2.0' }; module.exports = { load, getConfigDirectory, writeSettingsFile, writeDefaultsFile, readDefaultsFile, sendBaseDeltas, writeBaseDeltasFile, package: packageJson }; //# sourceMappingURL=config.js.map