streamby-core 0.27.3 → 0.27.4
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/nosql.d.ts +2 -7
- package/dist/adapters/database/nosql.js +17 -30
- package/dist/adapters/database/sql.d.ts +1 -7
- package/dist/adapters/database/sql.js +14 -45
- package/dist/middleware/public.js +1 -1
- package/dist/middleware/routes/export.js +33 -10
- package/dist/models/Model.js +20 -7
- package/dist/services/auth.js +1 -1
- package/dist/services/credential.js +3 -3
- package/dist/services/dbConnection.js +71 -27
- package/dist/services/export.d.ts +1 -1
- package/dist/services/export.js +13 -17
- package/dist/services/pipeline.js +3 -2
- package/package.json +1 -1
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { MongoClient, Document } from 'mongodb';
|
|
2
|
-
import {
|
|
2
|
+
import { NodeSchema } from '../../types';
|
|
3
3
|
export declare const ensureCollectionsExist: (client: MongoClient) => Promise<void>;
|
|
4
|
-
export declare const createNoSQLExportCollection: (connection: MongoClient, projectId: string, exportName: string,
|
|
5
|
-
collectionName: string;
|
|
6
|
-
exportId: string;
|
|
7
|
-
}>;
|
|
8
|
-
export declare const createNoSQLRawExportCollection: (connection: MongoClient, projectId: string, exportName: string, method: string, nodeSchema?: NodeSchema) => Promise<{
|
|
9
|
-
collectionName: string;
|
|
4
|
+
export declare const createNoSQLExportCollection: (connection: MongoClient, projectId: string, exportName: string, method: string, nodeSchema?: NodeSchema) => Promise<{
|
|
10
5
|
exportId: string;
|
|
11
6
|
}>;
|
|
12
7
|
export declare const nosqlAdapter: {
|
|
@@ -1,45 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.nosqlAdapter = exports.
|
|
3
|
+
exports.nosqlAdapter = exports.createNoSQLExportCollection = exports.ensureCollectionsExist = void 0;
|
|
4
4
|
const mongodb_1 = require("mongodb");
|
|
5
5
|
const ensureCollectionsExist = async (client) => {
|
|
6
6
|
const db = client.db();
|
|
7
|
-
|
|
8
|
-
// Schema: { _id: ObjectId, name: string, description: string, dbType: string, createdAt: Date, updatedAt: Date, members: [{ userId: string, archived: boolean }] }
|
|
9
|
-
const projectsCollection = db.collection('projects');
|
|
10
|
-
await projectsCollection.findOne({}); // Attempt to find one to ensure collection is created if it doesn't exist
|
|
7
|
+
await db.collection('projects').findOne({});
|
|
11
8
|
console.log('✅ "projects" collection ensured to exist.');
|
|
12
|
-
|
|
13
|
-
// Schema: { _id: ObjectId, projectId: ObjectId, status: string, filePath: string, createdAt: Date, updatedAt: Date }
|
|
14
|
-
const exportsCollection = db.collection('exports');
|
|
15
|
-
await exportsCollection.findOne({}); // Attempt to find one to ensure collection is created if it doesn't exist
|
|
9
|
+
await db.collection('exports').findOne({});
|
|
16
10
|
console.log('✅ "exports" collection ensured to exist.');
|
|
11
|
+
await db.collection('records').findOne({});
|
|
12
|
+
console.log('✅ "records" collection ensured to exist.');
|
|
13
|
+
await db.collection('_tables').findOne({});
|
|
14
|
+
console.log('✅ "_tables" collection ensured to exist.');
|
|
17
15
|
};
|
|
18
16
|
exports.ensureCollectionsExist = ensureCollectionsExist;
|
|
19
|
-
const createNoSQLExportCollection = async (connection, projectId, exportName,
|
|
17
|
+
const createNoSQLExportCollection = async (connection, projectId, exportName, method, nodeSchema) => {
|
|
20
18
|
const db = connection.db();
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
};
|
|
31
|
-
await db.collection(collectionName).insertOne(metadataDocument);
|
|
32
|
-
console.log(`✅ Collection '${collectionName}' created with metadata.`);
|
|
33
|
-
return { collectionName, exportId: collectionName };
|
|
19
|
+
const result = await db.collection('exports').insertOne({
|
|
20
|
+
projectId,
|
|
21
|
+
name: exportName,
|
|
22
|
+
method,
|
|
23
|
+
nodeSchema,
|
|
24
|
+
createdAt: new Date(),
|
|
25
|
+
updatedAt: new Date(),
|
|
26
|
+
});
|
|
27
|
+
return { exportId: result.insertedId.toHexString() };
|
|
34
28
|
};
|
|
35
29
|
exports.createNoSQLExportCollection = createNoSQLExportCollection;
|
|
36
|
-
const createNoSQLRawExportCollection = async (connection, projectId, exportName, method, nodeSchema) => {
|
|
37
|
-
const db = connection.db();
|
|
38
|
-
const collectionName = projectId;
|
|
39
|
-
const result = await db.collection(collectionName).insertOne({ nodeSchema, name: exportName, method, createdAt: new Date(), updatedAt: new Date() });
|
|
40
|
-
return { collectionName, exportId: result.insertedId.toHexString() };
|
|
41
|
-
};
|
|
42
|
-
exports.createNoSQLRawExportCollection = createNoSQLRawExportCollection;
|
|
43
30
|
exports.nosqlAdapter = {
|
|
44
31
|
find: async (connection, tableName, filter) => {
|
|
45
32
|
const db = connection.db();
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
|
-
|
|
3
|
-
export declare const createSQLExportTable: (connection: Pool, projectId: string, exportName: string, fields: FieldDefinition[]) => Promise<{
|
|
4
|
-
collectionName: string;
|
|
5
|
-
exportId: string;
|
|
6
|
-
}>;
|
|
7
|
-
export declare const createSQLRawExportTable: (connection: Pool, projectId: string, exportName: string, nodeSchema?: any) => Promise<{
|
|
8
|
-
collectionName: string;
|
|
2
|
+
export declare const createSQLExportTable: (connection: Pool, projectId: string, exportName: string, nodeSchema?: any) => Promise<{
|
|
9
3
|
exportId: string;
|
|
10
4
|
}>;
|
|
11
5
|
export declare const sqlAdapter: {
|
|
@@ -1,51 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sqlAdapter = exports.
|
|
3
|
+
exports.sqlAdapter = exports.createSQLExportTable = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"projectId" UUID NOT NULL,
|
|
13
|
-
__metadata JSONB NOT NULL,
|
|
14
|
-
${columns ? `, ${columns}` : ''}
|
|
15
|
-
);
|
|
16
|
-
`;
|
|
17
|
-
await connection.query(createTableQuery);
|
|
18
|
-
console.log(`✅ Table '${tableName}' created with metadata.`);
|
|
19
|
-
const metadata = {
|
|
20
|
-
fields,
|
|
21
|
-
createdAt: new Date(),
|
|
22
|
-
updatedAt: new Date(),
|
|
23
|
-
};
|
|
24
|
-
// Insert metadata as the first row, or a dedicated metadata row
|
|
25
|
-
// For simplicity, we'll just create the table with the metadata column.
|
|
26
|
-
// Actual metadata insertion for validation would happen on data inserts.
|
|
27
|
-
return { collectionName: tableName, exportId: tableName };
|
|
28
|
-
};
|
|
29
|
-
exports.createSQLExportTable = createSQLExportTable;
|
|
30
|
-
const createSQLRawExportTable = async (connection, projectId, exportName, nodeSchema) => {
|
|
31
|
-
const tableName = projectId;
|
|
5
|
+
function serializeForPg(v) {
|
|
6
|
+
if (Array.isArray(v) || (v !== null && typeof v === 'object' && !(v instanceof Date))) {
|
|
7
|
+
return JSON.stringify(v);
|
|
8
|
+
}
|
|
9
|
+
return v;
|
|
10
|
+
}
|
|
11
|
+
const createSQLExportTable = async (connection, projectId, exportName, nodeSchema) => {
|
|
32
12
|
const exportId = (0, crypto_1.randomUUID)();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
node_schema JSONB,
|
|
37
|
-
name TEXT,
|
|
38
|
-
method TEXT,
|
|
39
|
-
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
40
|
-
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
41
|
-
);
|
|
42
|
-
`;
|
|
43
|
-
await connection.query(createTableQuery);
|
|
44
|
-
await connection.query(`INSERT INTO "${tableName}" (id, node_schema, name, method) VALUES ($1, $2, $3, $4)`, [exportId, nodeSchema ? JSON.stringify(nodeSchema) : null, exportName, 'GET']);
|
|
45
|
-
console.log(`✅ Raw table '${tableName}' created.`);
|
|
46
|
-
return { collectionName: tableName, exportId };
|
|
13
|
+
await connection.query(`INSERT INTO "exports" (id, "projectId", node_schema, name, method, created_at, updated_at)
|
|
14
|
+
VALUES ($1, $2, $3, $4, 'GET', NOW(), NOW())`, [exportId, projectId, nodeSchema ? JSON.stringify(nodeSchema) : null, exportName]);
|
|
15
|
+
return { exportId };
|
|
47
16
|
};
|
|
48
|
-
exports.
|
|
17
|
+
exports.createSQLExportTable = createSQLExportTable;
|
|
49
18
|
exports.sqlAdapter = {
|
|
50
19
|
find: async (connection, tableName, filter, schema) => {
|
|
51
20
|
const keys = Object.keys(filter);
|
|
@@ -69,7 +38,7 @@ exports.sqlAdapter = {
|
|
|
69
38
|
},
|
|
70
39
|
create: async (connection, tableName, data, schema) => {
|
|
71
40
|
const keys = Object.keys(data).map(key => `"${key}"`).join(', ');
|
|
72
|
-
const values = Object.values(data);
|
|
41
|
+
const values = Object.values(data).map(serializeForPg);
|
|
73
42
|
const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
|
|
74
43
|
const fullTableName = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
|
|
75
44
|
const result = await connection.query(`INSERT INTO ${fullTableName} (${keys}) VALUES (${placeholders}) RETURNING *`, values);
|
|
@@ -78,7 +47,7 @@ exports.sqlAdapter = {
|
|
|
78
47
|
update: async (connection, tableName, filter, data, schema) => {
|
|
79
48
|
const dataKeys = Object.keys(data);
|
|
80
49
|
const filterKeys = Object.keys(filter);
|
|
81
|
-
const values = [...Object.values(data), ...Object.values(filter)];
|
|
50
|
+
const values = [...Object.values(data).map(serializeForPg), ...Object.values(filter)];
|
|
82
51
|
const setClause = dataKeys.map((key, i) => `"${key}" = $${i + 1}`).join(', ');
|
|
83
52
|
const whereClause = filterKeys.map((key, i) => `"${key}" = $${dataKeys.length + i + 1}`).join(' AND ');
|
|
84
53
|
const fullTableName = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getExportRoute = getExportRoute;
|
|
4
4
|
function getExportRoute(req, res, next) {
|
|
5
|
-
if (req.path.includes('/
|
|
5
|
+
if (req.path.includes('/export/')) {
|
|
6
6
|
return next();
|
|
7
7
|
}
|
|
8
8
|
return next();
|
|
@@ -84,12 +84,11 @@ function exportRouter(config) {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
else if (exportMetadata.type === 'json') {
|
|
87
|
-
const rawData = await db.collection(
|
|
87
|
+
const rawData = await db.collection('exports').findOne({ _id: new mongodb_1.ObjectId(exportId) });
|
|
88
88
|
data = {
|
|
89
89
|
json: rawData?.json,
|
|
90
90
|
name: rawData?.name,
|
|
91
91
|
method: rawData?.method,
|
|
92
|
-
collectionName: rawData?.collectionName,
|
|
93
92
|
createdAt: rawData?.createdAt,
|
|
94
93
|
updatedAt: rawData?.updatedAt,
|
|
95
94
|
type: exportMetadata.type,
|
|
@@ -98,7 +97,7 @@ function exportRouter(config) {
|
|
|
98
97
|
};
|
|
99
98
|
}
|
|
100
99
|
else if (exportMetadata.type === 'externalApi') {
|
|
101
|
-
const ProjectModel = (0, manager_1.getModel)('projects'
|
|
100
|
+
const ProjectModel = (0, manager_1.getModel)('projects');
|
|
102
101
|
const currentProject = await ProjectModel.findOne({ _id: projectId });
|
|
103
102
|
if (!currentProject) {
|
|
104
103
|
return res.status(404).json({ message: 'Project not found.' });
|
|
@@ -144,14 +143,13 @@ function exportRouter(config) {
|
|
|
144
143
|
};
|
|
145
144
|
}
|
|
146
145
|
else {
|
|
147
|
-
const rawData = await db.collection(
|
|
146
|
+
const rawData = await db.collection('exports').findOne({ _id: new mongodb_1.ObjectId(exportId) });
|
|
148
147
|
data = {
|
|
149
148
|
json: rawData?.json,
|
|
150
149
|
name: rawData?.name,
|
|
151
150
|
method: rawData?.method,
|
|
152
151
|
createdAt: rawData?.createdAt,
|
|
153
152
|
updatedAt: rawData?.updatedAt,
|
|
154
|
-
collectionName: rawData?.collectionName,
|
|
155
153
|
type: exportMetadata.type,
|
|
156
154
|
credentialId: exportMetadata.credentialId,
|
|
157
155
|
description: rawData?.description,
|
|
@@ -160,7 +158,33 @@ function exportRouter(config) {
|
|
|
160
158
|
}
|
|
161
159
|
}
|
|
162
160
|
else if (project.dbType === 'sql') {
|
|
163
|
-
|
|
161
|
+
const pool = connection.client;
|
|
162
|
+
if (exportMetadata.nodeSchema) {
|
|
163
|
+
data = {
|
|
164
|
+
name: exportMetadata.name,
|
|
165
|
+
description: exportMetadata.description,
|
|
166
|
+
nodeSchema: exportMetadata.nodeSchema,
|
|
167
|
+
useConnections: exportMetadata.useConnections,
|
|
168
|
+
useCredentials: exportMetadata.useCredentials,
|
|
169
|
+
type: exportMetadata.type,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
const result = await pool.query(`SELECT * FROM "exports" WHERE id = $1`, [exportId]);
|
|
174
|
+
const rawData = result.rows[0];
|
|
175
|
+
if (rawData) {
|
|
176
|
+
data = {
|
|
177
|
+
json: rawData.json,
|
|
178
|
+
name: rawData.name,
|
|
179
|
+
method: rawData.method,
|
|
180
|
+
createdAt: rawData.created_at,
|
|
181
|
+
updatedAt: rawData.updated_at,
|
|
182
|
+
type: exportMetadata.type,
|
|
183
|
+
fields: exportMetadata.fields,
|
|
184
|
+
description: rawData.description,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
164
188
|
}
|
|
165
189
|
if (!data) {
|
|
166
190
|
return res.status(404).json({ message: 'Export data not found' });
|
|
@@ -253,14 +277,14 @@ function exportRouter(config) {
|
|
|
253
277
|
if (!exportMetadata) {
|
|
254
278
|
return res.status(404).json({ message: 'Export not found in this project' });
|
|
255
279
|
}
|
|
256
|
-
await (0, export_1.deleteExport)(config, projectId, exportId, project.dbType
|
|
280
|
+
await (0, export_1.deleteExport)(config, projectId, exportId, project.dbType);
|
|
257
281
|
res.status(200).json({ message: 'Export deleted successfully' });
|
|
258
282
|
}
|
|
259
283
|
catch (err) {
|
|
260
284
|
res.status(500).json({ message: 'Failed to delete export', details: err.message });
|
|
261
285
|
}
|
|
262
286
|
});
|
|
263
|
-
router.get('/:projectId/
|
|
287
|
+
router.get('/:projectId/export/:exportName', async (req, res) => {
|
|
264
288
|
try {
|
|
265
289
|
const { projectId, exportName } = req.params;
|
|
266
290
|
const project = await Project.findOne({ _id: projectId });
|
|
@@ -328,9 +352,8 @@ function exportRouter(config) {
|
|
|
328
352
|
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
|
|
329
353
|
if (project.dbType === 'nosql') {
|
|
330
354
|
const db = connection.client.db();
|
|
331
|
-
const legacyCollection = exportMetadata.collectionName ?? projectId;
|
|
332
355
|
if (exportMetadata.type === 'json') {
|
|
333
|
-
const rawData = await db.collection(
|
|
356
|
+
const rawData = await db.collection('exports').findOne({ _id: new mongodb_1.ObjectId(exportMetadata.id.toString()) });
|
|
334
357
|
data = rawData ? rawData.json : null;
|
|
335
358
|
}
|
|
336
359
|
else if (exportMetadata.type === 'externalApi') {
|
package/dist/models/Model.js
CHANGED
|
@@ -129,6 +129,11 @@ class Model {
|
|
|
129
129
|
archived: member.archived,
|
|
130
130
|
}));
|
|
131
131
|
}
|
|
132
|
+
// Track which connection found this record so update() routes correctly
|
|
133
|
+
try {
|
|
134
|
+
Object.defineProperty(transformedResult, '__foundByConnectionId', { value: connectionId, enumerable: false, writable: true });
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
132
137
|
return transformedResult;
|
|
133
138
|
}
|
|
134
139
|
}
|
|
@@ -144,8 +149,13 @@ class Model {
|
|
|
144
149
|
}
|
|
145
150
|
}
|
|
146
151
|
const result = await nosql_1.nosqlAdapter.findOne(connection, this.tableName, processedFilter);
|
|
147
|
-
if (result)
|
|
152
|
+
if (result) {
|
|
153
|
+
try {
|
|
154
|
+
Object.defineProperty(result, '__foundByConnectionId', { value: connectionId, enumerable: false, writable: true });
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
148
157
|
return result;
|
|
158
|
+
}
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
161
|
return null;
|
|
@@ -219,14 +229,17 @@ class Model {
|
|
|
219
229
|
if (!existingProject) {
|
|
220
230
|
return null; // Project not found
|
|
221
231
|
}
|
|
222
|
-
const targetDbType = existingProject.dbType; // Get the actual dbType of the project
|
|
223
|
-
// Find the connection that matches the targetDbType (only among active connections)
|
|
224
232
|
const activeConnectionIds = this.connectionIds.filter(id => (0, connectionManager_1.getConnectedIds)().includes(id));
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
233
|
+
// Use the connection where findOne actually found the record, not project.dbType.
|
|
234
|
+
// project.dbType is the DATA db type (where the project's collections live), not the
|
|
235
|
+
// STORAGE db type (where the project document itself lives). Using dbType here would
|
|
236
|
+
// route SQL-stored projects with dbType:'nosql' to MongoDB, causing update failures.
|
|
237
|
+
const storageConnectionId = existingProject.__foundByConnectionId;
|
|
238
|
+
const clientEntry = (storageConnectionId && activeConnectionIds.includes(storageConnectionId))
|
|
239
|
+
? (0, connectionManager_1.getConnection)(storageConnectionId)
|
|
240
|
+
: activeConnectionIds.map(id => (0, connectionManager_1.getConnection)(id)).find(entry => entry.type === existingProject.dbType);
|
|
228
241
|
if (!clientEntry) {
|
|
229
|
-
throw new Error(`No active connection found for database type: ${
|
|
242
|
+
throw new Error(`No active connection found for database type: ${existingProject.dbType}`);
|
|
230
243
|
}
|
|
231
244
|
const connection = clientEntry.client;
|
|
232
245
|
const dbType = clientEntry.type; // This will be the targetDbType
|
package/dist/services/auth.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.authenticate = void 0;
|
|
4
4
|
exports.checkRole = checkRole;
|
|
5
5
|
const authenticate = (config) => async (req, res, next) => {
|
|
6
|
-
if (req.path.includes('/
|
|
6
|
+
if (req.path.includes('/export/')) {
|
|
7
7
|
return next();
|
|
8
8
|
}
|
|
9
9
|
try {
|
|
@@ -10,7 +10,7 @@ async function addCredential(config, projectId, newCredential) {
|
|
|
10
10
|
if (!(0, encryption_1.isEncryptionKeySet)()) {
|
|
11
11
|
throw new Error('Encryption key is not set. Cannot add credentials.');
|
|
12
12
|
}
|
|
13
|
-
const ProjectModel = (0, manager_1.getModel)('projects'
|
|
13
|
+
const ProjectModel = (0, manager_1.getModel)('projects');
|
|
14
14
|
const project = await ProjectModel.findOne({ _id: projectId });
|
|
15
15
|
if (!project) {
|
|
16
16
|
throw new Error('Project not found.');
|
|
@@ -31,7 +31,7 @@ async function updateCredential(config, projectId, credentialId, updates) {
|
|
|
31
31
|
if (!(0, encryption_1.isEncryptionKeySet)()) {
|
|
32
32
|
throw new Error('Encryption key is not set. Cannot update credentials.');
|
|
33
33
|
}
|
|
34
|
-
const ProjectModel = (0, manager_1.getModel)('projects'
|
|
34
|
+
const ProjectModel = (0, manager_1.getModel)('projects');
|
|
35
35
|
const project = await ProjectModel.findOne({ _id: projectId });
|
|
36
36
|
if (!project) {
|
|
37
37
|
throw new Error('Project not found.');
|
|
@@ -57,7 +57,7 @@ async function updateCredential(config, projectId, credentialId, updates) {
|
|
|
57
57
|
return updatedProject;
|
|
58
58
|
}
|
|
59
59
|
async function deleteCredential(config, projectId, credentialId) {
|
|
60
|
-
const ProjectModel = (0, manager_1.getModel)('projects'
|
|
60
|
+
const ProjectModel = (0, manager_1.getModel)('projects');
|
|
61
61
|
const project = await ProjectModel.findOne({ _id: projectId });
|
|
62
62
|
if (!project) {
|
|
63
63
|
throw new Error('Project not found.');
|
|
@@ -172,10 +172,16 @@ async function deleteRecord(connectionString, dbType, tableName, recordId) {
|
|
|
172
172
|
}
|
|
173
173
|
// ─── Internal (pool-based) variants ──────────────────────────────────────────
|
|
174
174
|
// These operate on already-connected Pool / MongoClient from connectionManager.
|
|
175
|
+
//
|
|
176
|
+
// nosql storage pattern: all user records go into a single 'records' collection
|
|
177
|
+
// with _projectId and _tableName metadata fields. Table membership is tracked in
|
|
178
|
+
// a separate '_tables' collection. SQL keeps the legacy db_{projectId}_{tableName}
|
|
179
|
+
// table-per-collection pattern which is appropriate for relational databases.
|
|
175
180
|
function quoteSqlTable(tableName) {
|
|
176
181
|
const parts = tableName.split('.');
|
|
177
182
|
return parts.length === 2 ? `"${parts[0]}"."${parts[1]}"` : `"${tableName}"`;
|
|
178
183
|
}
|
|
184
|
+
const RECORD_META_PROJECTION = { _projectId: 0, _tableName: 0 };
|
|
179
185
|
async function listTablesInternal(client, dbType, projectId) {
|
|
180
186
|
const prefix = projectId ? `db_${projectId}_` : null;
|
|
181
187
|
if (dbType === 'sql') {
|
|
@@ -190,40 +196,54 @@ async function listTablesInternal(client, dbType, projectId) {
|
|
|
190
196
|
return prefix ? full.slice(prefix.length) : full;
|
|
191
197
|
});
|
|
192
198
|
}
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return
|
|
197
|
-
|
|
199
|
+
const db = client.db();
|
|
200
|
+
if (!projectId) {
|
|
201
|
+
const cols = await db.listCollections().toArray();
|
|
202
|
+
return cols.map(c => c.name).sort();
|
|
203
|
+
}
|
|
204
|
+
const tables = await db.collection('_tables').find({ _projectId: projectId }).toArray();
|
|
205
|
+
return tables.map((t) => t.tableName).sort();
|
|
198
206
|
}
|
|
199
207
|
async function queryRecordsInternal(client, dbType, tableName, limit = 50, offset = 0, projectId) {
|
|
200
|
-
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
201
208
|
if (dbType === 'sql') {
|
|
209
|
+
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
202
210
|
const result = await client.query(`SELECT * FROM ${quoteSqlTable(fullName)} LIMIT $1 OFFSET $2`, [limit, offset]);
|
|
203
211
|
return result.rows;
|
|
204
212
|
}
|
|
205
|
-
|
|
213
|
+
const db = client.db();
|
|
214
|
+
const filter = {};
|
|
215
|
+
if (projectId)
|
|
216
|
+
filter._projectId = projectId;
|
|
217
|
+
if (tableName)
|
|
218
|
+
filter._tableName = tableName;
|
|
219
|
+
return db.collection('records').find(filter, { projection: RECORD_META_PROJECTION }).limit(limit).skip(offset).toArray();
|
|
206
220
|
}
|
|
207
221
|
async function queryRecordByIdInternal(client, dbType, tableName, recordId, projectId) {
|
|
208
|
-
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
209
222
|
if (dbType === 'sql') {
|
|
223
|
+
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
210
224
|
const result = await client.query(`SELECT * FROM ${quoteSqlTable(fullName)} WHERE id = $1 LIMIT 1`, [recordId]);
|
|
211
225
|
return result.rows[0] ?? null;
|
|
212
226
|
}
|
|
213
227
|
const db = client.db();
|
|
214
|
-
|
|
228
|
+
const meta = {};
|
|
229
|
+
if (projectId)
|
|
230
|
+
meta._projectId = projectId;
|
|
231
|
+
if (tableName)
|
|
232
|
+
meta._tableName = tableName;
|
|
233
|
+
const opts = { projection: RECORD_META_PROJECTION };
|
|
234
|
+
let doc = await db.collection('records').findOne({ ...meta, _id: recordId }, opts);
|
|
215
235
|
if (!doc) {
|
|
216
236
|
try {
|
|
217
237
|
const { ObjectId } = await Promise.resolve().then(() => __importStar(require('mongodb')));
|
|
218
|
-
doc = await db.collection(
|
|
238
|
+
doc = await db.collection('records').findOne({ ...meta, _id: new ObjectId(recordId) }, opts);
|
|
219
239
|
}
|
|
220
240
|
catch { /* invalid ObjectId format — not found */ }
|
|
221
241
|
}
|
|
222
242
|
return doc;
|
|
223
243
|
}
|
|
224
244
|
async function createTableOrCollectionInternal(client, dbType, schema, projectId) {
|
|
225
|
-
const fullName = projectId ? `db_${projectId}_${schema.tableName}` : schema.tableName;
|
|
226
245
|
if (dbType === 'sql') {
|
|
246
|
+
const fullName = projectId ? `db_${projectId}_${schema.tableName}` : schema.tableName;
|
|
227
247
|
const columnDdl = schema.columns.map(col => {
|
|
228
248
|
const nullable = col.nullable === false ? 'NOT NULL' : '';
|
|
229
249
|
const pk = col.primaryKey ? 'PRIMARY KEY' : '';
|
|
@@ -232,11 +252,16 @@ async function createTableOrCollectionInternal(client, dbType, schema, projectId
|
|
|
232
252
|
await client.query(`CREATE TABLE IF NOT EXISTS ${quoteSqlTable(fullName)} (${columnDdl})`);
|
|
233
253
|
return;
|
|
234
254
|
}
|
|
235
|
-
|
|
255
|
+
const db = client.db();
|
|
256
|
+
await db.collection('_tables').insertOne({
|
|
257
|
+
_projectId: projectId ?? null,
|
|
258
|
+
tableName: schema.tableName,
|
|
259
|
+
createdAt: new Date(),
|
|
260
|
+
});
|
|
236
261
|
}
|
|
237
262
|
async function insertRecordInternal(client, dbType, tableName, record, projectId) {
|
|
238
|
-
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
239
263
|
if (dbType === 'sql') {
|
|
264
|
+
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
240
265
|
const keys = Object.keys(record);
|
|
241
266
|
const values = Object.values(record);
|
|
242
267
|
const cols = keys.map(k => `"${k}"`).join(', ');
|
|
@@ -244,12 +269,14 @@ async function insertRecordInternal(client, dbType, tableName, record, projectId
|
|
|
244
269
|
const result = await client.query(`INSERT INTO ${quoteSqlTable(fullName)} (${cols}) VALUES (${placeholders}) RETURNING *`, values);
|
|
245
270
|
return result.rows[0];
|
|
246
271
|
}
|
|
247
|
-
const
|
|
272
|
+
const db = client.db();
|
|
273
|
+
const doc = { _projectId: projectId ?? null, _tableName: tableName, ...record };
|
|
274
|
+
const result = await db.collection('records').insertOne(doc);
|
|
248
275
|
return { ...record, _id: result.insertedId };
|
|
249
276
|
}
|
|
250
277
|
async function updateRecordInternal(client, dbType, tableName, recordId, updates, projectId) {
|
|
251
|
-
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
252
278
|
if (dbType === 'sql') {
|
|
279
|
+
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
253
280
|
const keys = Object.keys(updates).filter(k => k !== 'id');
|
|
254
281
|
if (!keys.length)
|
|
255
282
|
return null;
|
|
@@ -258,31 +285,41 @@ async function updateRecordInternal(client, dbType, tableName, recordId, updates
|
|
|
258
285
|
return result.rows[0] ?? null;
|
|
259
286
|
}
|
|
260
287
|
const { ObjectId } = await Promise.resolve().then(() => __importStar(require('mongodb')));
|
|
261
|
-
const { _id, ...fields } = updates;
|
|
262
|
-
|
|
288
|
+
const { _id, _projectId: _p, _tableName: _t, ...fields } = updates;
|
|
289
|
+
const meta = {};
|
|
290
|
+
if (projectId)
|
|
291
|
+
meta._projectId = projectId;
|
|
292
|
+
if (tableName)
|
|
293
|
+
meta._tableName = tableName;
|
|
294
|
+
let idFilter;
|
|
263
295
|
try {
|
|
264
|
-
|
|
296
|
+
idFilter = new ObjectId(recordId);
|
|
265
297
|
}
|
|
266
298
|
catch {
|
|
267
|
-
|
|
299
|
+
idFilter = recordId;
|
|
268
300
|
}
|
|
269
|
-
return client.db().collection(
|
|
301
|
+
return client.db().collection('records').findOneAndUpdate({ ...meta, _id: idFilter }, { $set: fields }, { returnDocument: 'after', projection: RECORD_META_PROJECTION });
|
|
270
302
|
}
|
|
271
303
|
async function deleteRecordInternal(client, dbType, tableName, recordId, projectId) {
|
|
272
|
-
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
273
304
|
if (dbType === 'sql') {
|
|
305
|
+
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
274
306
|
const result = await client.query(`DELETE FROM ${quoteSqlTable(fullName)} WHERE id = $1`, [recordId]);
|
|
275
307
|
return (result.rowCount ?? 0) > 0;
|
|
276
308
|
}
|
|
277
309
|
const { ObjectId } = await Promise.resolve().then(() => __importStar(require('mongodb')));
|
|
278
|
-
|
|
310
|
+
const meta = {};
|
|
311
|
+
if (projectId)
|
|
312
|
+
meta._projectId = projectId;
|
|
313
|
+
if (tableName)
|
|
314
|
+
meta._tableName = tableName;
|
|
315
|
+
let idFilter;
|
|
279
316
|
try {
|
|
280
|
-
|
|
317
|
+
idFilter = new ObjectId(recordId);
|
|
281
318
|
}
|
|
282
319
|
catch {
|
|
283
|
-
|
|
320
|
+
idFilter = recordId;
|
|
284
321
|
}
|
|
285
|
-
const result = await client.db().collection(
|
|
322
|
+
const result = await client.db().collection('records').deleteOne({ ...meta, _id: idFilter });
|
|
286
323
|
return result.deletedCount > 0;
|
|
287
324
|
}
|
|
288
325
|
async function deleteTableOrCollection(connectionString, dbType, tableName) {
|
|
@@ -295,10 +332,17 @@ async function deleteTableOrCollection(connectionString, dbType, tableName) {
|
|
|
295
332
|
});
|
|
296
333
|
}
|
|
297
334
|
async function deleteTableOrCollectionInternal(client, dbType, tableName, projectId) {
|
|
298
|
-
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
299
335
|
if (dbType === 'sql') {
|
|
336
|
+
const fullName = projectId ? `db_${projectId}_${tableName}` : tableName;
|
|
300
337
|
await client.query(`DROP TABLE IF EXISTS ${quoteSqlTable(fullName)}`);
|
|
301
338
|
return;
|
|
302
339
|
}
|
|
303
|
-
|
|
340
|
+
const db = client.db();
|
|
341
|
+
const meta = {};
|
|
342
|
+
if (projectId)
|
|
343
|
+
meta._projectId = projectId;
|
|
344
|
+
if (tableName)
|
|
345
|
+
meta._tableName = tableName;
|
|
346
|
+
await db.collection('records').deleteMany(meta);
|
|
347
|
+
await db.collection('_tables').deleteOne({ _projectId: projectId ?? null, tableName });
|
|
304
348
|
}
|
|
@@ -5,7 +5,7 @@ interface ExportResult {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function createExport(config: StreamByConfig, projectId: string, description: string, exportName: string, dbType: DatabaseType, exportType: 'json' | 'externalApi', isPrivate?: boolean, allowedOrigin?: string[], nodeSchema?: NodeSchema, useConnections?: boolean, useCredentials?: boolean, storageDbId?: string): Promise<ExportResult>;
|
|
7
7
|
export declare function updateExport(config: StreamByConfig, projectId: string, exportId: string, description: string, exportName: string, dbType: DatabaseType, exportType: 'json' | 'externalApi', isPrivate?: boolean, allowedOrigin?: string[], devMode?: boolean, devPorts?: number[], nodeSchema?: NodeSchema, useConnections?: boolean, useCredentials?: boolean): Promise<ExportResult>;
|
|
8
|
-
export declare function deleteExport(config: StreamByConfig, projectId: string, exportId: string, dbType: DatabaseType
|
|
8
|
+
export declare function deleteExport(config: StreamByConfig, projectId: string, exportId: string, dbType: DatabaseType): Promise<{
|
|
9
9
|
message: string;
|
|
10
10
|
}>;
|
|
11
11
|
export {};
|
package/dist/services/export.js
CHANGED
|
@@ -19,25 +19,20 @@ async function createExport(config, projectId, description, exportName, dbType,
|
|
|
19
19
|
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
|
|
20
20
|
const resolvedDbType = targetDb.type;
|
|
21
21
|
let exportId;
|
|
22
|
-
let collectionName;
|
|
23
22
|
if (resolvedDbType === 'nosql') {
|
|
24
|
-
const result = await (0, nosql_1.
|
|
23
|
+
const result = await (0, nosql_1.createNoSQLExportCollection)(connection.client, projectId, exportName, 'GET', nodeSchema);
|
|
25
24
|
exportId = result.exportId;
|
|
26
|
-
collectionName = result.collectionName;
|
|
27
25
|
}
|
|
28
26
|
else if (resolvedDbType === 'sql') {
|
|
29
|
-
const result = await (0, sql_1.
|
|
27
|
+
const result = await (0, sql_1.createSQLExportTable)(connection.client, projectId, exportName, nodeSchema);
|
|
30
28
|
exportId = result.exportId;
|
|
31
29
|
}
|
|
32
30
|
else {
|
|
33
31
|
throw new Error('Unsupported database type');
|
|
34
32
|
}
|
|
35
|
-
|
|
36
|
-
const Project = (0, manager_1.getModel)('projects');
|
|
37
|
-
const project = await Project.findOne({ _id: projectId });
|
|
38
|
-
const isSqlProject = project?.dbType === 'sql';
|
|
33
|
+
const now = new Date();
|
|
39
34
|
const exportEntry = {
|
|
40
|
-
id:
|
|
35
|
+
id: exportId, // Always a plain string; works for both UUID (SQL export) and ObjectId hex (nosql export)
|
|
41
36
|
name: exportName,
|
|
42
37
|
type: exportType,
|
|
43
38
|
method: 'GET',
|
|
@@ -48,9 +43,10 @@ async function createExport(config, projectId, description, exportName, dbType,
|
|
|
48
43
|
useCredentials,
|
|
49
44
|
description,
|
|
50
45
|
storageDbId: targetDb.id,
|
|
51
|
-
|
|
46
|
+
createdAt: now,
|
|
47
|
+
updatedAt: now,
|
|
52
48
|
};
|
|
53
|
-
const ProjectModel =
|
|
49
|
+
const ProjectModel = (0, manager_1.getModel)('projects');
|
|
54
50
|
const updated = await ProjectModel.update({ _id: projectId }, { $push: { exports: exportEntry } });
|
|
55
51
|
if (!updated) {
|
|
56
52
|
throw new Error(`Failed to save export entry to project ${projectId}. The project may not exist or the database update failed.`);
|
|
@@ -75,7 +71,7 @@ async function updateExport(config, projectId, exportId, description, exportName
|
|
|
75
71
|
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
|
|
76
72
|
if (targetDb.type === 'nosql') {
|
|
77
73
|
const db = connection.client.db();
|
|
78
|
-
await db.collection(
|
|
74
|
+
await db.collection('exports').updateOne({ _id: new mongodb_1.ObjectId(exportId) }, { $set: { nodeSchema, description, updatedAt: new Date() } });
|
|
79
75
|
}
|
|
80
76
|
// SQL raw export table update would go here if needed in future
|
|
81
77
|
// Update the export entry in the project document
|
|
@@ -89,12 +85,12 @@ async function updateExport(config, projectId, exportId, description, exportName
|
|
|
89
85
|
await SqlProject.update({ _id: projectId }, { exports: updatedExports });
|
|
90
86
|
}
|
|
91
87
|
else {
|
|
92
|
-
const NoSQLProject = (0, manager_1.getModel)('projects'
|
|
88
|
+
const NoSQLProject = (0, manager_1.getModel)('projects');
|
|
93
89
|
await NoSQLProject.update({ _id: new mongodb_1.ObjectId(projectId), 'exports.id': { $in: [new mongodb_1.ObjectId(exportId), exportId] } }, { $set: { 'exports.$.name': exportName, 'exports.$.private': isPrivate, 'exports.$.allowedOrigin': allowedOrigin, 'exports.$.devMode': devMode, 'exports.$.devPorts': devPorts, 'exports.$.nodeSchema': nodeSchema, 'exports.$.useConnections': useConnections, 'exports.$.useCredentials': useCredentials, 'exports.$.description': description } });
|
|
94
90
|
}
|
|
95
91
|
return { exportId, message: `${exportType} export updated successfully` };
|
|
96
92
|
}
|
|
97
|
-
async function deleteExport(config, projectId, exportId, dbType
|
|
93
|
+
async function deleteExport(config, projectId, exportId, dbType) {
|
|
98
94
|
// Resolve the project to find where its raw export data lives
|
|
99
95
|
const Project = (0, manager_1.getModel)('projects');
|
|
100
96
|
const project = await Project.findOne({ _id: projectId });
|
|
@@ -111,15 +107,15 @@ async function deleteExport(config, projectId, exportId, dbType, exportName) {
|
|
|
111
107
|
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
|
|
112
108
|
if (targetDb.type === 'nosql') {
|
|
113
109
|
const db = connection.client.db();
|
|
114
|
-
await db.collection(
|
|
110
|
+
await db.collection('exports').deleteOne({ _id: new mongodb_1.ObjectId(exportId) });
|
|
115
111
|
}
|
|
116
|
-
// SQL
|
|
112
|
+
// SQL export row deletion would go here if needed in future
|
|
117
113
|
if (isSqlProject) {
|
|
118
114
|
const SqlProject = (0, manager_1.getModel)('projects', 'sql');
|
|
119
115
|
await SqlProject.update({ _id: projectId }, { $pull: { exports: { id: exportId } } });
|
|
120
116
|
}
|
|
121
117
|
else {
|
|
122
|
-
const NoSQLProject = (0, manager_1.getModel)('projects'
|
|
118
|
+
const NoSQLProject = (0, manager_1.getModel)('projects');
|
|
123
119
|
await NoSQLProject.update({ _id: new mongodb_1.ObjectId(projectId) }, { $pull: { exports: { id: new mongodb_1.ObjectId(exportId) } } });
|
|
124
120
|
}
|
|
125
121
|
return { message: 'Export deleted successfully' };
|
|
@@ -91,9 +91,10 @@ async function executePipeline(nodeSchema, project, config) {
|
|
|
91
91
|
continue;
|
|
92
92
|
const connectionId = node.data?.connectionId;
|
|
93
93
|
const recordId = node.data?.recordId;
|
|
94
|
+
const projectIdentifier = project._id?.toString() ?? project.id;
|
|
94
95
|
const fetchData = async (client, type) => recordId
|
|
95
|
-
? (0, dbConnection_1.queryRecordByIdInternal)(client, type, tableName, recordId,
|
|
96
|
-
: (0, dbConnection_1.queryRecordsInternal)(client, type, tableName, 500, 0,
|
|
96
|
+
? (0, dbConnection_1.queryRecordByIdInternal)(client, type, tableName, recordId, projectIdentifier)
|
|
97
|
+
: (0, dbConnection_1.queryRecordsInternal)(client, type, tableName, 500, 0, projectIdentifier);
|
|
97
98
|
if (connectionId) {
|
|
98
99
|
const { client, type } = (0, connectionManager_1.getConnection)(connectionId);
|
|
99
100
|
dataResults.push(await fetchData(client, type));
|