@resolveio/server-lib 12.5.52 → 12.5.54

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 (400) hide show
  1. package/.nodemon.json +5 -0
  2. package/.vscode/settings.json +3 -0
  3. package/README.md +22 -0
  4. package/compileDTS.pl +64 -0
  5. package/gulpfile.js +127 -0
  6. package/mongodbensurers.js +2 -0
  7. package/mongostop.js +3 -0
  8. package/package.json +1 -1
  9. package/settings.development.json +13 -0
  10. package/src/client-server-app.ts +7 -0
  11. package/src/collections/app-status.collection.ts +51 -0
  12. package/src/collections/counter.collection.ts +54 -0
  13. package/src/collections/cron-job-history.collection.ts +82 -0
  14. package/src/collections/cron-job.collection.ts +85 -0
  15. package/src/collections/email-history.collection.ts +66 -0
  16. package/src/collections/email-verified.collection.ts +61 -0
  17. package/src/collections/file.collection.ts +73 -0
  18. package/src/collections/flag.collection.ts +56 -0
  19. package/src/collections/log-method-latency.collection.ts +64 -0
  20. package/src/collections/log-subscription.collection.ts +68 -0
  21. package/src/collections/log-ts.collection.ts +78 -0
  22. package/src/collections/log.collection.ts +77 -0
  23. package/src/collections/logged-in-users.collection.ts +65 -0
  24. package/src/collections/method-response.collection.ts +66 -0
  25. package/src/collections/monitor-cpu.collection.ts +63 -0
  26. package/src/collections/monitor-memory.collection.ts +75 -0
  27. package/src/collections/monitor-mongo.collection.ts +69 -0
  28. package/src/collections/notification.collection.ts +51 -0
  29. package/src/collections/report-builder-dashboard-builder.collection.ts +109 -0
  30. package/src/collections/report-builder-library.collection.ts +89 -0
  31. package/src/collections/report-builder-report.collection.ts +149 -0
  32. package/src/collections/user-group.collection.ts +90 -0
  33. package/src/collections/user-guide.collection.ts +57 -0
  34. package/src/collections/user.collection.ts +259 -0
  35. package/src/cron/cron.ts +188 -0
  36. package/src/fixtures/cron-jobs.ts +25 -0
  37. package/src/fixtures/init.ts +10 -0
  38. package/src/http/auth.ts +732 -0
  39. package/src/http/health.ts +7 -0
  40. package/src/http/home.ts +71 -0
  41. package/src/index.ts +71 -0
  42. package/src/managers/cron.manager.ts +208 -0
  43. package/src/managers/method.manager.ts +607 -0
  44. package/src/managers/mongo.manager.ts +2282 -0
  45. package/src/managers/monitor.manager.ts +323 -0
  46. package/src/managers/subscription.manager.ts +1196 -0
  47. package/{managers/subscription.performance.js → src/managers/subscription.performance.ts} +68 -33
  48. package/src/methods/accounts.ts +202 -0
  49. package/src/methods/aws.ts +507 -0
  50. package/src/methods/collections.ts +512 -0
  51. package/src/methods/counters.ts +75 -0
  52. package/src/methods/cron-jobs.ts +1454 -0
  53. package/src/methods/flags.ts +7 -0
  54. package/src/methods/logs.ts +327 -0
  55. package/src/methods/monitor.ts +548 -0
  56. package/src/methods/pdf.ts +615 -0
  57. package/src/methods/report-builder.ts +876 -0
  58. package/src/methods/support.ts +146 -0
  59. package/{models/app-status.model.d.ts → src/models/app-status.model.ts} +3 -2
  60. package/{models/billing-logged-in-users.model.d.ts → src/models/billing-logged-in-users.model.ts} +5 -4
  61. package/src/models/collection-document.model.ts +22 -0
  62. package/{models/counter.model.d.ts → src/models/counter.model.ts} +4 -3
  63. package/src/models/cron-job-history.model.ts +13 -0
  64. package/src/models/cron-job.model.ts +14 -0
  65. package/src/models/dialog.model.ts +25 -0
  66. package/{models/email-history.model.d.ts → src/models/email-history.model.ts} +8 -7
  67. package/{models/email-verified.model.d.ts → src/models/email-verified.model.ts} +6 -5
  68. package/{models/file.model.d.ts → src/models/file.model.ts} +8 -7
  69. package/{models/flag.model.d.ts → src/models/flag.model.ts} +4 -3
  70. package/{models/log-method-latency.model.d.ts → src/models/log-method-latency.model.ts} +5 -4
  71. package/{models/log-subscription.model.d.ts → src/models/log-subscription.model.ts} +8 -6
  72. package/{models/log.model.d.ts → src/models/log.model.ts} +12 -10
  73. package/{models/logged-in-users.model.d.ts → src/models/logged-in-users.model.ts} +6 -5
  74. package/{models/method-response.model.d.ts → src/models/method-response.model.ts} +7 -6
  75. package/src/models/method.model.ts +11 -0
  76. package/{models/monitor-cpu.model.d.ts → src/models/monitor-cpu.model.ts} +9 -7
  77. package/src/models/monitor-memory.model.ts +17 -0
  78. package/src/models/monitor-mongo.model.ts +15 -0
  79. package/{models/notification.model.d.ts → src/models/notification.model.ts} +3 -2
  80. package/src/models/pagination.model.ts +30 -0
  81. package/src/models/permission.model.ts +14 -0
  82. package/src/models/report-builder-dashboard-builder.model.ts +29 -0
  83. package/src/models/report-builder-library.model.ts +20 -0
  84. package/src/models/report-builder-report.model.ts +110 -0
  85. package/src/models/report-builder.model.ts +68 -0
  86. package/src/models/select-data-label.model.ts +9 -0
  87. package/src/models/server-response.model.ts +5 -0
  88. package/src/models/subscription.model.ts +64 -0
  89. package/src/models/support-ticket.model.ts +81 -0
  90. package/src/models/user-group.model.ts +23 -0
  91. package/{models/user-guide.model.d.ts → src/models/user-guide.model.ts} +5 -4
  92. package/src/models/user.model.ts +79 -0
  93. package/{public_api.d.ts → src/public_api.ts} +1 -1
  94. package/src/publications/app-status.ts +13 -0
  95. package/src/publications/cron-jobs.ts +30 -0
  96. package/src/publications/files.ts +34 -0
  97. package/src/publications/flags.ts +20 -0
  98. package/src/publications/logs.ts +164 -0
  99. package/src/publications/method-responses.ts +13 -0
  100. package/src/publications/notifications.ts +13 -0
  101. package/src/publications/report-builder-dashboard-builders.ts +39 -0
  102. package/src/publications/report-builder-libraries.ts +41 -0
  103. package/src/publications/report-builder-reports.ts +48 -0
  104. package/src/publications/super-admin.ts +13 -0
  105. package/src/publications/user-groups.ts +12 -0
  106. package/src/publications/user-guides.ts +12 -0
  107. package/src/server-app.ts +769 -0
  108. package/src/util/common.ts +519 -0
  109. package/src/util/schema-report-builder.ts +413 -0
  110. package/tsconfig.json +30 -0
  111. package/tslint.json +143 -0
  112. package/client-server-app.d.ts +0 -1
  113. package/client-server-app.js +0 -48
  114. package/client-server-app.js.map +0 -1
  115. package/collections/app-status.collection.d.ts +0 -3
  116. package/collections/app-status.collection.js +0 -51
  117. package/collections/app-status.collection.js.map +0 -1
  118. package/collections/counter.collection.d.ts +0 -3
  119. package/collections/counter.collection.js +0 -54
  120. package/collections/counter.collection.js.map +0 -1
  121. package/collections/cron-job-history.collection.d.ts +0 -3
  122. package/collections/cron-job-history.collection.js +0 -82
  123. package/collections/cron-job-history.collection.js.map +0 -1
  124. package/collections/cron-job.collection.d.ts +0 -3
  125. package/collections/cron-job.collection.js +0 -85
  126. package/collections/cron-job.collection.js.map +0 -1
  127. package/collections/email-history.collection.d.ts +0 -3
  128. package/collections/email-history.collection.js +0 -66
  129. package/collections/email-history.collection.js.map +0 -1
  130. package/collections/email-verified.collection.d.ts +0 -3
  131. package/collections/email-verified.collection.js +0 -61
  132. package/collections/email-verified.collection.js.map +0 -1
  133. package/collections/file.collection.d.ts +0 -3
  134. package/collections/file.collection.js +0 -73
  135. package/collections/file.collection.js.map +0 -1
  136. package/collections/flag.collection.d.ts +0 -3
  137. package/collections/flag.collection.js +0 -56
  138. package/collections/flag.collection.js.map +0 -1
  139. package/collections/log-method-latency.collection.d.ts +0 -3
  140. package/collections/log-method-latency.collection.js +0 -64
  141. package/collections/log-method-latency.collection.js.map +0 -1
  142. package/collections/log-subscription.collection.d.ts +0 -3
  143. package/collections/log-subscription.collection.js +0 -68
  144. package/collections/log-subscription.collection.js.map +0 -1
  145. package/collections/log-ts.collection.d.ts +0 -3
  146. package/collections/log-ts.collection.js +0 -78
  147. package/collections/log-ts.collection.js.map +0 -1
  148. package/collections/log.collection.d.ts +0 -3
  149. package/collections/log.collection.js +0 -77
  150. package/collections/log.collection.js.map +0 -1
  151. package/collections/logged-in-users.collection.d.ts +0 -3
  152. package/collections/logged-in-users.collection.js +0 -65
  153. package/collections/logged-in-users.collection.js.map +0 -1
  154. package/collections/method-response.collection.d.ts +0 -3
  155. package/collections/method-response.collection.js +0 -66
  156. package/collections/method-response.collection.js.map +0 -1
  157. package/collections/monitor-cpu.collection.d.ts +0 -3
  158. package/collections/monitor-cpu.collection.js +0 -63
  159. package/collections/monitor-cpu.collection.js.map +0 -1
  160. package/collections/monitor-memory.collection.d.ts +0 -3
  161. package/collections/monitor-memory.collection.js +0 -75
  162. package/collections/monitor-memory.collection.js.map +0 -1
  163. package/collections/monitor-mongo.collection.d.ts +0 -3
  164. package/collections/monitor-mongo.collection.js +0 -69
  165. package/collections/monitor-mongo.collection.js.map +0 -1
  166. package/collections/notification.collection.d.ts +0 -3
  167. package/collections/notification.collection.js +0 -51
  168. package/collections/notification.collection.js.map +0 -1
  169. package/collections/report-builder-dashboard-builder.collection.d.ts +0 -3
  170. package/collections/report-builder-dashboard-builder.collection.js +0 -109
  171. package/collections/report-builder-dashboard-builder.collection.js.map +0 -1
  172. package/collections/report-builder-library.collection.d.ts +0 -3
  173. package/collections/report-builder-library.collection.js +0 -87
  174. package/collections/report-builder-library.collection.js.map +0 -1
  175. package/collections/report-builder-report.collection.d.ts +0 -4
  176. package/collections/report-builder-report.collection.js +0 -149
  177. package/collections/report-builder-report.collection.js.map +0 -1
  178. package/collections/user-group.collection.d.ts +0 -4
  179. package/collections/user-group.collection.js +0 -90
  180. package/collections/user-group.collection.js.map +0 -1
  181. package/collections/user-guide.collection.d.ts +0 -3
  182. package/collections/user-guide.collection.js +0 -57
  183. package/collections/user-guide.collection.js.map +0 -1
  184. package/collections/user.collection.d.ts +0 -4
  185. package/collections/user.collection.js +0 -259
  186. package/collections/user.collection.js.map +0 -1
  187. package/cron/cron.d.ts +0 -5
  188. package/cron/cron.js +0 -162
  189. package/cron/cron.js.map +0 -1
  190. package/fixtures/cron-jobs.d.ts +0 -1
  191. package/fixtures/cron-jobs.js +0 -73
  192. package/fixtures/cron-jobs.js.map +0 -1
  193. package/fixtures/init.d.ts +0 -1
  194. package/fixtures/init.js +0 -59
  195. package/fixtures/init.js.map +0 -1
  196. package/http/auth.d.ts +0 -2
  197. package/http/auth.js +0 -763
  198. package/http/auth.js.map +0 -1
  199. package/http/health.d.ts +0 -1
  200. package/http/health.js +0 -12
  201. package/http/health.js.map +0 -1
  202. package/http/home.d.ts +0 -2
  203. package/http/home.js +0 -66
  204. package/http/home.js.map +0 -1
  205. package/index.d.ts +0 -23
  206. package/index.js +0 -127
  207. package/index.js.map +0 -1
  208. package/managers/cron.manager.d.ts +0 -17
  209. package/managers/cron.manager.js +0 -242
  210. package/managers/cron.manager.js.map +0 -1
  211. package/managers/method.manager.d.ts +0 -38
  212. package/managers/method.manager.js +0 -595
  213. package/managers/method.manager.js.map +0 -1
  214. package/managers/mongo.manager.d.ts +0 -178
  215. package/managers/mongo.manager.js +0 -2221
  216. package/managers/mongo.manager.js.map +0 -1
  217. package/managers/monitor.manager.d.ts +0 -59
  218. package/managers/monitor.manager.js +0 -351
  219. package/managers/monitor.manager.js.map +0 -1
  220. package/managers/subscription.manager.d.ts +0 -50
  221. package/managers/subscription.manager.js +0 -1091
  222. package/managers/subscription.manager.js.map +0 -1
  223. package/managers/subscription.performance.d.ts +0 -1
  224. package/managers/subscription.performance.js.map +0 -1
  225. package/methods/accounts.d.ts +0 -2
  226. package/methods/accounts.js +0 -274
  227. package/methods/accounts.js.map +0 -1
  228. package/methods/aws.d.ts +0 -2
  229. package/methods/aws.js +0 -617
  230. package/methods/aws.js.map +0 -1
  231. package/methods/collections.d.ts +0 -2
  232. package/methods/collections.js +0 -499
  233. package/methods/collections.js.map +0 -1
  234. package/methods/counters.d.ts +0 -2
  235. package/methods/counters.js +0 -122
  236. package/methods/counters.js.map +0 -1
  237. package/methods/cron-jobs.d.ts +0 -2
  238. package/methods/cron-jobs.js +0 -1315
  239. package/methods/cron-jobs.js.map +0 -1
  240. package/methods/flags.d.ts +0 -2
  241. package/methods/flags.js +0 -9
  242. package/methods/flags.js.map +0 -1
  243. package/methods/logs.d.ts +0 -2
  244. package/methods/logs.js +0 -371
  245. package/methods/logs.js.map +0 -1
  246. package/methods/monitor.d.ts +0 -2
  247. package/methods/monitor.js +0 -565
  248. package/methods/monitor.js.map +0 -1
  249. package/methods/pdf.d.ts +0 -2
  250. package/methods/pdf.js +0 -324
  251. package/methods/pdf.js.map +0 -1
  252. package/methods/report-builder.d.ts +0 -2
  253. package/methods/report-builder.js +0 -858
  254. package/methods/report-builder.js.map +0 -1
  255. package/methods/support.d.ts +0 -2
  256. package/methods/support.js +0 -246
  257. package/methods/support.js.map +0 -1
  258. package/models/app-status.model.js +0 -4
  259. package/models/app-status.model.js.map +0 -1
  260. package/models/billing-logged-in-users.model.js +0 -4
  261. package/models/billing-logged-in-users.model.js.map +0 -1
  262. package/models/collection-document.model.d.ts +0 -19
  263. package/models/collection-document.model.js +0 -4
  264. package/models/collection-document.model.js.map +0 -1
  265. package/models/counter.model.js +0 -4
  266. package/models/counter.model.js.map +0 -1
  267. package/models/cron-job-history.model.d.ts +0 -12
  268. package/models/cron-job-history.model.js +0 -4
  269. package/models/cron-job-history.model.js.map +0 -1
  270. package/models/cron-job.model.d.ts +0 -13
  271. package/models/cron-job.model.js +0 -4
  272. package/models/cron-job.model.js.map +0 -1
  273. package/models/dialog.model.d.ts +0 -23
  274. package/models/dialog.model.js +0 -4
  275. package/models/dialog.model.js.map +0 -1
  276. package/models/email-history.model.js +0 -4
  277. package/models/email-history.model.js.map +0 -1
  278. package/models/email-verified.model.js +0 -4
  279. package/models/email-verified.model.js.map +0 -1
  280. package/models/file.model.js +0 -4
  281. package/models/file.model.js.map +0 -1
  282. package/models/flag.model.js +0 -4
  283. package/models/flag.model.js.map +0 -1
  284. package/models/log-method-latency.model.js +0 -4
  285. package/models/log-method-latency.model.js.map +0 -1
  286. package/models/log-subscription.model.js +0 -4
  287. package/models/log-subscription.model.js.map +0 -1
  288. package/models/log.model.js +0 -4
  289. package/models/log.model.js.map +0 -1
  290. package/models/logged-in-users.model.js +0 -4
  291. package/models/logged-in-users.model.js.map +0 -1
  292. package/models/method-response.model.js +0 -4
  293. package/models/method-response.model.js.map +0 -1
  294. package/models/method.model.d.ts +0 -9
  295. package/models/method.model.js +0 -4
  296. package/models/method.model.js.map +0 -1
  297. package/models/monitor-cpu.model.js +0 -4
  298. package/models/monitor-cpu.model.js.map +0 -1
  299. package/models/monitor-memory.model.d.ts +0 -15
  300. package/models/monitor-memory.model.js +0 -4
  301. package/models/monitor-memory.model.js.map +0 -1
  302. package/models/monitor-mongo.model.d.ts +0 -13
  303. package/models/monitor-mongo.model.js +0 -4
  304. package/models/monitor-mongo.model.js.map +0 -1
  305. package/models/notification.model.js +0 -4
  306. package/models/notification.model.js.map +0 -1
  307. package/models/pagination.model.d.ts +0 -10
  308. package/models/pagination.model.js +0 -24
  309. package/models/pagination.model.js.map +0 -1
  310. package/models/permission.model.d.ts +0 -12
  311. package/models/permission.model.js +0 -4
  312. package/models/permission.model.js.map +0 -1
  313. package/models/report-builder-dashboard-builder.model.d.ts +0 -25
  314. package/models/report-builder-dashboard-builder.model.js +0 -4
  315. package/models/report-builder-dashboard-builder.model.js.map +0 -1
  316. package/models/report-builder-library.model.d.ts +0 -17
  317. package/models/report-builder-library.model.js +0 -4
  318. package/models/report-builder-library.model.js.map +0 -1
  319. package/models/report-builder-report.model.d.ts +0 -98
  320. package/models/report-builder-report.model.js +0 -4
  321. package/models/report-builder-report.model.js.map +0 -1
  322. package/models/report-builder.model.d.ts +0 -61
  323. package/models/report-builder.model.js +0 -4
  324. package/models/report-builder.model.js.map +0 -1
  325. package/models/select-data-label.model.d.ts +0 -9
  326. package/models/select-data-label.model.js +0 -4
  327. package/models/select-data-label.model.js.map +0 -1
  328. package/models/server-response.model.d.ts +0 -5
  329. package/models/server-response.model.js +0 -4
  330. package/models/server-response.model.js.map +0 -1
  331. package/models/subscription.model.d.ts +0 -23
  332. package/models/subscription.model.js +0 -4
  333. package/models/subscription.model.js.map +0 -1
  334. package/models/support-ticket.model.d.ts +0 -72
  335. package/models/support-ticket.model.js +0 -4
  336. package/models/support-ticket.model.js.map +0 -1
  337. package/models/user-group.model.d.ts +0 -19
  338. package/models/user-group.model.js +0 -4
  339. package/models/user-group.model.js.map +0 -1
  340. package/models/user-guide.model.js +0 -4
  341. package/models/user-guide.model.js.map +0 -1
  342. package/models/user.model.d.ts +0 -70
  343. package/models/user.model.js +0 -4
  344. package/models/user.model.js.map +0 -1
  345. package/public_api.js +0 -75
  346. package/public_api.js.map +0 -1
  347. package/publications/app-status.d.ts +0 -2
  348. package/publications/app-status.js +0 -17
  349. package/publications/app-status.js.map +0 -1
  350. package/publications/cron-jobs.d.ts +0 -2
  351. package/publications/cron-jobs.js +0 -33
  352. package/publications/cron-jobs.js.map +0 -1
  353. package/publications/files.d.ts +0 -2
  354. package/publications/files.js +0 -37
  355. package/publications/files.js.map +0 -1
  356. package/publications/flags.d.ts +0 -2
  357. package/publications/flags.js +0 -23
  358. package/publications/flags.js.map +0 -1
  359. package/publications/logs.d.ts +0 -2
  360. package/publications/logs.js +0 -165
  361. package/publications/logs.js.map +0 -1
  362. package/publications/method-responses.d.ts +0 -2
  363. package/publications/method-responses.js +0 -17
  364. package/publications/method-responses.js.map +0 -1
  365. package/publications/notifications.d.ts +0 -2
  366. package/publications/notifications.js +0 -17
  367. package/publications/notifications.js.map +0 -1
  368. package/publications/report-builder-dashboard-builders.d.ts +0 -2
  369. package/publications/report-builder-dashboard-builders.js +0 -43
  370. package/publications/report-builder-dashboard-builders.js.map +0 -1
  371. package/publications/report-builder-libraries.d.ts +0 -2
  372. package/publications/report-builder-libraries.js +0 -90
  373. package/publications/report-builder-libraries.js.map +0 -1
  374. package/publications/report-builder-reports.d.ts +0 -2
  375. package/publications/report-builder-reports.js +0 -51
  376. package/publications/report-builder-reports.js.map +0 -1
  377. package/publications/super-admin.d.ts +0 -2
  378. package/publications/super-admin.js +0 -17
  379. package/publications/super-admin.js.map +0 -1
  380. package/publications/user-groups.d.ts +0 -1
  381. package/publications/user-groups.js +0 -17
  382. package/publications/user-groups.js.map +0 -1
  383. package/publications/user-guides.d.ts +0 -1
  384. package/publications/user-guides.js +0 -17
  385. package/publications/user-guides.js.map +0 -1
  386. package/server-app.d.ts +0 -52
  387. package/server-app.js +0 -739
  388. package/server-app.js.map +0 -1
  389. package/util/common.d.ts +0 -25
  390. package/util/common.js +0 -524
  391. package/util/common.js.map +0 -1
  392. package/util/schema-report-builder.d.ts +0 -6
  393. package/util/schema-report-builder.js +0 -453
  394. package/util/schema-report-builder.js.map +0 -1
  395. /package/{private → src/private}/email-templates/enrollment.html +0 -0
  396. /package/{private → src/private}/email-templates/forgot-password.html +0 -0
  397. /package/{private → src/private}/email-templates/support-ticket-deleted.html +0 -0
  398. /package/{private → src/private}/email-templates/support-ticket-modified.html +0 -0
  399. /package/{private → src/private}/email-templates/support-ticket.html +0 -0
  400. /package/{private → src/private}/images/ResolveIO.png +0 -0
