@manyos/smileconnect-api 1.28.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.
Files changed (141) hide show
  1. package/.github/workflows/nodejs.yml +26 -0
  2. package/CHANGELOG.md +75 -0
  3. package/Dockerfile +21 -0
  4. package/README.md +1 -0
  5. package/app.js +316 -0
  6. package/conf/clients.json +2491 -0
  7. package/conf/mapping.json +1048 -0
  8. package/conf/scripts/p1.js +11 -0
  9. package/conf/scripts/p2.js +1 -0
  10. package/conf/scripts/p3.js +1 -0
  11. package/conf/scripts/p4.js +2 -0
  12. package/conf/scripts/script1.js +2 -0
  13. package/conf/scripts/script2.js +2 -0
  14. package/conf/scripts/script3.js +2 -0
  15. package/controller/cmdbobjectController.js +291 -0
  16. package/controller/eventLogController.js +78 -0
  17. package/controller/orgdataController.js +440 -0
  18. package/controller/relatedObjectsController.js +194 -0
  19. package/controller/scriptController.js +213 -0
  20. package/controller/taskController.js +368 -0
  21. package/controller/templateController.js +97 -0
  22. package/controller/ticketCIRelationController.js +522 -0
  23. package/controller/ticketController.js +329 -0
  24. package/controller/ticketWorkLogController.js +195 -0
  25. package/docs/.gitattributes +48 -0
  26. package/docs/404.html +13 -0
  27. package/docs/CNAME +1 -0
  28. package/docs/Gemfile +7 -0
  29. package/docs/Gemfile.lock +249 -0
  30. package/docs/_config.yml +257 -0
  31. package/docs/_data/SocialNetworks.yml +92 -0
  32. package/docs/_data/ui-text.yml +494 -0
  33. package/docs/_includes/disqus.html +17 -0
  34. package/docs/_includes/ext-css.html +7 -0
  35. package/docs/_includes/ext-js.html +7 -0
  36. package/docs/_includes/fb-comment.html +14 -0
  37. package/docs/_includes/footer-minimal.html +16 -0
  38. package/docs/_includes/footer-scripts.html +32 -0
  39. package/docs/_includes/footer.html +51 -0
  40. package/docs/_includes/google_analytics.html +14 -0
  41. package/docs/_includes/gtag.html +11 -0
  42. package/docs/_includes/gtm_body.html +6 -0
  43. package/docs/_includes/gtm_head.html +9 -0
  44. package/docs/_includes/head.html +131 -0
  45. package/docs/_includes/header.html +76 -0
  46. package/docs/_includes/just_comments.html +4 -0
  47. package/docs/_includes/matomo.html +17 -0
  48. package/docs/_includes/nav.html +57 -0
  49. package/docs/_includes/social-share.html +42 -0
  50. package/docs/_includes/staticman-comment.html +22 -0
  51. package/docs/_includes/staticman-comments.html +81 -0
  52. package/docs/_layouts/base.html +35 -0
  53. package/docs/_layouts/default.html +9 -0
  54. package/docs/_layouts/minimal.html +26 -0
  55. package/docs/_layouts/page.html +26 -0
  56. package/docs/_layouts/post.html +82 -0
  57. package/docs/_posts/2015-02-28-test-markdown.md +77 -0
  58. package/docs/aboutme.md +18 -0
  59. package/docs/css/bootstrap-social.css +147 -0
  60. package/docs/css/bootstrap-theme.css +476 -0
  61. package/docs/css/bootstrap-theme.css.map +1 -0
  62. package/docs/css/bootstrap-theme.min.css +5 -0
  63. package/docs/css/bootstrap.css +6566 -0
  64. package/docs/css/bootstrap.css.map +1 -0
  65. package/docs/css/bootstrap.min.css +5 -0
  66. package/docs/css/main-minimal.css +13 -0
  67. package/docs/css/main.css +788 -0
  68. package/docs/css/normalize.css +427 -0
  69. package/docs/css/pygment_highlights.css +61 -0
  70. package/docs/css/staticman.css +180 -0
  71. package/docs/eventlog/events.md +65 -0
  72. package/docs/feed.xml +24 -0
  73. package/docs/general/architecture.md +10 -0
  74. package/docs/general/config.md +192 -0
  75. package/docs/general/field-management.md +119 -0
  76. package/docs/general/release-notes.md +9 -0
  77. package/docs/getting-started.md +19 -0
  78. package/docs/howto/cmdbobjects.md +339 -0
  79. package/docs/howto/incident-worklogs.md +186 -0
  80. package/docs/howto/incidents.md +244 -0
  81. package/docs/howto/sample-config.md +518 -0
  82. package/docs/howto/token.md +71 -0
  83. package/docs/howto/worklog-attachment.md +113 -0
  84. package/docs/img/404-southpark.jpg +0 -0
  85. package/docs/img/architecture.jpeg +0 -0
  86. package/docs/img/attachment-upload.png +0 -0
  87. package/docs/img/avatar-icon.png +0 -0
  88. package/docs/img/bgimage.png +0 -0
  89. package/docs/img/gb-isapi.jpg +0 -0
  90. package/docs/img/install-steps.gif +0 -0
  91. package/docs/img/workflow.png +0 -0
  92. package/docs/index.md +41 -0
  93. package/docs/installation.md +123 -0
  94. package/docs/js/bootstrap.js +2306 -0
  95. package/docs/js/bootstrap.min.js +7 -0
  96. package/docs/js/jquery-1.11.2.min.js +4 -0
  97. package/docs/js/main.js +140 -0
  98. package/docs/js/staticman.js +54 -0
  99. package/docs/openapi.json +15097 -0
  100. package/docs/postinstall.md +169 -0
  101. package/docs/preinstall.md +19 -0
  102. package/docs/spec/index.html +24 -0
  103. package/docs/staticman.yml +110 -0
  104. package/docs/tags.html +34 -0
  105. package/docs/workflow.md +127 -0
  106. package/nodemon.json +3 -0
  107. package/package.json +46 -0
  108. package/routes/appConfigRoutes.js +352 -0
  109. package/routes/ciRelationRoutes.js +38 -0
  110. package/routes/cmdbObjectRoutes.js +154 -0
  111. package/routes/organisationRoutes.js +121 -0
  112. package/routes/peopleRelationRoutes.js +38 -0
  113. package/routes/personRoutes.js +131 -0
  114. package/routes/supportgroupRoutes.js +122 -0
  115. package/routes/taskRoutes.js +306 -0
  116. package/routes/templateRoutes.js +67 -0
  117. package/routes/ticketRoutes.js +181 -0
  118. package/routes/ticketWorkLogRoutes.js +185 -0
  119. package/screwdriver.yaml +52 -0
  120. package/test/appTest.js +3 -0
  121. package/test/changeTest.js +541 -0
  122. package/test/cmdbobjectTest.js +167 -0
  123. package/test/files/logo.png +0 -0
  124. package/test/incidentTest.js +539 -0
  125. package/test/orgdataTest.js +156 -0
  126. package/test/problemTest.js +512 -0
  127. package/test/templateTest.js +80 -0
  128. package/test/testUtils.js +21 -0
  129. package/test/workorderTest.js +544 -0
  130. package/util/arquery.js +416 -0
  131. package/util/auth.js +37 -0
  132. package/util/cache.service.js +52 -0
  133. package/util/config.js +361 -0
  134. package/util/constants.js +73 -0
  135. package/util/mappingUtil.js +96 -0
  136. package/util/paramHelper.js +43 -0
  137. package/util/relationUtil.js +63 -0
  138. package/util/responsehandler.js +92 -0
  139. package/util/schemas/clientConfigSchema.js +180 -0
  140. package/util/schemas/fieldMappingSchema.js +211 -0
  141. package/util/searchUtil.js +148 -0
