@kalisio/kdk 2.1.5 → 2.1.7

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 (799) hide show
  1. package/.codeclimate.yml +34 -34
  2. package/.nycrc +5 -5
  3. package/.travis.doc.sh +8 -8
  4. package/.travis.test.sh +40 -40
  5. package/CHANGELOG.md +727 -727
  6. package/LICENSE +21 -21
  7. package/README.md +32 -32
  8. package/core/api/application.js +591 -591
  9. package/core/api/authentication.js +203 -203
  10. package/core/api/db.js +226 -226
  11. package/core/api/hooks/hooks.authentication.js +73 -73
  12. package/core/api/hooks/hooks.authorisations.js +376 -376
  13. package/core/api/hooks/hooks.groups.js +48 -48
  14. package/core/api/hooks/hooks.logger.js +26 -26
  15. package/core/api/hooks/hooks.model.js +290 -290
  16. package/core/api/hooks/hooks.organisations.js +156 -156
  17. package/core/api/hooks/hooks.push.js +56 -56
  18. package/core/api/hooks/hooks.query.js +246 -246
  19. package/core/api/hooks/hooks.schemas.js +73 -73
  20. package/core/api/hooks/hooks.service.js +78 -78
  21. package/core/api/hooks/hooks.storage.js +36 -36
  22. package/core/api/hooks/hooks.users.js +261 -261
  23. package/core/api/hooks/index.js +12 -12
  24. package/core/api/index.js +21 -21
  25. package/core/api/marshall.js +90 -90
  26. package/core/api/models/groups.model.mongodb.js +8 -8
  27. package/core/api/models/organisations.model.mongodb.js +3 -3
  28. package/core/api/models/tags.model.mongodb.js +10 -10
  29. package/core/api/models/users.model.mongodb.js +10 -10
  30. package/core/api/services/account/account.hooks.js +37 -37
  31. package/core/api/services/account/account.service.js +117 -117
  32. package/core/api/services/authorisations/authorisations.hooks.js +33 -33
  33. package/core/api/services/authorisations/authorisations.service.js +139 -139
  34. package/core/api/services/databases/databases.hooks.js +36 -36
  35. package/core/api/services/databases/databases.service.js +5 -5
  36. package/core/api/services/groups/groups.hooks.js +31 -31
  37. package/core/api/services/index.js +126 -126
  38. package/core/api/services/mailer/mailer.hooks.js +35 -35
  39. package/core/api/services/mailer/mailer.service.js +11 -11
  40. package/core/api/services/organisations/organisations.hooks.js +31 -31
  41. package/core/api/services/organisations/organisations.service.js +86 -86
  42. package/core/api/services/push/push.hooks.js +35 -35
  43. package/core/api/services/push/push.service.js +12 -12
  44. package/core/api/services/storage/storage.hooks.js +35 -35
  45. package/core/api/services/storage/storage.service.js +29 -29
  46. package/core/api/services/tags/tags.hooks.js +31 -31
  47. package/core/api/services/users/users.hooks.js +75 -75
  48. package/core/api/utils.js +11 -11
  49. package/core/client/api.js +293 -293
  50. package/core/client/capabilities.js +22 -22
  51. package/core/client/components/KAction.vue +393 -393
  52. package/core/client/components/KAvatar.vue +129 -129
  53. package/core/client/components/KBlock.vue +67 -67
  54. package/core/client/components/KChip.vue +68 -68
  55. package/core/client/components/KChipsPane.vue +103 -103
  56. package/core/client/components/KContent.vue +105 -105
  57. package/core/client/components/KDialog.vue +160 -160
  58. package/core/client/components/KExpandable.vue +49 -49
  59. package/core/client/components/KLogo.vue +32 -32
  60. package/core/client/components/KModal.vue +173 -173
  61. package/core/client/components/KPanel.vue +64 -64
  62. package/core/client/components/KScrollArea.vue +88 -88
  63. package/core/client/components/KSponsor.vue +40 -40
  64. package/core/client/components/KStamp.vue +65 -65
  65. package/core/client/components/KStore.vue +102 -102
  66. package/core/client/components/KTextArea.vue +143 -143
  67. package/core/client/components/KTree.vue +31 -31
  68. package/core/client/components/KVersion.vue +19 -19
  69. package/core/client/components/account/KAccount.vue +68 -68
  70. package/core/client/components/account/KDeleteAccountManager.vue +62 -62
  71. package/core/client/components/account/KEmailManager.vue +128 -128
  72. package/core/client/components/account/KPasswordManager.vue +90 -90
  73. package/core/client/components/account/KProfile.vue +109 -109
  74. package/core/client/components/account/KResetPassword.vue +120 -120
  75. package/core/client/components/account/KSendResetPassword.vue +97 -97
  76. package/core/client/components/account/KSubscription.vue +71 -71
  77. package/core/client/components/account/KSubscriptionsManager.vue +46 -46
  78. package/core/client/components/account/KVerifyEmailManager.vue +105 -105
  79. package/core/client/components/account/index.js +7 -7
  80. package/core/client/components/app/KAbout.vue +98 -98
  81. package/core/client/components/app/KHome.vue +12 -12
  82. package/core/client/components/app/KPlatform.vue +55 -55
  83. package/core/client/components/app/KSettings.vue +42 -42
  84. package/core/client/components/app/KTerms.vue +41 -41
  85. package/core/client/components/app/KTour.vue +448 -448
  86. package/core/client/components/app/KWelcome.vue +133 -133
  87. package/core/client/components/app/index.js +11 -11
  88. package/core/client/components/chart/KChart.vue +197 -197
  89. package/core/client/components/chart/KDataTable.vue +183 -183
  90. package/core/client/components/chart/KStatisticsChart.vue +94 -94
  91. package/core/client/components/chart/KTimeSeriesChart.vue +431 -431
  92. package/core/client/components/chart/index.js +9 -9
  93. package/core/client/components/collection/KBoard.vue +65 -65
  94. package/core/client/components/collection/KCard.vue +196 -196
  95. package/core/client/components/collection/KCardSection.vue +52 -52
  96. package/core/client/components/collection/KColumn.vue +229 -225
  97. package/core/client/components/collection/KFilter.vue +158 -158
  98. package/core/client/components/collection/KGrid.vue +121 -121
  99. package/core/client/components/collection/KHistory.vue +118 -112
  100. package/core/client/components/collection/KHistoryEntry.vue +111 -111
  101. package/core/client/components/collection/KItem.vue +128 -128
  102. package/core/client/components/collection/KList.vue +135 -135
  103. package/core/client/components/collection/KSorter.vue +46 -46
  104. package/core/client/components/collection/KTable.vue +238 -238
  105. package/core/client/components/collection/index.js +19 -19
  106. package/core/client/components/editor/KEditor.vue +52 -52
  107. package/core/client/components/editor/KModalEditor.vue +96 -96
  108. package/core/client/components/editor/index.js +7 -7
  109. package/core/client/components/form/KChipsField.vue +162 -162
  110. package/core/client/components/form/KColorField.vue +69 -69
  111. package/core/client/components/form/KColorScaleField.vue +86 -86
  112. package/core/client/components/form/KDateTimeRangeField.vue +65 -65
  113. package/core/client/components/form/KDatetimeField.vue +70 -70
  114. package/core/client/components/form/KEmailField.vue +33 -33
  115. package/core/client/components/form/KFileField.vue +161 -161
  116. package/core/client/components/form/KForm.vue +247 -247
  117. package/core/client/components/form/KIconField.vue +101 -101
  118. package/core/client/components/form/KItemField.vue +123 -123
  119. package/core/client/components/form/KNumberField.vue +33 -33
  120. package/core/client/components/form/KOptionsField.vue +63 -63
  121. package/core/client/components/form/KPasswordField.vue +57 -57
  122. package/core/client/components/form/KPhoneField.vue +32 -32
  123. package/core/client/components/form/KPropertyItemField.vue +119 -119
  124. package/core/client/components/form/KRoleField.vue +60 -60
  125. package/core/client/components/form/KSelectField.vue +143 -143
  126. package/core/client/components/form/KTextField.vue +48 -48
  127. package/core/client/components/form/KTextareaField.vue +88 -88
  128. package/core/client/components/form/KToggleField.vue +46 -46
  129. package/core/client/components/form/KTokenField.vue +80 -80
  130. package/core/client/components/form/KUnitField.vue +57 -57
  131. package/core/client/components/form/KUrlField.vue +32 -32
  132. package/core/client/components/form/KView.vue +118 -118
  133. package/core/client/components/form/index.js +7 -7
  134. package/core/client/components/index.js +48 -48
  135. package/core/client/components/input/KColorChooser.vue +63 -63
  136. package/core/client/components/input/KIconChooser.vue +308 -308
  137. package/core/client/components/input/KOptionsChooser.vue +122 -122
  138. package/core/client/components/input/KPalette.vue +40 -40
  139. package/core/client/components/input/index.js +11 -11
  140. package/core/client/components/layout/KFab.vue +113 -113
  141. package/core/client/components/layout/KLayout.vue +64 -64
  142. package/core/client/components/layout/KOpener.vue +140 -140
  143. package/core/client/components/layout/KPage.vue +320 -320
  144. package/core/client/components/layout/KPageSticky.vue +53 -53
  145. package/core/client/components/layout/KWindow.vue +443 -443
  146. package/core/client/components/layout/index.js +13 -13
  147. package/core/client/components/media/KColorScale.vue +254 -254
  148. package/core/client/components/media/KImageViewer.vue +44 -44
  149. package/core/client/components/media/KMarkdownViewer.vue +55 -55
  150. package/core/client/components/media/KMediaBrowser.vue +340 -340
  151. package/core/client/components/media/KShape.vue +120 -120
  152. package/core/client/components/media/index.js +13 -13
  153. package/core/client/components/menu/KMenu.vue +147 -147
  154. package/core/client/components/menu/KRadialFab.vue +122 -122
  155. package/core/client/components/menu/KRadialFabItem.vue +73 -73
  156. package/core/client/components/menu/index.js +9 -9
  157. package/core/client/components/screen/KEndpointScreen.vue +80 -80
  158. package/core/client/components/screen/KErrorScreen.vue +24 -24
  159. package/core/client/components/screen/KLoginScreen.vue +84 -85
  160. package/core/client/components/screen/KLogoutScreen.vue +20 -20
  161. package/core/client/components/screen/KRegisterScreen.vue +114 -114
  162. package/core/client/components/screen/KScreen.vue +106 -106
  163. package/core/client/components/screen/KScreenFooter.vue +32 -32
  164. package/core/client/components/screen/KScreenHeader.vue +17 -17
  165. package/core/client/components/screen/index.js +5 -5
  166. package/core/client/components/team/KAddMember.vue +375 -375
  167. package/core/client/components/team/KAddTag.vue +121 -121
  168. package/core/client/components/team/KChangeRole.vue +118 -118
  169. package/core/client/components/team/KGroupCard.vue +110 -110
  170. package/core/client/components/team/KGroupsActivity.vue +66 -66
  171. package/core/client/components/team/KJoinGroup.vue +132 -132
  172. package/core/client/components/team/KMemberCard.vue +328 -328
  173. package/core/client/components/team/KMemberFilter.vue +49 -49
  174. package/core/client/components/team/KMembersActivity.vue +126 -126
  175. package/core/client/components/team/KOrganisationsActivity.vue +53 -53
  176. package/core/client/components/team/KTagCard.vue +72 -72
  177. package/core/client/components/team/KTagsActivity.vue +61 -61
  178. package/core/client/components/team/index.js +9 -9
  179. package/core/client/components/time/KAbsoluteTimeRange.vue +183 -183
  180. package/core/client/components/time/KDate.vue +75 -75
  181. package/core/client/components/time/KDateTime.vue +172 -172
  182. package/core/client/components/time/KDateTimeRange.vue +118 -118
  183. package/core/client/components/time/KRelativeTimeRanges.vue +193 -193
  184. package/core/client/components/time/KTime.vue +74 -74
  185. package/core/client/components/time/index.js +7 -7
  186. package/core/client/components/viewer/KModalViewer.vue +47 -47
  187. package/core/client/components/viewer/KViewer.vue +30 -30
  188. package/core/client/composables/activity.js +67 -67
  189. package/core/client/composables/collection.js +181 -181
  190. package/core/client/composables/index.js +8 -8
  191. package/core/client/composables/pwa.js +71 -71
  192. package/core/client/composables/schema.js +64 -64
  193. package/core/client/composables/selection.js +88 -88
  194. package/core/client/composables/session.js +172 -172
  195. package/core/client/composables/store.js +49 -49
  196. package/core/client/composables/version.js +47 -47
  197. package/core/client/events.js +3 -3
  198. package/core/client/filter.js +60 -60
  199. package/core/client/guards.js +101 -101
  200. package/core/client/hooks/hooks.events.js +7 -7
  201. package/core/client/hooks/hooks.logger.js +24 -24
  202. package/core/client/hooks/hooks.users.js +22 -22
  203. package/core/client/hooks/index.js +3 -3
  204. package/core/client/i18n/core_en.json +845 -845
  205. package/core/client/i18n/core_fr.json +843 -843
  206. package/core/client/i18n.js +88 -88
  207. package/core/client/index.js +84 -84
  208. package/core/client/layout.js +323 -323
  209. package/core/client/local-storage.js +28 -28
  210. package/core/client/mixins/index.js +12 -12
  211. package/core/client/mixins/mixin.account.js +61 -61
  212. package/core/client/mixins/mixin.base-activity.js +232 -232
  213. package/core/client/mixins/mixin.base-collection.js +162 -162
  214. package/core/client/mixins/mixin.base-context.js +54 -54
  215. package/core/client/mixins/mixin.base-editor.js +194 -194
  216. package/core/client/mixins/mixin.base-field.js +105 -105
  217. package/core/client/mixins/mixin.base-item.js +166 -166
  218. package/core/client/mixins/mixin.base-modal.js +36 -36
  219. package/core/client/mixins/mixin.base-viewer.js +43 -43
  220. package/core/client/mixins/mixin.object-proxy.js +46 -46
  221. package/core/client/mixins/mixin.schema-proxy.js +97 -97
  222. package/core/client/mixins/mixin.service.js +22 -22
  223. package/core/client/reader.js +65 -65
  224. package/core/client/readers/index.js +3 -3
  225. package/core/client/readers/reader.blob.js +39 -39
  226. package/core/client/readers/reader.csv.js +36 -36
  227. package/core/client/readers/reader.json.js +34 -34
  228. package/core/client/search.js +47 -47
  229. package/core/client/services/index.js +49 -49
  230. package/core/client/services/local-settings.service.js +67 -67
  231. package/core/client/sorter.js +31 -31
  232. package/core/client/storage.js +102 -102
  233. package/core/client/store.js +39 -39
  234. package/core/client/theme.js +40 -40
  235. package/core/client/time.js +167 -167
  236. package/core/client/units.js +218 -218
  237. package/core/client/utils/index.js +203 -203
  238. package/core/client/utils/utils.account.js +52 -52
  239. package/core/client/utils/utils.colors.js +36 -36
  240. package/core/client/utils/utils.content.js +185 -185
  241. package/core/client/utils/utils.locale.js +28 -28
  242. package/core/client/utils/utils.platform.js +11 -11
  243. package/core/client/utils/utils.push.js +53 -53
  244. package/core/client/utils/utils.pwa.js +62 -62
  245. package/core/client/utils/utils.session.js +89 -89
  246. package/core/common/errors.js +1 -1
  247. package/core/common/index.js +10 -10
  248. package/core/common/permissions.js +318 -318
  249. package/core/common/schema.js +35 -35
  250. package/core/common/schemas/groups.create.json +28 -28
  251. package/core/common/schemas/groups.update.json +28 -28
  252. package/core/common/schemas/organisations.create.json +28 -28
  253. package/core/common/schemas/organisations.update.json +49 -49
  254. package/core/common/schemas/settings.update.json +234 -234
  255. package/core/common/schemas/tags.create.json +35 -35
  256. package/core/common/schemas/tags.update.json +35 -35
  257. package/core/common/schemas/users.update-profile.json +33 -33
  258. package/core/common/utils.js +45 -45
  259. package/core.api.js +5 -5
  260. package/core.client.js +5 -5
  261. package/core.common.js +1 -1
  262. package/coverage/core/api/application.js.html +272 -332
  263. package/coverage/core/api/authentication.js.html +56 -56
  264. package/coverage/core/api/db.js.html +132 -144
  265. package/coverage/core/api/hooks/hooks.account.js.html +1 -1
  266. package/coverage/core/api/hooks/hooks.authentication.js.html +12 -12
  267. package/coverage/core/api/hooks/hooks.authorisations.js.html +187 -199
  268. package/coverage/core/api/hooks/{hooks.push.js.html → hooks.devices.js.html} +56 -77
  269. package/coverage/core/api/hooks/hooks.groups.js.html +7 -7
  270. package/coverage/core/api/hooks/hooks.logger.js.html +18 -18
  271. package/coverage/core/api/hooks/hooks.model.js.html +160 -166
  272. package/coverage/core/api/hooks/hooks.organisations.js.html +406 -136
  273. package/coverage/core/api/hooks/hooks.pusher.js.html +730 -0
  274. package/coverage/core/api/hooks/hooks.query.js.html +129 -129
  275. package/coverage/core/api/hooks/hooks.schemas.js.html +22 -82
  276. package/coverage/core/api/hooks/hooks.service.js.html +13 -13
  277. package/coverage/core/api/hooks/hooks.storage.js.html +7 -7
  278. package/coverage/core/api/hooks/hooks.tags.js.html +1 -1
  279. package/coverage/core/api/hooks/hooks.users.js.html +126 -63
  280. package/coverage/core/api/hooks/index.html +119 -74
  281. package/coverage/core/api/hooks/index.js.html +26 -17
  282. package/coverage/core/api/index.html +26 -41
  283. package/coverage/core/api/index.js.html +17 -17
  284. package/coverage/core/api/marshall.js.html +21 -21
  285. package/coverage/core/api/models/groups.model.mongodb.js.html +8 -8
  286. package/coverage/core/api/models/index.html +1 -1
  287. package/coverage/core/api/models/organisations.model.mongodb.js.html +1 -1
  288. package/coverage/core/api/models/tags.model.mongodb.js.html +10 -10
  289. package/coverage/core/api/models/users.model.mongodb.js.html +1 -1
  290. package/coverage/core/api/oauth2-handler.js.html +202 -0
  291. package/coverage/core/api/oauth2-verifier.js.html +355 -0
  292. package/coverage/core/api/services/account/account.hooks.js.html +4 -4
  293. package/coverage/core/api/services/account/account.service.js.html +15 -12
  294. package/coverage/core/api/services/account/index.html +10 -10
  295. package/coverage/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
  296. package/coverage/core/api/services/authorisations/authorisations.service.js.html +77 -77
  297. package/coverage/core/api/services/authorisations/index.html +1 -1
  298. package/coverage/core/api/services/databases/databases.hooks.js.html +1 -1
  299. package/coverage/core/api/services/databases/databases.service.js.html +1 -1
  300. package/coverage/core/api/services/databases/index.html +1 -1
  301. package/coverage/core/api/services/devices/devices.hooks.js.html +190 -0
  302. package/coverage/core/api/services/devices/devices.service.js.html +382 -0
  303. package/coverage/core/api/services/devices/index.html +131 -0
  304. package/coverage/core/api/services/groups/groups.hooks.js.html +13 -7
  305. package/coverage/core/api/services/groups/index.html +5 -5
  306. package/coverage/core/api/services/index.html +19 -19
  307. package/coverage/core/api/services/index.js.html +80 -83
  308. package/coverage/core/api/services/mailer/index.html +1 -1
  309. package/coverage/core/api/services/mailer/mailer.hooks.js.html +1 -1
  310. package/coverage/core/api/services/mailer/mailer.service.js.html +1 -1
  311. package/coverage/core/api/services/organisations/index.html +11 -11
  312. package/coverage/core/api/services/organisations/organisations.hooks.js.html +1 -1
  313. package/coverage/core/api/services/organisations/organisations.service.js.html +32 -56
  314. package/coverage/{lcov-report/core/api/services/push → core/api/services/pusher}/index.html +36 -21
  315. package/coverage/{lcov-report/core/api/services/push/push.service.js.html → core/api/services/pusher/pusher.channels.js.html} +11 -38
  316. package/coverage/{lcov-report/core/api/services/push/push.hooks.js.html → core/api/services/pusher/pusher.hooks.js.html} +8 -8
  317. package/coverage/core/api/services/pusher/pusher.service.js.html +1420 -0
  318. package/coverage/core/api/services/storage/index.html +1 -1
  319. package/coverage/core/api/services/storage/storage.hooks.js.html +1 -1
  320. package/coverage/core/api/services/storage/storage.service.js.html +22 -22
  321. package/coverage/core/api/services/tags/index.html +7 -7
  322. package/coverage/core/api/services/tags/tags.hooks.js.html +54 -12
  323. package/coverage/core/api/services/users/index.html +1 -1
  324. package/coverage/core/api/services/users/users.hooks.js.html +3 -3
  325. package/coverage/core/common/errors.js.html +2 -2
  326. package/coverage/core/common/index.html +19 -19
  327. package/coverage/core/common/index.js.html +11 -11
  328. package/coverage/core/common/permissions.js.html +249 -273
  329. package/coverage/core/common/schema.js.html +23 -23
  330. package/coverage/core/common/utils.js.html +14 -14
  331. package/coverage/index.html +131 -116
  332. package/coverage/lcov-report/core/api/application.js.html +272 -332
  333. package/coverage/lcov-report/core/api/authentication.js.html +56 -56
  334. package/coverage/lcov-report/core/api/db.js.html +132 -144
  335. package/coverage/lcov-report/core/api/hooks/hooks.account.js.html +1 -1
  336. package/coverage/lcov-report/core/api/hooks/hooks.authentication.js.html +12 -12
  337. package/coverage/lcov-report/core/api/hooks/hooks.authorisations.js.html +187 -199
  338. package/coverage/lcov-report/core/api/hooks/{hooks.push.js.html → hooks.devices.js.html} +56 -77
  339. package/coverage/lcov-report/core/api/hooks/hooks.groups.js.html +7 -7
  340. package/coverage/lcov-report/core/api/hooks/hooks.logger.js.html +18 -18
  341. package/coverage/lcov-report/core/api/hooks/hooks.model.js.html +160 -166
  342. package/coverage/lcov-report/core/api/hooks/hooks.organisations.js.html +406 -136
  343. package/coverage/lcov-report/core/api/hooks/hooks.pusher.js.html +730 -0
  344. package/coverage/lcov-report/core/api/hooks/hooks.query.js.html +129 -129
  345. package/coverage/lcov-report/core/api/hooks/hooks.schemas.js.html +22 -82
  346. package/coverage/lcov-report/core/api/hooks/hooks.service.js.html +13 -13
  347. package/coverage/lcov-report/core/api/hooks/hooks.storage.js.html +7 -7
  348. package/coverage/lcov-report/core/api/hooks/hooks.tags.js.html +1 -1
  349. package/coverage/lcov-report/core/api/hooks/hooks.users.js.html +126 -63
  350. package/coverage/lcov-report/core/api/hooks/index.html +119 -74
  351. package/coverage/lcov-report/core/api/hooks/index.js.html +26 -17
  352. package/coverage/lcov-report/core/api/index.html +26 -41
  353. package/coverage/lcov-report/core/api/index.js.html +17 -17
  354. package/coverage/lcov-report/core/api/marshall.js.html +21 -21
  355. package/coverage/lcov-report/core/api/models/groups.model.mongodb.js.html +8 -8
  356. package/coverage/lcov-report/core/api/models/index.html +1 -1
  357. package/coverage/lcov-report/core/api/models/organisations.model.mongodb.js.html +1 -1
  358. package/coverage/lcov-report/core/api/models/tags.model.mongodb.js.html +10 -10
  359. package/coverage/lcov-report/core/api/models/users.model.mongodb.js.html +1 -1
  360. package/coverage/lcov-report/core/api/oauth2-handler.js.html +202 -0
  361. package/coverage/lcov-report/core/api/oauth2-verifier.js.html +355 -0
  362. package/coverage/lcov-report/core/api/services/account/account.hooks.js.html +4 -4
  363. package/coverage/lcov-report/core/api/services/account/account.service.js.html +15 -12
  364. package/coverage/lcov-report/core/api/services/account/index.html +10 -10
  365. package/coverage/lcov-report/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
  366. package/coverage/lcov-report/core/api/services/authorisations/authorisations.service.js.html +77 -77
  367. package/coverage/lcov-report/core/api/services/authorisations/index.html +1 -1
  368. package/coverage/lcov-report/core/api/services/databases/databases.hooks.js.html +1 -1
  369. package/coverage/lcov-report/core/api/services/databases/databases.service.js.html +1 -1
  370. package/coverage/lcov-report/core/api/services/databases/index.html +1 -1
  371. package/coverage/lcov-report/core/api/services/devices/devices.hooks.js.html +190 -0
  372. package/coverage/lcov-report/core/api/services/devices/devices.service.js.html +382 -0
  373. package/coverage/lcov-report/core/api/services/devices/index.html +131 -0
  374. package/coverage/lcov-report/core/api/services/groups/groups.hooks.js.html +13 -7
  375. package/coverage/lcov-report/core/api/services/groups/index.html +5 -5
  376. package/coverage/lcov-report/core/api/services/index.html +19 -19
  377. package/coverage/lcov-report/core/api/services/index.js.html +80 -83
  378. package/coverage/lcov-report/core/api/services/mailer/index.html +1 -1
  379. package/coverage/lcov-report/core/api/services/mailer/mailer.hooks.js.html +1 -1
  380. package/coverage/lcov-report/core/api/services/mailer/mailer.service.js.html +1 -1
  381. package/coverage/lcov-report/core/api/services/organisations/index.html +11 -11
  382. package/coverage/lcov-report/core/api/services/organisations/organisations.hooks.js.html +1 -1
  383. package/coverage/lcov-report/core/api/services/organisations/organisations.service.js.html +32 -56
  384. package/coverage/{core/api/services/push → lcov-report/core/api/services/pusher}/index.html +36 -21
  385. package/coverage/{core/api/services/push/push.service.js.html → lcov-report/core/api/services/pusher/pusher.channels.js.html} +11 -38
  386. package/coverage/{core/api/services/push/push.hooks.js.html → lcov-report/core/api/services/pusher/pusher.hooks.js.html} +8 -8
  387. package/coverage/lcov-report/core/api/services/pusher/pusher.service.js.html +1420 -0
  388. package/coverage/lcov-report/core/api/services/storage/index.html +1 -1
  389. package/coverage/lcov-report/core/api/services/storage/storage.hooks.js.html +1 -1
  390. package/coverage/lcov-report/core/api/services/storage/storage.service.js.html +22 -22
  391. package/coverage/lcov-report/core/api/services/tags/index.html +7 -7
  392. package/coverage/lcov-report/core/api/services/tags/tags.hooks.js.html +54 -12
  393. package/coverage/lcov-report/core/api/services/users/index.html +1 -1
  394. package/coverage/lcov-report/core/api/services/users/users.hooks.js.html +3 -3
  395. package/coverage/lcov-report/core/common/errors.js.html +2 -2
  396. package/coverage/lcov-report/core/common/index.html +19 -19
  397. package/coverage/lcov-report/core/common/index.js.html +11 -11
  398. package/coverage/lcov-report/core/common/permissions.js.html +249 -273
  399. package/coverage/lcov-report/core/common/schema.js.html +23 -23
  400. package/coverage/lcov-report/core/common/utils.js.html +14 -14
  401. package/coverage/lcov-report/index.html +131 -116
  402. package/coverage/lcov-report/map/api/hooks/hooks.catalog.js.html +48 -48
  403. package/coverage/lcov-report/map/api/hooks/hooks.features.js.html +51 -51
  404. package/coverage/lcov-report/map/api/hooks/hooks.query.js.html +40 -46
  405. package/coverage/lcov-report/map/api/hooks/index.html +49 -49
  406. package/coverage/lcov-report/map/api/hooks/index.js.html +16 -16
  407. package/coverage/lcov-report/map/api/index.html +26 -26
  408. package/coverage/lcov-report/map/api/index.js.html +38 -38
  409. package/coverage/lcov-report/map/api/marshall.js.html +17 -17
  410. package/coverage/lcov-report/map/api/models/alerts.model.mongodb.js.html +1 -1
  411. package/coverage/lcov-report/map/api/models/catalog.model.mongodb.js.html +1 -1
  412. package/coverage/lcov-report/map/api/models/features.model.mongodb.js.html +11 -5
  413. package/coverage/lcov-report/map/api/models/index.html +5 -5
  414. package/coverage/lcov-report/map/api/services/alerts/alerts.hooks.js.html +1 -1
  415. package/coverage/lcov-report/map/api/services/alerts/alerts.service.js.html +1 -1
  416. package/coverage/lcov-report/map/api/services/alerts/index.html +1 -1
  417. package/coverage/lcov-report/map/api/services/catalog/catalog.hooks.js.html +1 -1
  418. package/coverage/lcov-report/map/api/services/catalog/index.html +1 -1
  419. package/coverage/lcov-report/map/api/services/daptiles/daptiles.service.js.html +1 -1
  420. package/coverage/lcov-report/map/api/services/daptiles/index.html +1 -1
  421. package/coverage/lcov-report/map/api/services/features/features.hooks.js.html +1 -1
  422. package/coverage/lcov-report/map/api/services/features/features.service.js.html +1 -1
  423. package/coverage/lcov-report/map/api/services/features/index.html +1 -1
  424. package/coverage/lcov-report/map/api/services/geocoder/geocoder.hooks.js.html +1 -1
  425. package/coverage/lcov-report/map/api/services/geocoder/geocoder.service.js.html +1 -1
  426. package/coverage/lcov-report/map/api/services/geocoder/index.html +1 -1
  427. package/coverage/lcov-report/map/api/services/index.html +17 -17
  428. package/coverage/lcov-report/map/api/services/index.js.html +81 -90
  429. package/coverage/lcov-report/map/common/dynamic-grid-source.js.html +68 -68
  430. package/coverage/lcov-report/map/common/errors.js.html +16 -16
  431. package/coverage/lcov-report/map/common/geotiff-grid-source.js.html +53 -53
  432. package/coverage/lcov-report/map/common/grid.js.html +298 -298
  433. package/coverage/lcov-report/map/common/index.html +148 -148
  434. package/coverage/lcov-report/map/common/index.js.html +67 -67
  435. package/coverage/lcov-report/map/common/meteo-model-grid-source.js.html +68 -68
  436. package/coverage/lcov-report/map/common/moment-utils.js.html +18 -18
  437. package/coverage/lcov-report/map/common/opendap-grid-source.js.html +60 -60
  438. package/coverage/lcov-report/map/common/opendap-utils.js.html +113 -113
  439. package/coverage/lcov-report/map/common/permissions.js.html +11 -11
  440. package/coverage/lcov-report/map/common/time-based-grid-source.js.html +54 -54
  441. package/coverage/lcov-report/map/common/tms-utils.js.html +1 -1
  442. package/coverage/lcov-report/map/common/wcs-grid-source.js.html +42 -42
  443. package/coverage/lcov-report/map/common/wcs-utils.js.html +211 -211
  444. package/coverage/lcov-report/map/common/weacast-grid-source.js.html +72 -72
  445. package/coverage/lcov-report/map/common/wfs-utils.js.html +1 -1
  446. package/coverage/lcov-report/map/common/wms-utils.js.html +1 -1
  447. package/coverage/lcov-report/map/common/wmts-utils.js.html +1 -1
  448. package/coverage/lcov.info +5502 -3743
  449. package/coverage/map/api/hooks/hooks.catalog.js.html +48 -48
  450. package/coverage/map/api/hooks/hooks.features.js.html +51 -51
  451. package/coverage/map/api/hooks/hooks.query.js.html +40 -46
  452. package/coverage/map/api/hooks/index.html +49 -49
  453. package/coverage/map/api/hooks/index.js.html +16 -16
  454. package/coverage/map/api/index.html +26 -26
  455. package/coverage/map/api/index.js.html +38 -38
  456. package/coverage/map/api/marshall.js.html +17 -17
  457. package/coverage/map/api/models/alerts.model.mongodb.js.html +1 -1
  458. package/coverage/map/api/models/catalog.model.mongodb.js.html +1 -1
  459. package/coverage/map/api/models/features.model.mongodb.js.html +11 -5
  460. package/coverage/map/api/models/index.html +5 -5
  461. package/coverage/map/api/services/alerts/alerts.hooks.js.html +1 -1
  462. package/coverage/map/api/services/alerts/alerts.service.js.html +1 -1
  463. package/coverage/map/api/services/alerts/index.html +1 -1
  464. package/coverage/map/api/services/catalog/catalog.hooks.js.html +1 -1
  465. package/coverage/map/api/services/catalog/index.html +1 -1
  466. package/coverage/map/api/services/daptiles/daptiles.service.js.html +1 -1
  467. package/coverage/map/api/services/daptiles/index.html +1 -1
  468. package/coverage/map/api/services/features/features.hooks.js.html +1 -1
  469. package/coverage/map/api/services/features/features.service.js.html +1 -1
  470. package/coverage/map/api/services/features/index.html +1 -1
  471. package/coverage/map/api/services/geocoder/geocoder.hooks.js.html +1 -1
  472. package/coverage/map/api/services/geocoder/geocoder.service.js.html +1 -1
  473. package/coverage/map/api/services/geocoder/index.html +1 -1
  474. package/coverage/map/api/services/index.html +17 -17
  475. package/coverage/map/api/services/index.js.html +81 -90
  476. package/coverage/map/common/dynamic-grid-source.js.html +68 -68
  477. package/coverage/map/common/errors.js.html +16 -16
  478. package/coverage/map/common/geotiff-grid-source.js.html +53 -53
  479. package/coverage/map/common/grid.js.html +298 -298
  480. package/coverage/map/common/index.html +148 -148
  481. package/coverage/map/common/index.js.html +67 -67
  482. package/coverage/map/common/meteo-model-grid-source.js.html +68 -68
  483. package/coverage/map/common/moment-utils.js.html +18 -18
  484. package/coverage/map/common/opendap-grid-source.js.html +60 -60
  485. package/coverage/map/common/opendap-utils.js.html +113 -113
  486. package/coverage/map/common/permissions.js.html +11 -11
  487. package/coverage/map/common/time-based-grid-source.js.html +54 -54
  488. package/coverage/map/common/tms-utils.js.html +1 -1
  489. package/coverage/map/common/wcs-grid-source.js.html +42 -42
  490. package/coverage/map/common/wcs-utils.js.html +211 -211
  491. package/coverage/map/common/weacast-grid-source.js.html +72 -72
  492. package/coverage/map/common/wfs-utils.js.html +1 -1
  493. package/coverage/map/common/wms-utils.js.html +1 -1
  494. package/coverage/map/common/wmts-utils.js.html +1 -1
  495. package/coverage/tmp/coverage-19264-1684834146599-0.json +1 -0
  496. package/coverage/tmp/coverage-2508-1684834182408-0.json +1 -0
  497. package/coverage/tmp/coverage-25908-1684834189369-0.json +1 -0
  498. package/coverage/tmp/coverage-32364-1684834189420-0.json +1 -0
  499. package/coverage/tmp/coverage-36604-1684834189485-0.json +1 -0
  500. package/coverage/tmp/coverage-40652-1684834182272-0.json +1 -0
  501. package/coverage/tmp/coverage-48484-1684834182342-0.json +1 -0
  502. package/coverage/tmp/coverage-51652-1684834189584-0.json +1 -0
  503. package/coverage/tmp/coverage-9776-1684834189519-0.json +1 -0
  504. package/extras/css/core.variables.scss +38 -38
  505. package/extras/icons/center-on-feature.svg +315 -315
  506. package/extras/icons/json.svg +83 -83
  507. package/extras/icons/map-legend.svg +251 -251
  508. package/extras/icons/target.svg +37 -37
  509. package/extras/tours/core/account-profile.js +32 -32
  510. package/extras/tours/core/account.js +143 -143
  511. package/extras/tours/core/add-member.js +74 -74
  512. package/extras/tours/core/add-tag.js +12 -12
  513. package/extras/tours/core/create-group.js +19 -19
  514. package/extras/tours/core/create-organisation.js +19 -19
  515. package/extras/tours/core/create-tag.js +26 -26
  516. package/extras/tours/core/edit-member-role.js +13 -13
  517. package/extras/tours/core/groups.js +65 -65
  518. package/extras/tours/core/join-group.js +13 -13
  519. package/extras/tours/core/login.js +41 -41
  520. package/extras/tours/core/members.js +108 -108
  521. package/extras/tours/core/register.js +61 -61
  522. package/extras/tours/core/send-reset-password.js +14 -14
  523. package/extras/tours/core/tags.js +65 -65
  524. package/extras/tours/map/add-layer.js +28 -28
  525. package/extras/tours/map/catalog-categories.js +59 -59
  526. package/extras/tours/map/catalog-panel.js +112 -112
  527. package/extras/tours/map/connect-layer.js +39 -39
  528. package/extras/tours/map/create-layer.js +33 -33
  529. package/extras/tours/map/create-view.js +25 -25
  530. package/extras/tours/map/fab.js +26 -26
  531. package/extras/tours/map/import-layer.js +33 -33
  532. package/extras/tours/map/navigation-bar.js +185 -185
  533. package/extras/tours/map/side-nav.js +35 -35
  534. package/extras/tours/map/timeline.js +77 -77
  535. package/map/api/config/categories.cjs +42 -42
  536. package/map/api/config/layers.cjs +43 -43
  537. package/map/api/config/sublegends.cjs +42 -42
  538. package/map/api/hooks/hooks.catalog.js +85 -85
  539. package/map/api/hooks/hooks.features.js +84 -84
  540. package/map/api/hooks/hooks.query.js +361 -361
  541. package/map/api/hooks/index.js +3 -3
  542. package/map/api/index.js +18 -18
  543. package/map/api/marshall.js +31 -31
  544. package/map/api/models/alerts.model.mongodb.js +7 -7
  545. package/map/api/models/catalog.model.mongodb.js +14 -14
  546. package/map/api/models/features.model.mongodb.js +37 -37
  547. package/map/api/services/alerts/alerts.hooks.js +63 -63
  548. package/map/api/services/alerts/alerts.service.js +175 -175
  549. package/map/api/services/catalog/catalog.hooks.js +76 -76
  550. package/map/api/services/daptiles/daptiles.service.js +475 -475
  551. package/map/api/services/features/features.hooks.js +40 -40
  552. package/map/api/services/features/features.service.js +52 -52
  553. package/map/api/services/geocoder/geocoder.hooks.js +31 -31
  554. package/map/api/services/geocoder/geocoder.service.js +79 -79
  555. package/map/api/services/index.js +228 -228
  556. package/map/client/canvas-draw-context.js +16 -16
  557. package/map/client/cesium/utils.js +133 -133
  558. package/map/client/components/KCaptureToolbar.vue +155 -155
  559. package/map/client/components/KColorLegend.vue +349 -349
  560. package/map/client/components/KCompass.vue +143 -143
  561. package/map/client/components/KEditLayerData.vue +85 -85
  562. package/map/client/components/KFeatureActionButton.vue +119 -119
  563. package/map/client/components/KFeatureEditor.vue +92 -92
  564. package/map/client/components/KFeaturesChart.vue +285 -285
  565. package/map/client/components/KFeaturesFilter.vue +259 -259
  566. package/map/client/components/KFeaturesTable.vue +99 -99
  567. package/map/client/components/KLayerEditionToolbar.vue +54 -54
  568. package/map/client/components/KLayerEditor.vue +48 -48
  569. package/map/client/components/KLayerStyleEditor.vue +118 -118
  570. package/map/client/components/KLayerStyleForm.vue +906 -906
  571. package/map/client/components/KLevelSlider.vue +100 -100
  572. package/map/client/components/KMeasureTool.vue +568 -568
  573. package/map/client/components/KPositionIndicator.vue +90 -90
  574. package/map/client/components/KTimeline.vue +293 -293
  575. package/map/client/components/KTimezoneMap.vue +158 -158
  576. package/map/client/components/KUrlLegend.vue +122 -122
  577. package/map/client/components/catalog/KAddLayer.vue +66 -66
  578. package/map/client/components/catalog/KBaseLayersSelector.vue +114 -114
  579. package/map/client/components/catalog/KConnectLayer.vue +323 -323
  580. package/map/client/components/catalog/KCreateLayer.vue +184 -184
  581. package/map/client/components/catalog/KCreateView.vue +108 -108
  582. package/map/client/components/catalog/KFilteredLayerItem.vue +53 -53
  583. package/map/client/components/catalog/KImportLayer.vue +168 -168
  584. package/map/client/components/catalog/KLayerCategories.vue +274 -274
  585. package/map/client/components/catalog/KLayerItem.vue +70 -70
  586. package/map/client/components/catalog/KLayersPanel.vue +137 -137
  587. package/map/client/components/catalog/KLayersSelector.vue +78 -78
  588. package/map/client/components/catalog/KViewSelector.vue +46 -46
  589. package/map/client/components/catalog/KViewsPanel.vue +123 -123
  590. package/map/client/components/catalog/KWeatherLayersSelector.vue +129 -129
  591. package/map/client/components/form/KDirectionField.vue +113 -113
  592. package/map/client/components/form/KLayerCategoryField.vue +46 -46
  593. package/map/client/components/form/KLocationField.vue +189 -189
  594. package/map/client/components/form/KOwsLayerField.vue +130 -130
  595. package/map/client/components/form/KOwsServiceField.vue +238 -238
  596. package/map/client/components/form/KTimezoneField.vue +142 -142
  597. package/map/client/components/index.js +1 -1
  598. package/map/client/components/legend/KColorScaleLegend.vue +31 -31
  599. package/map/client/components/legend/KImageLegend.vue +52 -52
  600. package/map/client/components/legend/KLegend.vue +191 -191
  601. package/map/client/components/legend/KLegendRenderer.vue +20 -20
  602. package/map/client/components/legend/KSymbolsLegend.vue +101 -101
  603. package/map/client/components/location/KGeocodersFilter.vue +44 -44
  604. package/map/client/components/location/KLocationCardSection.vue +61 -61
  605. package/map/client/components/location/KLocationMap.vue +280 -280
  606. package/map/client/components/location/KLocationSearch.vue +144 -144
  607. package/map/client/components/location/KLocationTip.vue +29 -29
  608. package/map/client/components/tools/KGeolocateTool.vue +46 -46
  609. package/map/client/components/tools/KSearchTool.vue +93 -93
  610. package/map/client/components/widget/KElevationProfile.vue +454 -454
  611. package/map/client/components/widget/KInformationBox.vue +145 -145
  612. package/map/client/components/widget/KMapillaryViewer.vue +178 -178
  613. package/map/client/components/widget/KStackableTimeSeries.vue +214 -214
  614. package/map/client/components/widget/KTimeSeries.vue +500 -500
  615. package/map/client/composables/activity.js +88 -88
  616. package/map/client/composables/highlight.js +214 -214
  617. package/map/client/composables/index.js +7 -7
  618. package/map/client/composables/location.js +53 -53
  619. package/map/client/composables/measure.js +42 -42
  620. package/map/client/composables/probe.js +133 -133
  621. package/map/client/composables/selection.js +300 -300
  622. package/map/client/composables/weather.js +213 -213
  623. package/map/client/elevation-utils.js +258 -258
  624. package/map/client/geolocation.js +119 -119
  625. package/map/client/globe.js +13 -13
  626. package/map/client/i18n/map_en.json +645 -645
  627. package/map/client/i18n/map_fr.json +646 -646
  628. package/map/client/index.js +18 -18
  629. package/map/client/init.js +80 -80
  630. package/map/client/leaflet/GSMaPLayer.js +55 -55
  631. package/map/client/leaflet/GradientPath.js +180 -180
  632. package/map/client/leaflet/MaskLayer.js +57 -57
  633. package/map/client/leaflet/TiledFeatureLayer.js +572 -572
  634. package/map/client/leaflet/TiledMeshLayer.js +486 -486
  635. package/map/client/leaflet/TiledWindLayer.js +384 -384
  636. package/map/client/leaflet/utils.js +246 -246
  637. package/map/client/map.js +15 -15
  638. package/map/client/mixins/globe/index.js +8 -8
  639. package/map/client/mixins/globe/mixin.base-globe.js +462 -462
  640. package/map/client/mixins/globe/mixin.file-layers.js +40 -40
  641. package/map/client/mixins/globe/mixin.geojson-layers.js +314 -314
  642. package/map/client/mixins/globe/mixin.globe-activity.js +52 -52
  643. package/map/client/mixins/globe/mixin.opendap-layers.js +51 -51
  644. package/map/client/mixins/globe/mixin.popup.js +82 -82
  645. package/map/client/mixins/globe/mixin.style.js +110 -110
  646. package/map/client/mixins/globe/mixin.tooltip.js +101 -101
  647. package/map/client/mixins/index.js +9 -9
  648. package/map/client/mixins/map/index.js +16 -16
  649. package/map/client/mixins/map/mixin.base-map.js +592 -592
  650. package/map/client/mixins/map/mixin.canvas-layers.js +529 -529
  651. package/map/client/mixins/map/mixin.edit-layers.js +474 -474
  652. package/map/client/mixins/map/mixin.file-layers.js +49 -49
  653. package/map/client/mixins/map/mixin.forecast-layers.js +79 -79
  654. package/map/client/mixins/map/mixin.geojson-layers.js +557 -557
  655. package/map/client/mixins/map/mixin.georaster-layers.js +114 -114
  656. package/map/client/mixins/map/mixin.gsmap-layers.js +27 -27
  657. package/map/client/mixins/map/mixin.heatmap-layers.js +116 -116
  658. package/map/client/mixins/map/mixin.map-activity.js +38 -38
  659. package/map/client/mixins/map/mixin.mapillary-layers.js +45 -45
  660. package/map/client/mixins/map/mixin.popup.js +53 -53
  661. package/map/client/mixins/map/mixin.style.js +73 -73
  662. package/map/client/mixins/map/mixin.tiled-mesh-layers.js +120 -120
  663. package/map/client/mixins/map/mixin.tiled-wind-layers.js +126 -126
  664. package/map/client/mixins/map/mixin.tooltip.js +47 -47
  665. package/map/client/mixins/mixin.activity.js +477 -477
  666. package/map/client/mixins/mixin.catalog-panel.js +26 -26
  667. package/map/client/mixins/mixin.context.js +262 -262
  668. package/map/client/mixins/mixin.feature-selection.js +28 -28
  669. package/map/client/mixins/mixin.feature-service.js +403 -403
  670. package/map/client/mixins/mixin.infobox.js +29 -29
  671. package/map/client/mixins/mixin.levels.js +66 -66
  672. package/map/client/mixins/mixin.style.js +29 -29
  673. package/map/client/mixins/mixin.weacast.js +212 -212
  674. package/map/client/pixi-utils.js +256 -256
  675. package/map/client/readers/index.js +4 -4
  676. package/map/client/readers/reader.geojson.js +64 -64
  677. package/map/client/readers/reader.gpx.js +36 -36
  678. package/map/client/readers/reader.kml.js +36 -36
  679. package/map/client/readers/reader.shp.js +82 -82
  680. package/map/client/utils/index.js +3 -0
  681. package/map/client/utils/utils.js +228 -0
  682. package/map/client/utils/utils.location.js +45 -45
  683. package/map/client/utils/utils.schema.js +75 -0
  684. package/map/client/utils.all.js +3 -0
  685. package/map/client/utils.globe.js +2 -0
  686. package/map/client/utils.js +4 -303
  687. package/map/client/utils.map.js +2 -0
  688. package/map/common/dynamic-grid-source.js +127 -127
  689. package/map/common/errors.js +3 -3
  690. package/map/common/geotiff-grid-source.js +150 -150
  691. package/map/common/grid.js +509 -509
  692. package/map/common/index.js +29 -29
  693. package/map/common/meteo-model-grid-source.js +157 -157
  694. package/map/common/moment-utils.js +24 -24
  695. package/map/common/opendap-grid-source.js +261 -261
  696. package/map/common/opendap-utils.js +247 -247
  697. package/map/common/permissions.js +11 -11
  698. package/map/common/schemas/catalog.update.json +28 -28
  699. package/map/common/time-based-grid-source.js +111 -111
  700. package/map/common/tms-utils.js +63 -63
  701. package/map/common/wcs-grid-source.js +93 -93
  702. package/map/common/wcs-utils.js +167 -167
  703. package/map/common/weacast-grid-source.js +316 -316
  704. package/map/common/wfs-utils.js +163 -163
  705. package/map/common/wms-utils.js +117 -117
  706. package/map/common/wmts-utils.js +154 -154
  707. package/map.api.js +5 -5
  708. package/map.client.globe.js +5 -5
  709. package/map.client.js +5 -5
  710. package/map.client.map.js +5 -5
  711. package/map.common.js +1 -1
  712. package/map.config.cjs +9 -9
  713. package/package.json +161 -161
  714. package/test/api/core/account.test.js +485 -485
  715. package/test/api/core/client.test.js.skip +37 -37
  716. package/test/api/core/config/default.cjs +118 -118
  717. package/test/api/core/config/email-templates/confirmInvitation/html.ejs +18 -18
  718. package/test/api/core/config/email-templates/identityChange/html.ejs +14 -14
  719. package/test/api/core/config/email-templates/newDevice/html.ejs +7 -7
  720. package/test/api/core/config/email-templates/newSubscription/html.ejs +7 -7
  721. package/test/api/core/config/email-templates/passwordChange/html.ejs +5 -5
  722. package/test/api/core/config/email-templates/resendVerifySignup/html.ejs +12 -12
  723. package/test/api/core/config/email-templates/resetPwd/html.ejs +5 -5
  724. package/test/api/core/config/email-templates/sendResetPwd/html.ejs +12 -12
  725. package/test/api/core/config/email-templates/verifySignup/html.ejs +3 -3
  726. package/test/api/core/data/invalid-objects.json +59 -59
  727. package/test/api/core/data/schema.json +57 -57
  728. package/test/api/core/data/valid-objects.json +37 -37
  729. package/test/api/core/hooks.test.js +330 -330
  730. package/test/api/core/index.js +1 -1
  731. package/test/api/core/index.test.js +478 -478
  732. package/test/api/core/push.test.js +197 -197
  733. package/test/api/core/schemas.test.js +82 -82
  734. package/test/api/core/storage.test.js +153 -153
  735. package/test/api/core/team.test.js +670 -670
  736. package/test/api/core/utils.js +92 -92
  737. package/test/api/index.js +3 -3
  738. package/test/api/map/alerts.test.js +560 -560
  739. package/test/api/map/config/default.cjs +125 -125
  740. package/test/api/map/config/layers.json +38 -38
  741. package/test/api/map/data/DescribeCoverage.xml +54 -54
  742. package/test/api/map/data/adsb.observations.json +131 -131
  743. package/test/api/map/data/dataset.grb.das +55 -55
  744. package/test/api/map/data/dataset.grb.dds +17 -17
  745. package/test/api/map/data/vigicrues.observations.json +47041 -47041
  746. package/test/api/map/data/vigicrues.stations.json +15421 -15421
  747. package/test/api/map/data/zones.json +1227 -1227
  748. package/test/api/map/grid-sources.test.js +313 -313
  749. package/test/api/map/hooks.test.js +98 -98
  750. package/test/api/map/index.test.js +563 -563
  751. package/test/api/map/test-log-2022-12-01.log +19 -0
  752. package/test/api/map/test-log-2023-01-05.log +4 -0
  753. package/test/api/map/test-log-2023-01-09.log +3 -0
  754. package/test/api/map/{test-log-2023-09-21.log → test-log-2023-02-05.log} +11 -12
  755. package/test/api/map/test-log-2023-02-14.log +82 -0
  756. package/test/api/map/test-log-2023-02-28.log +28 -0
  757. package/test/api/map/{test-log-2023-07-19.log → test-log-2023-03-02.log} +11 -13
  758. package/test/api/map/test-log-2023-03-06.log +11 -0
  759. package/test/api/map/test-log-2023-03-07.log +61 -0
  760. package/test/api/map/test-log-2023-03-31.log +11 -0
  761. package/test/api/map/test-log-2023-05-23.log +11 -0
  762. package/test/api/map/test-log-2023-05-24.log +11 -0
  763. package/test/client/core/account.js +36 -36
  764. package/test/client/core/api.js +361 -361
  765. package/test/client/core/collection.js +64 -64
  766. package/test/client/core/index.js +8 -8
  767. package/test/client/core/layout.js +116 -116
  768. package/test/client/core/runner.js +224 -224
  769. package/test/client/core/screens.js +35 -35
  770. package/test/client/core/time.js +10 -10
  771. package/test/client/core/utils.js +260 -260
  772. package/test/client/index.js +3 -3
  773. package/test/client/map/catalog.js +193 -175
  774. package/test/client/map/controls.js +41 -41
  775. package/test/client/map/index.js +4 -4
  776. package/test/client/map/time.js +24 -24
  777. package/test/client/map/utils.js +27 -27
  778. package/coverage/core/api/utils.js.html +0 -118
  779. package/coverage/lcov-report/core/api/utils.js.html +0 -118
  780. package/coverage/tmp/coverage-59096-1692631696256-0.json +0 -1
  781. package/coverage/tmp/coverage-59108-1692631696233-0.json +0 -1
  782. package/coverage/tmp/coverage-59119-1692631696222-0.json +0 -1
  783. package/coverage/tmp/coverage-59131-1692631696200-0.json +0 -1
  784. package/coverage/tmp/coverage-59138-1692631696175-0.json +0 -1
  785. package/test/api/core/test-log-2023-07-10.log +0 -2
  786. package/test/api/core/test-log-2023-07-12.log +0 -0
  787. package/test/api/core/test-log-2023-07-18.log +0 -78
  788. package/test/api/core/test-log-2023-07-19.log +0 -44
  789. package/test/api/core/test-log-2023-08-01.log +0 -162
  790. package/test/api/core/test-log-2023-08-21.log +0 -66
  791. package/test/api/core/test-log-2023-08-22.log +0 -96
  792. package/test/api/core/test-log-2023-08-23.log +0 -22
  793. package/test/api/core/test-log-2023-09-20.log +0 -22
  794. package/test/api/core/test-log-2023-09-21.log +0 -105
  795. package/test/api/core/test-log-2023-10-04.log +0 -22
  796. package/test/api/map/test-log-2023-07-18.log +0 -62
  797. package/test/api/map/test-log-2023-08-21.log +0 -65
  798. package/test/api/map/test-log-2023-09-20.log +0 -60
  799. /package/test/api/core/{test-log-2023-07-04.log → test-log-2023-06-05.log} +0 -0
