streamby-core 0.29.12 → 0.32.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/dist/adapters/database/connectionManager.js +7 -4
- package/dist/middleware/createRouter.js +0 -2
- package/dist/middleware/routes/ai.js +1 -1
- package/dist/middleware/routes/connection.js +35 -50
- package/dist/middleware/routes/dbConnection.js +60 -222
- package/dist/middleware/routes/distribution.js +5 -4
- package/dist/middleware/routes/export.js +10 -17
- package/dist/middleware/routes/project.js +1 -1
- package/dist/middleware/routes/renderFarm.js +5 -4
- package/dist/middleware/routes/storage.js +2 -43
- package/dist/middleware/routes/storageConnection.js +15 -12
- package/dist/services/backfillBuiltinConnections.js +0 -2
- package/dist/services/connection.d.ts +18 -0
- package/dist/services/connection.js +78 -0
- package/dist/services/connectionResolver.d.ts +19 -0
- package/dist/services/connectionResolver.js +127 -0
- package/dist/services/executor.d.ts +5 -0
- package/dist/services/executor.js +53 -27
- package/dist/services/projectIntegration.js +4 -4
- package/dist/types/index.d.ts +20 -14
- package/dist/types/nodePersistence.d.ts +3 -0
- package/dist/types/nodePersistence.js +15 -0
- package/dist/utils/sanitize.d.ts +10 -0
- package/dist/utils/sanitize.js +16 -1
- package/package.json +1 -1
|
@@ -1,87 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.dbConnectionRouter = dbConnectionRouter;
|
|
37
4
|
const express_1 = require("express");
|
|
38
5
|
const mongodb_1 = require("mongodb");
|
|
39
6
|
const manager_1 = require("../../models/manager");
|
|
40
7
|
const auth_1 = require("../../utils/auth");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
8
|
+
const sanitize_1 = require("../../utils/sanitize");
|
|
9
|
+
const encryption_1 = require("../../utils/encryption");
|
|
10
|
+
const connectionResolver_1 = require("../../services/connectionResolver");
|
|
44
11
|
const dbConnection_1 = require("../../services/dbConnection");
|
|
45
12
|
const VALID_DB_TYPES = ['postgresql', 'mongodb'];
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return
|
|
13
|
+
// Resolves the client for `connId`, runs `fn`, and writes the response — releasing
|
|
14
|
+
// ephemeral (non-builtin) clients afterward. Builtin clients are pooled and owned by
|
|
15
|
+
// connectionManager, never closed here. Responds with the resolve error directly if
|
|
16
|
+
// resolution fails, so callers never have to distinguish an error shape from fn's result.
|
|
17
|
+
async function withResolvedDbClient(project, connId, config, auth, res, fn) {
|
|
18
|
+
const resolved = await (0, connectionResolver_1.resolveDbConnectionClient)(project, connId, config, auth);
|
|
19
|
+
if ('error' in resolved) {
|
|
20
|
+
res.status(resolved.status).json({ message: resolved.error });
|
|
21
|
+
return;
|
|
55
22
|
}
|
|
56
|
-
|
|
57
|
-
|
|
23
|
+
try {
|
|
24
|
+
await fn(resolved);
|
|
58
25
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// (persisted at creation/backfill time) — NOT by comparing the row's own id against
|
|
62
|
-
// config.databases, which no longer holds since builtin rows get their own generated id.
|
|
63
|
-
function findDbConnection(project, connId) {
|
|
64
|
-
return project.dbConnections?.find((c) => c.id === connId);
|
|
65
|
-
}
|
|
66
|
-
async function getDecryptedConnectionString(project, connId) {
|
|
67
|
-
const conn = project.dbConnections?.find((c) => c.id === connId);
|
|
68
|
-
if (!conn)
|
|
69
|
-
return { error: 'DB connection not found', status: 404 };
|
|
70
|
-
if (conn.source === 'integration') {
|
|
71
|
-
if (!conn.integrationId)
|
|
72
|
-
return { error: 'Connection is missing its integrationId', status: 500 };
|
|
73
|
-
const credential = await (0, userIntegration_1.getDecryptedIntegrationCredentialById)(conn.integrationId);
|
|
74
|
-
if (!credential)
|
|
75
|
-
return { error: 'Integration not found', status: 400 };
|
|
76
|
-
return { connectionString: credential, conn };
|
|
26
|
+
finally {
|
|
27
|
+
await (0, connectionResolver_1.releaseDbClient)(resolved);
|
|
77
28
|
}
|
|
78
|
-
const { decrypt, isEncryptionKeySet } = await Promise.resolve().then(() => __importStar(require('../../utils/encryption')));
|
|
79
|
-
if (!isEncryptionKeySet())
|
|
80
|
-
return { error: 'Encryption key is not set', status: 500 };
|
|
81
|
-
const credential = project.credentials?.find((c) => c.id === conn.credentialId);
|
|
82
|
-
if (!credential)
|
|
83
|
-
return { error: 'Credential not found in project', status: 400 };
|
|
84
|
-
return { connectionString: decrypt(credential.encryptedValue), conn };
|
|
85
29
|
}
|
|
86
30
|
function dbConnectionRouter(config) {
|
|
87
31
|
const router = (0, express_1.Router)();
|
|
@@ -95,7 +39,7 @@ function dbConnectionRouter(config) {
|
|
|
95
39
|
return res.status(404).json({ message: 'Project not found' });
|
|
96
40
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
97
41
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
98
|
-
return res.status(200).json({ data: project.dbConnections
|
|
42
|
+
return res.status(200).json({ data: (0, sanitize_1.sanitizeConnections)(project.dbConnections) });
|
|
99
43
|
}
|
|
100
44
|
catch (err) {
|
|
101
45
|
res.status(500).json({ message: 'Failed to fetch DB connections', details: err.message });
|
|
@@ -108,15 +52,15 @@ function dbConnectionRouter(config) {
|
|
|
108
52
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
109
53
|
return res.status(403).json({ message: 'Permission denied' });
|
|
110
54
|
}
|
|
111
|
-
const { name, dbType,
|
|
55
|
+
const { name, dbType, credential, integrationId, description } = req.body;
|
|
112
56
|
if (!name || !dbType) {
|
|
113
57
|
return res.status(400).json({ message: 'name and dbType are required' });
|
|
114
58
|
}
|
|
115
|
-
if (!
|
|
116
|
-
return res.status(400).json({ message: 'Either
|
|
59
|
+
if (!credential && !integrationId) {
|
|
60
|
+
return res.status(400).json({ message: 'Either credential or integrationId is required' });
|
|
117
61
|
}
|
|
118
|
-
if (
|
|
119
|
-
return res.status(400).json({ message: 'Provide only one of
|
|
62
|
+
if (credential && integrationId) {
|
|
63
|
+
return res.status(400).json({ message: 'Provide only one of credential or integrationId' });
|
|
120
64
|
}
|
|
121
65
|
if (!VALID_DB_TYPES.includes(dbType)) {
|
|
122
66
|
return res.status(400).json({ message: `dbType must be one of: ${VALID_DB_TYPES.join(', ')}` });
|
|
@@ -126,6 +70,7 @@ function dbConnectionRouter(config) {
|
|
|
126
70
|
return res.status(404).json({ message: 'Project not found' });
|
|
127
71
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
128
72
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
73
|
+
let encryptedCredential;
|
|
129
74
|
if (integrationId) {
|
|
130
75
|
const UserIntegrationModel = (0, manager_1.getModel)('user_integrations');
|
|
131
76
|
const integration = await UserIntegrationModel.findOne({ id: integrationId, userId: auth.userId });
|
|
@@ -135,23 +80,23 @@ function dbConnectionRouter(config) {
|
|
|
135
80
|
return res.status(400).json({ message: 'Integration is not a database integration' });
|
|
136
81
|
}
|
|
137
82
|
else {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
83
|
+
if (!(0, encryption_1.isEncryptionKeySet)())
|
|
84
|
+
return res.status(500).json({ message: 'Encryption key is not set' });
|
|
85
|
+
encryptedCredential = (0, encryption_1.encrypt)(credential);
|
|
141
86
|
}
|
|
142
87
|
const connection = {
|
|
143
88
|
id: new mongodb_1.ObjectId().toHexString(),
|
|
144
89
|
name,
|
|
145
90
|
dbType,
|
|
146
|
-
credentialId: credentialId ?? '',
|
|
147
91
|
projectId: req.params.id,
|
|
148
92
|
createdAt: new Date(),
|
|
149
93
|
source: integrationId ? 'integration' : 'manual',
|
|
150
94
|
...(integrationId && { integrationId }),
|
|
95
|
+
...(encryptedCredential !== undefined && { encryptedCredential }),
|
|
151
96
|
...(description !== undefined && { description }),
|
|
152
97
|
};
|
|
153
98
|
await Project.update({ _id: req.params.id }, { $push: { dbConnections: connection } });
|
|
154
|
-
return res.status(201).json({ data: connection });
|
|
99
|
+
return res.status(201).json({ data: (0, sanitize_1.sanitizeConnection)(connection) });
|
|
155
100
|
}
|
|
156
101
|
catch (err) {
|
|
157
102
|
res.status(500).json({ message: 'Failed to add DB connection', details: err.message });
|
|
@@ -169,7 +114,7 @@ function dbConnectionRouter(config) {
|
|
|
169
114
|
return res.status(404).json({ message: 'Project not found' });
|
|
170
115
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
171
116
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
172
|
-
const conn = findDbConnection(project, req.params.connId);
|
|
117
|
+
const conn = (0, connectionResolver_1.findDbConnection)(project, req.params.connId);
|
|
173
118
|
if (!conn)
|
|
174
119
|
return res.status(404).json({ message: 'DB connection not found' });
|
|
175
120
|
if (conn.source === 'builtin') {
|
|
@@ -191,25 +136,10 @@ function dbConnectionRouter(config) {
|
|
|
191
136
|
return res.status(404).json({ message: 'Project not found' });
|
|
192
137
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
193
138
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const builtinId = conn.integrationId;
|
|
199
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'database'))) {
|
|
200
|
-
return res.status(403).json({ message: 'Access to this built-in database is not permitted' });
|
|
201
|
-
}
|
|
202
|
-
const internal = resolveBuiltinConnection(builtinId, config);
|
|
203
|
-
if ('error' in internal)
|
|
204
|
-
return res.status(internal.status).json({ message: internal.error });
|
|
205
|
-
const tables = await (0, dbConnection_1.listTablesInternal)(internal.client, internal.dbType, req.params.id);
|
|
206
|
-
return res.status(200).json({ data: tables });
|
|
207
|
-
}
|
|
208
|
-
const resolved = await getDecryptedConnectionString(project, req.params.connId);
|
|
209
|
-
if ('error' in resolved)
|
|
210
|
-
return res.status(resolved.status).json({ message: resolved.error });
|
|
211
|
-
const tables = await (0, dbConnection_1.listTablesOrCollections)(resolved.connectionString, resolved.conn.dbType);
|
|
212
|
-
return res.status(200).json({ data: tables });
|
|
139
|
+
await withResolvedDbClient(project, req.params.connId, config, auth, res, async (resolved) => {
|
|
140
|
+
const tables = await (0, dbConnection_1.listTablesInternal)(resolved.client, resolved.type, req.params.id);
|
|
141
|
+
res.status(200).json({ data: tables });
|
|
142
|
+
});
|
|
213
143
|
}
|
|
214
144
|
catch (err) {
|
|
215
145
|
res.status(500).json({ message: 'Failed to list tables', details: err.message });
|
|
@@ -230,31 +160,14 @@ function dbConnectionRouter(config) {
|
|
|
230
160
|
const schema = req.body;
|
|
231
161
|
if (!schema.tableName)
|
|
232
162
|
return res.status(400).json({ message: 'tableName is required' });
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const builtinId = conn.integrationId;
|
|
238
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'database'))) {
|
|
239
|
-
return res.status(403).json({ message: 'Access to this built-in database is not permitted' });
|
|
240
|
-
}
|
|
241
|
-
const internal = resolveBuiltinConnection(builtinId, config);
|
|
242
|
-
if ('error' in internal)
|
|
243
|
-
return res.status(internal.status).json({ message: internal.error });
|
|
244
|
-
if (internal.dbType === 'sql' && (!Array.isArray(schema.columns) || schema.columns.length === 0)) {
|
|
245
|
-
return res.status(400).json({ message: 'columns are required for SQL tables' });
|
|
163
|
+
await withResolvedDbClient(project, req.params.connId, config, auth, res, async (resolved) => {
|
|
164
|
+
if (resolved.type === 'sql' && (!Array.isArray(schema.columns) || schema.columns.length === 0)) {
|
|
165
|
+
res.status(400).json({ message: 'columns are required for SQL tables' });
|
|
166
|
+
return;
|
|
246
167
|
}
|
|
247
|
-
await (0, dbConnection_1.createTableOrCollectionInternal)(
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
const resolved = await getDecryptedConnectionString(project, req.params.connId);
|
|
251
|
-
if ('error' in resolved)
|
|
252
|
-
return res.status(resolved.status).json({ message: resolved.error });
|
|
253
|
-
if (resolved.conn.dbType === 'postgresql' && (!Array.isArray(schema.columns) || schema.columns.length === 0)) {
|
|
254
|
-
return res.status(400).json({ message: 'columns are required for PostgreSQL tables' });
|
|
255
|
-
}
|
|
256
|
-
await (0, dbConnection_1.createTableOrCollection)(resolved.connectionString, resolved.conn.dbType, schema);
|
|
257
|
-
return res.status(201).json({ message: `${resolved.conn.dbType === 'postgresql' ? 'Table' : 'Collection'} created successfully` });
|
|
168
|
+
await (0, dbConnection_1.createTableOrCollectionInternal)(resolved.client, resolved.type, schema, req.params.id);
|
|
169
|
+
res.status(201).json({ message: `${resolved.type === 'sql' ? 'Table' : 'Collection'} created successfully` });
|
|
170
|
+
});
|
|
258
171
|
}
|
|
259
172
|
catch (err) {
|
|
260
173
|
res.status(500).json({ message: 'Failed to create table/collection', details: err.message });
|
|
@@ -272,25 +185,10 @@ function dbConnectionRouter(config) {
|
|
|
272
185
|
return res.status(404).json({ message: 'Project not found' });
|
|
273
186
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
274
187
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const builtinId = conn.integrationId;
|
|
280
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'database'))) {
|
|
281
|
-
return res.status(403).json({ message: 'Access to this built-in database is not permitted' });
|
|
282
|
-
}
|
|
283
|
-
const internal = resolveBuiltinConnection(builtinId, config);
|
|
284
|
-
if ('error' in internal)
|
|
285
|
-
return res.status(internal.status).json({ message: internal.error });
|
|
286
|
-
await (0, dbConnection_1.deleteTableOrCollectionInternal)(internal.client, internal.dbType, req.params.tableName, req.params.id);
|
|
287
|
-
return res.status(200).json({ message: 'Table/collection deleted' });
|
|
288
|
-
}
|
|
289
|
-
const resolved = await getDecryptedConnectionString(project, req.params.connId);
|
|
290
|
-
if ('error' in resolved)
|
|
291
|
-
return res.status(resolved.status).json({ message: resolved.error });
|
|
292
|
-
await (0, dbConnection_1.deleteTableOrCollection)(resolved.connectionString, resolved.conn.dbType, req.params.tableName);
|
|
293
|
-
return res.status(200).json({ message: 'Table/collection deleted' });
|
|
188
|
+
await withResolvedDbClient(project, req.params.connId, config, auth, res, async (resolved) => {
|
|
189
|
+
await (0, dbConnection_1.deleteTableOrCollectionInternal)(resolved.client, resolved.type, req.params.tableName, req.params.id);
|
|
190
|
+
res.status(200).json({ message: 'Table/collection deleted' });
|
|
191
|
+
});
|
|
294
192
|
}
|
|
295
193
|
catch (err) {
|
|
296
194
|
res.status(500).json({ message: 'Failed to delete table/collection', details: err.message });
|
|
@@ -307,25 +205,10 @@ function dbConnectionRouter(config) {
|
|
|
307
205
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
308
206
|
const limit = Math.min(parseInt(String(req.query.limit ?? 50), 10), 500);
|
|
309
207
|
const offset = Math.max(parseInt(String(req.query.offset ?? 0), 10), 0);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const builtinId = conn.integrationId;
|
|
315
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'database'))) {
|
|
316
|
-
return res.status(403).json({ message: 'Access to this built-in database is not permitted' });
|
|
317
|
-
}
|
|
318
|
-
const internal = resolveBuiltinConnection(builtinId, config);
|
|
319
|
-
if ('error' in internal)
|
|
320
|
-
return res.status(internal.status).json({ message: internal.error });
|
|
321
|
-
const records = await (0, dbConnection_1.queryRecordsInternal)(internal.client, internal.dbType, req.params.tableName, limit, offset, req.params.id);
|
|
322
|
-
return res.status(200).json({ data: records });
|
|
323
|
-
}
|
|
324
|
-
const resolved = await getDecryptedConnectionString(project, req.params.connId);
|
|
325
|
-
if ('error' in resolved)
|
|
326
|
-
return res.status(resolved.status).json({ message: resolved.error });
|
|
327
|
-
const records = await (0, dbConnection_1.queryRecords)(resolved.connectionString, resolved.conn.dbType, req.params.tableName, limit, offset);
|
|
328
|
-
return res.status(200).json({ data: records });
|
|
208
|
+
await withResolvedDbClient(project, req.params.connId, config, auth, res, async (resolved) => {
|
|
209
|
+
const records = await (0, dbConnection_1.queryRecordsInternal)(resolved.client, resolved.type, req.params.tableName, limit, offset, req.params.id);
|
|
210
|
+
res.status(200).json({ data: records });
|
|
211
|
+
});
|
|
329
212
|
}
|
|
330
213
|
catch (err) {
|
|
331
214
|
res.status(500).json({ message: 'Failed to query records', details: err.message });
|
|
@@ -347,25 +230,10 @@ function dbConnectionRouter(config) {
|
|
|
347
230
|
if (!record || typeof record !== 'object' || Array.isArray(record)) {
|
|
348
231
|
return res.status(400).json({ message: 'Request body must be a JSON object' });
|
|
349
232
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const builtinId = conn.integrationId;
|
|
355
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'database'))) {
|
|
356
|
-
return res.status(403).json({ message: 'Access to this built-in database is not permitted' });
|
|
357
|
-
}
|
|
358
|
-
const internal = resolveBuiltinConnection(builtinId, config);
|
|
359
|
-
if ('error' in internal)
|
|
360
|
-
return res.status(internal.status).json({ message: internal.error });
|
|
361
|
-
const inserted = await (0, dbConnection_1.insertRecordInternal)(internal.client, internal.dbType, req.params.tableName, record, req.params.id);
|
|
362
|
-
return res.status(201).json({ data: inserted });
|
|
363
|
-
}
|
|
364
|
-
const resolved = await getDecryptedConnectionString(project, req.params.connId);
|
|
365
|
-
if ('error' in resolved)
|
|
366
|
-
return res.status(resolved.status).json({ message: resolved.error });
|
|
367
|
-
const inserted = await (0, dbConnection_1.insertRecord)(resolved.connectionString, resolved.conn.dbType, req.params.tableName, record);
|
|
368
|
-
return res.status(201).json({ data: inserted });
|
|
233
|
+
await withResolvedDbClient(project, req.params.connId, config, auth, res, async (resolved) => {
|
|
234
|
+
const inserted = await (0, dbConnection_1.insertRecordInternal)(resolved.client, resolved.type, req.params.tableName, record, req.params.id);
|
|
235
|
+
res.status(201).json({ data: inserted });
|
|
236
|
+
});
|
|
369
237
|
}
|
|
370
238
|
catch (err) {
|
|
371
239
|
res.status(500).json({ message: 'Failed to insert record', details: err.message });
|
|
@@ -386,25 +254,10 @@ function dbConnectionRouter(config) {
|
|
|
386
254
|
if (!updates || typeof updates !== 'object' || Array.isArray(updates)) {
|
|
387
255
|
return res.status(400).json({ message: 'Request body must be a JSON object' });
|
|
388
256
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const builtinId = conn.integrationId;
|
|
394
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'database'))) {
|
|
395
|
-
return res.status(403).json({ message: 'Access to this built-in database is not permitted' });
|
|
396
|
-
}
|
|
397
|
-
const internal = resolveBuiltinConnection(builtinId, config);
|
|
398
|
-
if ('error' in internal)
|
|
399
|
-
return res.status(internal.status).json({ message: internal.error });
|
|
400
|
-
const updated = await (0, dbConnection_1.updateRecordInternal)(internal.client, internal.dbType, req.params.tableName, req.params.recordId, updates, req.params.id);
|
|
401
|
-
return res.status(200).json({ data: updated });
|
|
402
|
-
}
|
|
403
|
-
const resolved = await getDecryptedConnectionString(project, req.params.connId);
|
|
404
|
-
if ('error' in resolved)
|
|
405
|
-
return res.status(resolved.status).json({ message: resolved.error });
|
|
406
|
-
const updated = await (0, dbConnection_1.updateRecord)(resolved.connectionString, resolved.conn.dbType, req.params.tableName, req.params.recordId, updates);
|
|
407
|
-
return res.status(200).json({ data: updated });
|
|
257
|
+
await withResolvedDbClient(project, req.params.connId, config, auth, res, async (resolved) => {
|
|
258
|
+
const updated = await (0, dbConnection_1.updateRecordInternal)(resolved.client, resolved.type, req.params.tableName, req.params.recordId, updates, req.params.id);
|
|
259
|
+
res.status(200).json({ data: updated });
|
|
260
|
+
});
|
|
408
261
|
}
|
|
409
262
|
catch (err) {
|
|
410
263
|
res.status(500).json({ message: 'Failed to update record', details: err.message });
|
|
@@ -421,25 +274,10 @@ function dbConnectionRouter(config) {
|
|
|
421
274
|
return res.status(404).json({ message: 'Project not found' });
|
|
422
275
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
423
276
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const builtinId = conn.integrationId;
|
|
429
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'database'))) {
|
|
430
|
-
return res.status(403).json({ message: 'Access to this built-in database is not permitted' });
|
|
431
|
-
}
|
|
432
|
-
const internal = resolveBuiltinConnection(builtinId, config);
|
|
433
|
-
if ('error' in internal)
|
|
434
|
-
return res.status(internal.status).json({ message: internal.error });
|
|
435
|
-
await (0, dbConnection_1.deleteRecordInternal)(internal.client, internal.dbType, req.params.tableName, req.params.recordId, req.params.id);
|
|
436
|
-
return res.status(200).json({ message: 'Record deleted' });
|
|
437
|
-
}
|
|
438
|
-
const resolved = await getDecryptedConnectionString(project, req.params.connId);
|
|
439
|
-
if ('error' in resolved)
|
|
440
|
-
return res.status(resolved.status).json({ message: resolved.error });
|
|
441
|
-
await (0, dbConnection_1.deleteRecord)(resolved.connectionString, resolved.conn.dbType, req.params.tableName, req.params.recordId);
|
|
442
|
-
return res.status(200).json({ message: 'Record deleted' });
|
|
277
|
+
await withResolvedDbClient(project, req.params.connId, config, auth, res, async (resolved) => {
|
|
278
|
+
await (0, dbConnection_1.deleteRecordInternal)(resolved.client, resolved.type, req.params.tableName, req.params.recordId, req.params.id);
|
|
279
|
+
res.status(200).json({ message: 'Record deleted' });
|
|
280
|
+
});
|
|
443
281
|
}
|
|
444
282
|
catch (err) {
|
|
445
283
|
res.status(500).json({ message: 'Failed to delete record', details: err.message });
|
|
@@ -9,6 +9,7 @@ const manager_1 = require("../../models/manager");
|
|
|
9
9
|
const auth_1 = require("../../utils/auth");
|
|
10
10
|
const wsHub_1 = require("../../services/wsHub");
|
|
11
11
|
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
12
|
+
const sanitize_1 = require("../../utils/sanitize");
|
|
12
13
|
const crypto_1 = __importDefault(require("crypto"));
|
|
13
14
|
function getDb() {
|
|
14
15
|
const ids = (0, connectionManager_1.getConnectedIds)().filter(id => (0, connectionManager_1.getConnection)(id).type === 'nosql');
|
|
@@ -33,7 +34,7 @@ function distributionRouter(_config) {
|
|
|
33
34
|
return res.status(404).json({ message: 'Project not found' });
|
|
34
35
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
35
36
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
36
|
-
res.json({ connections: project.distributionConnections
|
|
37
|
+
res.json({ connections: (0, sanitize_1.sanitizeConnections)(project.distributionConnections) });
|
|
37
38
|
}
|
|
38
39
|
catch (err) {
|
|
39
40
|
res.status(500).json({ message: err.message });
|
|
@@ -43,7 +44,7 @@ function distributionRouter(_config) {
|
|
|
43
44
|
try {
|
|
44
45
|
const auth = req.auth;
|
|
45
46
|
const { projectId } = req.params;
|
|
46
|
-
const { name, target,
|
|
47
|
+
const { name, target, encryptedCredential, config, description } = req.body;
|
|
47
48
|
if (!name || !target)
|
|
48
49
|
return res.status(400).json({ message: 'name and target are required' });
|
|
49
50
|
const project = await Project.findOne({ _id: projectId });
|
|
@@ -55,14 +56,14 @@ function distributionRouter(_config) {
|
|
|
55
56
|
id: crypto_1.default.randomUUID(),
|
|
56
57
|
name,
|
|
57
58
|
target,
|
|
58
|
-
|
|
59
|
+
encryptedCredential,
|
|
59
60
|
config: config ?? {},
|
|
60
61
|
description,
|
|
61
62
|
projectId,
|
|
62
63
|
createdAt: new Date(),
|
|
63
64
|
};
|
|
64
65
|
await Project.update({ _id: projectId }, { $push: { distributionConnections: connection } });
|
|
65
|
-
res.status(201).json({ connection });
|
|
66
|
+
res.status(201).json({ connection: (0, sanitize_1.sanitizeConnection)(connection) });
|
|
66
67
|
}
|
|
67
68
|
catch (err) {
|
|
68
69
|
res.status(500).json({ message: err.message });
|
|
@@ -120,19 +120,16 @@ function exportRouter(config) {
|
|
|
120
120
|
return res.status(404).json({ message: 'Project not found.' });
|
|
121
121
|
}
|
|
122
122
|
let headers = {};
|
|
123
|
-
if (exportMetadata.
|
|
123
|
+
if (exportMetadata.encryptedCredential) {
|
|
124
124
|
if (!(0, encryption_1.isEncryptionKeySet)()) {
|
|
125
125
|
throw new Error('Encryption key is not set. Cannot use encrypted credentials.');
|
|
126
126
|
}
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
'Content-Type': 'application/json',
|
|
134
|
-
};
|
|
135
|
-
}
|
|
127
|
+
const decryptedValue = (0, encryption_1.decrypt)(exportMetadata.encryptedCredential);
|
|
128
|
+
const authPrefix = exportMetadata.prefix ? `${exportMetadata.prefix} ` : '';
|
|
129
|
+
headers = {
|
|
130
|
+
'Authorization': `${authPrefix}${decryptedValue}`,
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
};
|
|
136
133
|
}
|
|
137
134
|
const externalApiData = await fetch(exportMetadata.apiUrl, { headers });
|
|
138
135
|
const apiResponse = await externalApiData.json();
|
|
@@ -168,7 +165,7 @@ function exportRouter(config) {
|
|
|
168
165
|
createdAt: rawData?.createdAt,
|
|
169
166
|
updatedAt: rawData?.updatedAt,
|
|
170
167
|
type: exportMetadata.type,
|
|
171
|
-
|
|
168
|
+
hasCredential: !!exportMetadata.encryptedCredential,
|
|
172
169
|
description: rawData?.description,
|
|
173
170
|
fields: exportMetadata.fields,
|
|
174
171
|
};
|
|
@@ -372,15 +369,11 @@ function exportRouter(config) {
|
|
|
372
369
|
}
|
|
373
370
|
else if (exportMetadata.type === 'externalApi') {
|
|
374
371
|
let headers = {};
|
|
375
|
-
if (exportMetadata.
|
|
372
|
+
if (exportMetadata.encryptedCredential) {
|
|
376
373
|
if (!(0, encryption_1.isEncryptionKeySet)()) {
|
|
377
374
|
throw new Error('Encryption key is not set. Cannot use encrypted credentials.');
|
|
378
375
|
}
|
|
379
|
-
const
|
|
380
|
-
if (!credential) {
|
|
381
|
-
throw new Error(`Credential with ID ${exportMetadata.credentialId} not found.`);
|
|
382
|
-
}
|
|
383
|
-
const decryptedValue = (0, encryption_1.decrypt)(credential.encryptedValue);
|
|
376
|
+
const decryptedValue = (0, encryption_1.decrypt)(exportMetadata.encryptedCredential);
|
|
384
377
|
const authPrefix = exportMetadata.prefix ? `${exportMetadata.prefix} ` : '';
|
|
385
378
|
headers = {
|
|
386
379
|
'Authorization': `${authPrefix}${decryptedValue}`,
|
|
@@ -193,7 +193,7 @@ function projectRouter(config) {
|
|
|
193
193
|
useConnections: e.useConnections,
|
|
194
194
|
useCredentials: e.useCredentials,
|
|
195
195
|
}));
|
|
196
|
-
const sanitized = (0, sanitize_1.sanitizeProject)({ ...project, id: project._id || project.id, _id: undefined,
|
|
196
|
+
const sanitized = (0, sanitize_1.sanitizeProject)({ ...project, id: project._id || project.id, _id: undefined, connections: project.connections || [], exports });
|
|
197
197
|
res.json({ project: sanitized, message: 'Project fetched successfully' });
|
|
198
198
|
}
|
|
199
199
|
catch (err) {
|
|
@@ -9,6 +9,7 @@ const manager_1 = require("../../models/manager");
|
|
|
9
9
|
const auth_1 = require("../../utils/auth");
|
|
10
10
|
const jobQueue_1 = require("../../services/jobQueue");
|
|
11
11
|
const vfxProcessor_1 = require("../../services/vfxProcessor");
|
|
12
|
+
const sanitize_1 = require("../../utils/sanitize");
|
|
12
13
|
const crypto_1 = __importDefault(require("crypto"));
|
|
13
14
|
function renderFarmRouter(_config) {
|
|
14
15
|
const router = (0, express_1.Router)();
|
|
@@ -23,7 +24,7 @@ function renderFarmRouter(_config) {
|
|
|
23
24
|
return res.status(404).json({ message: 'Project not found' });
|
|
24
25
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
25
26
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
26
|
-
res.json({ connections: project.renderFarmConnections
|
|
27
|
+
res.json({ connections: (0, sanitize_1.sanitizeConnections)(project.renderFarmConnections) });
|
|
27
28
|
}
|
|
28
29
|
catch (err) {
|
|
29
30
|
res.status(500).json({ message: err.message });
|
|
@@ -33,7 +34,7 @@ function renderFarmRouter(_config) {
|
|
|
33
34
|
try {
|
|
34
35
|
const auth = req.auth;
|
|
35
36
|
const { projectId } = req.params;
|
|
36
|
-
const { name, provider, apiUrl,
|
|
37
|
+
const { name, provider, apiUrl, encryptedCredential, description } = req.body;
|
|
37
38
|
if (!name || !provider || !apiUrl) {
|
|
38
39
|
return res.status(400).json({ message: 'name, provider and apiUrl are required' });
|
|
39
40
|
}
|
|
@@ -47,13 +48,13 @@ function renderFarmRouter(_config) {
|
|
|
47
48
|
name,
|
|
48
49
|
provider,
|
|
49
50
|
apiUrl,
|
|
50
|
-
|
|
51
|
+
encryptedCredential,
|
|
51
52
|
description,
|
|
52
53
|
projectId,
|
|
53
54
|
createdAt: new Date(),
|
|
54
55
|
};
|
|
55
56
|
await Project.update({ _id: projectId }, { $push: { renderFarmConnections: connection } });
|
|
56
|
-
res.status(201).json({ connection });
|
|
57
|
+
res.status(201).json({ connection: (0, sanitize_1.sanitizeConnection)(connection) });
|
|
57
58
|
}
|
|
58
59
|
catch (err) {
|
|
59
60
|
res.status(500).json({ message: err.message });
|
|
@@ -8,13 +8,11 @@ const express_1 = require("express");
|
|
|
8
8
|
const file_1 = require("../../services/file");
|
|
9
9
|
const presign_1 = require("../../services/presign");
|
|
10
10
|
const storage_1 = require("../../providers/storage");
|
|
11
|
-
const s3_1 = require("../../adapters/storage/s3");
|
|
12
11
|
const manager_1 = require("../../models/manager");
|
|
13
12
|
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
14
13
|
const auth_1 = require("../../utils/auth");
|
|
15
|
-
const encryption_1 = require("../../utils/encryption");
|
|
16
14
|
const builtinAccess_1 = require("../../utils/builtinAccess");
|
|
17
|
-
const
|
|
15
|
+
const connectionResolver_1 = require("../../services/connectionResolver");
|
|
18
16
|
const mongodb_1 = require("mongodb");
|
|
19
17
|
const crypto_1 = __importDefault(require("crypto"));
|
|
20
18
|
const VALID_CATEGORIES = ['images', 'audios', 'videos', '3d-models'];
|
|
@@ -69,46 +67,7 @@ function storageRouter(config) {
|
|
|
69
67
|
: { storageConnectionId: connId };
|
|
70
68
|
return { projectId, ...connMatch, ...(extra ?? {}) };
|
|
71
69
|
}
|
|
72
|
-
|
|
73
|
-
const conn = findStorageConn(project, connId);
|
|
74
|
-
if (!conn)
|
|
75
|
-
return { error: 'Storage connection not found', status: 404 };
|
|
76
|
-
if (conn.source === 'builtin') {
|
|
77
|
-
const builtinId = conn.integrationId;
|
|
78
|
-
if (!(await (0, builtinAccess_1.assertBuiltinAccess)(auth, builtinId, config, 'storage'))) {
|
|
79
|
-
return { error: 'Access to this built-in storage is not permitted', status: 403 };
|
|
80
|
-
}
|
|
81
|
-
const provider = config.storageProviders.find(p => p.id === builtinId);
|
|
82
|
-
if (!provider)
|
|
83
|
-
return { error: 'Storage provider not found', status: 404 };
|
|
84
|
-
return provider.type === 's3' ? new s3_1.S3Adapter(provider.config) : adapter;
|
|
85
|
-
}
|
|
86
|
-
if (conn.source === 'integration') {
|
|
87
|
-
if (!conn.integrationId)
|
|
88
|
-
return { error: 'Connection is missing its integrationId', status: 500 };
|
|
89
|
-
try {
|
|
90
|
-
const s3Config = await (0, userIntegration_1.getDecryptedIntegrationCredentialById)(conn.integrationId);
|
|
91
|
-
if (!s3Config)
|
|
92
|
-
return { error: 'Integration not found', status: 400 };
|
|
93
|
-
return new s3_1.S3Adapter(s3Config);
|
|
94
|
-
}
|
|
95
|
-
catch (e) {
|
|
96
|
-
return { error: `Failed to initialize storage adapter: ${e.message}`, status: 500 };
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (!(0, encryption_1.isEncryptionKeySet)())
|
|
100
|
-
return { error: 'Encryption key not set', status: 500 };
|
|
101
|
-
const credential = project.credentials?.find((c) => c.id === conn.credentialId);
|
|
102
|
-
if (!credential)
|
|
103
|
-
return { error: 'Credential not found in project', status: 400 };
|
|
104
|
-
try {
|
|
105
|
-
const s3Config = JSON.parse((0, encryption_1.decrypt)(credential.encryptedValue));
|
|
106
|
-
return new s3_1.S3Adapter(s3Config);
|
|
107
|
-
}
|
|
108
|
-
catch (e) {
|
|
109
|
-
return { error: `Failed to initialize storage adapter: ${e.message}`, status: 500 };
|
|
110
|
-
}
|
|
111
|
-
}
|
|
70
|
+
const resolveConnAdapter = (connId, project, auth) => (0, connectionResolver_1.resolveStorageAdapter)(project, connId, { ...config, adapter }, auth);
|
|
112
71
|
router.get('/upload-project-image-url/:id', async (req, res) => {
|
|
113
72
|
try {
|
|
114
73
|
const auth = req.auth;
|