@@ -0,0 +1,1196 @@
1
+ import { ServerResponseModel } from '../models/server-response.model';
2
+ import { ActiveSubscriptionModel, SubscriptionModel } from '../models/subscription.model';
3
+ import * as WebSocket from 'ws';
4
+ import { loadLogPublications } from '../publications/logs';
5
+ import { loadAppStatusPublications } from '../publications/app-status';
6
+ import { loadFilePublications } from '../publications/files';
7
+ import { loadSuperAdminPublications } from '../publications/super-admin';
8
+ import { LoggedInUsers } from '../collections/logged-in-users.collection';
9
+ import { loadCronJobPublications } from '../publications/cron-jobs';
10
+ import { loadFlagsPublications } from '../publications/flags';
11
+ import { loadMethodResponsePublications } from '../publications/method-responses';
12
+ import ResolveIOMainServer from '../server-app';
13
+ import { ResolveIOServer } from '../index';
14
+ import { loadNotificationPublications } from '../publications/notifications';
15
+ import { loadReportBuilderReportPublications } from '../publications/report-builder-reports';
16
+ import { LoggedInUserModel } from '../models/logged-in-users.model';
17
+
18
+ import { ChangeStream, ChangeStreamDeleteDocument, ChangeStreamDocument, ChangeStreamInsertDocument, ChangeStreamReplaceDocument, ChangeStreamUpdateDocument, ResumeToken } from 'mongodb';
19
+ import { loadReportBuilderLibraryPublications } from '../publications/report-builder-libraries';
20
+ import { loadUserGroupPublications } from '../publications/user-groups';
21
+ import { loadUserGuidePublications } from '../publications/user-guides';
22
+ import { loadReportBuilderDashboardBuilderPublications } from '../publications/report-builder-dashboard-builders';
23
+ import { objectIdHexString } from './mongo.manager';
24
+ import { dateReviver, deepCopy } from '../util/common';
25
+ import * as NodeCache from 'node-cache';
26
+ import { MonitorManagerFunction } from './monitor.manager';
27
+ import { Flags } from '../collections/flag.collection';
28
+ import { cpus } from 'os';
29
+ const numCPUs = cpus().length;
30
+ const v8 = require('v8');
31
+
32
+ // Performance Dependencies
33
+ // import * as path from 'path';
34
+ // import { Worker } from 'worker_threads';
35
+
36
+ interface MongoQueueModel {
37
+ _id: number,
38
+ type: string;
39
+ collection: string;
40
+ subscription: ActiveSubscriptionModel;
41
+ running: boolean;
42
+ run_again: boolean;
43
+ }
44
+
45
+ // interface CurrentPerformanceMonitor {
46
+ // _id: number;
47
+ // function: string;
48
+ // publication: string;
49
+ // subscriptionData: any[];
50
+ // date_start: Date;
51
+ // date_end: Date;
52
+ // duration: number;
53
+ // result: string;
54
+ // }
55
+
56
+ export class SubscriptionManager {
57
+
58
+ private _mainServer: ResolveIOMainServer;
59
+ private _publications: SubscriptionModel = {};
60
+ private _subscriptions: ActiveSubscriptionModel[] = [];
61
+ private _wss: WebSocket.Server;
62
+ private _loggedInUsers: LoggedInUserModel[] = [];
63
+
64
+ private _mongoQueue: MongoQueueModel[] = [];
65
+ private _mongoQueueId = 0;
66
+
67
+ private _sendQueue = [];
68
+ private _runningQueue = false;
69
+
70
+ private _oplog: ChangeStream;
71
+
72
+ private _nodeCache;
73
+ private _cacheId = 1;
74
+
75
+ private serverConfig;
76
+
77
+ private _heapStats = v8.getHeapStatistics();
78
+ private _heapLimit = this._heapStats.heap_size_limit / numCPUs / 8;
79
+
80
+ private _monitorManagerFunction: MonitorManagerFunction;
81
+
82
+ private _enableDebug = false;
83
+ private _debugOplogCollections = [];
84
+ private _debugOplogHits = 0;
85
+ private _debugSubCollections = [];
86
+ private _debugSubHits = 0;
87
+ private _debugUnSubHits = 0;
88
+ private _debugUnSubAllHits = 0;
89
+ private _debugMongoQueueHits = 0;
90
+ private _debugMongoQueueCollections = [];
91
+ private _debugSendQueueHits = 0;
92
+ private _debugRemoveCacheHits = 0;
93
+
94
+ private _oplogRetryCount = 0;
95
+
96
+ // private currentPerfomanceMonitor: CurrentPerformanceMonitor[] = [];
97
+ // private idPerformance: number = 0;
98
+ // private performanceThread;
99
+
100
+ constructor(mainServer, wss: WebSocket.Server, serverConfig, monitorManagerFunction: MonitorManagerFunction) {
101
+ this._mainServer = mainServer;
102
+ this._monitorManagerFunction = monitorManagerFunction;
103
+
104
+ this._nodeCache = new NodeCache( { stdTTL: 0, checkperiod: 0 } );
105
+
106
+ // setTimeout(() => {
107
+ // console.log('Setting up performance thread');
108
+
109
+ // this.performanceThread = new Worker(path.join(__dirname, './subscription.performance.js'));
110
+
111
+ // this.performanceThread.on('exit', code => {
112
+ // console.error(new Date(), 'THREAD EXITED!!!!!!!!!!!!!!!!!!', code);
113
+ // });
114
+
115
+ // this.performanceThread.on('error', code => {
116
+ // console.error(new Date(), 'THREAD RECV ERROR !!!!!!!!!!!!!!!!!!', code);
117
+ // });
118
+ // }, 5000);
119
+
120
+ // setInterval(() => {
121
+ // console.log('Post thread msg');
122
+ // this.performanceThread.postMessage(this.currentPerfomanceMonitor);
123
+ // this.currentPerfomanceMonitor = [];
124
+ // }, 10000);
125
+
126
+ this.serverConfig = serverConfig;
127
+ this._wss = wss;
128
+
129
+ // Publications
130
+ loadSuperAdminPublications(this);
131
+ loadAppStatusPublications(this);
132
+ loadLogPublications(this);
133
+ loadFilePublications(this);
134
+ loadCronJobPublications(this);
135
+ loadFlagsPublications(this);
136
+ loadMethodResponsePublications(this);
137
+ loadNotificationPublications(this);
138
+ loadReportBuilderReportPublications(this);
139
+ loadReportBuilderLibraryPublications(this);
140
+ loadUserGroupPublications(this);
141
+ loadUserGuidePublications(this);
142
+ loadReportBuilderDashboardBuilderPublications(this);
143
+
144
+ this.tailOpLog();
145
+
146
+ setInterval(() => {
147
+ this._oplogRetryCount = 0;
148
+ }, 15000);
149
+
150
+ setInterval(() => {
151
+ if (this.getEnableDebug()) {
152
+ console.log(new Date(), 'Sub Manager', 'Subs', this._subscriptions.length);
153
+ console.log(new Date(), 'Sub Manager', 'Logged In Users', this._loggedInUsers.length);
154
+ console.log(new Date(), 'Sub Manager', 'Mongo Queue', this._mongoQueue.length);
155
+ console.log(new Date(), 'Sub Manager', 'Mongo Queue Hits', this._debugMongoQueueHits);
156
+ console.log(new Date(), 'Sub Manager', 'Mongo Queue Collections', JSON.stringify(this._debugMongoQueueCollections.sort((a, b) => a.collection.localeCompare(b.collection) || a.publication.localeCompare(b.publication)), null, 2));
157
+ console.log(new Date(), 'Sub Manager', 'Oplog Hits', this._debugOplogHits);
158
+ console.log(new Date(), 'Sub Manager', 'Oplog Collections', JSON.stringify(this._debugOplogCollections.sort((a, b) => a.collection.localeCompare(b.collection) || a.type.localeCompare(b.type)), null, 2));
159
+ console.log(new Date(), 'Sub Manager', 'Send Queue Hits', this._debugSendQueueHits);
160
+ console.log(new Date(), 'Sub Manager', 'Sub Hits', this._debugSubHits);
161
+ console.log(new Date(), 'Sub Manager', 'Sub Collections', JSON.stringify(this._debugSubCollections.sort((a, b) => a.publication.localeCompare(b.publication)), null, 2));
162
+ console.log(new Date(), 'Sub Manager', 'Unsub Hits', this._debugUnSubHits);
163
+ console.log(new Date(), 'Sub Manager', 'Unsub All Hits', this._debugUnSubAllHits);
164
+ console.log(new Date(), 'Sub Manager', 'Cache Cleanup Hits', this._debugRemoveCacheHits);
165
+ }
166
+
167
+ this._debugOplogHits = 0;
168
+ this._debugOplogCollections = [];
169
+ this._debugSubCollections = [];
170
+ this._debugMongoQueueHits = 0;
171
+ this._debugMongoQueueCollections = [];
172
+ this._debugSendQueueHits = 0;
173
+ this._debugSubHits = 0;
174
+ this._debugUnSubHits = 0;
175
+ this._debugUnSubAllHits = 0;
176
+ this._debugRemoveCacheHits = 0;
177
+ }, 60000);
178
+
179
+ setInterval(async () => {
180
+ if (!this._runningQueue) {
181
+ this._runningQueue = true;
182
+
183
+ if (this._sendQueue.length) {
184
+ // let startDate = new Date();
185
+ for (let i = this._sendQueue.length - 1; i >= 0; i--) {
186
+ this._debugSendQueueHits += 1;
187
+
188
+ let ws = this._mainServer.getWS(this._sendQueue[i].id_ws);
189
+ if (ws && ws.readyState === ws.OPEN) {
190
+ if (ws.bufferedAmount < 20480) {
191
+ let sendItem = this._sendQueue.pop();
192
+ ws.send(JSON.stringify(sendItem.data), (error) => {
193
+ if (error) {
194
+ console.log('Error on WS: ', error);
195
+ if (this.getEnableDebug()) {
196
+ console.log(new Date(), 'Sub Manager', 'Unsub WS', ws['user'], ws['id_socket'], 1);
197
+ }
198
+ this.unsubscribeAll(ws);
199
+ }
200
+ });
201
+ }
202
+ }
203
+ else {
204
+ this._sendQueue.splice(i, 1);
205
+ }
206
+ }
207
+
208
+ // let endDate = new Date();
209
+ // this.currentPerfomanceMonitor.push({
210
+ // _id: this.idPerformance++,
211
+ // function: 'sendInterval',
212
+ // publication: '',
213
+ // subscriptionData: [],
214
+ // date_start: startDate,
215
+ // date_end: endDate,
216
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
217
+ // result: 'Done'
218
+ // });
219
+ }
220
+
221
+ const queueArr = this._mongoQueue.filter(a => !a.running);
222
+ if (queueArr.length) {
223
+ queueArr.forEach(entry => {
224
+ entry.running = true;
225
+ });
226
+
227
+ for (let i = queueArr.length - 1; i >= 0; i--) {
228
+ this._debugMongoQueueHits += 1;
229
+
230
+ let mongoQueue = queueArr[i];
231
+
232
+ if (!this._debugMongoQueueCollections.some(a => a.collection === mongoQueue.collection && a.publication === mongoQueue.subscription.publication)) {
233
+ this._debugMongoQueueCollections.push({
234
+ collection: mongoQueue.collection,
235
+ publication: mongoQueue.subscription.publication,
236
+ hits: 1
237
+ });
238
+ }
239
+ else {
240
+ this._debugMongoQueueCollections.filter(a => a.collection === mongoQueue.collection && a.publication === mongoQueue.subscription.publication)[0].hits += 1;
241
+ }
242
+
243
+ if (this._publications[mongoQueue.subscription.publication].user_specific) {
244
+ let promises = [];
245
+ for (let j = 0; j < mongoQueue.subscription.clients.length; j++) {
246
+ let client = mongoQueue.subscription.clients[j];
247
+ let ws = this._mainServer.getWS(client.id_socket);
248
+ if (ws && ws['id_socket'] === client.id_socket) {
249
+ promises.push(this.sendDataToOne(ws, client.messageId, mongoQueue.subscription, mongoQueue.type, mongoQueue.collection).catch((err) => {
250
+ this._mainServer.getMethodManager().sendEmail('dev@resolveio.com', 'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'], 'Error Detected During sendDataToOne - User Specific - Socket: ' + client.id_socket + ', User: ' + client.id_user + ', MessageId: ' + client.messageId + ', Pub: ' + mongoQueue.subscription.publication + ', Err: ' + JSON.stringify(err, null, 2));
251
+ return null;
252
+ }));
253
+ }
254
+ }
255
+
256
+ Promise.all(promises).then(() => {
257
+ if (mongoQueue.run_again) {
258
+ mongoQueue.running = false;
259
+ mongoQueue.run_again = false;
260
+ }
261
+ else {
262
+ if (this._mongoQueue.map(a => a._id).indexOf(mongoQueue._id) >= 0) {
263
+ this._mongoQueue.splice(this._mongoQueue.map(a => a._id).indexOf(mongoQueue._id), 1);
264
+ }
265
+ }
266
+ });
267
+ }
268
+ else {
269
+ this.sendDataToAll(mongoQueue).then(() => {
270
+ if (mongoQueue.run_again) {
271
+ mongoQueue.running = false;
272
+ mongoQueue.run_again = false;
273
+ }
274
+ else {
275
+ if (this._mongoQueue.map(a => a._id).indexOf(mongoQueue._id) >= 0) {
276
+ this._mongoQueue.splice(this._mongoQueue.map(a => a._id).indexOf(mongoQueue._id), 1);
277
+ }
278
+ }
279
+ }, () => {
280
+ if (this._mongoQueue.map(a => a._id).indexOf(mongoQueue._id) >= 0) {
281
+ this._mongoQueue.splice(this._mongoQueue.map(a => a._id).indexOf(mongoQueue._id), 1);
282
+ }
283
+ });
284
+ }
285
+ }
286
+
287
+ // let endDate = new Date();
288
+
289
+ // this.currentPerfomanceMonitor.push({
290
+ // _id: this.idPerformance++,
291
+ // function: 'mongoQueue',
292
+ // publication: '',
293
+ // subscriptionData: [],
294
+ // date_start: startDate,
295
+ // date_end: endDate,
296
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
297
+ // result: 'Done'
298
+ // });
299
+ }
300
+
301
+ this._runningQueue = false;
302
+ }
303
+ }, 25);
304
+
305
+ setInterval(async () => {
306
+ this._loggedInUsers = await LoggedInUsers.find();
307
+
308
+ let userCopy = deepCopy(this._loggedInUsers);
309
+ for (let i = this._loggedInUsers.length - 1; i >= 0; i--) {
310
+ let loggedInUser = userCopy[i];
311
+
312
+ if (!loggedInUser.date || Date.now() - loggedInUser.date.getTime() > 120000) {
313
+ if (this._mainServer.getWS(loggedInUser.id_ws)) {
314
+ if (this.getEnableDebug()) {
315
+ console.log(new Date(), 'Sub Manager', 'Unsub WS', this._mainServer.getWS(loggedInUser.id_ws)['user'], this._mainServer.getWS(loggedInUser.id_ws)['id_socket'], 2);
316
+ }
317
+ this.unsubscribeAll(this._mainServer.getWS(loggedInUser.id_ws));
318
+ }
319
+ else {
320
+ this._subscriptions.forEach(sub => {
321
+ for (let j = sub.clients.length - 1; j >= 0; j--) {
322
+ let client = sub.clients[j];
323
+
324
+ if (client.id_socket === loggedInUser.id_ws) {
325
+ sub.clients.splice(j, 1);
326
+ }
327
+ }
328
+ });
329
+
330
+ LoggedInUsers.deleteOne({_id: loggedInUser._id});
331
+
332
+ if (this._loggedInUsers.findIndex(a => a._id === loggedInUser._id) >= 0) {
333
+ this._loggedInUsers.splice(this._loggedInUsers.findIndex(a => a._id === loggedInUser._id), 1);
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ for (let i = 0; i < this._subscriptions.length; i++) {
340
+ let sub = this._subscriptions[i];
341
+
342
+ for (let j = sub.clients.length - 1; j >= 0; j--) {
343
+ let client = sub.clients[j];
344
+
345
+ if (!this._loggedInUsers.some(a => a.id_ws === client.id_socket)) {
346
+ sub.clients.splice(j, 1);
347
+ }
348
+ }
349
+ }
350
+ }, 30000);
351
+
352
+ Flags.findOne({type: 'Enable Debug'}).then(flag => {
353
+ if (flag && flag.value) {
354
+ this._enableDebug = true;
355
+ }
356
+ else {
357
+ this._enableDebug = false;
358
+ }
359
+ });
360
+ }
361
+
362
+ private addToQueue(mongoQueue: MongoQueueModel) {
363
+ // let startDate = new Date();
364
+
365
+ let _mongoQueue = this._mongoQueue.filter(a => a.subscription.publication === mongoQueue.subscription.publication && JSON.stringify(a.subscription.subscriptionData) === JSON.stringify(mongoQueue.subscription.subscriptionData))[0];
366
+
367
+ if (_mongoQueue) {
368
+ if (_mongoQueue.running) {
369
+ _mongoQueue.run_again = true;
370
+
371
+ // let endDate = new Date();
372
+ // this.currentPerfomanceMonitor.push({
373
+ // _id: this.idPerformance++,
374
+ // function: 'addToQueue',
375
+ // publication: mongoQueue.subscription.publication,
376
+ // // subscriptionData: mongoQueue.subscription.subscriptionData,
377
+ // subscriptionData: [],
378
+ // date_start: startDate,
379
+ // date_end: endDate,
380
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
381
+ // result: 'Run Again'
382
+ // });
383
+ }
384
+ // else {
385
+ // let endDate = new Date();
386
+ // this.currentPerfomanceMonitor.push({
387
+ // _id: this.idPerformance++,
388
+ // function: 'addToQueue',
389
+ // publication: mongoQueue.subscription.publication,
390
+ // // subscriptionData: mongoQueue.subscription.subscriptionData,
391
+ // subscriptionData: [],
392
+ // date_start: startDate,
393
+ // date_end: endDate,
394
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
395
+ // result: 'Not Running Yet'
396
+ // });
397
+ // }
398
+ }
399
+ else {
400
+ mongoQueue._id = this._mongoQueueId;
401
+ this._mongoQueueId += 1;
402
+
403
+ this._mongoQueue.splice(0, 0, mongoQueue);
404
+
405
+ // let endDate = new Date();
406
+ // this.currentPerfomanceMonitor.push({
407
+ // _id: this.idPerformance++,
408
+ // function: 'addToQueue',
409
+ // publication: mongoQueue.subscription.publication,
410
+ // // subscriptionData: mongoQueue.subscription.subscriptionData,
411
+ // subscriptionData: [],
412
+ // date_start: startDate,
413
+ // date_end: endDate,
414
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
415
+ // result: 'Add to Queue'
416
+ // });
417
+ }
418
+ }
419
+
420
+ public invalidatePubsCache(collection, type) {
421
+ // let startDate = new Date();
422
+
423
+ ResolveIOServer.getMongoManager().invalidateQueryCache(collection);
424
+
425
+ let collSubs = this._subscriptions.filter(a => a.collections.includes(collection));
426
+
427
+ for (let i = collSubs.length - 1; i >= 0; i--) {
428
+ let sub = collSubs[i];
429
+
430
+ this.addToQueue({
431
+ _id: 0,
432
+ type: type,
433
+ collection: collection,
434
+ subscription: sub,
435
+ running: false,
436
+ run_again: false
437
+ });
438
+ }
439
+
440
+ // let endDate = new Date();
441
+ // this.currentPerfomanceMonitor.push({
442
+ // _id: this.idPerformance++,
443
+ // function: 'invalidatePubsCache',
444
+ // publication: collection,
445
+ // subscriptionData: [],
446
+ // date_start: startDate,
447
+ // date_end: endDate,
448
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
449
+ // result: 'Done'
450
+ // });
451
+ }
452
+
453
+ // Add all files to publications private object
454
+ public publications(method: SubscriptionModel) {
455
+ this._publications = Object.assign(this._publications, method);
456
+ }
457
+
458
+ public loggedInLatency(ws: WebSocket) {
459
+ let loggedInUser = this._loggedInUsers.filter(a => a.id_ws === ws['id_socket'])[0];
460
+
461
+ if (loggedInUser) {
462
+ loggedInUser.date = new Date();
463
+
464
+ LoggedInUsers.updateOne({id_ws: ws['id_socket']}, {$set: {latency: ws['latency'], date: loggedInUser.date}}).then(res => {
465
+ if (!res) {
466
+ this.unsubscribeAll(ws);
467
+ if (this.getEnableDebug()) {
468
+ console.log(new Date(), 'Sub Manager', 'Unsub WS', ws['user'], ws['id_socket'], 3);
469
+ }
470
+ }
471
+ }, () => {
472
+ this.unsubscribeAll(ws);
473
+ if (this.getEnableDebug()) {
474
+ console.log(new Date(), 'Sub Manager', 'Unsub WS', ws['user'], ws['id_socket'], 4);
475
+ }
476
+ });
477
+ }
478
+ }
479
+
480
+ // Subscribe to publication
481
+ public subscribe(messageRoute: string, messageDate: Date, ws: WebSocket, messageId: number, publication: string, subscriptionData: any[]) {
482
+ // let startDate = new Date();
483
+ this._debugSubHits += 1;
484
+
485
+ if (!this._debugSubCollections.some(a => a.publication === publication)) {
486
+ this._debugSubCollections.push({
487
+ publication: publication,
488
+ hits: 1
489
+ });
490
+ }
491
+ else {
492
+ this._debugSubCollections.filter(a => a.publication === publication)[0].hits += 1;
493
+ }
494
+
495
+ let pub = this._publications[publication];
496
+
497
+ if (!pub) {
498
+ console.error(new Date(), 'No Publication: ' + publication);
499
+ return;
500
+ }
501
+ else {
502
+ if (subscriptionData.length > 1 || subscriptionData[0]) {
503
+ if (!pub.check) {
504
+ console.error(new Date(), 'No Check Function For Pub ' + publication);
505
+ return;
506
+ }
507
+ else if (!pub.check._schema) {
508
+ console.error(new Date(), 'No Check Schema For Pub ' + publication);
509
+ return;
510
+ }
511
+ else {
512
+ let valObj = {};
513
+ let valKeys = Object.keys(pub.check._schema);
514
+
515
+ let rootKeys = valKeys.filter(a => !a.includes('.'));
516
+
517
+ for (let i = 0; i < subscriptionData.length; i++) {
518
+ valObj[rootKeys[i]] = subscriptionData[i];
519
+ }
520
+
521
+ try {
522
+ pub.check.validate(valObj);
523
+ }
524
+ catch (errors) {
525
+ if (errors) {
526
+ console.error(new Date(), 'Error in Pub Check (' + publication + ')', errors);
527
+ return;
528
+ }
529
+ }
530
+ }
531
+ }
532
+
533
+ if (messageRoute !== 'Bypass') {
534
+ let urlData = messageRoute.split('/');
535
+ let urlModule = '';
536
+ let urlNext = urlData[0];
537
+
538
+ if (urlData[0] === '') {
539
+ urlModule = '/';
540
+ urlNext = urlData[1];
541
+ }
542
+
543
+ urlModule += urlNext;
544
+
545
+ if (urlData.length > 1) {
546
+ urlModule += '/';
547
+ }
548
+
549
+ let otherRouteSubs = this._subscriptions.filter(a => a.clients.some(b => b.id_socket === ws['id_socket'] && b.messageRoute !== 'Bypass' && b.messageRoute !== '/' && b.messageRoute !== messageRoute && !b.messageRoute.startsWith(urlModule)));
550
+
551
+ if (otherRouteSubs.length) {
552
+ this._mainServer.getMethodManager().sendEmail('dev@resolveio.com', 'SERVER - Detected Undestroyed Subscription - ' + this.serverConfig['CLIENT_NAME'], 'USER: ' + ws['user'] + ' (Socket: ' + ws['id_socket'] + ') ' + ' is on route: ' + messageRoute + ' but has the following subscriptions on other routes:' + JSON.stringify(otherRouteSubs, null, 2));
553
+
554
+ otherRouteSubs.forEach(otherSub => {
555
+ otherSub.clients.filter(a => a.id_socket === ws['id_socket']).forEach(client => {
556
+ this.unsubscribe(client.messageRoute, new Date(), ws, client.messageId, otherSub.publication, otherSub.subscriptionData);
557
+ });
558
+ });
559
+ }
560
+ }
561
+
562
+ let sub = this._subscriptions.filter(a => a.publication === publication && JSON.stringify(a.subscriptionData) === JSON.stringify(subscriptionData))[0];
563
+
564
+ // If sub found (another user watching same data), add client to same sub
565
+ if (sub) {
566
+ if (!sub.clients.some(a => a.id_socket === ws['id_socket'] && a.messageId === messageId)) {
567
+ sub.clients.push({
568
+ id_user: ws['id_user'],
569
+ messageId: messageId,
570
+ id_socket: ws['id_socket'],
571
+ messageRoute: messageRoute
572
+ });
573
+ }
574
+ }
575
+ // If sub not found, create new sub
576
+ else {
577
+ this._subscriptions.push({
578
+ publication: publication,
579
+ subscriptionData: subscriptionData,
580
+ collections: this.getPublicationCollections(publication),
581
+ clients: [{
582
+ id_user: ws['id_user'],
583
+ messageId: messageId,
584
+ id_socket: ws['id_socket'],
585
+ messageRoute: messageRoute
586
+ }],
587
+ cacheId: 0
588
+ });
589
+ }
590
+
591
+ if (!sub) {
592
+ sub = this._subscriptions.filter(a => a.publication === publication && JSON.stringify(a.subscriptionData) === JSON.stringify(subscriptionData))[0];
593
+ }
594
+
595
+ if (!sub.cacheId && !this._mongoQueue.some(a => a.subscription.publication === sub.publication && JSON.stringify(a.subscription.subscriptionData) === JSON.stringify(sub.subscriptionData))) {
596
+ this.addToQueue({
597
+ _id: 0,
598
+ type: 'newSub',
599
+ collection: '',
600
+ subscription: sub,
601
+ running: false,
602
+ run_again: false
603
+ });
604
+
605
+ // let endDate = new Date();
606
+ // this.currentPerfomanceMonitor.push({
607
+ // _id: this.idPerformance++,
608
+ // function: 'subscribe',
609
+ // publication: publication,
610
+ // // subscriptionData: subscriptionData,
611
+ // subscriptionData: [],
612
+ // date_start: startDate,
613
+ // date_end: endDate,
614
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
615
+ // result: 'Add to Queue'
616
+ // });
617
+ }
618
+ else if (sub.cacheId) {
619
+ this.sendDataToOne(ws, messageId, sub, 'newSub', publication);
620
+
621
+ // let endDate = new Date();
622
+ // this.currentPerfomanceMonitor.push({
623
+ // _id: this.idPerformance++,
624
+ // function: 'subscribe',
625
+ // publication: publication,
626
+ // // subscriptionData: subscriptionData,
627
+ // subscriptionData: [],
628
+ // date_start: startDate,
629
+ // date_end: endDate,
630
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
631
+ // result: 'Send Pub Data Once'
632
+ // });
633
+ }
634
+ }
635
+ }
636
+
637
+ public async createLoggedInUser(id_ws: string): Promise<LoggedInUserModel> {
638
+ return new Promise(async (resolve, reject) => {
639
+ let ws = this._mainServer.getWS(id_ws);
640
+
641
+ if (ws) {
642
+ let user = {
643
+ _id: objectIdHexString(),
644
+ __v: 0,
645
+ date: new Date(),
646
+ id_user: ws['id_user'],
647
+ user: ws['user'],
648
+ id_ws: ws['id_socket']
649
+ };
650
+
651
+ this._loggedInUsers.push(user);
652
+ LoggedInUsers.insertOne(user);
653
+
654
+ resolve(user);
655
+ }
656
+ else {
657
+ resolve(null);
658
+ }
659
+ });
660
+ }
661
+
662
+ // Unsubscribe from publication
663
+ public unsubscribe(messageRoute: string, messageDate: Date, ws: WebSocket, messageId: number, publication: string, subscriptionData: any[]) {
664
+ this._debugUnSubHits += 1;
665
+ // let startDate = new Date();
666
+ // console.log('Before');
667
+ // console.dir(this._subscriptions.map(a => a.publication));
668
+
669
+ if (!this._publications[publication]) {
670
+ console.log('No Publication: ' + publication);
671
+ return;
672
+ }
673
+ else {
674
+ let sub = this._subscriptions.filter(a => a.publication === publication && JSON.stringify(a.subscriptionData) === JSON.stringify(subscriptionData))[0];
675
+
676
+ if (sub) {
677
+ for (let i = sub.clients.length - 1; i >= 0; i--) {
678
+ if (sub.clients[i].id_user === ws['id_user'] && sub.clients[i].messageId === messageId && sub.clients[i].id_socket === ws['id_socket']) {
679
+ sub.clients.splice(i, 1);
680
+ }
681
+ }
682
+ }
683
+ }
684
+
685
+ // let endDate = new Date();
686
+ // this.currentPerfomanceMonitor.push({
687
+ // _id: this.idPerformance++,
688
+ // function: 'unsubscribe',
689
+ // publication: publication,
690
+ // // subscriptionData: subscriptionData,
691
+ // subscriptionData: [],
692
+ // date_start: startDate,
693
+ // date_end: endDate,
694
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
695
+ // result: 'Done'
696
+ // });
697
+ }
698
+
699
+ // Unsubscribe from publication
700
+ public async unsubscribeAll(ws: WebSocket) {
701
+ this._debugUnSubAllHits += 1;
702
+ // console.log('Before');
703
+ // console.dir(this._subscriptions.map(a => a.publication));
704
+
705
+ // let startDate = new Date();
706
+
707
+ if (ws) {
708
+ let loggedInUser = await LoggedInUsers.findOne({id_ws: ws['id_socket']});
709
+
710
+ if (loggedInUser) {
711
+ if (this._loggedInUsers.map(a => a._id).indexOf(loggedInUser._id) >= 0) {
712
+ this._loggedInUsers.splice(this._loggedInUsers.map(a => a._id).indexOf(loggedInUser._id), 1);
713
+ }
714
+
715
+ LoggedInUsers.deleteOne({_id: loggedInUser._id});
716
+ }
717
+
718
+ let userSubs = this._subscriptions.filter(a => a.clients.some(b => b.id_user === ws['id_user'] && b.id_socket === ws['id_socket']));
719
+
720
+ for (let i = userSubs.length - 1; i >= 0; i--) {
721
+ let sub = userSubs[i];
722
+
723
+ for (let j = sub.clients.length - 1; j >= 0; j--) {
724
+ if (sub.clients[j].id_socket === ws['id_socket']) {
725
+ sub.clients.splice(j, 1);
726
+ }
727
+ }
728
+ }
729
+
730
+ ws.close();
731
+
732
+ process.nextTick(() => {
733
+ if ([ws.OPEN, ws.CLOSING].includes(<any>ws.readyState)) {
734
+ // Socket still hangs, hard close
735
+ ws.terminate();
736
+ }
737
+ });
738
+
739
+ // let endDate = new Date();
740
+ // this.currentPerfomanceMonitor.push({
741
+ // _id: this.idPerformance++,
742
+ // function: 'unsubscribeAll',
743
+ // publication: '',
744
+ // subscriptionData: [],
745
+ // date_start: startDate,
746
+ // date_end: endDate,
747
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
748
+ // result: 'Done'
749
+ // });
750
+ }
751
+
752
+ // console.log('After');
753
+ // console.dir(this._subscriptions.map(a => a.publication));
754
+ }
755
+
756
+ public getActiveSubscriptions() {
757
+ return this._subscriptions;
758
+ }
759
+
760
+ // Get publication collection
761
+ private getPublicationCollections(publication: string) {
762
+ return this._publications[publication].collections;
763
+ }
764
+
765
+ // Watch (tail) Mongo's operation log on the entire database (all insert/modify/delete will trigger this function)
766
+ private tailOpLog(resumeToken?: ResumeToken) {
767
+ if (!this._oplog || this._oplog.closed) {
768
+ this._oplogRetryCount += 1;
769
+
770
+ if (this._oplogRetryCount > 10) {
771
+ console.error('****************** TAIL OPLOG ERROR, RETRYING A BUNCH OF TIMES, KILLING PROCESS **************************')
772
+ process.exit(1);
773
+ }
774
+
775
+ let lastResumeToken: ResumeToken;
776
+
777
+ if (resumeToken) {
778
+ lastResumeToken = resumeToken;
779
+ try {
780
+ this._oplog = ResolveIOServer.getMainDB().watch([], {resumeAfter: resumeToken});
781
+ }
782
+ catch(errOp) {
783
+ if (this._oplog && !this._oplog.closed) {
784
+ this._oplog.close();
785
+ }
786
+ else {
787
+ this._oplog = ResolveIOServer.getMainDB().watch();
788
+ }
789
+ }
790
+ }
791
+ else {
792
+ this._oplog = ResolveIOServer.getMainDB().watch();
793
+ }
794
+
795
+ console.log(new Date(), 'oplog started');
796
+
797
+ this._oplog.on('change', (doc: ChangeStreamInsertDocument | ChangeStreamUpdateDocument | ChangeStreamReplaceDocument | ChangeStreamDeleteDocument) => {
798
+ // let startDate = new Date();
799
+
800
+ if (doc.ns) {
801
+ let collection = doc.ns.coll;
802
+
803
+ if (!this._debugOplogCollections.some(a => a.collection === doc.ns.coll && a.type === doc.operationType)) {
804
+ this._debugOplogCollections.push({
805
+ collection: doc.ns.coll,
806
+ type: doc.operationType,
807
+ hits: 1
808
+ });
809
+ }
810
+ else {
811
+ this._debugOplogCollections.filter(a => a.collection === doc.ns.coll && a.type === doc.operationType)[0].hits += 1;
812
+ }
813
+
814
+ // console.log(new Date(), 'Op log doc', doc.operationType, collection);
815
+
816
+ if (collection && !collection.endsWith('.versions') && !collection.startsWith('monitor-') && collection !== 'logs' && collection !== 'log-method-latencies' && collection !== 'log-subscriptions') {
817
+ this._debugOplogHits += 1;
818
+
819
+ if (doc.operationType === 'insert') {
820
+ if (collection === 'support-tickets') {
821
+ if (this.serverConfig['ROOT_URL'] === 'https://resolveio.com') {
822
+ this._mainServer.getMethodManager().callMethodInternal.call(this._mainServer.getMethodManager(), 'sendSupportTicketEmail', doc.documentKey['_id']);
823
+ this.invalidatePubsCache(collection, 'insert');
824
+ }
825
+ }
826
+
827
+ if (collection !== 'method-responses') {
828
+ this.invalidatePubsCache(collection, 'insert');
829
+ }
830
+
831
+ // let endDate = new Date();
832
+ // this.currentPerfomanceMonitor.push({
833
+ // _id: this.idPerformance++,
834
+ // function: 'oplog',
835
+ // publication: collection,
836
+ // subscriptionData: [],
837
+ // date_start: startDate,
838
+ // date_end: endDate,
839
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
840
+ // result: 'Insert'
841
+ // });
842
+ }
843
+ else if (doc.operationType === 'update' || doc.operationType === 'replace') {
844
+ if (collection !== 'method-responses') {
845
+ this.invalidatePubsCache(collection, 'update');
846
+ }
847
+
848
+ // let endDate = new Date();
849
+ // this.currentPerfomanceMonitor.push({
850
+ // _id: this.idPerformance++,
851
+ // function: 'oplog',
852
+ // publication: collection,
853
+ // subscriptionData: [],
854
+ // date_start: startDate,
855
+ // date_end: endDate,
856
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
857
+ // result: 'Update'
858
+ // });
859
+ }
860
+ else if (doc.operationType === 'delete') {
861
+ if (collection !== 'method-responses') {
862
+ this.invalidatePubsCache(collection, 'delete');
863
+ }
864
+
865
+ // let endDate = new Date();
866
+ // this.currentPerfomanceMonitor.push({
867
+ // _id: this.idPerformance++,
868
+ // function: 'oplog',
869
+ // publication: collection,
870
+ // subscriptionData: [],
871
+ // date_start: startDate,
872
+ // date_end: endDate,
873
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
874
+ // result: 'Delete'
875
+ // });
876
+ }
877
+ }
878
+
879
+ if (collection === 'flags') {
880
+ Flags.findOne({type: 'Enable Debug'}).then(flag => {
881
+ if (flag && flag.value) {
882
+ this._enableDebug = true;
883
+ }
884
+ else {
885
+ this._enableDebug = false;
886
+ }
887
+ });
888
+ }
889
+
890
+ lastResumeToken = doc._id;
891
+ }
892
+ });
893
+
894
+ this._oplog.on('error', error => {
895
+ console.log(new Date(), 'oplog error', error);
896
+ this._oplog.close();
897
+ });
898
+
899
+ this._oplog.on('end', () => {
900
+ console.log(new Date(), 'oplog end');
901
+ this._oplog.close();
902
+ });
903
+
904
+ this._oplog.on('close', () => {
905
+ this._oplog = null;
906
+
907
+ console.log(new Date(), 'oplog close');
908
+ this.tailOpLog(lastResumeToken);
909
+ });
910
+ }
911
+ else {
912
+ this._oplog.close();
913
+ }
914
+ }
915
+
916
+ // Fetch pub once, send to all clients linked to this pub
917
+ private sendDataToOne(ws: WebSocket, messageId: number, subscription: ActiveSubscriptionModel, type: string, collection: string) {
918
+ return new Promise(async (resolve, reject) => {
919
+ // let startDate = new Date();
920
+
921
+ if (!this._publications[subscription.publication].user_specific) { // Same pub for all users
922
+ if (subscription.cacheId) {
923
+ try {
924
+ let cacheData = JSON.parse(this._nodeCache.get(subscription.cacheId), dateReviver);
925
+
926
+ let serverRes: ServerResponseModel = {
927
+ messageId: messageId,
928
+ hasError: false,
929
+ data: cacheData
930
+ };
931
+
932
+ this.sendWS(ws, serverRes);
933
+ }
934
+ catch(err) {
935
+ this._nodeCache.del(subscription.cacheId);
936
+ subscription.cacheId = 0;
937
+
938
+ this.addToQueue({
939
+ _id: 0,
940
+ type: 'newSub',
941
+ collection: '',
942
+ subscription: subscription,
943
+ running: false,
944
+ run_again: false
945
+ });
946
+ }
947
+
948
+ // let endDate = new Date();
949
+ // this.currentPerfomanceMonitor.push({
950
+ // _id: this.idPerformance++,
951
+ // function: 'sendDataToOne',
952
+ // publication: subscription.publication,
953
+ // // subscriptionData: subscription.subscriptionData,
954
+ // subscriptionData: [],
955
+ // date_start: startDate,
956
+ // date_end: endDate,
957
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
958
+ // result: 'Send cache'
959
+ // });
960
+
961
+ resolve(true);
962
+ }
963
+ else {
964
+ this._mainServer.getMethodManager().sendEmail('dev@resolveio.com', 'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'], 'Error Detected During sendDataToOne - No Cache! - ' + subscription.publication + '\n\nData \n' + JSON.stringify(subscription.subscriptionData, null, 2));
965
+
966
+ resolve(false);
967
+ }
968
+ }
969
+ else { // Pub is different for each user (Look at users publication) [MUCH SLOWER, RE-RUNS FOR EACH USER]
970
+ let monitor = this._monitorManagerFunction.startMonitorFunction('User Specific Publication', subscription.publication, '', '', subscription.subscriptionData);
971
+ this._publications[subscription.publication].function.call(Object.assign({}, this, SubscriptionManager.prototype), ws['id_user'], ...subscription.subscriptionData)
972
+ .then(res => {
973
+ this._monitorManagerFunction.finishMonitorFunction(monitor);
974
+ let serverRes: ServerResponseModel = {
975
+ messageId: messageId,
976
+ hasError: false,
977
+ data: res
978
+ };
979
+
980
+ // console.log('Send Pub Data Once 2', new Date(), 'Success', subscription);
981
+
982
+ this.sendWS(ws, serverRes);
983
+
984
+ // let endDate = new Date();
985
+ // this.currentPerfomanceMonitor.push({
986
+ // _id: this.idPerformance++,
987
+ // function: 'sendDataToOne',
988
+ // publication: subscription.publication,
989
+ // // subscriptionData: subscription.subscriptionData,
990
+ // subscriptionData: [],
991
+ // date_start: startDate,
992
+ // date_end: endDate,
993
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
994
+ // result: 'Send ws spec data'
995
+ // });
996
+
997
+ resolve(true);
998
+
999
+ }, err => {
1000
+ this._monitorManagerFunction.finishMonitorFunction(monitor);
1001
+
1002
+ let serverRes: ServerResponseModel = {
1003
+ messageId: messageId,
1004
+ hasError: true,
1005
+ data: err
1006
+ };
1007
+
1008
+ // console.log('Send Pub Data Once 2', new Date(), 'Error', subscription);
1009
+
1010
+ this.sendWS(ws, serverRes);
1011
+
1012
+ this._mainServer.getMethodManager().sendEmail('dev@resolveio.com', 'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'], 'Error Detected During Subscription ' + subscription.publication + ' - (sendDataToOne - WS)\n\nData \n' + JSON.stringify(subscription.subscriptionData, null, 2) + '\n\nErrors\n' + JSON.stringify(err, null, 2));
1013
+
1014
+ reject(err);
1015
+ });
1016
+ }
1017
+ });
1018
+ }
1019
+
1020
+ // Fetch pub once, send to all clients linked to this pub
1021
+ private sendDataToAll(mongoQueue: MongoQueueModel) {
1022
+ return new Promise(async (resolve, reject) => {
1023
+ // let startDate = new Date();
1024
+ if (!mongoQueue.subscription.clients.length) {
1025
+ if (mongoQueue.subscription.cacheId) {
1026
+ this._nodeCache.del(mongoQueue.subscription.cacheId);
1027
+ }
1028
+
1029
+ if (this._subscriptions.findIndex(a => a.publication === mongoQueue.subscription.publication && JSON.stringify(a.subscriptionData) === JSON.stringify(mongoQueue.subscription.subscriptionData)) >= 0) {
1030
+ this._subscriptions.splice(this._subscriptions.findIndex(a => a.publication === mongoQueue.subscription.publication && JSON.stringify(a.subscriptionData) === JSON.stringify(mongoQueue.subscription.subscriptionData)), 1);
1031
+ }
1032
+
1033
+ resolve(false);
1034
+ }
1035
+ else {
1036
+ if (mongoQueue.subscription.publication !== 'superadminAPM' && mongoQueue.subscription.publication !== 'loggedInUsers') {
1037
+ this._mainServer.getMethodManager().callMethodInternal.call(this._mainServer.getMethodManager(), 'insertSubscriptionLog', mongoQueue.type, mongoQueue.subscription.publication, mongoQueue.collection, JSON.stringify(mongoQueue.subscription.subscriptionData));
1038
+ }
1039
+
1040
+ let monitor = this._monitorManagerFunction.startMonitorFunction('Publication', mongoQueue.subscription.publication, '', '', mongoQueue.subscription.subscriptionData);
1041
+ this._publications[mongoQueue.subscription.publication].function.call(Object.assign({}, this, SubscriptionManager.prototype), ...mongoQueue.subscription.subscriptionData)
1042
+ .then(res => {
1043
+ this._monitorManagerFunction.finishMonitorFunction(monitor);
1044
+ if (!mongoQueue.subscription.clients.length) {
1045
+ let nodeCacheSize = this._nodeCache.getStats().vsize;
1046
+
1047
+ if (nodeCacheSize > this._heapLimit) {
1048
+ let deleteCount = 0;
1049
+
1050
+ const subArr = this._subscriptions.filter(a => a.cacheId && !a.clients.length);
1051
+
1052
+ for (let zz = 0; zz < subArr.length; zz++) {
1053
+ this._debugRemoveCacheHits += 1;
1054
+
1055
+ this._nodeCache.del(subArr[zz].cacheId);
1056
+ subArr[zz].cacheId = 0;
1057
+
1058
+ deleteCount += 1;
1059
+
1060
+ nodeCacheSize = this._nodeCache.getStats().vsize;
1061
+
1062
+ if (nodeCacheSize < this._heapLimit * 0.75) {
1063
+ break;
1064
+ }
1065
+ }
1066
+
1067
+ console.log('Sub Cache: ' + 'Too Big - ' + mongoQueue.subscription.publication + ' - Deleted: ' + deleteCount + ' - ' + nodeCacheSize);
1068
+ }
1069
+
1070
+ if (mongoQueue.subscription.cacheId) {
1071
+ this._nodeCache.del(mongoQueue.subscription.cacheId);
1072
+ }
1073
+
1074
+ let cacheId = this._cacheId;
1075
+ this._cacheId += 1;
1076
+
1077
+ this._nodeCache.set(cacheId, JSON.stringify(res));
1078
+ mongoQueue.subscription.cacheId = cacheId;
1079
+
1080
+ // let endDate = new Date();
1081
+ // this.currentPerfomanceMonitor.push({
1082
+ // _id: this.idPerformance++,
1083
+ // function: 'sendPubData',
1084
+ // publication: subscription.publication,
1085
+ // // subscriptionData: subscription.subscriptionData,
1086
+ // subscriptionData: [],
1087
+ // date_start: startDate,
1088
+ // date_end: endDate,
1089
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
1090
+ // result: 'No clients'
1091
+ // });
1092
+
1093
+ resolve(false);
1094
+ }
1095
+ else {
1096
+ if (!mongoQueue.subscription.cacheId || this._nodeCache.get(mongoQueue.subscription.cacheId) !== JSON.stringify(res)) {
1097
+ mongoQueue.subscription.clients.forEach(client => {
1098
+ let ws = this._mainServer.getWS(client.id_socket);
1099
+
1100
+ if (ws) {
1101
+ let serverRes: ServerResponseModel = {
1102
+ messageId: client.messageId,
1103
+ hasError: false,
1104
+ data: res
1105
+ };
1106
+
1107
+ this.sendWS(ws, serverRes);
1108
+ }
1109
+ });
1110
+
1111
+ let nodeCacheSize = this._nodeCache.getStats().vsize;
1112
+
1113
+ if (nodeCacheSize > this._heapLimit) {
1114
+ let deleteCount = 0;
1115
+
1116
+ const subArr = this._subscriptions.filter(a => a.cacheId && !a.clients.length);
1117
+
1118
+ for (let zz = 0; zz < subArr.length; zz++) {
1119
+ this._nodeCache.del(subArr[zz].cacheId);
1120
+ subArr[zz].cacheId = 0;
1121
+
1122
+ deleteCount += 1;
1123
+
1124
+ nodeCacheSize = this._nodeCache.getStats().vsize;
1125
+
1126
+ if (nodeCacheSize < this._heapLimit * 0.75) {
1127
+ break;
1128
+ }
1129
+ }
1130
+
1131
+ console.log('Sub Cache: ' + 'Too Big - ' + mongoQueue.subscription.publication + ' - Deleted: ' + deleteCount + ' - ' + nodeCacheSize);
1132
+ }
1133
+
1134
+ if (mongoQueue.subscription.cacheId) {
1135
+ this._nodeCache.del(mongoQueue.subscription.cacheId);
1136
+ }
1137
+
1138
+ let cacheId = this._cacheId;
1139
+ this._cacheId += 1;
1140
+
1141
+ this._nodeCache.set(cacheId, JSON.stringify(res));
1142
+ mongoQueue.subscription.cacheId = cacheId;
1143
+
1144
+ // let endDate = new Date();
1145
+ // this.currentPerfomanceMonitor.push({
1146
+ // _id: this.idPerformance++,
1147
+ // function: 'sendPubData',
1148
+ // publication: subscription.publication,
1149
+ // // subscriptionData: subscription.subscriptionData,
1150
+ // subscriptionData: [],
1151
+ // date_start: startDate,
1152
+ // date_end: endDate,
1153
+ // duration: moment(endDate).diff(moment(startDate), 'milliseconds', false),
1154
+ // result: 'Same data'
1155
+ // });
1156
+ }
1157
+
1158
+ resolve(true);
1159
+ }
1160
+ }, err => {
1161
+ this._monitorManagerFunction.finishMonitorFunction(monitor);
1162
+ this._mainServer.getMethodManager().sendEmail('dev@resolveio.com', 'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'], 'Error Detected During Subscription ' + mongoQueue.subscription.publication + ' - (sendPubData)\n\nData \n' + JSON.stringify(mongoQueue.subscription.subscriptionData, null, 2) + '\n\nErrors\n' + JSON.stringify(err, null, 2));
1163
+
1164
+ mongoQueue.subscription.clients.forEach(client => {
1165
+ let ws = this._mainServer.getWS(client.id_socket);
1166
+
1167
+ if (ws) {
1168
+ if (ws['id_socket'] === client.id_socket) {
1169
+ let serverRes: ServerResponseModel = {
1170
+ messageId: client.messageId,
1171
+ hasError: true,
1172
+ data: err
1173
+ };
1174
+
1175
+ this.sendWS(ws, serverRes);
1176
+ }
1177
+ }
1178
+ });
1179
+
1180
+ reject(err);
1181
+ });
1182
+ }
1183
+ });
1184
+ }
1185
+
1186
+ private sendWS(ws: WebSocket, data: ServerResponseModel) {
1187
+ this._sendQueue.splice(0, 0, {
1188
+ id_ws: ws['id_socket'],
1189
+ data: data
1190
+ });
1191
+ }
1192
+
1193
+ public getEnableDebug() {
1194
+ return this._enableDebug;
1195
+ }
1196
+ }