@qwickapps/server 1.7.1 → 1.8.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 (259) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/src/core/control-panel.js +5 -5
  3. package/dist/src/core/control-panel.js.map +1 -1
  4. package/dist/src/core/gateway.d.ts.map +1 -1
  5. package/dist/src/core/gateway.js +117 -15
  6. package/dist/src/core/gateway.js.map +1 -1
  7. package/dist/src/core/plugin-registry.d.ts +70 -0
  8. package/dist/src/core/plugin-registry.d.ts.map +1 -1
  9. package/dist/src/core/plugin-registry.js +94 -0
  10. package/dist/src/core/plugin-registry.js.map +1 -1
  11. package/dist/src/index.d.ts +1 -1
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/plugins/api-keys/api-keys-plugin.d.ts +5 -2
  14. package/dist/src/plugins/api-keys/api-keys-plugin.d.ts.map +1 -1
  15. package/dist/src/plugins/api-keys/api-keys-plugin.js +113 -19
  16. package/dist/src/plugins/api-keys/api-keys-plugin.js.map +1 -1
  17. package/dist/src/plugins/api-keys/index.d.ts +1 -5
  18. package/dist/src/plugins/api-keys/index.d.ts.map +1 -1
  19. package/dist/src/plugins/api-keys/index.js +2 -3
  20. package/dist/src/plugins/api-keys/index.js.map +1 -1
  21. package/dist/src/plugins/api-keys/stores/postgres-store.d.ts.map +1 -1
  22. package/dist/src/plugins/api-keys/stores/postgres-store.js +83 -65
  23. package/dist/src/plugins/api-keys/stores/postgres-store.js.map +1 -1
  24. package/dist/src/plugins/api-keys/types.d.ts +22 -4
  25. package/dist/src/plugins/api-keys/types.d.ts.map +1 -1
  26. package/dist/src/plugins/api-keys/types.js.map +1 -1
  27. package/dist/src/plugins/auth/index.d.ts +0 -4
  28. package/dist/src/plugins/auth/index.d.ts.map +1 -1
  29. package/dist/src/plugins/auth/index.js +2 -3
  30. package/dist/src/plugins/auth/index.js.map +1 -1
  31. package/dist/src/plugins/bans/bans-plugin.d.ts +5 -2
  32. package/dist/src/plugins/bans/bans-plugin.d.ts.map +1 -1
  33. package/dist/src/plugins/bans/bans-plugin.js +71 -25
  34. package/dist/src/plugins/bans/bans-plugin.js.map +1 -1
  35. package/dist/src/plugins/bans/index.d.ts +0 -4
  36. package/dist/src/plugins/bans/index.d.ts.map +1 -1
  37. package/dist/src/plugins/bans/index.js +2 -3
  38. package/dist/src/plugins/bans/index.js.map +1 -1
  39. package/dist/src/plugins/bans/types.d.ts +13 -6
  40. package/dist/src/plugins/bans/types.d.ts.map +1 -1
  41. package/dist/src/plugins/devices/devices-plugin.d.ts +5 -2
  42. package/dist/src/plugins/devices/devices-plugin.d.ts.map +1 -1
  43. package/dist/src/plugins/devices/devices-plugin.js +62 -26
  44. package/dist/src/plugins/devices/devices-plugin.js.map +1 -1
  45. package/dist/src/plugins/devices/index.d.ts +0 -4
  46. package/dist/src/plugins/devices/index.d.ts.map +1 -1
  47. package/dist/src/plugins/devices/index.js +2 -3
  48. package/dist/src/plugins/devices/index.js.map +1 -1
  49. package/dist/src/plugins/diagnostics-plugin.d.ts.map +1 -1
  50. package/dist/src/plugins/diagnostics-plugin.js +73 -0
  51. package/dist/src/plugins/diagnostics-plugin.js.map +1 -1
  52. package/dist/src/plugins/entitlements/entitlements-plugin.d.ts +5 -2
  53. package/dist/src/plugins/entitlements/entitlements-plugin.d.ts.map +1 -1
  54. package/dist/src/plugins/entitlements/entitlements-plugin.js +78 -41
  55. package/dist/src/plugins/entitlements/entitlements-plugin.js.map +1 -1
  56. package/dist/src/plugins/entitlements/index.d.ts +0 -4
  57. package/dist/src/plugins/entitlements/index.d.ts.map +1 -1
  58. package/dist/src/plugins/entitlements/index.js +2 -3
  59. package/dist/src/plugins/entitlements/index.js.map +1 -1
  60. package/dist/src/plugins/entitlements/types.d.ts +9 -2
  61. package/dist/src/plugins/entitlements/types.d.ts.map +1 -1
  62. package/dist/src/plugins/index.d.ts +1 -1
  63. package/dist/src/plugins/index.d.ts.map +1 -1
  64. package/dist/src/plugins/maintenance/SeedExecutor.d.ts +2 -0
  65. package/dist/src/plugins/maintenance/SeedExecutor.d.ts.map +1 -1
  66. package/dist/src/plugins/maintenance/SeedExecutor.js +6 -2
  67. package/dist/src/plugins/maintenance/SeedExecutor.js.map +1 -1
  68. package/dist/src/plugins/maintenance/SeedList.d.ts +2 -2
  69. package/dist/src/plugins/maintenance/SeedList.d.ts.map +1 -1
  70. package/dist/src/plugins/maintenance/SeedList.js +39 -14
  71. package/dist/src/plugins/maintenance/SeedList.js.map +1 -1
  72. package/dist/src/plugins/maintenance/SeedManagementPage.d.ts +1 -1
  73. package/dist/src/plugins/maintenance/SeedManagementPage.d.ts.map +1 -1
  74. package/dist/src/plugins/maintenance/SeedManagementPage.js +9 -5
  75. package/dist/src/plugins/maintenance/SeedManagementPage.js.map +1 -1
  76. package/dist/src/plugins/maintenance/seed-executor.d.ts +6 -4
  77. package/dist/src/plugins/maintenance/seed-executor.d.ts.map +1 -1
  78. package/dist/src/plugins/maintenance/seed-executor.js +53 -17
  79. package/dist/src/plugins/maintenance/seed-executor.js.map +1 -1
  80. package/dist/src/plugins/maintenance-plugin.d.ts +24 -0
  81. package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
  82. package/dist/src/plugins/maintenance-plugin.js +222 -34
  83. package/dist/src/plugins/maintenance-plugin.js.map +1 -1
  84. package/dist/src/plugins/notifications/index.d.ts +0 -4
  85. package/dist/src/plugins/notifications/index.d.ts.map +1 -1
  86. package/dist/src/plugins/notifications/index.js +2 -3
  87. package/dist/src/plugins/notifications/index.js.map +1 -1
  88. package/dist/src/plugins/notifications/notifications-plugin.d.ts +5 -2
  89. package/dist/src/plugins/notifications/notifications-plugin.d.ts.map +1 -1
  90. package/dist/src/plugins/notifications/notifications-plugin.js +45 -13
  91. package/dist/src/plugins/notifications/notifications-plugin.js.map +1 -1
  92. package/dist/src/plugins/parental/index.d.ts +0 -4
  93. package/dist/src/plugins/parental/index.d.ts.map +1 -1
  94. package/dist/src/plugins/parental/index.js +2 -3
  95. package/dist/src/plugins/parental/index.js.map +1 -1
  96. package/dist/src/plugins/parental/parental-plugin.d.ts +5 -2
  97. package/dist/src/plugins/parental/parental-plugin.d.ts.map +1 -1
  98. package/dist/src/plugins/parental/parental-plugin.js +60 -24
  99. package/dist/src/plugins/parental/parental-plugin.js.map +1 -1
  100. package/dist/src/plugins/postgres-plugin.d.ts +12 -0
  101. package/dist/src/plugins/postgres-plugin.d.ts.map +1 -1
  102. package/dist/src/plugins/postgres-plugin.js +319 -5
  103. package/dist/src/plugins/postgres-plugin.js.map +1 -1
  104. package/dist/src/plugins/preferences/index.d.ts +0 -4
  105. package/dist/src/plugins/preferences/index.d.ts.map +1 -1
  106. package/dist/src/plugins/preferences/index.js +2 -3
  107. package/dist/src/plugins/preferences/index.js.map +1 -1
  108. package/dist/src/plugins/preferences/preferences-plugin.d.ts +5 -2
  109. package/dist/src/plugins/preferences/preferences-plugin.d.ts.map +1 -1
  110. package/dist/src/plugins/preferences/preferences-plugin.js +63 -19
  111. package/dist/src/plugins/preferences/preferences-plugin.js.map +1 -1
  112. package/dist/src/plugins/profiles/index.d.ts +0 -4
  113. package/dist/src/plugins/profiles/index.d.ts.map +1 -1
  114. package/dist/src/plugins/profiles/index.js +2 -3
  115. package/dist/src/plugins/profiles/index.js.map +1 -1
  116. package/dist/src/plugins/profiles/profiles-plugin.d.ts +5 -2
  117. package/dist/src/plugins/profiles/profiles-plugin.d.ts.map +1 -1
  118. package/dist/src/plugins/profiles/profiles-plugin.js +60 -26
  119. package/dist/src/plugins/profiles/profiles-plugin.js.map +1 -1
  120. package/dist/src/plugins/profiles/types.d.ts +9 -2
  121. package/dist/src/plugins/profiles/types.d.ts.map +1 -1
  122. package/dist/src/plugins/qwickbrain/index.d.ts +0 -4
  123. package/dist/src/plugins/qwickbrain/index.d.ts.map +1 -1
  124. package/dist/src/plugins/qwickbrain/index.js +2 -3
  125. package/dist/src/plugins/qwickbrain/index.js.map +1 -1
  126. package/dist/src/plugins/qwickbrain/qwickbrain-plugin.d.ts.map +1 -1
  127. package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js +117 -0
  128. package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js.map +1 -1
  129. package/dist/src/plugins/rate-limit/index.d.ts +0 -4
  130. package/dist/src/plugins/rate-limit/index.d.ts.map +1 -1
  131. package/dist/src/plugins/rate-limit/index.js +2 -3
  132. package/dist/src/plugins/rate-limit/index.js.map +1 -1
  133. package/dist/src/plugins/subscriptions/index.d.ts +0 -4
  134. package/dist/src/plugins/subscriptions/index.d.ts.map +1 -1
  135. package/dist/src/plugins/subscriptions/index.js +2 -3
  136. package/dist/src/plugins/subscriptions/index.js.map +1 -1
  137. package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts +5 -2
  138. package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -1
  139. package/dist/src/plugins/subscriptions/subscriptions-plugin.js +63 -29
  140. package/dist/src/plugins/subscriptions/subscriptions-plugin.js.map +1 -1
  141. package/dist/src/plugins/subscriptions/types.d.ts +8 -2
  142. package/dist/src/plugins/subscriptions/types.d.ts.map +1 -1
  143. package/dist/src/plugins/tenants/tenants-plugin.d.ts +5 -2
  144. package/dist/src/plugins/tenants/tenants-plugin.d.ts.map +1 -1
  145. package/dist/src/plugins/tenants/tenants-plugin.js +91 -58
  146. package/dist/src/plugins/tenants/tenants-plugin.js.map +1 -1
  147. package/dist/src/plugins/tenants/types.d.ts +8 -2
  148. package/dist/src/plugins/tenants/types.d.ts.map +1 -1
  149. package/dist/src/plugins/usage/index.d.ts +0 -4
  150. package/dist/src/plugins/usage/index.d.ts.map +1 -1
  151. package/dist/src/plugins/usage/index.js +2 -3
  152. package/dist/src/plugins/usage/index.js.map +1 -1
  153. package/dist/src/plugins/usage/usage-plugin.d.ts +5 -2
  154. package/dist/src/plugins/usage/usage-plugin.d.ts.map +1 -1
  155. package/dist/src/plugins/usage/usage-plugin.js +57 -23
  156. package/dist/src/plugins/usage/usage-plugin.js.map +1 -1
  157. package/dist/src/plugins/users/types.d.ts +7 -2
  158. package/dist/src/plugins/users/types.d.ts.map +1 -1
  159. package/dist/src/plugins/users/users-plugin.d.ts +5 -2
  160. package/dist/src/plugins/users/users-plugin.d.ts.map +1 -1
  161. package/dist/src/plugins/users/users-plugin.js +56 -23
  162. package/dist/src/plugins/users/users-plugin.js.map +1 -1
  163. package/dist/ui/src/components/ControlPanelApp.d.ts.map +1 -1
  164. package/dist/ui/src/components/ControlPanelApp.js +4 -3
  165. package/dist/ui/src/components/ControlPanelApp.js.map +1 -1
  166. package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
  167. package/dist/ui/src/dashboard/builtInWidgets.js +3 -1
  168. package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
  169. package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.d.ts.map +1 -1
  170. package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js +17 -4
  171. package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js.map +1 -1
  172. package/dist/ui/src/dashboard/widgets/CMSStatusWidget.d.ts.map +1 -1
  173. package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js +5 -1
  174. package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js.map +1 -1
  175. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts.map +1 -1
  176. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +4 -2
  177. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -1
  178. package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts +12 -0
  179. package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts.map +1 -0
  180. package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js +174 -0
  181. package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js.map +1 -0
  182. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts.map +1 -1
  183. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +6 -3
  184. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -1
  185. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts +1 -1
  186. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts.map +1 -1
  187. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +256 -16
  188. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -1
  189. package/dist/ui/src/dashboard/widgets/index.d.ts +1 -0
  190. package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
  191. package/dist/ui/src/dashboard/widgets/index.js +1 -0
  192. package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
  193. package/dist-ui/assets/index-BkGp7ZKd.js +529 -0
  194. package/dist-ui/assets/index-BkGp7ZKd.js.map +1 -0
  195. package/dist-ui/index.html +1 -1
  196. package/dist-ui-lib/index.js +3735 -3187
  197. package/dist-ui-lib/index.js.map +1 -1
  198. package/dist-ui-lib/src/dashboard/widgets/DatabaseOperationsWidget.d.ts +11 -0
  199. package/dist-ui-lib/src/dashboard/widgets/SeedManagementWidget.d.ts +1 -1
  200. package/dist-ui-lib/src/dashboard/widgets/index.d.ts +1 -0
  201. package/package.json +8 -5
  202. package/src/core/control-panel.ts +5 -5
  203. package/src/core/gateway.ts +135 -15
  204. package/src/core/plugin-registry.ts +171 -0
  205. package/src/index.ts +2 -0
  206. package/src/plugins/api-keys/api-keys-plugin.ts +121 -20
  207. package/src/plugins/api-keys/index.ts +3 -5
  208. package/src/plugins/api-keys/stores/postgres-store.ts +90 -67
  209. package/src/plugins/api-keys/types.ts +23 -4
  210. package/src/plugins/auth/index.ts +3 -5
  211. package/src/plugins/bans/bans-plugin.ts +71 -26
  212. package/src/plugins/bans/index.ts +3 -5
  213. package/src/plugins/bans/types.ts +13 -6
  214. package/src/plugins/devices/devices-plugin.ts +62 -27
  215. package/src/plugins/devices/index.ts +3 -5
  216. package/src/plugins/diagnostics-plugin.ts +77 -0
  217. package/src/plugins/entitlements/entitlements-plugin.ts +81 -43
  218. package/src/plugins/entitlements/index.ts +3 -5
  219. package/src/plugins/entitlements/types.ts +9 -2
  220. package/src/plugins/index.ts +1 -1
  221. package/src/plugins/maintenance/SeedExecutor.tsx +9 -1
  222. package/src/plugins/maintenance/SeedList.tsx +85 -38
  223. package/src/plugins/maintenance/SeedManagementPage.tsx +10 -4
  224. package/src/plugins/maintenance/seed-executor.ts +56 -17
  225. package/src/plugins/maintenance-plugin.ts +267 -36
  226. package/src/plugins/notifications/index.ts +3 -5
  227. package/src/plugins/notifications/notifications-plugin.ts +48 -19
  228. package/src/plugins/parental/index.ts +3 -5
  229. package/src/plugins/parental/parental-plugin.ts +63 -25
  230. package/src/plugins/postgres-plugin.ts +410 -5
  231. package/src/plugins/preferences/index.ts +3 -5
  232. package/src/plugins/preferences/preferences-plugin.ts +66 -20
  233. package/src/plugins/profiles/index.ts +3 -5
  234. package/src/plugins/profiles/profiles-plugin.ts +60 -27
  235. package/src/plugins/profiles/types.ts +9 -2
  236. package/src/plugins/qwickbrain/index.ts +3 -5
  237. package/src/plugins/qwickbrain/qwickbrain-plugin.ts +135 -0
  238. package/src/plugins/rate-limit/index.ts +3 -5
  239. package/src/plugins/subscriptions/index.ts +3 -5
  240. package/src/plugins/subscriptions/subscriptions-plugin.ts +63 -30
  241. package/src/plugins/subscriptions/types.ts +8 -2
  242. package/src/plugins/tenants/tenants-plugin.ts +95 -60
  243. package/src/plugins/tenants/types.ts +8 -2
  244. package/src/plugins/usage/index.ts +3 -5
  245. package/src/plugins/usage/usage-plugin.ts +60 -26
  246. package/src/plugins/users/types.ts +7 -2
  247. package/src/plugins/users/users-plugin.ts +56 -24
  248. package/ui/src/App.tsx +3 -3
  249. package/ui/src/components/ControlPanelApp.tsx +4 -3
  250. package/ui/src/dashboard/builtInWidgets.tsx +3 -0
  251. package/ui/src/dashboard/widgets/CMSMaintenanceWidget.tsx +17 -4
  252. package/ui/src/dashboard/widgets/CMSStatusWidget.tsx +5 -1
  253. package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +4 -2
  254. package/ui/src/dashboard/widgets/DatabaseOperationsWidget.tsx +410 -0
  255. package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +6 -3
  256. package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +533 -49
  257. package/ui/src/dashboard/widgets/index.ts +1 -0
  258. package/dist-ui/assets/index-8y0jDGcd.js +0 -528
  259. package/dist-ui/assets/index-8y0jDGcd.js.map +0 -1
