Skip to content

Commit

Permalink
Fix batch (nightscout#6248)
Browse files Browse the repository at this point in the history
* Use the delta plugin data to show the delta in the clock views
* Update Node checks
* Fix disabling the BG alarms for simple alarms
* Load battery and other rare events up to two months back
* Possibly fixes compatibility with ios9 - needs testing
* Unified black and color clock layouts
* Update clock data every 20 seconds
* Update clock time every second
* Fix how CSP policy is set for Helmet, fixes nightscout#6260
* Authorization fix for misformatted URLs
* Added unit test for batch upload of CGM entries
* Improved / removed some logging
* Test if user is in read only mode when Nightscout starts and give an error if so
  • Loading branch information
sulkaharo authored and tanja3981 committed Nov 15, 2020
1 parent 8b4e836 commit ace519e
Show file tree
Hide file tree
Showing 13 changed files with 327 additions and 199 deletions.
37 changes: 22 additions & 15 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,7 @@ function create (env, ctx) {

const enableCSP = env.secureCsp ? true : false;

console.info('Enabled SECURE_HSTS_HEADER (HTTP Strict Transport Security)');
const helmet = require('helmet');
var includeSubDomainsValue = env.secureHstsHeaderIncludeSubdomains;
var preloadValue = env.secureHstsHeaderPreload;
app.use(helmet({
hsts: {
maxAge: 31536000
, includeSubDomains: includeSubDomainsValue
, preload: preloadValue
}
, frameguard: false
, contentSecurityPolicy: enableCSP
}));
let cspPolicy = false;

if (enableCSP) {
var secureCspReportOnly = env.secureCspReportOnly;
Expand All @@ -60,7 +48,7 @@ function create (env, ctx) {
}
}

app.use(helmet.contentSecurityPolicy({ //TODO make NS work without 'unsafe-inline'
cspPolicy = { //TODO make NS work without 'unsafe-inline'
directives: {
defaultSrc: ["'self'"]
, styleSrc: ["'self'", 'https://fonts.googleapis.com/', 'https://fonts.gstatic.com/', "'unsafe-inline'"]
Expand All @@ -76,7 +64,26 @@ function create (env, ctx) {
, frameAncestors: frameAncestors
}
, reportOnly: secureCspReportOnly
}));
};
}


console.info('Enabled SECURE_HSTS_HEADER (HTTP Strict Transport Security)');
const helmet = require('helmet');
var includeSubDomainsValue = env.secureHstsHeaderIncludeSubdomains;
var preloadValue = env.secureHstsHeaderPreload;
app.use(helmet({
hsts: {
maxAge: 31536000
, includeSubDomains: includeSubDomainsValue
, preload: preloadValue
}
, frameguard: false
, contentSecurityPolicy: cspPolicy
}));

if (enableCSP) {

app.use(helmet.referrerPolicy({ policy: 'no-referrer' }));
app.use(bodyParser.json({ type: ['json', 'application/csp-report'] }));
app.post('/report-violation', (req, res) => {
Expand Down
28 changes: 20 additions & 8 deletions lib/authorization/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,28 @@ function init (env, ctx) {

if (!accessToken) return null;

var split_token = accessToken.split('-');
var prefix = split_token ? _.last(split_token) : '';

if (prefix.length < 16) {
return null;
}
function checkToken(accessToken) {
var split_token = accessToken.split('-');
var prefix = split_token ? _.last(split_token) : '';

return _.find(storage.subjects, function matches (subject) {
return subject.accessTokenDigest.indexOf(accessToken) === 0 || subject.digest.indexOf(prefix) === 0;
});
if (prefix.length < 16) {
return null;
}

return _.find(storage.subjects, function matches (subject) {
return subject.accessTokenDigest.indexOf(accessToken) === 0 || subject.digest.indexOf(prefix) === 0;
});
}

if (!Array.isArray(accessToken)) accessToken = [accessToken];

for (let i=0; i < accessToken.length; i++) {
const subject = checkToken(accessToken[i]);
if (subject) return subject;
}

return null;
};

storage.doesAccessTokenExist = function doesAccessTokenExist(accessToken) {
Expand Down
146 changes: 66 additions & 80 deletions lib/client/clock-client.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,55 @@
'use strict';

const browserSettings = require('./browser-settings');

var browserSettings = require('./browser-settings');
var client = {};
var latestProperties = {};

client.settings = browserSettings(client, window.serverSettings, $);

//console.log('settings', client.settings);
// client.settings now contains all settings

client.query = function query () {
console.log('query');
var parts = (location.search || '?').substring(1).split('&');
var token = '';
parts.forEach(function (val) {
parts.forEach(function(val) {
if (val.startsWith('token=')) {
token = val.substring('token='.length);
}
});

var secret = localStorage.getItem('apisecrethash');
var src = '/api/v1/entries.json?find[type]=sgv&count=3&t=' + new Date().getTime();
var src = '/api/v2/properties'; // Use precalculated data from the backend

if (secret) {
src += '&secret=' + secret;
var s = '?secret=' + secret;
src += s;
} else if (token) {
src += '&token=' + token;
var s2 = '?token=' + token;
src += s2;
}

$.ajax(src, {
success: client.render
error: function gotError (err) {
console.error(err);
}
, success: function gotData (data) {
latestProperties = data;
client.render();
}
});
};

client.render = function render (xhr) {
console.log('got data', xhr);
client.render = function render () {

let rec;
let delta;
if (!latestProperties.bgnow && !latestProperties.bgnow.sgvs) {
console.error('BG data not available');
return;
}

// Get SGV, calculate DELTA
xhr.forEach(element => {
if (element.sgv && !rec) {
rec = element;
}
else if (element.sgv && rec && delta==null) {
delta = (rec.sgv - element.sgv)/((rec.date - element.date)/(5*60*1000));
}
});
let rec = latestProperties.bgnow.sgvs[0];
let deltaDisplayValue;

if (latestProperties.delta) {
deltaDisplayValue = latestProperties.delta.display;
}

let $errorMessage = $('#errorMessage');
let $inner = $('#inner');
Expand All @@ -71,15 +73,12 @@ client.render = function render (xhr) {

// Backward compatible
if (face === 'clock-color') {
face = 'c' + (window.serverSettings.settings.showClockLastTime ? 'y' : 'n') + '13-sg40-' + (window.serverSettings.settings.showClockDelta ? 'dt14-' : '') + 'nl-ar30-nl-ag6';
}
else if (face === 'clock') {
face = 'c' + (window.serverSettings.settings.showClockLastTime ? 'y' : 'n') + '13-sg35-' + (window.serverSettings.settings.showClockDelta ? 'dt14-' : '') + 'nl-ar25-nl-ag6';
} else if (face === 'clock') {
face = 'bn0-sg40';
}
else if (face === 'bgclock') {
face = 'bn0-sg30-ar18-nl-nl-tm26';
}
else if (face === 'config') {
} else if (face === 'bgclock') {
face = 'b' + (window.serverSettings.settings.showClockLastTime ? 'y' : 'n') + '13-sg35-' + (window.serverSettings.settings.showClockDelta ? 'dt14-' : '') + 'nl-ar25-nl-ag6';
} else if (face === 'config') {
face = $inner.attr('data-face-config');
$inner.empty();
}
Expand All @@ -95,46 +94,19 @@ client.render = function render (xhr) {
if (param === '0') {
bgColor = (faceParams[param].substr(0, 1) === 'c'); // do we want colorful background?
alwaysShowTime = (faceParams[param].substr(1, 1) === 'y'); // always show "stale time" text?
staleMinutes = (faceParams[param].substr(2,2) - 0 >= 0) ? faceParams[param].substr(2,2) : 13; // threshold value (0=never)
} else if (!clockCreated){
let div = '<div class="' + faceParams[param].substr(0,2) + '"' + ((faceParams[param].substr(2,2) - 0 > 0) ? ' style="' + ((faceParams[param].substr(0,2) === 'ar') ? 'height' : 'font-size') + ':' + faceParams[param].substr(2,2) + 'vmin"' : '') + '></div>';
staleMinutes = (faceParams[param].substr(2, 2) - 0 >= 0) ? faceParams[param].substr(2, 2) : 13; // threshold value (0=never)
} else if (!clockCreated) {
let div = '<div class="' + faceParams[param].substr(0, 2) + '"' + ((faceParams[param].substr(2, 2) - 0 > 0) ? ' style="' + ((faceParams[param].substr(0, 2) === 'ar') ? 'height' : 'font-size') + ':' + faceParams[param].substr(2, 2) + 'vmin"' : '') + '></div>';
$inner.append(div);
}
}

// Convert BG to mmol/L if necessary.
let displayValue;
let deltaDisplayValue;

if (window.serverSettings.settings.units === 'mmol') {
displayValue = window.Nightscout.units.mgdlToMMOL(rec.sgv);
deltaDisplayValue = window.Nightscout.units.mgdlToMMOL(delta);
} else {
displayValue = rec.sgv;
deltaDisplayValue = Math.round(delta);
}

if (deltaDisplayValue > 0) {
deltaDisplayValue = '+' + deltaDisplayValue;
}
let displayValue = rec.scaled;

// Insert the delta value text.
$('.dt').html(deltaDisplayValue);

// Generate and insert the clock.
let timeDivisor = parseInt(client.settings.timeFormat ? client.settings.timeFormat : 12, 10);
let today = new Date()
, h = today.getHours() % timeDivisor;
if (timeDivisor === 12) {
h = (h === 0) ? 12 : h; // In the case of 00:xx, change to 12:xx for 12h time
}
if (timeDivisor === 24) {
h = (h < 10) ? ("0" + h) : h; // Pad the hours with a 0 in 24h time
}
let m = today.getMinutes();
if (m < 10) m = "0" + m;
$('.tm').html(h + ":" + m);

// Color background
if (bgColor) {

Expand All @@ -150,7 +122,7 @@ client.render = function render (xhr) {
let bgTargetBottom = client.settings.thresholds.bgTargetBottom;
let bgTargetTop = client.settings.thresholds.bgTargetTop;

let bgNum = parseFloat(rec.sgv);
let bgNum = parseFloat(rec.mgdl);

// Threshold background coloring.
if (bgNum < bgLow) {
Expand All @@ -169,20 +141,16 @@ client.render = function render (xhr) {
$('body').css('background-color', red);
}

}
else {
} else {
$('body').css('background-color', 'black');
}

// Time before data considered stale.
let threshold = 1000 * 60 * staleMinutes;

let last = new Date(rec.date);
let now = new Date();

let elapsedMins = Math.round(((now - last) / 1000) / 60);

let thresholdReached = (now - last > threshold) && threshold > 0;
var elapsedms = Date.now() - rec.mills;
let elapsedMins = Math.floor((elapsedms / 1000) / 60);
let thresholdReached = (elapsedms > threshold) && threshold > 0;

// Insert the BG value text, toggle stale if necessary.
$('.sg').toggleClass('stale', thresholdReached).html(displayValue);
Expand All @@ -191,17 +159,14 @@ client.render = function render (xhr) {
let staleTimeText;
if (elapsedMins === 0) {
staleTimeText = 'Just now';
}
else if (elapsedMins === 1) {
} else if (elapsedMins === 1) {
staleTimeText = '1 minute ago';
}
else {
} else {
staleTimeText = elapsedMins + ' minutes ago';
}

$('.ag').html(staleTimeText);
}
else {
} else {
$('.ag').html('');
}

Expand All @@ -216,12 +181,33 @@ client.render = function render (xhr) {
$('body').css('color', bgColor ? 'white' : 'grey');
$('.ar').css('filter', bgColor ? 'brightness(100%)' : 'brightness(50%)').html(arrow);
}

updateClock();

};

function updateClock () {
let timeDivisor = parseInt(client.settings.timeFormat ? client.settings.timeFormat : 12, 10);
let today = new Date()
, h = today.getHours() % timeDivisor;
if (timeDivisor === 12) {
h = (h === 0) ? 12 : h; // In the case of 00:xx, change to 12:xx for 12h time
}
if (timeDivisor === 24) {
h = (h < 10) ? ("0" + h) : h; // Pad the hours with a 0 in 24h time
}
let m = today.getMinutes();
if (m < 10) m = "0" + m;
$('.tm').html(h + ":" + m);
}

client.init = function init () {
console.log('init');
console.log('Initializing clock');
client.query();
setInterval(client.query, 1 * 60 * 1000);
setInterval(client.query, 20 * 1000); // update every 20 seconds

// time update
setInterval(updateClock, 1000);
};

module.exports = client;
2 changes: 1 addition & 1 deletion lib/data/dataloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ function loadSensorAndInsulinTreatments(ddata, ctx, callback) {
function loadLatestSingle(ddata, ctx, dataType, callback) {

var dateRange = {
$gte: new Date(ddata.lastUpdated - (constants.ONE_DAY * 32)).toISOString()
$gte: new Date(ddata.lastUpdated - (constants.ONE_DAY * 62)).toISOString()
};

if (ddata.page && ddata.page.frame) {
Expand Down
13 changes: 8 additions & 5 deletions lib/plugins/ar2.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,19 @@ function init (ctx) {

var prop = sbx.properties.ar2;

if (prop) {
sbx.notifications.requestNotify({
console.log('ar2', prop);

if (prop && prop.level) {
const notify = {
level: prop.level
, title: buildTitle(prop, sbx)
, message: sbx.buildDefaultMessage()
, eventName: prop.eventName
, pushoverSound: pushoverSound(prop, sbx.levels)
, plugin: ar2
, debug: buildDebug(prop, sbx)
});
};
sbx.notifications.requestNotify(notify);
}
};

Expand Down Expand Up @@ -224,9 +227,9 @@ function selectEventType (prop, sbx) {
var eventName = '';

if (in20mins !== undefined) {
if (in20mins > sbx.scaleMgdl(sbx.settings.thresholds.bgTargetTop)) {
if (sbx.settings.alarmHigh && in20mins > sbx.scaleMgdl(sbx.settings.thresholds.bgTargetTop)) {
eventName = 'high';
} else if (in20mins < sbx.scaleMgdl(sbx.settings.thresholds.bgTargetBottom)) {
} else if (sbx.settings.alarmLow && in20mins < sbx.scaleMgdl(sbx.settings.thresholds.bgTargetBottom)) {
eventName = 'low';
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/plugins/bgnow.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,12 @@ function init (ctx) {
bgnow.calcDelta = function calcDelta (recent, previous, sbx) {

if (_.isEmpty(recent)) {
console.info('all buckets are empty');
//console.info('No recent CGM data is available');
return null;
}

if (_.isEmpty(previous)) {
console.info('previous bucket not found, not calculating delta');
//console.info('previous bucket not found, not calculating delta');
return null;
}

Expand Down
Loading

0 comments on commit ace519e

Please sign in to comment.