@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.
- package/CHANGELOG.md +67 -66
- package/CLAUDE.md +124 -148
- package/README.md +130 -165
- package/admin-scripts/index.js +16 -30
- package/admin-scripts/repositories/{admin-api-key-repository-documentdb.js → admin-process-repository-documentdb.js} +5 -5
- package/admin-scripts/repositories/{admin-api-key-repository-factory.js → admin-process-repository-factory.js} +15 -19
- 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-schedule-repository-factory.js +2 -6
- package/admin-scripts/repositories/script-schedule-repository-interface.js +14 -36
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +13 -25
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +13 -25
- package/application/commands/admin-script-commands.js +102 -266
- package/application/commands/integration-commands.js +6 -12
- package/application/index.js +3 -1
- package/assertions/get.js +4 -1
- package/associations/association.js +57 -59
- package/associations/model.js +41 -41
- package/core/CLAUDE.md +156 -222
- package/core/Worker.js +1 -5
- package/credential/repositories/credential-repository-documentdb.js +1 -71
- package/credential/repositories/credential-repository-interface.js +2 -8
- package/credential/repositories/credential-repository-mongo.js +4 -45
- package/credential/repositories/credential-repository-postgres.js +3 -44
- package/credential/repositories/credential-repository.js +4 -6
- package/credential/use-cases/get-credential-for-user.js +3 -21
- package/credential/use-cases/update-authentication-status.js +2 -5
- package/database/MONGODB_TRANSACTION_FIX.md +52 -72
- package/database/adapters/lambda-invoker.js +7 -8
- package/database/config.js +13 -15
- package/database/documentdb-encryption-service.js +13 -29
- package/database/documentdb-utils.js +12 -21
- package/database/encryption/README.md +36 -53
- package/database/encryption/encryption-schema-registry.js +9 -21
- package/database/encryption/field-encryption-service.js +4 -15
- package/database/encryption/logger.js +2 -3
- package/database/encryption/prisma-encryption-extension.js +8 -5
- package/database/index.js +2 -4
- package/database/models/IndividualUser.js +15 -15
- package/database/models/OrganizationUser.js +4 -6
- package/database/models/UserModel.js +2 -2
- package/database/models/WebsocketConnection.js +1 -4
- package/database/mongoose.js +2 -2
- package/database/prisma.js +14 -22
- package/database/repositories/health-check-repository-documentdb.js +7 -22
- package/database/repositories/health-check-repository-factory.js +3 -9
- package/database/repositories/health-check-repository-interface.js +5 -15
- package/database/repositories/health-check-repository-mongodb.js +6 -9
- package/database/repositories/health-check-repository-postgres.js +1 -1
- package/database/repositories/migration-status-repository-s3.js +5 -10
- package/database/use-cases/check-database-health-use-case.js +2 -3
- package/database/use-cases/check-database-state-use-case.js +7 -9
- package/database/use-cases/get-database-state-via-worker-use-case.js +12 -15
- package/database/use-cases/get-migration-status-use-case.js +2 -4
- package/database/use-cases/run-database-migration-use-case.js +9 -41
- package/database/use-cases/test-encryption-use-case.js +5 -4
- package/database/use-cases/trigger-database-migration-use-case.js +8 -9
- package/database/utils/mongodb-collection-utils.js +3 -11
- package/database/utils/mongodb-schema-init.js +4 -10
- package/database/utils/prisma-runner.js +74 -144
- package/database/utils/prisma-schema-parser.js +2 -2
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +80 -135
- package/encrypt/Cryptor.js +2 -8
- package/errors/fetch-error.js +1 -1
- package/generated/prisma-mongodb/edge.js +16 -59
- package/generated/prisma-mongodb/index-browser.js +12 -62
- package/generated/prisma-mongodb/index.d.ts +4850 -8136
- package/generated/prisma-mongodb/index.js +16 -59
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +33 -75
- package/generated/prisma-mongodb/wasm.js +16 -59
- package/generated/prisma-postgresql/edge.js +15 -59
- package/generated/prisma-postgresql/index-browser.js +11 -62
- package/generated/prisma-postgresql/index.d.ts +5963 -8930
- package/generated/prisma-postgresql/index.js +15 -59
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +33 -73
- package/generated/prisma-postgresql/wasm.js +15 -59
- package/handlers/WEBHOOKS.md +81 -104
- package/handlers/app-definition-loader.js +1 -1
- package/handlers/app-handler-helpers.js +2 -14
- package/handlers/backend-utils.js +1 -0
- package/handlers/database-migration-handler.js +45 -64
- package/handlers/middleware/admin-auth.js +53 -0
- package/handlers/routers/HEALTHCHECK.md +135 -170
- package/handlers/routers/auth.js +2 -23
- package/handlers/routers/db-migration.handler.js +3 -2
- package/handlers/routers/db-migration.js +33 -69
- package/handlers/routers/health.js +8 -23
- package/handlers/routers/integration-defined-routers.js +4 -4
- package/handlers/routers/integration-webhook-routers.js +7 -17
- package/handlers/routers/websocket.js +3 -9
- package/handlers/use-cases/check-integrations-health-use-case.js +6 -13
- package/handlers/workers/db-migration.js +26 -54
- package/index.js +9 -5
- package/integrations/WEBHOOK-QUICKSTART.md +14 -21
- package/integrations/index.js +0 -4
- package/integrations/integration-base.js +6 -9
- package/integrations/integration-router.js +146 -1817
- package/integrations/options.js +20 -47
- package/integrations/repositories/integration-repository-documentdb.js +24 -53
- package/integrations/repositories/integration-repository-factory.js +2 -6
- package/integrations/repositories/integration-repository-interface.js +9 -27
- package/integrations/repositories/integration-repository-postgres.js +3 -3
- package/integrations/repositories/process-repository-factory.js +1 -0
- package/integrations/repositories/process-repository-interface.js +5 -9
- package/integrations/repositories/process-repository-mongo.js +3 -7
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/config-capturing-integration.js +8 -9
- package/integrations/tests/doubles/dummy-integration-class.js +8 -81
- package/integrations/tests/doubles/test-integration-repository.js +11 -51
- package/integrations/use-cases/create-integration.js +32 -36
- package/integrations/use-cases/create-process.js +9 -20
- package/integrations/use-cases/delete-integration-for-user.js +4 -5
- package/integrations/use-cases/get-integration-for-user.js +10 -25
- package/integrations/use-cases/get-integration-instance-by-definition.js +7 -13
- package/integrations/use-cases/get-integrations-for-user.js +1 -4
- package/integrations/use-cases/get-possible-integrations.js +1 -1
- package/integrations/use-cases/get-process.js +6 -5
- package/integrations/use-cases/index.js +1 -3
- package/integrations/use-cases/load-integration-context.js +5 -1
- package/integrations/use-cases/update-process-state.js +8 -10
- package/integrations/utils/map-integration-dto.js +3 -9
- package/jest-setup.js +1 -1
- package/jest-teardown.js +1 -1
- package/lambda/TimeoutCatcher.js +30 -32
- package/lambda/index.js +1 -1
- package/logs/index.js +3 -3
- package/modules/module.js +1 -3
- package/modules/repositories/module-repository-documentdb.js +48 -114
- package/modules/repositories/module-repository-interface.js +0 -13
- package/modules/repositories/module-repository-mongo.js +6 -46
- package/modules/repositories/module-repository-postgres.js +6 -48
- package/modules/repositories/module-repository.js +1 -34
- package/modules/requester/api-key.js +2 -0
- package/modules/requester/basic.js +1 -0
- package/modules/requester/oauth-2.js +2 -3
- package/modules/test/mock-api/definition.js +10 -15
- package/modules/test/mock-api/mocks/hubspot.js +36 -36
- package/modules/tests/doubles/test-module-factory.js +4 -10
- package/modules/tests/doubles/test-module-repository.js +1 -1
- package/modules/use-cases/get-entities-for-user.js +1 -1
- package/modules/use-cases/get-entity-options-by-id.js +6 -8
- package/modules/use-cases/get-module.js +9 -12
- package/modules/use-cases/process-authorization-callback.js +6 -12
- package/modules/use-cases/refresh-entity-options.js +6 -8
- package/modules/use-cases/test-module-auth.js +6 -8
- package/modules/utils/map-module-dto.js +2 -2
- package/package.json +5 -7
- package/prisma-mongodb/schema.prisma +33 -75
- package/prisma-postgresql/schema.prisma +33 -73
- package/queues/queuer-util.js +1 -5
- package/syncs/model.js +51 -51
- package/syncs/repositories/sync-repository-documentdb.js +19 -39
- package/syncs/repositories/sync-repository-interface.js +2 -6
- package/syncs/repositories/sync-repository-postgres.js +13 -19
- package/syncs/sync.js +100 -104
- package/token/repositories/token-repository-factory.js +3 -1
- package/types/assertions/index.d.ts +72 -72
- package/types/associations/index.d.ts +57 -57
- package/types/core/index.d.ts +53 -59
- package/types/database/index.d.ts +2 -2
- package/types/encrypt/index.d.ts +3 -3
- package/types/errors/index.d.ts +54 -61
- package/types/eslint-config/index.d.ts +40 -40
- package/types/integrations/index.d.ts +183 -185
- package/types/lambda/index.d.ts +26 -26
- package/types/logs/index.d.ts +4 -4
- package/types/module-plugin/index.d.ts +237 -239
- package/types/prettier-config/index.d.ts +5 -5
- package/types/syncs/index.d.ts +108 -102
- package/types/test-environment/index.d.ts +14 -14
- package/types/tsconfig.json +93 -95
- package/user/tests/doubles/test-user-repository.js +2 -5
- package/user/use-cases/authenticate-user.js +4 -2
- package/user/use-cases/authenticate-with-shared-secret.js +1 -1
- package/user/use-cases/create-individual-user.js +7 -8
- 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/get-user-from-adopter-jwt.js +2 -0
- package/user/use-cases/get-user-from-bearer-token.js +3 -7
- package/user/use-cases/get-user-from-x-frigg-headers.js +11 -14
- package/user/use-cases/login-user.js +8 -8
- package/user/user.js +4 -18
- package/utils/backend-path.js +2 -2
- package/utils/index.js +1 -4
- package/websocket/repositories/websocket-connection-repository-documentdb.js +8 -21
- package/websocket/repositories/websocket-connection-repository-mongo.js +4 -8
- package/websocket/repositories/websocket-connection-repository-postgres.js +4 -8
- package/websocket/repositories/websocket-connection-repository.js +4 -8
- package/admin-scripts/repositories/admin-api-key-repository-interface.js +0 -121
- package/admin-scripts/repositories/admin-api-key-repository-mongo.js +0 -155
- package/admin-scripts/repositories/admin-api-key-repository-postgres.js +0 -189
- package/admin-scripts/repositories/script-execution-repository-documentdb.js +0 -21
- package/admin-scripts/repositories/script-execution-repository-factory.js +0 -55
- package/admin-scripts/repositories/script-execution-repository-interface.js +0 -193
- package/admin-scripts/repositories/script-execution-repository-mongo.js +0 -278
- package/admin-scripts/repositories/script-execution-repository-postgres.js +0 -320
- package/credential/credential-router.js +0 -262
- package/credential/use-cases/delete-credential-for-user.js +0 -50
- package/credential/use-cases/list-credentials-for-user.js +0 -31
- package/credential/use-cases/reauthorize-credential.js +0 -103
- package/handlers/routers/admin.js +0 -450
- package/handlers/routers/docs.js +0 -180
- package/handlers/routers/middleware/loadUser.js +0 -39
- package/handlers/routers/middleware/requireAdmin.js +0 -41
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -19
- package/integrations/use-cases/execute-proxy-request.js +0 -564
- package/modules/domain/entities/AuthorizationSession.js +0 -131
- package/modules/domain/entities/index.js +0 -10
- package/modules/repositories/authorization-session-repository-factory.js +0 -55
- package/modules/repositories/authorization-session-repository-interface.js +0 -75
- package/modules/repositories/authorization-session-repository-mongo.js +0 -169
- package/modules/repositories/authorization-session-repository-postgres.js +0 -170
- package/modules/use-cases/delete-module-entity.js +0 -23
- package/modules/use-cases/get-authorization-requirements.js +0 -110
- package/modules/use-cases/get-module-entity-by-id.js +0 -16
- package/modules/use-cases/process-authorization-step.js +0 -160
- package/modules/use-cases/start-authorization-session.js +0 -86
- package/modules/use-cases/update-module-entity.js +0 -27
- package/openapi/openapi-spec-generator.js +0 -370
- package/openapi/openapi-v1.yaml +0 -1360
- package/openapi/openapi-v2.yaml +0 -1535
- package/user/use-cases/delete-user.js +0 -47
|
@@ -17,9 +17,9 @@ schema.pre('save', async function () {
|
|
|
17
17
|
this.hashword = await bcrypt.hashSync(
|
|
18
18
|
this.hashword,
|
|
19
19
|
parseInt(this.schema.statics.decimals)
|
|
20
|
-
)
|
|
20
|
+
)
|
|
21
21
|
}
|
|
22
|
-
})
|
|
22
|
+
})
|
|
23
23
|
|
|
24
24
|
schema.static({
|
|
25
25
|
decimals: 10,
|
|
@@ -31,17 +31,18 @@ schema.static({
|
|
|
31
31
|
);
|
|
32
32
|
delete options.password;
|
|
33
33
|
}
|
|
34
|
-
return this.findOneAndUpdate(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
return this.findOneAndUpdate(
|
|
35
|
+
{_id: id},
|
|
36
|
+
options,
|
|
37
|
+
{new: true, useFindAndModify: true}
|
|
38
|
+
);
|
|
38
39
|
},
|
|
39
40
|
getUserByUsername: async function (username) {
|
|
40
41
|
let getByUser;
|
|
41
|
-
try
|
|
42
|
-
getByUser = await this.find({
|
|
42
|
+
try{
|
|
43
|
+
getByUser = await this.find({username});
|
|
43
44
|
} catch (e) {
|
|
44
|
-
console.log('oops')
|
|
45
|
+
console.log('oops')
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
if (getByUser.length > 1) {
|
|
@@ -63,14 +64,13 @@ schema.static({
|
|
|
63
64
|
);
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
|
|
66
68
|
if (getByUser.length === 1) {
|
|
67
69
|
return getByUser[0];
|
|
68
70
|
}
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
+
}
|
|
72
|
+
})
|
|
71
73
|
|
|
72
|
-
const IndividualUser =
|
|
73
|
-
Parent.discriminators?.IndividualUser ||
|
|
74
|
-
Parent.discriminator(collectionName, schema);
|
|
74
|
+
const IndividualUser = Parent.discriminators?.IndividualUser || Parent.discriminator(collectionName, schema);
|
|
75
75
|
|
|
76
|
-
module.exports = {
|
|
76
|
+
module.exports = {IndividualUser};
|
|
@@ -21,11 +21,9 @@ schema.static({
|
|
|
21
21
|
if (getByUser.length === 1) {
|
|
22
22
|
return getByUser[0];
|
|
23
23
|
}
|
|
24
|
-
}
|
|
25
|
-
})
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
26
|
|
|
27
|
-
const OrganizationUser =
|
|
28
|
-
Parent.discriminators?.OrganizationUser ||
|
|
29
|
-
Parent.discriminator(collectionName, schema);
|
|
27
|
+
const OrganizationUser = Parent.discriminators?.OrganizationUser || Parent.discriminator(collectionName, schema);
|
|
30
28
|
|
|
31
|
-
module.exports = {
|
|
29
|
+
module.exports = {OrganizationUser};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { mongoose } = require('../mongoose');
|
|
2
2
|
|
|
3
|
-
const schema = new mongoose.Schema({}, {
|
|
3
|
+
const schema = new mongoose.Schema({}, {timestamps: true})
|
|
4
4
|
|
|
5
|
-
const UserModel = mongoose.models.User || mongoose.model('User',
|
|
5
|
+
const UserModel = mongoose.models.User || mongoose.model('User',schema)
|
|
6
6
|
|
|
7
7
|
module.exports = { UserModel: UserModel };
|
|
@@ -31,10 +31,7 @@ schema.statics.getActiveConnections = async function () {
|
|
|
31
31
|
});
|
|
32
32
|
await apigwManagementApi.send(command);
|
|
33
33
|
} catch (error) {
|
|
34
|
-
if (
|
|
35
|
-
error.statusCode === 410 ||
|
|
36
|
-
error.$metadata?.httpStatusCode === 410
|
|
37
|
-
) {
|
|
34
|
+
if (error.statusCode === 410 || error.$metadata?.httpStatusCode === 410) {
|
|
38
35
|
console.log(`Stale connection ${conn.connectionId}`);
|
|
39
36
|
await this.deleteOne({
|
|
40
37
|
connectionId: conn.connectionId,
|
package/database/mongoose.js
CHANGED
package/database/prisma.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
const {
|
|
2
2
|
createEncryptionExtension,
|
|
3
3
|
} = require('./encryption/prisma-encryption-extension');
|
|
4
|
-
const {
|
|
5
|
-
loadCustomEncryptionSchema,
|
|
6
|
-
} = require('./encryption/encryption-schema-registry');
|
|
4
|
+
const { loadCustomEncryptionSchema } = require('./encryption/encryption-schema-registry');
|
|
7
5
|
const { logger } = require('./encryption/logger');
|
|
8
6
|
const { Cryptor } = require('../encrypt/Cryptor');
|
|
9
7
|
const config = require('./config');
|
|
@@ -12,7 +10,7 @@ const config = require('./config');
|
|
|
12
10
|
* Ensures DATABASE_URL is set for MongoDB connections
|
|
13
11
|
* Falls back to MONGO_URI if DATABASE_URL is not set
|
|
14
12
|
* Infrastructure layer concern - maps legacy MONGO_URI to Prisma's expected DATABASE_URL
|
|
15
|
-
*
|
|
13
|
+
*
|
|
16
14
|
* Note: This should only be called when DB_TYPE is 'mongodb' or 'documentdb'
|
|
17
15
|
*/
|
|
18
16
|
function ensureMongoDbUrl() {
|
|
@@ -24,9 +22,7 @@ function ensureMongoDbUrl() {
|
|
|
24
22
|
// Fallback to MONGO_URI for backwards compatibility with DocumentDB deployments
|
|
25
23
|
if (process.env.MONGO_URI && process.env.MONGO_URI.trim()) {
|
|
26
24
|
process.env.DATABASE_URL = process.env.MONGO_URI;
|
|
27
|
-
logger.debug(
|
|
28
|
-
'Using MONGO_URI as DATABASE_URL for Mongo-compatible connection'
|
|
29
|
-
);
|
|
25
|
+
logger.debug('Using MONGO_URI as DATABASE_URL for Mongo-compatible connection');
|
|
30
26
|
return;
|
|
31
27
|
}
|
|
32
28
|
|
|
@@ -52,7 +48,7 @@ function getEncryptionConfig() {
|
|
|
52
48
|
if (!hasKMS && !hasAES) {
|
|
53
49
|
logger.warn(
|
|
54
50
|
'No encryption keys configured (KMS_KEY_ARN or AES_KEY_ID). ' +
|
|
55
|
-
|
|
51
|
+
'Field-level encryption disabled. Set STAGE=production and configure keys to enable.'
|
|
56
52
|
);
|
|
57
53
|
return { enabled: false };
|
|
58
54
|
}
|
|
@@ -84,9 +80,7 @@ const prismaClientSingleton = () => {
|
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
throw new Error(
|
|
87
|
-
`Cannot find Prisma client for ${dbType}. Tried paths: ${paths.join(
|
|
88
|
-
', '
|
|
89
|
-
)}`
|
|
83
|
+
`Cannot find Prisma client for ${dbType}. Tried paths: ${paths.join(', ')}`
|
|
90
84
|
);
|
|
91
85
|
};
|
|
92
86
|
|
|
@@ -131,7 +125,10 @@ const prismaClientSingleton = () => {
|
|
|
131
125
|
`Field-level encryption enabled using ${encryptionConfig.method.toUpperCase()}`
|
|
132
126
|
);
|
|
133
127
|
} catch (error) {
|
|
134
|
-
logger.error(
|
|
128
|
+
logger.error(
|
|
129
|
+
'Failed to initialize encryption extension:',
|
|
130
|
+
error
|
|
131
|
+
);
|
|
135
132
|
logger.warn('Continuing without encryption...');
|
|
136
133
|
}
|
|
137
134
|
} else {
|
|
@@ -152,14 +149,11 @@ function getPrismaClient() {
|
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
// Export a getter for lazy initialization
|
|
155
|
-
const prisma = new Proxy(
|
|
156
|
-
{
|
|
157
|
-
|
|
158
|
-
get(target, prop) {
|
|
159
|
-
return getPrismaClient()[prop];
|
|
160
|
-
},
|
|
152
|
+
const prisma = new Proxy({}, {
|
|
153
|
+
get(target, prop) {
|
|
154
|
+
return getPrismaClient()[prop];
|
|
161
155
|
}
|
|
162
|
-
);
|
|
156
|
+
});
|
|
163
157
|
|
|
164
158
|
async function disconnectPrisma() {
|
|
165
159
|
await getPrismaClient().$disconnect();
|
|
@@ -172,9 +166,7 @@ async function connectPrisma() {
|
|
|
172
166
|
// Only run for MongoDB/DocumentDB (not PostgreSQL)
|
|
173
167
|
// This prevents "Cannot create namespace in multi-document transaction" errors
|
|
174
168
|
if (config.DB_TYPE === 'mongodb' || config.DB_TYPE === 'documentdb') {
|
|
175
|
-
const {
|
|
176
|
-
initializeMongoDBSchema,
|
|
177
|
-
} = require('./utils/mongodb-schema-init');
|
|
169
|
+
const { initializeMongoDBSchema } = require('./utils/mongodb-schema-init');
|
|
178
170
|
await initializeMongoDBSchema();
|
|
179
171
|
}
|
|
180
172
|
|
|
@@ -8,9 +8,7 @@ const {
|
|
|
8
8
|
insertOne,
|
|
9
9
|
deleteOne,
|
|
10
10
|
} = require('../documentdb-utils');
|
|
11
|
-
const {
|
|
12
|
-
DocumentDBEncryptionService,
|
|
13
|
-
} = require('../documentdb-encryption-service');
|
|
11
|
+
const { DocumentDBEncryptionService } = require('../documentdb-encryption-service');
|
|
14
12
|
|
|
15
13
|
class HealthCheckRepositoryDocumentDB extends HealthCheckRepositoryInterface {
|
|
16
14
|
/**
|
|
@@ -53,10 +51,7 @@ class HealthCheckRepositoryDocumentDB extends HealthCheckRepositoryInterface {
|
|
|
53
51
|
const pingStart = Date.now();
|
|
54
52
|
|
|
55
53
|
const timeoutPromise = new Promise((_, reject) =>
|
|
56
|
-
setTimeout(
|
|
57
|
-
() => reject(new Error('Database ping timeout')),
|
|
58
|
-
maxTimeMS
|
|
59
|
-
)
|
|
54
|
+
setTimeout(() => reject(new Error('Database ping timeout')), maxTimeMS)
|
|
60
55
|
);
|
|
61
56
|
|
|
62
57
|
await Promise.race([
|
|
@@ -80,14 +75,8 @@ class HealthCheckRepositoryDocumentDB extends HealthCheckRepositoryInterface {
|
|
|
80
75
|
'Credential',
|
|
81
76
|
document
|
|
82
77
|
);
|
|
83
|
-
const insertedId = await insertOne(
|
|
84
|
-
|
|
85
|
-
'Credential',
|
|
86
|
-
encryptedDocument
|
|
87
|
-
);
|
|
88
|
-
const created = await findOne(this.prisma, 'Credential', {
|
|
89
|
-
_id: insertedId,
|
|
90
|
-
});
|
|
78
|
+
const insertedId = await insertOne(this.prisma, 'Credential', encryptedDocument);
|
|
79
|
+
const created = await findOne(this.prisma, 'Credential', { _id: insertedId });
|
|
91
80
|
|
|
92
81
|
// Decrypt after read
|
|
93
82
|
const decrypted = await this.encryptionService.decryptFields(
|
|
@@ -109,10 +98,7 @@ class HealthCheckRepositoryDocumentDB extends HealthCheckRepositoryInterface {
|
|
|
109
98
|
if (!doc) return null;
|
|
110
99
|
|
|
111
100
|
// Decrypt sensitive fields
|
|
112
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
113
|
-
'Credential',
|
|
114
|
-
doc
|
|
115
|
-
);
|
|
101
|
+
const decrypted = await this.encryptionService.decryptFields('Credential', doc);
|
|
116
102
|
|
|
117
103
|
return {
|
|
118
104
|
id: fromObjectId(decrypted._id),
|
|
@@ -138,12 +124,11 @@ class HealthCheckRepositoryDocumentDB extends HealthCheckRepositoryInterface {
|
|
|
138
124
|
const objectId = toObjectId(id);
|
|
139
125
|
if (!objectId) return false;
|
|
140
126
|
|
|
141
|
-
const result = await deleteOne(this.prisma, 'Credential', {
|
|
142
|
-
_id: objectId,
|
|
143
|
-
});
|
|
127
|
+
const result = await deleteOne(this.prisma, 'Credential', { _id: objectId });
|
|
144
128
|
const deleted = result?.n ?? 0;
|
|
145
129
|
return deleted > 0;
|
|
146
130
|
}
|
|
147
131
|
}
|
|
148
132
|
|
|
149
133
|
module.exports = { HealthCheckRepositoryDocumentDB };
|
|
134
|
+
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
const {
|
|
2
|
-
|
|
3
|
-
} = require('./health-check-repository-
|
|
4
|
-
const {
|
|
5
|
-
HealthCheckRepositoryPostgreSQL,
|
|
6
|
-
} = require('./health-check-repository-postgres');
|
|
7
|
-
const {
|
|
8
|
-
HealthCheckRepositoryDocumentDB,
|
|
9
|
-
} = require('./health-check-repository-documentdb');
|
|
1
|
+
const { HealthCheckRepositoryMongoDB } = require('./health-check-repository-mongodb');
|
|
2
|
+
const { HealthCheckRepositoryPostgreSQL } = require('./health-check-repository-postgres');
|
|
3
|
+
const { HealthCheckRepositoryDocumentDB } = require('./health-check-repository-documentdb');
|
|
10
4
|
const config = require('../config');
|
|
11
5
|
|
|
12
6
|
/**
|
|
@@ -19,9 +19,7 @@ class HealthCheckRepositoryInterface {
|
|
|
19
19
|
* @abstract
|
|
20
20
|
*/
|
|
21
21
|
async getDatabaseConnectionState() {
|
|
22
|
-
throw new Error(
|
|
23
|
-
'Method getDatabaseConnectionState must be implemented by subclass'
|
|
24
|
-
);
|
|
22
|
+
throw new Error('Method getDatabaseConnectionState must be implemented by subclass');
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
/**
|
|
@@ -44,9 +42,7 @@ class HealthCheckRepositoryInterface {
|
|
|
44
42
|
* @abstract
|
|
45
43
|
*/
|
|
46
44
|
async createCredential(credentialData) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
'Method createCredential must be implemented by subclass'
|
|
49
|
-
);
|
|
45
|
+
throw new Error('Method createCredential must be implemented by subclass');
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
/**
|
|
@@ -57,9 +53,7 @@ class HealthCheckRepositoryInterface {
|
|
|
57
53
|
* @abstract
|
|
58
54
|
*/
|
|
59
55
|
async findCredentialById(id) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
'Method findCredentialById must be implemented by subclass'
|
|
62
|
-
);
|
|
56
|
+
throw new Error('Method findCredentialById must be implemented by subclass');
|
|
63
57
|
}
|
|
64
58
|
|
|
65
59
|
/**
|
|
@@ -70,9 +64,7 @@ class HealthCheckRepositoryInterface {
|
|
|
70
64
|
* @abstract
|
|
71
65
|
*/
|
|
72
66
|
async getRawCredentialById(id) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
'Method getRawCredentialById must be implemented by subclass'
|
|
75
|
-
);
|
|
67
|
+
throw new Error('Method getRawCredentialById must be implemented by subclass');
|
|
76
68
|
}
|
|
77
69
|
|
|
78
70
|
/**
|
|
@@ -83,9 +75,7 @@ class HealthCheckRepositoryInterface {
|
|
|
83
75
|
* @abstract
|
|
84
76
|
*/
|
|
85
77
|
async deleteCredential(id) {
|
|
86
|
-
throw new Error(
|
|
87
|
-
'Method deleteCredential must be implemented by subclass'
|
|
88
|
-
);
|
|
78
|
+
throw new Error('Method deleteCredential must be implemented by subclass');
|
|
89
79
|
}
|
|
90
80
|
}
|
|
91
81
|
|
|
@@ -19,7 +19,7 @@ class HealthCheckRepositoryMongoDB extends HealthCheckRepositoryInterface {
|
|
|
19
19
|
async getDatabaseConnectionState() {
|
|
20
20
|
let isConnected = false;
|
|
21
21
|
let stateName = 'unknown';
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
try {
|
|
24
24
|
await this.prisma.$runCommandRaw({ ping: 1 });
|
|
25
25
|
isConnected = true;
|
|
@@ -38,22 +38,19 @@ class HealthCheckRepositoryMongoDB extends HealthCheckRepositoryInterface {
|
|
|
38
38
|
|
|
39
39
|
async pingDatabase(maxTimeMS = 2000) {
|
|
40
40
|
const pingStart = Date.now();
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
// Create a timeout promise that rejects after maxTimeMS
|
|
43
|
-
const timeoutPromise = new Promise((_, reject) =>
|
|
44
|
-
setTimeout(
|
|
45
|
-
() => reject(new Error('Database ping timeout')),
|
|
46
|
-
maxTimeMS
|
|
47
|
-
)
|
|
43
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
44
|
+
setTimeout(() => reject(new Error('Database ping timeout')), maxTimeMS)
|
|
48
45
|
);
|
|
49
|
-
|
|
46
|
+
|
|
50
47
|
// Race between the database ping and the timeout
|
|
51
48
|
await Promise.race([
|
|
52
49
|
prisma.$queryRaw`SELECT 1`.catch(() => {
|
|
53
50
|
// For MongoDB, use runCommandRaw instead
|
|
54
51
|
return prisma.$runCommandRaw({ ping: 1 });
|
|
55
52
|
}),
|
|
56
|
-
timeoutPromise
|
|
53
|
+
timeoutPromise
|
|
57
54
|
]);
|
|
58
55
|
|
|
59
56
|
return Date.now() - pingStart;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Migration Status Repository - S3 Storage
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Infrastructure Layer - Hexagonal Architecture
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Stores migration status in S3 to avoid chicken-and-egg dependency on User/Process tables.
|
|
7
7
|
* Initial database migrations can't use Process table (requires User FK which doesn't exist yet).
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const {
|
|
11
|
-
S3Client,
|
|
12
|
-
PutObjectCommand,
|
|
13
|
-
GetObjectCommand,
|
|
14
|
-
} = require('@aws-sdk/client-s3');
|
|
10
|
+
const { S3Client, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
|
|
15
11
|
const { randomUUID } = require('crypto');
|
|
16
12
|
|
|
17
13
|
class MigrationStatusRepositoryS3 {
|
|
@@ -21,9 +17,7 @@ class MigrationStatusRepositoryS3 {
|
|
|
21
17
|
*/
|
|
22
18
|
constructor(bucketName, s3Client = null) {
|
|
23
19
|
this.bucketName = bucketName;
|
|
24
|
-
this.s3Client =
|
|
25
|
-
s3Client ||
|
|
26
|
-
new S3Client({ region: process.env.AWS_REGION || 'us-east-1' });
|
|
20
|
+
this.s3Client = s3Client || new S3Client({ region: process.env.AWS_REGION || 'us-east-1' });
|
|
27
21
|
}
|
|
28
22
|
|
|
29
23
|
/**
|
|
@@ -140,3 +134,4 @@ class MigrationStatusRepositoryS3 {
|
|
|
140
134
|
}
|
|
141
135
|
|
|
142
136
|
module.exports = { MigrationStatusRepositoryS3 };
|
|
137
|
+
|
|
@@ -11,8 +11,7 @@ class CheckDatabaseHealthUseCase {
|
|
|
11
11
|
* @returns {Promise<{status: string, state: string, responseTime?: number}>}
|
|
12
12
|
*/
|
|
13
13
|
async execute() {
|
|
14
|
-
const { stateName, isConnected } =
|
|
15
|
-
await this.repository.getDatabaseConnectionState();
|
|
14
|
+
const { stateName, isConnected } = await this.repository.getDatabaseConnectionState();
|
|
16
15
|
|
|
17
16
|
const result = {
|
|
18
17
|
status: isConnected ? 'healthy' : 'unhealthy',
|
|
@@ -27,4 +26,4 @@ class CheckDatabaseHealthUseCase {
|
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
module.exports = { CheckDatabaseHealthUseCase };
|
|
29
|
+
module.exports = { CheckDatabaseHealthUseCase };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Check Database State Use Case
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Domain logic for checking database state (pending migrations, errors, etc).
|
|
5
5
|
* Does NOT trigger migrations, just reports current state.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* Architecture: Hexagonal/Clean
|
|
8
8
|
* - Use Case (Domain Layer)
|
|
9
9
|
* - Depends on prismaRunner (Infrastructure abstraction)
|
|
@@ -31,7 +31,7 @@ class CheckDatabaseStateUseCase {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Execute check migration status
|
|
34
|
-
*
|
|
34
|
+
*
|
|
35
35
|
* @param {string} dbType - Database type (postgresql, mongodb, or documentdb)
|
|
36
36
|
* @param {string} stage - Deployment stage (default: 'production')
|
|
37
37
|
* @returns {Promise<Object>} Migration status
|
|
@@ -43,9 +43,7 @@ class CheckDatabaseStateUseCase {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
if (!['postgresql', 'mongodb', 'documentdb'].includes(dbType)) {
|
|
46
|
-
throw new ValidationError(
|
|
47
|
-
'dbType must be postgresql, mongodb, or documentdb'
|
|
48
|
-
);
|
|
46
|
+
throw new ValidationError('dbType must be postgresql, mongodb, or documentdb');
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
console.log(`Checking migration status for ${dbType} in ${stage}`);
|
|
@@ -64,13 +62,12 @@ class CheckDatabaseStateUseCase {
|
|
|
64
62
|
// Add error if present
|
|
65
63
|
if (state.error) {
|
|
66
64
|
response.error = state.error;
|
|
67
|
-
response.recommendation =
|
|
68
|
-
'Run POST /db-migrate to initialize database';
|
|
65
|
+
response.recommendation = 'Run POST /admin/db-migrate to initialize database';
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
// Add recommendation if migrations pending
|
|
72
69
|
if (!state.upToDate && state.pendingMigrations > 0) {
|
|
73
|
-
response.recommendation = `Run POST /db-migrate to apply ${state.pendingMigrations} pending migration(s)`;
|
|
70
|
+
response.recommendation = `Run POST /admin/db-migrate to apply ${state.pendingMigrations} pending migration(s)`;
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
return response;
|
|
@@ -81,3 +78,4 @@ module.exports = {
|
|
|
81
78
|
CheckDatabaseStateUseCase,
|
|
82
79
|
ValidationError,
|
|
83
80
|
};
|
|
81
|
+
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Get Database State Via Worker Use Case
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Domain logic for getting database state by invoking the worker Lambda.
|
|
5
5
|
* This use case delegates to the worker Lambda which has Prisma CLI installed,
|
|
6
6
|
* keeping the router Lambda lightweight.
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* Architecture: Hexagonal/Clean
|
|
9
9
|
* - Use Case (Domain Layer)
|
|
10
10
|
* - Depends on LambdaInvoker (Infrastructure abstraction)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Domain Use Case: Get database state by invoking worker Lambda
|
|
16
|
-
*
|
|
16
|
+
*
|
|
17
17
|
* This use case delegates database state checking to the worker Lambda,
|
|
18
18
|
* which has Prisma CLI installed. Keeps the router Lambda lightweight.
|
|
19
19
|
*/
|
|
@@ -36,29 +36,26 @@ class GetDatabaseStateViaWorkerUseCase {
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Execute database state check via worker Lambda
|
|
39
|
-
*
|
|
39
|
+
*
|
|
40
40
|
* @param {string} stage - Deployment stage (prod, dev, etc)
|
|
41
41
|
* @returns {Promise<Object>} Database state result
|
|
42
42
|
*/
|
|
43
43
|
async execute(stage = 'production') {
|
|
44
44
|
const dbType = process.env.DB_TYPE || 'postgresql';
|
|
45
45
|
|
|
46
|
-
console.log(
|
|
47
|
-
`Invoking worker Lambda to check database state: ${this.workerFunctionName}`
|
|
48
|
-
);
|
|
46
|
+
console.log(`Invoking worker Lambda to check database state: ${this.workerFunctionName}`);
|
|
49
47
|
|
|
50
48
|
// Invoke worker Lambda with checkStatus action
|
|
51
|
-
const result = await this.lambdaInvoker.invoke(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
stage,
|
|
57
|
-
}
|
|
58
|
-
);
|
|
49
|
+
const result = await this.lambdaInvoker.invoke(this.workerFunctionName, {
|
|
50
|
+
action: 'checkStatus',
|
|
51
|
+
dbType,
|
|
52
|
+
stage,
|
|
53
|
+
});
|
|
59
54
|
|
|
60
55
|
return result;
|
|
61
56
|
}
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
module.exports = { GetDatabaseStateViaWorkerUseCase };
|
|
60
|
+
|
|
61
|
+
|
|
@@ -39,10 +39,7 @@ class GetMigrationStatusUseCase {
|
|
|
39
39
|
|
|
40
40
|
// Get migration status from S3
|
|
41
41
|
try {
|
|
42
|
-
const migrationStatus = await this.migrationStatusRepository.get(
|
|
43
|
-
migrationId,
|
|
44
|
-
effectiveStage
|
|
45
|
-
);
|
|
42
|
+
const migrationStatus = await this.migrationStatusRepository.get(migrationId, effectiveStage);
|
|
46
43
|
return migrationStatus;
|
|
47
44
|
} catch (error) {
|
|
48
45
|
if (error.message.includes('not found')) {
|
|
@@ -93,3 +90,4 @@ module.exports = {
|
|
|
93
90
|
ValidationError,
|
|
94
91
|
NotFoundError,
|
|
95
92
|
};
|
|
93
|
+
|
|
@@ -38,22 +38,12 @@ class RunDatabaseMigrationUseCase {
|
|
|
38
38
|
this._validateParams({ dbType, stage });
|
|
39
39
|
|
|
40
40
|
// Step 1: Generate Prisma client
|
|
41
|
-
const generateResult = await this.prismaRunner.runPrismaGenerate(
|
|
42
|
-
dbType,
|
|
43
|
-
verbose
|
|
44
|
-
);
|
|
41
|
+
const generateResult = await this.prismaRunner.runPrismaGenerate(dbType, verbose);
|
|
45
42
|
|
|
46
43
|
if (!generateResult.success) {
|
|
47
44
|
throw new MigrationError(
|
|
48
|
-
`Failed to generate Prisma client: ${
|
|
49
|
-
|
|
50
|
-
}`,
|
|
51
|
-
{
|
|
52
|
-
dbType,
|
|
53
|
-
stage,
|
|
54
|
-
step: 'generate',
|
|
55
|
-
output: generateResult.output,
|
|
56
|
-
}
|
|
45
|
+
`Failed to generate Prisma client: ${generateResult.error || 'Unknown error'}`,
|
|
46
|
+
{ dbType, stage, step: 'generate', output: generateResult.output }
|
|
57
47
|
);
|
|
58
48
|
}
|
|
59
49
|
|
|
@@ -63,45 +53,23 @@ class RunDatabaseMigrationUseCase {
|
|
|
63
53
|
|
|
64
54
|
if (dbType === 'postgresql') {
|
|
65
55
|
migrationCommand = this.prismaRunner.getMigrationCommand(stage);
|
|
66
|
-
migrationResult = await this.prismaRunner.runPrismaMigrate(
|
|
67
|
-
migrationCommand,
|
|
68
|
-
verbose
|
|
69
|
-
);
|
|
56
|
+
migrationResult = await this.prismaRunner.runPrismaMigrate(migrationCommand, verbose);
|
|
70
57
|
|
|
71
58
|
if (!migrationResult.success) {
|
|
72
59
|
throw new MigrationError(
|
|
73
|
-
`PostgreSQL migration failed: ${
|
|
74
|
-
|
|
75
|
-
}`,
|
|
76
|
-
{
|
|
77
|
-
dbType,
|
|
78
|
-
stage,
|
|
79
|
-
command: migrationCommand,
|
|
80
|
-
step: 'migrate',
|
|
81
|
-
output: migrationResult.output,
|
|
82
|
-
}
|
|
60
|
+
`PostgreSQL migration failed: ${migrationResult.error || 'Unknown error'}`,
|
|
61
|
+
{ dbType, stage, command: migrationCommand, step: 'migrate', output: migrationResult.output }
|
|
83
62
|
);
|
|
84
63
|
}
|
|
85
64
|
} else if (dbType === 'mongodb' || dbType === 'documentdb') {
|
|
86
65
|
migrationCommand = 'db push';
|
|
87
66
|
// Use non-interactive mode for automated/Lambda environments
|
|
88
|
-
migrationResult = await this.prismaRunner.runPrismaDbPush(
|
|
89
|
-
verbose,
|
|
90
|
-
true
|
|
91
|
-
);
|
|
67
|
+
migrationResult = await this.prismaRunner.runPrismaDbPush(verbose, true);
|
|
92
68
|
|
|
93
69
|
if (!migrationResult.success) {
|
|
94
70
|
throw new MigrationError(
|
|
95
|
-
`Mongo-compatible push failed: ${
|
|
96
|
-
|
|
97
|
-
}`,
|
|
98
|
-
{
|
|
99
|
-
dbType,
|
|
100
|
-
stage,
|
|
101
|
-
command: migrationCommand,
|
|
102
|
-
step: 'push',
|
|
103
|
-
output: migrationResult.output,
|
|
104
|
-
}
|
|
71
|
+
`Mongo-compatible push failed: ${migrationResult.error || 'Unknown error'}`,
|
|
72
|
+
{ dbType, stage, command: migrationCommand, step: 'push', output: migrationResult.output }
|
|
105
73
|
);
|
|
106
74
|
}
|
|
107
75
|
} else {
|
|
@@ -88,9 +88,9 @@ class TestEncryptionUseCase {
|
|
|
88
88
|
return {
|
|
89
89
|
externalId: 'test-encryption-entity',
|
|
90
90
|
data: {
|
|
91
|
-
access_token: testData.testSecret,
|
|
91
|
+
access_token: testData.testSecret, // Encrypted field
|
|
92
92
|
refresh_token: testData.nestedSecret?.value, // Encrypted field
|
|
93
|
-
domain: testData.normalField,
|
|
93
|
+
domain: testData.normalField, // Not encrypted
|
|
94
94
|
},
|
|
95
95
|
};
|
|
96
96
|
}
|
|
@@ -203,7 +203,8 @@ class TestEncryptionUseCase {
|
|
|
203
203
|
) {
|
|
204
204
|
return {
|
|
205
205
|
status: 'enabled',
|
|
206
|
-
testResult:
|
|
206
|
+
testResult:
|
|
207
|
+
'Encryption and decryption verified successfully',
|
|
207
208
|
encryptionWorks: true,
|
|
208
209
|
};
|
|
209
210
|
}
|
|
@@ -249,4 +250,4 @@ class TestEncryptionUseCase {
|
|
|
249
250
|
}
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
module.exports = { TestEncryptionUseCase };
|
|
253
|
+
module.exports = { TestEncryptionUseCase };
|