@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
@@ -11,6 +11,7 @@ import type { Request, Response } from 'express';
11
11
  import type { Plugin, PluginConfig, PluginRegistry } from '../../core/plugin-registry.js';
12
12
  import type {
13
13
  AuthPluginConfig,
14
+ AuthAdapter,
14
15
  Auth0AdapterConfig,
15
16
  SupabaseAdapterConfig,
16
17
  SupertokensAdapterConfig,
@@ -18,12 +19,19 @@ import type {
18
19
  AuthEnvPluginOptions,
19
20
  AuthConfigStatus,
20
21
  AuthPluginState,
22
+ AuthAdapterType,
23
+ RuntimeAuthConfig,
24
+ UpdateAuthConfigRequest,
25
+ TestProviderRequest,
26
+ TestProviderResponse,
27
+ AuthConfigStore,
21
28
  } from './types.js';
22
29
  import { createAuthPlugin } from './auth-plugin.js';
23
30
  import { auth0Adapter } from './adapters/auth0-adapter.js';
24
31
  import { supabaseAdapter } from './adapters/supabase-adapter.js';
25
32
  import { supertokensAdapter } from './adapters/supertokens-adapter.js';
26
33
  import { basicAdapter } from './adapters/basic-adapter.js';
34
+ import { createAdapterWrapper, type AdapterWrapper } from './adapter-wrapper.js';
27
35
 
28
36
  // ═══════════════════════════════════════════════════════════════════════════
29
37
  // Module State
@@ -34,6 +42,15 @@ let currentStatus: AuthConfigStatus = {
34
42
  adapter: null,
35
43
  };
36
44
 
45
+ // Runtime configuration store (PostgreSQL-backed)
46
+ let configStore: AuthConfigStore | null = null;
47
+
48
+ // Adapter wrapper for hot-reload support
49
+ let adapterWrapper: AdapterWrapper | null = null;
50
+
51
+ // Unsubscribe function for config change listener
52
+ let configUnsubscribe: (() => void) | null = null;
53
+
37
54
  // ═══════════════════════════════════════════════════════════════════════════
38
55
  // Environment Variable Helpers
39
56
  // ═══════════════════════════════════════════════════════════════════════════
@@ -476,32 +493,324 @@ function getMaskedConfig(adapter: AdapterName): Record<string, string> {
476
493
  * Register config API routes
477
494
  */
478
495
  function registerConfigRoutes(registry: PluginRegistry): void {
479
- // GET /api/auth/config/status - Get current auth status
496
+ // GET /auth/config/status - Get current auth status
480
497
  registry.addRoute({
481
498
  method: 'get',
482
- path: '/api/auth/config/status',
499
+ path: '/auth/config/status',
483
500
  pluginId: 'auth',
484
501
  handler: (_req: Request, res: Response) => {
485
502
  res.json(getAuthStatus());
486
503
  },
487
504
  });
488
505
 
489
- // GET /api/auth/config - Get current configuration (masked)
506
+ // GET /auth/config - Get current configuration (masked)
490
507
  registry.addRoute({
491
508
  method: 'get',
492
- path: '/api/auth/config',
509
+ path: '/auth/config',
493
510
  pluginId: 'auth',
494
- handler: (_req: Request, res: Response) => {
511
+ handler: async (_req: Request, res: Response) => {
495
512
  const status = getAuthStatus();
496
- res.json({
513
+ const response: Record<string, unknown> = {
497
514
  state: status.state,
498
515
  adapter: status.adapter,
499
516
  config: status.config || {},
500
517
  error: status.error,
501
518
  missingVars: status.missingVars,
502
- });
519
+ };
520
+
521
+ // Include runtime config if available from store
522
+ if (configStore) {
523
+ try {
524
+ const runtimeConfig = await configStore.load();
525
+ if (runtimeConfig) {
526
+ response.runtimeConfig = runtimeConfig;
527
+ }
528
+ } catch (err) {
529
+ console.error('[AuthPlugin] Failed to load runtime config:', err);
530
+ }
531
+ }
532
+
533
+ res.json(response);
534
+ },
535
+ });
536
+
537
+ // PUT /auth/config - Save new configuration
538
+ registry.addRoute({
539
+ method: 'put',
540
+ path: '/auth/config',
541
+ pluginId: 'auth',
542
+ handler: async (req: Request, res: Response) => {
543
+ try {
544
+ // Check if config store is available
545
+ if (!configStore) {
546
+ return res.status(503).json({
547
+ error: 'Configuration store not available',
548
+ message: 'Runtime configuration requires a config store (PostgreSQL)',
549
+ });
550
+ }
551
+
552
+ // Parse and validate request body
553
+ const body = req.body as UpdateAuthConfigRequest;
554
+ if (!body || !body.adapter) {
555
+ return res.status(400).json({
556
+ error: 'Invalid request',
557
+ message: 'Request body must include adapter type',
558
+ });
559
+ }
560
+
561
+ // Validate adapter type
562
+ if (!VALID_ADAPTERS.includes(body.adapter as AdapterName)) {
563
+ return res.status(400).json({
564
+ error: 'Invalid adapter',
565
+ message: `Valid adapters: ${VALID_ADAPTERS.join(', ')}`,
566
+ });
567
+ }
568
+
569
+ // Validate adapter config
570
+ const validation = validateAdapterConfig(body.adapter, body.config || {});
571
+ if (!validation.valid) {
572
+ return res.status(400).json({
573
+ error: 'Invalid configuration',
574
+ message: validation.errors.join(', '),
575
+ errors: validation.errors,
576
+ });
577
+ }
578
+
579
+ // Build runtime config
580
+ const runtimeConfig: RuntimeAuthConfig = {
581
+ adapter: body.adapter,
582
+ config: {
583
+ [body.adapter]: body.config,
584
+ } as RuntimeAuthConfig['config'],
585
+ settings: body.settings || {},
586
+ updatedAt: new Date().toISOString(),
587
+ };
588
+
589
+ // Save to store
590
+ await configStore.save(runtimeConfig);
591
+
592
+ // Apply config immediately (hot-reload)
593
+ if (adapterWrapper) {
594
+ const adapter = createAdapterFromConfig(body.adapter, runtimeConfig.config);
595
+ if (adapter) {
596
+ await adapterWrapper.setAdapter(adapter);
597
+ }
598
+ }
599
+
600
+ // Update status
601
+ currentStatus = {
602
+ state: 'enabled',
603
+ adapter: body.adapter,
604
+ config: getMaskedRuntimeConfig(runtimeConfig),
605
+ };
606
+
607
+ res.json({
608
+ success: true,
609
+ status: getAuthStatus(),
610
+ });
611
+ } catch (error) {
612
+ console.error('[AuthPlugin] Failed to save config:', error);
613
+ res.status(500).json({
614
+ error: 'Failed to save configuration',
615
+ message: error instanceof Error ? error.message : 'Unknown error',
616
+ });
617
+ }
503
618
  },
504
619
  });
620
+
621
+ // DELETE /auth/config - Revert to environment variables
622
+ registry.addRoute({
623
+ method: 'delete',
624
+ path: '/auth/config',
625
+ pluginId: 'auth',
626
+ handler: async (_req: Request, res: Response) => {
627
+ try {
628
+ // Check if config store is available
629
+ if (!configStore) {
630
+ return res.status(503).json({
631
+ error: 'Configuration store not available',
632
+ message: 'Runtime configuration requires a config store (PostgreSQL)',
633
+ });
634
+ }
635
+
636
+ // Delete from store
637
+ await configStore.delete();
638
+
639
+ // Revert to env vars
640
+ const adapterName = getEnv('AUTH_ADAPTER')?.toLowerCase() as AdapterName | undefined;
641
+ if (adapterName && VALID_ADAPTERS.includes(adapterName)) {
642
+ const parseResult = getParseResultForAdapter(adapterName);
643
+ if (parseResult.config && adapterWrapper) {
644
+ const adapter = createAdapterForName(adapterName, parseResult.config);
645
+ await adapterWrapper.setAdapter(adapter);
646
+ currentStatus = {
647
+ state: 'enabled',
648
+ adapter: adapterName,
649
+ config: getMaskedConfig(adapterName),
650
+ };
651
+ } else if (parseResult.errors.length > 0) {
652
+ currentStatus = {
653
+ state: 'error',
654
+ adapter: adapterName,
655
+ error: `Missing env vars: ${parseResult.errors.join(', ')}`,
656
+ missingVars: parseResult.errors,
657
+ };
658
+ }
659
+ } else {
660
+ currentStatus = {
661
+ state: 'disabled',
662
+ adapter: null,
663
+ };
664
+ }
665
+
666
+ res.json({
667
+ success: true,
668
+ status: getAuthStatus(),
669
+ });
670
+ } catch (error) {
671
+ console.error('[AuthPlugin] Failed to delete config:', error);
672
+ res.status(500).json({
673
+ error: 'Failed to delete configuration',
674
+ message: error instanceof Error ? error.message : 'Unknown error',
675
+ });
676
+ }
677
+ },
678
+ });
679
+
680
+ // POST /auth/test-provider - Test provider connection
681
+ registry.addRoute({
682
+ method: 'post',
683
+ path: '/auth/test-provider',
684
+ pluginId: 'auth',
685
+ handler: async (req: Request, res: Response) => {
686
+ try {
687
+ const body = req.body as TestProviderRequest;
688
+ if (!body || !body.adapter) {
689
+ return res.status(400).json({
690
+ error: 'Invalid request',
691
+ message: 'Request body must include adapter type',
692
+ });
693
+ }
694
+
695
+ // Validate adapter type
696
+ if (!VALID_ADAPTERS.includes(body.adapter as AdapterName)) {
697
+ return res.status(400).json({
698
+ error: 'Invalid adapter',
699
+ message: `Valid adapters: ${VALID_ADAPTERS.join(', ')}`,
700
+ });
701
+ }
702
+
703
+ // Test the connection
704
+ const result = await testProviderConnection(body.adapter, body.config || {});
705
+ res.json(result);
706
+ } catch (error) {
707
+ console.error('[AuthPlugin] Test provider error:', error);
708
+ res.status(500).json({
709
+ success: false,
710
+ message: error instanceof Error ? error.message : 'Unknown error',
711
+ });
712
+ }
713
+ },
714
+ });
715
+
716
+ // POST /auth/test-current - Test current auth configuration (env-based or runtime)
717
+ registry.addRoute({
718
+ method: 'post',
719
+ path: '/auth/test-current',
720
+ pluginId: 'auth',
721
+ handler: async (_req: Request, res: Response) => {
722
+ try {
723
+ const status = getAuthStatus();
724
+
725
+ if (status.state !== 'enabled' || !status.adapter) {
726
+ return res.status(400).json({
727
+ success: false,
728
+ message: 'No active auth configuration to test',
729
+ });
730
+ }
731
+
732
+ // Get the current configuration from env vars
733
+ const adapterName = status.adapter as AdapterName;
734
+ const parseResult = getParseResultForAdapter(adapterName);
735
+
736
+ if (parseResult.errors.length > 0) {
737
+ return res.json({
738
+ success: false,
739
+ message: `Missing configuration: ${parseResult.errors.join(', ')}`,
740
+ });
741
+ }
742
+
743
+ // Test the connection using the current env-based config
744
+ // Use JSON round-trip for safe conversion to plain object
745
+ const configObj = JSON.parse(JSON.stringify(parseResult.config)) as Record<string, unknown>;
746
+ const result = await testProviderConnection(adapterName, configObj);
747
+ res.json(result);
748
+ } catch (error) {
749
+ console.error('[AuthPlugin] Test current provider error:', error);
750
+ res.status(500).json({
751
+ success: false,
752
+ message: error instanceof Error ? error.message : 'Unknown error',
753
+ });
754
+ }
755
+ },
756
+ });
757
+
758
+ // Register UI menu item for auth configuration
759
+ registry.addMenuItem({
760
+ pluginId: 'auth',
761
+ id: 'auth:sidebar',
762
+ label: 'Authentication',
763
+ icon: 'security',
764
+ route: '/auth',
765
+ order: 50, // After Entitlements (35)
766
+ });
767
+
768
+ // Register page contribution
769
+ registry.addPage({
770
+ pluginId: 'auth',
771
+ id: 'auth:config-page',
772
+ route: '/auth',
773
+ component: 'AuthPage',
774
+ });
775
+
776
+ // Register dashboard widget
777
+ registry.addWidget({
778
+ id: 'auth-status',
779
+ title: 'Authentication',
780
+ component: 'AuthStatusWidget',
781
+ priority: 40, // Show before integrations
782
+ showByDefault: true,
783
+ pluginId: 'auth',
784
+ });
785
+
786
+ // Register health check for auth provider connection
787
+ const status = getAuthStatus();
788
+ if (status.state === 'enabled' && status.adapter) {
789
+ registry.registerHealthCheck({
790
+ name: `auth-${status.adapter}`,
791
+ type: 'custom',
792
+ check: async () => {
793
+ const currentStatus = getAuthStatus();
794
+ if (currentStatus.state !== 'enabled' || !currentStatus.adapter) {
795
+ return { healthy: false, message: 'Auth not configured' };
796
+ }
797
+
798
+ const adapterName = currentStatus.adapter as AdapterName;
799
+ const parseResult = getParseResultForAdapter(adapterName);
800
+ if (parseResult.errors.length > 0) {
801
+ return { healthy: false, message: `Missing config: ${parseResult.errors.join(', ')}` };
802
+ }
803
+
804
+ const configObj = JSON.parse(JSON.stringify(parseResult.config)) as Record<string, unknown>;
805
+ const result = await testProviderConnection(adapterName, configObj);
806
+ return {
807
+ healthy: result.success,
808
+ message: result.message,
809
+ details: result.details,
810
+ };
811
+ },
812
+ });
813
+ }
505
814
  }
506
815
 
507
816
  // ═══════════════════════════════════════════════════════════════════════════
@@ -554,6 +863,401 @@ function createErrorPlugin(error: string): Plugin {
554
863
  };
555
864
  }
556
865
 
866
+ // ═══════════════════════════════════════════════════════════════════════════
867
+ // Runtime Configuration Support
868
+ // ═══════════════════════════════════════════════════════════════════════════
869
+
870
+ /**
871
+ * Extended options for createAuthPluginFromEnv with runtime config support
872
+ */
873
+ export interface AuthEnvPluginOptionsExtended extends AuthEnvPluginOptions {
874
+ /** Configuration store for runtime config persistence */
875
+ configStore?: AuthConfigStore;
876
+ }
877
+
878
+ /**
879
+ * Create an adapter from runtime configuration
880
+ */
881
+ function createAdapterFromConfig(
882
+ adapterType: AuthAdapterType,
883
+ config: RuntimeAuthConfig['config']
884
+ ): AuthAdapter | null {
885
+ switch (adapterType) {
886
+ case 'supertokens':
887
+ if (config.supertokens) {
888
+ return supertokensAdapter(config.supertokens);
889
+ }
890
+ break;
891
+ case 'auth0':
892
+ if (config.auth0) {
893
+ return auth0Adapter(config.auth0);
894
+ }
895
+ break;
896
+ case 'supabase':
897
+ if (config.supabase) {
898
+ return supabaseAdapter(config.supabase);
899
+ }
900
+ break;
901
+ case 'basic':
902
+ if (config.basic) {
903
+ return basicAdapter(config.basic);
904
+ }
905
+ break;
906
+ }
907
+ return null;
908
+ }
909
+
910
+ /**
911
+ * Validate adapter configuration
912
+ */
913
+ function validateAdapterConfig(
914
+ adapterType: AuthAdapterType,
915
+ config: Record<string, unknown>
916
+ ): { valid: boolean; errors: string[] } {
917
+ const errors: string[] = [];
918
+
919
+ switch (adapterType) {
920
+ case 'supertokens':
921
+ if (!config.connectionUri) errors.push('connectionUri is required');
922
+ if (!config.appName) errors.push('appName is required');
923
+ if (!config.apiDomain) errors.push('apiDomain is required');
924
+ if (!config.websiteDomain) errors.push('websiteDomain is required');
925
+ break;
926
+ case 'auth0':
927
+ if (!config.domain) errors.push('domain is required');
928
+ if (!config.clientId) errors.push('clientId is required');
929
+ if (!config.clientSecret) errors.push('clientSecret is required');
930
+ if (!config.baseUrl) errors.push('baseUrl is required');
931
+ if (!config.secret) errors.push('secret is required');
932
+ break;
933
+ case 'supabase':
934
+ if (!config.url) errors.push('url is required');
935
+ if (!config.anonKey) errors.push('anonKey is required');
936
+ break;
937
+ case 'basic':
938
+ if (!config.username) errors.push('username is required');
939
+ if (!config.password) errors.push('password is required');
940
+ break;
941
+ }
942
+
943
+ return { valid: errors.length === 0, errors };
944
+ }
945
+
946
+ /**
947
+ * Validate a URL for security (prevent SSRF attacks)
948
+ */
949
+ function validateUrl(urlString: string): URL {
950
+ const url = new URL(urlString);
951
+ if (!['http:', 'https:'].includes(url.protocol)) {
952
+ throw new Error('URL must use http or https protocol');
953
+ }
954
+ // Block private/internal IPs (basic SSRF protection)
955
+ const hostname = url.hostname.toLowerCase();
956
+ if (
957
+ hostname === 'localhost' ||
958
+ hostname === '127.0.0.1' ||
959
+ hostname === '::1' ||
960
+ hostname.startsWith('192.168.') ||
961
+ hostname.startsWith('10.') ||
962
+ hostname.startsWith('172.') ||
963
+ hostname.endsWith('.internal') ||
964
+ hostname.endsWith('.local')
965
+ ) {
966
+ // Allow localhost for dev/testing, but log it
967
+ console.warn(`[AuthPlugin] Testing connection to internal address: ${hostname}`);
968
+ }
969
+ return url;
970
+ }
971
+
972
+ /**
973
+ * Test a provider connection
974
+ */
975
+ async function testProviderConnection(
976
+ adapterType: AuthAdapterType,
977
+ config: Record<string, unknown>
978
+ ): Promise<TestProviderResponse> {
979
+ const startTime = Date.now();
980
+
981
+ try {
982
+ // Validate config first
983
+ const validation = validateAdapterConfig(adapterType, config);
984
+ if (!validation.valid) {
985
+ return {
986
+ success: false,
987
+ message: `Invalid configuration: ${validation.errors.join(', ')}`,
988
+ };
989
+ }
990
+
991
+ // Test connection based on adapter type
992
+ switch (adapterType) {
993
+ case 'supertokens': {
994
+ // Try to connect to Supertokens core
995
+ const connectionUri = config.connectionUri as string;
996
+ validateUrl(connectionUri); // Validate URL before making request
997
+ const apiKey = config.apiKey as string | undefined;
998
+
999
+ const headers: Record<string, string> = {
1000
+ 'Content-Type': 'application/json',
1001
+ };
1002
+ if (apiKey) {
1003
+ headers['api-key'] = apiKey;
1004
+ }
1005
+
1006
+ const response = await fetch(`${connectionUri}/apiversion`, { headers });
1007
+ if (!response.ok) {
1008
+ return {
1009
+ success: false,
1010
+ message: `Failed to connect to Supertokens core: ${response.status} ${response.statusText}`,
1011
+ };
1012
+ }
1013
+
1014
+ const data = (await response.json()) as { versions?: string[] };
1015
+ return {
1016
+ success: true,
1017
+ message: 'Successfully connected to Supertokens core',
1018
+ details: {
1019
+ latency: Date.now() - startTime,
1020
+ version: data.versions?.[0],
1021
+ },
1022
+ };
1023
+ }
1024
+
1025
+ case 'auth0': {
1026
+ // Test Auth0 domain is reachable
1027
+ const domain = config.domain as string;
1028
+ const response = await fetch(`https://${domain}/.well-known/openid-configuration`);
1029
+ if (!response.ok) {
1030
+ return {
1031
+ success: false,
1032
+ message: `Failed to connect to Auth0: ${response.status} ${response.statusText}`,
1033
+ };
1034
+ }
1035
+
1036
+ return {
1037
+ success: true,
1038
+ message: 'Successfully connected to Auth0',
1039
+ details: {
1040
+ latency: Date.now() - startTime,
1041
+ },
1042
+ };
1043
+ }
1044
+
1045
+ case 'supabase': {
1046
+ // Test Supabase endpoint
1047
+ const url = config.url as string;
1048
+ validateUrl(url); // Validate URL before making request
1049
+ const anonKey = config.anonKey as string;
1050
+
1051
+ const response = await fetch(`${url}/rest/v1/`, {
1052
+ headers: {
1053
+ apikey: anonKey,
1054
+ Authorization: `Bearer ${anonKey}`,
1055
+ },
1056
+ });
1057
+
1058
+ // Supabase returns 200 even without tables
1059
+ if (!response.ok && response.status !== 404) {
1060
+ return {
1061
+ success: false,
1062
+ message: `Failed to connect to Supabase: ${response.status} ${response.statusText}`,
1063
+ };
1064
+ }
1065
+
1066
+ return {
1067
+ success: true,
1068
+ message: 'Successfully connected to Supabase',
1069
+ details: {
1070
+ latency: Date.now() - startTime,
1071
+ },
1072
+ };
1073
+ }
1074
+
1075
+ case 'basic': {
1076
+ // Basic auth just validates credentials are present
1077
+ return {
1078
+ success: true,
1079
+ message: 'Basic auth configuration is valid',
1080
+ details: {
1081
+ latency: Date.now() - startTime,
1082
+ },
1083
+ };
1084
+ }
1085
+
1086
+ default:
1087
+ return {
1088
+ success: false,
1089
+ message: `Unknown adapter type: ${adapterType}`,
1090
+ };
1091
+ }
1092
+ } catch (error) {
1093
+ // Provide more helpful error messages for common network errors
1094
+ let message = 'Connection test failed';
1095
+ if (error instanceof Error) {
1096
+ const errorWithCause = error as Error & { cause?: Error };
1097
+ if (error.message === 'fetch failed' || errorWithCause.cause) {
1098
+ // Network error - server not reachable
1099
+ const cause = errorWithCause.cause;
1100
+ if (cause?.message?.includes('ECONNREFUSED')) {
1101
+ const uri = adapterType === 'supertokens' ? config.connectionUri :
1102
+ adapterType === 'supabase' ? config.url :
1103
+ adapterType === 'auth0' ? `https://${config.domain}` : 'server';
1104
+ message = `Cannot connect to ${adapterType} at ${uri}. Is the server running?`;
1105
+ } else {
1106
+ message = `Network error: Unable to reach the ${adapterType} server. Check if it's running and accessible.`;
1107
+ }
1108
+ } else {
1109
+ message = `Connection test failed: ${error.message}`;
1110
+ }
1111
+ }
1112
+ return {
1113
+ success: false,
1114
+ message,
1115
+ };
1116
+ }
1117
+ }
1118
+
1119
+ /**
1120
+ * Handle configuration change (from database)
1121
+ * Called when pg_notify fires on another instance
1122
+ */
1123
+ async function handleConfigChange(newConfig: RuntimeAuthConfig | null): Promise<void> {
1124
+ if (!adapterWrapper) return;
1125
+
1126
+ if (!newConfig || !newConfig.adapter) {
1127
+ // No config in database - revert to env vars
1128
+ const adapterName = getEnv('AUTH_ADAPTER')?.toLowerCase() as AdapterName | undefined;
1129
+ if (adapterName && VALID_ADAPTERS.includes(adapterName)) {
1130
+ // Re-create adapter from env vars
1131
+ const parseResult = getParseResultForAdapter(adapterName);
1132
+ if (parseResult.config) {
1133
+ const adapter = createAdapterForName(adapterName, parseResult.config);
1134
+ if (adapter) {
1135
+ await adapterWrapper.setAdapter(adapter);
1136
+ currentStatus = {
1137
+ state: 'enabled',
1138
+ adapter: adapterName,
1139
+ config: getMaskedConfig(adapterName),
1140
+ };
1141
+ }
1142
+ }
1143
+ } else {
1144
+ // No env config either - disable
1145
+ currentStatus = {
1146
+ state: 'disabled',
1147
+ adapter: null,
1148
+ };
1149
+ }
1150
+ return;
1151
+ }
1152
+
1153
+ // Apply new config
1154
+ const adapter = createAdapterFromConfig(newConfig.adapter, newConfig.config);
1155
+ if (adapter) {
1156
+ await adapterWrapper.setAdapter(adapter);
1157
+ currentStatus = {
1158
+ state: 'enabled',
1159
+ adapter: newConfig.adapter,
1160
+ config: getMaskedRuntimeConfig(newConfig),
1161
+ };
1162
+ }
1163
+ }
1164
+
1165
+ /**
1166
+ * Get masked runtime configuration
1167
+ */
1168
+ function getMaskedRuntimeConfig(config: RuntimeAuthConfig): Record<string, string> {
1169
+ const result: Record<string, string> = {};
1170
+
1171
+ if (!config.adapter) return result;
1172
+
1173
+ const sensitiveKeys = ['secret', 'password', 'key', 'token', 'anonKey', 'clientSecret', 'apiKey'];
1174
+ const isSensitive = (key: string): boolean =>
1175
+ sensitiveKeys.some((s) => key.toLowerCase().includes(s.toLowerCase()));
1176
+
1177
+ // Get adapter-specific config
1178
+ const adapterConfig = config.config[config.adapter];
1179
+ if (adapterConfig) {
1180
+ for (const [key, value] of Object.entries(adapterConfig)) {
1181
+ if (typeof value === 'string') {
1182
+ result[key] = isSensitive(key) ? maskValue(value) : value;
1183
+ } else if (typeof value === 'boolean' || typeof value === 'number') {
1184
+ result[key] = String(value);
1185
+ }
1186
+ }
1187
+ }
1188
+
1189
+ result['AUTH_ADAPTER'] = config.adapter;
1190
+
1191
+ return result;
1192
+ }
1193
+
1194
+ /**
1195
+ * Helper to get parse result for a given adapter
1196
+ */
1197
+ function getParseResultForAdapter(adapterName: AdapterName): EnvParseResult<
1198
+ Auth0AdapterConfig | SupabaseAdapterConfig | SupertokensAdapterConfig | BasicAdapterConfig
1199
+ > {
1200
+ switch (adapterName) {
1201
+ case 'supertokens':
1202
+ return parseSupertokensEnv();
1203
+ case 'auth0':
1204
+ return parseAuth0Env();
1205
+ case 'supabase':
1206
+ return parseSupabaseEnv();
1207
+ case 'basic':
1208
+ return parseBasicAuthEnv();
1209
+ }
1210
+ }
1211
+
1212
+ /**
1213
+ * Helper to create adapter for a given name and config
1214
+ */
1215
+ function createAdapterForName(
1216
+ adapterName: AdapterName,
1217
+ config: Auth0AdapterConfig | SupabaseAdapterConfig | SupertokensAdapterConfig | BasicAdapterConfig
1218
+ ): AuthAdapter {
1219
+ switch (adapterName) {
1220
+ case 'supertokens':
1221
+ return supertokensAdapter(config as SupertokensAdapterConfig);
1222
+ case 'auth0':
1223
+ return auth0Adapter(config as Auth0AdapterConfig);
1224
+ case 'supabase':
1225
+ return supabaseAdapter(config as SupabaseAdapterConfig);
1226
+ case 'basic':
1227
+ return basicAdapter(config as BasicAdapterConfig);
1228
+ }
1229
+ }
1230
+
1231
+ /**
1232
+ * Set the configuration store for runtime auth config persistence.
1233
+ *
1234
+ * This must be called during application startup to enable runtime configuration
1235
+ * features. Without a config store, the PUT/DELETE endpoints will return 503.
1236
+ *
1237
+ * @param store - PostgreSQL-backed config store from `postgresAuthConfigStore()`
1238
+ *
1239
+ * @example
1240
+ * ```typescript
1241
+ * import { Pool } from 'pg';
1242
+ * import { setAuthConfigStore, postgresAuthConfigStore } from '@qwickapps/server';
1243
+ *
1244
+ * const pool = new Pool({ connectionString: process.env.DATABASE_URL });
1245
+ * const configStore = postgresAuthConfigStore({ pool });
1246
+ * await configStore.initialize();
1247
+ * setAuthConfigStore(configStore);
1248
+ * ```
1249
+ */
1250
+ export function setAuthConfigStore(store: AuthConfigStore): void {
1251
+ configStore = store;
1252
+ }
1253
+
1254
+ /**
1255
+ * Get the current adapter wrapper
1256
+ */
1257
+ export function getAdapterWrapper(): AdapterWrapper | null {
1258
+ return adapterWrapper;
1259
+ }
1260
+
557
1261
  // ═══════════════════════════════════════════════════════════════════════════
558
1262
  // Exports for Testing
559
1263
  // ═══════════════════════════════════════════════════════════════════════════
@@ -569,4 +1273,7 @@ export const __testing = {
569
1273
  getEnvList,
570
1274
  maskValue,
571
1275
  VALID_ADAPTERS,
1276
+ validateAdapterConfig,
1277
+ testProviderConnection,
1278
+ createAdapterFromConfig,
572
1279
  };