wingbot-mongodb 2.17.0-alpha.1 → 2.19.1
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/.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 +8 -3
- package/src/AuditLogStorage.js +364 -0
- package/src/BaseStorage.js +63 -17
- package/src/BotConfigStorage.js +32 -11
- package/src/BotTokenStorage.js +1 -1
- package/src/ChatLogStorage.js +59 -28
- package/src/NotificationsStorage.js +7 -7
- package/src/StateStorage.js +1 -1
- package/src/main.js +3 -1
- package/.nyc_output/c50dc91d-0a7c-4ebe-80c8-8786cdcd0d7e.json +0 -1
- package/.nyc_output/processinfo/c50dc91d-0a7c-4ebe-80c8-8786cdcd0d7e.json +0 -1
package/README.md
CHANGED
|
@@ -32,17 +32,17 @@ Contains storage for tokens, chat states, bot config and chat logs.
|
|
|
32
32
|
## Typedefs
|
|
33
33
|
|
|
34
34
|
<dl>
|
|
35
|
-
<dt><a href="#State">State</a> : <code>
|
|
35
|
+
<dt><a href="#State">State</a> : <code>object</code></dt>
|
|
36
36
|
<dd></dd>
|
|
37
|
-
<dt><a href="#StateCondition">StateCondition</a> : <code>
|
|
37
|
+
<dt><a href="#StateCondition">StateCondition</a> : <code>object</code></dt>
|
|
38
38
|
<dd></dd>
|
|
39
|
-
<dt><a href="#Token">Token</a> : <code>
|
|
39
|
+
<dt><a href="#Token">Token</a> : <code>object</code></dt>
|
|
40
40
|
<dd></dd>
|
|
41
41
|
<dt><a href="#Target">Target</a> : <code>Object</code></dt>
|
|
42
42
|
<dd></dd>
|
|
43
43
|
<dt><a href="#Subscribtion">Subscribtion</a> : <code>Object</code></dt>
|
|
44
44
|
<dd></dd>
|
|
45
|
-
<dt><a href="#Campaign">Campaign</a> : <code>
|
|
45
|
+
<dt><a href="#Campaign">Campaign</a> : <code>object</code></dt>
|
|
46
46
|
<dd></dd>
|
|
47
47
|
<dt><a href="#Task">Task</a> : <code>Object</code></dt>
|
|
48
48
|
<dd></dd>
|
|
@@ -57,13 +57,11 @@ Storage for chat states
|
|
|
57
57
|
|
|
58
58
|
* [StateStorage](#StateStorage)
|
|
59
59
|
* [new StateStorage(mongoDb, collectionName, [log], isCosmo)](#new_StateStorage_new)
|
|
60
|
-
* [.
|
|
61
|
-
* [.addCustomIndex(index, options)](#StateStorage+addCustomIndex)
|
|
62
|
-
* [._getCollection()](#StateStorage+_getCollection) ⇒ <code>Promise.<mongodb.Collection></code>
|
|
60
|
+
* ~~[.addCustomIndex(index, options)](#StateStorage+addCustomIndex)~~
|
|
63
61
|
* [.getState(senderId, pageId)](#StateStorage+getState) ⇒ <code>Promise.<(State\|null)></code>
|
|
64
|
-
* [.getOrCreateAndLock(senderId, pageId, [defaultState], [timeout])](#StateStorage+getOrCreateAndLock) ⇒ <code>Promise.<
|
|
62
|
+
* [.getOrCreateAndLock(senderId, pageId, [defaultState], [timeout])](#StateStorage+getOrCreateAndLock) ⇒ <code>Promise.<object></code>
|
|
65
63
|
* [.getStates(condition, limit, lastKey)](#StateStorage+getStates) ⇒ <code>Promise.<{Array.<data:State>, lastKey:string}></code>
|
|
66
|
-
* [.saveState(state)](#StateStorage+saveState) ⇒ <code>Promise.<
|
|
64
|
+
* [.saveState(state)](#StateStorage+saveState) ⇒ <code>Promise.<object></code>
|
|
67
65
|
|
|
68
66
|
<a name="new_StateStorage_new"></a>
|
|
69
67
|
|
|
@@ -72,31 +70,25 @@ Storage for chat states
|
|
|
72
70
|
| Param | Type | Default | Description |
|
|
73
71
|
| --- | --- | --- | --- |
|
|
74
72
|
| mongoDb | <code>mongodb.Db</code> \| <code>Object</code> | | |
|
|
75
|
-
| collectionName | <code>string</code> | <code>"
|
|
73
|
+
| collectionName | <code>string</code> | <code>"chatlogs"</code> | |
|
|
76
74
|
| [log] | <code>Object</code> | | console like logger |
|
|
77
75
|
| isCosmo | <code>boolean</code> | <code>false</code> | |
|
|
78
76
|
|
|
79
|
-
<a name="StateStorage+_collection"></a>
|
|
80
|
-
|
|
81
|
-
### stateStorage.\_collection : <code>Promise.<mongodb.Collection></code>
|
|
82
|
-
**Kind**: instance property of [<code>StateStorage</code>](#StateStorage)
|
|
83
77
|
<a name="StateStorage+addCustomIndex"></a>
|
|
84
78
|
|
|
85
|
-
### stateStorage.addCustomIndex(index, options)
|
|
79
|
+
### ~~stateStorage.addCustomIndex(index, options)~~
|
|
80
|
+
***Deprecated***
|
|
81
|
+
|
|
86
82
|
Add custom indexing rule
|
|
87
83
|
|
|
88
84
|
**Kind**: instance method of [<code>StateStorage</code>](#StateStorage)
|
|
89
85
|
|
|
90
86
|
| Param | Type |
|
|
91
87
|
| --- | --- |
|
|
92
|
-
| index | <code>
|
|
93
|
-
| options | <code>
|
|
88
|
+
| index | <code>object</code> |
|
|
89
|
+
| options | <code>object</code> |
|
|
94
90
|
| options.name | <code>string</code> |
|
|
95
91
|
|
|
96
|
-
<a name="StateStorage+_getCollection"></a>
|
|
97
|
-
|
|
98
|
-
### stateStorage.\_getCollection() ⇒ <code>Promise.<mongodb.Collection></code>
|
|
99
|
-
**Kind**: instance method of [<code>StateStorage</code>](#StateStorage)
|
|
100
92
|
<a name="StateStorage+getState"></a>
|
|
101
93
|
|
|
102
94
|
### stateStorage.getState(senderId, pageId) ⇒ <code>Promise.<(State\|null)></code>
|
|
@@ -109,17 +101,17 @@ Add custom indexing rule
|
|
|
109
101
|
|
|
110
102
|
<a name="StateStorage+getOrCreateAndLock"></a>
|
|
111
103
|
|
|
112
|
-
### stateStorage.getOrCreateAndLock(senderId, pageId, [defaultState], [timeout]) ⇒ <code>Promise.<
|
|
104
|
+
### stateStorage.getOrCreateAndLock(senderId, pageId, [defaultState], [timeout]) ⇒ <code>Promise.<object></code>
|
|
113
105
|
Load state from database and lock it to prevent another reads
|
|
114
106
|
|
|
115
107
|
**Kind**: instance method of [<code>StateStorage</code>](#StateStorage)
|
|
116
|
-
**Returns**: <code>Promise.<
|
|
108
|
+
**Returns**: <code>Promise.<object></code> - - conversation state
|
|
117
109
|
|
|
118
110
|
| Param | Type | Default | Description |
|
|
119
111
|
| --- | --- | --- | --- |
|
|
120
112
|
| senderId | <code>string</code> | | sender identifier |
|
|
121
113
|
| pageId | <code>string</code> | | page identifier |
|
|
122
|
-
| [defaultState] | <code>
|
|
114
|
+
| [defaultState] | <code>object</code> | | default state of the conversation |
|
|
123
115
|
| [timeout] | <code>number</code> | <code>300</code> | given default state |
|
|
124
116
|
|
|
125
117
|
<a name="StateStorage+getStates"></a>
|
|
@@ -135,14 +127,14 @@ Load state from database and lock it to prevent another reads
|
|
|
135
127
|
|
|
136
128
|
<a name="StateStorage+saveState"></a>
|
|
137
129
|
|
|
138
|
-
### stateStorage.saveState(state) ⇒ <code>Promise.<
|
|
130
|
+
### stateStorage.saveState(state) ⇒ <code>Promise.<object></code>
|
|
139
131
|
Save the state to database
|
|
140
132
|
|
|
141
133
|
**Kind**: instance method of [<code>StateStorage</code>](#StateStorage)
|
|
142
134
|
|
|
143
135
|
| Param | Type | Description |
|
|
144
136
|
| --- | --- | --- |
|
|
145
|
-
| state | <code>
|
|
137
|
+
| state | <code>object</code> | conversation state |
|
|
146
138
|
|
|
147
139
|
<a name="BotTokenStorage"></a>
|
|
148
140
|
|
|
@@ -244,9 +236,9 @@ Log single event
|
|
|
244
236
|
| Param | Type | Description |
|
|
245
237
|
| --- | --- | --- |
|
|
246
238
|
| senderId | <code>string</code> | |
|
|
247
|
-
| responses | <code>Array.<
|
|
248
|
-
| request | <code>
|
|
249
|
-
| [metadata] | <code>
|
|
239
|
+
| responses | <code>Array.<object></code> | list of sent responses |
|
|
240
|
+
| request | <code>object</code> | event request |
|
|
241
|
+
| [metadata] | <code>object</code> | request metadata |
|
|
250
242
|
|
|
251
243
|
<a name="BotConfigStorage"></a>
|
|
252
244
|
|
|
@@ -262,8 +254,9 @@ Storage for wingbot.ai conversation config
|
|
|
262
254
|
* [.api([onUpdate], [acl])](#BotConfigStorage+api) ⇒ <code>Object</code>
|
|
263
255
|
* [.invalidateConfig()](#BotConfigStorage+invalidateConfig) ⇒ <code>Promise</code>
|
|
264
256
|
* [.getConfigTimestamp()](#BotConfigStorage+getConfigTimestamp) ⇒ <code>Promise.<number></code>
|
|
265
|
-
* [.updateConfig(newConfig)](#BotConfigStorage+updateConfig) ⇒ <code>Promise.<T></code>
|
|
266
|
-
* [.
|
|
257
|
+
* [.updateConfig(newConfig, [id])](#BotConfigStorage+updateConfig) ⇒ <code>Promise.<T></code>
|
|
258
|
+
* [.setConfig(id, newConfig)](#BotConfigStorage+setConfig)
|
|
259
|
+
* [.getConfig([id])](#BotConfigStorage+getConfig) ⇒ <code>Promise.<(object\|null)></code>
|
|
267
260
|
|
|
268
261
|
<a name="new_BotConfigStorage_new"></a>
|
|
269
262
|
|
|
@@ -306,17 +299,33 @@ Invalidates current configuration
|
|
|
306
299
|
**Kind**: instance method of [<code>BotConfigStorage</code>](#BotConfigStorage)
|
|
307
300
|
<a name="BotConfigStorage+updateConfig"></a>
|
|
308
301
|
|
|
309
|
-
### botConfigStorage.updateConfig(newConfig) ⇒ <code>Promise.<T></code>
|
|
302
|
+
### botConfigStorage.updateConfig(newConfig, [id]) ⇒ <code>Promise.<T></code>
|
|
310
303
|
**Kind**: instance method of [<code>BotConfigStorage</code>](#BotConfigStorage)
|
|
311
304
|
|
|
312
305
|
| Param | Type |
|
|
313
306
|
| --- | --- |
|
|
314
307
|
| newConfig | <code>T</code> |
|
|
308
|
+
| [id] | <code>string</code> |
|
|
309
|
+
|
|
310
|
+
<a name="BotConfigStorage+setConfig"></a>
|
|
311
|
+
|
|
312
|
+
### botConfigStorage.setConfig(id, newConfig)
|
|
313
|
+
**Kind**: instance method of [<code>BotConfigStorage</code>](#BotConfigStorage)
|
|
314
|
+
|
|
315
|
+
| Param | Type |
|
|
316
|
+
| --- | --- |
|
|
317
|
+
| id | <code>string</code> |
|
|
318
|
+
| newConfig | <code>object</code> |
|
|
315
319
|
|
|
316
320
|
<a name="BotConfigStorage+getConfig"></a>
|
|
317
321
|
|
|
318
|
-
### botConfigStorage.getConfig() ⇒ <code>Promise.<(
|
|
322
|
+
### botConfigStorage.getConfig([id]) ⇒ <code>Promise.<(object\|null)></code>
|
|
319
323
|
**Kind**: instance method of [<code>BotConfigStorage</code>](#BotConfigStorage)
|
|
324
|
+
|
|
325
|
+
| Param | Type |
|
|
326
|
+
| --- | --- |
|
|
327
|
+
| [id] | <code>string</code> |
|
|
328
|
+
|
|
320
329
|
<a name="AttachmentCache"></a>
|
|
321
330
|
|
|
322
331
|
## AttachmentCache
|
|
@@ -428,7 +437,7 @@ Cache storage for Facebook attachments
|
|
|
428
437
|
|
|
429
438
|
| Param | Type |
|
|
430
439
|
| --- | --- |
|
|
431
|
-
| tasks | <code>
|
|
440
|
+
| tasks | <code>object</code> |
|
|
432
441
|
|
|
433
442
|
<a name="NotificationsStorage+getUnsuccessfulSubscribersByCampaign"></a>
|
|
434
443
|
|
|
@@ -460,7 +469,7 @@ Return Task By Id
|
|
|
460
469
|
| Param | Type |
|
|
461
470
|
| --- | --- |
|
|
462
471
|
| taskId | <code>string</code> |
|
|
463
|
-
| data | <code>
|
|
472
|
+
| data | <code>object</code> |
|
|
464
473
|
|
|
465
474
|
<a name="NotificationsStorage+getSentTask"></a>
|
|
466
475
|
|
|
@@ -506,8 +515,8 @@ Get last sent task from campaign
|
|
|
506
515
|
|
|
507
516
|
| Param | Type | Default |
|
|
508
517
|
| --- | --- | --- |
|
|
509
|
-
| campaign | <code>
|
|
510
|
-
| [updateCampaign] | <code>
|
|
518
|
+
| campaign | <code>object</code> | |
|
|
519
|
+
| [updateCampaign] | <code>object</code> | <code></code> |
|
|
511
520
|
|
|
512
521
|
<a name="NotificationsStorage+removeCampaign"></a>
|
|
513
522
|
|
|
@@ -526,7 +535,7 @@ Get last sent task from campaign
|
|
|
526
535
|
| Param | Type |
|
|
527
536
|
| --- | --- |
|
|
528
537
|
| campaignId | <code>string</code> |
|
|
529
|
-
| increment | <code>
|
|
538
|
+
| increment | <code>object</code> |
|
|
530
539
|
|
|
531
540
|
<a name="NotificationsStorage+updateCampaign"></a>
|
|
532
541
|
|
|
@@ -536,7 +545,7 @@ Get last sent task from campaign
|
|
|
536
545
|
| Param | Type |
|
|
537
546
|
| --- | --- |
|
|
538
547
|
| campaignId | <code>string</code> |
|
|
539
|
-
| data | <code>
|
|
548
|
+
| data | <code>object</code> |
|
|
540
549
|
|
|
541
550
|
<a name="NotificationsStorage+popCampaign"></a>
|
|
542
551
|
|
|
@@ -572,9 +581,9 @@ Get last sent task from campaign
|
|
|
572
581
|
|
|
573
582
|
| Param | Type | Default |
|
|
574
583
|
| --- | --- | --- |
|
|
575
|
-
| condition | <code>
|
|
584
|
+
| condition | <code>object</code> | |
|
|
576
585
|
| [limit] | <code>number</code> | <code></code> |
|
|
577
|
-
| [lastKey] | <code>
|
|
586
|
+
| [lastKey] | <code>object</code> | <code></code> |
|
|
578
587
|
|
|
579
588
|
<a name="NotificationsStorage+subscribe"></a>
|
|
580
589
|
|
|
@@ -692,9 +701,8 @@ Add custom indexing rule
|
|
|
692
701
|
|
|
693
702
|
| Param | Type |
|
|
694
703
|
| --- | --- |
|
|
695
|
-
| index | <code>
|
|
696
|
-
| options | <code>
|
|
697
|
-
| options.name | <code>string</code> |
|
|
704
|
+
| index | <code>object</code> |
|
|
705
|
+
| options | <code>mongodb.IndexOptions</code> |
|
|
698
706
|
|
|
699
707
|
<a name="BaseStorage+_getCollection"></a>
|
|
700
708
|
|
|
@@ -704,7 +712,7 @@ Returns the collection to operate with
|
|
|
704
712
|
**Kind**: instance method of [<code>BaseStorage</code>](#BaseStorage)
|
|
705
713
|
<a name="State"></a>
|
|
706
714
|
|
|
707
|
-
## State : <code>
|
|
715
|
+
## State : <code>object</code>
|
|
708
716
|
**Kind**: global typedef
|
|
709
717
|
**Properties**
|
|
710
718
|
|
|
@@ -712,11 +720,11 @@ Returns the collection to operate with
|
|
|
712
720
|
| --- | --- |
|
|
713
721
|
| senderId | <code>string</code> |
|
|
714
722
|
| pageId | <code>string</code> |
|
|
715
|
-
| state | <code>
|
|
723
|
+
| state | <code>object</code> |
|
|
716
724
|
|
|
717
725
|
<a name="StateCondition"></a>
|
|
718
726
|
|
|
719
|
-
## StateCondition : <code>
|
|
727
|
+
## StateCondition : <code>object</code>
|
|
720
728
|
**Kind**: global typedef
|
|
721
729
|
**Properties**
|
|
722
730
|
|
|
@@ -726,7 +734,7 @@ Returns the collection to operate with
|
|
|
726
734
|
|
|
727
735
|
<a name="Token"></a>
|
|
728
736
|
|
|
729
|
-
## Token : <code>
|
|
737
|
+
## Token : <code>object</code>
|
|
730
738
|
**Kind**: global typedef
|
|
731
739
|
**Properties**
|
|
732
740
|
|
|
@@ -761,7 +769,7 @@ Returns the collection to operate with
|
|
|
761
769
|
|
|
762
770
|
<a name="Campaign"></a>
|
|
763
771
|
|
|
764
|
-
## Campaign : <code>
|
|
772
|
+
## Campaign : <code>object</code>
|
|
765
773
|
**Kind**: global typedef
|
|
766
774
|
**Properties**
|
|
767
775
|
|
|
@@ -781,7 +789,7 @@ Returns the collection to operate with
|
|
|
781
789
|
| leaved | <code>number</code> | |
|
|
782
790
|
| queued | <code>number</code> | Interaction |
|
|
783
791
|
| action | <code>string</code> | |
|
|
784
|
-
| [data] | <code>
|
|
792
|
+
| [data] | <code>object</code> | Setup |
|
|
785
793
|
| sliding | <code>boolean</code> | |
|
|
786
794
|
| slide | <code>number</code> | |
|
|
787
795
|
| slideRound | <code>number</code> | |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wingbot-mongodb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.1",
|
|
4
4
|
"description": "MongoDB storage for wingbot.ai",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -46,10 +46,15 @@
|
|
|
46
46
|
"mocha": "^9.1.3",
|
|
47
47
|
"mongodb": "^3.7.3",
|
|
48
48
|
"nyc": "^15.1.0",
|
|
49
|
-
"wingbot": "^3.
|
|
49
|
+
"wingbot": "^3.25.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"mongodb": "^3.0.0"
|
|
52
|
+
"mongodb": "^3.0.0"
|
|
53
|
+
},
|
|
54
|
+
"optionalDependencies": {
|
|
53
55
|
"wingbot": "^3.0.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"jsonwebtoken": "^8.5.1"
|
|
54
59
|
}
|
|
55
60
|
}
|
|
@@ -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;
|