@ultrst-armada/kloks 1.5.2 → 1.7.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/.krusbl-release-attestation.json +3 -3
- package/package.json +1 -1
- package/packages/backend/dist/{chunk-E72GXBXF.js → chunk-6K75XXFD.js} +9 -200
- package/packages/backend/dist/{chunk-GPXV4GBD.js → chunk-6QTNNZSE.js} +7 -5
- package/packages/backend/dist/{chunk-OZDYAEH4.js → chunk-BI5RDDB4.js} +210 -1
- package/packages/backend/dist/{chunk-DBZVGMJ7.js → chunk-KSZ67VZJ.js} +1 -1
- package/packages/backend/dist/{chunk-VDROUXFT.js → chunk-N4Z57FVR.js} +10 -10
- package/packages/backend/dist/{chunk-3Z7EUZWE.js → chunk-SQGBAWZN.js} +238 -6
- package/packages/backend/dist/chunk-VAFCR2MS.js +203 -0
- package/packages/backend/dist/{chunk-GQUNN7SA.js → chunk-Y3X3H2XX.js} +6 -4
- package/packages/backend/dist/development/index.d.ts +100 -1
- package/packages/backend/dist/development/index.js +15 -1
- package/packages/backend/dist/index.d.ts +2 -2
- package/packages/backend/dist/index.js +39 -213
- package/packages/backend/dist/krnl-services.js +5 -4
- package/packages/backend/dist/services/index.js +3 -2
- package/packages/backend/dist/srkl/index.d.ts +48 -1
- package/packages/backend/dist/srkl/index.js +3 -2
- package/packages/backend/dist/workspace/index.js +3 -2
- package/packages/backend/kloks.manifest.json +52 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"packageName": "@ultrst-armada/kloks",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"sourceCommit": "
|
|
4
|
+
"version": "1.7.0",
|
|
5
|
+
"sourceCommit": "b794ff2065167edac2d5c817897b5244e9136952",
|
|
6
6
|
"releaseWorkflow": "npm Release",
|
|
7
|
-
"workflowRunId": "
|
|
7
|
+
"workflowRunId": "40036485979"
|
|
8
8
|
}
|
package/package.json
CHANGED
|
@@ -1,193 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (typeof process !== "undefined" && process.env) {
|
|
11
|
-
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) || "";
|
|
12
|
-
}
|
|
13
|
-
return "";
|
|
14
|
-
}
|
|
15
|
-
async function queryUnifiedDb(sqlText, params = [], env) {
|
|
16
|
-
const dbUrl = getUnifiedDbUrl(env);
|
|
17
|
-
if (!dbUrl) {
|
|
18
|
-
throw new Error("[UnifiedDB] No database URL configured in environment.");
|
|
19
|
-
}
|
|
20
|
-
if (!neon) {
|
|
21
|
-
const mod = await import("@neondatabase/serverless");
|
|
22
|
-
neon = mod.neon;
|
|
23
|
-
}
|
|
24
|
-
const sql = neon(dbUrl);
|
|
25
|
-
try {
|
|
26
|
-
const rows = await (sql.query ? sql.query(sqlText, params) : sql(sqlText, params));
|
|
27
|
-
return {
|
|
28
|
-
rows: Array.isArray(rows) ? rows : [],
|
|
29
|
-
rowCount: Array.isArray(rows) ? rows.length : 0
|
|
30
|
-
};
|
|
31
|
-
} catch (err) {
|
|
32
|
-
console.error("[UnifiedDB] Query execution error:", err.message);
|
|
33
|
-
throw err;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// src/core/b2DirectCore.ts
|
|
38
|
-
var authCache = /* @__PURE__ */ new Map();
|
|
39
|
-
var bucketIdCache = /* @__PURE__ */ new Map();
|
|
40
|
-
async function authorizeB2(keyId, applicationKey) {
|
|
41
|
-
const cacheKey = `${keyId.trim()}:${applicationKey.trim()}`;
|
|
42
|
-
const cached = authCache.get(cacheKey);
|
|
43
|
-
if (cached && Date.now() < cached.expiresAt) return cached.auth;
|
|
44
|
-
const authHeader = typeof Buffer !== "undefined" ? Buffer.from(`${keyId.trim()}:${applicationKey.trim()}`).toString("base64") : btoa(`${keyId.trim()}:${applicationKey.trim()}`);
|
|
45
|
-
let res = await fetch("https://api.backblazeb2.com/b2api/v2/b2_authorize_account", {
|
|
46
|
-
headers: { Authorization: `Basic ${authHeader}` },
|
|
47
|
-
signal: AbortSignal.timeout(1e4)
|
|
48
|
-
});
|
|
49
|
-
if (!res.ok) {
|
|
50
|
-
res = await fetch("https://api.backblazeb2.com/b2api/v4/b2_authorize_account", {
|
|
51
|
-
headers: { Authorization: `Basic ${authHeader}` },
|
|
52
|
-
signal: AbortSignal.timeout(1e4)
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
if (!res.ok) {
|
|
56
|
-
const errText = await res.text();
|
|
57
|
-
throw new Error(`Backblaze B2 authorization failed (${res.status}): ${errText}`);
|
|
58
|
-
}
|
|
59
|
-
const data = await res.json();
|
|
60
|
-
const auth = {
|
|
61
|
-
accountId: data.accountId,
|
|
62
|
-
authorizationToken: data.authorizationToken,
|
|
63
|
-
apiUrl: (data.apiInfo?.storageApi?.apiUrl || data.apiUrl || "https://api.backblazeb2.com").replace(/\/+$/, ""),
|
|
64
|
-
downloadUrl: (data.apiInfo?.storageApi?.downloadUrl || data.downloadUrl || data.apiUrl || "https://f003.backblazeb2.com").replace(/\/+$/, ""),
|
|
65
|
-
s3ApiUrl: data.apiInfo?.storageApi?.s3ApiUrl || data.s3ApiUrl,
|
|
66
|
-
allowed: data.apiInfo?.storageApi?.allowed || data.allowed
|
|
67
|
-
};
|
|
68
|
-
authCache.set(cacheKey, { auth, expiresAt: Date.now() + 20 * 60 * 1e3 });
|
|
69
|
-
return auth;
|
|
70
|
-
}
|
|
71
|
-
async function b2ListBuckets(keyId, applicationKey, bucketId, bucketName) {
|
|
72
|
-
const auth = await authorizeB2(keyId, applicationKey);
|
|
73
|
-
const rBucketId = bucketId || auth.allowed?.bucketId || auth.allowed?.buckets?.[0]?.bucketId;
|
|
74
|
-
const rBucketName = bucketName || auth.allowed?.bucketName || auth.allowed?.buckets?.[0]?.bucketName;
|
|
75
|
-
if (!auth.allowed?.bucketId && !auth.allowed?.bucketName) {
|
|
76
|
-
try {
|
|
77
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_buckets`, {
|
|
78
|
-
method: "POST",
|
|
79
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
80
|
-
body: JSON.stringify({ accountId: auth.accountId }),
|
|
81
|
-
signal: AbortSignal.timeout(8e3)
|
|
82
|
-
});
|
|
83
|
-
if (res.ok) {
|
|
84
|
-
const data = await res.json();
|
|
85
|
-
if (Array.isArray(data.buckets) && data.buckets.length > 0) return data.buckets;
|
|
86
|
-
}
|
|
87
|
-
} catch (err) {
|
|
88
|
-
console.warn("[B2DirectCore] list_buckets query failed:", err.message);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (rBucketId || rBucketName) {
|
|
92
|
-
const body = { accountId: auth.accountId };
|
|
93
|
-
if (rBucketId) body.bucketId = rBucketId;
|
|
94
|
-
else if (rBucketName) body.bucketName = rBucketName;
|
|
95
|
-
try {
|
|
96
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_buckets`, {
|
|
97
|
-
method: "POST",
|
|
98
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
99
|
-
body: JSON.stringify(body),
|
|
100
|
-
signal: AbortSignal.timeout(8e3)
|
|
101
|
-
});
|
|
102
|
-
if (res.ok) {
|
|
103
|
-
const data = await res.json();
|
|
104
|
-
if (Array.isArray(data.buckets) && data.buckets.length > 0) return data.buckets;
|
|
105
|
-
}
|
|
106
|
-
} catch {
|
|
107
|
-
}
|
|
108
|
-
return [{
|
|
109
|
-
accountId: auth.accountId,
|
|
110
|
-
bucketId: rBucketId || "restricted",
|
|
111
|
-
bucketName: rBucketName || (bucketName || "default-bucket"),
|
|
112
|
-
bucketType: "allPrivate"
|
|
113
|
-
}];
|
|
114
|
-
}
|
|
115
|
-
return [];
|
|
116
|
-
}
|
|
117
|
-
async function getB2BucketId(keyId, applicationKey, bucketName) {
|
|
118
|
-
const cacheKey = `${keyId.trim()}:${bucketName.trim()}`;
|
|
119
|
-
if (bucketIdCache.has(cacheKey)) return bucketIdCache.get(cacheKey);
|
|
120
|
-
const buckets = await b2ListBuckets(keyId, applicationKey, void 0, bucketName);
|
|
121
|
-
const match = buckets.find((b) => b.bucketName === bucketName);
|
|
122
|
-
if (!match) throw new Error(`B2 bucket "${bucketName}" not found in account`);
|
|
123
|
-
bucketIdCache.set(cacheKey, match.bucketId);
|
|
124
|
-
return match.bucketId;
|
|
125
|
-
}
|
|
126
|
-
async function b2UploadFile(keyId, applicationKey, bucketName, fileName, buffer, contentType = "b2/x-auto") {
|
|
127
|
-
const auth = await authorizeB2(keyId, applicationKey);
|
|
128
|
-
const bucketId = await getB2BucketId(keyId, applicationKey, bucketName);
|
|
129
|
-
const urlRes = await fetch(`${auth.apiUrl}/b2api/v2/b2_get_upload_url`, {
|
|
130
|
-
method: "POST",
|
|
131
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
132
|
-
body: JSON.stringify({ bucketId })
|
|
133
|
-
});
|
|
134
|
-
if (!urlRes.ok) throw new Error(`B2 get upload url failed (${urlRes.status})`);
|
|
135
|
-
const { uploadUrl, authorizationToken } = await urlRes.json();
|
|
136
|
-
const encodedFileName = encodeURIComponent(fileName).replace(/%2F/g, "/");
|
|
137
|
-
const uploadRes = await fetch(uploadUrl, {
|
|
138
|
-
method: "POST",
|
|
139
|
-
headers: {
|
|
140
|
-
Authorization: authorizationToken,
|
|
141
|
-
"X-Bz-File-Name": encodedFileName,
|
|
142
|
-
"Content-Type": contentType,
|
|
143
|
-
"Content-Length": String(buffer.byteLength),
|
|
144
|
-
"X-Bz-Content-Sha1": "do_not_verify"
|
|
145
|
-
},
|
|
146
|
-
body: buffer
|
|
147
|
-
});
|
|
148
|
-
if (!uploadRes.ok) throw new Error(`B2 upload failed for ${fileName} (${uploadRes.status}): ${await uploadRes.text()}`);
|
|
149
|
-
return uploadRes.json();
|
|
150
|
-
}
|
|
151
|
-
async function b2DownloadFile(keyId, applicationKey, bucketName, fileName) {
|
|
152
|
-
const auth = await authorizeB2(keyId, applicationKey);
|
|
153
|
-
const encodedName = encodeURIComponent(fileName).replace(/%2F/g, "/");
|
|
154
|
-
const res = await fetch(`${auth.downloadUrl}/file/${bucketName}/${encodedName}`, { headers: { Authorization: auth.authorizationToken } });
|
|
155
|
-
if (res.status === 404) return null;
|
|
156
|
-
if (!res.ok) throw new Error(`B2 download failed for ${fileName} (${res.status})`);
|
|
157
|
-
return new Uint8Array(await res.arrayBuffer());
|
|
158
|
-
}
|
|
159
|
-
async function b2ListFileNames(keyId, applicationKey, bucketName, prefix = "") {
|
|
160
|
-
const auth = await authorizeB2(keyId, applicationKey);
|
|
161
|
-
const bucketId = await getB2BucketId(keyId, applicationKey, bucketName);
|
|
162
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_file_names`, {
|
|
163
|
-
method: "POST",
|
|
164
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
165
|
-
body: JSON.stringify({ bucketId, prefix, maxFileCount: 1e3 })
|
|
166
|
-
});
|
|
167
|
-
if (!res.ok) throw new Error(`B2 list files failed (${res.status})`);
|
|
168
|
-
const data = await res.json();
|
|
169
|
-
return data.files || [];
|
|
170
|
-
}
|
|
171
|
-
async function b2DeleteFileVersion(keyId, applicationKey, fileName, fileId) {
|
|
172
|
-
const auth = await authorizeB2(keyId, applicationKey);
|
|
173
|
-
const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_delete_file_version`, {
|
|
174
|
-
method: "POST",
|
|
175
|
-
headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
|
|
176
|
-
body: JSON.stringify({ fileName, fileId })
|
|
177
|
-
});
|
|
178
|
-
if (!res.ok) throw new Error(`B2 delete file version failed (${res.status})`);
|
|
179
|
-
return res.json();
|
|
180
|
-
}
|
|
181
|
-
async function b2DeleteFolder(keyId, applicationKey, bucketName, folderPrefix) {
|
|
182
|
-
const cleanPrefix = folderPrefix.replace(/\/+$/, "") + "/";
|
|
183
|
-
const files = await b2ListFileNames(keyId, applicationKey, bucketName, cleanPrefix);
|
|
184
|
-
let deletedCount = 0;
|
|
185
|
-
for (const file of files) {
|
|
186
|
-
await b2DeleteFileVersion(keyId, applicationKey, file.fileName, file.fileId);
|
|
187
|
-
deletedCount++;
|
|
188
|
-
}
|
|
189
|
-
return { success: true, deletedCount };
|
|
190
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
b2DeleteFileVersion,
|
|
3
|
+
b2DeleteFolder,
|
|
4
|
+
b2DownloadFile,
|
|
5
|
+
b2ListBuckets,
|
|
6
|
+
b2ListFileNames,
|
|
7
|
+
b2UploadFile,
|
|
8
|
+
queryUnifiedDb
|
|
9
|
+
} from "./chunk-VAFCR2MS.js";
|
|
191
10
|
|
|
192
11
|
// src/core/workspaceFileCore.ts
|
|
193
12
|
async function getFs() {
|
|
@@ -718,16 +537,6 @@ async function getWorkspaceStorageDetails(workspaceId, env) {
|
|
|
718
537
|
}
|
|
719
538
|
|
|
720
539
|
export {
|
|
721
|
-
cleanDbUrl,
|
|
722
|
-
getUnifiedDbUrl,
|
|
723
|
-
queryUnifiedDb,
|
|
724
|
-
b2ListBuckets,
|
|
725
|
-
getB2BucketId,
|
|
726
|
-
b2UploadFile,
|
|
727
|
-
b2DownloadFile,
|
|
728
|
-
b2ListFileNames,
|
|
729
|
-
b2DeleteFileVersion,
|
|
730
|
-
b2DeleteFolder,
|
|
731
540
|
getWorkspaceFolderPrefix,
|
|
732
541
|
isB2Workspace,
|
|
733
542
|
writeWorkspaceFile,
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
b2DownloadFile,
|
|
3
|
-
b2ListFileNames,
|
|
4
|
-
b2UploadFile,
|
|
5
2
|
createWorkspace,
|
|
6
3
|
deleteWorkspace,
|
|
7
4
|
deleteWorkspaceFile,
|
|
8
5
|
findUserWorkspace,
|
|
9
6
|
getWorkspaces,
|
|
10
7
|
listWorkspaceFiles,
|
|
11
|
-
queryUnifiedDb,
|
|
12
8
|
readWorkspaceFile,
|
|
13
9
|
updateWorkspace,
|
|
14
10
|
writeWorkspaceFile
|
|
15
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-6K75XXFD.js";
|
|
12
|
+
import {
|
|
13
|
+
b2DownloadFile,
|
|
14
|
+
b2ListFileNames,
|
|
15
|
+
b2UploadFile,
|
|
16
|
+
queryUnifiedDb
|
|
17
|
+
} from "./chunk-VAFCR2MS.js";
|
|
16
18
|
|
|
17
19
|
// src/workspace/WorkspaceFS.ts
|
|
18
20
|
import { SkipoBase } from "@ultrst-armada/kloks/functions";
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
b2ListBuckets,
|
|
3
|
+
queryUnifiedDb
|
|
4
|
+
} from "./chunk-VAFCR2MS.js";
|
|
5
|
+
|
|
1
6
|
// src/srkl/SrklClient.ts
|
|
2
7
|
var CANONICAL_SRKL_URL = "https://srkl-engine.vercel.app";
|
|
3
8
|
var SrklClient = class {
|
|
@@ -492,6 +497,200 @@ async function syncSrklAccounts(userId, sessionToken, srklUrl) {
|
|
|
492
497
|
return await resp.json();
|
|
493
498
|
}
|
|
494
499
|
|
|
500
|
+
// src/core/srklCore.ts
|
|
501
|
+
var cachedSrklUrl = null;
|
|
502
|
+
async function getPrimarySrklDeploymentUrl(env) {
|
|
503
|
+
if (cachedSrklUrl && Date.now() < cachedSrklUrl.expiresAt) return cachedSrklUrl.url;
|
|
504
|
+
try {
|
|
505
|
+
const res = await queryUnifiedDb(
|
|
506
|
+
`SELECT url FROM "ultrst-srkl-deployments" WHERE is_active = true ORDER BY is_primary DESC, priority DESC, created_at ASC LIMIT 1`,
|
|
507
|
+
[],
|
|
508
|
+
env
|
|
509
|
+
);
|
|
510
|
+
if (res.rows.length > 0 && res.rows[0].url) {
|
|
511
|
+
const url = res.rows[0].url.trim().replace(/\/+$/, "");
|
|
512
|
+
cachedSrklUrl = { url, expiresAt: Date.now() + 10 * 60 * 1e3 };
|
|
513
|
+
return url;
|
|
514
|
+
}
|
|
515
|
+
} catch (err) {
|
|
516
|
+
console.warn("[SrklCore] Deployment DB lookup fallback:", err.message);
|
|
517
|
+
}
|
|
518
|
+
const fallback = "https://srkl-engine.vercel.app";
|
|
519
|
+
cachedSrklUrl = { url: fallback, expiresAt: Date.now() + 10 * 60 * 1e3 };
|
|
520
|
+
return fallback;
|
|
521
|
+
}
|
|
522
|
+
function deduplicateAccounts(items) {
|
|
523
|
+
const seen = /* @__PURE__ */ new Set();
|
|
524
|
+
const out = [];
|
|
525
|
+
for (const a of items) {
|
|
526
|
+
if (!a) continue;
|
|
527
|
+
const creds = a.credentials || a.metadata?.credentials || a;
|
|
528
|
+
const keyId = (creds.keyId || creds.b2KeyId || creds.accessKeyId || "").trim();
|
|
529
|
+
const token = (creds.token || creds.apiToken || creds.applicationKey || creds.b2ApplicationKey || "").trim();
|
|
530
|
+
const sId = (a.serviceId || a.provider || "").toLowerCase().trim();
|
|
531
|
+
const sig = keyId ? `key:${keyId}` : token ? `tok:${sId}:${token.slice(-12)}` : a.id ? `id:${a.id}` : `nam:${sId}:${a.name || a.label}`;
|
|
532
|
+
if (!seen.has(sig)) {
|
|
533
|
+
seen.add(sig);
|
|
534
|
+
if (a.id) seen.add(`id:${a.id}`);
|
|
535
|
+
out.push(a);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return out;
|
|
539
|
+
}
|
|
540
|
+
async function getSrklAccounts(userId, service2, env) {
|
|
541
|
+
const rawAccounts = [];
|
|
542
|
+
const srklUrl = await getPrimarySrklDeploymentUrl(env);
|
|
543
|
+
if (userId) {
|
|
544
|
+
try {
|
|
545
|
+
const uRes = await queryUnifiedDb(
|
|
546
|
+
`SELECT app_profiles FROM "ultrst-users" WHERE id = $1 OR email = $1 LIMIT 1`,
|
|
547
|
+
[userId],
|
|
548
|
+
env
|
|
549
|
+
);
|
|
550
|
+
if (uRes.rows.length > 0) {
|
|
551
|
+
const p = typeof uRes.rows[0].app_profiles === "string" ? JSON.parse(uRes.rows[0].app_profiles) : uRes.rows[0].app_profiles || {};
|
|
552
|
+
const conns = [];
|
|
553
|
+
for (const app of Object.values(p)) {
|
|
554
|
+
if (app && typeof app === "object" && Array.isArray(app.storage_connections)) {
|
|
555
|
+
conns.push(...app.storage_connections);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
for (const c of conns) {
|
|
559
|
+
if (!service2 || (c.provider || "").toLowerCase().includes(service2.toLowerCase())) {
|
|
560
|
+
rawAccounts.push({
|
|
561
|
+
id: c.id,
|
|
562
|
+
name: c.label || c.name || "Backblaze B2 Account",
|
|
563
|
+
provider: c.provider === "backblaze_b2" ? "backblaze" : c.provider,
|
|
564
|
+
serviceId: "backblaze",
|
|
565
|
+
category: "storage",
|
|
566
|
+
groupIds: c.groupIds || (c.groupId ? [c.groupId] : []),
|
|
567
|
+
credentials: {
|
|
568
|
+
keyId: c.b2KeyId || c.keyId || c.accessKeyId || "",
|
|
569
|
+
applicationKey: c.b2ApplicationKey || c.applicationKey || c.secretAccessKey || "",
|
|
570
|
+
bucketName: c.b2BucketName || c.bucketName || "",
|
|
571
|
+
bucketId: c.b2BucketId || c.bucketId || "",
|
|
572
|
+
endpoint: c.b2Endpoint || c.endpoint || "s3.eu-central-003.backblazeb2.com",
|
|
573
|
+
region: c.b2Region || c.region || "eu-central-003"
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
} catch (err) {
|
|
580
|
+
console.warn("[SrklCore] Storage connection DB lookup fallback:", err.message);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
try {
|
|
584
|
+
const fs = await import("fs/promises");
|
|
585
|
+
const path = await import("path");
|
|
586
|
+
const accPath = path.resolve(".skipo/accounts.json");
|
|
587
|
+
const raw = await fs.readFile(accPath, "utf-8");
|
|
588
|
+
const localUsers = JSON.parse(raw);
|
|
589
|
+
for (const u of Object.values(localUsers)) {
|
|
590
|
+
const conns = Array.isArray(u.storage_connections) ? u.storage_connections : [];
|
|
591
|
+
for (const c of conns) {
|
|
592
|
+
if (!service2 || (c.provider || "").toLowerCase().includes(service2.toLowerCase())) {
|
|
593
|
+
rawAccounts.push({
|
|
594
|
+
id: c.id,
|
|
595
|
+
name: c.label || c.name || "Local B2 Account",
|
|
596
|
+
provider: "backblaze",
|
|
597
|
+
serviceId: "backblaze",
|
|
598
|
+
credentials: {
|
|
599
|
+
keyId: c.b2KeyId || c.keyId || "",
|
|
600
|
+
applicationKey: c.b2ApplicationKey || c.applicationKey || "",
|
|
601
|
+
bucketName: c.b2BucketName || c.bucketName || "",
|
|
602
|
+
bucketId: c.b2BucketId || "",
|
|
603
|
+
endpoint: c.b2Endpoint || "s3.eu-central-003.backblazeb2.com",
|
|
604
|
+
region: c.b2Region || "eu-central-003"
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
} catch {
|
|
611
|
+
}
|
|
612
|
+
let fetchError;
|
|
613
|
+
try {
|
|
614
|
+
const srklRes = await fetch(`${srklUrl}/api/srkl/accounts?service=${service2 || "backblaze"}`, {
|
|
615
|
+
signal: AbortSignal.timeout(5e3)
|
|
616
|
+
});
|
|
617
|
+
if (srklRes.ok) {
|
|
618
|
+
const sData = await srklRes.json();
|
|
619
|
+
if (Array.isArray(sData.accounts)) {
|
|
620
|
+
for (const sa of sData.accounts) {
|
|
621
|
+
rawAccounts.push(sa);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
} catch (err) {
|
|
626
|
+
fetchError = err.message;
|
|
627
|
+
}
|
|
628
|
+
const accounts = deduplicateAccounts(rawAccounts);
|
|
629
|
+
return {
|
|
630
|
+
success: true,
|
|
631
|
+
deploymentUrl: srklUrl,
|
|
632
|
+
userId,
|
|
633
|
+
count: accounts.length,
|
|
634
|
+
accounts,
|
|
635
|
+
error: accounts.length > 0 ? void 0 : fetchError
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
async function listBackblazeBuckets(payload, rawUserId, env) {
|
|
639
|
+
try {
|
|
640
|
+
const userId = rawUserId || payload?.userId || payload?.credentials?.userId || "";
|
|
641
|
+
const creds = payload?.credentials || {};
|
|
642
|
+
let keyId = (payload?.keyId || payload?.b2KeyId || payload?.accessKeyId || creds.keyId || creds.b2KeyId || creds.accessKeyId || "").trim();
|
|
643
|
+
let appKey = (payload?.applicationKey || payload?.b2ApplicationKey || payload?.appKey || payload?.secretAccessKey || creds.applicationKey || creds.b2ApplicationKey || creds.appKey || creds.secretAccessKey || "").trim();
|
|
644
|
+
const bucketId = (payload?.bucketId || creds.bucketId || "").trim();
|
|
645
|
+
const bucketName = (payload?.bucketName || payload?.b2BucketName || creds.bucketName || creds.b2BucketName || "").trim();
|
|
646
|
+
if ((!keyId || !appKey) && (payload?.accountId || payload?.id)) {
|
|
647
|
+
const srkl = await getSrklAccounts(userId, "backblaze", env);
|
|
648
|
+
const matched = srkl.accounts?.find((a) => a.id === (payload.accountId || payload.id));
|
|
649
|
+
if (matched?.credentials) {
|
|
650
|
+
keyId = (matched.credentials.keyId || matched.credentials.b2KeyId || matched.credentials.accessKeyId || keyId).trim();
|
|
651
|
+
appKey = (matched.credentials.applicationKey || matched.credentials.b2ApplicationKey || matched.credentials.appKey || matched.credentials.secretAccessKey || appKey).trim();
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
if (!keyId || !appKey) return { success: false, error: "Backblaze credentials (Key ID and Application Key) required." };
|
|
655
|
+
try {
|
|
656
|
+
const buckets = await b2ListBuckets(keyId, appKey, bucketId || void 0, bucketName || void 0);
|
|
657
|
+
if (Array.isArray(buckets) && buckets.length > 0) return { success: true, buckets };
|
|
658
|
+
} catch (err) {
|
|
659
|
+
console.warn("[SrklCore] Direct B2 bucket query failed, trying SRKL fallback:", err.message);
|
|
660
|
+
}
|
|
661
|
+
const srklUrl = await getPrimarySrklDeploymentUrl(env);
|
|
662
|
+
const srklRes = await fetch(`${srklUrl}/api/backblaze/buckets`, {
|
|
663
|
+
method: "POST",
|
|
664
|
+
headers: { "Content-Type": "application/json", "x-user-id": userId || "" },
|
|
665
|
+
body: JSON.stringify({ keyId, applicationKey: appKey, bucketId: bucketId || void 0 }),
|
|
666
|
+
signal: AbortSignal.timeout(6e3)
|
|
667
|
+
});
|
|
668
|
+
if (srklRes.ok) {
|
|
669
|
+
const data = await srklRes.json();
|
|
670
|
+
if (data.success && Array.isArray(data.buckets)) return { success: true, buckets: data.buckets };
|
|
671
|
+
}
|
|
672
|
+
return { success: true, buckets: [] };
|
|
673
|
+
} catch (err) {
|
|
674
|
+
return { success: false, error: err.message || "Failed to list Backblaze buckets" };
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
var srklListBackblazeBuckets = (keyId, applicationKey, bucketId, bucketName, userId, accountId) => listBackblazeBuckets({ keyId, applicationKey, bucketId, bucketName, accountId }, userId);
|
|
678
|
+
async function getSrklStatus(env) {
|
|
679
|
+
const srklUrl = await getPrimarySrklDeploymentUrl(env);
|
|
680
|
+
const start = Date.now();
|
|
681
|
+
try {
|
|
682
|
+
const resp = await fetch(`${srklUrl}/api/ecosystem/health`, { signal: AbortSignal.timeout(5e3) });
|
|
683
|
+
const latencyMs = Date.now() - start;
|
|
684
|
+
if (resp.ok) {
|
|
685
|
+
const data = await resp.json().catch(() => ({}));
|
|
686
|
+
return { success: true, deploymentUrl: srklUrl, online: true, latencyMs, details: data };
|
|
687
|
+
}
|
|
688
|
+
return { success: false, deploymentUrl: srklUrl, online: false, status: resp.status, latencyMs };
|
|
689
|
+
} catch (err) {
|
|
690
|
+
return { success: false, deploymentUrl: srklUrl, online: false, error: err.message, latencyMs: Date.now() - start };
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
495
694
|
// src/srkl/srkl.ts
|
|
496
695
|
var runtime = {
|
|
497
696
|
VaultAccessClient: SrklVaultAccessClient,
|
|
@@ -508,7 +707,12 @@ var service = {
|
|
|
508
707
|
name: "srkl",
|
|
509
708
|
description: "SRKL Ecosystem Volatile Memory Credential Vault Service",
|
|
510
709
|
runtime,
|
|
511
|
-
deployment
|
|
710
|
+
deployment,
|
|
711
|
+
getSrklAccounts: (userId, serviceName, env) => getSrklAccounts(userId, serviceName, env),
|
|
712
|
+
getAccounts: (userId, serviceName, env) => getSrklAccounts(userId, serviceName, env),
|
|
713
|
+
listBackblazeBuckets: (payload, userId, env) => listBackblazeBuckets(payload, userId, env),
|
|
714
|
+
getSrklStatus: (env) => getSrklStatus(env),
|
|
715
|
+
getDeploymentUrl: (env) => getPrimarySrklDeploymentUrl(env)
|
|
512
716
|
};
|
|
513
717
|
var srklService = service;
|
|
514
718
|
|
|
@@ -523,5 +727,10 @@ export {
|
|
|
523
727
|
globalCredentialRegistry,
|
|
524
728
|
SrklVaultAccessClient,
|
|
525
729
|
syncSrklAccounts,
|
|
730
|
+
getPrimarySrklDeploymentUrl,
|
|
731
|
+
getSrklAccounts,
|
|
732
|
+
listBackblazeBuckets,
|
|
733
|
+
srklListBackblazeBuckets,
|
|
734
|
+
getSrklStatus,
|
|
526
735
|
srklService
|
|
527
736
|
};
|
|
@@ -33,12 +33,12 @@ async function getSrklAccounts(userId, service, queryDb) {
|
|
|
33
33
|
const uRes = await queryDb(`SELECT app_profiles FROM "ultrst-users" WHERE id = $1 LIMIT 1`, [userId]);
|
|
34
34
|
if (uRes.rows.length > 0) {
|
|
35
35
|
const p = typeof uRes.rows[0].app_profiles === "string" ? JSON.parse(uRes.rows[0].app_profiles) : uRes.rows[0].app_profiles || {};
|
|
36
|
-
const pool = [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const pool = [];
|
|
37
|
+
for (const app of Object.values(p)) {
|
|
38
|
+
if (app && typeof app === "object" && Array.isArray(app.storage_connections)) {
|
|
39
|
+
pool.push(...app.storage_connections);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
42
|
for (const conn of pool) {
|
|
43
43
|
if (service && !conn.provider?.toLowerCase().includes(service.toLowerCase())) continue;
|
|
44
44
|
const kId = (conn.b2KeyId || conn.keyId || conn.accessKeyId || "").trim();
|
|
@@ -87,12 +87,12 @@ async function getSrklAccounts(userId, service, queryDb) {
|
|
|
87
87
|
}
|
|
88
88
|
return { success: true, deploymentUrl: srklUrl, count: accounts.length, accounts };
|
|
89
89
|
}
|
|
90
|
-
async function srklListBackblazeBuckets(keyId, applicationKey, _bucketId, _bucketName, userId, queryDb) {
|
|
90
|
+
async function srklListBackblazeBuckets(keyId, applicationKey, _bucketId, _bucketName, userId, queryDb, accountId) {
|
|
91
91
|
let resolvedKeyId = (keyId || "").trim();
|
|
92
92
|
let resolvedAppKey = (applicationKey || "").trim();
|
|
93
|
-
if ((!resolvedKeyId || !resolvedAppKey) && userId
|
|
94
|
-
const accountsRes = await getSrklAccounts(userId, "backblaze", queryDb);
|
|
95
|
-
const matched = accountsRes.accounts.find((a) => (a.credentials?.keyId || a.credentials?.b2KeyId) === resolvedKeyId) || accountsRes.accounts[0];
|
|
93
|
+
if ((!resolvedKeyId || !resolvedAppKey) && (userId || accountId)) {
|
|
94
|
+
const accountsRes = await getSrklAccounts(userId || "", "backblaze", queryDb);
|
|
95
|
+
const matched = accountsRes.accounts.find((a) => accountId && a.id === accountId || resolvedKeyId && (a.credentials?.keyId || a.credentials?.b2KeyId) === resolvedKeyId) || accountsRes.accounts[0];
|
|
96
96
|
if (matched) {
|
|
97
97
|
resolvedKeyId = matched.credentials?.keyId || matched.credentials?.b2KeyId || resolvedKeyId;
|
|
98
98
|
resolvedAppKey = matched.credentials?.applicationKey || matched.credentials?.b2ApplicationKey || resolvedAppKey;
|