@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
@@ -0,0 +1,140 @@
1
+ /**
2
+ * In-memory User Store for Demo/Testing
3
+ *
4
+ * Implements the UserStore interface with in-memory storage.
5
+ * Pre-populated with demo users for testing and showcase purposes.
6
+ */
7
+
8
+ export function createInMemoryUserStore() {
9
+ const users = new Map<string, any>();
10
+ let idCounter = 1;
11
+
12
+ // Pre-populate demo users
13
+ const demoUsers = [
14
+ { email: 'demo@example.com', name: 'Demo User' },
15
+ { email: 'pro@example.com', name: 'Pro User' },
16
+ { email: 'enterprise@example.com', name: 'Enterprise User' },
17
+ { email: 'basic@example.com', name: 'Basic User' },
18
+ ];
19
+
20
+ demoUsers.forEach((u) => {
21
+ const id = String(idCounter++);
22
+ users.set(id, {
23
+ id,
24
+ email: u.email,
25
+ name: u.name,
26
+ created_at: new Date(),
27
+ updated_at: new Date(),
28
+ });
29
+ });
30
+
31
+ return {
32
+ name: 'in-memory',
33
+
34
+ async initialize() {
35
+ console.log('[InMemoryUserStore] Initialized with demo users');
36
+ },
37
+
38
+ async getById(id: string) {
39
+ return users.get(id) || null;
40
+ },
41
+
42
+ async getByEmail(email: string) {
43
+ for (const user of users.values()) {
44
+ if (user.email === email.toLowerCase()) {
45
+ return user;
46
+ }
47
+ }
48
+ return null;
49
+ },
50
+
51
+ async getByExternalId(externalId: string, provider: string) {
52
+ for (const user of users.values()) {
53
+ if (user.external_id === externalId && user.provider === provider) {
54
+ return user;
55
+ }
56
+ }
57
+ return null;
58
+ },
59
+
60
+ async create(input: any) {
61
+ const id = String(idCounter++);
62
+ const user = {
63
+ id,
64
+ email: input.email.toLowerCase(),
65
+ name: input.name || null,
66
+ external_id: input.external_id,
67
+ provider: input.provider,
68
+ picture: input.picture,
69
+ created_at: new Date(),
70
+ updated_at: new Date(),
71
+ metadata: input.metadata || {},
72
+ };
73
+ users.set(id, user);
74
+ return user;
75
+ },
76
+
77
+ async update(id: string, input: any) {
78
+ const user = users.get(id);
79
+ if (!user) return null;
80
+ Object.assign(user, input, { updated_at: new Date() });
81
+ return user;
82
+ },
83
+
84
+ async delete(id: string) {
85
+ return users.delete(id);
86
+ },
87
+
88
+ async search(params: any = {}) {
89
+ let result = Array.from(users.values());
90
+
91
+ if (params.query) {
92
+ const query = params.query.toLowerCase();
93
+ result = result.filter(
94
+ (u) =>
95
+ u.email.toLowerCase().includes(query) ||
96
+ (u.name && u.name.toLowerCase().includes(query))
97
+ );
98
+ }
99
+
100
+ if (params.provider) {
101
+ result = result.filter((u) => u.provider === params.provider);
102
+ }
103
+
104
+ const sortBy = params.sortBy || 'created_at';
105
+ const sortOrder = params.sortOrder || 'desc';
106
+ result.sort((a, b) => {
107
+ const aVal = a[sortBy];
108
+ const bVal = b[sortBy];
109
+ if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1;
110
+ if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1;
111
+ return 0;
112
+ });
113
+
114
+ const total = result.length;
115
+ const page = params.page || 1;
116
+ const limit = params.limit || 20;
117
+ const offset = (page - 1) * limit;
118
+ result = result.slice(offset, offset + limit);
119
+
120
+ return {
121
+ users: result,
122
+ total,
123
+ page,
124
+ limit,
125
+ totalPages: Math.ceil(total / limit),
126
+ };
127
+ },
128
+
129
+ async updateLastLogin(id: string) {
130
+ const user = users.get(id);
131
+ if (user) {
132
+ user.last_login_at = new Date();
133
+ }
134
+ },
135
+
136
+ async shutdown() {
137
+ console.log('[InMemoryUserStore] Shutdown');
138
+ },
139
+ };
140
+ }
@@ -5,3 +5,4 @@
5
5
  */
6
6
 
7
7
  export { postgresUserStore } from './postgres-store.js';
