@qwickapps/server 1.7.0 → 1.7.2

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 (335) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +13 -116
  3. package/dist/src/core/control-panel.d.ts.map +1 -1
  4. package/dist/src/core/control-panel.js +6 -4
  5. package/dist/src/core/control-panel.js.map +1 -1
  6. package/dist/src/core/gateway.d.ts.map +1 -1
  7. package/dist/src/core/gateway.js +24 -2
  8. package/dist/src/core/gateway.js.map +1 -1
  9. package/dist/src/core/plugin-registry.d.ts +15 -2
  10. package/dist/src/core/plugin-registry.d.ts.map +1 -1
  11. package/dist/src/core/plugin-registry.js.map +1 -1
  12. package/dist/src/index.d.ts +2 -2
  13. package/dist/src/index.d.ts.map +1 -1
  14. package/dist/src/index.js +9 -3
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/plugins/api-keys/api-keys-plugin.d.ts +5 -2
  17. package/dist/src/plugins/api-keys/api-keys-plugin.d.ts.map +1 -1
  18. package/dist/src/plugins/api-keys/api-keys-plugin.js +61 -19
  19. package/dist/src/plugins/api-keys/api-keys-plugin.js.map +1 -1
  20. package/dist/src/plugins/api-keys/index.d.ts +0 -4
  21. package/dist/src/plugins/api-keys/index.d.ts.map +1 -1
  22. package/dist/src/plugins/api-keys/index.js +2 -3
  23. package/dist/src/plugins/api-keys/index.js.map +1 -1
  24. package/dist/src/plugins/api-keys/stores/postgres-store.d.ts.map +1 -1
  25. package/dist/src/plugins/api-keys/stores/postgres-store.js +29 -0
  26. package/dist/src/plugins/api-keys/stores/postgres-store.js.map +1 -1
  27. package/dist/src/plugins/api-keys/types.d.ts +9 -3
  28. package/dist/src/plugins/api-keys/types.d.ts.map +1 -1
  29. package/dist/src/plugins/api-keys/types.js.map +1 -1
  30. package/dist/src/plugins/auth/auth-plugin.d.ts.map +1 -1
  31. package/dist/src/plugins/auth/auth-plugin.js +4 -2
  32. package/dist/src/plugins/auth/auth-plugin.js.map +1 -1
  33. package/dist/src/plugins/auth/env-config.d.ts.map +1 -1
  34. package/dist/src/plugins/auth/env-config.js +1 -0
  35. package/dist/src/plugins/auth/env-config.js.map +1 -1
  36. package/dist/src/plugins/auth/index.d.ts +0 -4
  37. package/dist/src/plugins/auth/index.d.ts.map +1 -1
  38. package/dist/src/plugins/auth/index.js +2 -3
  39. package/dist/src/plugins/auth/index.js.map +1 -1
  40. package/dist/src/plugins/bans/bans-plugin.d.ts +5 -2
  41. package/dist/src/plugins/bans/bans-plugin.d.ts.map +1 -1
  42. package/dist/src/plugins/bans/bans-plugin.js +71 -25
  43. package/dist/src/plugins/bans/bans-plugin.js.map +1 -1
  44. package/dist/src/plugins/bans/index.d.ts +1 -5
  45. package/dist/src/plugins/bans/index.d.ts.map +1 -1
  46. package/dist/src/plugins/bans/index.js +3 -4
  47. package/dist/src/plugins/bans/index.js.map +1 -1
  48. package/dist/src/plugins/bans/stores/in-memory-store.d.ts +34 -0
  49. package/dist/src/plugins/bans/stores/in-memory-store.d.ts.map +1 -0
  50. package/dist/src/plugins/bans/stores/in-memory-store.js +97 -0
  51. package/dist/src/plugins/bans/stores/in-memory-store.js.map +1 -0
  52. package/dist/src/plugins/bans/stores/index.d.ts +1 -0
  53. package/dist/src/plugins/bans/stores/index.d.ts.map +1 -1
  54. package/dist/src/plugins/bans/stores/index.js +1 -0
  55. package/dist/src/plugins/bans/stores/index.js.map +1 -1
  56. package/dist/src/plugins/bans/types.d.ts +13 -6
  57. package/dist/src/plugins/bans/types.d.ts.map +1 -1
  58. package/dist/src/plugins/cache-plugin.d.ts +35 -16
  59. package/dist/src/plugins/cache-plugin.d.ts.map +1 -1
  60. package/dist/src/plugins/cache-plugin.js +299 -20
  61. package/dist/src/plugins/cache-plugin.js.map +1 -1
  62. package/dist/src/plugins/cms/cms-plugin.d.ts.map +1 -1
  63. package/dist/src/plugins/cms/cms-plugin.js +3 -1
  64. package/dist/src/plugins/cms/cms-plugin.js.map +1 -1
  65. package/dist/src/plugins/devices/devices-plugin.d.ts +5 -2
  66. package/dist/src/plugins/devices/devices-plugin.d.ts.map +1 -1
  67. package/dist/src/plugins/devices/devices-plugin.js +62 -26
  68. package/dist/src/plugins/devices/devices-plugin.js.map +1 -1
  69. package/dist/src/plugins/devices/index.d.ts +0 -4
  70. package/dist/src/plugins/devices/index.d.ts.map +1 -1
  71. package/dist/src/plugins/devices/index.js +2 -3
  72. package/dist/src/plugins/devices/index.js.map +1 -1
  73. package/dist/src/plugins/entitlements/entitlements-plugin.d.ts +5 -2
  74. package/dist/src/plugins/entitlements/entitlements-plugin.d.ts.map +1 -1
  75. package/dist/src/plugins/entitlements/entitlements-plugin.js +78 -41
  76. package/dist/src/plugins/entitlements/entitlements-plugin.js.map +1 -1
  77. package/dist/src/plugins/entitlements/index.d.ts +1 -5
  78. package/dist/src/plugins/entitlements/index.d.ts.map +1 -1
  79. package/dist/src/plugins/entitlements/index.js +3 -4
  80. package/dist/src/plugins/entitlements/index.js.map +1 -1
  81. package/dist/src/plugins/entitlements/sources/in-memory-source.d.ts +9 -0
  82. package/dist/src/plugins/entitlements/sources/in-memory-source.d.ts.map +1 -0
  83. package/dist/src/plugins/entitlements/sources/in-memory-source.js +65 -0
  84. package/dist/src/plugins/entitlements/sources/in-memory-source.js.map +1 -0
  85. package/dist/src/plugins/entitlements/sources/index.d.ts +1 -0
  86. package/dist/src/plugins/entitlements/sources/index.d.ts.map +1 -1
  87. package/dist/src/plugins/entitlements/sources/index.js +1 -0
  88. package/dist/src/plugins/entitlements/sources/index.js.map +1 -1
  89. package/dist/src/plugins/entitlements/types.d.ts +9 -2
  90. package/dist/src/plugins/entitlements/types.d.ts.map +1 -1
  91. package/dist/src/plugins/health-plugin.d.ts.map +1 -1
  92. package/dist/src/plugins/health-plugin.js +1 -0
  93. package/dist/src/plugins/health-plugin.js.map +1 -1
  94. package/dist/src/plugins/index.d.ts +4 -4
  95. package/dist/src/plugins/index.d.ts.map +1 -1
  96. package/dist/src/plugins/index.js +4 -4
  97. package/dist/src/plugins/index.js.map +1 -1
  98. package/dist/src/plugins/logs-plugin.d.ts.map +1 -1
  99. package/dist/src/plugins/logs-plugin.js +49 -1
  100. package/dist/src/plugins/logs-plugin.js.map +1 -1
  101. package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
  102. package/dist/src/plugins/maintenance-plugin.js +39 -0
  103. package/dist/src/plugins/maintenance-plugin.js.map +1 -1
  104. package/dist/src/plugins/notifications/index.d.ts +0 -4
  105. package/dist/src/plugins/notifications/index.d.ts.map +1 -1
  106. package/dist/src/plugins/notifications/index.js +2 -3
  107. package/dist/src/plugins/notifications/index.js.map +1 -1
  108. package/dist/src/plugins/notifications/notifications-plugin.d.ts +5 -2
  109. package/dist/src/plugins/notifications/notifications-plugin.d.ts.map +1 -1
  110. package/dist/src/plugins/notifications/notifications-plugin.js +46 -13
  111. package/dist/src/plugins/notifications/notifications-plugin.js.map +1 -1
  112. package/dist/src/plugins/parental/index.d.ts +0 -4
  113. package/dist/src/plugins/parental/index.d.ts.map +1 -1
  114. package/dist/src/plugins/parental/index.js +2 -3
  115. package/dist/src/plugins/parental/index.js.map +1 -1
  116. package/dist/src/plugins/parental/parental-plugin.d.ts +5 -2
  117. package/dist/src/plugins/parental/parental-plugin.d.ts.map +1 -1
  118. package/dist/src/plugins/parental/parental-plugin.js +60 -24
  119. package/dist/src/plugins/parental/parental-plugin.js.map +1 -1
  120. package/dist/src/plugins/postgres-plugin.d.ts +3 -1
  121. package/dist/src/plugins/postgres-plugin.d.ts.map +1 -1
  122. package/dist/src/plugins/postgres-plugin.js +18 -8
  123. package/dist/src/plugins/postgres-plugin.js.map +1 -1
  124. package/dist/src/plugins/preferences/index.d.ts +0 -4
  125. package/dist/src/plugins/preferences/index.d.ts.map +1 -1
  126. package/dist/src/plugins/preferences/index.js +2 -3
  127. package/dist/src/plugins/preferences/index.js.map +1 -1
  128. package/dist/src/plugins/preferences/preferences-plugin.d.ts +5 -2
  129. package/dist/src/plugins/preferences/preferences-plugin.d.ts.map +1 -1
  130. package/dist/src/plugins/preferences/preferences-plugin.js +63 -19
  131. package/dist/src/plugins/preferences/preferences-plugin.js.map +1 -1
  132. package/dist/src/plugins/profiles/index.d.ts +0 -4
  133. package/dist/src/plugins/profiles/index.d.ts.map +1 -1
  134. package/dist/src/plugins/profiles/index.js +2 -3
  135. package/dist/src/plugins/profiles/index.js.map +1 -1
  136. package/dist/src/plugins/profiles/profiles-plugin.d.ts +5 -2
  137. package/dist/src/plugins/profiles/profiles-plugin.d.ts.map +1 -1
  138. package/dist/src/plugins/profiles/profiles-plugin.js +60 -26
  139. package/dist/src/plugins/profiles/profiles-plugin.js.map +1 -1
  140. package/dist/src/plugins/profiles/types.d.ts +9 -2
  141. package/dist/src/plugins/profiles/types.d.ts.map +1 -1
  142. package/dist/src/plugins/qwickbrain/index.d.ts +0 -4
  143. package/dist/src/plugins/qwickbrain/index.d.ts.map +1 -1
  144. package/dist/src/plugins/qwickbrain/index.js +2 -3
  145. package/dist/src/plugins/qwickbrain/index.js.map +1 -1
  146. package/dist/src/plugins/qwickbrain/qwickbrain-plugin.d.ts.map +1 -1
  147. package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js +117 -0
  148. package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js.map +1 -1
  149. package/dist/src/plugins/rate-limit/index.d.ts +0 -4
  150. package/dist/src/plugins/rate-limit/index.d.ts.map +1 -1
  151. package/dist/src/plugins/rate-limit/index.js +2 -3
  152. package/dist/src/plugins/rate-limit/index.js.map +1 -1
  153. package/dist/src/plugins/subscriptions/index.d.ts +0 -4
  154. package/dist/src/plugins/subscriptions/index.d.ts.map +1 -1
  155. package/dist/src/plugins/subscriptions/index.js +2 -3
  156. package/dist/src/plugins/subscriptions/index.js.map +1 -1
  157. package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts +5 -2
  158. package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -1
  159. package/dist/src/plugins/subscriptions/subscriptions-plugin.js +63 -29
  160. package/dist/src/plugins/subscriptions/subscriptions-plugin.js.map +1 -1
  161. package/dist/src/plugins/subscriptions/types.d.ts +8 -2
  162. package/dist/src/plugins/subscriptions/types.d.ts.map +1 -1
  163. package/dist/src/plugins/tenants/index.d.ts +1 -1
  164. package/dist/src/plugins/tenants/index.d.ts.map +1 -1
  165. package/dist/src/plugins/tenants/index.js +1 -1
  166. package/dist/src/plugins/tenants/index.js.map +1 -1
  167. package/dist/src/plugins/tenants/stores/in-memory-store.d.ts +59 -0
  168. package/dist/src/plugins/tenants/stores/in-memory-store.d.ts.map +1 -0
  169. package/dist/src/plugins/tenants/stores/in-memory-store.js +257 -0
  170. package/dist/src/plugins/tenants/stores/in-memory-store.js.map +1 -0
  171. package/dist/src/plugins/tenants/stores/index.d.ts +8 -0
  172. package/dist/src/plugins/tenants/stores/index.d.ts.map +1 -0
  173. package/dist/src/plugins/tenants/stores/index.js +8 -0
  174. package/dist/src/plugins/tenants/stores/index.js.map +1 -0
  175. package/dist/src/plugins/tenants/tenants-plugin.d.ts +5 -2
  176. package/dist/src/plugins/tenants/tenants-plugin.d.ts.map +1 -1
  177. package/dist/src/plugins/tenants/tenants-plugin.js +93 -60
  178. package/dist/src/plugins/tenants/tenants-plugin.js.map +1 -1
  179. package/dist/src/plugins/tenants/types.d.ts +8 -2
  180. package/dist/src/plugins/tenants/types.d.ts.map +1 -1
  181. package/dist/src/plugins/usage/index.d.ts +0 -4
  182. package/dist/src/plugins/usage/index.d.ts.map +1 -1
  183. package/dist/src/plugins/usage/index.js +2 -3
  184. package/dist/src/plugins/usage/index.js.map +1 -1
  185. package/dist/src/plugins/usage/usage-plugin.d.ts +5 -2
  186. package/dist/src/plugins/usage/usage-plugin.d.ts.map +1 -1
  187. package/dist/src/plugins/usage/usage-plugin.js +57 -23
  188. package/dist/src/plugins/usage/usage-plugin.js.map +1 -1
  189. package/dist/src/plugins/users/index.d.ts +1 -1
  190. package/dist/src/plugins/users/index.d.ts.map +1 -1
  191. package/dist/src/plugins/users/index.js +1 -1
  192. package/dist/src/plugins/users/index.js.map +1 -1
  193. package/dist/src/plugins/users/stores/in-memory-store.d.ts +36 -0
  194. package/dist/src/plugins/users/stores/in-memory-store.d.ts.map +1 -0
  195. package/dist/src/plugins/users/stores/in-memory-store.js +122 -0
  196. package/dist/src/plugins/users/stores/in-memory-store.js.map +1 -0
  197. package/dist/src/plugins/users/stores/index.d.ts +1 -0
  198. package/dist/src/plugins/users/stores/index.d.ts.map +1 -1
  199. package/dist/src/plugins/users/stores/index.js +1 -0
  200. package/dist/src/plugins/users/stores/index.js.map +1 -1
  201. package/dist/src/plugins/users/types.d.ts +7 -2
  202. package/dist/src/plugins/users/types.d.ts.map +1 -1
  203. package/dist/src/plugins/users/users-plugin.d.ts +5 -2
  204. package/dist/src/plugins/users/users-plugin.d.ts.map +1 -1
  205. package/dist/src/plugins/users/users-plugin.js +56 -23
  206. package/dist/src/plugins/users/users-plugin.js.map +1 -1
  207. package/dist/ui/src/api/controlPanelApi.d.ts +10 -1
  208. package/dist/ui/src/api/controlPanelApi.d.ts.map +1 -1
  209. package/dist/ui/src/api/controlPanelApi.js.map +1 -1
  210. package/dist/ui/src/dashboard/PluginWidgetRenderer.d.ts +3 -1
  211. package/dist/ui/src/dashboard/PluginWidgetRenderer.d.ts.map +1 -1
  212. package/dist/ui/src/dashboard/PluginWidgetRenderer.js +5 -1
  213. package/dist/ui/src/dashboard/PluginWidgetRenderer.js.map +1 -1
  214. package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
  215. package/dist/ui/src/dashboard/builtInWidgets.js +13 -1
  216. package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
  217. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts +11 -0
  218. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts.map +1 -0
  219. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +77 -0
  220. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -0
  221. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.d.ts +10 -0
  222. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.d.ts.map +1 -0
  223. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.js +14 -0
  224. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.js.map +1 -0
  225. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.d.ts +10 -0
  226. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.d.ts.map +1 -0
  227. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.js +14 -0
  228. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.js.map +1 -0
  229. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts +11 -0
  230. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts.map +1 -0
  231. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +96 -0
  232. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -0
  233. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts +10 -0
  234. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts.map +1 -0
  235. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +55 -0
  236. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -0
  237. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.d.ts +10 -0
  238. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.d.ts.map +1 -0
  239. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.js +14 -0
  240. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.js.map +1 -0
  241. package/dist/ui/src/dashboard/widgets/index.d.ts +6 -0
  242. package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
  243. package/dist/ui/src/dashboard/widgets/index.js +6 -0
  244. package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
  245. package/dist/ui/src/pages/DashboardPage.js +1 -1
  246. package/dist/ui/src/pages/DashboardPage.js.map +1 -1
  247. package/dist-ui/assets/index-0gzisPdy.js +528 -0
  248. package/dist-ui/assets/{index-lm1yX6UD.js.map → index-0gzisPdy.js.map} +1 -1
  249. package/dist-ui/index.html +1 -1
  250. package/dist-ui-lib/index.js +3109 -2774
  251. package/dist-ui-lib/index.js.map +1 -1
  252. package/dist-ui-lib/src/api/controlPanelApi.d.ts +10 -1
  253. package/dist-ui-lib/src/dashboard/PluginWidgetRenderer.d.ts +3 -1
  254. package/dist-ui-lib/src/dashboard/widgets/CacheMaintenanceWidget.d.ts +10 -0
  255. package/dist-ui-lib/src/dashboard/widgets/DatabaseOpsWidget.d.ts +9 -0
  256. package/dist-ui-lib/src/dashboard/widgets/EnvironmentConfigWidget.d.ts +9 -0
  257. package/dist-ui-lib/src/dashboard/widgets/LogsMaintenanceWidget.d.ts +10 -0
  258. package/dist-ui-lib/src/dashboard/widgets/SeedManagementWidget.d.ts +9 -0
  259. package/dist-ui-lib/src/dashboard/widgets/ServiceControlWidget.d.ts +9 -0
  260. package/dist-ui-lib/src/dashboard/widgets/index.d.ts +6 -0
  261. package/package.json +12 -6
  262. package/src/core/control-panel.ts +6 -4
  263. package/src/core/gateway.ts +25 -2
  264. package/src/core/plugin-registry.ts +15 -2
  265. package/src/index.ts +53 -0
  266. package/src/plugins/api-keys/api-keys-plugin.ts +64 -20
  267. package/src/plugins/api-keys/index.ts +2 -5
  268. package/src/plugins/api-keys/stores/postgres-store.ts +30 -0
  269. package/src/plugins/api-keys/types.ts +9 -3
  270. package/src/plugins/auth/auth-plugin.ts +4 -2
  271. package/src/plugins/auth/env-config.ts +1 -0
  272. package/src/plugins/auth/index.ts +3 -5
  273. package/src/plugins/bans/bans-plugin.ts +71 -26
  274. package/src/plugins/bans/index.ts +4 -6
  275. package/src/plugins/bans/stores/in-memory-store.ts +106 -0
  276. package/src/plugins/bans/stores/index.ts +1 -0
  277. package/src/plugins/bans/types.ts +13 -6
  278. package/src/plugins/cache-plugin.test.ts +2 -2
  279. package/src/plugins/cache-plugin.ts +331 -30
  280. package/src/plugins/cms/cms-plugin.ts +3 -1
  281. package/src/plugins/devices/devices-plugin.ts +62 -27
  282. package/src/plugins/devices/index.ts +3 -5
  283. package/src/plugins/entitlements/entitlements-plugin.ts +81 -43
  284. package/src/plugins/entitlements/index.ts +4 -6
  285. package/src/plugins/entitlements/sources/in-memory-source.ts +76 -0
  286. package/src/plugins/entitlements/sources/index.ts +1 -0
  287. package/src/plugins/entitlements/types.ts +9 -2
  288. package/src/plugins/health-plugin.ts +1 -0
  289. package/src/plugins/index.ts +4 -1
  290. package/src/plugins/logs-plugin.ts +55 -1
  291. package/src/plugins/maintenance-plugin.ts +43 -0
  292. package/src/plugins/notifications/index.ts +3 -5
  293. package/src/plugins/notifications/notifications-plugin.ts +49 -19
  294. package/src/plugins/parental/index.ts +3 -5
  295. package/src/plugins/parental/parental-plugin.ts +63 -25
  296. package/src/plugins/postgres-plugin.test.ts +2 -2
  297. package/src/plugins/postgres-plugin.ts +20 -9
  298. package/src/plugins/preferences/index.ts +3 -5
  299. package/src/plugins/preferences/preferences-plugin.ts +66 -20
  300. package/src/plugins/profiles/index.ts +3 -5
  301. package/src/plugins/profiles/profiles-plugin.ts +60 -27
  302. package/src/plugins/profiles/types.ts +9 -2
  303. package/src/plugins/qwickbrain/index.ts +3 -5
  304. package/src/plugins/qwickbrain/qwickbrain-plugin.ts +135 -0
  305. package/src/plugins/rate-limit/index.ts +3 -5
  306. package/src/plugins/subscriptions/index.ts +3 -5
  307. package/src/plugins/subscriptions/subscriptions-plugin.ts +63 -30
  308. package/src/plugins/subscriptions/types.ts +8 -2
  309. package/src/plugins/tenants/index.ts +1 -1
  310. package/src/plugins/tenants/stores/in-memory-store.ts +335 -0
  311. package/src/plugins/tenants/stores/index.ts +13 -0
  312. package/src/plugins/tenants/tenants-plugin.ts +97 -62
  313. package/src/plugins/tenants/types.ts +8 -2
  314. package/src/plugins/usage/index.ts +3 -5
  315. package/src/plugins/usage/usage-plugin.ts +60 -26
  316. package/src/plugins/users/index.ts +1 -1
  317. package/src/plugins/users/stores/in-memory-store.ts +140 -0
  318. package/src/plugins/users/stores/index.ts +1 -0
  319. package/src/plugins/users/types.ts +7 -2
  320. package/src/plugins/users/users-plugin.ts +56 -24
  321. package/src/testing/index.ts +1 -0
  322. package/src/testing/pg-mem-pool.ts +33 -0
  323. package/ui/src/api/controlPanelApi.ts +10 -1
  324. package/ui/src/dashboard/PluginWidgetRenderer.tsx +8 -0
  325. package/ui/src/dashboard/builtInWidgets.tsx +19 -1
  326. package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +195 -0
  327. package/ui/src/dashboard/widgets/DatabaseOpsWidget.tsx +29 -0
  328. package/ui/src/dashboard/widgets/EnvironmentConfigWidget.tsx +29 -0
  329. package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +247 -0
  330. package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +128 -0
  331. package/ui/src/dashboard/widgets/ServiceControlWidget.tsx +29 -0
  332. package/ui/src/dashboard/widgets/index.ts +6 -0
  333. package/ui/src/pages/DashboardPage.tsx +2 -2
  334. package/ui/src/pages/MaintenancePage.tsx +1 -1
  335. package/dist-ui/assets/index-lm1yX6UD.js +0 -528
