Files
cgm-remote-monitor/env.js
T

253 lines
11 KiB
JavaScript

'use strict';
var env = { };
var _ = require('lodash');
var crypto = require('crypto');
var consts = require('./lib/constants');
var fs = require('fs');
// Module to constrain all config and environment parsing to one spot.
function config ( ) {
/*
* First inspect a bunch of environment variables:
* * PORT - serve http on this port
* * MONGO_CONNECTION, CUSTOMCONNSTR_mongo - mongodb://... uri
* * CUSTOMCONNSTR_mongo_collection - name of mongo collection with `sgv` documents
* * API_SECRET - if defined, this passphrase is fed to a sha1 hash digest, the hex output is used to create a single-use token for API authorization
* * NIGHTSCOUT_STATIC_FILES - the `base directory` to use for serving
* static files over http. Default value is the included `static`
* directory.
*/
var software = require('./package.json');
var git = require('git-rev');
if (readENV('APPSETTING_ScmType') == readENV('ScmType') && readENV('ScmType') == 'GitHub') {
env.head = require('./scm-commit-id.json');
console.log('SCM COMMIT ID', env.head);
} else {
git.short(function record_git_head (head) {
console.log('GIT HEAD', head);
env.head = head || readENV('SCM_COMMIT_ID') || readENV('COMMIT_HASH', '');
});
}
env.version = software.version;
env.name = software.name;
env.DISPLAY_UNITS = readENV('DISPLAY_UNITS', 'mg/dl');
env.PORT = readENV('PORT', 1337);
env.mongo = readENV('MONGO_CONNECTION') || readENV('MONGO') || readENV('MONGOLAB_URI');
env.mongo_collection = readENV('MONGO_COLLECTION', 'entries');
env.MQTT_MONITOR = readENV('MQTT_MONITOR', null);
if (env.MQTT_MONITOR) {
var hostDbCollection = [env.mongo.split('mongodb://').pop().split('@').pop( ), env.mongo_collection].join('/');
var mongoHash = crypto.createHash('sha1');
mongoHash.update(hostDbCollection);
//some MQTT servers only allow the client id to be 23 chars
env.mqtt_client_id = mongoHash.digest('base64').substring(0, 23);
console.info('Using Mongo host/db/collection to create the default MQTT client_id', hostDbCollection);
if (env.MQTT_MONITOR.indexOf('?clientId=') == -1) {
console.info('Set MQTT client_id to: ', env.mqtt_client_id);
} else {
console.info('MQTT configured to use a custom client id, it will override the default: ', env.mqtt_client_id);
}
}
env.treatments_collection = readENV('MONGO_TREATMENTS_COLLECTION', 'treatments');
env.profile_collection = readENV('MONGO_PROFILE_COLLECTION', 'profile');
env.devicestatus_collection = readENV('MONGO_DEVICESTATUS_COLLECTION', 'devicestatus');
env.enable = readENV('ENABLE', '');
env.defaults = { // currently supported keys must defined be here
'units': 'mg/dL'
, 'timeFormat': '12'
, 'nightMode': false
, 'showRawbg': 'never'
, 'customTitle': 'Nightscout'
, 'theme': 'default'
, 'alarmUrgentHigh': true
, 'alarmHigh': true
, 'alarmLow': true
, 'alarmUrgentLow': true
, 'alarmTimeAgoWarn': true
, 'alarmTimeAgoWarnMins': 15
, 'alarmTimeAgoUrgent': true
, 'alarmTimeAgoUrgentMins': 30
, 'language': 'en' // not used yet
} ;
// add units from separate variable
env.defaults.units = env.DISPLAY_UNITS;
// Highest priority per line defaults
env.defaults.timeFormat = readENV('TIME_FORMAT', env.defaults.timeFormat);
env.defaults.nightMode = readENV('NIGHT_MODE', env.defaults.nightMode);
env.defaults.showRawbg = readENV('SHOW_RAWBG', env.defaults.showRawbg);
env.defaults.customTitle = readENV('CUSTOM_TITLE', env.defaults.customTitle);
env.defaults.theme = readENV('THEME', env.defaults.theme);
env.defaults.alarmUrgentHigh = readENV('ALARM_URGENT_HIGH', env.defaults.alarmUrgentHigh);
env.defaults.alarmHigh = readENV('ALARM_HIGH', env.defaults.alarmHigh);
env.defaults.alarmLow = readENV('ALARM_LOW', env.defaults.alarmLow);
env.defaults.alarmUrgentLow = readENV('ALARM_URGENT_LOW', env.defaults.alarmUrgentLow);
env.defaults.alarmTimeAgoWarn = readENV('ALARM_TIMEAGO_WARN', env.defaults.alarmTimeAgoWarn);
env.defaults.alarmTimeAgoWarnMins = readENV('ALARM_TIMEAGO_WARN_MINS', env.defaults.alarmTimeAgoWarnMins);
env.defaults.alarmTimeAgoUrgent = readENV('ALARM_TIMEAGO_URGENT', env.defaults.alarmTimeAgoUrgent);
env.defaults.alarmTimeAgoUrgentMins = readENV('ALARM_TIMEAGO_URGENT_MINS', env.defaults.alarmTimeAgoUrgentMins);
env.defaults.showPlugins = readENV('SHOW_PLUGINS', '');
//TODO: figure out something for some plugins to have them shown by default
if (env.defaults.showPlugins != '') {
env.defaults.showPlugins += ' delta upbat';
if (env.defaults.showRawbg == 'always' || env.defaults.showRawbg == 'noise') {
env.defaults.showPlugins += 'rawbg';
}
}
//console.log(JSON.stringify(env.defaults));
env.SSL_KEY = readENV('SSL_KEY');
env.SSL_CERT = readENV('SSL_CERT');
env.SSL_CA = readENV('SSL_CA');
env.ssl = false;
if (env.SSL_KEY && env.SSL_CERT) {
env.ssl = {
key: fs.readFileSync(env.SSL_KEY)
, cert: fs.readFileSync(env.SSL_CERT)
};
if (env.SSL_CA) {
env.ca = fs.readFileSync(env.SSL_CA);
}
}
var shasum = crypto.createHash('sha1');
/////////////////////////////////////////////////////////////////
// A little ugly, but we don't want to read the secret into a var
/////////////////////////////////////////////////////////////////
var useSecret = (readENV('API_SECRET') && readENV('API_SECRET').length > 0);
env.api_secret = null;
// if a passphrase was provided, get the hex digest to mint a single token
if (useSecret) {
if (readENV('API_SECRET').length < consts.MIN_PASSPHRASE_LENGTH) {
var msg = ['API_SECRET should be at least', consts.MIN_PASSPHRASE_LENGTH, 'characters'];
var err = new Error(msg.join(' '));
// console.error(err);
throw err;
process.exit(1);
}
shasum.update(readENV('API_SECRET'));
env.api_secret = shasum.digest('hex');
}
env.thresholds = {
bg_high: readIntENV('BG_HIGH', 260)
, bg_target_top: readIntENV('BG_TARGET_TOP', 180)
, bg_target_bottom: readIntENV('BG_TARGET_BOTTOM', 80)
, bg_low: readIntENV('BG_LOW', 55)
};
//NOTE: using +/- 1 here to make the thresholds look visibly wrong in the UI
// if all thresholds were set to the same value you should see 4 lines stacked right on top of each other
if (env.thresholds.bg_target_bottom >= env.thresholds.bg_target_top) {
console.warn('BG_TARGET_BOTTOM(' + env.thresholds.bg_target_bottom + ') was >= BG_TARGET_TOP(' + env.thresholds.bg_target_top + ')');
env.thresholds.bg_target_bottom = env.thresholds.bg_target_top - 1;
console.warn('BG_TARGET_BOTTOM is now ' + env.thresholds.bg_target_bottom);
}
if (env.thresholds.bg_target_top <= env.thresholds.bg_target_bottom) {
console.warn('BG_TARGET_TOP(' + env.thresholds.bg_target_top + ') was <= BG_TARGET_BOTTOM(' + env.thresholds.bg_target_bottom + ')');
env.thresholds.bg_target_top = env.thresholds.bg_target_bottom + 1;
console.warn('BG_TARGET_TOP is now ' + env.thresholds.bg_target_top);
}
if (env.thresholds.bg_low >= env.thresholds.bg_target_bottom) {
console.warn('BG_LOW(' + env.thresholds.bg_low + ') was >= BG_TARGET_BOTTOM(' + env.thresholds.bg_target_bottom + ')');
env.thresholds.bg_low = env.thresholds.bg_target_bottom - 1;
console.warn('BG_LOW is now ' + env.thresholds.bg_low);
}
if (env.thresholds.bg_high <= env.thresholds.bg_target_top) {
console.warn('BG_HIGH(' + env.thresholds.bg_high + ') was <= BG_TARGET_TOP(' + env.thresholds.bg_target_top + ')');
env.thresholds.bg_high = env.thresholds.bg_target_top + 1;
console.warn('BG_HIGH is now ' + env.thresholds.bg_high);
}
//if any of the BG_* thresholds are set, default to `simple` otherwise default to `predict` to preserve current behavior
var thresholdsSet = readIntENV('BG_HIGH') || readIntENV('BG_TARGET_TOP') || readIntENV('BG_TARGET_BOTTOM') || readIntENV('BG_LOW');
env.alarm_types = readENV('ALARM_TYPES') || (thresholdsSet ? 'simple' : 'predict');
//TODO: maybe get rid of ALARM_TYPES and only use enable?
if (env.alarm_types.indexOf('simple') > -1) {
env.enable = 'simplealarms ' + env.enable;
}
if (env.alarm_types.indexOf('predict') > -1) {
env.enable = 'ar2 ' + env.enable;
}
// For pushing notifications to Pushover.
//TODO: handle PUSHOVER_ as generic plugin props
env.pushover_api_token = readENV('PUSHOVER_API_TOKEN');
env.pushover_user_key = readENV('PUSHOVER_USER_KEY') || readENV('PUSHOVER_GROUP_KEY');
if (env.pushover_api_token && env.pushover_user_key) {
env.enable += ' pushover';
//TODO: after config changes are documented this shouldn't be auto enabled
env.enable += ' treatmentnotify';
}
//TODO: figure out something for default plugins, how can they be disabled?
env.enable += ' delta upbat errorcodes';
env.extendedSettings = findExtendedSettings(env.enable, process.env);
env.baseUrl = readENV('BASE_URL');
// TODO: clean up a bit
// Some people prefer to use a json configuration file instead.
// This allows a provided json config to override environment variables
var DB = require('./database_configuration.json'),
DB_URL = DB.url ? DB.url : env.mongo,
DB_COLLECTION = DB.collection ? DB.collection : env.mongo_collection;
env.mongo = DB_URL;
env.mongo_collection = DB_COLLECTION;
env.static_files = readENV('NIGHTSCOUT_STATIC_FILES', __dirname + '/static/');
return env;
}
function readIntENV(varName, defaultValue) {
return parseInt(readENV(varName)) || defaultValue;
}
function readENV(varName, defaultValue) {
//for some reason Azure uses this prefix, maybe there is a good reason
var value = process.env['CUSTOMCONNSTR_' + varName]
|| process.env['CUSTOMCONNSTR_' + varName.toLowerCase()]
|| process.env[varName]
|| process.env[varName.toLowerCase()];
if (typeof value === 'string' && value.toLowerCase() == 'on') { value = true; }
if (typeof value === 'string' && value.toLowerCase() == 'off') { value = false; }
return value != null ? value : defaultValue;
}
function findExtendedSettings (enables, envs) {
var extended = {};
enables.split(' ').forEach(function eachEnable(enable) {
if (_.trim(enable)) {
_.forIn(envs, function eachEnvPair (value, key) {
if (_.startsWith(key, enable.toUpperCase() + '_') || _.startsWith(key, enable.toLowerCase() + '_')) {
var split = key.indexOf('_');
if (split > -1 && split <= key.length) {
var exts = extended[enable] || {};
extended[enable] = exts;
var ext = _.camelCase(key.substring(split + 1).toLowerCase());
exts[ext] = value;
}
}
});
}
});
return extended;
}
module.exports = config;