@friggframework/core 2.0.0--canary.522.cbd3d5a.0 → 2.0.0--canary.517.35ee143.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 (225) hide show
  1. package/CHANGELOG.md +67 -66
  2. package/CLAUDE.md +124 -148
  3. package/README.md +130 -165
  4. package/admin-scripts/index.js +16 -30
  5. package/admin-scripts/repositories/{admin-api-key-repository-documentdb.js → admin-process-repository-documentdb.js} +5 -5
  6. package/admin-scripts/repositories/{admin-api-key-repository-factory.js → admin-process-repository-factory.js} +15 -19
  7. package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
  8. package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
  9. package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
  10. package/admin-scripts/repositories/script-schedule-repository-factory.js +2 -6
  11. package/admin-scripts/repositories/script-schedule-repository-interface.js +14 -36
  12. package/admin-scripts/repositories/script-schedule-repository-mongo.js +13 -25
  13. package/admin-scripts/repositories/script-schedule-repository-postgres.js +13 -25
  14. package/application/commands/admin-script-commands.js +102 -266
  15. package/application/commands/integration-commands.js +6 -12
  16. package/application/index.js +3 -1
  17. package/assertions/get.js +4 -1
  18. package/associations/association.js +57 -59
  19. package/associations/model.js +41 -41
  20. package/core/CLAUDE.md +156 -222
  21. package/core/Worker.js +1 -5
  22. package/credential/repositories/credential-repository-documentdb.js +1 -71
  23. package/credential/repositories/credential-repository-interface.js +2 -8
  24. package/credential/repositories/credential-repository-mongo.js +4 -45
  25. package/credential/repositories/credential-repository-postgres.js +3 -44
  26. package/credential/repositories/credential-repository.js +4 -6
  27. package/credential/use-cases/get-credential-for-user.js +3 -21
  28. package/credential/use-cases/update-authentication-status.js +2 -5
  29. package/database/MONGODB_TRANSACTION_FIX.md +52 -72
  30. package/database/adapters/lambda-invoker.js +7 -8
  31. package/database/config.js +13 -15
  32. package/database/documentdb-encryption-service.js +13 -29
  33. package/database/documentdb-utils.js +12 -21
  34. package/database/encryption/README.md +36 -53
  35. package/database/encryption/encryption-schema-registry.js +9 -21
  36. package/database/encryption/field-encryption-service.js +4 -15
  37. package/database/encryption/logger.js +2 -3
  38. package/database/encryption/prisma-encryption-extension.js +8 -5
  39. package/database/index.js +2 -4
  40. package/database/models/IndividualUser.js +15 -15
  41. package/database/models/OrganizationUser.js +4 -6
  42. package/database/models/UserModel.js +2 -2
  43. package/database/models/WebsocketConnection.js +1 -4
  44. package/database/mongoose.js +2 -2
  45. package/database/prisma.js +14 -22
  46. package/database/repositories/health-check-repository-documentdb.js +7 -22
  47. package/database/repositories/health-check-repository-factory.js +3 -9
  48. package/database/repositories/health-check-repository-interface.js +5 -15
  49. package/database/repositories/health-check-repository-mongodb.js +6 -9
  50. package/database/repositories/health-check-repository-postgres.js +1 -1
  51. package/database/repositories/migration-status-repository-s3.js +5 -10
  52. package/database/use-cases/check-database-health-use-case.js +2 -3
  53. package/database/use-cases/check-database-state-use-case.js +7 -9
  54. package/database/use-cases/get-database-state-via-worker-use-case.js +12 -15
  55. package/database/use-cases/get-migration-status-use-case.js +2 -4
  56. package/database/use-cases/run-database-migration-use-case.js +9 -41
  57. package/database/use-cases/test-encryption-use-case.js +5 -4
  58. package/database/use-cases/trigger-database-migration-use-case.js +8 -9
  59. package/database/utils/mongodb-collection-utils.js +3 -11
  60. package/database/utils/mongodb-schema-init.js +4 -10
  61. package/database/utils/prisma-runner.js +74 -144
  62. package/database/utils/prisma-schema-parser.js +2 -2
  63. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +80 -135
  64. package/encrypt/Cryptor.js +2 -8
  65. package/errors/fetch-error.js +1 -1
  66. package/generated/prisma-mongodb/edge.js +16 -59
  67. package/generated/prisma-mongodb/index-browser.js +12 -62
  68. package/generated/prisma-mongodb/index.d.ts +4850 -8136
  69. package/generated/prisma-mongodb/index.js +16 -59
  70. package/generated/prisma-mongodb/package.json +1 -1
  71. package/generated/prisma-mongodb/schema.prisma +33 -75
  72. package/generated/prisma-mongodb/wasm.js +16 -59
  73. package/generated/prisma-postgresql/edge.js +15 -59
  74. package/generated/prisma-postgresql/index-browser.js +11 -62
  75. package/generated/prisma-postgresql/index.d.ts +5963 -8930
  76. package/generated/prisma-postgresql/index.js +15 -59
  77. package/generated/prisma-postgresql/package.json +1 -1
  78. package/generated/prisma-postgresql/schema.prisma +33 -73
  79. package/generated/prisma-postgresql/wasm.js +15 -59
  80. package/handlers/WEBHOOKS.md +81 -104
  81. package/handlers/app-definition-loader.js +1 -1
  82. package/handlers/app-handler-helpers.js +2 -14
  83. package/handlers/backend-utils.js +1 -0
  84. package/handlers/database-migration-handler.js +45 -64
  85. package/handlers/middleware/admin-auth.js +53 -0
  86. package/handlers/routers/HEALTHCHECK.md +135 -170
  87. package/handlers/routers/auth.js +2 -23
  88. package/handlers/routers/db-migration.handler.js +3 -2
  89. package/handlers/routers/db-migration.js +33 -69
  90. package/handlers/routers/health.js +8 -23
  91. package/handlers/routers/integration-defined-routers.js +4 -4
  92. package/handlers/routers/integration-webhook-routers.js +7 -17
  93. package/handlers/routers/websocket.js +3 -9
  94. package/handlers/use-cases/check-integrations-health-use-case.js +6 -13
  95. package/handlers/workers/db-migration.js +26 -54
  96. package/index.js +9 -5
  97. package/integrations/WEBHOOK-QUICKSTART.md +14 -21
  98. package/integrations/index.js +0 -4
  99. package/integrations/integration-base.js +6 -9
  100. package/integrations/integration-router.js +146 -1817
  101. package/integrations/options.js +20 -47
  102. package/integrations/repositories/integration-repository-documentdb.js +24 -53
  103. package/integrations/repositories/integration-repository-factory.js +2 -6
  104. package/integrations/repositories/integration-repository-interface.js +9 -27
  105. package/integrations/repositories/integration-repository-postgres.js +3 -3
  106. package/integrations/repositories/process-repository-factory.js +1 -0
  107. package/integrations/repositories/process-repository-interface.js +5 -9
  108. package/integrations/repositories/process-repository-mongo.js +3 -7
  109. package/integrations/repositories/process-repository-postgres.js +2 -2
  110. package/integrations/tests/doubles/config-capturing-integration.js +8 -9
  111. package/integrations/tests/doubles/dummy-integration-class.js +8 -81
  112. package/integrations/tests/doubles/test-integration-repository.js +11 -51
  113. package/integrations/use-cases/create-integration.js +32 -36
  114. package/integrations/use-cases/create-process.js +9 -20
  115. package/integrations/use-cases/delete-integration-for-user.js +4 -5
  116. package/integrations/use-cases/get-integration-for-user.js +10 -25
  117. package/integrations/use-cases/get-integration-instance-by-definition.js +7 -13
  118. package/integrations/use-cases/get-integrations-for-user.js +1 -4
  119. package/integrations/use-cases/get-possible-integrations.js +1 -1
  120. package/integrations/use-cases/get-process.js +6 -5
  121. package/integrations/use-cases/index.js +1 -3
  122. package/integrations/use-cases/load-integration-context.js +5 -1
  123. package/integrations/use-cases/update-process-state.js +8 -10
  124. package/integrations/utils/map-integration-dto.js +3 -9
  125. package/jest-setup.js +1 -1
  126. package/jest-teardown.js +1 -1
  127. package/lambda/TimeoutCatcher.js +30 -32
  128. package/lambda/index.js +1 -1
  129. package/logs/index.js +3 -3
  130. package/modules/module.js +1 -3
  131. package/modules/repositories/module-repository-documentdb.js +48 -114
  132. package/modules/repositories/module-repository-interface.js +0 -13
  133. package/modules/repositories/module-repository-mongo.js +6 -46
  134. package/modules/repositories/module-repository-postgres.js +6 -48
  135. package/modules/repositories/module-repository.js +1 -34
  136. package/modules/requester/api-key.js +2 -0
  137. package/modules/requester/basic.js +1 -0
  138. package/modules/requester/oauth-2.js +2 -3
  139. package/modules/test/mock-api/definition.js +10 -15
  140. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  141. package/modules/tests/doubles/test-module-factory.js +4 -10
  142. package/modules/tests/doubles/test-module-repository.js +1 -1
  143. package/modules/use-cases/get-entities-for-user.js +1 -1
  144. package/modules/use-cases/get-entity-options-by-id.js +6 -8
  145. package/modules/use-cases/get-module.js +9 -12
  146. package/modules/use-cases/process-authorization-callback.js +6 -12
  147. package/modules/use-cases/refresh-entity-options.js +6 -8
  148. package/modules/use-cases/test-module-auth.js +6 -8
  149. package/modules/utils/map-module-dto.js +2 -2
  150. package/package.json +5 -7
  151. package/prisma-mongodb/schema.prisma +33 -75
  152. package/prisma-postgresql/schema.prisma +33 -73
  153. package/queues/queuer-util.js +1 -5
  154. package/syncs/model.js +51 -51
  155. package/syncs/repositories/sync-repository-documentdb.js +19 -39
  156. package/syncs/repositories/sync-repository-interface.js +2 -6
  157. package/syncs/repositories/sync-repository-postgres.js +13 -19
  158. package/syncs/sync.js +100 -104
  159. package/token/repositories/token-repository-factory.js +3 -1
  160. package/types/assertions/index.d.ts +72 -72
  161. package/types/associations/index.d.ts +57 -57
  162. package/types/core/index.d.ts +53 -59
  163. package/types/database/index.d.ts +2 -2
  164. package/types/encrypt/index.d.ts +3 -3
  165. package/types/errors/index.d.ts +54 -61
  166. package/types/eslint-config/index.d.ts +40 -40
  167. package/types/integrations/index.d.ts +183 -185
  168. package/types/lambda/index.d.ts +26 -26
  169. package/types/logs/index.d.ts +4 -4
  170. package/types/module-plugin/index.d.ts +237 -239
  171. package/types/prettier-config/index.d.ts +5 -5
  172. package/types/syncs/index.d.ts +108 -102
  173. package/types/test-environment/index.d.ts +14 -14
  174. package/types/tsconfig.json +93 -95
  175. package/user/tests/doubles/test-user-repository.js +2 -5
  176. package/user/use-cases/authenticate-user.js +4 -2
  177. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  178. package/user/use-cases/create-individual-user.js +7 -8
  179. package/user/use-cases/create-organization-user.js +1 -1
  180. package/user/use-cases/create-token-for-user-id.js +1 -1
  181. package/user/use-cases/get-user-from-adopter-jwt.js +2 -0
  182. package/user/use-cases/get-user-from-bearer-token.js +3 -7
  183. package/user/use-cases/get-user-from-x-frigg-headers.js +11 -14
  184. package/user/use-cases/login-user.js +8 -8
  185. package/user/user.js +4 -18
  186. package/utils/backend-path.js +2 -2
  187. package/utils/index.js +1 -4
  188. package/websocket/repositories/websocket-connection-repository-documentdb.js +8 -21
  189. package/websocket/repositories/websocket-connection-repository-mongo.js +4 -8
  190. package/websocket/repositories/websocket-connection-repository-postgres.js +4 -8
  191. package/websocket/repositories/websocket-connection-repository.js +4 -8
  192. package/admin-scripts/repositories/admin-api-key-repository-interface.js +0 -121
  193. package/admin-scripts/repositories/admin-api-key-repository-mongo.js +0 -155
  194. package/admin-scripts/repositories/admin-api-key-repository-postgres.js +0 -189
  195. package/admin-scripts/repositories/script-execution-repository-documentdb.js +0 -21
  196. package/admin-scripts/repositories/script-execution-repository-factory.js +0 -55
  197. package/admin-scripts/repositories/script-execution-repository-interface.js +0 -193
  198. package/admin-scripts/repositories/script-execution-repository-mongo.js +0 -278
  199. package/admin-scripts/repositories/script-execution-repository-postgres.js +0 -320
  200. package/credential/credential-router.js +0 -262
  201. package/credential/use-cases/delete-credential-for-user.js +0 -50
  202. package/credential/use-cases/list-credentials-for-user.js +0 -31
  203. package/credential/use-cases/reauthorize-credential.js +0 -103
  204. package/handlers/routers/admin.js +0 -450
  205. package/handlers/routers/docs.js +0 -180
  206. package/handlers/routers/middleware/loadUser.js +0 -39
  207. package/handlers/routers/middleware/requireAdmin.js +0 -41
  208. package/handlers/routers/middleware/requireLoggedInUser.js +0 -19
  209. package/integrations/use-cases/execute-proxy-request.js +0 -564
  210. package/modules/domain/entities/AuthorizationSession.js +0 -131
  211. package/modules/domain/entities/index.js +0 -10
  212. package/modules/repositories/authorization-session-repository-factory.js +0 -55
  213. package/modules/repositories/authorization-session-repository-interface.js +0 -75
  214. package/modules/repositories/authorization-session-repository-mongo.js +0 -169
  215. package/modules/repositories/authorization-session-repository-postgres.js +0 -170
  216. package/modules/use-cases/delete-module-entity.js +0 -23
  217. package/modules/use-cases/get-authorization-requirements.js +0 -110
  218. package/modules/use-cases/get-module-entity-by-id.js +0 -16
  219. package/modules/use-cases/process-authorization-step.js +0 -160
  220. package/modules/use-cases/start-authorization-session.js +0 -86
  221. package/modules/use-cases/update-module-entity.js +0 -27
  222. package/openapi/openapi-spec-generator.js +0 -370
  223. package/openapi/openapi-v1.yaml +0 -1360
  224. package/openapi/openapi-v2.yaml +0 -1535
  225. package/user/use-cases/delete-user.js +0 -47
