@qwickapps/server 1.7.0 → 1.7.1

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 (200) hide show
  1. package/CHANGELOG.md +53 -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/stores/postgres-store.d.ts.map +1 -1
  17. package/dist/src/plugins/api-keys/stores/postgres-store.js +29 -0
  18. package/dist/src/plugins/api-keys/stores/postgres-store.js.map +1 -1
  19. package/dist/src/plugins/auth/auth-plugin.d.ts.map +1 -1
  20. package/dist/src/plugins/auth/auth-plugin.js +4 -2
  21. package/dist/src/plugins/auth/auth-plugin.js.map +1 -1
  22. package/dist/src/plugins/auth/env-config.d.ts.map +1 -1
  23. package/dist/src/plugins/auth/env-config.js +1 -0
  24. package/dist/src/plugins/auth/env-config.js.map +1 -1
  25. package/dist/src/plugins/bans/index.d.ts +1 -1
  26. package/dist/src/plugins/bans/index.d.ts.map +1 -1
  27. package/dist/src/plugins/bans/index.js +1 -1
  28. package/dist/src/plugins/bans/index.js.map +1 -1
  29. package/dist/src/plugins/bans/stores/in-memory-store.d.ts +34 -0
  30. package/dist/src/plugins/bans/stores/in-memory-store.d.ts.map +1 -0
  31. package/dist/src/plugins/bans/stores/in-memory-store.js +97 -0
  32. package/dist/src/plugins/bans/stores/in-memory-store.js.map +1 -0
  33. package/dist/src/plugins/bans/stores/index.d.ts +1 -0
  34. package/dist/src/plugins/bans/stores/index.d.ts.map +1 -1
  35. package/dist/src/plugins/bans/stores/index.js +1 -0
  36. package/dist/src/plugins/bans/stores/index.js.map +1 -1
  37. package/dist/src/plugins/cache-plugin.d.ts +35 -16
  38. package/dist/src/plugins/cache-plugin.d.ts.map +1 -1
  39. package/dist/src/plugins/cache-plugin.js +299 -20
  40. package/dist/src/plugins/cache-plugin.js.map +1 -1
  41. package/dist/src/plugins/cms/cms-plugin.d.ts.map +1 -1
  42. package/dist/src/plugins/cms/cms-plugin.js +3 -1
  43. package/dist/src/plugins/cms/cms-plugin.js.map +1 -1
  44. package/dist/src/plugins/entitlements/index.d.ts +1 -1
  45. package/dist/src/plugins/entitlements/index.d.ts.map +1 -1
  46. package/dist/src/plugins/entitlements/index.js +1 -1
  47. package/dist/src/plugins/entitlements/index.js.map +1 -1
  48. package/dist/src/plugins/entitlements/sources/in-memory-source.d.ts +9 -0
  49. package/dist/src/plugins/entitlements/sources/in-memory-source.d.ts.map +1 -0
  50. package/dist/src/plugins/entitlements/sources/in-memory-source.js +65 -0
  51. package/dist/src/plugins/entitlements/sources/in-memory-source.js.map +1 -0
  52. package/dist/src/plugins/entitlements/sources/index.d.ts +1 -0
  53. package/dist/src/plugins/entitlements/sources/index.d.ts.map +1 -1
  54. package/dist/src/plugins/entitlements/sources/index.js +1 -0
  55. package/dist/src/plugins/entitlements/sources/index.js.map +1 -1
  56. package/dist/src/plugins/health-plugin.d.ts.map +1 -1
  57. package/dist/src/plugins/health-plugin.js +1 -0
  58. package/dist/src/plugins/health-plugin.js.map +1 -1
  59. package/dist/src/plugins/index.d.ts +4 -4
  60. package/dist/src/plugins/index.d.ts.map +1 -1
  61. package/dist/src/plugins/index.js +4 -4
  62. package/dist/src/plugins/index.js.map +1 -1
  63. package/dist/src/plugins/logs-plugin.d.ts.map +1 -1
  64. package/dist/src/plugins/logs-plugin.js +49 -1
  65. package/dist/src/plugins/logs-plugin.js.map +1 -1
  66. package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
  67. package/dist/src/plugins/maintenance-plugin.js +39 -0
  68. package/dist/src/plugins/maintenance-plugin.js.map +1 -1
  69. package/dist/src/plugins/notifications/notifications-plugin.d.ts.map +1 -1
  70. package/dist/src/plugins/notifications/notifications-plugin.js +1 -0
  71. package/dist/src/plugins/notifications/notifications-plugin.js.map +1 -1
  72. package/dist/src/plugins/postgres-plugin.d.ts +3 -1
  73. package/dist/src/plugins/postgres-plugin.d.ts.map +1 -1
  74. package/dist/src/plugins/postgres-plugin.js +18 -8
  75. package/dist/src/plugins/postgres-plugin.js.map +1 -1
  76. package/dist/src/plugins/tenants/index.d.ts +1 -1
  77. package/dist/src/plugins/tenants/index.d.ts.map +1 -1
  78. package/dist/src/plugins/tenants/index.js +1 -1
  79. package/dist/src/plugins/tenants/index.js.map +1 -1
  80. package/dist/src/plugins/tenants/stores/in-memory-store.d.ts +59 -0
  81. package/dist/src/plugins/tenants/stores/in-memory-store.d.ts.map +1 -0
  82. package/dist/src/plugins/tenants/stores/in-memory-store.js +257 -0
  83. package/dist/src/plugins/tenants/stores/in-memory-store.js.map +1 -0
  84. package/dist/src/plugins/tenants/stores/index.d.ts +8 -0
  85. package/dist/src/plugins/tenants/stores/index.d.ts.map +1 -0
  86. package/dist/src/plugins/tenants/stores/index.js +8 -0
  87. package/dist/src/plugins/tenants/stores/index.js.map +1 -0
  88. package/dist/src/plugins/tenants/tenants-plugin.js +3 -3
  89. package/dist/src/plugins/tenants/tenants-plugin.js.map +1 -1
  90. package/dist/src/plugins/users/index.d.ts +1 -1
  91. package/dist/src/plugins/users/index.d.ts.map +1 -1
  92. package/dist/src/plugins/users/index.js +1 -1
  93. package/dist/src/plugins/users/index.js.map +1 -1
  94. package/dist/src/plugins/users/stores/in-memory-store.d.ts +36 -0
  95. package/dist/src/plugins/users/stores/in-memory-store.d.ts.map +1 -0
  96. package/dist/src/plugins/users/stores/in-memory-store.js +122 -0
  97. package/dist/src/plugins/users/stores/in-memory-store.js.map +1 -0
  98. package/dist/src/plugins/users/stores/index.d.ts +1 -0
  99. package/dist/src/plugins/users/stores/index.d.ts.map +1 -1
  100. package/dist/src/plugins/users/stores/index.js +1 -0
  101. package/dist/src/plugins/users/stores/index.js.map +1 -1
  102. package/dist/ui/src/api/controlPanelApi.d.ts +10 -1
  103. package/dist/ui/src/api/controlPanelApi.d.ts.map +1 -1
  104. package/dist/ui/src/api/controlPanelApi.js.map +1 -1
  105. package/dist/ui/src/dashboard/PluginWidgetRenderer.d.ts +3 -1
  106. package/dist/ui/src/dashboard/PluginWidgetRenderer.d.ts.map +1 -1
  107. package/dist/ui/src/dashboard/PluginWidgetRenderer.js +5 -1
  108. package/dist/ui/src/dashboard/PluginWidgetRenderer.js.map +1 -1
  109. package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
  110. package/dist/ui/src/dashboard/builtInWidgets.js +13 -1
  111. package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
  112. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts +11 -0
  113. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts.map +1 -0
  114. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +77 -0
  115. package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -0
  116. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.d.ts +10 -0
  117. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.d.ts.map +1 -0
  118. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.js +14 -0
  119. package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.js.map +1 -0
  120. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.d.ts +10 -0
  121. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.d.ts.map +1 -0
  122. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.js +14 -0
  123. package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.js.map +1 -0
  124. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts +11 -0
  125. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts.map +1 -0
  126. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +96 -0
  127. package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -0
  128. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts +10 -0
  129. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts.map +1 -0
  130. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +55 -0
  131. package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -0
  132. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.d.ts +10 -0
  133. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.d.ts.map +1 -0
  134. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.js +14 -0
  135. package/dist/ui/src/dashboard/widgets/ServiceControlWidget.js.map +1 -0
  136. package/dist/ui/src/dashboard/widgets/index.d.ts +6 -0
  137. package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
  138. package/dist/ui/src/dashboard/widgets/index.js +6 -0
  139. package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
  140. package/dist/ui/src/pages/DashboardPage.js +1 -1
  141. package/dist/ui/src/pages/DashboardPage.js.map +1 -1
  142. package/dist-ui/assets/{index-lm1yX6UD.js → index-8y0jDGcd.js} +112 -112
  143. package/dist-ui/assets/{index-lm1yX6UD.js.map → index-8y0jDGcd.js.map} +1 -1
  144. package/dist-ui/index.html +1 -1
  145. package/dist-ui-lib/index.js +3109 -2774
  146. package/dist-ui-lib/index.js.map +1 -1
  147. package/dist-ui-lib/src/api/controlPanelApi.d.ts +10 -1
  148. package/dist-ui-lib/src/dashboard/PluginWidgetRenderer.d.ts +3 -1
  149. package/dist-ui-lib/src/dashboard/widgets/CacheMaintenanceWidget.d.ts +10 -0
  150. package/dist-ui-lib/src/dashboard/widgets/DatabaseOpsWidget.d.ts +9 -0
  151. package/dist-ui-lib/src/dashboard/widgets/EnvironmentConfigWidget.d.ts +9 -0
  152. package/dist-ui-lib/src/dashboard/widgets/LogsMaintenanceWidget.d.ts +10 -0
  153. package/dist-ui-lib/src/dashboard/widgets/SeedManagementWidget.d.ts +9 -0
  154. package/dist-ui-lib/src/dashboard/widgets/ServiceControlWidget.d.ts +9 -0
  155. package/dist-ui-lib/src/dashboard/widgets/index.d.ts +6 -0
  156. package/package.json +5 -2
  157. package/src/core/control-panel.ts +6 -4
  158. package/src/core/gateway.ts +25 -2
  159. package/src/core/plugin-registry.ts +15 -2
  160. package/src/index.ts +53 -0
  161. package/src/plugins/api-keys/stores/postgres-store.ts +30 -0
  162. package/src/plugins/auth/auth-plugin.ts +4 -2
  163. package/src/plugins/auth/env-config.ts +1 -0
  164. package/src/plugins/bans/index.ts +1 -1
  165. package/src/plugins/bans/stores/in-memory-store.ts +106 -0
  166. package/src/plugins/bans/stores/index.ts +1 -0
  167. package/src/plugins/cache-plugin.test.ts +2 -2
  168. package/src/plugins/cache-plugin.ts +331 -30
  169. package/src/plugins/cms/cms-plugin.ts +3 -1
  170. package/src/plugins/entitlements/index.ts +1 -1
  171. package/src/plugins/entitlements/sources/in-memory-source.ts +76 -0
  172. package/src/plugins/entitlements/sources/index.ts +1 -0
  173. package/src/plugins/health-plugin.ts +1 -0
  174. package/src/plugins/index.ts +4 -1
  175. package/src/plugins/logs-plugin.ts +55 -1
  176. package/src/plugins/maintenance-plugin.ts +43 -0
  177. package/src/plugins/notifications/notifications-plugin.ts +1 -0
  178. package/src/plugins/postgres-plugin.test.ts +2 -2
  179. package/src/plugins/postgres-plugin.ts +20 -9
  180. package/src/plugins/tenants/index.ts +1 -1
  181. package/src/plugins/tenants/stores/in-memory-store.ts +335 -0
  182. package/src/plugins/tenants/stores/index.ts +13 -0
  183. package/src/plugins/tenants/tenants-plugin.ts +3 -3
  184. package/src/plugins/users/index.ts +1 -1
  185. package/src/plugins/users/stores/in-memory-store.ts +140 -0
  186. package/src/plugins/users/stores/index.ts +1 -0
  187. package/src/testing/index.ts +1 -0
  188. package/src/testing/pg-mem-pool.ts +33 -0
  189. package/ui/src/api/controlPanelApi.ts +10 -1
  190. package/ui/src/dashboard/PluginWidgetRenderer.tsx +8 -0
  191. package/ui/src/dashboard/builtInWidgets.tsx +19 -1
  192. package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +195 -0
  193. package/ui/src/dashboard/widgets/DatabaseOpsWidget.tsx +29 -0
  194. package/ui/src/dashboard/widgets/EnvironmentConfigWidget.tsx +29 -0
  195. package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +247 -0
  196. package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +128 -0
  197. package/ui/src/dashboard/widgets/ServiceControlWidget.tsx +29 -0
  198. package/ui/src/dashboard/widgets/index.ts +6 -0
  199. package/ui/src/pages/DashboardPage.tsx +2 -2
  200. package/ui/src/pages/MaintenancePage.tsx +1 -1
