@tiledesk/tiledesk-server 2.27.0 → 2.27.2
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/CHANGELOG.md +7 -0
- package/channels/chat21/chat21Handler.js +569 -873
- package/channels/chat21/package-lock.json +70 -32
- package/channels/chat21/package.json +1 -1
- package/models/chat21SyncTrace.js +53 -0
- package/package.json +1 -1
- package/services/chat21SyncTracer.js +157 -0
- package/services/requestService.js +58 -1
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiledesk/tiledesk-chat21-app",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@tiledesk/tiledesk-chat21-app",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.8",
|
|
10
10
|
"license": "AGPL-3.0",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@chat21/chat21-node-sdk": "^1.1.
|
|
12
|
+
"@chat21/chat21-node-sdk": "^1.1.12",
|
|
13
13
|
"firebase-admin": "^9.5.0",
|
|
14
14
|
"winston": "^3.3.3"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {}
|
|
17
17
|
},
|
|
18
18
|
"node_modules/@chat21/chat21-node-sdk": {
|
|
19
|
-
"version": "1.1.
|
|
20
|
-
"resolved": "https://registry.npmjs.org/@chat21/chat21-node-sdk/-/chat21-node-sdk-1.1.
|
|
21
|
-
"integrity": "sha512-
|
|
19
|
+
"version": "1.1.12",
|
|
20
|
+
"resolved": "https://registry.npmjs.org/@chat21/chat21-node-sdk/-/chat21-node-sdk-1.1.12.tgz",
|
|
21
|
+
"integrity": "sha512-KllkQ4HckOAG/l6xIycCCNgM5Y/fDDfEFbK5eVCCMoBJsS4DGJoRB/Zh1Td792E/tdzvn/skTBpGA9+I/hXqGA==",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"chai": "^4.3.0",
|
|
24
24
|
"dotenv": "^8.2.0",
|
|
@@ -199,15 +199,6 @@
|
|
|
199
199
|
"@firebase/app": "0.x"
|
|
200
200
|
}
|
|
201
201
|
},
|
|
202
|
-
"node_modules/@firebase/auth-interop-types": {
|
|
203
|
-
"version": "0.1.5",
|
|
204
|
-
"resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz",
|
|
205
|
-
"integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==",
|
|
206
|
-
"peerDependencies": {
|
|
207
|
-
"@firebase/app-types": "0.x",
|
|
208
|
-
"@firebase/util": "0.x"
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
202
|
"node_modules/@firebase/auth-types": {
|
|
212
203
|
"version": "0.10.3",
|
|
213
204
|
"resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.10.3.tgz",
|
|
@@ -261,6 +252,23 @@
|
|
|
261
252
|
"@firebase/app-types": "0.6.1"
|
|
262
253
|
}
|
|
263
254
|
},
|
|
255
|
+
"node_modules/@firebase/database/node_modules/@firebase/auth-interop-types": {
|
|
256
|
+
"version": "0.1.5",
|
|
257
|
+
"resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz",
|
|
258
|
+
"integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==",
|
|
259
|
+
"peerDependencies": {
|
|
260
|
+
"@firebase/app-types": "0.x",
|
|
261
|
+
"@firebase/util": "0.x"
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"node_modules/@firebase/database/node_modules/@firebase/util": {
|
|
265
|
+
"version": "0.3.4",
|
|
266
|
+
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.4.tgz",
|
|
267
|
+
"integrity": "sha512-VwjJUE2Vgr2UMfH63ZtIX9Hd7x+6gayi6RUXaTqEYxSbf/JmehLmAEYSuxS/NckfzAXWeGnKclvnXVibDgpjQQ==",
|
|
268
|
+
"dependencies": {
|
|
269
|
+
"tslib": "^1.11.1"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
264
272
|
"node_modules/@firebase/firestore": {
|
|
265
273
|
"version": "2.4.1",
|
|
266
274
|
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-2.4.1.tgz",
|
|
@@ -765,13 +773,24 @@
|
|
|
765
773
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
|
766
774
|
},
|
|
767
775
|
"node_modules/@firebase/util": {
|
|
768
|
-
"version": "
|
|
769
|
-
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-
|
|
770
|
-
"integrity": "sha512-
|
|
776
|
+
"version": "1.15.3",
|
|
777
|
+
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.15.3.tgz",
|
|
778
|
+
"integrity": "sha512-c/z/gaIlaaLZEuGbE6sLUuJ61tskg1JghvhcNQzW948ASBinbVBBRnZTC4b4yt4LaEtJQkYlyzqLHcutFwEIvA==",
|
|
779
|
+
"hasInstallScript": true,
|
|
780
|
+
"peer": true,
|
|
771
781
|
"dependencies": {
|
|
772
|
-
"tslib": "^1.
|
|
782
|
+
"tslib": "^2.1.0"
|
|
783
|
+
},
|
|
784
|
+
"engines": {
|
|
785
|
+
"node": ">=20.0.0"
|
|
773
786
|
}
|
|
774
787
|
},
|
|
788
|
+
"node_modules/@firebase/util/node_modules/tslib": {
|
|
789
|
+
"version": "2.8.1",
|
|
790
|
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
791
|
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
792
|
+
"peer": true
|
|
793
|
+
},
|
|
775
794
|
"node_modules/@firebase/webchannel-wrapper": {
|
|
776
795
|
"version": "0.5.1",
|
|
777
796
|
"resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.5.1.tgz",
|
|
@@ -3645,9 +3664,9 @@
|
|
|
3645
3664
|
},
|
|
3646
3665
|
"dependencies": {
|
|
3647
3666
|
"@chat21/chat21-node-sdk": {
|
|
3648
|
-
"version": "1.1.
|
|
3649
|
-
"resolved": "https://registry.npmjs.org/@chat21/chat21-node-sdk/-/chat21-node-sdk-1.1.
|
|
3650
|
-
"integrity": "sha512-
|
|
3667
|
+
"version": "1.1.12",
|
|
3668
|
+
"resolved": "https://registry.npmjs.org/@chat21/chat21-node-sdk/-/chat21-node-sdk-1.1.12.tgz",
|
|
3669
|
+
"integrity": "sha512-KllkQ4HckOAG/l6xIycCCNgM5Y/fDDfEFbK5eVCCMoBJsS4DGJoRB/Zh1Td792E/tdzvn/skTBpGA9+I/hXqGA==",
|
|
3651
3670
|
"requires": {
|
|
3652
3671
|
"chai": "^4.3.0",
|
|
3653
3672
|
"dotenv": "^8.2.0",
|
|
@@ -3825,12 +3844,6 @@
|
|
|
3825
3844
|
"@firebase/auth-types": "0.10.3"
|
|
3826
3845
|
}
|
|
3827
3846
|
},
|
|
3828
|
-
"@firebase/auth-interop-types": {
|
|
3829
|
-
"version": "0.1.5",
|
|
3830
|
-
"resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz",
|
|
3831
|
-
"integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==",
|
|
3832
|
-
"requires": {}
|
|
3833
|
-
},
|
|
3834
3847
|
"@firebase/auth-types": {
|
|
3835
3848
|
"version": "0.10.3",
|
|
3836
3849
|
"resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.10.3.tgz",
|
|
@@ -3873,6 +3886,22 @@
|
|
|
3873
3886
|
"@firebase/util": "0.3.4",
|
|
3874
3887
|
"faye-websocket": "0.11.3",
|
|
3875
3888
|
"tslib": "^1.11.1"
|
|
3889
|
+
},
|
|
3890
|
+
"dependencies": {
|
|
3891
|
+
"@firebase/auth-interop-types": {
|
|
3892
|
+
"version": "0.1.5",
|
|
3893
|
+
"resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz",
|
|
3894
|
+
"integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==",
|
|
3895
|
+
"requires": {}
|
|
3896
|
+
},
|
|
3897
|
+
"@firebase/util": {
|
|
3898
|
+
"version": "0.3.4",
|
|
3899
|
+
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.4.tgz",
|
|
3900
|
+
"integrity": "sha512-VwjJUE2Vgr2UMfH63ZtIX9Hd7x+6gayi6RUXaTqEYxSbf/JmehLmAEYSuxS/NckfzAXWeGnKclvnXVibDgpjQQ==",
|
|
3901
|
+
"requires": {
|
|
3902
|
+
"tslib": "^1.11.1"
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3876
3905
|
}
|
|
3877
3906
|
},
|
|
3878
3907
|
"@firebase/database-types": {
|
|
@@ -4309,11 +4338,20 @@
|
|
|
4309
4338
|
"requires": {}
|
|
4310
4339
|
},
|
|
4311
4340
|
"@firebase/util": {
|
|
4312
|
-
"version": "
|
|
4313
|
-
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-
|
|
4314
|
-
"integrity": "sha512-
|
|
4341
|
+
"version": "1.15.3",
|
|
4342
|
+
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.15.3.tgz",
|
|
4343
|
+
"integrity": "sha512-c/z/gaIlaaLZEuGbE6sLUuJ61tskg1JghvhcNQzW948ASBinbVBBRnZTC4b4yt4LaEtJQkYlyzqLHcutFwEIvA==",
|
|
4344
|
+
"peer": true,
|
|
4315
4345
|
"requires": {
|
|
4316
|
-
"tslib": "^1.
|
|
4346
|
+
"tslib": "^2.1.0"
|
|
4347
|
+
},
|
|
4348
|
+
"dependencies": {
|
|
4349
|
+
"tslib": {
|
|
4350
|
+
"version": "2.8.1",
|
|
4351
|
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
4352
|
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
4353
|
+
"peer": true
|
|
4354
|
+
}
|
|
4317
4355
|
}
|
|
4318
4356
|
},
|
|
4319
4357
|
"@firebase/webchannel-wrapper": {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
const Schema = mongoose.Schema;
|
|
3
|
+
|
|
4
|
+
const TTL_SECONDS = 60 * 60 * 24 * 7; // 1 week
|
|
5
|
+
|
|
6
|
+
const Chat21SyncTraceSchema = new Schema(
|
|
7
|
+
{
|
|
8
|
+
request_id: {
|
|
9
|
+
type: String,
|
|
10
|
+
required: false
|
|
11
|
+
},
|
|
12
|
+
id_project: {
|
|
13
|
+
type: String,
|
|
14
|
+
required: false
|
|
15
|
+
},
|
|
16
|
+
event: {
|
|
17
|
+
type: String,
|
|
18
|
+
required: true
|
|
19
|
+
},
|
|
20
|
+
step: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
outcome: {
|
|
25
|
+
type: String,
|
|
26
|
+
enum: ['info', 'success', 'error', 'skipped'],
|
|
27
|
+
default: 'info'
|
|
28
|
+
},
|
|
29
|
+
data: {
|
|
30
|
+
type: Schema.Types.Mixed,
|
|
31
|
+
required: false
|
|
32
|
+
},
|
|
33
|
+
host: {
|
|
34
|
+
type: String,
|
|
35
|
+
required: false
|
|
36
|
+
},
|
|
37
|
+
pid: {
|
|
38
|
+
type: Number,
|
|
39
|
+
required: false
|
|
40
|
+
},
|
|
41
|
+
createdAt: {
|
|
42
|
+
type: Date,
|
|
43
|
+
default: Date.now
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
Chat21SyncTraceSchema.index({ request_id: 1, createdAt: 1 });
|
|
49
|
+
Chat21SyncTraceSchema.index({ createdAt: 1 }, { expireAfterSeconds: TTL_SECONDS });
|
|
50
|
+
|
|
51
|
+
const Chat21SyncTrace = mongoose.model('Chat21SyncTrace', Chat21SyncTraceSchema);
|
|
52
|
+
|
|
53
|
+
module.exports = Chat21SyncTrace;
|
package/package.json
CHANGED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const winston = require('../config/winston');
|
|
3
|
+
const Chat21SyncTrace = require('../models/chat21SyncTrace');
|
|
4
|
+
|
|
5
|
+
const MAX_STRING_LENGTH = 2000;
|
|
6
|
+
const enabled = process.env.CHAT21_SYNC_TRACE_ENABLED === "true";
|
|
7
|
+
const host = os.hostname();
|
|
8
|
+
|
|
9
|
+
function positiveNumber(value, defaultValue) {
|
|
10
|
+
const parsed = parseInt(value, 10);
|
|
11
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultValue;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const FLUSH_INTERVAL_MS = positiveNumber(process.env.CHAT21_SYNC_TRACE_FLUSH_INTERVAL_MS, 1000);
|
|
15
|
+
const BATCH_SIZE = positiveNumber(process.env.CHAT21_SYNC_TRACE_BATCH_SIZE, 200);
|
|
16
|
+
const MAX_BUFFER_SIZE = positiveNumber(process.env.CHAT21_SYNC_TRACE_MAX_BUFFER_SIZE, 10000);
|
|
17
|
+
|
|
18
|
+
let buffer = [];
|
|
19
|
+
let flushing = false;
|
|
20
|
+
let dropped = 0;
|
|
21
|
+
|
|
22
|
+
if (enabled) {
|
|
23
|
+
winston.info("[Chat21SyncTracer] Chat21 sync tracing enabled. Flush interval (ms): " + FLUSH_INTERVAL_MS +
|
|
24
|
+
" batch size: " + BATCH_SIZE + " max buffer size: " + MAX_BUFFER_SIZE);
|
|
25
|
+
// The TTL index must exist even when MONGOOSE_AUTOINDEX is false
|
|
26
|
+
Chat21SyncTrace.createIndexes().catch((err) => {
|
|
27
|
+
winston.error("[Chat21SyncTracer] Error creating indexes: ", err);
|
|
28
|
+
});
|
|
29
|
+
setInterval(flush, FLUSH_INTERVAL_MS).unref();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Writes the buffered traces in batches. Only one flush runs at a time: while mongo is slow
|
|
34
|
+
* the buffer grows up to MAX_BUFFER_SIZE, then new traces are dropped.
|
|
35
|
+
* @returns {Promise<void>} never rejects
|
|
36
|
+
*/
|
|
37
|
+
async function flush() {
|
|
38
|
+
if (flushing || buffer.length === 0) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
flushing = true;
|
|
42
|
+
try {
|
|
43
|
+
if (dropped > 0) {
|
|
44
|
+
winston.warn("[Chat21SyncTracer] Buffer full, dropped " + dropped + " traces");
|
|
45
|
+
dropped = 0;
|
|
46
|
+
}
|
|
47
|
+
while (buffer.length > 0) {
|
|
48
|
+
const batch = buffer.splice(0, BATCH_SIZE);
|
|
49
|
+
try {
|
|
50
|
+
await Chat21SyncTrace.insertMany(batch, { ordered: false });
|
|
51
|
+
} catch (err) {
|
|
52
|
+
winston.error("[Chat21SyncTracer] Error saving " + batch.length + " traces: ", err);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} finally {
|
|
56
|
+
flushing = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function truncate(value) {
|
|
61
|
+
if (typeof value !== 'string' || value.length <= MAX_STRING_LENGTH) {
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
return value.substring(0, MAX_STRING_LENGTH) + "...[truncated]";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function describeError(err) {
|
|
68
|
+
if (!err) {
|
|
69
|
+
return err;
|
|
70
|
+
}
|
|
71
|
+
if (err instanceof Error) {
|
|
72
|
+
return { message: err.message, code: err.code, stack: truncate(err.stack) };
|
|
73
|
+
}
|
|
74
|
+
return describeResponse(err);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Serialized as a string: chat21 responses may contain keys that MongoDB does not accept (e.g. "$" or ".")
|
|
78
|
+
function describeResponse(body) {
|
|
79
|
+
if (body === undefined) {
|
|
80
|
+
return "undefined";
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
return truncate(typeof body === 'string' ? body : JSON.stringify(body));
|
|
84
|
+
} catch (err) {
|
|
85
|
+
return truncate(String(body));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function describeRequest(request) {
|
|
90
|
+
if (!request) {
|
|
91
|
+
return request;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
status: request.status,
|
|
95
|
+
participants: Array.isArray(request.participants) ? Array.from(request.participants) : request.participants,
|
|
96
|
+
channelOutbound: request.channelOutbound?.name,
|
|
97
|
+
lead_id: request.lead?.lead_id
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Buffers a trace document, saved by the next flush. Never throws and never blocks the caller.
|
|
103
|
+
* @param {string} event the traced flow, e.g. 'request.close'
|
|
104
|
+
* @param {{request_id: string, id_project: *}} ref
|
|
105
|
+
* @param {string} step
|
|
106
|
+
* @param {'info'|'success'|'error'|'skipped'} outcome
|
|
107
|
+
* @param {object} [data]
|
|
108
|
+
*/
|
|
109
|
+
function trace(event, ref, step, outcome, data) {
|
|
110
|
+
if (!enabled) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (buffer.length >= MAX_BUFFER_SIZE) {
|
|
114
|
+
dropped++;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// createdAt is set here: the document is written later, and the timeline order must reflect the event time
|
|
118
|
+
buffer.push({
|
|
119
|
+
request_id: ref?.request_id,
|
|
120
|
+
id_project: ref?.id_project ? String(ref.id_project) : undefined,
|
|
121
|
+
event: event,
|
|
122
|
+
step: step,
|
|
123
|
+
outcome: outcome,
|
|
124
|
+
data: data,
|
|
125
|
+
host: host,
|
|
126
|
+
pid: process.pid,
|
|
127
|
+
createdAt: new Date()
|
|
128
|
+
});
|
|
129
|
+
if (buffer.length >= BATCH_SIZE) {
|
|
130
|
+
flush();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Traces a chat21 sdk call as <action>.sent, then <action>.response or <action>.error.
|
|
136
|
+
* The returned promise settles exactly like the one returned by callFn.
|
|
137
|
+
*/
|
|
138
|
+
function traceCall(event, ref, action, data, callFn) {
|
|
139
|
+
trace(event, ref, action + '.sent', 'info', data);
|
|
140
|
+
return callFn().then((response) => {
|
|
141
|
+
trace(event, ref, action + '.response', 'success', { ...data, response: describeResponse(response) });
|
|
142
|
+
return response;
|
|
143
|
+
}, (err) => {
|
|
144
|
+
trace(event, ref, action + '.error', 'error', { ...data, error: describeError(err) });
|
|
145
|
+
throw err;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
enabled,
|
|
151
|
+
trace,
|
|
152
|
+
traceCall,
|
|
153
|
+
flush,
|
|
154
|
+
describeError,
|
|
155
|
+
describeResponse,
|
|
156
|
+
describeRequest
|
|
157
|
+
};
|