streamby-core 0.33.0 → 0.35.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/LICENSE +661 -0
  3. package/README.md +111 -11
  4. package/dist/adapters/database/connectionManager.js +18 -0
  5. package/dist/index.d.ts +3 -1
  6. package/dist/index.js +4 -1
  7. package/dist/middleware/createRouter.js +19 -2
  8. package/dist/middleware/routes/ai.js +3 -3
  9. package/dist/middleware/routes/{credential.d.ts → apiToken.d.ts} +1 -1
  10. package/dist/middleware/routes/apiToken.js +58 -0
  11. package/dist/middleware/routes/connection.js +3 -12
  12. package/dist/middleware/routes/dbConnection.js +7 -26
  13. package/dist/middleware/routes/distribution.js +12 -5
  14. package/dist/middleware/routes/export.js +5 -4
  15. package/dist/middleware/routes/media.js +27 -10
  16. package/dist/middleware/routes/member.js +3 -12
  17. package/dist/middleware/routes/pipeline.js +3 -3
  18. package/dist/middleware/routes/production.js +9 -9
  19. package/dist/middleware/routes/project.js +12 -25
  20. package/dist/middleware/routes/renderFarm.js +13 -6
  21. package/dist/middleware/routes/review.js +4 -4
  22. package/dist/middleware/routes/rights.js +1 -1
  23. package/dist/middleware/routes/storage.js +21 -15
  24. package/dist/middleware/routes/storageConnection.js +2 -8
  25. package/dist/middleware/routes/workflow.js +1 -1
  26. package/dist/services/apiTokens.d.ts +47 -0
  27. package/dist/services/apiTokens.js +110 -0
  28. package/dist/services/auth.d.ts +5 -3
  29. package/dist/services/auth.js +49 -11
  30. package/dist/services/connectionResolver.js +2 -2
  31. package/dist/services/executor.d.ts +4 -1
  32. package/dist/services/executor.js +29 -11
  33. package/dist/services/reencryptCredentials.d.ts +5 -0
  34. package/dist/services/reencryptCredentials.js +74 -0
  35. package/dist/services/userIntegration.d.ts +2 -2
  36. package/dist/services/userIntegration.js +7 -1
  37. package/dist/services/wsHub.js +6 -0
  38. package/dist/types/index.d.ts +9 -0
  39. package/dist/utils/auth.d.ts +15 -1
  40. package/dist/utils/auth.js +21 -6
  41. package/dist/utils/encryption.d.ts +6 -1
  42. package/dist/utils/encryption.js +80 -27
  43. package/dist/utils/serviceEndpoints.d.ts +2 -0
  44. package/dist/utils/serviceEndpoints.js +26 -0
  45. package/package.json +4 -3
  46. package/dist/middleware/routes/apiConnection.d.ts +0 -3
  47. package/dist/middleware/routes/apiConnection.js +0 -100
  48. package/dist/middleware/routes/credential.js +0 -80
  49. package/dist/services/apiConnection.d.ts +0 -18
  50. package/dist/services/apiConnection.js +0 -65
  51. package/dist/services/credential.d.ts +0 -8
  52. package/dist/services/credential.js +0 -70
  53. package/dist/services/pipeline.d.ts +0 -2
  54. package/dist/services/pipeline.js +0 -292
@@ -8,6 +8,7 @@ const jobQueue_1 = require("../../services/jobQueue");
8
8
  const mediaProcessor_1 = require("../../services/mediaProcessor");
9
9
  const storage_1 = require("../../providers/storage");
10
10
  const connectionManager_1 = require("../../adapters/database/connectionManager");