@@ -0,0 +1,335 @@
1
+ /**
2
+ * In-memory Tenant Store for Demo/Testing
3
+ *
4
+ * Implements the TenantStore interface with in-memory storage.
5
+ * Pre-populated with demo tenants and memberships.
6
+ */
7
+
8
+ import type {
9
+ Tenant,
10
+ TenantStore,
11
+ CreateTenantInput,
12
+ UpdateTenantInput,
13
+ TenantSearchParams,
14
+ TenantListResponse,
15
+ TenantMembership,
16
+ CreateTenantMembershipInput,
17
+ UpdateTenantMembershipInput,
18
+ TenantWithMembership,
19
+ TenantType,
20
+ } from '../types.js';
21
+
22
+ /**
23
+ * Default demo user ID used for pre-populated tenants.
24
+ * Matches the user ID assigned by basic auth guard.
25
+ */
26
+ export const DEMO_USER_ID = 'basic-auth-user';
27
+
28
+ /**
29
+ * Logger interface for in-memory stores.
30
+ */
31
+ export interface InMemoryStoreLogger {
32
+ info: (message: string) => void;
33
+ debug?: (message: string) => void;
34
+ }
35
+
36
+ /**
37
+ * Options for creating an in-memory tenant store.
38
+ */
39
+ export interface InMemoryTenantStoreOptions {
40
+ /**
41
+ * Demo user ID for pre-populated tenants.
42
+ * @default 'basic-auth-user'
43
+ */
44
+ demoUserId?: string;
45
+
46
+ /**
47
+ * Optional logger for store operations.
48
+ * If not provided, uses console.log as fallback.
49
+ */
50
+ logger?: InMemoryStoreLogger;
51
+ }
52
+
53
+ /**
54
+ * Creates an in-memory tenant store for demo/testing purposes.
55
+ * Pre-populated with 4 demo tenants (organization, group, department, user).
56
+ *
57
+ * This store is NOT suitable for production use - data is lost on restart.
58
+ * Use postgresTenantsStore for production deployments.
59
+ *
60
+ * @param options - Configuration options for the store
61
+ * @returns TenantStore implementation with in-memory storage
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * import { createInMemoryTenantStore } from '@qwickapps/server';
66
+ *
67
+ * const store = createInMemoryTenantStore({
68
+ * demoUserId: 'test-user-123',
69
+ * logger: console,
70
+ * });
71
+ *
72
+ * await store.initialize();
73
+ * const tenants = await store.getTenantsForUser('test-user-123');
74
+ * ```
75
+ */
76
+ export function createInMemoryTenantStore(
77
+ options: InMemoryTenantStoreOptions = {}
78
+ ): TenantStore {
79
+ const demoUserId = options.demoUserId || DEMO_USER_ID;
80
+ const logger = options.logger || {
81
+ info: (msg: string) => console.log(msg),
82
+ debug: (msg: string) => console.log(msg),
83
+ };
84
+
85
+ const tenants = new Map<string, Tenant>();
86
+ const memberships = new Map<string, TenantMembership>();
87
+ let tenantIdCounter = 1;
88
+ let membershipIdCounter = 1;
89
+
90
+ // Pre-populate demo tenants
91
+ const demoTenants: Array<{ name: string; type: TenantType; owner_id: string }> = [
92
+ { name: 'Acme Corporation', type: 'organization', owner_id: demoUserId },
93
+ { name: 'Engineering Team', type: 'group', owner_id: demoUserId },
94
+ { name: 'Finance Department', type: 'department', owner_id: demoUserId },
95
+ { name: 'Demo User Workspace', type: 'user', owner_id: demoUserId },
96
+ ];
97
+
98
+ demoTenants.forEach((t) => {
99
+ const id = `tenant-${tenantIdCounter++}`;
100
+ const tenant: Tenant = {
101
+ id,
102
+ name: t.name,
103
+ type: t.type,
104
+ owner_id: t.owner_id,
105
+ metadata: {},
106
+ created_at: new Date(),
107
+ updated_at: new Date(),
108
+ };
109
+ tenants.set(id, tenant);
110
+
111
+ // Auto-create membership for owner
112
+ const membershipId = `membership-${membershipIdCounter++}`;
113
+ const membership: TenantMembership = {
114
+ id: membershipId,
115
+ tenant_id: id,
116
+ user_id: t.owner_id,
117
+ role: 'owner',
118
+ joined_at: new Date(),
119
+ };
120
+ memberships.set(membershipId, membership);
121
+ });
122
+
123
+ return {
124
+ name: 'in-memory',
125
+
126
+ async initialize(): Promise<void> {
127
+ logger.info('[InMemoryTenantStore] Initialized with demo tenants');
128
+ },
129
+
130
+ async getById(id: string): Promise<Tenant | null> {
131
+ return tenants.get(id) || null;
132
+ },
133
+
134
+ async getByIds(ids: string[]): Promise<Tenant[]> {
135
+ return ids.map((id) => tenants.get(id)).filter((t): t is Tenant => t !== undefined);
136
+ },
137
+
138
+ async getByName(name: string): Promise<Tenant | null> {
139
+ for (const tenant of tenants.values()) {
140
+ if (tenant.name.toLowerCase() === name.toLowerCase()) {
141
+ return tenant;
142
+ }
143
+ }
144
+ return null;
145
+ },
146
+
147
+ async create(input: CreateTenantInput): Promise<Tenant> {
148
+ const id = `tenant-${tenantIdCounter++}`;
149
+ const tenant: Tenant = {
150
+ id,
151
+ name: input.name,
152
+ type: input.type,
153
+ owner_id: input.owner_id,
154
+ metadata: input.metadata || {},
155
+ created_at: new Date(),
156
+ updated_at: new Date(),
157
+ };
158
+ tenants.set(id, tenant);
159
+
160
+ // Auto-create membership for owner
161
+ const membershipId = `membership-${membershipIdCounter++}`;
162
+ const membership: TenantMembership = {
163
+ id: membershipId,
164
+ tenant_id: id,
165
+ user_id: input.owner_id,
166
+ role: 'owner',
167
+ joined_at: new Date(),
168
+ };
169
+ memberships.set(membershipId, membership);
170
+
171
+ return tenant;
172
+ },
173
+
174
+ async update(id: string, input: UpdateTenantInput): Promise<Tenant | null> {
175
+ const tenant = tenants.get(id);
176
+ if (!tenant) return null;
177
+
178
+ const updated: Tenant = {
179
+ ...tenant,
180
+ ...input,
181
+ id: tenant.id, // Preserve ID
182
+ created_at: tenant.created_at, // Preserve created_at
183
+ updated_at: new Date(),
184
+ };
185
+ tenants.set(id, updated);
186
+ return updated;
187
+ },
188
+
189
+ async delete(id: string): Promise<boolean> {
190
+ // Delete all memberships for this tenant
191
+ for (const [key, membership] of memberships.entries()) {
192
+ if (membership.tenant_id === id) {
193
+ memberships.delete(key);
194
+ }
195
+ }
196
+ return tenants.delete(id);
197
+ },
198
+
199
+ async search(params: TenantSearchParams = {}): Promise<TenantListResponse> {
200
+ let result = Array.from(tenants.values());
201
+
202
+ if (params.query) {
203
+ const query = params.query.toLowerCase();
204
+ result = result.filter((t) => t.name.toLowerCase().includes(query));
205
+ }
206
+
207
+ if (params.type) {
208
+ result = result.filter((t) => t.type === params.type);
209
+ }
210
+
211
+ if (params.owner_id) {
212
+ result = result.filter((t) => t.owner_id === params.owner_id);
213
+ }
214
+
215
+ const sortBy = params.sortBy || 'created_at';
216
+ const sortOrder = params.sortOrder || 'desc';
217
+
218
+ result.sort((a, b) => {
219
+ const aVal = a[sortBy as keyof Tenant];
220
+ const bVal = b[sortBy as keyof Tenant];
221
+ if (aVal == null || bVal == null) return 0;
222
+ if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1;
223
+ if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1;
224
+ return 0;
225
+ });
226
+
227
+ const total = result.length;
228
+ const page = params.page || 1;
229
+ const limit = params.limit || 20;
230
+ const offset = (page - 1) * limit;
231
+ result = result.slice(offset, offset + limit);
232
+
233
+ return {
234
+ tenants: result,
235
+ total,
236
+ page,
237
+ limit,
238
+ totalPages: Math.ceil(total / limit),
239
+ };
240
+ },
241
+
242
+ async getTenantsForUser(userId: string): Promise<TenantWithMembership[]> {
243
+ const userMemberships = Array.from(memberships.values()).filter(
244
+ (m) => m.user_id === userId
245
+ );
246
+ const results: TenantWithMembership[] = [];
247
+ for (const membership of userMemberships) {
248
+ const tenant = tenants.get(membership.tenant_id);
249
+ if (tenant) {
250
+ results.push({
251
+ ...tenant,
252
+ user_role: membership.role,
253
+ membership,
254
+ });
255
+ }
256
+ }
257
+ return results;
258
+ },
259
+
260
+ async getTenantForUser(
261
+ tenantId: string,
262
+ userId: string
263
+ ): Promise<TenantWithMembership | null> {
264
+ const membership = await this.getMembership(tenantId, userId);
265
+ if (!membership) return null;
266
+
267
+ const tenant = tenants.get(tenantId);
268
+ if (!tenant) return null;
269
+
270
+ return {
271
+ ...tenant,
272
+ user_role: membership.role,
273
+ membership,
274
+ };
275
+ },
276
+
277
+ async getMembers(tenantId: string): Promise<TenantMembership[]> {
278
+ return Array.from(memberships.values()).filter((m) => m.tenant_id === tenantId);
279
+ },
280
+
281
+ async getMembership(tenantId: string, userId: string): Promise<TenantMembership | null> {
282
+ for (const membership of memberships.values()) {
283
+ if (membership.tenant_id === tenantId && membership.user_id === userId) {
284
+ return membership;
285
+ }
286
+ }
287
+ return null;
288
+ },
289
+
290
+ async addMember(input: CreateTenantMembershipInput): Promise<TenantMembership> {
291
+ const id = `membership-${membershipIdCounter++}`;
292
+ const membership: TenantMembership = {
293
+ id,
294
+ tenant_id: input.tenant_id,
295
+ user_id: input.user_id,
296
+ role: input.role,
297
+ joined_at: new Date(),
298
+ };
299
+ memberships.set(id, membership);
300
+ return membership;
301
+ },
302
+
303
+ async updateMember(
304
+ tenantId: string,
305
+ userId: string,
306
+ input: UpdateTenantMembershipInput
307
+ ): Promise<TenantMembership | null> {
308
+ for (const membership of memberships.values()) {
309
+ if (membership.tenant_id === tenantId && membership.user_id === userId) {
310
+ const updated: TenantMembership = {
311
+ ...membership,
312
+ ...input,
313
+ };
314
+ memberships.set(membership.id, updated);
315
+ return updated;
316
+ }
317
+ }
318
+ return null;
319
+ },
320
+
321
+ async removeMember(tenantId: string, userId: string): Promise<boolean> {
322
+ for (const [key, membership] of memberships.entries()) {
323
+ if (membership.tenant_id === tenantId && membership.user_id === userId) {
324
+ memberships.delete(key);
325
+ return true;
326
+ }
327
+ }
328
+ return false;
329
+ },
330
+
331
+ async shutdown(): Promise<void> {
332
+ logger.info('[InMemoryTenantStore] Shutdown');
333
+ },
334
+ };
335
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Tenant Stores Index
3
+ *
4
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
5
+ */
6
+
7
+ export { postgresTenantStore } from './postgres-store.js';
8
+ export {
9
+ createInMemoryTenantStore as inMemoryTenantStore,
10
+ DEMO_USER_ID,
11
+ type InMemoryStoreLogger,
12
+ type InMemoryTenantStoreOptions,
13
+ } from './in-memory-store.js';
@@ -23,58 +23,26 @@ import type {
23
23
  } from './types.js';
24
24
  import { getUserById } from '../users/users-plugin.js';
25
25
  import { getAuthenticatedUser, isAuthenticated } from '../auth/index.js';
26
+ import { hasPostgres, getPostgres } from '../postgres-plugin.js';
27
+ import { postgresTenantStore } from './stores/index.js';
26
28
 
27
29
  // Store instance and registry for helper access
28
30
  let currentStore: TenantStore | null = null;
29
31
  let currentRegistry: PluginRegistry | null = null;
30
32
 
31
33
  /**
32
- * Create the Tenants plugin
34
+ * Create the Tenants plugin with smart defaults
35
+ *
36
+ * Config is optional - plugin will use defaults and get dependencies from registry.
37
+ * Gracefully handles missing dependencies with clear log messages.
33
38
  */
34
- export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
35
- const debug = config.debug || false;
36
- const apiPrefix = config.apiPrefix || '/';
37
- const apiEnabled = config.apiEnabled !== false;
38
-
39
- function log(message: string, data?: Record<string, unknown>) {
40
- if (debug) {
41
- console.log(`[TenantsPlugin] ${message}`, data || '');
42
- }
43
- }
44
-
45
- /**
46
- * Helper to check if user has access to a tenant
47
- */
48
- async function canAccessTenant(userId: string, tenantId: string): Promise<boolean> {
49
- try {
50
- const membership = await config.store.getTenantForUser(tenantId, userId);
51
- return membership !== null;
52
- } catch {
53
- return false;
54
- }
55
- }
56
-
57
- /**
58
- * Helper to check if user has admin/owner role in a tenant
59
- */
60
- async function canManageTenant(userId: string, tenantId: string): Promise<boolean> {
61
- try {
62
- const membership = await config.store.getTenantForUser(tenantId, userId);
63
- return membership !== null && ['owner', 'admin'].includes(membership.user_role);
64
- } catch {
65
- return false;
66
- }
67
- }
68
-
69
- /**
70
- * Helper to check if user is owner of a tenant
71
- */
72
- async function isOwnerOfTenant(userId: string, tenantId: string): Promise<boolean> {
73
- try {
74
- const membership = await config.store.getTenantForUser(tenantId, userId);
75
- return membership !== null && membership.user_role === 'owner';
76
- } catch {
77
- return false;
39
+ export function createTenantsPlugin(config: Partial<TenantsPluginConfig> = {}): Plugin {
40
+ function log(message: string, data?: Record<string, unknown>, isError = false) {
41
+ const prefix = '[TenantsPlugin]';
42
+ if (isError) {
43
+ console.error(`${prefix} ${message}`, data || '');
44
+ } else if (config.debug) {
45
+ console.log(`${prefix} ${message}`, data || '');
78
46
  }
79
47
  }
80
48
 
@@ -84,14 +52,43 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
84
52
  version: '1.0.0',
85
53
 
86
54
  async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
55
+ const logger = registry.getLogger('tenants');
56
+
57
+ // Check for postgres in registry
58
+ if (!hasPostgres()) {
59
+ logger.warn('No Database! Tenants plugin disabled.');
60
+ registry.registerHealthCheck({
61
+ name: 'tenants-store',
62
+ type: 'custom',
63
+ check: async () => ({
64
+ healthy: false,
65
+ details: {
66
+ error: 'PostgreSQL not available',
67
+ state: 'disabled',
68
+ },
69
+ }),
70
+ });
71
+ return;
72
+ }
73
+
74
+ // Smart defaults - get dependencies from registry
75
+ const store = config.store ?? postgresTenantStore({
76
+ pool: () => getPostgres().getPool(),
77
+ autoCreateTables: true,
78
+ });
79
+
80
+ const debug = config.debug ?? false;
81
+ const apiPrefix = config.apiPrefix ?? '/tenants';
82
+ const apiEnabled = config.apiEnabled ?? true;
83
+
87
84
  log('Starting tenants plugin');
88
85
 
89
86
  // Initialize the store (creates tables if needed)
90
- await config.store.initialize();
87
+ await store.initialize();
91
88
  log('Tenants plugin migrations complete');
92
89
 
93
90
  // Store references for helper access
94
- currentStore = config.store;
91
+ currentStore = store;
95
92
  currentRegistry = registry;
96
93
 
97
94
  // Register health check
@@ -101,7 +98,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
101
98
  check: async () => {
102
99
  try {
103
100
  // Simple health check - try to search with limit 1
104
- await config.store.search({ limit: 1 });
101
+ await store.search({ limit: 1 });
105
102
  return { healthy: true };
106
103
  } catch {
107
104
  return { healthy: false };
@@ -109,6 +106,42 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
109
106
  },
110
107
  });
111
108
 
109
+ /**
110
+ * Helper to check if user has access to a tenant
111
+ */
112
+ async function canAccessTenant(userId: string, tenantId: string): Promise<boolean> {
113
+ try {
114
+ const membership = await store.getTenantForUser(tenantId, userId);
115
+ return membership !== null;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Helper to check if user has admin/owner role in a tenant
123
+ */
124
+ async function canManageTenant(userId: string, tenantId: string): Promise<boolean> {
125
+ try {
126
+ const membership = await store.getTenantForUser(tenantId, userId);
127
+ return membership !== null && ['owner', 'admin'].includes(membership.user_role);
128
+ } catch {
129
+ return false;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Helper to check if user is owner of a tenant
135
+ */
136
+ async function isOwnerOfTenant(userId: string, tenantId: string): Promise<boolean> {
137
+ try {
138
+ const membership = await store.getTenantForUser(tenantId, userId);
139
+ return membership !== null && membership.user_role === 'owner';
140
+ } catch {
141
+ return false;
142
+ }
143
+ }
144
+
112
145
  if (!apiEnabled) return;
113
146
 
114
147
  // ========================================================================
@@ -118,7 +151,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
118
151
  // List/Search tenants
119
152
  registry.addRoute({
120
153
  method: 'get',
121
- path: apiPrefix,
154
+ path: apiPrefix || '/',
122
155
  pluginId: 'tenants',
123
156
  handler: async (req: Request, res: Response) => {
124
157
  try {
@@ -140,7 +173,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
140
173
 
141
174
  // Users can only search their own tenants
142
175
  // Get all tenants user belongs to
143
- const userTenants = await config.store.getTenantsForUser(user.id);
176
+ const userTenants = await store.getTenantsForUser(user.id);
144
177
  res.json({
145
178
  tenants: userTenants,
146
179
  total: userTenants.length,
@@ -186,7 +219,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
186
219
  });
187
220
  }
188
221
 
189
- const tenant = await config.store.getById(req.params.id);
222
+ const tenant = await store.getById(req.params.id);
190
223
  if (!tenant) {
191
224
  return res.status(404).json({ error: 'Tenant not found' });
192
225
  }
@@ -201,7 +234,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
201
234
  // Create tenant
202
235
  registry.addRoute({
203
236
  method: 'post',
204
- path: apiPrefix,
237
+ path: apiPrefix || '/',
205
238
  pluginId: 'tenants',
206
239
  handler: async (req: Request, res: Response) => {
207
240
  try {
@@ -252,10 +285,10 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
252
285
  });
253
286
  }
254
287
 
255
- const tenant = await config.store.create(input);
288
+ const tenant = await store.create(input);
256
289
 
257
290
  // Automatically add creator as owner
258
- await config.store.addMember({
291
+ await store.addMember({
259
292
  tenant_id: tenant.id,
260
293
  user_id: user.id,
261
294
  role: 'owner',
@@ -306,7 +339,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
306
339
  metadata: req.body.metadata,
307
340
  };
308
341
 
309
- const tenant = await config.store.update(req.params.id, input);
342
+ const tenant = await store.update(req.params.id, input);
310
343
  if (!tenant) {
311
344
  return res.status(404).json({ error: 'Tenant not found' });
312
345
  }
@@ -351,7 +384,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
351
384
  });
352
385
  }
353
386
 
354
- const deleted = await config.store.delete(req.params.id);
387
+ const deleted = await store.delete(req.params.id);
355
388
  if (!deleted) {
356
389
  return res.status(404).json({ error: 'Tenant not found' });
357
390
  }
@@ -400,7 +433,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
400
433
  });
401
434
  }
402
435
 
403
- const tenants = await config.store.getTenantsForUser(req.params.userId);
436
+ const tenants = await store.getTenantsForUser(req.params.userId);
404
437
  res.json({ tenants, total: tenants.length });
405
438
  } catch (error) {
406
439
  console.error('[TenantsPlugin] Get user tenants error:', error);
@@ -444,7 +477,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
444
477
  });
445
478
  }
446
479
 
447
- const members = await config.store.getMembers(req.params.tenantId);
480
+ const members = await store.getMembers(req.params.tenantId);
448
481
  res.json({ members, total: members.length });
449
482
  } catch (error) {
450
483
  console.error('[TenantsPlugin] Get members error:', error);
@@ -506,7 +539,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
506
539
  });
507
540
  }
508
541
 
509
- const membership = await config.store.addMember(input);
542
+ const membership = await store.addMember(input);
510
543
  log('Member added to tenant', {
511
544
  tenantId: input.tenant_id,
512
545
  userId: input.user_id,
@@ -568,7 +601,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
568
601
  });
569
602
  }
570
603
 
571
- const membership = await config.store.updateMember(req.params.tenantId, req.params.userId, input);
604
+ const membership = await store.updateMember(req.params.tenantId, req.params.userId, input);
572
605
  if (!membership) {
573
606
  return res.status(404).json({ error: 'Membership not found' });
574
607
  }
@@ -618,7 +651,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
618
651
  });
