@qwickapps/server 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +12 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -1
  6. package/dist/plugins/bans/bans-plugin.js +12 -3
  7. package/dist/plugins/bans/bans-plugin.js.map +1 -1
  8. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts +11 -0
  9. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts.map +1 -0
  10. package/dist/plugins/devices/__tests__/devices-plugin.test.js +410 -0
  11. package/dist/plugins/devices/__tests__/devices-plugin.test.js.map +1 -0
  12. package/dist/plugins/devices/__tests__/token-utils.test.d.ts +7 -0
  13. package/dist/plugins/devices/__tests__/token-utils.test.d.ts.map +1 -0
  14. package/dist/plugins/devices/__tests__/token-utils.test.js +197 -0
  15. package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -0
  16. package/dist/plugins/devices/adapters/compute-adapter.d.ts +36 -0
  17. package/dist/plugins/devices/adapters/compute-adapter.d.ts.map +1 -0
  18. package/dist/plugins/devices/adapters/compute-adapter.js +100 -0
  19. package/dist/plugins/devices/adapters/compute-adapter.js.map +1 -0
  20. package/dist/plugins/devices/adapters/index.d.ts +12 -0
  21. package/dist/plugins/devices/adapters/index.d.ts.map +1 -0
  22. package/dist/plugins/devices/adapters/index.js +10 -0
  23. package/dist/plugins/devices/adapters/index.js.map +1 -0
  24. package/dist/plugins/devices/adapters/mobile-adapter.d.ts +41 -0
  25. package/dist/plugins/devices/adapters/mobile-adapter.d.ts.map +1 -0
  26. package/dist/plugins/devices/adapters/mobile-adapter.js +131 -0
  27. package/dist/plugins/devices/adapters/mobile-adapter.js.map +1 -0
  28. package/dist/plugins/devices/devices-plugin.d.ts +70 -0
  29. package/dist/plugins/devices/devices-plugin.d.ts.map +1 -0
  30. package/dist/plugins/devices/devices-plugin.js +453 -0
  31. package/dist/plugins/devices/devices-plugin.js.map +1 -0
  32. package/dist/plugins/devices/index.d.ts +18 -0
  33. package/dist/plugins/devices/index.d.ts.map +1 -0
  34. package/dist/plugins/devices/index.js +18 -0
  35. package/dist/plugins/devices/index.js.map +1 -0
  36. package/dist/plugins/devices/stores/index.d.ts +9 -0
  37. package/dist/plugins/devices/stores/index.d.ts.map +1 -0
  38. package/dist/plugins/devices/stores/index.js +9 -0
  39. package/dist/plugins/devices/stores/index.js.map +1 -0
  40. package/dist/plugins/devices/stores/postgres-store.d.ts +26 -0
  41. package/dist/plugins/devices/stores/postgres-store.d.ts.map +1 -0
  42. package/dist/plugins/devices/stores/postgres-store.js +199 -0
  43. package/dist/plugins/devices/stores/postgres-store.js.map +1 -0
  44. package/dist/plugins/devices/token-utils.d.ts +100 -0
  45. package/dist/plugins/devices/token-utils.d.ts.map +1 -0
  46. package/dist/plugins/devices/token-utils.js +162 -0
  47. package/dist/plugins/devices/token-utils.js.map +1 -0
  48. package/dist/plugins/devices/types.d.ts +307 -0
  49. package/dist/plugins/devices/types.d.ts.map +1 -0
  50. package/dist/plugins/devices/types.js +10 -0
  51. package/dist/plugins/devices/types.js.map +1 -0
  52. package/dist/plugins/index.d.ts +14 -2
  53. package/dist/plugins/index.d.ts.map +1 -1
  54. package/dist/plugins/index.js +13 -1
  55. package/dist/plugins/index.js.map +1 -1
  56. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts +5 -0
  57. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts.map +1 -0
  58. package/dist/plugins/notifications/__tests__/notifications-manager.test.js +470 -0
  59. package/dist/plugins/notifications/__tests__/notifications-manager.test.js.map +1 -0
  60. package/dist/plugins/notifications/index.d.ts +71 -0
  61. package/dist/plugins/notifications/index.d.ts.map +1 -0
  62. package/dist/plugins/notifications/index.js +72 -0
  63. package/dist/plugins/notifications/index.js.map +1 -0
  64. package/dist/plugins/notifications/notifications-manager.d.ts +182 -0
  65. package/dist/plugins/notifications/notifications-manager.d.ts.map +1 -0
  66. package/dist/plugins/notifications/notifications-manager.js +610 -0
  67. package/dist/plugins/notifications/notifications-manager.js.map +1 -0
  68. package/dist/plugins/notifications/notifications-plugin.d.ts +83 -0
  69. package/dist/plugins/notifications/notifications-plugin.d.ts.map +1 -0
  70. package/dist/plugins/notifications/notifications-plugin.js +337 -0
  71. package/dist/plugins/notifications/notifications-plugin.js.map +1 -0
  72. package/dist/plugins/notifications/types.d.ts +164 -0
  73. package/dist/plugins/notifications/types.d.ts.map +1 -0
  74. package/dist/plugins/notifications/types.js +9 -0
  75. package/dist/plugins/notifications/types.js.map +1 -0
  76. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts +12 -0
  77. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts.map +1 -0
  78. package/dist/plugins/parental/__tests__/parental-plugin.test.js +349 -0
  79. package/dist/plugins/parental/__tests__/parental-plugin.test.js.map +1 -0
  80. package/dist/plugins/parental/adapters/index.d.ts +8 -0
  81. package/dist/plugins/parental/adapters/index.d.ts.map +1 -0
  82. package/dist/plugins/parental/adapters/index.js +7 -0
  83. package/dist/plugins/parental/adapters/index.js.map +1 -0
  84. package/dist/plugins/parental/adapters/kids-adapter.d.ts +24 -0
  85. package/dist/plugins/parental/adapters/kids-adapter.d.ts.map +1 -0
  86. package/dist/plugins/parental/adapters/kids-adapter.js +174 -0
  87. package/dist/plugins/parental/adapters/kids-adapter.js.map +1 -0
  88. package/dist/plugins/parental/index.d.ts +14 -0
  89. package/dist/plugins/parental/index.d.ts.map +1 -0
  90. package/dist/plugins/parental/index.js +15 -0
  91. package/dist/plugins/parental/index.js.map +1 -0
  92. package/dist/plugins/parental/parental-plugin.d.ts +88 -0
  93. package/dist/plugins/parental/parental-plugin.d.ts.map +1 -0
  94. package/dist/plugins/parental/parental-plugin.js +666 -0
  95. package/dist/plugins/parental/parental-plugin.js.map +1 -0
  96. package/dist/plugins/parental/stores/index.d.ts +7 -0
  97. package/dist/plugins/parental/stores/index.d.ts.map +1 -0
  98. package/dist/plugins/parental/stores/index.js +7 -0
  99. package/dist/plugins/parental/stores/index.js.map +1 -0
  100. package/dist/plugins/parental/stores/postgres-store.d.ts +10 -0
  101. package/dist/plugins/parental/stores/postgres-store.d.ts.map +1 -0
  102. package/dist/plugins/parental/stores/postgres-store.js +209 -0
  103. package/dist/plugins/parental/stores/postgres-store.js.map +1 -0
  104. package/dist/plugins/parental/types.d.ts +154 -0
  105. package/dist/plugins/parental/types.d.ts.map +1 -0
  106. package/dist/plugins/parental/types.js +10 -0
  107. package/dist/plugins/parental/types.js.map +1 -0
  108. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts +11 -0
  109. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts.map +1 -0
  110. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js +243 -0
  111. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js.map +1 -0
  112. package/dist/plugins/profiles/index.d.ts +12 -0
  113. package/dist/plugins/profiles/index.d.ts.map +1 -0
  114. package/dist/plugins/profiles/index.js +13 -0
  115. package/dist/plugins/profiles/index.js.map +1 -0
  116. package/dist/plugins/profiles/profiles-plugin.d.ts +71 -0
  117. package/dist/plugins/profiles/profiles-plugin.d.ts.map +1 -0
  118. package/dist/plugins/profiles/profiles-plugin.js +481 -0
  119. package/dist/plugins/profiles/profiles-plugin.js.map +1 -0
  120. package/dist/plugins/profiles/stores/index.d.ts +9 -0
  121. package/dist/plugins/profiles/stores/index.d.ts.map +1 -0
  122. package/dist/plugins/profiles/stores/index.js +9 -0
  123. package/dist/plugins/profiles/stores/index.js.map +1 -0
  124. package/dist/plugins/profiles/stores/postgres-store.d.ts +18 -0
  125. package/dist/plugins/profiles/stores/postgres-store.d.ts.map +1 -0
  126. package/dist/plugins/profiles/stores/postgres-store.js +310 -0
  127. package/dist/plugins/profiles/stores/postgres-store.js.map +1 -0
  128. package/dist/plugins/profiles/types.d.ts +289 -0
  129. package/dist/plugins/profiles/types.d.ts.map +1 -0
  130. package/dist/plugins/profiles/types.js +10 -0
  131. package/dist/plugins/profiles/types.js.map +1 -0
  132. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts +11 -0
  133. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts.map +1 -0
  134. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js +305 -0
  135. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js.map +1 -0
  136. package/dist/plugins/subscriptions/index.d.ts +12 -0
  137. package/dist/plugins/subscriptions/index.d.ts.map +1 -0
  138. package/dist/plugins/subscriptions/index.js +13 -0
  139. package/dist/plugins/subscriptions/index.js.map +1 -0
  140. package/dist/plugins/subscriptions/stores/index.d.ts +9 -0
  141. package/dist/plugins/subscriptions/stores/index.d.ts.map +1 -0
  142. package/dist/plugins/subscriptions/stores/index.js +9 -0
  143. package/dist/plugins/subscriptions/stores/index.js.map +1 -0
  144. package/dist/plugins/subscriptions/stores/postgres-store.d.ts +14 -0
  145. package/dist/plugins/subscriptions/stores/postgres-store.d.ts.map +1 -0
  146. package/dist/plugins/subscriptions/stores/postgres-store.js +359 -0
  147. package/dist/plugins/subscriptions/stores/postgres-store.js.map +1 -0
  148. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts +82 -0
  149. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -0
  150. package/dist/plugins/subscriptions/subscriptions-plugin.js +449 -0
  151. package/dist/plugins/subscriptions/subscriptions-plugin.js.map +1 -0
  152. package/dist/plugins/subscriptions/types.d.ts +308 -0
  153. package/dist/plugins/subscriptions/types.d.ts.map +1 -0
  154. package/dist/plugins/subscriptions/types.js +10 -0
  155. package/dist/plugins/subscriptions/types.js.map +1 -0
  156. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts +11 -0
  157. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts.map +1 -0
  158. package/dist/plugins/usage/__tests__/usage-plugin.test.js +218 -0
  159. package/dist/plugins/usage/__tests__/usage-plugin.test.js.map +1 -0
  160. package/dist/plugins/usage/index.d.ts +12 -0
  161. package/dist/plugins/usage/index.d.ts.map +1 -0
  162. package/dist/plugins/usage/index.js +13 -0
  163. package/dist/plugins/usage/index.js.map +1 -0
  164. package/dist/plugins/usage/stores/index.d.ts +9 -0
  165. package/dist/plugins/usage/stores/index.d.ts.map +1 -0
  166. package/dist/plugins/usage/stores/index.js +9 -0
  167. package/dist/plugins/usage/stores/index.js.map +1 -0
  168. package/dist/plugins/usage/stores/postgres-store.d.ts +14 -0
  169. package/dist/plugins/usage/stores/postgres-store.d.ts.map +1 -0
  170. package/dist/plugins/usage/stores/postgres-store.js +146 -0
  171. package/dist/plugins/usage/stores/postgres-store.js.map +1 -0
  172. package/dist/plugins/usage/types.d.ts +195 -0
  173. package/dist/plugins/usage/types.d.ts.map +1 -0
  174. package/dist/plugins/usage/types.js +10 -0
  175. package/dist/plugins/usage/types.js.map +1 -0
  176. package/dist/plugins/usage/usage-plugin.d.ts +51 -0
  177. package/dist/plugins/usage/usage-plugin.d.ts.map +1 -0
  178. package/dist/plugins/usage/usage-plugin.js +412 -0
  179. package/dist/plugins/usage/usage-plugin.js.map +1 -0
  180. package/dist/plugins/users/__tests__/postgres-store.test.d.ts +10 -0
  181. package/dist/plugins/users/__tests__/postgres-store.test.d.ts.map +1 -0
  182. package/dist/plugins/users/__tests__/postgres-store.test.js +229 -0
  183. package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -0
  184. package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
  185. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
  186. package/dist/plugins/users/index.d.ts +2 -2
  187. package/dist/plugins/users/index.d.ts.map +1 -1
  188. package/dist/plugins/users/index.js +1 -1
  189. package/dist/plugins/users/index.js.map +1 -1
  190. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
  191. package/dist/plugins/users/stores/postgres-store.js +76 -0
  192. package/dist/plugins/users/stores/postgres-store.js.map +1 -1
  193. package/dist/plugins/users/types.d.ts +74 -6
  194. package/dist/plugins/users/types.d.ts.map +1 -1
  195. package/dist/plugins/users/users-plugin.d.ts +15 -1
  196. package/dist/plugins/users/users-plugin.d.ts.map +1 -1
  197. package/dist/plugins/users/users-plugin.js +29 -0
  198. package/dist/plugins/users/users-plugin.js.map +1 -1
  199. package/dist-ui/assets/index-CynOqPkb.js +469 -0
  200. package/dist-ui/assets/index-CynOqPkb.js.map +1 -0
  201. package/dist-ui/index.html +1 -1
  202. package/dist-ui-lib/api/controlPanelApi.d.ts +46 -0
  203. package/dist-ui-lib/components/StatCard.d.ts +16 -0
  204. package/dist-ui-lib/dashboard/widgets/NotificationsStatsWidget.d.ts +12 -0
  205. package/dist-ui-lib/dashboard/widgets/index.d.ts +1 -0
  206. package/dist-ui-lib/index.js +1822 -1611
  207. package/dist-ui-lib/index.js.map +1 -1
  208. package/dist-ui-lib/pages/NotificationsPage.d.ts +9 -0
  209. package/dist-ui-lib/utils/formatters.d.ts +19 -0
  210. package/package.json +1 -1
  211. package/src/index.ts +178 -0
  212. package/src/plugins/bans/bans-plugin.ts +15 -3
  213. package/src/plugins/devices/__tests__/devices-plugin.test.ts +551 -0
  214. package/src/plugins/devices/__tests__/token-utils.test.ts +264 -0
  215. package/src/plugins/devices/adapters/compute-adapter.ts +139 -0
  216. package/src/plugins/devices/adapters/index.ts +13 -0
  217. package/src/plugins/devices/adapters/mobile-adapter.ts +179 -0
  218. package/src/plugins/devices/devices-plugin.ts +538 -0
  219. package/src/plugins/devices/index.ts +69 -0
  220. package/src/plugins/devices/stores/index.ts +9 -0
  221. package/src/plugins/devices/stores/postgres-store.ts +304 -0
  222. package/src/plugins/devices/token-utils.ts +213 -0
  223. package/src/plugins/devices/types.ts +351 -0
  224. package/src/plugins/index.ts +218 -0
  225. package/src/plugins/notifications/__tests__/notifications-manager.test.ts +637 -0
  226. package/src/plugins/notifications/index.ts +91 -0
  227. package/src/plugins/notifications/notifications-manager.ts +773 -0
  228. package/src/plugins/notifications/notifications-plugin.ts +398 -0
  229. package/src/plugins/notifications/types.ts +207 -0
  230. package/src/plugins/parental/__tests__/parental-plugin.test.ts +465 -0
  231. package/src/plugins/parental/adapters/index.ts +8 -0
  232. package/src/plugins/parental/adapters/kids-adapter.ts +206 -0
  233. package/src/plugins/parental/index.ts +55 -0
  234. package/src/plugins/parental/parental-plugin.ts +759 -0
  235. package/src/plugins/parental/stores/index.ts +7 -0
  236. package/src/plugins/parental/stores/postgres-store.ts +304 -0
  237. package/src/plugins/parental/types.ts +180 -0
  238. package/src/plugins/profiles/__tests__/profiles-plugin.test.ts +321 -0
  239. package/src/plugins/profiles/index.ts +49 -0
  240. package/src/plugins/profiles/profiles-plugin.ts +546 -0
  241. package/src/plugins/profiles/stores/index.ts +9 -0
  242. package/src/plugins/profiles/stores/postgres-store.ts +439 -0
  243. package/src/plugins/profiles/types.ts +338 -0
  244. package/src/plugins/subscriptions/__tests__/subscriptions-plugin.test.ts +404 -0
  245. package/src/plugins/subscriptions/index.ts +51 -0
  246. package/src/plugins/subscriptions/stores/index.ts +9 -0
  247. package/src/plugins/subscriptions/stores/postgres-store.ts +482 -0
  248. package/src/plugins/subscriptions/subscriptions-plugin.ts +530 -0
  249. package/src/plugins/subscriptions/types.ts +355 -0
  250. package/src/plugins/usage/__tests__/usage-plugin.test.ts +288 -0
  251. package/src/plugins/usage/index.ts +39 -0
  252. package/src/plugins/usage/stores/index.ts +9 -0
  253. package/src/plugins/usage/stores/postgres-store.ts +213 -0
  254. package/src/plugins/usage/types.ts +222 -0
  255. package/src/plugins/usage/usage-plugin.ts +484 -0
  256. package/src/plugins/users/__tests__/postgres-store.test.ts +326 -0
  257. package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
  258. package/src/plugins/users/index.ts +6 -0
  259. package/src/plugins/users/stores/postgres-store.ts +104 -0
  260. package/src/plugins/users/types.ts +82 -6
  261. package/src/plugins/users/users-plugin.ts +37 -0
  262. package/ui/src/App.tsx +5 -1
  263. package/ui/src/api/controlPanelApi.ts +103 -6
  264. package/ui/src/components/StatCard.tsx +58 -0
  265. package/ui/src/dashboard/builtInWidgets.tsx +3 -1
  266. package/ui/src/dashboard/widgets/NotificationsStatsWidget.tsx +167 -0
  267. package/ui/src/dashboard/widgets/index.ts +1 -0
  268. package/ui/src/pages/NotificationsPage.tsx +417 -0
  269. package/ui/src/utils/formatters.ts +33 -0
  270. package/dist-ui/assets/index-D7DoZ9rL.js +0 -478
  271. package/dist-ui/assets/index-D7DoZ9rL.js.map +0 -1
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Notifications Plugin
3
+ *
4
+ * Provides realtime SSE-based notifications for @qwickapps/server applications.
5
+ * Uses PostgreSQL LISTEN/NOTIFY for event distribution.
6
+ *
7
+ * ## Features
8
+ * - PostgreSQL LISTEN/NOTIFY integration
9
+ * - SSE endpoint for client connections
10
+ * - Device/user-based event filtering
11
+ * - Automatic reconnection with exponential backoff
12
+ * - Heartbeat system for connection health
13
+ * - Statistics and monitoring
14
+ *
15
+ * ## Usage
16
+ *
17
+ * ```typescript
18
+ * import { createGateway, createNotificationsPlugin } from '@qwickapps/server';
19
+ *
20
+ * const gateway = createGateway({
21
+ * productName: 'MyApp',
22
+ * controlPanel: {
23
+ * plugins: [
24
+ * { plugin: createPostgresPlugin({ url: DATABASE_URL }) },
25
+ * { plugin: createNotificationsPlugin({
26
+ * channels: ['events', 'messages'],
27
+ * heartbeat: { interval: 60000 },
28
+ * }) },
29
+ * ],
30
+ * },
31
+ * });
32
+ * ```
33
+ *
34
+ * ## SSE Endpoint
35
+ *
36
+ * ```
37
+ * GET /notifications/stream?device_id=xxx&user_id=yyy
38
+ *
39
+ * Events:
40
+ * - connected: Initial connection confirmation
41
+ * - heartbeat: Periodic health check
42
+ * - {channel}: Events from subscribed channels
43
+ * ```
44
+ *
45
+ * ## Security Note
46
+ *
47
+ * This plugin does NOT handle authentication. Authentication should be
48
+ * configured at the gateway level using the `guard` option or an auth
49
+ * middleware. The plugin trusts that requests reaching it are authorized.
50
+ *
51
+ * Example with gateway guard:
52
+ * ```typescript
53
+ * const gateway = createGateway({
54
+ * controlPanel: {
55
+ * guard: { type: 'basic', username: 'admin', password: 'secret' },
56
+ * plugins: [createNotificationsPlugin({ ... })],
57
+ * },
58
+ * });
59
+ * ```
60
+ *
61
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
62
+ */
63
+ import type { Plugin } from '../../core/plugin-registry.js';
64
+ import type { NotificationsPluginConfig } from './types.js';
65
+ /**
66
+ * Create the Notifications plugin
67
+ *
68
+ * @param config Plugin configuration
69
+ * @returns Plugin instance
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * import { createNotificationsPlugin } from '@qwickapps/server';
74
+ *
75
+ * const plugin = createNotificationsPlugin({
76
+ * channels: ['bot_events', 'chat_messages'],
77
+ * heartbeat: { interval: 60000 },
78
+ * api: { prefix: '/notifications' },
79
+ * });
80
+ * ```
81
+ */
82
+ export declare function createNotificationsPlugin(config: NotificationsPluginConfig): Plugin;
83
+ //# sourceMappingURL=notifications-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications-plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/notifications/notifications-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAgC,MAAM,+BAA+B,CAAC;AAC1F,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AA+B5D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,CA2RnF"}
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Notifications Plugin
3
+ *
4
+ * Provides realtime SSE-based notifications for @qwickapps/server applications.
5
+ * Uses PostgreSQL LISTEN/NOTIFY for event distribution.
6
+ *
7
+ * ## Features
8
+ * - PostgreSQL LISTEN/NOTIFY integration
9
+ * - SSE endpoint for client connections
10
+ * - Device/user-based event filtering
11
+ * - Automatic reconnection with exponential backoff
12
+ * - Heartbeat system for connection health
13
+ * - Statistics and monitoring
14
+ *
15
+ * ## Usage
16
+ *
17
+ * ```typescript
18
+ * import { createGateway, createNotificationsPlugin } from '@qwickapps/server';
19
+ *
20
+ * const gateway = createGateway({
21
+ * productName: 'MyApp',
22
+ * controlPanel: {
23
+ * plugins: [
24
+ * { plugin: createPostgresPlugin({ url: DATABASE_URL }) },
25
+ * { plugin: createNotificationsPlugin({
26
+ * channels: ['events', 'messages'],
27
+ * heartbeat: { interval: 60000 },
28
+ * }) },
29
+ * ],
30
+ * },
31
+ * });
32
+ * ```
33
+ *
34
+ * ## SSE Endpoint
35
+ *
36
+ * ```
37
+ * GET /notifications/stream?device_id=xxx&user_id=yyy
38
+ *
39
+ * Events:
40
+ * - connected: Initial connection confirmation
41
+ * - heartbeat: Periodic health check
42
+ * - {channel}: Events from subscribed channels
43
+ * ```
44
+ *
45
+ * ## Security Note
46
+ *
47
+ * This plugin does NOT handle authentication. Authentication should be
48
+ * configured at the gateway level using the `guard` option or an auth
49
+ * middleware. The plugin trusts that requests reaching it are authorized.
50
+ *
51
+ * Example with gateway guard:
52
+ * ```typescript
53
+ * const gateway = createGateway({
54
+ * controlPanel: {
55
+ * guard: { type: 'basic', username: 'admin', password: 'secret' },
56
+ * plugins: [createNotificationsPlugin({ ... })],
57
+ * },
58
+ * });
59
+ * ```
60
+ *
61
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
62
+ */
63
+ import { randomUUID } from 'crypto';
64
+ import { NotificationsManager, setNotificationsManager, } from './notifications-manager.js';
65
+ import { getPostgres, hasPostgres } from '../postgres-plugin.js';
66
+ import { getAuthenticatedUser } from '../auth/auth-plugin.js';
67
+ // Validation constants
68
+ const MAX_ID_LENGTH = 128;
69
+ const ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
70
+ /**
71
+ * Validate a device_id or user_id parameter
72
+ */
73
+ function validateId(id, paramName) {
74
+ if (!id) {
75
+ return { valid: true }; // undefined is allowed
76
+ }
77
+ if (id.length > MAX_ID_LENGTH) {
78
+ return { valid: false, error: `${paramName} exceeds maximum length of ${MAX_ID_LENGTH} characters` };
79
+ }
80
+ if (!ID_PATTERN.test(id)) {
81
+ return { valid: false, error: `${paramName} contains invalid characters (allowed: alphanumeric, underscore, hyphen)` };
82
+ }
83
+ return { valid: true };
84
+ }
85
+ /**
86
+ * Create the Notifications plugin
87
+ *
88
+ * @param config Plugin configuration
89
+ * @returns Plugin instance
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * import { createNotificationsPlugin } from '@qwickapps/server';
94
+ *
95
+ * const plugin = createNotificationsPlugin({
96
+ * channels: ['bot_events', 'chat_messages'],
97
+ * heartbeat: { interval: 60000 },
98
+ * api: { prefix: '/notifications' },
99
+ * });
100
+ * ```
101
+ */
102
+ export function createNotificationsPlugin(config) {
103
+ const apiPrefix = config.api?.prefix || '/notifications';
104
+ const streamEnabled = config.api?.stream !== false;
105
+ const statsEnabled = config.api?.stats !== false;
106
+ let manager = null;
107
+ return {
108
+ id: 'notifications',
109
+ name: 'Notifications',
110
+ version: '1.0.0',
111
+ async onStart(_pluginConfig, registry) {
112
+ const logger = registry.getLogger('notifications');
113
+ // Check for postgres plugin dependency
114
+ if (!hasPostgres()) {
115
+ throw new Error('Notifications plugin requires postgres plugin. ' +
116
+ 'Please add createPostgresPlugin() before createNotificationsPlugin().');
117
+ }
118
+ // Get database connection string from postgres plugin
119
+ const postgres = getPostgres();
120
+ const pool = postgres.getPool();
121
+ // Extract connection string from pool config
122
+ // Note: pg.Pool stores config internally, we need to reconstruct it
123
+ const poolConfig = pool.options;
124
+ let connectionString = poolConfig?.connectionString;
125
+ if (!connectionString) {
126
+ // If no connection string, try to get from environment
127
+ connectionString = process.env.DATABASE_URL;
128
+ }
129
+ if (!connectionString) {
130
+ throw new Error('Could not determine PostgreSQL connection string. ' +
131
+ 'Ensure DATABASE_URL is set or postgres plugin was configured with a URL.');
132
+ }
133
+ logger.debug('Initializing notifications manager', {
134
+ channels: config.channels,
135
+ heartbeatInterval: config.heartbeat?.interval,
136
+ });
137
+ // Create and initialize manager
138
+ manager = new NotificationsManager(connectionString, config.channels, config, logger);
139
+ await manager.initialize();
140
+ setNotificationsManager(manager);
141
+ // Register health check
142
+ registry.registerHealthCheck({
143
+ name: 'notifications',
144
+ type: 'custom',
145
+ check: async () => {
146
+ const health = manager?.getConnectionHealth();
147
+ return {
148
+ healthy: health?.isHealthy ?? false,
149
+ details: {
150
+ connected: health?.isConnected,
151
+ channels: config.channels,
152
+ activeClients: manager?.getStats().currentConnections ?? 0,
153
+ lastEventAt: health?.lastEventAt?.toISOString(),
154
+ isReconnecting: health?.isReconnecting,
155
+ },
156
+ };
157
+ },
158
+ });
159
+ // Register SSE stream endpoint
160
+ if (streamEnabled) {
161
+ registry.addRoute({
162
+ method: 'get',
163
+ path: `${apiPrefix}/stream`,
164
+ pluginId: 'notifications',
165
+ handler: (req, res) => {
166
+ const deviceId = req.query.device_id;
167
+ const userId = req.query.user_id;
168
+ // Require at least one filter
169
+ if (!deviceId && !userId) {
170
+ res.status(400).json({
171
+ error: 'Bad Request',
172
+ message: 'At least one of device_id or user_id query parameter is required',
173
+ });
174
+ return;
175
+ }
176
+ // Validate device_id
177
+ const deviceValidation = validateId(deviceId, 'device_id');
178
+ if (!deviceValidation.valid) {
179
+ res.status(400).json({
180
+ error: 'Bad Request',
181
+ message: deviceValidation.error,
182
+ });
183
+ return;
184
+ }
185
+ // Validate user_id
186
+ const userValidation = validateId(userId, 'user_id');
187
+ if (!userValidation.valid) {
188
+ res.status(400).json({
189
+ error: 'Bad Request',
190
+ message: userValidation.error,
191
+ });
192
+ return;
193
+ }
194
+ // Set SSE headers
195
+ res.setHeader('Content-Type', 'text/event-stream');
196
+ res.setHeader('Cache-Control', 'no-cache');
197
+ res.setHeader('Connection', 'keep-alive');
198
+ res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
199
+ // Disable compression for SSE
200
+ res.setHeader('Content-Encoding', 'identity');
201
+ // Flush headers
202
+ res.flushHeaders();
203
+ // Generate client ID and register
204
+ const clientId = randomUUID();
205
+ const registered = manager?.registerClient(clientId, deviceId, userId, res);
206
+ // Handle capacity limit
207
+ if (!registered) {
208
+ res.write('event: error\n');
209
+ res.write('data: {"error": "Server at capacity, please try again later"}\n\n');
210
+ res.end();
211
+ }
212
+ },
213
+ });
214
+ logger.debug(`SSE endpoint registered: GET ${apiPrefix}/stream`);
215
+ }
216
+ // Register stats endpoint
217
+ if (statsEnabled) {
218
+ registry.addRoute({
219
+ method: 'get',
220
+ path: `${apiPrefix}/stats`,
221
+ pluginId: 'notifications',
222
+ handler: (_req, res) => {
223
+ if (!manager) {
224
+ res.status(503).json({ error: 'Service unavailable' });
225
+ return;
226
+ }
227
+ const stats = manager.getStats();
228
+ res.json({
229
+ ...stats,
230
+ channels: config.channels,
231
+ lastEventAt: stats.connectionHealth.lastEventAt?.toISOString(),
232
+ lastReconnectionAt: stats.lastReconnectionAt?.toISOString(),
233
+ });
234
+ },
235
+ });
236
+ logger.debug(`Stats endpoint registered: GET ${apiPrefix}/stats`);
237
+ // Register clients endpoint
238
+ registry.addRoute({
239
+ method: 'get',
240
+ path: `${apiPrefix}/clients`,
241
+ pluginId: 'notifications',
242
+ handler: (_req, res) => {
243
+ if (!manager) {
244
+ res.status(503).json({ error: 'Service unavailable' });
245
+ return;
246
+ }
247
+ const clients = manager.getClients();
248
+ res.json({
249
+ clients,
250
+ total: clients.length,
251
+ });
252
+ },
253
+ });
254
+ logger.debug(`Clients endpoint registered: GET ${apiPrefix}/clients`);
255
+ // Register disconnect client endpoint
256
+ registry.addRoute({
257
+ method: 'delete',
258
+ path: `${apiPrefix}/clients/:id`,
259
+ pluginId: 'notifications',
260
+ handler: (req, res) => {
261
+ if (!manager) {
262
+ res.status(503).json({ error: 'Service unavailable' });
263
+ return;
264
+ }
265
+ const clientId = req.params.id;
266
+ if (!clientId) {
267
+ res.status(400).json({ error: 'Bad Request', message: 'Client ID is required' });
268
+ return;
269
+ }
270
+ // Get admin user info for audit logging
271
+ const adminUser = getAuthenticatedUser(req);
272
+ const disconnectedBy = {
273
+ userId: adminUser?.id,
274
+ email: adminUser?.email,
275
+ ip: req.ip || req.socket.remoteAddress,
276
+ };
277
+ const disconnected = manager.disconnectClient(clientId, disconnectedBy);
278
+ if (!disconnected) {
279
+ res.status(404).json({ error: 'Not Found', message: 'Client not found' });
280
+ return;
281
+ }
282
+ res.json({ success: true });
283
+ },
284
+ });
285
+ logger.debug(`Disconnect endpoint registered: DELETE ${apiPrefix}/clients/:id`);
286
+ // Register force reconnect endpoint
287
+ registry.addRoute({
288
+ method: 'post',
289
+ path: `${apiPrefix}/reconnect`,
290
+ pluginId: 'notifications',
291
+ handler: async (_req, res) => {
292
+ if (!manager) {
293
+ res.status(503).json({ error: 'Service unavailable' });
294
+ return;
295
+ }
296
+ try {
297
+ await manager.forceReconnect();
298
+ res.json({ success: true, message: 'Reconnection initiated' });
299
+ }
300
+ catch (error) {
301
+ const errorMsg = error instanceof Error ? error.message : 'Unknown error';
302
+ res.status(500).json({ error: 'Reconnection failed', message: errorMsg });
303
+ }
304
+ },
305
+ });
306
+ logger.debug(`Reconnect endpoint registered: POST ${apiPrefix}/reconnect`);
307
+ // Register UI menu item for management page
308
+ registry.addMenuItem({
309
+ pluginId: 'notifications',
310
+ id: 'notifications:sidebar',
311
+ label: 'Notifications',
312
+ icon: 'notifications',
313
+ route: '/notifications',
314
+ order: 45, // After Rate Limits (40)
315
+ });
316
+ // Register dashboard widget
317
+ registry.addWidget({
318
+ id: 'notifications-stats',
319
+ title: 'Notifications',
320
+ component: 'NotificationsStatsWidget',
321
+ priority: 25, // After ServiceHealthWidget (10) and AuthStatusWidget (20)
322
+ showByDefault: true,
323
+ pluginId: 'notifications',
324
+ });
325
+ }
326
+ logger.info('Notifications plugin started');
327
+ },
328
+ async onStop() {
329
+ if (manager) {
330
+ await manager.shutdown();
331
+ setNotificationsManager(null);
332
+ manager = null;
333
+ }
334
+ },
335
+ };
336
+ }
337
+ //# sourceMappingURL=notifications-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications-plugin.js","sourceRoot":"","sources":["../../../src/plugins/notifications/notifications-plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Notifications Plugin Types
3
+ *
4
+ * Type definitions for the realtime notifications plugin.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+ import type { Response } from 'express';
9
+ /**
10
+ * Configuration for the notifications plugin
11
+ */
12
+ export interface NotificationsPluginConfig {
13
+ /**
14
+ * PostgreSQL channels to LISTEN on.
15
+ * Each channel maps to a NOTIFY channel in PostgreSQL.
16
+ * Example: ['bot_events', 'chat_messages']
17
+ */
18
+ channels: string[];
19
+ /**
20
+ * Heartbeat configuration
21
+ */
22
+ heartbeat?: {
23
+ /** Interval in milliseconds (default: 60000 = 1 minute) */
24
+ interval?: number;
25
+ /** Include server status in heartbeat (default: true) */
26
+ includeStatus?: boolean;
27
+ };
28
+ /**
29
+ * Reconnection configuration for PostgreSQL LISTEN connection
30
+ */
31
+ reconnect?: {
32
+ /** Maximum reconnection attempts before giving up (default: 10) */
33
+ maxAttempts?: number;
34
+ /** Base delay in milliseconds for exponential backoff (default: 1000) */
35
+ baseDelay?: number;
36
+ /** Maximum delay in milliseconds (default: 60000) */
37
+ maxDelay?: number;
38
+ };
39
+ /**
40
+ * API endpoint configuration
41
+ */
42
+ api?: {
43
+ /** Route prefix (default: '/notifications') */
44
+ prefix?: string;
45
+ /** Enable /stream SSE endpoint (default: true) */
46
+ stream?: boolean;
47
+ /** Enable /stats endpoint (default: true) */
48
+ stats?: boolean;
49
+ };
50
+ /**
51
+ * Enable debug logging (default: false)
52
+ */
53
+ debug?: boolean;
54
+ }
55
+ /**
56
+ * Represents a connected SSE client
57
+ */
58
+ export interface SSEClient {
59
+ /** Unique client identifier */
60
+ id: string;
61
+ /** Device ID for filtering (optional) */
62
+ deviceId?: string;
63
+ /** User ID for filtering (optional) */
64
+ userId?: string;
65
+ /** Express response object for SSE */
66
+ response: Response;
67
+ /** Connection timestamp */
68
+ connectedAt: Date;
69
+ }
70
+ /**
71
+ * PostgreSQL NOTIFY payload structure
72
+ */
73
+ export interface NotifyPayload {
74
+ /** Event type (e.g., 'command', 'status') */
75
+ eventType?: string;
76
+ /** Target device ID (for routing) */
77
+ deviceId?: string;
78
+ /** Target user ID (for routing) */
79
+ userId?: string;
80
+ /** Event payload data */
81
+ payload?: unknown;
82
+ /** Additional fields */
83
+ [key: string]: unknown;
84
+ }
85
+ /**
86
+ * SSE event to send to clients
87
+ */
88
+ export interface SSEEvent {
89
+ /** Event type name */
90
+ eventType: string;
91
+ /** Event data */
92
+ payload: unknown;
93
+ }
94
+ /**
95
+ * Connection health information
96
+ */
97
+ export interface ConnectionHealth {
98
+ /** Whether LISTEN connection is established */
99
+ isConnected: boolean;
100
+ /** Whether connection is considered healthy */
101
+ isHealthy: boolean;
102
+ /** Last time an event was received */
103
+ lastEventAt: Date | null;
104
+ /** Time since last event in milliseconds */
105
+ timeSinceLastEvent: number;
106
+ /** Number of channels being listened to */
107
+ channelCount: number;
108
+ /** Whether reconnection is in progress */
109
+ isReconnecting: boolean;
110
+ /** Current reconnection attempt number */
111
+ reconnectAttempts: number;
112
+ }
113
+ /**
114
+ * Notifications manager statistics
115
+ */
116
+ export interface NotificationsStats {
117
+ /** Total connections since startup */
118
+ totalConnections: number;
119
+ /** Currently active connections */
120
+ currentConnections: number;
121
+ /** Total events received from PostgreSQL */
122
+ eventsProcessed: number;
123
+ /** Total events routed to clients */
124
+ eventsRouted: number;
125
+ /** Events that failed JSON parsing */
126
+ eventsParseFailed: number;
127
+ /** Events dropped because no clients matched */
128
+ eventsDroppedNoClients: number;
129
+ /** Total reconnection attempts */
130
+ reconnectionAttempts: number;
131
+ /** Last reconnection timestamp */
132
+ lastReconnectionAt?: Date;
133
+ /** Client breakdown by type */
134
+ clientsByType: {
135
+ /** Clients with device_id filter */
136
+ device: number;
137
+ /** Clients with user_id filter */
138
+ user: number;
139
+ };
140
+ /** Connection health status */
141
+ connectionHealth: ConnectionHealth;
142
+ }
143
+ /**
144
+ * NotificationsManager interface for external access
145
+ */
146
+ export interface NotificationsManagerInterface {
147
+ /** Register a new SSE client */
148
+ registerClient(id: string, deviceId: string | undefined, userId: string | undefined, response: Response): void;
149
+ /** Broadcast event to a specific device */
150
+ broadcastToDevice(deviceId: string, eventType: string, payload: unknown): number;
151
+ /** Broadcast event to all devices for a user */
152
+ broadcastToUser(userId: string, eventType: string, payload: unknown): number;
153
+ /** Broadcast to all clients (channel-level broadcast) */
154
+ broadcastToAll(eventType: string, payload: unknown): number;
155
+ /** Get current statistics */
156
+ getStats(): NotificationsStats;
157
+ /** Get connection health */
158
+ getConnectionHealth(): ConnectionHealth;
159
+ /** Force reconnection to PostgreSQL */
160
+ forceReconnect(): Promise<void>;
161
+ /** Shutdown the manager */
162
+ shutdown(): Promise<void>;
163
+ }
164
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/plugins/notifications/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Notifications Plugin Types
3
+ *
4
+ * Type definitions for the realtime notifications plugin.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/plugins/notifications/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Parental Plugin Tests
3
+ *
4
+ * Unit tests for the parental controls plugin including:
5
+ * - Plugin lifecycle
6
+ * - Guardian settings management
7
+ * - Profile restrictions
8
+ * - Activity logging
9
+ * - PIN verification
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=parental-plugin.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parental-plugin.test.d.ts","sourceRoot":"","sources":["../../../../src/plugins/parental/__tests__/parental-plugin.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}