streamby-core 0.21.7 → 0.22.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/nosql.d.ts +10 -1
- package/dist/adapters/database/nosql.js +32 -2
- package/dist/adapters/database/sql.d.ts +9 -0
- package/dist/adapters/database/sql.js +43 -1
- package/dist/middleware/createRouter.js +2 -2
- package/dist/middleware/routes/auth.js +2 -2
- package/dist/middleware/routes/database.js +3 -3
- package/dist/middleware/routes/export.js +39 -19
- package/dist/middleware/routes/project.js +63 -42
- package/dist/middleware/routes/{file.d.ts → storage.d.ts} +1 -1
- package/dist/middleware/routes/{file.js → storage.js} +17 -17
- package/dist/services/export.d.ts +10 -0
- package/dist/services/export.js +59 -0
- package/dist/types/index.d.ts +9 -0
- package/package.json +1 -1
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { MongoClient, Document } from 'mongodb';
|
|
2
|
+
import { FieldDefinition } from '../../types';
|
|
2
3
|
export declare const ensureCollectionsExist: (client: MongoClient) => Promise<void>;
|
|
4
|
+
export declare const createNoSQLExportCollection: (connection: MongoClient, projectId: string, exportName: string, fields: FieldDefinition[]) => Promise<{
|
|
5
|
+
collectionName: string;
|
|
6
|
+
exportId: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const createNoSQLRawExportCollection: (connection: MongoClient, projectId: string, exportName: string, jsonData: any) => Promise<{
|
|
9
|
+
collectionName: string;
|
|
10
|
+
exportId: string;
|
|
11
|
+
}>;
|
|
3
12
|
export declare const nosqlAdapter: {
|
|
4
13
|
find: (connection: MongoClient, tableName: string, filter: any) => Promise<Document[]>;
|
|
5
14
|
findOne: (connection: MongoClient, tableName: string, filter: any) => Promise<Document | null>;
|
|
6
15
|
create: <T extends Document>(connection: MongoClient, tableName: string, data: T) => Promise<T>;
|
|
7
|
-
update: (connection: MongoClient, tableName: string, filter: any, data: Partial<Document>) => Promise<Document | null>;
|
|
16
|
+
update: (connection: MongoClient, tableName: string, filter: any, data: Document | Partial<Document>) => Promise<Document | null>;
|
|
8
17
|
delete: (connection: MongoClient, tableName: string, filter: any) => Promise<number>;
|
|
9
18
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.nosqlAdapter = exports.ensureCollectionsExist = void 0;
|
|
3
|
+
exports.nosqlAdapter = exports.createNoSQLRawExportCollection = 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();
|
|
@@ -16,6 +16,33 @@ const ensureCollectionsExist = async (client) => {
|
|
|
16
16
|
console.log('✅ "exports" collection ensured to exist.');
|
|
17
17
|
};
|
|
18
18
|
exports.ensureCollectionsExist = ensureCollectionsExist;
|
|
19
|
+
const createNoSQLExportCollection = async (connection, projectId, exportName, fields) => {
|
|
20
|
+
const db = connection.db();
|
|
21
|
+
const slug = exportName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
22
|
+
const collectionName = `export_${projectId}_${slug}`;
|
|
23
|
+
// Create the collection implicitly by inserting the metadata document
|
|
24
|
+
const metadataDocument = {
|
|
25
|
+
__metadata: {
|
|
26
|
+
fields,
|
|
27
|
+
createdAt: new Date(),
|
|
28
|
+
updatedAt: new Date(),
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
await db.collection(collectionName).insertOne(metadataDocument);
|
|
32
|
+
console.log(`✅ Collection '${collectionName}' created with metadata.`);
|
|
33
|
+
return { collectionName, exportId: collectionName };
|
|
34
|
+
};
|
|
35
|
+
exports.createNoSQLExportCollection = createNoSQLExportCollection;
|
|
36
|
+
const createNoSQLRawExportCollection = async (connection, projectId, exportName, jsonData) => {
|
|
37
|
+
const db = connection.db();
|
|
38
|
+
const slug = exportName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
39
|
+
const collectionName = `raw_export_${projectId}_${slug}`;
|
|
40
|
+
// Insert the raw JSON data directly into the new collection
|
|
41
|
+
await db.collection(collectionName).insertOne(jsonData);
|
|
42
|
+
console.log(`✅ Raw collection '${collectionName}' created with provided JSON data.`);
|
|
43
|
+
return { collectionName, exportId: collectionName };
|
|
44
|
+
};
|
|
45
|
+
exports.createNoSQLRawExportCollection = createNoSQLRawExportCollection;
|
|
19
46
|
exports.nosqlAdapter = {
|
|
20
47
|
find: async (connection, tableName, filter) => {
|
|
21
48
|
const db = connection.db();
|
|
@@ -42,7 +69,10 @@ exports.nosqlAdapter = {
|
|
|
42
69
|
if (processedFilter._id && typeof processedFilter._id === 'string') {
|
|
43
70
|
processedFilter._id = new mongodb_1.ObjectId(processedFilter._id);
|
|
44
71
|
}
|
|
45
|
-
|
|
72
|
+
// Check if the update data contains MongoDB update operators (e.g., $set, $push)
|
|
73
|
+
const isUpdateOperator = Object.keys(data).some(key => key.startsWith('$'));
|
|
74
|
+
const updateDocument = isUpdateOperator ? data : { $set: data };
|
|
75
|
+
const result = await db.collection(tableName).findOneAndUpdate(processedFilter, updateDocument, { returnDocument: 'after' });
|
|
46
76
|
if (!result) {
|
|
47
77
|
return null;
|
|
48
78
|
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
|
+
import { FieldDefinition } from '../../types';
|
|
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, jsonData: any) => Promise<{
|
|
8
|
+
collectionName: string;
|
|
9
|
+
exportId: string;
|
|
10
|
+
}>;
|
|
2
11
|
export declare const sqlAdapter: {
|
|
3
12
|
find: (connection: Pool, tableName: string, filter: any) => Promise<any[]>;
|
|
4
13
|
findOne: (connection: Pool, tableName: string, filter: any) => Promise<any | null>;
|
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sqlAdapter = void 0;
|
|
3
|
+
exports.sqlAdapter = exports.createSQLRawExportTable = exports.createSQLExportTable = void 0;
|
|
4
|
+
const createSQLExportTable = async (connection, projectId, exportName, fields) => {
|
|
5
|
+
const slug = exportName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
6
|
+
const tableName = `export_${projectId}_${slug}`;
|
|
7
|
+
const columns = fields.map(field => `"${field.name}" ${field.type}`).join(',');
|
|
8
|
+
const createTableQuery = `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS "${tableName}" (
|
|
10
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
11
|
+
"projectId" UUID NOT NULL,
|
|
12
|
+
__metadata JSONB NOT NULL,
|
|
13
|
+
${columns ? `, ${columns}` : ''}
|
|
14
|
+
);
|
|
15
|
+
`;
|
|
16
|
+
await connection.query(createTableQuery);
|
|
17
|
+
console.log(`✅ Table '${tableName}' created with metadata.`);
|
|
18
|
+
const metadata = {
|
|
19
|
+
fields,
|
|
20
|
+
createdAt: new Date(),
|
|
21
|
+
updatedAt: new Date(),
|
|
22
|
+
};
|
|
23
|
+
// Insert metadata as the first row, or a dedicated metadata row
|
|
24
|
+
// For simplicity, we'll just create the table with the metadata column.
|
|
25
|
+
// Actual metadata insertion for validation would happen on data inserts.
|
|
26
|
+
return { collectionName: tableName, exportId: tableName };
|
|
27
|
+
};
|
|
28
|
+
exports.createSQLExportTable = createSQLExportTable;
|
|
29
|
+
const createSQLRawExportTable = async (connection, projectId, exportName, jsonData) => {
|
|
30
|
+
const slug = exportName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
31
|
+
const tableName = `raw_export_${projectId}_${slug}`;
|
|
32
|
+
const createTableQuery = `
|
|
33
|
+
CREATE TABLE IF NOT EXISTS "${tableName}" (
|
|
34
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
35
|
+
data JSONB NOT NULL
|
|
36
|
+
);
|
|
37
|
+
`;
|
|
38
|
+
await connection.query(createTableQuery);
|
|
39
|
+
console.log(`✅ Raw table '${tableName}' created.`);
|
|
40
|
+
// Insert the raw JSON data directly into the new table
|
|
41
|
+
await connection.query(`INSERT INTO "${tableName}" (data) VALUES ($1)`, [jsonData]);
|
|
42
|
+
console.log(`✅ Raw JSON data inserted into table '${tableName}'.`);
|
|
43
|
+
return { collectionName: tableName, exportId: tableName };
|
|
44
|
+
};
|
|
45
|
+
exports.createSQLRawExportTable = createSQLRawExportTable;
|
|
4
46
|
exports.sqlAdapter = {
|
|
5
47
|
find: async (connection, tableName, filter) => {
|
|
6
48
|
const keys = Object.keys(filter);
|
|
@@ -10,7 +10,7 @@ const storage_1 = require("../providers/storage");
|
|
|
10
10
|
const connectionManager_1 = require("../adapters/database/connectionManager");
|
|
11
11
|
const auth_1 = require("./routes/auth");
|
|
12
12
|
const database_1 = require("./routes/database");
|
|
13
|
-
const
|
|
13
|
+
const storage_2 = require("./routes/storage");
|
|
14
14
|
const project_1 = require("./routes/project");
|
|
15
15
|
const export_1 = require("./routes/export");
|
|
16
16
|
function createStreamByRouter(config) {
|
|
@@ -28,7 +28,7 @@ function createStreamByRouter(config) {
|
|
|
28
28
|
}
|
|
29
29
|
router.use((0, auth_1.authRouter)(config));
|
|
30
30
|
router.use((0, database_1.databaseRouter)(config));
|
|
31
|
-
router.use((0,
|
|
31
|
+
router.use((0, storage_2.storageRouter)(config));
|
|
32
32
|
router.use((0, project_1.projectRouter)(config));
|
|
33
33
|
router.use((0, export_1.exportRouter)(config));
|
|
34
34
|
return router;
|
|
@@ -12,10 +12,10 @@ function authRouter(config) {
|
|
|
12
12
|
!auth.role) {
|
|
13
13
|
throw new Error('Invalid or missing authentication context');
|
|
14
14
|
}
|
|
15
|
-
return res.status(200).json({ logged: true, ...auth });
|
|
15
|
+
return res.status(200).json({ logged: true, message: 'Authentication successful', ...auth });
|
|
16
16
|
}
|
|
17
17
|
catch (err) {
|
|
18
|
-
return res.status(401).json({ logged: false });
|
|
18
|
+
return res.status(401).json({ logged: false, message: err.message });
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
return router;
|
|
@@ -11,16 +11,16 @@ function databaseRouter(config) {
|
|
|
11
11
|
if (!auth ||
|
|
12
12
|
!auth.userId ||
|
|
13
13
|
!auth.role) {
|
|
14
|
-
return res.status(401).json({
|
|
14
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
15
15
|
}
|
|
16
16
|
const connectedDbIds = (0, connectionManager_1.getConnectedIds)();
|
|
17
17
|
const databases = (config.databases || [])
|
|
18
18
|
.filter(db => connectedDbIds.includes(db.id))
|
|
19
19
|
.map(db => ({ name: db.id, value: db.type }));
|
|
20
|
-
res.status(200).json({ databases });
|
|
20
|
+
res.status(200).json({ databases, message: 'Databases retrieved successfully' });
|
|
21
21
|
}
|
|
22
22
|
catch (err) {
|
|
23
|
-
res.status(500).json({
|
|
23
|
+
res.status(500).json({ message: err.message });
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
return router;
|
|
@@ -4,32 +4,33 @@ exports.exportRouter = exportRouter;
|
|
|
4
4
|
const express_1 = require("express");
|
|
5
5
|
const manager_1 = require("../../models/manager");
|
|
6
6
|
const auth_1 = require("../../utils/auth");
|
|
7
|
+
const export_1 = require("../../services/export");
|
|
7
8
|
function exportRouter(config) {
|
|
8
9
|
const router = (0, express_1.Router)();
|
|
9
10
|
const Project = (0, manager_1.getModel)('projects');
|
|
10
|
-
const Export = (0, manager_1.getModel)('exports');
|
|
11
11
|
router.get('/projects/:id/exports/:export_id', async (req, res) => {
|
|
12
12
|
try {
|
|
13
13
|
const auth = await config.authProvider(req);
|
|
14
14
|
if (!auth ||
|
|
15
15
|
!auth.userId ||
|
|
16
16
|
!auth.role) {
|
|
17
|
-
return res.status(401).json({
|
|
17
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
18
18
|
}
|
|
19
19
|
const projectId = req.params.id;
|
|
20
20
|
const exportId = req.params.export_id;
|
|
21
21
|
const project = await Project.findOne({ _id: projectId });
|
|
22
22
|
if (!project) {
|
|
23
|
-
return res.status(404).json({
|
|
23
|
+
return res.status(404).json({ message: 'Project not found' });
|
|
24
24
|
}
|
|
25
25
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
26
|
-
return res.status(403).json({
|
|
26
|
+
return res.status(403).json({ message: 'Unauthorized access' });
|
|
27
27
|
}
|
|
28
|
+
const Export = (0, manager_1.getModel)('exports', project.dbType);
|
|
28
29
|
const data = await Export.findOne({ _id: exportId });
|
|
29
|
-
res.json({ data: { ...data, id: data._id || data.id, _id: undefined } });
|
|
30
|
+
res.json({ data: { ...data, id: data._id || data.id, _id: undefined }, message: 'Export data fetched successfully' });
|
|
30
31
|
}
|
|
31
32
|
catch (err) {
|
|
32
|
-
res.status(500).json({
|
|
33
|
+
res.status(500).json({ message: 'Failed to fetch export data', details: err.message });
|
|
33
34
|
}
|
|
34
35
|
});
|
|
35
36
|
router.post('/projects/:id/exports', async (req, res) => {
|
|
@@ -38,27 +39,46 @@ function exportRouter(config) {
|
|
|
38
39
|
if (!auth ||
|
|
39
40
|
!auth.userId ||
|
|
40
41
|
!auth.role) {
|
|
41
|
-
return res.status(401).json({
|
|
42
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
42
43
|
}
|
|
43
44
|
const projectId = req.params.id;
|
|
44
|
-
const { name, description, collectionName } = req.body;
|
|
45
|
-
if (!name || !collectionName) {
|
|
46
|
-
return res.status(400).json({
|
|
45
|
+
const { name, description, collectionName, fields } = req.body;
|
|
46
|
+
if (!name || !collectionName || !fields) {
|
|
47
|
+
return res.status(400).json({ message: 'Missing export name, collectionName, or fields' });
|
|
47
48
|
}
|
|
48
49
|
const project = await Project.findOne({ _id: projectId });
|
|
49
50
|
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
50
|
-
return res.status(403).json({
|
|
51
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
51
52
|
}
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
description,
|
|
55
|
-
collectionName,
|
|
56
|
-
projectId
|
|
57
|
-
});
|
|
58
|
-
res.status(201).json({ data: { ...newExport, id: newExport._id || newExport.id, _id: undefined } });
|
|
53
|
+
const result = await (0, export_1.createExport)(config, projectId, name, collectionName, fields, project.dbType);
|
|
54
|
+
res.status(201).json({ data: result, message: result.message });
|
|
59
55
|
}
|
|
60
56
|
catch (err) {
|
|
61
|
-
res.status(500).json({
|
|
57
|
+
res.status(500).json({ message: 'Failed to create export', details: err.message });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
router.post('/projects/:id/exports/raw', async (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
const auth = await config.authProvider(req);
|
|
63
|
+
if (!auth ||
|
|
64
|
+
!auth.userId ||
|
|
65
|
+
!auth.role) {
|
|
66
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
67
|
+
}
|
|
68
|
+
const projectId = req.params.id;
|
|
69
|
+
const { name, description, collectionName, jsonData } = req.body;
|
|
70
|
+
if (!name || !collectionName || !jsonData) {
|
|
71
|
+
return res.status(400).json({ message: 'Missing export name, collectionName, or jsonData' });
|
|
72
|
+
}
|
|
73
|
+
const project = await Project.findOne({ _id: projectId });
|
|
74
|
+
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
75
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
76
|
+
}
|
|
77
|
+
const result = await (0, export_1.createRawExport)(config, projectId, name, collectionName, jsonData, project.dbType);
|
|
78
|
+
res.status(201).json({ data: result, message: result.message });
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
res.status(500).json({ message: 'Failed to create raw export', details: err.message });
|
|
62
82
|
}
|
|
63
83
|
});
|
|
64
84
|
return router;
|
|
@@ -39,29 +39,29 @@ function projectRouter(config) {
|
|
|
39
39
|
return true;
|
|
40
40
|
})
|
|
41
41
|
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
42
|
-
res.json({ projects });
|
|
42
|
+
res.json({ projects, message: 'Projects listed successfully' });
|
|
43
43
|
}
|
|
44
44
|
catch (err) {
|
|
45
45
|
console.error('Error in /projects endpoint:', err);
|
|
46
|
-
res.status(500).json({
|
|
46
|
+
res.status(500).json({ message: 'Failed to list projects', details: err });
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
router.post('/projects/create', async (req, res) => {
|
|
50
50
|
try {
|
|
51
51
|
const auth = req.auth;
|
|
52
52
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
53
|
-
return res.status(403).json({
|
|
53
|
+
return res.status(403).json({ message: 'Permission denied' });
|
|
54
54
|
}
|
|
55
55
|
const { name, description, dbType, image } = req.body;
|
|
56
56
|
const mainDb = config.databases?.find(db => db.main);
|
|
57
57
|
if (!mainDb) {
|
|
58
|
-
return res.status(500).json({
|
|
58
|
+
return res.status(500).json({ message: 'Main database not configured' });
|
|
59
59
|
}
|
|
60
60
|
const userDbType = mainDb.type;
|
|
61
61
|
const User = (0, manager_1.getModel)('users', userDbType);
|
|
62
62
|
const user = await User.findOne({ _id: auth.userId });
|
|
63
63
|
if (!user) {
|
|
64
|
-
return res.status(404).json({
|
|
64
|
+
return res.status(404).json({ message: 'User not found' });
|
|
65
65
|
}
|
|
66
66
|
const newProject = await Project.create({
|
|
67
67
|
dbType: dbType || 'nosql',
|
|
@@ -70,10 +70,17 @@ function projectRouter(config) {
|
|
|
70
70
|
image: image || '',
|
|
71
71
|
members: [{ userId: auth.userId, username: user.username, role: "admin", archived: false }]
|
|
72
72
|
});
|
|
73
|
-
|
|
73
|
+
const allProjects = await Project.find({});
|
|
74
|
+
const projects = allProjects
|
|
75
|
+
.filter(project => {
|
|
76
|
+
const isMember = project.members?.some((m) => m.userId?.toString() === auth.userId?.toString());
|
|
77
|
+
return isMember; // Only include projects where the user is a member
|
|
78
|
+
})
|
|
79
|
+
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
80
|
+
res.status(200).json({ success: true, projects: projects, projectId: newProject._id || newProject.id, message: 'Project created successfully' });
|
|
74
81
|
}
|
|
75
82
|
catch (err) {
|
|
76
|
-
res.status(500).json({
|
|
83
|
+
res.status(500).json({ message: 'Failed to create project', details: err.message });
|
|
77
84
|
}
|
|
78
85
|
});
|
|
79
86
|
router.get('/projects/:id', async (req, res) => {
|
|
@@ -82,43 +89,50 @@ function projectRouter(config) {
|
|
|
82
89
|
const projectId = req.params.id;
|
|
83
90
|
const project = await Project.findOne({ _id: projectId });
|
|
84
91
|
if (!project) {
|
|
85
|
-
return res.status(404).json({
|
|
92
|
+
return res.status(404).json({ message: 'Project not found' });
|
|
86
93
|
}
|
|
87
94
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
88
|
-
return res.status(403).json({
|
|
95
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
89
96
|
}
|
|
90
|
-
res.json({ project: { ...project, id: project._id || project.id, _id: undefined } });
|
|
97
|
+
res.json({ project: { ...project, id: project._id || project.id, _id: undefined }, message: 'Project fetched successfully' });
|
|
91
98
|
}
|
|
92
99
|
catch (err) {
|
|
93
|
-
res.status(500).json({
|
|
100
|
+
res.status(500).json({ message: 'Failed to fetch project', details: err.message });
|
|
94
101
|
}
|
|
95
102
|
});
|
|
96
103
|
router.patch('/projects/:id', async (req, res) => {
|
|
97
104
|
try {
|
|
98
105
|
const auth = req.auth;
|
|
99
106
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
100
|
-
return res.status(403).json({
|
|
107
|
+
return res.status(403).json({ message: 'Permission denied' });
|
|
101
108
|
}
|
|
102
109
|
const projectId = req.params.id;
|
|
103
110
|
const updates = req.body;
|
|
104
111
|
const project = await Project.findOne({ _id: projectId });
|
|
105
112
|
if (!project) {
|
|
106
|
-
return res.status(404).json({
|
|
113
|
+
return res.status(404).json({ message: 'Project not found' });
|
|
107
114
|
}
|
|
108
115
|
if (!updates || typeof updates !== 'object') {
|
|
109
|
-
return res.status(400).json({
|
|
116
|
+
return res.status(400).json({ message: 'Missing updates payload' });
|
|
110
117
|
}
|
|
111
118
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
112
|
-
return res.status(403).json({
|
|
119
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
113
120
|
}
|
|
114
121
|
const updated = await Project.update({ _id: projectId }, updates);
|
|
115
122
|
if (!updated) {
|
|
116
|
-
return res.status(404).json({
|
|
123
|
+
return res.status(404).json({ message: 'Project not found or not updated' });
|
|
117
124
|
}
|
|
118
|
-
|
|
125
|
+
const allProjects = await Project.find({});
|
|
126
|
+
const projects = allProjects
|
|
127
|
+
.filter(project => {
|
|
128
|
+
const isMember = project.members?.some((m) => m.userId?.toString() === auth.userId?.toString());
|
|
129
|
+
return isMember; // Only include projects where the user is a member
|
|
130
|
+
})
|
|
131
|
+
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
132
|
+
res.status(200).json({ success: true, projects: projects, projectId: updated._id || updated.id, message: 'Project updated successfully' });
|
|
119
133
|
}
|
|
120
134
|
catch (err) {
|
|
121
|
-
res.status(500).json({
|
|
135
|
+
res.status(500).json({ message: 'Failed to update project', details: err.message });
|
|
122
136
|
}
|
|
123
137
|
});
|
|
124
138
|
router.delete('/projects/:id', async (req, res) => {
|
|
@@ -127,16 +141,23 @@ function projectRouter(config) {
|
|
|
127
141
|
const projectId = req.params.id;
|
|
128
142
|
const project = await Project.findOne({ _id: projectId });
|
|
129
143
|
if (!project) {
|
|
130
|
-
return res.status(404).json({
|
|
144
|
+
return res.status(404).json({ message: 'Project not found' });
|
|
131
145
|
}
|
|
132
146
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
133
|
-
return res.status(403).json({
|
|
147
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
134
148
|
}
|
|
135
149
|
await Project.delete({ _id: projectId });
|
|
136
|
-
|
|
150
|
+
const allProjects = await Project.find({});
|
|
151
|
+
const projects = allProjects
|
|
152
|
+
.filter(project => {
|
|
153
|
+
const isMember = project.members?.some((m) => m.userId?.toString() === auth.userId?.toString());
|
|
154
|
+
return isMember; // Only include projects where the user is a member
|
|
155
|
+
})
|
|
156
|
+
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
157
|
+
res.status(200).json({ success: true, projects: projects, message: 'Project deleted successfully' });
|
|
137
158
|
}
|
|
138
159
|
catch (err) {
|
|
139
|
-
res.status(500).json({
|
|
160
|
+
res.status(500).json({ message: 'Failed to delete project', details: err.message });
|
|
140
161
|
}
|
|
141
162
|
});
|
|
142
163
|
router.patch('/projects/:id/archive', async (req, res) => {
|
|
@@ -145,7 +166,7 @@ function projectRouter(config) {
|
|
|
145
166
|
const projectId = req.params.id;
|
|
146
167
|
const project = await Project.findOne({ _id: projectId });
|
|
147
168
|
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
148
|
-
return res.status(403).json({
|
|
169
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
149
170
|
}
|
|
150
171
|
if (project.dbType === 'sql') {
|
|
151
172
|
const connection = (0, connectionManager_1.getConnection)(config.databases?.find(db => db.main)?.id || '').client;
|
|
@@ -154,7 +175,7 @@ function projectRouter(config) {
|
|
|
154
175
|
else {
|
|
155
176
|
const memberIndex = project.members.findIndex((member) => member.userId === auth.userId);
|
|
156
177
|
if (memberIndex === -1) {
|
|
157
|
-
return res.status(403).json({
|
|
178
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
158
179
|
}
|
|
159
180
|
project.members[memberIndex].archived = true;
|
|
160
181
|
project.members[memberIndex].archivedBy = auth.userId;
|
|
@@ -169,10 +190,10 @@ function projectRouter(config) {
|
|
|
169
190
|
return isMember; // Only include projects where the user is a member
|
|
170
191
|
})
|
|
171
192
|
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
172
|
-
res.status(200).json({ success: true, projects: projects });
|
|
193
|
+
res.status(200).json({ success: true, projects: projects, message: 'Project archived successfully' });
|
|
173
194
|
}
|
|
174
195
|
catch (err) {
|
|
175
|
-
res.status(500).json({
|
|
196
|
+
res.status(500).json({ message: 'Failed to archive project', details: err.message });
|
|
176
197
|
}
|
|
177
198
|
});
|
|
178
199
|
router.patch('/projects/:id/unarchive', async (req, res) => {
|
|
@@ -181,7 +202,7 @@ function projectRouter(config) {
|
|
|
181
202
|
const projectId = req.params.id;
|
|
182
203
|
const project = await Project.findOne({ _id: projectId });
|
|
183
204
|
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
184
|
-
return res.status(403).json({
|
|
205
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
185
206
|
}
|
|
186
207
|
if (project.dbType === 'sql') {
|
|
187
208
|
const connection = (0, connectionManager_1.getConnection)(config.databases?.find(db => db.main)?.id || '').client;
|
|
@@ -190,7 +211,7 @@ function projectRouter(config) {
|
|
|
190
211
|
else {
|
|
191
212
|
const memberIndex = project.members.findIndex((member) => member.userId === auth.userId);
|
|
192
213
|
if (memberIndex === -1) {
|
|
193
|
-
return res.status(403).json({
|
|
214
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
194
215
|
}
|
|
195
216
|
project.members[memberIndex].archived = false;
|
|
196
217
|
project.members[memberIndex].archivedBy = null;
|
|
@@ -205,10 +226,10 @@ function projectRouter(config) {
|
|
|
205
226
|
return isMember; // Only include projects where the user is a member
|
|
206
227
|
})
|
|
207
228
|
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
208
|
-
res.status(200).json({ success: true, projects: projects });
|
|
229
|
+
res.status(200).json({ success: true, projects: projects, message: 'Project unarchived successfully' });
|
|
209
230
|
}
|
|
210
231
|
catch (err) {
|
|
211
|
-
res.status(500).json({
|
|
232
|
+
res.status(500).json({ message: 'Failed to unarchive project', details: err.message });
|
|
212
233
|
}
|
|
213
234
|
});
|
|
214
235
|
router.get('/projects/:id/members', async (req, res) => {
|
|
@@ -217,14 +238,14 @@ function projectRouter(config) {
|
|
|
217
238
|
const projectId = req.params.id;
|
|
218
239
|
const project = await Project.findOne({ _id: projectId });
|
|
219
240
|
if (!project) {
|
|
220
|
-
return res.status(404).json({
|
|
241
|
+
return res.status(404).json({ message: 'Project not found' });
|
|
221
242
|
}
|
|
222
243
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
223
|
-
return res.status(403).json({
|
|
244
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
224
245
|
}
|
|
225
246
|
const mainDb = config.databases?.find(db => db.main);
|
|
226
247
|
if (!mainDb) {
|
|
227
|
-
return res.status(500).json({
|
|
248
|
+
return res.status(500).json({ message: 'Main database not configured' });
|
|
228
249
|
}
|
|
229
250
|
const userDbType = mainDb.type;
|
|
230
251
|
const User = (0, manager_1.getModel)('users', userDbType);
|
|
@@ -237,38 +258,38 @@ function projectRouter(config) {
|
|
|
237
258
|
profilePic: user ? user.profilePic : ''
|
|
238
259
|
};
|
|
239
260
|
}));
|
|
240
|
-
res.json({ members: membersWithUsernames });
|
|
261
|
+
res.json({ members: membersWithUsernames, message: 'Project members fetched successfully' });
|
|
241
262
|
}
|
|
242
263
|
catch (err) {
|
|
243
|
-
res.status(500).json({
|
|
264
|
+
res.status(500).json({ message: 'Failed to fetch project members', details: err.message });
|
|
244
265
|
}
|
|
245
266
|
});
|
|
246
267
|
router.post('/projects/:id/image', async (req, res) => {
|
|
247
268
|
try {
|
|
248
269
|
const auth = req.auth;
|
|
249
270
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
250
|
-
return res.status(403).json({
|
|
271
|
+
return res.status(403).json({ message: 'Permission denied' });
|
|
251
272
|
}
|
|
252
273
|
const projectId = req.params.id;
|
|
253
274
|
const { imageUrl } = req.body;
|
|
254
275
|
if (!projectId || !imageUrl) {
|
|
255
|
-
return res.status(400).json({
|
|
276
|
+
return res.status(400).json({ message: 'Missing projectId or imageUrl' });
|
|
256
277
|
}
|
|
257
278
|
const project = await Project.findOne({ _id: projectId });
|
|
258
279
|
if (!project) {
|
|
259
|
-
return res.status(404).json({
|
|
280
|
+
return res.status(404).json({ message: 'Project not found' });
|
|
260
281
|
}
|
|
261
282
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
262
|
-
return res.status(403).json({
|
|
283
|
+
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
263
284
|
}
|
|
264
285
|
const updatedProject = await Project.update({ _id: projectId }, { image: imageUrl });
|
|
265
286
|
if (!updatedProject) {
|
|
266
|
-
return res.status(404).json({
|
|
287
|
+
return res.status(404).json({ message: 'Project not found or not updated' });
|
|
267
288
|
}
|
|
268
|
-
res.status(200).json({ success: true, project: { ...updatedProject, id: updatedProject._id || updatedProject.id, _id: undefined } });
|
|
289
|
+
res.status(200).json({ success: true, project: { ...updatedProject, id: updatedProject._id || updatedProject.id, _id: undefined }, message: 'Project image updated successfully' });
|
|
269
290
|
}
|
|
270
291
|
catch (err) {
|
|
271
|
-
res.status(500).json({
|
|
292
|
+
res.status(500).json({ message: 'Failed to update project image', details: err.message });
|
|
272
293
|
}
|
|
273
294
|
});
|
|
274
295
|
return router;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { StreamByConfig, StorageAdapter } from '../../types';
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function storageRouter(config: StreamByConfig & {
|
|
4
4
|
adapter?: StorageAdapter;
|
|
5
5
|
}): Router;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.storageRouter = storageRouter;
|
|
4
4
|
const express_1 = require("express");
|
|
5
5
|
const file_1 = require("../../services/file");
|
|
6
6
|
const presign_1 = require("../../services/presign");
|
|
7
7
|
const storage_1 = require("../../providers/storage");
|
|
8
8
|
const manager_1 = require("../../models/manager");
|
|
9
|
-
function
|
|
9
|
+
function storageRouter(config) {
|
|
10
10
|
const router = (0, express_1.Router)();
|
|
11
11
|
const adapter = config.adapter || (0, storage_1.createStorageProvider)(config.storageProviders);
|
|
12
12
|
const Project = (0, manager_1.getModel)('projects');
|
|
@@ -16,20 +16,20 @@ function fileRouter(config) {
|
|
|
16
16
|
if (!auth ||
|
|
17
17
|
!auth.userId ||
|
|
18
18
|
!auth.role) {
|
|
19
|
-
return res.status(401).json({
|
|
19
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
20
20
|
}
|
|
21
21
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
22
|
-
return res.status(403).json({
|
|
22
|
+
return res.status(403).json({ message: 'Permission denied' });
|
|
23
23
|
}
|
|
24
24
|
const projectId = req.params.id;
|
|
25
25
|
if (!projectId) {
|
|
26
|
-
return res.status(400).json({
|
|
26
|
+
return res.status(400).json({ message: 'Missing projectId' });
|
|
27
27
|
}
|
|
28
28
|
const response = await (0, presign_1.getPresignedProjectImageUrl)(adapter, projectId);
|
|
29
|
-
res.status(200).json(response);
|
|
29
|
+
res.status(200).json({ ...response, message: 'Presigned URL generated successfully' });
|
|
30
30
|
}
|
|
31
31
|
catch (err) {
|
|
32
|
-
res.status(500).json({
|
|
32
|
+
res.status(500).json({ message: 'Failed to generate presigned URL', details: err.message });
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
router.delete('/delete-project-image-url/:id', async (req, res) => {
|
|
@@ -38,40 +38,40 @@ function fileRouter(config) {
|
|
|
38
38
|
if (!auth ||
|
|
39
39
|
!auth.userId ||
|
|
40
40
|
!auth.role) {
|
|
41
|
-
return res.status(401).json({
|
|
41
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
42
42
|
}
|
|
43
43
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
44
|
-
return res.status(403).json({
|
|
44
|
+
return res.status(403).json({ message: 'Permission denied' });
|
|
45
45
|
}
|
|
46
46
|
const projectId = req.params.id;
|
|
47
47
|
if (!projectId) {
|
|
48
|
-
return res.status(400).json({
|
|
48
|
+
return res.status(400).json({ message: 'Missing projectId' });
|
|
49
49
|
}
|
|
50
50
|
await (0, file_1.deleteProjectImage)(adapter, projectId);
|
|
51
51
|
const updated = await Project.update({ _id: projectId }, { image: '' });
|
|
52
|
-
res.status(201).json(updated);
|
|
52
|
+
res.status(201).json({ ...updated, message: 'Image deleted successfully' });
|
|
53
53
|
}
|
|
54
54
|
catch (err) {
|
|
55
|
-
res.status(500).json({
|
|
55
|
+
res.status(500).json({ message: 'Failed to delete image', details: err.message });
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
|
-
router.get('/
|
|
58
|
+
router.get('/storages', async (req, res) => {
|
|
59
59
|
try {
|
|
60
60
|
const auth = await config.authProvider(req);
|
|
61
61
|
if (!auth ||
|
|
62
62
|
!auth.userId ||
|
|
63
63
|
!auth.role) {
|
|
64
|
-
return res.status(401).json({
|
|
64
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
65
65
|
}
|
|
66
66
|
const projectId = (req.query.projectId || req.headers['x-project-id']);
|
|
67
67
|
if (!projectId) {
|
|
68
|
-
return res.status(403).json({
|
|
68
|
+
return res.status(403).json({ message: 'Unauthorized or missing projectId' });
|
|
69
69
|
}
|
|
70
70
|
const files = await (0, file_1.listFilesService)(adapter, req, projectId);
|
|
71
|
-
res.json(files);
|
|
71
|
+
res.json({ files, message: 'Files listed successfully' });
|
|
72
72
|
}
|
|
73
73
|
catch (err) {
|
|
74
|
-
res.status(500).json({
|
|
74
|
+
res.status(500).json({ message: 'Failed to list files', details: err });
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
77
|
return router;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FieldDefinition, DatabaseType } from '../types';
|
|
2
|
+
interface CreateExportResult {
|
|
3
|
+
exportId: string;
|
|
4
|
+
collectionName: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
import { StreamByConfig } from '../types';
|
|
8
|
+
export declare function createExport(config: StreamByConfig, projectId: string, exportName: string, collectionName: string, fields: FieldDefinition[], dbType: DatabaseType): Promise<CreateExportResult>;
|
|
9
|
+
export declare function createRawExport(config: StreamByConfig, projectId: string, exportName: string, collectionName: string, jsonData: any, dbType: DatabaseType): Promise<CreateExportResult>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createExport = createExport;
|
|
4
|
+
exports.createRawExport = createRawExport;
|
|
5
|
+
const manager_1 = require("../models/manager");
|
|
6
|
+
const connectionManager_1 = require("../adapters/database/connectionManager");
|
|
7
|
+
const nosql_1 = require("../adapters/database/nosql");
|
|
8
|
+
const sql_1 = require("../adapters/database/sql");
|
|
9
|
+
async function createExport(config, projectId, exportName, collectionName, fields, dbType) {
|
|
10
|
+
const Project = (0, manager_1.getModel)('projects');
|
|
11
|
+
const project = await Project.findOne({ _id: projectId });
|
|
12
|
+
if (!project) {
|
|
13
|
+
throw new Error('Project not found');
|
|
14
|
+
}
|
|
15
|
+
const targetDb = config.databases?.find(db => db.type === dbType && db.main) ||
|
|
16
|
+
config.databases?.find(db => db.type === dbType);
|
|
17
|
+
if (!targetDb) {
|
|
18
|
+
throw new Error(`Database connection not found for type ${dbType}`);
|
|
19
|
+
}
|
|
20
|
+
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
|
|
21
|
+
let result;
|
|
22
|
+
if (dbType === 'nosql') {
|
|
23
|
+
result = await (0, nosql_1.createNoSQLExportCollection)(connection.client, projectId, exportName, fields);
|
|
24
|
+
}
|
|
25
|
+
else if (dbType === 'sql') {
|
|
26
|
+
result = await (0, sql_1.createSQLExportTable)(connection.client, projectId, exportName, fields);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
throw new Error('Unsupported database type');
|
|
30
|
+
}
|
|
31
|
+
// Update the project with the new export reference
|
|
32
|
+
// Assuming project metadata is always in NoSQL (MongoDB)
|
|
33
|
+
const NoSQLProject = (0, manager_1.getModel)('projects', 'nosql');
|
|
34
|
+
await NoSQLProject.update({ _id: projectId }, { $push: { exports: { id: result.exportId, collectionName: result.collectionName, type: 'structured', fields } } });
|
|
35
|
+
return { ...result, message: 'Export created successfully' };
|
|
36
|
+
}
|
|
37
|
+
async function createRawExport(config, projectId, exportName, collectionName, jsonData, dbType) {
|
|
38
|
+
const targetDb = config.databases?.find(db => db.type === dbType && db.main) ||
|
|
39
|
+
config.databases?.find(db => db.type === dbType);
|
|
40
|
+
if (!targetDb) {
|
|
41
|
+
throw new Error(`Database connection not found for type ${dbType}`);
|
|
42
|
+
}
|
|
43
|
+
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
|
|
44
|
+
let result;
|
|
45
|
+
if (dbType === 'nosql') {
|
|
46
|
+
result = await (0, nosql_1.createNoSQLRawExportCollection)(connection.client, projectId, exportName, jsonData);
|
|
47
|
+
}
|
|
48
|
+
else if (dbType === 'sql') {
|
|
49
|
+
result = await (0, sql_1.createSQLRawExportTable)(connection.client, projectId, exportName, jsonData);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw new Error('Unsupported database type');
|
|
53
|
+
}
|
|
54
|
+
// Update the project with the new export reference
|
|
55
|
+
// Assuming project metadata is always in NoSQL (MongoDB)
|
|
56
|
+
const NoSQLProject = (0, manager_1.getModel)('projects', 'nosql');
|
|
57
|
+
await NoSQLProject.update({ _id: projectId }, { $push: { exports: { id: result.exportId, collectionName: result.collectionName, type: 'raw', jsonData } } });
|
|
58
|
+
return { ...result, message: 'Raw export created successfully' };
|
|
59
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export interface StorageAdapter {
|
|
|
17
17
|
getPresignedUrl?: (contentType: string, projectId: string) => Promise<any>;
|
|
18
18
|
getPresignedProjectImageUrl?: (projectId: string) => Promise<any>;
|
|
19
19
|
}
|
|
20
|
+
export interface FieldDefinition {
|
|
21
|
+
name: string;
|
|
22
|
+
type: string;
|
|
23
|
+
label: string;
|
|
24
|
+
required?: boolean;
|
|
25
|
+
}
|
|
20
26
|
export interface ProjectInfo {
|
|
21
27
|
_id?: string;
|
|
22
28
|
id: string;
|
|
@@ -74,4 +80,7 @@ export interface Export {
|
|
|
74
80
|
projectId: string;
|
|
75
81
|
createdAt: Date;
|
|
76
82
|
updatedAt: Date;
|
|
83
|
+
type: 'structured' | 'raw';
|
|
84
|
+
fields?: FieldDefinition[];
|
|
85
|
+
jsonData?: any;
|
|
77
86
|
}
|