@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.dba001a.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 +66 -67
- package/CLAUDE.md +148 -124
- package/README.md +165 -130
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
- package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
- package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
- package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
- package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
- package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
- package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
- package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
- package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
- package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/admin-script-commands.js +348 -0
- package/application/commands/integration-commands.js +12 -6
- package/application/index.js +5 -3
- package/assertions/get.js +17 -7
- package/associations/association.js +59 -57
- package/core/CLAUDE.md +222 -156
- package/core/Worker.js +33 -12
- package/credential/credential-router.js +262 -0
- package/credential/repositories/credential-repository-documentdb.js +71 -1
- package/credential/repositories/credential-repository-factory.js +4 -6
- package/credential/repositories/credential-repository-interface.js +8 -2
- package/credential/repositories/credential-repository-mongo.js +45 -4
- package/credential/repositories/credential-repository-postgres.js +44 -3
- package/credential/repositories/credential-repository.js +3 -1
- package/credential/use-cases/delete-credential-for-user.js +50 -0
- package/credential/use-cases/get-credential-for-user.js +21 -3
- package/credential/use-cases/list-credentials-for-user.js +31 -0
- package/credential/use-cases/reauthorize-credential.js +103 -0
- package/credential/use-cases/update-authentication-status.js +5 -2
- package/database/MONGODB_TRANSACTION_FIX.md +72 -52
- package/database/config.js +13 -59
- package/database/documentdb-encryption-service.js +29 -13
- package/database/documentdb-utils.js +21 -12
- package/database/encryption/README.md +53 -36
- package/database/encryption/encryption-schema-registry.js +29 -23
- package/database/encryption/field-encryption-service.js +15 -4
- package/database/encryption/logger.js +3 -2
- package/database/encryption/prisma-encryption-extension.js +5 -8
- package/database/prisma.js +22 -14
- package/database/repositories/health-check-repository-factory.js +7 -5
- package/database/repositories/health-check-repository-interface.js +15 -5
- package/database/repositories/health-check-repository-postgres.js +1 -1
- package/database/use-cases/check-database-health-use-case.js +3 -2
- package/database/use-cases/check-database-state-use-case.js +8 -7
- package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
- package/database/use-cases/get-migration-status-use-case.js +4 -2
- package/database/use-cases/run-database-migration-use-case.js +41 -9
- package/database/use-cases/test-encryption-use-case.js +4 -5
- package/database/use-cases/trigger-database-migration-use-case.js +9 -8
- package/database/utils/mongodb-collection-utils.js +11 -3
- package/database/utils/mongodb-schema-init.js +10 -4
- package/database/utils/prisma-runner.js +144 -74
- package/database/utils/prisma-schema-parser.js +2 -2
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
- package/encrypt/Cryptor.js +54 -49
- package/encrypt/aes-encryption-key-provider.js +82 -0
- package/encrypt/encryption-key-provider-interface.js +50 -0
- package/errors/fetch-error.js +1 -1
- package/generated/prisma-mongodb/edge.js +59 -4
- package/generated/prisma-mongodb/index-browser.js +49 -1
- package/generated/prisma-mongodb/index.d.ts +4023 -61
- package/generated/prisma-mongodb/index.js +59 -4
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +87 -0
- package/generated/prisma-mongodb/wasm.js +59 -4
- package/generated/prisma-postgresql/edge.js +58 -4
- package/generated/prisma-postgresql/index-browser.js +48 -1
- package/generated/prisma-postgresql/index.d.ts +4738 -54
- package/generated/prisma-postgresql/index.js +58 -4
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +85 -0
- package/generated/prisma-postgresql/wasm.js +58 -4
- package/handlers/WEBHOOKS.md +104 -81
- package/handlers/app-definition-loader.js +53 -10
- package/handlers/app-handler-helpers.js +14 -3
- package/handlers/backend-utils.js +0 -1
- package/handlers/database-migration-handler.js +64 -45
- package/handlers/middleware/admin-auth.js +53 -0
- package/handlers/routers/HEALTHCHECK.md +170 -135
- package/handlers/routers/admin.js +450 -0
- package/handlers/routers/auth.js +23 -2
- package/handlers/routers/db-migration.handler.js +2 -3
- package/handlers/routers/db-migration.js +102 -66
- package/handlers/routers/docs.js +180 -0
- package/handlers/routers/health.js +39 -249
- package/handlers/routers/integration-defined-routers.js +4 -4
- package/handlers/routers/integration-webhook-routers.js +17 -7
- package/handlers/routers/middleware/loadUser.js +39 -0
- package/handlers/routers/middleware/requireAdmin.js +43 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
- package/handlers/routers/websocket.js +9 -3
- package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
- package/handlers/workers/db-migration.js +74 -35
- package/index.js +50 -16
- package/infrastructure/scheduler/index.js +15 -11
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
- package/integrations/WEBHOOK-QUICKSTART.md +21 -14
- package/integrations/index.js +4 -0
- package/integrations/integration-base.js +9 -6
- package/integrations/integration-router.js +1828 -157
- package/integrations/options.js +47 -20
- package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
- package/integrations/repositories/integration-repository-documentdb.js +53 -24
- package/integrations/repositories/integration-repository-factory.js +6 -6
- package/integrations/repositories/integration-repository-interface.js +27 -9
- package/integrations/repositories/integration-repository-postgres.js +3 -3
- package/integrations/repositories/process-repository-factory.js +4 -7
- package/integrations/repositories/process-repository-interface.js +9 -5
- package/integrations/repositories/process-repository-mongo.js +7 -3
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/config-capturing-integration.js +9 -8
- package/integrations/tests/doubles/dummy-integration-class.js +81 -8
- package/integrations/tests/doubles/test-integration-repository.js +51 -11
- package/integrations/use-cases/create-integration.js +36 -32
- package/integrations/use-cases/create-process.js +20 -9
- package/integrations/use-cases/delete-integration-for-user.js +5 -4
- package/integrations/use-cases/execute-proxy-request.js +564 -0
- package/integrations/use-cases/get-integration-for-user.js +25 -10
- package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
- package/integrations/use-cases/get-integrations-for-user.js +4 -1
- package/integrations/use-cases/get-possible-integrations.js +1 -1
- package/integrations/use-cases/get-process.js +5 -6
- package/integrations/use-cases/index.js +3 -1
- package/integrations/use-cases/load-integration-context.js +1 -5
- package/integrations/use-cases/update-process-state.js +10 -8
- package/integrations/utils/map-integration-dto.js +9 -3
- package/jest-setup.js +1 -1
- package/jest-teardown.js +1 -1
- package/lambda/TimeoutCatcher.js +32 -30
- package/lambda/index.js +1 -1
- package/logs/index.js +3 -3
- package/modules/domain/entities/AuthorizationSession.js +131 -0
- package/modules/domain/entities/index.js +10 -0
- package/modules/module.js +4 -9
- package/modules/repositories/authorization-session-repository-factory.js +53 -0
- package/modules/repositories/authorization-session-repository-interface.js +75 -0
- package/modules/repositories/authorization-session-repository-mongo.js +169 -0
- package/modules/repositories/authorization-session-repository-postgres.js +170 -0
- package/modules/repositories/module-repository-documentdb.js +89 -41
- package/modules/repositories/module-repository-factory.js +4 -6
- package/modules/repositories/module-repository-interface.js +13 -0
- package/modules/repositories/module-repository-mongo.js +40 -5
- package/modules/repositories/module-repository-postgres.js +42 -5
- package/modules/repositories/module-repository.js +28 -0
- package/modules/requester/api-key.js +0 -2
- package/modules/requester/basic.js +0 -1
- package/modules/requester/oauth-2.js +3 -14
- package/modules/test/mock-api/definition.js +15 -10
- package/modules/test/mock-api/mocks/hubspot.js +36 -36
- package/modules/tests/doubles/test-module-factory.js +10 -4
- package/modules/tests/doubles/test-module-repository.js +1 -1
- package/modules/use-cases/delete-module-entity.js +23 -0
- package/modules/use-cases/get-authorization-requirements.js +110 -0
- package/modules/use-cases/get-entities-for-user.js +1 -1
- package/modules/use-cases/get-entity-options-by-id.js +8 -6
- package/modules/use-cases/get-module-entity-by-id.js +16 -0
- package/modules/use-cases/get-module.js +12 -9
- package/modules/use-cases/process-authorization-callback.js +12 -6
- package/modules/use-cases/process-authorization-step.js +160 -0
- package/modules/use-cases/refresh-entity-options.js +8 -6
- package/modules/use-cases/start-authorization-session.js +86 -0
- package/modules/use-cases/test-module-auth.js +8 -6
- package/modules/use-cases/update-module-entity.js +27 -0
- package/modules/utils/map-module-dto.js +2 -2
- package/openapi/openapi-spec-generator.js +370 -0
- package/openapi/openapi-v1.yaml +1360 -0
- package/openapi/openapi-v2.yaml +1535 -0
- package/package.json +7 -9
- package/prisma-mongodb/schema.prisma +87 -0
- package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
- package/prisma-postgresql/schema.prisma +85 -0
- package/providers/index.js +13 -0
- package/providers/resolve-provider.js +93 -0
- package/queues/index.js +19 -0
- package/queues/providers/index.js +7 -0
- package/queues/providers/netlify-background-provider.js +76 -0
- package/queues/providers/qstash-queue-provider.js +122 -0
- package/queues/queue-client-interface.js +55 -0
- package/queues/queue-provider-factory.js +93 -0
- package/queues/queue-provider.js +46 -0
- package/queues/queuer-util.js +33 -25
- package/syncs/repositories/sync-repository-documentdb.js +39 -19
- package/syncs/repositories/sync-repository-factory.js +4 -4
- package/syncs/repositories/sync-repository-interface.js +6 -2
- package/syncs/repositories/sync-repository-postgres.js +19 -13
- package/syncs/sync.js +104 -100
- package/token/repositories/token-repository-factory.js +4 -6
- package/types/assertions/index.d.ts +72 -72
- package/types/core/index.d.ts +98 -53
- package/types/errors/index.d.ts +61 -54
- package/types/eslint-config/index.d.ts +40 -40
- package/types/lambda/index.d.ts +26 -26
- package/types/logs/index.d.ts +4 -4
- package/types/prettier-config/index.d.ts +5 -5
- package/types/test-environment/index.d.ts +14 -14
- package/types/tsconfig.json +95 -93
- package/user/repositories/user-repository-factory.js +4 -4
- package/user/tests/doubles/test-user-repository.js +5 -2
- package/user/use-cases/authenticate-user.js +2 -4
- package/user/use-cases/authenticate-with-shared-secret.js +1 -1
- package/user/use-cases/create-individual-user.js +8 -7
- package/user/use-cases/create-organization-user.js +1 -1
- package/user/use-cases/create-token-for-user-id.js +1 -1
- package/user/use-cases/delete-user.js +47 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
- package/user/use-cases/get-user-from-bearer-token.js +7 -3
- package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
- package/user/use-cases/login-user.js +8 -8
- package/user/user.js +18 -4
- package/utils/backend-path.js +2 -2
- package/utils/index.js +4 -1
- package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
- package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
- package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
- package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
- package/websocket/repositories/websocket-connection-repository.js +26 -20
- package/websocket/websocket-message-sender-interface.js +38 -0
- package/database/adapters/lambda-invoker.js +0 -97
- package/database/repositories/migration-status-repository-s3.js +0 -137
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { createAppHandler } = require('./../app-handler-helpers');
|
|
4
|
+
const { requireAdmin } = require('./middleware/requireAdmin');
|
|
5
|
+
const catchAsyncError = require('express-async-handler');
|
|
6
|
+
const bcrypt = require('bcryptjs');
|
|
7
|
+
const {
|
|
8
|
+
createUserRepository,
|
|
9
|
+
} = require('../../user/repositories/user-repository-factory');
|
|
10
|
+
const { loadAppDefinition } = require('../app-definition-loader');
|
|
11
|
+
const {
|
|
12
|
+
createModuleRepository,
|
|
13
|
+
} = require('../../modules/repositories/module-repository-factory');
|
|
14
|
+
const {
|
|
15
|
+
GetModuleEntityById,
|
|
16
|
+
} = require('../../modules/use-cases/get-module-entity-by-id');
|
|
17
|
+
const {
|
|
18
|
+
UpdateModuleEntity,
|
|
19
|
+
} = require('../../modules/use-cases/update-module-entity');
|
|
20
|
+
const {
|
|
21
|
+
DeleteModuleEntity,
|
|
22
|
+
} = require('../../modules/use-cases/delete-module-entity');
|
|
23
|
+
const {
|
|
24
|
+
CreateTokenForUserId,
|
|
25
|
+
} = require('../../user/use-cases/create-token-for-user-id');
|
|
26
|
+
const { DeleteUser } = require('../../user/use-cases/delete-user');
|
|
27
|
+
|
|
28
|
+
// Initialize repositories and use cases
|
|
29
|
+
const { userConfig } = loadAppDefinition();
|
|
30
|
+
const userRepository = createUserRepository({ userConfig });
|
|
31
|
+
const moduleRepository = createModuleRepository();
|
|
32
|
+
|
|
33
|
+
// Use cases
|
|
34
|
+
const getModuleEntityById = new GetModuleEntityById({ moduleRepository });
|
|
35
|
+
const updateModuleEntity = new UpdateModuleEntity({ moduleRepository });
|
|
36
|
+
const deleteModuleEntity = new DeleteModuleEntity({ moduleRepository });
|
|
37
|
+
const createTokenForUserId = new CreateTokenForUserId({ userRepository });
|
|
38
|
+
const deleteUser = new DeleteUser({ userRepository });
|
|
39
|
+
|
|
40
|
+
// Debug logging
|
|
41
|
+
router.use((req, res, next) => {
|
|
42
|
+
console.log(
|
|
43
|
+
`[Admin Router] ${req.method} ${req.path} | Original URL: ${req.originalUrl}`
|
|
44
|
+
);
|
|
45
|
+
next();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Apply admin API key auth middleware to all admin routes
|
|
49
|
+
router.use(requireAdmin);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* USER MANAGEMENT ENDPOINTS
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* GET /api/admin/users
|
|
57
|
+
* List all users with pagination
|
|
58
|
+
*/
|
|
59
|
+
router.get(
|
|
60
|
+
'/api/admin/users',
|
|
61
|
+
catchAsyncError(async (req, res) => {
|
|
62
|
+
const {
|
|
63
|
+
page = 1,
|
|
64
|
+
limit = 50,
|
|
65
|
+
sortBy = 'createdAt',
|
|
66
|
+
sortOrder = 'desc',
|
|
67
|
+
} = req.query;
|
|
68
|
+
const skip = (parseInt(page) - 1) * parseInt(limit);
|
|
69
|
+
|
|
70
|
+
// Build sort object
|
|
71
|
+
const sort = {};
|
|
72
|
+
sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
|
|
73
|
+
|
|
74
|
+
// Use repository to get users
|
|
75
|
+
const users = await userRepository.findAllUsers({
|
|
76
|
+
skip,
|
|
77
|
+
limit: parseInt(limit),
|
|
78
|
+
sort,
|
|
79
|
+
excludeFields: ['-hashword'], // Exclude password hash
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const totalCount = await userRepository.countUsers();
|
|
83
|
+
|
|
84
|
+
res.json({
|
|
85
|
+
users,
|
|
86
|
+
pagination: {
|
|
87
|
+
page: parseInt(page),
|
|
88
|
+
limit: parseInt(limit),
|
|
89
|
+
total: totalCount,
|
|
90
|
+
pages: Math.ceil(totalCount / parseInt(limit)),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* GET /api/admin/users/search
|
|
98
|
+
* Search users by username or email
|
|
99
|
+
*/
|
|
100
|
+
router.get(
|
|
101
|
+
'/api/admin/users/search',
|
|
102
|
+
catchAsyncError(async (req, res) => {
|
|
103
|
+
const {
|
|
104
|
+
q,
|
|
105
|
+
page = 1,
|
|
106
|
+
limit = 50,
|
|
107
|
+
sortBy = 'createdAt',
|
|
108
|
+
sortOrder = 'desc',
|
|
109
|
+
} = req.query;
|
|
110
|
+
|
|
111
|
+
if (!q) {
|
|
112
|
+
return res.status(400).json({
|
|
113
|
+
status: 'error',
|
|
114
|
+
message: 'Search query parameter "q" is required',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const skip = (parseInt(page) - 1) * parseInt(limit);
|
|
119
|
+
|
|
120
|
+
// Build sort object
|
|
121
|
+
const sort = {};
|
|
122
|
+
sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
|
|
123
|
+
|
|
124
|
+
// Use repository to search users
|
|
125
|
+
const users = await userRepository.searchUsers({
|
|
126
|
+
query: q,
|
|
127
|
+
skip,
|
|
128
|
+
limit: parseInt(limit),
|
|
129
|
+
sort,
|
|
130
|
+
excludeFields: ['-hashword'],
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const totalCount = await userRepository.countUsersBySearchQuery(q);
|
|
134
|
+
|
|
135
|
+
res.json({
|
|
136
|
+
users,
|
|
137
|
+
pagination: {
|
|
138
|
+
page: parseInt(page),
|
|
139
|
+
limit: parseInt(limit),
|
|
140
|
+
total: totalCount,
|
|
141
|
+
pages: Math.ceil(totalCount / parseInt(limit)),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* POST /api/admin/users
|
|
149
|
+
* Create a new user (admin only)
|
|
150
|
+
* Admin-specific features:
|
|
151
|
+
* - Can create users with custom roles
|
|
152
|
+
* - Can set verified status
|
|
153
|
+
* - Can assign to organizations
|
|
154
|
+
* - No email verification required
|
|
155
|
+
*/
|
|
156
|
+
router.post(
|
|
157
|
+
'/api/admin/users',
|
|
158
|
+
catchAsyncError(async (req, res) => {
|
|
159
|
+
const {
|
|
160
|
+
username,
|
|
161
|
+
email,
|
|
162
|
+
password,
|
|
163
|
+
type = 'INDIVIDUAL',
|
|
164
|
+
appUserId,
|
|
165
|
+
organizationId,
|
|
166
|
+
verified = true, // Admins can create pre-verified users
|
|
167
|
+
} = req.body;
|
|
168
|
+
|
|
169
|
+
// Validate required fields
|
|
170
|
+
if (!username || !email || !password) {
|
|
171
|
+
return res.status(400).json({
|
|
172
|
+
status: 'error',
|
|
173
|
+
message: 'Username, email, and password are required',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check if user already exists
|
|
178
|
+
const existingUser = await userRepository.findIndividualUserByUsername(
|
|
179
|
+
username
|
|
180
|
+
);
|
|
181
|
+
if (existingUser) {
|
|
182
|
+
return res.status(409).json({
|
|
183
|
+
status: 'error',
|
|
184
|
+
message: 'User with this username already exists',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const existingEmail = await userRepository.findIndividualUserByEmail(
|
|
189
|
+
email
|
|
190
|
+
);
|
|
191
|
+
if (existingEmail) {
|
|
192
|
+
return res.status(409).json({
|
|
193
|
+
status: 'error',
|
|
194
|
+
message: 'User with this email already exists',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Hash password (using bcryptjs which is already imported)
|
|
199
|
+
const hashword = await bcrypt.hash(password, 10);
|
|
200
|
+
|
|
201
|
+
// Create user with admin-specified attributes
|
|
202
|
+
const userData = {
|
|
203
|
+
username,
|
|
204
|
+
email,
|
|
205
|
+
hashword,
|
|
206
|
+
type,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// Add optional fields if provided
|
|
210
|
+
if (appUserId) userData.appUserId = appUserId;
|
|
211
|
+
if (organizationId) userData.organizationId = organizationId;
|
|
212
|
+
|
|
213
|
+
const user = await userRepository.createIndividualUser(userData);
|
|
214
|
+
|
|
215
|
+
// Remove sensitive fields
|
|
216
|
+
const userObj = user.toObject ? user.toObject() : user;
|
|
217
|
+
delete userObj.hashword;
|
|
218
|
+
|
|
219
|
+
res.status(201).json({
|
|
220
|
+
user: userObj,
|
|
221
|
+
message: 'User created successfully by admin',
|
|
222
|
+
});
|
|
223
|
+
})
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* GET /api/admin/users/:userId
|
|
228
|
+
* Get a specific user by ID
|
|
229
|
+
*/
|
|
230
|
+
router.get(
|
|
231
|
+
'/api/admin/users/:userId',
|
|
232
|
+
catchAsyncError(async (req, res) => {
|
|
233
|
+
const { userId } = req.params;
|
|
234
|
+
|
|
235
|
+
const user = await userRepository.findUserById(userId);
|
|
236
|
+
|
|
237
|
+
if (!user) {
|
|
238
|
+
return res.status(404).json({
|
|
239
|
+
status: 'error',
|
|
240
|
+
message: 'User not found',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Remove sensitive fields
|
|
245
|
+
const userObj = user.toObject ? user.toObject() : user;
|
|
246
|
+
delete userObj.hashword;
|
|
247
|
+
|
|
248
|
+
res.json({ user: userObj });
|
|
249
|
+
})
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* POST /api/admin/users/:userId/impersonate
|
|
254
|
+
* Generate a token for a user without requiring password (admin impersonation)
|
|
255
|
+
* Allows admins to login as any user for support/testing purposes
|
|
256
|
+
*/
|
|
257
|
+
router.post(
|
|
258
|
+
'/api/admin/users/:userId/impersonate',
|
|
259
|
+
catchAsyncError(async (req, res) => {
|
|
260
|
+
const { userId } = req.params;
|
|
261
|
+
const { expiresInMinutes = 120 } = req.body;
|
|
262
|
+
|
|
263
|
+
// Find the user
|
|
264
|
+
const user = await userRepository.findUserById(userId);
|
|
265
|
+
|
|
266
|
+
if (!user) {
|
|
267
|
+
return res.status(404).json({
|
|
268
|
+
status: 'error',
|
|
269
|
+
message: 'User not found',
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Generate token without password verification
|
|
274
|
+
const token = await createTokenForUserId.execute(
|
|
275
|
+
userId,
|
|
276
|
+
expiresInMinutes
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
res.json({
|
|
280
|
+
token,
|
|
281
|
+
message: `Impersonating user: ${user.username || user.email}`,
|
|
282
|
+
expiresInMinutes,
|
|
283
|
+
});
|
|
284
|
+
})
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* DELETE /api/admin/users/:userId
|
|
289
|
+
* Delete a user by ID (admin only)
|
|
290
|
+
* IMPORTANT: This is a destructive operation - use with caution
|
|
291
|
+
*/
|
|
292
|
+
router.delete(
|
|
293
|
+
'/api/admin/users/:userId',
|
|
294
|
+
catchAsyncError(async (req, res) => {
|
|
295
|
+
const { userId } = req.params;
|
|
296
|
+
|
|
297
|
+
// Execute delete user use case
|
|
298
|
+
await deleteUser.execute(userId);
|
|
299
|
+
|
|
300
|
+
res.status(204).send();
|
|
301
|
+
})
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* GLOBAL ENTITY MANAGEMENT ENDPOINTS
|
|
306
|
+
*/
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* GET /api/admin/entities
|
|
310
|
+
* List all global entities
|
|
311
|
+
*/
|
|
312
|
+
router.get(
|
|
313
|
+
'/api/admin/entities',
|
|
314
|
+
catchAsyncError(async (req, res) => {
|
|
315
|
+
const { type, status } = req.query;
|
|
316
|
+
|
|
317
|
+
const query = { isGlobal: true };
|
|
318
|
+
if (type) query.type = type;
|
|
319
|
+
if (status) query.status = status;
|
|
320
|
+
|
|
321
|
+
const entities = await moduleRepository.findEntitiesBy(query);
|
|
322
|
+
|
|
323
|
+
res.json({ entities });
|
|
324
|
+
})
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* GET /api/admin/entities/:entityId
|
|
329
|
+
* Get a specific global entity
|
|
330
|
+
*/
|
|
331
|
+
router.get(
|
|
332
|
+
'/api/admin/entities/:entityId',
|
|
333
|
+
catchAsyncError(async (req, res) => {
|
|
334
|
+
const { entityId } = req.params;
|
|
335
|
+
|
|
336
|
+
const entity = await getModuleEntityById.execute(entityId);
|
|
337
|
+
|
|
338
|
+
if (!entity || !entity.isGlobal) {
|
|
339
|
+
return res.status(404).json({
|
|
340
|
+
status: 'error',
|
|
341
|
+
message: 'Global entity not found',
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
res.json({ entity });
|
|
346
|
+
})
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* POST /api/admin/entities
|
|
351
|
+
* Create a new global entity
|
|
352
|
+
*/
|
|
353
|
+
router.post(
|
|
354
|
+
'/api/admin/entities',
|
|
355
|
+
catchAsyncError(async (req, res) => {
|
|
356
|
+
const { type, ...entityData } = req.body;
|
|
357
|
+
|
|
358
|
+
if (!type) {
|
|
359
|
+
return res.status(400).json({
|
|
360
|
+
status: 'error',
|
|
361
|
+
message: 'Entity type is required',
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Create entity with isGlobal flag
|
|
366
|
+
const entity = await moduleRepository.createEntity({
|
|
367
|
+
...entityData,
|
|
368
|
+
type,
|
|
369
|
+
isGlobal: true,
|
|
370
|
+
status: 'connected',
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
res.status(201).json({ entity });
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* PUT /api/admin/entities/:entityId
|
|
379
|
+
* Update a global entity
|
|
380
|
+
*/
|
|
381
|
+
router.put(
|
|
382
|
+
'/api/admin/entities/:entityId',
|
|
383
|
+
catchAsyncError(async (req, res) => {
|
|
384
|
+
const { entityId } = req.params;
|
|
385
|
+
|
|
386
|
+
const entity = await updateModuleEntity.execute(entityId, req.body);
|
|
387
|
+
|
|
388
|
+
if (!entity) {
|
|
389
|
+
return res.status(404).json({
|
|
390
|
+
status: 'error',
|
|
391
|
+
message: 'Global entity not found',
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
res.json({ entity });
|
|
396
|
+
})
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* DELETE /api/admin/entities/:entityId
|
|
401
|
+
* Delete a global entity
|
|
402
|
+
*/
|
|
403
|
+
router.delete(
|
|
404
|
+
'/api/admin/entities/:entityId',
|
|
405
|
+
catchAsyncError(async (req, res) => {
|
|
406
|
+
const { entityId } = req.params;
|
|
407
|
+
|
|
408
|
+
await deleteModuleEntity.execute(entityId);
|
|
409
|
+
|
|
410
|
+
res.status(204).send();
|
|
411
|
+
})
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* POST /api/admin/entities/:entityId/test
|
|
416
|
+
* Test connection for a global entity
|
|
417
|
+
*/
|
|
418
|
+
router.post(
|
|
419
|
+
'/api/admin/entities/:entityId/test',
|
|
420
|
+
catchAsyncError(async (req, res) => {
|
|
421
|
+
const { entityId } = req.params;
|
|
422
|
+
|
|
423
|
+
const entity = await getModuleEntityById.execute(entityId);
|
|
424
|
+
|
|
425
|
+
if (!entity || !entity.isGlobal) {
|
|
426
|
+
return res.status(404).json({
|
|
427
|
+
status: 'error',
|
|
428
|
+
message: 'Global entity not found',
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Test the entity connection
|
|
433
|
+
try {
|
|
434
|
+
// This would use a TestModuleAuth use case
|
|
435
|
+
res.json({
|
|
436
|
+
status: 'success',
|
|
437
|
+
message: 'Entity connection test successful',
|
|
438
|
+
});
|
|
439
|
+
} catch (error) {
|
|
440
|
+
res.status(500).json({
|
|
441
|
+
status: 'error',
|
|
442
|
+
message: `Entity connection test failed: ${error.message}`,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
})
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const handler = createAppHandler('HTTP Event: Admin', router);
|
|
449
|
+
|
|
450
|
+
module.exports = { handler, router };
|
package/handlers/routers/auth.js
CHANGED
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
const { createIntegrationRouter } = require('@friggframework/core');
|
|
2
2
|
const { createAppHandler } = require('./../app-handler-helpers');
|
|
3
|
+
const { requireLoggedInUser } = require('./middleware/requireLoggedInUser');
|
|
4
|
+
const { loadAppDefinition } = require('../app-definition-loader');
|
|
3
5
|
|
|
4
6
|
const router = createIntegrationRouter();
|
|
5
7
|
|
|
6
8
|
router.route('/api/integrations/redirect/:appId').get((req, res) => {
|
|
7
9
|
res.redirect(
|
|
8
|
-
`${process.env.FRONTEND_URI}/redirect/${
|
|
10
|
+
`${process.env.FRONTEND_URI}/redirect/${
|
|
11
|
+
req.params.appId
|
|
9
12
|
}?${new URLSearchParams(req.query)}`
|
|
10
13
|
);
|
|
11
14
|
});
|
|
12
15
|
|
|
16
|
+
// Integration settings endpoint
|
|
17
|
+
router
|
|
18
|
+
.route('/config/integration-settings')
|
|
19
|
+
.get(requireLoggedInUser, (req, res) => {
|
|
20
|
+
const appDefinition = loadAppDefinition();
|
|
21
|
+
|
|
22
|
+
const settings = {
|
|
23
|
+
autoProvisioningEnabled:
|
|
24
|
+
appDefinition.integration?.autoProvisioningEnabled ?? true,
|
|
25
|
+
credentialReuseStrategy:
|
|
26
|
+
appDefinition.integration?.credentialReuseStrategy ?? 'shared',
|
|
27
|
+
allowUserManagedEntities:
|
|
28
|
+
appDefinition.integration?.allowUserManagedEntities ?? true,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
res.json(settings);
|
|
32
|
+
});
|
|
33
|
+
|
|
13
34
|
const handler = createAppHandler('HTTP Event: Auth', router);
|
|
14
35
|
|
|
15
|
-
module.exports = { handler };
|
|
36
|
+
module.exports = { handler, router };
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Minimal Lambda wrapper that avoids loading core/index.js
|
|
5
5
|
* (which would try to load user/** modules excluded from migration packages)
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* This handler is intentionally simpler than health.handler.js to avoid dependencies.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const serverlessHttp = require('serverless-http');
|
|
11
11
|
const express = require('express');
|
|
12
12
|
const cors = require('cors');
|
|
13
|
-
const dbMigrationRouter = require('./db-migration');
|
|
13
|
+
const { router: dbMigrationRouter } = require('./db-migration');
|
|
14
14
|
|
|
15
15
|
// Create minimal Express app
|
|
16
16
|
const app = express();
|
|
@@ -26,4 +26,3 @@ app.use((err, req, res, next) => {
|
|
|
26
26
|
|
|
27
27
|
// Export as .handler property (Lambda config: db-migration.handler)
|
|
28
28
|
module.exports.handler = serverlessHttp(app);
|
|
29
|
-
|