@hiliosai/sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INTEGRATION_SERVICE.md +394 -0
- package/package.json +5 -3
- package/src/configs/constants.ts +122 -0
- package/src/configs/index.ts +1 -0
- package/src/configs/moleculer/bulkhead.ts +8 -0
- package/src/configs/moleculer/channels.ts +102 -0
- package/src/configs/moleculer/circuit-breaker.ts +17 -0
- package/src/configs/moleculer/index.ts +85 -0
- package/src/configs/moleculer/logger.ts +17 -0
- package/src/configs/moleculer/metrics.ts +20 -0
- package/src/configs/moleculer/registry.ts +7 -0
- package/src/configs/moleculer/retry-policy.ts +17 -0
- package/src/configs/moleculer/tracing.ts +6 -0
- package/src/configs/moleculer/tracking.ts +6 -0
- package/src/datasources/base.datasource.ts +79 -0
- package/src/datasources/index.ts +1 -0
- package/src/middlewares/context-helpers.middleware.ts +4 -1
- package/src/middlewares/datasource.middleware.ts +3 -1
- package/src/middlewares/index.ts +0 -1
- package/src/mixins/datasource.mixin.ts +118 -0
- package/src/mixins/index.ts +1 -0
- package/src/service/define-integration.ts +177 -61
- package/src/service/define-service.ts +10 -30
- package/src/service/example-user/user.service.ts +2 -10
- package/src/types/context.ts +7 -4
- package/src/types/message.ts +1 -0
- package/src/types/service.ts +3 -4
- package/src/examples/cache-usage.example.ts +0 -293
- package/src/middlewares/cache.middleware.ts +0 -278
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import crypto from 'crypto';
|
|
2
2
|
|
|
3
|
+
import {defineService} from './define-service';
|
|
3
4
|
import type {AppContext} from '../types/context';
|
|
4
5
|
import type {IntegrationConfig} from '../types/integration';
|
|
5
6
|
import type {
|
|
6
7
|
NormalizedMessage,
|
|
7
8
|
PlatformMessage,
|
|
9
|
+
SendResult,
|
|
8
10
|
WebhookEvent,
|
|
9
11
|
} from '../types/message';
|
|
10
12
|
import type {
|
|
@@ -12,18 +14,83 @@ import type {
|
|
|
12
14
|
IntegrationServiceSchema,
|
|
13
15
|
} from '../types/service';
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Security helpers for webhook validation
|
|
19
|
+
*/
|
|
20
|
+
const SecurityHelpers = {
|
|
21
|
+
/**
|
|
22
|
+
* Secure comparison using Node.js crypto.timingSafeEqual
|
|
23
|
+
*/
|
|
24
|
+
secureCompare(a: string, b: string): boolean {
|
|
25
|
+
try {
|
|
26
|
+
return crypto.timingSafeEqual(
|
|
27
|
+
Buffer.from(a, 'utf8'),
|
|
28
|
+
Buffer.from(b, 'utf8')
|
|
29
|
+
);
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate webhook timestamp to prevent replay attacks
|
|
37
|
+
*/
|
|
38
|
+
validateTimestamp(timestamp: number, maxAgeMs = 5 * 60 * 1000): boolean {
|
|
39
|
+
return Date.now() - timestamp <= maxAgeMs;
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate correlation ID for request tracking
|
|
44
|
+
*/
|
|
45
|
+
generateCorrelationId(): string {
|
|
46
|
+
return crypto.randomUUID();
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Retry mechanism with exponential backoff
|
|
52
|
+
*/
|
|
53
|
+
async function executeWithRetry<T>(
|
|
54
|
+
operation: () => Promise<T>,
|
|
55
|
+
maxRetries = 3,
|
|
56
|
+
baseDelayMs = 1000,
|
|
57
|
+
context = 'operation'
|
|
58
|
+
): Promise<T> {
|
|
59
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
60
|
+
try {
|
|
61
|
+
return await operation();
|
|
62
|
+
} catch (error: unknown) {
|
|
63
|
+
if (attempt === maxRetries) {
|
|
64
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
65
|
+
throw new Error(
|
|
66
|
+
`${context} failed after ${maxRetries} retries: ${err.message}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const delay = baseDelayMs * Math.pow(2, attempt);
|
|
71
|
+
const jitter = Math.random() * 1000;
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw new Error('Retry logic error');
|
|
76
|
+
}
|
|
77
|
+
|
|
15
78
|
export function defineIntegration<
|
|
16
79
|
TPlatformMessage extends PlatformMessage = PlatformMessage,
|
|
17
80
|
TSettings = unknown,
|
|
18
|
-
|
|
81
|
+
TDatasources = unknown
|
|
19
82
|
>(
|
|
20
|
-
config: IntegrationServiceConfig<
|
|
83
|
+
config: IntegrationServiceConfig<
|
|
84
|
+
TPlatformMessage,
|
|
85
|
+
TSettings,
|
|
86
|
+
AppContext<TDatasources>
|
|
87
|
+
>
|
|
21
88
|
): IntegrationServiceSchema<TPlatformMessage, TSettings> {
|
|
22
89
|
// Create actions from integration methods
|
|
23
|
-
const actions
|
|
24
|
-
|
|
90
|
+
const actions = {
|
|
91
|
+
i_receiveWebhook: {
|
|
25
92
|
rest: {
|
|
26
|
-
method: 'POST',
|
|
93
|
+
method: 'POST' as const,
|
|
27
94
|
path: '/webhook',
|
|
28
95
|
},
|
|
29
96
|
params: {
|
|
@@ -36,14 +103,25 @@ export function defineIntegration<
|
|
|
36
103
|
headers: 'object',
|
|
37
104
|
timestamp: 'number',
|
|
38
105
|
},
|
|
39
|
-
async handler(
|
|
106
|
+
async handler(
|
|
107
|
+
ctx: AppContext<TDatasources, WebhookEvent>
|
|
108
|
+
): Promise<{success: boolean; messages: number}> {
|
|
40
109
|
const webhook = ctx.params;
|
|
110
|
+
const correlationId = SecurityHelpers.generateCorrelationId();
|
|
111
|
+
|
|
112
|
+
// Add correlation ID to context for tracking
|
|
113
|
+
ctx.meta.correlationId = correlationId;
|
|
114
|
+
|
|
115
|
+
// Validate timestamp to prevent replay attacks
|
|
116
|
+
if (!SecurityHelpers.validateTimestamp(webhook.timestamp)) {
|
|
117
|
+
throw new Error('Webhook timestamp too old - possible replay attack');
|
|
118
|
+
}
|
|
41
119
|
|
|
42
120
|
// Validate webhook if method exists
|
|
43
121
|
if (config.validateWebhook) {
|
|
44
122
|
const isValid = await config.validateWebhook(webhook);
|
|
45
123
|
if (!isValid) {
|
|
46
|
-
throw new Error('Invalid webhook');
|
|
124
|
+
throw new Error('Invalid webhook payload format');
|
|
47
125
|
}
|
|
48
126
|
}
|
|
49
127
|
|
|
@@ -51,22 +129,26 @@ export function defineIntegration<
|
|
|
51
129
|
if (config.validateSignature) {
|
|
52
130
|
const isValidSignature = config.validateSignature(webhook);
|
|
53
131
|
if (!isValidSignature) {
|
|
54
|
-
throw new Error('
|
|
132
|
+
throw new Error('Webhook signature validation failed');
|
|
55
133
|
}
|
|
56
134
|
}
|
|
57
135
|
|
|
58
136
|
// Normalize the message
|
|
59
|
-
const normalizedMessages = await config.normalize(
|
|
137
|
+
const normalizedMessages: NormalizedMessage[] = await config.normalize(
|
|
138
|
+
webhook
|
|
139
|
+
);
|
|
60
140
|
|
|
61
141
|
// Process each message
|
|
62
142
|
for (const message of normalizedMessages) {
|
|
63
|
-
// Emit event for further processing
|
|
143
|
+
// Emit event for further processing with correlation ID
|
|
64
144
|
ctx.emit('integration.message.received', {
|
|
145
|
+
correlationId,
|
|
65
146
|
tenantId: webhook.tenantId,
|
|
66
147
|
channelId: webhook.channelId,
|
|
67
148
|
integrationId: webhook.integrationId,
|
|
68
149
|
platform: webhook.platform,
|
|
69
150
|
message,
|
|
151
|
+
timestamp: Date.now(),
|
|
70
152
|
});
|
|
71
153
|
}
|
|
72
154
|
|
|
@@ -74,16 +156,16 @@ export function defineIntegration<
|
|
|
74
156
|
},
|
|
75
157
|
},
|
|
76
158
|
|
|
77
|
-
|
|
159
|
+
i_sendMessage: {
|
|
78
160
|
rest: {
|
|
79
|
-
method: 'POST',
|
|
161
|
+
method: 'POST' as const,
|
|
80
162
|
path: '/send',
|
|
81
163
|
},
|
|
82
164
|
params: {
|
|
83
165
|
message: 'object',
|
|
84
166
|
config: 'object',
|
|
85
167
|
},
|
|
86
|
-
async handler(ctx:
|
|
168
|
+
async handler(ctx: AppContext<TDatasources>): Promise<unknown> {
|
|
87
169
|
const {message, config: integrationConfig} = ctx.params as {
|
|
88
170
|
message: NormalizedMessage;
|
|
89
171
|
config: IntegrationConfig;
|
|
@@ -94,11 +176,12 @@ export function defineIntegration<
|
|
|
94
176
|
await config.transform(message, integrationConfig);
|
|
95
177
|
}
|
|
96
178
|
|
|
97
|
-
// Send the message
|
|
98
|
-
const result = await
|
|
99
|
-
ctx
|
|
100
|
-
|
|
101
|
-
|
|
179
|
+
// Send the message with retry mechanism
|
|
180
|
+
const result: SendResult = await executeWithRetry(
|
|
181
|
+
() => config.sendMessage(ctx, message, integrationConfig),
|
|
182
|
+
3,
|
|
183
|
+
1000,
|
|
184
|
+
`Send message via ${config.integration.platform}`
|
|
102
185
|
);
|
|
103
186
|
|
|
104
187
|
// Emit event based on result
|
|
@@ -120,31 +203,52 @@ export function defineIntegration<
|
|
|
120
203
|
},
|
|
121
204
|
},
|
|
122
205
|
|
|
123
|
-
|
|
206
|
+
i_healthCheck: {
|
|
124
207
|
rest: {
|
|
125
|
-
method: 'GET',
|
|
208
|
+
method: 'GET' as const,
|
|
126
209
|
path: '/health',
|
|
127
210
|
},
|
|
128
211
|
params: {
|
|
129
212
|
config: {type: 'object', optional: true},
|
|
130
213
|
},
|
|
131
|
-
async handler(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
214
|
+
async handler(
|
|
215
|
+
ctx: AppContext<TDatasources, {config?: IntegrationConfig}>
|
|
216
|
+
): Promise<unknown> {
|
|
217
|
+
try {
|
|
218
|
+
if (config.checkHealth) {
|
|
219
|
+
const integrationConfig = ctx.params.config;
|
|
220
|
+
if (integrationConfig) {
|
|
221
|
+
return await config.checkHealth(ctx, integrationConfig);
|
|
222
|
+
}
|
|
136
223
|
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
status: 'healthy',
|
|
227
|
+
message: `${config.integration.name} integration is running`,
|
|
228
|
+
details: {
|
|
229
|
+
integration: config.integration.id,
|
|
230
|
+
version: config.integration.version,
|
|
231
|
+
timestamp: new Date().toISOString(),
|
|
232
|
+
capabilities: config.integration.capabilities,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
} catch (error: unknown) {
|
|
236
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
237
|
+
return {
|
|
238
|
+
status: 'unhealthy',
|
|
239
|
+
message: err.message,
|
|
240
|
+
details: {
|
|
241
|
+
integration: config.integration.id,
|
|
242
|
+
timestamp: new Date().toISOString(),
|
|
243
|
+
},
|
|
244
|
+
};
|
|
137
245
|
}
|
|
138
|
-
return {
|
|
139
|
-
status: 'healthy',
|
|
140
|
-
message: 'Integration is running',
|
|
141
|
-
};
|
|
142
246
|
},
|
|
143
247
|
},
|
|
144
248
|
|
|
145
|
-
|
|
249
|
+
i_verifyWebhook: {
|
|
146
250
|
rest: {
|
|
147
|
-
method: 'GET',
|
|
251
|
+
method: 'GET' as const,
|
|
148
252
|
path: '/webhook',
|
|
149
253
|
},
|
|
150
254
|
params: {
|
|
@@ -152,21 +256,26 @@ export function defineIntegration<
|
|
|
152
256
|
token: 'string',
|
|
153
257
|
challenge: 'string',
|
|
154
258
|
},
|
|
155
|
-
handler(
|
|
259
|
+
handler(
|
|
260
|
+
ctx: AppContext<
|
|
261
|
+
TDatasources,
|
|
262
|
+
{mode: string; token: string; challenge: string}
|
|
263
|
+
>
|
|
264
|
+
): string {
|
|
156
265
|
if (config.verifyWebhook) {
|
|
157
|
-
const result = config.verifyWebhook(ctx.params);
|
|
266
|
+
const result: string | null = config.verifyWebhook(ctx.params);
|
|
158
267
|
return result ?? '';
|
|
159
268
|
}
|
|
160
269
|
return ctx.params.challenge;
|
|
161
270
|
},
|
|
162
271
|
},
|
|
163
272
|
|
|
164
|
-
|
|
273
|
+
i_status: {
|
|
165
274
|
rest: {
|
|
166
|
-
method: 'GET',
|
|
275
|
+
method: 'GET' as const,
|
|
167
276
|
path: '/status',
|
|
168
277
|
},
|
|
169
|
-
handler() {
|
|
278
|
+
handler(): object {
|
|
170
279
|
return {
|
|
171
280
|
id: config.integration.id,
|
|
172
281
|
name: config.integration.name,
|
|
@@ -178,11 +287,13 @@ export function defineIntegration<
|
|
|
178
287
|
},
|
|
179
288
|
},
|
|
180
289
|
|
|
181
|
-
|
|
290
|
+
i_validateCredentials: {
|
|
182
291
|
params: {
|
|
183
292
|
credentials: 'object',
|
|
184
293
|
},
|
|
185
|
-
async handler(
|
|
294
|
+
async handler(
|
|
295
|
+
ctx: AppContext<TDatasources, {credentials: Record<string, string>}>
|
|
296
|
+
): Promise<boolean> {
|
|
186
297
|
if (config.validateCredentials) {
|
|
187
298
|
return await config.validateCredentials(ctx.params.credentials);
|
|
188
299
|
}
|
|
@@ -196,42 +307,47 @@ export function defineIntegration<
|
|
|
196
307
|
Object.assign(actions, config.actions);
|
|
197
308
|
}
|
|
198
309
|
|
|
199
|
-
//
|
|
200
|
-
const
|
|
310
|
+
// Use defineService to get all the mixins and proper setup
|
|
311
|
+
const baseService = defineService<TDatasources, TSettings>({
|
|
201
312
|
name: config.name,
|
|
202
313
|
version: config.version,
|
|
203
314
|
settings: config.settings as TSettings,
|
|
204
315
|
dependencies: config.dependencies,
|
|
316
|
+
datasources: config.datasources,
|
|
205
317
|
metadata: {
|
|
206
318
|
...config.metadata,
|
|
207
319
|
integration: config.integration,
|
|
208
320
|
},
|
|
209
321
|
actions,
|
|
210
322
|
events: config.events ?? {},
|
|
211
|
-
methods:
|
|
323
|
+
methods: {
|
|
324
|
+
...(config.methods ?? {}),
|
|
325
|
+
// Add integration-specific methods to the service methods (filter out undefined)
|
|
326
|
+
// normalize is required, no need for conditional
|
|
327
|
+
normalize: config.normalize,
|
|
328
|
+
...(config.transform && {transform: config.transform}),
|
|
329
|
+
...(config.validateWebhook && {validateWebhook: config.validateWebhook}),
|
|
330
|
+
// sendMessage is required, no need for conditional
|
|
331
|
+
sendMessage: config.sendMessage,
|
|
332
|
+
...(config.verifyWebhook && {verifyWebhook: config.verifyWebhook}),
|
|
333
|
+
...(config.checkHealth && {checkHealth: config.checkHealth}),
|
|
334
|
+
...(config.validateCredentials && {
|
|
335
|
+
validateCredentials: config.validateCredentials,
|
|
336
|
+
}),
|
|
337
|
+
...(config.validateSignature && {
|
|
338
|
+
validateSignature: config.validateSignature,
|
|
339
|
+
}),
|
|
340
|
+
},
|
|
212
341
|
hooks: config.hooks ?? {},
|
|
213
342
|
created: config.created,
|
|
214
343
|
started: config.started,
|
|
215
344
|
stopped: config.stopped,
|
|
216
|
-
|
|
217
|
-
// Integration-specific methods
|
|
218
|
-
integration: config.integration,
|
|
219
|
-
normalize: config.normalize,
|
|
220
|
-
transform: config.transform,
|
|
221
|
-
validateWebhook: config.validateWebhook,
|
|
222
|
-
sendMessage: config.sendMessage,
|
|
223
|
-
verifyWebhook: config.verifyWebhook,
|
|
224
|
-
checkHealth: config.checkHealth,
|
|
225
|
-
validateCredentials: config.validateCredentials,
|
|
226
|
-
validateSignature: config.validateSignature,
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
// Remove undefined properties
|
|
230
|
-
Object.keys(serviceSchema).forEach((key) => {
|
|
231
|
-
if (serviceSchema[key as keyof typeof serviceSchema] === undefined) {
|
|
232
|
-
delete serviceSchema[key as keyof typeof serviceSchema];
|
|
233
|
-
}
|
|
234
345
|
});
|
|
235
346
|
|
|
236
|
-
|
|
347
|
+
// Return the service schema - integration methods are available via service methods
|
|
348
|
+
return {
|
|
349
|
+
...baseService,
|
|
350
|
+
// Only add the integration metadata
|
|
351
|
+
integration: config.integration,
|
|
352
|
+
} as IntegrationServiceSchema<TPlatformMessage, TSettings>;
|
|
237
353
|
}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import omit from 'lodash/omit';
|
|
2
2
|
import type {ServiceSchema as MoleculerServiceSchema} from 'moleculer';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
MemoizeMixin,
|
|
7
|
-
PermissionsMiddleware,
|
|
8
|
-
createDatasourceMiddleware,
|
|
9
|
-
createCacheMiddleware,
|
|
10
|
-
} from '../middlewares';
|
|
4
|
+
import {MemoizeMixin} from '../middlewares';
|
|
5
|
+
import {DatasourceMixin} from '../mixins';
|
|
11
6
|
import type {ServiceConfig} from '../types/service';
|
|
12
7
|
|
|
13
8
|
/**
|
|
@@ -18,13 +13,6 @@ import type {ServiceConfig} from '../types/service';
|
|
|
18
13
|
* export default defineService({
|
|
19
14
|
* name: 'user',
|
|
20
15
|
*
|
|
21
|
-
* // Per-service cache configuration
|
|
22
|
-
* cache: {
|
|
23
|
-
* redisUrl: 'redis://localhost:6379',
|
|
24
|
-
* ttl: 10 * 60 * 1000, // 10 minutes
|
|
25
|
-
* namespace: 'user-service',
|
|
26
|
-
* },
|
|
27
|
-
*
|
|
28
16
|
* actions: {
|
|
29
17
|
* // Action with schema
|
|
30
18
|
* getUser: {
|
|
@@ -32,19 +20,14 @@ import type {ServiceConfig} from '../types/service';
|
|
|
32
20
|
* id: 'string'
|
|
33
21
|
* },
|
|
34
22
|
* handler(ctx) {
|
|
35
|
-
* // ctx is typed as AppContext with cache helpers
|
|
36
23
|
* const { tenantId } = ctx.meta;
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* return ctx.cache.wrap(`user:${ctx.params.id}`, async () => {
|
|
40
|
-
* return { id: ctx.params.id, tenantId };
|
|
41
|
-
* });
|
|
24
|
+
*
|
|
25
|
+
* return { id: ctx.params.id, tenantId };
|
|
42
26
|
* }
|
|
43
27
|
* },
|
|
44
28
|
*
|
|
45
29
|
* // Direct handler
|
|
46
30
|
* listUsers(ctx) {
|
|
47
|
-
* // ctx is typed as AppContext
|
|
48
31
|
* return [];
|
|
49
32
|
* }
|
|
50
33
|
* }
|
|
@@ -54,23 +37,20 @@ import type {ServiceConfig} from '../types/service';
|
|
|
54
37
|
export function defineService<TDatasources = unknown, TSettings = unknown>(
|
|
55
38
|
config: ServiceConfig<TSettings, TDatasources>
|
|
56
39
|
): MoleculerServiceSchema<TSettings> {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
const propsToOmit = ['datasources'];
|
|
41
|
+
const serviceSchema = omit(
|
|
42
|
+
config,
|
|
43
|
+
propsToOmit
|
|
44
|
+
) as unknown as MoleculerServiceSchema<TSettings>;
|
|
61
45
|
|
|
62
46
|
const datasources = config.datasources ?? {};
|
|
63
|
-
const cacheOptions = config.cache;
|
|
64
47
|
|
|
65
48
|
// TODO: Add mixins config support
|
|
66
49
|
return {
|
|
67
50
|
...serviceSchema,
|
|
68
51
|
mixins: [
|
|
69
|
-
|
|
70
|
-
...(cacheOptions ? [createCacheMiddleware(cacheOptions)] : []),
|
|
52
|
+
DatasourceMixin(datasources),
|
|
71
53
|
MemoizeMixin(),
|
|
72
|
-
PermissionsMiddleware,
|
|
73
|
-
ContextHelpersMiddleware,
|
|
74
54
|
...(serviceSchema.mixins ?? []),
|
|
75
55
|
],
|
|
76
56
|
};
|
|
@@ -8,11 +8,6 @@ export type GetUserActionInputParams = {
|
|
|
8
8
|
export default defineService<UserDatasourceTypes>({
|
|
9
9
|
name: 'test',
|
|
10
10
|
datasources,
|
|
11
|
-
cache: {
|
|
12
|
-
redisUrl: process.env.USER_SERVICE_REDIS_URL,
|
|
13
|
-
ttl: 5 * 60 * 1000, // 5 minutes
|
|
14
|
-
namespace: 'user-service',
|
|
15
|
-
},
|
|
16
11
|
actions: {
|
|
17
12
|
user: {
|
|
18
13
|
params: {
|
|
@@ -20,11 +15,8 @@ export default defineService<UserDatasourceTypes>({
|
|
|
20
15
|
},
|
|
21
16
|
handler(ctx) {
|
|
22
17
|
const userDs = ctx.datasources.user;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return ctx.cache.wrap(`user:${ctx.params.id}`, async () => {
|
|
26
|
-
return userDs.getUser();
|
|
27
|
-
});
|
|
18
|
+
|
|
19
|
+
return userDs.getUser();
|
|
28
20
|
},
|
|
29
21
|
},
|
|
30
22
|
},
|
package/src/types/context.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type {Context} from 'moleculer';
|
|
2
|
-
import type {User} from './user';
|
|
3
2
|
import type {Tenant} from './tenant';
|
|
4
|
-
import type {
|
|
3
|
+
import type {User} from './user';
|
|
5
4
|
|
|
6
5
|
export interface AppMeta {
|
|
7
6
|
user?: User;
|
|
@@ -11,6 +10,7 @@ export interface AppMeta {
|
|
|
11
10
|
integrationId?: string;
|
|
12
11
|
channelId?: string;
|
|
13
12
|
requestId?: string;
|
|
13
|
+
correlationId?: string;
|
|
14
14
|
userAgent?: string;
|
|
15
15
|
clientIP?: string;
|
|
16
16
|
[key: string]: unknown;
|
|
@@ -25,7 +25,11 @@ export interface PermissionHelpers {
|
|
|
25
25
|
ensureTenant(): Tenant;
|
|
26
26
|
// New enhanced helpers
|
|
27
27
|
getUserPermissions(): Promise<string[]>;
|
|
28
|
-
auditLog(
|
|
28
|
+
auditLog(
|
|
29
|
+
action: string,
|
|
30
|
+
resource?: unknown,
|
|
31
|
+
metadata?: Record<string, unknown>
|
|
32
|
+
): void;
|
|
29
33
|
createError(message: string, code: string, statusCode?: number): Error;
|
|
30
34
|
}
|
|
31
35
|
|
|
@@ -37,5 +41,4 @@ export type AppContext<
|
|
|
37
41
|
> = Context<TParams, TMeta, TLocals> &
|
|
38
42
|
PermissionHelpers & {
|
|
39
43
|
datasources: TDatasources;
|
|
40
|
-
cache: CacheHelpers;
|
|
41
44
|
};
|
package/src/types/message.ts
CHANGED
package/src/types/service.ts
CHANGED
|
@@ -8,7 +8,6 @@ import type {
|
|
|
8
8
|
} from 'moleculer';
|
|
9
9
|
|
|
10
10
|
import type {DatasourceConstructorRegistry} from '../middlewares/datasource.middleware';
|
|
11
|
-
import type {CacheOptions} from '../middlewares/cache.middleware';
|
|
12
11
|
import type {AppContext} from './context';
|
|
13
12
|
import type {BaseIntegration, IntegrationConfig} from './integration';
|
|
14
13
|
import type {
|
|
@@ -125,15 +124,15 @@ export type ServiceConfig<
|
|
|
125
124
|
TDatasources = unknown
|
|
126
125
|
> = ServiceSchema<TSettings, TDatasources> & {
|
|
127
126
|
datasources?: DatasourceConstructorRegistry;
|
|
128
|
-
cache?: CacheOptions;
|
|
129
127
|
};
|
|
130
128
|
|
|
131
129
|
// Integration-specific types
|
|
132
130
|
export interface IntegrationServiceConfig<
|
|
133
131
|
TPlatformMessage extends PlatformMessage = PlatformMessage,
|
|
134
132
|
TSettings = unknown,
|
|
135
|
-
TContext extends AppContext = AppContext
|
|
136
|
-
|
|
133
|
+
TContext extends AppContext = AppContext,
|
|
134
|
+
TDatasources = unknown
|
|
135
|
+
> extends ServiceConfig<TSettings, TDatasources> {
|
|
137
136
|
name: string;
|
|
138
137
|
integration: BaseIntegration;
|
|
139
138
|
|