Skip to content

Commit 34e2596

Browse files
committed
refactor: rework application configuration
To support the following: 1. Splitting immutable "core" data (included with this repository) and mutable "user" data (higher-precedence data provided by the user), and 2. Loading plain JSON data (easier to emit with automation tooling).
1 parent c648eb5 commit 34e2596

5 files changed

Lines changed: 139 additions & 109 deletions

File tree

config.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// **********************************************************************************
2+
// Websocket server backend for the RaspberryPi-Gateway App
3+
// http://lowpowerlab.com/gateway
4+
// **********************************************************************************
5+
// Common application configuration settings.
6+
// **********************************************************************************
7+
8+
const JSON5 = require('json5'); // https://github.com/aseemk/json5
9+
const nconf = require('nconf'); // https://github.com/indexzero/nconf
10+
const path = require('path');
11+
12+
exports.load = function({defaultStateDir = __dirname, defaultContentDir = null} = {}) {
13+
nconf.argv().env()
14+
15+
const stateDir = path.normalize(nconf.get('MOTEINO_GATEWAY_STATE_DIRECTORY') || defaultStateDir);
16+
17+
const resolvePath = (base) => (...dirs) => path.resolve(base, ...dirs);
18+
19+
const resolveCoreStatePath = resolvePath(defaultStateDir);
20+
const resolveUserStatePath = resolvePath(stateDir);
21+
22+
const coreContentDir = resolveCoreStatePath('www');
23+
const contentDir = path.normalize(nconf.get('MOTEINO_GATEWAY_CONTENT_DIRECTORY') || defaultContentDir || coreContentDir);
24+
25+
const resolveCoreContentPath = resolvePath(coreContentDir);
26+
const resolveUserContentPath = resolvePath(contentDir);
27+
28+
const coreImagesDir = resolveCoreContentPath('images');
29+
const userImagesDir = resolveUserContentPath('images');
30+
31+
const coreMetricsDir = resolveCoreStatePath('metrics');
32+
const userMetricsDir = resolveUserStatePath('metrics');
33+
34+
const dbDir = resolveUserStatePath('data/db');
35+
36+
if (stateDir == defaultStateDir)
37+
{
38+
nconf.file('mutable5', {
39+
file: resolveCoreStatePath('settings.json5'),
40+
format: JSON5,
41+
});
42+
43+
nconf.file('mutable', {
44+
file: resolveCoreStatePath('settings.json'),
45+
});
46+
}
47+
else
48+
{
49+
nconf.file('mutable5', {
50+
file: resolveUserStatePath('settings.json5'),
51+
format: JSON5,
52+
});
53+
54+
nconf.file('mutable', {
55+
file: resolveUserStatePath('settings.json'),
56+
});
57+
58+
nconf.file('immutable5', {
59+
file: resolveCoreStatePath('settings.json5'),
60+
format: JSON5,
61+
});
62+
63+
nconf.file('immutable', {
64+
file: resolveCoreStatePath('settings.json'),
65+
});
66+
}
67+
68+
return {
69+
'nconf': nconf,
70+
'stateDir': stateDir,
71+
'contentDir': contentDir,
72+
'coreContentDir': coreContentDir,
73+
'coreImagesDir': coreImagesDir,
74+
'userImagesDir': userImagesDir,
75+
'coreMetricsDir': coreMetricsDir,
76+
'userMetricsDir': userMetricsDir,
77+
'dbDir': dbDir,
78+
'resolveCoreContentPath': resolveCoreContentPath,
79+
'resolveCoreStatePath': resolveCoreStatePath,
80+
'resolveUserContentPath': resolveUserContentPath,
81+
'resolveUserStatePath': resolveUserStatePath,
82+
};
83+
};
84+
85+
// Load configuration and export it
86+
Object.assign(exports, exports.load());

gateway.js

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,20 @@
2929
// ********************************************************************************************
3030
// Note: In NodeJS modules are loaded synchronously and processed in the order they occur
3131
// ********************************************************************************************
32-
var nconf = require('nconf'); //https://github.com/indexzero/nconf
33-
var JSON5 = require('json5'); //https://github.com/aseemk/json5
32+
var fs = require('fs');
3433
var path = require('path');
3534

36-
nconf.argv()
37-
.env()
38-
.file({
39-
file: './settings.json5',
40-
format: JSON5,
41-
});
35+
const config = require('./config');
36+
37+
const nconf = config.nconf;
38+
const coreImagesDir = config.coreImagesDir;
39+
const userImagesDir = config.userImagesDir;
40+
const coreMetricsDir = config.coreMetricsDir;
41+
const userMetricsDir = config.userMetricsDir;
42+
const dbDir = config.dbDir;
43+
44+
fs.mkdirSync(dbDir, {'recursive': true});
45+
fs.mkdirSync(userImagesDir, {'recursive': true});
4246

4347
global.settings = nconf.get('settings');
4448

