@qwickapps/server 1.7.1 → 1.7.2
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 +13 -0
- 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 +61 -19
- package/dist/src/plugins/api-keys/api-keys-plugin.js.map +1 -1
- package/dist/src/plugins/api-keys/index.d.ts +0 -4
- 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/types.d.ts +9 -3
- 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/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/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/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/assets/index-0gzisPdy.js +528 -0
- package/dist-ui/assets/{index-8y0jDGcd.js.map → index-0gzisPdy.js.map} +1 -1
- package/dist-ui/index.html +1 -1
- package/package.json +8 -5
- package/src/plugins/api-keys/api-keys-plugin.ts +64 -20
- package/src/plugins/api-keys/index.ts +2 -5
- package/src/plugins/api-keys/types.ts +9 -3
- 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/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/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/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/dist-ui/assets/index-8y0jDGcd.js +0 -528
|
@@ -28,6 +28,9 @@ import {
|
|
|
28
28
|
isTokenExpired,
|
|
29
29
|
getTokenExpiration,
|
|
30
30
|
} from './token-utils.js';
|
|
31
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
32
|
+
import { postgresDeviceStore } from './stores/index.js';
|
|
33
|
+
import { computeDeviceAdapter } from './adapters/index.js';
|
|
31
34
|
|
|
32
35
|
// Store instances for helper access
|
|
33
36
|
let currentStore: DeviceStore | null = null;
|
|
@@ -35,16 +38,18 @@ let currentAdapter: DeviceAdapter | null = null;
|
|
|
35
38
|
let currentConfig: DevicesPluginConfig | null = null;
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
|
-
* Create the Devices plugin
|
|
41
|
+
* Create the Devices plugin with smart defaults
|
|
42
|
+
*
|
|
43
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
44
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
39
45
|
*/
|
|
40
|
-
export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(`[DevicesPlugin] ${message}`, data || '');
|
|
46
|
+
export function createDevicesPlugin(config: Partial<DevicesPluginConfig> = {}): Plugin {
|
|
47
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
48
|
+
const prefix = '[DevicesPlugin]';
|
|
49
|
+
if (isError) {
|
|
50
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
51
|
+
} else if (config.debug) {
|
|
52
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -54,16 +59,46 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
54
59
|
version: '1.0.0',
|
|
55
60
|
|
|
56
61
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
57
|
-
|
|
62
|
+
const logger = registry.getLogger('devices');
|
|
63
|
+
|
|
64
|
+
// Check for postgres in registry
|
|
65
|
+
if (!hasPostgres()) {
|
|
66
|
+
logger.warn('No Database! Devices plugin disabled.');
|
|
67
|
+
registry.registerHealthCheck({
|
|
68
|
+
name: 'devices-store',
|
|
69
|
+
type: 'custom',
|
|
70
|
+
check: async () => ({
|
|
71
|
+
healthy: false,
|
|
72
|
+
details: {
|
|
73
|
+
error: 'PostgreSQL not available',
|
|
74
|
+
state: 'disabled',
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Smart defaults - get dependencies from registry
|
|
82
|
+
const store = config.store ?? postgresDeviceStore({
|
|
83
|
+
pool: () => getPostgres().getPool(),
|
|
84
|
+
autoCreateTables: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const adapter = config.adapter ?? computeDeviceAdapter();
|
|
88
|
+
const debug = config.debug ?? false;
|
|
89
|
+
const defaultTokenValidityDays = config.defaultTokenValidityDays ?? 90;
|
|
90
|
+
const apiPrefix = config.api?.prefix ?? '/devices';
|
|
91
|
+
|
|
92
|
+
log('Starting devices plugin', { adapter: adapter.name });
|
|
58
93
|
|
|
59
94
|
// Initialize the store (creates tables if needed)
|
|
60
|
-
await
|
|
95
|
+
await store.initialize();
|
|
61
96
|
log('Devices plugin migrations complete');
|
|
62
97
|
|
|
63
98
|
// Store references for helper access
|
|
64
|
-
currentStore =
|
|
65
|
-
currentAdapter =
|
|
66
|
-
currentConfig = config;
|
|
99
|
+
currentStore = store;
|
|
100
|
+
currentAdapter = adapter;
|
|
101
|
+
currentConfig = { ...config, store, adapter, debug, defaultTokenValidityDays };
|
|
67
102
|
|
|
68
103
|
// Register health check
|
|
69
104
|
registry.registerHealthCheck({
|
|
@@ -71,12 +106,12 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
71
106
|
type: 'custom',
|
|
72
107
|
check: async () => {
|
|
73
108
|
try {
|
|
74
|
-
await
|
|
109
|
+
await store.search({ limit: 1 });
|
|
75
110
|
return {
|
|
76
111
|
healthy: true,
|
|
77
112
|
details: {
|
|
78
|
-
adapter:
|
|
79
|
-
tokenPrefix:
|
|
113
|
+
adapter: adapter.name,
|
|
114
|
+
tokenPrefix: adapter.tokenPrefix,
|
|
80
115
|
},
|
|
81
116
|
};
|
|
82
117
|
} catch {
|
|
@@ -106,7 +141,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
106
141
|
sortOrder: (req.query.sortOrder as DeviceSearchParams['sortOrder']) || 'desc',
|
|
107
142
|
};
|
|
108
143
|
|
|
109
|
-
const result = await
|
|
144
|
+
const result = await store.search(params);
|
|
110
145
|
res.json(result);
|
|
111
146
|
} catch (error) {
|
|
112
147
|
console.error('[DevicesPlugin] Search error:', error);
|
|
@@ -122,7 +157,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
122
157
|
pluginId: 'devices',
|
|
123
158
|
handler: async (req: Request, res: Response) => {
|
|
124
159
|
try {
|
|
125
|
-
const device = await
|
|
160
|
+
const device = await store.getById(req.params.id);
|
|
126
161
|
if (!device) {
|
|
127
162
|
return res.status(404).json({ error: 'Device not found' });
|
|
128
163
|
}
|
|
@@ -150,7 +185,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
150
185
|
};
|
|
151
186
|
|
|
152
187
|
// Validate using adapter
|
|
153
|
-
const validation =
|
|
188
|
+
const validation = adapter.validateDeviceInput(input);
|
|
154
189
|
if (!validation.valid) {
|
|
155
190
|
return res.status(400).json({
|
|
156
191
|
error: 'Validation failed',
|
|
@@ -181,7 +216,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
181
216
|
metadata: req.body.metadata,
|
|
182
217
|
};
|
|
183
218
|
|
|
184
|
-
const device = await
|
|
219
|
+
const device = await store.update(req.params.id, input);
|
|
185
220
|
if (!device) {
|
|
186
221
|
return res.status(404).json({ error: 'Device not found' });
|
|
187
222
|
}
|
|
@@ -200,19 +235,19 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
200
235
|
pluginId: 'devices',
|
|
201
236
|
handler: async (req: Request, res: Response) => {
|
|
202
237
|
try {
|
|
203
|
-
const device = await
|
|
238
|
+
const device = await store.getById(req.params.id);
|
|
204
239
|
if (!device) {
|
|
205
240
|
return res.status(404).json({ error: 'Device not found' });
|
|
206
241
|
}
|
|
207
242
|
|
|
208
|
-
const deleted = await
|
|
243
|
+
const deleted = await store.delete(req.params.id);
|
|
209
244
|
if (!deleted) {
|
|
210
245
|
return res.status(404).json({ error: 'Device not found' });
|
|
211
246
|
}
|
|
212
247
|
|
|
213
248
|
// Call adapter hook
|
|
214
|
-
if (
|
|
215
|
-
await
|
|
249
|
+
if (adapter.onDeviceDeleted) {
|
|
250
|
+
await adapter.onDeviceDeleted(device);
|
|
216
251
|
}
|
|
217
252
|
|
|
218
253
|
res.status(204).send();
|
|
@@ -230,7 +265,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
230
265
|
pluginId: 'devices',
|
|
231
266
|
handler: async (req: Request, res: Response) => {
|
|
232
267
|
try {
|
|
233
|
-
const device = await
|
|
268
|
+
const device = await store.getById(req.params.id);
|
|
234
269
|
if (!device) {
|
|
235
270
|
return res.status(404).json({ error: 'Device not found' });
|
|
236
271
|
}
|
|
@@ -295,7 +330,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
295
330
|
|
|
296
331
|
async onStop(): Promise<void> {
|
|
297
332
|
log('Stopping devices plugin');
|
|
298
|
-
await
|
|
333
|
+
if (currentStore) { await currentStore.shutdown(); };
|
|
299
334
|
currentStore = null;
|
|
300
335
|
currentAdapter = null;
|
|
301
336
|
currentConfig = null;
|
|
@@ -68,8 +68,6 @@ export {
|
|
|
68
68
|
} from './token-utils.js';
|
|
69
69
|
export type { DeviceTokenPair } from './token-utils.js';
|
|
70
70
|
|
|
71
|
-
// UI Components
|
|
72
|
-
export
|
|
73
|
-
|
|
74
|
-
export { DevicesManagementPage } from './DevicesManagementPage.js';
|
|
75
|
-
export type { DevicesManagementPageProps } from './DevicesManagementPage.js';
|
|
71
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
72
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
73
|
+
|
|
@@ -21,7 +21,9 @@ import type {
|
|
|
21
21
|
EntitlementStats,
|
|
22
22
|
} from './types.js';
|
|
23
23
|
import type { AuthenticatedRequest } from '../auth/types.js';
|
|
24
|
-
import { getCache, type CacheInstance } from '../cache-plugin.js';
|
|
24
|
+
import { getCache, hasCache, type CacheInstance } from '../cache-plugin.js';
|
|
25
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
26
|
+
import { postgresEntitlementSource } from './sources/index.js';
|
|
25
27
|
|
|
26
28
|
// Plugin state
|
|
27
29
|
let primarySource: EntitlementSource | null = null;
|
|
@@ -35,67 +37,103 @@ let cacheEnabled = true;
|
|
|
35
37
|
let cacheVersion = 1;
|
|
36
38
|
|
|
37
39
|
/**
|
|
38
|
-
* Create the Entitlements plugin
|
|
40
|
+
* Create the Entitlements plugin with smart defaults
|
|
41
|
+
*
|
|
42
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
43
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
39
44
|
*/
|
|
40
|
-
export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plugin {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
function log(message: string, data?: Record<string, unknown>) {
|
|
48
|
-
if (debug) {
|
|
49
|
-
console.log(`[EntitlementsPlugin] ${message}`, data || '');
|
|
45
|
+
export function createEntitlementsPlugin(config: Partial<EntitlementsPluginConfig> = {}): Plugin {
|
|
46
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
47
|
+
const prefix = '[EntitlementsPlugin]';
|
|
48
|
+
if (isError) {
|
|
49
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
50
|
+
} else if (config.debug) {
|
|
51
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
// Cache key helpers
|
|
54
|
-
const keys = {
|
|
55
|
-
entitlements: (email: string) => `${cacheKeyPrefix}user:${email.toLowerCase()}`,
|
|
56
|
-
mapping: (source: string, id: string) => `${cacheKeyPrefix}mapping:${source}:${id}`,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
55
|
return {
|
|
60
56
|
id: 'entitlements',
|
|
61
57
|
name: 'Entitlements',
|
|
62
58
|
version: '1.0.0',
|
|
63
59
|
|
|
64
60
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
61
|
+
const logger = registry.getLogger('entitlements');
|
|
62
|
+
|
|
63
|
+
// Check for postgres in registry (needed for default source)
|
|
64
|
+
if (!hasPostgres()) {
|
|
65
|
+
logger.warn('No Database! Entitlements plugin disabled.');
|
|
66
|
+
registry.registerHealthCheck({
|
|
67
|
+
name: 'entitlements-source',
|
|
68
|
+
type: 'custom',
|
|
69
|
+
check: async () => ({
|
|
70
|
+
healthy: false,
|
|
71
|
+
details: {
|
|
72
|
+
error: 'PostgreSQL not available',
|
|
73
|
+
state: 'disabled',
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Smart defaults - get dependencies from registry
|
|
81
|
+
const source = config.source ?? postgresEntitlementSource({
|
|
82
|
+
pool: () => getPostgres().getPool(),
|
|
83
|
+
autoCreateTables: true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const debug = config.debug ?? false;
|
|
87
|
+
const apiPrefix = config.api?.prefix ?? '/entitlements';
|
|
88
|
+
const apiEnabled = config.api?.enabled ?? true;
|
|
89
|
+
const enableWriteApi = config.api?.enableWrite ?? true;
|
|
90
|
+
|
|
65
91
|
log('Starting entitlements plugin');
|
|
66
92
|
|
|
67
93
|
// Initialize primary source
|
|
68
|
-
await
|
|
69
|
-
primarySource =
|
|
70
|
-
log('Primary source initialized', { source:
|
|
94
|
+
await source.initialize();
|
|
95
|
+
primarySource = source;
|
|
96
|
+
log('Primary source initialized', { source: source.name });
|
|
71
97
|
|
|
72
98
|
// Initialize additional sources
|
|
73
99
|
additionalSources = config.additionalSources || [];
|
|
74
|
-
for (const
|
|
75
|
-
await
|
|
76
|
-
log('Additional source initialized', { source:
|
|
100
|
+
for (const additionalSource of additionalSources) {
|
|
101
|
+
await additionalSource.initialize();
|
|
102
|
+
log('Additional source initialized', { source: additionalSource.name });
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
// Store config
|
|
80
|
-
pluginConfig = config;
|
|
106
|
+
pluginConfig = { ...config, source, debug };
|
|
81
107
|
|
|
82
|
-
// Setup caching if enabled
|
|
108
|
+
// Setup caching if enabled and available
|
|
83
109
|
cacheEnabled = config.cache?.enabled !== false;
|
|
84
110
|
if (cacheEnabled) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
111
|
+
if (hasCache()) {
|
|
112
|
+
try {
|
|
113
|
+
const instanceName = config.cache?.instanceName || 'default';
|
|
114
|
+
cacheInstance = getCache(instanceName);
|
|
115
|
+
cacheKeyPrefix = config.cache?.keyPrefix || 'entitlements:';
|
|
116
|
+
cacheTtl = config.cache?.ttl || 300;
|
|
117
|
+
cacheMappingTtl = config.cache?.mappingTtl || cacheTtl * 2;
|
|
118
|
+
log('Cache configured', { instanceName, prefix: cacheKeyPrefix, ttl: cacheTtl });
|
|
119
|
+
} catch {
|
|
120
|
+
log('Cache instance not available, running without caching');
|
|
121
|
+
cacheEnabled = false;
|
|
122
|
+
cacheInstance = null;
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
log('Cache plugin not available, running without caching');
|
|
94
126
|
cacheEnabled = false;
|
|
95
127
|
cacheInstance = null;
|
|
96
128
|
}
|
|
97
129
|
}
|
|
98
130
|
|
|
131
|
+
// Cache key helpers
|
|
132
|
+
const keys = {
|
|
133
|
+
entitlements: (email: string) => `${cacheKeyPrefix}user:${email.toLowerCase()}`,
|
|
134
|
+
mapping: (sourceId: string, id: string) => `${cacheKeyPrefix}mapping:${sourceId}:${id}`,
|
|
135
|
+
};
|
|
136
|
+
|
|
99
137
|
// Register health check
|
|
100
138
|
registry.registerHealthCheck({
|
|
101
139
|
name: 'entitlements-source',
|
|
@@ -104,8 +142,8 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
104
142
|
try {
|
|
105
143
|
// Use source's isHealthy() method if available (avoids API calls)
|
|
106
144
|
// Otherwise just check that source is initialized
|
|
107
|
-
if (
|
|
108
|
-
const healthy = await
|
|
145
|
+
if (source.isHealthy) {
|
|
146
|
+
const healthy = await source.isHealthy();
|
|
109
147
|
return { healthy };
|
|
110
148
|
}
|
|
111
149
|
// Source is healthy if initialized (we got here means it started)
|
|
@@ -145,9 +183,9 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
145
183
|
try {
|
|
146
184
|
const sources = [
|
|
147
185
|
{
|
|
148
|
-
name:
|
|
149
|
-
description:
|
|
150
|
-
readonly:
|
|
186
|
+
name: source.name,
|
|
187
|
+
description: source.description,
|
|
188
|
+
readonly: source.readonly ?? false,
|
|
151
189
|
primary: true,
|
|
152
190
|
},
|
|
153
191
|
...additionalSources.map((s) => ({
|
|
@@ -159,8 +197,8 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
159
197
|
];
|
|
160
198
|
|
|
161
199
|
res.json({
|
|
162
|
-
readonly:
|
|
163
|
-
writeEnabled: enableWriteApi && !
|
|
200
|
+
readonly: source.readonly ?? false,
|
|
201
|
+
writeEnabled: enableWriteApi && !source.readonly,
|
|
164
202
|
cacheEnabled,
|
|
165
203
|
cacheTtl,
|
|
166
204
|
sources,
|
|
@@ -311,7 +349,7 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
311
349
|
});
|
|
312
350
|
|
|
313
351
|
// Write endpoints (grant/revoke) - only if enabled and source is writable
|
|
314
|
-
if (enableWriteApi && !
|
|
352
|
+
if (enableWriteApi && !source.readonly) {
|
|
315
353
|
// Grant entitlement
|
|
316
354
|
registry.addRoute({
|
|
317
355
|
method: 'post',
|
|
@@ -50,8 +50,6 @@ export type {
|
|
|
50
50
|
EntitlementStats,
|
|
51
51
|
} from './types.js';
|
|
52
52
|
|
|
53
|
-
// UI Components
|
|
54
|
-
export
|
|
55
|
-
|
|
56
|
-
export { EntitlementsManagementPage } from './EntitlementsManagementPage.js';
|
|
57
|
-
export type { EntitlementsManagementPageProps } from './EntitlementsManagementPage.js';
|
|
53
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
54
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
55
|
+
|
|
@@ -200,10 +200,17 @@ export interface EntitlementsApiConfig {
|
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
202
|
* Entitlements plugin configuration
|
|
203
|
+
*
|
|
204
|
+
* All properties are optional - plugin will use smart defaults:
|
|
205
|
+
* - source: Postgres entitlement source using registry's postgres instance
|
|
206
|
+
* - cache: Uses cache from registry if available
|
|
207
|
+
* - api.prefix: '/entitlements'
|
|
208
|
+
* - api.enabled: true
|
|
209
|
+
* - debug: false
|
|
203
210
|
*/
|
|
204
211
|
export interface EntitlementsPluginConfig {
|
|
205
|
-
/** Primary entitlement source */
|
|
206
|
-
source
|
|
212
|
+
/** Primary entitlement source (default: postgres source from registry) */
|
|
213
|
+
source?: EntitlementSource;
|
|
207
214
|
|
|
208
215
|
/** Additional sources to query (results are merged) */
|
|
209
216
|
additionalSources?: EntitlementSource[];
|
|
@@ -90,8 +90,6 @@ export type {
|
|
|
90
90
|
NotificationsManagerInterface,
|
|
91
91
|
} from './types.js';
|
|
92
92
|
|
|
93
|
-
// UI Components
|
|
94
|
-
export
|
|
95
|
-
|
|
96
|
-
export type { NotificationsStatusWidgetProps } from './NotificationsStatusWidget.js';
|
|
97
|
-
export type { NotificationsManagementPageProps } from './NotificationsManagementPage.js';
|
|
93
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
94
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
95
|
+
|
|
@@ -96,7 +96,10 @@ function validateId(id: string | undefined, paramName: string): { valid: boolean
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
* Create the Notifications plugin
|
|
99
|
+
* Create the Notifications plugin with smart defaults
|
|
100
|
+
*
|
|
101
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
102
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
100
103
|
*
|
|
101
104
|
* @param config Plugin configuration
|
|
102
105
|
* @returns Plugin instance
|
|
@@ -112,11 +115,7 @@ function validateId(id: string | undefined, paramName: string): { valid: boolean
|
|
|
112
115
|
* });
|
|
113
116
|
* ```
|
|
114
117
|
*/
|
|
115
|
-
export function createNotificationsPlugin(config: NotificationsPluginConfig): Plugin {
|
|
116
|
-
const apiPrefix = config.api?.prefix || '/'; // Framework adds /notifications prefix automatically
|
|
117
|
-
const streamEnabled = config.api?.stream !== false;
|
|
118
|
-
const statsEnabled = config.api?.stats !== false;
|
|
119
|
-
|
|
118
|
+
export function createNotificationsPlugin(config: Partial<NotificationsPluginConfig> = {}): Plugin {
|
|
120
119
|
let manager: NotificationsManager | null = null;
|
|
121
120
|
|
|
122
121
|
return {
|
|
@@ -129,10 +128,19 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
129
128
|
|
|
130
129
|
// Check for postgres plugin dependency
|
|
131
130
|
if (!hasPostgres()) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
logger.warn('No Database! Notifications plugin disabled.');
|
|
132
|
+
registry.registerHealthCheck({
|
|
133
|
+
name: 'notifications',
|
|
134
|
+
type: 'custom',
|
|
135
|
+
check: async () => ({
|
|
136
|
+
healthy: false,
|
|
137
|
+
details: {
|
|
138
|
+
error: 'PostgreSQL not available',
|
|
139
|
+
state: 'disabled',
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
// Get database connection string from postgres plugin
|
|
@@ -150,22 +158,43 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
if (!connectionString) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
logger.warn('Could not determine PostgreSQL connection string. Notifications plugin disabled.');
|
|
162
|
+
registry.registerHealthCheck({
|
|
163
|
+
name: 'notifications',
|
|
164
|
+
type: 'custom',
|
|
165
|
+
check: async () => ({
|
|
166
|
+
healthy: false,
|
|
167
|
+
details: {
|
|
168
|
+
error: 'Connection string not available',
|
|
169
|
+
state: 'disabled',
|
|
170
|
+
},
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
157
174
|
}
|
|
158
175
|
|
|
176
|
+
// Smart defaults
|
|
177
|
+
const channels = config.channels ?? [];
|
|
178
|
+
const apiPrefix = config.api?.prefix ?? '/'; // Framework adds /notifications prefix automatically
|
|
179
|
+
const streamEnabled = config.api?.stream ?? true;
|
|
180
|
+
const statsEnabled = config.api?.stats ?? true;
|
|
181
|
+
|
|
159
182
|
logger.debug('Initializing notifications manager', {
|
|
160
|
-
channels:
|
|
183
|
+
channels: channels,
|
|
161
184
|
heartbeatInterval: config.heartbeat?.interval,
|
|
162
185
|
});
|
|
163
186
|
|
|
187
|
+
// Create full config with defaults for required fields
|
|
188
|
+
const fullConfig: NotificationsPluginConfig = {
|
|
189
|
+
...config,
|
|
190
|
+
channels, // Required field with default
|
|
191
|
+
};
|
|
192
|
+
|
|
164
193
|
// Create and initialize manager
|
|
165
194
|
manager = new NotificationsManager(
|
|
166
195
|
connectionString,
|
|
167
|
-
|
|
168
|
-
|
|
196
|
+
channels,
|
|
197
|
+
fullConfig,
|
|
169
198
|
logger
|
|
170
199
|
);
|
|
171
200
|
|
|
@@ -182,7 +211,7 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
182
211
|
healthy: health?.isHealthy ?? false,
|
|
183
212
|
details: {
|
|
184
213
|
connected: health?.isConnected,
|
|
185
|
-
channels:
|
|
214
|
+
channels: channels,
|
|
186
215
|
activeClients: manager?.getStats().currentConnections ?? 0,
|
|
187
216
|
lastEventAt: health?.lastEventAt?.toISOString(),
|
|
188
217
|
isReconnecting: health?.isReconnecting,
|
|
@@ -273,7 +302,7 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
273
302
|
const stats = manager.getStats();
|
|
274
303
|
res.json({
|
|
275
304
|
...stats,
|
|
276
|
-
channels:
|
|
305
|
+
channels: channels,
|
|
277
306
|
lastEventAt: stats.connectionHealth.lastEventAt?.toISOString(),
|
|
278
307
|
lastReconnectionAt: stats.lastReconnectionAt?.toISOString(),
|
|
279
308
|
});
|
|
@@ -54,8 +54,6 @@ export { postgresParentalStore } from './stores/index.js';
|
|
|
54
54
|
export { kidsAdapter } from './adapters/index.js';
|
|
55
55
|
export type { KidsAdapterConfig } from './adapters/index.js';
|
|
56
56
|
|
|
57
|
-
// UI Components
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
export { ParentalManagementPage } from './ParentalManagementPage.js';
|
|
61
|
-
export type { ParentalManagementPageProps } from './ParentalManagementPage.js';
|
|
57
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
58
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
59
|
+
|