streamby-core 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/LICENSE +661 -661
- package/README.md +680 -573
- package/dist/adapters/database/connectionManager.js +179 -124
- package/dist/adapters/database/sql.js +1 -1
- package/dist/mcp/tools.js +41 -0
- package/dist/middleware/createRouter.js +11 -1
- package/dist/middleware/routes/ai.js +3 -3
- package/dist/middleware/routes/deliverable.d.ts +4 -2
- package/dist/middleware/routes/deliverable.js +16 -7
- package/dist/middleware/routes/media.js +7 -6
- package/dist/middleware/routes/project.js +5 -1
- package/dist/middleware/routes/renderFarm.js +4 -77
- package/dist/middleware/routes/storage.js +13 -27
- package/dist/middleware/routes/worker.d.ts +7 -0
- package/dist/middleware/routes/worker.js +125 -0
- package/dist/middleware/routes/workerCallback.d.ts +5 -0
- package/dist/middleware/routes/workerCallback.js +31 -0
- package/dist/models/Model.js +9 -9
- package/dist/services/aiProcessor.js +11 -11
- package/dist/services/apiTokenPermissions.js +6 -5
- package/dist/services/executor.js +10 -10
- package/dist/services/jobQueue.d.ts +6 -4
- package/dist/services/jobQueue.js +28 -16
- package/dist/services/jobStore.d.ts +27 -0
- package/dist/services/jobStore.js +177 -0
- package/dist/services/mediaProcessor.js +16 -16
- package/dist/services/vfxProcessor.js +12 -12
- package/dist/services/workerJobs.d.ts +73 -0
- package/dist/services/workerJobs.js +459 -0
- package/dist/services/workers.d.ts +78 -0
- package/dist/services/workers.js +179 -0
- package/dist/types/index.d.ts +52 -2
- package/dist/utils/assetCategories.d.ts +4 -0
- package/dist/utils/assetCategories.js +21 -0
- package/dist/utils/builtinAccess.d.ts +1 -1
- package/dist/utils/builtinAccess.js +4 -3
- package/dist/utils/privateAddress.d.ts +10 -0
- package/dist/utils/privateAddress.js +85 -0
- package/package.json +1 -1
- package/dist/middleware/routes/apiConnection.d.ts +0 -3
- package/dist/middleware/routes/apiConnection.js +0 -100
- package/dist/middleware/routes/credential.d.ts +0 -3
- package/dist/middleware/routes/credential.js +0 -80
- package/dist/services/apiConnection.d.ts +0 -18
- package/dist/services/apiConnection.js +0 -65
- package/dist/services/credential.d.ts +0 -8
- package/dist/services/credential.js +0 -70
- package/dist/services/pipeline.d.ts +0 -2
- package/dist/services/pipeline.js +0 -292
- package/dist/utils/crypto.d.ts +0 -3
- package/dist/utils/crypto.js +0 -32
|
@@ -41,159 +41,214 @@ const getConnection = (id) => {
|
|
|
41
41
|
return clientEntry;
|
|
42
42
|
};
|
|
43
43
|
exports.getConnection = getConnection;
|
|
44
|
+
// StreamBy's tables, made (or brought up to date) at startup. Several instances starting at once
|
|
45
|
+
// against the same database would deadlock on these statements, so one runs them at a time:
|
|
46
|
+
// in a transaction holding a transaction-level advisory lock, which ends with it. (A session
|
|
47
|
+
// lock could outlive its unlock behind a pooler in transaction mode, such as Supabase's, which
|
|
48
|
+
// may run the unlock on another connection, and block every later startup.)
|
|
44
49
|
const ensureTablesExist = async (pool) => {
|
|
50
|
+
const client = await pool.connect();
|
|
45
51
|
try {
|
|
46
|
-
await
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
await client.query('BEGIN');
|
|
53
|
+
await client.query(`SELECT pg_advisory_xact_lock(hashtext('streamby:ensure-tables'))`);
|
|
54
|
+
await createTables(client);
|
|
55
|
+
await client.query('COMMIT');
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
await client.query('ROLLBACK').catch(() => { });
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
client.release();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const createTables = async (pool) => {
|
|
66
|
+
try {
|
|
67
|
+
await pool.query(`
|
|
68
|
+
CREATE SCHEMA IF NOT EXISTS streamby;
|
|
69
|
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
49
70
|
`);
|
|
50
71
|
// Migrate id column from UUID to TEXT if it was created with the old schema
|
|
51
|
-
await pool.query(`
|
|
52
|
-
DO $$ BEGIN
|
|
53
|
-
ALTER TABLE streamby.projects ALTER COLUMN id TYPE TEXT USING id::text;
|
|
54
|
-
ALTER TABLE streamby.projects ALTER COLUMN id SET DEFAULT uuid_generate_v4()::text;
|
|
55
|
-
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
72
|
+
await pool.query(`
|
|
73
|
+
DO $$ BEGIN
|
|
74
|
+
ALTER TABLE streamby.projects ALTER COLUMN id TYPE TEXT USING id::text;
|
|
75
|
+
ALTER TABLE streamby.projects ALTER COLUMN id SET DEFAULT uuid_generate_v4()::text;
|
|
76
|
+
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
56
77
|
`);
|
|
57
|
-
await pool.query(`
|
|
58
|
-
CREATE TABLE IF NOT EXISTS streamby.projects (
|
|
59
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
60
|
-
name VARCHAR(255) NOT NULL,
|
|
61
|
-
description TEXT,
|
|
62
|
-
image TEXT,
|
|
63
|
-
"allowedOrigin" JSONB DEFAULT '[]',
|
|
64
|
-
exports JSONB DEFAULT '[]',
|
|
65
|
-
connections JSONB DEFAULT '[]',
|
|
66
|
-
"dbConnections" JSONB DEFAULT '[]',
|
|
67
|
-
"dbType" VARCHAR(50) NOT NULL DEFAULT 'sql',
|
|
68
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
69
|
-
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
70
|
-
);
|
|
78
|
+
await pool.query(`
|
|
79
|
+
CREATE TABLE IF NOT EXISTS streamby.projects (
|
|
80
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
81
|
+
name VARCHAR(255) NOT NULL,
|
|
82
|
+
description TEXT,
|
|
83
|
+
image TEXT,
|
|
84
|
+
"allowedOrigin" JSONB DEFAULT '[]',
|
|
85
|
+
exports JSONB DEFAULT '[]',
|
|
86
|
+
connections JSONB DEFAULT '[]',
|
|
87
|
+
"dbConnections" JSONB DEFAULT '[]',
|
|
88
|
+
"dbType" VARCHAR(50) NOT NULL DEFAULT 'sql',
|
|
89
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
90
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
91
|
+
);
|
|
71
92
|
`);
|
|
72
93
|
console.log('✅ "projects" table ensured to exist.');
|
|
73
94
|
// Add JSONB columns to existing tables that predate this schema version
|
|
74
|
-
await pool.query(`
|
|
75
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "allowedOrigin" JSONB DEFAULT '[]';
|
|
76
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS exports JSONB DEFAULT '[]';
|
|
77
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS connections JSONB DEFAULT '[]';
|
|
78
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbConnections" JSONB DEFAULT '[]';
|
|
79
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "storageConnections" JSONB DEFAULT '[]';
|
|
80
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS workflow JSONB;
|
|
81
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS pipelines JSONB DEFAULT '[]';
|
|
82
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "builtinBackfilledAt" TIMESTAMP WITH TIME ZONE;
|
|
83
|
-
-- Written when a project is made (routes/project) and by its connection routes.
|
|
84
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS public BOOLEAN DEFAULT TRUE;
|
|
85
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS category TEXT;
|
|
86
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "integrationConnections" JSONB DEFAULT '[]';
|
|
87
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "distributionConnections" JSONB DEFAULT '[]';
|
|
88
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "renderFarmConnections" JSONB DEFAULT '[]';
|
|
95
|
+
await pool.query(`
|
|
96
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "allowedOrigin" JSONB DEFAULT '[]';
|
|
97
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS exports JSONB DEFAULT '[]';
|
|
98
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS connections JSONB DEFAULT '[]';
|
|
99
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbConnections" JSONB DEFAULT '[]';
|
|
100
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "storageConnections" JSONB DEFAULT '[]';
|
|
101
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS workflow JSONB;
|
|
102
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS pipelines JSONB DEFAULT '[]';
|
|
103
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "builtinBackfilledAt" TIMESTAMP WITH TIME ZONE;
|
|
104
|
+
-- Written when a project is made (routes/project) and by its connection routes.
|
|
105
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS public BOOLEAN DEFAULT TRUE;
|
|
106
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS category TEXT;
|
|
107
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "integrationConnections" JSONB DEFAULT '[]';
|
|
108
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "distributionConnections" JSONB DEFAULT '[]';
|
|
109
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "renderFarmConnections" JSONB DEFAULT '[]';
|
|
89
110
|
`);
|
|
90
111
|
// Ensure dbType exists with a non-null default
|
|
91
|
-
await pool.query(`
|
|
92
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbType" VARCHAR(50) NOT NULL DEFAULT 'sql';
|
|
112
|
+
await pool.query(`
|
|
113
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbType" VARCHAR(50) NOT NULL DEFAULT 'sql';
|
|
93
114
|
`);
|
|
94
|
-
await pool.query(`
|
|
95
|
-
CREATE TABLE IF NOT EXISTS streamby.project_members (
|
|
96
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
97
|
-
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
98
|
-
"userId" VARCHAR(255) NOT NULL,
|
|
99
|
-
role VARCHAR(255) NOT NULL DEFAULT 'member',
|
|
100
|
-
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
|
101
|
-
archived BOOLEAN NOT NULL DEFAULT FALSE,
|
|
102
|
-
"archivedBy" VARCHAR(255),
|
|
103
|
-
"archivedAt" TIMESTAMP WITH TIME ZONE,
|
|
104
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
105
|
-
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
106
|
-
UNIQUE ("projectId", "userId")
|
|
107
|
-
);
|
|
115
|
+
await pool.query(`
|
|
116
|
+
CREATE TABLE IF NOT EXISTS streamby.project_members (
|
|
117
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
118
|
+
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
119
|
+
"userId" VARCHAR(255) NOT NULL,
|
|
120
|
+
role VARCHAR(255) NOT NULL DEFAULT 'member',
|
|
121
|
+
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
|
122
|
+
archived BOOLEAN NOT NULL DEFAULT FALSE,
|
|
123
|
+
"archivedBy" VARCHAR(255),
|
|
124
|
+
"archivedAt" TIMESTAMP WITH TIME ZONE,
|
|
125
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
126
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
127
|
+
UNIQUE ("projectId", "userId")
|
|
128
|
+
);
|
|
108
129
|
`);
|
|
109
|
-
await pool.query(`
|
|
110
|
-
ALTER TABLE streamby.project_members ADD COLUMN IF NOT EXISTS status VARCHAR(50) NOT NULL DEFAULT 'active';
|
|
130
|
+
await pool.query(`
|
|
131
|
+
ALTER TABLE streamby.project_members ADD COLUMN IF NOT EXISTS status VARCHAR(50) NOT NULL DEFAULT 'active';
|
|
111
132
|
`);
|
|
112
133
|
console.log('✅ "project_members" table ensured to exist.');
|
|
113
134
|
// Migrate projectId FK column from UUID to TEXT if table predates this schema version
|
|
114
|
-
await pool.query(`
|
|
115
|
-
DO $$ BEGIN
|
|
116
|
-
ALTER TABLE streamby.project_members DROP CONSTRAINT IF EXISTS project_members_projectId_fkey;
|
|
117
|
-
ALTER TABLE streamby.project_members ALTER COLUMN "projectId" TYPE TEXT;
|
|
118
|
-
ALTER TABLE streamby.project_members ADD CONSTRAINT project_members_projectid_fkey
|
|
119
|
-
FOREIGN KEY ("projectId") REFERENCES streamby.projects(id) ON DELETE CASCADE;
|
|
120
|
-
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
135
|
+
await pool.query(`
|
|
136
|
+
DO $$ BEGIN
|
|
137
|
+
ALTER TABLE streamby.project_members DROP CONSTRAINT IF EXISTS project_members_projectId_fkey;
|
|
138
|
+
ALTER TABLE streamby.project_members ALTER COLUMN "projectId" TYPE TEXT;
|
|
139
|
+
ALTER TABLE streamby.project_members ADD CONSTRAINT project_members_projectid_fkey
|
|
140
|
+
FOREIGN KEY ("projectId") REFERENCES streamby.projects(id) ON DELETE CASCADE;
|
|
141
|
+
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
121
142
|
`);
|
|
122
|
-
await pool.query(`
|
|
123
|
-
CREATE TABLE IF NOT EXISTS streamby.exports (
|
|
124
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
125
|
-
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
126
|
-
status VARCHAR(50) NOT NULL DEFAULT 'pending',
|
|
127
|
-
"filePath" TEXT,
|
|
128
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
129
|
-
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
130
|
-
);
|
|
143
|
+
await pool.query(`
|
|
144
|
+
CREATE TABLE IF NOT EXISTS streamby.exports (
|
|
145
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
146
|
+
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
147
|
+
status VARCHAR(50) NOT NULL DEFAULT 'pending',
|
|
148
|
+
"filePath" TEXT,
|
|
149
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
150
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
151
|
+
);
|
|
131
152
|
`);
|
|
132
153
|
console.log('✅ "exports" table ensured to exist.');
|
|
133
154
|
// user_integrations is registered against the main db only (see createRouter.ts), but
|
|
134
155
|
// this function runs per-SQL-connection like projects/exports above — harmless on
|
|
135
156
|
// non-main connections since the model never queries them there.
|
|
136
|
-
await pool.query(`
|
|
137
|
-
CREATE TABLE IF NOT EXISTS streamby.user_integrations (
|
|
138
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
139
|
-
"userId" VARCHAR(255) NOT NULL,
|
|
140
|
-
kind VARCHAR(20) NOT NULL,
|
|
141
|
-
provider VARCHAR(20) NOT NULL,
|
|
142
|
-
name VARCHAR(255) NOT NULL,
|
|
143
|
-
description TEXT,
|
|
144
|
-
"encryptedCredential" TEXT NOT NULL,
|
|
145
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
146
|
-
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
147
|
-
);
|
|
148
|
-
CREATE INDEX IF NOT EXISTS user_integrations_userid_idx ON streamby.user_integrations ("userId");
|
|
157
|
+
await pool.query(`
|
|
158
|
+
CREATE TABLE IF NOT EXISTS streamby.user_integrations (
|
|
159
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
160
|
+
"userId" VARCHAR(255) NOT NULL,
|
|
161
|
+
kind VARCHAR(20) NOT NULL,
|
|
162
|
+
provider VARCHAR(20) NOT NULL,
|
|
163
|
+
name VARCHAR(255) NOT NULL,
|
|
164
|
+
description TEXT,
|
|
165
|
+
"encryptedCredential" TEXT NOT NULL,
|
|
166
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
167
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
168
|
+
);
|
|
169
|
+
CREATE INDEX IF NOT EXISTS user_integrations_userid_idx ON streamby.user_integrations ("userId");
|
|
149
170
|
`);
|
|
150
171
|
console.log('✅ "user_integrations" table ensured to exist.');
|
|
151
172
|
// API tokens: only their hash is kept (see services/apiTokens). Main db only, like
|
|
152
173
|
// user_integrations above. A table from before tokens had a kind and permissions gets the
|
|
153
174
|
// new columns; its old tokens no longer work (they have no kind) and aren't listed.
|
|
154
|
-
await pool.query(`
|
|
155
|
-
CREATE TABLE IF NOT EXISTS streamby.api_tokens (
|
|
156
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
157
|
-
kind VARCHAR(10),
|
|
158
|
-
"userId" VARCHAR(255) NOT NULL,
|
|
159
|
-
"createdByName" VARCHAR(255),
|
|
160
|
-
name VARCHAR(255) NOT NULL,
|
|
161
|
-
prefix VARCHAR(32) NOT NULL,
|
|
162
|
-
hash CHAR(64) NOT NULL UNIQUE,
|
|
163
|
-
permissions JSONB NOT NULL DEFAULT '[]',
|
|
164
|
-
"projectIds" JSONB NOT NULL,
|
|
165
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
166
|
-
"expiresAt" TIMESTAMP WITH TIME ZONE,
|
|
167
|
-
"lastUsedAt" TIMESTAMP WITH TIME ZONE
|
|
168
|
-
);
|
|
169
|
-
ALTER TABLE streamby.api_tokens ADD COLUMN IF NOT EXISTS kind VARCHAR(10);
|
|
170
|
-
ALTER TABLE streamby.api_tokens ADD COLUMN IF NOT EXISTS "createdByName" VARCHAR(255);
|
|
171
|
-
ALTER TABLE streamby.api_tokens ADD COLUMN IF NOT EXISTS permissions JSONB NOT NULL DEFAULT '[]';
|
|
172
|
-
DO $$ BEGIN
|
|
173
|
-
IF EXISTS (SELECT 1 FROM information_schema.columns
|
|
174
|
-
WHERE table_schema = 'streamby' AND table_name = 'api_tokens' AND column_name = 'access') THEN
|
|
175
|
-
ALTER TABLE streamby.api_tokens ALTER COLUMN access DROP NOT NULL;
|
|
176
|
-
END IF;
|
|
177
|
-
END $$;
|
|
178
|
-
CREATE INDEX IF NOT EXISTS api_tokens_userid_idx ON streamby.api_tokens ("userId");
|
|
179
|
-
CREATE INDEX IF NOT EXISTS api_tokens_kind_idx ON streamby.api_tokens (kind);
|
|
175
|
+
await pool.query(`
|
|
176
|
+
CREATE TABLE IF NOT EXISTS streamby.api_tokens (
|
|
177
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
178
|
+
kind VARCHAR(10),
|
|
179
|
+
"userId" VARCHAR(255) NOT NULL,
|
|
180
|
+
"createdByName" VARCHAR(255),
|
|
181
|
+
name VARCHAR(255) NOT NULL,
|
|
182
|
+
prefix VARCHAR(32) NOT NULL,
|
|
183
|
+
hash CHAR(64) NOT NULL UNIQUE,
|
|
184
|
+
permissions JSONB NOT NULL DEFAULT '[]',
|
|
185
|
+
"projectIds" JSONB NOT NULL,
|
|
186
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
187
|
+
"expiresAt" TIMESTAMP WITH TIME ZONE,
|
|
188
|
+
"lastUsedAt" TIMESTAMP WITH TIME ZONE
|
|
189
|
+
);
|
|
190
|
+
ALTER TABLE streamby.api_tokens ADD COLUMN IF NOT EXISTS kind VARCHAR(10);
|
|
191
|
+
ALTER TABLE streamby.api_tokens ADD COLUMN IF NOT EXISTS "createdByName" VARCHAR(255);
|
|
192
|
+
ALTER TABLE streamby.api_tokens ADD COLUMN IF NOT EXISTS permissions JSONB NOT NULL DEFAULT '[]';
|
|
193
|
+
DO $$ BEGIN
|
|
194
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns
|
|
195
|
+
WHERE table_schema = 'streamby' AND table_name = 'api_tokens' AND column_name = 'access') THEN
|
|
196
|
+
ALTER TABLE streamby.api_tokens ALTER COLUMN access DROP NOT NULL;
|
|
197
|
+
END IF;
|
|
198
|
+
END $$;
|
|
199
|
+
CREATE INDEX IF NOT EXISTS api_tokens_userid_idx ON streamby.api_tokens ("userId");
|
|
200
|
+
CREATE INDEX IF NOT EXISTS api_tokens_kind_idx ON streamby.api_tokens (kind);
|
|
180
201
|
`);
|
|
181
202
|
console.log('✅ "api_tokens" table ensured to exist.');
|
|
203
|
+
// Jobs (see services/jobStore): kept here so they survive restarts and every instance sees
|
|
204
|
+
// them. Main db only, like the tables above.
|
|
205
|
+
await pool.query(`
|
|
206
|
+
CREATE TABLE IF NOT EXISTS streamby.jobs (
|
|
207
|
+
id TEXT PRIMARY KEY,
|
|
208
|
+
"jobType" VARCHAR(40) NOT NULL,
|
|
209
|
+
"userId" VARCHAR(255) NOT NULL,
|
|
210
|
+
"projectId" VARCHAR(255) NOT NULL,
|
|
211
|
+
"assetId" VARCHAR(255),
|
|
212
|
+
status VARCHAR(20) NOT NULL,
|
|
213
|
+
stage VARCHAR(60) NOT NULL,
|
|
214
|
+
progress INTEGER NOT NULL DEFAULT 0,
|
|
215
|
+
payload JSONB NOT NULL DEFAULT '{}',
|
|
216
|
+
error TEXT,
|
|
217
|
+
worker JSONB,
|
|
218
|
+
kind VARCHAR(100),
|
|
219
|
+
profile JSONB,
|
|
220
|
+
"versionId" VARCHAR(255),
|
|
221
|
+
"resultMode" VARCHAR(20),
|
|
222
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
223
|
+
"maxAttempts" INTEGER NOT NULL DEFAULT 1,
|
|
224
|
+
"nextAttemptAt" TIMESTAMP WITH TIME ZONE,
|
|
225
|
+
"leaseUntil" TIMESTAMP WITH TIME ZONE,
|
|
226
|
+
"callbackHash" CHAR(64),
|
|
227
|
+
upload JSONB,
|
|
228
|
+
report JSONB,
|
|
229
|
+
result JSONB,
|
|
230
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
231
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
232
|
+
);
|
|
233
|
+
CREATE INDEX IF NOT EXISTS jobs_project_idx ON streamby.jobs ("projectId", "createdAt");
|
|
234
|
+
CREATE INDEX IF NOT EXISTS jobs_due_idx ON streamby.jobs (status, "nextAttemptAt") WHERE worker IS NOT NULL;
|
|
235
|
+
`);
|
|
236
|
+
console.log('✅ "jobs" table ensured to exist.');
|
|
182
237
|
// The MCP server's audit log: every tool call (see mcp/audit). Main db only.
|
|
183
|
-
await pool.query(`
|
|
184
|
-
CREATE TABLE IF NOT EXISTS streamby.mcp_audit (
|
|
185
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
186
|
-
"userId" VARCHAR(255) NOT NULL,
|
|
187
|
-
"tokenId" VARCHAR(255) NOT NULL,
|
|
188
|
-
"projectId" VARCHAR(255),
|
|
189
|
-
tool VARCHAR(100) NOT NULL,
|
|
190
|
-
ok BOOLEAN NOT NULL,
|
|
191
|
-
status INTEGER,
|
|
192
|
-
error TEXT,
|
|
193
|
-
"durationMs" INTEGER NOT NULL,
|
|
194
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
195
|
-
);
|
|
196
|
-
CREATE INDEX IF NOT EXISTS mcp_audit_token_idx ON streamby.mcp_audit ("tokenId", "createdAt");
|
|
238
|
+
await pool.query(`
|
|
239
|
+
CREATE TABLE IF NOT EXISTS streamby.mcp_audit (
|
|
240
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
241
|
+
"userId" VARCHAR(255) NOT NULL,
|
|
242
|
+
"tokenId" VARCHAR(255) NOT NULL,
|
|
243
|
+
"projectId" VARCHAR(255),
|
|
244
|
+
tool VARCHAR(100) NOT NULL,
|
|
245
|
+
ok BOOLEAN NOT NULL,
|
|
246
|
+
status INTEGER,
|
|
247
|
+
error TEXT,
|
|
248
|
+
"durationMs" INTEGER NOT NULL,
|
|
249
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
250
|
+
);
|
|
251
|
+
CREATE INDEX IF NOT EXISTS mcp_audit_token_idx ON streamby.mcp_audit ("tokenId", "createdAt");
|
|
197
252
|
`);
|
|
198
253
|
console.log('✅ "mcp_audit" table ensured to exist.');
|
|
199
254
|
}
|
|
@@ -10,7 +10,7 @@ function serializeForPg(v) {
|
|
|
10
10
|
}
|
|
11
11
|
const createSQLExportTable = async (connection, projectId, exportName, nodeSchema) => {
|
|
12
12
|
const exportId = (0, crypto_1.randomUUID)();
|
|
13
|
-
await connection.query(`INSERT INTO "exports" (id, "projectId", node_schema, name, method, created_at, updated_at)
|
|
13
|
+
await connection.query(`INSERT INTO "exports" (id, "projectId", node_schema, name, method, created_at, updated_at)
|
|
14
14
|
VALUES ($1, $2, $3, $4, 'GET', NOW(), NOW())`, [exportId, projectId, nodeSchema ? JSON.stringify(nodeSchema) : null, exportName]);
|
|
15
15
|
return { exportId };
|
|
16
16
|
};
|
package/dist/mcp/tools.js
CHANGED
|
@@ -317,6 +317,47 @@ exports.TOOLS = [
|
|
|
317
317
|
projectOf: byProject,
|
|
318
318
|
call: ({ projectId, deliverableId }) => ({ method: 'DELETE', path: `/projects/${enc(projectId)}/deliverables/${enc(deliverableId)}` }),
|
|
319
319
|
}),
|
|
320
|
+
// ── Processing (workers) ──
|
|
321
|
+
tool({
|
|
322
|
+
name: 'list_workers', title: 'List workers', permissions: ['project:read'],
|
|
323
|
+
description: "The processing workers a project can use: the host's and the project's own (each one's id, source, what it does — its kinds — and the asset categories it takes). They show in the project's Workers settings.",
|
|
324
|
+
input: { projectId },
|
|
325
|
+
projectOf: byProject,
|
|
326
|
+
call: ({ projectId }) => ({ method: 'GET', path: `/projects/${enc(projectId)}/workers` }),
|
|
327
|
+
pick: (data) => data?.data ?? [],
|
|
328
|
+
}),
|
|
329
|
+
tool({
|
|
330
|
+
name: 'process_asset', title: 'Process an asset', permissions: ['jobs:run'], write: true,
|
|
331
|
+
description: "Asks a worker to process an asset (or one of its versions), e.g. optimize a glTF. The result is kept as a new asset derived from it (resultMode 'derived', the default: the original is untouched) or as the asset's new version ('version': its URL then serves the result). It runs in the background: follow it with get_job or list_jobs; it shows in the file's Jobs tab, in Storage.",
|
|
332
|
+
input: {
|
|
333
|
+
projectId,
|
|
334
|
+
assetId: id('asset'),
|
|
335
|
+
versionId: zod_1.z.string().optional().describe('A version (list_asset_versions); the latest if not given'),
|
|
336
|
+
worker: zod_1.z.object({ source: zod_1.z.enum(['builtin', 'project']), id: zod_1.z.string().min(1) }).describe('A worker, from list_workers'),
|
|
337
|
+
kind: zod_1.z.string().min(1).describe("What it should do: one of the worker's kinds"),
|
|
338
|
+
profile: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional().describe("Options for the worker (e.g. { \"textureFormat\": \"ktx2\" })"),
|
|
339
|
+
resultMode: zod_1.z.enum(['derived', 'version']).optional(),
|
|
340
|
+
},
|
|
341
|
+
projectOf: byProject,
|
|
342
|
+
call: ({ projectId, ...fields }) => ({ method: 'POST', path: `/projects/${enc(projectId)}/jobs/process`, body: defined(fields) }),
|
|
343
|
+
pick: (data) => data?.job,
|
|
344
|
+
}),
|
|
345
|
+
tool({
|
|
346
|
+
name: 'list_jobs', title: 'List jobs', permissions: ['jobs:read'],
|
|
347
|
+
description: "A project's jobs, newest first (of one asset if given): their status, stage, progress, error and, when done, their worker's report and result.",
|
|
348
|
+
input: { projectId, assetId: zod_1.z.string().optional() },
|
|
349
|
+
projectOf: byProject,
|
|
350
|
+
call: ({ projectId, assetId }) => ({ method: 'GET', path: `/projects/${enc(projectId)}/jobs${assetId ? `?${new URLSearchParams({ assetId })}` : ''}` }),
|
|
351
|
+
pick: (data) => data?.data ?? [],
|
|
352
|
+
}),
|
|
353
|
+
tool({
|
|
354
|
+
name: 'retry_job', title: 'Retry a job', permissions: ['jobs:run'], write: true,
|
|
355
|
+
description: "Sends a failed worker job to its worker again.",
|
|
356
|
+
input: { projectId, jobId: id('job') },
|
|
357
|
+
projectOf: byProject,
|
|
358
|
+
call: ({ projectId, jobId }) => ({ method: 'POST', path: `/projects/${enc(projectId)}/jobs/${enc(jobId)}/retry` }),
|
|
359
|
+
pick: (data) => data?.job,
|
|
360
|
+
}),
|
|
320
361
|
// ── Projects ──
|
|
321
362
|
tool({
|
|
322
363
|
name: 'create_project', title: 'Create a project', permissions: ['projects:create'], write: true,
|
|
@@ -15,6 +15,7 @@ const auth_1 = require("../services/auth");
|
|
|
15
15
|
const apiToken_1 = require("./routes/apiToken");
|
|
16
16
|
const mcp_1 = require("./routes/mcp");
|
|
17
17
|
const encryption_1 = require("../utils/encryption");
|
|
18
|
+
const jobStore_1 = require("../services/jobStore");
|
|
18
19
|
const auth_2 = require("./routes/auth");
|
|
19
20
|
const user_1 = require("./routes/user");
|
|
20
21
|
const export_1 = require("./routes/export");
|
|
@@ -38,11 +39,15 @@ const production_1 = require("./routes/production");
|
|
|
38
39
|
const rights_1 = require("./routes/rights");
|
|
39
40
|
const workflow_1 = require("./routes/workflow");
|
|
40
41
|
const pipeline_1 = require("./routes/pipeline");
|
|
42
|
+
const worker_1 = require("./routes/worker");
|
|
43
|
+
const workerCallback_1 = require("./routes/workerCallback");
|
|
44
|
+
const workerJobs_1 = require("../services/workerJobs");
|
|
41
45
|
const deliverable_1 = require("./routes/deliverable");
|
|
42
46
|
function createStreamByRouter(config) {
|
|
43
47
|
const router = express_1.default.Router();
|
|
44
48
|
if (config.encrypt)
|
|
45
49
|
(0, encryption_1.setEncryptionKey)(config.encrypt, config.encryptPreviousKeys);
|
|
50
|
+
(0, jobStore_1.setJobStoreDatabase)(config);
|
|
46
51
|
// Establish DB connections asynchronously (Postgres/Mongo + schema migrations).
|
|
47
52
|
// Until they're ready, HOLD incoming requests instead of letting them through —
|
|
48
53
|
// route handlers resolve projects/exports (e.g. the /menu-list export) against a
|
|
@@ -69,6 +74,8 @@ function createStreamByRouter(config) {
|
|
|
69
74
|
})
|
|
70
75
|
.finally(() => {
|
|
71
76
|
ready = true;
|
|
77
|
+
// Jobs left pending (or with their worker's time up) by a restart are sent again.
|
|
78
|
+
(0, workerJobs_1.startWorkerSweeper)({ ...config, adapter });
|
|
72
79
|
});
|
|
73
80
|
router.use((_req, _res, next) => {
|
|
74
81
|
if (ready)
|
|
@@ -108,6 +115,8 @@ function createStreamByRouter(config) {
|
|
|
108
115
|
// /auth answers whether there's a session (it reads it itself); everything after needs one,
|
|
109
116
|
// but for the public export route (see authenticate).
|
|
110
117
|
router.use((0, auth_2.authRouter)(config));
|
|
118
|
+
// Workers call back with their job's token, not a session (see routes/workerCallback).
|
|
119
|
+
router.use((0, workerCallback_1.workerCallbackRouter)({ ...config, adapter }));
|
|
111
120
|
router.use((0, auth_1.authenticate)(config));
|
|
112
121
|
router.use(auth_1.enforceApiTokenScope);
|
|
113
122
|
router.use(subscription_1.ensureSubscription);
|
|
@@ -135,6 +144,7 @@ function createStreamByRouter(config) {
|
|
|
135
144
|
router.use((0, rights_1.rightsRouter)(config));
|
|
136
145
|
router.use((0, workflow_1.workflowRouter)(config));
|
|
137
146
|
router.use((0, pipeline_1.pipelineRouter)(config));
|
|
138
|
-
router.use((0,
|
|
147
|
+
router.use((0, worker_1.workerRouter)({ ...config, adapter }));
|
|
148
|
+
router.use((0, deliverable_1.deliverableRouter)({ ...config, adapter }));
|
|
139
149
|
return router;
|
|
140
150
|
}
|
|
@@ -31,7 +31,7 @@ function aiRouter(_config) {
|
|
|
31
31
|
return res.status(404).json({ message: 'Project not found' });
|
|
32
32
|
if (!(0, auth_1.canEditProject)(project, auth.userId))
|
|
33
33
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
34
|
-
const job = (0, jobQueue_1.createJob)('transcription', auth.userId, projectId, { fileId, model, sourceLanguage, outputFormats, provider, credentialId });
|
|
34
|
+
const job = await (0, jobQueue_1.createJob)('transcription', auth.userId, projectId, { fileId, model, sourceLanguage, outputFormats, provider, credentialId });
|
|
35
35
|
setImmediate(() => (0, aiProcessor_1.runTranscriptionJob)(job.jobId, fileId, projectId, { model, sourceLanguage, outputFormats, provider, credentialId }));
|
|
36
36
|
res.status(202).json({ jobId: job.jobId, status: job.status });
|
|
37
37
|
}
|
|
@@ -52,7 +52,7 @@ function aiRouter(_config) {
|
|
|
52
52
|
return res.status(404).json({ message: 'Project not found' });
|
|
53
53
|
if (!(0, auth_1.canEditProject)(project, auth.userId))
|
|
54
54
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
55
|
-
const job = (0, jobQueue_1.createJob)('upscale', auth.userId, projectId, { fileId, scale, model, mode, credentialId });
|
|
55
|
+
const job = await (0, jobQueue_1.createJob)('upscale', auth.userId, projectId, { fileId, scale, model, mode, credentialId });
|
|
56
56
|
setImmediate(() => (0, aiProcessor_1.runUpscaleJob)(job.jobId, fileId, projectId, { scale, model, mode, credentialId }));
|
|
57
57
|
res.status(202).json({ jobId: job.jobId, status: job.status });
|
|
58
58
|
}
|
|
@@ -73,7 +73,7 @@ function aiRouter(_config) {
|
|
|
73
73
|
return res.status(404).json({ message: 'Project not found' });
|
|
74
74
|
if (!(0, auth_1.canEditProject)(project, auth.userId))
|
|
75
75
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
76
|
-
const job = (0, jobQueue_1.createJob)('generate-asset', auth.userId, projectId, { assetType, provider, prompt, seed, credentialId });
|
|
76
|
+
const job = await (0, jobQueue_1.createJob)('generate-asset', auth.userId, projectId, { assetType, provider, prompt, seed, credentialId });
|
|
77
77
|
setImmediate(() => (0, aiProcessor_1.runGenerateAssetJob)(job.jobId, projectId, { assetType, provider, prompt, seed, credentialId }));
|
|
78
78
|
res.status(202).json({ jobId: job.jobId, status: job.status });
|
|
79
79
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { StreamByConfig } from '../../types';
|
|
3
|
-
export declare function deliverableRouter(
|
|
2
|
+
import { StorageAdapter, StreamByConfig } from '../../types';
|
|
3
|
+
export declare function deliverableRouter(config: StreamByConfig & {
|
|
4
|
+
adapter?: StorageAdapter;
|
|
5
|
+
}): Router;
|
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deliverableRouter = deliverableRouter;
|
|
4
4
|
const express_1 = require("express");
|
|
5
|
+
const workerJobs_1 = require("../../services/workerJobs");
|
|
6
|
+
const worker_1 = require("./worker");
|
|
7
|
+
const auth_1 = require("../../services/auth");
|
|
5
8
|
const manager_1 = require("../../models/manager");
|
|
6
|
-
const
|
|
9
|
+
const auth_2 = require("../../utils/auth");
|
|
7
10
|
const nosqlDb_1 = require("../../utils/nosqlDb");
|
|
8
11
|
const assetVersions_1 = require("../../services/assetVersions");
|
|
9
12
|
const deliverables_1 = require("../../services/deliverables");
|
|
10
13
|
const MAX_NOTE = 1000;
|
|
11
14
|
// A project's deliverables (see services/deliverables): marked, listed and removed. Removing
|
|
12
|
-
// one never deletes its asset.
|
|
13
|
-
|
|
15
|
+
// one never deletes its asset. Marking one starts the workers set to process its kind of asset
|
|
16
|
+
// by themselves.
|
|
17
|
+
function deliverableRouter(config) {
|
|
14
18
|
const router = (0, express_1.Router)();
|
|
15
19
|
const Project = (0, manager_1.getModel)('projects');
|
|
16
20
|
const exportsOf = (project) => project.exports ?? [];
|
|
@@ -48,7 +52,7 @@ function deliverableRouter(_config) {
|
|
|
48
52
|
const project = await Project.findOne({ _id: projectId });
|
|
49
53
|
if (!project)
|
|
50
54
|
return res.status(404).json({ message: 'Project not found' });
|
|
51
|
-
if (!(0,
|
|
55
|
+
if (!(0, auth_2.isProjectMember)(project, auth.userId))
|
|
52
56
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
53
57
|
const deliverables = await (0, deliverables_1.listDeliverables)(projectId, { shotId, exportId });
|
|
54
58
|
res.json({ deliverables: await describe(project, deliverables) });
|
|
@@ -77,7 +81,7 @@ function deliverableRouter(_config) {
|
|
|
77
81
|
const project = await Project.findOne({ _id: projectId });
|
|
78
82
|
if (!project)
|
|
79
83
|
return res.status(404).json({ message: 'Project not found' });
|
|
80
|
-
if (!(0,
|
|
84
|
+
if (!(0, auth_2.canEditProject)(project, auth.userId))
|
|
81
85
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
82
86
|
const db = (0, nosqlDb_1.nosqlDb)();
|
|
83
87
|
if (!db)
|
|
@@ -104,7 +108,12 @@ function deliverableRouter(_config) {
|
|
|
104
108
|
createdBy: auth.userId,
|
|
105
109
|
});
|
|
106
110
|
const [described] = await describe(project, [deliverable]);
|
|
107
|
-
|
|
111
|
+
// The project's workers set to process its kind of asset by themselves start on it: not
|
|
112
|
+
// for an API token that can't run jobs.
|
|
113
|
+
const jobs = (0, auth_1.tokenAllows)(auth.apiToken, 'jobs:run')
|
|
114
|
+
? await (0, workerJobs_1.enqueueAutomaticJobs)(project, assetId, versionId || null, auth, config, (0, worker_1.callbackBaseOf)(req, config)).catch(() => [])
|
|
115
|
+
: [];
|
|
116
|
+
res.status(201).json({ deliverable: described, jobs: jobs.map(workerJobs_1.publicJob) });
|
|
108
117
|
}
|
|
109
118
|
catch (err) {
|
|
110
119
|
res.status(500).json({ message: 'Failed to mark the deliverable', details: err.message });
|
|
@@ -117,7 +126,7 @@ function deliverableRouter(_config) {
|
|
|
117
126
|
const project = await Project.findOne({ _id: projectId });
|
|
118
127
|
if (!project)
|
|
119
128
|
return res.status(404).json({ message: 'Project not found' });
|
|
120
|
-
if (!(0,
|
|
129
|
+
if (!(0, auth_2.canEditProject)(project, auth.userId))
|
|
121
130
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
122
131
|
if (!(await (0, deliverables_1.deleteDeliverable)(projectId, deliverableId)))
|
|
123
132
|
return res.status(404).json({ message: 'Deliverable not found' });
|
|
@@ -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 workerJobs_1 = require("../../services/workerJobs");
|
|
11
12
|
const auth_2 = require("../../services/auth");
|
|
12
13
|
function getMetadataCollection() {
|
|
13
14
|
const ids = (0, connectionManager_1.getConnectedIds)().filter(id => (0, connectionManager_1.getConnection)(id).type === 'nosql');
|
|
@@ -31,7 +32,7 @@ function mediaRouter(config) {
|
|
|
31
32
|
router.get('/jobs/:jobId', async (req, res) => {
|
|
32
33
|
try {
|
|
33
34
|
const auth = req.auth;
|
|
34
|
-
const job = (0, jobQueue_1.getJob)(req.params.jobId);
|
|
35
|
+
const job = await (0, jobQueue_1.getJob)(req.params.jobId);
|
|
35
36
|
if (!job)
|
|
36
37
|
return res.status(404).json({ message: 'Job not found' });
|
|
37
38
|
// With an API token, only a job of a project it reaches.
|
|
@@ -42,7 +43,7 @@ function mediaRouter(config) {
|
|
|
42
43
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
43
44
|
return res.status(404).json({ message: 'Job not found' });
|
|
44
45
|
}
|
|
45
|
-
res.json({ job });
|
|
46
|
+
res.json({ job: (0, workerJobs_1.publicJob)(job) });
|
|
46
47
|
}
|
|
47
48
|
catch (err) {
|
|
48
49
|
res.status(500).json({ message: 'Failed to fetch job', details: err.message });
|
|
@@ -61,7 +62,7 @@ function mediaRouter(config) {
|
|
|
61
62
|
return res.status(404).json({ message: 'Project not found' });
|
|
62
63
|
if (!(0, auth_1.canEditProject)(project, auth.userId))
|
|
63
64
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
64
|
-
const job = (0, jobQueue_1.createJob)('ingest', auth.userId, projectId, { fileId });
|
|
65
|
+
const job = await (0, jobQueue_1.createJob)('ingest', auth.userId, projectId, { fileId });
|
|
65
66
|
// Run async — response returns immediately with jobId
|
|
66
67
|
setImmediate(() => (0, mediaProcessor_1.runIngestJob)(job.jobId, fileId, projectId));
|
|
67
68
|
res.status(202).json({ jobId: job.jobId, status: job.status });
|
|
@@ -83,7 +84,7 @@ function mediaRouter(config) {
|
|
|
83
84
|
return res.status(404).json({ message: 'Project not found' });
|
|
84
85
|
if (!(0, auth_1.canEditProject)(project, auth.userId))
|
|
85
86
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
86
|
-
const job = (0, jobQueue_1.createJob)('transcode', auth.userId, projectId, {
|
|
87
|
+
const job = await (0, jobQueue_1.createJob)('transcode', auth.userId, projectId, {
|
|
87
88
|
fileId, codec, resolution, outputFormat, bitrate, audioCodec,
|
|
88
89
|
});
|
|
89
90
|
setImmediate(() => (0, mediaProcessor_1.runTranscodeJob)(job.jobId, fileId, projectId, { codec, resolution, outputFormat, bitrate, audioCodec }, adapter));
|
|
@@ -106,7 +107,7 @@ function mediaRouter(config) {
|
|
|
106
107
|
return res.status(404).json({ message: 'Project not found' });
|
|
107
108
|
if (!(0, auth_1.canEditProject)(project, auth.userId))
|
|
108
109
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
109
|
-
const job = (0, jobQueue_1.createJob)('thumbnail', auth.userId, projectId, { fileId, timecode, resolution, strategy });
|
|
110
|
+
const job = await (0, jobQueue_1.createJob)('thumbnail', auth.userId, projectId, { fileId, timecode, resolution, strategy });
|
|
110
111
|
setImmediate(() => (0, mediaProcessor_1.runThumbnailJob)(job.jobId, fileId, projectId, { timecode, resolution, strategy }));
|
|
111
112
|
res.status(202).json({ jobId: job.jobId, status: job.status });
|
|
112
113
|
}
|
|
@@ -127,7 +128,7 @@ function mediaRouter(config) {
|
|
|
127
128
|
return res.status(404).json({ message: 'Project not found' });
|
|
128
129
|
if (!(0, auth_1.canEditProject)(project, auth.userId))
|
|
129
130
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
130
|
-
const job = (0, jobQueue_1.createJob)('caption', auth.userId, projectId, { fileId, sourceLanguage, outputFormat, provider });
|
|
131
|
+
const job = await (0, jobQueue_1.createJob)('caption', auth.userId, projectId, { fileId, sourceLanguage, outputFormat, provider });
|
|
131
132
|
setImmediate(() => (0, mediaProcessor_1.runCaptionJob)(job.jobId, fileId, projectId, { sourceLanguage, outputFormat, provider }));
|
|
132
133
|
res.status(202).json({ jobId: job.jobId, status: job.status });
|
|
133
134
|
}
|
|
@@ -12,6 +12,7 @@ const auth_2 = require("../../services/auth");
|
|
|
12
12
|
const apiTokens_1 = require("../../services/apiTokens");
|
|
13
13
|
const nosqlDb_1 = require("../../utils/nosqlDb");
|
|
14
14
|
const production_1 = require("../../services/production");
|
|
15
|
+
const jobStore_1 = require("../../services/jobStore");
|
|
15
16
|
function projectRouter(config) {
|
|
16
17
|
const router = (0, express_1.Router)();
|
|
17
18
|
const Project = (0, manager_1.getModel)('projects');
|
|
@@ -254,9 +255,12 @@ function projectRouter(config) {
|
|
|
254
255
|
return res.status(403).json({ message: 'Only a project admin can delete it' });
|
|
255
256
|
}
|
|
256
257
|
await Project.useDbType(project.dbType).delete({ _id: projectId });
|
|
257
|
-
// Its tokens have nothing left to reach, and its production data
|
|
258
|
+
// Its tokens have nothing left to reach, and its production data and jobs nothing to
|
|
259
|
+
// belong to.
|
|
258
260
|
await (0, apiTokens_1.deleteProjectApiTokens)(String(projectId));
|
|
259
261
|
await (0, production_1.deleteProductionData)(String(projectId));
|
|
262
|
+
if (projectId)
|
|
263
|
+
await (0, jobStore_1.jobStore)().deleteProject(String(projectId));
|
|
260
264
|
const allProjects = await Project.find({});
|
|
261
265
|
const projects = allProjects
|
|
262
266
|
.filter(project => {
|