+# User defined
+public/mVk7Yr3k/
+
# Logs
logs
*.log
const prodStatus = require('./routes/' + ver + '/status');
const prodEvents = require('./routes/' + ver + '/events');
const prodControl = require('./routes/' + ver + '/control');
+const prodNotification = require('./routes/' + ver + '/notification');
const testStatus = require('./routes/' + ver + '/test/status');
const testEvents = require('./routes/' + ver + '/test/events').router;
const testControl = require('./routes/' + ver + '/test/control');
+const testNotification = require('./routes/' + ver + '/test/notification');
const testSettings = require('./routes/' + ver + '/test/settings');
// api-docs
app.use('/' + ver + '/status', prodStatus);
app.use('/' + ver + '/events', prodEvents);
app.use('/' + ver + '/control', prodControl);
+app.use('/' + ver + '/notification', prodNotification);
app.use('/' + ver + '/test/status', testStatus);
app.use('/' + ver + '/test/events', testEvents);
app.use('/' + ver + '/test/control', testControl);
+app.use('/' + ver + '/test/notification', testNotification);
app.use('/' + ver + '/test/settings', testSettings);
// api-docs
--- /dev/null
+
+
+function generateVAPIDKeys() {
+ var curve = crypto.createECDH('prime256v1');
+ curve.generateKeys();
+
+ return {
+ publicKey: curve.getPublicKey(),
+ privateKey: curve.getPrivateKey(),
+ };
+}
+
// create db
mongo.connect(url + '/' + name, function(err, db) {
if (err) {
- throw err;
- return;
- }
+ throw err;
+ return;
+ }
console.log('garnod created!');
- // create collections
+ // create collections
var dbo = db.db(name);
dbo.createCollection('keys', function(err, res) {
if (err) {
- throw err;
- return;
- }
+ throw err;
+ return;
+ }
console.log('keys created!');
- // populate collection
- dbo.collection('keys').insertOne({
- _id: 1,
- area: 'test',
- key: '2TTqCD4mNNny'
- }, function(err, res) {
- if (err) {
- throw err;
- return;
- }
- console.log('test key set!');
- });
+ // populate collection
+ dbo.collection('keys').insertOne({
+ _id: 1,
+ area: 'test',
+ key: '2TTqCD4mNNny'
+ }, function(err, res) {
+ if (err) {
+ throw err;
+ return;
+ }
+ console.log('test key set!');
+ });
+ });
+
+ dbo.createCollection('notifications', function(err, res) {
+ if (err) {
+ throw err;
+ return;
+ }
+ console.log('notifications created!');
});
dbo.createCollection('settings', function(err, res) {
if (err) {
- throw err;
- return;
- }
+ throw err;
+ return;
+ }
console.log('settings created!');
- // populate collection
- dbo.collection('settings').insertOne({
- _id: 1,
- status: {
- state: 'closed'
- },
- events: {
- state: 'closed',
- delay: 0
- }
- }, function(err, res) {
- if (err) {
- throw err;
- return;
- }
- console.log('settings set!');
- });
+ // populate collection
+ dbo.collection('settings').insertOne({
+ _id: 1,
+ status: {
+ state: 'closed'
+ },
+ events: {
+ state: 'closed',
+ delay: 0
+ },
+ notification: {
+ delay: 0
+ }
+ }, function(err, res) {
+ if (err) {
+ throw err;
+ return;
+ }
+ console.log('settings set!');
+ });
});
});
--- /dev/null
+{"publicKey":"BLDSdGasI5sLks30brbIWvlLMFqzoxxkOs7aW_E9PDBzIO_mDs6-tvtb2U0-BVFDafNd58DJgoXxdK5711FF29c","privateKey":"_AFTIegzYV_l_5RYwzOCc22cpYcMUmpkA8bLbrlNq9I"}
var db = null;
const msg = {
- ok: 'ok',
- dbError: 'dbError',
- keyMismatch: 'keyMismatch'
+ ok: 'ok',
+ dbError: 'dbError',
+ keyMismatch: 'keyMismatch'
};
function connect() {
connect();
function setSettings(data, callback) {
- // resolve API key
- new Promise((resolve, reject) => {
- var keys = db.db(name).collection('keys');
- log.debug('Search for API key, area', data.area);
- keys.findOne({ area: data.area }, {}, (err, res) => {
- if (err) {
- log.error('Error on searching for API key', JSON.stringify(err));
- return reject(msg.dbError);
- }
- if (res === null) {
- log.warn('No such key area found in DB');
- return reject(msg.dbError);
- }
- if (res.key !== data.key) {
- log.info('API key doesn\'t match');
- return reject(msg.keyMismatch);
- }
- log.debug('API key is valid');
- resolve(msg.ok);
- })
- })
- .catch (err => {
- log.error('Error on searching for API key', err);
- return callback(err, null);
- })
-
- // save settings
- .then(res => {
- return new Promise((resolve, reject) => {
- var settings = db.db(name).collection('settings');
- log.debug('Update settings:', JSON.stringify(data.settings));
- settings.updateOne({ _id: 1 }, { $set: data.settings }, { upsert: true }, (err, res) => {
- if (err) {
- log.error('Error on updating settings', JSON.stringify(err));
- return reject(msg.dbError);
- }
- log.debug('Update settings done');
- resolve(msg.ok);
- });
- });
- })
- .catch (err => {
- log.error('Error on updating settings', err);
- return callback(err, null);
- })
-
- // return result
- .then(res => {
- return callback(null, res);
- });
+ // resolve API key
+ new Promise((resolve, reject) => {
+ var keys = db.db(name).collection('keys');
+ log.debug('Search for API key, area', data.area);
+ keys.findOne({ area: data.area }, {}, (err, res) => {
+ if (err) {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ if (res === null) {
+ log.warn('No such key area found in DB');
+ return reject(msg.dbError);
+ }
+ if (res.key !== data.key) {
+ log.info('API key doesn\'t match');
+ return reject(msg.keyMismatch);
+ }
+ log.debug('API key is valid');
+ resolve(msg.ok);
+ })
+ })
+ .catch (err => {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return callback(err, null);
+ })
+
+ // save settings
+ .then(res => {
+ return new Promise((resolve, reject) => {
+ var settings = db.db(name).collection('settings');
+ log.debug('Update settings:', JSON.stringify(data.settings));
+ settings.updateOne({ _id: 1 }, { $set: data.settings }, { upsert: true }, (err, res) => {
+ if (err) {
+ log.error('Error on updating settings', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ log.debug('Update settings done');
+ resolve(msg.ok);
+ });
+ });
+ })
+ .catch (err => {
+ log.error('Error on updating settings', JSON.stringify(err));
+ return callback(err, null);
+ })
+
+ // return result
+ .then(res => {
+ return callback(null, res);
+ });
+}
+
+function setNotification(data, callback) {
+ // resolve API key
+ new Promise((resolve, reject) => {
+ var keys = db.db(name).collection('keys');
+ log.debug('Search for API key, area', data.area);
+ keys.findOne({ area: data.area }, {}, (err, res) => {
+ if (err) {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ if (res === null) {
+ log.warn('No such key area found in DB');
+ return reject(msg.dbError);
+ }
+ if (res.key !== data.key) {
+ log.info('API key doesn\'t match');
+ return reject(msg.keyMismatch);
+ }
+ log.debug('API key is valid');
+ resolve(msg.ok);
+ })
+ })
+ .catch (err => {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return callback(err, null);
+ })
+
+ // save notification data
+ .then(res => {
+ return new Promise((resolve, reject) => {
+ var settings = db.db(name).collection('notifications');
+ log.debug('Update notifications:', JSON.stringify(data.notification));
+ settings.updateOne({ _id: 1 }, { $set: data.notification }, { upsert: true }, (err, res) => {
+ if (err) {
+ log.error('Error on updating notification', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ log.debug('Update notification done');
+ resolve(msg.ok);
+ });
+ });
+ })
+ .catch (err => {
+ log.error('Error on updating notification', JSON.stringify(err));
+ return callback(err, null);
+ })
+
+ // return result
+ .then(res => {
+ return callback(null, res);
+ });
}
function getSettings(data, callback) {
- // resolve API key
- new Promise((resolve, reject) => {
- var keys = db.db(name).collection('keys');
- log.debug('Search for API key, area', data.area);
- keys.findOne({ area: data.area }, {}, (err, res) => {
- if (err) {
- log.error('Error on searching for API key', JSON.stringify(err));
- return reject(msg.dbError);
- }
- if (res === null) {
- log.warn('No such key area found in DB');
- return reject(msg.dbError);
- }
- if (res.key !== data.key) {
- log.info('API key doesn\'t match');
- return reject(msg.keyMismatch);
- }
- log.debug('API key is valid');
- resolve(msg.ok);
- })
- })
- .catch (err => {
- log.error('Error on searching for API key', err);
- return callback(err, null);
- })
-
- // get settings
- .then(res => {
- return new Promise((resolve, reject) => {
- var settings = db.db(name).collection('settings');
- log.debug('Search for settings object');
- settings.findOne({ _id: 1 }, {}, (err, res) => {
- if (err) {
- log.error('Error on getting settings', JSON.stringify(err));
- return reject(msg.dbError);
- }
- log.debug('Get settings done:', JSON.stringify(res));
- resolve(res);
- });
- });
- })
- .catch (err => {
- log.error('Error on getting settings', err);
- return callback(err, null);
- })
-
- // return result
- .then(res => {
- return callback(null, res);
- });
+ // resolve API key
+ new Promise((resolve, reject) => {
+ var keys = db.db(name).collection('keys');
+ log.debug('Search for API key, area', data.area);
+ keys.findOne({ area: data.area }, {}, (err, res) => {
+ if (err) {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ if (res === null) {
+ log.warn('No such key area found in DB');
+ return reject(msg.dbError);
+ }
+ if (res.key !== data.key) {
+ log.info('API key doesn\'t match');
+ return reject(msg.keyMismatch);
+ }
+ log.debug('API key is valid');
+ resolve(msg.ok);
+ })
+ })
+ .catch (err => {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return callback(err, null);
+ })
+
+ // get settings
+ .then(res => {
+ return new Promise((resolve, reject) => {
+ var settings = db.db(name).collection('settings');
+ log.debug('Search for settings object');
+ settings.findOne({ _id: 1 }, {}, (err, res) => {
+ if (err) {
+ log.error('Error on getting settings', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ log.debug('Get settings done:', JSON.stringify(res));
+ resolve(res);
+ });
+ });
+ })
+ .catch (err => {
+ log.error('Error on getting settings', err);
+ return callback(err, null);
+ })
+
+ // return result
+ .then(res => {
+ return callback(null, res);
+ });
+}
+
+function getNotification(data, callback) {
+ // resolve API key
+ new Promise((resolve, reject) => {
+ var keys = db.db(name).collection('keys');
+ log.debug('Search for API key, area', data.area);
+ keys.findOne({ area: data.area }, {}, (err, res) => {
+ if (err) {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ if (res === null) {
+ log.warn('No such key area found in DB');
+ return reject(msg.dbError);
+ }
+ if (res.key !== data.key) {
+ log.info('API key doesn\'t match');
+ return reject(msg.keyMismatch);
+ }
+ log.debug('API key is valid');
+ resolve(msg.ok);
+ })
+ })
+ .catch (err => {
+ log.error('Error on searching for API key', JSON.stringify(err));
+ return callback(err, null);
+ })
+
+ // get notification
+ .then(res => {
+ return new Promise((resolve, reject) => {
+ var settings = db.db(name).collection('notifications');
+ log.debug('Search for notification object');
+ settings.findOne({ _id: 1 }, {}, (err, res) => {
+ if (err) {
+ log.error('Error on getting notification', JSON.stringify(err));
+ return reject(msg.dbError);
+ }
+ log.debug('Get notification done:', JSON.stringify(res));
+ resolve(res);
+ });
+ });
+ })
+ .catch (err => {
+ log.error('Error on getting notification', JSON.stringify(err));
+ return callback(err, null);
+ })
+
+ // return result
+ .then(res => {
+ return callback(null, res);
+ });
}
module.exports = {
- msg,
- setSettings,
- getSettings
+ msg,
+ setSettings,
+ getSettings,
+ setNotification,
+ getNotification
}
});
module.exports = function(fileName) {
- var myLogger = {
- error: function(...args) {
- logger.error('[' + fileName + ']', ...args);
- },
- warn: function(...args) {
- logger.warn('[' + fileName + ']', ...args);
- },
- info: function(...args) {
- logger.info('[' + fileName + ']', ...args);
- },
- todo: function(...args) {
- logger.todo('[' + fileName + ']', ...args);
- },
- verbose: function(...args) {
- logger.verbose('[' + fileName + ']', ...args);
- },
- debug: function(...args) {
- logger.debug('[' + fileName + ']', ...args);
- },
- }
+ var myLogger = {
+ error: function(...args) {
+ logger.error('[' + fileName + ']', ...args);
+ },
+ warn: function(...args) {
+ logger.warn('[' + fileName + ']', ...args);
+ },
+ info: function(...args) {
+ logger.info('[' + fileName + ']', ...args);
+ },
+ todo: function(...args) {
+ logger.todo('[' + fileName + ']', ...args);
+ },
+ verbose: function(...args) {
+ logger.verbose('[' + fileName + ']', ...args);
+ },
+ debug: function(...args) {
+ logger.debug('[' + fileName + ']', ...args);
+ },
+ }
- return myLogger
+ return myLogger
}
"negotiator": "0.6.2"
}
},
+ "agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
"array-find-index": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
},
+ "asn1.js": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz",
+ "integrity": "sha512-Q7hnYGGNYbcmGrCPulXfkEw7oW7qjWeM4ZTALmgpuIcZLxyqqKYWxCZg2UBm8bklrnB4m2mGyJPWfoktdORD8A==",
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
"async": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
"safe-buffer": "5.1.2"
}
},
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+ },
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz",
"integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg=="
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
"buffer-shims": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
"quick-lru": "^1.0.0"
}
},
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "cliff": {
+ "version": "0.1.10",
+ "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz",
+ "integrity": "sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=",
+ "requires": {
+ "colors": "~1.0.3",
+ "eyes": "~0.1.8",
+ "winston": "0.8.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
+ },
+ "winston": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz",
+ "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=",
+ "requires": {
+ "async": "0.2.x",
+ "colors": "0.6.x",
+ "cycle": "1.0.x",
+ "eyes": "0.1.x",
+ "isstream": "0.1.x",
+ "pkginfo": "0.3.x",
+ "stack-trace": "0.0.x"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w="
+ }
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ },
"colors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
"integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
},
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "requires": {
+ "es6-promise": "^4.0.3"
+ },
+ "dependencies": {
+ "es6-promise": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+ }
+ }
+ },
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz",
"integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q=="
},
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ },
+ "homedir": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/homedir/-/homedir-0.6.0.tgz",
+ "integrity": "sha1-KyHbZr8Ipts4JJo+/1LX0YcGrx4="
+ },
"hosted-git-info": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz",
"integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ=="
},
+ "html-entities": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
+ "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
+ },
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"statuses": ">= 1.4.0 < 2"
}
},
+ "http_ece": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz",
+ "integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==",
+ "requires": {
+ "urlsafe-base64": "~1.0.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz",
+ "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==",
+ "requires": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
"path-exists": "^3.0.0"
}
},
+ "lodash.throttle": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+ },
"loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
"signal-exit": "^3.0.0"
}
},
+ "m3u8stream": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/m3u8stream/-/m3u8stream-0.6.2.tgz",
+ "integrity": "sha512-WsuM2bd5pPN80xvfrB+1DZqr4M7+kJl8byi6+ZCy6cmVjEiHhmr/desN53Ngsa6Hs13kYumeVgT4wL0oIJ+v6g==",
+ "requires": {
+ "miniget": "^1.4.0",
+ "sax": "^1.2.4"
+ }
+ },
"map-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz",
"mime-db": "1.40.0"
}
},
+ "miniget": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/miniget/-/miniget-1.6.1.tgz",
+ "integrity": "sha512-I5oBwZmcaOuJrjQn7lpS29HM+aAZDbzKbX5ouxVyhFYdg6fA6YKOTwOCgzZQwlHuMek3FlCxz6eNrd4pOXbwOA=="
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
},
+ "progress-bar": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/progress-bar/-/progress-bar-0.1.1.tgz",
+ "integrity": "sha1-wyg0+I8PjqxGxjjg5jjxShwXvR8="
+ },
"proxy-addr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
+ "sanitize-filename": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
+ "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
+ "requires": {
+ "truncate-utf8-bytes": "^1.0.0"
+ }
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ },
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
"integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g="
},
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
"swagger-ui-dist": {
"version": "3.23.11",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.23.11.tgz",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz",
"integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA="
},
+ "truncate-utf8-bytes": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
+ "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=",
+ "requires": {
+ "utf8-byte-length": "^1.0.1"
+ }
+ },
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
+ "urlsafe-base64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz",
+ "integrity": "sha1-I/iQaabGL0bPOh07ABac77kL4MY="
+ },
+ "utf8-byte-length": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
+ "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E="
+ },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
+ "web-push": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.4.1.tgz",
+ "integrity": "sha512-wtx18llPtWWW+x8hv+Gxvz+2VjO+vZuyihInsjySNpNGNVswH1Bb2KkbbCtE96yi52VUmbFMdidxM8kJAPaSWQ==",
+ "requires": {
+ "asn1.js": "^5.0.0",
+ "http_ece": "1.1.0",
+ "https-proxy-agent": "^3.0.0",
+ "jws": "^3.1.3",
+ "minimist": "^1.2.0",
+ "urlsafe-base64": "^1.0.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ }
+ }
+ },
"winston": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/winston/-/winston-1.1.2.tgz",
"requires": {
"camelcase": "^4.1.0"
}
+ },
+ "ytdl": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/ytdl/-/ytdl-0.13.1.tgz",
+ "integrity": "sha512-+CJ4xjmMhCcL3vNLmg30TiFjeem7EZ5IbMzp1Ciaauyf5Lb+oe0S7JJotJ/FRRQCBlcsV8R8sv9s+KGNW4Nvng==",
+ "requires": {
+ "chalk": "^2.0.0",
+ "cliff": "~0.1.8",
+ "commander": "^3.0.0",
+ "homedir": "^0.6.0",
+ "lodash.throttle": "^4.1.1",
+ "progress-bar": "~0.1.1",
+ "sanitize-filename": "^1.6.1",
+ "ytdl-core": "^1.0.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
+ "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow=="
+ }
+ }
+ },
+ "ytdl-core": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ytdl-core/-/ytdl-core-1.0.0.tgz",
+ "integrity": "sha512-aP/UBWbZtBSYlqHRYOx1oZ4tMRCAk2I5Y+OJy20/hrrr5XOhgxZ6+vJ20397h1FxYRzOTRSwb3VpBs3/CLe3fA==",
+ "requires": {
+ "html-entities": "^1.1.3",
+ "m3u8stream": "^0.6.2",
+ "miniget": "^1.6.0",
+ "sax": "^1.1.3"
+ }
}
}
}
{
"name": "garnod",
- "version": "0.0.0",
- "description": "A garage door monitoring application.",
+ "version": "1.0.0",
+ "description": "A garage door monitoring application (server).",
"scripts": {
- "start": "node ./bin/www"
+ "start": "node ./bin/garnod"
},
"author": "ebelcrom",
"license": "GPL-2.0-or-later",
"morgan": "~1.9.1",
"query-string": "^6.8.3",
"swagger-ui-express": "^4.1.2",
- "winston": "1.1.2"
+ "web-push": "^3.4.1",
+ "winston": "1.1.2",
+ "ytdl": "^0.13.1"
},
"devDependencies": {
"express-generator": "^4.16.1"
+++ /dev/null
-<html>
-
-<head>
- <title>Express</title>
- <link rel="stylesheet" href="/stylesheets/style.css">
-</head>
-
-<body>
- <h1>Express</h1>
- <p>Welcome to Express</p>
-</body>
-
-</html>
+++ /dev/null
-body {
- padding: 50px;
- font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
-}
-
-a {
- color: #00B7FF;
-}
var express = require('express');
var router = express.Router();
-/* GET home page. */
+/* GET / */
router.get('/', function(req, res, next) {
- res.render('index', { title: 'Express' });
+ res.render('index');
});
module.exports = router;
var express = require('express');
var router = express.Router();
-/* GET home page. */
+/* GET */
router.get('/', function(req, res, next) {
- res.render('index', { title: 'Express' });
+ res.status(500).send;
});
module.exports = router;
var express = require('express');
var router = express.Router();
-/* GET home page. */
+/* GET */
router.get('/', function(req, res, next) {
- res.render('index', { title: 'Express' });
+ res.status(500).send;
});
module.exports = router;
--- /dev/null
+var express = require('express');
+var router = express.Router();
+
+/* GET */
+router.get('/', function(req, res, next) {
+ res.status(500).send;
+});
+
+module.exports = router;
var express = require('express');
-const log = require('./../../lib/logger')(__filename.slice(__dirname.length + 1));
-const qStr = require('query-string');
var router = express.Router();
-/* GET /v1/status?image=true */
+/* GET */
router.get('/', function(req, res, next) {
- if (typeof req.query.image == 'string') {
- switch (req.query.image) {
- case 'true':
- res.json({
- 'image': 'image',
- 'state': 'open'
- });
- break;
- case 'false':
- res.json({
- 'state': 'open'
- });
- break;
- default:
- res.status(400).json([ 'image' ]);
- break;
- }
- } else {
- res.status(400).json([ 'image' ]);
- }
+ res.status(500).send;
});
module.exports = router;
/* POST /vN/test/control */
router.post('/', function(req, res, next) {
- // check header
- if (typeof req.header('X-API-Key-Test') === 'undefined') {
- log.info('API key not set in request header');
- res.status(401).send();
- return;
- } else {
- log.debug('API key set in request header');
- }
+ // check header
+ if (typeof req.header('X-API-Key-Test') === 'undefined') {
+ log.info('API key not set in request header');
+ res.status(401).send();
+ return;
+ } else {
+ log.debug('API key set in request header');
+ }
- // check query parameter
- var command = 'move';
- if (typeof req.query.command != 'undefined') {
- if (typeof req.query.command == 'string') {
- switch (req.query.command) {
- case 'move':
- move(req, res, next);
- break;
- default:
- log.info('Value of command query parameter unknown');
- res.status(400).send();
- return;
- }
- }
- } else {
- // default action
- move(req, res, next);
- }
+ // check query parameter
+ var command = 'move';
+ if (typeof req.query.command != 'undefined') {
+ if (typeof req.query.command == 'string') {
+ switch (req.query.command) {
+ case 'move':
+ move(req, res, next);
+ break;
+ default:
+ log.info('Value of command query parameter unknown');
+ res.status(400).send();
+ return;
+ }
+ }
+ } else {
+ // default action
+ move(req, res, next);
+ }
});
function move(req, res, next) {
- // get settings
- dblib.getSettings({
- area: 'test',
- key: req.header('X-API-Key-Test')
- }, (err, data) => {
- if (err) {
- switch (err) {
- case dblib.msg.dbError:
- log.info('Server error response');
- res.status(500).send();
- break;
- case dblib.msg.keyMismatch:
- log.info('Unauthorized access');
- res.status(401).send();
- break;
- default:
- log.error('Error result unexpected');
- res.status(500).send();
- break;
- }
- } else {
- // schedule event
- scheduleEvent(data.events.delay, data);
- // send response
- res.status(200).send();
- }
- });
+ // get settings
+ dblib.getSettings({
+ area: 'test',
+ key: req.header('X-API-Key-Test')
+ }, (err, data) => {
+ if (err) {
+ switch (err) {
+ case dblib.msg.dbError:
+ log.info('Server error response');
+ res.status(500).send();
+ break;
+ case dblib.msg.keyMismatch:
+ log.info('Unauthorized access');
+ res.status(401).send();
+ break;
+ default:
+ log.error('Error result unexpected');
+ res.status(500).send();
+ break;
+ }
+ } else {
+ // schedule event
+ scheduleEvent(data.events.delay, data);
+ // send response
+ res.status(200).send();
+ }
+ });
}
function scheduleEvent(delay, settings) {
- if (timer !== null) {
- clearTimeout(timer);
- timer = null;
- }
- timer = setTimeout(executeEvent, delay * 1000, settings);
- log.debug('Event set to execute in ' + delay + ' seconds');
+ if (timer !== null) {
+ clearTimeout(timer);
+ timer = null;
+ }
+ timer = setTimeout(executeEvent, delay * 1000, settings);
+ log.debug('Event set to execute in ' + delay + ' seconds');
}
function executeEvent(settings) {
- clearTimeout(timer);
- timer = null;
- events.emit('event', settings);
+ clearTimeout(timer);
+ timer = null;
+ events.emit('event', settings);
}
module.exports = router;
var response = null;
var request = null;
var timer = null;
-const delayMin = 1;
+const delayMin = -1;
const delayMax = 300;
+function nocache(req, res, next) {
+ res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
+ res.header('Expires', '-1');
+ res.header('Pragma', 'no-cache');
+ next();
+}
+
/* GET /vN/test/events */
-router.get('/', function(req, res, next) {
- // check header
- if (typeof req.header('X-API-Key-Test') === 'undefined') {
- log.info('API key not set in request header');
- res.status(401).send();
- return;
- } else {
- log.debug('API key set in request header');
- }
+router.get('/', nocache, function(req, res, next) {
+ // check header
+ if (typeof req.header('X-API-Key-Test') === 'undefined') {
+ log.info('API key not set in request header');
+ res.status(401).send();
+ return;
+ } else {
+ log.debug('API key set in request header');
+ }
- // TODO: dequeue?
+ // TODO: dequeue?
- // check query parameter
- var timeout = 30;
- if (typeof req.query.timeout != 'undefined') {
- var delay = parseInt(req.query.timeout);
- if (!isNaN(delay)) {
- if (delay < delayMin || delay > delayMax) {
- log.info('Value of timeout query parameter not in range');
- res.status(400).send();
- return;
- } else {
- timeout = delay;
- }
- }
- }
+ // check query parameter
+ var timeout = 30;
+ if (typeof req.query.timeout != 'undefined') {
+ var delay = parseInt(req.query.timeout);
+ if (!isNaN(delay)) {
+ if (delay < delayMin || delay > delayMax) {
+ log.info('Value of timeout query parameter not in range');
+ res.status(400).send();
+ return;
+ } else {
+ timeout = delay;
+ }
+ }
+ }
- // schedule resonse on timeout
- response = res;
- request = req;
- if (timer === null) {
- timer = setTimeout(processTimeout, timeout * 1000);
- log.debug('Response timeout scheduled');
- } else {
- log.todo('not implemented');
- }
+ // schedule resonse on timeout
+ response = res;
+ request = req;
+ if (timer === null && timeout !== delayMin) {
+ timer = setTimeout(processTimeout, timeout * 1000);
+ log.debug('Response timeout scheduled');
+ } else {
+ log.todo('not implemented');
+ }
});
events.on('event', (settings) => {
- log.debug('Event ready for sending');
- if (timer !== null) {
- clearTimeout(timer);
- timer = null;
- }
+ log.debug('Event ready for sending');
+ if (timer !== null) {
+ clearTimeout(timer);
+ timer = null;
+ }
- // read settings
- log.debug('Got settings:', JSON.stringify(settings));
- var image = true;
- if (request !== null) {
- if (typeof request.query.image != 'undefined') {
- if (request.query.image === 'false') {
- image = false;
- }
- }
- request = null;
- }
+ // read settings
+ log.debug('Got settings:', JSON.stringify(settings));
+ var image = true;
+ if (request !== null) {
+ if (typeof request.query.image != 'undefined') {
+ if (request.query.image === 'false') {
+ image = false;
+ }
+ }
+ request = null;
+ }
- // TODO: enqueue?
+ // TODO: enqueue?
- // response
- if (response !== null) {
- var content = null;
- if (image) {
- var file = null;
- switch (settings.events.state) {
- case 'open':
- file = fs.readFileSync(__dirname + '/../../../public/images/open.jpg', 'base64');
- break;
- case 'closed':
- file = fs.readFileSync(__dirname + '/../../../public/images/closed.jpg', 'base64');
- break;
- default:
- log.error('Unexpected status from settings');
- response.status(500).send();
- return;
- }
- content = {
- 'image': file,
- 'state': settings.events.state
- };
- } else {
- content = {
- 'state': settings.events.state
- };
- }
- response.json(content);
- response = null;
- }
+ // response
+ if (response !== null) {
+ var content = null;
+ if (image) {
+ var file = null;
+ switch (settings.events.state) {
+ case 'open':
+ file = fs.readFileSync(__dirname + '/../../../public/images/open.jpg', 'base64');
+ break;
+ case 'closed':
+ file = fs.readFileSync(__dirname + '/../../../public/images/closed.jpg', 'base64');
+ break;
+ default:
+ log.error('Unexpected status from settings');
+ response.status(500).send();
+ return;
+ }
+ content = {
+ 'image': file,
+ 'state': settings.events.state
+ };
+ } else {
+ content = {
+ 'state': settings.events.state
+ };
+ }
+ response.json(content);
+ response = null;
+ }
});
function processTimeout() {
- log.debug('Timeout, no events');
- clearTimeout(timer);
- timer = null;
- response.status(304).send();
- response = null;
+ log.debug('Timeout, no events');
+ clearTimeout(timer);
+ timer = null;
+//console.log('response:', response);
+ response.status(304).end('');
+ response = null;
}
module.exports = {
- router,
- events
+ router,
+ events
}
--- /dev/null
+const express = require('express');
+const log = require('./../../../lib/logger')(__filename.slice(__dirname.length + 1));
+const qStr = require('query-string');
+const router = express.Router();
+const dblib = require('./../../../lib/dblib');
+
+/* POST /vN/test/notification */
+router.post('/', function(req, res, next) {
+ // check header
+ if (typeof req.header('X-API-Key-Test') === 'undefined') {
+ log.info('API key not set in request header');
+ res.status(401).send();
+ return;
+ } else {
+ log.debug('API key set in request header');
+ }
+
+ // check body
+ if (typeof req.body != 'object') {
+ log.info('Request body is not JSON');
+ res.status(400).send();
+ return;
+ } else{
+ // check endpoint
+ if (typeof req.body.endpoint != 'string' ) {
+ log.info('Type of endpoint in content invalid');
+ res.status(400).send();
+ return;
+ }
+
+ // check expirationTime
+ if (req.body.expirationTime !== null) {
+ if (typeof req.body.expirationTime != 'string' ) {
+ log.info('Type of expirationTime in content invalid');
+ res.status(400).send();
+ return;
+ }
+ }
+
+ // check keys
+ if (typeof req.body.keys == 'object' ) {
+ if (typeof req.body.keys.p256dh != 'string' ) {
+ log.info('Type of keys.p256dh in content invalid');
+ res.status(400).send();
+ return;
+ }
+ if (typeof req.body.keys.auth != 'string' ) {
+ log.info('Type of keys.auth in content invalid');
+ res.status(400).send();
+ return;
+ }
+ }
+
+ // save to DB
+ dblib.setNotification({
+ area: 'test',
+ key: req.header('X-API-Key-Test'),
+ notification: req.body
+ }, (err, data) => {
+ if (err) {
+ switch (err) {
+ case dblib.msg.dbError:
+ log.info('Server error response');
+ res.status(500).send();
+ break;
+ case dblib.msg.keyMismatch:
+ log.info('Unauthorized access');
+ res.status(401).send();
+ break;
+ default:
+ log.error('Error result unexpected');
+ res.status(500).send();
+ break;
+ }
+ } else {
+ res.status(200).send();
+ }
+ });
+ }
+});
+
+module.exports = router;
const qStr = require('query-string');
const router = express.Router();
const dblib = require('./../../../lib/dblib');
+const webpush = require('web-push');
-const delayMin = 1;
+const delayMin = -1;
const delayMax = 300;
+var timer = null;
/* POST /vN/test/settings */
router.post('/', function(req, res, next) {
- // check header
- if (typeof req.header('X-API-Key-Test') === 'undefined') {
- log.info('API key not set in request header');
- res.status(401).send();
- return;
- } else {
- log.debug('API key set in request header');
- }
+ // check header
+ if (typeof req.header('X-API-Key-Test') === 'undefined') {
+ log.info('API key not set in request header');
+ res.status(401).send();
+ return;
+ } else {
+ log.debug('API key set in request header');
+ }
- // check body
- if (typeof req.body != 'object') {
- log.info('Request body is not JSON');
- res.status(400).send();
- return;
- } else{
- var settings = {};
+ // check body
+ if (typeof req.body != 'object') {
+ log.info('Request body is not JSON');
+ res.status(400).send();
+ return;
+ } else{
+ var settings = {};
- // populate status
- if (typeof req.body.status == 'object' ) {
- if (typeof req.body.status.state == 'string' ) {
- switch (req.body.status.state) {
- case 'open':
- settings.status = {
- state: 'open'
- };
- break;
- case 'closed':
- settings.status = {
- state: 'closed'
- };
- break;
- case 'moving':
- settings.status = {
- state: 'moving'
- };
- break;
- default:
- log.info('Value of status.state in content unknown');
- res.status(400).send();
- return;
- }
- } else {
- log.info('Type of status.state in content invalid');
- res.status(400).send();
- return;
- }
- }
+ // populate status
+ if (typeof req.body.status == 'object' ) {
+ if (typeof req.body.status.state == 'string' ) {
+ switch (req.body.status.state) {
+ case 'open':
+ settings.status = {
+ state: 'open'
+ };
+ break;
+ case 'closed':
+ settings.status = {
+ state: 'closed'
+ };
+ break;
+ case 'moving':
+ settings.status = {
+ state: 'moving'
+ };
+ break;
+ default:
+ log.info('Value of status.state in content unknown');
+ res.status(400).send();
+ return;
+ }
+ } else {
+ log.info('Type of status.state in content invalid');
+ res.status(400).send();
+ return;
+ }
+ }
- // populate events
- if (typeof req.body.events == 'object' ) {
- if (typeof req.body.events.state == 'string' ) {
- switch (req.body.events.state) {
- case 'open':
- settings.events = {
- state: 'open'
- };
- break;
- case 'closed':
- settings.events = {
- state: 'closed'
- };
- break;
- default:
- log.info('Value of events.state in content unknown');
- res.status(400).send();
- return;
- }
- } else {
- log.info('Type of events.state in content invalid');
- res.status(400).send();
- return;
- }
- if (typeof req.body.events.delay == 'number' ) {
- var delay = parseInt(req.body.events.delay);
- if (delay < delayMin || delay > delayMax) {
- log.info('Value of events.delay in content not in range');
- res.status(400).send();
- return;
- } else {
- settings.events.delay = delay;
- }
- } else {
- log.info('Type of events.delay in content invalid');
- res.status(400).send();
- return;
- }
- }
- log.debug('Parsed settings:', JSON.stringify(settings));
+ // populate events
+ if (typeof req.body.events == 'object' ) {
+ if (typeof req.body.events.state == 'string' ) {
+ switch (req.body.events.state) {
+ case 'open':
+ settings.events = {
+ state: 'open'
+ };
+ break;
+ case 'closed':
+ settings.events = {
+ state: 'closed'
+ };
+ break;
+ default:
+ log.info('Value of events.state in content unknown');
+ res.status(400).send();
+ return;
+ }
+ } else {
+ log.info('Type of events.state in content invalid');
+ res.status(400).send();
+ return;
+ }
+ if (typeof req.body.events.delay == 'number' ) {
+ var delay = parseInt(req.body.events.delay);
+ if (delay < delayMin || delay > delayMax) {
+ log.info('Value of events.delay in content not in range');
+ res.status(400).send();
+ return;
+ } else {
+ settings.events.delay = delay;
+ }
+ } else {
+ log.info('Type of events.delay in content invalid');
+ res.status(400).send();
+ return;
+ }
+ }
- // check settings
- if (Object.getOwnPropertyNames(settings).length === 0) {
- log.info('Settings is empty');
- res.status(400).send();
- return;
- }
+ // populate notification
+ if (typeof req.body.notification == 'object' ) {
+ if (typeof req.body.notification.delay == 'number' ) {
+ var delay = parseInt(req.body.notification.delay);
+ if (delay < delayMin || delay > delayMax) {
+ log.info('Value of notification.delay in content not in range');
+ res.status(400).send();
+ return;
+ } else {
+ settings.notification = {
+ delay: delay
+ };
+ }
+ } else {
+ log.info('Type of notification.delay in content invalid');
+ res.status(400).send();
+ return;
+ }
+ }
+ log.debug('Parsed settings:', JSON.stringify(settings));
- // save to DB
- dblib.setSettings({
- area: 'test',
- key: req.header('X-API-Key-Test'),
- settings: settings
- }, (err, data) => {
- if (err) {
- switch (err) {
- case dblib.msg.dbError:
- log.info('Server error response');
- res.status(500).send();
- break;
- case dblib.msg.keyMismatch:
- log.info('Unauthorized access');
- res.status(401).send();
- break;
- default:
- log.error('Error result unexpected');
- res.status(500).send();
- break;
- }
- } else {
- res.status(200).send();
- }
- });
- }
+ // check settings
+ if (Object.getOwnPropertyNames(settings).length === 0) {
+ log.info('Settings is empty');
+ res.status(400).send();
+ return;
+ }
+
+ // save to DB
+ dblib.setSettings({
+ area: 'test',
+ key: req.header('X-API-Key-Test'),
+ settings: settings
+ }, (err, data) => {
+ if (err) {
+ switch (err) {
+ case dblib.msg.dbError:
+ log.info('Server error response');
+ res.status(500).send();
+ break;
+ case dblib.msg.keyMismatch:
+ log.info('Unauthorized access');
+ res.status(401).send();
+ break;
+ default:
+ log.error('Error result unexpected');
+ res.status(500).send();
+ break;
+ }
+ } else {
+ // notify
+ if (typeof req.body.notification == 'object' ) {
+ if (typeof req.body.notification.delay == 'number' &&
+ req.body.notification.delay !== delayMin) {
+ notify(req, res, next, settings.notification.delay);
+ } else {
+ res.status(200).send();
+ }
+ }
+ }
+ });
+ }
});
+function notify(req, res, next, delay) {
+ // get settings
+ dblib.getNotification({
+ area: 'test',
+ key: req.header('X-API-Key-Test')
+ }, (err, data) => {
+ if (err) {
+ switch (err) {
+ case dblib.msg.dbError:
+ log.info('Server error response');
+ res.status(500).send();
+ break;
+ case dblib.msg.keyMismatch:
+ log.info('Unauthorized access');
+ res.status(401).send();
+ break;
+ default:
+ log.error('Error result unexpected');
+ res.status(500).send();
+ break;
+ }
+ } else {
+ // schedule event
+ scheduleNotification(delay, data);
+ res.status(200).send();
+ }
+ });
+}
+
+function scheduleNotification(delay, notification) {
+ if (timer !== null) {
+ clearTimeout(timer);
+ timer = null;
+ }
+ timer = setTimeout(executeNotification, delay * 1000, notification);
+ log.debug('Notification set to execute in ' + delay + ' seconds');
+}
+
+/*
+{
+ "endpoint":"https://fcm.googleapis.com/fcm/send/eGS6ZOFQcUk:APA91bGZliEmCLdFcuytpz2KFdLZVNm4YwAdbjaRxyF8tl6vhmuNd7L0NZagys77AjA3GWc3RNhad3i3Bc3rwBQEPKfAX4LZ0jOPzo_heG-WL7MNZx5w9lI2qycrdNk8UiQS0IlhL-j5",
+ "expirationTime":null,
+ "keys":{
+ "p256dh":"BAMu7XxsCuoB0j1zdaNGXoRterzytIpyG7yxzfEnAaA0SF4xTWJOztnbUoKX4IBdKpteJA9UhhyLI286mZKUlTQ",
+ "auth":"iPebCb94XG_zeVdfAmKN8Q"
+ }
+}
+*/
+function executeNotification(notification) {
+ clearTimeout(timer);
+ timer = null;
+
+ // web push
+ webpush.setGCMAPIKey('AAAAUtHuYco:APA91bEBTxCRGaez9_glljXAlit3PY5HMwhLSqWYMC1j-jFSp6nvnNqjI42jAVFApQbM0oyAOQjCUilIovB76cwTFxyZTP96wm9n09XwiMRXJjhwiJX1hO32mBB2zwK6X-w7epE1V67K');
+ webpush.setVapidDetails(
+ 'mailto:ebelcrom@gmail.com',
+ 'BLDSdGasI5sLks30brbIWvlLMFqzoxxkOs7aW_E9PDBzIO_mDs6-tvtb2U0-BVFDafNd58DJgoXxdK5711FF29c',
+ '_AFTIegzYV_l_5RYwzOCc22cpYcMUmpkA8bLbrlNq9I'
+ );
+
+ log.debug('Notification data:', JSON.stringify(notification))
+ const pushSubscription = {
+ endpoint: notification.endpoint,
+ keys: {
+ auth: notification.keys.auth,
+ p256dh: notification.keys.p256dh
+ }
+ };
+ const options = {
+ actions: [
+ {
+ action: 'Open Garage Node',
+ title: 'Garage Node'
+ }
+ ],
+ body: 'The garage door is open!',
+ badge: './img/icons/android-chrome-192x192.png',
+ icon: './img/icons/android-chrome-192x192.png'
+ };
+
+ webpush.sendNotification(pushSubscription, 'The garage door is open!')
+ .then(data => {
+ if (data) {
+ log.info('sent, data:', JSON.stringify(data));
+ }
+ })
+ .catch(err => {
+ log.info('sent, err:', JSON.stringify(err));
+ });
+}
+
module.exports = router;
/* GET /vN/status */
router.get('/', function(req, res, next) {
- // check header
- if (typeof req.header('X-API-Key-Test') === 'undefined') {
- log.info('API key not set in request header');
- res.status(401).send();
- return;
- } else {
- log.debug('API key set in request header');
- }
+ // check header
+ if (typeof req.header('X-API-Key-Test') === 'undefined') {
+ log.info('API key not set in request header');
+ res.status(401).send();
+ return;
+ } else {
+ log.debug('API key set in request header');
+ }
- // check query parameter
- var image = true;
- if (typeof req.query.image != 'undefined') {
- if (req.query.image === 'false') {
- image = false;
- }
- }
+ // check query parameter
+ var image = true;
+ if (typeof req.query.image != 'undefined') {
+ if (req.query.image === 'false') {
+ image = false;
+ }
+ }
- // read settings
- dblib.getSettings({
- area: 'test',
- key: req.header('X-API-Key-Test')
- }, (err, data) => {
- if (err) {
- switch (err) {
- case dblib.msg.dbError:
- log.info('Server error response');
- res.status(500).send();
- break;
- case dblib.msg.keyMismatch:
- log.info('Unauthorized access');
- res.status(401).send();
- break;
- default:
- log.error('Error result unexpected');
- res.status(500).send();
- break;
- }
- } else {
- // response
- var content = null;
- if (image) {
- var file = null;
- switch (data.status.state) {
- case 'open':
- file = fs.readFileSync(__dirname + '/../../../public/images/open.jpg', 'base64');
- break;
- case 'closed':
- file = fs.readFileSync(__dirname + '/../../../public/images/closed.jpg', 'base64');
- break;
- default:
- log.error('Unexpected status from settings');
- res.status(500).send();
- return;
- }
- content = {
- 'image': file,
- 'state': data.status.state
- };
- } else {
- content = {
- 'state': data.status.state
- };
- }
- res.json(content);
- }
- });
+ // read settings
+ dblib.getSettings({
+ area: 'test',
+ key: req.header('X-API-Key-Test')
+ }, (err, data) => {
+ if (err) {
+ switch (err) {
+ case dblib.msg.dbError:
+ log.info('Server error response');
+ res.status(500).send();
+ break;
+ case dblib.msg.keyMismatch:
+ log.info('Unauthorized access');
+ res.status(401).send();
+ break;
+ default:
+ log.error('Error result unexpected');
+ res.status(500).send();
+ break;
+ }
+ } else {
+ // response
+ var content = null;
+ if (image) {
+ var file = null;
+ switch (data.status.state) {
+ case 'open':
+ file = fs.readFileSync(__dirname + '/../../../public/images/open.jpg', 'base64');
+ break;
+ case 'closed':
+ file = fs.readFileSync(__dirname + '/../../../public/images/closed.jpg', 'base64');
+ break;
+ default:
+ log.error('Unexpected status from settings');
+ res.status(500).send();
+ return;
+ }
+ content = {
+ 'image': file,
+ 'state': data.status.state
+ };
+ } else {
+ content = {
+ 'state': data.status.state
+ };
+ }
+ res.json(content);
+ }
+ });
});
module.exports = router;
{
"openapi": "3.0.2",
"info": {
- "description": "Garage Node is inteded to be a garage door watch an control application based on a RESTful API. The (web) server is watching the door state and shall inform the user via a push notification when a garage door is open for a while. Then the user shall see the current door state and alternatively perform an motion action using a client application such as a PWA.",
+ "description": "Garage Node is inteded to be a garage door watch and control application based on a RESTful API. The (web) server is watching the door state and shall inform the user via a push notification when a garage door is open for a while. Then the user shall see the current door state and alternatively perform an motion action using a client application such as a PWA.",
"contact": {
"name": "ebelcrom"
},
},
"servers": [
{
- "url": "http://localhost:3000/v1"
+ "url": "https://binomiant.duckdns.org/mVk7Yr3k/v1"
}
],
"tags": [
{
"name": "production",
- "description": "Production area of this API. All requests go to the real server instance as well as all responses come from it."
+ "description": "Production area of this API. All requests go to the real server instance as well as all responses come from it."
},
{
"name": "test",
- "description": "Test area of this API. All requests go to a simulated server instance as well as all responses come from it."
+ "description": "Test area of this API. All requests go to a simulated server instance as well as all responses come from it."
}
],
"paths": {
"/events": {
"get": {
"summary": "Listen on events.",
- "description": "For listening on events, you have to long poll this ressource. If no events occur within the timeout range, request again. <p> <b>Note</b>: This call only makes sense if you have sent a control command before. </p>",
+ "description": "For listening on events, you have to long poll this ressource. If no events occur within the timeout range, request again. <p> <b>Note</b>: This call only makes sense if you have sent a control command before. </p>",
"operationId": "prod_events",
"tags": [
"production"
"/control": {
"post": {
"summary": "Send a command.",
- "description": "After sending a command you have to poll a state change. The command result only returns the command state.",
+ "description": "After sending a command you have to poll a state change. The command result only returns the command state.",
"operationId": "prod_control",
"tags": [
"production"
}
}
},
+ "/notification": {
+ "post": {
+ "summary": "Setup web push notification.",
+ "description": "To be able to send web push notifications the server has to have subscription data from the client.",
+ "operationId": "prod_notification",
+ "tags": [
+ "production"
+ ],
+ "security": [
+ {
+ "api_key_auth": []
+ }
+ ],
+ "requestBody": {
+ "description": "Request body for setup push notification.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/notification"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Ok, settings accepted"
+ },
+ "400": {
+ "description": "Bad request, check request body"
+ },
+ "401": {
+ "$ref": "#/components/responses/error_authentication"
+ }
+ }
+ }
+ },
"/test/status": {
"get": {
"summary": "Get the current state.",
"/test/events": {
"get": {
"summary": "Listen on events.",
- "description": "For listening on events, you have to long poll this ressource. If no events occur within the timeout range, request again. <p> <b>Note</b>: This call only makes sense if you have sent a control command before. </p>",
+ "description": "For listening on events, you have to long poll this ressource. If no events occur within the timeout range, request again. <p> <b>Note</b>: This call only makes sense if you have sent a control command before. </p>",
"operationId": "test_events",
"tags": [
"test"
"/test/control": {
"post": {
"summary": "Send a command.",
- "description": "After sending a command you have to poll a state change. The command result only returns the command state.",
+ "description": "After sending a command you have to poll a state change. The command result only returns the command state.",
"operationId": "test_control",
"tags": [
"test"
"$ref": "#/components/schemas/state"
},
"delay": {
- "description": "Delay for event to occur.",
+ "description": "Delay for event to occur. If set to -1 the event is disabled.\n",
"type": "integer",
"format": "int32",
- "minimum": 1,
+ "minimum": -1,
"maximum": 300
}
}
+ },
+ "notification": {
+ "description": "Settings for push notification of an open door.\n",
+ "type": "object",
+ "properties": {
+ "delay": {
+ "description": "Delay for the notification to occur. If set to -1 the notification is\ndisabled.\n",
+ "type": "integer",
+ "format": "int32",
+ "minimum": -1,
+ "maximum": 300
+ }
+ }
+ }
+ }
+ },
+ "notification": {
+ "description": "Notification Data.",
+ "type": "object",
+ "required": [
+ "endpoint",
+ "expirationTime",
+ "keys"
+ ],
+ "properties": {
+ "endpoint": {
+ "description": "Push service endpoint.",
+ "type": "string"
+ },
+ "expirationTime": {
+ "description": "Expiration time of subscription.",
+ "type": "string"
+ },
+ "keys": {
+ "type": "object",
+ "required": [
+ "p256dh",
+ "auth"
+ ],
+ "properties": {
+ "p256dh": {
+ "description": "An elliptic curve Diffie–Hellman public key on the P-256 curve.",
+ "type": "string"
+ },
+ "auth": {
+ "description": "An authentication secret.",
+ "type": "string"
+ }
+ }
}
}
},
}
},
"timeout": {
- "description": "Time in seconds for returning even when no events are available.",
+ "description": "Time in seconds for returning even when no events are available.\n",
"name": "timeout",
"in": "query",
"schema": {
openapi: 3.0.2
info:
description: >-
- Garage Node is inteded to be a garage door watch an control application
+ Garage Node is inteded to be a garage door watch and control application
based on a RESTful API. The (web) server is watching the door state and
shall inform the user via a push notification when a garage door is open for
a while. Then the user shall see the current door state and alternatively
name: GPL 3.0
url: 'https://www.gnu.org/licenses/gpl-3.0.txt'
servers:
-# - url: 'https://binomiant.duckdns.org/TYtse53t/v1'
- - url: 'http://localhost:3000/v1'
+ - url: 'https://binomiant.duckdns.org/mVk7Yr3k/v1'
tags:
- name: production
description: >-
- Production area of this API. All requests go to the real server instance
+ Production area of this API. All requests go to the real server instance
as well as all responses come from it.
- name: test
description: >-
- Test area of this API. All requests go to a simulated server instance as
+ Test area of this API. All requests go to a simulated server instance as
well as all responses come from it.
paths:
/status:
get:
summary: Listen on events.
description: >-
- For listening on events, you have to long poll this ressource. If no
+ For listening on events, you have to long poll this ressource. If no
events occur within the timeout range, request again. <p> <b>Note</b>:
This call only makes sense if you have sent a control command before.
</p>
post:
summary: Send a command.
description: >-
- After sending a command you have to poll a state change. The command
+ After sending a command you have to poll a state change. The command
result only returns the command state.
operationId: prod_control
tags:
$ref: '#/components/responses/error_param'
'401':
$ref: '#/components/responses/error_authentication'
+ /notification:
+ post:
+ summary: Setup web push notification.
+ description: >-
+ To be able to send web push notifications the server has to have
+ subscription data from the client.
+ operationId: prod_notification
+ tags:
+ - production
+ security:
+ - api_key_auth: []
+ requestBody:
+ description: Request body for setup push notification.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/notification'
+ responses:
+ '200':
+ description: 'Ok, settings accepted'
+ '400':
+ description: 'Bad request, check request body'
+ '401':
+ $ref: '#/components/responses/error_authentication'
/test/status:
get:
summary: Get the current state.
get:
summary: Listen on events.
description: >-
- For listening on events, you have to long poll this ressource. If no
+ For listening on events, you have to long poll this ressource. If no
events occur within the timeout range, request again. <p> <b>Note</b>:
This call only makes sense if you have sent a control command before.
</p>
post:
summary: Send a command.
description: >-
- After sending a command you have to poll a state change. The command
+ After sending a command you have to poll a state change. The command
result only returns the command state.
operationId: test_control
tags:
state:
$ref: '#/components/schemas/state'
delay:
- description: Delay for event to occur.
+ description: |
+ Delay for event to occur. If set to -1 the event is disabled.
+ type: integer
+ format: int32
+ minimum: -1
+ maximum: 300
+ notification:
+ description: |
+ Settings for push notification of an open door.
+ type: object
+ properties:
+ delay:
+ description: |
+ Delay for the notification to occur. If set to -1 the notification is
+ disabled.
type: integer
format: int32
- minimum: 1
+ minimum: -1
maximum: 300
+ notification:
+ description: Notification Data.
+ type: object
+ required:
+ - endpoint
+ - expirationTime
+ - keys
+ properties:
+ endpoint:
+ description: Push service endpoint.
+ type: string
+ expirationTime:
+ description: Expiration time of subscription.
+ type: string
+ keys:
+ type: object
+ required:
+ - p256dh
+ - auth
+ properties:
+ p256dh:
+ description: An elliptic curve Diffie–Hellman public key on the P-256 curve.
+ type: string
+ auth:
+ description: An authentication secret.
+ type: string
state:
description: Current door state.
type: string
type: boolean
default: true
timeout:
- description: Time in seconds for returning even when no events are available.
+ description: |
+ Time in seconds for returning even when no events are available.
name: timeout
in: query
schema: