streamby-core 0.32.1 → 0.34.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 (55) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/LICENSE +661 -0
  3. package/README.md +53 -2
  4. package/dist/middleware/createRouter.js +17 -2
  5. package/dist/middleware/routes/ai.js +3 -3
  6. package/dist/middleware/routes/connection.js +3 -12
  7. package/dist/middleware/routes/dbConnection.js +7 -26
  8. package/dist/middleware/routes/distribution.js +12 -5
  9. package/dist/middleware/routes/export.js +5 -4
  10. package/dist/middleware/routes/media.js +23 -10
  11. package/dist/middleware/routes/member.js +3 -12
  12. package/dist/middleware/routes/{credential.d.ts → oauth.d.ts} +1 -1
  13. package/dist/middleware/routes/oauth.js +101 -0
  14. package/dist/middleware/routes/pipeline.js +3 -3
  15. package/dist/middleware/routes/production.js +9 -9
  16. package/dist/middleware/routes/project.js +24 -31
  17. package/dist/middleware/routes/renderFarm.js +13 -6
  18. package/dist/middleware/routes/review.js +4 -4
  19. package/dist/middleware/routes/rights.js +1 -1
  20. package/dist/middleware/routes/storage.js +21 -15
  21. package/dist/middleware/routes/storageConnection.js +2 -8
  22. package/dist/middleware/routes/userIntegration.js +17 -2
  23. package/dist/middleware/routes/workflow.js +1 -1
  24. package/dist/services/auth.d.ts +1 -2
  25. package/dist/services/auth.js +9 -11
  26. package/dist/services/connectionResolver.js +6 -6
  27. package/dist/services/executor.d.ts +4 -1
  28. package/dist/services/executor.js +63 -6
  29. package/dist/services/oauthClient.d.ts +10 -0
  30. package/dist/services/oauthClient.js +134 -0
  31. package/dist/services/projectIntegration.d.ts +4 -1
  32. package/dist/services/projectIntegration.js +13 -3
  33. package/dist/services/reencryptCredentials.d.ts +5 -0
  34. package/dist/services/reencryptCredentials.js +74 -0
  35. package/dist/services/userIntegration.d.ts +23 -3
  36. package/dist/services/userIntegration.js +130 -7
  37. package/dist/types/index.d.ts +31 -3
  38. package/dist/utils/auth.d.ts +15 -1
  39. package/dist/utils/auth.js +21 -6
  40. package/dist/utils/encryption.d.ts +6 -1
  41. package/dist/utils/encryption.js +80 -27
  42. package/dist/utils/sanitize.d.ts +6 -0
  43. package/dist/utils/sanitize.js +18 -1
  44. package/dist/utils/serviceEndpoints.d.ts +2 -0
  45. package/dist/utils/serviceEndpoints.js +26 -0
  46. package/package.json +4 -3
  47. package/dist/middleware/routes/apiConnection.d.ts +0 -3
  48. package/dist/middleware/routes/apiConnection.js +0 -100
  49. package/dist/middleware/routes/credential.js +0 -80
  50. package/dist/services/apiConnection.d.ts +0 -18
  51. package/dist/services/apiConnection.js +0 -65
  52. package/dist/services/credential.d.ts +0 -8
  53. package/dist/services/credential.js +0 -70
  54. package/dist/services/pipeline.d.ts +0 -2
  55. package/dist/services/pipeline.js +0 -292
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.oauthRouter = oauthRouter;
7
+ const express_1 = require("express");
8
+ const crypto_1 = __importDefault(require("crypto"));
9
+ const oauthClient_1 = require("../../services/oauthClient");
10
+ const userIntegration_1 = require("../../services/userIntegration");
11
+ const OAUTH_PROVIDERS = new Set(['jira', 'google']);
12
+ const PROVIDER_DISPLAY_NAME = {
13
+ jira: 'Jira',
14
+ google: 'Google',
15
+ };
16
+ // In-memory, single-process state store — the OAuth dance completes within a few minutes
17
+ // of the same request cycle, so no DB collection is needed. A restart mid-flow just means
18
+ // the user retries /start, which is harmless.
19
+ const pendingOAuthStates = new Map();
20
+ const STATE_TTL_MS = 10 * 60 * 1000;
21
+ function sweepExpiredStates() {
22
+ const cutoff = Date.now() - STATE_TTL_MS;
23
+ for (const [state, entry] of pendingOAuthStates) {
24
+ if (entry.issuedAt < cutoff)
25
+ pendingOAuthStates.delete(state);
26
+ }
27
+ }
28
+ function isOAuthProvider(value) {
29
+ return OAUTH_PROVIDERS.has(value);
30
+ }
31
+ function oauthRouter(config) {
32
+ const router = (0, express_1.Router)();
33
+ router.get('/user/integrations/oauth/:provider/start', async (req, res) => {
34
+ try {
35
+ const auth = req.auth;
36
+ const { provider } = req.params;
37
+ if (!isOAuthProvider(provider)) {
38
+ return res.status(400).json({ message: `Provider '${provider}' does not support OAuth.` });
39
+ }
40
+ const oauthConfig = config.oauthProviders?.[provider];
41
+ if (!oauthConfig) {
42
+ return res.status(503).json({ message: `OAuth for '${provider}' is not configured on this deployment.` });
43
+ }
44
+ sweepExpiredStates();
45
+ const state = crypto_1.default.randomBytes(32).toString('hex');
46
+ pendingOAuthStates.set(state, { userId: auth.userId, provider, issuedAt: Date.now() });
47
+ res.redirect((0, oauthClient_1.buildAuthorizeUrl)(provider, oauthConfig, state));
48
+ }
49
+ catch (err) {
50
+ res.status(500).json({ message: 'Failed to start OAuth flow', details: err.message });
51
+ }
52
+ });
53
+ router.get('/user/integrations/oauth/:provider/callback', async (req, res) => {
54
+ const { provider: providerParam } = req.params;
55
+ const provider = isOAuthProvider(providerParam) ? providerParam : null;
56
+ const oauthConfig = provider ? config.oauthProviders?.[provider] : undefined;
57
+ const failureRedirect = (reason) => {
58
+ const failureUrl = oauthConfig?.failureRedirectUrl;
59
+ if (!failureUrl)
60
+ return res.status(400).json({ message: `OAuth failed: ${reason}` });
61
+ const url = new URL(failureUrl);
62
+ url.searchParams.set('oauthError', reason);
63
+ if (provider)
64
+ url.searchParams.set('provider', provider);
65
+ return res.redirect(url.toString());
66
+ };
67
+ try {
68
+ const auth = req.auth;
69
+ if (!provider)
70
+ return res.status(400).json({ message: `Provider '${providerParam}' does not support OAuth.` });
71
+ if (!oauthConfig)
72
+ return res.status(503).json({ message: `OAuth for '${provider}' is not configured on this deployment.` });
73
+ if (req.query.error)
74
+ return failureRedirect(String(req.query.error));
75
+ const code = req.query.code;
76
+ const state = req.query.state;
77
+ if (!code || !state)
78
+ return failureRedirect('missing_code');
79
+ const pending = pendingOAuthStates.get(state);
80
+ pendingOAuthStates.delete(state); // single-use, regardless of outcome below
81
+ if (!pending || pending.provider !== provider || pending.userId !== auth.userId) {
82
+ return failureRedirect(pending ? 'state_mismatch' : 'invalid_state');
83
+ }
84
+ let tokens;
85
+ try {
86
+ tokens = await (0, oauthClient_1.exchangeCodeForTokens)(provider, oauthConfig, code);
87
+ }
88
+ catch {
89
+ return failureRedirect('token_exchange_failed');
90
+ }
91
+ await (0, userIntegration_1.upsertOAuthServiceIntegration)(auth.userId, provider, PROVIDER_DISPLAY_NAME[provider], tokens);
92
+ const url = new URL(oauthConfig.successRedirectUrl);
93
+ url.searchParams.set('connected', provider);
94
+ res.redirect(url.toString());
95
+ }
96
+ catch (err) {
97
+ failureRedirect('unexpected_error');
98
+ }
99
+ });
100
+ return router;
101
+ }
@@ -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)
@@ -72,9 +72,6 @@ function projectRouter(config) {
72
72
  if (req.subscription === 'freemium') {
73
73
  return res.status(403).json({ error: 'Freemium users cannot create projects' });
74
74
  }
75
- if (auth.role !== 'admin' && auth.role !== 'editor') {
76
- return res.status(403).json({ message: 'Permission denied' });
77
- }
78
75
  const { name, description, image, allowedOrigin, public: isPublic = true, category, integrationIds } = req.body;
79
76
  if (isPublic === false && req.subscription === 'freemium') {
80
77
  return res.status(403).json({ error: 'Private projects require a subscriber plan' });
@@ -93,6 +90,7 @@ function projectRouter(config) {
93
90
  // builtin id. Any invalid id rejects the whole request (no project is created).
94
91
  const dbConnections = [];
95
92
  const storageConnections = [];
93
+ const integrationConnections = [];
96
94
  if (Array.isArray(integrationIds)) {
97
95
  for (const id of integrationIds) {
98
96
  const result = await (0, projectIntegration_1.classifyIntegrationId)(id, auth, config, '');
@@ -100,8 +98,10 @@ function projectRouter(config) {
100
98
  return res.status(result.status).json({ message: result.error });
101
99
  if (result.kind === 'database')
102
100
  dbConnections.push(result.connection);
103
- else
101
+ else if (result.kind === 'storage')
104
102
  storageConnections.push(result.connection);
103
+ else
104
+ integrationConnections.push(result.connection);
105
105
  }
106
106
  }
107
107
  let newProject = await Project.create({
@@ -115,12 +115,14 @@ function projectRouter(config) {
115
115
  members: [{ userId: auth.userId, username: user.username, role: "admin", status: "active", archived: false }],
116
116
  dbConnections: [],
117
117
  storageConnections: [],
118
+ integrationConnections: [],
118
119
  });
119
120
  const newProjectId = newProject._id || newProject.id;
120
- if (dbConnections.length || storageConnections.length) {
121
+ if (dbConnections.length || storageConnections.length || integrationConnections.length) {
121
122
  const stamped = await Project.update({ _id: newProjectId }, {
122
123
  dbConnections: dbConnections.map(c => ({ ...c, projectId: newProjectId })),
123
124
  storageConnections: storageConnections.map(c => ({ ...c, projectId: newProjectId })),
125
+ integrationConnections: integrationConnections.map(c => ({ ...c, projectId: newProjectId })),
124
126
  });
125
127
  if (stamped)
126
128
  newProject = stamped;
@@ -203,9 +205,6 @@ function projectRouter(config) {
203
205
  router.patch('/projects/:id', async (req, res) => {
204
206
  try {
205
207
  const auth = req.auth;
206
- if (auth.role !== 'admin' && auth.role !== 'editor') {
207
- return res.status(403).json({ message: 'Permission denied' });
208
- }
209
208
  const projectId = req.params.id;
210
209
  const updates = req.body;
211
210
  const project = await Project.findOne({ _id: projectId });
@@ -215,7 +214,7 @@ function projectRouter(config) {
215
214
  if (!updates || typeof updates !== 'object') {
216
215
  return res.status(400).json({ message: 'Missing updates payload' });
217
216
  }
218
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
217
+ if (!(0, auth_1.canEditProject)(project, auth.userId)) {
219
218
  return res.status(403).json({ message: 'Unauthorized project access' });
220
219
  }
221
220
  const updated = await Project.update({ _id: projectId }, updates);
@@ -245,8 +244,9 @@ function projectRouter(config) {
245
244
  if (!project) {
246
245
  return res.status(404).json({ message: 'Project not found' });
247
246
  }
248
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
249
- return res.status(403).json({ message: 'Unauthorized project access' });
247
+ // Only an admin of the project deletes it.
248
+ if (!(0, auth_1.isProjectAdmin)(project, auth.userId)) {
249
+ return res.status(403).json({ message: 'Only a project admin can delete it' });
250
250
  }
251
251
  await Project.useDbType(project.dbType).delete({ _id: projectId });
252
252
  const allProjects = await Project.find({});
@@ -343,9 +343,6 @@ function projectRouter(config) {
343
343
  router.post('/projects/:id/image', async (req, res) => {
344
344
  try {
345
345
  const auth = req.auth;
346
- if (auth.role !== 'admin' && auth.role !== 'editor') {
347
- return res.status(403).json({ message: 'Permission denied' });
348
- }
349
346
  const projectId = req.params.id;
350
347
  const { imageUrl } = req.body;
351
348
  if (!projectId || !imageUrl) {
@@ -355,7 +352,7 @@ function projectRouter(config) {
355
352
  if (!project) {
356
353
  return res.status(404).json({ message: 'Project not found' });
357
354
  }
358
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
355
+ if (!(0, auth_1.canEditProject)(project, auth.userId)) {
359
356
  return res.status(403).json({ message: 'Unauthorized project access' });
360
357
  }
361
358
  const updatedProject = await Project.update({ _id: projectId }, { image: imageUrl });
@@ -371,16 +368,13 @@ function projectRouter(config) {
371
368
  router.patch('/projects/:id/origins', async (req, res) => {
372
369
  try {
373
370
  const auth = req.auth;
374
- if (auth.role !== 'admin' && auth.role !== 'editor') {
375
- return res.status(403).json({ message: 'Permission denied' });
376
- }
377
371
  const projectId = req.params.id;
378
372
  const { origins } = req.body;
379
373
  const project = await Project.findOne({ _id: projectId });
380
374
  if (!project) {
381
375
  return res.status(404).json({ message: 'Project not found' });
382
376
  }
383
- if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
377
+ if (!(0, auth_1.canEditProject)(project, auth.userId)) {
384
378
  return res.status(403).json({ message: 'Unauthorized project access' });
385
379
  }
386
380
  const updated = await Project.update({ _id: projectId }, { allowedOrigin: origins });
@@ -397,9 +391,6 @@ function projectRouter(config) {
397
391
  router.post('/projects/:id/integrations', async (req, res) => {
398
392
  try {
399
393
  const auth = req.auth;
400
- if (auth.role !== 'admin' && auth.role !== 'editor') {
401
- return res.status(403).json({ message: 'Permission denied' });
402
- }
403
394
  const projectId = req.params.id;
404
395
  const { integrationId } = req.body;
405
396
  if (!integrationId)
@@ -407,9 +398,9 @@ function projectRouter(config) {
407
398
  const project = await Project.findOne({ _id: projectId });
408
399
  if (!project)
409
400
  return res.status(404).json({ message: 'Project not found' });
410
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
401
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
411
402
  return res.status(403).json({ message: 'Unauthorized project access' });
412
- const alreadyConnected = [...(project.dbConnections ?? []), ...(project.storageConnections ?? [])]
403
+ const alreadyConnected = [...(project.dbConnections ?? []), ...(project.storageConnections ?? []), ...(project.integrationConnections ?? [])]
413
404
  .some((c) => c.integrationId === integrationId);
414
405
  if (alreadyConnected)
415
406
  return res.status(400).json({ message: 'This integration is already connected to the project' });
@@ -418,7 +409,9 @@ function projectRouter(config) {
418
409
  return res.status(result.status).json({ message: result.error });
419
410
  const updated = result.kind === 'database'
420
411
  ? await Project.update({ _id: projectId }, { $push: { dbConnections: result.connection } })
421
- : await Project.update({ _id: projectId }, { $push: { storageConnections: result.connection } });
412
+ : result.kind === 'storage'
413
+ ? await Project.update({ _id: projectId }, { $push: { storageConnections: result.connection } })
414
+ : await Project.update({ _id: projectId }, { $push: { integrationConnections: result.connection } });
422
415
  if (!updated)
423
416
  return res.status(404).json({ message: 'Project not found or not updated' });
424
417
  res.status(201).json({ data: result.connection, project: (0, sanitize_1.sanitizeProject)(updated) });
@@ -431,23 +424,23 @@ function projectRouter(config) {
431
424
  router.delete('/projects/:id/integrations/:integrationId', async (req, res) => {
432
425
  try {
433
426
  const auth = req.auth;
434
- if (auth.role !== 'admin' && auth.role !== 'editor') {
435
- return res.status(403).json({ message: 'Permission denied' });
436
- }
437
427
  const projectId = req.params.id;
438
428
  const { integrationId } = req.params;
439
429
  const project = await Project.findOne({ _id: projectId });
440
430
  if (!project)
441
431
  return res.status(404).json({ message: 'Project not found' });
442
- if (!(0, auth_1.isProjectMember)(project, auth.userId))
432
+ if (!(0, auth_1.canEditProject)(project, auth.userId))
443
433
  return res.status(403).json({ message: 'Unauthorized project access' });
444
434
  const dbConn = (project.dbConnections ?? []).find((c) => c.integrationId === integrationId);
445
435
  const storageConn = (project.storageConnections ?? []).find((c) => c.integrationId === integrationId);
446
- if (!dbConn && !storageConn)
436
+ const integrationConn = (project.integrationConnections ?? []).find((c) => c.integrationId === integrationId);
437
+ if (!dbConn && !storageConn && !integrationConn)
447
438
  return res.status(404).json({ message: 'Integration not connected to this project' });
448
439
  const updated = dbConn
449
440
  ? await Project.update({ _id: projectId }, { $pull: { dbConnections: { id: dbConn.id } } })
450
- : await Project.update({ _id: projectId }, { $pull: { storageConnections: { id: storageConn.id } } });
441
+ : storageConn
442
+ ? await Project.update({ _id: projectId }, { $pull: { storageConnections: { id: storageConn.id } } })
443
+ : await Project.update({ _id: projectId }, { $pull: { integrationConnections: { id: integrationConn.id } } });
451
444
  if (!updated)
452
445
  return res.status(404).json({ message: 'Project not found or not updated' });
453
446
  res.status(200).json({ message: 'Integration removed', project: (0, sanitize_1.sanitizeProject)(updated) });
@@ -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();