8
+ export { createInMemoryUserStore as inMemoryUserStore } from './in-memory-store.js';
@@ -0,0 +1 @@
1
+ export { createPgMemPool } from './pg-mem-pool.js';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * pg-mem Pool Adapter
3
+ *
4
+ * Provides a pg.Pool-compatible interface for pg-mem (in-memory PostgreSQL).
5
+ * Used by demos to run without requiring an actual PostgreSQL installation.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import { newDb } from 'pg-mem';
11
+
12
+ /**
13
+ * Create an in-memory PostgreSQL database with a Pool-compatible interface
14
+ *
15
+ * @returns Pool-compatible interface backed by pg-mem
16
+ */
17
+ export function createPgMemPool() {
18
+ const db = newDb();
19
+
20
+ // Get the Pool-compatible adapter from pg-mem
21
+ const { Pool } = db.adapters.createPg();
22
+ const pool = new Pool();
23
+
24
+ // Add a fake connection string for plugins that need it (like notifications)
25
+ // pg-mem doesn't use connection strings, but some plugins try to extract it
26
+ (pool as any).options = {
27
+ connectionString: 'postgresql://localhost/pgmem',
28
+ };
29
+
30
+ console.log('[pg-mem] In-memory PostgreSQL database created');
31
+
32
+ return pool;
33
+ }
@@ -304,9 +304,18 @@ export interface WidgetContribution {
304
304
  title: string;
305
305
  /** Component name to render (matched by frontend widget registry) */
306
306
  component: string;
307
+ /**
308
+ * Widget type/category - determines which page(s) can display this widget
309
+ * - 'status': System health, service status, monitoring metrics (Dashboard)
310
+ * - 'maintenance': Operational tasks like seeding, service control, config (Maintenance page)
311
+ * - 'analytics': Charts, graphs, usage metrics (Dashboard or Analytics page)
312
+ * - 'monitoring': Performance, logs, real-time data (Monitoring page)
313
+ * - 'custom': Custom widgets for specific use cases
314
+ */
315
+ type: 'status' | 'maintenance' | 'analytics' | 'monitoring' | 'custom';
307
316
  /** Priority for ordering (lower = first, default: 100) */
308
317
  priority?: number;
309
- /** Whether this widget is shown by default */
318
+ /** Whether this widget is shown by default on its designated page */
310
319
  showByDefault?: boolean;
311
320
  pluginId: string;
312
321
  }
@@ -19,12 +19,15 @@ interface WidgetContribution {
19
19
  id: string;
20
20
  title: string;
21
21
  component: string;
22
+ type: 'status' | 'maintenance' | 'analytics' | 'monitoring' | 'custom';
22
23
  priority?: number;
23
24
  showByDefault?: boolean;
24
25
  pluginId: string;
25
26
  }
26
27
 