@@ -16,8 +16,9 @@ import type {
16
16
  PreferencesStore,
17
17
  } from './types.js';
18
18
  import type { AuthenticatedRequest } from '../auth/types.js';
19
- import { deepMerge } from './stores/postgres-store.js';
19
+ import { deepMerge, postgresPreferencesStore } from './stores/postgres-store.js';
20
20
  import { MAX_PREFERENCES_SIZE, MAX_NESTING_DEPTH } from './types.js';
21
+ import { hasPostgres, getPostgres } from '../postgres-plugin.js';
21
22
 
22
23
  /**
23
24
  * Check if an object exceeds maximum nesting depth
@@ -38,17 +39,18 @@ let currentStore: PreferencesStore | null = null;
38
39
  let pluginDefaults: Record<string, unknown> = {};
39
40
 
40
41
  /**
41
- * Create the Preferences plugin
42
+ * Create the Preferences plugin with smart defaults
43
+ *
44
+ * Config is optional - plugin will use defaults and get dependencies from registry.
45
+ * Gracefully handles missing dependencies with clear log messages.
42
46
  */
43
- export function createPreferencesPlugin(config: PreferencesPluginConfig): Plugin {
44
- const debug = config.debug || false;
45
- // Routes are mounted under /api by the control panel, so don't include /api in prefix
46
- const apiPrefix = config.api?.prefix || '/'; // Framework adds /preferences prefix automatically
47
- const apiEnabled = config.api?.enabled !== false;
48
-
49
- function log(message: string, data?: Record<string, unknown>) {
50
- if (debug) {
51
- console.log(`[PreferencesPlugin] ${message}`, data || '');
47
+ export function createPreferencesPlugin(config: Partial<PreferencesPluginConfig> = {}): Plugin {
48
+ function log(message: string, data?: Record<string, unknown>, isError = false) {
49
+ const prefix = '[PreferencesPlugin]';
50
+ if (isError) {
51
+ console.error(`${prefix} ${message}`, data || '');
52
+ } else if (config.debug) {
53
+ console.log(`${prefix} ${message}`, data || '');
52
54
  }
53
55
  }
54
56
 
@@ -58,20 +60,62 @@ export function createPreferencesPlugin(config: PreferencesPluginConfig): Plugin
58
60
  version: '1.0.0',
59
61
 
60
62
  async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
61
- log('Starting preferences plugin');
63
+ const logger = registry.getLogger('preferences');
62
64
 
63
65
  // Check for users plugin dependency
64
66
  if (!registry.hasPlugin('users')) {
65
- throw new Error('Preferences plugin requires Users plugin to be loaded first');
67
+ logger.warn('Users plugin not loaded! Preferences plugin disabled.');
68
+ registry.registerHealthCheck({
69
+ name: 'preferences-store',
70
+ type: 'custom',
71
+ check: async () => ({
72
+ healthy: false,
73
+ details: {
74
+ error: 'Users plugin not available',
75
+ state: 'disabled',
76
+ },
77
+ }),
78
+ });
79
+ return;
80
+ }
81
+
82
+ // Check for postgres in registry
83
+ if (!hasPostgres()) {
84
+ logger.warn('No Database! Preferences plugin disabled.');
85
+ registry.registerHealthCheck({
86
+ name: 'preferences-store',
87
+ type: 'custom',
88
+ check: async () => ({
89
+ healthy: false,
90
+ details: {
91
+ error: 'PostgreSQL not available',
92
+ state: 'disabled',
93
+ },
94
+ }),
95
+ });
96
+ return;
66
97
  }
67
98
 
99
+ // Smart defaults - get dependencies from registry
100
+ const store = config.store ?? postgresPreferencesStore({
101
+ pool: () => getPostgres().getPool(),
102
+ autoCreateTables: true,
103
+ });
104
+
105
+ const debug = config.debug ?? false;
106
+ const apiPrefix = config.api?.prefix ?? '/'; // Framework adds /preferences prefix automatically
107
+ const apiEnabled = config.api?.enabled ?? true;
108
+ const defaults = config.defaults ?? {};
109
+
110
+ log('Starting preferences plugin');
111
+
68
112
  // Initialize the store (creates tables and RLS policies if needed)
69
- await config.store.initialize();
113
+ await store.initialize();
70
114
  log('Preferences plugin migrations complete');
71
115
 
72
116
  // Store references for helper access
73
- currentStore = config.store;
74
- pluginDefaults = config.defaults || {};
117
+ currentStore = store;
118
+ pluginDefaults = defaults;
75
119
 
76
120
  // Register health check
77
121
  registry.registerHealthCheck({
@@ -105,7 +149,7 @@ export function createPreferencesPlugin(config: PreferencesPluginConfig): Plugin
105
149
  return res.status(401).json({ error: 'Authentication required' });
106
150
  }
107
151
 
108
- const stored = await config.store.get(userId);
152
+ const stored = await store.get(userId);
109
153
 
110
154
  // Merge with defaults (defaults as base, stored values override)
111
155
  const preferences = stored
@@ -153,7 +197,7 @@ export function createPreferencesPlugin(config: PreferencesPluginConfig): Plugin
153
197
  return res.status(400).json({ error: 'Preferences object too deeply nested (max 10 levels)' });
154
198
  }
155
199
 
156
- const updated = await config.store.update(userId, newPreferences);
200
+ const updated = await store.update(userId, newPreferences);
157
201
 
158
202
  // Merge with defaults for response
159
203
  const preferences = deepMerge(pluginDefaults, updated);
@@ -183,7 +227,7 @@ export function createPreferencesPlugin(config: PreferencesPluginConfig): Plugin
183
227
  return res.status(401).json({ error: 'Authentication required' });
184
228
  }
185
229
 
186
- await config.store.delete(userId);
230
+ await store.delete(userId);
187
231
 
188
232
  // Return 204 No Content (idempotent - success even if no row existed)
189
233
  res.status(204).send();
@@ -208,7 +252,9 @@ export function createPreferencesPlugin(config: PreferencesPluginConfig): Plugin
208
252
 
209
253
  async onStop(): Promise<void> {
210
254
  log('Stopping preferences plugin');
211
- await config.store.shutdown();
255
+ if (currentStore) {
256
+ await currentStore.shutdown();
257
+ }
212
258
  currentStore = null;
213
259
  pluginDefaults = {};
214
260
  log('Preferences plugin stopped');
@@ -48,8 +48,6 @@ export type {
48
48
  // Stores
49
49
  export { postgresProfileStore } from './stores/index.js';
50
50
 
51
- // UI Components
52
- export { ProfilesStatusWidget } from './ProfilesStatusWidget.js';
53
- export type { ProfilesStatusWidgetProps } from './ProfilesStatusWidget.js';
54
- export { ProfilesManagementPage } from './ProfilesManagementPage.js';
55
- export type { ProfilesManagementPageProps } from './ProfilesManagementPage.js';
51
+ // UI Components are exported from main package index (@qwickapps/server)
52
+ // Do NOT export here to avoid loading UI dependencies when importing plugins
53
+
@@ -20,6 +20,8 @@ import type {
20
20
  ContentFilterLevel,
21
21
  TimeRestrictionResult,
22
22
  } from './types.js';
23
+ import { hasPostgres, getPostgres } from '../postgres-plugin.js';
24
+ import { postgresProfileStore } from './stores/index.js';
23
25
 
24
26
  // Store instance for helper access
25
27
  let currentStore: ProfileStore | null = null;
@@ -66,17 +68,18 @@ function isWithinAllowedHours(start?: string, end?: string): boolean {
66
68
  }
67
69
 
68
70
  /**
69
- * Create the Profiles plugin
71
+ * Create the Profiles plugin with smart defaults
72
+ *
73
+ * Config is optional - plugin will use defaults and get dependencies from registry.
74
+ * Gracefully handles missing dependencies with clear log messages.
70
75
  */
71
- export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
72
- const debug = config.debug || false;
73
- const maxProfilesPerUser = config.maxProfilesPerUser || 10;
74
- const defaultFilterLevel = config.defaultFilterLevel || 'moderate';
75
- const apiPrefix = config.api?.prefix || '/'; // Framework adds /profiles prefix automatically
76
-
77
- function log(message: string, data?: Record<string, unknown>) {
78
- if (debug) {
79
- console.log(`[ProfilesPlugin] ${message}`, data || '');
76
+ export function createProfilesPlugin(config: Partial<ProfilesPluginConfig> = {}): Plugin {
77
+ function log(message: string, data?: Record<string, unknown>, isError = false) {
78
+ const prefix = '[ProfilesPlugin]';
79
+ if (isError) {
80
+ console.error(`${prefix} ${message}`, data || '');
81
+ } else if (config.debug) {
82
+ console.log(`${prefix} ${message}`, data || '');
80
83
  }
81
84
  }
82
85
 
@@ -86,15 +89,45 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
86
89
  version: '1.0.0',
87
90
 
88
91
  async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
92
+ const logger = registry.getLogger('profiles');
93
+
94
+ // Check for postgres in registry
95
+ if (!hasPostgres()) {
96
+ logger.warn('No Database! Profiles plugin disabled.');
97
+ registry.registerHealthCheck({
98
+ name: 'profiles-store',
99
+ type: 'custom',
100
+ check: async () => ({
101
+ healthy: false,
102
+ details: {
103
+ error: 'PostgreSQL not available',
104
+ state: 'disabled',
105
+ },
106
+ }),
107
+ });
108
+ return;
109
+ }
110
+
111
+ // Smart defaults - get dependencies from registry
112
+ const store = config.store ?? postgresProfileStore({
113
+ pool: () => getPostgres().getPool(),
114
+ autoCreateTables: true,
115
+ });
116
+
117
+ const debug = config.debug ?? false;
118
+ const maxProfilesPerUser = config.maxProfilesPerUser ?? 10;
119
+ const defaultFilterLevel = config.defaultFilterLevel ?? 'moderate';
120
+ const apiPrefix = config.api?.prefix ?? '/profiles';
121
+
89
122
  log('Starting profiles plugin');
90
123
 
91
124
  // Initialize the store (creates tables if needed)
92
- await config.store.initialize();
125
+ await store.initialize();
93
126
  log('Profiles plugin migrations complete');
94
127
 
95
128
  // Store references for helper access
96
- currentStore = config.store;
97
- currentConfig = config;
129
+ currentStore = store;
130
+ currentConfig = { ...config, store, debug, maxProfilesPerUser, defaultFilterLevel };
98
131
 
99
132
  // Register health check
100
133
  registry.registerHealthCheck({
@@ -102,7 +135,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
102
135
  type: 'custom',
103
136
  check: async () => {
104
137
  try {
105
- await config.store.search({ limit: 1 });
138
+ await store.search({ limit: 1 });
106
139
  return {
107
140
  healthy: true,
108
141
  details: {
@@ -137,7 +170,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
137
170
  sortOrder: (req.query.sortOrder as ProfileSearchParams['sortOrder']) || 'desc',
138
171
  };
139
172
 
140
- const result = await config.store.search(params);
173
+ const result = await store.search(params);
141
174
  res.json(result);
142
175
  } catch (error) {
143
176
  console.error('[ProfilesPlugin] Search error:', error);
@@ -153,7 +186,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
153
186
  pluginId: 'profiles',
154
187
  handler: async (req: Request, res: Response) => {
155
188
  try {
156
- const profile = await config.store.getById(req.params.id);
189
+ const profile = await store.getById(req.params.id);
157
190
  if (!profile) {
158
191
  return res.status(404).json({ error: 'Profile not found' });
159
192
  }
@@ -196,14 +229,14 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
196
229
  }
197
230
 
198
231
  // Check profile limit
199
- const currentCount = await config.store.getProfileCount(input.user_id);
232
+ const currentCount = await store.getProfileCount(input.user_id);
200
233
  if (currentCount >= maxProfilesPerUser) {
201
234
  return res.status(400).json({
202
235
  error: `Maximum profiles (${maxProfilesPerUser}) reached for this user`,
203
236
  });
204
237
  }
205
238
 
206
- const profile = await config.store.create(input);
239
+ const profile = await store.create(input);
207
240
  res.status(201).json(profile);
208
241
  } catch (error) {
209
242
  console.error('[ProfilesPlugin] Create profile error:', error);
@@ -235,7 +268,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
235
268
  metadata: req.body.metadata,
236
269
  };
237
270
 
238
- const profile = await config.store.update(req.params.id, input);
271
+ const profile = await store.update(req.params.id, input);
239
272
  if (!profile) {
240
273
  return res.status(404).json({ error: 'Profile not found' });
241
274
  }
@@ -254,7 +287,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
254
287
  pluginId: 'profiles',
255
288
  handler: async (req: Request, res: Response) => {
256
289
  try {
257
- const deleted = await config.store.delete(req.params.id);
290
+ const deleted = await store.delete(req.params.id);
258
291
  if (!deleted) {
259
292
  return res.status(404).json({ error: 'Profile not found' });
260
293
  }
@@ -273,7 +306,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
273
306
  pluginId: 'profiles',
274
307
  handler: async (req: Request, res: Response) => {
275
308
  try {
276
- const profiles = await config.store.listByUser(req.params.userId);
309
+ const profiles = await store.listByUser(req.params.userId);
277
310
  res.json({ profiles });
278
311
  } catch (error) {
279
312
  console.error('[ProfilesPlugin] List user profiles error:', error);
@@ -289,7 +322,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
289
322
  pluginId: 'profiles',
290
323
  handler: async (req: Request, res: Response) => {
291
324
  try {
292
- const profile = await config.store.getDefaultProfile(req.params.userId);
325
+ const profile = await store.getDefaultProfile(req.params.userId);
293
326
  if (!profile) {
294
327
  return res.status(404).json({ error: 'No default profile found' });
295
328
  }
@@ -308,17 +341,17 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
308
341
  pluginId: 'profiles',
309
342
  handler: async (req: Request, res: Response) => {
310
343
  try {
311
- const profile = await config.store.getById(req.params.id);
344
+ const profile = await store.getById(req.params.id);
312
345
  if (!profile) {
313
346
  return res.status(404).json({ error: 'Profile not found' });
314
347
  }
315
348
 
316
- const success = await config.store.setDefaultProfile(req.params.id, profile.user_id);
349
+ const success = await store.setDefaultProfile(req.params.id, profile.user_id);
317
350
  if (!success) {
318
351
  return res.status(500).json({ error: 'Failed to set default profile' });
319
352
  }
320
353
 
321
- const updated = await config.store.getById(req.params.id);
354
+ const updated = await store.getById(req.params.id);
322
355
  res.json(updated);
323
356
  } catch (error) {
324
357
  console.error('[ProfilesPlugin] Set default profile error:', error);
@@ -334,7 +367,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
334
367
  pluginId: 'profiles',
335
368
  handler: async (req: Request, res: Response) => {
336
369
  try {
337
- const profile = await config.store.getById(req.params.id);
370
+ const profile = await store.getById(req.params.id);
338
371
  if (!profile) {
339
372
  return res.status(404).json({ error: 'Profile not found' });
340
373
  }
@@ -354,7 +387,7 @@ export function createProfilesPlugin(config: ProfilesPluginConfig): Plugin {
354
387
 
355
388
  async onStop(): Promise<void> {
356
389
  log('Stopping profiles plugin');
357
- await config.store.shutdown();
390
+ if (currentStore) { await currentStore.shutdown(); };
358
391
  currentStore = null;
359
392
  currentConfig = null;
360
393
  log('Profiles plugin stopped');
@@ -289,10 +289,17 @@ export interface ProfilesApiConfig {
289
289
 
290
290
  /**
291
291
  * Profiles plugin configuration
292
+ *
293
+ * All properties are optional - plugin will use smart defaults:
294
+ * - store: Postgres profile store using registry's postgres instance
295
+ * - maxProfilesPerUser: 10
296
+ * - defaultFilterLevel: 'moderate'
297
+ * - api.prefix: '/profiles'
298
+ * - debug: false
292
299
  */
293
300
  export interface ProfilesPluginConfig {
294
- /** Profile storage backend */
295
- store: ProfileStore;
301
+ /** Profile storage backend (default: postgres profile store from registry) */
302
+ store?: ProfileStore;
296
303
  /** Maximum profiles per user (default: 10) */
297
304
  maxProfilesPerUser?: number;
298
305
  /** Default content filter level (default: 'moderate') */
@@ -32,8 +32,6 @@ export type {
32
32
  MCPRateLimitConfig,
33
33
  } from './types.js';
34
34
 
35
- // UI Components
36
- export { QwickbrainStatusWidget } from './QwickbrainStatusWidget.js';
37
- export { QwickbrainManagementPage } from './QwickbrainManagementPage.js';
38
- export type { QwickbrainStatusWidgetProps } from './QwickbrainStatusWidget.js';
39
- export type { QwickbrainManagementPageProps } from './QwickbrainManagementPage.js';
35
+ // UI Components are exported from main package index (@qwickapps/server)
36
+ // Do NOT export here to avoid loading UI dependencies when importing plugins
37
+
@@ -530,6 +530,141 @@ export function createQwickBrainPlugin(config: QwickBrainPluginConfig): Plugin {
530
530
  },
531
531
  });
532
532
 
533
+ // POST /mcp/query - LLM Query endpoint (auth required, supports streaming)
534
+ registry.addRoute({
535
+ method: 'post',
536
+ path: `${apiPrefix}/query`,
537
+ pluginId: 'qwickbrain',
538
+ handler: async (req: Request, res: ExpressResponse) => {
539
+ // Check authentication
540
+ const authError = checkAuth(req);
541
+ if (authError) {
542
+ res.status(authError.status).json(authError.body);
543
+ return;
544
+ }
545
+
546
+ const user = getAuthenticatedUser(req);
547
+
548
+ // Check rate limits
549
+ const rateLimitError = checkRateLimits(user?.id);
550
+ if (rateLimitError) {
551
+ Object.entries(rateLimitError.headers).forEach(([key, value]) => {
552
+ res.setHeader(key, value);
553
+ });
554
+ res.status(rateLimitError.status).json(rateLimitError.body);
555
+ return;
556
+ }
557
+
558
+ try {
559
+ if (!connectionStatus.connected) {
560
+ res.status(503).json({
561
+ error: 'QwickBrain not connected',
562
+ details: connectionStatus.error,
563
+ });
564
+ return;
565
+ }
566
+
567
+ // Check if streaming is requested
568
+ const stream = req.query.stream === 'true';
569
+
570
+ // Build query string
571
+ const queryParams = new URLSearchParams();
572
+ if (stream) {
573
+ queryParams.set('stream', 'true');
574
+ }
575
+ const queryString = queryParams.toString();
576
+ const path = `/api/v1/query${queryString ? `?${queryString}` : ''}`;
577
+
578
+ log('LLM query', { userId: user?.id, streaming: stream, query: req.body.query });
579
+
580
+ if (stream) {
581
+ // Streaming mode: pipe SSE stream from QwickBrain to client
582
+ const url = `${config.qwickbrainUrl}${path}`;
583
+ const controller = new AbortController();
584
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
585
+
586
+ try {
587
+ const fetchResponse = await fetch(url, {
588
+ method: 'POST',
589
+ headers: {
590
+ 'Content-Type': 'application/json',
591
+ 'Accept': 'text/event-stream',
592
+ },
593
+ body: JSON.stringify(req.body),
594
+ signal: controller.signal,
595
+ });
596
+
597
+ clearTimeout(timeoutId);
598
+
599
+ if (!fetchResponse.ok) {
600
+ res.status(fetchResponse.status).json({
601
+ error: 'Query failed',
602
+ status: fetchResponse.status,
603
+ });
604
+ return;
605
+ }
606
+
607
+ // Set SSE headers
608
+ res.setHeader('Content-Type', 'text/event-stream');
609
+ res.setHeader('Cache-Control', 'no-cache');
610
+ res.setHeader('Connection', 'keep-alive');
611
+ res.setHeader('X-Accel-Buffering', 'no');
612
+
613
+ // Pipe the stream
614
+ const reader = fetchResponse.body?.getReader();
615
+ if (!reader) {
616
+ res.status(500).json({ error: 'No response body' });
617
+ return;
618
+ }
619
+
620
+ const decoder = new TextDecoder();
621
+
622
+ while (true) {
623
+ const { done, value } = await reader.read();
624
+ if (done) break;
625
+ const text = decoder.decode(value, { stream: true });
626
+ res.write(text);
627
+ }
628
+
629
+ res.end();
630
+ } catch (error) {
631
+ clearTimeout(timeoutId);
632
+ throw error;
633
+ }
634
+ } else {
635
+ // Non-streaming mode: proxy JSON response
636
+ const response = await proxyToQwickBrain(
637
+ config.qwickbrainUrl,
638
+ path,
639
+ {
640
+ method: 'POST',
641
+ body: req.body,
642
+ timeout,
643
+ }
644
+ );
645
+
646
+ if (!response.ok) {
647
+ const errorText = await response.text();
648
+ res.status(response.status).json({
649
+ error: 'Query failed',
650
+ details: errorText,
651
+ });
652
+ return;
653
+ }
654
+
655
+ const result = await response.json();
656
+ res.json(result);
657
+ }
658
+ } catch (error) {
659
+ log('Error executing query', { error: String(error) });
660
+ res.status(500).json({
661
+ error: 'Query execution failed',
662
+ details: error instanceof Error ? error.message : 'Unknown error',
663
+ });
664
+ }
665
+ },
666
+ });
667
+
533
668
  // GET /mcp/sse - Server-Sent Events endpoint for streaming (auth required)
534
669
  registry.addRoute({
535
670
  method: 'get',
@@ -127,8 +127,6 @@ export type {
127
127
  CheckLimitOptions,
128
128
  } from './types.js';
129
129
 
130
- // UI Components
131
- export { RateLimitStatusWidget } from './RateLimitStatusWidget.js';
132
- export { RateLimitManagementPage } from './RateLimitManagementPage.js';
133
- export type { RateLimitStatusWidgetProps } from './RateLimitStatusWidget.js';
134
- export type { RateLimitManagementPageProps } from './RateLimitManagementPage.js';
130
+ // UI Components are exported from main package index (@qwickapps/server)
131
+ // Do NOT export here to avoid loading UI dependencies when importing plugins
132
+
@@ -50,8 +50,6 @@ export type {
50
50
  // Stores
51
51
  export { postgresSubscriptionsStore } from './stores/index.js';
52
52
 
53
- // UI Components
54
- export { SubscriptionsStatusWidget } from './SubscriptionsStatusWidget.js';
55
- export type { SubscriptionsStatusWidgetProps } from './SubscriptionsStatusWidget.js';
56
- export { SubscriptionsManagementPage } from './SubscriptionsManagementPage.js';
57
- export type { SubscriptionsManagementPageProps } from './SubscriptionsManagementPage.js';
53
+ // UI Components are exported from main package index (@qwickapps/server)
54
+ // Do NOT export here to avoid loading UI dependencies when importing plugins
55
+