@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
|
@@ -76,11 +76,10 @@ class GetUserFromXFriggHeaders {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// Auto-link the users
|
|
79
|
-
individualUserData =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
);
|
|
79
|
+
individualUserData = await this.userRepository.linkIndividualToOrganization(
|
|
80
|
+
individualUserData.id,
|
|
81
|
+
organizationUserData.id
|
|
82
|
+
);
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
|
|
@@ -90,13 +89,12 @@ class GetUserFromXFriggHeaders {
|
|
|
90
89
|
appUserId &&
|
|
91
90
|
this.userConfig.individualUserRequired !== false
|
|
92
91
|
) {
|
|
93
|
-
individualUserData =
|
|
94
|
-
{
|
|
92
|
+
individualUserData =
|
|
93
|
+
await this.userRepository.createIndividualUser({
|
|
95
94
|
appUserId,
|
|
96
95
|
username: `app-user-${appUserId}`,
|
|
97
96
|
email: `${appUserId}@app.local`,
|
|
98
|
-
}
|
|
99
|
-
);
|
|
97
|
+
});
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
if (
|
|
@@ -111,11 +109,10 @@ class GetUserFromXFriggHeaders {
|
|
|
111
109
|
|
|
112
110
|
// Link individual user to newly created org user if individual exists
|
|
113
111
|
if (individualUserData && organizationUserData) {
|
|
114
|
-
individualUserData =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
);
|
|
112
|
+
individualUserData = await this.userRepository.linkIndividualToOrganization(
|
|
113
|
+
individualUserData.id,
|
|
114
|
+
organizationUserData.id
|
|
115
|
+
);
|
|
119
116
|
}
|
|
120
117
|
}
|
|
121
118
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const Boom = require('@hapi/boom');
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
RequiredPropertyError,
|
|
4
|
+
} = require('../../errors');
|
|
3
5
|
const { User } = require('../user');
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -91,11 +93,11 @@ class LoginUser {
|
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
|
|
94
97
|
if (this.userConfig.organizationUserRequired) {
|
|
98
|
+
|
|
95
99
|
const organizationUserData =
|
|
96
|
-
await this.userRepository.findOrganizationUserByAppOrgId(
|
|
97
|
-
appOrgId
|
|
98
|
-
);
|
|
100
|
+
await this.userRepository.findOrganizationUserByAppOrgId(appOrgId);
|
|
99
101
|
|
|
100
102
|
if (!organizationUserData) {
|
|
101
103
|
throw Boom.unauthorized(`org user ${appOrgId} not found`);
|
|
@@ -113,10 +115,8 @@ class LoginUser {
|
|
|
113
115
|
return organizationUser;
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
throw new Error(
|
|
117
|
-
'User configuration must require either individualUserRequired or organizationUserRequired'
|
|
118
|
-
);
|
|
118
|
+
throw new Error('User configuration must require either individualUserRequired or organizationUserRequired');
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
module.exports = { LoginUser };
|
|
122
|
+
module.exports = { LoginUser };
|
package/user/user.js
CHANGED
|
@@ -14,14 +14,7 @@ class User {
|
|
|
14
14
|
* @param {boolean} [individualUserRequired=true] - Whether the user is required to have an individual user.
|
|
15
15
|
* @param {boolean} [organizationUserRequired=false] - Whether the user is required to have an organization user.
|
|
16
16
|
*/
|
|
17
|
-
constructor(
|
|
18
|
-
individualUser = null,
|
|
19
|
-
organizationUser = null,
|
|
20
|
-
usePassword = false,
|
|
21
|
-
primary = 'individual',
|
|
22
|
-
individualUserRequired = true,
|
|
23
|
-
organizationUserRequired = false
|
|
24
|
-
) {
|
|
17
|
+
constructor(individualUser = null, organizationUser = null, usePassword = false, primary = 'individual', individualUserRequired = true, organizationUserRequired = false) {
|
|
25
18
|
this.individualUser = individualUser;
|
|
26
19
|
this.organizationUser = organizationUser;
|
|
27
20
|
this.usePassword = usePassword;
|
|
@@ -116,19 +109,12 @@ class User {
|
|
|
116
109
|
}
|
|
117
110
|
|
|
118
111
|
// When primary is 'organization', also check linked individual user
|
|
119
|
-
if (
|
|
120
|
-
this.config.primary === 'organization' &&
|
|
121
|
-
userIdStr === individualId
|
|
122
|
-
) {
|
|
112
|
+
if (this.config.primary === 'organization' && userIdStr === individualId) {
|
|
123
113
|
return true;
|
|
124
114
|
}
|
|
125
115
|
|
|
126
116
|
// When primary is 'individual', also check linked organization user if required
|
|
127
|
-
if (
|
|
128
|
-
this.config.primary === 'individual' &&
|
|
129
|
-
this.config.organizationUserRequired &&
|
|
130
|
-
userIdStr === organizationId
|
|
131
|
-
) {
|
|
117
|
+
if (this.config.primary === 'individual' && this.config.organizationUserRequired && userIdStr === organizationId) {
|
|
132
118
|
return true;
|
|
133
119
|
}
|
|
134
120
|
|
|
@@ -136,4 +122,4 @@ class User {
|
|
|
136
122
|
}
|
|
137
123
|
}
|
|
138
124
|
|
|
139
|
-
module.exports = { User };
|
|
125
|
+
module.exports = { User };
|
package/utils/backend-path.js
CHANGED
|
@@ -4,7 +4,7 @@ const PACKAGE_JSON = 'package.json';
|
|
|
4
4
|
|
|
5
5
|
function findNearestBackendPackageJson() {
|
|
6
6
|
let currentDir = process.cwd();
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
// First check if we're in production by looking for package.json in the current directory
|
|
9
9
|
const rootPackageJson = path.join(currentDir, PACKAGE_JSON);
|
|
10
10
|
if (fs.existsSync(rootPackageJson)) {
|
|
@@ -35,4 +35,4 @@ function validateBackendPath(backendPath) {
|
|
|
35
35
|
module.exports = {
|
|
36
36
|
findNearestBackendPackageJson,
|
|
37
37
|
validateBackendPath,
|
|
38
|
-
};
|
|
38
|
+
};
|
package/utils/index.js
CHANGED
|
@@ -29,21 +29,13 @@ class WebsocketConnectionRepositoryDocumentDB extends WebsocketConnectionReposit
|
|
|
29
29
|
createdAt: now,
|
|
30
30
|
updatedAt: now,
|
|
31
31
|
};
|
|
32
|
-
const insertedId = await insertOne(
|
|
33
|
-
|
|
34
|
-
'WebsocketConnection',
|
|
35
|
-
document
|
|
36
|
-
);
|
|
37
|
-
const created = await findOne(this.prisma, 'WebsocketConnection', {
|
|
38
|
-
_id: insertedId,
|
|
39
|
-
});
|
|
32
|
+
const insertedId = await insertOne(this.prisma, 'WebsocketConnection', document);
|
|
33
|
+
const created = await findOne(this.prisma, 'WebsocketConnection', { _id: insertedId });
|
|
40
34
|
return this._mapConnection(created);
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
async deleteConnection(connectionId) {
|
|
44
|
-
const result = await deleteOne(this.prisma, 'WebsocketConnection', {
|
|
45
|
-
connectionId,
|
|
46
|
-
});
|
|
38
|
+
const result = await deleteOne(this.prisma, 'WebsocketConnection', { connectionId });
|
|
47
39
|
const deleted = result?.n ?? 0;
|
|
48
40
|
return { acknowledged: true, deletedCount: deleted };
|
|
49
41
|
}
|
|
@@ -74,10 +66,7 @@ class WebsocketConnectionRepositoryDocumentDB extends WebsocketConnectionReposit
|
|
|
74
66
|
});
|
|
75
67
|
await apigwManagementApi.send(command);
|
|
76
68
|
} catch (error) {
|
|
77
|
-
if (
|
|
78
|
-
error.statusCode === 410 ||
|
|
79
|
-
error.$metadata?.httpStatusCode === 410
|
|
80
|
-
) {
|
|
69
|
+
if (error.statusCode === 410 || error.$metadata?.httpStatusCode === 410) {
|
|
81
70
|
console.log(`Stale connection ${conn.connectionId}`);
|
|
82
71
|
await deleteMany(this.prisma, 'WebsocketConnection', {
|
|
83
72
|
connectionId: conn.connectionId,
|
|
@@ -91,18 +80,14 @@ class WebsocketConnectionRepositoryDocumentDB extends WebsocketConnectionReposit
|
|
|
91
80
|
}
|
|
92
81
|
|
|
93
82
|
async findConnection(connectionId) {
|
|
94
|
-
const doc = await findOne(this.prisma, 'WebsocketConnection', {
|
|
95
|
-
connectionId,
|
|
96
|
-
});
|
|
83
|
+
const doc = await findOne(this.prisma, 'WebsocketConnection', { connectionId });
|
|
97
84
|
return doc ? this._mapConnection(doc) : null;
|
|
98
85
|
}
|
|
99
86
|
|
|
100
87
|
async findConnectionById(id) {
|
|
101
88
|
const objectId = toObjectId(id);
|
|
102
89
|
if (!objectId) return null;
|
|
103
|
-
const doc = await findOne(this.prisma, 'WebsocketConnection', {
|
|
104
|
-
_id: objectId,
|
|
105
|
-
});
|
|
90
|
+
const doc = await findOne(this.prisma, 'WebsocketConnection', { _id: objectId });
|
|
106
91
|
return doc ? this._mapConnection(doc) : null;
|
|
107
92
|
}
|
|
108
93
|
|
|
@@ -130,3 +115,5 @@ class WebsocketConnectionRepositoryDocumentDB extends WebsocketConnectionReposit
|
|
|
130
115
|
}
|
|
131
116
|
|
|
132
117
|
module.exports = { WebsocketConnectionRepositoryDocumentDB };
|
|
118
|
+
|
|
119
|
+
|
|
@@ -77,10 +77,9 @@ class WebsocketConnectionRepositoryMongo extends WebsocketConnectionRepositoryIn
|
|
|
77
77
|
return connections.map((conn) => ({
|
|
78
78
|
connectionId: conn.connectionId,
|
|
79
79
|
send: async (data) => {
|
|
80
|
-
const apigwManagementApi =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
});
|
|
80
|
+
const apigwManagementApi = new ApiGatewayManagementApiClient({
|
|
81
|
+
endpoint: process.env.WEBSOCKET_API_ENDPOINT,
|
|
82
|
+
});
|
|
84
83
|
|
|
85
84
|
try {
|
|
86
85
|
const command = new PostToConnectionCommand({
|
|
@@ -89,10 +88,7 @@ class WebsocketConnectionRepositoryMongo extends WebsocketConnectionRepositoryIn
|
|
|
89
88
|
});
|
|
90
89
|
await apigwManagementApi.send(command);
|
|
91
90
|
} catch (error) {
|
|
92
|
-
if (
|
|
93
|
-
error.statusCode === 410 ||
|
|
94
|
-
error.$metadata?.httpStatusCode === 410
|
|
95
|
-
) {
|
|
91
|
+
if (error.statusCode === 410 || error.$metadata?.httpStatusCode === 410) {
|
|
96
92
|
console.log(
|
|
97
93
|
`Stale connection ${conn.connectionId}`
|
|
98
94
|
);
|
|
@@ -111,10 +111,9 @@ class WebsocketConnectionRepositoryPostgres extends WebsocketConnectionRepositor
|
|
|
111
111
|
return connections.map((conn) => ({
|
|
112
112
|
connectionId: conn.connectionId,
|
|
113
113
|
send: async (data) => {
|
|
114
|
-
const apigwManagementApi =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
114
|
+
const apigwManagementApi = new ApiGatewayManagementApiClient({
|
|
115
|
+
endpoint: process.env.WEBSOCKET_API_ENDPOINT,
|
|
116
|
+
});
|
|
118
117
|
|
|
119
118
|
try {
|
|
120
119
|
const command = new PostToConnectionCommand({
|
|
@@ -123,10 +122,7 @@ class WebsocketConnectionRepositoryPostgres extends WebsocketConnectionRepositor
|
|
|
123
122
|
});
|
|
124
123
|
await apigwManagementApi.send(command);
|
|
125
124
|
} catch (error) {
|
|
126
|
-
if (
|
|
127
|
-
error.statusCode === 410 ||
|
|
128
|
-
error.$metadata?.httpStatusCode === 410
|
|
129
|
-
) {
|
|
125
|
+
if (error.statusCode === 410 || error.$metadata?.httpStatusCode === 410) {
|
|
130
126
|
console.log(
|
|
131
127
|
`Stale connection ${conn.connectionId}`
|
|
132
128
|
);
|
|
@@ -82,10 +82,9 @@ class WebsocketConnectionRepository extends WebsocketConnectionRepositoryInterfa
|
|
|
82
82
|
return connections.map((conn) => ({
|
|
83
83
|
connectionId: conn.connectionId,
|
|
84
84
|
send: async (data) => {
|
|
85
|
-
const apigwManagementApi =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
85
|
+
const apigwManagementApi = new ApiGatewayManagementApiClient({
|
|
86
|
+
endpoint: process.env.WEBSOCKET_API_ENDPOINT,
|
|
87
|
+
});
|
|
89
88
|
|
|
90
89
|
try {
|
|
91
90
|
const command = new PostToConnectionCommand({
|
|
@@ -94,10 +93,7 @@ class WebsocketConnectionRepository extends WebsocketConnectionRepositoryInterfa
|
|
|
94
93
|
});
|
|
95
94
|
await apigwManagementApi.send(command);
|
|
96
95
|
} catch (error) {
|
|
97
|
-
if (
|
|
98
|
-
error.statusCode === 410 ||
|
|
99
|
-
error.$metadata?.httpStatusCode === 410
|
|
100
|
-
) {
|
|
96
|
+
if (error.statusCode === 410 || error.$metadata?.httpStatusCode === 410) {
|
|
101
97
|
console.log(
|
|
102
98
|
`Stale connection ${conn.connectionId}`
|
|
103
99
|
);
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Admin API Key Repository Interface
|
|
3
|
-
* Abstract base class defining the contract for admin API key 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
|
-
* Admin API keys provide authentication for script execution and management endpoints.
|
|
11
|
-
* Keys are bcrypt-hashed for security and support scoping and expiration.
|
|
12
|
-
*
|
|
13
|
-
* @abstract
|
|
14
|
-
*/
|
|
15
|
-
class AdminApiKeyRepositoryInterface {
|
|
16
|
-
/**
|
|
17
|
-
* Create a new admin API key
|
|
18
|
-
*
|
|
19
|
-
* @param {Object} params - API key creation parameters
|
|
20
|
-
* @param {string} params.name - Human-readable name for the key
|
|
21
|
-
* @param {string} params.keyHash - bcrypt hash of the raw key
|
|
22
|
-
* @param {string} params.keyLast4 - Last 4 characters of key (for display)
|
|
23
|
-
* @param {string[]} params.scopes - Array of permission scopes (e.g., ['scripts:execute', 'scripts:read'])
|
|
24
|
-
* @param {Date} [params.expiresAt] - Optional expiration date
|
|
25
|
-
* @param {string} [params.createdBy] - Optional identifier of creator (user/admin)
|
|
26
|
-
* @returns {Promise<Object>} The created API key record
|
|
27
|
-
* @abstract
|
|
28
|
-
*/
|
|
29
|
-
async createApiKey({
|
|
30
|
-
name,
|
|
31
|
-
keyHash,
|
|
32
|
-
keyLast4,
|
|
33
|
-
scopes,
|
|
34
|
-
expiresAt,
|
|
35
|
-
createdBy,
|
|
36
|
-
}) {
|
|
37
|
-
throw new Error('Method createApiKey must be implemented by subclass');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Find an API key by its bcrypt hash
|
|
42
|
-
* Used during authentication to validate incoming keys
|
|
43
|
-
*
|
|
44
|
-
* @param {string} keyHash - The bcrypt hash to search for
|
|
45
|
-
* @returns {Promise<Object|null>} The API key record or null if not found
|
|
46
|
-
* @abstract
|
|
47
|
-
*/
|
|
48
|
-
async findApiKeyByHash(keyHash) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
'Method findApiKeyByHash must be implemented by subclass'
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Find an API key by its ID
|
|
56
|
-
*
|
|
57
|
-
* @param {string|number} id - The API key ID
|
|
58
|
-
* @returns {Promise<Object|null>} The API key record or null if not found
|
|
59
|
-
* @abstract
|
|
60
|
-
*/
|
|
61
|
-
async findApiKeyById(id) {
|
|
62
|
-
throw new Error(
|
|
63
|
-
'Method findApiKeyById must be implemented by subclass'
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Find all active (non-expired, non-deactivated) API keys
|
|
69
|
-
* Used during authentication to check all valid keys
|
|
70
|
-
*
|
|
71
|
-
* @returns {Promise<Array>} Array of active API key records
|
|
72
|
-
* @abstract
|
|
73
|
-
*/
|
|
74
|
-
async findActiveApiKeys() {
|
|
75
|
-
throw new Error(
|
|
76
|
-
'Method findActiveApiKeys must be implemented by subclass'
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Update the lastUsedAt timestamp for an API key
|
|
82
|
-
* Called after successful authentication
|
|
83
|
-
*
|
|
84
|
-
* @param {string|number} id - The API key ID
|
|
85
|
-
* @returns {Promise<Object>} Updated API key record
|
|
86
|
-
* @abstract
|
|
87
|
-
*/
|
|
88
|
-
async updateApiKeyLastUsed(id) {
|
|
89
|
-
throw new Error(
|
|
90
|
-
'Method updateApiKeyLastUsed must be implemented by subclass'
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Deactivate an API key (soft delete)
|
|
96
|
-
* Sets isActive to false, preventing further use
|
|
97
|
-
*
|
|
98
|
-
* @param {string|number} id - The API key ID
|
|
99
|
-
* @returns {Promise<Object>} Updated API key record
|
|
100
|
-
* @abstract
|
|
101
|
-
*/
|
|
102
|
-
async deactivateApiKey(id) {
|
|
103
|
-
throw new Error(
|
|
104
|
-
'Method deactivateApiKey must be implemented by subclass'
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Delete an API key (hard delete)
|
|
110
|
-
* Permanently removes the key from the database
|
|
111
|
-
*
|
|
112
|
-
* @param {string|number} id - The API key ID
|
|
113
|
-
* @returns {Promise<Object>} Deletion result
|
|
114
|
-
* @abstract
|
|
115
|
-
*/
|
|
116
|
-
async deleteApiKey(id) {
|
|
117
|
-
throw new Error('Method deleteApiKey must be implemented by subclass');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
module.exports = { AdminApiKeyRepositoryInterface };
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
const { prisma } = require('../../database/prisma');
|
|
2
|
-
const {
|
|
3
|
-
AdminApiKeyRepositoryInterface,
|
|
4
|
-
} = require('./admin-api-key-repository-interface');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* MongoDB Admin API Key Repository Adapter
|
|
8
|
-
* Handles admin API key persistence using Prisma with MongoDB
|
|
9
|
-
*
|
|
10
|
-
* MongoDB-specific characteristics:
|
|
11
|
-
* - IDs are strings with @db.ObjectId
|
|
12
|
-
* - Supports bcrypt hashed keys
|
|
13
|
-
* - Scopes stored as String[] array
|
|
14
|
-
*/
|
|
15
|
-
class AdminApiKeyRepositoryMongo extends AdminApiKeyRepositoryInterface {
|
|
16
|
-
constructor() {
|
|
17
|
-
super();
|
|
18
|
-
this.prisma = prisma;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create a new admin API key
|
|
23
|
-
*
|
|
24
|
-
* @param {Object} params - API key creation parameters
|
|
25
|
-
* @param {string} params.name - Human-readable name for the key
|
|
26
|
-
* @param {string} params.keyHash - bcrypt hash of the raw key
|
|
27
|
-
* @param {string} params.keyLast4 - Last 4 characters of key (for display)
|
|
28
|
-
* @param {string[]} params.scopes - Array of permission scopes
|
|
29
|
-
* @param {Date} [params.expiresAt] - Optional expiration date
|
|
30
|
-
* @param {string} [params.createdBy] - Optional identifier of creator
|
|
31
|
-
* @returns {Promise<Object>} The created API key record
|
|
32
|
-
*/
|
|
33
|
-
async createApiKey({
|
|
34
|
-
name,
|
|
35
|
-
keyHash,
|
|
36
|
-
keyLast4,
|
|
37
|
-
scopes,
|
|
38
|
-
expiresAt,
|
|
39
|
-
createdBy,
|
|
40
|
-
}) {
|
|
41
|
-
const apiKey = await this.prisma.adminApiKey.create({
|
|
42
|
-
data: {
|
|
43
|
-
name,
|
|
44
|
-
keyHash,
|
|
45
|
-
keyLast4,
|
|
46
|
-
scopes,
|
|
47
|
-
expiresAt,
|
|
48
|
-
createdBy,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
return apiKey;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Find an API key by its bcrypt hash
|
|
57
|
-
* Used during authentication to validate incoming keys
|
|
58
|
-
*
|
|
59
|
-
* @param {string} keyHash - The bcrypt hash to search for
|
|
60
|
-
* @returns {Promise<Object|null>} The API key record or null if not found
|
|
61
|
-
*/
|
|
62
|
-
async findApiKeyByHash(keyHash) {
|
|
63
|
-
const apiKey = await this.prisma.adminApiKey.findUnique({
|
|
64
|
-
where: { keyHash },
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
return apiKey;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Find an API key by its ID
|
|
72
|
-
*
|
|
73
|
-
* @param {string} id - The API key ID (MongoDB ObjectId as string)
|
|
74
|
-
* @returns {Promise<Object|null>} The API key record or null if not found
|
|
75
|
-
*/
|
|
76
|
-
async findApiKeyById(id) {
|
|
77
|
-
const apiKey = await this.prisma.adminApiKey.findUnique({
|
|
78
|
-
where: { id },
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return apiKey;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Find all active (non-expired, non-deactivated) API keys
|
|
86
|
-
* Used during authentication to check all valid keys
|
|
87
|
-
*
|
|
88
|
-
* @returns {Promise<Array>} Array of active API key records
|
|
89
|
-
*/
|
|
90
|
-
async findActiveApiKeys() {
|
|
91
|
-
const now = new Date();
|
|
92
|
-
const apiKeys = await this.prisma.adminApiKey.findMany({
|
|
93
|
-
where: {
|
|
94
|
-
isActive: true,
|
|
95
|
-
OR: [{ expiresAt: null }, { expiresAt: { gt: now } }],
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return apiKeys;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Update the lastUsedAt timestamp for an API key
|
|
104
|
-
* Called after successful authentication
|
|
105
|
-
*
|
|
106
|
-
* @param {string} id - The API key ID
|
|
107
|
-
* @returns {Promise<Object>} Updated API key record
|
|
108
|
-
*/
|
|
109
|
-
async updateApiKeyLastUsed(id) {
|
|
110
|
-
const apiKey = await this.prisma.adminApiKey.update({
|
|
111
|
-
where: { id },
|
|
112
|
-
data: {
|
|
113
|
-
lastUsedAt: new Date(),
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
return apiKey;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Deactivate an API key (soft delete)
|
|
122
|
-
* Sets isActive to false, preventing further use
|
|
123
|
-
*
|
|
124
|
-
* @param {string} id - The API key ID
|
|
125
|
-
* @returns {Promise<Object>} Updated API key record
|
|
126
|
-
*/
|
|
127
|
-
async deactivateApiKey(id) {
|
|
128
|
-
const apiKey = await this.prisma.adminApiKey.update({
|
|
129
|
-
where: { id },
|
|
130
|
-
data: {
|
|
131
|
-
isActive: false,
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
return apiKey;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Delete an API key (hard delete)
|
|
140
|
-
* Permanently removes the key from the database
|
|
141
|
-
*
|
|
142
|
-
* @param {string} id - The API key ID
|
|
143
|
-
* @returns {Promise<Object>} Deletion result
|
|
144
|
-
*/
|
|
145
|
-
async deleteApiKey(id) {
|
|
146
|
-
await this.prisma.adminApiKey.delete({
|
|
147
|
-
where: { id },
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Return Mongoose-compatible result
|
|
151
|
-
return { acknowledged: true, deletedCount: 1 };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
module.exports = { AdminApiKeyRepositoryMongo };
|