11
+ const auth_2 = require("../../services/auth");
11
12
  function getMetadataCollection() {
12
13
  const ids = (0, connectionManager_1.getConnectedIds)().filter(id => (0, connectionManager_1.getConnection)(id).type === 'nosql');
13
14
  if (!ids.length)
@@ -25,11 +26,27 @@ function mediaRouter(config) {
25
26
  const adapter = config.adapter || (0, storage_1.createStorageProvider)(config.storageProviders);
26
27
  const Project = (0, manager_1.getModel)('projects');
27
28
  // GET /jobs/:jobId — poll job status
28
- router.get('/jobs/:jobId', (req, res) => {
29
- const job = (0, jobQueue_1.getJob)(req.params.jobId);
30
- if (!job)
31
- return res.status(404).json({ message: 'Job not found' });
32
- res.json({ job });
29
+ // Only for whoever started it, or a member of its project: someone else's job is as if it
30
+ // didn't exist.
31
+ router.get('/jobs/:jobId', async (req, res) => {
32
+ try {
33
+ const auth = req.auth;
34
+ const job = (0, jobQueue_1.getJob)(req.params.jobId);
35
+ if (!job)
36
+ return res.status(404).json({ message: 'Job not found' });
37
+ // With an API token, only a job of a project it reaches.
38
+ if (!(0, auth_2.scopeReachesProject)(auth.apiToken, job.projectId))
39
+ return res.status(404).json({ message: 'Job not found' });
40
+ if (job.userId !== auth.userId) {
41
+ const project = await Project.findOne({ _id: job.projectId });
42
+ if (!(0, auth_1.isProjectMember)(project, auth.userId))
43
+ return res.status(404).json({ message: 'Job not found' });
44
+ }
45
+ res.json({ job });
46
+ }
47
+ catch (err) {
48
+ res.status(500).json({ message: 'Failed to fetch job', details: err.message });
49
+ }
33
50
  });
34
51
  // POST /projects/:projectId/jobs/ingest — queue an ingest job for an already-uploaded file
35
52
  router.post('/projects/:projectId/jobs/ingest', async (req, res) => {
@@ -42,7 +59,7 @@ function mediaRouter(config) {
42
59
  const project = await Project.findOne({ _id: projectId });
43
60
  if (!project)
44
61
  return res.status(404).json({ message: 'Project not found' });
45
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
62
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
46
63
  return res.status(403).json({ message: 'Unauthorized' });
47
64
  const job = (0, jobQueue_1.createJob)('ingest', auth.userId, projectId, { fileId });
48
65
  // Run async — response returns immediately with jobId
@@ -64,7 +81,7 @@ function mediaRouter(config) {
64
81
  const project = await Project.findOne({ _id: projectId });
65
82
  if (!project)
66
83
  return res.status(404).json({ message: 'Project not found' });
67
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
84
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
68
85
  return res.status(403).json({ message: 'Unauthorized' });
69
86
  const job = (0, jobQueue_1.createJob)('transcode', auth.userId, projectId, {
70
87
  fileId, codec, resolution, outputFormat, bitrate, audioCodec,
@@ -87,7 +104,7 @@ function mediaRouter(config) {
87
104
  const project = await Project.findOne({ _id: projectId });
88
105
  if (!project)
89
106
  return res.status(404).json({ message: 'Project not found' });
90
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
107
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
91
108
  return res.status(403).json({ message: 'Unauthorized' });
92
109
  const job = (0, jobQueue_1.createJob)('thumbnail', auth.userId, projectId, { fileId, timecode, resolution, strategy });
93
110
  setImmediate(() => (0, mediaProcessor_1.runThumbnailJob)(job.jobId, fileId, projectId, { timecode, resolution, strategy }));
@@ -108,7 +125,7 @@ function mediaRouter(config) {
108
125
  const project = await Project.findOne({ _id: projectId });
109
126
  if (!project)
110
127
  return res.status(404).json({ message: 'Project not found' });
111
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
128
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
112
129
  return res.status(403).json({ message: 'Unauthorized' });
113
130
  const job = (0, jobQueue_1.createJob)('caption', auth.userId, projectId, { fileId, sourceLanguage, outputFormat, provider });
114
131
  setImmediate(() => (0, mediaProcessor_1.runCaptionJob)(job.jobId, fileId, projectId, { sourceLanguage, outputFormat, provider }));
@@ -150,7 +167,7 @@ function mediaRouter(config) {
150
167
  const project = await Project.findOne({ _id: projectId });
151
168
  if (!project)
152
169
  return res.status(404).json({ message: 'Project not found' });
153
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
170
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
154
171
  return res.status(403).json({ message: 'Unauthorized' });
155
172
  const col = getMetadataCollection();
156
173
  if (!col)
@@ -52,9 +52,6 @@ function memberRouter(config) {
52
52
  router.post('/projects/:id/members', async (req, res) => {
53
53
  try {
54
54
  const auth = req.auth;
55
- if (auth.role !== 'admin') {
56
- return res.status(403).json({ message: 'Only admins can invite members' });
57
- }
58
55
  const projectId = req.params.id;
59
56
  const { userId, role } = req.body;
60
57
  if (!userId || !role) {
@@ -67,7 +64,7 @@ function memberRouter(config) {
67
64
  if (!project) {
68
65
  return res.status(404).json({ message: 'Project not found' });
69
66
  }
70
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
67
+ if (!(0, auth_1.isProjectAdmin)(project, auth.userId)) {
71
68
  return res.status(403).json({ message: 'Unauthorized project access' });
72
69
  }
73
70
  const alreadyMember = project.members?.some((m) => m.userId?.toString() === userId?.toString());
@@ -107,9 +104,6 @@ function memberRouter(config) {
107
104
  router.patch('/projects/:id/members/:userId', async (req, res) => {
108
105
  try {
109
106
  const auth = req.auth;
110
- if (auth.role !== 'admin') {
111
- return res.status(403).json({ message: 'Only admins can update member roles' });
112
- }
113
107
  const projectId = req.params.id;
114
108
  const targetUserId = req.params.userId;
115
109
  const { role } = req.body;
@@ -120,7 +114,7 @@ function memberRouter(config) {
120
114
  if (!project) {
121
115
  return res.status(404).json({ message: 'Project not found' });
122
116
  }
123
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
117
+ if (!(0, auth_1.isProjectAdmin)(project, auth.userId)) {
124
118
  return res.status(403).json({ message: 'Unauthorized project access' });
125
119
  }
126
120
  const memberExists = project.members?.some((m) => m.userId?.toString() === targetUserId?.toString());
@@ -282,9 +276,6 @@ function memberRouter(config) {
282
276
  router.delete('/projects/:id/members/:userId', async (req, res) => {
283
277
  try {
284
278
  const auth = req.auth;
285
- if (auth.role !== 'admin') {
286
- return res.status(403).json({ message: 'Only admins can remove members' });
287
- }
288
279
  const projectId = req.params.id;
289
280
  const targetUserId = req.params.userId;
290
281
  if (targetUserId === auth.userId) {
@@ -294,7 +285,7 @@ function memberRouter(config) {
294
285
  if (!project) {
295
286
  return res.status(404).json({ message: 'Project not found' });
296
287
  }
297
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
288
+ if (!(0, auth_1.isProjectAdmin)(project, auth.userId)) {
298
289
  return res.status(403).json({ message: 'Unauthorized project access' });
299
290
  }
300
291
  const memberExists = project.members?.some((m) => m.userId?.toString() === targetUserId?.toString());
@@ -67,7 +67,7 @@ function pipelineRouter(_config) {
67
67
  const project = await Project.findOne({ _id: projectId });
68
68
  if (!project)
69
69
  return res.status(404).json({ message: 'Project not found' });
70
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
70
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
71
71
  return res.status(403).json({ message: 'Unauthorized' });
72
72
  const db = getDb();
73
73
  if (!db)
@@ -120,7 +120,7 @@ function pipelineRouter(_config) {
120
120
  const project = await Project.findOne({ _id: projectId });
121
121
  if (!project)
122
122
  return res.status(404).json({ message: 'Project not found' });
123
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
123
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
124
124
  return res.status(403).json({ message: 'Unauthorized' });
125
125
  const db = getDb();
126
126
  if (!db)
@@ -154,7 +154,7 @@ function pipelineRouter(_config) {
154
154
  const project = await Project.findOne({ _id: projectId });
155
155
  if (!project)
156
156
  return res.status(404).json({ message: 'Project not found' });
157
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
157
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
158
158
  return res.status(403).json({ message: 'Unauthorized' });
159
159
  const db = getDb();
160
160
  if (!db)
@@ -51,7 +51,7 @@ function productionRouter(_config) {
51
51
  const project = await Project.findOne({ _id: projectId });
52
52
  if (!project)
53
53
  return res.status(404).json({ message: 'Project not found' });
54
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
54
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
55
55
  return res.status(403).json({ message: 'Unauthorized' });
56
56
  const db = getDb();
57
57
  if (!db)
@@ -81,7 +81,7 @@ function productionRouter(_config) {
81
81
  const project = await Project.findOne({ _id: projectId });
82
82
  if (!project)
83
83
  return res.status(404).json({ message: 'Project not found' });
84
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
84
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
85
85
  return res.status(403).json({ message: 'Unauthorized' });
86
86
  const db = getDb();
87
87
  if (!db)
@@ -110,7 +110,7 @@ function productionRouter(_config) {
110
110
  const project = await Project.findOne({ _id: projectId });
111
111
  if (!project)
112
112
  return res.status(404).json({ message: 'Project not found' });
113
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
113
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
114
114
  return res.status(403).json({ message: 'Unauthorized' });
115
115
  const db = getDb();
116
116
  if (!db)
@@ -156,7 +156,7 @@ function productionRouter(_config) {
156
156
  const project = await Project.findOne({ _id: projectId });
157
157
  if (!project)
158
158
  return res.status(404).json({ message: 'Project not found' });
159
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
159
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
160
160
  return res.status(403).json({ message: 'Unauthorized' });
161
161
  const db = getDb();
162
162
  if (!db)
@@ -192,7 +192,7 @@ function productionRouter(_config) {
192
192
  const project = await Project.findOne({ _id: projectId });
193
193
  if (!project)
194
194
  return res.status(404).json({ message: 'Project not found' });
195
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
195
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
196
196
  return res.status(403).json({ message: 'Unauthorized' });
197
197
  const db = getDb();
198
198
  if (!db)
@@ -231,7 +231,7 @@ function productionRouter(_config) {
231
231
  const project = await Project.findOne({ _id: projectId });
232
232
  if (!project)
233
233
  return res.status(404).json({ message: 'Project not found' });
234
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
234
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
235
235
  return res.status(403).json({ message: 'Unauthorized' });
236
236
  const db = getDb();
237
237
  if (!db)
@@ -277,7 +277,7 @@ function productionRouter(_config) {
277
277
  const project = await Project.findOne({ _id: projectId });
278
278
  if (!project)
279
279
  return res.status(404).json({ message: 'Project not found' });
280
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
280
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
281
281
  return res.status(403).json({ message: 'Unauthorized' });
282
282
  const db = getDb();
283
283
  if (!db)
@@ -311,7 +311,7 @@ function productionRouter(_config) {
311
311
  const project = await Project.findOne({ _id: projectId });
312
312
  if (!project)
313
313
  return res.status(404).json({ message: 'Project not found' });
314
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
314
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
315
315
  return res.status(403).json({ message: 'Unauthorized' });
316
316
  const db = getDb();
317
317
  if (!db)
@@ -346,7 +346,7 @@ function productionRouter(_config) {
346
346
  const project = await Project.findOne({ _id: projectId });
347
347
  if (!project)
348
348
  return res.status(404).json({ message: 'Project not found' });
349
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
349
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
350
350
  return res.status(403).json({ message: 'Unauthorized' });
351
351
  const db = getDb();
352
352
  if (!db)
@@ -8,6 +8,7 @@ const connectionManager_1 = require("../../adapters/database/connectionManager")
8
8
  const sql_1 = require("../../adapters/database/sql");
9
9
  const sanitize_1 = require("../../utils/sanitize");
10
10
  const projectIntegration_1 = require("../../services/projectIntegration");
11
+ const auth_2 = require("../../services/auth");
11
12
  function projectRouter(config) {
12
13
  const router = (0, express_1.Router)();
13
14
  const Project = (0, manager_1.getModel)('projects');
@@ -32,6 +33,9 @@ function projectRouter(config) {
32
33
  const currentUserMember = project.members?.find((m) => m.userId?.toString() === auth.userId?.toString());
33
34
  if (!currentUserMember || currentUserMember.status !== 'active')
34
35
  return false;
36
+ // With an API token, only the projects it reaches.
37
+ if (!(0, auth_2.scopeReachesProject)(auth.apiToken, (project._id ?? project.id)?.toString()))
38
+ return false;
35
39
  if (filterArchived !== undefined) {
36
40
  return (currentUserMember.archived || false) === filterArchived;
37
41
  }
@@ -72,9 +76,6 @@ function projectRouter(config) {
72
76
  if (req.subscription === 'freemium') {
73
77
  return res.status(403).json({ error: 'Freemium users cannot create projects' });
74
78
  }
75
- if (auth.role !== 'admin' && auth.role !== 'editor') {
76
- return res.status(403).json({ message: 'Permission denied' });
77
- }
78
79
  const { name, description, image, allowedOrigin, public: isPublic = true, category, integrationIds } = req.body;
79
80
  if (isPublic === false && req.subscription === 'freemium') {
80
81
  return res.status(403).json({ error: 'Private projects require a subscriber plan' });
@@ -208,9 +209,6 @@ function projectRouter(config) {
208
209
  router.patch('/projects/:id', async (req, res) => {
209
210
  try {
210
211
  const auth = req.auth;
211
- if (auth.role !== 'admin' && auth.role !== 'editor') {
212
- return res.status(403).json({ message: 'Permission denied' });
213
- }
214
212
  const projectId = req.params.id;
215
213
  const updates = req.body;
216
214
  const project = await Project.findOne({ _id: projectId });
@@ -220,7 +218,7 @@ function projectRouter(config) {
220
218
  if (!updates || typeof updates !== 'object') {
221
219
  return res.status(400).json({ message: 'Missing updates payload' });
222
220
  }
223
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
221
+ if (!(0, auth_1.canEditProject)(project, auth.userId)) {
224
222
  return res.status(403).json({ message: 'Unauthorized project access' });
225
223
  }
226
224
  const updated = await Project.update({ _id: projectId }, updates);
@@ -250,8 +248,9 @@ function projectRouter(config) {
250
248
  if (!project) {
251
249
  return res.status(404).json({ message: 'Project not found' });
252
250
  }
253
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
254
- return res.status(403).json({ message: 'Unauthorized project access' });
251
+ // Only an admin of the project deletes it.
252
+ if (!(0, auth_1.isProjectAdmin)(project, auth.userId)) {
253
+ return res.status(403).json({ message: 'Only a project admin can delete it' });
255
254
  }
256
255
  await Project.useDbType(project.dbType).delete({ _id: projectId });
257
256
  const allProjects = await Project.find({});
@@ -348,9 +347,6 @@ function projectRouter(config) {
348
347
  router.post('/projects/:id/image', async (req, res) => {
349
348
  try {
350
349
  const auth = req.auth;
351
- if (auth.role !== 'admin' && auth.role !== 'editor') {
352
- return res.status(403).json({ message: 'Permission denied' });
353
- }
354
350
  const projectId = req.params.id;
355
351
  const { imageUrl } = req.body;
356
352
  if (!projectId || !imageUrl) {
@@ -360,7 +356,7 @@ function projectRouter(config) {
360
356
  if (!project) {
361
357
  return res.status(404).json({ message: 'Project not found' });
362
358
  }
363
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
359
+ if (!(0, auth_1.canEditProject)(project, auth.userId)) {
364
360
  return res.status(403).json({ message: 'Unauthorized project access' });
365
361
  }
366
362
  const updatedProject = await Project.update({ _id: projectId }, { image: imageUrl });
@@ -376,16 +372,13 @@ function projectRouter(config) {
376
372
  router.patch('/projects/:id/origins', async (req, res) => {
377
373
  try {
378
374
  const auth = req.auth;
379
- if (auth.role !== 'admin' && auth.role !== 'editor') {
380
- return res.status(403).json({ message: 'Permission denied' });
381
- }
382
375
  const projectId = req.params.id;
383
376
  const { origins } = req.body;
384
377
  const project = await Project.findOne({ _id: projectId });
385
378
  if (!project) {
386
379
  return res.status(404).json({ message: 'Project not found' });
387
380
  }
388
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
381
+ if (!(0, auth_1.canEditProject)(project, auth.userId)) {
389
382
  return res.status(403).json({ message: 'Unauthorized project access' });
390
383
  }
391
384
  const updated = await Project.update({ _id: projectId }, { allowedOrigin: origins });
@@ -402,9 +395,6 @@ function projectRouter(config) {
402
395
  router.post('/projects/:id/integrations', async (req, res) => {
403
396
  try {
404
397
  const auth = req.auth;
405
- if (auth.role !== 'admin' && auth.role !== 'editor') {
406
- return res.status(403).json({ message: 'Permission denied' });
407
- }
408
398
  const projectId = req.params.id;
409
399
  const { integrationId } = req.body;
410
400
  if (!integrationId)
@@ -412,7 +402,7 @@ function projectRouter(config) {
412
402
  const project = await Project.findOne({ _id: projectId });
413
403
  if (!project)
414
404
  return res.status(404).json({ message: 'Project not found' });
415
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
405
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
416
406
  return res.status(403).json({ message: 'Unauthorized project access' });
417
407
  const alreadyConnected = [...(project.dbConnections ?? []), ...(project.storageConnections ?? []), ...(project.integrationConnections ?? [])]
418
408
  .some((c) => c.integrationId === integrationId);
@@ -438,15 +428,12 @@ function projectRouter(config) {
438
428
  router.delete('/projects/:id/integrations/:integrationId', async (req, res) => {
439
429
  try {
440
430
  const auth = req.auth;
441
- if (auth.role !== 'admin' && auth.role !== 'editor') {
442
- return res.status(403).json({ message: 'Permission denied' });
443
- }
444
431
  const projectId = req.params.id;
445
432
  const { integrationId } = req.params;
446
433
  const project = await Project.findOne({ _id: projectId });
447
434
  if (!project)
448
435
  return res.status(404).json({ message: 'Project not found' });
449
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
436
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
450
437
  return res.status(403).json({ message: 'Unauthorized project access' });
451
438
  const dbConn = (project.dbConnections ?? []).find((c) => c.integrationId === integrationId);
452
439
  const storageConn = (project.storageConnections ?? []).find((c) => c.integrationId === integrationId);
@@ -11,6 +11,7 @@ const jobQueue_1 = require("../../services/jobQueue");
11
11
  const vfxProcessor_1 = require("../../services/vfxProcessor");
12
12
  const sanitize_1 = require("../../utils/sanitize");
13
13
  const crypto_1 = __importDefault(require("crypto"));
14
+ const encryption_1 = require("../../utils/encryption");
14
15
  function renderFarmRouter(_config) {
15
16
  const router = (0, express_1.Router)();
16
17
  const Project = (0, manager_1.getModel)('projects');
@@ -34,14 +35,20 @@ function renderFarmRouter(_config) {
34
35
  try {
35
36
  const auth = req.auth;
36
37
  const { projectId } = req.params;
37
- const { name, provider, apiUrl, encryptedCredential, description } = req.body;
38
+ const { name, provider, apiUrl, credential: credentialValue, encryptedCredential: legacyCredential, description } = req.body;
39
+ // The credential, as given (`encryptedCredential` was its old name: it was always the
40
+ // value itself), stored encrypted.
41
+ const credential = credentialValue ?? legacyCredential;
42
+ if (credential && !(0, encryption_1.isEncryptionKeySet)())
43
+ return res.status(500).json({ message: 'Encryption key is not set' });
44
+ const encryptedCredential = credential ? (0, encryption_1.encrypt)(String(credential)) : undefined;
38
45
  if (!name || !provider || !apiUrl) {
39
46
  return res.status(400).json({ message: 'name, provider and apiUrl are required' });
40
47
  }
41
48
  const project = await Project.findOne({ _id: projectId });
42
49
  if (!project)
43
50
  return res.status(404).json({ message: 'Project not found' });
44
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
51
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
45
52
  return res.status(403).json({ message: 'Unauthorized' });
46
53
  const connection = {
47
54
  id: crypto_1.default.randomUUID(),
@@ -67,7 +74,7 @@ function renderFarmRouter(_config) {
67
74
  const project = await Project.findOne({ _id: projectId });
68
75
  if (!project)
69
76
  return res.status(404).json({ message: 'Project not found' });
70
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
77
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
71
78
  return res.status(403).json({ message: 'Unauthorized' });
72
79
  await Project.update({ _id: projectId }, { $pull: { renderFarmConnections: { id: connectionId } } });
73
80
  res.json({ message: 'Connection deleted' });
@@ -87,7 +94,7 @@ function renderFarmRouter(_config) {
87
94
  const project = await Project.findOne({ _id: projectId });
88
95
  if (!project)
89
96
  return res.status(404).json({ message: 'Project not found' });
90
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
97
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
91
98
  return res.status(403).json({ message: 'Unauthorized' });
92
99
  const job = (0, jobQueue_1.createJob)('render', auth.userId, projectId, { fileId, renderer, renderFarmConnectionId, frameRange, resolution, samples, outputFormat });
93
100
  setImmediate(() => (0, vfxProcessor_1.runRenderJob)(job.jobId, fileId, projectId, { renderer, renderFarmConnectionId, frameRange, resolution, samples, outputFormat }));
@@ -107,7 +114,7 @@ function renderFarmRouter(_config) {
107
114
  const project = await Project.findOne({ _id: projectId });
108
115
  if (!project)
109
116
  return res.status(404).json({ message: 'Project not found' });
110
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
117
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
111
118
  return res.status(403).json({ message: 'Unauthorized' });
112
119
  const job = (0, jobQueue_1.createJob)('format-convert', auth.userId, projectId, { fileId, inputFormat, outputFormat, applyTransforms, embedTextures });
113
120
  setImmediate(() => (0, vfxProcessor_1.runFormatConvertJob)(job.jobId, fileId, projectId, { inputFormat, outputFormat, applyTransforms, embedTextures }));
@@ -127,7 +134,7 @@ function renderFarmRouter(_config) {
127
134
  const project = await Project.findOne({ _id: projectId });
128
135
  if (!project)
129
136
  return res.status(404).json({ message: 'Project not found' });
130
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
137
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
131
138
  return res.status(403).json({ message: 'Unauthorized' });
132
139
  const job = (0, jobQueue_1.createJob)('lod', auth.userId, projectId, { fileId, levels, reductionRatios, algorithm, outputFormat });
133
140
  setImmediate(() => (0, vfxProcessor_1.runLodJob)(job.jobId, fileId, projectId, { levels, reductionRatios, algorithm, outputFormat }));
@@ -60,7 +60,7 @@ function reviewRouter(_config) {
60
60
  const project = await Project.findOne({ _id: projectId });
61
61
  if (!project)
62
62
  return res.status(404).json({ message: 'Project not found' });
63
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
63
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
64
64
  return res.status(403).json({ message: 'Unauthorized' });
65
65
  const db = getDb();
66
66
  if (!db)
@@ -120,7 +120,7 @@ function reviewRouter(_config) {
120
120
  const project = await Project.findOne({ _id: projectId });
121
121
  if (!project)
122
122
  return res.status(404).json({ message: 'Project not found' });
123
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
123
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
124
124
  return res.status(403).json({ message: 'Unauthorized' });
125
125
  const db = getDb();
126
126
  if (!db)
@@ -190,7 +190,7 @@ function reviewRouter(_config) {
190
190
  const project = await Project.findOne({ _id: projectId });
191
191
  if (!project)
192
192
  return res.status(404).json({ message: 'Project not found' });
193
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
193
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
194
194
  return res.status(403).json({ message: 'Unauthorized' });
195
195
  const db = getDb();
196
196
  if (!db)
@@ -224,7 +224,7 @@ function reviewRouter(_config) {
224
224
  const project = await Project.findOne({ _id: projectId });
225
225
  if (!project)
226
226
  return res.status(404).json({ message: 'Project not found' });
227
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
227
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
228
228
  return res.status(403).json({ message: 'Unauthorized' });
229
229
  const db = getDb();
230
230
  if (!db)
@@ -42,7 +42,7 @@ function rightsRouter(_config) {
42
42
  const project = await Project.findOne({ _id: projectId });
43
43
  if (!project)
44
44
  return res.status(404).json({ message: 'Project not found' });
45
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
45
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
46
46
  return res.status(403).json({ message: 'Unauthorized' });
47
47
  const db = getDb();
48
48
  if (!db)
@@ -71,11 +71,14 @@ function storageRouter(config) {
71
71
  router.get('/upload-project-image-url/:id', async (req, res) => {
72
72
  try {
73
73
  const auth = req.auth;
74
- if (auth.role !== 'admin' && auth.role !== 'editor')
75
- return res.status(403).json({ message: 'Permission denied' });
76
74
  const projectId = req.params.id;
77
75
  if (!projectId)
78
76
  return res.status(400).json({ message: 'Missing projectId' });
77
+ const project = await Project.findOne({ _id: projectId });
78
+ if (!project)
79
+ return res.status(404).json({ message: 'Project not found' });
80
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
81
+ return res.status(403).json({ message: 'Permission denied' });
79
82
  const response = await (0, presign_1.getPresignedProjectImageUrl)(adapter, projectId);
80
83
  res.status(200).json({ ...response, message: 'Presigned URL generated successfully' });
81
84
  }
@@ -86,11 +89,14 @@ function storageRouter(config) {
86
89
  router.delete('/delete-project-image-url/:id', async (req, res) => {
87
90
  try {
88
91
  const auth = req.auth;
89
- if (auth.role !== 'admin' && auth.role !== 'editor')
90
- return res.status(403).json({ message: 'Permission denied' });
91
92
  const projectId = req.params.id;
92
93
  if (!projectId)
93
94
  return res.status(400).json({ message: 'Missing projectId' });
95
+ const project = await Project.findOne({ _id: projectId });
96
+ if (!project)
97
+ return res.status(404).json({ message: 'Project not found' });
98
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
99
+ return res.status(403).json({ message: 'Permission denied' });
94
100
  await (0, file_1.deleteProjectImage)(adapter, projectId);
95
101
  const updated = await Project.update({ _id: projectId }, { image: '' });
96
102
  res.status(201).json({ ...updated, message: 'Image deleted successfully' });
@@ -119,7 +125,7 @@ function storageRouter(config) {
119
125
  const project = await Project.findOne({ _id: projectId });
120
126
  if (!project)
121
127
  return res.status(404).json({ message: 'Project not found' });
122
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
128
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
123
129
  return res.status(403).json({ message: 'Unauthorized access' });
124
130
  if (!adapter.getPresignedUploadUrl) {
125
131
  return res.status(501).json({ message: 'Storage adapter does not support presigned uploads' });
@@ -192,7 +198,7 @@ function storageRouter(config) {
192
198
  const project = await Project.findOne({ _id: projectId });
193
199
  if (!project)
194
200
  return res.status(404).json({ message: 'Project not found' });
195
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
201
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
196
202
  return res.status(403).json({ message: 'Unauthorized access' });
197
203
  const collection = getRawFilesCollection();
198
204
  if (!collection)
@@ -213,7 +219,7 @@ function storageRouter(config) {
213
219
  const project = await Project.findOne({ _id: projectId });
214
220
  if (!project)
215
221
  return res.status(404).json({ message: 'Project not found' });
216
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
222
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
217
223
  return res.status(403).json({ message: 'Unauthorized access' });
218
224
  const collection = getRawFilesCollection();
219
225
  if (!collection)
@@ -291,7 +297,7 @@ function storageRouter(config) {
291
297
  const project = await Project.findOne({ _id: projectId });
292
298
  if (!project)
293
299
  return res.status(404).json({ message: 'Project not found' });
294
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
300
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
295
301
  return res.status(403).json({ message: 'Unauthorized access' });
296
302
  const connAdapter = await resolveConnAdapter(connId, project, auth);
297
303
  if ('error' in connAdapter)
@@ -419,7 +425,7 @@ function storageRouter(config) {
419
425
  const project = await Project.findOne({ _id: projectId });
420
426
  if (!project)
421
427
  return res.status(404).json({ message: 'Project not found' });
422
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
428
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
423
429
  return res.status(403).json({ message: 'Unauthorized access' });
424
430
  const collection = getRawFilesCollection();
425
431
  if (!collection)
@@ -440,7 +446,7 @@ function storageRouter(config) {
440
446
  const project = await Project.findOne({ _id: projectId });
441
447
  if (!project)
442
448
  return res.status(404).json({ message: 'Project not found' });
443
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
449
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
444
450
  return res.status(403).json({ message: 'Unauthorized access' });
445
451
  const connAdapter = await resolveConnAdapter(connId, project, auth);
446
452
  if ('error' in connAdapter)
@@ -539,7 +545,7 @@ function storageRouter(config) {
539
545
  const project = await Project.findOne({ _id: projectId });
540
546
  if (!project)
541
547
  return res.status(404).json({ message: 'Project not found' });
542
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
548
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
543
549
  return res.status(403).json({ message: 'Unauthorized access' });
544
550
  const conn = findStorageConn(project, connId);
545
551
  const isBuiltin = conn?.source === 'builtin';
@@ -590,7 +596,7 @@ function storageRouter(config) {
590
596
  const project = await Project.findOne({ _id: projectId });
591
597
  if (!project)
592
598
  return res.status(404).json({ message: 'Project not found' });
593
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
599
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
594
600
  return res.status(403).json({ message: 'Unauthorized access' });
595
601
  const collection = getRawFoldersCollection();
596
602
  if (!collection)
@@ -624,7 +630,7 @@ function storageRouter(config) {
624
630
  const project = await Project.findOne({ _id: projectId });
625
631
  if (!project)
626
632
  return res.status(404).json({ message: 'Project not found' });
627
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
633
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
628
634
  return res.status(403).json({ message: 'Unauthorized access' });
629
635
  const collection = getRawFoldersCollection();
630
636
  if (!collection)
@@ -654,7 +660,7 @@ function storageRouter(config) {
654
660
  const project = await Project.findOne({ _id: projectId });
655
661
  if (!project)
656
662
  return res.status(404).json({ message: 'Project not found' });
657
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
663
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
658
664
  return res.status(403).json({ message: 'Unauthorized access' });
659
665
  const foldersCollection = getRawFoldersCollection();
660
666
  if (!foldersCollection)
@@ -682,7 +688,7 @@ function storageRouter(config) {
682
688
  const project = await Project.findOne({ _id: projectId });
683
689
  if (!project)
684
690
  return res.status(404).json({ message: 'Project not found' });
685
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
691
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
686
692
  return res.status(403).json({ message: 'Unauthorized access' });
687
693
  if (folderId != null) {
688
694
  const foldersCollection = getRawFoldersCollection();