wingbot-mongodb 2.16.3 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.js +14 -0
- package/.nyc_output/8e3ceac5-95c5-4bf9-a069-da51cccc2525.json +1 -0
- package/.nyc_output/processinfo/8e3ceac5-95c5-4bf9-a069-da51cccc2525.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -1
- package/README.md +58 -50
- package/package.json +21 -20
- package/src/AuditLogStorage.js +364 -0
- package/src/BaseStorage.js +73 -30
- package/src/BotConfigStorage.js +18 -7
- package/src/BotTokenStorage.js +3 -3
- package/src/ChatLogStorage.js +65 -34
- package/src/NotificationsStorage.js +37 -37
- package/src/StateStorage.js +36 -138
- package/src/main.js +3 -1
- package/.nyc_output/5a4dc5e6-8e98-4d63-a4fc-e2fe688626a9.json +0 -1
- package/.nyc_output/ccf7d45a-e479-43d4-862d-f3f229049ae1.json +0 -1
- package/.nyc_output/processinfo/5a4dc5e6-8e98-4d63-a4fc-e2fe688626a9.json +0 -1
- package/.nyc_output/processinfo/ccf7d45a-e479-43d4-862d-f3f229049ae1.json +0 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author wingbot.ai
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const jsonwebtoken = require('jsonwebtoken');
|
|
7
|
+
const BaseStorage = require('./BaseStorage');
|
|
8
|
+
|
|
9
|
+
/** @typedef {import('mongodb/lib/db')} Db */
|
|
10
|
+
|
|
11
|
+
const LEVEL_CRITICAL = 'Critical';
|
|
12
|
+
const LEVEL_IMPORTANT = 'Important';
|
|
13
|
+
const LEVEL_DEBUG = 'Debug';
|
|
14
|
+
|
|
15
|
+
const TYPE_ERROR = 'Error';
|
|
16
|
+
const TYPE_WARN = 'Warn';
|
|
17
|
+
const TYPE_INFO = 'Info';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {object} TrackingEvent
|
|
21
|
+
* @prop {string} [type='audit']
|
|
22
|
+
* @prop {string} category
|
|
23
|
+
* @prop {string} action
|
|
24
|
+
* @prop {string} [label]
|
|
25
|
+
* @prop {object} [payload]
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {object} User
|
|
30
|
+
* @prop {string} [id]
|
|
31
|
+
* @prop {string} [senderId]
|
|
32
|
+
* @prop {string} [pageId]
|
|
33
|
+
* @prop {string} [jwt] - jwt to check the authorship
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {object} Meta
|
|
38
|
+
* @prop {string} [ip]
|
|
39
|
+
* @prop {string} [ua]
|
|
40
|
+
* @prop {string} [ro] - referrer || origin
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {object} LogEntry
|
|
45
|
+
* @prop {string} date - ISO date
|
|
46
|
+
* @prop {number} delta - time skew in ms if there was a write conflict
|
|
47
|
+
* @prop {string} [eventType='audit']
|
|
48
|
+
* @prop {string} category
|
|
49
|
+
* @prop {string} action
|
|
50
|
+
* @prop {string} [label]
|
|
51
|
+
* @prop {object} [payload]
|
|
52
|
+
* @prop {string} level - (Critical|Important|Debug)
|
|
53
|
+
* @prop {boolean} ok - signature matches
|
|
54
|
+
* @prop {number} seq - sequence number
|
|
55
|
+
* @prop {string} type - (Error|Warn|Info)
|
|
56
|
+
* @prop {User} user
|
|
57
|
+
* @prop {string} wid - workspace id
|
|
58
|
+
* @prop {Meta} meta
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* JWT Verifier
|
|
63
|
+
*
|
|
64
|
+
* @callback JwtVerifier
|
|
65
|
+
* @param {string} token
|
|
66
|
+
* @param {string} userId
|
|
67
|
+
* @param {User} [user]
|
|
68
|
+
* @returns {Promise<boolean>}
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @typedef {object} AuditLogEntry
|
|
73
|
+
* @prop {string} date - ISO date
|
|
74
|
+
* @prop {string} [eventType='audit']
|
|
75
|
+
* @prop {string} category
|
|
76
|
+
* @prop {string} action
|
|
77
|
+
* @prop {string} [label]
|
|
78
|
+
* @prop {object} [payload]
|
|
79
|
+
* @prop {string} level - (Critical|Important|Debug)
|
|
80
|
+
* @prop {string} type - (Error|Warn|Info)
|
|
81
|
+
* @prop {User} user
|
|
82
|
+
* @prop {string} wid - workspace id
|
|
83
|
+
* @prop {Meta} meta
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Audit Log Callback
|
|
88
|
+
*
|
|
89
|
+
* @callback AuditLogCallback
|
|
90
|
+
* @param {AuditLogEntry} entry
|
|
91
|
+
* @returns {Promise}
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Storage for audit logs with signatures chain
|
|
96
|
+
*/
|
|
97
|
+
class AuditLogStorage extends BaseStorage {
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
*
|
|
101
|
+
* @param {Db|{():Promise<Db>}} mongoDb
|
|
102
|
+
* @param {string} collectionName
|
|
103
|
+
* @param {{error:Function,log:Function}} [log] - console like logger
|
|
104
|
+
* @param {boolean} [isCosmo]
|
|
105
|
+
* @param {string|Promise<string>} [secret]
|
|
106
|
+
* @param {string|Promise<string>} [jwtVerifier]
|
|
107
|
+
*/
|
|
108
|
+
constructor (mongoDb, collectionName = 'auditlog', log = console, isCosmo = false, secret = null, jwtVerifier = null) {
|
|
109
|
+
super(mongoDb, collectionName, log, isCosmo);
|
|
110
|
+
|
|
111
|
+
this.addIndex({
|
|
112
|
+
wid: 1,
|
|
113
|
+
seq: -1
|
|
114
|
+
}, {
|
|
115
|
+
unique: true,
|
|
116
|
+
name: 'wid_1_seq_-1'
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (isCosmo) {
|
|
120
|
+
this.addIndex({
|
|
121
|
+
wid: 1
|
|
122
|
+
}, {
|
|
123
|
+
name: 'wid_1'
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
this.addIndex({
|
|
127
|
+
seq: -1
|
|
128
|
+
}, {
|
|
129
|
+
name: 'seq_-1'
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
this.addIndex({
|
|
133
|
+
wid: 1, date: -1
|
|
134
|
+
}, {
|
|
135
|
+
name: 'wid_1_date_-1'
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.defaultWid = '0';
|
|
140
|
+
|
|
141
|
+
this.muteErrors = true;
|
|
142
|
+
this.maxRetries = 4;
|
|
143
|
+
this._secret = secret;
|
|
144
|
+
|
|
145
|
+
this.LEVEL_CRITICAL = LEVEL_CRITICAL;
|
|
146
|
+
this.LEVEL_IMPORTANT = LEVEL_IMPORTANT;
|
|
147
|
+
this.LEVEL_DEBUG = LEVEL_DEBUG;
|
|
148
|
+
|
|
149
|
+
this.TYPE_ERROR = TYPE_ERROR;
|
|
150
|
+
this.TYPE_WARN = TYPE_WARN;
|
|
151
|
+
this.TYPE_INFO = TYPE_INFO;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @type {JwtVerifier}
|
|
155
|
+
*/
|
|
156
|
+
// @ts-ignore
|
|
157
|
+
this._jwtVerify = typeof jwtVerifier === 'function' || jwtVerifier === null
|
|
158
|
+
? jwtVerifier
|
|
159
|
+
: async (token, userId) => {
|
|
160
|
+
const jwtSec = await Promise.resolve(jwtVerifier);
|
|
161
|
+
const decoded = await new Promise((resolve) => {
|
|
162
|
+
jsonwebtoken.verify(token, jwtSec, (err, res) => {
|
|
163
|
+
resolve(res || {});
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
return decoded.id === userId;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/** @type {AuditLogCallback} */
|
|
170
|
+
this.callback = null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Add a log
|
|
175
|
+
*
|
|
176
|
+
* @param {TrackingEvent} event
|
|
177
|
+
* @param {User} user
|
|
178
|
+
* @param {Meta} [meta]
|
|
179
|
+
* @param {string} [wid] - workspace ID
|
|
180
|
+
* @param {string} [type]
|
|
181
|
+
* @param {string} [level]
|
|
182
|
+
* @param {Date} [date]
|
|
183
|
+
* @returns {Promise}
|
|
184
|
+
*/
|
|
185
|
+
async log (
|
|
186
|
+
event,
|
|
187
|
+
user = {},
|
|
188
|
+
meta = {},
|
|
189
|
+
wid = this.defaultWid,
|
|
190
|
+
type = TYPE_INFO,
|
|
191
|
+
level = LEVEL_IMPORTANT,
|
|
192
|
+
date = new Date()
|
|
193
|
+
) {
|
|
194
|
+
const {
|
|
195
|
+
type: eventType = 'audit',
|
|
196
|
+
...rest
|
|
197
|
+
} = event;
|
|
198
|
+
const entry = {
|
|
199
|
+
date,
|
|
200
|
+
eventType,
|
|
201
|
+
...rest,
|
|
202
|
+
level,
|
|
203
|
+
meta,
|
|
204
|
+
type,
|
|
205
|
+
user,
|
|
206
|
+
wid
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const secret = await Promise.resolve(this._secret);
|
|
210
|
+
const stored = await this._storeWithRetry(secret, entry);
|
|
211
|
+
if (!this.callback) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
await this.callback(stored);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
this._log.error('Failed to send AuditLog', e, entry);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// logEvent(level, type, workflowType, workflowInstance, eventData, account)
|
|
221
|
+
// level is the criticality of the event ('Critical','Important','Debug').
|
|
222
|
+
// type is the type of the event ('Error','Warn','Info').
|
|
223
|
+
/**
|
|
224
|
+
* - log (N/A)
|
|
225
|
+
- report (Debug)
|
|
226
|
+
- conversation (N/A)
|
|
227
|
+
- audit (Important)
|
|
228
|
+
- user (N/A)
|
|
229
|
+
*/
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
*
|
|
234
|
+
* @param {string} [wid] - workspace id
|
|
235
|
+
* @param {number} [fromSeq] - for paging
|
|
236
|
+
* @param {number} [limit]
|
|
237
|
+
* @returns {Promise<LogEntry[]>}
|
|
238
|
+
*/
|
|
239
|
+
async list (wid = this.defaultWid, fromSeq = 0, limit = 40) {
|
|
240
|
+
const c = await this._getCollection();
|
|
241
|
+
|
|
242
|
+
const cond = { wid };
|
|
243
|
+
|
|
244
|
+
if (fromSeq) {
|
|
245
|
+
cond.seq = { $lt: fromSeq };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const data = await c.find({ wid })
|
|
249
|
+
.limit(limit + 1)
|
|
250
|
+
.sort({ seq: -1 })
|
|
251
|
+
.project({ _id: 0 })
|
|
252
|
+
.toArray();
|
|
253
|
+
|
|
254
|
+
const secret = await Promise.resolve(this._secret);
|
|
255
|
+
|
|
256
|
+
const len = data.length === limit + 1
|
|
257
|
+
? data.length - 1
|
|
258
|
+
: data.length;
|
|
259
|
+
const ret = new Array(len);
|
|
260
|
+
|
|
261
|
+
let verifyUser = false;
|
|
262
|
+
for (let i = 0; i < len; i++) {
|
|
263
|
+
const {
|
|
264
|
+
sign,
|
|
265
|
+
...log
|
|
266
|
+
} = data[i];
|
|
267
|
+
|
|
268
|
+
verifyUser = verifyUser || (log.user.id && log.user.jwt);
|
|
269
|
+
|
|
270
|
+
if (secret) {
|
|
271
|
+
const previous = data[i + 1] || { sign: null };
|
|
272
|
+
const objToSign = this._objectToSign(log);
|
|
273
|
+
const compare = this._signWithSecret(objToSign, secret, previous.sign);
|
|
274
|
+
log.ok = compare === sign;
|
|
275
|
+
if (!log.ok) {
|
|
276
|
+
this._log.error(`AuditLog: found wrong signature at wid: "${log.wid}", seq: "${log.seq}"`, log);
|
|
277
|
+
}
|
|
278
|
+
} else {
|
|
279
|
+
log.ok = null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
ret[i] = log;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (verifyUser && this._jwtVerify) {
|
|
286
|
+
return Promise.all(
|
|
287
|
+
ret.map(async (log) => {
|
|
288
|
+
if (!log.user.id || !log.user.jwt) {
|
|
289
|
+
return log;
|
|
290
|
+
}
|
|
291
|
+
const userChecked = await this._jwtVerify(log.user.jwt, log.user.id, log.user);
|
|
292
|
+
return Object.assign(log, {
|
|
293
|
+
// it's ok, when there was null
|
|
294
|
+
ok: log.ok !== false && userChecked
|
|
295
|
+
});
|
|
296
|
+
})
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return ret;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
_wait (ms) {
|
|
304
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async _storeWithRetry (secret, entry, delta = 0, i = 1) {
|
|
308
|
+
const start = Date.now();
|
|
309
|
+
Object.assign(entry, { delta });
|
|
310
|
+
try {
|
|
311
|
+
await this._store(entry, secret);
|
|
312
|
+
return entry;
|
|
313
|
+
} catch (e) {
|
|
314
|
+
if (e.code === 11000) {
|
|
315
|
+
// duplicate key
|
|
316
|
+
if (i >= this.maxRetries) {
|
|
317
|
+
throw new Error('AuditLog: cannot store log due to max-retries');
|
|
318
|
+
} else {
|
|
319
|
+
await this._wait((i * 50) + (Math.random() * 100));
|
|
320
|
+
const add = Date.now() - start;
|
|
321
|
+
return this._storeWithRetry(secret, entry, delta + add, i + 1);
|
|
322
|
+
}
|
|
323
|
+
} else if (this.muteErrors) {
|
|
324
|
+
this._log.error('Audit log store error', e, entry);
|
|
325
|
+
return entry;
|
|
326
|
+
} else {
|
|
327
|
+
throw e;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async _store (entry, secret) {
|
|
333
|
+
const c = await this._getCollection();
|
|
334
|
+
|
|
335
|
+
const previous = await c.findOne({
|
|
336
|
+
wid: entry.wid
|
|
337
|
+
}, {
|
|
338
|
+
sort: { seq: -1 },
|
|
339
|
+
projection: { seq: 1, _id: 0, sign: 1 }
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
Object.assign(entry, {
|
|
343
|
+
seq: previous ? previous.seq + 1 : 0
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
let insert;
|
|
347
|
+
if (secret) {
|
|
348
|
+
insert = this._objectToSign(entry);
|
|
349
|
+
const sign = this._signWithSecret(insert, secret, previous ? previous.sign : null);
|
|
350
|
+
Object.assign(insert, { sign });
|
|
351
|
+
} else {
|
|
352
|
+
insert = {
|
|
353
|
+
...entry,
|
|
354
|
+
sign: null
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// @ts-ignore
|
|
359
|
+
await c.insertOne(insert);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
module.exports = AuditLogStorage;
|
package/src/BaseStorage.js
CHANGED
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
const mongodb = require('mongodb'); // eslint-disable-line no-unused-vars
|
|
7
|
+
const crypto = require('crypto');
|
|
7
8
|
|
|
8
|
-
/** @typedef {import('mongodb')
|
|
9
|
+
/** @typedef {import('mongodb/lib/db')} Db */
|
|
10
|
+
/** @typedef {import('mongodb/lib/collection')} Collection */
|
|
9
11
|
|
|
10
12
|
class BaseStorage {
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
*
|
|
14
|
-
* @param {
|
|
16
|
+
* @param {Db|{():Promise<Db>}} mongoDb
|
|
15
17
|
* @param {string} collectionName
|
|
16
18
|
* @param {{error:Function,log:Function}} [log] - console like logger
|
|
17
|
-
* @param {boolean} isCosmo
|
|
19
|
+
* @param {boolean} [isCosmo]
|
|
18
20
|
* @example
|
|
19
21
|
*
|
|
20
22
|
* const { BaseStorage } = require('winbot-mongodb');
|
|
@@ -47,18 +49,21 @@ class BaseStorage {
|
|
|
47
49
|
this._log = log;
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
|
-
* @type {Promise<
|
|
52
|
+
* @type {Collection|Promise<Collection>}
|
|
51
53
|
*/
|
|
52
54
|
this._collection = null;
|
|
53
55
|
|
|
54
56
|
this._indexes = [];
|
|
57
|
+
|
|
58
|
+
this.ignoredSignatureKeys = ['_id', 'sign'];
|
|
59
|
+
this._secret = null;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
/**
|
|
58
63
|
* Add custom indexing rule
|
|
59
64
|
*
|
|
60
|
-
* @param {
|
|
61
|
-
* @param {IndexOptions} options
|
|
65
|
+
* @param {object} index
|
|
66
|
+
* @param {mongodb.IndexOptions} options
|
|
62
67
|
*/
|
|
63
68
|
addIndex (index, options) {
|
|
64
69
|
if (!options.name) {
|
|
@@ -81,7 +86,7 @@ class BaseStorage {
|
|
|
81
86
|
const collections = await db.collections();
|
|
82
87
|
|
|
83
88
|
collection = collections
|
|
84
|
-
.find(c => c.collectionName === name);
|
|
89
|
+
.find((c) => c.collectionName === name);
|
|
85
90
|
|
|
86
91
|
if (!collection) {
|
|
87
92
|
try {
|
|
@@ -94,13 +99,16 @@ class BaseStorage {
|
|
|
94
99
|
} else {
|
|
95
100
|
collection = db.collection(name);
|
|
96
101
|
}
|
|
102
|
+
|
|
103
|
+
await this._ensureIndexes(this._indexes, collection);
|
|
104
|
+
|
|
97
105
|
return collection;
|
|
98
106
|
}
|
|
99
107
|
|
|
100
108
|
/**
|
|
101
109
|
* Returns the collection to operate with
|
|
102
110
|
*
|
|
103
|
-
* @returns {Promise<
|
|
111
|
+
* @returns {Promise<Collection>}
|
|
104
112
|
*/
|
|
105
113
|
async _getCollection () {
|
|
106
114
|
if (this._collection === null) {
|
|
@@ -108,12 +116,11 @@ class BaseStorage {
|
|
|
108
116
|
try {
|
|
109
117
|
this._collection = this._getOrCreateCollection(this._collectionName);
|
|
110
118
|
c = await this._collection;
|
|
119
|
+
this._collection = c;
|
|
111
120
|
} catch (e) {
|
|
112
121
|
this._collection = null;
|
|
113
122
|
throw e;
|
|
114
123
|
}
|
|
115
|
-
|
|
116
|
-
await this._ensureIndexes(this._indexes, c);
|
|
117
124
|
}
|
|
118
125
|
return this._collection;
|
|
119
126
|
}
|
|
@@ -126,34 +133,70 @@ class BaseStorage {
|
|
|
126
133
|
existing = [];
|
|
127
134
|
}
|
|
128
135
|
|
|
129
|
-
await
|
|
130
|
-
.filter(e => !['_id_', '_id'].includes(e.name)
|
|
131
|
-
&& !indexes.some(i => e.name === i.options.name))
|
|
132
|
-
.
|
|
136
|
+
await existing
|
|
137
|
+
.filter((e) => !['_id_', '_id'].includes(e.name)
|
|
138
|
+
&& !indexes.some((i) => e.name === i.options.name))
|
|
139
|
+
.reduce((p, e) => {
|
|
133
140
|
// eslint-disable-next-line no-console
|
|
134
141
|
this._log.log(`dropping index ${e.name}`);
|
|
135
|
-
return
|
|
142
|
+
return p
|
|
143
|
+
.then(() => collection.dropIndex(e.name))
|
|
136
144
|
.catch((err) => {
|
|
137
145
|
// eslint-disable-next-line no-console
|
|
138
146
|
this._log.error(`dropping index ${e.name} FAILED`, err);
|
|
139
147
|
});
|
|
140
|
-
}));
|
|
141
|
-
|
|
142
|
-
await
|
|
143
|
-
.filter(i => !existing.some(e => e.name === i.options.name))
|
|
144
|
-
.
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this.
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
}, Promise.resolve());
|
|
149
|
+
|
|
150
|
+
await indexes
|
|
151
|
+
.filter((i) => !existing.some((e) => e.name === i.options.name))
|
|
152
|
+
.reduce((p, i) => {
|
|
153
|
+
this._log.log(`creating index ${i.name}`);
|
|
154
|
+
return p
|
|
155
|
+
.then(() => collection.createIndex(i.index, i.options))
|
|
156
|
+
.catch((e) => {
|
|
157
|
+
this._log.error(`failed to create index ${i.options.name} on ${collection.collectionName}`, e);
|
|
158
|
+
});
|
|
159
|
+
}, Promise.resolve());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async _sign (object) {
|
|
163
|
+
if (!this._secret) {
|
|
164
|
+
return object;
|
|
165
|
+
}
|
|
166
|
+
const secret = await Promise.resolve(this._secret);
|
|
167
|
+
const objToSign = this._objectToSign(object);
|
|
168
|
+
const sign = this._signWithSecret(objToSign, secret);
|
|
169
|
+
|
|
170
|
+
return Object.assign(objToSign, {
|
|
171
|
+
sign
|
|
172
|
+
});
|
|
155
173
|
}
|
|
156
174
|
|
|
175
|
+
_objectToSign (object) {
|
|
176
|
+
const entries = Object.keys(object)
|
|
177
|
+
.filter((key) => !this.ignoredSignatureKeys.includes(key));
|
|
178
|
+
|
|
179
|
+
entries.sort();
|
|
180
|
+
|
|
181
|
+
return entries.reduce((o, key) => {
|
|
182
|
+
let val = object[key];
|
|
183
|
+
if (val instanceof Date) {
|
|
184
|
+
val = val.toISOString();
|
|
185
|
+
}
|
|
186
|
+
return Object.assign(o, { [key]: val });
|
|
187
|
+
}, {});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
_signWithSecret (objToSign, secret, previous = null) {
|
|
191
|
+
const h = crypto.createHmac('sha3-224', secret)
|
|
192
|
+
.update(JSON.stringify(objToSign));
|
|
193
|
+
|
|
194
|
+
if (previous) {
|
|
195
|
+
h.update(previous);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return h.digest('base64');
|
|
199
|
+
}
|
|
157
200
|
}
|
|
158
201
|
|
|
159
202
|
module.exports = BaseStorage;
|
package/src/BotConfigStorage.js
CHANGED
|
@@ -95,25 +95,36 @@ class BotConfigStorage {
|
|
|
95
95
|
/**
|
|
96
96
|
* @template T
|
|
97
97
|
* @param {T} newConfig
|
|
98
|
+
* @param {string} [id]
|
|
98
99
|
* @returns {Promise<T>}
|
|
99
100
|
*/
|
|
100
|
-
async updateConfig (newConfig) {
|
|
101
|
+
async updateConfig (newConfig, id = CONFIG_ID) {
|
|
101
102
|
Object.assign(newConfig, { timestamp: Date.now() });
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
await c.replaceOne({ _id: CONFIG_ID }, newConfig, { upsert: true });
|
|
104
|
+
await this.setConfig(id, newConfig);
|
|
106
105
|
|
|
107
106
|
return newConfig;
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
/**
|
|
111
|
-
*
|
|
110
|
+
*
|
|
111
|
+
* @param {string} id
|
|
112
|
+
* @param {object} newConfig
|
|
113
|
+
*/
|
|
114
|
+
async setConfig (id, newConfig) {
|
|
115
|
+
const c = await this._getCollection();
|
|
116
|
+
|
|
117
|
+
await c.replaceOne({ _id: id }, newConfig, { upsert: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {string} [id]
|
|
122
|
+
* @returns {Promise<object | null>}
|
|
112
123
|
*/
|
|
113
|
-
async getConfig () {
|
|
124
|
+
async getConfig (id = CONFIG_ID) {
|
|
114
125
|
const c = await this._getCollection();
|
|
115
126
|
|
|
116
|
-
return c.findOne({ _id:
|
|
127
|
+
return c.findOne({ _id: id }, { projection: { _id: 0 } });
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
}
|
package/src/BotTokenStorage.js
CHANGED
|
@@ -10,7 +10,7 @@ const TOKEN_INDEX = 'token-index';
|
|
|
10
10
|
const USER_INDEX = 'user-page-index';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @typedef {
|
|
13
|
+
* @typedef {object} Token
|
|
14
14
|
* @prop {string} senderId
|
|
15
15
|
* @prop {string} pageId
|
|
16
16
|
* @prop {string} token
|
|
@@ -131,7 +131,7 @@ class BotTokenStorage {
|
|
|
131
131
|
}
|
|
132
132
|
}, {
|
|
133
133
|
upsert: true,
|
|
134
|
-
|
|
134
|
+
returnDocument: 'after'
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
res = res.value;
|
|
@@ -166,7 +166,7 @@ class BotTokenStorage {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
_wait (ms) {
|
|
169
|
-
return new Promise(r => setTimeout(r, ms));
|
|
169
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
}
|