@friggframework/core 2.0.0-next.6 → 2.0.0-next.61
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/CLAUDE.md +694 -0
- package/README.md +959 -50
- package/application/commands/README.md +451 -0
- package/application/commands/credential-commands.js +245 -0
- package/application/commands/entity-commands.js +336 -0
- package/application/commands/integration-commands.js +210 -0
- package/application/commands/user-commands.js +283 -0
- package/application/index.js +69 -0
- package/core/CLAUDE.md +690 -0
- package/core/Worker.js +8 -21
- package/core/create-handler.js +14 -7
- package/credential/repositories/credential-repository-documentdb.js +304 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +98 -0
- package/credential/repositories/credential-repository-mongo.js +269 -0
- package/credential/repositories/credential-repository-postgres.js +291 -0
- package/credential/repositories/credential-repository.js +302 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +136 -0
- package/database/encryption/README.md +839 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +268 -0
- package/database/encryption/field-encryption-service.js +226 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/prisma-encryption-extension.js +222 -0
- package/database/index.js +61 -21
- package/database/models/WebsocketConnection.js +16 -10
- package/database/models/readme.md +1 -0
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +134 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/health-check-repository.js +108 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +91 -0
- package/database/utils/mongodb-schema-init.js +106 -0
- package/database/utils/prisma-runner.js +477 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/encrypt/test-encrypt.js +0 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +2 -1
- package/errors/index.js +2 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +4 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +4 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +334 -0
- package/generated/prisma-mongodb/index-browser.js +316 -0
- package/generated/prisma-mongodb/index.d.ts +22903 -0
- package/generated/prisma-mongodb/index.js +359 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
- package/generated/prisma-mongodb/runtime/binary.js +289 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
- package/generated/prisma-mongodb/runtime/edge.js +34 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
- package/generated/prisma-mongodb/runtime/react-native.js +83 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-mongodb/schema.prisma +360 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +341 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +4 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +4 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +356 -0
- package/generated/prisma-postgresql/index-browser.js +338 -0
- package/generated/prisma-postgresql/index.d.ts +25077 -0
- package/generated/prisma-postgresql/index.js +381 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
- package/generated/prisma-postgresql/runtime/binary.js +289 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
- package/generated/prisma-postgresql/runtime/edge.js +34 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
- package/generated/prisma-postgresql/runtime/react-native.js +83 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-postgresql/schema.prisma +343 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +363 -0
- package/handlers/WEBHOOKS.md +653 -0
- package/handlers/app-definition-loader.js +38 -0
- package/handlers/app-handler-helpers.js +56 -0
- package/handlers/backend-utils.js +186 -0
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/integration-event-dispatcher.js +54 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +15 -0
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +326 -0
- package/handlers/routers/health.js +516 -0
- package/handlers/routers/integration-defined-routers.js +45 -0
- package/handlers/routers/integration-webhook-routers.js +67 -0
- package/handlers/routers/user.js +63 -0
- package/handlers/routers/websocket.js +57 -0
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +352 -0
- package/handlers/workers/integration-defined-workers.js +27 -0
- package/index.js +77 -22
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/index.js +12 -10
- package/integrations/integration-base.js +326 -55
- package/integrations/integration-router.js +374 -179
- package/integrations/options.js +1 -1
- package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-documentdb.js +210 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +127 -0
- package/integrations/repositories/integration-repository-mongo.js +303 -0
- package/integrations/repositories/integration-repository-postgres.js +352 -0
- package/integrations/repositories/process-repository-documentdb.js +243 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +90 -0
- package/integrations/repositories/process-repository-mongo.js +190 -0
- package/integrations/repositories/process-repository-postgres.js +217 -0
- package/integrations/tests/doubles/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +105 -0
- package/integrations/tests/doubles/test-integration-repository.js +99 -0
- package/integrations/use-cases/create-integration.js +83 -0
- package/integrations/use-cases/create-process.js +128 -0
- package/integrations/use-cases/delete-integration-for-user.js +101 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +87 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +92 -0
- package/integrations/use-cases/update-process-metrics.js +201 -0
- package/integrations/use-cases/update-process-state.js +119 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/logs/logger.js +0 -4
- package/{module-plugin → modules}/entity.js +1 -1
- package/{module-plugin → modules}/index.js +0 -8
- package/modules/module-factory.js +56 -0
- package/modules/module.js +221 -0
- package/modules/repositories/module-repository-documentdb.js +307 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +129 -0
- package/modules/repositories/module-repository-mongo.js +377 -0
- package/modules/repositories/module-repository-postgres.js +426 -0
- package/modules/repositories/module-repository.js +316 -0
- package/modules/requester/api-key.js +52 -0
- package/{module-plugin → modules}/requester/requester.js +1 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +71 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +31 -0
- package/modules/use-cases/get-module.js +74 -0
- package/modules/use-cases/process-authorization-callback.js +133 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +82 -50
- package/prisma-mongodb/schema.prisma +360 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +343 -0
- package/queues/queuer-util.js +27 -22
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/token/repositories/token-repository-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/core/index.d.ts +2 -2
- package/types/integrations/index.d.ts +2 -6
- package/types/module-plugin/index.d.ts +5 -59
- package/types/syncs/index.d.ts +0 -2
- package/user/repositories/user-repository-documentdb.js +441 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +201 -0
- package/user/repositories/user-repository-mongo.js +308 -0
- package/user/repositories/user-repository-postgres.js +360 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +127 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/mongo.js +0 -45
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/api-key.js +0 -36
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Repository Interface
|
|
3
|
+
* Abstract base class defining the contract for user persistence adapters
|
|
4
|
+
*
|
|
5
|
+
* This follows the Port in Hexagonal Architecture:
|
|
6
|
+
* - Domain layer depends on this abstraction
|
|
7
|
+
* - Concrete adapters implement this interface
|
|
8
|
+
* - Use cases receive repositories via dependency injection
|
|
9
|
+
*
|
|
10
|
+
* Note: Currently, User model has identical structure across MongoDB and PostgreSQL,
|
|
11
|
+
* so UserRepository serves both. This interface exists for consistency and
|
|
12
|
+
* future-proofing if database-specific implementations become needed.
|
|
13
|
+
*
|
|
14
|
+
* @abstract
|
|
15
|
+
*/
|
|
16
|
+
class UserRepositoryInterface {
|
|
17
|
+
/**
|
|
18
|
+
* Get session token from base64 buffer token
|
|
19
|
+
*
|
|
20
|
+
* @param {string} token - Base64 buffer token
|
|
21
|
+
* @returns {Promise<Object>} Session token object
|
|
22
|
+
* @abstract
|
|
23
|
+
*/
|
|
24
|
+
async getSessionToken(token) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
'Method getSessionToken must be implemented by subclass'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Find organization user by ID
|
|
32
|
+
*
|
|
33
|
+
* @param {string|number} userId - User ID
|
|
34
|
+
* @returns {Promise<Object|null>} User object or null
|
|
35
|
+
* @abstract
|
|
36
|
+
*/
|
|
37
|
+
async findOrganizationUserById(userId) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'Method findOrganizationUserById must be implemented by subclass'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Find individual user by ID
|
|
45
|
+
*
|
|
46
|
+
* @param {string|number} userId - User ID
|
|
47
|
+
* @returns {Promise<Object|null>} User object or null
|
|
48
|
+
* @abstract
|
|
49
|
+
*/
|
|
50
|
+
async findIndividualUserById(userId) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'Method findIndividualUserById must be implemented by subclass'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create token with expiration
|
|
58
|
+
*
|
|
59
|
+
* @param {string|number} userId - User ID
|
|
60
|
+
* @param {string} rawToken - Raw unhashed token
|
|
61
|
+
* @param {number} minutes - Minutes until expiration (default 120)
|
|
62
|
+
* @returns {Promise<string>} Base64 buffer token
|
|
63
|
+
* @abstract
|
|
64
|
+
*/
|
|
65
|
+
async createToken(userId, rawToken, minutes = 120) {
|
|
66
|
+
throw new Error('Method createToken must be implemented by subclass');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create individual user
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} params - User creation parameters
|
|
73
|
+
* @returns {Promise<Object>} Created user object
|
|
74
|
+
* @abstract
|
|
75
|
+
*/
|
|
76
|
+
async createIndividualUser(params) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
'Method createIndividualUser must be implemented by subclass'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create organization user
|
|
84
|
+
*
|
|
85
|
+
* @param {Object} params - Organization creation parameters
|
|
86
|
+
* @returns {Promise<Object>} Created organization object
|
|
87
|
+
* @abstract
|
|
88
|
+
*/
|
|
89
|
+
async createOrganizationUser(params) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'Method createOrganizationUser must be implemented by subclass'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Find individual user by username
|
|
97
|
+
*
|
|
98
|
+
* @param {string} username - Username to search for
|
|
99
|
+
* @returns {Promise<Object|null>} User object or null
|
|
100
|
+
* @abstract
|
|
101
|
+
*/
|
|
102
|
+
async findIndividualUserByUsername(username) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
'Method findIndividualUserByUsername must be implemented by subclass'
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Find individual user by app user ID
|
|
110
|
+
*
|
|
111
|
+
* @param {string} appUserId - App user ID to search for
|
|
112
|
+
* @returns {Promise<Object|null>} User object or null
|
|
113
|
+
* @abstract
|
|
114
|
+
*/
|
|
115
|
+
async findIndividualUserByAppUserId(appUserId) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
'Method findIndividualUserByAppUserId must be implemented by subclass'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Find organization user by app org ID
|
|
123
|
+
*
|
|
124
|
+
* @param {string} appOrgId - App organization ID to search for
|
|
125
|
+
* @returns {Promise<Object|null>} User object or null
|
|
126
|
+
* @abstract
|
|
127
|
+
*/
|
|
128
|
+
async findOrganizationUserByAppOrgId(appOrgId) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
'Method findOrganizationUserByAppOrgId must be implemented by subclass'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Find individual user by email
|
|
136
|
+
*
|
|
137
|
+
* @param {string} email - Email to search for
|
|
138
|
+
* @returns {Promise<Object|null>} User object or null
|
|
139
|
+
* @abstract
|
|
140
|
+
*/
|
|
141
|
+
async findIndividualUserByEmail(email) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
'Method findIndividualUserByEmail must be implemented by subclass'
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Update individual user
|
|
149
|
+
*
|
|
150
|
+
* @param {string|number} userId - User ID
|
|
151
|
+
* @param {Object} updates - Fields to update
|
|
152
|
+
* @returns {Promise<Object>} Updated user object
|
|
153
|
+
* @abstract
|
|
154
|
+
*/
|
|
155
|
+
async updateIndividualUser(userId, updates) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
'Method updateIndividualUser must be implemented by subclass'
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Update organization user
|
|
163
|
+
*
|
|
164
|
+
* @param {string|number} userId - User ID
|
|
165
|
+
* @param {Object} updates - Fields to update
|
|
166
|
+
* @returns {Promise<Object>} Updated user object
|
|
167
|
+
* @abstract
|
|
168
|
+
*/
|
|
169
|
+
async updateOrganizationUser(userId, updates) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
'Method updateOrganizationUser must be implemented by subclass'
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Delete user by ID
|
|
177
|
+
*
|
|
178
|
+
* @param {string|number} userId - User ID to delete
|
|
179
|
+
* @returns {Promise<boolean>} True if deleted successfully
|
|
180
|
+
* @abstract
|
|
181
|
+
*/
|
|
182
|
+
async deleteUser(userId) {
|
|
183
|
+
throw new Error('Method deleteUser must be implemented by subclass');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Link an individual user to an organization user
|
|
188
|
+
*
|
|
189
|
+
* @param {string|number} individualUserId - Individual user ID
|
|
190
|
+
* @param {string|number} organizationUserId - Organization user ID
|
|
191
|
+
* @returns {Promise<Object>} Updated individual user object
|
|
192
|
+
* @abstract
|
|
193
|
+
*/
|
|
194
|
+
async linkIndividualToOrganization(individualUserId, organizationUserId) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
'Method linkIndividualToOrganization must be implemented by subclass'
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = { UserRepositoryInterface };
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
const bcrypt = require('bcryptjs');
|
|
2
|
+
const { prisma } = require('../../database/prisma');
|
|
3
|
+
const {
|
|
4
|
+
createTokenRepository,
|
|
5
|
+
} = require('../../token/repositories/token-repository-factory');
|
|
6
|
+
const { UserRepositoryInterface } = require('./user-repository-interface');
|
|
7
|
+
const { ClientSafeError } = require('../../errors');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* MongoDB User Repository Adapter
|
|
11
|
+
* Handles user operations with discriminator pattern support
|
|
12
|
+
*
|
|
13
|
+
* MongoDB-specific characteristics:
|
|
14
|
+
* - Uses String IDs (ObjectId)
|
|
15
|
+
* - No ID conversion needed (IDs are already strings)
|
|
16
|
+
* - IndividualUser/OrganizationUser discriminators → User model with type field
|
|
17
|
+
*/
|
|
18
|
+
class UserRepositoryMongo extends UserRepositoryInterface {
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
this.prisma = prisma;
|
|
22
|
+
this.tokenRepository = createTokenRepository();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get session token from base64 buffer token
|
|
27
|
+
* Delegates to TokenRepository
|
|
28
|
+
*
|
|
29
|
+
* @param {string} token - Base64 buffer token
|
|
30
|
+
* @returns {Promise<Object>} Session token object with string IDs
|
|
31
|
+
*/
|
|
32
|
+
async getSessionToken(token) {
|
|
33
|
+
const jsonToken =
|
|
34
|
+
this.tokenRepository.getJSONTokenFromBase64BufferToken(token);
|
|
35
|
+
const sessionToken = await this.tokenRepository.validateAndGetToken(
|
|
36
|
+
jsonToken
|
|
37
|
+
);
|
|
38
|
+
return sessionToken;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Find organization user by ID
|
|
43
|
+
* Replaces: OrganizationUser.findById(userId)
|
|
44
|
+
*
|
|
45
|
+
* @param {string} userId - User ID
|
|
46
|
+
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
47
|
+
*/
|
|
48
|
+
async findOrganizationUserById(userId) {
|
|
49
|
+
return await this.prisma.user.findFirst({
|
|
50
|
+
where: {
|
|
51
|
+
id: userId,
|
|
52
|
+
type: 'ORGANIZATION',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Find individual user by ID
|
|
59
|
+
* Replaces: IndividualUser.findById(userId)
|
|
60
|
+
*
|
|
61
|
+
* @param {string} userId - User ID
|
|
62
|
+
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
63
|
+
*/
|
|
64
|
+
async findIndividualUserById(userId) {
|
|
65
|
+
return await this.prisma.user.findFirst({
|
|
66
|
+
where: {
|
|
67
|
+
id: userId,
|
|
68
|
+
type: 'INDIVIDUAL',
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create token with expiration
|
|
75
|
+
* Delegates to TokenRepository
|
|
76
|
+
*
|
|
77
|
+
* @param {string} userId - User ID
|
|
78
|
+
* @param {string} rawToken - Raw unhashed token
|
|
79
|
+
* @param {number} minutes - Minutes until expiration (default 120)
|
|
80
|
+
* @returns {Promise<string>} Base64 buffer token
|
|
81
|
+
*/
|
|
82
|
+
async createToken(userId, rawToken, minutes = 120) {
|
|
83
|
+
const createdToken = await this.tokenRepository.createTokenWithExpire(
|
|
84
|
+
userId,
|
|
85
|
+
rawToken,
|
|
86
|
+
minutes
|
|
87
|
+
);
|
|
88
|
+
return this.tokenRepository.createBase64BufferToken(
|
|
89
|
+
createdToken,
|
|
90
|
+
rawToken
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Create individual user
|
|
96
|
+
* Replaces: IndividualUser.create(params)
|
|
97
|
+
*
|
|
98
|
+
* @param {Object} params - User creation parameters
|
|
99
|
+
* @param {string} [params.hashword] - Plain text password (will be bcrypt hashed automatically)
|
|
100
|
+
* @returns {Promise<Object>} Created user object with string IDs
|
|
101
|
+
*/
|
|
102
|
+
async createIndividualUser(params) {
|
|
103
|
+
const data = {
|
|
104
|
+
type: 'INDIVIDUAL',
|
|
105
|
+
email: params.email,
|
|
106
|
+
username: params.username,
|
|
107
|
+
appUserId: params.appUserId,
|
|
108
|
+
organizationId: params.organization || params.organizationId,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
params.hashword !== undefined &&
|
|
113
|
+
params.hashword !== null &&
|
|
114
|
+
params.hashword !== ''
|
|
115
|
+
) {
|
|
116
|
+
if (typeof params.hashword !== 'string') {
|
|
117
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|
|
121
|
+
if (params.hashword.startsWith('$2')) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
'Password appears to be already hashed. Pass plain text password only.'
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
data.hashword = await bcrypt.hash(params.hashword, 10);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return await this.prisma.user.create({ data });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create organization user
|
|
135
|
+
* Replaces: OrganizationUser.create(params)
|
|
136
|
+
*
|
|
137
|
+
* @param {Object} params - Organization creation parameters
|
|
138
|
+
* @returns {Promise<Object>} Created organization object with string IDs
|
|
139
|
+
*/
|
|
140
|
+
async createOrganizationUser(params) {
|
|
141
|
+
return await this.prisma.user.create({
|
|
142
|
+
data: {
|
|
143
|
+
type: 'ORGANIZATION',
|
|
144
|
+
appOrgId: params.appOrgId,
|
|
145
|
+
name: params.name,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Find individual user by username
|
|
152
|
+
* Replaces: IndividualUser.findOne({ username })
|
|
153
|
+
*
|
|
154
|
+
* @param {string} username - Username to search for
|
|
155
|
+
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
156
|
+
*/
|
|
157
|
+
async findIndividualUserByUsername(username) {
|
|
158
|
+
return await this.prisma.user.findFirst({
|
|
159
|
+
where: {
|
|
160
|
+
type: 'INDIVIDUAL',
|
|
161
|
+
username,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Find individual user by app user ID
|
|
168
|
+
* Replaces: IndividualUser.getUserByAppUserId(appUserId)
|
|
169
|
+
*
|
|
170
|
+
* @param {string} appUserId - App user ID to search for
|
|
171
|
+
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
172
|
+
*/
|
|
173
|
+
async findIndividualUserByAppUserId(appUserId) {
|
|
174
|
+
return await this.prisma.user.findFirst({
|
|
175
|
+
where: {
|
|
176
|
+
type: 'INDIVIDUAL',
|
|
177
|
+
appUserId,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Find organization user by app org ID
|
|
184
|
+
* Replaces: OrganizationUser.getUserByAppOrgId(appOrgId)
|
|
185
|
+
*
|
|
186
|
+
* @param {string} appOrgId - App organization ID to search for
|
|
187
|
+
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
188
|
+
*/
|
|
189
|
+
async findOrganizationUserByAppOrgId(appOrgId) {
|
|
190
|
+
return await this.prisma.user.findFirst({
|
|
191
|
+
where: {
|
|
192
|
+
type: 'ORGANIZATION',
|
|
193
|
+
appOrgId,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Find individual user by email
|
|
200
|
+
* @param {string} email - Email to search for
|
|
201
|
+
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
202
|
+
*/
|
|
203
|
+
async findIndividualUserByEmail(email) {
|
|
204
|
+
return await this.prisma.user.findFirst({
|
|
205
|
+
where: {
|
|
206
|
+
type: 'INDIVIDUAL',
|
|
207
|
+
email,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Update individual user
|
|
214
|
+
* @param {string} userId - User ID
|
|
215
|
+
* @param {Object} updates - Fields to update
|
|
216
|
+
* @param {string} [updates.hashword] - Plain text password (will be bcrypt hashed automatically)
|
|
217
|
+
* @returns {Promise<Object>} Updated user object with string IDs
|
|
218
|
+
*/
|
|
219
|
+
async updateIndividualUser(userId, updates) {
|
|
220
|
+
const data = { ...updates };
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
data.hashword !== undefined &&
|
|
224
|
+
data.hashword !== null &&
|
|
225
|
+
data.hashword !== ''
|
|
226
|
+
) {
|
|
227
|
+
if (typeof data.hashword !== 'string') {
|
|
228
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|
|
232
|
+
if (data.hashword.startsWith('$2')) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
'Password appears to be already hashed. Pass plain text password only.'
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
data.hashword = await bcrypt.hash(data.hashword, 10);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return await this.prisma.user.update({
|
|
242
|
+
where: { id: userId },
|
|
243
|
+
data,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Update organization user
|
|
249
|
+
* @param {string} userId - User ID
|
|
250
|
+
* @param {Object} updates - Fields to update
|
|
251
|
+
* @returns {Promise<Object>} Updated user object with string IDs
|
|
252
|
+
*/
|
|
253
|
+
async updateOrganizationUser(userId, updates) {
|
|
254
|
+
return await this.prisma.user.update({
|
|
255
|
+
where: { id: userId },
|
|
256
|
+
data: updates,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Delete user by ID
|
|
262
|
+
*
|
|
263
|
+
* NOTE: This only deletes the user record itself.
|
|
264
|
+
* Prisma's onDelete: Cascade does NOT work reliably with MongoDB (no database-level referential integrity).
|
|
265
|
+
* Integration developers MUST manually cascade delete related records before calling this method:
|
|
266
|
+
* 1. Delete integrations (via deleteIntegrationById)
|
|
267
|
+
* 2. Delete entities (via deleteEntityById)
|
|
268
|
+
* 3. Delete credentials (via deleteCredentialById)
|
|
269
|
+
* 4. Finally delete user (via deleteUserById)
|
|
270
|
+
*
|
|
271
|
+
* @param {string} userId - User ID to delete
|
|
272
|
+
* @returns {Promise<boolean>} True if deleted successfully
|
|
273
|
+
*/
|
|
274
|
+
async deleteUser(userId) {
|
|
275
|
+
try {
|
|
276
|
+
await this.prisma.user.delete({
|
|
277
|
+
where: { id: userId },
|
|
278
|
+
});
|
|
279
|
+
return true;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (error.code === 'P2025') {
|
|
282
|
+
// Record not found
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Link an individual user to an organization user
|
|
291
|
+
* @param {string} individualUserId - Individual user ID (MongoDB ObjectId string)
|
|
292
|
+
* @param {string} organizationUserId - Organization user ID (MongoDB ObjectId string)
|
|
293
|
+
* @returns {Promise<Object>} Updated individual user object
|
|
294
|
+
*/
|
|
295
|
+
async linkIndividualToOrganization(individualUserId, organizationUserId) {
|
|
296
|
+
return await this.prisma.user.update({
|
|
297
|
+
where: {
|
|
298
|
+
id: individualUserId,
|
|
299
|
+
type: 'INDIVIDUAL',
|
|
300
|
+
},
|
|
301
|
+
data: {
|
|
302
|
+
organizationId: organizationUserId,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = { UserRepositoryMongo };
|