@friggframework/core 2.0.0--canary.545.dba001a.0 → 2.0.0--canary.545.c40eca4.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/handlers/routers/admin.js +52 -28
- package/handlers/routers/auth.js +46 -25
- package/handlers/routers/health.js +56 -45
- package/handlers/routers/integration-defined-routers.js +43 -32
- package/handlers/routers/integration-webhook-routers.js +69 -58
- package/handlers/routers/user.js +30 -14
- package/handlers/routers/websocket.js +11 -3
- package/handlers/workers/integration-defined-workers.js +31 -20
- package/package.json +5 -5
|
@@ -25,17 +25,41 @@ const {
|
|
|
25
25
|
} = require('../../user/use-cases/create-token-for-user-id');
|
|
26
26
|
const { DeleteUser } = require('../../user/use-cases/delete-user');
|
|
27
27
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
// Lazy-initialized repositories and use cases (deferred until first request)
|
|
29
|
+
let _userRepository,
|
|
30
|
+
_moduleRepository,
|
|
31
|
+
_getModuleEntityById,
|
|
32
|
+
_updateModuleEntity,
|
|
33
|
+
_deleteModuleEntity,
|
|
34
|
+
_createTokenForUserId,
|
|
35
|
+
_deleteUser;
|
|
36
|
+
|
|
37
|
+
function ensureInitialized() {
|
|
38
|
+
if (!_userRepository) {
|
|
39
|
+
const { userConfig } = loadAppDefinition();
|
|
40
|
+
_userRepository = createUserRepository({ userConfig });
|
|
41
|
+
_moduleRepository = createModuleRepository();
|
|
42
|
+
_getModuleEntityById = new GetModuleEntityById({
|
|
43
|
+
moduleRepository: _moduleRepository,
|
|
44
|
+
});
|
|
45
|
+
_updateModuleEntity = new UpdateModuleEntity({
|
|
46
|
+
moduleRepository: _moduleRepository,
|
|
47
|
+
});
|
|
48
|
+
_deleteModuleEntity = new DeleteModuleEntity({
|
|
49
|
+
moduleRepository: _moduleRepository,
|
|
50
|
+
});
|
|
51
|
+
_createTokenForUserId = new CreateTokenForUserId({
|
|
52
|
+
userRepository: _userRepository,
|
|
53
|
+
});
|
|
54
|
+
_deleteUser = new DeleteUser({ userRepository: _userRepository });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Lazy initialization middleware — runs once on first request
|
|
59
|
+
router.use((req, res, next) => {
|
|
60
|
+
ensureInitialized();
|
|
61
|
+
next();
|
|
62
|
+
});
|
|
39
63
|
|
|
40
64
|
// Debug logging
|
|
41
65
|
router.use((req, res, next) => {
|
|
@@ -72,14 +96,14 @@ router.get(
|
|
|
72
96
|
sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
|
|
73
97
|
|
|
74
98
|
// Use repository to get users
|
|
75
|
-
const users = await
|
|
99
|
+
const users = await _userRepository.findAllUsers({
|
|
76
100
|
skip,
|
|
77
101
|
limit: parseInt(limit),
|
|
78
102
|
sort,
|
|
79
103
|
excludeFields: ['-hashword'], // Exclude password hash
|
|
80
104
|
});
|
|
81
105
|
|
|
82
|
-
const totalCount = await
|
|
106
|
+
const totalCount = await _userRepository.countUsers();
|
|
83
107
|
|
|
84
108
|
res.json({
|
|
85
109
|
users,
|
|
@@ -122,7 +146,7 @@ router.get(
|
|
|
122
146
|
sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
|
|
123
147
|
|
|
124
148
|
// Use repository to search users
|
|
125
|
-
const users = await
|
|
149
|
+
const users = await _userRepository.searchUsers({
|
|
126
150
|
query: q,
|
|
127
151
|
skip,
|
|
128
152
|
limit: parseInt(limit),
|
|
@@ -130,7 +154,7 @@ router.get(
|
|
|
130
154
|
excludeFields: ['-hashword'],
|
|
131
155
|
});
|
|
132
156
|
|
|
133
|
-
const totalCount = await
|
|
157
|
+
const totalCount = await _userRepository.countUsersBySearchQuery(q);
|
|
134
158
|
|
|
135
159
|
res.json({
|
|
136
160
|
users,
|
|
@@ -175,7 +199,7 @@ router.post(
|
|
|
175
199
|
}
|
|
176
200
|
|
|
177
201
|
// Check if user already exists
|
|
178
|
-
const existingUser = await
|
|
202
|
+
const existingUser = await _userRepository.findIndividualUserByUsername(
|
|
179
203
|
username
|
|
180
204
|
);
|
|
181
205
|
if (existingUser) {
|
|
@@ -185,7 +209,7 @@ router.post(
|
|
|
185
209
|
});
|
|
186
210
|
}
|
|
187
211
|
|
|
188
|
-
const existingEmail = await
|
|
212
|
+
const existingEmail = await _userRepository.findIndividualUserByEmail(
|
|
189
213
|
email
|
|
190
214
|
);
|
|
191
215
|
if (existingEmail) {
|
|
@@ -210,7 +234,7 @@ router.post(
|
|
|
210
234
|
if (appUserId) userData.appUserId = appUserId;
|
|
211
235
|
if (organizationId) userData.organizationId = organizationId;
|
|
212
236
|
|
|
213
|
-
const user = await
|
|
237
|
+
const user = await _userRepository.createIndividualUser(userData);
|
|
214
238
|
|
|
215
239
|
// Remove sensitive fields
|
|
216
240
|
const userObj = user.toObject ? user.toObject() : user;
|
|
@@ -232,7 +256,7 @@ router.get(
|
|
|
232
256
|
catchAsyncError(async (req, res) => {
|
|
233
257
|
const { userId } = req.params;
|
|
234
258
|
|
|
235
|
-
const user = await
|
|
259
|
+
const user = await _userRepository.findUserById(userId);
|
|
236
260
|
|
|
237
261
|
if (!user) {
|
|
238
262
|
return res.status(404).json({
|
|
@@ -261,7 +285,7 @@ router.post(
|
|
|
261
285
|
const { expiresInMinutes = 120 } = req.body;
|
|
262
286
|
|
|
263
287
|
// Find the user
|
|
264
|
-
const user = await
|
|
288
|
+
const user = await _userRepository.findUserById(userId);
|
|
265
289
|
|
|
266
290
|
if (!user) {
|
|
267
291
|
return res.status(404).json({
|
|
@@ -271,7 +295,7 @@ router.post(
|
|
|
271
295
|
}
|
|
272
296
|
|
|
273
297
|
// Generate token without password verification
|
|
274
|
-
const token = await
|
|
298
|
+
const token = await _createTokenForUserId.execute(
|
|
275
299
|
userId,
|
|
276
300
|
expiresInMinutes
|
|
277
301
|
);
|
|
@@ -295,7 +319,7 @@ router.delete(
|
|
|
295
319
|
const { userId } = req.params;
|
|
296
320
|
|
|
297
321
|
// Execute delete user use case
|
|
298
|
-
await
|
|
322
|
+
await _deleteUser.execute(userId);
|
|
299
323
|
|
|
300
324
|
res.status(204).send();
|
|
301
325
|
})
|
|
@@ -318,7 +342,7 @@ router.get(
|
|
|
318
342
|
if (type) query.type = type;
|
|
319
343
|
if (status) query.status = status;
|
|
320
344
|
|
|
321
|
-
const entities = await
|
|
345
|
+
const entities = await _moduleRepository.findEntitiesBy(query);
|
|
322
346
|
|
|
323
347
|
res.json({ entities });
|
|
324
348
|
})
|
|
@@ -333,7 +357,7 @@ router.get(
|
|
|
333
357
|
catchAsyncError(async (req, res) => {
|
|
334
358
|
const { entityId } = req.params;
|
|
335
359
|
|
|
336
|
-
const entity = await
|
|
360
|
+
const entity = await _getModuleEntityById.execute(entityId);
|
|
337
361
|
|
|
338
362
|
if (!entity || !entity.isGlobal) {
|
|
339
363
|
return res.status(404).json({
|
|
@@ -363,7 +387,7 @@ router.post(
|
|
|
363
387
|
}
|
|
364
388
|
|
|
365
389
|
// Create entity with isGlobal flag
|
|
366
|
-
const entity = await
|
|
390
|
+
const entity = await _moduleRepository.createEntity({
|
|
367
391
|
...entityData,
|
|
368
392
|
type,
|
|
369
393
|
isGlobal: true,
|
|
@@ -383,7 +407,7 @@ router.put(
|
|
|
383
407
|
catchAsyncError(async (req, res) => {
|
|
384
408
|
const { entityId } = req.params;
|
|
385
409
|
|
|
386
|
-
const entity = await
|
|
410
|
+
const entity = await _updateModuleEntity.execute(entityId, req.body);
|
|
387
411
|
|
|
388
412
|
if (!entity) {
|
|
389
413
|
return res.status(404).json({
|
|
@@ -405,7 +429,7 @@ router.delete(
|
|
|
405
429
|
catchAsyncError(async (req, res) => {
|
|
406
430
|
const { entityId } = req.params;
|
|
407
431
|
|
|
408
|
-
await
|
|
432
|
+
await _deleteModuleEntity.execute(entityId);
|
|
409
433
|
|
|
410
434
|
res.status(204).send();
|
|
411
435
|
})
|
|
@@ -420,7 +444,7 @@ router.post(
|
|
|
420
444
|
catchAsyncError(async (req, res) => {
|
|
421
445
|
const { entityId } = req.params;
|
|
422
446
|
|
|
423
|
-
const entity = await
|
|
447
|
+
const entity = await _getModuleEntityById.execute(entityId);
|
|
424
448
|
|
|
425
449
|
if (!entity || !entity.isGlobal) {
|
|
426
450
|
return res.status(404).json({
|
package/handlers/routers/auth.js
CHANGED
|
@@ -3,34 +3,55 @@ const { createAppHandler } = require('./../app-handler-helpers');
|
|
|
3
3
|
const { requireLoggedInUser } = require('./middleware/requireLoggedInUser');
|
|
4
4
|
const { loadAppDefinition } = require('../app-definition-loader');
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
let _router;
|
|
7
|
+
let _handler;
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
req.params.appId
|
|
12
|
-
}?${new URLSearchParams(req.query)}`
|
|
13
|
-
);
|
|
14
|
-
});
|
|
9
|
+
function ensureRouter() {
|
|
10
|
+
if (!_router) {
|
|
11
|
+
_router = createIntegrationRouter();
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
_router
|
|
14
|
+
.route('/api/integrations/redirect/:appId')
|
|
15
|
+
.get((req, res) => {
|
|
16
|
+
res.redirect(
|
|
17
|
+
`${process.env.FRONTEND_URI}/redirect/${
|
|
18
|
+
req.params.appId
|
|
19
|
+
}?${new URLSearchParams(req.query)}`
|
|
20
|
+
);
|
|
21
|
+
});
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
appDefinition
|
|
27
|
-
allowUserManagedEntities:
|
|
28
|
-
appDefinition.integration?.allowUserManagedEntities ?? true,
|
|
29
|
-
};
|
|
23
|
+
// Integration settings endpoint
|
|
24
|
+
_router
|
|
25
|
+
.route('/config/integration-settings')
|
|
26
|
+
.get(requireLoggedInUser, (req, res) => {
|
|
27
|
+
const appDefinition = loadAppDefinition();
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const settings = {
|
|
30
|
+
autoProvisioningEnabled:
|
|
31
|
+
appDefinition.integration
|
|
32
|
+
?.autoProvisioningEnabled ?? true,
|
|
33
|
+
credentialReuseStrategy:
|
|
34
|
+
appDefinition.integration
|
|
35
|
+
?.credentialReuseStrategy ?? 'shared',
|
|
36
|
+
allowUserManagedEntities:
|
|
37
|
+
appDefinition.integration
|
|
38
|
+
?.allowUserManagedEntities ?? true,
|
|
39
|
+
};
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
res.json(settings);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return _router;
|
|
45
|
+
}
|
|
35
46
|
|
|
36
|
-
module.exports = {
|
|
47
|
+
module.exports = {
|
|
48
|
+
get router() {
|
|
49
|
+
return ensureRouter();
|
|
50
|
+
},
|
|
51
|
+
get handler() {
|
|
52
|
+
if (!_handler) {
|
|
53
|
+
_handler = createAppHandler('HTTP Event: Auth', ensureRouter());
|
|
54
|
+
}
|
|
55
|
+
return _handler;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -29,49 +29,58 @@ const {
|
|
|
29
29
|
} = require('../use-cases/check-integrations-health-use-case');
|
|
30
30
|
|
|
31
31
|
const router = Router();
|
|
32
|
-
const healthCheckRepository = createHealthCheckRepository({
|
|
33
|
-
prismaClient: prisma,
|
|
34
|
-
});
|
|
35
32
|
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
integrationClasses = appDef.integrations || [];
|
|
33
|
+
// Lazy-initialized use cases (deferred until first request)
|
|
34
|
+
let _checkDatabaseHealthUseCase,
|
|
35
|
+
_checkEncryptionHealthUseCase,
|
|
36
|
+
_checkExternalApisHealthUseCase,
|
|
37
|
+
_checkIntegrationsHealthUseCase;
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
function ensureInitialized() {
|
|
40
|
+
if (!_checkDatabaseHealthUseCase) {
|
|
41
|
+
const healthCheckRepository = createHealthCheckRepository({
|
|
42
|
+
prismaClient: prisma,
|
|
43
|
+
});
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
45
|
+
let moduleFactory, integrationClasses;
|
|
46
|
+
try {
|
|
47
|
+
const appDef = loadAppDefinition();
|
|
48
|
+
integrationClasses = appDef.integrations || [];
|
|
49
|
+
|
|
50
|
+
const moduleRepository = createModuleRepository();
|
|
51
|
+
const moduleDefinitions =
|
|
52
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses);
|
|
53
|
+
|
|
54
|
+
moduleFactory = new ModuleFactory({
|
|
55
|
+
moduleRepository,
|
|
56
|
+
moduleDefinitions,
|
|
57
|
+
});
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(
|
|
60
|
+
'Failed to load integrations for health check:',
|
|
61
|
+
error.message
|
|
62
|
+
);
|
|
63
|
+
moduleFactory = undefined;
|
|
64
|
+
integrationClasses = [];
|
|
65
|
+
}
|
|
60
66
|
|
|
61
|
-
const testEncryptionUseCase = new TestEncryptionUseCase({
|
|
62
|
-
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
const testEncryptionUseCase = new TestEncryptionUseCase({
|
|
68
|
+
healthCheckRepository,
|
|
69
|
+
});
|
|
70
|
+
_checkDatabaseHealthUseCase = new CheckDatabaseHealthUseCase({
|
|
71
|
+
healthCheckRepository,
|
|
72
|
+
});
|
|
73
|
+
_checkEncryptionHealthUseCase = new CheckEncryptionHealthUseCase({
|
|
74
|
+
testEncryptionUseCase,
|
|
75
|
+
});
|
|
76
|
+
_checkExternalApisHealthUseCase =
|
|
77
|
+
new CheckExternalApisHealthUseCase();
|
|
78
|
+
_checkIntegrationsHealthUseCase = new CheckIntegrationsHealthUseCase({
|
|
79
|
+
moduleFactory,
|
|
80
|
+
integrationClasses,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
75
84
|
|
|
76
85
|
const validateApiKey = (req, res, next) => {
|
|
77
86
|
const apiKey = req.headers['x-frigg-health-api-key'];
|
|
@@ -132,6 +141,7 @@ router.get('/health', async (_req, res) => {
|
|
|
132
141
|
});
|
|
133
142
|
|
|
134
143
|
router.get('/health/detailed', async (_req, res) => {
|
|
144
|
+
ensureInitialized();
|
|
135
145
|
console.log('Starting detailed health check');
|
|
136
146
|
const startTime = Date.now();
|
|
137
147
|
|
|
@@ -197,7 +207,7 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
try {
|
|
200
|
-
response.checks.database = await
|
|
210
|
+
response.checks.database = await _checkDatabaseHealthUseCase.execute();
|
|
201
211
|
if (response.checks.database.status === 'unhealthy') {
|
|
202
212
|
response.status = 'unhealthy';
|
|
203
213
|
}
|
|
@@ -213,7 +223,7 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
213
223
|
|
|
214
224
|
try {
|
|
215
225
|
response.checks.encryption =
|
|
216
|
-
await
|
|
226
|
+
await _checkEncryptionHealthUseCase.execute();
|
|
217
227
|
if (response.checks.encryption.status === 'unhealthy') {
|
|
218
228
|
response.status = 'unhealthy';
|
|
219
229
|
}
|
|
@@ -229,7 +239,7 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
229
239
|
|
|
230
240
|
try {
|
|
231
241
|
const { apiStatuses, allReachable } =
|
|
232
|
-
await
|
|
242
|
+
await _checkExternalApisHealthUseCase.execute();
|
|
233
243
|
response.checks.externalApis = apiStatuses;
|
|
234
244
|
if (!allReachable) {
|
|
235
245
|
response.status = 'unhealthy';
|
|
@@ -248,7 +258,7 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
248
258
|
}
|
|
249
259
|
|
|
250
260
|
try {
|
|
251
|
-
response.checks.integrations =
|
|
261
|
+
response.checks.integrations = _checkIntegrationsHealthUseCase.execute();
|
|
252
262
|
console.log(
|
|
253
263
|
'Integrations check completed:',
|
|
254
264
|
response.checks.integrations
|
|
@@ -283,10 +293,11 @@ router.get('/health/live', (_req, res) => {
|
|
|
283
293
|
});
|
|
284
294
|
|
|
285
295
|
router.get('/health/ready', async (_req, res) => {
|
|
286
|
-
|
|
296
|
+
ensureInitialized();
|
|
297
|
+
const dbHealth = await _checkDatabaseHealthUseCase.execute();
|
|
287
298
|
const isDbReady = dbHealth.status === 'healthy';
|
|
288
299
|
|
|
289
|
-
const integrationsHealth =
|
|
300
|
+
const integrationsHealth = _checkIntegrationsHealthUseCase.execute();
|
|
290
301
|
const areModulesReady = integrationsHealth.modules.count > 0;
|
|
291
302
|
|
|
292
303
|
const isReady = isDbReady && areModulesReady;
|
|
@@ -3,43 +3,54 @@ const { loadAppDefinition } = require('../app-definition-loader');
|
|
|
3
3
|
const { Router } = require('express');
|
|
4
4
|
const { loadRouterFromObject } = require('../backend-utils');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const { integrations: integrationClasses } = loadAppDefinition();
|
|
6
|
+
let _handlers;
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
function ensureHandlers() {
|
|
9
|
+
if (!_handlers) {
|
|
10
|
+
_handlers = {};
|
|
11
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
//todo: this should be in a use case class
|
|
14
|
+
for (const IntegrationClass of integrationClasses) {
|
|
15
|
+
const router = Router();
|
|
16
|
+
const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
router.use(basePath, routeDef(IntegrationClass));
|
|
21
|
-
console.log(`│ ANY ${basePath}/* (function handler)`);
|
|
22
|
-
} else if (typeof routeDef === 'object') {
|
|
23
|
-
router.use(
|
|
24
|
-
basePath,
|
|
25
|
-
loadRouterFromObject(IntegrationClass, routeDef)
|
|
18
|
+
console.log(
|
|
19
|
+
`\n│ Configuring routes for ${IntegrationClass.Definition.name} Integration:`
|
|
26
20
|
);
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
|
|
22
|
+
for (const routeDef of IntegrationClass.Definition.routes) {
|
|
23
|
+
if (typeof routeDef === 'function') {
|
|
24
|
+
router.use(basePath, routeDef(IntegrationClass));
|
|
25
|
+
console.log(`│ ANY ${basePath}/* (function handler)`);
|
|
26
|
+
} else if (typeof routeDef === 'object') {
|
|
27
|
+
router.use(
|
|
28
|
+
basePath,
|
|
29
|
+
loadRouterFromObject(IntegrationClass, routeDef)
|
|
30
|
+
);
|
|
31
|
+
const method = (routeDef.method || 'ANY').toUpperCase();
|
|
32
|
+
const fullPath = `${basePath}${routeDef.path}`;
|
|
33
|
+
console.log(`│ ${method} ${fullPath}`);
|
|
34
|
+
} else if (routeDef instanceof express.Router) {
|
|
35
|
+
router.use(basePath, routeDef);
|
|
36
|
+
console.log(`│ ANY ${basePath}/* (express router)`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
console.log('│');
|
|
40
|
+
|
|
41
|
+
_handlers[`${IntegrationClass.Definition.name}`] = {
|
|
42
|
+
handler: createAppHandler(
|
|
43
|
+
`HTTP Event: ${IntegrationClass.Definition.name}`,
|
|
44
|
+
router
|
|
45
|
+
),
|
|
46
|
+
};
|
|
33
47
|
}
|
|
34
48
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
handlers[`${IntegrationClass.Definition.name}`] = {
|
|
38
|
-
handler: createAppHandler(
|
|
39
|
-
`HTTP Event: ${IntegrationClass.Definition.name}`,
|
|
40
|
-
router
|
|
41
|
-
),
|
|
42
|
-
};
|
|
49
|
+
return _handlers;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
|
-
module.exports = {
|
|
52
|
+
module.exports = {
|
|
53
|
+
get handlers() {
|
|
54
|
+
return ensureHandlers();
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -5,73 +5,84 @@ const {
|
|
|
5
5
|
IntegrationEventDispatcher,
|
|
6
6
|
} = require('../integration-event-dispatcher');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
const { integrations: integrationClasses } = loadAppDefinition();
|
|
8
|
+
let _handlers;
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
function ensureHandlers() {
|
|
11
|
+
if (!_handlers) {
|
|
12
|
+
_handlers = {};
|
|
13
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
!webhookConfig ||
|
|
17
|
-
(typeof webhookConfig === 'object' && !webhookConfig.enabled)
|
|
18
|
-
) {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
15
|
+
for (const IntegrationClass of integrationClasses) {
|
|
16
|
+
const webhookConfig = IntegrationClass.Definition.webhooks;
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
// Skip if webhooks not enabled
|
|
19
|
+
if (
|
|
20
|
+
!webhookConfig ||
|
|
21
|
+
(typeof webhookConfig === 'object' && !webhookConfig.enabled)
|
|
22
|
+
) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
26
|
+
const router = Router();
|
|
27
|
+
const basePath = `/api/${IntegrationClass.Definition.name}-integration/webhooks`;
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const integrationInstance = new IntegrationClass();
|
|
33
|
-
const dispatcher = new IntegrationEventDispatcher(
|
|
34
|
-
integrationInstance
|
|
29
|
+
console.log(
|
|
30
|
+
`\n│ Configuring webhook routes for ${IntegrationClass.Definition.name}:`
|
|
35
31
|
);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
|
|
33
|
+
// General webhook route (no integration ID)
|
|
34
|
+
router.post(basePath, async (req, res, next) => {
|
|
35
|
+
try {
|
|
36
|
+
const integrationInstance = new IntegrationClass();
|
|
37
|
+
const dispatcher = new IntegrationEventDispatcher(
|
|
38
|
+
integrationInstance
|
|
39
|
+
);
|
|
40
|
+
await dispatcher.dispatchHttp({
|
|
41
|
+
event: 'WEBHOOK_RECEIVED',
|
|
42
|
+
req,
|
|
43
|
+
res,
|
|
44
|
+
next,
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
next(error);
|
|
48
|
+
}
|
|
41
49
|
});
|
|
42
|
-
|
|
43
|
-
next(error);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
console.log(`│ POST ${basePath}`);
|
|
50
|
+
console.log(`│ POST ${basePath}`);
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
// Integration-specific webhook route (with integration ID)
|
|
53
|
+
router.post(`${basePath}/:integrationId`, async (req, res, next) => {
|
|
54
|
+
try {
|
|
55
|
+
const integrationInstance = new IntegrationClass();
|
|
56
|
+
const dispatcher = new IntegrationEventDispatcher(
|
|
57
|
+
integrationInstance
|
|
58
|
+
);
|
|
59
|
+
await dispatcher.dispatchHttp({
|
|
60
|
+
event: 'WEBHOOK_RECEIVED',
|
|
61
|
+
req,
|
|
62
|
+
res,
|
|
63
|
+
next,
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
next(error);
|
|
67
|
+
}
|
|
60
68
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
console.log(`│ POST ${basePath}/:integrationId`);
|
|
66
|
-
console.log('│');
|
|
69
|
+
console.log(`│ POST ${basePath}/:integrationId`);
|
|
70
|
+
console.log('│');
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
_handlers[`${IntegrationClass.Definition.name}Webhook`] = {
|
|
73
|
+
handler: createAppHandler(
|
|
74
|
+
`HTTP Event: ${IntegrationClass.Definition.name} Webhook`,
|
|
75
|
+
router,
|
|
76
|
+
false // shouldUseDatabase = false
|
|
77
|
+
),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return _handlers;
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
module.exports = {
|
|
84
|
+
module.exports = {
|
|
85
|
+
get handlers() {
|
|
86
|
+
return ensureHandlers();
|
|
87
|
+
},
|
|
88
|
+
};
|
package/handlers/routers/user.js
CHANGED
|
@@ -15,17 +15,33 @@ const catchAsyncError = require('express-async-handler');
|
|
|
15
15
|
const { loadAppDefinition } = require('../app-definition-loader');
|
|
16
16
|
|
|
17
17
|
const router = express();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
|
|
19
|
+
// Lazy-initialized repositories and use cases (deferred until first request)
|
|
20
|
+
let _userRepository, _createIndividualUser, _loginUser, _createTokenForUserId;
|
|
21
|
+
|
|
22
|
+
function ensureInitialized() {
|
|
23
|
+
if (!_userRepository) {
|
|
24
|
+
const { userConfig } = loadAppDefinition();
|
|
25
|
+
_userRepository = createUserRepository();
|
|
26
|
+
_createIndividualUser = new CreateIndividualUser({
|
|
27
|
+
userRepository: _userRepository,
|
|
28
|
+
userConfig,
|
|
29
|
+
});
|
|
30
|
+
_loginUser = new LoginUser({
|
|
31
|
+
userRepository: _userRepository,
|
|
32
|
+
userConfig,
|
|
33
|
+
});
|
|
34
|
+
_createTokenForUserId = new CreateTokenForUserId({
|
|
35
|
+
userRepository: _userRepository,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Lazy initialization middleware — runs once on first request
|
|
41
|
+
router.use((req, res, next) => {
|
|
42
|
+
ensureInitialized();
|
|
43
|
+
next();
|
|
27
44
|
});
|
|
28
|
-
const createTokenForUserId = new CreateTokenForUserId({ userRepository });
|
|
29
45
|
|
|
30
46
|
// define the login endpoint
|
|
31
47
|
router.route('/user/login').post(
|
|
@@ -34,8 +50,8 @@ router.route('/user/login').post(
|
|
|
34
50
|
'username',
|
|
35
51
|
'password',
|
|
36
52
|
]);
|
|
37
|
-
const user = await
|
|
38
|
-
const token = await
|
|
53
|
+
const user = await _loginUser.execute({ username, password });
|
|
54
|
+
const token = await _createTokenForUserId.execute(user.getId(), 120);
|
|
39
55
|
res.status(201);
|
|
40
56
|
res.json({ token });
|
|
41
57
|
})
|
|
@@ -48,11 +64,11 @@ router.route('/user/create').post(
|
|
|
48
64
|
'password',
|
|
49
65
|
]);
|
|
50
66
|
|
|
51
|
-
const user = await
|
|
67
|
+
const user = await _createIndividualUser.execute({
|
|
52
68
|
username,
|
|
53
69
|
password,
|
|
54
70
|
});
|
|
55
|
-
const token = await
|
|
71
|
+
const token = await _createTokenForUserId.execute(user.getId(), 120);
|
|
56
72
|
res.status(201);
|
|
57
73
|
res.json({ token });
|
|
58
74
|
})
|
|
@@ -3,7 +3,15 @@ const {
|
|
|
3
3
|
createWebsocketConnectionRepository,
|
|
4
4
|
} = require('../../database/websocket-connection-repository-factory');
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
let _websocketConnectionRepository;
|
|
7
|
+
|
|
8
|
+
function getRepository() {
|
|
9
|
+
if (!_websocketConnectionRepository) {
|
|
10
|
+
_websocketConnectionRepository =
|
|
11
|
+
createWebsocketConnectionRepository();
|
|
12
|
+
}
|
|
13
|
+
return _websocketConnectionRepository;
|
|
14
|
+
}
|
|
7
15
|
|
|
8
16
|
const handleWebSocketConnection = async (event, context) => {
|
|
9
17
|
// Handle different WebSocket events
|
|
@@ -12,7 +20,7 @@ const handleWebSocketConnection = async (event, context) => {
|
|
|
12
20
|
// Handle new connection
|
|
13
21
|
try {
|
|
14
22
|
const connectionId = event.requestContext.connectionId;
|
|
15
|
-
await
|
|
23
|
+
await getRepository().createConnection(
|
|
16
24
|
connectionId
|
|
17
25
|
);
|
|
18
26
|
console.log(`Stored new connection: ${connectionId}`);
|
|
@@ -26,7 +34,7 @@ const handleWebSocketConnection = async (event, context) => {
|
|
|
26
34
|
// Handle disconnection
|
|
27
35
|
try {
|
|
28
36
|
const connectionId = event.requestContext.connectionId;
|
|
29
|
-
await
|
|
37
|
+
await getRepository().deleteConnection(
|
|
30
38
|
connectionId
|
|
31
39
|
);
|
|
32
40
|
console.log(`Removed connection: ${connectionId}`);
|
|
@@ -2,26 +2,37 @@ const { createHandler } = require('@friggframework/core');
|
|
|
2
2
|
const { loadAppDefinition } = require('../app-definition-loader');
|
|
3
3
|
const { createQueueWorker } = require('../backend-utils');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
const { integrations: integrationClasses } = loadAppDefinition();
|
|
5
|
+
let _handlers;
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
function ensureHandlers() {
|
|
8
|
+
if (!_handlers) {
|
|
9
|
+
_handlers = {};
|
|
10
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
|
|
14
|
-
isUserFacingResponse: false,
|
|
15
|
-
method: async (event, context) => {
|
|
16
|
-
const worker = new defaultQueueWorker();
|
|
17
|
-
await worker.run(event, context);
|
|
18
|
-
return {
|
|
19
|
-
message: 'Successfully processed the Generic Queue Worker',
|
|
20
|
-
input: event,
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
}),
|
|
24
|
-
};
|
|
25
|
-
});
|
|
12
|
+
integrationClasses.forEach((IntegrationClass) => {
|
|
13
|
+
const defaultQueueWorker = createQueueWorker(IntegrationClass);
|
|
26
14
|
|
|
27
|
-
|
|
15
|
+
_handlers[`${IntegrationClass.Definition.name}`] = {
|
|
16
|
+
queueWorker: createHandler({
|
|
17
|
+
eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
|
|
18
|
+
isUserFacingResponse: false,
|
|
19
|
+
method: async (event, context) => {
|
|
20
|
+
const worker = new defaultQueueWorker();
|
|
21
|
+
await worker.run(event, context);
|
|
22
|
+
return {
|
|
23
|
+
message: 'Successfully processed the Generic Queue Worker',
|
|
24
|
+
input: event,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return _handlers;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
get handlers() {
|
|
36
|
+
return ensureHandlers();
|
|
37
|
+
},
|
|
38
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.545.
|
|
4
|
+
"version": "2.0.0--canary.545.c40eca4.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/boom": "^10.0.1",
|
|
7
7
|
"bcryptjs": "^2.4.3",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@friggframework/eslint-config": "2.0.0--canary.545.
|
|
39
|
-
"@friggframework/prettier-config": "2.0.0--canary.545.
|
|
40
|
-
"@friggframework/test": "2.0.0--canary.545.
|
|
38
|
+
"@friggframework/eslint-config": "2.0.0--canary.545.c40eca4.0",
|
|
39
|
+
"@friggframework/prettier-config": "2.0.0--canary.545.c40eca4.0",
|
|
40
|
+
"@friggframework/test": "2.0.0--canary.545.c40eca4.0",
|
|
41
41
|
"@prisma/client": "^6.17.0",
|
|
42
42
|
"@types/lodash": "4.17.15",
|
|
43
43
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"publishConfig": {
|
|
79
79
|
"access": "public"
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "c40eca49e72472f0499b24ce709a655a7503727b"
|
|
82
82
|
}
|