@@ -245,9 +245,18 @@ export interface WidgetContribution {
245
245
  title: string;
246
246
  /** Component name to render (matched by frontend widget registry) */
247
247
  component: string;
248
+ /**
249
+ * Widget type/category - determines which page(s) can display this widget
250
+ * - 'status': System health, service status, monitoring metrics (Dashboard)
251
+ * - 'maintenance': Operational tasks like seeding, service control, config (Maintenance page)
252
+ * - 'analytics': Charts, graphs, usage metrics (Dashboard or Analytics page)
253
+ * - 'monitoring': Performance, logs, real-time data (Monitoring page)
254
+ * - 'custom': Custom widgets for specific use cases
255
+ */
256
+ type: 'status' | 'maintenance' | 'analytics' | 'monitoring' | 'custom';
248
257
  /** Priority for ordering (lower = first, default: 100) */
249
258
  priority?: number;
250
- /** Whether this widget is shown by default */
259
+ /** Whether this widget is shown by default on its designated page */
251
260
  showByDefault?: boolean;
252
261
  pluginId: string;
253
262
  }
@@ -7,6 +7,8 @@
7
7
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
8
  */
9
9
  interface PluginWidgetRendererProps {
10
+ /** Filter widgets by type (e.g., 'status' for dashboard, 'maintenance' for maintenance page) */
11
+ widgetType?: 'status' | 'maintenance' | 'analytics' | 'monitoring' | 'custom';
10
12
  /** Only show widgets marked as showByDefault (default: true) */
11
13
  defaultOnly?: boolean;
12
14
  /** Additional widget IDs to show (beyond showByDefault) */
@@ -15,5 +17,5 @@ interface PluginWidgetRendererProps {
15
17
  /**
16
18
  * Renders widgets from plugins that have registered them via the server API
17
19
  */
18
- export declare function PluginWidgetRenderer({ defaultOnly, additionalWidgetIds, }: PluginWidgetRendererProps): import("react/jsx-runtime").JSX.Element | null;
20
+ export declare function PluginWidgetRenderer({ widgetType, defaultOnly, additionalWidgetIds, }: PluginWidgetRendererProps): import("react/jsx-runtime").JSX.Element | null;
19
21
  export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Cache Maintenance Widget
3
+ *
4
+ * Provides cache management operations:
5
+ * - View cache statistics
6
+ * - Clear cache (flush all keys)
7
+ *
8
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
9
+ */
10
+ export declare function CacheMaintenanceWidget(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Database Operations Widget
3
+ *
4
+ * Provides database backup, restore, and maintenance operations.
5
+ * Part of the maintenance plugin.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+ export declare function DatabaseOpsWidget(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Environment Configuration Widget
3
+ *
4
+ * Displays and allows editing environment variables.
5
+ * Part of the maintenance plugin.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+ export declare function EnvironmentConfigWidget(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Logs Maintenance Widget
3
+ *
4
+ * Provides log management operations:
5
+ * - View log statistics
6
+ * - Clear log files
7
+ *
8
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
9
+ */
10
+ export declare function LogsMaintenanceWidget(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Seed Management Widget
3
+ *
4
+ * Displays available seed scripts and allows executing them.
5
+ * Part of the maintenance plugin.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+ export declare function SeedManagementWidget(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Service Control Widget
3
+ *
4
+ * Provides controls for starting, stopping, and restarting services.
5
+ * Part of the maintenance plugin.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+ export declare function ServiceControlWidget(): import("react/jsx-runtime").JSX.Element;
@@ -9,3 +9,9 @@ export { AuthStatusWidget } from './AuthStatusWidget';
9
9
  export { NotificationsStatsWidget } from './NotificationsStatsWidget';
10
10
  export { CMSStatusWidget } from './CMSStatusWidget';
11
11
  export { CMSMaintenanceWidget } from './CMSMaintenanceWidget';
12
+ export { SeedManagementWidget } from './SeedManagementWidget';
13
+ export { ServiceControlWidget } from './ServiceControlWidget';
14
+ export { EnvironmentConfigWidget } from './EnvironmentConfigWidget';
15
+ export { DatabaseOpsWidget } from './DatabaseOpsWidget';
16
+ export { LogsMaintenanceWidget } from './LogsMaintenanceWidget';
17
+ export { CacheMaintenanceWidget } from './CacheMaintenanceWidget';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwickapps/server",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "Plugin-based application server framework for building websites, APIs, admin dashboards, and full-stack products",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -41,7 +41,9 @@
41
41
  "test:e2e": "playwright test",
42
42
  "test:e2e:ui": "playwright test --ui",
43
43
  "test:e2e:headed": "playwright test --headed",
44
- "demo": "npx tsx examples/demo-server.ts",
44
+ "demo": "npm run build:server && pnpm tsx examples/demo-server.ts",
45
+ "demo:gateway": "npm run build:server && pnpm tsx examples/demo-gateway.ts",
46
+ "demo:all": "npm run build:server && pnpm tsx examples/demo-all.ts",
45
47
  "type-check": "tsc --noEmit",
46
48
  "type-check:ui": "cd ui && tsc --noEmit",
47
49
  "validate:clean-install": "./qa/clean-install/validate.sh",
@@ -81,6 +83,7 @@
81
83
  "ioredis": "^5.4.0",
82
84
  "jsdom": "^25.0.0",
83
85
  "pg": "^8.13.0",
86
+ "pg-mem": "^3.0.5",
84
87
  "react": "^18.2.0",
85
88
  "react-dom": "^18.2.0",
86
89
  "react-router-dom": "^6.30.1",
@@ -41,10 +41,12 @@ import { createCorePlugin } from '../plugins/core/index.js';
41
41
  // Get the package root directory for serving UI assets
42
42
  const __filename = fileURLToPath(import.meta.url);
43
43
  const __dirname = dirname(__filename);
44
- // Handle both src/core and dist/core paths - go up to find package root
45
- const packageRoot = __dirname.includes('/src/')
46
- ? join(__dirname, '..', '..')
47
- : join(__dirname, '..', '..');
44
+ // Handle both src/core and dist/src/core paths - go up to find package root
45
+ // - src/core/control-panel.ts → go up 2 levels to package root
46
+ // - dist/src/core/control-panel.js → go up 3 levels to package root
47
+ const packageRoot = __dirname.includes('/dist/src/')
48
+ ? join(__dirname, '..', '..', '..') // dist/src/core → package root (3 levels)
49
+ : join(__dirname, '..', '..'); // src/core → package root (2 levels)
48
50
  const uiDistPath = join(packageRoot, 'dist-ui');
49
51
 
50
52
  // Read @qwickapps/server package version
@@ -37,8 +37,31 @@ import { fileURLToPath } from 'url';
37
37
  // Get QwickApps Server version from package.json
38
38
  const __filename = fileURLToPath(import.meta.url);
39
39
  const __dirname = dirname(__filename);
40
- const serverPackageJson = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8'));
41
- const QWICKAPPS_SERVER_VERSION = serverPackageJson.version || '1.0.0';
40
+
41
+ // Find package.json by walking up directories
42
+ // Recommended approach for libraries that work in both source and compiled contexts
43
+ function findPackageJson(): string {
44
+ let currentDir = __dirname;
45
+ // Walk up max 5 levels looking for the correct package.json
46
+ for (let i = 0; i < 5; i++) {
47
+ try {
48
+ const packagePath = join(currentDir, 'package.json');
49
+ if (existsSync(packagePath)) {
50
+ const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
51
+ // Verify it's the right package
52
+ if (pkg.name === '@qwickapps/server') {
53
+ return pkg.version || '1.0.0';
54
+ }
55
+ }
56
+ } catch {
57
+ // Continue searching
58
+ }
59
+ currentDir = join(currentDir, '..');
60
+ }
61
+ return '1.0.0';
62
+ }
63
+
64
+ const QWICKAPPS_SERVER_VERSION = findPackageJson();
42
65
 
43
66
  /**
44
67
  * Maintenance mode configuration for a mounted app
@@ -185,7 +185,7 @@ export interface PageContribution {
185
185
  }
186
186
 
187
187
  /**
188
- * Widget contribution for dashboards
188
+ * Widget contribution for dashboards and pages
189
189
  */
190
190
  export interface WidgetContribution {
191
191
  /** Unique ID for this widget */
@@ -194,9 +194,22 @@ export interface WidgetContribution {
194
194
  title: string;
195
195
  /** Component name to render (matched by frontend widget registry) */
196
196
  component: string;
197
+ /**
198
+ * Widget type/category - determines which page(s) can display this widget
199
+ * - 'status': System health, service status, monitoring metrics (Dashboard)
200
+ * - 'maintenance': Operational tasks like seeding, service control, config (Maintenance page)
201
+ * - 'analytics': Charts, graphs, usage metrics (Dashboard or Analytics page)
202
+ * - 'monitoring': Performance, logs, real-time data (Monitoring page)
203
+ * - 'custom': Custom widgets for specific use cases
204
+ */
205
+ type: 'status' | 'maintenance' | 'analytics' | 'monitoring' | 'custom';
197
206
  /** Priority for ordering (lower = first, default: 100) */
198
207
  priority?: number;
199
- /** Whether this widget is shown by default (default: false) */
208
+ /**
209
+ * Whether this widget is shown by default on its page (default: false)
210
+ * true = widget appears by default in the initial layout
211
+ * false = widget is available but admin must add it manually
212
+ */
200
213
  showByDefault?: boolean;
201
214
  /** Default size */
202
215
  defaultSize?: { width: number; height: number };
package/src/index.ts CHANGED
@@ -116,6 +116,7 @@ export {
116
116
  linkUserIdentifiers,
117
117
  findOrCreateUser,
118
118
  postgresUserStore,
119
+ inMemoryUserStore,
119
120
  // Bans plugin (separate from Users, depends on Users)
120
121
  createBansPlugin,
121
122
  getBanStore,
@@ -126,6 +127,7 @@ export {
126
127
  unbanUser,
127
128
  listActiveBans,
128
129
  postgresBanStore,
130
+ inMemoryBanStore,
129
131
  // API Keys plugin (M2M authentication, depends on Users)
130
132
  createApiKeysPlugin,
131
133
  getApiKeysStore,
@@ -158,6 +160,21 @@ export {
158
160
  requireAnyEntitlement,
159
161
  requireAllEntitlements,
160
162
  postgresEntitlementSource,
163
+ inMemoryEntitlementSource,
164
+ // Preferences plugin
165
+ createPreferencesPlugin,
166
+ getPreferencesStore,
167
+ getPreferences,
168
+ updatePreferences,
169
+ deletePreferences,
170
+ getDefaultPreferences,
171
+ postgresPreferencesStore,
172
+ deepMerge,
173
+ // Tenants plugin (multi-tenant data isolation, depends on Users)
174
+ createTenantsPlugin,
175
+ getTenantStore,
176
+ postgresTenantStore,
177
+ inMemoryTenantStore,
161
178
  // Rate Limit plugin
162
179
  createRateLimitPlugin,
163
180
  createRateLimitPluginFromEnv,
@@ -276,6 +293,14 @@ export {
276
293
  getActivityLog,
277
294
  postgresParentalStore,
278
295
  kidsAdapter,
296
+ // Notifications plugin
297
+ createNotificationsPlugin,
298
+ NotificationsManager,
299
+ getNotificationsManager,
300
+ hasNotificationsManager,
301
+ broadcastToDevice,
302
+ broadcastToUser,
303
+ broadcastToAll,
279
304
  // QwickBrain plugin
280
305
  createQwickBrainPlugin,
281
306
  getConnectionStatus,
@@ -355,6 +380,26 @@ export type {
355
380
  UserEntitlement,
356
381
  CachedEntitlements,
357
382
  EntitlementStats,
383
+ // Preferences plugin types
384
+ PreferencesPluginConfig,
385
+ PreferencesStore,
386
+ UserPreferences,
387
+ PostgresPreferencesStoreConfig,
388
+ PreferencesApiConfig,
389
+ // Tenants plugin types
390
+ TenantsPluginConfig,
391
+ TenantStore,
392
+ TenantType,
393
+ Tenant,
394
+ CreateTenantInput,
395
+ UpdateTenantInput,
396
+ TenantSearchParams,
397
+ TenantListResponse,
398
+ TenantMembership,
399
+ CreateTenantMembershipInput,
400
+ UpdateTenantMembershipInput,
401
+ TenantWithMembership,
402
+ PostgresTenantStoreConfig,
358
403
  // Rate Limit plugin types
359
404
  RateLimitPluginConfig,
360
405
  RateLimitEnvPluginOptions,
@@ -445,6 +490,14 @@ export type {
445
490
  ParentalApiConfig,
446
491
  PostgresParentalStoreConfig,
447
492
  KidsAdapterConfig,
493
+ // Notifications plugin types
494
+ NotificationsPluginConfig,
495
+ SSEClient,
496
+ NotifyPayload,
497
+ SSEEvent,
498
+ NotificationsStats,
499
+ ConnectionHealth,
500
+ NotificationsManagerInterface,
448
501
  // QwickBrain plugin types
449
502
  QwickBrainPluginConfig,
450
503
  MCPToolDefinition,
@@ -266,6 +266,36 @@ export function postgresApiKeyStore(config: PostgresApiKeyStoreConfig): ApiKeySt
266
266
  CREATE INDEX IF NOT EXISTS idx_${tableName}_key_hash ON ${tableFullName}(key_hash);
267
267
  `);
268
268
 
269
+ // Migration: Add user_id column if it doesn't exist (for existing installations)
270
+ await pool.query(`
271
+ DO $$
272
+ DECLARE
273
+ row_count INTEGER;
274
+ BEGIN
275
+ IF NOT EXISTS (
276
+ SELECT 1 FROM information_schema.columns
277
+ WHERE table_schema = '${schema}'
278
+ AND table_name = '${tableName}'
279
+ AND column_name = 'user_id'
280
+ ) THEN
281
+ -- Check if table has data
282
+ EXECUTE 'SELECT COUNT(*) FROM ${tableFullName}' INTO row_count;
283
+
284
+ IF row_count > 0 THEN
285
+ -- If table has data, cannot add NOT NULL column
286
+ -- This requires manual migration or data cleanup
287
+ RAISE EXCEPTION 'Cannot add user_id column: table ${tableFullName} contains % rows. Please migrate data or clear the table first.', row_count;
288
+ ELSE
289
+ -- Table is empty, safe to add NOT NULL column
290
+ ALTER TABLE ${tableFullName}
291
+ ADD COLUMN user_id UUID NOT NULL REFERENCES "public"."users"(id) ON DELETE CASCADE;
292
+
293
+ CREATE INDEX IF NOT EXISTS idx_${tableName}_user_id ON ${tableFullName}(user_id);
294
+ END IF;
295
+ END IF;
296
+ END $$;
297
+ `);
298
+
269
299
  // Enable RLS if configured
270
300
  if (enableRLS) {
271
301
  await pool.query(`
@@ -42,6 +42,7 @@ export function createAuthPlugin(config: AuthPluginConfig): Plugin {
42
42
 
43
43
  async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
44
44
  const app = registry.getApp();
45
+ const router = registry.getRouter();
45
46
 
46
47
  // Store adapters for helper access
47
48
  currentAdapter = config.adapter;
@@ -72,8 +73,9 @@ export function createAuthPlugin(config: AuthPluginConfig): Plugin {
72
73
  }
73
74
  }
74
75
 
75
- // Add the auth checking middleware
76
- app.use(createAuthMiddleware());
76
+ // Add the auth checking middleware to router (not app)
77
+ // This ensures it processes requests to /api/* routes
78
+ router.use(createAuthMiddleware());
77
79
 
78
80
  // Register auth status route
79
81
  registry.addRoute({
@@ -790,6 +790,7 @@ export function registerAuthConfigRoutes(registry: PluginRegistry): void {
790
790
  id: 'auth-status',
791
791
  title: 'Authentication',
792
792
  component: 'AuthStatusWidget',
793
+ type: 'status',
793
794
  priority: 40, // Show before integrations
794
795
  showByDefault: true,
795
796
  pluginId: 'auth',
@@ -28,7 +28,7 @@ export type {
28
28
  } from './types.js';
29
29
 
30
30
  // Stores
31
- export { postgresBanStore } from './stores/index.js';
31
+ export { postgresBanStore, inMemoryBanStore } from './stores/index.js';
32
32
 
33
33
  // UI Components
34
34
  export { BansStatusWidget } from './BansStatusWidget.js';
@@ -0,0 +1,106 @@
1
+ /**
2
+ * In-memory Ban Store for Demo/Testing
3
+ *
4
+ * Implements the BanStore interface with in-memory storage.
5
+ * Supports temporary and permanent bans with expiration.
6
+ */
7
+
8
+ export function createInMemoryBanStore() {
9
+ const bans = new Map<string, any>();
10
+ let idCounter = 1;
11
+
12
+ function isActiveBan(ban: any): boolean {
13
+ if (!ban.is_active) return false;
14
+ if (ban.expires_at && new Date(ban.expires_at) <= new Date()) return false;
15
+ return true;
16
+ }
17
+
18
+ return {
19
+ name: 'in-memory',
20
+
21
+ async initialize() {
22
+ console.log('[InMemoryBanStore] Initialized');
23
+ },
24
+
25
+ async isBanned(userId: string) {
26
+ for (const ban of bans.values()) {
27
+ if (ban.user_id === userId && isActiveBan(ban)) {
28
+ return true;
29
+ }
30
+ }
31
+ return false;
32
+ },
33
+
34
+ async getActiveBan(userId: string) {
35
+ for (const ban of bans.values()) {
36
+ if (ban.user_id === userId && isActiveBan(ban)) {
37
+ return ban;
38
+ }
39
+ }
40
+ return null;
41
+ },
42
+
43
+ async createBan(input: any) {
44
+ const id = String(idCounter++);
45
+ const now = new Date();
46
+ const ban = {
47
+ id,
48
+ user_id: input.user_id,
49
+ reason: input.reason,
50
+ banned_by: input.banned_by || 'system',
51
+ banned_at: now,
52
+ expires_at: input.duration ? new Date(now.getTime() + input.duration * 1000) : null,
53
+ is_active: true,
54
+ metadata: input.metadata || {},
55
+ };
56
+ bans.set(id, ban);
57
+ return ban;
58
+ },
59
+
60
+ async removeBan(input: any) {
61
+ for (const ban of bans.values()) {
62
+ if (ban.user_id === input.user_id && isActiveBan(ban)) {
63
+ ban.is_active = false;
64
+ ban.removed_at = new Date();
65
+ ban.removed_by = input.removed_by;
66
+ return true;
67
+ }
68
+ }
69
+ return false;
70
+ },
71
+
72
+ async listBans(userId: string) {
73
+ const userBans: any[] = [];
74
+ for (const ban of bans.values()) {
75
+ if (ban.user_id === userId) {
76
+ userBans.push(ban);
77
+ }
78
+ }
79
+ return userBans;
80
+ },
81
+
82
+ async listActiveBans(options: { limit?: number; offset?: number } = {}) {
83
+ const activeBans = Array.from(bans.values()).filter(isActiveBan);
84
+ const total = activeBans.length;
85
+ const offset = options.offset || 0;
86
+ const limit = options.limit || 50;
87
+ const result = activeBans.slice(offset, offset + limit);
88
+ return { bans: result, total };
89
+ },
90
+
91
+ async cleanupExpiredBans() {
92
+ let cleaned = 0;
93
+ for (const ban of bans.values()) {
94
+ if (ban.is_active && ban.expires_at && new Date(ban.expires_at) <= new Date()) {
95
+ ban.is_active = false;
96
+ cleaned++;
97
+ }
98
+ }
99
+ return cleaned;
100
+ },
101
+
102
+ async shutdown() {
103
+ console.log('[InMemoryBanStore] Shutdown');
104
+ },
105
+ };
106
+ }
@@ -5,3 +5,4 @@
5
5
  */
6
6
 
7
7
  export { postgresBanStore } from './postgres-store.js';
8
+ export { createInMemoryBanStore as inMemoryBanStore } from './in-memory-store.js';
@@ -141,12 +141,12 @@ describe('Cache Plugin', () => {
141
141
  expect(hasCache('test')).toBe(true);
142
142
  });
143
143
 
144
- it('should log debug message on successful connection', async () => {
144
+ it('should log info message on successful connection', async () => {
145
145
  const plugin = createCachePlugin(mockConfig, 'test');
146
146
  await plugin.onStart({}, mockRegistry);
147
147
 
148
148
  const logger = mockRegistry.getLogger('cache:test');
149
- expect(logger.debug).toHaveBeenCalledWith(
149
+ expect(logger.info).toHaveBeenCalledWith(
150
150
  expect.stringContaining('connected')
151
151
  );
152
152
  });