@qwickapps/server 1.3.0 → 1.4.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.
- package/README.md +311 -0
- package/dist/core/control-panel.d.ts.map +1 -1
- package/dist/core/control-panel.js +144 -2
- package/dist/core/control-panel.js.map +1 -1
- package/dist/core/plugin-registry.d.ts +36 -0
- package/dist/core/plugin-registry.d.ts.map +1 -1
- package/dist/core/plugin-registry.js +26 -0
- package/dist/core/plugin-registry.js.map +1 -1
- package/dist/core/types.d.ts +19 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/plugins/auth/adapter-wrapper.d.ts +47 -0
- package/dist/plugins/auth/adapter-wrapper.d.ts.map +1 -0
- package/dist/plugins/auth/adapter-wrapper.js +166 -0
- package/dist/plugins/auth/adapter-wrapper.js.map +1 -0
- package/dist/plugins/auth/adapter-wrapper.test.d.ts +7 -0
- package/dist/plugins/auth/adapter-wrapper.test.d.ts.map +1 -0
- package/dist/plugins/auth/adapter-wrapper.test.js +303 -0
- package/dist/plugins/auth/adapter-wrapper.test.js.map +1 -0
- package/dist/plugins/auth/adapters/index.d.ts +1 -0
- package/dist/plugins/auth/adapters/index.d.ts.map +1 -1
- package/dist/plugins/auth/adapters/index.js +1 -0
- package/dist/plugins/auth/adapters/index.js.map +1 -1
- package/dist/plugins/auth/adapters/supabase-adapter.d.ts.map +1 -1
- package/dist/plugins/auth/adapters/supabase-adapter.js.map +1 -1
- package/dist/plugins/auth/adapters/supertokens-adapter.d.ts +18 -0
- package/dist/plugins/auth/adapters/supertokens-adapter.d.ts.map +1 -0
- package/dist/plugins/auth/adapters/supertokens-adapter.js +267 -0
- package/dist/plugins/auth/adapters/supertokens-adapter.js.map +1 -0
- package/dist/plugins/auth/config-store.d.ts +11 -0
- package/dist/plugins/auth/config-store.d.ts.map +1 -0
- package/dist/plugins/auth/config-store.js +232 -0
- package/dist/plugins/auth/config-store.js.map +1 -0
- package/dist/plugins/auth/config-store.test.d.ts +7 -0
- package/dist/plugins/auth/config-store.test.d.ts.map +1 -0
- package/dist/plugins/auth/config-store.test.js +299 -0
- package/dist/plugins/auth/config-store.test.js.map +1 -0
- package/dist/plugins/auth/env-config.d.ts +138 -0
- package/dist/plugins/auth/env-config.d.ts.map +1 -0
- package/dist/plugins/auth/env-config.js +1122 -0
- package/dist/plugins/auth/env-config.js.map +1 -0
- package/dist/plugins/auth/index.d.ts +7 -1
- package/dist/plugins/auth/index.d.ts.map +1 -1
- package/dist/plugins/auth/index.js +7 -0
- package/dist/plugins/auth/index.js.map +1 -1
- package/dist/plugins/auth/supertokens-adapter.test.d.ts +10 -0
- package/dist/plugins/auth/supertokens-adapter.test.d.ts.map +1 -0
- package/dist/plugins/auth/supertokens-adapter.test.js +486 -0
- package/dist/plugins/auth/supertokens-adapter.test.js.map +1 -0
- package/dist/plugins/auth/types.d.ts +176 -0
- package/dist/plugins/auth/types.d.ts.map +1 -1
- package/dist/plugins/auth/types.js.map +1 -1
- package/dist/plugins/cache-plugin.test.js +3 -0
- package/dist/plugins/cache-plugin.test.js.map +1 -1
- package/dist/plugins/index.d.ts +6 -2
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +5 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/postgres-plugin.test.js +3 -0
- package/dist/plugins/postgres-plugin.test.js.map +1 -1
- package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts +7 -0
- package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts.map +1 -0
- package/dist/plugins/preferences/__tests__/deep-merge.test.js +215 -0
- package/dist/plugins/preferences/__tests__/deep-merge.test.js.map +1 -0
- package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts +7 -0
- package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts.map +1 -0
- package/dist/plugins/preferences/__tests__/preferences-plugin.test.js +265 -0
- package/dist/plugins/preferences/__tests__/preferences-plugin.test.js.map +1 -0
- package/dist/plugins/preferences/index.d.ts +12 -0
- package/dist/plugins/preferences/index.d.ts.map +1 -0
- package/dist/plugins/preferences/index.js +13 -0
- package/dist/plugins/preferences/index.js.map +1 -0
- package/dist/plugins/preferences/preferences-plugin.d.ts +39 -0
- package/dist/plugins/preferences/preferences-plugin.d.ts.map +1 -0
- package/dist/plugins/preferences/preferences-plugin.js +226 -0
- package/dist/plugins/preferences/preferences-plugin.js.map +1 -0
- package/dist/plugins/preferences/stores/index.d.ts +9 -0
- package/dist/plugins/preferences/stores/index.d.ts.map +1 -0
- package/dist/plugins/preferences/stores/index.js +9 -0
- package/dist/plugins/preferences/stores/index.js.map +1 -0
- package/dist/plugins/preferences/stores/postgres-store.d.ts +41 -0
- package/dist/plugins/preferences/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/preferences/stores/postgres-store.js +181 -0
- package/dist/plugins/preferences/stores/postgres-store.js.map +1 -0
- package/dist/plugins/preferences/types.d.ts +91 -0
- package/dist/plugins/preferences/types.d.ts.map +1 -0
- package/dist/plugins/preferences/types.js +10 -0
- package/dist/plugins/preferences/types.js.map +1 -0
- package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts +7 -0
- package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts.map +1 -0
- package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js +220 -0
- package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js.map +1 -0
- package/dist/plugins/rate-limit/cleanup.d.ts +40 -0
- package/dist/plugins/rate-limit/cleanup.d.ts.map +1 -0
- package/dist/plugins/rate-limit/cleanup.js +72 -0
- package/dist/plugins/rate-limit/cleanup.js.map +1 -0
- package/dist/plugins/rate-limit/env-config.d.ts +91 -0
- package/dist/plugins/rate-limit/env-config.d.ts.map +1 -0
- package/dist/plugins/rate-limit/env-config.js +318 -0
- package/dist/plugins/rate-limit/env-config.js.map +1 -0
- package/dist/plugins/rate-limit/index.d.ts +76 -0
- package/dist/plugins/rate-limit/index.d.ts.map +1 -0
- package/dist/plugins/rate-limit/index.js +79 -0
- package/dist/plugins/rate-limit/index.js.map +1 -0
- package/dist/plugins/rate-limit/middleware.d.ts +40 -0
- package/dist/plugins/rate-limit/middleware.d.ts.map +1 -0
- package/dist/plugins/rate-limit/middleware.js +169 -0
- package/dist/plugins/rate-limit/middleware.js.map +1 -0
- package/dist/plugins/rate-limit/rate-limit-plugin.d.ts +44 -0
- package/dist/plugins/rate-limit/rate-limit-plugin.d.ts.map +1 -0
- package/dist/plugins/rate-limit/rate-limit-plugin.js +354 -0
- package/dist/plugins/rate-limit/rate-limit-plugin.js.map +1 -0
- package/dist/plugins/rate-limit/rate-limit-service.d.ts +110 -0
- package/dist/plugins/rate-limit/rate-limit-service.d.ts.map +1 -0
- package/dist/plugins/rate-limit/rate-limit-service.js +172 -0
- package/dist/plugins/rate-limit/rate-limit-service.js.map +1 -0
- package/dist/plugins/rate-limit/stores/cache-store.d.ts +33 -0
- package/dist/plugins/rate-limit/stores/cache-store.d.ts.map +1 -0
- package/dist/plugins/rate-limit/stores/cache-store.js +225 -0
- package/dist/plugins/rate-limit/stores/cache-store.js.map +1 -0
- package/dist/plugins/rate-limit/stores/index.d.ts +8 -0
- package/dist/plugins/rate-limit/stores/index.d.ts.map +1 -0
- package/dist/plugins/rate-limit/stores/index.js +8 -0
- package/dist/plugins/rate-limit/stores/index.js.map +1 -0
- package/dist/plugins/rate-limit/stores/postgres-store.d.ts +34 -0
- package/dist/plugins/rate-limit/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/rate-limit/stores/postgres-store.js +320 -0
- package/dist/plugins/rate-limit/stores/postgres-store.js.map +1 -0
- package/dist/plugins/rate-limit/strategies/fixed-window.d.ts +21 -0
- package/dist/plugins/rate-limit/strategies/fixed-window.d.ts.map +1 -0
- package/dist/plugins/rate-limit/strategies/fixed-window.js +97 -0
- package/dist/plugins/rate-limit/strategies/fixed-window.js.map +1 -0
- package/dist/plugins/rate-limit/strategies/index.d.ts +14 -0
- package/dist/plugins/rate-limit/strategies/index.d.ts.map +1 -0
- package/dist/plugins/rate-limit/strategies/index.js +27 -0
- package/dist/plugins/rate-limit/strategies/index.js.map +1 -0
- package/dist/plugins/rate-limit/strategies/sliding-window.d.ts +22 -0
- package/dist/plugins/rate-limit/strategies/sliding-window.d.ts.map +1 -0
- package/dist/plugins/rate-limit/strategies/sliding-window.js +122 -0
- package/dist/plugins/rate-limit/strategies/sliding-window.js.map +1 -0
- package/dist/plugins/rate-limit/strategies/token-bucket.d.ts +28 -0
- package/dist/plugins/rate-limit/strategies/token-bucket.d.ts.map +1 -0
- package/dist/plugins/rate-limit/strategies/token-bucket.js +121 -0
- package/dist/plugins/rate-limit/strategies/token-bucket.js.map +1 -0
- package/dist/plugins/rate-limit/types.d.ts +265 -0
- package/dist/plugins/rate-limit/types.d.ts.map +1 -0
- package/dist/plugins/rate-limit/types.js +9 -0
- package/dist/plugins/rate-limit/types.js.map +1 -0
- package/dist/plugins/users/__tests__/users-plugin.test.d.ts +9 -0
- package/dist/plugins/users/__tests__/users-plugin.test.d.ts.map +1 -0
- package/dist/plugins/users/__tests__/users-plugin.test.js +546 -0
- package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -0
- package/dist/plugins/users/index.d.ts +2 -2
- package/dist/plugins/users/index.d.ts.map +1 -1
- package/dist/plugins/users/index.js +1 -1
- package/dist/plugins/users/index.js.map +1 -1
- package/dist/plugins/users/types.d.ts +36 -0
- package/dist/plugins/users/types.d.ts.map +1 -1
- package/dist/plugins/users/users-plugin.d.ts +8 -2
- package/dist/plugins/users/users-plugin.d.ts.map +1 -1
- package/dist/plugins/users/users-plugin.js +122 -5
- package/dist/plugins/users/users-plugin.js.map +1 -1
- package/dist-ui/assets/index-D7DoZ9rL.js +478 -0
- package/dist-ui/assets/index-D7DoZ9rL.js.map +1 -0
- package/dist-ui/index.html +1 -1
- package/dist-ui-lib/api/controlPanelApi.d.ts +194 -7
- package/dist-ui-lib/dashboard/WidgetComponentRegistry.d.ts +9 -5
- package/dist-ui-lib/dashboard/builtInWidgets.d.ts +7 -1
- package/dist-ui-lib/dashboard/widgets/AuthStatusWidget.d.ts +9 -0
- package/dist-ui-lib/dashboard/widgets/IntegrationStatusWidget.d.ts +9 -0
- package/dist-ui-lib/dashboard/widgets/index.d.ts +2 -0
- package/dist-ui-lib/index.js +3665 -3945
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/pages/AuthPage.d.ts +1 -0
- package/dist-ui-lib/pages/IntegrationsPage.d.ts +1 -0
- package/dist-ui-lib/pages/PluginsPage.d.ts +1 -0
- package/dist-ui-lib/pages/RateLimitPage.d.ts +1 -0
- package/package.json +7 -2
- package/src/core/control-panel.ts +161 -2
- package/src/core/plugin-registry.ts +63 -0
- package/src/core/types.ts +17 -0
- package/src/index.ts +45 -0
- package/src/plugins/auth/adapter-wrapper.test.ts +395 -0
- package/src/plugins/auth/adapter-wrapper.ts +205 -0
- package/src/plugins/auth/adapters/index.ts +1 -0
- package/src/plugins/auth/adapters/supabase-adapter.ts +22 -14
- package/src/plugins/auth/adapters/supertokens-adapter.ts +326 -0
- package/src/plugins/auth/config-store.test.ts +417 -0
- package/src/plugins/auth/config-store.ts +305 -0
- package/src/plugins/auth/env-config.ts +1279 -0
- package/src/plugins/auth/index.ts +30 -0
- package/src/plugins/auth/supertokens-adapter.test.ts +621 -0
- package/src/plugins/auth/types.ts +218 -0
- package/src/plugins/cache-plugin.test.ts +3 -0
- package/src/plugins/index.ts +75 -0
- package/src/plugins/postgres-plugin.test.ts +3 -0
- package/src/plugins/preferences/__tests__/deep-merge.test.ts +242 -0
- package/src/plugins/preferences/__tests__/preferences-plugin.test.ts +350 -0
- package/src/plugins/preferences/index.ts +30 -0
- package/src/plugins/preferences/preferences-plugin.ts +270 -0
- package/src/plugins/preferences/stores/index.ts +9 -0
- package/src/plugins/preferences/stores/postgres-store.ts +252 -0
- package/src/plugins/preferences/types.ts +100 -0
- package/src/plugins/rate-limit/__tests__/rate-limit-plugin.test.ts +259 -0
- package/src/plugins/rate-limit/cleanup.ts +117 -0
- package/src/plugins/rate-limit/env-config.ts +400 -0
- package/src/plugins/rate-limit/index.ts +128 -0
- package/src/plugins/rate-limit/middleware.ts +212 -0
- package/src/plugins/rate-limit/rate-limit-plugin.ts +400 -0
- package/src/plugins/rate-limit/rate-limit-service.ts +228 -0
- package/src/plugins/rate-limit/stores/cache-store.ts +261 -0
- package/src/plugins/rate-limit/stores/index.ts +8 -0
- package/src/plugins/rate-limit/stores/postgres-store.ts +402 -0
- package/src/plugins/rate-limit/strategies/fixed-window.ts +116 -0
- package/src/plugins/rate-limit/strategies/index.ts +30 -0
- package/src/plugins/rate-limit/strategies/sliding-window.ts +157 -0
- package/src/plugins/rate-limit/strategies/token-bucket.ts +154 -0
- package/src/plugins/rate-limit/types.ts +338 -0
- package/src/plugins/users/__tests__/users-plugin.test.ts +690 -0
- package/src/plugins/users/index.ts +3 -0
- package/src/plugins/users/types.ts +38 -0
- package/src/plugins/users/users-plugin.ts +142 -5
- package/ui/src/App.tsx +35 -14
- package/ui/src/api/controlPanelApi.ts +326 -1
- package/ui/src/components/ControlPanelApp.tsx +3 -0
- package/ui/src/dashboard/PluginWidgetRenderer.tsx +13 -10
- package/ui/src/dashboard/WidgetComponentRegistry.tsx +13 -9
- package/ui/src/dashboard/builtInWidgets.tsx +13 -3
- package/ui/src/dashboard/widgets/AuthStatusWidget.tsx +143 -0
- package/ui/src/dashboard/widgets/IntegrationStatusWidget.tsx +135 -0
- package/ui/src/dashboard/widgets/index.ts +2 -0
- package/ui/src/pages/AuthPage.tsx +1103 -0
- package/ui/src/pages/IntegrationsPage.tsx +288 -0
- package/ui/src/pages/PluginsPage.tsx +394 -0
- package/ui/src/pages/RateLimitPage.tsx +292 -0
- package/ui/vite.lib.config.ts +5 -0
- package/dist-ui/assets/index-Bsp2ntcw.js +0 -465
- package/dist-ui/assets/index-Bsp2ntcw.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function AuthPage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function IntegrationsPage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function PluginsPage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function RateLimitPage(): import("react/jsx-runtime").JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qwickapps/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Plugin-based application server framework for building websites, APIs, admin dashboards, and full-stack products",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"react": "^18.2.0",
|
|
77
77
|
"react-dom": "^18.2.0",
|
|
78
78
|
"react-router-dom": "^6.30.1",
|
|
79
|
+
"supertokens-node": "^20.1.7",
|
|
79
80
|
"tsx": "^4.20.6",
|
|
80
81
|
"typescript": "^5.3.3",
|
|
81
82
|
"vite": "^6.0.0",
|
|
@@ -85,7 +86,8 @@
|
|
|
85
86
|
"@qwickapps/react-framework": ">=1.0.0",
|
|
86
87
|
"express-openid-connect": ">=2.0.0",
|
|
87
88
|
"ioredis": ">=5.0.0",
|
|
88
|
-
"pg": ">=8.0.0"
|
|
89
|
+
"pg": ">=8.0.0",
|
|
90
|
+
"supertokens-node": ">=20.0.0"
|
|
89
91
|
},
|
|
90
92
|
"peerDependenciesMeta": {
|
|
91
93
|
"@qwickapps/react-framework": {
|
|
@@ -99,6 +101,9 @@
|
|
|
99
101
|
},
|
|
100
102
|
"pg": {
|
|
101
103
|
"optional": true
|
|
104
|
+
},
|
|
105
|
+
"supertokens-node": {
|
|
106
|
+
"optional": true
|
|
102
107
|
}
|
|
103
108
|
},
|
|
104
109
|
"keywords": [
|
|
@@ -197,11 +197,42 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
|
|
|
197
197
|
});
|
|
198
198
|
|
|
199
199
|
/**
|
|
200
|
-
* GET /api/plugins - List all registered plugins
|
|
200
|
+
* GET /api/plugins - List all registered plugins with contribution counts
|
|
201
201
|
*/
|
|
202
202
|
router.get('/plugins', (_req: Request, res: Response) => {
|
|
203
|
+
const plugins = pluginRegistry.listPlugins().map((plugin) => {
|
|
204
|
+
const contributions = pluginRegistry.getPluginContributions(plugin.id);
|
|
205
|
+
return {
|
|
206
|
+
...plugin,
|
|
207
|
+
contributionCounts: {
|
|
208
|
+
routes: contributions.routes.length,
|
|
209
|
+
menuItems: contributions.menuItems.length,
|
|
210
|
+
pages: contributions.pages.length,
|
|
211
|
+
widgets: contributions.widgets.length,
|
|
212
|
+
hasConfig: !!contributions.config,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
res.json({ plugins });
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* GET /api/plugins/:id - Get detailed plugin info with contributions
|
|
221
|
+
*/
|
|
222
|
+
router.get('/plugins/:id', (req: Request, res: Response) => {
|
|
223
|
+
const { id } = req.params;
|
|
224
|
+
const plugins = pluginRegistry.listPlugins();
|
|
225
|
+
const plugin = plugins.find((p) => p.id === id);
|
|
226
|
+
|
|
227
|
+
if (!plugin) {
|
|
228
|
+
res.status(404).json({ error: `Plugin not found: ${id}` });
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const contributions = pluginRegistry.getPluginContributions(id);
|
|
203
233
|
res.json({
|
|
204
|
-
|
|
234
|
+
...plugin,
|
|
235
|
+
contributions,
|
|
205
236
|
});
|
|
206
237
|
});
|
|
207
238
|
|
|
@@ -289,6 +320,22 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
|
|
|
289
320
|
}
|
|
290
321
|
}
|
|
291
322
|
|
|
323
|
+
// Serve landing page at root (/) when control panel is mounted elsewhere
|
|
324
|
+
if (mountPath !== '/' && config.landingPage !== false) {
|
|
325
|
+
const landingConfig = config.landingPage || {};
|
|
326
|
+
app.get('/', (_req: Request, res: Response) => {
|
|
327
|
+
const html = generateLandingPageHtml({
|
|
328
|
+
productName: config.productName,
|
|
329
|
+
title: landingConfig.title || config.productName,
|
|
330
|
+
heading: landingConfig.heading || `Welcome to ${config.productName}`,
|
|
331
|
+
description: landingConfig.description || `${config.productName} is running.`,
|
|
332
|
+
controlPanelPath: mountPath,
|
|
333
|
+
links: landingConfig.links,
|
|
334
|
+
});
|
|
335
|
+
res.type('html').send(html);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
292
339
|
// Start a plugin with the registry
|
|
293
340
|
const startPlugin = async (plugin: Plugin, pluginConfig: PluginConfig = {}): Promise<boolean> => {
|
|
294
341
|
return pluginRegistry.startPlugin(plugin, pluginConfig);
|
|
@@ -533,3 +580,115 @@ function generateDashboardHtml(
|
|
|
533
580
|
</body>
|
|
534
581
|
</html>`;
|
|
535
582
|
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Generate landing page HTML for root path
|
|
586
|
+
*/
|
|
587
|
+
function generateLandingPageHtml(options: {
|
|
588
|
+
productName: string;
|
|
589
|
+
title: string;
|
|
590
|
+
heading: string;
|
|
591
|
+
description: string;
|
|
592
|
+
controlPanelPath: string;
|
|
593
|
+
links?: Array<{ label: string; url: string }>;
|
|
594
|
+
}): string {
|
|
595
|
+
const { productName, title, heading, description, controlPanelPath, links = [] } = options;
|
|
596
|
+
|
|
597
|
+
const linksHtml = [
|
|
598
|
+
{ label: 'Control Panel', url: controlPanelPath },
|
|
599
|
+
...links,
|
|
600
|
+
]
|
|
601
|
+
.map(
|
|
602
|
+
(link) =>
|
|
603
|
+
`<a href="${link.url}" class="link">${link.label}</a>`
|
|
604
|
+
)
|
|
605
|
+
.join('');
|
|
606
|
+
|
|
607
|
+
return `<!DOCTYPE html>
|
|
608
|
+
<html lang="en">
|
|
609
|
+
<head>
|
|
610
|
+
<meta charset="UTF-8">
|
|
611
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
612
|
+
<title>${title}</title>
|
|
613
|
+
<style>
|
|
614
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
615
|
+
body {
|
|
616
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
617
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%);
|
|
618
|
+
color: #e2e8f0;
|
|
619
|
+
min-height: 100vh;
|
|
620
|
+
display: flex;
|
|
621
|
+
align-items: center;
|
|
622
|
+
justify-content: center;
|
|
623
|
+
}
|
|
624
|
+
.container {
|
|
625
|
+
text-align: center;
|
|
626
|
+
max-width: 600px;
|
|
627
|
+
padding: 2rem;
|
|
628
|
+
}
|
|
629
|
+
.logo {
|
|
630
|
+
width: 80px;
|
|
631
|
+
height: 80px;
|
|
632
|
+
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
633
|
+
border-radius: 20px;
|
|
634
|
+
margin: 0 auto 2rem;
|
|
635
|
+
display: flex;
|
|
636
|
+
align-items: center;
|
|
637
|
+
justify-content: center;
|
|
638
|
+
font-size: 2rem;
|
|
639
|
+
font-weight: bold;
|
|
640
|
+
}
|
|
641
|
+
h1 {
|
|
642
|
+
font-size: 2.5rem;
|
|
643
|
+
margin-bottom: 1rem;
|
|
644
|
+
background: linear-gradient(135deg, #6366f1 0%, #a78bfa 100%);
|
|
645
|
+
-webkit-background-clip: text;
|
|
646
|
+
-webkit-text-fill-color: transparent;
|
|
647
|
+
background-clip: text;
|
|
648
|
+
}
|
|
649
|
+
p {
|
|
650
|
+
color: #94a3b8;
|
|
651
|
+
font-size: 1.125rem;
|
|
652
|
+
margin-bottom: 2rem;
|
|
653
|
+
line-height: 1.6;
|
|
654
|
+
}
|
|
655
|
+
.links {
|
|
656
|
+
display: flex;
|
|
657
|
+
flex-wrap: wrap;
|
|
658
|
+
gap: 1rem;
|
|
659
|
+
justify-content: center;
|
|
660
|
+
}
|
|
661
|
+
.link {
|
|
662
|
+
display: inline-block;
|
|
663
|
+
padding: 0.875rem 2rem;
|
|
664
|
+
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
665
|
+
color: white;
|
|
666
|
+
text-decoration: none;
|
|
667
|
+
border-radius: 0.75rem;
|
|
668
|
+
font-weight: 600;
|
|
669
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
670
|
+
}
|
|
671
|
+
.link:hover {
|
|
672
|
+
transform: translateY(-2px);
|
|
673
|
+
box-shadow: 0 10px 25px -5px rgba(99, 102, 241, 0.4);
|
|
674
|
+
}
|
|
675
|
+
.footer {
|
|
676
|
+
margin-top: 3rem;
|
|
677
|
+
color: #475569;
|
|
678
|
+
font-size: 0.875rem;
|
|
679
|
+
}
|
|
680
|
+
</style>
|
|
681
|
+
</head>
|
|
682
|
+
<body>
|
|
683
|
+
<div class="container">
|
|
684
|
+
<div class="logo">${productName.charAt(0)}</div>
|
|
685
|
+
<h1>${heading}</h1>
|
|
686
|
+
<p>${description}</p>
|
|
687
|
+
<div class="links">${linksHtml}</div>
|
|
688
|
+
<div class="footer">
|
|
689
|
+
Powered by QwickApps Server
|
|
690
|
+
</div>
|
|
691
|
+
</div>
|
|
692
|
+
</body>
|
|
693
|
+
</html>`;
|
|
694
|
+
}
|
|
@@ -164,6 +164,31 @@ export interface RouteDefinition {
|
|
|
164
164
|
pluginId: string;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Configuration UI contribution for plugin settings
|
|
169
|
+
*/
|
|
170
|
+
export interface ConfigContribution {
|
|
171
|
+
/** Unique ID for this config contribution */
|
|
172
|
+
id: string;
|
|
173
|
+
/** React component name to render (matched by frontend registry) */
|
|
174
|
+
component: string;
|
|
175
|
+
/** Display title for the config section */
|
|
176
|
+
title?: string;
|
|
177
|
+
/** Plugin ID that contributed this */
|
|
178
|
+
pluginId: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Aggregated contributions for a specific plugin
|
|
183
|
+
*/
|
|
184
|
+
export interface PluginContributions {
|
|
185
|
+
routes: Array<{ method: string; path: string }>;
|
|
186
|
+
menuItems: MenuContribution[];
|
|
187
|
+
pages: PageContribution[];
|
|
188
|
+
widgets: WidgetContribution[];
|
|
189
|
+
config?: ConfigContribution;
|
|
190
|
+
}
|
|
191
|
+
|
|
167
192
|
// =============================================================================
|
|
168
193
|
// Plugin Registry Interface
|
|
169
194
|
// =============================================================================
|
|
@@ -204,6 +229,9 @@ export interface PluginRegistry {
|
|
|
204
229
|
/** Register a widget */
|
|
205
230
|
addWidget(widget: WidgetContribution): void;
|
|
206
231
|
|
|
232
|
+
/** Register a config component for plugin settings UI */
|
|
233
|
+
addConfigComponent(config: ConfigContribution): void;
|
|
234
|
+
|
|
207
235
|
// ---------------------------------------------------------------------------
|
|
208
236
|
// Contribution queries
|
|
209
237
|
// ---------------------------------------------------------------------------
|
|
@@ -220,6 +248,12 @@ export interface PluginRegistry {
|
|
|
220
248
|
/** Get all widgets */
|
|
221
249
|
getWidgets(): WidgetContribution[];
|
|
222
250
|
|
|
251
|
+
/** Get all config components */
|
|
252
|
+
getConfigComponents(): ConfigContribution[];
|
|
253
|
+
|
|
254
|
+
/** Get all contributions for a specific plugin */
|
|
255
|
+
getPluginContributions(pluginId: string): PluginContributions;
|
|
256
|
+
|
|
223
257
|
// ---------------------------------------------------------------------------
|
|
224
258
|
// Configuration
|
|
225
259
|
// ---------------------------------------------------------------------------
|
|
@@ -292,6 +326,7 @@ export class PluginRegistryImpl implements PluginRegistry {
|
|
|
292
326
|
private menuItems: MenuContribution[] = [];
|
|
293
327
|
private pages: PageContribution[] = [];
|
|
294
328
|
private widgets: WidgetContribution[] = [];
|
|
329
|
+
private configComponents: ConfigContribution[] = [];
|
|
295
330
|
|
|
296
331
|
private eventHandlers = new Set<PluginEventHandler>();
|
|
297
332
|
|
|
@@ -385,6 +420,17 @@ export class PluginRegistryImpl implements PluginRegistry {
|
|
|
385
420
|
this.logger.debug(`Widget registered: ${widget.title} by ${widget.pluginId}`);
|
|
386
421
|
}
|
|
387
422
|
|
|
423
|
+
addConfigComponent(config: ConfigContribution): void {
|
|
424
|
+
// Only one config component per plugin - warn if replacing
|
|
425
|
+
const existing = this.configComponents.find((c) => c.pluginId === config.pluginId);
|
|
426
|
+
if (existing) {
|
|
427
|
+
this.logger.warn(`Replacing config component for plugin ${config.pluginId}: ${existing.component} → ${config.component}`);
|
|
428
|
+
}
|
|
429
|
+
this.configComponents = this.configComponents.filter((c) => c.pluginId !== config.pluginId);
|
|
430
|
+
this.configComponents.push(config);
|
|
431
|
+
this.logger.debug(`Config component registered: ${config.component} by ${config.pluginId}`);
|
|
432
|
+
}
|
|
433
|
+
|
|
388
434
|
// ---------------------------------------------------------------------------
|
|
389
435
|
// Contribution queries
|
|
390
436
|
// ---------------------------------------------------------------------------
|
|
@@ -405,6 +451,22 @@ export class PluginRegistryImpl implements PluginRegistry {
|
|
|
405
451
|
return [...this.widgets];
|
|
406
452
|
}
|
|
407
453
|
|
|
454
|
+
getConfigComponents(): ConfigContribution[] {
|
|
455
|
+
return [...this.configComponents];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
getPluginContributions(pluginId: string): PluginContributions {
|
|
459
|
+
return {
|
|
460
|
+
routes: this.routes
|
|
461
|
+
.filter((r) => r.pluginId === pluginId)
|
|
462
|
+
.map((r) => ({ method: r.method, path: r.path })),
|
|
463
|
+
menuItems: this.menuItems.filter((m) => m.pluginId === pluginId),
|
|
464
|
+
pages: this.pages.filter((p) => p.pluginId === pluginId),
|
|
465
|
+
widgets: this.widgets.filter((w) => w.pluginId === pluginId),
|
|
466
|
+
config: this.configComponents.find((c) => c.pluginId === pluginId),
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
408
470
|
// ---------------------------------------------------------------------------
|
|
409
471
|
// Configuration
|
|
410
472
|
// ---------------------------------------------------------------------------
|
|
@@ -577,6 +639,7 @@ export class PluginRegistryImpl implements PluginRegistry {
|
|
|
577
639
|
this.menuItems = this.menuItems.filter((m) => m.pluginId !== pluginId);
|
|
578
640
|
this.pages = this.pages.filter((p) => p.pluginId !== pluginId);
|
|
579
641
|
this.widgets = this.widgets.filter((w) => w.pluginId !== pluginId);
|
|
642
|
+
this.configComponents = this.configComponents.filter((c) => c.pluginId !== pluginId);
|
|
580
643
|
|
|
581
644
|
this.emit({
|
|
582
645
|
type: 'plugin:stopped',
|
package/src/core/types.ts
CHANGED
|
@@ -175,6 +175,23 @@ export interface ControlPanelConfig {
|
|
|
175
175
|
|
|
176
176
|
/** Optional: Custom path to a dist-ui folder for serving a custom React UI */
|
|
177
177
|
customUiPath?: string;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Optional: Landing page configuration for root path (/).
|
|
181
|
+
* Only used when mountPath is not '/'.
|
|
182
|
+
* If not provided, a default landing page is generated.
|
|
183
|
+
* Set to false to disable the landing page.
|
|
184
|
+
*/
|
|
185
|
+
landingPage?: {
|
|
186
|
+
/** Page title */
|
|
187
|
+
title?: string;
|
|
188
|
+
/** Main heading */
|
|
189
|
+
heading?: string;
|
|
190
|
+
/** Description text */
|
|
191
|
+
description?: string;
|
|
192
|
+
/** Additional links */
|
|
193
|
+
links?: Array<{ label: string; url: string }>;
|
|
194
|
+
} | false;
|
|
178
195
|
}
|
|
179
196
|
|
|
180
197
|
// Plugin types are now in plugin-registry.ts
|
package/src/index.ts
CHANGED
|
@@ -89,6 +89,10 @@ export {
|
|
|
89
89
|
hasCache,
|
|
90
90
|
// Auth plugin
|
|
91
91
|
createAuthPlugin,
|
|
92
|
+
createAuthPluginFromEnv,
|
|
93
|
+
getAuthStatus,
|
|
94
|
+
setAuthConfigStore,
|
|
95
|
+
postgresAuthConfigStore,
|
|
92
96
|
isAuthenticated,
|
|
93
97
|
getAuthenticatedUser,
|
|
94
98
|
getAccessToken,
|
|
@@ -98,6 +102,7 @@ export {
|
|
|
98
102
|
auth0Adapter,
|
|
99
103
|
basicAdapter,
|
|
100
104
|
supabaseAdapter,
|
|
105
|
+
supertokensAdapter,
|
|
101
106
|
isAuthenticatedRequest,
|
|
102
107
|
// Users plugin
|
|
103
108
|
createUsersPlugin,
|
|
@@ -137,6 +142,28 @@ export {
|
|
|
137
142
|
requireAnyEntitlement,
|
|
138
143
|
requireAllEntitlements,
|
|
139
144
|
postgresEntitlementSource,
|
|
145
|
+
// Rate Limit plugin
|
|
146
|
+
createRateLimitPlugin,
|
|
147
|
+
createRateLimitPluginFromEnv,
|
|
148
|
+
getRateLimitConfigStatus,
|
|
149
|
+
postgresRateLimitStore,
|
|
150
|
+
createRateLimitCache,
|
|
151
|
+
createNoOpCache,
|
|
152
|
+
createSlidingWindowStrategy,
|
|
153
|
+
createFixedWindowStrategy,
|
|
154
|
+
createTokenBucketStrategy,
|
|
155
|
+
getStrategy,
|
|
156
|
+
rateLimitMiddleware,
|
|
157
|
+
rateLimitStatusMiddleware,
|
|
158
|
+
RateLimitService,
|
|
159
|
+
getRateLimitService,
|
|
160
|
+
isLimited,
|
|
161
|
+
checkLimit,
|
|
162
|
+
incrementLimit,
|
|
163
|
+
getRemainingRequests,
|
|
164
|
+
getLimitStatus,
|
|
165
|
+
clearLimit,
|
|
166
|
+
createCleanupJob,
|
|
140
167
|
} from './plugins/index.js';
|
|
141
168
|
export type {
|
|
142
169
|
HealthPluginConfig,
|
|
@@ -159,6 +186,12 @@ export type {
|
|
|
159
186
|
Auth0AdapterConfig,
|
|
160
187
|
SupabaseAdapterConfig,
|
|
161
188
|
BasicAdapterConfig,
|
|
189
|
+
SupertokensAdapterConfig,
|
|
190
|
+
AuthPluginState,
|
|
191
|
+
AuthEnvPluginOptions,
|
|
192
|
+
AuthConfigStatus,
|
|
193
|
+
AuthConfigStore,
|
|
194
|
+
PostgresAuthConfigStoreConfig,
|
|
162
195
|
// Users plugin types
|
|
163
196
|
UsersPluginConfig,
|
|
164
197
|
UserStore,
|
|
@@ -191,4 +224,16 @@ export type {
|
|
|
191
224
|
UserEntitlement,
|
|
192
225
|
CachedEntitlements,
|
|
193
226
|
EntitlementStats,
|
|
227
|
+
// Rate Limit plugin types
|
|
228
|
+
RateLimitPluginConfig,
|
|
229
|
+
RateLimitEnvPluginOptions,
|
|
230
|
+
RateLimitStrategy,
|
|
231
|
+
LimitStatus,
|
|
232
|
+
StoredLimit,
|
|
233
|
+
IncrementOptions,
|
|
234
|
+
RateLimitStore,
|
|
235
|
+
PostgresRateLimitStoreConfig,
|
|
236
|
+
RateLimitCache,
|
|
237
|
+
RateLimitCacheConfig,
|
|
238
|
+
RateLimitMiddlewareOptions,
|
|
194
239
|
} from './plugins/index.js';
|