27
28
  interface PluginWidgetRendererProps {
29
+ /** Filter widgets by type (e.g., 'status' for dashboard, 'maintenance' for maintenance page) */
30
+ widgetType?: 'status' | 'maintenance' | 'analytics' | 'monitoring' | 'custom';
28
31
  /** Only show widgets marked as showByDefault (default: true) */
29
32
  defaultOnly?: boolean;
30
33
  /** Additional widget IDs to show (beyond showByDefault) */
@@ -35,6 +38,7 @@ interface PluginWidgetRendererProps {
35
38
  * Renders widgets from plugins that have registered them via the server API
36
39
  */
37
40
  export function PluginWidgetRenderer({
41
+ widgetType,
38
42
  defaultOnly = true,
39
43
  additionalWidgetIds = [],
40
44
  }: PluginWidgetRendererProps) {
@@ -78,6 +82,10 @@ export function PluginWidgetRenderer({
78
82
  // Filter widgets to show
79
83
  const visibleWidgets = widgets
80
84
  .filter(widget => {
85
+ // Filter by widget type if specified
86
+ if (widgetType && widget.type !== widgetType) {
87
+ return false;
88
+ }
81
89
  // Show if marked as default, or if in additionalWidgetIds
82
90
  if (defaultOnly) {
83
91
  return widget.showByDefault || additionalWidgetIds.includes(widget.id);
@@ -16,7 +16,13 @@ import {
16
16
  AuthStatusWidget,
17
17
  NotificationsStatsWidget,
18
18
  CMSStatusWidget,
19
- CMSMaintenanceWidget
19
+ CMSMaintenanceWidget,
20
+ SeedManagementWidget,
21
+ ServiceControlWidget,
22
+ EnvironmentConfigWidget,
23
+ DatabaseOpsWidget,
24
+ LogsMaintenanceWidget,
25
+ CacheMaintenanceWidget,
20
26
  } from './widgets';
21
27
  import { PreferencesPage } from '../pages/PreferencesPage';
22
28
  import type { WidgetComponent } from './WidgetComponentRegistry';
@@ -32,6 +38,12 @@ export const builtInWidgetComponents: Record<string, React.ComponentType> = {
32
38
  NotificationsStatsWidget: NotificationsStatsWidget,
33
39
  CMSStatusWidget: CMSStatusWidget,
34
40
  CMSMaintenanceWidget: CMSMaintenanceWidget,
41
+ SeedManagementWidget: SeedManagementWidget,
42
+ ServiceControlWidget: ServiceControlWidget,
43
+ EnvironmentConfigWidget: EnvironmentConfigWidget,
44
+ DatabaseOpsWidget: DatabaseOpsWidget,
45
+ LogsMaintenanceWidget: LogsMaintenanceWidget,
46
+ CacheMaintenanceWidget: CacheMaintenanceWidget,
35
47
  PreferencesPage: PreferencesPage,
36
48
  };
37
49
 
@@ -50,6 +62,12 @@ export function getBuiltInWidgetComponents(): WidgetComponent[] {
50
62
  { name: 'NotificationsStatsWidget', component: NotificationsStatsWidget },
51
63
  { name: 'CMSStatusWidget', component: CMSStatusWidget },
52
64
  { name: 'CMSMaintenanceWidget', component: CMSMaintenanceWidget },
65
+ { name: 'SeedManagementWidget', component: SeedManagementWidget },
66
+ { name: 'ServiceControlWidget', component: ServiceControlWidget },
67
+ { name: 'EnvironmentConfigWidget', component: EnvironmentConfigWidget },
68
+ { name: 'DatabaseOpsWidget', component: DatabaseOpsWidget },
69
+ { name: 'LogsMaintenanceWidget', component: LogsMaintenanceWidget },
70
+ { name: 'CacheMaintenanceWidget', component: CacheMaintenanceWidget },
53
71
  { name: 'PreferencesPage', component: PreferencesPage },
54
72
  ];
55
73
  }
@@ -0,0 +1,195 @@
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
+
11
+ import { useState, useEffect } from 'react';
12
+ import {
13
+ Card,
14
+ CardContent,
15
+ Typography,
16
+ Button,
17
+ Alert,
18
+ Box,
19
+ Dialog,
20
+ DialogTitle,
21
+ DialogContent,
22
+ DialogContentText,
23
+ DialogActions,
24
+ CircularProgress,
25
+ Chip,
26
+ } from '@mui/material';
27
+ import DeleteIcon from '@mui/icons-material/Delete';
28
+ import RefreshIcon from '@mui/icons-material/Refresh';
29
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
30
+ import ErrorIcon from '@mui/icons-material/Error';
31
+
32
+ interface CacheStats {
33
+ connected: boolean;
34
+ keyCount: number;
35
+ usedMemory?: string;
36
+ }
37
+
38
+ export function CacheMaintenanceWidget() {
39
+ const [stats, setStats] = useState<CacheStats | null>(null);
40
+ const [loading, setLoading] = useState(true);
41
+ const [flushing, setFlushing] = useState(false);
42
+ const [error, setError] = useState<string | null>(null);
43
+ const [success, setSuccess] = useState<string | null>(null);
44
+ const [confirmOpen, setConfirmOpen] = useState(false);
45
+
46
+ const fetchStats = async () => {
47
+ setLoading(true);
48
+ setError(null);
49
+ try {
50
+ const response = await fetch('/api/cache:default/stats');
51
+ if (!response.ok) {
52
+ if (response.status === 404) {
53
+ throw new Error('Cache plugin not configured');
54
+ }
55
+ throw new Error('Failed to fetch cache stats');
56
+ }
57
+ const data = await response.json();
58
+ setStats(data);
59
+ } catch (err) {
60
+ setError(err instanceof Error ? err.message : 'Failed to fetch cache stats');
61
+ setStats(null);
62
+ } finally {
63
+ setLoading(false);
64
+ }
65
+ };
66
+
67
+ useEffect(() => {
68
+ fetchStats();
69
+ }, []);
70
+
71
+ const handleFlushCache = async () => {
72
+ setConfirmOpen(false);
73
+ setFlushing(true);
74
+ setError(null);
75
+ setSuccess(null);
76
+
77
+ try {
78
+ const response = await fetch('/api/cache:default/flush', {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ });
82
+
83
+ if (!response.ok) {
84
+ const data = await response.json();
85
+ throw new Error(data.error || 'Failed to flush cache');
86
+ }
87
+
88
+ const data = await response.json();
89
+ setSuccess(
90
+ data.message + (data.deletedCount !== undefined ? ` (${data.deletedCount} keys deleted)` : '')
91
+ );
92
+
93
+ // Refresh stats
94
+ await fetchStats();
95
+ } catch (err) {
96
+ setError(err instanceof Error ? err.message : 'Failed to flush cache');
97
+ } finally {
98
+ setFlushing(false);
99
+ }
100
+ };
101
+
102
+ return (
103
+ <Card>
104
+ <CardContent>
105
+ <Typography variant="h6" gutterBottom>
106
+ Cache Management
107
+ </Typography>
108
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
109
+ View cache statistics and clear cache
110
+ </Typography>
111
+
112
+ {error && (
113
+ <Alert severity="error" sx={{ mb: 2 }} onClose={() => setError(null)}>
114
+ {error}
115
+ </Alert>
116
+ )}
117
+
118
+ {success && (
119
+ <Alert severity="success" sx={{ mb: 2 }} onClose={() => setSuccess(null)}>
120
+ {success}
121
+ </Alert>
122
+ )}
123
+
124
+ {loading ? (
125
+ <Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
126
+ <CircularProgress size={30} />
127
+ </Box>
128
+ ) : stats ? (
129
+ <Box sx={{ mb: 2 }}>
130
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
131
+ <Typography variant="body2" color="text.secondary">
132
+ <strong>Status:</strong>
133
+ </Typography>
134
+ <Chip
135
+ size="small"
136
+ icon={stats.connected ? <CheckCircleIcon /> : <ErrorIcon />}
137
+ label={stats.connected ? 'Connected' : 'Disconnected'}
138
+ color={stats.connected ? 'success' : 'error'}
139
+ />
140
+ </Box>
141
+
142
+ <Typography variant="body2" color="text.secondary">
143
+ <strong>Key Count:</strong> {stats.keyCount.toLocaleString()}
144
+ </Typography>
145
+
146
+ {stats.usedMemory && (
147
+ <Typography variant="body2" color="text.secondary">
148
+ <strong>Memory Used:</strong> {stats.usedMemory}
149
+ </Typography>
150
+ )}
151
+ </Box>
152
+ ) : null}
153
+
154
+ <Box sx={{ display: 'flex', gap: 1 }}>
155
+ <Button
156
+ variant="outlined"
157
+ color="primary"
158
+ size="small"
159
+ startIcon={<RefreshIcon />}
160
+ onClick={fetchStats}
161
+ disabled={loading}
162
+ >
163
+ Refresh
164
+ </Button>
165
+ <Button
166
+ variant="contained"
167
+ color="error"
168
+ size="small"
169
+ startIcon={flushing ? <CircularProgress size={16} color="inherit" /> : <DeleteIcon />}
170
+ onClick={() => setConfirmOpen(true)}
171
+ disabled={!stats || !stats.connected || flushing || loading}
172
+ >
173
+ Flush Cache
174
+ </Button>
175
+ </Box>
176
+
177
+ <Dialog open={confirmOpen} onClose={() => setConfirmOpen(false)}>
178
+ <DialogTitle>Flush Cache</DialogTitle>
179
+ <DialogContent>
180
+ <DialogContentText>
181
+ Are you sure you want to flush the cache? This will delete{' '}
182
+ {stats?.keyCount.toLocaleString()} keys. This action cannot be undone.
183
+ </DialogContentText>
184
+ </DialogContent>
185
+ <DialogActions>
186
+ <Button onClick={() => setConfirmOpen(false)}>Cancel</Button>
187
+ <Button onClick={handleFlushCache} color="error" variant="contained">
188
+ Flush
189
+ </Button>
190
+ </DialogActions>
191
+ </Dialog>
192
+ </CardContent>
193
+ </Card>
194
+ );
195
+ }
@@ -0,0 +1,29 @@
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
+
10
+ import { Card, CardContent, Typography, Alert } from '@mui/material';
11
+
12
+ export function DatabaseOpsWidget() {
13
+ return (
14
+ <Card>
15
+ <CardContent>
16
+ <Typography variant="h6" gutterBottom>
17
+ Database Operations
18
+ </Typography>
19
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
20
+ Backup, restore, and maintain database
21
+ </Typography>
22
+
23
+ <Alert severity="info">
24
+ Database operations UI coming soon. This will allow you to backup and restore your database.
25
+ </Alert>
26
+ </CardContent>
27
+ </Card>
28
+ );
29
+ }
@@ -0,0 +1,29 @@
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
+
10
+ import { Card, CardContent, Typography, Alert } from '@mui/material';
11
+
12
+ export function EnvironmentConfigWidget() {
13
+ return (
14
+ <Card>
15
+ <CardContent>
16
+ <Typography variant="h6" gutterBottom>
17
+ Environment Configuration
18
+ </Typography>
19
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
20
+ View and manage environment variables
21
+ </Typography>
22
+
23
+ <Alert severity="info">
24
+ Environment configuration UI coming soon. This will allow you to view and edit environment variables.
25
+ </Alert>
26
+ </CardContent>
27
+ </Card>
28
+ );
29
+ }