@@ -1,450 +0,0 @@
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,180 +0,0 @@
1
- /**
2
- * API Documentation Router
3
- *
4
- * Serves dynamic OpenAPI specs and Scalar UI documentation for both v1 and v2 APIs.
5
- * Specs are generated dynamically based on appDefinition and installed modules.
6
- *
7
- * Endpoints:
8
- * - GET /api/docs - Main documentation UI with version selector
9
- * - GET /api/v1/docs - v1-specific documentation
10
- * - GET /api/v2/docs - v2-specific documentation
11
- * - GET /api/openapi.json - v2 spec (default/current)
12
- * - GET /api/openapi-v1.json - v1 spec
13
- * - GET /api/openapi-v2.json - v2 spec
14
- */
15
-
16
- const { Router } = require('express');
17
- const { createAppHandler } = require('./../app-handler-helpers');
18
- const {
19
- generateOpenApiSpec,
20
- generateOpenApiSpecV1,
21
- generateOpenApiSpecV2,
22
- } = require('../../openapi/openapi-spec-generator');
23
-
24
- const router = Router();
25
-
26
- let cachedAppDefinition = null;
27
-
28
- /**
29
- * Load the appDefinition for spec generation
30
- * Lazy-loads and caches to avoid performance overhead
31
- */
32
- function loadAppDefinitionForDocs() {
33
- if (cachedAppDefinition) return cachedAppDefinition;
34
-
35
- try {
36
- const { loadAppDefinition } = require('../app-definition-loader');
37
- const { integrations } = loadAppDefinition();
38
- cachedAppDefinition = { integrations };
39
- return cachedAppDefinition;
40
- } catch (error) {
41
- // App definition not available (e.g., in test environment)
42
- return null;
43
- }
44
- }
45
-
46
- /**
47
- * Generate Scalar HTML with version selector
48
- * @param {Object} options - Configuration options
49
- * @param {string} options.specUrl - Primary spec URL
50
- * @param {Array} options.sources - Array of spec sources for version selector
51
- * @param {string} options.title - Page title
52
- */
53
- function generateScalarHtml({
54
- specUrl,
55
- sources,
56
- title = 'Frigg API Documentation',
57
- }) {
58
- // If sources provided, use multi-spec configuration
59
- const config = sources ? { sources } : { url: specUrl };
60
-
61
- return `<!DOCTYPE html>
62
- <html>
63
- <head>
64
- <title>${title}</title>
65
- <meta charset="utf-8" />
66
- <meta name="viewport" content="width=device-width, initial-scale=1" />
67
- <style>
68
- body { margin: 0; padding: 0; }
69
- </style>
70
- </head>
71
- <body>
72
- <script id="api-reference" data-configuration='${JSON.stringify(
73
- config
74
- )}'></script>
75
- <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
76
- </body>
77
- </html>`;
78
- }
79
-
80
- // ============================================================================
81
- // OpenAPI Spec Endpoints
82
- // ============================================================================
83
-
84
- /**
85
- * GET /api/openapi.json - Default (v2) OpenAPI spec
86
- */
87
- router.get('/api/openapi.json', (req, res) => {
88
- try {
89
- const serverUrl = `${req.protocol}://${req.get('host')}`;
90
- const appDefinition = loadAppDefinitionForDocs();
91
- const spec = generateOpenApiSpecV2(appDefinition, { serverUrl });
92
- res.json(spec);
93
- } catch (error) {
94
- console.error('Failed to generate OpenAPI spec:', error.message);
95
- res.status(500).json({ error: 'Failed to load API specification' });
96
- }
97
- });
98
-
99
- /**
100
- * GET /api/openapi-v1.json - v1 API OpenAPI spec
101
- */
102
- router.get('/api/openapi-v1.json', (req, res) => {
103
- try {
104
- const serverUrl = `${req.protocol}://${req.get('host')}`;
105
- const appDefinition = loadAppDefinitionForDocs();
106
- const spec = generateOpenApiSpecV1(appDefinition, { serverUrl });
107
- res.json(spec);
108
- } catch (error) {
109
- console.error('Failed to generate v1 OpenAPI spec:', error.message);
110
- res.status(500).json({ error: 'Failed to load API specification' });
111
- }
112
- });
113
-
114
- /**
115
- * GET /api/openapi-v2.json - v2 API OpenAPI spec
116
- */
117
- router.get('/api/openapi-v2.json', (req, res) => {
118
- try {
119
- const serverUrl = `${req.protocol}://${req.get('host')}`;
120
- const appDefinition = loadAppDefinitionForDocs();
121
- const spec = generateOpenApiSpecV2(appDefinition, { serverUrl });
122
- res.json(spec);
123
- } catch (error) {
124
- console.error('Failed to generate v2 OpenAPI spec:', error.message);
125
- res.status(500).json({ error: 'Failed to load API specification' });
126
- }
127
- });
128
-
129
- // ============================================================================
130
- // Documentation UI Endpoints
131
- // ============================================================================
132
-
133
- /**
134
- * GET /api/docs - Main documentation with version selector
135
- * Shows both v1 and v2 APIs with a dropdown to switch between them
136
- */
137
- router.get('/api/docs', (_req, res) => {
138
- const html = generateScalarHtml({
139
- sources: [
140
- {
141
- title: 'API v2 (Current)',
142
- slug: 'v2',
143
- url: '/api/openapi-v2.json',
144
- },
145
- {
146
- title: 'API v1 (Legacy)',
147
- slug: 'v1',
148
- url: '/api/openapi-v1.json',
149
- },
150
- ],
151
- title: 'Frigg API Documentation',
152
- });
153
- res.type('html').send(html);
154
- });
155
-
156
- /**
157
- * GET /api/v1/docs - v1-specific documentation
158
- */
159
- router.get('/api/v1/docs', (_req, res) => {
160
- const html = generateScalarHtml({
161
- specUrl: '/api/openapi-v1.json',
162
- title: 'Frigg API v1 Documentation',
163
- });
164
- res.type('html').send(html);
165
- });
166
-
167
- /**
168
- * GET /api/v2/docs - v2-specific documentation
169
- */
170
- router.get('/api/v2/docs', (_req, res) => {
171
- const html = generateScalarHtml({
172
- specUrl: '/api/openapi-v2.json',
173
- title: 'Frigg API v2 Documentation',
174
- });
175
- res.type('html').send(html);
176
- });
177
-
178
- const handler = createAppHandler('HTTP Event: Docs', router, false);
179
-
180
- module.exports = { handler, router };
@@ -1,39 +0,0 @@
1
- const catchAsyncError = require('express-async-handler');
2
- const {
3
- GetUserFromBearerToken,
4
- } = require('../../../user/use-cases/get-user-from-bearer-token');
5
- const {
6
- createUserRepository,
7
- } = require('../../../user/repositories/user-repository-factory');
8
- const { loadAppDefinition } = require('../../app-definition-loader');
9
-
10
- /**
11
- * Load user from bearer token middleware
12
- * Uses DDD pattern: Handler → Use Case → Repository
13
- */
14
- module.exports = catchAsyncError(async (req, res, next) => {
15
- const authorizationHeader = req.headers.authorization;
16
-
17
- if (authorizationHeader) {
18
- // Initialize dependencies following DDD pattern
19
- const { userConfig } = loadAppDefinition();
20
- const userRepository = createUserRepository({ userConfig });
21
- const getUserFromBearerToken = new GetUserFromBearerToken({
22
- userRepository,
23
- userConfig,
24
- });
25
-
26
- try {
27
- // Execute use case to load user
28
- req.user = await getUserFromBearerToken.execute(
29
- authorizationHeader
30
- );
31
- } catch (error) {
32
- // Don't fail - just leave req.user undefined
33
- // Let requireLoggedInUser middleware handle auth failures
34
- console.debug('Failed to load user from token:', error.message);
35
- }
36
- }
37
-
38
- return next();
39
- });
@@ -1,41 +0,0 @@
1
- /**
2
- * Middleware to require admin API key authentication.
3
- * Checks for X-API-Key header matching ADMIN_API_KEY environment variable.
4
- * In non-production environments, allows all requests through for easier development.
5
- *
6
- * @param {import('express').Request} req - Express request object
7
- * @param {import('express').Response} res - Express response object
8
- * @param {import('express').NextFunction} next - Express next middleware function
9
- */
10
- const requireAdmin = (req, res, next) => {
11
- // Allow access in local development (when NODE_ENV is not production)
12
- if (process.env.NODE_ENV !== 'production') {
13
- console.log('[requireAdmin] Development mode - bypassing admin auth');
14
- return next();
15
- }
16
-
17
- const apiKey = req.headers['x-api-key'];
18
-
19
- if (!apiKey) {
20
- console.error('[requireAdmin] Missing X-API-Key header');
21
- return res.status(401).json({
22
- status: 'error',
23
- message: 'Unauthorized - Admin API key required',
24
- code: 'MISSING_API_KEY',
25
- });
26
- }
27
-
28
- if (apiKey !== process.env.ADMIN_API_KEY) {
29
- console.error('[requireAdmin] Invalid API key provided');
30
- return res.status(401).json({
31
- status: 'error',
32
- message: 'Unauthorized - Invalid admin API key',
33
- code: 'INVALID_API_KEY',
34
- });
35
- }
36
-
37
- console.log('[requireAdmin] Admin authentication successful');
38
- next();
39
- };
40
-
41
- module.exports = { requireAdmin };