From 0fa7c0cf943e8534f1b74ae983ea3daa41d23298 Mon Sep 17 00:00:00 2001 From: Matthias Palmer Date: Sat, 19 Mar 2022 15:39:42 +0100 Subject: [PATCH] Daily reports for door unlocks --- .meteor/packages | 1 + .meteor/versions | 1 + lib/models.js | 5 ++ lib/schemas.js | 1 + server/cronjob/syncAndMailUnlocks.js | 96 ++++++++++++++++++++++++++++ server/main.js | 10 ++- server/methods/lock.js | 43 +++++++++++++ 7 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 server/cronjob/syncAndMailUnlocks.js diff --git a/.meteor/packages b/.meteor/packages index be6ef44..c4fb508 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -32,3 +32,4 @@ reactive-dict@1.3.0 accounts-ui@1.3.1 useraccounts:bootstrap accounts-password@1.6.0 +littledata:synced-cron diff --git a/.meteor/versions b/.meteor/versions index 998f27e..6dfebaa 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -51,6 +51,7 @@ kadira:blaze-layout@2.3.0 kadira:flow-router@2.12.1 launch-screen@1.2.0 less@2.8.0 +littledata:synced-cron@1.5.1 livedata@1.0.18 localstorage@1.2.0 logging@1.1.20 diff --git a/lib/models.js b/lib/models.js index 13cfac0..79c3b06 100644 --- a/lib/models.js +++ b/lib/models.js @@ -222,6 +222,11 @@ export const models = { readonly: true, }}, }, + unlocks: { + timestamp: {label: 'Timestamp', type: Date}, + username: {label: 'Username', type: String, max: 50}, + user: {label: 'User', type: String, max: 25} + }, comment: { text: {label: 'Text', type: String, max: 2000, optional: true}, created: {label: 'Date', type: Date}, diff --git a/lib/schemas.js b/lib/schemas.js index 5d62e87..95b5c99 100644 --- a/lib/schemas.js +++ b/lib/schemas.js @@ -11,4 +11,5 @@ export const schemas = { lockusers: new SimpleSchema(models.lockusers), mails: new SimpleSchema(models.mail), comments: new SimpleSchema(models.comment), + unlocks: new SimpleSchema(models.unlocks) }; \ No newline at end of file diff --git a/server/cronjob/syncAndMailUnlocks.js b/server/cronjob/syncAndMailUnlocks.js new file mode 100644 index 0000000..11b1e0e --- /dev/null +++ b/server/cronjob/syncAndMailUnlocks.js @@ -0,0 +1,96 @@ +import {HTTP} from 'meteor/http'; +import { Unlocks } from '/collections/unlocks'; +import { Email } from 'meteor/email' + +let credentials; +let assumeUser; +let lockid; +let groupid; +let authTime; +let options; +const authenticate = () => { + if (!authTime || (new Date().getTime() - authTime.getTime()) > 3600000) { + authTime = new Date(); + const params = { + 'grant_type': 'password', + 'client_id': 'danalock-web', + 'username': Meteor.settings.lockUsername, + 'password': Meteor.settings.lockPassword, + }; + try { + credentials = JSON.parse(HTTP.post('https://api.danalock.com/oauth2/token', { params }).content); + const Authorization = `${credentials.token_type} ${credentials.access_token}`; + const identities = JSON.parse(HTTP.get('https://api.danalock.com/user/v1/identities', { headers: { Authorization } }).content); + assumeUser = identities.find(obj => obj.domain.indexOf(Meteor.settings.lockAssumeUser) !== -1).id; + options = { + headers: { + Authorization, + 'X-Assume-User': assumeUser, + } + }; + const locks = JSON.parse(HTTP.get('https://api.danalock.com/locks/v1?page=0', options).content); + lockid = locks.find(l => l.name.indexOf(Meteor.settings.lockName) !== -1).id; + const groups = JSON.parse(HTTP.get('https://api.danalock.com/groups/v1?page=0', options).content); + groupid = groups.find(l => l.name.indexOf(Meteor.settings.groupLockName) !== -1).id; + } catch (e) { + console.log(e); + } + } +}; + +const syncUnlocks = () => { + const res = HTTP.get(`https://api.danalock.com/log/v1/lock/${lockid}?page=0&perpage=50`, options); + let importedCount = 0; + res.data.forEach((event) => { + const timestamp = new Date(event.timestamp); + const unlock = Unlocks.findOne({ timestamp }); + if (!unlock) { + const colonlocation = event.username.lastIndexOf(':') + const username = colonlocation === -1 ? event.username : event.username.substr(0, colonlocation + 1); + Unlocks.insert({timestamp: timestamp, username: username, user: event.user}); + importedCount += 1; + } + }); + return importedCount; +}; + +if (Meteor.isServer) { + SyncedCron.add({ + name: 'Sync unlocks and send a mail', + schedule: function (parser) { +// return parser.recur().on(0).hour(); + return parser.recur().on(3).hour(); + }, + job: function () { + authenticate(); + syncUnlocks(); + const today = new Date(); + const yesterday = new Date(); + yesterday.setHours(-23); + const unlocks = Unlocks.find({ + 'timestamp': { + $gte: yesterday, + $lt: today + } + }).fetch(); + + const log = unlocks.map(t => `${t.timestamp.toISOString()} ${t.user}`).join("\n"); + const message = `${unlocks.length} låsöppningar av ytterdörren från ${yesterday.toISOString()} till ${today.toISOString()}\n\n${log}`; + Email.send({ + to: 'pass@ekebyindustrihus.com', + from: 'kansliet@uppsalamakerspace.se', + subject: 'Låsöppningar UMS', + text: message + }); + Email.send({ + to: 'mpalmer@gmail.com', + from: 'kansliet@uppsalamakerspace.se', + subject: 'Låsöppningar UMS', + text: message + }); + return message; + } + }); + + SyncedCron.start(); +} \ No newline at end of file diff --git a/server/main.js b/server/main.js index 7fb9d99..cb10888 100644 --- a/server/main.js +++ b/server/main.js @@ -6,6 +6,7 @@ import { Messages } from '/collections/messages'; import { Payments } from '/collections/payments'; import { Mails } from '/collections/mails'; import { Comments} from "/collections/comments"; +import { Unlocks } from '/collections/unlocks'; import './methods/import'; import './methods/update'; import './methods/mail'; @@ -13,6 +14,7 @@ import './methods/bank'; import './methods/lock'; import './methods/check'; import './methods/settings'; +import './cronjob/syncAndMailUnlocks'; Meteor.startup(() => { // code to run on server at startup @@ -112,4 +114,10 @@ if (Meteor.isServer) { return Comments.find(); } }); -} \ No newline at end of file + Meteor.publish('unlocks', function() { + if (this.userId) { + return Unlocks.find(); + } + }); +} + diff --git a/server/methods/lock.js b/server/methods/lock.js index 78b6651..04b62bd 100644 --- a/server/methods/lock.js +++ b/server/methods/lock.js @@ -1,4 +1,6 @@ import {HTTP} from 'meteor/http'; +import { Unlocks } from '/collections/unlocks'; +import { Email } from 'meteor/email' let credentials; let assumeUser; @@ -36,7 +38,48 @@ const authenticate = () => { } }; +const synkaUnlocks = () => { + const res = HTTP.get(`https://api.danalock.com/log/v1/lock/${lockid}?page=0&perpage=50`, options); + let importedCount = 0; + res.data.forEach((event) => { + const timestamp = new Date(event.timestamp); + const unlock = Unlocks.findOne({ timestamp }); + if (!unlock) { + const colonlocation = event.username.lastIndexOf(':') + const username = colonlocation === -1 ? event.username : event.username.substr(0, colonlocation + 1); + Unlocks.insert({timestamp: timestamp, username: username, user: event.user}); + importedCount += 1; + } + }); + return importedCount; +}; + Meteor.methods({ + 'syncLockHistory': () => { + authenticate(); + return synkaUnlocks(); + }, + 'syncAndMailLockHistory': () => { + if (!this.userId) { + throw new Meteor.Error('Not authorized'); + } + authenticate(); + synkaUnlocks(); + const today = new Date(); + const yesterday = new Date(); + yesterday.setHours(-25); + const unlocks = Unlocks.find({ + 'timestamp': { + $gte: yesterday, + $lt: today + } + }).fetch(); + + const log = unlocks.map(t => `${t.timestamp.toISOString()} ${t.user}`).join("\n"); + const message = `${unlocks.length} låsöppningar av ytterdörren från ${yesterday.toISOString()} till ${today.toISOString()}\n\n${log}`; + Email.send({ to: 'mpalmer@gmail.com', from: 'ums@uppsalamakerspace.se', subject: 'Låsöppningar UMS', text: message }); + return message; + }, 'lockHistory': () => { authenticate(); const afterDate = new Date();