@friggframework/core 2.0.0-next.54 → 2.0.0-next.55
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/application/commands/credential-commands.js +1 -1
- package/core/create-handler.js +12 -0
- package/credential/repositories/credential-repository-documentdb.js +81 -77
- package/credential/repositories/credential-repository-mongo.js +16 -54
- package/credential/repositories/credential-repository-postgres.js +14 -41
- package/credential/use-cases/get-credential-for-user.js +7 -3
- package/database/encryption/README.md +0 -1
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +2 -1
- package/errors/index.js +2 -0
- package/integrations/integration-router.js +6 -6
- package/integrations/repositories/integration-mapping-repository-documentdb.js +178 -33
- package/integrations/repositories/integration-repository-documentdb.js +21 -0
- package/integrations/repositories/process-repository-documentdb.js +143 -41
- package/package.json +5 -5
- package/token/repositories/token-repository-documentdb.js +20 -8
- package/token/repositories/token-repository-mongo.js +10 -3
- package/token/repositories/token-repository-postgres.js +10 -3
- package/user/repositories/user-repository-documentdb.js +177 -37
- package/user/repositories/user-repository-mongo.js +3 -2
- package/user/repositories/user-repository-postgres.js +3 -2
- package/user/use-cases/login-user.js +1 -1
|
@@ -8,17 +8,23 @@ const {
|
|
|
8
8
|
updateOne,
|
|
9
9
|
deleteOne,
|
|
10
10
|
} = require('../../database/documentdb-utils');
|
|
11
|
-
const {
|
|
11
|
+
const {
|
|
12
|
+
ProcessRepositoryInterface,
|
|
13
|
+
} = require('./process-repository-interface');
|
|
14
|
+
const {
|
|
15
|
+
DocumentDBEncryptionService,
|
|
16
|
+
} = require('../../database/documentdb-encryption-service');
|
|
12
17
|
|
|
13
18
|
class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
|
|
14
19
|
constructor() {
|
|
15
20
|
super();
|
|
16
21
|
this.prisma = prisma;
|
|
22
|
+
this.encryptionService = new DocumentDBEncryptionService();
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
async create(processData) {
|
|
20
26
|
const now = new Date();
|
|
21
|
-
const
|
|
27
|
+
const plainDocument = {
|
|
22
28
|
userId: toObjectId(processData.userId),
|
|
23
29
|
integrationId: toObjectId(processData.integrationId),
|
|
24
30
|
name: processData.name,
|
|
@@ -26,45 +32,127 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
|
|
|
26
32
|
state: processData.state || 'INITIALIZING',
|
|
27
33
|
context: processData.context || {},
|
|
28
34
|
results: processData.results || {},
|
|
29
|
-
childProcesses: (processData.childProcesses || [])
|
|
30
|
-
|
|
35
|
+
childProcesses: (processData.childProcesses || [])
|
|
36
|
+
.map((id) => toObjectId(id))
|
|
37
|
+
.filter(Boolean),
|
|
38
|
+
parentProcessId: processData.parentProcessId
|
|
39
|
+
? toObjectId(processData.parentProcessId)
|
|
40
|
+
: null,
|
|
31
41
|
createdAt: now,
|
|
32
42
|
updatedAt: now,
|
|
33
43
|
};
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
44
|
+
|
|
45
|
+
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
46
|
+
'Process',
|
|
47
|
+
plainDocument
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const insertedId = await insertOne(
|
|
51
|
+
this.prisma,
|
|
52
|
+
'Process',
|
|
53
|
+
encryptedDocument
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const created = await findOne(this.prisma, 'Process', {
|
|
57
|
+
_id: insertedId,
|
|
58
|
+
});
|
|
59
|
+
if (!created) {
|
|
60
|
+
console.error(
|
|
61
|
+
'[ProcessRepositoryDocumentDB] Process not found after insert',
|
|
62
|
+
{
|
|
63
|
+
insertedId: fromObjectId(insertedId),
|
|
64
|
+
processData: {
|
|
65
|
+
userId: processData.userId,
|
|
66
|
+
integrationId: processData.integrationId,
|
|
67
|
+
name: processData.name,
|
|
68
|
+
type: processData.type,
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
throw new Error(
|
|
73
|
+
'Failed to create process: Document not found after insert. ' +
|
|
74
|
+
'This indicates a database consistency issue.'
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const decryptedProcess = await this.encryptionService.decryptFields(
|
|
78
|
+
'Process',
|
|
79
|
+
created
|
|
80
|
+
);
|
|
81
|
+
return this._mapProcess(decryptedProcess);
|
|
37
82
|
}
|
|
38
83
|
|
|
39
84
|
async findById(processId) {
|
|
40
85
|
const objectId = toObjectId(processId);
|
|
41
86
|
if (!objectId) return null;
|
|
42
87
|
const doc = await findOne(this.prisma, 'Process', { _id: objectId });
|
|
43
|
-
|
|
88
|
+
if (!doc) return null;
|
|
89
|
+
|
|
90
|
+
const decryptedProcess = await this.encryptionService.decryptFields(
|
|
91
|
+
'Process',
|
|
92
|
+
doc
|
|
93
|
+
);
|
|
94
|
+
return this._mapProcess(decryptedProcess);
|
|
44
95
|
}
|
|
45
96
|
|
|
46
97
|
async update(processId, updates) {
|
|
47
98
|
const objectId = toObjectId(processId);
|
|
48
99
|
if (!objectId) return null;
|
|
100
|
+
|
|
101
|
+
const existing = await findOne(this.prisma, 'Process', {
|
|
102
|
+
_id: objectId,
|
|
103
|
+
});
|
|
104
|
+
if (!existing) return null;
|
|
105
|
+
|
|
49
106
|
const updatePayload = {};
|
|
50
107
|
if (updates.state !== undefined) updatePayload.state = updates.state;
|
|
51
|
-
if (updates.context !== undefined)
|
|
52
|
-
|
|
108
|
+
if (updates.context !== undefined)
|
|
109
|
+
updatePayload.context = updates.context;
|
|
110
|
+
if (updates.results !== undefined)
|
|
111
|
+
updatePayload.results = updates.results;
|
|
53
112
|
if (updates.childProcesses !== undefined) {
|
|
54
|
-
updatePayload.childProcesses = (updates.childProcesses || [])
|
|
113
|
+
updatePayload.childProcesses = (updates.childProcesses || [])
|
|
114
|
+
.map((id) => toObjectId(id))
|
|
115
|
+
.filter(Boolean);
|
|
55
116
|
}
|
|
56
117
|
if (updates.parentProcessId !== undefined) {
|
|
57
|
-
updatePayload.parentProcessId = updates.parentProcessId
|
|
118
|
+
updatePayload.parentProcessId = updates.parentProcessId
|
|
119
|
+
? toObjectId(updates.parentProcessId)
|
|
120
|
+
: null;
|
|
58
121
|
}
|
|
59
122
|
updatePayload.updatedAt = new Date();
|
|
123
|
+
|
|
124
|
+
const encryptedUpdate = await this.encryptionService.encryptFields(
|
|
125
|
+
'Process',
|
|
126
|
+
updatePayload
|
|
127
|
+
);
|
|
128
|
+
|
|
60
129
|
await updateOne(
|
|
61
130
|
this.prisma,
|
|
62
131
|
'Process',
|
|
63
132
|
{ _id: objectId },
|
|
64
|
-
{ $set:
|
|
133
|
+
{ $set: encryptedUpdate }
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const updated = await findOne(this.prisma, 'Process', {
|
|
137
|
+
_id: objectId,
|
|
138
|
+
});
|
|
139
|
+
if (!updated) {
|
|
140
|
+
console.error(
|
|
141
|
+
'[ProcessRepositoryDocumentDB] Process not found after update',
|
|
142
|
+
{
|
|
143
|
+
processId: fromObjectId(objectId),
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
throw new Error(
|
|
147
|
+
'Failed to update process: Document not found after update. ' +
|
|
148
|
+
'This indicates a database consistency issue.'
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const decryptedProcess = await this.encryptionService.decryptFields(
|
|
152
|
+
'Process',
|
|
153
|
+
updated
|
|
65
154
|
);
|
|
66
|
-
|
|
67
|
-
return updated ? this._mapProcess(updated) : null;
|
|
155
|
+
return this._mapProcess(decryptedProcess);
|
|
68
156
|
}
|
|
69
157
|
|
|
70
158
|
async findByIntegrationAndType(integrationId, type) {
|
|
@@ -76,30 +164,36 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
|
|
|
76
164
|
const docs = await findMany(this.prisma, 'Process', filter, {
|
|
77
165
|
sort: { createdAt: -1 },
|
|
78
166
|
});
|
|
79
|
-
|
|
167
|
+
|
|
168
|
+
const decryptedDocs = await Promise.all(
|
|
169
|
+
docs.map((doc) =>
|
|
170
|
+
this.encryptionService.decryptFields('Process', doc)
|
|
171
|
+
)
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return decryptedDocs.map((doc) => this._mapProcess(doc));
|
|
80
175
|
}
|
|
81
176
|
|
|
82
|
-
async findActiveProcesses(
|
|
177
|
+
async findActiveProcesses(
|
|
178
|
+
integrationId,
|
|
179
|
+
excludeStates = ['COMPLETED', 'ERROR']
|
|
180
|
+
) {
|
|
83
181
|
const integrationObjectId = toObjectId(integrationId);
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
pipeline,
|
|
100
|
-
cursor: {},
|
|
101
|
-
}).then((res) => res?.cursor?.firstBatch || []);
|
|
102
|
-
return docs.map((doc) => this._mapProcess(doc));
|
|
182
|
+
const filter = {
|
|
183
|
+
integrationId: integrationObjectId,
|
|
184
|
+
state: { $nin: excludeStates },
|
|
185
|
+
};
|
|
186
|
+
const docs = await findMany(this.prisma, 'Process', filter, {
|
|
187
|
+
sort: { createdAt: -1 },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const decryptedDocs = await Promise.all(
|
|
191
|
+
docs.map((doc) =>
|
|
192
|
+
this.encryptionService.decryptFields('Process', doc)
|
|
193
|
+
)
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
return decryptedDocs.map((doc) => this._mapProcess(doc));
|
|
103
197
|
}
|
|
104
198
|
|
|
105
199
|
async findByName(name) {
|
|
@@ -109,7 +203,13 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
|
|
|
109
203
|
{ name },
|
|
110
204
|
{ sort: { createdAt: -1 } }
|
|
111
205
|
);
|
|
112
|
-
|
|
206
|
+
if (!doc) return null;
|
|
207
|
+
|
|
208
|
+
const decryptedProcess = await this.encryptionService.decryptFields(
|
|
209
|
+
'Process',
|
|
210
|
+
doc
|
|
211
|
+
);
|
|
212
|
+
return this._mapProcess(decryptedProcess);
|
|
113
213
|
}
|
|
114
214
|
|
|
115
215
|
async deleteById(processId) {
|
|
@@ -128,8 +228,12 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
|
|
|
128
228
|
state: doc?.state ?? null,
|
|
129
229
|
context: doc?.context ?? {},
|
|
130
230
|
results: doc?.results ?? {},
|
|
131
|
-
childProcesses: (doc?.childProcesses || []).map((id) =>
|
|
132
|
-
|
|
231
|
+
childProcesses: (doc?.childProcesses || []).map((id) =>
|
|
232
|
+
fromObjectId(id)
|
|
233
|
+
),
|
|
234
|
+
parentProcessId: doc?.parentProcessId
|
|
235
|
+
? fromObjectId(doc.parentProcessId)
|
|
236
|
+
: null,
|
|
133
237
|
createdAt: doc?.createdAt ? new Date(doc.createdAt) : null,
|
|
134
238
|
updatedAt: doc?.updatedAt ? new Date(doc.updatedAt) : null,
|
|
135
239
|
};
|
|
@@ -137,5 +241,3 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
|
|
|
137
241
|
}
|
|
138
242
|
|
|
139
243
|
module.exports = { ProcessRepositoryDocumentDB };
|
|
140
|
-
|
|
141
|
-
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0-next.
|
|
4
|
+
"version": "2.0.0-next.55",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.588.0",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
42
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
43
|
-
"@friggframework/test": "2.0.0-next.
|
|
41
|
+
"@friggframework/eslint-config": "2.0.0-next.55",
|
|
42
|
+
"@friggframework/prettier-config": "2.0.0-next.55",
|
|
43
|
+
"@friggframework/test": "2.0.0-next.55",
|
|
44
44
|
"@prisma/client": "^6.17.0",
|
|
45
45
|
"@types/lodash": "4.17.15",
|
|
46
46
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "7f86d4b5faaab4de74e6e3676948b62d1e1a5bb1"
|
|
84
84
|
}
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
deleteMany,
|
|
11
11
|
} = require('../../database/documentdb-utils');
|
|
12
12
|
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
13
|
+
const { ClientSafeError } = require('../../errors');
|
|
13
14
|
|
|
14
15
|
const BCRYPT_ROUNDS = 10;
|
|
15
16
|
|
|
@@ -30,25 +31,36 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
|
|
|
30
31
|
created: now,
|
|
31
32
|
};
|
|
32
33
|
const insertedId = await insertOne(this.prisma, 'Token', document);
|
|
33
|
-
const created = await findOne(this.prisma, 'Token', {
|
|
34
|
+
const created = await findOne(this.prisma, 'Token', {
|
|
35
|
+
_id: insertedId,
|
|
36
|
+
});
|
|
34
37
|
return this._mapToken(created);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
async validateAndGetToken(tokenObj) {
|
|
38
41
|
const objectId = toObjectId(tokenObj.id);
|
|
39
42
|
if (!objectId) {
|
|
40
|
-
throw new
|
|
43
|
+
throw new ClientSafeError(
|
|
44
|
+
'Invalid Token: Token does not exist',
|
|
45
|
+
401
|
|
46
|
+
);
|
|
41
47
|
}
|
|
42
48
|
const record = await findOne(this.prisma, 'Token', { _id: objectId });
|
|
43
49
|
if (!record) {
|
|
44
|
-
throw new
|
|
50
|
+
throw new ClientSafeError(
|
|
51
|
+
'Invalid Token: Token does not exist',
|
|
52
|
+
401
|
|
53
|
+
);
|
|
45
54
|
}
|
|
46
55
|
const isValid = await bcrypt.compare(tokenObj.token, record.token);
|
|
47
56
|
if (!isValid) {
|
|
48
|
-
throw new
|
|
57
|
+
throw new ClientSafeError(
|
|
58
|
+
'Invalid Token: Token does not match',
|
|
59
|
+
401
|
|
60
|
+
);
|
|
49
61
|
}
|
|
50
62
|
if (record.expires && new Date(record.expires) < new Date()) {
|
|
51
|
-
throw new
|
|
63
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
52
64
|
}
|
|
53
65
|
return this._mapToken(record);
|
|
54
66
|
}
|
|
@@ -86,7 +98,9 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
|
|
|
86
98
|
async deleteTokensByUserId(userId) {
|
|
87
99
|
const objectId = toObjectId(userId);
|
|
88
100
|
if (!objectId) return { acknowledged: true, deletedCount: 0 };
|
|
89
|
-
const result = await deleteMany(this.prisma, 'Token', {
|
|
101
|
+
const result = await deleteMany(this.prisma, 'Token', {
|
|
102
|
+
userId: objectId,
|
|
103
|
+
});
|
|
90
104
|
const deleted = result?.n ?? 0;
|
|
91
105
|
return { acknowledged: true, deletedCount: deleted };
|
|
92
106
|
}
|
|
@@ -121,5 +135,3 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
|
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
module.exports = { TokenRepositoryDocumentDB };
|
|
124
|
-
|
|
125
|
-
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
2
|
const bcrypt = require('bcryptjs');
|
|
3
3
|
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
4
|
+
const { ClientSafeError } = require('../../errors');
|
|
4
5
|
|
|
5
6
|
const BCRYPT_ROUNDS = 10;
|
|
6
7
|
|
|
@@ -58,7 +59,10 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
if (!sessionToken) {
|
|
61
|
-
throw new
|
|
62
|
+
throw new ClientSafeError(
|
|
63
|
+
'Invalid Token: Token does not exist',
|
|
64
|
+
401
|
|
65
|
+
);
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
// Verify token hash matches
|
|
@@ -67,7 +71,10 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
|
67
71
|
sessionToken.token
|
|
68
72
|
);
|
|
69
73
|
if (!isValid) {
|
|
70
|
-
throw new
|
|
74
|
+
throw new ClientSafeError(
|
|
75
|
+
'Invalid Token: Token does not match',
|
|
76
|
+
401
|
|
77
|
+
);
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
// Check if token is expired
|
|
@@ -75,7 +82,7 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
|
75
82
|
sessionToken.expires &&
|
|
76
83
|
new Date(sessionToken.expires) < new Date()
|
|
77
84
|
) {
|
|
78
|
-
throw new
|
|
85
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
return sessionToken;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
2
|
const bcrypt = require('bcryptjs');
|
|
3
3
|
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
4
|
+
const { ClientSafeError } = require('../../errors');
|
|
4
5
|
|
|
5
6
|
const BCRYPT_ROUNDS = 10;
|
|
6
7
|
|
|
@@ -92,7 +93,10 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
|
|
|
92
93
|
});
|
|
93
94
|
|
|
94
95
|
if (!sessionToken) {
|
|
95
|
-
throw new
|
|
96
|
+
throw new ClientSafeError(
|
|
97
|
+
'Invalid Token: Token does not exist',
|
|
98
|
+
401
|
|
99
|
+
);
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
// Verify token hash matches
|
|
@@ -101,7 +105,10 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
|
|
|
101
105
|
sessionToken.token
|
|
102
106
|
);
|
|
103
107
|
if (!isValid) {
|
|
104
|
-
throw new
|
|
108
|
+
throw new ClientSafeError(
|
|
109
|
+
'Invalid Token: Token does not match',
|
|
110
|
+
401
|
|
111
|
+
);
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
// Check if token is expired
|
|
@@ -109,7 +116,7 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
|
|
|
109
116
|
sessionToken.expires &&
|
|
110
117
|
new Date(sessionToken.expires) < new Date()
|
|
111
118
|
) {
|
|
112
|
-
throw new
|
|
119
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
113
120
|
}
|
|
114
121
|
|
|
115
122
|
return this._convertTokenIds(sessionToken);
|