@@ -1,591 +1,591 @@
1
- import path from 'path'
2
- import url from 'url'
3
- import fs from 'fs-extra'
4
- import makeDebug from 'debug'
5
- import winston from 'winston'
6
- import _ from 'lodash'
7
- import siftModule from 'sift'
8
- import 'winston-daily-rotate-file'
9
- import compress from 'compression'
10
- import cors from 'cors'
11
- import helmet from 'helmet'
12
- import { RateLimiter as SocketLimiter } from 'limiter'
13
- import HttpLimiter from 'express-rate-limit'
14
- import feathers from '@feathersjs/feathers'
15
- import configuration from '@feathersjs/configuration'
16
- import errors from '@feathersjs/errors'
17
- import express, { authenticate } from '@feathersjs/express'
18
- import socketio from '@feathersjs/socketio'
19
- import mongodb from 'mongodb'
20
- import { Database, idToString } from './db.js'
21
- import auth from './authentication.js'
22
-
23
- const debug = makeDebug('kdk:core:application')
24
- const debugLimiter = makeDebug('kdk:core:application:limiter')
25
- const { TooManyRequests, Forbidden, BadRequest } = errors
26
- const { ObjectID } = mongodb
27
- const { rest } = express
28
- const sift = siftModule.default
29
-
30
- // Initialize debugger to be used in feathers
31
- feathers.setDebug(makeDebug)
32
-
33
- function tooManyRequests (socket, message, key) {
34
- debug(message)
35
- const error = new TooManyRequests(message, { translation: { key } })
36
- socket.emit('rate-limit', error)
37
- // Add a timeout so that error message is correctly handled
38
- setTimeout(() => socket.disconnect(true), 3000)
39
- }
40
-
41
- export function declareService (name, app, service, serviceOptions = {}) {
42
- let servicePath = serviceOptions.path || name
43
- let contextId
44
- if (serviceOptions.context) {
45
- contextId = idToString(serviceOptions.context)
46
- servicePath = contextId + '/' + servicePath
47
- }
48
-
49
- let feathersPath = app.get('apiPath') + '/' + servicePath
50
- if (feathersPath.startsWith('/')) feathersPath = feathersPath.substr(1)
51
-
52
- try {
53
- const feathersService = app.service(feathersPath)
54
- // Some internal Feathers service might internally declare the service
55
- return feathersService
56
- } catch (error) {
57
- // Initialize our service by providing any middleware as well
58
- let args = [feathersPath]
59
- if (_.has(serviceOptions, 'middlewares.before')) args = args.concat(_.get(serviceOptions, 'middlewares.before'))
60
- args.push(service)
61
- const options = _.pick(serviceOptions, ['methods', 'events'])
62
- if (!_.isEmpty(options)) args = args.concat(options)
63
- if (_.has(serviceOptions, 'middlewares.after')) args = args.concat(_.get(serviceOptions, 'middlewares.after'))
64
- app.use.apply(app, args)
65
- // Get the Feathers service, ie base service + Feathers' internals
66
- service = app.service(feathersPath)
67
- debug('Service declared on path ' + feathersPath)
68
- // Then configuration
69
- service.name = name
70
- service.app = app
71
- if (!service.options) service.options = serviceOptions
72
- else Object.assign(service.options, serviceOptions)
73
- service.path = servicePath
74
- service.context = serviceOptions.context
75
-
76
- // Add some utility functions
77
- service.getPath = function (withApiPrefix) {
78
- let path = service.path
79
- if (withApiPrefix && !path.startsWith(app.get('apiPath'))) {
80
- path = app.get('apiPath') + '/' + path
81
- }
82
- return path
83
- }
84
- service.getContextId = function () {
85
- return contextId // As string
86
- }
87
- return service
88
- }
89
- }
90
-
91
- export async function configureService (name, service, servicesPath) {
92
- let filepath = path.join(servicesPath, name, `${name}.hooks.js`)
93
- try {
94
- const fileExists = await fs.pathExists(filepath)
95
- if (fileExists) {
96
- const hooks = (await import(url.pathToFileURL(filepath))).default
97
- service.hooks(hooks)
98
- debug(name + ' service hooks configured on path ' + servicesPath)
99
- }
100
- } catch (error) {
101
- debug('No ' + name + ' service hooks configured on path ' + servicesPath)
102
- if (error.code !== 'ERR_MODULE_NOT_FOUND') {
103
- // Log error in this case as this might be linked to a syntax error in required file
104
- debug(filepath, error)
105
- }
106
- // As this is optionnal this require has to fail silently
107
- }
108
-
109
- filepath = path.join(servicesPath, name, `${name}.channels.js`)
110
- try {
111
- const fileExists = await fs.pathExists(filepath)
112
- if (fileExists) {
113
- const channels = (await import(url.pathToFileURL(filepath))).default
114
- _.forOwn(channels, (publisher, event) => {
115
- if (event === 'all') service.publish(publisher)
116
- else service.publish(event, publisher)
117
- })
118
- debug(name + ' service channels configured on path ' + servicesPath)
119
- }
120
- } catch (error) {
121
- debug('No ' + name + ' service channels configured on path ' + servicesPath)
122
- if (error.code !== 'ERR_MODULE_NOT_FOUND') {
123
- // Log error in this case as this might be linked to a syntax error in required file
124
- debug(filepath, error)
125
- }
126
- // As this is optionnal this require has to fail silently
127
- }
128
-
129
- return service
130
- }
131
-
132
- export function createProxyService (options) {
133
- const targetService = options.service
134
- function proxyParams (params) {
135
- if (options.params) {
136
- let proxiedParams
137
- if (typeof options.params === 'function') {
138
- proxiedParams = options.params(params)
139
- } else {
140
- proxiedParams = _.merge(params, options.params)
141
- }
142
- return proxiedParams
143
- } else return params
144
- }
145
- function proxyId (id) {
146
- if (options.id) return options.id(id)
147
- else return id
148
- }
149
- function proxyData (data) {
150
- if (options.data) return options.data(data)
151
- else return data
152
- }
153
- function proxyResult (data) {
154
- if (options.result) return options.result(data)
155
- else return data
156
- }
157
- return {
158
- async find (params) { return proxyResult(await targetService.find(proxyParams(params))) },
159
- async get (id, params) { return proxyResult(await targetService.get(proxyId(id), proxyParams(params))) },
160
- async create (data, params) { return proxyResult(await targetService.create(proxyData(data), proxyParams(params))) },
161
- async update (id, data, params) { return proxyResult(await targetService.update(proxyId(id), proxyData(data), proxyParams(params))) },
162
- async patch (id, data, params) { return proxyResult(await targetService.patch(proxyId(id), proxyData(data), proxyParams(params))) },
163
- async remove (id, params) { return proxyResult(await targetService.remove(proxyId(id), proxyParams(params))) }
164
- }
165
- }
166
-
167
- async function createService (name, app, options = {}) {
168
- debug(`Creating service ${name}`)
169
- const createFeathersService = (await import('feathers-' + app.db.adapter)).default
170
-
171
- const paginate = app.get('paginate')
172
- const serviceOptions = Object.assign({
173
- name,
174
- paginate,
175
- multi: true,
176
- whitelist: [
177
- '$exists', '$and', '$or', '$eq', '$elemMatch', '$distinct', '$groupBy', '$group', '$regex',
178
- '$text', '$search', '$caseSensitive', '$language', '$diacriticSensitive',
179
- '$aggregate', '$near', '$nearSphere', '$geoIntersects', '$geoWithin',
180
- '$maxDistance', '$minDistance', '$geometry', '$box', '$polygon', '$center', '$centerSphere'
181
- ]
182
- }, options)
183
- if (serviceOptions.disabled) return undefined
184
- // For DB services a model has to be provided
185
- const fileName = serviceOptions.fileName || name
186
-
187
- let dbService = false
188
- try {
189
- if (serviceOptions.modelsPath) {
190
- const filepath = path.join(serviceOptions.modelsPath, `${fileName}.model.${app.db.adapter}.js`)
191
- const fileExists = await fs.pathExists(filepath)
192
- if (fileExists) {
193
- const configureModel = (await import(url.pathToFileURL(filepath))).default
194
- configureModel(app, serviceOptions)
195
- debug(name + ' service model configured on path ' + serviceOptions.modelsPath)
196
- dbService = true
197
- }
198
- }
199
- } catch (error) {
200
- debug('No ' + fileName + ' service model configured on path ' + serviceOptions.modelsPath)
201
- if (error.code !== 'ERR_MODULE_NOT_FOUND') {
202
- // Log error in this case as this might be linked to a syntax error in required file
203
- debug(fileName, error)
204
- }
205
- // As this is optionnal this require has to fail silently
206
- }
207
-
208
- // Initialize our service with any options it requires
209
- let service
210
- if (dbService) {
211
- service = createFeathersService(serviceOptions)
212
- dbService = service
213
- } else if (serviceOptions.proxy) {
214
- service = createProxyService(serviceOptions.proxy)
215
- } else {
216
- const filepath = path.join(serviceOptions.servicesPath, fileName, `${fileName}.service.js`)
217
- // Otherwise we expect the service to be provided as a Feathers service interface
218
- service = (await import(url.pathToFileURL(filepath))).default
219
- // If we get a function try to call it assuming it will return the service object
220
- if (typeof service === 'function') {
221
- service = service(name, app, serviceOptions)
222
- }
223
- // Need to set this manually for services not using class inheritance or default adapters
224
- if (serviceOptions.events) service.events = serviceOptions.events
225
- }
226
- // Optionnally a specific service mixin can be provided, apply it
227
- if (dbService && serviceOptions.servicesPath) {
228
- const filepath = path.join(serviceOptions.servicesPath, fileName, `${fileName}.service.js`)
229
- try {
230
- const fileExists = await fs.pathExists(filepath)
231
- if (fileExists) {
232
- let serviceMixin = (await import(url.pathToFileURL(filepath))).default
233
- // If we get a function try to call it assuming it will return the mixin object
234
- if (typeof serviceMixin === 'function') {
235
- serviceMixin = await serviceMixin.bind(dbService)(fileName, app, serviceOptions)
236
- }
237
- Object.assign(service, serviceMixin)
238
- debug(name + ' service mixin configured on path ' + serviceOptions.servicesPath)
239
- }
240
- } catch (error) {
241
- debug('No ' + fileName + ' service mixin configured on path ' + serviceOptions.servicesPath)
242
- // if (error.code !== 'ERR_MODULE_NOT_FOUND') {
243
- // Log error in this case as this might be linked to a syntax error in required file
244
- debug(filepath, error)
245
- // }
246
- // As this is optionnal this require has to fail silently
247
- }
248
- }
249
-
250
- service = declareService(name, app, service, serviceOptions)
251
- // Register hooks and event filters
252
- service = await configureService(fileName, service, serviceOptions.servicesPath)
253
-
254
- debug(service.name + ' service registration completed')
255
- app.emit('service', service)
256
-
257
- return service
258
- }
259
-
260
- async function removeService (service, app) {
261
- // Often called like removeService(getService('xxx'))
262
- // so that we don't want to crash if the service does not exist or has already been unregistered
263
- if (!service) return
264
- let feathersPath = app.get('apiPath') + '/' + service.path
265
- if (feathersPath.startsWith('/')) feathersPath = feathersPath.substr(1)
266
-
267
- app.unuse(feathersPath)
268
- debug(service.name + ' service unregistration completed')
269
- app.emit('service-removed', service)
270
-
271
- return service
272
- }
273
-
274
- export function createWebhook (path, app, options = {}) {
275
- let webhookPath = path
276
- if (options.context) {
277
- webhookPath = idToString(options.context) + '/' + webhookPath
278
- }
279
- const isAllowed = (payload) => {
280
- // Default is to expose all services/operations
281
- if (!options.filter) return true
282
- const result = [payload].filter(sift(options.filter))
283
- return result.length > 0
284
- }
285
-
286
- app.post(app.get('apiPath') + '/webhooks/' + webhookPath, authenticate('jwt'), async (req, res, next) => {
287
- const payload = req.body
288
- const config = app.get('authentication')
289
- res.set('content-type', 'application/json')
290
- const params = {}
291
- try {
292
- if (options.preprocessor) {
293
- await options.preprocessor(req, res, payload)
294
- }
295
- // Authenticate when required
296
- if (config) {
297
- params.user = _.get(req, 'feathers.user')
298
- params.checkAuthorisation = true
299
- }
300
- if (req.headers['content-type'] !== 'application/json') {
301
- throw new BadRequest('Webhooks expect application/json content type')
302
- }
303
- if (!isAllowed(payload)) throw new Forbidden('Service or operation not allowed for webhook')
304
- const service = app.getService(payload.service, options.context || payload.context)
305
- if (!service) throw new BadRequest('Service could not be found')
306
- if (typeof service[payload.operation] !== 'function') throw new BadRequest('Service operation could not be found')
307
- const args = []
308
- // Get/Update/Patch/Remove
309
- const operationsWithId = ['get', 'update', 'patch', 'remove']
310
- if (operationsWithId.includes(payload.operation)) {
311
- if (!_.has(payload, 'id')) throw new BadRequest('Missing id for operation')
312
- args.push(_.get(payload, 'id'))
313
- }
314
- // Create/Update/Patch
315
- const operationsWithData = ['create', 'update', 'patch']
316
- if (operationsWithData.includes(payload.operation)) {
317
- if (!_.has(payload, 'data')) throw new BadRequest('Missing data for operation')
318
- args.push(_.get(payload, 'data'))
319
- }
320
- // Params
321
- args.push(params)
322
- if (options.postprocessor) {
323
- await options.postprocessor(service, args, payload)
324
- }
325
- try {
326
- debug(`Performing ${payload.service} service ${payload.operation} operation through webhook ${webhookPath} with args:`, args)
327
- const result = await service[payload.operation].apply(service, args)
328
- // Send back result
329
- res.json(result)
330
- } catch (error) {
331
- throw new BadRequest('Service operation could not be performed')
332
- }
333
- } catch (error) {
334
- debug(`Webhook ${webhookPath} error:`, error)
335
- // Send back error
336
- res.status(error.code || 500).json(error.toJSON())
337
- }
338
- })
339
-
340
- debug(`Webhook ${webhookPath} registration completed`)
341
- }
342
-
343
- function setupLogger (app) {
344
- debug('Setup application loggers')
345
- const logsConfig = Object.assign({ // Default logger erased by provided one if any
346
- Console: {
347
- format: winston.format.combine(winston.format.colorize(), winston.format.simple())
348
- }
349
- }, _.omit(app.get('logs'), ['level', 'format']))
350
- // Remove winston defaults
351
- try {
352
- winston.clear()
353
- } catch (error) {
354
- // Logger might be down, use console
355
- console.error('Could not remove default logger transport(s)', error)
356
- }
357
- // We have one entry per log type
358
- const logsTypes = Object.getOwnPropertyNames(logsConfig)
359
- // Create corresponding winston transports with options
360
- const transports = []
361
- logsTypes.forEach(logType => {
362
- const logOptions = logsConfig[logType]
363
- debug(`Setup ${logType} transport with options`, logOptions)
364
- try {
365
- transports.push(new winston.transports[logType](logOptions))
366
- } catch (error) {
367
- // Logger might be down, use console
368
- console.error(`Could not setup logger ${logType}`, error)
369
- }
370
- })
371
- app.logger = winston.createLogger(Object.assign({
372
- level: (process.env.NODE_ENV === 'development' ? 'debug' : 'info'), transports
373
- }, _.pick(logsConfig, ['level', 'format'])))
374
- }
375
-
376
- function setupSockets (app) {
377
- const apiLimiter = app.get('apiLimiter')
378
- const connections = {}
379
- let nbConnections = 0
380
-
381
- return io => {
382
- // By default EventEmitters will print a warning if more than 10 listeners are added for a particular event.
383
- // The value can be set to Infinity (or 0) to indicate an unlimited number of listeners.
384
- io.sockets.setMaxListeners(0)
385
- const maxConnections = _.get(apiLimiter, 'websocket.maxConcurrency', 0)
386
- const maxIpConnections = _.get(apiLimiter, 'websocket.concurrency', 0)
387
-
388
- io.on('connection', socket => {
389
- nbConnections++
390
- debug(`New socket connection on server with pid ${process.pid}`, socket.id, socket.conn.remoteAddress, nbConnections)
391
- // Setup disconnect handler first
392
- socket.on('disconnect', (reason) => {
393
- nbConnections--
394
- debug(reason)
395
- debug(`Socket disconnection on server with pid ${process.pid}`, socket.id, socket.conn.remoteAddress, nbConnections)
396
- if (maxIpConnections > 0) {
397
- const nbIpConnections = _.get(connections, socket.conn.remoteAddress) - 1
398
- debug('Total number of connections for', socket.id, socket.conn.remoteAddress, nbIpConnections)
399
- _.set(connections, socket.conn.remoteAddress, nbIpConnections)
400
- }
401
- })
402
- if (maxConnections > 0) {
403
- if (nbConnections > maxConnections) {
404
- tooManyRequests(socket, 'Too many concurrent connections (rate limiting)', 'RATE_LIMITING_CONCURRENCY')
405
- return
406
- }
407
- }
408
- if (maxIpConnections > 0) {
409
- if (_.has(connections, socket.conn.remoteAddress)) {
410
- const nbIpConnections = _.get(connections, socket.conn.remoteAddress) + 1
411
- debug('Total number of connections for', socket.id, socket.conn.remoteAddress, nbConnections)
412
- _.set(connections, socket.conn.remoteAddress, nbIpConnections)
413
- if (nbIpConnections > maxIpConnections) {
414
- tooManyRequests(socket, 'Too many concurrent connections (rate limiting)', 'RATE_LIMITING_CONCURRENCY')
415
- return
416
- }
417
- } else {
418
- _.set(connections, socket.conn.remoteAddress, 1)
419
- }
420
- }
421
- /* For debug purpose: trace all data received
422
- socket.use((packet, next) => {
423
- console.log(packet)
424
- next()
425
- })
426
- */
427
- if (apiLimiter && apiLimiter.websocket) {
428
- const { tokensPerInterval, interval } = apiLimiter.websocket
429
- // Function used to filter whitelisted services, defaults to none
430
- const services = _.get(apiLimiter.websocket, 'services', (service) => false)
431
- socket.socketLimiter = new SocketLimiter(tokensPerInterval, interval)
432
- socket.use((packet, next) => {
433
- if (packet.length > 0) {
434
- // Packets are formatted according to service interface,
435
- // e.g. like [service_method, service_path, id or data, params]
436
- // Bypass rate limiting on whitelist
437
- if ((packet.length > 1) && (typeof packet[1] === 'string')) {
438
- // Service path includes API prefix (but without trailing /)
439
- // Get name from service path without api prefix
440
- const serviceName = packet[1].replace(app.get('apiPath').substring(1) + '/', '')
441
- const service = app.getService(serviceName)
442
- if (service && services(service)) {
443
- debugLimiter('By-pass rate limiting on whitelisted service operation', socket.id, socket.conn.remoteAddress, packet[0], packet[1])
444
- next()
445
- return
446
- }
447
- }
448
- debugLimiter(socket.socketLimiter.getTokensRemaining() + ' remaining API token for socket', socket.id, socket.conn.remoteAddress)
449
- if (!socket.socketLimiter.tryRemoveTokens(1)) { // if exceeded
450
- tooManyRequests(socket, 'Too many requests in a given amount of time (rate limiting)', 'RATE_LIMITING')
451
- // FIXME: calling this causes a client timeout
452
- // next(error)
453
- // Need to normalize the error object as JSON
454
- // let result = {}
455
- // Object.getOwnPropertyNames(error).forEach(key => (result[key] = error[key]))
456
- // Trying to send error like in https://github.com/feathersjs/transport-commons/blob/auk/src/events.js#L103
457
- // does not work either (also generates a client timeout)
458
- // socket.emit(`${servicePath} error`, result)
459
- // socket.emit(result)
460
- return
461
- }
462
- }
463
- next()
464
- })
465
- }
466
- })
467
- }
468
- }
469
-
470
- function setupHealthcheck (app) {
471
- app.get('/healthcheck', async (req, res) => {
472
- res.set('Content-Type', 'application/json')
473
- const result = await app.db.healthcheck()
474
- const status = (result ? 200 : 500)
475
- return res.status(status).json({ isRunning: true, isDatabaseRunning: result })
476
- })
477
- }
478
-
479
- export function kdk (config = {}) {
480
- const app = express(feathers())
481
- // By default EventEmitters will print a warning if more than 10 listeners are added for a particular event.
482
- // The value can be set to Infinity (or 0) to indicate an unlimited number of listeners.
483
- app.setMaxListeners(0)
484
- // Load app configuration first
485
- app.configure(configuration())
486
- // Then setup logger, healthcheck, etc.
487
- setupLogger(app)
488
- setupHealthcheck(app)
489
-
490
- // Override config
491
- Object.keys(config).forEach((name) => {
492
- const value = config[name]
493
- debug(`Setting ${name} configuration value to`, value)
494
- app.set(name, value)
495
- })
496
-
497
- // This retrieve corresponding service options from app config if any
498
- app.getServiceOptions = function (name) {
499
- const services = app.get('services')
500
- if (!services) return {}
501
- return _.get(services, name, {})
502
- }
503
- // This avoid managing the API path before each service name
504
- app.getService = function (path, context) {
505
- try {
506
- // Context is given as string ID
507
- if (context && typeof context === 'string') {
508
- return app.service(app.get('apiPath') + '/' + context + '/' + path)
509
- } else if (context && typeof context === 'object') {
510
- // Could be Object ID or raw object
511
- if (ObjectID.isValid(context)) return app.service(app.get('apiPath') + '/' + context.toString() + '/' + path)
512
- else return app.service(app.get('apiPath') + '/' + context._id.toString() + '/' + path)
513
- } else {
514
- return app.service(app.get('apiPath') + '/' + path)
515
- }
516
- } catch {
517
- // We return a false-y value in case the service wasn't found
518
- return null
519
- }
520
- }
521
- // This is used to add hooks/filters to services
522
- app.configureService = async function (name, service, servicesPath) {
523
- service = await configureService(name, service, servicesPath)
524
- return service
525
- }
526
- // This is used to create standard services
527
- app.createService = async function (name, options) {
528
- const service = await createService(name, app, options)
529
- return service
530
- }
531
- // This is used to remove standard services
532
- app.removeService = async function (service) {
533
- service = await removeService(service, app)
534
- return service
535
- }
536
- // This is used to create webhooks
537
- app.createWebhook = function (path, options) {
538
- return createWebhook(path, app, options)
539
- }
540
- // Override Feathers configure that do not manage async operations,
541
- // here we also simply call the function given as parameter but await for it
542
- app.configure = async function (fn) {
543
- await fn.call(this, this)
544
- return this
545
- }
546
- const apiLimiter = app.get('apiLimiter')
547
- if (apiLimiter && apiLimiter.http) {
548
- // Function used to filter whitelisted services, defaults to none
549
- const services = _.get(apiLimiter.http, 'services', (service) => false)
550
- const handler = (req, res, next) => {
551
- // Bypass rate limiting on whitelist
552
- let service
553
- try {
554
- const serviceUrl = new url.URL(req.originalUrl)
555
- service = app.service(serviceUrl.pathname)
556
- } catch (error) {
557
- debugLimiter(error)
558
- }
559
- if (service && services(service)) {
560
- debugLimiter('By-pass rate limiting on whitelisted service operation', req.method, path)
561
- next()
562
- } else {
563
- const error = new TooManyRequests('Too many requests in a given amount of time (rate limiting)',
564
- { translation: { key: 'RATE_LIMITING' } })
565
- res.status(error.code)
566
- res.set('Content-Type', 'application/json')
567
- res.json(Object.assign({}, error.toJSON()))
568
- }
569
- }
570
- app.use(app.get('apiPath') || '/', new HttpLimiter(Object.assign({ handler }, apiLimiter.http)))
571
- }
572
-
573
- // Enable CORS, security, compression, and body parsing
574
- app.use(cors(app.get('cors')))
575
- app.use(helmet(app.get('helmet')))
576
- app.use(compress(app.get('compression')))
577
- const bodyParserConfig = app.get('bodyParser')
578
- app.use(express.json(_.get(bodyParserConfig, 'json')))
579
- app.use(express.urlencoded(Object.assign({ extended: true }, _.get(bodyParserConfig, 'urlencoded'))))
580
-
581
- // Set up plugins and providers
582
- app.configure(rest())
583
- const socketioConfig = app.get('socketio') || {}
584
- app.configure(socketio(Object.assign({ path: (app.get('apiPath') || '/') + 'ws' }, socketioConfig), setupSockets(app)))
585
- app.configure(auth)
586
-
587
- // Initialize DB
588
- app.db = Database.create(app)
589
-
590
- return app
591
- }
1
+ import path from 'path'
2
+ import url from 'url'
3
+ import fs from 'fs-extra'
4
+ import makeDebug from 'debug'
5
+ import winston from 'winston'
6
+ import _ from 'lodash'
7
+ import siftModule from 'sift'
8
+ import 'winston-daily-rotate-file'
9
+ import compress from 'compression'
10
+ import cors from 'cors'
11
+ import helmet from 'helmet'
12
+ import { RateLimiter as SocketLimiter } from 'limiter'
13
+ import HttpLimiter from 'express-rate-limit'
14
+ import feathers from '@feathersjs/feathers'
15
+ import configuration from '@feathersjs/configuration'
16
+ import errors from '@feathersjs/errors'
17
+ import express, { authenticate } from '@feathersjs/express'
18
+ import socketio from '@feathersjs/socketio'
19
+ import mongodb from 'mongodb'
20
+ import { Database, idToString } from './db.js'
21
+ import auth from './authentication.js'
22
+
23
+ const debug = makeDebug('kdk:core:application')
24
+ const debugLimiter = makeDebug('kdk:core:application:limiter')
25
+ const { TooManyRequests, Forbidden, BadRequest } = errors
26
+ const { ObjectID } = mongodb
27
+ const { rest } = express
28
+ const sift = siftModule.default
29
+
30
+ // Initialize debugger to be used in feathers
31
+ feathers.setDebug(makeDebug)
32
+
33
+ function tooManyRequests (socket, message, key) {
34
+ debug(message)
35
+ const error = new TooManyRequests(message, { translation: { key } })
36
+ socket.emit('rate-limit', error)
37
+ // Add a timeout so that error message is correctly handled
38
+ setTimeout(() => socket.disconnect(true), 3000)
39
+ }
40
+
41
+ export function declareService (name, app, service, serviceOptions = {}) {
42
+ let servicePath = serviceOptions.path || name
43
+ let contextId
44
+ if (serviceOptions.context) {
45
+ contextId = idToString(serviceOptions.context)
46
+ servicePath = contextId + '/' + servicePath
47
+ }
48
+
49
+ let feathersPath = app.get('apiPath') + '/' + servicePath
50
+ if (feathersPath.startsWith('/')) feathersPath = feathersPath.substr(1)
51
+
52
+ try {
53
+ const feathersService = app.service(feathersPath)
54
+ // Some internal Feathers service might internally declare the service
55
+ return feathersService
56
+ } catch (error) {
57
+ // Initialize our service by providing any middleware as well
58
+ let args = [feathersPath]
59
+ if (_.has(serviceOptions, 'middlewares.before')) args = args.concat(_.get(serviceOptions, 'middlewares.before'))
60
+ args.push(service)
61
+ const options = _.pick(serviceOptions, ['methods', 'events'])
62
+ if (!_.isEmpty(options)) args = args.concat(options)
63
+ if (_.has(serviceOptions, 'middlewares.after')) args = args.concat(_.get(serviceOptions, 'middlewares.after'))
64
+ app.use.apply(app, args)
65
+ // Get the Feathers service, ie base service + Feathers' internals
66
+ service = app.service(feathersPath)
67
+ debug('Service declared on path ' + feathersPath)
68
+ // Then configuration
69
+ service.name = name
70
+ service.app = app
71
+ if (!service.options) service.options = serviceOptions
72
+ else Object.assign(service.options, serviceOptions)
73
+ service.path = servicePath
74
+ service.context = serviceOptions.context
75
+
76
+ // Add some utility functions
77
+ service.getPath = function (withApiPrefix) {
78
+ let path = service.path
79
+ if (withApiPrefix && !path.startsWith(app.get('apiPath'))) {
80
+ path = app.get('apiPath') + '/' + path
81
+ }
82
+ return path
83
+ }
84
+ service.getContextId = function () {
85
+ return contextId // As string
86
+ }
87
+ return service
88
+ }
89
+ }
90
+
91
+ export async function configureService (name, service, servicesPath) {
92
+ let filepath = path.join(servicesPath, name, `${name}.hooks.js`)
93
+ try {
94
+ const fileExists = await fs.pathExists(filepath)
95
+ if (fileExists) {
96
+ const hooks = (await import(url.pathToFileURL(filepath))).default
97
+ service.hooks(hooks)
98
+ debug(name + ' service hooks configured on path ' + servicesPath)
99
+ }
100
+ } catch (error) {
101
+ debug('No ' + name + ' service hooks configured on path ' + servicesPath)
102
+ if (error.code !== 'ERR_MODULE_NOT_FOUND') {
103
+ // Log error in this case as this might be linked to a syntax error in required file
104
+ debug(filepath, error)
105
+ }
106
+ // As this is optionnal this require has to fail silently
107
+ }
108
+
109
+ filepath = path.join(servicesPath, name, `${name}.channels.js`)
110
+ try {
111
+ const fileExists = await fs.pathExists(filepath)
112
+ if (fileExists) {
113
+ const channels = (await import(url.pathToFileURL(filepath))).default
114
+ _.forOwn(channels, (publisher, event) => {
115
+ if (event === 'all') service.publish(publisher)
116
+ else service.publish(event, publisher)
117
+ })
118
+ debug(name + ' service channels configured on path ' + servicesPath)
119
+ }
120
+ } catch (error) {
121
+ debug('No ' + name + ' service channels configured on path ' + servicesPath)
122
+ if (error.code !== 'ERR_MODULE_NOT_FOUND') {
123
+ // Log error in this case as this might be linked to a syntax error in required file
124
+ debug(filepath, error)
125
+ }
126
+ // As this is optionnal this require has to fail silently
127
+ }
128
+
129
+ return service
130
+ }
131
+
132
+ export function createProxyService (options) {
133
+ const targetService = options.service
134
+ function proxyParams (params) {
135
+ if (options.params) {
136
+ let proxiedParams
137
+ if (typeof options.params === 'function') {
138
+ proxiedParams = options.params(params)
139
+ } else {
140
+ proxiedParams = _.merge(params, options.params)
141
+ }
142
+ return proxiedParams
143
+ } else return params
144
+ }
145
+ function proxyId (id) {
146
+ if (options.id) return options.id(id)
147
+ else return id
148
+ }
149
+ function proxyData (data) {
150
+ if (options.data) return options.data(data)
151
+ else return data
152
+ }
153
+ function proxyResult (data) {
154
+ if (options.result) return options.result(data)
155
+ else return data
156
+ }
157
+ return {
158
+ async find (params) { return proxyResult(await targetService.find(proxyParams(params))) },
159
+ async get (id, params) { return proxyResult(await targetService.get(proxyId(id), proxyParams(params))) },
160
+ async create (data, params) { return proxyResult(await targetService.create(proxyData(data), proxyParams(params))) },
161
+ async update (id, data, params) { return proxyResult(await targetService.update(proxyId(id), proxyData(data), proxyParams(params))) },
162
+ async patch (id, data, params) { return proxyResult(await targetService.patch(proxyId(id), proxyData(data), proxyParams(params))) },
163
+ async remove (id, params) { return proxyResult(await targetService.remove(proxyId(id), proxyParams(params))) }
164
+ }
165
+ }
166
+
167
+ async function createService (name, app, options = {}) {
168
+ debug(`Creating service ${name}`)
169
+ const createFeathersService = (await import('feathers-' + app.db.adapter)).default
170
+
171
+ const paginate = app.get('paginate')
172
+ const serviceOptions = Object.assign({
173
+ name,
174
+ paginate,
175
+ multi: true,
176
+ whitelist: [
177
+ '$exists', '$and', '$or', '$eq', '$elemMatch', '$distinct', '$groupBy', '$group', '$regex',
178
+ '$text', '$search', '$caseSensitive', '$language', '$diacriticSensitive',
179
+ '$aggregate', '$near', '$nearSphere', '$geoIntersects', '$geoWithin',
180
+ '$maxDistance', '$minDistance', '$geometry', '$box', '$polygon', '$center', '$centerSphere'
181
+ ]
182
+ }, options)
183
+ if (serviceOptions.disabled) return undefined
184
+ // For DB services a model has to be provided
185
+ const fileName = serviceOptions.fileName || name
186
+
187
+ let dbService = false
188
+ try {
189
+ if (serviceOptions.modelsPath) {
190
+ const filepath = path.join(serviceOptions.modelsPath, `${fileName}.model.${app.db.adapter}.js`)
191
+ const fileExists = await fs.pathExists(filepath)
192
+ if (fileExists) {
193
+ const configureModel = (await import(url.pathToFileURL(filepath))).default
194
+ configureModel(app, serviceOptions)
195
+ debug(name + ' service model configured on path ' + serviceOptions.modelsPath)
196
+ dbService = true
197
+ }
198
+ }
199
+ } catch (error) {
200
+ debug('No ' + fileName + ' service model configured on path ' + serviceOptions.modelsPath)
201
+ if (error.code !== 'ERR_MODULE_NOT_FOUND') {
202
+ // Log error in this case as this might be linked to a syntax error in required file
203
+ debug(fileName, error)
204
+ }
205
+ // As this is optionnal this require has to fail silently
206
+ }
207
+
208
+ // Initialize our service with any options it requires
209
+ let service
210
+ if (dbService) {
211
+ service = createFeathersService(serviceOptions)
212
+ dbService = service
213
+ } else if (serviceOptions.proxy) {
214
+ service = createProxyService(serviceOptions.proxy)
215
+ } else {
216
+ const filepath = path.join(serviceOptions.servicesPath, fileName, `${fileName}.service.js`)
217
+ // Otherwise we expect the service to be provided as a Feathers service interface
218
+ service = (await import(url.pathToFileURL(filepath))).default
219
+ // If we get a function try to call it assuming it will return the service object
220
+ if (typeof service === 'function') {
221
+ service = service(name, app, serviceOptions)
222
+ }
223
+ // Need to set this manually for services not using class inheritance or default adapters
224
+ if (serviceOptions.events) service.events = serviceOptions.events
225
+ }
226
+ // Optionnally a specific service mixin can be provided, apply it
227
+ if (dbService && serviceOptions.servicesPath) {
228
+ const filepath = path.join(serviceOptions.servicesPath, fileName, `${fileName}.service.js`)
229
+ try {
230
+ const fileExists = await fs.pathExists(filepath)
231
+ if (fileExists) {
232
+ let serviceMixin = (await import(url.pathToFileURL(filepath))).default
233
+ // If we get a function try to call it assuming it will return the mixin object
234
+ if (typeof serviceMixin === 'function') {
235
+ serviceMixin = await serviceMixin.bind(dbService)(fileName, app, serviceOptions)
236
+ }
237
+ Object.assign(service, serviceMixin)
238
+ debug(name + ' service mixin configured on path ' + serviceOptions.servicesPath)
239
+ }
240
+ } catch (error) {
241
+ debug('No ' + fileName + ' service mixin configured on path ' + serviceOptions.servicesPath)
242
+ // if (error.code !== 'ERR_MODULE_NOT_FOUND') {
243
+ // Log error in this case as this might be linked to a syntax error in required file
244
+ debug(filepath, error)
245
+ // }
246
+ // As this is optionnal this require has to fail silently
247
+ }
248
+ }
249
+
250
+ service = declareService(name, app, service, serviceOptions)
251
+ // Register hooks and event filters
252
+ service = await configureService(fileName, service, serviceOptions.servicesPath)
253
+
254
+ debug(service.name + ' service registration completed')
255
+ app.emit('service', service)
256
+
257
+ return service
258
+ }
259
+
260
+ async function removeService (service, app) {
261
+ // Often called like removeService(getService('xxx'))
262
+ // so that we don't want to crash if the service does not exist or has already been unregistered
263
+ if (!service) return
264
+ let feathersPath = app.get('apiPath') + '/' + service.path
265
+ if (feathersPath.startsWith('/')) feathersPath = feathersPath.substr(1)
266
+
267
+ app.unuse(feathersPath)
268
+ debug(service.name + ' service unregistration completed')
269
+ app.emit('service-removed', service)
270
+
271
+ return service
272
+ }
273
+
274
+ export function createWebhook (path, app, options = {}) {
275
+ let webhookPath = path
276
+ if (options.context) {
277
+ webhookPath = idToString(options.context) + '/' + webhookPath
278
+ }
279
+ const isAllowed = (payload) => {
280
+ // Default is to expose all services/operations
281
+ if (!options.filter) return true
282
+ const result = [payload].filter(sift(options.filter))
283
+ return result.length > 0
284
+ }
285
+
286
+ app.post(app.get('apiPath') + '/webhooks/' + webhookPath, authenticate('jwt'), async (req, res, next) => {
287
+ const payload = req.body
288
+ const config = app.get('authentication')
289
+ res.set('content-type', 'application/json')
290
+ const params = {}
291
+ try {
292
+ if (options.preprocessor) {
293
+ await options.preprocessor(req, res, payload)
294
+ }
295
+ // Authenticate when required
296
+ if (config) {
297
+ params.user = _.get(req, 'feathers.user')
298
+ params.checkAuthorisation = true
299
+ }
300
+ if (req.headers['content-type'] !== 'application/json') {
301
+ throw new BadRequest('Webhooks expect application/json content type')
302
+ }
303
+ if (!isAllowed(payload)) throw new Forbidden('Service or operation not allowed for webhook')
304
+ const service = app.getService(payload.service, options.context || payload.context)
305
+ if (!service) throw new BadRequest('Service could not be found')
306
+ if (typeof service[payload.operation] !== 'function') throw new BadRequest('Service operation could not be found')
307
+ const args = []
308
+ // Get/Update/Patch/Remove
309
+ const operationsWithId = ['get', 'update', 'patch', 'remove']
310
+ if (operationsWithId.includes(payload.operation)) {
311
+ if (!_.has(payload, 'id')) throw new BadRequest('Missing id for operation')
312
+ args.push(_.get(payload, 'id'))
313
+ }
314
+ // Create/Update/Patch
315
+ const operationsWithData = ['create', 'update', 'patch']
316
+ if (operationsWithData.includes(payload.operation)) {
317
+ if (!_.has(payload, 'data')) throw new BadRequest('Missing data for operation')
318
+ args.push(_.get(payload, 'data'))
319
+ }
320
+ // Params
321
+ args.push(params)
322
+ if (options.postprocessor) {
323
+ await options.postprocessor(service, args, payload)
324
+ }
325
+ try {
326
+ debug(`Performing ${payload.service} service ${payload.operation} operation through webhook ${webhookPath} with args:`, args)
327
+ const result = await service[payload.operation].apply(service, args)
328
+ // Send back result
329
+ res.json(result)
330
+ } catch (error) {
331
+ throw new BadRequest('Service operation could not be performed')
332
+ }
333
+ } catch (error) {
334
+ debug(`Webhook ${webhookPath} error:`, error)
335
+ // Send back error
336
+ res.status(error.code || 500).json(error.toJSON())
337
+ }
338
+ })
339
+
340
+ debug(`Webhook ${webhookPath} registration completed`)
341
+ }
342
+
343
+ function setupLogger (app) {
344
+ debug('Setup application loggers')
345
+ const logsConfig = Object.assign({ // Default logger erased by provided one if any
346
+ Console: {
347
+ format: winston.format.combine(winston.format.colorize(), winston.format.simple())
348
+ }
349
+ }, _.omit(app.get('logs'), ['level', 'format']))
350
+ // Remove winston defaults
351
+ try {
352
+ winston.clear()
353
+ } catch (error) {
354
+ // Logger might be down, use console
355
+ console.error('Could not remove default logger transport(s)', error)
356
+ }
357
+ // We have one entry per log type
358
+ const logsTypes = Object.getOwnPropertyNames(logsConfig)
359
+ // Create corresponding winston transports with options
360
+ const transports = []
361
+ logsTypes.forEach(logType => {
362
+ const logOptions = logsConfig[logType]
363
+ debug(`Setup ${logType} transport with options`, logOptions)
364
+ try {
365
+ transports.push(new winston.transports[logType](logOptions))
366
+ } catch (error) {
367
+ // Logger might be down, use console
368
+ console.error(`Could not setup logger ${logType}`, error)
369
+ }
370
+ })
371
+ app.logger = winston.createLogger(Object.assign({
372
+ level: (process.env.NODE_ENV === 'development' ? 'debug' : 'info'), transports
373
+ }, _.pick(logsConfig, ['level', 'format'])))
374
+ }
375
+
376
+ function setupSockets (app) {
377
+ const apiLimiter = app.get('apiLimiter')
378
+ const connections = {}
379
+ let nbConnections = 0
380
+
381
+ return io => {
382
+ // By default EventEmitters will print a warning if more than 10 listeners are added for a particular event.
383
+ // The value can be set to Infinity (or 0) to indicate an unlimited number of listeners.
384
+ io.sockets.setMaxListeners(0)
385
+ const maxConnections = _.get(apiLimiter, 'websocket.maxConcurrency', 0)
386
+ const maxIpConnections = _.get(apiLimiter, 'websocket.concurrency', 0)
387
+
388
+ io.on('connection', socket => {
389
+ nbConnections++
390
+ debug(`New socket connection on server with pid ${process.pid}`, socket.id, socket.conn.remoteAddress, nbConnections)
391
+ // Setup disconnect handler first
392
+ socket.on('disconnect', (reason) => {
393
+ nbConnections--
394
+ debug(reason)
395
+ debug(`Socket disconnection on server with pid ${process.pid}`, socket.id, socket.conn.remoteAddress, nbConnections)
396
+ if (maxIpConnections > 0) {
397
+ const nbIpConnections = _.get(connections, socket.conn.remoteAddress) - 1
398
+ debug('Total number of connections for', socket.id, socket.conn.remoteAddress, nbIpConnections)
399
+ _.set(connections, socket.conn.remoteAddress, nbIpConnections)
400
+ }
401
+ })
402
+ if (maxConnections > 0) {
403
+ if (nbConnections > maxConnections) {
404
+ tooManyRequests(socket, 'Too many concurrent connections (rate limiting)', 'RATE_LIMITING_CONCURRENCY')
405
+ return
406
+ }
407
+ }
408
+ if (maxIpConnections > 0) {
409
+ if (_.has(connections, socket.conn.remoteAddress)) {
410
+ const nbIpConnections = _.get(connections, socket.conn.remoteAddress) + 1
411
+ debug('Total number of connections for', socket.id, socket.conn.remoteAddress, nbConnections)
412
+ _.set(connections, socket.conn.remoteAddress, nbIpConnections)
413
+ if (nbIpConnections > maxIpConnections) {
414
+ tooManyRequests(socket, 'Too many concurrent connections (rate limiting)', 'RATE_LIMITING_CONCURRENCY')
415
+ return
416
+ }
417
+ } else {
418
+ _.set(connections, socket.conn.remoteAddress, 1)
419
+ }
420
+ }
421
+ /* For debug purpose: trace all data received
422
+ socket.use((packet, next) => {
423
+ console.log(packet)
424
+ next()
425
+ })
426
+ */
427
+ if (apiLimiter && apiLimiter.websocket) {
428
+ const { tokensPerInterval, interval } = apiLimiter.websocket
429
+ // Function used to filter whitelisted services, defaults to none
430
+ const services = _.get(apiLimiter.websocket, 'services', (service) => false)
431
+ socket.socketLimiter = new SocketLimiter(tokensPerInterval, interval)
432
+ socket.use((packet, next) => {
433
+ if (packet.length > 0) {
434
+ // Packets are formatted according to service interface,
435
+ // e.g. like [service_method, service_path, id or data, params]
436
+ // Bypass rate limiting on whitelist
437
+ if ((packet.length > 1) && (typeof packet[1] === 'string')) {
438
+ // Service path includes API prefix (but without trailing /)
439
+ // Get name from service path without api prefix
440
+ const serviceName = packet[1].replace(app.get('apiPath').substring(1) + '/', '')
441
+ const service = app.getService(serviceName)
442
+ if (service && services(service)) {
443
+ debugLimiter('By-pass rate limiting on whitelisted service operation', socket.id, socket.conn.remoteAddress, packet[0], packet[1])
444
+ next()
445
+ return
446
+ }
447
+ }
448
+ debugLimiter(socket.socketLimiter.getTokensRemaining() + ' remaining API token for socket', socket.id, socket.conn.remoteAddress)
449
+ if (!socket.socketLimiter.tryRemoveTokens(1)) { // if exceeded
450
+ tooManyRequests(socket, 'Too many requests in a given amount of time (rate limiting)', 'RATE_LIMITING')
451
+ // FIXME: calling this causes a client timeout
452
+ // next(error)
453
+ // Need to normalize the error object as JSON
454
+ // let result = {}
455
+ // Object.getOwnPropertyNames(error).forEach(key => (result[key] = error[key]))
456
+ // Trying to send error like in https://github.com/feathersjs/transport-commons/blob/auk/src/events.js#L103
457
+ // does not work either (also generates a client timeout)
458
+ // socket.emit(`${servicePath} error`, result)
459
+ // socket.emit(result)
460
+ return
461
+ }
462
+ }
463
+ next()
464
+ })
465
+ }
466
+ })
467
+ }
468
+ }
469
+
470
+ function setupHealthcheck (app) {
471
+ app.get('/healthcheck', async (req, res) => {
472
+ res.set('Content-Type', 'application/json')
473
+ const result = await app.db.healthcheck()
474
+ const status = (result ? 200 : 500)
475
+ return res.status(status).json({ isRunning: true, isDatabaseRunning: result })
476
+ })
477
+ }
478
+
479
+ export function kdk (config = {}) {
480
+ const app = express(feathers())
481
+ // By default EventEmitters will print a warning if more than 10 listeners are added for a particular event.
482
+ // The value can be set to Infinity (or 0) to indicate an unlimited number of listeners.
483
+ app.setMaxListeners(0)
484
+ // Load app configuration first
485
+ app.configure(configuration())
486
+ // Then setup logger, healthcheck, etc.
487
+ setupLogger(app)
488
+ setupHealthcheck(app)
489
+
490
+ // Override config
491
+ Object.keys(config).forEach((name) => {
492
+ const value = config[name]
493
+ debug(`Setting ${name} configuration value to`, value)
494
+ app.set(name, value)
495
+ })
496
+
497
+ // This retrieve corresponding service options from app config if any
498
+ app.getServiceOptions = function (name) {
499
+ const services = app.get('services')
500
+ if (!services) return {}
501
+ return _.get(services, name, {})
502
+ }
503
+ // This avoid managing the API path before each service name
504
+ app.getService = function (path, context) {
505
+ try {
506
+ // Context is given as string ID
507
+ if (context && typeof context === 'string') {
508
+ return app.service(app.get('apiPath') + '/' + context + '/' + path)
509
+ } else if (context && typeof context === 'object') {
510
+ // Could be Object ID or raw object
511
+ if (ObjectID.isValid(context)) return app.service(app.get('apiPath') + '/' + context.toString() + '/' + path)
512
+ else return app.service(app.get('apiPath') + '/' + context._id.toString() + '/' + path)
513
+ } else {
514
+ return app.service(app.get('apiPath') + '/' + path)
515
+ }
516
+ } catch {
517
+ // We return a false-y value in case the service wasn't found
518
+ return null
519
+ }
520
+ }
521
+ // This is used to add hooks/filters to services
522
+ app.configureService = async function (name, service, servicesPath) {
523
+ service = await configureService(name, service, servicesPath)
524
+ return service
525
+ }
526
+ // This is used to create standard services
527
+ app.createService = async function (name, options) {
528
+ const service = await createService(name, app, options)
529
+ return service
530
+ }
531
+ // This is used to remove standard services
532
+ app.removeService = async function (service) {
533
+ service = await removeService(service, app)
534
+ return service
535
+ }
536
+ // This is used to create webhooks
537
+ app.createWebhook = function (path, options) {
538
+ return createWebhook(path, app, options)
539
+ }
540
+ // Override Feathers configure that do not manage async operations,
541
+ // here we also simply call the function given as parameter but await for it
542
+ app.configure = async function (fn) {
543
+ await fn.call(this, this)
544
+ return this
545
+ }
546
+ const apiLimiter = app.get('apiLimiter')
547
+ if (apiLimiter && apiLimiter.http) {
548
+ // Function used to filter whitelisted services, defaults to none
549
+ const services = _.get(apiLimiter.http, 'services', (service) => false)
550
+ const handler = (req, res, next) => {
551
+ // Bypass rate limiting on whitelist
552
+ let service
553
+ try {
554
+ const serviceUrl = new url.URL(req.originalUrl)
555
+ service = app.service(serviceUrl.pathname)
556
+ } catch (error) {
557
+ debugLimiter(error)
558
+ }
559
+ if (service && services(service)) {
560
+ debugLimiter('By-pass rate limiting on whitelisted service operation', req.method, path)
561
+ next()
562
+ } else {
563
+ const error = new TooManyRequests('Too many requests in a given amount of time (rate limiting)',
564
+ { translation: { key: 'RATE_LIMITING' } })
565
+ res.status(error.code)
566
+ res.set('Content-Type', 'application/json')
567
+ res.json(Object.assign({}, error.toJSON()))
568
+ }
569
+ }
570
+ app.use(app.get('apiPath') || '/', new HttpLimiter(Object.assign({ handler }, apiLimiter.http)))
571
+ }
572
+
573
+ // Enable CORS, security, compression, and body parsing
574
+ app.use(cors(app.get('cors')))
575
+ app.use(helmet(app.get('helmet')))
576
+ app.use(compress(app.get('compression')))
577
+ const bodyParserConfig = app.get('bodyParser')
578
+ app.use(express.json(_.get(bodyParserConfig, 'json')))
579
+ app.use(express.urlencoded(Object.assign({ extended: true }, _.get(bodyParserConfig, 'urlencoded'))))
580
+
581
+ // Set up plugins and providers
582
+ app.configure(rest())
583
+ const socketioConfig = app.get('socketio') || {}
584
+ app.configure(socketio(Object.assign({ path: (app.get('apiPath') || '/') + 'ws' }, socketioConfig), setupSockets(app)))
585
+ app.configure(auth)
586
+
587
+ // Initialize DB
588
+ app.db = Database.create(app)
589
+
590
+ return app
591
+ }