streamby-core 0.26.4 → 0.26.5
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/middleware/createRouter.js +19 -19
- package/dist/middleware/routes/connection.d.ts +1 -1
- package/dist/middleware/routes/connection.js +2 -2
- package/dist/middleware/routes/export.js +38 -12
- package/dist/middleware/routes/member.js +4 -3
- package/dist/middleware/routes/notification.js +45 -7
- package/dist/middleware/routes/project.js +58 -19
- package/dist/middleware/routes/storage.js +116 -125
- package/dist/models/Model.js +1 -0
- package/dist/services/notification.js +20 -1
- package/package.json +1 -1
|
@@ -8,19 +8,19 @@ const express_1 = __importDefault(require("express"));
|
|
|
8
8
|
const manager_1 = require("../models/manager");
|
|
9
9
|
const storage_1 = require("../providers/storage");
|
|
10
10
|
const connectionManager_1 = require("../adapters/database/connectionManager");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const wsHub_1 = require("../services/wsHub");
|
|
12
|
+
const auth_1 = require("../services/auth");
|
|
13
|
+
const encryption_1 = require("../utils/encryption");
|
|
14
|
+
const auth_2 = require("./routes/auth");
|
|
15
|
+
const user_1 = require("./routes/user");
|
|
16
|
+
const export_1 = require("./routes/export");
|
|
17
|
+
const member_1 = require("./routes/member");
|
|
13
18
|
const storage_2 = require("./routes/storage");
|
|
14
19
|
const project_1 = require("./routes/project");
|
|
15
|
-
const
|
|
20
|
+
const database_1 = require("./routes/database");
|
|
16
21
|
const credential_1 = require("./routes/credential");
|
|
17
22
|
const connection_1 = require("./routes/connection");
|
|
18
|
-
const member_1 = require("./routes/member");
|
|
19
|
-
const user_1 = require("./routes/user");
|
|
20
23
|
const notification_1 = require("./routes/notification");
|
|
21
|
-
const auth_2 = require("../services/auth");
|
|
22
|
-
const encryption_1 = require("../utils/encryption");
|
|
23
|
-
const wsHub_1 = require("../services/wsHub");
|
|
24
24
|
function createStreamByRouter(config) {
|
|
25
25
|
const router = express_1.default.Router();
|
|
26
26
|
if (config.encrypt)
|
|
@@ -29,13 +29,13 @@ function createStreamByRouter(config) {
|
|
|
29
29
|
const adapter = config.adapter || (0, storage_1.createStorageProvider)(config.storageProviders);
|
|
30
30
|
if (config.databases) {
|
|
31
31
|
const allDbIds = config.databases.map(db => db.id);
|
|
32
|
-
const
|
|
33
|
-
const streambySchema = sqlDbs.length > 0 ? 'streamby' : undefined;
|
|
34
|
-
(0, manager_1.registerModel)('projects', allDbIds, 'projects', streambySchema);
|
|
32
|
+
const streambySchema = 'streamby';
|
|
35
33
|
(0, manager_1.registerModel)('exports', allDbIds, 'exports', streambySchema);
|
|
34
|
+
(0, manager_1.registerModel)('projects', allDbIds, 'projects', streambySchema);
|
|
36
35
|
const nosqlDbIds = config.databases.filter(db => db.type === 'nosql').map(db => db.id);
|
|
37
36
|
if (nosqlDbIds.length > 0) {
|
|
38
|
-
(0, manager_1.registerModel)('notifications', nosqlDbIds, 'notifications');
|
|
37
|
+
(0, manager_1.registerModel)('notifications', nosqlDbIds, 'notifications', streambySchema);
|
|
38
|
+
(0, manager_1.registerModel)('storage_files', nosqlDbIds, 'storage_files');
|
|
39
39
|
}
|
|
40
40
|
const mainDb = config.databases.find(db => db.main);
|
|
41
41
|
if (mainDb) {
|
|
@@ -45,16 +45,16 @@ function createStreamByRouter(config) {
|
|
|
45
45
|
if (config.websocket?.server) {
|
|
46
46
|
(0, wsHub_1.initWsHub)(config.websocket.server, config, config.websocket.path);
|
|
47
47
|
}
|
|
48
|
-
router.use((0,
|
|
49
|
-
router.use((0,
|
|
50
|
-
router.use((0,
|
|
48
|
+
router.use((0, user_1.userRouter)(config));
|
|
49
|
+
router.use((0, auth_2.authRouter)(config));
|
|
50
|
+
router.use((0, auth_1.authenticate)(config));
|
|
51
|
+
router.use((0, export_1.exportRouter)(config));
|
|
52
|
+
router.use((0, member_1.memberRouter)(config));
|
|
51
53
|
router.use((0, storage_2.storageRouter)(config));
|
|
52
54
|
router.use((0, project_1.projectRouter)(config));
|
|
53
|
-
router.use((0,
|
|
55
|
+
router.use((0, database_1.databaseRouter)(config));
|
|
54
56
|
router.use((0, credential_1.credentialRouter)(config));
|
|
55
|
-
router.use((0, connection_1.
|
|
56
|
-
router.use((0, member_1.memberRouter)(config));
|
|
57
|
-
router.use((0, user_1.userRouter)(config));
|
|
57
|
+
router.use((0, connection_1.connectionRouter)(config));
|
|
58
58
|
router.use((0, notification_1.notificationRouter)(config));
|
|
59
59
|
return router;
|
|
60
60
|
}
|
|
@@ -33,13 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
36
|
+
exports.connectionRouter = connectionRouter;
|
|
37
37
|
const express_1 = require("express");
|
|
38
38
|
const manager_1 = require("../../models/manager");
|
|
39
39
|
const auth_1 = require("../../utils/auth");
|
|
40
40
|
const apiConnection_1 = require("../../services/apiConnection");
|
|
41
41
|
const VALID_METHODS = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'];
|
|
42
|
-
function
|
|
42
|
+
function connectionRouter(config) {
|
|
43
43
|
const router = (0, express_1.Router)();
|
|
44
44
|
const Project = (0, manager_1.getModel)('projects');
|
|
45
45
|
// List API connections for a project
|
|
@@ -9,6 +9,37 @@ const pipeline_1 = require("../../services/pipeline");
|
|
|
9
9
|
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
10
10
|
const mongodb_1 = require("mongodb");
|
|
11
11
|
const encryption_1 = require("../../utils/encryption");
|
|
12
|
+
const DEFAULT_NODE_SCHEMA = {
|
|
13
|
+
nodes: [
|
|
14
|
+
{
|
|
15
|
+
id: 'client',
|
|
16
|
+
type: 'clientNode',
|
|
17
|
+
position: { x: 0, y: 100 },
|
|
18
|
+
data: { label: 'Client', subtitle: 'GET' },
|
|
19
|
+
width: 148,
|
|
20
|
+
height: 100,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'streamby',
|
|
24
|
+
type: 'streambyNode',
|
|
25
|
+
position: { x: 240, y: 100 },
|
|
26
|
+
data: { label: 'StreamBy', subtitle: 'Middleware' },
|
|
27
|
+
width: 158,
|
|
28
|
+
height: 100,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
edges: [
|
|
32
|
+
{
|
|
33
|
+
id: 'e-client-streamby',
|
|
34
|
+
source: 'client',
|
|
35
|
+
sourceHandle: 'out-right',
|
|
36
|
+
target: 'streamby',
|
|
37
|
+
targetHandle: 'in-left',
|
|
38
|
+
animated: true,
|
|
39
|
+
style: { stroke: '#38B6FF', strokeWidth: 2 },
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
12
43
|
function exportRouter(config) {
|
|
13
44
|
const router = (0, express_1.Router)();
|
|
14
45
|
const Project = (0, manager_1.getModel)('projects');
|
|
@@ -162,15 +193,13 @@ function exportRouter(config) {
|
|
|
162
193
|
if (!name) {
|
|
163
194
|
return res.status(400).json({ message: 'Missing export name' });
|
|
164
195
|
}
|
|
165
|
-
|
|
166
|
-
return res.status(400).json({ message: 'Missing nodeSchema' });
|
|
167
|
-
}
|
|
196
|
+
const resolvedNodeSchema = nodeSchema ?? DEFAULT_NODE_SCHEMA;
|
|
168
197
|
const exportType = useConnections ? 'externalApi' : 'json';
|
|
169
198
|
const project = await Project.findOne({ _id: projectId });
|
|
170
199
|
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
171
200
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
172
201
|
}
|
|
173
|
-
const result = await (0, export_1.createExport)(config, projectId, description, name, project.dbType, exportType, isPrivate, allowedOrigin,
|
|
202
|
+
const result = await (0, export_1.createExport)(config, projectId, description, name, project.dbType, exportType, isPrivate, allowedOrigin, resolvedNodeSchema, useConnections, useCredentials);
|
|
174
203
|
res.status(201).json({ data: result, message: result.message });
|
|
175
204
|
}
|
|
176
205
|
catch (err) {
|
|
@@ -185,13 +214,6 @@ function exportRouter(config) {
|
|
|
185
214
|
}
|
|
186
215
|
const { id: projectId, export_id: exportId } = req.params;
|
|
187
216
|
const { name, description, isPrivate, allowedOrigin, useConnections, useCredentials, nodeSchema } = req.body;
|
|
188
|
-
if (!name) {
|
|
189
|
-
return res.status(400).json({ message: 'Missing name' });
|
|
190
|
-
}
|
|
191
|
-
if (!nodeSchema) {
|
|
192
|
-
return res.status(400).json({ message: 'Missing nodeSchema' });
|
|
193
|
-
}
|
|
194
|
-
const exportType = useConnections ? 'externalApi' : 'json';
|
|
195
217
|
const project = await Project.findOne({ _id: projectId });
|
|
196
218
|
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
197
219
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
@@ -200,7 +222,11 @@ function exportRouter(config) {
|
|
|
200
222
|
if (!exportMetadata) {
|
|
201
223
|
return res.status(404).json({ message: 'Export not found' });
|
|
202
224
|
}
|
|
203
|
-
const
|
|
225
|
+
const resolvedName = name ?? exportMetadata.name;
|
|
226
|
+
const resolvedNodeSchema = nodeSchema ?? exportMetadata.nodeSchema;
|
|
227
|
+
const resolvedUseConnections = useConnections ?? exportMetadata.useConnections;
|
|
228
|
+
const exportType = resolvedUseConnections ? 'externalApi' : 'json';
|
|
229
|
+
const result = await (0, export_1.updateExport)(config, projectId, exportId, description ?? exportMetadata.description, resolvedName, project.dbType, exportType, isPrivate ?? exportMetadata.private, allowedOrigin ?? exportMetadata.allowedOrigin, resolvedNodeSchema, resolvedUseConnections, useCredentials ?? exportMetadata.useCredentials);
|
|
204
230
|
res.status(200).json({ data: result, message: result.message });
|
|
205
231
|
}
|
|
206
232
|
catch (err) {
|
|
@@ -32,14 +32,15 @@ function memberRouter(config) {
|
|
|
32
32
|
if (!mainDb) {
|
|
33
33
|
return res.status(500).json({ message: 'Main database not configured' });
|
|
34
34
|
}
|
|
35
|
-
const User = (0, manager_1.getModel)('users');
|
|
35
|
+
const User = (0, manager_1.getModel)('users', mainDb.type);
|
|
36
36
|
const membersWithUsernames = await Promise.all(project.members.map(async (member) => {
|
|
37
37
|
const user = await User.findOne({ _id: member.userId });
|
|
38
38
|
return {
|
|
39
39
|
userId: member.userId,
|
|
40
40
|
username: user ? user.username : 'Unknown',
|
|
41
41
|
role: member.role,
|
|
42
|
-
|
|
42
|
+
status: member.status,
|
|
43
|
+
profilePic: user ? user.profilePic : user.googlePic,
|
|
43
44
|
};
|
|
44
45
|
}));
|
|
45
46
|
res.json({ members: membersWithUsernames, message: 'Project members fetched successfully' });
|
|
@@ -96,7 +97,7 @@ function memberRouter(config) {
|
|
|
96
97
|
else {
|
|
97
98
|
await Project.update({ _id: projectId }, { $push: { members: newMember } });
|
|
98
99
|
}
|
|
99
|
-
await (0, notification_1.createNotification)(userId, 'member_invited', `
|
|
100
|
+
await (0, notification_1.createNotification)(userId, 'member_invited', `You has been invited to join the project "${project.name}"`, { projectId, role, invitedBy: auth.userId });
|
|
100
101
|
res.status(201).json({ member: newMember, message: 'Member invited successfully' });
|
|
101
102
|
}
|
|
102
103
|
catch (err) {
|
|
@@ -3,14 +3,44 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.notificationRouter = notificationRouter;
|
|
4
4
|
const express_1 = require("express");
|
|
5
5
|
const manager_1 = require("../../models/manager");
|
|
6
|
+
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
7
|
+
const mongodb_1 = require("mongodb");
|
|
8
|
+
const MAX_NOTIFICATIONS = 20;
|
|
9
|
+
function getRawNotifCollection() {
|
|
10
|
+
const model = (0, manager_1.getModel)('notifications', 'nosql');
|
|
11
|
+
const connectionIds = model.getConnectionIds();
|
|
12
|
+
const activeId = connectionIds.find((id) => (0, connectionManager_1.getConnectedIds)().includes(id) && (0, connectionManager_1.getConnection)(id).type === 'nosql');
|
|
13
|
+
if (!activeId)
|
|
14
|
+
return null;
|
|
15
|
+
return (0, connectionManager_1.getConnection)(activeId).client.db().collection('notifications');
|
|
16
|
+
}
|
|
6
17
|
function notificationRouter(config) {
|
|
7
18
|
const router = (0, express_1.Router)();
|
|
8
|
-
const Notification = (0, manager_1.getModel)('notifications');
|
|
9
19
|
router.get('/notifications', async (req, res) => {
|
|
10
20
|
try {
|
|
11
21
|
const auth = req.auth;
|
|
12
|
-
const
|
|
13
|
-
|
|
22
|
+
const limit = Math.min(parseInt(req.query.limit) || MAX_NOTIFICATIONS, MAX_NOTIFICATIONS);
|
|
23
|
+
const page = Math.max(parseInt(req.query.page) || 1, 1);
|
|
24
|
+
const skip = (page - 1) * limit;
|
|
25
|
+
const appIdQuery = req.query.appId;
|
|
26
|
+
const collection = getRawNotifCollection();
|
|
27
|
+
if (!collection)
|
|
28
|
+
return res.status(500).json({ message: 'Database not available' });
|
|
29
|
+
const filter = { userId: auth.userId.toString() };
|
|
30
|
+
if (appIdQuery !== undefined) {
|
|
31
|
+
filter.appId = appIdQuery === '0' ? null : appIdQuery;
|
|
32
|
+
}
|
|
33
|
+
const total = await collection.countDocuments(filter);
|
|
34
|
+
const notifications = await collection
|
|
35
|
+
.find(filter)
|
|
36
|
+
.sort({ createdAt: -1 })
|
|
37
|
+
.skip(skip)
|
|
38
|
+
.limit(limit)
|
|
39
|
+
.toArray();
|
|
40
|
+
res.status(200).json({
|
|
41
|
+
data: notifications,
|
|
42
|
+
pagination: { page, limit, total, pages: Math.ceil(total / limit) },
|
|
43
|
+
});
|
|
14
44
|
}
|
|
15
45
|
catch (err) {
|
|
16
46
|
res.status(500).json({ message: 'Failed to fetch notifications', details: err.message });
|
|
@@ -19,10 +49,15 @@ function notificationRouter(config) {
|
|
|
19
49
|
router.patch('/notifications/read-all', async (req, res) => {
|
|
20
50
|
try {
|
|
21
51
|
const auth = req.auth;
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
52
|
+
const appIdQuery = req.query.appId;
|
|
53
|
+
const collection = getRawNotifCollection();
|
|
54
|
+
if (!collection)
|
|
55
|
+
return res.status(500).json({ message: 'Database not available' });
|
|
56
|
+
const filter = { userId: auth.userId.toString(), read: false };
|
|
57
|
+
if (appIdQuery !== undefined) {
|
|
58
|
+
filter.appId = appIdQuery === '0' ? null : appIdQuery;
|
|
25
59
|
}
|
|
60
|
+
await collection.updateMany(filter, { $set: { read: true, readAt: new Date() } });
|
|
26
61
|
res.status(200).json({ message: 'All notifications marked as read' });
|
|
27
62
|
}
|
|
28
63
|
catch (err) {
|
|
@@ -33,7 +68,10 @@ function notificationRouter(config) {
|
|
|
33
68
|
try {
|
|
34
69
|
const auth = req.auth;
|
|
35
70
|
const { id } = req.params;
|
|
36
|
-
|
|
71
|
+
const collection = getRawNotifCollection();
|
|
72
|
+
if (!collection)
|
|
73
|
+
return res.status(500).json({ message: 'Database not available' });
|
|
74
|
+
await collection.updateOne({ _id: new mongodb_1.ObjectId(id), userId: auth.userId.toString() }, { $set: { read: true, readAt: new Date() } });
|
|
37
75
|
res.status(200).json({ message: 'Notification marked as read' });
|
|
38
76
|
}
|
|
39
77
|
catch (err) {
|
|
@@ -6,10 +6,8 @@ const manager_1 = require("../../models/manager");
|
|
|
6
6
|
const auth_1 = require("../../utils/auth");
|
|
7
7
|
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
8
8
|
const sql_1 = require("../../adapters/database/sql");
|
|
9
|
-
const auth_2 = require("../../services/auth");
|
|
10
9
|
function projectRouter(config) {
|
|
11
10
|
const router = (0, express_1.Router)();
|
|
12
|
-
router.use((0, auth_2.authenticate)(config));
|
|
13
11
|
const Project = (0, manager_1.getModel)('projects');
|
|
14
12
|
const mapProjectToResponseFormat = (project, userId) => {
|
|
15
13
|
const currentUserMember = project.members?.find((member) => member.userId === userId);
|
|
@@ -29,12 +27,11 @@ function projectRouter(config) {
|
|
|
29
27
|
const allProjects = await Project.find({}); // Fetch all projects
|
|
30
28
|
const projects = allProjects
|
|
31
29
|
.filter(project => {
|
|
32
|
-
const
|
|
33
|
-
if (!
|
|
30
|
+
const currentUserMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
|
|
31
|
+
if (!currentUserMember || currentUserMember.status !== 'active')
|
|
34
32
|
return false;
|
|
35
33
|
if (filterArchived !== undefined) {
|
|
36
|
-
|
|
37
|
-
return currentUserMember ? (currentUserMember.archived || false) === filterArchived : false;
|
|
34
|
+
return (currentUserMember.archived || false) === filterArchived;
|
|
38
35
|
}
|
|
39
36
|
return true;
|
|
40
37
|
})
|
|
@@ -69,13 +66,15 @@ function projectRouter(config) {
|
|
|
69
66
|
description: description || '',
|
|
70
67
|
image: image || '',
|
|
71
68
|
allowedOrigin: allowedOrigin || [],
|
|
72
|
-
members: [{ userId: auth.userId, username: user.username, role: "admin", archived: false }],
|
|
69
|
+
members: [{ userId: auth.userId, username: user.username, role: "admin", status: "active", archived: false }],
|
|
73
70
|
});
|
|
74
71
|
const allProjects = await Project.find({});
|
|
75
72
|
const projects = allProjects
|
|
76
73
|
.filter(project => {
|
|
77
|
-
const
|
|
78
|
-
|
|
74
|
+
const currentUserMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
|
|
75
|
+
if (!currentUserMember || currentUserMember.status !== 'active')
|
|
76
|
+
return false;
|
|
77
|
+
return true;
|
|
79
78
|
})
|
|
80
79
|
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
81
80
|
res.status(200).json({ success: true, projects: projects, projectId: newProject._id || newProject.id, message: 'Project created successfully' });
|
|
@@ -85,6 +84,38 @@ function projectRouter(config) {
|
|
|
85
84
|
res.status(500).json({ message: 'Failed to create project', details: err.message });
|
|
86
85
|
}
|
|
87
86
|
});
|
|
87
|
+
router.get('/projects/:id/preview', async (req, res) => {
|
|
88
|
+
try {
|
|
89
|
+
const auth = req.auth;
|
|
90
|
+
const projectId = req.params.id;
|
|
91
|
+
const project = await Project.findOne({ _id: projectId });
|
|
92
|
+
if (!project) {
|
|
93
|
+
return res.status(404).json({ message: 'Project not found' });
|
|
94
|
+
}
|
|
95
|
+
const currentMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
|
|
96
|
+
const membership = currentMember
|
|
97
|
+
? { isMember: true, status: currentMember.status || 'active' }
|
|
98
|
+
: { isMember: false, status: null };
|
|
99
|
+
res.json({
|
|
100
|
+
membership,
|
|
101
|
+
project: {
|
|
102
|
+
id: project._id || project.id,
|
|
103
|
+
name: project.name,
|
|
104
|
+
description: project.description || '',
|
|
105
|
+
image: project.image || '',
|
|
106
|
+
dbType: project.dbType,
|
|
107
|
+
members: (project.members || [])
|
|
108
|
+
.filter((m) => !m.archived)
|
|
109
|
+
.map((m) => ({ role: m.role })),
|
|
110
|
+
exports: (project.exports || []).map((e) => ({ method: e.method })),
|
|
111
|
+
},
|
|
112
|
+
message: 'Project preview fetched successfully',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
res.status(500).json({ message: 'Failed to fetch project preview', details: err.message });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
88
119
|
router.get('/projects/:id', async (req, res) => {
|
|
89
120
|
try {
|
|
90
121
|
const auth = req.auth;
|
|
@@ -136,8 +167,10 @@ function projectRouter(config) {
|
|
|
136
167
|
const allProjects = await Project.find({});
|
|
137
168
|
const projects = allProjects
|
|
138
169
|
.filter(project => {
|
|
139
|
-
const
|
|
140
|
-
|
|
170
|
+
const currentUserMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
|
|
171
|
+
if (!currentUserMember || currentUserMember.status !== 'active')
|
|
172
|
+
return false;
|
|
173
|
+
return true;
|
|
141
174
|
})
|
|
142
175
|
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
143
176
|
res.status(200).json({ success: true, projects: projects, projectId: updated._id || updated.id, message: 'Project updated successfully' });
|
|
@@ -160,11 +193,13 @@ function projectRouter(config) {
|
|
|
160
193
|
await Project.useDbType(project.dbType).delete({ _id: projectId });
|
|
161
194
|
const allProjects = await Project.find({});
|
|
162
195
|
const projects = allProjects
|
|
163
|
-
.filter(
|
|
164
|
-
const
|
|
165
|
-
|
|
196
|
+
.filter(project => {
|
|
197
|
+
const currentUserMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
|
|
198
|
+
if (!currentUserMember || currentUserMember.status !== 'active')
|
|
199
|
+
return false;
|
|
200
|
+
return true;
|
|
166
201
|
})
|
|
167
|
-
.map(
|
|
202
|
+
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
168
203
|
res.status(200).json({ success: true, projects: projects, message: 'Project deleted successfully' });
|
|
169
204
|
}
|
|
170
205
|
catch (err) {
|
|
@@ -197,8 +232,10 @@ function projectRouter(config) {
|
|
|
197
232
|
const allProjects = await Project.find({});
|
|
198
233
|
const projects = allProjects
|
|
199
234
|
.filter(project => {
|
|
200
|
-
const
|
|
201
|
-
|
|
235
|
+
const currentUserMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
|
|
236
|
+
if (!currentUserMember || currentUserMember.status !== 'active')
|
|
237
|
+
return false;
|
|
238
|
+
return true;
|
|
202
239
|
})
|
|
203
240
|
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
204
241
|
res.status(200).json({ success: true, projects: projects, message: 'Project archived successfully' });
|
|
@@ -233,8 +270,10 @@ function projectRouter(config) {
|
|
|
233
270
|
const allProjects = await Project.find({});
|
|
234
271
|
const projects = allProjects
|
|
235
272
|
.filter(project => {
|
|
236
|
-
const
|
|
237
|
-
|
|
273
|
+
const currentUserMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
|
|
274
|
+
if (!currentUserMember || currentUserMember.status !== 'active')
|
|
275
|
+
return false;
|
|
276
|
+
return true;
|
|
238
277
|
})
|
|
239
278
|
.map(project => mapProjectToResponseFormat(project, auth.userId));
|
|
240
279
|
res.status(200).json({ success: true, projects: projects, message: 'Project unarchived successfully' });
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.storageRouter = storageRouter;
|
|
4
7
|
const express_1 = require("express");
|
|
@@ -6,7 +9,10 @@ const file_1 = require("../../services/file");
|
|
|
6
9
|
const presign_1 = require("../../services/presign");
|
|
7
10
|
const storage_1 = require("../../providers/storage");
|
|
8
11
|
const manager_1 = require("../../models/manager");
|
|
12
|
+
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
9
13
|
const auth_1 = require("../../utils/auth");
|
|
14
|
+
const mongodb_1 = require("mongodb");
|
|
15
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
16
|
const VALID_CATEGORIES = ['images', 'audios', 'videos', '3d-models'];
|
|
11
17
|
const VALID_3D_EXTENSIONS = new Set(['.glb', '.gltf', '.obj', '.fbx', '.stl', '.ply']);
|
|
12
18
|
function validateContentType(contentType, filename, category) {
|
|
@@ -22,11 +28,13 @@ function validateContentType(contentType, filename, category) {
|
|
|
22
28
|
}
|
|
23
29
|
return false;
|
|
24
30
|
}
|
|
25
|
-
function
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
31
|
+
function getRawFilesCollection() {
|
|
32
|
+
const model = (0, manager_1.getModel)('storage_files', 'nosql');
|
|
33
|
+
const connectionIds = model.getConnectionIds();
|
|
34
|
+
const activeId = connectionIds.find((id) => (0, connectionManager_1.getConnectedIds)().includes(id) && (0, connectionManager_1.getConnection)(id).type === 'nosql');
|
|
35
|
+
if (!activeId)
|
|
36
|
+
return null;
|
|
37
|
+
return (0, connectionManager_1.getConnection)(activeId).client.db().collection('storage_files');
|
|
30
38
|
}
|
|
31
39
|
function storageRouter(config) {
|
|
32
40
|
const router = (0, express_1.Router)();
|
|
@@ -34,19 +42,12 @@ function storageRouter(config) {
|
|
|
34
42
|
const Project = (0, manager_1.getModel)('projects');
|
|
35
43
|
router.get('/upload-project-image-url/:id', async (req, res) => {
|
|
36
44
|
try {
|
|
37
|
-
const auth =
|
|
38
|
-
if (
|
|
39
|
-
!auth.userId ||
|
|
40
|
-
!auth.role) {
|
|
41
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
42
|
-
}
|
|
43
|
-
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
45
|
+
const auth = req.auth;
|
|
46
|
+
if (auth.role !== 'admin' && auth.role !== 'editor')
|
|
44
47
|
return res.status(403).json({ message: 'Permission denied' });
|
|
45
|
-
}
|
|
46
48
|
const projectId = req.params.id;
|
|
47
|
-
if (!projectId)
|
|
49
|
+
if (!projectId)
|
|
48
50
|
return res.status(400).json({ message: 'Missing projectId' });
|
|
49
|
-
}
|
|
50
51
|
const response = await (0, presign_1.getPresignedProjectImageUrl)(adapter, projectId);
|
|
51
52
|
res.status(200).json({ ...response, message: 'Presigned URL generated successfully' });
|
|
52
53
|
}
|
|
@@ -56,19 +57,12 @@ function storageRouter(config) {
|
|
|
56
57
|
});
|
|
57
58
|
router.delete('/delete-project-image-url/:id', async (req, res) => {
|
|
58
59
|
try {
|
|
59
|
-
const auth =
|
|
60
|
-
if (
|
|
61
|
-
!auth.userId ||
|
|
62
|
-
!auth.role) {
|
|
63
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
64
|
-
}
|
|
65
|
-
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
60
|
+
const auth = req.auth;
|
|
61
|
+
if (auth.role !== 'admin' && auth.role !== 'editor')
|
|
66
62
|
return res.status(403).json({ message: 'Permission denied' });
|
|
67
|
-
}
|
|
68
63
|
const projectId = req.params.id;
|
|
69
|
-
if (!projectId)
|
|
64
|
+
if (!projectId)
|
|
70
65
|
return res.status(400).json({ message: 'Missing projectId' });
|
|
71
|
-
}
|
|
72
66
|
await (0, file_1.deleteProjectImage)(adapter, projectId);
|
|
73
67
|
const updated = await Project.update({ _id: projectId }, { image: '' });
|
|
74
68
|
res.status(201).json({ ...updated, message: 'Image deleted successfully' });
|
|
@@ -79,10 +73,7 @@ function storageRouter(config) {
|
|
|
79
73
|
});
|
|
80
74
|
router.get('/projects/:projectId/storage/:category/upload-url', async (req, res) => {
|
|
81
75
|
try {
|
|
82
|
-
const auth =
|
|
83
|
-
if (!auth || !auth.userId || !auth.role) {
|
|
84
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
85
|
-
}
|
|
76
|
+
const auth = req.auth;
|
|
86
77
|
const { projectId, category } = req.params;
|
|
87
78
|
const { fileName, contentType } = req.query;
|
|
88
79
|
if (!VALID_CATEGORIES.includes(category)) {
|
|
@@ -98,150 +89,131 @@ function storageRouter(config) {
|
|
|
98
89
|
return res.status(400).json({ message: `Files in the ${category} category must match ${hint}` });
|
|
99
90
|
}
|
|
100
91
|
const project = await Project.findOne({ _id: projectId });
|
|
101
|
-
if (!project)
|
|
92
|
+
if (!project)
|
|
102
93
|
return res.status(404).json({ message: 'Project not found' });
|
|
103
|
-
|
|
104
|
-
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
94
|
+
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
105
95
|
return res.status(403).json({ message: 'Unauthorized access' });
|
|
106
|
-
}
|
|
107
96
|
if (!adapter.getPresignedUploadUrl) {
|
|
108
97
|
return res.status(501).json({ message: 'Storage adapter does not support presigned uploads' });
|
|
109
98
|
}
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
99
|
+
const ext = fileName.includes('.') ? `.${fileName.split('.').pop().toLowerCase()}` : '';
|
|
100
|
+
const fileId = crypto_1.default.randomUUID();
|
|
101
|
+
const storageKey = `${projectId}/${category}/${fileId}${ext}`;
|
|
102
|
+
const collection = getRawFilesCollection();
|
|
103
|
+
if (collection) {
|
|
104
|
+
await collection.insertOne({
|
|
105
|
+
_id: new mongodb_1.ObjectId(),
|
|
106
|
+
fileId,
|
|
107
|
+
projectId,
|
|
108
|
+
category,
|
|
109
|
+
storageKey,
|
|
110
|
+
displayName: fileName,
|
|
111
|
+
contentType,
|
|
112
|
+
uploadedBy: auth.userId,
|
|
113
|
+
createdAt: new Date(),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const url = await adapter.getPresignedUploadUrl(storageKey, contentType);
|
|
117
|
+
res.json({ url, fileId, storageKey, displayName: fileName });
|
|
113
118
|
}
|
|
114
119
|
catch (err) {
|
|
115
120
|
res.status(500).json({ message: 'Failed to generate upload URL', details: err.message });
|
|
116
121
|
}
|
|
117
122
|
});
|
|
118
|
-
router.
|
|
123
|
+
router.patch('/projects/:projectId/storage/files/:fileId', async (req, res) => {
|
|
119
124
|
try {
|
|
120
|
-
const auth =
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
const auth = req.auth;
|
|
126
|
+
const { projectId, fileId } = req.params;
|
|
127
|
+
const { displayName } = req.body;
|
|
128
|
+
if (!displayName || typeof displayName !== 'string') {
|
|
129
|
+
return res.status(400).json({ message: 'displayName is required' });
|
|
123
130
|
}
|
|
124
|
-
const { projectId } = req.params;
|
|
125
131
|
const project = await Project.findOne({ _id: projectId });
|
|
126
|
-
if (!project)
|
|
132
|
+
if (!project)
|
|
127
133
|
return res.status(404).json({ message: 'Project not found' });
|
|
128
|
-
|
|
129
|
-
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
134
|
+
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
130
135
|
return res.status(403).json({ message: 'Unauthorized access' });
|
|
131
|
-
|
|
132
|
-
if (!
|
|
133
|
-
return res.status(
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
.sort((a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime())
|
|
139
|
-
.slice(0, 12);
|
|
140
|
-
res.json({ data: latest });
|
|
136
|
+
const collection = getRawFilesCollection();
|
|
137
|
+
if (!collection)
|
|
138
|
+
return res.status(500).json({ message: 'Database not available' });
|
|
139
|
+
const result = await collection.findOneAndUpdate({ fileId, projectId }, { $set: { displayName } }, { returnDocument: 'after' });
|
|
140
|
+
if (!result)
|
|
141
|
+
return res.status(404).json({ message: 'File not found' });
|
|
142
|
+
res.json({ message: 'File renamed successfully', file: result });
|
|
141
143
|
}
|
|
142
144
|
catch (err) {
|
|
143
|
-
res.status(500).json({ message: 'Failed to
|
|
145
|
+
res.status(500).json({ message: 'Failed to rename file', details: err.message });
|
|
144
146
|
}
|
|
145
147
|
});
|
|
146
|
-
router.
|
|
148
|
+
router.delete('/projects/:projectId/storage/files/:fileId', async (req, res) => {
|
|
147
149
|
try {
|
|
148
|
-
const auth =
|
|
149
|
-
|
|
150
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
151
|
-
}
|
|
152
|
-
const { projectId, category } = req.params;
|
|
153
|
-
if (!VALID_CATEGORIES.includes(category)) {
|
|
154
|
-
return res.status(400).json({ message: `Invalid category. Must be one of: ${VALID_CATEGORIES.join(', ')}` });
|
|
155
|
-
}
|
|
150
|
+
const auth = req.auth;
|
|
151
|
+
const { projectId, fileId } = req.params;
|
|
156
152
|
const project = await Project.findOne({ _id: projectId });
|
|
157
|
-
if (!project)
|
|
153
|
+
if (!project)
|
|
158
154
|
return res.status(404).json({ message: 'Project not found' });
|
|
159
|
-
|
|
160
|
-
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
155
|
+
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
161
156
|
return res.status(403).json({ message: 'Unauthorized access' });
|
|
162
|
-
|
|
163
|
-
if (!
|
|
164
|
-
return res.status(
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
157
|
+
const collection = getRawFilesCollection();
|
|
158
|
+
if (!collection)
|
|
159
|
+
return res.status(500).json({ message: 'Database not available' });
|
|
160
|
+
const fileDoc = await collection.findOne({ fileId, projectId });
|
|
161
|
+
if (!fileDoc)
|
|
162
|
+
return res.status(404).json({ message: 'File not found' });
|
|
163
|
+
if (adapter.deleteFile)
|
|
164
|
+
await adapter.deleteFile(fileDoc.storageKey);
|
|
165
|
+
await collection.deleteOne({ fileId, projectId });
|
|
166
|
+
res.json({ message: 'File deleted successfully' });
|
|
168
167
|
}
|
|
169
168
|
catch (err) {
|
|
170
|
-
res.status(500).json({ message: 'Failed to
|
|
169
|
+
res.status(500).json({ message: 'Failed to delete file', details: err.message });
|
|
171
170
|
}
|
|
172
171
|
});
|
|
173
|
-
router.
|
|
172
|
+
router.get('/projects/:projectId/storage/lasts', async (req, res) => {
|
|
174
173
|
try {
|
|
175
|
-
const auth =
|
|
176
|
-
|
|
177
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
178
|
-
}
|
|
179
|
-
const { projectId, category } = req.params;
|
|
180
|
-
const key = decodeURIComponent(req.params.key);
|
|
181
|
-
const { contentType } = req.body;
|
|
182
|
-
if (!VALID_CATEGORIES.includes(category)) {
|
|
183
|
-
return res.status(400).json({ message: `Invalid category. Must be one of: ${VALID_CATEGORIES.join(', ')}` });
|
|
184
|
-
}
|
|
185
|
-
if (!contentType) {
|
|
186
|
-
return res.status(400).json({ message: 'contentType is required' });
|
|
187
|
-
}
|
|
188
|
-
if (!validateContentType(contentType, key, category)) {
|
|
189
|
-
const hint = category === '3d-models'
|
|
190
|
-
? '.glb, .gltf, .obj, .fbx, .stl, or .ply'
|
|
191
|
-
: `${category.slice(0, -1)}/* content type`;
|
|
192
|
-
return res.status(400).json({ message: `Files in the ${category} category must match ${hint}` });
|
|
193
|
-
}
|
|
174
|
+
const auth = req.auth;
|
|
175
|
+
const { projectId } = req.params;
|
|
194
176
|
const project = await Project.findOne({ _id: projectId });
|
|
195
|
-
if (!project)
|
|
177
|
+
if (!project)
|
|
196
178
|
return res.status(404).json({ message: 'Project not found' });
|
|
197
|
-
|
|
198
|
-
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
179
|
+
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
199
180
|
return res.status(403).json({ message: 'Unauthorized access' });
|
|
200
|
-
|
|
201
|
-
if (!
|
|
202
|
-
return res.status(
|
|
203
|
-
}
|
|
204
|
-
const
|
|
205
|
-
res.json({
|
|
181
|
+
const collection = getRawFilesCollection();
|
|
182
|
+
if (!collection)
|
|
183
|
+
return res.status(500).json({ message: 'Database not available' });
|
|
184
|
+
const files = await collection.find({ projectId }).sort({ createdAt: -1 }).limit(12).toArray();
|
|
185
|
+
const data = await Promise.all(files.map(f => resolveFileUrl(f, adapter)));
|
|
186
|
+
res.json({ data });
|
|
206
187
|
}
|
|
207
188
|
catch (err) {
|
|
208
|
-
res.status(500).json({ message: 'Failed to
|
|
189
|
+
res.status(500).json({ message: 'Failed to fetch latest files', details: err.message });
|
|
209
190
|
}
|
|
210
191
|
});
|
|
211
|
-
router.
|
|
192
|
+
router.get('/projects/:projectId/storage/:category', async (req, res) => {
|
|
212
193
|
try {
|
|
213
|
-
const auth =
|
|
214
|
-
if (!auth || !auth.userId || !auth.role) {
|
|
215
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
216
|
-
}
|
|
194
|
+
const auth = req.auth;
|
|
217
195
|
const { projectId, category } = req.params;
|
|
218
|
-
const key = decodeURIComponent(req.params.key);
|
|
219
196
|
if (!VALID_CATEGORIES.includes(category)) {
|
|
220
197
|
return res.status(400).json({ message: `Invalid category. Must be one of: ${VALID_CATEGORIES.join(', ')}` });
|
|
221
198
|
}
|
|
222
199
|
const project = await Project.findOne({ _id: projectId });
|
|
223
|
-
if (!project)
|
|
200
|
+
if (!project)
|
|
224
201
|
return res.status(404).json({ message: 'Project not found' });
|
|
225
|
-
|
|
226
|
-
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
202
|
+
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
227
203
|
return res.status(403).json({ message: 'Unauthorized access' });
|
|
228
|
-
|
|
229
|
-
if (!
|
|
230
|
-
return res.status(
|
|
231
|
-
}
|
|
232
|
-
await
|
|
233
|
-
res.json({
|
|
204
|
+
const collection = getRawFilesCollection();
|
|
205
|
+
if (!collection)
|
|
206
|
+
return res.status(500).json({ message: 'Database not available' });
|
|
207
|
+
const files = await collection.find({ projectId, category }).sort({ createdAt: -1 }).toArray();
|
|
208
|
+
const data = await Promise.all(files.map(f => resolveFileUrl(f, adapter)));
|
|
209
|
+
res.json({ data });
|
|
234
210
|
}
|
|
235
211
|
catch (err) {
|
|
236
|
-
res.status(500).json({ message: 'Failed to
|
|
212
|
+
res.status(500).json({ message: 'Failed to list files', details: err.message });
|
|
237
213
|
}
|
|
238
214
|
});
|
|
239
|
-
router.get('/storages', async (
|
|
215
|
+
router.get('/storages', async (_req, res) => {
|
|
240
216
|
try {
|
|
241
|
-
const auth = await config.authProvider(req);
|
|
242
|
-
if (!auth || !auth.userId || !auth.role) {
|
|
243
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
244
|
-
}
|
|
245
217
|
const STORAGE_DISPLAY = {
|
|
246
218
|
s3: { value: 'aws-s3', name: 'AWS S3' },
|
|
247
219
|
gcs: { value: 'google-cloud-storage', name: 'Google Cloud Storage' },
|
|
@@ -260,3 +232,22 @@ function storageRouter(config) {
|
|
|
260
232
|
});
|
|
261
233
|
return router;
|
|
262
234
|
}
|
|
235
|
+
async function resolveFileUrl(file, adapter) {
|
|
236
|
+
let url = null;
|
|
237
|
+
if (adapter.getPresignedGetUrl) {
|
|
238
|
+
try {
|
|
239
|
+
url = await adapter.getPresignedGetUrl(file.storageKey);
|
|
240
|
+
}
|
|
241
|
+
catch { /* leave null */ }
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
id: file.fileId,
|
|
245
|
+
displayName: file.displayName,
|
|
246
|
+
storageKey: file.storageKey,
|
|
247
|
+
category: file.category,
|
|
248
|
+
contentType: file.contentType,
|
|
249
|
+
uploadedBy: file.uploadedBy,
|
|
250
|
+
createdAt: file.createdAt,
|
|
251
|
+
url,
|
|
252
|
+
};
|
|
253
|
+
}
|
package/dist/models/Model.js
CHANGED
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createNotification = createNotification;
|
|
4
4
|
const manager_1 = require("../models/manager");
|
|
5
|
+
const connectionManager_1 = require("../adapters/database/connectionManager");
|
|
5
6
|
const wsHub_1 = require("./wsHub");
|
|
7
|
+
const MAX_NOTIFICATIONS = 20;
|
|
8
|
+
function getRawNotifCollection() {
|
|
9
|
+
const model = (0, manager_1.getModel)('notifications', 'nosql');
|
|
10
|
+
const connectionIds = model.getConnectionIds();
|
|
11
|
+
const activeId = connectionIds.find((id) => (0, connectionManager_1.getConnectedIds)().includes(id) && (0, connectionManager_1.getConnection)(id).type === 'nosql');
|
|
12
|
+
if (!activeId)
|
|
13
|
+
return null;
|
|
14
|
+
return (0, connectionManager_1.getConnection)(activeId).client.db().collection('notifications');
|
|
15
|
+
}
|
|
6
16
|
async function createNotification(userId, type, message, data, appId, callback) {
|
|
7
|
-
const Notification = (0, manager_1.getModel)('notifications');
|
|
17
|
+
const Notification = (0, manager_1.getModel)('notifications', 'nosql');
|
|
8
18
|
const notification = await Notification.create({
|
|
9
19
|
userId,
|
|
10
20
|
appId: appId ?? null,
|
|
@@ -16,6 +26,15 @@ async function createNotification(userId, type, message, data, appId, callback)
|
|
|
16
26
|
readAt: null,
|
|
17
27
|
createdAt: new Date(),
|
|
18
28
|
});
|
|
29
|
+
const collection = getRawNotifCollection();
|
|
30
|
+
if (collection) {
|
|
31
|
+
const count = await collection.countDocuments({ userId });
|
|
32
|
+
if (count > MAX_NOTIFICATIONS) {
|
|
33
|
+
const oldest = await collection.find({ userId }).sort({ createdAt: 1 }).limit(1).toArray();
|
|
34
|
+
if (oldest.length > 0)
|
|
35
|
+
await collection.deleteOne({ _id: oldest[0]._id });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
19
38
|
(0, wsHub_1.emitToUser)(userId, { type: 'notification', data: notification });
|
|
20
39
|
return notification;
|
|
21
40
|
}
|