@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.
Files changed (235) hide show
  1. package/CHANGELOG.md +66 -67
  2. package/CLAUDE.md +148 -124
  3. package/README.md +165 -130
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
  8. package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
  9. package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
  10. package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
  12. package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
  13. package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
  14. package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
  15. package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
  16. package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
  17. package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
  18. package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
  19. package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
  20. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  21. package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
  22. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  23. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  24. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  25. package/application/commands/admin-script-commands.js +348 -0
  26. package/application/commands/integration-commands.js +12 -6
  27. package/application/index.js +5 -3
  28. package/assertions/get.js +17 -7
  29. package/associations/association.js +59 -57
  30. package/core/CLAUDE.md +222 -156
  31. package/core/Worker.js +33 -12
  32. package/credential/credential-router.js +262 -0
  33. package/credential/repositories/credential-repository-documentdb.js +71 -1
  34. package/credential/repositories/credential-repository-factory.js +4 -6
  35. package/credential/repositories/credential-repository-interface.js +8 -2
  36. package/credential/repositories/credential-repository-mongo.js +45 -4
  37. package/credential/repositories/credential-repository-postgres.js +44 -3
  38. package/credential/repositories/credential-repository.js +3 -1
  39. package/credential/use-cases/delete-credential-for-user.js +50 -0
  40. package/credential/use-cases/get-credential-for-user.js +21 -3
  41. package/credential/use-cases/list-credentials-for-user.js +31 -0
  42. package/credential/use-cases/reauthorize-credential.js +103 -0
  43. package/credential/use-cases/update-authentication-status.js +5 -2
  44. package/database/MONGODB_TRANSACTION_FIX.md +72 -52
  45. package/database/config.js +13 -59
  46. package/database/documentdb-encryption-service.js +29 -13
  47. package/database/documentdb-utils.js +21 -12
  48. package/database/encryption/README.md +53 -36
  49. package/database/encryption/encryption-schema-registry.js +29 -23
  50. package/database/encryption/field-encryption-service.js +15 -4
  51. package/database/encryption/logger.js +3 -2
  52. package/database/encryption/prisma-encryption-extension.js +5 -8
  53. package/database/prisma.js +22 -14
  54. package/database/repositories/health-check-repository-factory.js +7 -5
  55. package/database/repositories/health-check-repository-interface.js +15 -5
  56. package/database/repositories/health-check-repository-postgres.js +1 -1
  57. package/database/use-cases/check-database-health-use-case.js +3 -2
  58. package/database/use-cases/check-database-state-use-case.js +8 -7
  59. package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
  60. package/database/use-cases/get-migration-status-use-case.js +4 -2
  61. package/database/use-cases/run-database-migration-use-case.js +41 -9
  62. package/database/use-cases/test-encryption-use-case.js +4 -5
  63. package/database/use-cases/trigger-database-migration-use-case.js +9 -8
  64. package/database/utils/mongodb-collection-utils.js +11 -3
  65. package/database/utils/mongodb-schema-init.js +10 -4
  66. package/database/utils/prisma-runner.js +144 -74
  67. package/database/utils/prisma-schema-parser.js +2 -2
  68. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
  69. package/encrypt/Cryptor.js +54 -49
  70. package/encrypt/aes-encryption-key-provider.js +82 -0
  71. package/encrypt/encryption-key-provider-interface.js +50 -0
  72. package/errors/fetch-error.js +1 -1
  73. package/generated/prisma-mongodb/edge.js +59 -4
  74. package/generated/prisma-mongodb/index-browser.js +49 -1
  75. package/generated/prisma-mongodb/index.d.ts +4023 -61
  76. package/generated/prisma-mongodb/index.js +59 -4
  77. package/generated/prisma-mongodb/package.json +1 -1
  78. package/generated/prisma-mongodb/schema.prisma +87 -0
  79. package/generated/prisma-mongodb/wasm.js +59 -4
  80. package/generated/prisma-postgresql/edge.js +58 -4
  81. package/generated/prisma-postgresql/index-browser.js +48 -1
  82. package/generated/prisma-postgresql/index.d.ts +4738 -54
  83. package/generated/prisma-postgresql/index.js +58 -4
  84. package/generated/prisma-postgresql/package.json +1 -1
  85. package/generated/prisma-postgresql/schema.prisma +85 -0
  86. package/generated/prisma-postgresql/wasm.js +58 -4
  87. package/handlers/WEBHOOKS.md +104 -81
  88. package/handlers/app-definition-loader.js +53 -10
  89. package/handlers/app-handler-helpers.js +14 -3
  90. package/handlers/backend-utils.js +0 -1
  91. package/handlers/database-migration-handler.js +64 -45
  92. package/handlers/middleware/admin-auth.js +53 -0
  93. package/handlers/routers/HEALTHCHECK.md +170 -135
  94. package/handlers/routers/admin.js +450 -0
  95. package/handlers/routers/auth.js +23 -2
  96. package/handlers/routers/db-migration.handler.js +2 -3
  97. package/handlers/routers/db-migration.js +102 -66
  98. package/handlers/routers/docs.js +180 -0
  99. package/handlers/routers/health.js +39 -249
  100. package/handlers/routers/integration-defined-routers.js +4 -4
  101. package/handlers/routers/integration-webhook-routers.js +17 -7
  102. package/handlers/routers/middleware/loadUser.js +39 -0
  103. package/handlers/routers/middleware/requireAdmin.js +43 -0
  104. package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
  105. package/handlers/routers/websocket.js +9 -3
  106. package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
  107. package/handlers/workers/db-migration.js +74 -35
  108. package/index.js +50 -16
  109. package/infrastructure/scheduler/index.js +15 -11
  110. package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
  111. package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
  112. package/integrations/WEBHOOK-QUICKSTART.md +21 -14
  113. package/integrations/index.js +4 -0
  114. package/integrations/integration-base.js +9 -6
  115. package/integrations/integration-router.js +1828 -157
  116. package/integrations/options.js +47 -20
  117. package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
  118. package/integrations/repositories/integration-repository-documentdb.js +53 -24
  119. package/integrations/repositories/integration-repository-factory.js +6 -6
  120. package/integrations/repositories/integration-repository-interface.js +27 -9
  121. package/integrations/repositories/integration-repository-postgres.js +3 -3
  122. package/integrations/repositories/process-repository-factory.js +4 -7
  123. package/integrations/repositories/process-repository-interface.js +9 -5
  124. package/integrations/repositories/process-repository-mongo.js +7 -3
  125. package/integrations/repositories/process-repository-postgres.js +2 -2
  126. package/integrations/tests/doubles/config-capturing-integration.js +9 -8
  127. package/integrations/tests/doubles/dummy-integration-class.js +81 -8
  128. package/integrations/tests/doubles/test-integration-repository.js +51 -11
  129. package/integrations/use-cases/create-integration.js +36 -32
  130. package/integrations/use-cases/create-process.js +20 -9
  131. package/integrations/use-cases/delete-integration-for-user.js +5 -4
  132. package/integrations/use-cases/execute-proxy-request.js +564 -0
  133. package/integrations/use-cases/get-integration-for-user.js +25 -10
  134. package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
  135. package/integrations/use-cases/get-integrations-for-user.js +4 -1
  136. package/integrations/use-cases/get-possible-integrations.js +1 -1
  137. package/integrations/use-cases/get-process.js +5 -6
  138. package/integrations/use-cases/index.js +3 -1
  139. package/integrations/use-cases/load-integration-context.js +1 -5
  140. package/integrations/use-cases/update-process-state.js +10 -8
  141. package/integrations/utils/map-integration-dto.js +9 -3
  142. package/jest-setup.js +1 -1
  143. package/jest-teardown.js +1 -1
  144. package/lambda/TimeoutCatcher.js +32 -30
  145. package/lambda/index.js +1 -1
  146. package/logs/index.js +3 -3
  147. package/modules/domain/entities/AuthorizationSession.js +131 -0
  148. package/modules/domain/entities/index.js +10 -0
  149. package/modules/module.js +4 -9
  150. package/modules/repositories/authorization-session-repository-factory.js +53 -0
  151. package/modules/repositories/authorization-session-repository-interface.js +75 -0
  152. package/modules/repositories/authorization-session-repository-mongo.js +169 -0
  153. package/modules/repositories/authorization-session-repository-postgres.js +170 -0
  154. package/modules/repositories/module-repository-documentdb.js +89 -41
  155. package/modules/repositories/module-repository-factory.js +4 -6
  156. package/modules/repositories/module-repository-interface.js +13 -0
  157. package/modules/repositories/module-repository-mongo.js +40 -5
  158. package/modules/repositories/module-repository-postgres.js +42 -5
  159. package/modules/repositories/module-repository.js +28 -0
  160. package/modules/requester/api-key.js +0 -2
  161. package/modules/requester/basic.js +0 -1
  162. package/modules/requester/oauth-2.js +3 -14
  163. package/modules/test/mock-api/definition.js +15 -10
  164. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  165. package/modules/tests/doubles/test-module-factory.js +10 -4
  166. package/modules/tests/doubles/test-module-repository.js +1 -1
  167. package/modules/use-cases/delete-module-entity.js +23 -0
  168. package/modules/use-cases/get-authorization-requirements.js +110 -0
  169. package/modules/use-cases/get-entities-for-user.js +1 -1
  170. package/modules/use-cases/get-entity-options-by-id.js +8 -6
  171. package/modules/use-cases/get-module-entity-by-id.js +16 -0
  172. package/modules/use-cases/get-module.js +12 -9
  173. package/modules/use-cases/process-authorization-callback.js +12 -6
  174. package/modules/use-cases/process-authorization-step.js +160 -0
  175. package/modules/use-cases/refresh-entity-options.js +8 -6
  176. package/modules/use-cases/start-authorization-session.js +86 -0
  177. package/modules/use-cases/test-module-auth.js +8 -6
  178. package/modules/use-cases/update-module-entity.js +27 -0
  179. package/modules/utils/map-module-dto.js +2 -2
  180. package/openapi/openapi-spec-generator.js +370 -0
  181. package/openapi/openapi-v1.yaml +1360 -0
  182. package/openapi/openapi-v2.yaml +1535 -0
  183. package/package.json +7 -9
  184. package/prisma-mongodb/schema.prisma +87 -0
  185. package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
  186. package/prisma-postgresql/schema.prisma +85 -0
  187. package/providers/index.js +13 -0
  188. package/providers/resolve-provider.js +93 -0
  189. package/queues/index.js +19 -0
  190. package/queues/providers/index.js +7 -0
  191. package/queues/providers/netlify-background-provider.js +76 -0
  192. package/queues/providers/qstash-queue-provider.js +122 -0
  193. package/queues/queue-client-interface.js +55 -0
  194. package/queues/queue-provider-factory.js +93 -0
  195. package/queues/queue-provider.js +46 -0
  196. package/queues/queuer-util.js +33 -25
  197. package/syncs/repositories/sync-repository-documentdb.js +39 -19
  198. package/syncs/repositories/sync-repository-factory.js +4 -4
  199. package/syncs/repositories/sync-repository-interface.js +6 -2
  200. package/syncs/repositories/sync-repository-postgres.js +19 -13
  201. package/syncs/sync.js +104 -100
  202. package/token/repositories/token-repository-factory.js +4 -6
  203. package/types/assertions/index.d.ts +72 -72
  204. package/types/core/index.d.ts +98 -53
  205. package/types/errors/index.d.ts +61 -54
  206. package/types/eslint-config/index.d.ts +40 -40
  207. package/types/lambda/index.d.ts +26 -26
  208. package/types/logs/index.d.ts +4 -4
  209. package/types/prettier-config/index.d.ts +5 -5
  210. package/types/test-environment/index.d.ts +14 -14
  211. package/types/tsconfig.json +95 -93
  212. package/user/repositories/user-repository-factory.js +4 -4
  213. package/user/tests/doubles/test-user-repository.js +5 -2
  214. package/user/use-cases/authenticate-user.js +2 -4
  215. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  216. package/user/use-cases/create-individual-user.js +8 -7
  217. package/user/use-cases/create-organization-user.js +1 -1
  218. package/user/use-cases/create-token-for-user-id.js +1 -1
  219. package/user/use-cases/delete-user.js +47 -0
  220. package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
  221. package/user/use-cases/get-user-from-bearer-token.js +7 -3
  222. package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
  223. package/user/use-cases/login-user.js +8 -8
  224. package/user/user.js +18 -4
  225. package/utils/backend-path.js +2 -2
  226. package/utils/index.js +4 -1
  227. package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
  228. package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
  229. package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
  230. package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
  231. package/websocket/repositories/websocket-connection-repository.js +26 -20
  232. package/websocket/websocket-message-sender-interface.js +38 -0
  233. package/database/adapters/lambda-invoker.js +0 -97
  234. package/database/repositories/migration-status-repository-s3.js +0 -137
  235. 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 };
@@ -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/${req.params.appId
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
-