@qwickapps/server 1.3.1 → 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 (395) hide show
  1. package/README.md +157 -0
  2. package/dist/core/control-panel.d.ts.map +1 -1
  3. package/dist/core/control-panel.js +114 -0
  4. package/dist/core/control-panel.js.map +1 -1
  5. package/dist/core/types.d.ts +19 -0
  6. package/dist/core/types.d.ts.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +15 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/plugins/auth/adapter-wrapper.d.ts +47 -0
  12. package/dist/plugins/auth/adapter-wrapper.d.ts.map +1 -0
  13. package/dist/plugins/auth/adapter-wrapper.js +166 -0
  14. package/dist/plugins/auth/adapter-wrapper.js.map +1 -0
  15. package/dist/plugins/auth/adapter-wrapper.test.d.ts +7 -0
  16. package/dist/plugins/auth/adapter-wrapper.test.d.ts.map +1 -0
  17. package/dist/plugins/auth/adapter-wrapper.test.js +303 -0
  18. package/dist/plugins/auth/adapter-wrapper.test.js.map +1 -0
  19. package/dist/plugins/auth/config-store.d.ts +11 -0
  20. package/dist/plugins/auth/config-store.d.ts.map +1 -0
  21. package/dist/plugins/auth/config-store.js +232 -0
  22. package/dist/plugins/auth/config-store.js.map +1 -0
  23. package/dist/plugins/auth/config-store.test.d.ts +7 -0
  24. package/dist/plugins/auth/config-store.test.d.ts.map +1 -0
  25. package/dist/plugins/auth/config-store.test.js +299 -0
  26. package/dist/plugins/auth/config-store.test.js.map +1 -0
  27. package/dist/plugins/auth/env-config.d.ts +51 -1
  28. package/dist/plugins/auth/env-config.d.ts.map +1 -1
  29. package/dist/plugins/auth/env-config.js +640 -7
  30. package/dist/plugins/auth/env-config.js.map +1 -1
  31. package/dist/plugins/auth/index.d.ts +6 -2
  32. package/dist/plugins/auth/index.d.ts.map +1 -1
  33. package/dist/plugins/auth/index.js +5 -1
  34. package/dist/plugins/auth/index.js.map +1 -1
  35. package/dist/plugins/auth/types.d.ts +106 -0
  36. package/dist/plugins/auth/types.d.ts.map +1 -1
  37. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -1
  38. package/dist/plugins/bans/bans-plugin.js +12 -3
  39. package/dist/plugins/bans/bans-plugin.js.map +1 -1
  40. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts +11 -0
  41. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts.map +1 -0
  42. package/dist/plugins/devices/__tests__/devices-plugin.test.js +410 -0
  43. package/dist/plugins/devices/__tests__/devices-plugin.test.js.map +1 -0
  44. package/dist/plugins/devices/__tests__/token-utils.test.d.ts +7 -0
  45. package/dist/plugins/devices/__tests__/token-utils.test.d.ts.map +1 -0
  46. package/dist/plugins/devices/__tests__/token-utils.test.js +197 -0
  47. package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -0
  48. package/dist/plugins/devices/adapters/compute-adapter.d.ts +36 -0
  49. package/dist/plugins/devices/adapters/compute-adapter.d.ts.map +1 -0
  50. package/dist/plugins/devices/adapters/compute-adapter.js +100 -0
  51. package/dist/plugins/devices/adapters/compute-adapter.js.map +1 -0
  52. package/dist/plugins/devices/adapters/index.d.ts +12 -0
  53. package/dist/plugins/devices/adapters/index.d.ts.map +1 -0
  54. package/dist/plugins/devices/adapters/index.js +10 -0
  55. package/dist/plugins/devices/adapters/index.js.map +1 -0
  56. package/dist/plugins/devices/adapters/mobile-adapter.d.ts +41 -0
  57. package/dist/plugins/devices/adapters/mobile-adapter.d.ts.map +1 -0
  58. package/dist/plugins/devices/adapters/mobile-adapter.js +131 -0
  59. package/dist/plugins/devices/adapters/mobile-adapter.js.map +1 -0
  60. package/dist/plugins/devices/devices-plugin.d.ts +70 -0
  61. package/dist/plugins/devices/devices-plugin.d.ts.map +1 -0
  62. package/dist/plugins/devices/devices-plugin.js +453 -0
  63. package/dist/plugins/devices/devices-plugin.js.map +1 -0
  64. package/dist/plugins/devices/index.d.ts +18 -0
  65. package/dist/plugins/devices/index.d.ts.map +1 -0
  66. package/dist/plugins/devices/index.js +18 -0
  67. package/dist/plugins/devices/index.js.map +1 -0
  68. package/dist/plugins/devices/stores/index.d.ts +9 -0
  69. package/dist/plugins/devices/stores/index.d.ts.map +1 -0
  70. package/dist/plugins/devices/stores/index.js +9 -0
  71. package/dist/plugins/devices/stores/index.js.map +1 -0
  72. package/dist/plugins/devices/stores/postgres-store.d.ts +26 -0
  73. package/dist/plugins/devices/stores/postgres-store.d.ts.map +1 -0
  74. package/dist/plugins/devices/stores/postgres-store.js +199 -0
  75. package/dist/plugins/devices/stores/postgres-store.js.map +1 -0
  76. package/dist/plugins/devices/token-utils.d.ts +100 -0
  77. package/dist/plugins/devices/token-utils.d.ts.map +1 -0
  78. package/dist/plugins/devices/token-utils.js +162 -0
  79. package/dist/plugins/devices/token-utils.js.map +1 -0
  80. package/dist/plugins/devices/types.d.ts +307 -0
  81. package/dist/plugins/devices/types.d.ts.map +1 -0
  82. package/dist/plugins/devices/types.js +10 -0
  83. package/dist/plugins/devices/types.js.map +1 -0
  84. package/dist/plugins/index.d.ts +18 -4
  85. package/dist/plugins/index.d.ts.map +1 -1
  86. package/dist/plugins/index.js +16 -2
  87. package/dist/plugins/index.js.map +1 -1
  88. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts +5 -0
  89. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts.map +1 -0
  90. package/dist/plugins/notifications/__tests__/notifications-manager.test.js +470 -0
  91. package/dist/plugins/notifications/__tests__/notifications-manager.test.js.map +1 -0
  92. package/dist/plugins/notifications/index.d.ts +71 -0
  93. package/dist/plugins/notifications/index.d.ts.map +1 -0
  94. package/dist/plugins/notifications/index.js +72 -0
  95. package/dist/plugins/notifications/index.js.map +1 -0
  96. package/dist/plugins/notifications/notifications-manager.d.ts +182 -0
  97. package/dist/plugins/notifications/notifications-manager.d.ts.map +1 -0
  98. package/dist/plugins/notifications/notifications-manager.js +610 -0
  99. package/dist/plugins/notifications/notifications-manager.js.map +1 -0
  100. package/dist/plugins/notifications/notifications-plugin.d.ts +83 -0
  101. package/dist/plugins/notifications/notifications-plugin.d.ts.map +1 -0
  102. package/dist/plugins/notifications/notifications-plugin.js +337 -0
  103. package/dist/plugins/notifications/notifications-plugin.js.map +1 -0
  104. package/dist/plugins/notifications/types.d.ts +164 -0
  105. package/dist/plugins/notifications/types.d.ts.map +1 -0
  106. package/dist/plugins/notifications/types.js +9 -0
  107. package/dist/plugins/notifications/types.js.map +1 -0
  108. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts +12 -0
  109. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts.map +1 -0
  110. package/dist/plugins/parental/__tests__/parental-plugin.test.js +349 -0
  111. package/dist/plugins/parental/__tests__/parental-plugin.test.js.map +1 -0
  112. package/dist/plugins/parental/adapters/index.d.ts +8 -0
  113. package/dist/plugins/parental/adapters/index.d.ts.map +1 -0
  114. package/dist/plugins/parental/adapters/index.js +7 -0
  115. package/dist/plugins/parental/adapters/index.js.map +1 -0
  116. package/dist/plugins/parental/adapters/kids-adapter.d.ts +24 -0
  117. package/dist/plugins/parental/adapters/kids-adapter.d.ts.map +1 -0
  118. package/dist/plugins/parental/adapters/kids-adapter.js +174 -0
  119. package/dist/plugins/parental/adapters/kids-adapter.js.map +1 -0
  120. package/dist/plugins/parental/index.d.ts +14 -0
  121. package/dist/plugins/parental/index.d.ts.map +1 -0
  122. package/dist/plugins/parental/index.js +15 -0
  123. package/dist/plugins/parental/index.js.map +1 -0
  124. package/dist/plugins/parental/parental-plugin.d.ts +88 -0
  125. package/dist/plugins/parental/parental-plugin.d.ts.map +1 -0
  126. package/dist/plugins/parental/parental-plugin.js +666 -0
  127. package/dist/plugins/parental/parental-plugin.js.map +1 -0
  128. package/dist/plugins/parental/stores/index.d.ts +7 -0
  129. package/dist/plugins/parental/stores/index.d.ts.map +1 -0
  130. package/dist/plugins/parental/stores/index.js +7 -0
  131. package/dist/plugins/parental/stores/index.js.map +1 -0
  132. package/dist/plugins/parental/stores/postgres-store.d.ts +10 -0
  133. package/dist/plugins/parental/stores/postgres-store.d.ts.map +1 -0
  134. package/dist/plugins/parental/stores/postgres-store.js +209 -0
  135. package/dist/plugins/parental/stores/postgres-store.js.map +1 -0
  136. package/dist/plugins/parental/types.d.ts +154 -0
  137. package/dist/plugins/parental/types.d.ts.map +1 -0
  138. package/dist/plugins/parental/types.js +10 -0
  139. package/dist/plugins/parental/types.js.map +1 -0
  140. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts +11 -0
  141. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts.map +1 -0
  142. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js +243 -0
  143. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js.map +1 -0
  144. package/dist/plugins/profiles/index.d.ts +12 -0
  145. package/dist/plugins/profiles/index.d.ts.map +1 -0
  146. package/dist/plugins/profiles/index.js +13 -0
  147. package/dist/plugins/profiles/index.js.map +1 -0
  148. package/dist/plugins/profiles/profiles-plugin.d.ts +71 -0
  149. package/dist/plugins/profiles/profiles-plugin.d.ts.map +1 -0
  150. package/dist/plugins/profiles/profiles-plugin.js +481 -0
  151. package/dist/plugins/profiles/profiles-plugin.js.map +1 -0
  152. package/dist/plugins/profiles/stores/index.d.ts +9 -0
  153. package/dist/plugins/profiles/stores/index.d.ts.map +1 -0
  154. package/dist/plugins/profiles/stores/index.js +9 -0
  155. package/dist/plugins/profiles/stores/index.js.map +1 -0
  156. package/dist/plugins/profiles/stores/postgres-store.d.ts +18 -0
  157. package/dist/plugins/profiles/stores/postgres-store.d.ts.map +1 -0
  158. package/dist/plugins/profiles/stores/postgres-store.js +310 -0
  159. package/dist/plugins/profiles/stores/postgres-store.js.map +1 -0
  160. package/dist/plugins/profiles/types.d.ts +289 -0
  161. package/dist/plugins/profiles/types.d.ts.map +1 -0
  162. package/dist/plugins/profiles/types.js +10 -0
  163. package/dist/plugins/profiles/types.js.map +1 -0
  164. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts +7 -0
  165. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts.map +1 -0
  166. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js +220 -0
  167. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js.map +1 -0
  168. package/dist/plugins/rate-limit/cleanup.d.ts +40 -0
  169. package/dist/plugins/rate-limit/cleanup.d.ts.map +1 -0
  170. package/dist/plugins/rate-limit/cleanup.js +72 -0
  171. package/dist/plugins/rate-limit/cleanup.js.map +1 -0
  172. package/dist/plugins/rate-limit/env-config.d.ts +91 -0
  173. package/dist/plugins/rate-limit/env-config.d.ts.map +1 -0
  174. package/dist/plugins/rate-limit/env-config.js +318 -0
  175. package/dist/plugins/rate-limit/env-config.js.map +1 -0
  176. package/dist/plugins/rate-limit/index.d.ts +76 -0
  177. package/dist/plugins/rate-limit/index.d.ts.map +1 -0
  178. package/dist/plugins/rate-limit/index.js +79 -0
  179. package/dist/plugins/rate-limit/index.js.map +1 -0
  180. package/dist/plugins/rate-limit/middleware.d.ts +40 -0
  181. package/dist/plugins/rate-limit/middleware.d.ts.map +1 -0
  182. package/dist/plugins/rate-limit/middleware.js +169 -0
  183. package/dist/plugins/rate-limit/middleware.js.map +1 -0
  184. package/dist/plugins/rate-limit/rate-limit-plugin.d.ts +44 -0
  185. package/dist/plugins/rate-limit/rate-limit-plugin.d.ts.map +1 -0
  186. package/dist/plugins/rate-limit/rate-limit-plugin.js +354 -0
  187. package/dist/plugins/rate-limit/rate-limit-plugin.js.map +1 -0
  188. package/dist/plugins/rate-limit/rate-limit-service.d.ts +110 -0
  189. package/dist/plugins/rate-limit/rate-limit-service.d.ts.map +1 -0
  190. package/dist/plugins/rate-limit/rate-limit-service.js +172 -0
  191. package/dist/plugins/rate-limit/rate-limit-service.js.map +1 -0
  192. package/dist/plugins/rate-limit/stores/cache-store.d.ts +33 -0
  193. package/dist/plugins/rate-limit/stores/cache-store.d.ts.map +1 -0
  194. package/dist/plugins/rate-limit/stores/cache-store.js +225 -0
  195. package/dist/plugins/rate-limit/stores/cache-store.js.map +1 -0
  196. package/dist/plugins/rate-limit/stores/index.d.ts +8 -0
  197. package/dist/plugins/rate-limit/stores/index.d.ts.map +1 -0
  198. package/dist/plugins/rate-limit/stores/index.js +8 -0
  199. package/dist/plugins/rate-limit/stores/index.js.map +1 -0
  200. package/dist/plugins/rate-limit/stores/postgres-store.d.ts +34 -0
  201. package/dist/plugins/rate-limit/stores/postgres-store.d.ts.map +1 -0
  202. package/dist/plugins/rate-limit/stores/postgres-store.js +320 -0
  203. package/dist/plugins/rate-limit/stores/postgres-store.js.map +1 -0
  204. package/dist/plugins/rate-limit/strategies/fixed-window.d.ts +21 -0
  205. package/dist/plugins/rate-limit/strategies/fixed-window.d.ts.map +1 -0
  206. package/dist/plugins/rate-limit/strategies/fixed-window.js +97 -0
  207. package/dist/plugins/rate-limit/strategies/fixed-window.js.map +1 -0
  208. package/dist/plugins/rate-limit/strategies/index.d.ts +14 -0
  209. package/dist/plugins/rate-limit/strategies/index.d.ts.map +1 -0
  210. package/dist/plugins/rate-limit/strategies/index.js +27 -0
  211. package/dist/plugins/rate-limit/strategies/index.js.map +1 -0
  212. package/dist/plugins/rate-limit/strategies/sliding-window.d.ts +22 -0
  213. package/dist/plugins/rate-limit/strategies/sliding-window.d.ts.map +1 -0
  214. package/dist/plugins/rate-limit/strategies/sliding-window.js +122 -0
  215. package/dist/plugins/rate-limit/strategies/sliding-window.js.map +1 -0
  216. package/dist/plugins/rate-limit/strategies/token-bucket.d.ts +28 -0
  217. package/dist/plugins/rate-limit/strategies/token-bucket.d.ts.map +1 -0
  218. package/dist/plugins/rate-limit/strategies/token-bucket.js +121 -0
  219. package/dist/plugins/rate-limit/strategies/token-bucket.js.map +1 -0
  220. package/dist/plugins/rate-limit/types.d.ts +265 -0
  221. package/dist/plugins/rate-limit/types.d.ts.map +1 -0
  222. package/dist/plugins/rate-limit/types.js +9 -0
  223. package/dist/plugins/rate-limit/types.js.map +1 -0
  224. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts +11 -0
  225. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts.map +1 -0
  226. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js +305 -0
  227. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js.map +1 -0
  228. package/dist/plugins/subscriptions/index.d.ts +12 -0
  229. package/dist/plugins/subscriptions/index.d.ts.map +1 -0
  230. package/dist/plugins/subscriptions/index.js +13 -0
  231. package/dist/plugins/subscriptions/index.js.map +1 -0
  232. package/dist/plugins/subscriptions/stores/index.d.ts +9 -0
  233. package/dist/plugins/subscriptions/stores/index.d.ts.map +1 -0
  234. package/dist/plugins/subscriptions/stores/index.js +9 -0
  235. package/dist/plugins/subscriptions/stores/index.js.map +1 -0
  236. package/dist/plugins/subscriptions/stores/postgres-store.d.ts +14 -0
  237. package/dist/plugins/subscriptions/stores/postgres-store.d.ts.map +1 -0
  238. package/dist/plugins/subscriptions/stores/postgres-store.js +359 -0
  239. package/dist/plugins/subscriptions/stores/postgres-store.js.map +1 -0
  240. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts +82 -0
  241. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -0
  242. package/dist/plugins/subscriptions/subscriptions-plugin.js +449 -0
  243. package/dist/plugins/subscriptions/subscriptions-plugin.js.map +1 -0
  244. package/dist/plugins/subscriptions/types.d.ts +308 -0
  245. package/dist/plugins/subscriptions/types.d.ts.map +1 -0
  246. package/dist/plugins/subscriptions/types.js +10 -0
  247. package/dist/plugins/subscriptions/types.js.map +1 -0
  248. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts +11 -0
  249. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts.map +1 -0
  250. package/dist/plugins/usage/__tests__/usage-plugin.test.js +218 -0
  251. package/dist/plugins/usage/__tests__/usage-plugin.test.js.map +1 -0
  252. package/dist/plugins/usage/index.d.ts +12 -0
  253. package/dist/plugins/usage/index.d.ts.map +1 -0
  254. package/dist/plugins/usage/index.js +13 -0
  255. package/dist/plugins/usage/index.js.map +1 -0
  256. package/dist/plugins/usage/stores/index.d.ts +9 -0
  257. package/dist/plugins/usage/stores/index.d.ts.map +1 -0
  258. package/dist/plugins/usage/stores/index.js +9 -0
  259. package/dist/plugins/usage/stores/index.js.map +1 -0
  260. package/dist/plugins/usage/stores/postgres-store.d.ts +14 -0
  261. package/dist/plugins/usage/stores/postgres-store.d.ts.map +1 -0
  262. package/dist/plugins/usage/stores/postgres-store.js +146 -0
  263. package/dist/plugins/usage/stores/postgres-store.js.map +1 -0
  264. package/dist/plugins/usage/types.d.ts +195 -0
  265. package/dist/plugins/usage/types.d.ts.map +1 -0
  266. package/dist/plugins/usage/types.js +10 -0
  267. package/dist/plugins/usage/types.js.map +1 -0
  268. package/dist/plugins/usage/usage-plugin.d.ts +51 -0
  269. package/dist/plugins/usage/usage-plugin.d.ts.map +1 -0
  270. package/dist/plugins/usage/usage-plugin.js +412 -0
  271. package/dist/plugins/usage/usage-plugin.js.map +1 -0
  272. package/dist/plugins/users/__tests__/postgres-store.test.d.ts +10 -0
  273. package/dist/plugins/users/__tests__/postgres-store.test.d.ts.map +1 -0
  274. package/dist/plugins/users/__tests__/postgres-store.test.js +229 -0
  275. package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -0
  276. package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
  277. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
  278. package/dist/plugins/users/index.d.ts +2 -2
  279. package/dist/plugins/users/index.d.ts.map +1 -1
  280. package/dist/plugins/users/index.js +1 -1
  281. package/dist/plugins/users/index.js.map +1 -1
  282. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
  283. package/dist/plugins/users/stores/postgres-store.js +76 -0
  284. package/dist/plugins/users/stores/postgres-store.js.map +1 -1
  285. package/dist/plugins/users/types.d.ts +74 -6
  286. package/dist/plugins/users/types.d.ts.map +1 -1
  287. package/dist/plugins/users/users-plugin.d.ts +15 -1
  288. package/dist/plugins/users/users-plugin.d.ts.map +1 -1
  289. package/dist/plugins/users/users-plugin.js +29 -0
  290. package/dist/plugins/users/users-plugin.js.map +1 -1
  291. package/dist-ui/assets/index-CynOqPkb.js +469 -0
  292. package/dist-ui/assets/{index-BY8OxNgO.js.map → index-CynOqPkb.js.map} +1 -1
  293. package/dist-ui/index.html +1 -1
  294. package/dist-ui-lib/api/controlPanelApi.d.ts +187 -0
  295. package/dist-ui-lib/components/StatCard.d.ts +16 -0
  296. package/dist-ui-lib/dashboard/widgets/AuthStatusWidget.d.ts +9 -0
  297. package/dist-ui-lib/dashboard/widgets/IntegrationStatusWidget.d.ts +9 -0
  298. package/dist-ui-lib/dashboard/widgets/NotificationsStatsWidget.d.ts +12 -0
  299. package/dist-ui-lib/dashboard/widgets/index.d.ts +3 -0
  300. package/dist-ui-lib/index.js +3579 -2379
  301. package/dist-ui-lib/index.js.map +1 -1
  302. package/dist-ui-lib/pages/IntegrationsPage.d.ts +1 -0
  303. package/dist-ui-lib/pages/NotificationsPage.d.ts +9 -0
  304. package/dist-ui-lib/pages/RateLimitPage.d.ts +1 -0
  305. package/dist-ui-lib/utils/formatters.d.ts +19 -0
  306. package/package.json +1 -1
  307. package/src/core/control-panel.ts +128 -0
  308. package/src/core/types.ts +17 -0
  309. package/src/index.ts +216 -0
  310. package/src/plugins/auth/adapter-wrapper.test.ts +395 -0
  311. package/src/plugins/auth/adapter-wrapper.ts +205 -0
  312. package/src/plugins/auth/config-store.test.ts +417 -0
  313. package/src/plugins/auth/config-store.ts +305 -0
  314. package/src/plugins/auth/env-config.ts +714 -7
  315. package/src/plugins/auth/index.ts +22 -1
  316. package/src/plugins/auth/types.ts +138 -0
  317. package/src/plugins/bans/bans-plugin.ts +15 -3
  318. package/src/plugins/devices/__tests__/devices-plugin.test.ts +551 -0
  319. package/src/plugins/devices/__tests__/token-utils.test.ts +264 -0
  320. package/src/plugins/devices/adapters/compute-adapter.ts +139 -0
  321. package/src/plugins/devices/adapters/index.ts +13 -0
  322. package/src/plugins/devices/adapters/mobile-adapter.ts +179 -0
  323. package/src/plugins/devices/devices-plugin.ts +538 -0
  324. package/src/plugins/devices/index.ts +69 -0
  325. package/src/plugins/devices/stores/index.ts +9 -0
  326. package/src/plugins/devices/stores/postgres-store.ts +304 -0
  327. package/src/plugins/devices/token-utils.ts +213 -0
  328. package/src/plugins/devices/types.ts +351 -0
  329. package/src/plugins/index.ts +267 -0
  330. package/src/plugins/notifications/__tests__/notifications-manager.test.ts +637 -0
  331. package/src/plugins/notifications/index.ts +91 -0
  332. package/src/plugins/notifications/notifications-manager.ts +773 -0
  333. package/src/plugins/notifications/notifications-plugin.ts +398 -0
  334. package/src/plugins/notifications/types.ts +207 -0
  335. package/src/plugins/parental/__tests__/parental-plugin.test.ts +465 -0
  336. package/src/plugins/parental/adapters/index.ts +8 -0
  337. package/src/plugins/parental/adapters/kids-adapter.ts +206 -0
  338. package/src/plugins/parental/index.ts +55 -0
  339. package/src/plugins/parental/parental-plugin.ts +759 -0
  340. package/src/plugins/parental/stores/index.ts +7 -0
  341. package/src/plugins/parental/stores/postgres-store.ts +304 -0
  342. package/src/plugins/parental/types.ts +180 -0
  343. package/src/plugins/profiles/__tests__/profiles-plugin.test.ts +321 -0
  344. package/src/plugins/profiles/index.ts +49 -0
  345. package/src/plugins/profiles/profiles-plugin.ts +546 -0
  346. package/src/plugins/profiles/stores/index.ts +9 -0
  347. package/src/plugins/profiles/stores/postgres-store.ts +439 -0
  348. package/src/plugins/profiles/types.ts +338 -0
  349. package/src/plugins/rate-limit/__tests__/rate-limit-plugin.test.ts +259 -0
  350. package/src/plugins/rate-limit/cleanup.ts +117 -0
  351. package/src/plugins/rate-limit/env-config.ts +400 -0
  352. package/src/plugins/rate-limit/index.ts +128 -0
  353. package/src/plugins/rate-limit/middleware.ts +212 -0
  354. package/src/plugins/rate-limit/rate-limit-plugin.ts +400 -0
  355. package/src/plugins/rate-limit/rate-limit-service.ts +228 -0
  356. package/src/plugins/rate-limit/stores/cache-store.ts +261 -0
  357. package/src/plugins/rate-limit/stores/index.ts +8 -0
  358. package/src/plugins/rate-limit/stores/postgres-store.ts +402 -0
  359. package/src/plugins/rate-limit/strategies/fixed-window.ts +116 -0
  360. package/src/plugins/rate-limit/strategies/index.ts +30 -0
  361. package/src/plugins/rate-limit/strategies/sliding-window.ts +157 -0
  362. package/src/plugins/rate-limit/strategies/token-bucket.ts +154 -0
  363. package/src/plugins/rate-limit/types.ts +338 -0
  364. package/src/plugins/subscriptions/__tests__/subscriptions-plugin.test.ts +404 -0
  365. package/src/plugins/subscriptions/index.ts +51 -0
  366. package/src/plugins/subscriptions/stores/index.ts +9 -0
  367. package/src/plugins/subscriptions/stores/postgres-store.ts +482 -0
  368. package/src/plugins/subscriptions/subscriptions-plugin.ts +530 -0
  369. package/src/plugins/subscriptions/types.ts +355 -0
  370. package/src/plugins/usage/__tests__/usage-plugin.test.ts +288 -0
  371. package/src/plugins/usage/index.ts +39 -0
  372. package/src/plugins/usage/stores/index.ts +9 -0
  373. package/src/plugins/usage/stores/postgres-store.ts +213 -0
  374. package/src/plugins/usage/types.ts +222 -0
  375. package/src/plugins/usage/usage-plugin.ts +484 -0
  376. package/src/plugins/users/__tests__/postgres-store.test.ts +326 -0
  377. package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
  378. package/src/plugins/users/index.ts +6 -0
  379. package/src/plugins/users/stores/postgres-store.ts +104 -0
  380. package/src/plugins/users/types.ts +82 -6
  381. package/src/plugins/users/users-plugin.ts +37 -0
  382. package/ui/src/App.tsx +36 -14
  383. package/ui/src/api/controlPanelApi.ts +329 -6
  384. package/ui/src/components/StatCard.tsx +58 -0
  385. package/ui/src/dashboard/builtInWidgets.tsx +7 -1
  386. package/ui/src/dashboard/widgets/AuthStatusWidget.tsx +143 -0
  387. package/ui/src/dashboard/widgets/IntegrationStatusWidget.tsx +135 -0
  388. package/ui/src/dashboard/widgets/NotificationsStatsWidget.tsx +167 -0
  389. package/ui/src/dashboard/widgets/index.ts +3 -0
  390. package/ui/src/pages/AuthPage.tsx +986 -142
  391. package/ui/src/pages/IntegrationsPage.tsx +288 -0
  392. package/ui/src/pages/NotificationsPage.tsx +417 -0
  393. package/ui/src/pages/RateLimitPage.tsx +292 -0
  394. package/ui/src/utils/formatters.ts +33 -0
  395. package/dist-ui/assets/index-BY8OxNgO.js +0 -465
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Profiles Plugin Types
3
+ *
4
+ * Type definitions for generic multi-profile management with age support.
5
+ * Supports child profiles for QwickBot, player profiles for gaming, etc.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ /**
11
+ * Content filter levels for age-appropriate content
12
+ */
13
+ export type ContentFilterLevel = 'strict' | 'moderate' | 'minimal' | 'none';
14
+
15
+ /**
16
+ * Age groups for categorization
17
+ */
18
+ export type AgeGroup = 'child' | 'teen' | 'adult';
19
+
20
+ /**
21
+ * Profile record in the database
22
+ */
23
+ export interface Profile {
24
+ /** Primary key - UUID */
25
+ id: string;
26
+ /** Organization ID for multi-tenant isolation */
27
+ org_id?: string;
28
+ /** Owner/parent user ID */
29
+ user_id: string;
30
+ /** Profile name */
31
+ name: string;
32
+ /** Avatar identifier or URL */
33
+ avatar?: string;
34
+
35
+ // Age-based features
36
+ /** Birth date for precise age calculation */
37
+ birth_date?: Date;
38
+ /** Static age (if birth_date not provided) */
39
+ age?: number;
40
+ /** Computed age group (child, teen, adult) */
41
+ age_group?: AgeGroup;
42
+
43
+ // Content/access control
44
+ /** Content filter level */
45
+ content_filter_level: ContentFilterLevel;
46
+
47
+ // Time restrictions
48
+ /** Daily time limit in minutes (null = no limit) */
49
+ daily_time_limit_minutes?: number;
50
+ /** Allowed hours start (null = no restriction) */
51
+ allowed_hours_start?: string;
52
+ /** Allowed hours end (null = no restriction) */
53
+ allowed_hours_end?: string;
54
+
55
+ // Status
56
+ /** Whether profile is active */
57
+ is_active: boolean;
58
+ /** Whether this is the default profile for the user */
59
+ is_default: boolean;
60
+
61
+ // Extensibility
62
+ /** App-specific metadata */
63
+ metadata: Record<string, unknown>;
64
+
65
+ /** When the profile was created */
66
+ created_at: Date;
67
+ /** When the profile was last updated */
68
+ updated_at: Date;
69
+ /** Soft delete timestamp */
70
+ deleted_at?: Date;
71
+ }
72
+
73
+ /**
74
+ * Profile creation payload
75
+ */
76
+ export interface CreateProfileInput {
77
+ /** Organization ID */
78
+ org_id?: string;
79
+ /** Owner user ID */
80
+ user_id: string;
81
+ /** Profile name */
82
+ name: string;
83
+ /** Avatar identifier or URL */
84
+ avatar?: string;
85
+ /** Birth date for age calculation */
86
+ birth_date?: Date;
87
+ /** Static age (if birth_date not provided) */
88
+ age?: number;
89
+ /** Content filter level (default: 'moderate') */
90
+ content_filter_level?: ContentFilterLevel;
91
+ /** Daily time limit in minutes */
92
+ daily_time_limit_minutes?: number;
93
+ /** Allowed hours start (HH:MM format) */
94
+ allowed_hours_start?: string;
95
+ /** Allowed hours end (HH:MM format) */
96
+ allowed_hours_end?: string;
97
+ /** Whether this is the default profile */
98
+ is_default?: boolean;
99
+ /** App-specific metadata */
100
+ metadata?: Record<string, unknown>;
101
+ }
102
+
103
+ /**
104
+ * Profile update payload
105
+ */
106
+ export interface UpdateProfileInput {
107
+ /** Profile name */
108
+ name?: string;
109
+ /** Avatar identifier or URL */
110
+ avatar?: string;
111
+ /** Birth date for age calculation */
112
+ birth_date?: Date | null;
113
+ /** Static age */
114
+ age?: number | null;
115
+ /** Content filter level */
116
+ content_filter_level?: ContentFilterLevel;
117
+ /** Daily time limit in minutes (null to remove) */
118
+ daily_time_limit_minutes?: number | null;
119
+ /** Allowed hours start (null to remove) */
120
+ allowed_hours_start?: string | null;
121
+ /** Allowed hours end (null to remove) */
122
+ allowed_hours_end?: string | null;
123
+ /** Whether profile is active */
124
+ is_active?: boolean;
125
+ /** Whether this is the default profile */
126
+ is_default?: boolean;
127
+ /** App-specific metadata */
128
+ metadata?: Record<string, unknown>;
129
+ }
130
+
131
+ /**
132
+ * Profile search parameters
133
+ */
134
+ export interface ProfileSearchParams {
135
+ /** Organization ID filter */
136
+ org_id?: string;
137
+ /** User ID filter */
138
+ user_id?: string;
139
+ /** Age group filter */
140
+ age_group?: AgeGroup;
141
+ /** Active status filter */
142
+ is_active?: boolean;
143
+ /** Search query (searches name) */
144
+ query?: string;
145
+ /** Page number (1-indexed) */
146
+ page?: number;
147
+ /** Items per page */
148
+ limit?: number;
149
+ /** Sort field */
150
+ sortBy?: 'name' | 'created_at' | 'age';
151
+ /** Sort direction */
152
+ sortOrder?: 'asc' | 'desc';
153
+ }
154
+
155
+ /**
156
+ * Paginated profile list response
157
+ */
158
+ export interface ProfileListResponse {
159
+ profiles: Profile[];
160
+ total: number;
161
+ page: number;
162
+ limit: number;
163
+ totalPages: number;
164
+ }
165
+
166
+ /**
167
+ * Time restriction check result
168
+ */
169
+ export interface TimeRestrictionResult {
170
+ /** Whether access is allowed right now */
171
+ allowed: boolean;
172
+ /** Reason if not allowed */
173
+ reason?: string;
174
+ /** Minutes remaining today (if has limit) */
175
+ minutes_remaining?: number;
176
+ /** When restriction ends (if currently restricted) */
177
+ available_at?: Date;
178
+ }
179
+
180
+ // ═══════════════════════════════════════════════════════════════════════════
181
+ // Store Interface
182
+ // ═══════════════════════════════════════════════════════════════════════════
183
+
184
+ /**
185
+ * Profile store interface - all storage backends must implement this
186
+ */
187
+ export interface ProfileStore {
188
+ /** Store name (e.g., 'postgres', 'memory') */
189
+ name: string;
190
+
191
+ /**
192
+ * Initialize the store (create tables, etc.)
193
+ */
194
+ initialize(): Promise<void>;
195
+
196
+ /**
197
+ * Get a profile by ID
198
+ */
199
+ getById(id: string): Promise<Profile | null>;
200
+
201
+ /**
202
+ * Create a new profile
203
+ */
204
+ create(input: CreateProfileInput): Promise<Profile>;
205
+
206
+ /**
207
+ * Update an existing profile
208
+ */
209
+ update(id: string, input: UpdateProfileInput): Promise<Profile | null>;
210
+
211
+ /**
212
+ * Soft delete a profile
213
+ */
214
+ delete(id: string): Promise<boolean>;
215
+
216
+ /**
217
+ * Search/list profiles
218
+ */
219
+ search(params: ProfileSearchParams): Promise<ProfileListResponse>;
220
+
221
+ /**
222
+ * List profiles for a user
223
+ */
224
+ listByUser(userId: string): Promise<Profile[]>;
225
+
226
+ /**
227
+ * Get the default profile for a user
228
+ */
229
+ getDefaultProfile(userId: string): Promise<Profile | null>;
230
+
231
+ /**
232
+ * Count profiles for a user
233
+ */
234
+ getProfileCount(userId: string): Promise<number>;
235
+
236
+ /**
237
+ * Get profiles by age group
238
+ */
239
+ getByAgeGroup(userId: string, ageGroup: AgeGroup): Promise<Profile[]>;
240
+
241
+ /**
242
+ * Set a profile as default (unsets others)
243
+ */
244
+ setDefaultProfile(profileId: string, userId: string): Promise<boolean>;
245
+
246
+ /**
247
+ * Shutdown the store
248
+ */
249
+ shutdown(): Promise<void>;
250
+ }
251
+
252
+ // ═══════════════════════════════════════════════════════════════════════════
253
+ // Configuration Types
254
+ // ═══════════════════════════════════════════════════════════════════════════
255
+
256
+ /**
257
+ * PostgreSQL profile store configuration
258
+ */
259
+ export interface PostgresProfileStoreConfig {
260
+ /** PostgreSQL pool instance or a function that returns one (for lazy initialization) */
261
+ pool: unknown | (() => unknown);
262
+ /** Profiles table name (default: 'profiles') */
263
+ tableName?: string;
264
+ /** Schema name (default: 'public') */
265
+ schema?: string;
266
+ /** Auto-create tables on init (default: true) */
267
+ autoCreateTables?: boolean;
268
+ }
269
+
270
+ /**
271
+ * Age thresholds for categorization
272
+ */
273
+ export interface AgeThresholds {
274
+ /** Maximum age for 'child' (default: 12) */
275
+ child: number;
276
+ /** Maximum age for 'teen' (default: 17) */
277
+ teen: number;
278
+ }
279
+
280
+ /**
281
+ * API configuration
282
+ */
283
+ export interface ProfilesApiConfig {
284
+ /** API route prefix (default: '/profiles') */
285
+ prefix?: string;
286
+ /** Enable CRUD endpoints */
287
+ crud?: boolean;
288
+ }
289
+
290
+ /**
291
+ * Profiles plugin configuration
292
+ */
293
+ export interface ProfilesPluginConfig {
294
+ /** Profile storage backend */
295
+ store: ProfileStore;
296
+ /** Maximum profiles per user (default: 10) */
297
+ maxProfilesPerUser?: number;
298
+ /** Default content filter level (default: 'moderate') */
299
+ defaultFilterLevel?: ContentFilterLevel;
300
+ /** Age thresholds for categorization */
301
+ ageThresholds?: AgeThresholds;
302
+ /** API configuration */
303
+ api?: ProfilesApiConfig;
304
+ /** Enable debug logging */
305
+ debug?: boolean;
306
+ }
307
+
308
+ // ═══════════════════════════════════════════════════════════════════════════
309
+ // App-specific Metadata Types (examples)
310
+ // ═══════════════════════════════════════════════════════════════════════════
311
+
312
+ /**
313
+ * QwickBot profile metadata (stored in metadata field)
314
+ */
315
+ export interface QwickBotProfileMetadata {
316
+ /** Voice ID for text-to-speech */
317
+ voice_id?: string;
318
+ /** Avatar style identifier */
319
+ avatar_style?: string;
320
+ /** Personality prompt for AI */
321
+ personality_prompt?: string;
322
+ /** User's interests */
323
+ interests?: string[];
324
+ }
325
+
326
+ /**
327
+ * Gaming profile metadata (stored in metadata field)
328
+ */
329
+ export interface GamingProfileMetadata {
330
+ /** Gamer tag */
331
+ gamer_tag?: string;
332
+ /** Avatar URL */
333
+ avatar_url?: string;
334
+ /** Allowed game ratings (E, E10+, T, M) */
335
+ allowed_ratings?: string[];
336
+ /** Preferred genres */
337
+ preferred_genres?: string[];
338
+ }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Rate Limit Plugin Tests
3
+ *
4
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
5
+ */
6
+
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
+ import type { RateLimitStore, RateLimitCache, CachedLimit, StoredLimit, IncrementOptions } from '../types.js';
9
+ import { RateLimitService } from '../rate-limit-service.js';
10
+ import { createSlidingWindowStrategy } from '../strategies/sliding-window.js';
11
+ import { createFixedWindowStrategy } from '../strategies/fixed-window.js';
12
+ import { createTokenBucketStrategy } from '../strategies/token-bucket.js';
13
+
14
+ // Mock store implementation
15
+ function createMockStore(): RateLimitStore {
16
+ const records = new Map<string, StoredLimit>();
17
+
18
+ return {
19
+ name: 'mock',
20
+
21
+ async initialize(): Promise<void> {
22
+ // No-op
23
+ },
24
+
25
+ async get(key: string): Promise<StoredLimit | null> {
26
+ return records.get(key) || null;
27
+ },
28
+
29
+ async increment(key: string, options: IncrementOptions): Promise<StoredLimit> {
30
+ const now = new Date();
31
+ const windowMs = options.windowMs;
32
+ const windowStart = new Date(now.getTime() - (now.getTime() % windowMs));
33
+ const windowEnd = new Date(windowStart.getTime() + windowMs);
34
+
35
+ const existing = records.get(key);
36
+ if (existing && existing.windowStart.getTime() === windowStart.getTime()) {
37
+ existing.count += options.amount || 1;
38
+ existing.updatedAt = now;
39
+ return existing;
40
+ }
41
+
42
+ const newRecord: StoredLimit = {
43
+ id: `mock-${Date.now()}`,
44
+ key,
45
+ count: options.amount || 1,
46
+ maxRequests: options.maxRequests,
47
+ windowMs: options.windowMs,
48
+ windowStart,
49
+ windowEnd,
50
+ strategy: options.strategy,
51
+ userId: options.userId,
52
+ tenantId: options.tenantId,
53
+ ipAddress: options.ipAddress,
54
+ createdAt: now,
55
+ updatedAt: now,
56
+ };
57
+ records.set(key, newRecord);
58
+ return newRecord;
59
+ },
60
+
61
+ async clear(key: string): Promise<boolean> {
62
+ return records.delete(key);
63
+ },
64
+
65
+ async cleanup(): Promise<number> {
66
+ const now = Date.now();
67
+ let deleted = 0;
68
+ for (const [key, record] of records) {
69
+ if (record.windowEnd.getTime() < now) {
70
+ records.delete(key);
71
+ deleted++;
72
+ }
73
+ }
74
+ return deleted;
75
+ },
76
+
77
+ async shutdown(): Promise<void> {
78
+ records.clear();
79
+ },
80
+ };
81
+ }
82
+
83
+ // Mock cache implementation
84
+ function createMockCache(): RateLimitCache {
85
+ const cache = new Map<string, { value: CachedLimit; expiresAt: number }>();
86
+
87
+ return {
88
+ name: 'mock',
89
+
90
+ async get(key: string): Promise<CachedLimit | null> {
91
+ const entry = cache.get(key);
92
+ if (!entry) return null;
93
+ if (entry.expiresAt <= Date.now()) {
94
+ cache.delete(key);
95
+ return null;
96
+ }
97
+ return entry.value;
98
+ },
99
+
100
+ async set(key: string, value: CachedLimit, ttlMs: number): Promise<void> {
101
+ cache.set(key, { value, expiresAt: Date.now() + ttlMs });
102
+ },
103
+
104
+ async increment(key: string, amount = 1): Promise<number | null> {
105
+ const entry = cache.get(key);
106
+ if (!entry || entry.expiresAt <= Date.now()) return null;
107
+ entry.value.count += amount;
108
+ return entry.value.count;
109
+ },
110
+
111
+ async delete(key: string): Promise<boolean> {
112
+ return cache.delete(key);
113
+ },
114
+
115
+ isAvailable(): boolean {
116
+ return true;
117
+ },
118
+
119
+ async shutdown(): Promise<void> {
120
+ cache.clear();
121
+ },
122
+ };
123
+ }
124
+
125
+ describe('RateLimitService', () => {
126
+ let store: RateLimitStore;
127
+ let cache: RateLimitCache;
128
+ let service: RateLimitService;
129
+
130
+ beforeEach(() => {
131
+ store = createMockStore();
132
+ cache = createMockCache();
133
+ service = new RateLimitService({
134
+ store,
135
+ cache,
136
+ defaults: {
137
+ windowMs: 60000,
138
+ maxRequests: 100,
139
+ strategy: 'sliding-window',
140
+ },
141
+ });
142
+ });
143
+
144
+ afterEach(async () => {
145
+ await store.shutdown();
146
+ await cache.shutdown();
147
+ });
148
+
149
+ describe('checkLimit', () => {
150
+ it('should return not limited for first request', async () => {
151
+ const status = await service.checkLimit('test:key', { increment: false });
152
+
153
+ expect(status.limited).toBe(false);
154
+ expect(status.current).toBe(0);
155
+ expect(status.limit).toBe(100);
156
+ expect(status.remaining).toBe(100);
157
+ });
158
+
159
+ it('should use provided options over defaults', async () => {
160
+ const status = await service.checkLimit('test:key', {
161
+ maxRequests: 50,
162
+ windowMs: 30000,
163
+ increment: false,
164
+ });
165
+
166
+ expect(status.limit).toBe(50);
167
+ });
168
+ });
169
+
170
+ describe('incrementLimit', () => {
171
+ it('should increment the counter', async () => {
172
+ const status1 = await service.incrementLimit('test:key');
173
+ expect(status1.current).toBe(1);
174
+ expect(status1.remaining).toBe(99);
175
+
176
+ const status2 = await service.incrementLimit('test:key');
177
+ expect(status2.current).toBe(2);
178
+ expect(status2.remaining).toBe(98);
179
+ });
180
+
181
+ it('should return limited when max reached', async () => {
182
+ // Set low limit for testing
183
+ for (let i = 0; i < 5; i++) {
184
+ await service.incrementLimit('test:key', { maxRequests: 5 });
185
+ }
186
+
187
+ const status = await service.incrementLimit('test:key', { maxRequests: 5 });
188
+ expect(status.limited).toBe(true);
189
+ expect(status.remaining).toBe(0);
190
+ });
191
+ });
192
+
193
+ describe('isLimited', () => {
194
+ it('should return false when not limited', async () => {
195
+ const limited = await service.isLimited('test:key');
196
+ expect(limited).toBe(false);
197
+ });
198
+
199
+ it('should return true when limited', async () => {
200
+ // Exhaust the limit
201
+ for (let i = 0; i < 5; i++) {
202
+ await service.incrementLimit('test:key', { maxRequests: 5 });
203
+ }
204
+
205
+ const limited = await service.isLimited('test:key', { maxRequests: 5 });
206
+ expect(limited).toBe(true);
207
+ });
208
+ });
209
+
210
+ describe('clearLimit', () => {
211
+ it('should clear the limit', async () => {
212
+ // Create some limits
213
+ await service.incrementLimit('test:key');
214
+ await service.incrementLimit('test:key');
215
+
216
+ // Verify exists
217
+ const beforeStatus = await service.checkLimit('test:key', { increment: false });
218
+ expect(beforeStatus.current).toBeGreaterThan(0);
219
+
220
+ // Clear
221
+ await service.clearLimit('test:key');
222
+
223
+ // Verify cleared
224
+ const afterStatus = await service.checkLimit('test:key', { increment: false });
225
+ expect(afterStatus.current).toBe(0);
226
+ });
227
+ });
228
+ });
229
+
230
+ describe('Strategies', () => {
231
+ describe('Sliding Window', () => {
232
+ it('should create strategy with correct name', () => {
233
+ const strategy = createSlidingWindowStrategy();
234
+ expect(strategy.name).toBe('sliding-window');
235
+ });
236
+ });
237
+
238
+ describe('Fixed Window', () => {
239
+ it('should create strategy with correct name', () => {
240
+ const strategy = createFixedWindowStrategy();
241
+ expect(strategy.name).toBe('fixed-window');
242
+ });
243
+ });
244
+
245
+ describe('Token Bucket', () => {
246
+ it('should create strategy with correct name', () => {
247
+ const strategy = createTokenBucketStrategy();
248
+ expect(strategy.name).toBe('token-bucket');
249
+ });
250
+ });
251
+ });
252
+
253
+ describe('Types', () => {
254
+ it('should export all required types', async () => {
255
+ // This test verifies that the types module compiles correctly
256
+ const types = await import('../types.js');
257
+ expect(types).toBeDefined();
258
+ });
259
+ });
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Rate Limit Cleanup Job
3
+ *
4
+ * Periodically removes expired rate limit records from the database.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ import type { RateLimitStore } from './types.js';
10
+
11
+ /**
12
+ * Cleanup job configuration
13
+ */
14
+ export interface CleanupJobConfig {
15
+ /** Store to clean up */
16
+ store: RateLimitStore;
17
+
18
+ /** Cleanup interval in milliseconds (default: 300000 = 5 min) */
19
+ intervalMs?: number;
20
+
21
+ /** Enable debug logging */
22
+ debug?: boolean;
23
+ }
24
+
25
+ /**
26
+ * Cleanup job state
27
+ */
28
+ export interface CleanupJob {
29
+ /** Start the cleanup job */
30
+ start(): void;
31
+
32
+ /** Stop the cleanup job */
33
+ stop(): void;
34
+
35
+ /** Run cleanup immediately */
36
+ runNow(): Promise<number>;
37
+
38
+ /** Check if job is running */
39
+ isRunning(): boolean;
40
+ }
41
+
42
+ /**
43
+ * Create a cleanup job
44
+ *
45
+ * @param config Cleanup configuration
46
+ * @returns CleanupJob instance
47
+ */
48
+ export function createCleanupJob(config: CleanupJobConfig): CleanupJob {
49
+ const {
50
+ store,
51
+ intervalMs = 300000, // 5 minutes default
52
+ debug = false,
53
+ } = config;
54
+
55
+ let intervalId: ReturnType<typeof setInterval> | null = null;
56
+ let isRunningCleanup = false;
57
+
58
+ function log(message: string, data?: Record<string, unknown>) {
59
+ if (debug) {
60
+ console.log(`[RateLimitCleanup] ${message}`, data || '');
61
+ }
62
+ }
63
+
64
+ async function runCleanup(): Promise<number> {
65
+ if (isRunningCleanup) {
66
+ log('Cleanup already in progress, skipping');
67
+ return 0;
68
+ }
69
+
70
+ isRunningCleanup = true;
71
+ try {
72
+ log('Starting cleanup');
73
+ const startTime = Date.now();
74
+ const deletedCount = await store.cleanup();
75
+ const duration = Date.now() - startTime;
76
+
77
+ log('Cleanup complete', { deletedCount, durationMs: duration });
78
+ return deletedCount;
79
+ } catch (error) {
80
+ console.error('[RateLimitCleanup] Error during cleanup:', error);
81
+ return 0;
82
+ } finally {
83
+ isRunningCleanup = false;
84
+ }
85
+ }
86
+
87
+ return {
88
+ start() {
89
+ if (intervalId) {
90
+ log('Cleanup job already running');
91
+ return;
92
+ }
93
+
94
+ log('Starting cleanup job', { intervalMs });
95
+ intervalId = setInterval(runCleanup, intervalMs);
96
+
97
+ // Run initial cleanup after a short delay
98
+ setTimeout(runCleanup, 10000);
99
+ },
100
+
101
+ stop() {
102
+ if (intervalId) {
103
+ log('Stopping cleanup job');
104
+ clearInterval(intervalId);
105
+ intervalId = null;
106
+ }
107
+ },
108
+
109
+ async runNow(): Promise<number> {
110
+ return runCleanup();
111
+ },
112
+
113
+ isRunning(): boolean {
114
+ return intervalId !== null;
115
+ },
116
+ };
117
+ }