@ultrst-armada/kloks 1.2.1 → 1.4.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/package.json +44 -1
- package/packages/backend/dist/chunk-BM4QMVPA.js +338 -0
- package/packages/backend/dist/chunk-DZ4ZGIZM.js +1323 -0
- package/packages/backend/dist/{chunk-MN54ZIKZ.js → chunk-KX3OTBQ3.js} +309 -4
- package/packages/backend/dist/index.d.ts +145 -4
- package/packages/backend/dist/index.js +447 -1398
- package/packages/backend/dist/krnl-services.js +2 -2
- package/packages/backend/dist/services/index.d.ts +168 -1
- package/packages/backend/dist/services/index.js +15 -3
- package/packages/backend/dist/srkl/index.d.ts +108 -1
- package/packages/backend/dist/srkl/index.js +15 -1
- package/packages/backend/dist/workspace/index.d.ts +119 -1
- package/packages/backend/dist/workspace/index.js +11 -3
- package/packages/client/dist/index.d.ts +154 -0
- package/packages/client/dist/index.js +62 -0
- package/packages/client/package.json +41 -0
- package/packages/state-graph/dist/index.js +14 -14
- package/packages/backend/dist/chunk-KTLD7Z4L.js +0 -124
- package/packages/backend/dist/chunk-P4HL4C6V.js +0 -149
- package/packages/backend/dist/{chunk-OYQKQAEF.js → chunk-CU7RZBMF.js} +4 -4
|
@@ -1,12 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CompositeBackend,
|
|
3
|
+
EndpointGenerator,
|
|
4
|
+
LOCAL_STORAGE_GRAPHS_PREFIX,
|
|
5
|
+
NodeGraphPersistenceService,
|
|
6
|
+
createBackend,
|
|
7
|
+
createCompositeService,
|
|
8
|
+
createLocalService,
|
|
9
|
+
createRemoteService,
|
|
10
|
+
exportGraphAsJsonFile,
|
|
11
|
+
nodeGraphPersistence,
|
|
12
|
+
parseGraphJsonFile,
|
|
13
|
+
validateGraphDocument
|
|
14
|
+
} from "./chunk-BM4QMVPA.js";
|
|
1
15
|
import {
|
|
2
16
|
CANONICAL_SRKL_URL,
|
|
3
17
|
SrklClient,
|
|
18
|
+
SrklCredentialRegistry,
|
|
19
|
+
SrklVaultAccessClient,
|
|
4
20
|
createSrklClient,
|
|
21
|
+
decryptVaultBufferInBrowser,
|
|
5
22
|
getPrimarySrklDeploymentUrl,
|
|
6
23
|
getSrklAccounts,
|
|
24
|
+
globalCredentialRegistry,
|
|
25
|
+
hexToBytes,
|
|
7
26
|
srklListBackblazeBuckets,
|
|
27
|
+
srklService,
|
|
28
|
+
syncSrklAccounts,
|
|
8
29
|
useSrklServices
|
|
9
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-KX3OTBQ3.js";
|
|
10
31
|
import "./chunk-LS3HWJBX.js";
|
|
11
32
|
import {
|
|
12
33
|
authorizeB2,
|
|
@@ -16,16 +37,48 @@ import {
|
|
|
16
37
|
b2UploadFile
|
|
17
38
|
} from "./chunk-4QEMALXF.js";
|
|
18
39
|
import {
|
|
19
|
-
|
|
20
|
-
createSSEResponse,
|
|
21
|
-
withAuth,
|
|
22
|
-
withCors
|
|
23
|
-
} from "./chunk-TWCEVES2.js";
|
|
24
|
-
import {
|
|
40
|
+
RemoteWorkspaceService,
|
|
25
41
|
SyncEngine,
|
|
42
|
+
WorkingWorkspaceService,
|
|
26
43
|
WorkspaceCloneSyncEngine,
|
|
27
|
-
WorkspaceFS
|
|
28
|
-
|
|
44
|
+
WorkspaceFS,
|
|
45
|
+
b2DeleteFileVersion,
|
|
46
|
+
b2DeleteFolder,
|
|
47
|
+
b2ListBuckets as b2ListBuckets2,
|
|
48
|
+
cleanDbUrl,
|
|
49
|
+
createWorkspace,
|
|
50
|
+
deleteWorkspace,
|
|
51
|
+
deleteWorkspaceFile,
|
|
52
|
+
deleteWorkspaceFolder,
|
|
53
|
+
findUserWorkspace,
|
|
54
|
+
findWorkspaceRecord,
|
|
55
|
+
getB2BucketId,
|
|
56
|
+
getDefaultWorkingWorkspaceDir,
|
|
57
|
+
getUnifiedDbUrl,
|
|
58
|
+
getWorkingWorkspaceConfig,
|
|
59
|
+
getWorkingWorkspaceDir,
|
|
60
|
+
getWorkspaceFolderKey,
|
|
61
|
+
getWorkspaceFolderPrefix,
|
|
62
|
+
getWorkspaceStorageDetails,
|
|
63
|
+
getWorkspaces,
|
|
64
|
+
isB2Workspace,
|
|
65
|
+
listWorkspaceFiles,
|
|
66
|
+
loadWorkspaceData,
|
|
67
|
+
queryUnifiedDb,
|
|
68
|
+
readWorkspaceFile,
|
|
69
|
+
remoteWorkspaceService,
|
|
70
|
+
renameAppInWorkspaceData,
|
|
71
|
+
resolveWorkspaceConnection,
|
|
72
|
+
saveWorkspaceData,
|
|
73
|
+
setWorkingWorkspaceConfig,
|
|
74
|
+
syncWorkspace,
|
|
75
|
+
syncWorkspaceCreateToBucket,
|
|
76
|
+
syncWorkspaceDataToBucket,
|
|
77
|
+
syncWorkspaceDeleteToBucket,
|
|
78
|
+
updateWorkspace,
|
|
79
|
+
workingWorkspaceService,
|
|
80
|
+
writeWorkspaceFile
|
|
81
|
+
} from "./chunk-DZ4ZGIZM.js";
|
|
29
82
|
import {
|
|
30
83
|
AuthServiceDefinition,
|
|
31
84
|
AuthServiceFacade,
|
|
@@ -41,7 +94,8 @@ import {
|
|
|
41
94
|
StorageServiceFacade,
|
|
42
95
|
WorkspaceServiceDefinition,
|
|
43
96
|
WorkspaceServiceFacade
|
|
44
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-CU7RZBMF.js";
|
|
98
|
+
import "./chunk-IPP4VURQ.js";
|
|
45
99
|
import {
|
|
46
100
|
BackblazeB2Adapter,
|
|
47
101
|
BaseStorageAdapter,
|
|
@@ -61,6 +115,17 @@ import {
|
|
|
61
115
|
formatCanonicalWorkspaceFolderName,
|
|
62
116
|
sanitizeWorkspaceName
|
|
63
117
|
} from "./chunk-FZKBTF34.js";
|
|
118
|
+
import {
|
|
119
|
+
BaseApiRoute,
|
|
120
|
+
createSSEResponse,
|
|
121
|
+
withAuth,
|
|
122
|
+
withCors
|
|
123
|
+
} from "./chunk-TWCEVES2.js";
|
|
124
|
+
import {
|
|
125
|
+
BackendProvider,
|
|
126
|
+
useBackend,
|
|
127
|
+
useBackendClient
|
|
128
|
+
} from "./chunk-G3ZOSXUR.js";
|
|
64
129
|
import {
|
|
65
130
|
AdminGuard,
|
|
66
131
|
BaseAccountProvider,
|
|
@@ -74,10 +139,13 @@ import {
|
|
|
74
139
|
SessionManager
|
|
75
140
|
} from "./chunk-25Y7AVTP.js";
|
|
76
141
|
import {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
142
|
+
AuditLogger,
|
|
143
|
+
ControlPlaneManager,
|
|
144
|
+
DatabaseClusterManager,
|
|
145
|
+
detectDatabaseEngine,
|
|
146
|
+
getSystemDatabaseUrls,
|
|
147
|
+
resolveSystemDatabaseClusterNodes
|
|
148
|
+
} from "./chunk-RCCQ7IRM.js";
|
|
81
149
|
import {
|
|
82
150
|
BackendRegistry,
|
|
83
151
|
ConcurrencyConflictError,
|
|
@@ -100,14 +168,6 @@ import {
|
|
|
100
168
|
WebSocketTransport,
|
|
101
169
|
createBackendClient
|
|
102
170
|
} from "./chunk-NIP22V2G.js";
|
|
103
|
-
import {
|
|
104
|
-
AuditLogger,
|
|
105
|
-
ControlPlaneManager,
|
|
106
|
-
DatabaseClusterManager,
|
|
107
|
-
detectDatabaseEngine,
|
|
108
|
-
getSystemDatabaseUrls,
|
|
109
|
-
resolveSystemDatabaseClusterNodes
|
|
110
|
-
} from "./chunk-RCCQ7IRM.js";
|
|
111
171
|
import {
|
|
112
172
|
JobDependencyGraph,
|
|
113
173
|
ResultFlowBridge,
|
|
@@ -124,15 +184,6 @@ import {
|
|
|
124
184
|
import {
|
|
125
185
|
IdempotencyManager
|
|
126
186
|
} from "./chunk-3EPY34R6.js";
|
|
127
|
-
import {
|
|
128
|
-
CompositeBackend,
|
|
129
|
-
EndpointGenerator,
|
|
130
|
-
createBackend,
|
|
131
|
-
createCompositeService,
|
|
132
|
-
createLocalService,
|
|
133
|
-
createRemoteService
|
|
134
|
-
} from "./chunk-P4HL4C6V.js";
|
|
135
|
-
import "./chunk-IPP4VURQ.js";
|
|
136
187
|
|
|
137
188
|
// src/data/BaseDataProvider.ts
|
|
138
189
|
import { SkipoBase } from "@ultrst-armada/kloks/functions";
|
|
@@ -205,40 +256,6 @@ function validateServiceDefinition(def) {
|
|
|
205
256
|
// src/core/authCore.ts
|
|
206
257
|
import crypto2 from "crypto";
|
|
207
258
|
import bcrypt from "bcryptjs";
|
|
208
|
-
|
|
209
|
-
// src/core/unifiedDb.ts
|
|
210
|
-
import { neon } from "@neondatabase/serverless";
|
|
211
|
-
function cleanDbUrl(url) {
|
|
212
|
-
if (!url || typeof url !== "string") return "";
|
|
213
|
-
return url.trim().replace(/^["']|["']$/g, "");
|
|
214
|
-
}
|
|
215
|
-
function getUnifiedDbUrl(env) {
|
|
216
|
-
const fromEnv = cleanDbUrl(env?.SYSTEM_DATABASE_URL_1) || cleanDbUrl(env?.DATABASE_URL) || cleanDbUrl(env?.SYSTEM_DATABASE_URL_2);
|
|
217
|
-
if (fromEnv) return fromEnv;
|
|
218
|
-
if (typeof process !== "undefined" && process.env) {
|
|
219
|
-
return cleanDbUrl(process.env.SYSTEM_DATABASE_URL_1) || cleanDbUrl(process.env.DATABASE_URL) || cleanDbUrl(process.env.SYSTEM_DATABASE_URL_2) || cleanDbUrl(process.env.SYSTEM_DATABASE_URL_3) || "";
|
|
220
|
-
}
|
|
221
|
-
return "";
|
|
222
|
-
}
|
|
223
|
-
async function queryUnifiedDb(sqlText, params = [], env) {
|
|
224
|
-
const dbUrl = getUnifiedDbUrl(env);
|
|
225
|
-
if (!dbUrl) {
|
|
226
|
-
throw new Error("[UnifiedDB] No database URL configured in environment.");
|
|
227
|
-
}
|
|
228
|
-
const sql = neon(dbUrl);
|
|
229
|
-
try {
|
|
230
|
-
const rows = await (sql.query ? sql.query(sqlText, params) : sql(sqlText, params));
|
|
231
|
-
return {
|
|
232
|
-
rows: Array.isArray(rows) ? rows : [],
|
|
233
|
-
rowCount: Array.isArray(rows) ? rows.length : 0
|
|
234
|
-
};
|
|
235
|
-
} catch (err) {
|
|
236
|
-
console.error("[UnifiedDB] Query execution error:", err.message);
|
|
237
|
-
throw err;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/core/authCore.ts
|
|
242
259
|
async function loginUser(email, password, isAdmin = false, env) {
|
|
243
260
|
const normEmail = (email || "").trim().toLowerCase();
|
|
244
261
|
const rawPassword = (password || "").trim();
|
|
@@ -314,473 +331,11 @@ async function getUsers(env) {
|
|
|
314
331
|
}
|
|
315
332
|
}
|
|
316
333
|
|
|
317
|
-
// src/core/b2DirectCore.ts
|
|
318
|
-
var authCache = /* @__PURE__ */ new Map();
|
|
319
|
-
var bucketIdCache = /* @__PURE__ */ new Map();
|
|
320
|
-
async function authorizeB22(keyId, applicationKey) {
|
|
321
|
-
const cacheKey = `${keyId.trim()}:${applicationKey.trim()}`;
|
|
322
|
-
const cached = authCache.get(cacheKey);
|
|
323
|
-
if (cached && Date.now() < cached.expiresAt) return cached.auth;
|
|
324
|
-
const authHeader = typeof Buffer !== "undefined" ? Buffer.from(`${keyId.trim()}:${applicationKey.trim()}`).toString("base64") : btoa(`${keyId.trim()}:${applicationKey.trim()}`);
|
|
325
|
-
let res = await fetch("https://api.backblazeb2.com/b2api/v2/b2_authorize_account", {
|
|
326
|
-
headers: { Authorization: `Basic ${authHeader}` },
|
|
327
|
-
signal: AbortSignal.timeout(1e4)
|
|
328
|
-
});
|
|
329
|
-
if (!res.ok) {
|
|
330
|
-
res = await fetch("https://api.backblazeb2.com/b2api/v4/b2_authorize_account", {
|
|
331
|
-
headers: { Authorization: `Basic ${authHeader}` },
|
|
332
|
-
signal: AbortSignal.timeout(1e4)
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
if (!res.ok) {
|
|
336
|
-
const errText = await res.text();
|
|
337
|
-
throw new Error(`Backblaze B2 authorization failed (${res.status}): ${errText}`);
|
|
338
|
-
}
|
|
339
|
-
const data = await res.json();
|
|
340
|
-
const auth = {
|
|
341
|
-
accountId: data.accountId,
|
|
342
|
-
authorizationToken: data.authorizationToken,
|
|
343
|
-
apiUrl: (data.apiInfo?.storageApi?.apiUrl || data.apiUrl || "https://api.backblazeb2.com").replace(/\/+$/, ""),
|
|
344
|
-
downloadUrl: (data.apiInfo?.storageApi?.downloadUrl || data.downloadUrl || data.apiUrl || "https://f003.backblazeb2.com").replace(/\/+$/, ""),
|
|
345
|
-
s3ApiUrl: data.apiInfo?.storageApi?.s3ApiUrl || data.s3ApiUrl,
|
|
346
|
-
allowed: data.apiInfo?.storageApi?.allowed || data.allowed
|
|
347
|
-
};
|
|
348
|
-
authCache.set(cacheKey, { auth, expiresAt: Date.now() + 20 * 60 * 1e3 });
|
|
349
|
-
return auth;
|
|
350
|
-
}
|
|
351
|
-
async function b2ListBuckets2(keyId, applicationKey, bucketId, bucketName) {
|
|
352
|
-
const auth = await authorizeB22(keyId, applicationKey);
|
|
353
|
-
const rBucketId = bucketId || auth.allowed?.bucketId || auth.allowed?.buckets?.[0]?.bucketId;
|
|
354
|
-
const rBucketName = bucketName || auth.allowed?.bucketName || auth.allowed?.buckets?.[0]?.bucketName;
|
|
355
|
-
if (!auth.allowed?.bucketId && !auth.allowed?.bucketName) {
|
|
356
|
-
try {
|
|
357
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_buckets`, {
|
|
358
|
-
method: "POST",
|
|
359
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
360
|
-
body: JSON.stringify({ accountId: auth.accountId }),
|
|
361
|
-
signal: AbortSignal.timeout(8e3)
|
|
362
|
-
});
|
|
363
|
-
if (res.ok) {
|
|
364
|
-
const data = await res.json();
|
|
365
|
-
if (Array.isArray(data.buckets) && data.buckets.length > 0) return data.buckets;
|
|
366
|
-
}
|
|
367
|
-
} catch (err) {
|
|
368
|
-
console.warn("[B2DirectCore] list_buckets query failed:", err.message);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (rBucketId || rBucketName) {
|
|
372
|
-
const body = { accountId: auth.accountId };
|
|
373
|
-
if (rBucketId) body.bucketId = rBucketId;
|
|
374
|
-
else if (rBucketName) body.bucketName = rBucketName;
|
|
375
|
-
try {
|
|
376
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_buckets`, {
|
|
377
|
-
method: "POST",
|
|
378
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
379
|
-
body: JSON.stringify(body),
|
|
380
|
-
signal: AbortSignal.timeout(8e3)
|
|
381
|
-
});
|
|
382
|
-
if (res.ok) {
|
|
383
|
-
const data = await res.json();
|
|
384
|
-
if (Array.isArray(data.buckets) && data.buckets.length > 0) return data.buckets;
|
|
385
|
-
}
|
|
386
|
-
} catch {
|
|
387
|
-
}
|
|
388
|
-
return [{
|
|
389
|
-
accountId: auth.accountId,
|
|
390
|
-
bucketId: rBucketId || "restricted",
|
|
391
|
-
bucketName: rBucketName || (bucketName || "default-bucket"),
|
|
392
|
-
bucketType: "allPrivate"
|
|
393
|
-
}];
|
|
394
|
-
}
|
|
395
|
-
return [];
|
|
396
|
-
}
|
|
397
|
-
async function getB2BucketId(keyId, applicationKey, bucketName) {
|
|
398
|
-
const cacheKey = `${keyId.trim()}:${bucketName.trim()}`;
|
|
399
|
-
if (bucketIdCache.has(cacheKey)) return bucketIdCache.get(cacheKey);
|
|
400
|
-
const buckets = await b2ListBuckets2(keyId, applicationKey, void 0, bucketName);
|
|
401
|
-
const match = buckets.find((b) => b.bucketName === bucketName);
|
|
402
|
-
if (!match) throw new Error(`B2 bucket "${bucketName}" not found in account`);
|
|
403
|
-
bucketIdCache.set(cacheKey, match.bucketId);
|
|
404
|
-
return match.bucketId;
|
|
405
|
-
}
|
|
406
|
-
async function b2UploadFile2(keyId, applicationKey, bucketName, fileName, buffer, contentType = "b2/x-auto") {
|
|
407
|
-
const auth = await authorizeB22(keyId, applicationKey);
|
|
408
|
-
const bucketId = await getB2BucketId(keyId, applicationKey, bucketName);
|
|
409
|
-
const urlRes = await fetch(`${auth.apiUrl}/b2api/v2/b2_get_upload_url`, {
|
|
410
|
-
method: "POST",
|
|
411
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
412
|
-
body: JSON.stringify({ bucketId })
|
|
413
|
-
});
|
|
414
|
-
if (!urlRes.ok) throw new Error(`B2 get upload url failed (${urlRes.status})`);
|
|
415
|
-
const { uploadUrl, authorizationToken } = await urlRes.json();
|
|
416
|
-
const encodedFileName = encodeURIComponent(fileName).replace(/%2F/g, "/");
|
|
417
|
-
const uploadRes = await fetch(uploadUrl, {
|
|
418
|
-
method: "POST",
|
|
419
|
-
headers: {
|
|
420
|
-
Authorization: authorizationToken,
|
|
421
|
-
"X-Bz-File-Name": encodedFileName,
|
|
422
|
-
"Content-Type": contentType,
|
|
423
|
-
"Content-Length": String(buffer.byteLength),
|
|
424
|
-
"X-Bz-Content-Sha1": "do_not_verify"
|
|
425
|
-
},
|
|
426
|
-
body: buffer
|
|
427
|
-
});
|
|
428
|
-
if (!uploadRes.ok) throw new Error(`B2 upload failed for ${fileName} (${uploadRes.status}): ${await uploadRes.text()}`);
|
|
429
|
-
return uploadRes.json();
|
|
430
|
-
}
|
|
431
|
-
async function b2DownloadFile2(keyId, applicationKey, bucketName, fileName) {
|
|
432
|
-
const auth = await authorizeB22(keyId, applicationKey);
|
|
433
|
-
const encodedName = encodeURIComponent(fileName).replace(/%2F/g, "/");
|
|
434
|
-
const res = await fetch(`${auth.downloadUrl}/file/${bucketName}/${encodedName}`, { headers: { Authorization: auth.authorizationToken } });
|
|
435
|
-
if (res.status === 404) return null;
|
|
436
|
-
if (!res.ok) throw new Error(`B2 download failed for ${fileName} (${res.status})`);
|
|
437
|
-
return new Uint8Array(await res.arrayBuffer());
|
|
438
|
-
}
|
|
439
|
-
async function b2ListFileNames2(keyId, applicationKey, bucketName, prefix = "") {
|
|
440
|
-
const auth = await authorizeB22(keyId, applicationKey);
|
|
441
|
-
const bucketId = await getB2BucketId(keyId, applicationKey, bucketName);
|
|
442
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_file_names`, {
|
|
443
|
-
method: "POST",
|
|
444
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
445
|
-
body: JSON.stringify({ bucketId, prefix, maxFileCount: 1e3 })
|
|
446
|
-
});
|
|
447
|
-
if (!res.ok) throw new Error(`B2 list files failed (${res.status})`);
|
|
448
|
-
const data = await res.json();
|
|
449
|
-
return data.files || [];
|
|
450
|
-
}
|
|
451
|
-
async function b2DeleteFileVersion(keyId, applicationKey, fileName, fileId) {
|
|
452
|
-
const auth = await authorizeB22(keyId, applicationKey);
|
|
453
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_delete_file_version`, {
|
|
454
|
-
method: "POST",
|
|
455
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
456
|
-
body: JSON.stringify({ fileName, fileId })
|
|
457
|
-
});
|
|
458
|
-
if (!res.ok) throw new Error(`B2 delete file version failed (${res.status})`);
|
|
459
|
-
return res.json();
|
|
460
|
-
}
|
|
461
|
-
async function b2DeleteFolder(keyId, applicationKey, bucketName, folderPrefix) {
|
|
462
|
-
const cleanPrefix = folderPrefix.replace(/\/+$/, "") + "/";
|
|
463
|
-
const files = await b2ListFileNames2(keyId, applicationKey, bucketName, cleanPrefix);
|
|
464
|
-
let deletedCount = 0;
|
|
465
|
-
for (const file of files) {
|
|
466
|
-
await b2DeleteFileVersion(keyId, applicationKey, file.fileName, file.fileId);
|
|
467
|
-
deletedCount++;
|
|
468
|
-
}
|
|
469
|
-
return { success: true, deletedCount };
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// src/core/workspaceSyncEngine.ts
|
|
473
|
-
import fs from "fs";
|
|
474
|
-
import path from "path";
|
|
475
|
-
|
|
476
|
-
// src/core/workspaceCanonical.ts
|
|
477
|
-
function formatCanonicalWorkspaceFolderName2(appName, userName, workspaceName) {
|
|
478
|
-
const safeApp = (appName || "qwnsr").replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
|
|
479
|
-
const safeUser = (userName || "user").replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
|
|
480
|
-
const safeWs = (workspaceName || "workspace").replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
|
|
481
|
-
return `ultrst_${safeApp}_${safeUser}_${safeWs}`;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// src/core/workspaceSyncEngine.ts
|
|
485
|
-
var ACCOUNTS_FILE = path.resolve(".skipo/accounts.json");
|
|
486
|
-
var CONFIG_FILE = path.resolve(".skipo/workspace_config.json");
|
|
487
|
-
function getDefaultWorkingWorkspaceDir() {
|
|
488
|
-
if (process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME) {
|
|
489
|
-
return path.join("/tmp", "workspace");
|
|
490
|
-
}
|
|
491
|
-
return path.join(process.cwd(), "workspace");
|
|
492
|
-
}
|
|
493
|
-
function getWorkingWorkspaceConfig() {
|
|
494
|
-
try {
|
|
495
|
-
if (fs.existsSync(CONFIG_FILE)) {
|
|
496
|
-
const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
497
|
-
const parsed = JSON.parse(raw);
|
|
498
|
-
if (parsed.workingDirectory) {
|
|
499
|
-
return {
|
|
500
|
-
workingDirectory: parsed.workingDirectory,
|
|
501
|
-
autoSyncIntervalMinutes: parsed.autoSyncIntervalMinutes || 5
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
} catch {
|
|
506
|
-
}
|
|
507
|
-
return {
|
|
508
|
-
workingDirectory: getDefaultWorkingWorkspaceDir(),
|
|
509
|
-
autoSyncIntervalMinutes: 5
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
function setWorkingWorkspaceConfig(cfg) {
|
|
513
|
-
const current = getWorkingWorkspaceConfig();
|
|
514
|
-
const updated = {
|
|
515
|
-
workingDirectory: (cfg.workingDirectory || current.workingDirectory).trim(),
|
|
516
|
-
autoSyncIntervalMinutes: cfg.autoSyncIntervalMinutes || current.autoSyncIntervalMinutes
|
|
517
|
-
};
|
|
518
|
-
try {
|
|
519
|
-
fs.mkdirSync(path.dirname(CONFIG_FILE), { recursive: true });
|
|
520
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(updated, null, 2), "utf-8");
|
|
521
|
-
} catch (err) {
|
|
522
|
-
console.warn("[WorkspaceSync] Failed to persist working config:", err);
|
|
523
|
-
}
|
|
524
|
-
return updated;
|
|
525
|
-
}
|
|
526
|
-
function getWorkingWorkspaceDir() {
|
|
527
|
-
const dir = getWorkingWorkspaceConfig().workingDirectory;
|
|
528
|
-
if (!fs.existsSync(dir)) {
|
|
529
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
530
|
-
}
|
|
531
|
-
return dir;
|
|
532
|
-
}
|
|
533
|
-
async function findWorkspaceRecord(workspaceId) {
|
|
534
|
-
try {
|
|
535
|
-
if (fs.existsSync(ACCOUNTS_FILE)) {
|
|
536
|
-
const raw = fs.readFileSync(ACCOUNTS_FILE, "utf-8");
|
|
537
|
-
const accounts = JSON.parse(raw);
|
|
538
|
-
for (const u of Object.values(accounts)) {
|
|
539
|
-
if (Array.isArray(u.workspaces)) {
|
|
540
|
-
const ws = u.workspaces.find((w) => w.id === workspaceId);
|
|
541
|
-
if (ws) {
|
|
542
|
-
const conns = Array.isArray(u.storage_connections) ? u.storage_connections : [];
|
|
543
|
-
const conn = conns.find((c) => c.id === ws.storageConnectionId) || conns[0] || null;
|
|
544
|
-
return { user: u, workspace: ws, connection: conn };
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
} catch {
|
|
550
|
-
}
|
|
551
|
-
try {
|
|
552
|
-
const res = await queryUnifiedDb(`SELECT id, email, username, app_profiles FROM "ultrst-users"`);
|
|
553
|
-
for (const u of res.rows) {
|
|
554
|
-
const profiles = typeof u.app_profiles === "string" ? JSON.parse(u.app_profiles) : u.app_profiles || {};
|
|
555
|
-
const qwnsr = profiles.qwnsr || {};
|
|
556
|
-
const workspaces = Array.isArray(qwnsr.workspaces) ? qwnsr.workspaces : [];
|
|
557
|
-
const ws = workspaces.find((w) => w.id === workspaceId);
|
|
558
|
-
if (ws) {
|
|
559
|
-
const conns = qwnsr.storage_connections || profiles.srkl?.storage_connections || [];
|
|
560
|
-
const conn = conns.find((c) => c.id === ws.storageConnectionId) || conns[0] || null;
|
|
561
|
-
return { user: u, workspace: ws, connection: conn };
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
} catch {
|
|
565
|
-
}
|
|
566
|
-
return null;
|
|
567
|
-
}
|
|
568
|
-
function getWorkspaceFolderKey(workspace, user) {
|
|
569
|
-
if (workspace.localFolderKey) return workspace.localFolderKey;
|
|
570
|
-
if (workspace.localFolderName && workspace.localFolderName.startsWith("ultrst_")) return workspace.localFolderName;
|
|
571
|
-
const userName = user?.username || user?.name || (user?.email ? user.email.split("@")[0] : "user");
|
|
572
|
-
return formatCanonicalWorkspaceFolderName2("qwnsr", userName, workspace.name);
|
|
573
|
-
}
|
|
574
|
-
async function collectLocalFiles(dir, baseDir = dir) {
|
|
575
|
-
if (!fs.existsSync(dir)) return [];
|
|
576
|
-
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
577
|
-
const results = [];
|
|
578
|
-
for (const entry of entries) {
|
|
579
|
-
if (entry.name.startsWith(".") && entry.name !== ".bzEmpty") continue;
|
|
580
|
-
const full = path.join(dir, entry.name);
|
|
581
|
-
if (entry.isDirectory()) {
|
|
582
|
-
const nested = await collectLocalFiles(full, baseDir);
|
|
583
|
-
results.push(...nested);
|
|
584
|
-
} else if (entry.isFile()) {
|
|
585
|
-
const st = await fs.promises.stat(full);
|
|
586
|
-
const rel = path.relative(baseDir, full).replace(/\\/g, "/");
|
|
587
|
-
results.push({ relPath: rel, fullPath: full, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
return results;
|
|
591
|
-
}
|
|
592
|
-
async function syncWorkspace(workspaceId) {
|
|
593
|
-
const match = await findWorkspaceRecord(workspaceId);
|
|
594
|
-
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
595
|
-
if (!match) {
|
|
596
|
-
return {
|
|
597
|
-
success: false,
|
|
598
|
-
workspaceId,
|
|
599
|
-
folderKey: workspaceId,
|
|
600
|
-
workingDir: getWorkingWorkspaceDir(),
|
|
601
|
-
persistenceType: "local_fs",
|
|
602
|
-
targetLocation: "Unknown",
|
|
603
|
-
syncedFiles: [],
|
|
604
|
-
uploadedCount: 0,
|
|
605
|
-
downloadedCount: 0,
|
|
606
|
-
syncedAt: nowIso,
|
|
607
|
-
error: `Workspace '${workspaceId}' not found.`
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
const { workspace, user, connection } = match;
|
|
611
|
-
const folderKey = getWorkspaceFolderKey(workspace, user);
|
|
612
|
-
const workingDir = getWorkingWorkspaceDir();
|
|
613
|
-
const isB2 = workspace.storageProvider === "backblaze_b2" || workspace.storageConfig?.type === "b2";
|
|
614
|
-
const syncedFiles = [];
|
|
615
|
-
let uploadedCount = 0;
|
|
616
|
-
let downloadedCount = 0;
|
|
617
|
-
if (isB2) {
|
|
618
|
-
const creds = workspace.storageConfig || connection?.credentials || connection || {};
|
|
619
|
-
const keyId = creds.keyId || creds.b2KeyId;
|
|
620
|
-
const appKey = creds.applicationKey || creds.b2ApplicationKey || creds.appKey;
|
|
621
|
-
const bucketName = creds.bucketName || creds.b2BucketName || workspace.b2BucketName || "ultrst-apps";
|
|
622
|
-
if (!keyId || !appKey) {
|
|
623
|
-
return {
|
|
624
|
-
success: false,
|
|
625
|
-
workspaceId,
|
|
626
|
-
folderKey,
|
|
627
|
-
workingDir,
|
|
628
|
-
persistenceType: "backblaze_b2",
|
|
629
|
-
targetLocation: bucketName,
|
|
630
|
-
syncedFiles: [],
|
|
631
|
-
uploadedCount: 0,
|
|
632
|
-
downloadedCount: 0,
|
|
633
|
-
syncedAt: nowIso,
|
|
634
|
-
error: "Missing Backblaze B2 credentials for sync."
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
try {
|
|
638
|
-
const b2Prefix = `${folderKey}/`;
|
|
639
|
-
const remoteFiles = await b2ListFileNames2(keyId, appKey, bucketName, b2Prefix);
|
|
640
|
-
const remoteMap = /* @__PURE__ */ new Map();
|
|
641
|
-
for (const rf of remoteFiles) {
|
|
642
|
-
const cleanName = rf.fileName.slice(b2Prefix.length);
|
|
643
|
-
if (cleanName) remoteMap.set(cleanName, { fileId: rf.fileId, size: rf.contentLength, timestamp: rf.uploadTimestamp });
|
|
644
|
-
}
|
|
645
|
-
const localFiles = await collectLocalFiles(workingDir);
|
|
646
|
-
const localMap = /* @__PURE__ */ new Map();
|
|
647
|
-
for (const lf of localFiles) {
|
|
648
|
-
localMap.set(lf.relPath, lf);
|
|
649
|
-
}
|
|
650
|
-
for (const [relPath, rInfo] of remoteMap.entries()) {
|
|
651
|
-
const lFile = localMap.get(relPath);
|
|
652
|
-
const shouldDownload = !lFile || rInfo.timestamp > lFile.mtimeMs + 2e3;
|
|
653
|
-
if (shouldDownload) {
|
|
654
|
-
const buf = await b2DownloadFile2(keyId, appKey, bucketName, `${b2Prefix}${relPath}`);
|
|
655
|
-
if (buf) {
|
|
656
|
-
const destPath = path.join(workingDir, relPath);
|
|
657
|
-
await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
|
|
658
|
-
await fs.promises.writeFile(destPath, buf);
|
|
659
|
-
try {
|
|
660
|
-
const fileTime = new Date(rInfo.timestamp);
|
|
661
|
-
await fs.promises.utimes(destPath, fileTime, fileTime);
|
|
662
|
-
} catch {
|
|
663
|
-
}
|
|
664
|
-
syncedFiles.push({ fileName: relPath, action: "DOWNLOADED", sizeBytes: buf.byteLength });
|
|
665
|
-
downloadedCount++;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
for (const [relPath, lFile] of localMap.entries()) {
|
|
670
|
-
const rInfo = remoteMap.get(relPath);
|
|
671
|
-
const shouldUpload = !rInfo || lFile.mtimeMs > rInfo.timestamp + 2e3;
|
|
672
|
-
if (shouldUpload) {
|
|
673
|
-
const content = await fs.promises.readFile(lFile.fullPath);
|
|
674
|
-
await b2UploadFile2(keyId, appKey, bucketName, `${b2Prefix}${relPath}`, new Uint8Array(content));
|
|
675
|
-
syncedFiles.push({ fileName: relPath, action: "UPLOADED", sizeBytes: lFile.sizeBytes });
|
|
676
|
-
uploadedCount++;
|
|
677
|
-
} else if (!syncedFiles.some((f) => f.fileName === relPath)) {
|
|
678
|
-
syncedFiles.push({ fileName: relPath, action: "UP_TO_DATE", sizeBytes: lFile.sizeBytes });
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
return {
|
|
682
|
-
success: true,
|
|
683
|
-
workspaceId,
|
|
684
|
-
folderKey,
|
|
685
|
-
workingDir,
|
|
686
|
-
persistenceType: "backblaze_b2",
|
|
687
|
-
targetLocation: `B2://${bucketName}/${folderKey}/`,
|
|
688
|
-
syncedFiles,
|
|
689
|
-
uploadedCount,
|
|
690
|
-
downloadedCount,
|
|
691
|
-
syncedAt: nowIso
|
|
692
|
-
};
|
|
693
|
-
} catch (err) {
|
|
694
|
-
return {
|
|
695
|
-
success: false,
|
|
696
|
-
workspaceId,
|
|
697
|
-
folderKey,
|
|
698
|
-
workingDir,
|
|
699
|
-
persistenceType: "backblaze_b2",
|
|
700
|
-
targetLocation: bucketName,
|
|
701
|
-
syncedFiles,
|
|
702
|
-
uploadedCount,
|
|
703
|
-
downloadedCount,
|
|
704
|
-
syncedAt: nowIso,
|
|
705
|
-
error: err.message || "Backblaze sync failed."
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
const vaultDir = path.resolve(process.cwd(), folderKey);
|
|
710
|
-
await fs.promises.mkdir(vaultDir, { recursive: true });
|
|
711
|
-
try {
|
|
712
|
-
const vaultFiles = await collectLocalFiles(vaultDir);
|
|
713
|
-
const vaultMap = new Map(vaultFiles.map((f) => [f.relPath, f]));
|
|
714
|
-
const workingFiles = await collectLocalFiles(workingDir);
|
|
715
|
-
const workingMap = new Map(workingFiles.map((f) => [f.relPath, f]));
|
|
716
|
-
for (const [relPath, vFile] of vaultMap.entries()) {
|
|
717
|
-
const wFile = workingMap.get(relPath);
|
|
718
|
-
const shouldDownload = !wFile || vFile.mtimeMs > wFile.mtimeMs + 1e3;
|
|
719
|
-
if (shouldDownload) {
|
|
720
|
-
const dest = path.join(workingDir, relPath);
|
|
721
|
-
await fs.promises.mkdir(path.dirname(dest), { recursive: true });
|
|
722
|
-
await fs.promises.copyFile(vFile.fullPath, dest);
|
|
723
|
-
try {
|
|
724
|
-
const vTime = new Date(vFile.mtimeMs);
|
|
725
|
-
await fs.promises.utimes(dest, vTime, vTime);
|
|
726
|
-
} catch {
|
|
727
|
-
}
|
|
728
|
-
syncedFiles.push({ fileName: relPath, action: "DOWNLOADED", sizeBytes: vFile.sizeBytes });
|
|
729
|
-
downloadedCount++;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
for (const [relPath, wFile] of workingMap.entries()) {
|
|
733
|
-
const vFile = vaultMap.get(relPath);
|
|
734
|
-
const shouldUpload = !vFile || wFile.mtimeMs > vFile.mtimeMs + 1e3;
|
|
735
|
-
if (shouldUpload) {
|
|
736
|
-
const dest = path.join(vaultDir, relPath);
|
|
737
|
-
await fs.promises.mkdir(path.dirname(dest), { recursive: true });
|
|
738
|
-
await fs.promises.copyFile(wFile.fullPath, dest);
|
|
739
|
-
try {
|
|
740
|
-
const wTime = new Date(wFile.mtimeMs);
|
|
741
|
-
await fs.promises.utimes(dest, wTime, wTime);
|
|
742
|
-
} catch {
|
|
743
|
-
}
|
|
744
|
-
syncedFiles.push({ fileName: relPath, action: "UPLOADED", sizeBytes: wFile.sizeBytes });
|
|
745
|
-
uploadedCount++;
|
|
746
|
-
} else if (!syncedFiles.some((f) => f.fileName === relPath)) {
|
|
747
|
-
syncedFiles.push({ fileName: relPath, action: "UP_TO_DATE", sizeBytes: wFile.sizeBytes });
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return {
|
|
751
|
-
success: true,
|
|
752
|
-
workspaceId,
|
|
753
|
-
folderKey,
|
|
754
|
-
workingDir,
|
|
755
|
-
persistenceType: "local_fs",
|
|
756
|
-
targetLocation: vaultDir,
|
|
757
|
-
syncedFiles,
|
|
758
|
-
uploadedCount,
|
|
759
|
-
downloadedCount,
|
|
760
|
-
syncedAt: nowIso
|
|
761
|
-
};
|
|
762
|
-
} catch (err) {
|
|
763
|
-
return {
|
|
764
|
-
success: false,
|
|
765
|
-
workspaceId,
|
|
766
|
-
folderKey,
|
|
767
|
-
workingDir,
|
|
768
|
-
persistenceType: "local_fs",
|
|
769
|
-
targetLocation: vaultDir,
|
|
770
|
-
syncedFiles,
|
|
771
|
-
uploadedCount,
|
|
772
|
-
downloadedCount,
|
|
773
|
-
syncedAt: nowIso,
|
|
774
|
-
error: err.message
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
334
|
// src/core/SrklStorageService.ts
|
|
780
335
|
import { S3Client, ListBucketsCommand, HeadBucketCommand } from "@aws-sdk/client-s3";
|
|
781
|
-
import { promises as
|
|
782
|
-
import
|
|
783
|
-
var
|
|
336
|
+
import { promises as fs } from "fs";
|
|
337
|
+
import path from "path";
|
|
338
|
+
var ACCOUNTS_FILE = path.resolve(".skipo/accounts.json");
|
|
784
339
|
var SrklStorageService = class {
|
|
785
340
|
srklBaseUrl;
|
|
786
341
|
constructor(srklBaseUrl) {
|
|
@@ -893,10 +448,10 @@ var SrklStorageService = class {
|
|
|
893
448
|
}
|
|
894
449
|
async persistWorkspaceConfig(wsId, config) {
|
|
895
450
|
try {
|
|
896
|
-
await
|
|
451
|
+
await fs.mkdir(path.dirname(ACCOUNTS_FILE), { recursive: true });
|
|
897
452
|
let accounts = {};
|
|
898
453
|
try {
|
|
899
|
-
accounts = JSON.parse(await
|
|
454
|
+
accounts = JSON.parse(await fs.readFile(ACCOUNTS_FILE, "utf-8"));
|
|
900
455
|
} catch {
|
|
901
456
|
accounts = {};
|
|
902
457
|
}
|
|
@@ -907,7 +462,7 @@ var SrklStorageService = class {
|
|
|
907
462
|
ws.storageConfig = config;
|
|
908
463
|
ws.storageProvider = "backblaze_b2";
|
|
909
464
|
ws.updatedAt = Date.now();
|
|
910
|
-
await
|
|
465
|
+
await fs.writeFile(ACCOUNTS_FILE, JSON.stringify(accounts, null, 2), "utf-8");
|
|
911
466
|
return;
|
|
912
467
|
}
|
|
913
468
|
}
|
|
@@ -932,7 +487,7 @@ var SrklStorageService = class {
|
|
|
932
487
|
});
|
|
933
488
|
}
|
|
934
489
|
try {
|
|
935
|
-
const raw = await
|
|
490
|
+
const raw = await fs.readFile(ACCOUNTS_FILE, "utf-8");
|
|
936
491
|
const accounts = JSON.parse(raw);
|
|
937
492
|
for (const user of Object.values(accounts)) {
|
|
938
493
|
const conns = Array.isArray(user?.storage_connections) ? user.storage_connections : [];
|
|
@@ -1061,10 +616,10 @@ async function getSrklAccounts2(userId, service, env) {
|
|
|
1061
616
|
}
|
|
1062
617
|
}
|
|
1063
618
|
try {
|
|
1064
|
-
const
|
|
1065
|
-
const
|
|
1066
|
-
const accPath =
|
|
1067
|
-
const raw = await
|
|
619
|
+
const fs9 = await import("fs/promises");
|
|
620
|
+
const path9 = await import("path");
|
|
621
|
+
const accPath = path9.resolve(".skipo/accounts.json");
|
|
622
|
+
const raw = await fs9.readFile(accPath, "utf-8");
|
|
1068
623
|
const localUsers = JSON.parse(raw);
|
|
1069
624
|
for (const u of Object.values(localUsers)) {
|
|
1070
625
|
const conns = Array.isArray(u.storage_connections) ? u.storage_connections : [];
|
|
@@ -1171,155 +726,6 @@ async function getSrklStatus(env) {
|
|
|
1171
726
|
}
|
|
1172
727
|
}
|
|
1173
728
|
|
|
1174
|
-
// src/core/workspaceFileCore.ts
|
|
1175
|
-
async function getFs() {
|
|
1176
|
-
if (typeof process !== "undefined" && process.versions?.node) {
|
|
1177
|
-
try {
|
|
1178
|
-
const mod = await import("fs");
|
|
1179
|
-
return mod.promises || mod.default?.promises || mod;
|
|
1180
|
-
} catch {
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
return null;
|
|
1184
|
-
}
|
|
1185
|
-
async function getPath() {
|
|
1186
|
-
if (typeof process !== "undefined" && process.versions?.node) {
|
|
1187
|
-
try {
|
|
1188
|
-
const mod = await import("path");
|
|
1189
|
-
return mod.default || mod;
|
|
1190
|
-
} catch {
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
return null;
|
|
1194
|
-
}
|
|
1195
|
-
function getWorkspaceFolderPrefix(workspace) {
|
|
1196
|
-
return workspace.localFolderKey || workspace.localFolderName || (workspace.workspacePath ? workspace.workspacePath.replace(/^\.\//, "") : workspace.id);
|
|
1197
|
-
}
|
|
1198
|
-
function isB2Workspace(workspace, connection) {
|
|
1199
|
-
return connection?.provider === "backblaze_b2" || connection?.provider === "BACKBLAZE_B2" || workspace?.storageProvider === "backblaze_b2";
|
|
1200
|
-
}
|
|
1201
|
-
async function writeWorkspaceFile(workspace, connection, relativePath, content, contentType = "application/json") {
|
|
1202
|
-
const cleanRel = relativePath.replace(/^\/+/, "");
|
|
1203
|
-
const prefix = getWorkspaceFolderPrefix(workspace);
|
|
1204
|
-
const buffer = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
1205
|
-
if (isB2Workspace(workspace, connection)) {
|
|
1206
|
-
const keyId = connection.b2KeyId || connection.keyId;
|
|
1207
|
-
const appKey = connection.b2ApplicationKey || connection.appKey;
|
|
1208
|
-
const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
|
|
1209
|
-
const fullKey = `${prefix}/${cleanRel}`;
|
|
1210
|
-
await b2UploadFile2(keyId, appKey, bucketName, fullKey, buffer, contentType);
|
|
1211
|
-
return;
|
|
1212
|
-
}
|
|
1213
|
-
const fs10 = await getFs();
|
|
1214
|
-
const path10 = await getPath();
|
|
1215
|
-
if (!fs10 || !path10) throw new Error("Local filesystem operations not supported in this runtime.");
|
|
1216
|
-
const localDir = path10.resolve(process.cwd(), prefix, path10.dirname(cleanRel));
|
|
1217
|
-
await fs10.mkdir(localDir, { recursive: true });
|
|
1218
|
-
await fs10.writeFile(path10.resolve(process.cwd(), prefix, cleanRel), buffer);
|
|
1219
|
-
}
|
|
1220
|
-
async function readWorkspaceFile(workspace, connection, relativePath) {
|
|
1221
|
-
const cleanRel = relativePath.replace(/^\/+/, "");
|
|
1222
|
-
const prefix = getWorkspaceFolderPrefix(workspace);
|
|
1223
|
-
if (isB2Workspace(workspace, connection)) {
|
|
1224
|
-
const keyId = connection.b2KeyId || connection.keyId;
|
|
1225
|
-
const appKey = connection.b2ApplicationKey || connection.appKey;
|
|
1226
|
-
const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
|
|
1227
|
-
const fullKey = `${prefix}/${cleanRel}`;
|
|
1228
|
-
return await b2DownloadFile2(keyId, appKey, bucketName, fullKey);
|
|
1229
|
-
}
|
|
1230
|
-
const fs10 = await getFs();
|
|
1231
|
-
const path10 = await getPath();
|
|
1232
|
-
if (!fs10 || !path10) return null;
|
|
1233
|
-
const fullPath = path10.resolve(process.cwd(), prefix, cleanRel);
|
|
1234
|
-
try {
|
|
1235
|
-
const buf = await fs10.readFile(fullPath);
|
|
1236
|
-
return new Uint8Array(buf);
|
|
1237
|
-
} catch {
|
|
1238
|
-
return null;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
async function deleteWorkspaceFile(workspace, connection, relativePath) {
|
|
1242
|
-
const cleanRel = relativePath.replace(/^\/+/, "");
|
|
1243
|
-
const prefix = getWorkspaceFolderPrefix(workspace);
|
|
1244
|
-
if (isB2Workspace(workspace, connection)) {
|
|
1245
|
-
const keyId = connection.b2KeyId || connection.keyId;
|
|
1246
|
-
const appKey = connection.b2ApplicationKey || connection.appKey;
|
|
1247
|
-
const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
|
|
1248
|
-
const fullKey = `${prefix}/${cleanRel}`;
|
|
1249
|
-
const files = await b2ListFileNames2(keyId, appKey, bucketName, fullKey);
|
|
1250
|
-
const match = files.find((f) => f.fileName === fullKey);
|
|
1251
|
-
if (match) {
|
|
1252
|
-
await b2DeleteFileVersion(keyId, appKey, match.fileName, match.fileId);
|
|
1253
|
-
return true;
|
|
1254
|
-
}
|
|
1255
|
-
return false;
|
|
1256
|
-
}
|
|
1257
|
-
const fs10 = await getFs();
|
|
1258
|
-
const path10 = await getPath();
|
|
1259
|
-
if (!fs10 || !path10) return false;
|
|
1260
|
-
const fullPath = path10.resolve(process.cwd(), prefix, cleanRel);
|
|
1261
|
-
try {
|
|
1262
|
-
await fs10.unlink(fullPath);
|
|
1263
|
-
return true;
|
|
1264
|
-
} catch {
|
|
1265
|
-
return false;
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
async function listWorkspaceFiles(workspace, connection, subPrefix = "") {
|
|
1269
|
-
const folderPrefix = getWorkspaceFolderPrefix(workspace);
|
|
1270
|
-
const targetPrefix = subPrefix ? `${folderPrefix}/${subPrefix.replace(/^\/+/, "")}` : `${folderPrefix}/`;
|
|
1271
|
-
if (isB2Workspace(workspace, connection)) {
|
|
1272
|
-
const keyId = connection.b2KeyId || connection.keyId;
|
|
1273
|
-
const appKey = connection.b2ApplicationKey || connection.appKey;
|
|
1274
|
-
const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
|
|
1275
|
-
const rawFiles = await b2ListFileNames2(keyId, appKey, bucketName, targetPrefix);
|
|
1276
|
-
return rawFiles.map((f) => ({
|
|
1277
|
-
fileName: f.fileName.slice(folderPrefix.length + 1),
|
|
1278
|
-
sizeBytes: f.contentLength,
|
|
1279
|
-
uploadTimestamp: f.uploadTimestamp
|
|
1280
|
-
}));
|
|
1281
|
-
}
|
|
1282
|
-
const fs10 = await getFs();
|
|
1283
|
-
const path10 = await getPath();
|
|
1284
|
-
if (!fs10 || !path10) return [];
|
|
1285
|
-
const baseDir = path10.resolve(process.cwd(), folderPrefix, subPrefix);
|
|
1286
|
-
try {
|
|
1287
|
-
const entries = await fs10.readdir(baseDir, { withFileTypes: true, recursive: true });
|
|
1288
|
-
const result = [];
|
|
1289
|
-
for (const entry of entries) {
|
|
1290
|
-
if (entry.isFile()) {
|
|
1291
|
-
const full = path10.join(entry.parentPath || baseDir, entry.name);
|
|
1292
|
-
const st = await fs10.stat(full);
|
|
1293
|
-
result.push({
|
|
1294
|
-
fileName: path10.relative(path10.resolve(process.cwd(), folderPrefix), full),
|
|
1295
|
-
sizeBytes: st.size
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
return result;
|
|
1300
|
-
} catch {
|
|
1301
|
-
return [];
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
async function deleteWorkspaceFolder(workspace, connection) {
|
|
1305
|
-
const prefix = getWorkspaceFolderPrefix(workspace);
|
|
1306
|
-
if (isB2Workspace(workspace, connection)) {
|
|
1307
|
-
const keyId = connection.b2KeyId || connection.keyId;
|
|
1308
|
-
const appKey = connection.b2ApplicationKey || connection.appKey;
|
|
1309
|
-
const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
|
|
1310
|
-
await b2DeleteFolder(keyId, appKey, bucketName, prefix);
|
|
1311
|
-
return;
|
|
1312
|
-
}
|
|
1313
|
-
const fs10 = await getFs();
|
|
1314
|
-
const path10 = await getPath();
|
|
1315
|
-
if (!fs10 || !path10) return;
|
|
1316
|
-
const localDir = path10.resolve(process.cwd(), prefix);
|
|
1317
|
-
try {
|
|
1318
|
-
await fs10.rm(localDir, { recursive: true, force: true });
|
|
1319
|
-
} catch {
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
729
|
// src/core/credentialVaultCore.ts
|
|
1324
730
|
var MANIFEST_PATH = "credentials/manifest.json";
|
|
1325
731
|
var SECRET_PREFIX = "credentials/secrets";
|
|
@@ -2684,385 +2090,6 @@ async function matchGatewayIngressAsync(urlPath, search, env) {
|
|
|
2684
2090
|
return null;
|
|
2685
2091
|
}
|
|
2686
2092
|
|
|
2687
|
-
// src/core/workspaceBucketSyncCore.ts
|
|
2688
|
-
function resolveWorkspaceConnection(profiles, workspace) {
|
|
2689
|
-
const depm = profiles.depm || {};
|
|
2690
|
-
const conns = depm.storage_connections?.length ? depm.storage_connections : profiles.srkl?.storage_connections || profiles.docustack?.storage_connections || [];
|
|
2691
|
-
return conns.find((c) => c.id === workspace.storageConnectionId) || conns.find((c) => c.role === "primary") || conns[0] || null;
|
|
2692
|
-
}
|
|
2693
|
-
async function syncWorkspaceCreateToBucket(workspace, connection, env) {
|
|
2694
|
-
if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") return;
|
|
2695
|
-
try {
|
|
2696
|
-
const wsMeta = {
|
|
2697
|
-
id: workspace.id,
|
|
2698
|
-
name: workspace.name,
|
|
2699
|
-
createdAt: workspace.createdAt,
|
|
2700
|
-
updatedAt: workspace.updatedAt,
|
|
2701
|
-
storageProvider: "backblaze_b2",
|
|
2702
|
-
bucket: connection.b2BucketName || "ultrst-apps"
|
|
2703
|
-
};
|
|
2704
|
-
await writeWorkspaceFile(workspace, connection, ".bzEmpty", new Uint8Array(0), "application/x-empty");
|
|
2705
|
-
await writeWorkspaceFile(workspace, connection, "workspace.json", JSON.stringify(wsMeta, null, 2), "application/json");
|
|
2706
|
-
await writeWorkspaceFile(
|
|
2707
|
-
workspace,
|
|
2708
|
-
connection,
|
|
2709
|
-
"ecosystem.json",
|
|
2710
|
-
JSON.stringify({ apps: [], nodes: [], edges: [], ecosystems: [] }, null, 2),
|
|
2711
|
-
"application/json"
|
|
2712
|
-
);
|
|
2713
|
-
} catch (err) {
|
|
2714
|
-
console.warn("[WorkspaceBucketSync] Create sync error:", err.message);
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
async function syncWorkspaceDeleteToBucket(workspace, connection, env) {
|
|
2718
|
-
if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") return;
|
|
2719
|
-
try {
|
|
2720
|
-
await deleteWorkspaceFolder(workspace, connection);
|
|
2721
|
-
} catch (err) {
|
|
2722
|
-
console.warn("[WorkspaceBucketSync] Delete sync error:", err.message);
|
|
2723
|
-
}
|
|
2724
|
-
}
|
|
2725
|
-
async function syncWorkspaceDataToBucket(workspace, connection, data, env) {
|
|
2726
|
-
if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") return;
|
|
2727
|
-
try {
|
|
2728
|
-
await writeWorkspaceFile(workspace, connection, "ecosystem.json", JSON.stringify(data, null, 2), "application/json");
|
|
2729
|
-
} catch (err) {
|
|
2730
|
-
console.warn("[WorkspaceBucketSync] Data sync error:", err.message);
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
|
|
2734
|
-
// src/core/workspaceDataCore.ts
|
|
2735
|
-
async function resolveUserRow(userId, env) {
|
|
2736
|
-
if (userId) {
|
|
2737
|
-
const res = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1 OR email = $1 LIMIT 1`, [userId], env);
|
|
2738
|
-
if (res.rows.length > 0) return res.rows[0];
|
|
2739
|
-
}
|
|
2740
|
-
return null;
|
|
2741
|
-
}
|
|
2742
|
-
async function loadWorkspaceData(userId, workspaceId, env) {
|
|
2743
|
-
if (!workspaceId) return { success: false, error: "workspaceId required", workspace: null };
|
|
2744
|
-
const user = await resolveUserRow(userId, env);
|
|
2745
|
-
if (!user) return { success: false, error: "User not found", workspace: null };
|
|
2746
|
-
const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
|
|
2747
|
-
const depm = profiles.depm || {};
|
|
2748
|
-
const workspaceDataMap = depm.workspace_data || {};
|
|
2749
|
-
let data = workspaceDataMap[workspaceId];
|
|
2750
|
-
const isEmptyData = !data || (!data.nodes || data.nodes.length === 0) && (!data.apps || data.apps.length === 0);
|
|
2751
|
-
if (isEmptyData) {
|
|
2752
|
-
const ws = (depm.workspaces || []).find((w) => w.id === workspaceId);
|
|
2753
|
-
if (ws) {
|
|
2754
|
-
const conn = resolveWorkspaceConnection(profiles, ws);
|
|
2755
|
-
if (conn) {
|
|
2756
|
-
try {
|
|
2757
|
-
const fileBuf = await readWorkspaceFile(ws, conn, "ecosystem.json");
|
|
2758
|
-
if (fileBuf && fileBuf.length > 0) {
|
|
2759
|
-
const parsed = JSON.parse(new TextDecoder().decode(fileBuf));
|
|
2760
|
-
if (parsed.nodes && parsed.nodes.length > 0 || parsed.apps && parsed.apps.length > 0) {
|
|
2761
|
-
if (!profiles.depm) profiles.depm = {};
|
|
2762
|
-
if (!profiles.depm.workspace_data) profiles.depm.workspace_data = {};
|
|
2763
|
-
profiles.depm.workspace_data[workspaceId] = parsed;
|
|
2764
|
-
data = parsed;
|
|
2765
|
-
await queryUnifiedDb(
|
|
2766
|
-
`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`,
|
|
2767
|
-
[JSON.stringify(profiles), user.id],
|
|
2768
|
-
env
|
|
2769
|
-
);
|
|
2770
|
-
return { success: true, workspace: data, workspaceId };
|
|
2771
|
-
}
|
|
2772
|
-
}
|
|
2773
|
-
} catch (err) {
|
|
2774
|
-
console.warn("[WorkspaceDataCore] Storage recovery check:", err.message);
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
}
|
|
2779
|
-
if (!data) {
|
|
2780
|
-
const ws = (depm.workspaces || []).find((w) => w.id === workspaceId);
|
|
2781
|
-
const ecoName = ws?.ecosystemName || `${ws?.name || "Workspace"} Ecosystem`;
|
|
2782
|
-
const fallback = {
|
|
2783
|
-
apps: [],
|
|
2784
|
-
accounts: [],
|
|
2785
|
-
deployments: [],
|
|
2786
|
-
nodes: [],
|
|
2787
|
-
edges: [],
|
|
2788
|
-
ecosystems: [
|
|
2789
|
-
{
|
|
2790
|
-
id: `eco_${workspaceId}`,
|
|
2791
|
-
name: ecoName,
|
|
2792
|
-
icon: "Globe",
|
|
2793
|
-
health: "HEALTHY",
|
|
2794
|
-
status: "SYSTEM ONLINE",
|
|
2795
|
-
connectedAppsCount: 0,
|
|
2796
|
-
meshLinkCount: 0,
|
|
2797
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2798
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2799
|
-
}
|
|
2800
|
-
],
|
|
2801
|
-
ecosystemName: ecoName,
|
|
2802
|
-
ecosystemNodes: [
|
|
2803
|
-
{
|
|
2804
|
-
id: `node_eco_root_${workspaceId}`,
|
|
2805
|
-
type: "ECOSYSTEM",
|
|
2806
|
-
name: ecoName,
|
|
2807
|
-
x: 0,
|
|
2808
|
-
y: 0,
|
|
2809
|
-
appId: "__ecosystem__",
|
|
2810
|
-
icon: "Globe",
|
|
2811
|
-
ecosystemData: { connectedAppsCount: 0, health: "HEALTHY", status: "SYSTEM ONLINE" }
|
|
2812
|
-
}
|
|
2813
|
-
],
|
|
2814
|
-
ecosystemEdges: []
|
|
2815
|
-
};
|
|
2816
|
-
return { success: true, workspace: fallback, workspaceId };
|
|
2817
|
-
}
|
|
2818
|
-
return { success: true, workspace: data, workspaceId };
|
|
2819
|
-
}
|
|
2820
|
-
async function saveWorkspaceData(userId, workspaceId, data, env) {
|
|
2821
|
-
if (!workspaceId) return { success: false, error: "workspaceId required" };
|
|
2822
|
-
const user = await resolveUserRow(userId, env);
|
|
2823
|
-
if (!user) return { success: false, error: "User not found" };
|
|
2824
|
-
const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
|
|
2825
|
-
if (!profiles.depm) profiles.depm = {};
|
|
2826
|
-
if (!profiles.depm.workspace_data) profiles.depm.workspace_data = {};
|
|
2827
|
-
const existing = profiles.depm.workspace_data[workspaceId];
|
|
2828
|
-
const incomingEmpty = (!data.nodes || data.nodes.length === 0) && (!data.apps || data.apps.length === 0);
|
|
2829
|
-
const existingHasData = existing && (existing.nodes && existing.nodes.length > 0 || existing.apps && existing.apps.length > 0);
|
|
2830
|
-
if (incomingEmpty && existingHasData && !data._forceEmptySave) {
|
|
2831
|
-
console.warn(`[WorkspaceDataCore] Blocked empty overwrite for workspace ${workspaceId}.`);
|
|
2832
|
-
return { success: true, workspaceId, workspace: existing, protected: true };
|
|
2833
|
-
}
|
|
2834
|
-
profiles.depm.workspace_data[workspaceId] = data;
|
|
2835
|
-
if (Array.isArray(profiles.depm.workspaces)) {
|
|
2836
|
-
const ws2 = profiles.depm.workspaces.find((w) => w.id === workspaceId);
|
|
2837
|
-
if (ws2) {
|
|
2838
|
-
ws2.appsCount = Array.isArray(data.apps) ? data.apps.length : 0;
|
|
2839
|
-
ws2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
await queryUnifiedDb(
|
|
2843
|
-
`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`,
|
|
2844
|
-
[JSON.stringify(profiles), user.id],
|
|
2845
|
-
env
|
|
2846
|
-
);
|
|
2847
|
-
const ws = Array.isArray(profiles.depm?.workspaces) ? profiles.depm.workspaces.find((w) => w.id === workspaceId) : null;
|
|
2848
|
-
if (ws) {
|
|
2849
|
-
const conn = resolveWorkspaceConnection(profiles, ws);
|
|
2850
|
-
await syncWorkspaceDataToBucket(ws, conn, data, env);
|
|
2851
|
-
}
|
|
2852
|
-
return { success: true, workspaceId, workspace: data };
|
|
2853
|
-
}
|
|
2854
|
-
async function renameAppInWorkspaceData(userId, workspaceId, appId, newName, env) {
|
|
2855
|
-
if (!workspaceId || !appId || !newName) {
|
|
2856
|
-
return { success: false, error: "workspaceId, appId and newName required" };
|
|
2857
|
-
}
|
|
2858
|
-
const loadRes = await loadWorkspaceData(userId, workspaceId, env);
|
|
2859
|
-
if (!loadRes.success || !loadRes.workspace) return loadRes;
|
|
2860
|
-
const wsData = loadRes.workspace;
|
|
2861
|
-
const trimmed = newName.trim();
|
|
2862
|
-
const folderName = trimmed.replace(/[/\\?%*:|"<>]/g, "_");
|
|
2863
|
-
if (Array.isArray(wsData.apps)) {
|
|
2864
|
-
const targetApp = wsData.apps.find((a) => a.id === appId);
|
|
2865
|
-
if (targetApp) {
|
|
2866
|
-
targetApp.name = trimmed;
|
|
2867
|
-
targetApp.folderName = folderName;
|
|
2868
|
-
targetApp.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
if (Array.isArray(wsData.nodes)) {
|
|
2872
|
-
wsData.nodes = wsData.nodes.map((n) => {
|
|
2873
|
-
if (n.appId === appId && (n.type === "APP" || n.type === "APP_NODE" || n.type === "APP_GRAPH_NODE")) {
|
|
2874
|
-
return { ...n, name: trimmed, appData: n.appData ? { ...n.appData, name: trimmed, folderName } : void 0 };
|
|
2875
|
-
}
|
|
2876
|
-
return n;
|
|
2877
|
-
});
|
|
2878
|
-
}
|
|
2879
|
-
return await saveWorkspaceData(userId, workspaceId, wsData, env);
|
|
2880
|
-
}
|
|
2881
|
-
|
|
2882
|
-
// src/core/workspaceStorageDetailsCore.ts
|
|
2883
|
-
async function getWorkspaceStorageDetails(workspaceId, env) {
|
|
2884
|
-
if (!workspaceId) return { success: false, error: "workspaceId is required" };
|
|
2885
|
-
const { workspace, connection } = await findUserWorkspace(workspaceId, env);
|
|
2886
|
-
const folderPrefix = workspace?.localFolderKey || workspace?.localFolderName || (workspace?.workspacePath ? workspace.workspacePath.replace(/^\.\//, "") : workspaceId);
|
|
2887
|
-
if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") {
|
|
2888
|
-
return {
|
|
2889
|
-
success: true,
|
|
2890
|
-
workspaceId,
|
|
2891
|
-
storageProvider: workspace?.storageProvider || "local_fs",
|
|
2892
|
-
connection: connection || null,
|
|
2893
|
-
folderPrefix,
|
|
2894
|
-
localStats: { exists: false, appsCount: 0, ecosystemsCount: 0 },
|
|
2895
|
-
bucket: null,
|
|
2896
|
-
files: []
|
|
2897
|
-
};
|
|
2898
|
-
}
|
|
2899
|
-
const keyId = connection.b2KeyId || connection.keyId;
|
|
2900
|
-
const applicationKey = connection.b2ApplicationKey || connection.appKey;
|
|
2901
|
-
const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
|
|
2902
|
-
try {
|
|
2903
|
-
const buckets = await b2ListBuckets2(keyId, applicationKey, void 0, bucketName);
|
|
2904
|
-
const bucketObj = buckets.find((b) => b.bucketName === bucketName);
|
|
2905
|
-
if (!bucketObj) {
|
|
2906
|
-
return {
|
|
2907
|
-
success: true,
|
|
2908
|
-
workspaceId,
|
|
2909
|
-
storageProvider: "backblaze_b2",
|
|
2910
|
-
connection: { id: connection.id, label: connection.label, provider: connection.provider },
|
|
2911
|
-
folderPrefix,
|
|
2912
|
-
bucket: { name: bucketName, status: "BUCKET_NOT_FOUND" },
|
|
2913
|
-
files: []
|
|
2914
|
-
};
|
|
2915
|
-
}
|
|
2916
|
-
const rawFiles = await b2ListFileNames2(keyId, applicationKey, bucketName, `${folderPrefix}/`);
|
|
2917
|
-
return {
|
|
2918
|
-
success: true,
|
|
2919
|
-
workspaceId,
|
|
2920
|
-
storageProvider: "backblaze_b2",
|
|
2921
|
-
connection: { id: connection.id, label: connection.label, provider: connection.provider },
|
|
2922
|
-
folderPrefix,
|
|
2923
|
-
bucket: {
|
|
2924
|
-
id: bucketObj.bucketId,
|
|
2925
|
-
name: bucketObj.bucketName,
|
|
2926
|
-
type: bucketObj.bucketType,
|
|
2927
|
-
endpoint: connection.b2Endpoint || "s3.eu-central-003.backblazeb2.com",
|
|
2928
|
-
region: connection.b2Region || "eu-central-003",
|
|
2929
|
-
status: "CONNECTED"
|
|
2930
|
-
},
|
|
2931
|
-
fileCount: rawFiles.length,
|
|
2932
|
-
files: rawFiles.map((f) => ({
|
|
2933
|
-
fileName: f.fileName,
|
|
2934
|
-
sizeBytes: f.contentLength,
|
|
2935
|
-
uploadTimestamp: f.uploadTimestamp
|
|
2936
|
-
}))
|
|
2937
|
-
};
|
|
2938
|
-
} catch (err) {
|
|
2939
|
-
return {
|
|
2940
|
-
success: false,
|
|
2941
|
-
workspaceId,
|
|
2942
|
-
storageProvider: "backblaze_b2",
|
|
2943
|
-
error: err.message,
|
|
2944
|
-
folderPrefix
|
|
2945
|
-
};
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
|
|
2949
|
-
// src/core/workspaceCore.ts
|
|
2950
|
-
async function findUserWorkspace(workspaceId, env) {
|
|
2951
|
-
const res = await queryUnifiedDb(`SELECT id, email, username, app_profiles FROM "ultrst-users"`, [], env);
|
|
2952
|
-
for (const user of res.rows) {
|
|
2953
|
-
const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
|
|
2954
|
-
const depm = profiles.depm || {};
|
|
2955
|
-
const workspaces = Array.isArray(depm.workspaces) ? depm.workspaces : [];
|
|
2956
|
-
const ws = workspaces.find((w) => w.id === workspaceId);
|
|
2957
|
-
if (ws) {
|
|
2958
|
-
const conns = depm.storage_connections || profiles.srkl?.storage_connections || profiles.docustack?.storage_connections || [];
|
|
2959
|
-
const conn = conns.find((c) => c.id === ws.storageConnectionId) || conns.find((c) => c.role === "primary") || conns[0] || null;
|
|
2960
|
-
return { user, workspace: ws, connection: conn };
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
return { user: null, workspace: null, connection: null };
|
|
2964
|
-
}
|
|
2965
|
-
async function getWorkspaces(userId, env) {
|
|
2966
|
-
if (!userId) {
|
|
2967
|
-
return { success: true, workspaces: [], activeWorkspaceId: null, storage_connections: [] };
|
|
2968
|
-
}
|
|
2969
|
-
const res = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1 OR email = $1 LIMIT 1`, [userId], env);
|
|
2970
|
-
const userRow = res.rows[0];
|
|
2971
|
-
if (!userRow) {
|
|
2972
|
-
return { success: true, workspaces: [], activeWorkspaceId: null, storage_connections: [] };
|
|
2973
|
-
}
|
|
2974
|
-
const profiles = typeof userRow.app_profiles === "string" ? JSON.parse(userRow.app_profiles) : userRow.app_profiles || {};
|
|
2975
|
-
const depm = profiles.depm || {};
|
|
2976
|
-
const workspaces = Array.isArray(depm.workspaces) ? depm.workspaces : [];
|
|
2977
|
-
const activeWorkspaceId = depm.activeWorkspaceId || null;
|
|
2978
|
-
const conns = depm.storage_connections?.length ? depm.storage_connections : profiles.srkl?.storage_connections || [];
|
|
2979
|
-
return { success: true, workspaces, activeWorkspaceId, storage_connections: conns };
|
|
2980
|
-
}
|
|
2981
|
-
async function createWorkspace(userId, wsData, env) {
|
|
2982
|
-
const uRes = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1`, [userId], env);
|
|
2983
|
-
const user = uRes.rows[0];
|
|
2984
|
-
if (!user) throw new Error("User account not found.");
|
|
2985
|
-
const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
|
|
2986
|
-
if (!profiles.depm) profiles.depm = {};
|
|
2987
|
-
const workspaces = Array.isArray(profiles.depm.workspaces) ? profiles.depm.workspaces : [];
|
|
2988
|
-
const wsName = (wsData.name || "New Workspace").trim();
|
|
2989
|
-
const wsId = wsData.id || `ws-depm-${Date.now()}`;
|
|
2990
|
-
const userName = user.username || user.name || "user";
|
|
2991
|
-
const folderKey = `ultrst_depm_${userName.toLowerCase().replace(/[^a-z0-9]/g, "_")}_${wsName.toLowerCase().replace(/[^a-z0-9]/g, "_")}`;
|
|
2992
|
-
const newWs = {
|
|
2993
|
-
...wsData,
|
|
2994
|
-
id: wsId,
|
|
2995
|
-
name: wsName,
|
|
2996
|
-
localFolderName: folderKey,
|
|
2997
|
-
localFolderKey: folderKey,
|
|
2998
|
-
workspacePath: `./${folderKey}`,
|
|
2999
|
-
storageProvider: wsData.storageProvider || "backblaze_b2",
|
|
3000
|
-
b2BucketName: wsData.b2BucketName || "ultrst-apps",
|
|
3001
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3002
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3003
|
-
};
|
|
3004
|
-
workspaces.push(newWs);
|
|
3005
|
-
profiles.depm.workspaces = workspaces;
|
|
3006
|
-
profiles.depm.activeWorkspaceId = wsId;
|
|
3007
|
-
await queryUnifiedDb(`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`, [JSON.stringify(profiles), user.id], env);
|
|
3008
|
-
const conn = resolveWorkspaceConnection(profiles, newWs);
|
|
3009
|
-
await syncWorkspaceCreateToBucket(newWs, conn, env);
|
|
3010
|
-
return { success: true, workspace: newWs, workspaces };
|
|
3011
|
-
}
|
|
3012
|
-
async function updateWorkspace(userId, params, env) {
|
|
3013
|
-
const uRes = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1`, [userId], env);
|
|
3014
|
-
const user = uRes.rows[0];
|
|
3015
|
-
if (!user) throw new Error("User not found.");
|
|
3016
|
-
const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
|
|
3017
|
-
if (!profiles.depm) profiles.depm = {};
|
|
3018
|
-
const workspaces = Array.isArray(profiles.depm.workspaces) ? profiles.depm.workspaces : [];
|
|
3019
|
-
const { action, workspaceId, updates } = params;
|
|
3020
|
-
const targetId = workspaceId || updates?.id;
|
|
3021
|
-
const match = workspaces.find((w) => w.id === targetId);
|
|
3022
|
-
if (!match) throw new Error("Workspace not found.");
|
|
3023
|
-
if (action === "switch") {
|
|
3024
|
-
profiles.depm.activeWorkspaceId = targetId;
|
|
3025
|
-
} else {
|
|
3026
|
-
const safeUpdates = { ...updates || params };
|
|
3027
|
-
delete safeUpdates.storageProvider;
|
|
3028
|
-
delete safeUpdates.storageConnectionId;
|
|
3029
|
-
delete safeUpdates.b2BucketName;
|
|
3030
|
-
delete safeUpdates.b2Endpoint;
|
|
3031
|
-
delete safeUpdates.b2Region;
|
|
3032
|
-
delete safeUpdates.localFolderName;
|
|
3033
|
-
delete safeUpdates.localFolderKey;
|
|
3034
|
-
delete safeUpdates.workspacePath;
|
|
3035
|
-
delete safeUpdates.primaryStorage;
|
|
3036
|
-
delete safeUpdates.id;
|
|
3037
|
-
delete safeUpdates.action;
|
|
3038
|
-
delete safeUpdates.workspaceId;
|
|
3039
|
-
Object.assign(match, safeUpdates, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3040
|
-
}
|
|
3041
|
-
await queryUnifiedDb(`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`, [JSON.stringify(profiles), user.id], env);
|
|
3042
|
-
return { success: true, workspace: match, workspaces };
|
|
3043
|
-
}
|
|
3044
|
-
async function deleteWorkspace(userId, wsId, env) {
|
|
3045
|
-
const uRes = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1`, [userId], env);
|
|
3046
|
-
const user = uRes.rows[0];
|
|
3047
|
-
if (!user) throw new Error("User not found.");
|
|
3048
|
-
const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
|
|
3049
|
-
if (!profiles.depm?.workspaces) return { success: true, workspaces: [] };
|
|
3050
|
-
const targetWs = profiles.depm.workspaces.find((w) => w.id === wsId);
|
|
3051
|
-
const conn = targetWs ? resolveWorkspaceConnection(profiles, targetWs) : null;
|
|
3052
|
-
profiles.depm.workspaces = profiles.depm.workspaces.filter((w) => w.id !== wsId);
|
|
3053
|
-
if (profiles.depm.activeWorkspaceId === wsId) {
|
|
3054
|
-
profiles.depm.activeWorkspaceId = profiles.depm.workspaces[0]?.id || null;
|
|
3055
|
-
}
|
|
3056
|
-
if (profiles.depm.workspace_data?.[wsId]) {
|
|
3057
|
-
delete profiles.depm.workspace_data[wsId];
|
|
3058
|
-
}
|
|
3059
|
-
await queryUnifiedDb(`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`, [JSON.stringify(profiles), user.id], env);
|
|
3060
|
-
if (targetWs && conn) {
|
|
3061
|
-
await syncWorkspaceDeleteToBucket(targetWs, conn, env);
|
|
3062
|
-
}
|
|
3063
|
-
return { success: true, workspaces: profiles.depm.workspaces };
|
|
3064
|
-
}
|
|
3065
|
-
|
|
3066
2093
|
// src/core/cloudflareIntegrationCore.ts
|
|
3067
2094
|
var cfFetcher = new CloudflareResourceFetcher();
|
|
3068
2095
|
function buildHeaders(token, authKey, authEmail) {
|
|
@@ -3529,8 +2556,8 @@ function createWavBuffer(sampleRate, numChannels, samples) {
|
|
|
3529
2556
|
}
|
|
3530
2557
|
|
|
3531
2558
|
// src/server/sampleGenerator.ts
|
|
3532
|
-
import
|
|
3533
|
-
import
|
|
2559
|
+
import fs2 from "fs";
|
|
2560
|
+
import path2 from "path";
|
|
3534
2561
|
var GLOBAL_SAMPLE_DEFS = [
|
|
3535
2562
|
{
|
|
3536
2563
|
filename: "808_sub_boom.wav",
|
|
@@ -3627,13 +2654,13 @@ var GLOBAL_SAMPLE_DEFS = [
|
|
|
3627
2654
|
}
|
|
3628
2655
|
];
|
|
3629
2656
|
async function ensureGlobalSamples(globalSamplesDir) {
|
|
3630
|
-
if (!
|
|
3631
|
-
|
|
2657
|
+
if (!fs2.existsSync(globalSamplesDir)) {
|
|
2658
|
+
fs2.mkdirSync(globalSamplesDir, { recursive: true });
|
|
3632
2659
|
}
|
|
3633
2660
|
const sampleRate = 44100;
|
|
3634
2661
|
for (const def of GLOBAL_SAMPLE_DEFS) {
|
|
3635
|
-
const filePath =
|
|
3636
|
-
if (!
|
|
2662
|
+
const filePath = path2.join(globalSamplesDir, def.filename);
|
|
2663
|
+
if (!fs2.existsSync(filePath)) {
|
|
3637
2664
|
const numSamples = Math.floor(sampleRate * def.durationSec);
|
|
3638
2665
|
const chL = new Float32Array(numSamples);
|
|
3639
2666
|
const chR = new Float32Array(numSamples);
|
|
@@ -3645,16 +2672,16 @@ async function ensureGlobalSamples(globalSamplesDir) {
|
|
|
3645
2672
|
chR[i] = r;
|
|
3646
2673
|
}
|
|
3647
2674
|
const wavBuf = createWavBuffer(sampleRate, 2, [chL, chR]);
|
|
3648
|
-
await
|
|
2675
|
+
await fs2.promises.writeFile(filePath, wavBuf);
|
|
3649
2676
|
}
|
|
3650
2677
|
}
|
|
3651
2678
|
}
|
|
3652
2679
|
|
|
3653
2680
|
// src/server/services/assetPoolService.ts
|
|
3654
|
-
import
|
|
3655
|
-
import
|
|
3656
|
-
var WORKSPACE_ASSETS_ROOT =
|
|
3657
|
-
var PROJECTS_ROOT =
|
|
2681
|
+
import fs3 from "fs";
|
|
2682
|
+
import path3 from "path";
|
|
2683
|
+
var WORKSPACE_ASSETS_ROOT = path3.join(process.cwd(), "assets");
|
|
2684
|
+
var PROJECTS_ROOT = path3.join(process.cwd(), "projects");
|
|
3658
2685
|
var SUPPORTED_AUDIO_EXTS = [
|
|
3659
2686
|
".wav",
|
|
3660
2687
|
".mp3",
|
|
@@ -3672,27 +2699,27 @@ var SUPPORTED_AUDIO_EXTS = [
|
|
|
3672
2699
|
var SUPPORTED_MIDI_EXTS = [".mid", ".midi", ".json"];
|
|
3673
2700
|
async function ensureAssetPoolDirectories(rootDir) {
|
|
3674
2701
|
const dirs = [
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
2702
|
+
path3.join(rootDir, "audio", "stereo", "default"),
|
|
2703
|
+
path3.join(rootDir, "audio", "stereo", "userdefined"),
|
|
2704
|
+
path3.join(rootDir, "audio", "mono", "default"),
|
|
2705
|
+
path3.join(rootDir, "audio", "mono", "userdefined"),
|
|
2706
|
+
path3.join(rootDir, "midi", "default"),
|
|
2707
|
+
path3.join(rootDir, "midi", "userdefined")
|
|
3681
2708
|
];
|
|
3682
2709
|
for (const dir of dirs) {
|
|
3683
|
-
if (!
|
|
3684
|
-
await
|
|
2710
|
+
if (!fs3.existsSync(dir)) {
|
|
2711
|
+
await fs3.promises.mkdir(dir, { recursive: true });
|
|
3685
2712
|
}
|
|
3686
2713
|
}
|
|
3687
2714
|
}
|
|
3688
2715
|
async function seedDefaultAssets() {
|
|
3689
2716
|
await ensureAssetPoolDirectories(WORKSPACE_ASSETS_ROOT);
|
|
3690
|
-
const stereoDefaultDir =
|
|
3691
|
-
const monoDefaultDir =
|
|
3692
|
-
const midiDefaultDir =
|
|
2717
|
+
const stereoDefaultDir = path3.join(WORKSPACE_ASSETS_ROOT, "audio", "stereo", "default");
|
|
2718
|
+
const monoDefaultDir = path3.join(WORKSPACE_ASSETS_ROOT, "audio", "mono", "default");
|
|
2719
|
+
const midiDefaultDir = path3.join(WORKSPACE_ASSETS_ROOT, "midi", "default");
|
|
3693
2720
|
const sampleRate = 44100;
|
|
3694
|
-
const subBoomPath =
|
|
3695
|
-
if (!
|
|
2721
|
+
const subBoomPath = path3.join(stereoDefaultDir, "808_sub_boom.wav");
|
|
2722
|
+
if (!fs3.existsSync(subBoomPath)) {
|
|
3696
2723
|
const numSamples = Math.floor(sampleRate * 1.8);
|
|
3697
2724
|
const chL = new Float32Array(numSamples);
|
|
3698
2725
|
const chR = new Float32Array(numSamples);
|
|
@@ -3703,10 +2730,10 @@ async function seedDefaultAssets() {
|
|
|
3703
2730
|
chL[i] = s;
|
|
3704
2731
|
chR[i] = s;
|
|
3705
2732
|
}
|
|
3706
|
-
await
|
|
2733
|
+
await fs3.promises.writeFile(subBoomPath, createWavBuffer(sampleRate, 2, [chL, chR]));
|
|
3707
2734
|
}
|
|
3708
|
-
const leadPath =
|
|
3709
|
-
if (!
|
|
2735
|
+
const leadPath = path3.join(stereoDefaultDir, "celestial_lead.wav");
|
|
2736
|
+
if (!fs3.existsSync(leadPath)) {
|
|
3710
2737
|
const numSamples = Math.floor(sampleRate * 2.5);
|
|
3711
2738
|
const chL = new Float32Array(numSamples);
|
|
3712
2739
|
const chR = new Float32Array(numSamples);
|
|
@@ -3720,10 +2747,10 @@ async function seedDefaultAssets() {
|
|
|
3720
2747
|
chL[i] = s1 * env;
|
|
3721
2748
|
chR[i] = (s1 + s2 * 0.5) * env;
|
|
3722
2749
|
}
|
|
3723
|
-
await
|
|
2750
|
+
await fs3.promises.writeFile(leadPath, createWavBuffer(sampleRate, 2, [chL, chR]));
|
|
3724
2751
|
}
|
|
3725
|
-
const padPath =
|
|
3726
|
-
if (!
|
|
2752
|
+
const padPath = path3.join(stereoDefaultDir, "ethereal_pad.wav");
|
|
2753
|
+
if (!fs3.existsSync(padPath)) {
|
|
3727
2754
|
const numSamples = Math.floor(sampleRate * 3);
|
|
3728
2755
|
const chL = new Float32Array(numSamples);
|
|
3729
2756
|
const chR = new Float32Array(numSamples);
|
|
@@ -3742,10 +2769,10 @@ async function seedDefaultAssets() {
|
|
|
3742
2769
|
chL[i] = left * env;
|
|
3743
2770
|
chR[i] = right * env;
|
|
3744
2771
|
}
|
|
3745
|
-
await
|
|
2772
|
+
await fs3.promises.writeFile(padPath, createWavBuffer(sampleRate, 2, [chL, chR]));
|
|
3746
2773
|
}
|
|
3747
|
-
const snarePath =
|
|
3748
|
-
if (!
|
|
2774
|
+
const snarePath = path3.join(monoDefaultDir, "snare_snappy.wav");
|
|
2775
|
+
if (!fs3.existsSync(snarePath)) {
|
|
3749
2776
|
const numSamples = Math.floor(sampleRate * 0.8);
|
|
3750
2777
|
const chM = new Float32Array(numSamples);
|
|
3751
2778
|
for (let i = 0; i < numSamples; i++) {
|
|
@@ -3755,10 +2782,10 @@ async function seedDefaultAssets() {
|
|
|
3755
2782
|
const noise = (Math.random() * 2 - 1) * Math.exp(-p * 4.5) * 0.6;
|
|
3756
2783
|
chM[i] = tone + noise;
|
|
3757
2784
|
}
|
|
3758
|
-
await
|
|
2785
|
+
await fs3.promises.writeFile(snarePath, createWavBuffer(sampleRate, 1, [chM]));
|
|
3759
2786
|
}
|
|
3760
|
-
const hihatPath =
|
|
3761
|
-
if (!
|
|
2787
|
+
const hihatPath = path3.join(monoDefaultDir, "hihat_closed.wav");
|
|
2788
|
+
if (!fs3.existsSync(hihatPath)) {
|
|
3762
2789
|
const numSamples = Math.floor(sampleRate * 0.25);
|
|
3763
2790
|
const chM = new Float32Array(numSamples);
|
|
3764
2791
|
for (let i = 0; i < numSamples; i++) {
|
|
@@ -3766,10 +2793,10 @@ async function seedDefaultAssets() {
|
|
|
3766
2793
|
const noise = (Math.random() * 2 - 1) * Math.exp(-p * 20);
|
|
3767
2794
|
chM[i] = noise * 0.5;
|
|
3768
2795
|
}
|
|
3769
|
-
await
|
|
2796
|
+
await fs3.promises.writeFile(hihatPath, createWavBuffer(sampleRate, 1, [chM]));
|
|
3770
2797
|
}
|
|
3771
|
-
const midiChordPath =
|
|
3772
|
-
if (!
|
|
2798
|
+
const midiChordPath = path3.join(midiDefaultDir, "lydian_celestial_pad.json");
|
|
2799
|
+
if (!fs3.existsSync(midiChordPath)) {
|
|
3773
2800
|
const starterMidiPattern = {
|
|
3774
2801
|
id: "pat-default-lydian",
|
|
3775
2802
|
name: "Lydian Celestial Pad",
|
|
@@ -3782,31 +2809,31 @@ async function seedDefaultAssets() {
|
|
|
3782
2809
|
{ note: 71, name: "B4", velocity: 0.75, time: 0, duration: 3.8 }
|
|
3783
2810
|
]
|
|
3784
2811
|
};
|
|
3785
|
-
await
|
|
2812
|
+
await fs3.promises.writeFile(midiChordPath, JSON.stringify(starterMidiPattern, null, 2), "utf-8");
|
|
3786
2813
|
}
|
|
3787
2814
|
await ensureProjectAssets("default-kingdom");
|
|
3788
2815
|
}
|
|
3789
2816
|
async function ensureProjectAssets(projectId) {
|
|
3790
|
-
const safeId =
|
|
3791
|
-
const projectAssetsDir =
|
|
2817
|
+
const safeId = path3.basename(projectId);
|
|
2818
|
+
const projectAssetsDir = path3.join(PROJECTS_ROOT, safeId, "assets");
|
|
3792
2819
|
await ensureAssetPoolDirectories(projectAssetsDir);
|
|
3793
2820
|
return projectAssetsDir;
|
|
3794
2821
|
}
|
|
3795
2822
|
function getAssetPoolLocationPath(scope, projectId, dataType, channelType, subfolder = "userdefined") {
|
|
3796
|
-
const root = scope === "project" && projectId ?
|
|
3797
|
-
const safeSub =
|
|
2823
|
+
const root = scope === "project" && projectId ? path3.join(PROJECTS_ROOT, path3.basename(projectId), "assets") : WORKSPACE_ASSETS_ROOT;
|
|
2824
|
+
const safeSub = path3.basename(subfolder).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3798
2825
|
if (dataType === "audio") {
|
|
3799
2826
|
const safeChannel = channelType === "mono" ? "mono" : "stereo";
|
|
3800
|
-
return
|
|
2827
|
+
return path3.join(root, "audio", safeChannel, safeSub);
|
|
3801
2828
|
} else {
|
|
3802
|
-
return
|
|
2829
|
+
return path3.join(root, "midi", safeSub);
|
|
3803
2830
|
}
|
|
3804
2831
|
}
|
|
3805
2832
|
function getAssetPoolPublicUrl(scope, projectId, dataType, channelType, subfolder, filename) {
|
|
3806
|
-
const safeFilename =
|
|
3807
|
-
const safeSub =
|
|
2833
|
+
const safeFilename = path3.basename(filename);
|
|
2834
|
+
const safeSub = path3.basename(subfolder);
|
|
3808
2835
|
if (scope === "project" && projectId) {
|
|
3809
|
-
const safeProj =
|
|
2836
|
+
const safeProj = path3.basename(projectId);
|
|
3810
2837
|
if (dataType === "audio") {
|
|
3811
2838
|
const channel = channelType === "mono" ? "mono" : "stereo";
|
|
3812
2839
|
return `/projects/${safeProj}/assets/audio/${channel}/${safeSub}/${safeFilename}`;
|
|
@@ -3828,31 +2855,31 @@ async function scanAssetPool(scope, projectId) {
|
|
|
3828
2855
|
} else {
|
|
3829
2856
|
await ensureAssetPoolDirectories(WORKSPACE_ASSETS_ROOT);
|
|
3830
2857
|
}
|
|
3831
|
-
const rootDir = scope === "project" && projectId ?
|
|
2858
|
+
const rootDir = scope === "project" && projectId ? path3.join(PROJECTS_ROOT, path3.basename(projectId), "assets") : WORKSPACE_ASSETS_ROOT;
|
|
3832
2859
|
const folders = [];
|
|
3833
2860
|
const items = [];
|
|
3834
2861
|
async function scanCategoryFolder(dataType, channelType) {
|
|
3835
|
-
const catDir = dataType === "audio" ?
|
|
3836
|
-
if (!
|
|
3837
|
-
await
|
|
2862
|
+
const catDir = dataType === "audio" ? path3.join(rootDir, "audio", channelType === "mono" ? "mono" : "stereo") : path3.join(rootDir, "midi");
|
|
2863
|
+
if (!fs3.existsSync(catDir)) {
|
|
2864
|
+
await fs3.promises.mkdir(catDir, { recursive: true });
|
|
3838
2865
|
}
|
|
3839
|
-
const subentries = await
|
|
2866
|
+
const subentries = await fs3.promises.readdir(catDir, { withFileTypes: true });
|
|
3840
2867
|
for (const entry of subentries) {
|
|
3841
2868
|
if (!entry.isDirectory()) continue;
|
|
3842
2869
|
const subfolderName = entry.name;
|
|
3843
|
-
const subfolderDir =
|
|
3844
|
-
const files = await
|
|
2870
|
+
const subfolderDir = path3.join(catDir, subfolderName);
|
|
2871
|
+
const files = await fs3.promises.readdir(subfolderDir);
|
|
3845
2872
|
let validFileCount = 0;
|
|
3846
2873
|
for (const file of files) {
|
|
3847
2874
|
if (file.startsWith(".")) continue;
|
|
3848
|
-
const ext =
|
|
2875
|
+
const ext = path3.extname(file).toLowerCase();
|
|
3849
2876
|
const isAudio = SUPPORTED_AUDIO_EXTS.includes(ext);
|
|
3850
2877
|
const isMidi = SUPPORTED_MIDI_EXTS.includes(ext);
|
|
3851
2878
|
if (dataType === "audio" && !isAudio || dataType === "midi" && !isMidi) continue;
|
|
3852
2879
|
validFileCount++;
|
|
3853
|
-
const filePath =
|
|
3854
|
-
const stat = await
|
|
3855
|
-
const cleanName =
|
|
2880
|
+
const filePath = path3.join(subfolderDir, file);
|
|
2881
|
+
const stat = await fs3.promises.stat(filePath);
|
|
2882
|
+
const cleanName = path3.basename(file, ext).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
3856
2883
|
const relPath = dataType === "audio" ? `audio/${channelType === "mono" ? "mono" : "stereo"}/${subfolderName}/${file}` : `midi/${subfolderName}/${file}`;
|
|
3857
2884
|
const url = getAssetPoolPublicUrl(
|
|
3858
2885
|
scope,
|
|
@@ -3893,7 +2920,7 @@ async function scanAssetPool(scope, projectId) {
|
|
|
3893
2920
|
return { folders, items };
|
|
3894
2921
|
}
|
|
3895
2922
|
async function createAssetSubfolder(params) {
|
|
3896
|
-
const safeName =
|
|
2923
|
+
const safeName = path3.basename(params.folderName).trim().replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3897
2924
|
if (!safeName) throw new Error("Invalid subfolder name");
|
|
3898
2925
|
const dirPath = getAssetPoolLocationPath(
|
|
3899
2926
|
params.scope,
|
|
@@ -3902,8 +2929,8 @@ async function createAssetSubfolder(params) {
|
|
|
3902
2929
|
params.channelType,
|
|
3903
2930
|
safeName
|
|
3904
2931
|
);
|
|
3905
|
-
if (!
|
|
3906
|
-
await
|
|
2932
|
+
if (!fs3.existsSync(dirPath)) {
|
|
2933
|
+
await fs3.promises.mkdir(dirPath, { recursive: true });
|
|
3907
2934
|
}
|
|
3908
2935
|
return {
|
|
3909
2936
|
name: safeName,
|
|
@@ -3922,16 +2949,16 @@ async function saveAssetToPool(params) {
|
|
|
3922
2949
|
params.channelType,
|
|
3923
2950
|
subfolder
|
|
3924
2951
|
);
|
|
3925
|
-
if (!
|
|
3926
|
-
await
|
|
2952
|
+
if (!fs3.existsSync(targetDir)) {
|
|
2953
|
+
await fs3.promises.mkdir(targetDir, { recursive: true });
|
|
3927
2954
|
}
|
|
3928
|
-
const safeFilename =
|
|
3929
|
-
const ext =
|
|
3930
|
-
const filePath =
|
|
2955
|
+
const safeFilename = path3.basename(params.filename).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
2956
|
+
const ext = path3.extname(safeFilename).toLowerCase() || (params.dataType === "audio" ? ".wav" : ".json");
|
|
2957
|
+
const filePath = path3.join(targetDir, safeFilename);
|
|
3931
2958
|
const buffer = Buffer.from(params.base64Content, "base64");
|
|
3932
|
-
await
|
|
3933
|
-
const stat = await
|
|
3934
|
-
const cleanName =
|
|
2959
|
+
await fs3.promises.writeFile(filePath, buffer);
|
|
2960
|
+
const stat = await fs3.promises.stat(filePath);
|
|
2961
|
+
const cleanName = path3.basename(safeFilename, ext).replace(/[-_]/g, " ");
|
|
3935
2962
|
const relPath = params.dataType === "audio" ? `audio/${params.channelType === "mono" ? "mono" : "stereo"}/${subfolder}/${safeFilename}` : `midi/${subfolder}/${safeFilename}`;
|
|
3936
2963
|
const url = getAssetPoolPublicUrl(
|
|
3937
2964
|
params.scope,
|
|
@@ -3965,19 +2992,19 @@ async function deleteAssetFromPool(params) {
|
|
|
3965
2992
|
params.channelType,
|
|
3966
2993
|
params.subfolder
|
|
3967
2994
|
);
|
|
3968
|
-
const filePath =
|
|
3969
|
-
if (
|
|
3970
|
-
await
|
|
2995
|
+
const filePath = path3.join(targetDir, path3.basename(params.filename));
|
|
2996
|
+
if (fs3.existsSync(filePath)) {
|
|
2997
|
+
await fs3.promises.unlink(filePath);
|
|
3971
2998
|
return true;
|
|
3972
2999
|
}
|
|
3973
3000
|
return false;
|
|
3974
3001
|
}
|
|
3975
3002
|
|
|
3976
3003
|
// src/server/services/graphAssetService.ts
|
|
3977
|
-
import
|
|
3978
|
-
import
|
|
3979
|
-
var WORKSPACE_ROOT =
|
|
3980
|
-
var GRAPHS_ROOT =
|
|
3004
|
+
import fs4 from "fs";
|
|
3005
|
+
import path4 from "path";
|
|
3006
|
+
var WORKSPACE_ROOT = path4.join(process.cwd(), "workspaces", "default");
|
|
3007
|
+
var GRAPHS_ROOT = path4.join(WORKSPACE_ROOT, "graphs");
|
|
3981
3008
|
var SUPPORTED_AUDIO_EXTS2 = [
|
|
3982
3009
|
".wav",
|
|
3983
3010
|
".mp3",
|
|
@@ -3995,26 +3022,26 @@ var SUPPORTED_AUDIO_EXTS2 = [
|
|
|
3995
3022
|
var SUPPORTED_MIDI_EXTS2 = [".mid", ".midi", ".json"];
|
|
3996
3023
|
function ensureGraphAssetDirectories(graphDir) {
|
|
3997
3024
|
const dirs = [
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
3025
|
+
path4.join(graphDir, "assets", "audio", "stereo", "default"),
|
|
3026
|
+
path4.join(graphDir, "assets", "audio", "stereo", "userdefined"),
|
|
3027
|
+
path4.join(graphDir, "assets", "audio", "mono", "default"),
|
|
3028
|
+
path4.join(graphDir, "assets", "audio", "mono", "userdefined"),
|
|
3029
|
+
path4.join(graphDir, "assets", "midi", "default"),
|
|
3030
|
+
path4.join(graphDir, "assets", "midi", "userdefined")
|
|
4004
3031
|
];
|
|
4005
3032
|
for (const dir of dirs) {
|
|
4006
|
-
if (!
|
|
4007
|
-
|
|
3033
|
+
if (!fs4.existsSync(dir)) {
|
|
3034
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
4008
3035
|
}
|
|
4009
3036
|
}
|
|
4010
3037
|
}
|
|
4011
3038
|
function seedStarterGraphAssets(graphDir) {
|
|
4012
|
-
const stereoDefaultDir =
|
|
4013
|
-
const monoDefaultDir =
|
|
4014
|
-
const midiDefaultDir =
|
|
3039
|
+
const stereoDefaultDir = path4.join(graphDir, "assets", "audio", "stereo", "default");
|
|
3040
|
+
const monoDefaultDir = path4.join(graphDir, "assets", "audio", "mono", "default");
|
|
3041
|
+
const midiDefaultDir = path4.join(graphDir, "assets", "midi", "default");
|
|
4015
3042
|
const sampleRate = 44100;
|
|
4016
|
-
const subBoomPath =
|
|
4017
|
-
if (!
|
|
3043
|
+
const subBoomPath = path4.join(stereoDefaultDir, "808_sub_boom.wav");
|
|
3044
|
+
if (!fs4.existsSync(subBoomPath)) {
|
|
4018
3045
|
const numSamples = Math.floor(sampleRate * 1.8);
|
|
4019
3046
|
const chL = new Float32Array(numSamples);
|
|
4020
3047
|
const chR = new Float32Array(numSamples);
|
|
@@ -4025,10 +3052,10 @@ function seedStarterGraphAssets(graphDir) {
|
|
|
4025
3052
|
chL[i] = s;
|
|
4026
3053
|
chR[i] = s;
|
|
4027
3054
|
}
|
|
4028
|
-
|
|
3055
|
+
fs4.writeFileSync(subBoomPath, createWavBuffer(sampleRate, 2, [chL, chR]));
|
|
4029
3056
|
}
|
|
4030
|
-
const leadPath =
|
|
4031
|
-
if (!
|
|
3057
|
+
const leadPath = path4.join(stereoDefaultDir, "celestial_lead.wav");
|
|
3058
|
+
if (!fs4.existsSync(leadPath)) {
|
|
4032
3059
|
const numSamples = Math.floor(sampleRate * 2.5);
|
|
4033
3060
|
const chL = new Float32Array(numSamples);
|
|
4034
3061
|
const chR = new Float32Array(numSamples);
|
|
@@ -4042,10 +3069,10 @@ function seedStarterGraphAssets(graphDir) {
|
|
|
4042
3069
|
chL[i] = s1 * env;
|
|
4043
3070
|
chR[i] = (s1 + s2 * 0.5) * env;
|
|
4044
3071
|
}
|
|
4045
|
-
|
|
3072
|
+
fs4.writeFileSync(leadPath, createWavBuffer(sampleRate, 2, [chL, chR]));
|
|
4046
3073
|
}
|
|
4047
|
-
const padPath =
|
|
4048
|
-
if (!
|
|
3074
|
+
const padPath = path4.join(stereoDefaultDir, "ethereal_pad.wav");
|
|
3075
|
+
if (!fs4.existsSync(padPath)) {
|
|
4049
3076
|
const numSamples = Math.floor(sampleRate * 3);
|
|
4050
3077
|
const chL = new Float32Array(numSamples);
|
|
4051
3078
|
const chR = new Float32Array(numSamples);
|
|
@@ -4064,10 +3091,10 @@ function seedStarterGraphAssets(graphDir) {
|
|
|
4064
3091
|
chL[i] = left * env;
|
|
4065
3092
|
chR[i] = right * env;
|
|
4066
3093
|
}
|
|
4067
|
-
|
|
3094
|
+
fs4.writeFileSync(padPath, createWavBuffer(sampleRate, 2, [chL, chR]));
|
|
4068
3095
|
}
|
|
4069
|
-
const snarePath =
|
|
4070
|
-
if (!
|
|
3096
|
+
const snarePath = path4.join(monoDefaultDir, "snare_snappy.wav");
|
|
3097
|
+
if (!fs4.existsSync(snarePath)) {
|
|
4071
3098
|
const numSamples = Math.floor(sampleRate * 0.8);
|
|
4072
3099
|
const chM = new Float32Array(numSamples);
|
|
4073
3100
|
for (let i = 0; i < numSamples; i++) {
|
|
@@ -4077,10 +3104,10 @@ function seedStarterGraphAssets(graphDir) {
|
|
|
4077
3104
|
const noise = (Math.random() * 2 - 1) * Math.exp(-p * 4.5) * 0.6;
|
|
4078
3105
|
chM[i] = tone + noise;
|
|
4079
3106
|
}
|
|
4080
|
-
|
|
3107
|
+
fs4.writeFileSync(snarePath, createWavBuffer(sampleRate, 1, [chM]));
|
|
4081
3108
|
}
|
|
4082
|
-
const hihatPath =
|
|
4083
|
-
if (!
|
|
3109
|
+
const hihatPath = path4.join(monoDefaultDir, "hihat_closed.wav");
|
|
3110
|
+
if (!fs4.existsSync(hihatPath)) {
|
|
4084
3111
|
const numSamples = Math.floor(sampleRate * 0.25);
|
|
4085
3112
|
const chM = new Float32Array(numSamples);
|
|
4086
3113
|
for (let i = 0; i < numSamples; i++) {
|
|
@@ -4088,10 +3115,10 @@ function seedStarterGraphAssets(graphDir) {
|
|
|
4088
3115
|
const noise = (Math.random() * 2 - 1) * Math.exp(-p * 20);
|
|
4089
3116
|
chM[i] = noise * 0.5;
|
|
4090
3117
|
}
|
|
4091
|
-
|
|
3118
|
+
fs4.writeFileSync(hihatPath, createWavBuffer(sampleRate, 1, [chM]));
|
|
4092
3119
|
}
|
|
4093
|
-
const midiChordPath =
|
|
4094
|
-
if (!
|
|
3120
|
+
const midiChordPath = path4.join(midiDefaultDir, "lydian_celestial_pad.json");
|
|
3121
|
+
if (!fs4.existsSync(midiChordPath)) {
|
|
4095
3122
|
const starterMidiPattern = {
|
|
4096
3123
|
id: "pat-default-lydian",
|
|
4097
3124
|
name: "Lydian Celestial Pad",
|
|
@@ -4104,43 +3131,43 @@ function seedStarterGraphAssets(graphDir) {
|
|
|
4104
3131
|
{ note: 71, name: "B4", velocity: 0.75, time: 0, duration: 3.8 }
|
|
4105
3132
|
]
|
|
4106
3133
|
};
|
|
4107
|
-
|
|
3134
|
+
fs4.writeFileSync(midiChordPath, JSON.stringify(starterMidiPattern, null, 2), "utf-8");
|
|
4108
3135
|
}
|
|
4109
3136
|
}
|
|
4110
3137
|
async function scanGraphAssets(graphId, includeImported = true) {
|
|
4111
|
-
const safeGraphId =
|
|
4112
|
-
const graphDir =
|
|
4113
|
-
if (!
|
|
4114
|
-
await
|
|
3138
|
+
const safeGraphId = path4.basename(graphId);
|
|
3139
|
+
const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
|
|
3140
|
+
if (!fs4.existsSync(graphDir)) {
|
|
3141
|
+
await fs4.promises.mkdir(graphDir, { recursive: true });
|
|
4115
3142
|
}
|
|
4116
3143
|
ensureGraphAssetDirectories(graphDir);
|
|
4117
3144
|
const folders = [];
|
|
4118
3145
|
const items = [];
|
|
4119
3146
|
const importedItems = [];
|
|
4120
3147
|
async function scanSingleGraph(gId, isImported, ownerName) {
|
|
4121
|
-
const currentGraphDir =
|
|
4122
|
-
const assetsDir =
|
|
4123
|
-
if (!
|
|
3148
|
+
const currentGraphDir = path4.join(GRAPHS_ROOT, path4.basename(gId));
|
|
3149
|
+
const assetsDir = path4.join(currentGraphDir, "assets");
|
|
3150
|
+
if (!fs4.existsSync(assetsDir)) return;
|
|
4124
3151
|
async function scanCategoryFolder(dataType, channelType) {
|
|
4125
|
-
const catDir = dataType === "audio" ?
|
|
4126
|
-
if (!
|
|
4127
|
-
const subentries = await
|
|
3152
|
+
const catDir = dataType === "audio" ? path4.join(assetsDir, "audio", channelType === "mono" ? "mono" : "stereo") : path4.join(assetsDir, "midi");
|
|
3153
|
+
if (!fs4.existsSync(catDir)) return;
|
|
3154
|
+
const subentries = await fs4.promises.readdir(catDir, { withFileTypes: true });
|
|
4128
3155
|
for (const entry of subentries) {
|
|
4129
3156
|
if (!entry.isDirectory()) continue;
|
|
4130
3157
|
const subfolderName = entry.name;
|
|
4131
|
-
const subfolderDir =
|
|
4132
|
-
const files = await
|
|
3158
|
+
const subfolderDir = path4.join(catDir, subfolderName);
|
|
3159
|
+
const files = await fs4.promises.readdir(subfolderDir);
|
|
4133
3160
|
let validFileCount = 0;
|
|
4134
3161
|
for (const file of files) {
|
|
4135
3162
|
if (file.startsWith(".")) continue;
|
|
4136
|
-
const ext =
|
|
3163
|
+
const ext = path4.extname(file).toLowerCase();
|
|
4137
3164
|
const isAudio = SUPPORTED_AUDIO_EXTS2.includes(ext);
|
|
4138
3165
|
const isMidi = SUPPORTED_MIDI_EXTS2.includes(ext);
|
|
4139
3166
|
if (dataType === "audio" && !isAudio || dataType === "midi" && !isMidi) continue;
|
|
4140
3167
|
validFileCount++;
|
|
4141
|
-
const filePath =
|
|
4142
|
-
const stat = await
|
|
4143
|
-
const cleanName =
|
|
3168
|
+
const filePath = path4.join(subfolderDir, file);
|
|
3169
|
+
const stat = await fs4.promises.stat(filePath);
|
|
3170
|
+
const cleanName = path4.basename(file, ext).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
4144
3171
|
const relPath = dataType === "audio" ? `audio/${channelType === "mono" ? "mono" : "stereo"}/${subfolderName}/${file}` : `midi/${subfolderName}/${file}`;
|
|
4145
3172
|
const url = dataType === "audio" ? `/graphs/${gId}/assets/audio/${channelType === "mono" ? "mono" : "stereo"}/${subfolderName}/${file}` : `/graphs/${gId}/assets/midi/${subfolderName}/${file}`;
|
|
4146
3173
|
const item = {
|
|
@@ -4183,18 +3210,18 @@ async function scanGraphAssets(graphId, includeImported = true) {
|
|
|
4183
3210
|
}
|
|
4184
3211
|
await scanSingleGraph(safeGraphId, false);
|
|
4185
3212
|
if (includeImported) {
|
|
4186
|
-
const qmsFile =
|
|
4187
|
-
if (
|
|
3213
|
+
const qmsFile = path4.join(graphDir, "graph.qms");
|
|
3214
|
+
if (fs4.existsSync(qmsFile)) {
|
|
4188
3215
|
try {
|
|
4189
|
-
const raw = await
|
|
3216
|
+
const raw = await fs4.promises.readFile(qmsFile, "utf-8");
|
|
4190
3217
|
const pkg = JSON.parse(raw);
|
|
4191
3218
|
const importedIds = pkg?.metadata?.importedGraphIds || [];
|
|
4192
3219
|
for (const impId of importedIds) {
|
|
4193
|
-
const impQms =
|
|
3220
|
+
const impQms = path4.join(GRAPHS_ROOT, impId, "graph.qms");
|
|
4194
3221
|
let impName = impId;
|
|
4195
|
-
if (
|
|
3222
|
+
if (fs4.existsSync(impQms)) {
|
|
4196
3223
|
try {
|
|
4197
|
-
const impRaw = await
|
|
3224
|
+
const impRaw = await fs4.promises.readFile(impQms, "utf-8");
|
|
4198
3225
|
const impPkg = JSON.parse(impRaw);
|
|
4199
3226
|
impName = impPkg?.metadata?.name || impId;
|
|
4200
3227
|
} catch {
|
|
@@ -4210,14 +3237,14 @@ async function scanGraphAssets(graphId, includeImported = true) {
|
|
|
4210
3237
|
return { folders, items, importedItems };
|
|
4211
3238
|
}
|
|
4212
3239
|
async function createGraphAssetFolder(params) {
|
|
4213
|
-
const safeGraphId =
|
|
4214
|
-
const safeFolderName =
|
|
3240
|
+
const safeGraphId = path4.basename(params.graphId);
|
|
3241
|
+
const safeFolderName = path4.basename(params.folderName).trim().replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4215
3242
|
if (!safeFolderName) throw new Error("Invalid subfolder name");
|
|
4216
|
-
const graphDir =
|
|
3243
|
+
const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
|
|
4217
3244
|
ensureGraphAssetDirectories(graphDir);
|
|
4218
|
-
const targetDir = params.dataType === "audio" ?
|
|
4219
|
-
if (!
|
|
4220
|
-
await
|
|
3245
|
+
const targetDir = params.dataType === "audio" ? path4.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", safeFolderName) : path4.join(graphDir, "assets", "midi", safeFolderName);
|
|
3246
|
+
if (!fs4.existsSync(targetDir)) {
|
|
3247
|
+
await fs4.promises.mkdir(targetDir, { recursive: true });
|
|
4221
3248
|
}
|
|
4222
3249
|
return {
|
|
4223
3250
|
name: safeFolderName,
|
|
@@ -4229,21 +3256,21 @@ async function createGraphAssetFolder(params) {
|
|
|
4229
3256
|
};
|
|
4230
3257
|
}
|
|
4231
3258
|
async function saveAssetToGraph(params) {
|
|
4232
|
-
const safeGraphId =
|
|
3259
|
+
const safeGraphId = path4.basename(params.graphId);
|
|
4233
3260
|
const subfolder = params.subfolder || "userdefined";
|
|
4234
|
-
const graphDir =
|
|
3261
|
+
const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
|
|
4235
3262
|
ensureGraphAssetDirectories(graphDir);
|
|
4236
|
-
const targetDir = params.dataType === "audio" ?
|
|
4237
|
-
if (!
|
|
4238
|
-
await
|
|
3263
|
+
const targetDir = params.dataType === "audio" ? path4.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", subfolder) : path4.join(graphDir, "assets", "midi", subfolder);
|
|
3264
|
+
if (!fs4.existsSync(targetDir)) {
|
|
3265
|
+
await fs4.promises.mkdir(targetDir, { recursive: true });
|
|
4239
3266
|
}
|
|
4240
|
-
const safeFilename =
|
|
4241
|
-
const ext =
|
|
4242
|
-
const filePath =
|
|
3267
|
+
const safeFilename = path4.basename(params.filename).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3268
|
+
const ext = path4.extname(safeFilename).toLowerCase() || (params.dataType === "audio" ? ".wav" : ".json");
|
|
3269
|
+
const filePath = path4.join(targetDir, safeFilename);
|
|
4243
3270
|
const buffer = Buffer.from(params.base64Content, "base64");
|
|
4244
|
-
await
|
|
4245
|
-
const stat = await
|
|
4246
|
-
const cleanName =
|
|
3271
|
+
await fs4.promises.writeFile(filePath, buffer);
|
|
3272
|
+
const stat = await fs4.promises.stat(filePath);
|
|
3273
|
+
const cleanName = path4.basename(safeFilename, ext).replace(/[-_]/g, " ");
|
|
4247
3274
|
const relPath = params.dataType === "audio" ? `audio/${params.channelType === "mono" ? "mono" : "stereo"}/${subfolder}/${safeFilename}` : `midi/${subfolder}/${safeFilename}`;
|
|
4248
3275
|
const url = params.dataType === "audio" ? `/graphs/${safeGraphId}/assets/audio/${params.channelType === "mono" ? "mono" : "stereo"}/${subfolder}/${safeFilename}` : `/graphs/${safeGraphId}/assets/midi/${subfolder}/${safeFilename}`;
|
|
4249
3276
|
return {
|
|
@@ -4262,32 +3289,32 @@ async function saveAssetToGraph(params) {
|
|
|
4262
3289
|
};
|
|
4263
3290
|
}
|
|
4264
3291
|
async function deleteAssetFromGraph(params) {
|
|
4265
|
-
const safeGraphId =
|
|
4266
|
-
const graphDir =
|
|
4267
|
-
const targetDir = params.dataType === "audio" ?
|
|
4268
|
-
const filePath =
|
|
4269
|
-
if (
|
|
4270
|
-
await
|
|
3292
|
+
const safeGraphId = path4.basename(params.graphId);
|
|
3293
|
+
const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
|
|
3294
|
+
const targetDir = params.dataType === "audio" ? path4.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", params.subfolder) : path4.join(graphDir, "assets", "midi", params.subfolder);
|
|
3295
|
+
const filePath = path4.join(targetDir, path4.basename(params.filename));
|
|
3296
|
+
if (fs4.existsSync(filePath)) {
|
|
3297
|
+
await fs4.promises.unlink(filePath);
|
|
4271
3298
|
return true;
|
|
4272
3299
|
}
|
|
4273
3300
|
return false;
|
|
4274
3301
|
}
|
|
4275
3302
|
|
|
4276
3303
|
// src/server/services/graphPackageService.ts
|
|
4277
|
-
import
|
|
4278
|
-
import
|
|
3304
|
+
import fs5 from "fs";
|
|
3305
|
+
import path5 from "path";
|
|
4279
3306
|
import { validateGraphImport } from "@ultrst-armada/kloks/state-graph";
|
|
4280
|
-
var WORKSPACE_ROOT2 =
|
|
4281
|
-
var GRAPHS_ROOT2 =
|
|
4282
|
-
var LEGACY_PROJECTS_ROOT =
|
|
3307
|
+
var WORKSPACE_ROOT2 = path5.join(process.cwd(), "workspaces", "default");
|
|
3308
|
+
var GRAPHS_ROOT2 = path5.join(WORKSPACE_ROOT2, "graphs");
|
|
3309
|
+
var LEGACY_PROJECTS_ROOT = path5.join(process.cwd(), "projects");
|
|
4283
3310
|
function ensureWorkspaceStructure() {
|
|
4284
|
-
if (!
|
|
4285
|
-
|
|
3311
|
+
if (!fs5.existsSync(GRAPHS_ROOT2)) {
|
|
3312
|
+
fs5.mkdirSync(GRAPHS_ROOT2, { recursive: true });
|
|
4286
3313
|
}
|
|
4287
3314
|
seedStarterGraphsIfEmpty();
|
|
4288
3315
|
}
|
|
4289
3316
|
function seedStarterGraphsIfEmpty() {
|
|
4290
|
-
const existing =
|
|
3317
|
+
const existing = fs5.existsSync(GRAPHS_ROOT2) ? fs5.readdirSync(GRAPHS_ROOT2) : [];
|
|
4291
3318
|
if (existing.length > 0) return;
|
|
4292
3319
|
const introId = "intro-motif-starter";
|
|
4293
3320
|
createInitialGraphPackageSync(
|
|
@@ -4556,9 +3583,9 @@ function seedStarterGraphsIfEmpty() {
|
|
|
4556
3583
|
);
|
|
4557
3584
|
}
|
|
4558
3585
|
function createInitialGraphPackageSync(id, name, description, type, bpm, masterVolume, currentBiome, nodes, connections, importedGraphIds = []) {
|
|
4559
|
-
const graphDir =
|
|
4560
|
-
if (!
|
|
4561
|
-
|
|
3586
|
+
const graphDir = path5.join(GRAPHS_ROOT2, id);
|
|
3587
|
+
if (!fs5.existsSync(graphDir)) {
|
|
3588
|
+
fs5.mkdirSync(graphDir, { recursive: true });
|
|
4562
3589
|
}
|
|
4563
3590
|
ensureGraphAssetDirectories(graphDir);
|
|
4564
3591
|
seedStarterGraphAssets(graphDir);
|
|
@@ -4591,29 +3618,29 @@ function createInitialGraphPackageSync(id, name, description, type, bpm, masterV
|
|
|
4591
3618
|
connections
|
|
4592
3619
|
}
|
|
4593
3620
|
};
|
|
4594
|
-
|
|
3621
|
+
fs5.writeFileSync(path5.join(graphDir, "graph.qms"), JSON.stringify(graphPackage, null, 2), "utf-8");
|
|
4595
3622
|
}
|
|
4596
3623
|
async function listAllGraphPackages() {
|
|
4597
3624
|
ensureWorkspaceStructure();
|
|
4598
|
-
const dirs = await
|
|
3625
|
+
const dirs = await fs5.promises.readdir(GRAPHS_ROOT2);
|
|
4599
3626
|
const packages = [];
|
|
4600
3627
|
for (const dir of dirs) {
|
|
4601
|
-
const graphDir =
|
|
4602
|
-
const stat = await
|
|
3628
|
+
const graphDir = path5.join(GRAPHS_ROOT2, dir);
|
|
3629
|
+
const stat = await fs5.promises.stat(graphDir);
|
|
4603
3630
|
if (!stat.isDirectory()) continue;
|
|
4604
|
-
const qmsFile =
|
|
4605
|
-
const legacyProjectFile =
|
|
3631
|
+
const qmsFile = path5.join(graphDir, "graph.qms");
|
|
3632
|
+
const legacyProjectFile = path5.join(graphDir, "project.json");
|
|
4606
3633
|
let packageData = null;
|
|
4607
|
-
if (
|
|
3634
|
+
if (fs5.existsSync(qmsFile)) {
|
|
4608
3635
|
try {
|
|
4609
|
-
const raw = await
|
|
3636
|
+
const raw = await fs5.promises.readFile(qmsFile, "utf-8");
|
|
4610
3637
|
packageData = JSON.parse(raw);
|
|
4611
3638
|
} catch (err) {
|
|
4612
3639
|
console.warn(`Corrupted graph.qms in ${dir}:`, err);
|
|
4613
3640
|
}
|
|
4614
|
-
} else if (
|
|
3641
|
+
} else if (fs5.existsSync(legacyProjectFile)) {
|
|
4615
3642
|
try {
|
|
4616
|
-
const raw = await
|
|
3643
|
+
const raw = await fs5.promises.readFile(legacyProjectFile, "utf-8");
|
|
4617
3644
|
const legacy = JSON.parse(raw);
|
|
4618
3645
|
packageData = {
|
|
4619
3646
|
format: "qms_graph_package",
|
|
@@ -4643,7 +3670,7 @@ async function listAllGraphPackages() {
|
|
|
4643
3670
|
mapIoConfig: legacy.mapIoConfig
|
|
4644
3671
|
}
|
|
4645
3672
|
};
|
|
4646
|
-
await
|
|
3673
|
+
await fs5.promises.writeFile(qmsFile, JSON.stringify(packageData, null, 2), "utf-8");
|
|
4647
3674
|
} catch (err) {
|
|
4648
3675
|
console.warn(`Failed to auto-migrate legacy project ${dir}:`, err);
|
|
4649
3676
|
}
|
|
@@ -4665,13 +3692,13 @@ async function listAllGraphPackages() {
|
|
|
4665
3692
|
}
|
|
4666
3693
|
async function getGraphPackageById(id) {
|
|
4667
3694
|
ensureWorkspaceStructure();
|
|
4668
|
-
const safeId =
|
|
4669
|
-
const graphDir =
|
|
4670
|
-
const qmsFile =
|
|
4671
|
-
if (!
|
|
4672
|
-
const legacyProject =
|
|
4673
|
-
if (
|
|
4674
|
-
const raw2 = await
|
|
3695
|
+
const safeId = path5.basename(id);
|
|
3696
|
+
const graphDir = path5.join(GRAPHS_ROOT2, safeId);
|
|
3697
|
+
const qmsFile = path5.join(graphDir, "graph.qms");
|
|
3698
|
+
if (!fs5.existsSync(qmsFile)) {
|
|
3699
|
+
const legacyProject = path5.join(LEGACY_PROJECTS_ROOT, safeId, "project.json");
|
|
3700
|
+
if (fs5.existsSync(legacyProject)) {
|
|
3701
|
+
const raw2 = await fs5.promises.readFile(legacyProject, "utf-8");
|
|
4675
3702
|
const legacy = JSON.parse(raw2);
|
|
4676
3703
|
return {
|
|
4677
3704
|
format: "qms_graph_package",
|
|
@@ -4704,7 +3731,7 @@ async function getGraphPackageById(id) {
|
|
|
4704
3731
|
}
|
|
4705
3732
|
return null;
|
|
4706
3733
|
}
|
|
4707
|
-
const raw = await
|
|
3734
|
+
const raw = await fs5.promises.readFile(qmsFile, "utf-8");
|
|
4708
3735
|
return JSON.parse(raw);
|
|
4709
3736
|
}
|
|
4710
3737
|
async function createGraphPackage(params) {
|
|
@@ -4712,8 +3739,8 @@ async function createGraphPackage(params) {
|
|
|
4712
3739
|
const name = params.name.trim();
|
|
4713
3740
|
const safeSlug = name.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-");
|
|
4714
3741
|
const id = `${safeSlug}-${Date.now().toString(36)}`;
|
|
4715
|
-
const graphDir =
|
|
4716
|
-
await
|
|
3742
|
+
const graphDir = path5.join(GRAPHS_ROOT2, id);
|
|
3743
|
+
await fs5.promises.mkdir(graphDir, { recursive: true });
|
|
4717
3744
|
await ensureGraphAssetDirectories(graphDir);
|
|
4718
3745
|
seedStarterGraphAssets(graphDir);
|
|
4719
3746
|
const now = Date.now();
|
|
@@ -4748,8 +3775,8 @@ async function createGraphPackage(params) {
|
|
|
4748
3775
|
connections: params.connections || []
|
|
4749
3776
|
}
|
|
4750
3777
|
};
|
|
4751
|
-
await
|
|
4752
|
-
|
|
3778
|
+
await fs5.promises.writeFile(
|
|
3779
|
+
path5.join(graphDir, "graph.qms"),
|
|
4753
3780
|
JSON.stringify(graphPackage, null, 2),
|
|
4754
3781
|
"utf-8"
|
|
4755
3782
|
);
|
|
@@ -4757,10 +3784,10 @@ async function createGraphPackage(params) {
|
|
|
4757
3784
|
}
|
|
4758
3785
|
async function saveGraphPackage(id, payload) {
|
|
4759
3786
|
ensureWorkspaceStructure();
|
|
4760
|
-
const safeId =
|
|
4761
|
-
const graphDir =
|
|
4762
|
-
if (!
|
|
4763
|
-
await
|
|
3787
|
+
const safeId = path5.basename(id);
|
|
3788
|
+
const graphDir = path5.join(GRAPHS_ROOT2, safeId);
|
|
3789
|
+
if (!fs5.existsSync(graphDir)) {
|
|
3790
|
+
await fs5.promises.mkdir(graphDir, { recursive: true });
|
|
4764
3791
|
await ensureGraphAssetDirectories(graphDir);
|
|
4765
3792
|
}
|
|
4766
3793
|
const existing = await getGraphPackageById(safeId);
|
|
@@ -4795,18 +3822,18 @@ async function saveGraphPackage(id, payload) {
|
|
|
4795
3822
|
mapIoConfig: payload.mapIoConfig || existing?.graph?.mapIoConfig
|
|
4796
3823
|
}
|
|
4797
3824
|
};
|
|
4798
|
-
await
|
|
4799
|
-
|
|
3825
|
+
await fs5.promises.writeFile(
|
|
3826
|
+
path5.join(graphDir, "graph.qms"),
|
|
4800
3827
|
JSON.stringify(graphPackage, null, 2),
|
|
4801
3828
|
"utf-8"
|
|
4802
3829
|
);
|
|
4803
3830
|
return graphPackage;
|
|
4804
3831
|
}
|
|
4805
3832
|
async function deleteGraphPackage(id) {
|
|
4806
|
-
const safeId =
|
|
4807
|
-
const graphDir =
|
|
4808
|
-
if (!
|
|
4809
|
-
await
|
|
3833
|
+
const safeId = path5.basename(id);
|
|
3834
|
+
const graphDir = path5.join(GRAPHS_ROOT2, safeId);
|
|
3835
|
+
if (!fs5.existsSync(graphDir)) return false;
|
|
3836
|
+
await fs5.promises.rm(graphDir, { recursive: true, force: true });
|
|
4810
3837
|
return true;
|
|
4811
3838
|
}
|
|
4812
3839
|
async function importGraphDependency(targetGraphId, candidateImportId) {
|
|
@@ -4848,26 +3875,26 @@ async function removeGraphDependency(targetGraphId, importIdToRemove) {
|
|
|
4848
3875
|
}
|
|
4849
3876
|
|
|
4850
3877
|
// src/server/services/projectService.ts
|
|
4851
|
-
import
|
|
4852
|
-
import
|
|
4853
|
-
var PROJECTS_ROOT2 =
|
|
3878
|
+
import fs6 from "fs";
|
|
3879
|
+
import path6 from "path";
|
|
3880
|
+
var PROJECTS_ROOT2 = path6.join(process.cwd(), "projects");
|
|
4854
3881
|
var customStarterProjectTemplate = null;
|
|
4855
3882
|
function configureStarterProjectTemplate(template) {
|
|
4856
3883
|
customStarterProjectTemplate = template;
|
|
4857
3884
|
}
|
|
4858
3885
|
function ensureProjectsRoot(starterTemplate) {
|
|
4859
|
-
if (!
|
|
4860
|
-
|
|
3886
|
+
if (!fs6.existsSync(PROJECTS_ROOT2)) {
|
|
3887
|
+
fs6.mkdirSync(PROJECTS_ROOT2, { recursive: true });
|
|
4861
3888
|
}
|
|
4862
3889
|
const template = starterTemplate || customStarterProjectTemplate;
|
|
4863
3890
|
if (template && template.id) {
|
|
4864
|
-
const defaultProjDir =
|
|
4865
|
-
if (!
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
3891
|
+
const defaultProjDir = path6.join(PROJECTS_ROOT2, String(template.id));
|
|
3892
|
+
if (!fs6.existsSync(defaultProjDir)) {
|
|
3893
|
+
fs6.mkdirSync(defaultProjDir, { recursive: true });
|
|
3894
|
+
fs6.mkdirSync(path6.join(defaultProjDir, "samples"), { recursive: true });
|
|
3895
|
+
fs6.mkdirSync(path6.join(defaultProjDir, "assets"), { recursive: true });
|
|
3896
|
+
fs6.writeFileSync(
|
|
3897
|
+
path6.join(defaultProjDir, "project.json"),
|
|
4871
3898
|
JSON.stringify({ ...template, createdAt: Date.now(), updatedAt: Date.now() }, null, 2),
|
|
4872
3899
|
"utf-8"
|
|
4873
3900
|
);
|
|
@@ -4876,22 +3903,22 @@ function ensureProjectsRoot(starterTemplate) {
|
|
|
4876
3903
|
}
|
|
4877
3904
|
async function listAllProjects() {
|
|
4878
3905
|
ensureProjectsRoot();
|
|
4879
|
-
const dirs = await
|
|
3906
|
+
const dirs = await fs6.promises.readdir(PROJECTS_ROOT2);
|
|
4880
3907
|
const projects = [];
|
|
4881
3908
|
for (const dir of dirs) {
|
|
4882
|
-
const projDir =
|
|
4883
|
-
const stat = await
|
|
3909
|
+
const projDir = path6.join(PROJECTS_ROOT2, dir);
|
|
3910
|
+
const stat = await fs6.promises.stat(projDir);
|
|
4884
3911
|
if (!stat.isDirectory()) continue;
|
|
4885
|
-
const projectFile =
|
|
4886
|
-
const samplesDir =
|
|
3912
|
+
const projectFile = path6.join(projDir, "project.json");
|
|
3913
|
+
const samplesDir = path6.join(projDir, "samples");
|
|
4887
3914
|
let sampleCount = 0;
|
|
4888
|
-
if (
|
|
4889
|
-
const samples = await
|
|
3915
|
+
if (fs6.existsSync(samplesDir)) {
|
|
3916
|
+
const samples = await fs6.promises.readdir(samplesDir);
|
|
4890
3917
|
sampleCount = samples.filter((f) => !f.startsWith(".")).length;
|
|
4891
3918
|
}
|
|
4892
|
-
if (
|
|
3919
|
+
if (fs6.existsSync(projectFile)) {
|
|
4893
3920
|
try {
|
|
4894
|
-
const raw = await
|
|
3921
|
+
const raw = await fs6.promises.readFile(projectFile, "utf-8");
|
|
4895
3922
|
const data = JSON.parse(raw);
|
|
4896
3923
|
projects.push({
|
|
4897
3924
|
id: data.id || dir,
|
|
@@ -4911,24 +3938,24 @@ async function listAllProjects() {
|
|
|
4911
3938
|
return projects.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
4912
3939
|
}
|
|
4913
3940
|
async function getProjectById(id) {
|
|
4914
|
-
const safeId =
|
|
4915
|
-
const projectFile =
|
|
4916
|
-
if (!
|
|
4917
|
-
const raw = await
|
|
3941
|
+
const safeId = path6.basename(id);
|
|
3942
|
+
const projectFile = path6.join(PROJECTS_ROOT2, safeId, "project.json");
|
|
3943
|
+
if (!fs6.existsSync(projectFile)) return null;
|
|
3944
|
+
const raw = await fs6.promises.readFile(projectFile, "utf-8");
|
|
4918
3945
|
return JSON.parse(raw);
|
|
4919
3946
|
}
|
|
4920
3947
|
async function saveProject(id, payload) {
|
|
4921
|
-
const safeId =
|
|
4922
|
-
const projDir =
|
|
4923
|
-
const projectFile =
|
|
4924
|
-
if (!
|
|
4925
|
-
await
|
|
4926
|
-
await
|
|
4927
|
-
await
|
|
3948
|
+
const safeId = path6.basename(id);
|
|
3949
|
+
const projDir = path6.join(PROJECTS_ROOT2, safeId);
|
|
3950
|
+
const projectFile = path6.join(projDir, "project.json");
|
|
3951
|
+
if (!fs6.existsSync(projDir)) {
|
|
3952
|
+
await fs6.promises.mkdir(projDir, { recursive: true });
|
|
3953
|
+
await fs6.promises.mkdir(path6.join(projDir, "samples"), { recursive: true });
|
|
3954
|
+
await fs6.promises.mkdir(path6.join(projDir, "assets"), { recursive: true });
|
|
4928
3955
|
}
|
|
4929
3956
|
let existingData = {};
|
|
4930
|
-
if (
|
|
4931
|
-
const raw = await
|
|
3957
|
+
if (fs6.existsSync(projectFile)) {
|
|
3958
|
+
const raw = await fs6.promises.readFile(projectFile, "utf-8");
|
|
4932
3959
|
existingData = JSON.parse(raw);
|
|
4933
3960
|
}
|
|
4934
3961
|
const updatedData = {
|
|
@@ -4937,22 +3964,22 @@ async function saveProject(id, payload) {
|
|
|
4937
3964
|
id: safeId,
|
|
4938
3965
|
updatedAt: Date.now()
|
|
4939
3966
|
};
|
|
4940
|
-
await
|
|
3967
|
+
await fs6.promises.writeFile(projectFile, JSON.stringify(updatedData, null, 2), "utf-8");
|
|
4941
3968
|
return updatedData;
|
|
4942
3969
|
}
|
|
4943
3970
|
async function deleteProject(id) {
|
|
4944
|
-
const safeId =
|
|
4945
|
-
const projDir =
|
|
4946
|
-
if (!
|
|
4947
|
-
await
|
|
3971
|
+
const safeId = path6.basename(id);
|
|
3972
|
+
const projDir = path6.join(PROJECTS_ROOT2, safeId);
|
|
3973
|
+
if (!fs6.existsSync(projDir)) return false;
|
|
3974
|
+
await fs6.promises.rm(projDir, { recursive: true, force: true });
|
|
4948
3975
|
return true;
|
|
4949
3976
|
}
|
|
4950
3977
|
|
|
4951
3978
|
// src/server/services/sampleService.ts
|
|
4952
|
-
import
|
|
4953
|
-
import
|
|
4954
|
-
var SAMPLES_GLOBAL_ROOT =
|
|
4955
|
-
var PROJECTS_ROOT3 =
|
|
3979
|
+
import fs7 from "fs";
|
|
3980
|
+
import path7 from "path";
|
|
3981
|
+
var SAMPLES_GLOBAL_ROOT = path7.join(process.cwd(), "samples", "global");
|
|
3982
|
+
var PROJECTS_ROOT3 = path7.join(process.cwd(), "projects");
|
|
4956
3983
|
var SUPPORTED_AUDIO_EXTS3 = [".wav", ".mp3", ".ogg", ".flac", ".m4a", ".webm", ".weba", ".aac", ".aiff", ".aif", ".opus", ".mp4"];
|
|
4957
3984
|
function initGlobalSamplesDirectory() {
|
|
4958
3985
|
ensureGlobalSamples(SAMPLES_GLOBAL_ROOT).catch((err) => {
|
|
@@ -4960,17 +3987,17 @@ function initGlobalSamplesDirectory() {
|
|
|
4960
3987
|
});
|
|
4961
3988
|
}
|
|
4962
3989
|
async function listGlobalSamples() {
|
|
4963
|
-
if (!
|
|
4964
|
-
await
|
|
3990
|
+
if (!fs7.existsSync(SAMPLES_GLOBAL_ROOT)) {
|
|
3991
|
+
await fs7.promises.mkdir(SAMPLES_GLOBAL_ROOT, { recursive: true });
|
|
4965
3992
|
}
|
|
4966
|
-
const files = await
|
|
3993
|
+
const files = await fs7.promises.readdir(SAMPLES_GLOBAL_ROOT);
|
|
4967
3994
|
const samples = [];
|
|
4968
3995
|
for (const file of files) {
|
|
4969
|
-
const ext =
|
|
3996
|
+
const ext = path7.extname(file).toLowerCase();
|
|
4970
3997
|
if (!SUPPORTED_AUDIO_EXTS3.includes(ext)) continue;
|
|
4971
|
-
const filePath =
|
|
4972
|
-
const stat = await
|
|
4973
|
-
const baseName =
|
|
3998
|
+
const filePath = path7.join(SAMPLES_GLOBAL_ROOT, file);
|
|
3999
|
+
const stat = await fs7.promises.stat(filePath);
|
|
4000
|
+
const baseName = path7.basename(file, ext);
|
|
4974
4001
|
const cleanName = baseName.replace(/[-_]/g, " ");
|
|
4975
4002
|
let category = "synth";
|
|
4976
4003
|
if (/808|kick|snare|hihat|drum|perc/i.test(baseName)) category = "drums";
|
|
@@ -4990,15 +4017,15 @@ async function listGlobalSamples() {
|
|
|
4990
4017
|
return samples;
|
|
4991
4018
|
}
|
|
4992
4019
|
async function saveGlobalSample(filename, base64Content, category) {
|
|
4993
|
-
const safeFilename =
|
|
4994
|
-
const ext =
|
|
4995
|
-
const filePath =
|
|
4020
|
+
const safeFilename = path7.basename(filename).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4021
|
+
const ext = path7.extname(safeFilename).toLowerCase() || ".wav";
|
|
4022
|
+
const filePath = path7.join(SAMPLES_GLOBAL_ROOT, safeFilename);
|
|
4996
4023
|
const buffer = Buffer.from(base64Content, "base64");
|
|
4997
|
-
await
|
|
4998
|
-
const stat = await
|
|
4024
|
+
await fs7.promises.writeFile(filePath, buffer);
|
|
4025
|
+
const stat = await fs7.promises.stat(filePath);
|
|
4999
4026
|
return {
|
|
5000
4027
|
filename: safeFilename,
|
|
5001
|
-
name:
|
|
4028
|
+
name: path7.basename(safeFilename, ext).replace(/[-_]/g, " "),
|
|
5002
4029
|
category: category || "custom",
|
|
5003
4030
|
format: ext.replace(".", "").toUpperCase(),
|
|
5004
4031
|
url: `/samples/global/${safeFilename}`,
|
|
@@ -5008,28 +4035,28 @@ async function saveGlobalSample(filename, base64Content, category) {
|
|
|
5008
4035
|
};
|
|
5009
4036
|
}
|
|
5010
4037
|
async function deleteGlobalSample(filename) {
|
|
5011
|
-
const safeFilename =
|
|
5012
|
-
const filePath =
|
|
5013
|
-
if (
|
|
5014
|
-
await
|
|
4038
|
+
const safeFilename = path7.basename(filename);
|
|
4039
|
+
const filePath = path7.join(SAMPLES_GLOBAL_ROOT, safeFilename);
|
|
4040
|
+
if (fs7.existsSync(filePath)) {
|
|
4041
|
+
await fs7.promises.unlink(filePath);
|
|
5015
4042
|
return true;
|
|
5016
4043
|
}
|
|
5017
4044
|
return false;
|
|
5018
4045
|
}
|
|
5019
4046
|
async function listProjectSamples(projectId) {
|
|
5020
|
-
const safeId =
|
|
5021
|
-
const samplesDir =
|
|
5022
|
-
if (!
|
|
5023
|
-
await
|
|
4047
|
+
const safeId = path7.basename(projectId);
|
|
4048
|
+
const samplesDir = path7.join(PROJECTS_ROOT3, safeId, "samples");
|
|
4049
|
+
if (!fs7.existsSync(samplesDir)) {
|
|
4050
|
+
await fs7.promises.mkdir(samplesDir, { recursive: true });
|
|
5024
4051
|
}
|
|
5025
|
-
const files = await
|
|
4052
|
+
const files = await fs7.promises.readdir(samplesDir);
|
|
5026
4053
|
const samples = [];
|
|
5027
4054
|
for (const file of files) {
|
|
5028
|
-
const ext =
|
|
4055
|
+
const ext = path7.extname(file).toLowerCase();
|
|
5029
4056
|
if (!SUPPORTED_AUDIO_EXTS3.includes(ext)) continue;
|
|
5030
|
-
const filePath =
|
|
5031
|
-
const stat = await
|
|
5032
|
-
const baseName =
|
|
4057
|
+
const filePath = path7.join(samplesDir, file);
|
|
4058
|
+
const stat = await fs7.promises.stat(filePath);
|
|
4059
|
+
const baseName = path7.basename(file, ext);
|
|
5033
4060
|
samples.push({
|
|
5034
4061
|
filename: file,
|
|
5035
4062
|
name: baseName.replace(/[-_]/g, " "),
|
|
@@ -5045,21 +4072,21 @@ async function listProjectSamples(projectId) {
|
|
|
5045
4072
|
return samples;
|
|
5046
4073
|
}
|
|
5047
4074
|
async function saveProjectSample(projectId, filename, base64Content, isRecording) {
|
|
5048
|
-
const safeId =
|
|
5049
|
-
const samplesDir =
|
|
5050
|
-
if (!
|
|
5051
|
-
await
|
|
4075
|
+
const safeId = path7.basename(projectId);
|
|
4076
|
+
const samplesDir = path7.join(PROJECTS_ROOT3, safeId, "samples");
|
|
4077
|
+
if (!fs7.existsSync(samplesDir)) {
|
|
4078
|
+
await fs7.promises.mkdir(samplesDir, { recursive: true });
|
|
5052
4079
|
}
|
|
5053
4080
|
const defaultName = isRecording ? `recording_take_${Date.now()}.wav` : `clip_${Date.now()}.wav`;
|
|
5054
|
-
const targetFilename = filename ?
|
|
5055
|
-
const ext =
|
|
5056
|
-
const filePath =
|
|
4081
|
+
const targetFilename = filename ? path7.basename(filename).replace(/[^a-zA-Z0-9._-]/g, "_") : defaultName;
|
|
4082
|
+
const ext = path7.extname(targetFilename).toLowerCase() || ".wav";
|
|
4083
|
+
const filePath = path7.join(samplesDir, targetFilename);
|
|
5057
4084
|
const buffer = Buffer.from(base64Content, "base64");
|
|
5058
|
-
await
|
|
5059
|
-
const stat = await
|
|
4085
|
+
await fs7.promises.writeFile(filePath, buffer);
|
|
4086
|
+
const stat = await fs7.promises.stat(filePath);
|
|
5060
4087
|
return {
|
|
5061
4088
|
filename: targetFilename,
|
|
5062
|
-
name:
|
|
4089
|
+
name: path7.basename(targetFilename, ext).replace(/[-_]/g, " "),
|
|
5063
4090
|
category: isRecording ? "recording" : "import",
|
|
5064
4091
|
format: ext.replace(".", "").toUpperCase(),
|
|
5065
4092
|
url: `/projects/${safeId}/samples/${targetFilename}`,
|
|
@@ -5070,26 +4097,26 @@ async function saveProjectSample(projectId, filename, base64Content, isRecording
|
|
|
5070
4097
|
};
|
|
5071
4098
|
}
|
|
5072
4099
|
async function deleteProjectSample(projectId, filename) {
|
|
5073
|
-
const safeId =
|
|
5074
|
-
const safeFilename =
|
|
5075
|
-
const filePath =
|
|
5076
|
-
if (
|
|
5077
|
-
await
|
|
4100
|
+
const safeId = path7.basename(projectId);
|
|
4101
|
+
const safeFilename = path7.basename(filename);
|
|
4102
|
+
const filePath = path7.join(PROJECTS_ROOT3, safeId, "samples", safeFilename);
|
|
4103
|
+
if (fs7.existsSync(filePath)) {
|
|
4104
|
+
await fs7.promises.unlink(filePath);
|
|
5078
4105
|
return true;
|
|
5079
4106
|
}
|
|
5080
4107
|
return false;
|
|
5081
4108
|
}
|
|
5082
4109
|
|
|
5083
4110
|
// src/server/services/workspaceGraphService.ts
|
|
5084
|
-
import
|
|
5085
|
-
import
|
|
5086
|
-
var AUTH_FILE_PATH =
|
|
4111
|
+
import fs8 from "fs";
|
|
4112
|
+
import path8 from "path";
|
|
4113
|
+
var AUTH_FILE_PATH = path8.resolve(".skipo/accounts.json");
|
|
5087
4114
|
async function resolveWorkspaceDir(workspaceIdOrSlug) {
|
|
5088
4115
|
const workingDir = getWorkingWorkspaceDir();
|
|
5089
4116
|
let targetWs = null;
|
|
5090
4117
|
try {
|
|
5091
|
-
if (
|
|
5092
|
-
const raw = await
|
|
4118
|
+
if (fs8.existsSync(AUTH_FILE_PATH)) {
|
|
4119
|
+
const raw = await fs8.promises.readFile(AUTH_FILE_PATH, "utf-8");
|
|
5093
4120
|
const accounts = JSON.parse(raw);
|
|
5094
4121
|
for (const user of Object.values(accounts)) {
|
|
5095
4122
|
if (Array.isArray(user.workspaces)) {
|
|
@@ -5109,35 +4136,35 @@ async function resolveWorkspaceDir(workspaceIdOrSlug) {
|
|
|
5109
4136
|
if (targetWs) {
|
|
5110
4137
|
const rawFolderName = targetWs.localFolderName || targetWs.name.replace(/[/\\?%*:|"<>]/g, "_");
|
|
5111
4138
|
const folderName = rawFolderName.trim();
|
|
5112
|
-
await
|
|
4139
|
+
await fs8.promises.mkdir(workingDir, { recursive: true });
|
|
5113
4140
|
return { wsDir: workingDir, wsName: targetWs.name, wsFolder: folderName, wsId: targetWs.id };
|
|
5114
4141
|
}
|
|
5115
|
-
await
|
|
4142
|
+
await fs8.promises.mkdir(workingDir, { recursive: true });
|
|
5116
4143
|
return { wsDir: workingDir, wsName: "Active Workspace", wsFolder: "workspace", wsId: workspaceIdOrSlug || "default" };
|
|
5117
4144
|
}
|
|
5118
4145
|
async function listWorkspaceGraphs(workspaceIdOrSlug) {
|
|
5119
4146
|
const { wsDir, wsFolder } = await resolveWorkspaceDir(workspaceIdOrSlug);
|
|
5120
4147
|
const graphs = [];
|
|
5121
4148
|
const inspectDirForGraph = async (dirPath, folderName) => {
|
|
5122
|
-
const projectJson =
|
|
5123
|
-
const graphQms =
|
|
4149
|
+
const projectJson = path8.join(dirPath, "project.json");
|
|
4150
|
+
const graphQms = path8.join(dirPath, "graph.qms");
|
|
5124
4151
|
let data = null;
|
|
5125
|
-
if (
|
|
4152
|
+
if (fs8.existsSync(projectJson)) {
|
|
5126
4153
|
try {
|
|
5127
|
-
const raw = await
|
|
4154
|
+
const raw = await fs8.promises.readFile(projectJson, "utf-8");
|
|
5128
4155
|
data = JSON.parse(raw);
|
|
5129
4156
|
} catch {
|
|
5130
4157
|
}
|
|
5131
|
-
} else if (
|
|
4158
|
+
} else if (fs8.existsSync(graphQms)) {
|
|
5132
4159
|
try {
|
|
5133
|
-
const raw = await
|
|
4160
|
+
const raw = await fs8.promises.readFile(graphQms, "utf-8");
|
|
5134
4161
|
const pkg = JSON.parse(raw);
|
|
5135
4162
|
data = { ...pkg.metadata || {}, ...pkg.graph || {} };
|
|
5136
4163
|
} catch {
|
|
5137
4164
|
}
|
|
5138
4165
|
}
|
|
5139
4166
|
if (!data) return null;
|
|
5140
|
-
const stat = await
|
|
4167
|
+
const stat = await fs8.promises.stat(dirPath);
|
|
5141
4168
|
const nodeCount = Array.isArray(data.nodes) ? data.nodes.length : 0;
|
|
5142
4169
|
const connectionCount = Array.isArray(data.connections) ? data.connections.length : 0;
|
|
5143
4170
|
return {
|
|
@@ -5145,7 +4172,7 @@ async function listWorkspaceGraphs(workspaceIdOrSlug) {
|
|
|
5145
4172
|
name: data.name || folderName,
|
|
5146
4173
|
description: data.description || "",
|
|
5147
4174
|
folderName,
|
|
5148
|
-
folderPath:
|
|
4175
|
+
folderPath: path8.relative(process.cwd(), dirPath).replace(/\\/g, "/"),
|
|
5149
4176
|
createdAt: data.createdAt || stat.birthtimeMs,
|
|
5150
4177
|
updatedAt: data.updatedAt || stat.mtimeMs,
|
|
5151
4178
|
bpm: data.bpm || 120,
|
|
@@ -5158,18 +4185,18 @@ async function listWorkspaceGraphs(workspaceIdOrSlug) {
|
|
|
5158
4185
|
connectionCount
|
|
5159
4186
|
};
|
|
5160
4187
|
};
|
|
5161
|
-
const entries = await
|
|
4188
|
+
const entries = await fs8.promises.readdir(wsDir);
|
|
5162
4189
|
for (const entry of entries) {
|
|
5163
4190
|
if (entry.startsWith(".") || entry === "samples" || entry === "assets") continue;
|
|
5164
|
-
const fullPath =
|
|
5165
|
-
const stat = await
|
|
4191
|
+
const fullPath = path8.join(wsDir, entry);
|
|
4192
|
+
const stat = await fs8.promises.stat(fullPath);
|
|
5166
4193
|
if (!stat.isDirectory()) continue;
|
|
5167
4194
|
if (entry === "graphs") {
|
|
5168
|
-
const subEntries = await
|
|
4195
|
+
const subEntries = await fs8.promises.readdir(fullPath);
|
|
5169
4196
|
for (const sub of subEntries) {
|
|
5170
4197
|
if (sub.startsWith(".")) continue;
|
|
5171
|
-
const subPath =
|
|
5172
|
-
const subStat = await
|
|
4198
|
+
const subPath = path8.join(fullPath, sub);
|
|
4199
|
+
const subStat = await fs8.promises.stat(subPath);
|
|
5173
4200
|
if (subStat.isDirectory()) {
|
|
5174
4201
|
const g2 = await inspectDirForGraph(subPath, sub);
|
|
5175
4202
|
if (g2) graphs.push(g2);
|
|
@@ -5186,10 +4213,10 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
|
|
|
5186
4213
|
const { wsDir, wsFolder, wsId } = await resolveWorkspaceDir(workspaceIdOrSlug);
|
|
5187
4214
|
const rawName = params.name.trim();
|
|
5188
4215
|
const folderName = rawName.replace(/[/\\?%*:|"<>]/g, "_");
|
|
5189
|
-
const graphDir =
|
|
5190
|
-
await
|
|
5191
|
-
await
|
|
5192
|
-
await
|
|
4216
|
+
const graphDir = path8.join(wsDir, folderName);
|
|
4217
|
+
await fs8.promises.mkdir(graphDir, { recursive: true });
|
|
4218
|
+
await fs8.promises.mkdir(path8.join(graphDir, "samples"), { recursive: true });
|
|
4219
|
+
await fs8.promises.mkdir(path8.join(graphDir, "assets"), { recursive: true });
|
|
5193
4220
|
const now = Date.now();
|
|
5194
4221
|
const nodes = params.templateData?.nodes || [];
|
|
5195
4222
|
const connections = params.templateData?.connections || [];
|
|
@@ -5200,7 +4227,7 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
|
|
|
5200
4227
|
id: folderName,
|
|
5201
4228
|
name: rawName,
|
|
5202
4229
|
description: params.description || `Workspace graph in ${wsFolder}`,
|
|
5203
|
-
folderPath:
|
|
4230
|
+
folderPath: path8.relative(process.cwd(), graphDir).replace(/\\/g, "/"),
|
|
5204
4231
|
createdAt: now,
|
|
5205
4232
|
updatedAt: now,
|
|
5206
4233
|
bpm,
|
|
@@ -5209,7 +4236,7 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
|
|
|
5209
4236
|
nodes,
|
|
5210
4237
|
connections
|
|
5211
4238
|
};
|
|
5212
|
-
await
|
|
4239
|
+
await fs8.promises.writeFile(path8.join(graphDir, "project.json"), JSON.stringify(projectData, null, 2), "utf-8");
|
|
5213
4240
|
const graphPackage = {
|
|
5214
4241
|
format: "qms_graph_package",
|
|
5215
4242
|
version: "2.0.0",
|
|
@@ -5227,27 +4254,27 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
|
|
|
5227
4254
|
},
|
|
5228
4255
|
graph: { bpm, masterVolume, currentBiome, nodes, connections }
|
|
5229
4256
|
};
|
|
5230
|
-
await
|
|
4257
|
+
await fs8.promises.writeFile(path8.join(graphDir, "graph.qms"), JSON.stringify(graphPackage, null, 2), "utf-8");
|
|
5231
4258
|
return projectData;
|
|
5232
4259
|
}
|
|
5233
4260
|
async function getWorkspaceGraph(workspaceIdOrSlug, graphIdOrName) {
|
|
5234
4261
|
const { wsDir } = await resolveWorkspaceDir(workspaceIdOrSlug);
|
|
5235
4262
|
const safeTarget = graphIdOrName.replace(/[/\\?%*:|"<>]/g, "_");
|
|
5236
4263
|
const candidates = [
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
4264
|
+
path8.join(wsDir, safeTarget),
|
|
4265
|
+
path8.join(wsDir, graphIdOrName),
|
|
4266
|
+
path8.join(wsDir, "graphs", safeTarget)
|
|
5240
4267
|
];
|
|
5241
4268
|
for (const cand of candidates) {
|
|
5242
|
-
if (
|
|
5243
|
-
const pJson =
|
|
5244
|
-
if (
|
|
5245
|
-
const raw = await
|
|
4269
|
+
if (fs8.existsSync(cand)) {
|
|
4270
|
+
const pJson = path8.join(cand, "project.json");
|
|
4271
|
+
if (fs8.existsSync(pJson)) {
|
|
4272
|
+
const raw = await fs8.promises.readFile(pJson, "utf-8");
|
|
5246
4273
|
return JSON.parse(raw);
|
|
5247
4274
|
}
|
|
5248
|
-
const gQms =
|
|
5249
|
-
if (
|
|
5250
|
-
const raw = await
|
|
4275
|
+
const gQms = path8.join(cand, "graph.qms");
|
|
4276
|
+
if (fs8.existsSync(gQms)) {
|
|
4277
|
+
const raw = await fs8.promises.readFile(gQms, "utf-8");
|
|
5251
4278
|
const pkg = JSON.parse(raw);
|
|
5252
4279
|
return { ...pkg.metadata || {}, ...pkg.graph || {} };
|
|
5253
4280
|
}
|
|
@@ -5258,39 +4285,39 @@ async function getWorkspaceGraph(workspaceIdOrSlug, graphIdOrName) {
|
|
|
5258
4285
|
async function saveWorkspaceGraph(workspaceIdOrSlug, graphIdOrName, payload) {
|
|
5259
4286
|
const { wsDir } = await resolveWorkspaceDir(workspaceIdOrSlug);
|
|
5260
4287
|
const safeTarget = graphIdOrName.replace(/[/\\?%*:|"<>]/g, "_");
|
|
5261
|
-
let targetDir =
|
|
5262
|
-
if (!
|
|
5263
|
-
targetDir =
|
|
4288
|
+
let targetDir = path8.join(wsDir, safeTarget);
|
|
4289
|
+
if (!fs8.existsSync(targetDir) && fs8.existsSync(path8.join(wsDir, "graphs", safeTarget))) {
|
|
4290
|
+
targetDir = path8.join(wsDir, "graphs", safeTarget);
|
|
5264
4291
|
}
|
|
5265
|
-
await
|
|
5266
|
-
const pJson =
|
|
4292
|
+
await fs8.promises.mkdir(targetDir, { recursive: true });
|
|
4293
|
+
const pJson = path8.join(targetDir, "project.json");
|
|
5267
4294
|
let existing = {};
|
|
5268
|
-
if (
|
|
4295
|
+
if (fs8.existsSync(pJson)) {
|
|
5269
4296
|
try {
|
|
5270
|
-
existing = JSON.parse(await
|
|
4297
|
+
existing = JSON.parse(await fs8.promises.readFile(pJson, "utf-8"));
|
|
5271
4298
|
} catch {
|
|
5272
4299
|
}
|
|
5273
4300
|
}
|
|
5274
4301
|
const updated = {
|
|
5275
4302
|
...existing,
|
|
5276
4303
|
...payload,
|
|
5277
|
-
id: existing.id ||
|
|
4304
|
+
id: existing.id || path8.basename(targetDir),
|
|
5278
4305
|
updatedAt: Date.now()
|
|
5279
4306
|
};
|
|
5280
|
-
await
|
|
4307
|
+
await fs8.promises.writeFile(pJson, JSON.stringify(updated, null, 2), "utf-8");
|
|
5281
4308
|
return updated;
|
|
5282
4309
|
}
|
|
5283
4310
|
async function deleteWorkspaceGraph(workspaceIdOrSlug, graphIdOrName) {
|
|
5284
4311
|
const { wsDir } = await resolveWorkspaceDir(workspaceIdOrSlug);
|
|
5285
4312
|
const safeTarget = graphIdOrName.replace(/[/\\?%*:|"<>]/g, "_");
|
|
5286
4313
|
const candidates = [
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
4314
|
+
path8.join(wsDir, safeTarget),
|
|
4315
|
+
path8.join(wsDir, graphIdOrName),
|
|
4316
|
+
path8.join(wsDir, "graphs", safeTarget)
|
|
5290
4317
|
];
|
|
5291
4318
|
for (const cand of candidates) {
|
|
5292
|
-
if (
|
|
5293
|
-
await
|
|
4319
|
+
if (fs8.existsSync(cand)) {
|
|
4320
|
+
await fs8.promises.rm(cand, { recursive: true, force: true });
|
|
5294
4321
|
return true;
|
|
5295
4322
|
}
|
|
5296
4323
|
}
|
|
@@ -5340,14 +4367,17 @@ export {
|
|
|
5340
4367
|
JobDependencyGraph,
|
|
5341
4368
|
JobScheduler,
|
|
5342
4369
|
LEGACY_PROJECTS_ROOT,
|
|
4370
|
+
LOCAL_STORAGE_GRAPHS_PREFIX,
|
|
5343
4371
|
LoadBalancer,
|
|
5344
4372
|
LocalAccountProvider,
|
|
5345
4373
|
LocalFSAdapter,
|
|
5346
4374
|
LockManager,
|
|
4375
|
+
NodeGraphPersistenceService,
|
|
5347
4376
|
PROJECTS_ROOT,
|
|
5348
4377
|
PasswordHasher,
|
|
5349
4378
|
RateLimiter,
|
|
5350
4379
|
RemoteExecutor,
|
|
4380
|
+
RemoteWorkspaceService,
|
|
5351
4381
|
ResourceFetcherRegistry,
|
|
5352
4382
|
ResultFlowBridge,
|
|
5353
4383
|
S3Adapter,
|
|
@@ -5358,7 +4388,9 @@ export {
|
|
|
5358
4388
|
ServiceDispatcher,
|
|
5359
4389
|
SessionManager,
|
|
5360
4390
|
SrklClient,
|
|
4391
|
+
SrklCredentialRegistry,
|
|
5361
4392
|
SrklStorageService,
|
|
4393
|
+
SrklVaultAccessClient,
|
|
5362
4394
|
StorageAdapterFactory,
|
|
5363
4395
|
StorageServiceDefinition,
|
|
5364
4396
|
StorageServiceFacade,
|
|
@@ -5369,6 +4401,7 @@ export {
|
|
|
5369
4401
|
WORKSPACE_ROOT,
|
|
5370
4402
|
WebSocketTransport,
|
|
5371
4403
|
WorkerExecutionBoundary,
|
|
4404
|
+
WorkingWorkspaceService,
|
|
5372
4405
|
WorkspaceCloneSyncEngine,
|
|
5373
4406
|
WorkspaceFS,
|
|
5374
4407
|
WorkspaceServiceDefinition,
|
|
@@ -5383,6 +4416,8 @@ export {
|
|
|
5383
4416
|
buildProjectObservability,
|
|
5384
4417
|
buildProjectsList,
|
|
5385
4418
|
cleanDbUrl,
|
|
4419
|
+
computeDeploymentFinalWeight,
|
|
4420
|
+
computeUsageWeightScore,
|
|
5386
4421
|
configureStarterProjectTemplate,
|
|
5387
4422
|
createAssetSubfolder,
|
|
5388
4423
|
createBackend,
|
|
@@ -5402,6 +4437,7 @@ export {
|
|
|
5402
4437
|
createWorkspace,
|
|
5403
4438
|
createWorkspaceGraph,
|
|
5404
4439
|
decryptSecretFromBlob,
|
|
4440
|
+
decryptVaultBufferInBrowser,
|
|
5405
4441
|
deleteAssetFromGraph,
|
|
5406
4442
|
deleteAssetFromPool,
|
|
5407
4443
|
deleteCredential,
|
|
@@ -5424,6 +4460,7 @@ export {
|
|
|
5424
4460
|
ensureS3DirectoryMarker,
|
|
5425
4461
|
ensureWorkspaceStructure,
|
|
5426
4462
|
exportEncryptedCredentials,
|
|
4463
|
+
exportGraphAsJsonFile,
|
|
5427
4464
|
fetchCloudflareData,
|
|
5428
4465
|
fetchVercelData,
|
|
5429
4466
|
findUserWorkspace,
|
|
@@ -5434,6 +4471,8 @@ export {
|
|
|
5434
4471
|
getAssetPoolLocationPath,
|
|
5435
4472
|
getAssetPoolPublicUrl,
|
|
5436
4473
|
getB2BucketId,
|
|
4474
|
+
getConnectedDeploymentNode,
|
|
4475
|
+
getConnectedWeightModifiers,
|
|
5437
4476
|
getCredential,
|
|
5438
4477
|
getDefaultWorkingWorkspaceDir,
|
|
5439
4478
|
getExecutions,
|
|
@@ -5450,6 +4489,7 @@ export {
|
|
|
5450
4489
|
getSystemDatabaseUrls,
|
|
5451
4490
|
getUnifiedDbUrl,
|
|
5452
4491
|
getUsers,
|
|
4492
|
+
getWeightModifierScore,
|
|
5453
4493
|
getWorkingWorkspaceConfig,
|
|
5454
4494
|
getWorkingWorkspaceDir,
|
|
5455
4495
|
getWorkspaceFolderKey,
|
|
@@ -5457,6 +4497,8 @@ export {
|
|
|
5457
4497
|
getWorkspaceGraph,
|
|
5458
4498
|
getWorkspaceStorageDetails,
|
|
5459
4499
|
getWorkspaces,
|
|
4500
|
+
globalCredentialRegistry,
|
|
4501
|
+
hexToBytes,
|
|
5460
4502
|
importEncryptedCredentials,
|
|
5461
4503
|
importGraphDependency,
|
|
5462
4504
|
initGlobalSamplesDirectory,
|
|
@@ -5476,12 +4518,15 @@ export {
|
|
|
5476
4518
|
loginUser,
|
|
5477
4519
|
matchGatewayIngress,
|
|
5478
4520
|
matchGatewayIngressAsync,
|
|
4521
|
+
nodeGraphPersistence,
|
|
5479
4522
|
normalizeBackblazeCredentials,
|
|
4523
|
+
parseGraphJsonFile,
|
|
5480
4524
|
permanentDeleteS3Folder,
|
|
5481
4525
|
permanentDeleteS3Object,
|
|
5482
4526
|
queryUnifiedDb,
|
|
5483
4527
|
readWorkspaceFile,
|
|
5484
4528
|
recordExecution,
|
|
4529
|
+
remoteWorkspaceService,
|
|
5485
4530
|
removeGraphDependency,
|
|
5486
4531
|
renameAppInWorkspaceData,
|
|
5487
4532
|
renameS3Folder,
|
|
@@ -5510,6 +4555,8 @@ export {
|
|
|
5510
4555
|
setWorkingWorkspaceConfig,
|
|
5511
4556
|
srklListBackblazeBuckets2 as srklCoreListBackblazeBuckets,
|
|
5512
4557
|
srklListBackblazeBuckets,
|
|
4558
|
+
srklService,
|
|
4559
|
+
syncSrklAccounts,
|
|
5513
4560
|
syncWorkspace,
|
|
5514
4561
|
syncWorkspaceCreateToBucket,
|
|
5515
4562
|
syncWorkspaceDataToBucket,
|
|
@@ -5520,8 +4567,10 @@ export {
|
|
|
5520
4567
|
useBackend,
|
|
5521
4568
|
useBackendClient,
|
|
5522
4569
|
useSrklServices,
|
|
4570
|
+
validateGraphDocument,
|
|
5523
4571
|
validateServiceDefinition,
|
|
5524
4572
|
withAuth,
|
|
5525
4573
|
withCors,
|
|
4574
|
+
workingWorkspaceService,
|
|
5526
4575
|
writeWorkspaceFile
|
|
5527
4576
|
};
|