@@ -51,10 +55,8 @@ var Datastore = require('nedb'); //https://github
5155
var nodemailer = require('nodemailer'); //https://github.com/andris9/Nodemailer
5256
var http = require('http');
5357
var url = require('url');
54-
var dbDir = path.resolve([settings.database.directory.value]);
5558
db = new Datastore({ filename: path.join(dbDir, settings.database.name.value), autoload: true }); //used to keep all node/metric data
5659
var dbCompacted = Date.now();
57-
var fs = require('fs');
5860
var gatewayUptime='';
5961
var gatewayFrequency='';
6062
global.port=undefined;
@@ -253,19 +255,42 @@ global.getGraphData = function(nodeId, metricKey, start, end, exportMode) {
253255
return { graphData:graphData, options : graphOptions };
254256
}
255257

256-
global.getNodeIcons = function(dir, files_, steps){
257-
files_ = files_ || [];
258-
dir = dir || __dirname + '/www/images';
259-
steps = steps || 0;
260-
var files = fs.readdirSync(dir);
261-
for (var i in files){
262-
var name = dir + '/' + files[i];
263-
if (fs.statSync(name).isDirectory() && steps==0) //recurse 1 level only
264-
getNodeIcons(name, files_, steps+1);
265-
else if (files[i].match(/^icon_.+\.(bmp|png|jpg|jpeg|ico)$/ig)) //images only
266-
files_.push(name.replace(__dirname+'/www/images/',''));
258+
global.getNodeIcons = function({
259+
dir = coreImagesDir,
260+
root = null,
261+
files = [],
262+
steps = 0,
263+
prefix = 'icon_',
264+
extensions = ['.bmp', '.png', '.jpg', '.jpeg', '.ico'],
265+
} = {}) {
266+
dir = path.resolve(dir);
267+
root = path.resolve(root || dir);
268+
269+
console.log({'dir': dir, 'root': root, 'files': files, 'steps': steps, 'prefix': prefix, 'extensions': extensions});
270+
271+
var basenames = fs.readdirSync(dir);
272+
for (var i in basenames) {
273+
var basename = basenames[i];
274+
var file = path.join(dir, basename);
275+
276+
if (fs.statSync(file).isDirectory() && steps==0) //recurse 1 level only
277+
getNodeIcons({'dir': file, 'root': root, 'files': files, 'steps': steps+1, 'prefix': prefix, 'extensions': extensions});
278+
else if (basename.startsWith(prefix) && extensions.includes(path.extname(file)))
279+
files.push(path.relative(root, file));
267280
}
268-
return files_;
281+
282+
return files;
283+
}
284+
285+
global.allNodeIcons = function () {
286+
var files;
287+
288+
if (coreImagesDir == userImagesDir)
289+
files = [];
290+
else
291+
files = getNodeIcons({'dir': userImagesDir, 'prefix': ''});
292+
293+
return getNodeIcons({'dir': coreImagesDir, 'files': files});
269294
}
270295

271296
//authorize handshake - make sure the request is proxied from localhost, not from the outside world
@@ -310,7 +335,7 @@ io.sockets.on('connection', function (socket) {
310335
socket.emit('SETTINGSDEF', settings);
311336
broadcastServerInfo(socket);
312337
socket.emit('DBCOMPACTED', dbCompacted);
313-
socket.emit('NODEICONS', getNodeIcons());
338+
socket.emit('NODEICONS', allNodeIcons());
314339

315340
//pull all nodes from the database and send them to client
316341
db.find({ _id : { $exists: true } }, function (err, entries) {
@@ -328,7 +353,7 @@ io.sockets.on('connection', function (socket) {
328353
});
329354

330355
socket.on('REFRESHICONS', function () {
331-
io.sockets.emit('NODEICONS', getNodeIcons());
356+
io.sockets.emit('NODEICONS', allNodeIcons());
332357
});
333358

334359
socket.on('UPDATENODELISTORDER', function (newOrder) {

metrics/core.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
// ********************************************************************************************
1212
// (C) Felix Rusu, Low Power Lab LLC (2020), http://lowpowerlab.com/contact
1313
// ********************************************************************************************
14-
var config = require('nconf');
15-
var JSON5 = require('json5');
1614
var suncalc = require('suncalc'); //https://github.com/mourner/suncalc
17-
config.argv().file({ file: require('path').resolve(__dirname, '../settings.json5'), format: JSON5 });
18-
var settings = config.get('settings'); //these are local to avoid runtime errors but in events they will reference the global settings declared in gateway.js
15+
16+
//these are local to avoid runtime errors but in events they will reference the
17+
//global settings declared in gateway.js
18+
var settings = require('../config').nconf.get('settings');
1919

2020
// ******************************************************************************************************************************************
2121
// SAMPLE METRICS DEFINITIONS

settings.json5

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@
4444
database: {
4545
editable: false,
4646
exposed: false,
47-
directory: {
48-
value: 'data/db',
49-
description: 'path to the database directory, expanded relative to the application working directory',
50-
},
5147
name: {
5248
value: 'gateway.db',
5349
description: 'where your node information is stored, should not really be changed',

www/upload.php

Lines changed: 0 additions & 77 deletions
This file was deleted.

0 commit comments

Comments
 (0)