@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +474 -0
- package/handlers/routers/auth.js +51 -9
- 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 +84 -283
- package/handlers/routers/integration-defined-routers.js +44 -33
- package/handlers/routers/integration-webhook-routers.js +73 -52
- 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/user.js +30 -14
- package/handlers/routers/websocket.js +18 -4
- package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
- package/handlers/workers/db-migration.js +74 -35
- package/handlers/workers/integration-defined-workers.js +31 -20
- 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,474 @@
|
|
|
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
|
+
// Lazy-initialized repositories and use cases (deferred until first request)
|
|
29
|
+
let _userRepository,
|
|
30
|
+
_moduleRepository,
|
|
31
|
+
_getModuleEntityById,
|
|
32
|
+
_updateModuleEntity,
|
|
33
|
+
_deleteModuleEntity,
|
|
34
|
+
_createTokenForUserId,
|
|
35
|
+
_deleteUser;
|
|
36
|
+
|
|
37
|
+
function ensureInitialized() {
|
|
38
|
+
if (!_userRepository) {
|
|
39
|
+
const { userConfig } = loadAppDefinition();
|
|
40
|
+
_userRepository = createUserRepository({ userConfig });
|
|
41
|
+
_moduleRepository = createModuleRepository();
|
|
42
|
+
_getModuleEntityById = new GetModuleEntityById({
|
|
43
|
+
moduleRepository: _moduleRepository,
|
|
44
|
+
});
|
|
45
|
+
_updateModuleEntity = new UpdateModuleEntity({
|
|
46
|
+
moduleRepository: _moduleRepository,
|
|
47
|
+
});
|
|
48
|
+
_deleteModuleEntity = new DeleteModuleEntity({
|
|
49
|
+
moduleRepository: _moduleRepository,
|
|
50
|
+
});
|
|
51
|
+
_createTokenForUserId = new CreateTokenForUserId({
|
|
52
|
+
userRepository: _userRepository,
|
|
53
|
+
});
|
|
54
|
+
_deleteUser = new DeleteUser({ userRepository: _userRepository });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Lazy initialization middleware — runs once on first request
|
|
59
|
+
router.use((req, res, next) => {
|
|
60
|
+
ensureInitialized();
|
|
61
|
+
next();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Debug logging
|
|
65
|
+
router.use((req, res, next) => {
|
|
66
|
+
console.log(
|
|
67
|
+
`[Admin Router] ${req.method} ${req.path} | Original URL: ${req.originalUrl}`
|
|
68
|
+
);
|
|
69
|
+
next();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Apply admin API key auth middleware to all admin routes
|
|
73
|
+
router.use(requireAdmin);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* USER MANAGEMENT ENDPOINTS
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* GET /api/admin/users
|
|
81
|
+
* List all users with pagination
|
|
82
|
+
*/
|
|
83
|
+
router.get(
|
|
84
|
+
'/api/admin/users',
|
|
85
|
+
catchAsyncError(async (req, res) => {
|
|
86
|
+
const {
|
|
87
|
+
page = 1,
|
|
88
|
+
limit = 50,
|
|
89
|
+
sortBy = 'createdAt',
|
|
90
|
+
sortOrder = 'desc',
|
|
91
|
+
} = req.query;
|
|
92
|
+
const skip = (parseInt(page) - 1) * parseInt(limit);
|
|
93
|
+
|
|
94
|
+
// Build sort object
|
|
95
|
+
const sort = {};
|
|
96
|
+
sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
|
|
97
|
+
|
|
98
|
+
// Use repository to get users
|
|
99
|
+
const users = await _userRepository.findAllUsers({
|
|
100
|
+
skip,
|
|
101
|
+
limit: parseInt(limit),
|
|
102
|
+
sort,
|
|
103
|
+
excludeFields: ['-hashword'], // Exclude password hash
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const totalCount = await _userRepository.countUsers();
|
|
107
|
+
|
|
108
|
+
res.json({
|
|
109
|
+
users,
|
|
110
|
+
pagination: {
|
|
111
|
+
page: parseInt(page),
|
|
112
|
+
limit: parseInt(limit),
|
|
113
|
+
total: totalCount,
|
|
114
|
+
pages: Math.ceil(totalCount / parseInt(limit)),
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* GET /api/admin/users/search
|
|
122
|
+
* Search users by username or email
|
|
123
|
+
*/
|
|
124
|
+
router.get(
|
|
125
|
+
'/api/admin/users/search',
|
|
126
|
+
catchAsyncError(async (req, res) => {
|
|
127
|
+
const {
|
|
128
|
+
q,
|
|
129
|
+
page = 1,
|
|
130
|
+
limit = 50,
|
|
131
|
+
sortBy = 'createdAt',
|
|
132
|
+
sortOrder = 'desc',
|
|
133
|
+
} = req.query;
|
|
134
|
+
|
|
135
|
+
if (!q) {
|
|
136
|
+
return res.status(400).json({
|
|
137
|
+
status: 'error',
|
|
138
|
+
message: 'Search query parameter "q" is required',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const skip = (parseInt(page) - 1) * parseInt(limit);
|
|
143
|
+
|
|
144
|
+
// Build sort object
|
|
145
|
+
const sort = {};
|
|
146
|
+
sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
|
|
147
|
+
|
|
148
|
+
// Use repository to search users
|
|
149
|
+
const users = await _userRepository.searchUsers({
|
|
150
|
+
query: q,
|
|
151
|
+
skip,
|
|
152
|
+
limit: parseInt(limit),
|
|
153
|
+
sort,
|
|
154
|
+
excludeFields: ['-hashword'],
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const totalCount = await _userRepository.countUsersBySearchQuery(q);
|
|
158
|
+
|
|
159
|
+
res.json({
|
|
160
|
+
users,
|
|
161
|
+
pagination: {
|
|
162
|
+
page: parseInt(page),
|
|
163
|
+
limit: parseInt(limit),
|
|
164
|
+
total: totalCount,
|
|
165
|
+
pages: Math.ceil(totalCount / parseInt(limit)),
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* POST /api/admin/users
|
|
173
|
+
* Create a new user (admin only)
|
|
174
|
+
* Admin-specific features:
|
|
175
|
+
* - Can create users with custom roles
|
|
176
|
+
* - Can set verified status
|
|
177
|
+
* - Can assign to organizations
|
|
178
|
+
* - No email verification required
|
|
179
|
+
*/
|
|
180
|
+
router.post(
|
|
181
|
+
'/api/admin/users',
|
|
182
|
+
catchAsyncError(async (req, res) => {
|
|
183
|
+
const {
|
|
184
|
+
username,
|
|
185
|
+
email,
|
|
186
|
+
password,
|
|
187
|
+
type = 'INDIVIDUAL',
|
|
188
|
+
appUserId,
|
|
189
|
+
organizationId,
|
|
190
|
+
verified = true, // Admins can create pre-verified users
|
|
191
|
+
} = req.body;
|
|
192
|
+
|
|
193
|
+
// Validate required fields
|
|
194
|
+
if (!username || !email || !password) {
|
|
195
|
+
return res.status(400).json({
|
|
196
|
+
status: 'error',
|
|
197
|
+
message: 'Username, email, and password are required',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check if user already exists
|
|
202
|
+
const existingUser = await _userRepository.findIndividualUserByUsername(
|
|
203
|
+
username
|
|
204
|
+
);
|
|
205
|
+
if (existingUser) {
|
|
206
|
+
return res.status(409).json({
|
|
207
|
+
status: 'error',
|
|
208
|
+
message: 'User with this username already exists',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const existingEmail = await _userRepository.findIndividualUserByEmail(
|
|
213
|
+
email
|
|
214
|
+
);
|
|
215
|
+
if (existingEmail) {
|
|
216
|
+
return res.status(409).json({
|
|
217
|
+
status: 'error',
|
|
218
|
+
message: 'User with this email already exists',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Hash password (using bcryptjs which is already imported)
|
|
223
|
+
const hashword = await bcrypt.hash(password, 10);
|
|
224
|
+
|
|
225
|
+
// Create user with admin-specified attributes
|
|
226
|
+
const userData = {
|
|
227
|
+
username,
|
|
228
|
+
email,
|
|
229
|
+
hashword,
|
|
230
|
+
type,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Add optional fields if provided
|
|
234
|
+
if (appUserId) userData.appUserId = appUserId;
|
|
235
|
+
if (organizationId) userData.organizationId = organizationId;
|
|
236
|
+
|
|
237
|
+
const user = await _userRepository.createIndividualUser(userData);
|
|
238
|
+
|
|
239
|
+
// Remove sensitive fields
|
|
240
|
+
const userObj = user.toObject ? user.toObject() : user;
|
|
241
|
+
delete userObj.hashword;
|
|
242
|
+
|
|
243
|
+
res.status(201).json({
|
|
244
|
+
user: userObj,
|
|
245
|
+
message: 'User created successfully by admin',
|
|
246
|
+
});
|
|
247
|
+
})
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* GET /api/admin/users/:userId
|
|
252
|
+
* Get a specific user by ID
|
|
253
|
+
*/
|
|
254
|
+
router.get(
|
|
255
|
+
'/api/admin/users/:userId',
|
|
256
|
+
catchAsyncError(async (req, res) => {
|
|
257
|
+
const { userId } = req.params;
|
|
258
|
+
|
|
259
|
+
const user = await _userRepository.findUserById(userId);
|
|
260
|
+
|
|
261
|
+
if (!user) {
|
|
262
|
+
return res.status(404).json({
|
|
263
|
+
status: 'error',
|
|
264
|
+
message: 'User not found',
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Remove sensitive fields
|
|
269
|
+
const userObj = user.toObject ? user.toObject() : user;
|
|
270
|
+
delete userObj.hashword;
|
|
271
|
+
|
|
272
|
+
res.json({ user: userObj });
|
|
273
|
+
})
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* POST /api/admin/users/:userId/impersonate
|
|
278
|
+
* Generate a token for a user without requiring password (admin impersonation)
|
|
279
|
+
* Allows admins to login as any user for support/testing purposes
|
|
280
|
+
*/
|
|
281
|
+
router.post(
|
|
282
|
+
'/api/admin/users/:userId/impersonate',
|
|
283
|
+
catchAsyncError(async (req, res) => {
|
|
284
|
+
const { userId } = req.params;
|
|
285
|
+
const { expiresInMinutes = 120 } = req.body;
|
|
286
|
+
|
|
287
|
+
// Find the user
|
|
288
|
+
const user = await _userRepository.findUserById(userId);
|
|
289
|
+
|
|
290
|
+
if (!user) {
|
|
291
|
+
return res.status(404).json({
|
|
292
|
+
status: 'error',
|
|
293
|
+
message: 'User not found',
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Generate token without password verification
|
|
298
|
+
const token = await _createTokenForUserId.execute(
|
|
299
|
+
userId,
|
|
300
|
+
expiresInMinutes
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
res.json({
|
|
304
|
+
token,
|
|
305
|
+
message: `Impersonating user: ${user.username || user.email}`,
|
|
306
|
+
expiresInMinutes,
|
|
307
|
+
});
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* DELETE /api/admin/users/:userId
|
|
313
|
+
* Delete a user by ID (admin only)
|
|
314
|
+
* IMPORTANT: This is a destructive operation - use with caution
|
|
315
|
+
*/
|
|
316
|
+
router.delete(
|
|
317
|
+
'/api/admin/users/:userId',
|
|
318
|
+
catchAsyncError(async (req, res) => {
|
|
319
|
+
const { userId } = req.params;
|
|
320
|
+
|
|
321
|
+
// Execute delete user use case
|
|
322
|
+
await _deleteUser.execute(userId);
|
|
323
|
+
|
|
324
|
+
res.status(204).send();
|
|
325
|
+
})
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* GLOBAL ENTITY MANAGEMENT ENDPOINTS
|
|
330
|
+
*/
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* GET /api/admin/entities
|
|
334
|
+
* List all global entities
|
|
335
|
+
*/
|
|
336
|
+
router.get(
|
|
337
|
+
'/api/admin/entities',
|
|
338
|
+
catchAsyncError(async (req, res) => {
|
|
339
|
+
const { type, status } = req.query;
|
|
340
|
+
|
|
341
|
+
const query = { isGlobal: true };
|
|
342
|
+
if (type) query.type = type;
|
|
343
|
+
if (status) query.status = status;
|
|
344
|
+
|
|
345
|
+
const entities = await _moduleRepository.findEntitiesBy(query);
|
|
346
|
+
|
|
347
|
+
res.json({ entities });
|
|
348
|
+
})
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* GET /api/admin/entities/:entityId
|
|
353
|
+
* Get a specific global entity
|
|
354
|
+
*/
|
|
355
|
+
router.get(
|
|
356
|
+
'/api/admin/entities/:entityId',
|
|
357
|
+
catchAsyncError(async (req, res) => {
|
|
358
|
+
const { entityId } = req.params;
|
|
359
|
+
|
|
360
|
+
const entity = await _getModuleEntityById.execute(entityId);
|
|
361
|
+
|
|
362
|
+
if (!entity || !entity.isGlobal) {
|
|
363
|
+
return res.status(404).json({
|
|
364
|
+
status: 'error',
|
|
365
|
+
message: 'Global entity not found',
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
res.json({ entity });
|
|
370
|
+
})
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* POST /api/admin/entities
|
|
375
|
+
* Create a new global entity
|
|
376
|
+
*/
|
|
377
|
+
router.post(
|
|
378
|
+
'/api/admin/entities',
|
|
379
|
+
catchAsyncError(async (req, res) => {
|
|
380
|
+
const { type, ...entityData } = req.body;
|
|
381
|
+
|
|
382
|
+
if (!type) {
|
|
383
|
+
return res.status(400).json({
|
|
384
|
+
status: 'error',
|
|
385
|
+
message: 'Entity type is required',
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Create entity with isGlobal flag
|
|
390
|
+
const entity = await _moduleRepository.createEntity({
|
|
391
|
+
...entityData,
|
|
392
|
+
type,
|
|
393
|
+
isGlobal: true,
|
|
394
|
+
status: 'connected',
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
res.status(201).json({ entity });
|
|
398
|
+
})
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* PUT /api/admin/entities/:entityId
|
|
403
|
+
* Update a global entity
|
|
404
|
+
*/
|
|
405
|
+
router.put(
|
|
406
|
+
'/api/admin/entities/:entityId',
|
|
407
|
+
catchAsyncError(async (req, res) => {
|
|
408
|
+
const { entityId } = req.params;
|
|
409
|
+
|
|
410
|
+
const entity = await _updateModuleEntity.execute(entityId, req.body);
|
|
411
|
+
|
|
412
|
+
if (!entity) {
|
|
413
|
+
return res.status(404).json({
|
|
414
|
+
status: 'error',
|
|
415
|
+
message: 'Global entity not found',
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
res.json({ entity });
|
|
420
|
+
})
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* DELETE /api/admin/entities/:entityId
|
|
425
|
+
* Delete a global entity
|
|
426
|
+
*/
|
|
427
|
+
router.delete(
|
|
428
|
+
'/api/admin/entities/:entityId',
|
|
429
|
+
catchAsyncError(async (req, res) => {
|
|
430
|
+
const { entityId } = req.params;
|
|
431
|
+
|
|
432
|
+
await _deleteModuleEntity.execute(entityId);
|
|
433
|
+
|
|
434
|
+
res.status(204).send();
|
|
435
|
+
})
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* POST /api/admin/entities/:entityId/test
|
|
440
|
+
* Test connection for a global entity
|
|
441
|
+
*/
|
|
442
|
+
router.post(
|
|
443
|
+
'/api/admin/entities/:entityId/test',
|
|
444
|
+
catchAsyncError(async (req, res) => {
|
|
445
|
+
const { entityId } = req.params;
|
|
446
|
+
|
|
447
|
+
const entity = await _getModuleEntityById.execute(entityId);
|
|
448
|
+
|
|
449
|
+
if (!entity || !entity.isGlobal) {
|
|
450
|
+
return res.status(404).json({
|
|
451
|
+
status: 'error',
|
|
452
|
+
message: 'Global entity not found',
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Test the entity connection
|
|
457
|
+
try {
|
|
458
|
+
// This would use a TestModuleAuth use case
|
|
459
|
+
res.json({
|
|
460
|
+
status: 'success',
|
|
461
|
+
message: 'Entity connection test successful',
|
|
462
|
+
});
|
|
463
|
+
} catch (error) {
|
|
464
|
+
res.status(500).json({
|
|
465
|
+
status: 'error',
|
|
466
|
+
message: `Entity connection test failed: ${error.message}`,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
})
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
const handler = createAppHandler('HTTP Event: Admin', router);
|
|
473
|
+
|
|
474
|
+
module.exports = { handler, router };
|
package/handlers/routers/auth.js
CHANGED
|
@@ -1,15 +1,57 @@
|
|
|
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
|
+
let _router;
|
|
7
|
+
let _handler;
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}?${new URLSearchParams(req.query)}`
|
|
10
|
-
);
|
|
11
|
-
});
|
|
9
|
+
function ensureRouter() {
|
|
10
|
+
if (!_router) {
|
|
11
|
+
_router = createIntegrationRouter();
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
_router
|
|
14
|
+
.route('/api/integrations/redirect/:appId')
|
|
15
|
+
.get((req, res) => {
|
|
16
|
+
res.redirect(
|
|
17
|
+
`${process.env.FRONTEND_URI}/redirect/${
|
|
18
|
+
req.params.appId
|
|
19
|
+
}?${new URLSearchParams(req.query)}`
|
|
20
|
+
);
|
|
21
|
+
});
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
// Integration settings endpoint
|
|
24
|
+
_router
|
|
25
|
+
.route('/config/integration-settings')
|
|
26
|
+
.get(requireLoggedInUser, (req, res) => {
|
|
27
|
+
const appDefinition = loadAppDefinition();
|
|
28
|
+
|
|
29
|
+
const settings = {
|
|
30
|
+
autoProvisioningEnabled:
|
|
31
|
+
appDefinition.integration
|
|
32
|
+
?.autoProvisioningEnabled ?? true,
|
|
33
|
+
credentialReuseStrategy:
|
|
34
|
+
appDefinition.integration
|
|
35
|
+
?.credentialReuseStrategy ?? 'shared',
|
|
36
|
+
allowUserManagedEntities:
|
|
37
|
+
appDefinition.integration
|
|
38
|
+
?.allowUserManagedEntities ?? true,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
res.json(settings);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return _router;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
get router() {
|
|
49
|
+
return ensureRouter();
|
|
50
|
+
},
|
|
51
|
+
get handler() {
|
|
52
|
+
if (!_handler) {
|
|
53
|
+
_handler = createAppHandler('HTTP Event: Auth', ensureRouter());
|
|
54
|
+
}
|
|
55
|
+
return _handler;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -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
|
-
|