@qwickapps/server 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/src/core/control-panel.js +5 -5
- package/dist/src/core/control-panel.js.map +1 -1
- package/dist/src/core/gateway.d.ts.map +1 -1
- package/dist/src/core/gateway.js +117 -15
- package/dist/src/core/gateway.js.map +1 -1
- package/dist/src/core/plugin-registry.d.ts +70 -0
- package/dist/src/core/plugin-registry.d.ts.map +1 -1
- package/dist/src/core/plugin-registry.js +94 -0
- package/dist/src/core/plugin-registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/api-keys-plugin.d.ts +5 -2
- package/dist/src/plugins/api-keys/api-keys-plugin.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/api-keys-plugin.js +113 -19
- package/dist/src/plugins/api-keys/api-keys-plugin.js.map +1 -1
- package/dist/src/plugins/api-keys/index.d.ts +1 -5
- package/dist/src/plugins/api-keys/index.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/index.js +2 -3
- package/dist/src/plugins/api-keys/index.js.map +1 -1
- package/dist/src/plugins/api-keys/stores/postgres-store.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/stores/postgres-store.js +83 -65
- package/dist/src/plugins/api-keys/stores/postgres-store.js.map +1 -1
- package/dist/src/plugins/api-keys/types.d.ts +22 -4
- package/dist/src/plugins/api-keys/types.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/types.js.map +1 -1
- package/dist/src/plugins/auth/index.d.ts +0 -4
- package/dist/src/plugins/auth/index.d.ts.map +1 -1
- package/dist/src/plugins/auth/index.js +2 -3
- package/dist/src/plugins/auth/index.js.map +1 -1
- package/dist/src/plugins/bans/bans-plugin.d.ts +5 -2
- package/dist/src/plugins/bans/bans-plugin.d.ts.map +1 -1
- package/dist/src/plugins/bans/bans-plugin.js +71 -25
- package/dist/src/plugins/bans/bans-plugin.js.map +1 -1
- package/dist/src/plugins/bans/index.d.ts +0 -4
- package/dist/src/plugins/bans/index.d.ts.map +1 -1
- package/dist/src/plugins/bans/index.js +2 -3
- package/dist/src/plugins/bans/index.js.map +1 -1
- package/dist/src/plugins/bans/types.d.ts +13 -6
- package/dist/src/plugins/bans/types.d.ts.map +1 -1
- package/dist/src/plugins/devices/devices-plugin.d.ts +5 -2
- package/dist/src/plugins/devices/devices-plugin.d.ts.map +1 -1
- package/dist/src/plugins/devices/devices-plugin.js +62 -26
- package/dist/src/plugins/devices/devices-plugin.js.map +1 -1
- package/dist/src/plugins/devices/index.d.ts +0 -4
- package/dist/src/plugins/devices/index.d.ts.map +1 -1
- package/dist/src/plugins/devices/index.js +2 -3
- package/dist/src/plugins/devices/index.js.map +1 -1
- package/dist/src/plugins/diagnostics-plugin.d.ts.map +1 -1
- package/dist/src/plugins/diagnostics-plugin.js +73 -0
- package/dist/src/plugins/diagnostics-plugin.js.map +1 -1
- package/dist/src/plugins/entitlements/entitlements-plugin.d.ts +5 -2
- package/dist/src/plugins/entitlements/entitlements-plugin.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/entitlements-plugin.js +78 -41
- package/dist/src/plugins/entitlements/entitlements-plugin.js.map +1 -1
- package/dist/src/plugins/entitlements/index.d.ts +0 -4
- package/dist/src/plugins/entitlements/index.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/index.js +2 -3
- package/dist/src/plugins/entitlements/index.js.map +1 -1
- package/dist/src/plugins/entitlements/types.d.ts +9 -2
- package/dist/src/plugins/entitlements/types.d.ts.map +1 -1
- package/dist/src/plugins/index.d.ts +1 -1
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedExecutor.d.ts +2 -0
- package/dist/src/plugins/maintenance/SeedExecutor.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedExecutor.js +6 -2
- package/dist/src/plugins/maintenance/SeedExecutor.js.map +1 -1
- package/dist/src/plugins/maintenance/SeedList.d.ts +2 -2
- package/dist/src/plugins/maintenance/SeedList.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedList.js +39 -14
- package/dist/src/plugins/maintenance/SeedList.js.map +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.d.ts +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js +9 -5
- package/dist/src/plugins/maintenance/SeedManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/seed-executor.d.ts +6 -4
- package/dist/src/plugins/maintenance/seed-executor.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/seed-executor.js +53 -17
- package/dist/src/plugins/maintenance/seed-executor.js.map +1 -1
- package/dist/src/plugins/maintenance-plugin.d.ts +24 -0
- package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
- package/dist/src/plugins/maintenance-plugin.js +222 -34
- package/dist/src/plugins/maintenance-plugin.js.map +1 -1
- package/dist/src/plugins/notifications/index.d.ts +0 -4
- package/dist/src/plugins/notifications/index.d.ts.map +1 -1
- package/dist/src/plugins/notifications/index.js +2 -3
- package/dist/src/plugins/notifications/index.js.map +1 -1
- package/dist/src/plugins/notifications/notifications-plugin.d.ts +5 -2
- package/dist/src/plugins/notifications/notifications-plugin.d.ts.map +1 -1
- package/dist/src/plugins/notifications/notifications-plugin.js +45 -13
- package/dist/src/plugins/notifications/notifications-plugin.js.map +1 -1
- package/dist/src/plugins/parental/index.d.ts +0 -4
- package/dist/src/plugins/parental/index.d.ts.map +1 -1
- package/dist/src/plugins/parental/index.js +2 -3
- package/dist/src/plugins/parental/index.js.map +1 -1
- package/dist/src/plugins/parental/parental-plugin.d.ts +5 -2
- package/dist/src/plugins/parental/parental-plugin.d.ts.map +1 -1
- package/dist/src/plugins/parental/parental-plugin.js +60 -24
- package/dist/src/plugins/parental/parental-plugin.js.map +1 -1
- package/dist/src/plugins/postgres-plugin.d.ts +12 -0
- package/dist/src/plugins/postgres-plugin.d.ts.map +1 -1
- package/dist/src/plugins/postgres-plugin.js +319 -5
- package/dist/src/plugins/postgres-plugin.js.map +1 -1
- package/dist/src/plugins/preferences/index.d.ts +0 -4
- package/dist/src/plugins/preferences/index.d.ts.map +1 -1
- package/dist/src/plugins/preferences/index.js +2 -3
- package/dist/src/plugins/preferences/index.js.map +1 -1
- package/dist/src/plugins/preferences/preferences-plugin.d.ts +5 -2
- package/dist/src/plugins/preferences/preferences-plugin.d.ts.map +1 -1
- package/dist/src/plugins/preferences/preferences-plugin.js +63 -19
- package/dist/src/plugins/preferences/preferences-plugin.js.map +1 -1
- package/dist/src/plugins/profiles/index.d.ts +0 -4
- package/dist/src/plugins/profiles/index.d.ts.map +1 -1
- package/dist/src/plugins/profiles/index.js +2 -3
- package/dist/src/plugins/profiles/index.js.map +1 -1
- package/dist/src/plugins/profiles/profiles-plugin.d.ts +5 -2
- package/dist/src/plugins/profiles/profiles-plugin.d.ts.map +1 -1
- package/dist/src/plugins/profiles/profiles-plugin.js +60 -26
- package/dist/src/plugins/profiles/profiles-plugin.js.map +1 -1
- package/dist/src/plugins/profiles/types.d.ts +9 -2
- package/dist/src/plugins/profiles/types.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/index.d.ts +0 -4
- package/dist/src/plugins/qwickbrain/index.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/index.js +2 -3
- package/dist/src/plugins/qwickbrain/index.js.map +1 -1
- package/dist/src/plugins/qwickbrain/qwickbrain-plugin.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js +117 -0
- package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js.map +1 -1
- package/dist/src/plugins/rate-limit/index.d.ts +0 -4
- package/dist/src/plugins/rate-limit/index.d.ts.map +1 -1
- package/dist/src/plugins/rate-limit/index.js +2 -3
- package/dist/src/plugins/rate-limit/index.js.map +1 -1
- package/dist/src/plugins/subscriptions/index.d.ts +0 -4
- package/dist/src/plugins/subscriptions/index.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/index.js +2 -3
- package/dist/src/plugins/subscriptions/index.js.map +1 -1
- package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts +5 -2
- package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/subscriptions-plugin.js +63 -29
- package/dist/src/plugins/subscriptions/subscriptions-plugin.js.map +1 -1
- package/dist/src/plugins/subscriptions/types.d.ts +8 -2
- package/dist/src/plugins/subscriptions/types.d.ts.map +1 -1
- package/dist/src/plugins/tenants/tenants-plugin.d.ts +5 -2
- package/dist/src/plugins/tenants/tenants-plugin.d.ts.map +1 -1
- package/dist/src/plugins/tenants/tenants-plugin.js +91 -58
- package/dist/src/plugins/tenants/tenants-plugin.js.map +1 -1
- package/dist/src/plugins/tenants/types.d.ts +8 -2
- package/dist/src/plugins/tenants/types.d.ts.map +1 -1
- package/dist/src/plugins/usage/index.d.ts +0 -4
- package/dist/src/plugins/usage/index.d.ts.map +1 -1
- package/dist/src/plugins/usage/index.js +2 -3
- package/dist/src/plugins/usage/index.js.map +1 -1
- package/dist/src/plugins/usage/usage-plugin.d.ts +5 -2
- package/dist/src/plugins/usage/usage-plugin.d.ts.map +1 -1
- package/dist/src/plugins/usage/usage-plugin.js +57 -23
- package/dist/src/plugins/usage/usage-plugin.js.map +1 -1
- package/dist/src/plugins/users/types.d.ts +7 -2
- package/dist/src/plugins/users/types.d.ts.map +1 -1
- package/dist/src/plugins/users/users-plugin.d.ts +5 -2
- package/dist/src/plugins/users/users-plugin.d.ts.map +1 -1
- package/dist/src/plugins/users/users-plugin.js +56 -23
- package/dist/src/plugins/users/users-plugin.js.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.d.ts.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.js +4 -3
- package/dist/ui/src/components/ControlPanelApp.js.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.js +3 -1
- package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js +17 -4
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js +5 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +4 -2
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts +12 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js +174 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +6 -3
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts +1 -1
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +256 -16
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.d.ts +1 -0
- package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.js +1 -0
- package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
- package/dist-ui/assets/index-BkGp7ZKd.js +529 -0
- package/dist-ui/assets/index-BkGp7ZKd.js.map +1 -0
- package/dist-ui/index.html +1 -1
- package/dist-ui-lib/index.js +3735 -3187
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/src/dashboard/widgets/DatabaseOperationsWidget.d.ts +11 -0
- package/dist-ui-lib/src/dashboard/widgets/SeedManagementWidget.d.ts +1 -1
- package/dist-ui-lib/src/dashboard/widgets/index.d.ts +1 -0
- package/package.json +8 -5
- package/src/core/control-panel.ts +5 -5
- package/src/core/gateway.ts +135 -15
- package/src/core/plugin-registry.ts +171 -0
- package/src/index.ts +2 -0
- package/src/plugins/api-keys/api-keys-plugin.ts +121 -20
- package/src/plugins/api-keys/index.ts +3 -5
- package/src/plugins/api-keys/stores/postgres-store.ts +90 -67
- package/src/plugins/api-keys/types.ts +23 -4
- package/src/plugins/auth/index.ts +3 -5
- package/src/plugins/bans/bans-plugin.ts +71 -26
- package/src/plugins/bans/index.ts +3 -5
- package/src/plugins/bans/types.ts +13 -6
- package/src/plugins/devices/devices-plugin.ts +62 -27
- package/src/plugins/devices/index.ts +3 -5
- package/src/plugins/diagnostics-plugin.ts +77 -0
- package/src/plugins/entitlements/entitlements-plugin.ts +81 -43
- package/src/plugins/entitlements/index.ts +3 -5
- package/src/plugins/entitlements/types.ts +9 -2
- package/src/plugins/index.ts +1 -1
- package/src/plugins/maintenance/SeedExecutor.tsx +9 -1
- package/src/plugins/maintenance/SeedList.tsx +85 -38
- package/src/plugins/maintenance/SeedManagementPage.tsx +10 -4
- package/src/plugins/maintenance/seed-executor.ts +56 -17
- package/src/plugins/maintenance-plugin.ts +267 -36
- package/src/plugins/notifications/index.ts +3 -5
- package/src/plugins/notifications/notifications-plugin.ts +48 -19
- package/src/plugins/parental/index.ts +3 -5
- package/src/plugins/parental/parental-plugin.ts +63 -25
- package/src/plugins/postgres-plugin.ts +410 -5
- package/src/plugins/preferences/index.ts +3 -5
- package/src/plugins/preferences/preferences-plugin.ts +66 -20
- package/src/plugins/profiles/index.ts +3 -5
- package/src/plugins/profiles/profiles-plugin.ts +60 -27
- package/src/plugins/profiles/types.ts +9 -2
- package/src/plugins/qwickbrain/index.ts +3 -5
- package/src/plugins/qwickbrain/qwickbrain-plugin.ts +135 -0
- package/src/plugins/rate-limit/index.ts +3 -5
- package/src/plugins/subscriptions/index.ts +3 -5
- package/src/plugins/subscriptions/subscriptions-plugin.ts +63 -30
- package/src/plugins/subscriptions/types.ts +8 -2
- package/src/plugins/tenants/tenants-plugin.ts +95 -60
- package/src/plugins/tenants/types.ts +8 -2
- package/src/plugins/usage/index.ts +3 -5
- package/src/plugins/usage/usage-plugin.ts +60 -26
- package/src/plugins/users/types.ts +7 -2
- package/src/plugins/users/users-plugin.ts +56 -24
- package/ui/src/App.tsx +3 -3
- package/ui/src/components/ControlPanelApp.tsx +4 -3
- package/ui/src/dashboard/builtInWidgets.tsx +3 -0
- package/ui/src/dashboard/widgets/CMSMaintenanceWidget.tsx +17 -4
- package/ui/src/dashboard/widgets/CMSStatusWidget.tsx +5 -1
- package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +4 -2
- package/ui/src/dashboard/widgets/DatabaseOperationsWidget.tsx +410 -0
- package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +6 -3
- package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +533 -49
- package/ui/src/dashboard/widgets/index.ts +1 -0
- package/dist-ui/assets/index-8y0jDGcd.js +0 -528
- package/dist-ui/assets/index-8y0jDGcd.js.map +0 -1
|
@@ -23,22 +23,25 @@ import {
|
|
|
23
23
|
CreateApiKeySchema,
|
|
24
24
|
UpdateApiKeySchema,
|
|
25
25
|
} from './types.js';
|
|
26
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
27
|
+
import { postgresApiKeyStore } from './stores/index.js';
|
|
26
28
|
|
|
27
29
|
// Store instance for helper access
|
|
28
30
|
let currentStore: ApiKeyStore | null = null;
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
|
-
* Create the API Keys plugin
|
|
33
|
+
* Create the API Keys plugin with smart defaults
|
|
34
|
+
*
|
|
35
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
36
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
32
37
|
*/
|
|
33
|
-
export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (debug) {
|
|
41
|
-
console.log(`[ApiKeysPlugin] ${message}`, data || '');
|
|
38
|
+
export function createApiKeysPlugin(config: Partial<ApiKeysPluginConfig> = {}): Plugin {
|
|
39
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
40
|
+
const prefix = '[ApiKeysPlugin]';
|
|
41
|
+
if (isError) {
|
|
42
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
43
|
+
} else if (config.debug) {
|
|
44
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
|
|
@@ -48,15 +51,113 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
48
51
|
version: '1.0.0',
|
|
49
52
|
|
|
50
53
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
51
|
-
|
|
54
|
+
const logger = registry.getLogger('api-keys');
|
|
52
55
|
|
|
53
56
|
// Check for users plugin dependency
|
|
54
57
|
if (!registry.hasPlugin('users')) {
|
|
55
|
-
|
|
58
|
+
logger.warn('Users plugin not loaded! API Keys plugin disabled.');
|
|
59
|
+
registry.registerHealthCheck({
|
|
60
|
+
name: 'api-keys-store',
|
|
61
|
+
type: 'custom',
|
|
62
|
+
check: async () => ({
|
|
63
|
+
healthy: false,
|
|
64
|
+
details: {
|
|
65
|
+
error: 'Users plugin not available',
|
|
66
|
+
state: 'disabled',
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
56
71
|
}
|
|
57
72
|
|
|
73
|
+
// Check for postgres in registry
|
|
74
|
+
if (!hasPostgres()) {
|
|
75
|
+
logger.warn('No Database! API Keys plugin disabled.');
|
|
76
|
+
registry.registerHealthCheck({
|
|
77
|
+
name: 'api-keys-store',
|
|
78
|
+
type: 'custom',
|
|
79
|
+
check: async () => ({
|
|
80
|
+
healthy: false,
|
|
81
|
+
details: {
|
|
82
|
+
error: 'PostgreSQL not available',
|
|
83
|
+
state: 'disabled',
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Smart defaults - get dependencies from registry
|
|
91
|
+
const store = config.store ?? postgresApiKeyStore({
|
|
92
|
+
pool: () => getPostgres().getPool(),
|
|
93
|
+
autoCreateTables: true,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const debug = config.debug ?? false;
|
|
97
|
+
const apiPrefix = config.api?.prefix ?? '/api-keys';
|
|
98
|
+
const apiEnabled = config.api?.enabled ?? true;
|
|
99
|
+
|
|
100
|
+
log('Starting API keys plugin');
|
|
101
|
+
|
|
58
102
|
// Initialize the store (creates tables and RLS policies if needed)
|
|
59
|
-
await
|
|
103
|
+
const initResult = await store.initialize();
|
|
104
|
+
|
|
105
|
+
if (!initResult.success) {
|
|
106
|
+
logger.error('API keys store initialization failed', { error: initResult.error });
|
|
107
|
+
|
|
108
|
+
// If migration is required, register maintenance actions
|
|
109
|
+
if (initResult.requiresMaintenance) {
|
|
110
|
+
registry.registerMaintenance({
|
|
111
|
+
pluginId: 'api-keys',
|
|
112
|
+
error: initResult.error || 'Store initialization failed',
|
|
113
|
+
actions: [
|
|
114
|
+
{
|
|
115
|
+
id: 'truncate-api-keys-table',
|
|
116
|
+
name: 'Clear API Keys Table',
|
|
117
|
+
description: 'Delete all API keys from the database. This allows the migration to proceed by removing old data.',
|
|
118
|
+
destructive: true,
|
|
119
|
+
handler: async () => {
|
|
120
|
+
try {
|
|
121
|
+
const pool = getPostgres().getPool();
|
|
122
|
+
await pool.query('TRUNCATE TABLE "public"."api_keys" CASCADE');
|
|
123
|
+
logger.info('API keys table truncated successfully');
|
|
124
|
+
return {
|
|
125
|
+
success: true,
|
|
126
|
+
message: 'API keys table cleared. Plugin will retry initialization.',
|
|
127
|
+
};
|
|
128
|
+
} catch (error) {
|
|
129
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
130
|
+
logger.error('Failed to truncate API keys table', { error: errorMessage });
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: `Failed to truncate table: ${errorMessage}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
recommendedAction: 'truncate-api-keys-table',
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
logger.warn('API keys plugin requires maintenance - registered recovery actions');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Register unhealthy health check
|
|
146
|
+
registry.registerHealthCheck({
|
|
147
|
+
name: 'api-keys-store',
|
|
148
|
+
type: 'custom',
|
|
149
|
+
check: async () => ({
|
|
150
|
+
healthy: false,
|
|
151
|
+
details: {
|
|
152
|
+
error: initResult.error || 'Initialization failed',
|
|
153
|
+
requiresMaintenance: initResult.requiresMaintenance,
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return; // Don't continue startup
|
|
159
|
+
}
|
|
160
|
+
|
|
60
161
|
log('API keys store initialized');
|
|
61
162
|
|
|
62
163
|
// Initialize optional Phase 2 stores
|
|
@@ -71,7 +172,7 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
71
172
|
}
|
|
72
173
|
|
|
73
174
|
// Store reference for helper access
|
|
74
|
-
currentStore =
|
|
175
|
+
currentStore = store;
|
|
75
176
|
|
|
76
177
|
// Register health check
|
|
77
178
|
registry.registerHealthCheck({
|
|
@@ -117,7 +218,7 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
117
218
|
};
|
|
118
219
|
|
|
119
220
|
// Create the API key
|
|
120
|
-
const apiKey = await
|
|
221
|
+
const apiKey = await store.create(params);
|
|
121
222
|
|
|
122
223
|
// Return the key with plaintext (ONLY time plaintext is accessible)
|
|
123
224
|
res.status(201).json({
|
|
@@ -152,7 +253,7 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
152
253
|
return res.status(401).json({ error: 'Authentication required' });
|
|
153
254
|
}
|
|
154
255
|
|
|
155
|
-
const keys = await
|
|
256
|
+
const keys = await store.list(userId);
|
|
156
257
|
|
|
157
258
|
// Remove sensitive fields from response
|
|
158
259
|
const sanitized = keys.map(key => ({
|
|
@@ -191,7 +292,7 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
191
292
|
}
|
|
192
293
|
|
|
193
294
|
const { id } = req.params;
|
|
194
|
-
const key = await
|
|
295
|
+
const key = await store.get(userId, id);
|
|
195
296
|
|
|
196
297
|
if (!key) {
|
|
197
298
|
return res.status(404).json({ error: 'API key not found' });
|
|
@@ -244,7 +345,7 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
244
345
|
const { id } = req.params;
|
|
245
346
|
const params: UpdateApiKeyParams = validation.data;
|
|
246
347
|
|
|
247
|
-
const updated = await
|
|
348
|
+
const updated = await store.update(userId, id, params);
|
|
248
349
|
|
|
249
350
|
if (!updated) {
|
|
250
351
|
return res.status(404).json({ error: 'API key not found' });
|
|
@@ -287,7 +388,7 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
287
388
|
}
|
|
288
389
|
|
|
289
390
|
const { id } = req.params;
|
|
290
|
-
const deleted = await
|
|
391
|
+
const deleted = await store.delete(userId, id);
|
|
291
392
|
|
|
292
393
|
if (!deleted) {
|
|
293
394
|
return res.status(404).json({ error: 'API key not found' });
|
|
@@ -357,7 +458,7 @@ export function createApiKeysPlugin(config: ApiKeysPluginConfig): Plugin {
|
|
|
357
458
|
const { id: keyId } = req.params;
|
|
358
459
|
|
|
359
460
|
// Verify key belongs to user
|
|
360
|
-
const key = await
|
|
461
|
+
const key = await store.get(userId, keyId);
|
|
361
462
|
if (!key) {
|
|
362
463
|
return res.status(404).json({ error: 'API key not found' });
|
|
363
464
|
}
|
|
@@ -31,6 +31,7 @@ export type {
|
|
|
31
31
|
UpdateApiKeyParams,
|
|
32
32
|
PostgresApiKeyStoreConfig,
|
|
33
33
|
ApiKeysApiConfig,
|
|
34
|
+
StoreInitializationResult,
|
|
34
35
|
} from './types.js';
|
|
35
36
|
|
|
36
37
|
// Zod schemas
|
|
@@ -48,8 +49,5 @@ export { postgresApiKeyStore } from './stores/index.js';
|
|
|
48
49
|
// Middleware
|
|
49
50
|
export { bearerTokenAuth } from './middleware/index.js';
|
|
50
51
|
|
|
51
|
-
// UI Components
|
|
52
|
-
export
|
|
53
|
-
export type { ApiKeysStatusWidgetProps } from './ApiKeysStatusWidget.js';
|
|
54
|
-
export { ApiKeysManagementPage } from './ApiKeysManagementPage.js';
|
|
55
|
-
export type { ApiKeysManagementPageProps } from './ApiKeysManagementPage.js';
|
|
52
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
53
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
UpdateApiKeyParams,
|
|
22
22
|
ApiKey,
|
|
23
23
|
ApiKeyWithPlaintext,
|
|
24
|
+
StoreInitializationResult,
|
|
24
25
|
} from '../types.js';
|
|
25
26
|
|
|
26
27
|
// Pool interface (from pg package)
|
|
@@ -239,82 +240,104 @@ export function postgresApiKeyStore(config: PostgresApiKeyStoreConfig): ApiKeySt
|
|
|
239
240
|
return {
|
|
240
241
|
name: 'postgres',
|
|
241
242
|
|
|
242
|
-
async initialize(): Promise<
|
|
243
|
-
if (!autoCreateTables)
|
|
243
|
+
async initialize(): Promise<StoreInitializationResult> {
|
|
244
|
+
if (!autoCreateTables) {
|
|
245
|
+
return { success: true };
|
|
246
|
+
}
|
|
244
247
|
|
|
245
|
-
|
|
248
|
+
try {
|
|
249
|
+
const pool = getPool();
|
|
246
250
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
251
|
+
// Create table with foreign key to users
|
|
252
|
+
await pool.query(`
|
|
253
|
+
CREATE TABLE IF NOT EXISTS ${tableFullName} (
|
|
254
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
255
|
+
user_id UUID NOT NULL REFERENCES "public"."users"(id) ON DELETE CASCADE,
|
|
256
|
+
name VARCHAR(255) NOT NULL,
|
|
257
|
+
key_hash VARCHAR(64) NOT NULL,
|
|
258
|
+
key_prefix VARCHAR(12) NOT NULL,
|
|
259
|
+
key_type VARCHAR(10) NOT NULL CHECK (key_type IN ('m2m', 'pat')),
|
|
260
|
+
scopes TEXT[] NOT NULL DEFAULT '{}',
|
|
261
|
+
last_used_at TIMESTAMPTZ,
|
|
262
|
+
expires_at TIMESTAMPTZ,
|
|
263
|
+
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
264
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
265
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_user_id ON ${tableFullName}(user_id);
|
|
269
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_key_prefix ON ${tableFullName}(key_prefix);
|
|
270
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_key_hash ON ${tableFullName}(key_hash);
|
|
271
|
+
`);
|
|
263
272
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
273
|
+
// Migration: Check if table needs user_id column migration
|
|
274
|
+
const needsMigration = await pool.query(`
|
|
275
|
+
SELECT
|
|
276
|
+
NOT EXISTS (
|
|
277
|
+
SELECT 1 FROM information_schema.columns
|
|
278
|
+
WHERE table_schema = '${schema}'
|
|
279
|
+
AND table_name = '${tableName}'
|
|
280
|
+
AND column_name = 'user_id'
|
|
281
|
+
) AS missing_column,
|
|
282
|
+
(SELECT COUNT(*) FROM ${tableFullName}) AS row_count
|
|
283
|
+
`);
|
|
284
|
+
|
|
285
|
+
const { missing_column, row_count } = needsMigration.rows[0] as { missing_column: boolean; row_count: number };
|
|
286
|
+
|
|
287
|
+
if (missing_column) {
|
|
288
|
+
if (row_count > 0) {
|
|
289
|
+
// Table has data but is missing user_id column - requires maintenance
|
|
290
|
+
logger.warn(
|
|
291
|
+
`Table ${tableFullName} has ${row_count} rows but is missing user_id column. ` +
|
|
292
|
+
`Requires maintenance action to migrate or clear data.`
|
|
293
|
+
);
|
|
294
|
+
return {
|
|
295
|
+
success: false,
|
|
296
|
+
error: `Table ${tableFullName} contains ${row_count} rows without user_id column. Migration required.`,
|
|
297
|
+
requiresMaintenance: true,
|
|
298
|
+
};
|
|
299
|
+
} else {
|
|
300
|
+
// Table is empty, safe to add NOT NULL column
|
|
301
|
+
await pool.query(`
|
|
290
302
|
ALTER TABLE ${tableFullName}
|
|
291
303
|
ADD COLUMN user_id UUID NOT NULL REFERENCES "public"."users"(id) ON DELETE CASCADE;
|
|
292
304
|
|
|
293
305
|
CREATE INDEX IF NOT EXISTS idx_${tableName}_user_id ON ${tableFullName}(user_id);
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// Enable RLS if configured
|
|
300
|
-
if (enableRLS) {
|
|
301
|
-
await pool.query(`
|
|
302
|
-
ALTER TABLE ${tableFullName} ENABLE ROW LEVEL SECURITY;
|
|
303
|
-
ALTER TABLE ${tableFullName} FORCE ROW LEVEL SECURITY;
|
|
304
|
-
`);
|
|
306
|
+
`);
|
|
307
|
+
logger.info(`Added user_id column to empty table ${tableFullName}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
305
310
|
|
|
306
|
-
//
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
311
|
+
// Enable RLS if configured
|
|
312
|
+
if (enableRLS) {
|
|
313
|
+
await pool.query(`
|
|
314
|
+
ALTER TABLE ${tableFullName} ENABLE ROW LEVEL SECURITY;
|
|
315
|
+
ALTER TABLE ${tableFullName} FORCE ROW LEVEL SECURITY;
|
|
316
|
+
`);
|
|
317
|
+
|
|
318
|
+
// Create or replace the RLS policy
|
|
319
|
+
await pool.query(`
|
|
320
|
+
DROP POLICY IF EXISTS "${tableName}_owner" ON ${tableFullName};
|
|
321
|
+
`);
|
|
322
|
+
|
|
323
|
+
// RLS policy: users can only access their own keys
|
|
324
|
+
await pool.query(`
|
|
325
|
+
CREATE POLICY "${tableName}_owner" ON ${tableFullName}
|
|
326
|
+
FOR ALL
|
|
327
|
+
USING (user_id::text = current_setting('app.current_user_id', true))
|
|
328
|
+
WITH CHECK (user_id::text = current_setting('app.current_user_id', true));
|
|
329
|
+
`);
|
|
330
|
+
}
|
|
310
331
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
332
|
+
logger.info(`API keys store initialized successfully (table: ${tableFullName})`);
|
|
333
|
+
return { success: true };
|
|
334
|
+
} catch (error) {
|
|
335
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
336
|
+
logger.error(`Failed to initialize API keys store: ${errorMessage}`, { error });
|
|
337
|
+
return {
|
|
338
|
+
success: false,
|
|
339
|
+
error: `Database initialization failed: ${errorMessage}`,
|
|
340
|
+
};
|
|
318
341
|
}
|
|
319
342
|
},
|
|
320
343
|
|
|
@@ -140,6 +140,18 @@ export interface ApiKeyWithPlaintext extends ApiKey {
|
|
|
140
140
|
plaintext_key: string;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Result of store initialization
|
|
145
|
+
*/
|
|
146
|
+
export interface StoreInitializationResult {
|
|
147
|
+
/** Whether initialization succeeded */
|
|
148
|
+
success: boolean;
|
|
149
|
+
/** Error message if initialization failed */
|
|
150
|
+
error?: string;
|
|
151
|
+
/** Additional details about what needs to be done */
|
|
152
|
+
requiresMaintenance?: boolean;
|
|
153
|
+
}
|
|
154
|
+
|
|
143
155
|
/**
|
|
144
156
|
* API key store interface - all storage backends must implement this
|
|
145
157
|
*/
|
|
@@ -149,8 +161,9 @@ export interface ApiKeyStore {
|
|
|
149
161
|
|
|
150
162
|
/**
|
|
151
163
|
* Initialize the store (create tables, RLS policies, etc.)
|
|
164
|
+
* Returns initialization status instead of throwing
|
|
152
165
|
*/
|
|
153
|
-
initialize(): Promise<
|
|
166
|
+
initialize(): Promise<StoreInitializationResult>;
|
|
154
167
|
|
|
155
168
|
/**
|
|
156
169
|
* Create a new API key
|
|
@@ -230,17 +243,23 @@ export interface ApiKeysApiConfig {
|
|
|
230
243
|
|
|
231
244
|
/**
|
|
232
245
|
* API keys plugin configuration
|
|
246
|
+
*
|
|
247
|
+
* All properties are optional - plugin will use smart defaults:
|
|
248
|
+
* - store: Postgres API key store using registry's postgres instance
|
|
249
|
+
* - api.prefix: '/api-keys'
|
|
250
|
+
* - api.enabled: true
|
|
251
|
+
* - debug: false
|
|
233
252
|
*/
|
|
234
253
|
export interface ApiKeysPluginConfig {
|
|
235
|
-
/** API key storage backend */
|
|
236
|
-
store
|
|
254
|
+
/** API key storage backend (default: postgres API key store from registry) */
|
|
255
|
+
store?: ApiKeyStore;
|
|
237
256
|
/** Plugin scope storage backend (optional, for Phase 2) */
|
|
238
257
|
scopeStore?: import('./stores/plugin-scope-store.js').PluginScopeStore;
|
|
239
258
|
/** Usage log storage backend (optional, for Phase 2) */
|
|
240
259
|
usageStore?: import('./stores/usage-log-store.js').UsageLogStore;
|
|
241
260
|
/** API configuration */
|
|
242
261
|
api?: ApiKeysApiConfig;
|
|
243
|
-
/** Enable debug logging */
|
|
262
|
+
/** Enable debug logging (default: false) */
|
|
244
263
|
debug?: boolean;
|
|
245
264
|
}
|
|
246
265
|
|
|
@@ -65,8 +65,6 @@ export { basicAdapter } from './adapters/basic-adapter.js';
|
|
|
65
65
|
export { supabaseAdapter } from './adapters/supabase-adapter.js';
|
|
66
66
|
export { supertokensAdapter } from './adapters/supertokens-adapter.js';
|
|
67
67
|
|
|
68
|
-
// UI Components
|
|
69
|
-
export
|
|
70
|
-
|
|
71
|
-
export { AuthManagementPage } from './AuthManagementPage.js';
|
|
72
|
-
export type { AuthManagementPageProps } from './AuthManagementPage.js';
|
|
68
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
69
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
70
|
+
|