@@ -0,0 +1,416 @@
1
+ require('dotenv').config();
2
+ const request = require('request-promise-native');
3
+ const path = require('path');
4
+ const log = require('@manyos/logger').setupLog('SMILEconnect_' + path.basename(__filename));
5
+
6
+ async function deleteEntries(form, baseQuery, query) {
7
+ let fullQuery = query;
8
+ if (baseQuery)
9
+ fullQuery = baseQuery + " AND " + query;
10
+ fullQuery = encodeURIComponent(fullQuery);
11
+ let port = 0;
12
+
13
+ if (process.env.AR_PORT && process.env.AR_PORT != undefined)
14
+ port = process.env.AR_PORT;
15
+
16
+ let uri = process.env.BASEURL
17
+ + "/" + process.env.AR_SERVER
18
+ + "/" + form
19
+ + "/" + fullQuery
20
+ + "?port=" + port;
21
+
22
+ log.debug('start delete on:', uri);
23
+
24
+ let options = {
25
+ method: 'DELETE',
26
+ uri: uri,
27
+ json: true // Automatically stringifies the body to JSON
28
+ };
29
+
30
+ const value = await request(options).auth(process.env.AR_USER, process.env.AR_PASSWORD, true);
31
+ log.debug('request done', uri, value);
32
+
33
+ if (value && value.status && value.status === 'error') {
34
+ throw new Error(value);
35
+ }
36
+ return value;
37
+ }
38
+
39
+ async function executeARQuery(form, baseQuery, query, fields, options) {
40
+ let fullQuery = query;
41
+ if (baseQuery)
42
+ fullQuery = "(" + baseQuery + ") AND " + query;
43
+ fullQuery = encodeURIComponent(fullQuery);
44
+ let port = 0;
45
+
46
+ log.debug('Use options for query', options);
47
+
48
+ const limits = {};
49
+
50
+ limits.globalDefault = Number.parseInt(process.env.LIMIT_DEFAULT);
51
+ limits.globalMax = Number.parseInt(process.env.LIMIT_MAX);
52
+ if (options) {
53
+ limits.clientMax = Number.parseInt(options.clientLimit);
54
+ limits.clientRequested = Number.parseInt(options.limit);
55
+ }
56
+ const limit = getLimits(limits);
57
+
58
+ if (process.env.AR_PORT && process.env.AR_PORT != undefined)
59
+ port = process.env.AR_PORT;
60
+
61
+ let uri = process.env.BASEURL
62
+ + "/" + process.env.AR_SERVER
63
+ + "/" + form
64
+ + "/" + fullQuery
65
+ + "?port=" + port
66
+ + "&fields=" + fields;
67
+
68
+ if (options != null && options != undefined) {
69
+ if (options.dateFormat != null && options.dateFormat != undefined) {
70
+ uri = uri + "&dateFormat=" + options.dateFormat;
71
+ }
72
+ if (options.offset != null && options.offset != undefined) {
73
+ uri = uri + "&firstEntry=" + options.offset;
74
+ }
75
+
76
+ if (options.sort != null && options.sort != undefined) {
77
+ const sortString = prepareSortString(options.sort);
78
+ if (sortString != null && sortString != undefined) {
79
+ uri = uri + "&sort=" + sortString;
80
+ }
81
+ }
82
+
83
+ if (options.impersonateUser) {
84
+ uri = uri + "&impersonateUser=" + options.impersonateUser;
85
+ }
86
+
87
+ if (options.translateSelectionFields !== undefined && options.translateSelectionFields === false) {
88
+ uri = uri + "&translateSelectionFields=false";
89
+ }
90
+ }
91
+ //Use limit
92
+ if (limit) {
93
+ uri = uri + "&maxEntries=" + limit;
94
+ }
95
+ log.debug('start request on:', uri);
96
+
97
+ const value = await request(uri).auth(process.env.AR_USER, process.env.AR_PASSWORD, true);
98
+ log.debug('request done', uri, value);
99
+
100
+ if (value && value.status && value.status === 'error') {
101
+ throw new Error(value);
102
+ }
103
+ const result = JSON.parse(value);
104
+ if (result.status && result.status === 'success') {
105
+ result.data = result.data.map(x => x.values);
106
+ }
107
+ return result;
108
+ }
109
+
110
+ async function getMetaData(form) {
111
+ let port = 0;
112
+
113
+ if (process.env.AR_PORT && process.env.AR_PORT != undefined)
114
+ port = process.env.AR_PORT;
115
+
116
+ let uri = process.env.BASEURL
117
+ + "/" + process.env.AR_SERVER;
118
+
119
+ if (form != null && form != undefined) {
120
+ uri = uri + '/' + form;
121
+ }
122
+
123
+ log.debug('start metadata request on:', uri);
124
+
125
+ const value = await request(uri).auth(process.env.AR_USER, process.env.AR_PASSWORD, true);
126
+ log.debug('request done', uri, value);
127
+
128
+ if (value && value.status && value.status === 'error') {
129
+ throw new Error(value);
130
+ }
131
+ const result = JSON.parse(value);
132
+ if (result.status && result.status === 'success') {
133
+ if (result.forms) {
134
+ return result.forms;
135
+ } else if (result.fields) {
136
+ return result.fields;
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+
142
+ function getLimits(limits) {
143
+ log.debug('Defined limits', limits);
144
+ const max = limits.clientMax || limits.globalMax;
145
+ const requested = limits.clientRequested || limits.globalDefault;
146
+ log.debug('max', max);
147
+ log.debug('requested', requested);
148
+ let limit = undefined;
149
+ if (max && requested) {
150
+ if (requested > max) {
151
+ limit = max;
152
+ } else {
153
+ limit = requested;
154
+ }
155
+ } else if (requested) {
156
+ limit = requested;
157
+ } else if (max) {
158
+ limit = max;
159
+ } else {
160
+ limit = undefined;
161
+ }
162
+ log.debug('Use limit', limit);
163
+ return limit;
164
+ }
165
+
166
+ function prepareSortString(sortOptions) {
167
+ log.debug('prepare sortString', sortOptions);
168
+ const sortArray = [];
169
+ Object.keys(sortOptions).forEach(sortKey => {
170
+ if (sortOptions[sortKey] === -1) {
171
+ sortArray.push('-'+sortKey);
172
+ } else {
173
+ sortArray.push(sortKey);
174
+ }
175
+ });
176
+ log.debug('sortString', sortArray.toString());
177
+ return sortArray.toString();
178
+ }
179
+
180
+ //todo add impersonate
181
+ function createEntry(form, entryData, clientOptions) {
182
+ let port = 0;
183
+ if (process.env.AR_PORT && process.env.AR_PORT != undefined)
184
+ port = process.env.AR_PORT;
185
+ //Apply mapping
186
+ log.debug('Data to create new entry', entryData);
187
+ let entries = [];
188
+ if (!Array.isArray(entryData)) {
189
+ entries.push(entryData);
190
+ } else {
191
+ entries = entryData;
192
+ }
193
+
194
+ const finalEntries = {};
195
+ let i = 0;
196
+ for (i=0;i<entries.length;i++) {
197
+ finalEntries[i] = entries[i];
198
+ }
199
+ log.debug('New Entry', finalEntries);
200
+ //log.debug('object', Object.keys(entryData));
201
+ return new Promise((resolve, reject) => {
202
+ let uri = process.env.BASEURL
203
+ + "/" + process.env.AR_SERVER
204
+ + "/" + form
205
+ + "?port=" + port;
206
+
207
+ if (clientOptions && clientOptions.impersonateUser) {
208
+ uri = uri + "&impersonateUser=" + clientOptions.impersonateUser;
209
+ }
210
+
211
+ let options = {
212
+ method: 'POST',
213
+ uri: uri,
214
+ body: finalEntries,
215
+ timeout: 6000000, // 10 min.
216
+ json: true // Automatically stringifies the body to JSON
217
+ };
218
+
219
+ log.debug('start request with:', options.body);
220
+ log.debug('start request on:', uri);
221
+ const singleRecord = Object.keys(finalEntries).length == 1;
222
+ request(options).auth(process.env.AR_USER, process.env.AR_PASSWORD, true)
223
+ .then(function (response) {
224
+ log.debug('RAPI return', response[0]);
225
+ //check for error on single record
226
+ if (singleRecord) {
227
+ if (response[0].startsWith('null - ')) {
228
+ reject(response[0]);
229
+ }
230
+ }
231
+ resolve(response);
232
+
233
+ }).catch(function (error) {
234
+ log.error(error);
235
+ reject(error);
236
+ });
237
+ });
238
+ }
239
+
240
+ function setAttachment(form, entryId, attachmentfield, fileData) {
241
+ //log.debug ('file', fileData);
242
+ let port = 0;
243
+ if (process.env.AR_PORT && process.env.AR_PORT != undefined)
244
+ port = process.env.AR_PORT;
245
+
246
+ return new Promise((resolve, reject) => {
247
+ const uri = process.env.BASEURL
248
+ + "/" + process.env.AR_SERVER
249
+ + "/" + form
250
+ + "/setAttachment"
251
+ + "/" + entryId
252
+ + "/" + attachmentfield
253
+ + "?port=" + port;
254
+
255
+ const myFormData = {};
256
+ myFormData[fileData.name] = {
257
+ value: fileData.data,
258
+ options: {
259
+ filename: fileData.name,
260
+ contentType: fileData.mimetype
261
+ }
262
+ };
263
+
264
+
265
+ var options = {
266
+ method: 'POST',
267
+ uri: uri,
268
+ timeout: 600000, // 10 min.
269
+ formData: myFormData
270
+ };
271
+
272
+ log.debug('start attachment request on:', uri);
273
+ request(options).auth(process.env.AR_USER, process.env.AR_PASSWORD, true)
274
+ .then(function (response) {
275
+ log.debug('RAPI return', response);
276
+ //TODO check for status and reject if Error -> Achtug array
277
+ resolve(response);
278
+ })
279
+ .catch(function (error) {
280
+ log.error(error);
281
+ reject(error);
282
+ });
283
+ });
284
+ }
285
+
286
+ function getAttachment(form, entryId, attachmentfield) {
287
+ //log.debug ('file', fileData);
288
+ let port = 0;
289
+ if (process.env.AR_PORT && process.env.AR_PORT != undefined)
290
+ port = process.env.AR_PORT;
291
+
292
+ return new Promise((resolve, reject) => {
293
+ const uri = process.env.BASEURL
294
+ + "/" + process.env.AR_SERVER
295
+ + "/" + form
296
+ + "/getAttachment"
297
+ + "/" + entryId
298
+ + "/" + attachmentfield
299
+ + "?port=" + port;
300
+
301
+ var options = {
302
+ method: 'Get',
303
+ uri: uri,
304
+ encoding: null,
305
+ resolveWithFullResponse: true,
306
+ timeout: 600000 // 10 min.
307
+ };
308
+
309
+ log.debug('start attachment request on:', uri);
310
+ request(options).auth(process.env.AR_USER, process.env.AR_PASSWORD, true)
311
+ .then(function (response) {
312
+ log.debug('RAPI return', response.headers);
313
+ const fileName = response.headers['content-disposition'].split(';')[1].split('=')[1];
314
+ log.debug('filename', fileName);
315
+ const file = {
316
+ data : response.body,
317
+ fileName : fileName
318
+ }
319
+ resolve(file);
320
+ })
321
+ .catch(function (error) {
322
+ log.error(error);
323
+ reject(error);
324
+ });
325
+ });
326
+ }
327
+
328
+ function applyMapping(entryData, mapping, constants) {
329
+ Object.keys(entryData).forEach(function (objectKey) {
330
+ const found = mapping.find(function(element) {
331
+ return element.newName === objectKey;
332
+ });
333
+
334
+ if (found == null || found=== undefined) {
335
+ log.debug('delete', objectKey)
336
+ delete entryData[objectKey];
337
+ }
338
+ });
339
+ mapping.forEach(function (mappingEntry) {
340
+ try {
341
+ Object.defineProperty(entryData, mappingEntry.oldName, Object.getOwnPropertyDescriptor(entryData, mappingEntry.newName));
342
+ //delete mapping only if no self mapping
343
+ if (!(mappingEntry.newName == mappingEntry.oldName)) {
344
+ delete entryData[mappingEntry.newName];
345
+ }
346
+ } catch (e) {
347
+ //ignore missing mapping
348
+ }
349
+ });
350
+ //add constant values (defaults)
351
+ if (constants && Array.isArray(constants) && constants.length) {
352
+
353
+ constants.forEach(function (element) {
354
+ log.debug('add constant', element);
355
+ try {
356
+ entryData[element.name] = element.value;
357
+ } catch (e) {
358
+ log.error(e);
359
+ }
360
+ });
361
+ }
362
+ return entryData;
363
+ }
364
+
365
+ function updateEntry(form, id, entryData) {
366
+ let port = 0;
367
+ if (process.env.AR_PORT && process.env.AR_PORT != undefined)
368
+ port = process.env.AR_PORT;
369
+ //Apply mapping
370
+ log.debug('Data to update entry with id: ' + id, entryData);
371
+ //entryData = applyMapping(entryData, mapping, constants);
372
+ const myEntry = {};
373
+ myEntry.id = id;
374
+ myEntry.values = entryData;
375
+ //Create record data. Use field id to identify each record
376
+ log.debug('New Entry', myEntry);
377
+ //log.debug('object', Object.keys(entryData));
378
+ return new Promise((resolve, reject) => {
379
+ const uri = process.env.BASEURL
380
+ + "/" + process.env.AR_SERVER
381
+ + "/" + form
382
+ + "?port=" + port;
383
+
384
+
385
+ var options = {
386
+ method: 'PUT',
387
+ uri: uri,
388
+ body: myEntry,
389
+ timeout: 6000000, // 10 min.
390
+ json: true // Automatically stringifies the body to JSON
391
+ };
392
+
393
+ log.debug('start request with:', options.body);
394
+ log.debug('start request on:', uri);
395
+ request(options).auth(process.env.AR_USER, process.env.AR_PASSWORD, true)
396
+ .then(function (response) {
397
+ log.debug('RAPI update return', response);
398
+ const singleRecord = Object.keys(response).length == 1;
399
+ if (singleRecord) {
400
+ if (response[0].startsWith('null - ')) {
401
+ reject(response[0]);
402
+ }
403
+ }
404
+ //TODO check for status and reject if Error
405
+ resolve(response);
406
+ })
407
+ .catch(function (error) {
408
+ log.error(error);
409
+ reject(error);
410
+ });
411
+ });
412
+ }
413
+
414
+ module.exports = {
415
+ executeARQuery, createEntry, updateEntry, setAttachment, getAttachment, deleteEntries, getMetaData
416
+ };
package/util/auth.js ADDED
@@ -0,0 +1,37 @@
1
+ require('dotenv').config();
2
+ const path = require('path');
3
+ const log = require('@manyos/logger').setupLog('SMILEconnect_' + path.basename(__filename));
4
+
5
+ function isAuthorizedAdmin(req, res, next) {
6
+ log.debug(req.user);
7
+ const admins = process.env.ADMIN_USERS;
8
+ if (admins !== null
9
+ && admins !== undefined
10
+ && req.user !== null
11
+ && req.user !== undefined
12
+ && isUserInList(admins, req.user.username)) {
13
+ log.debug('user authorized');
14
+ next();
15
+ } else {
16
+ const err = new Error('Not authorized as admin!');
17
+ err.status = 401;
18
+ return next(err);
19
+ }
20
+ }
21
+
22
+ function isUserInList(userList, userName) {
23
+ log.debug('Check if user is in List', userName, userList)
24
+ if (userList !== null && userList !== undefined && userName !== null && userName !== undefined) {
25
+ let userArray = userList.split(',');
26
+ userArray = userArray.map(item => {
27
+ return item.trim();
28
+ });
29
+ log.debug(userArray.includes(userName.trim()));
30
+ return (userArray.includes(userName.trim()));
31
+ }
32
+ return false;
33
+ }
34
+
35
+ module.exports = {
36
+ isAuthorizedAdmin
37
+ }
@@ -0,0 +1,52 @@
1
+ const NodeCache = require ('node-cache/index');
2
+ const path = require('path');
3
+ const log = require('@manyos/logger').setupLog('SMILEconnect_' + path.basename(__filename));
4
+
5
+ class Cache {
6
+
7
+ constructor(ttlSeconds) {
8
+ log.debug('cache created. ttl seconds:', ttlSeconds);
9
+ this.cache = new NodeCache({ stdTTL: ttlSeconds, checkperiod: ttlSeconds * 0.2, useClones: false });
10
+ }
11
+
12
+ get(key, storeFunction) {
13
+ const value = this.cache.get(key);
14
+ if (value) {
15
+ return Promise.resolve(value);
16
+ }
17
+
18
+ return storeFunction().then((result) => {
19
+ this.cache.set(key, result);
20
+ return result;
21
+ });
22
+ }
23
+
24
+ del(keys) {
25
+ log.debug('Delete cache keys', keys);
26
+ this.cache.del(keys);
27
+ }
28
+
29
+ delStartWith(startStr = '') {
30
+ if (!startStr) {
31
+ return;
32
+ }
33
+
34
+ const keys = this.cache.keys();
35
+ for (const key of keys) {
36
+ if (key.indexOf(startStr) === 0) {
37
+ this.del(key);
38
+ }
39
+ }
40
+ }
41
+
42
+ flush() {
43
+ this.cache.flushAll();
44
+ }
45
+
46
+ getTtl(key) {
47
+ return this.cache.getTtl(key);
48
+ }
49
+ }
50
+
51
+
52
+ module.exports = Cache;