619
652
  }
620
653
 
621
- const deleted = await config.store.removeMember(req.params.tenantId, req.params.userId);
654
+ const deleted = await store.removeMember(req.params.tenantId, req.params.userId);
622
655
  if (!deleted) {
623
656
  return res.status(404).json({ error: 'Membership not found' });
624
657
  }
@@ -641,7 +674,9 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
641
674
 
642
675
  async onStop(): Promise<void> {
643
676
  log('Stopping tenants plugin');
644
- await config.store.shutdown();
677
+ if (currentStore) {
678
+ await currentStore.shutdown();
679
+ }
645
680
  currentStore = null;
646
681
  currentRegistry = null;
647
682
  },
@@ -236,10 +236,16 @@ export interface PostgresTenantStoreConfig {
236
236
 
237
237
  /**
238
238
  * Tenants plugin configuration
239
+ *
240
+ * All properties are optional - plugin will use smart defaults:
241
+ * - store: Postgres tenant store using registry's postgres instance
242
+ * - apiPrefix: '/tenants'
243
+ * - apiEnabled: true
244
+ * - debug: false
239
245
  */
240
246
  export interface TenantsPluginConfig {
241
- /** Tenant store implementation */
242
- store: TenantStore;
247
+ /** Tenant store implementation (default: postgres tenant store from registry) */
248
+ store?: TenantStore;
243
249
  /** API route prefix (default: '/tenants') */
244
250
  apiPrefix?: string;
245
251
  /** Enable API endpoints (default: true) */
@@ -38,8 +38,6 @@ export type {
38
38
  // Stores
39
39
  export { postgresUsageStore } from './stores/index.js';
40
40
 
41
- // UI Components
42
- export { UsageStatusWidget } from './UsageStatusWidget.js';
43
- export type { UsageStatusWidgetProps } from './UsageStatusWidget.js';
44
- export { UsageManagementPage } from './UsageManagementPage.js';
45
- export type { UsageManagementPageProps } from './UsageManagementPage.js';
41
+ // UI Components are exported from main package index (@qwickapps/server)
42
+ // Do NOT export here to avoid loading UI dependencies when importing plugins
43
+