@ultrst-armada/kloks 1.7.0 → 1.7.1
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/{BaseStorageAdapter-tosOoSUg.d.ts → BaseStorageAdapter-6x_BQiB3.d.ts} +6 -1
- package/packages/backend/dist/{chunk-BI5RDDB4.js → chunk-3IUA66RS.js} +9 -2
- package/packages/backend/dist/{chunk-6QTNNZSE.js → chunk-6W7OAGNV.js} +52 -70
- package/packages/backend/dist/{chunk-Y3X3H2XX.js → chunk-7ZT2ZPPH.js} +3 -3
- package/packages/backend/dist/{chunk-KSZ67VZJ.js → chunk-F6IDQLC2.js} +1 -1
- package/packages/backend/dist/chunk-JUPWFW7V.js +134 -0
- package/packages/backend/dist/{chunk-J2J24JVC.js → chunk-R45CEV46.js} +210 -153
- package/packages/backend/dist/{chunk-N4Z57FVR.js → chunk-XHADDA75.js} +9 -3
- package/packages/backend/dist/index.d.ts +3 -3
- package/packages/backend/dist/index.js +22 -13
- package/packages/backend/dist/krnl-services.js +4 -4
- package/packages/backend/dist/services/index.js +2 -2
- package/packages/backend/dist/srkl/index.d.ts +7 -1
- package/packages/backend/dist/srkl/index.js +2 -2
- package/packages/backend/dist/storage/index.d.ts +35 -3
- package/packages/backend/dist/storage/index.js +12 -3
- package/packages/backend/dist/workspace/index.d.ts +27 -33
- package/packages/backend/dist/workspace/index.js +1 -1
- package/packages/client/dist/index.d.ts +34 -1
- package/packages/client/dist/index.js +116 -0
- package/packages/backend/dist/chunk-LS3HWJBX.js +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"packageName": "@ultrst-armada/kloks",
|
|
4
|
-
"version": "1.7.
|
|
5
|
-
"sourceCommit": "
|
|
4
|
+
"version": "1.7.1",
|
|
5
|
+
"sourceCommit": "67d641336636d076f15240809b73c0a4f9f438b5",
|
|
6
6
|
"releaseWorkflow": "npm Release",
|
|
7
|
-
"workflowRunId": "
|
|
7
|
+
"workflowRunId": "40193283076"
|
|
8
8
|
}
|
package/package.json
CHANGED
package/packages/backend/dist/{BaseStorageAdapter-tosOoSUg.d.ts → BaseStorageAdapter-6x_BQiB3.d.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SkipoBase } from '@ultrst-armada/kloks/functions';
|
|
2
2
|
|
|
3
3
|
type StorageMode = 'local' | 'local_hosted' | 'remote';
|
|
4
|
-
type StorageProviderType = 'local_fs' | '
|
|
4
|
+
type StorageProviderType = 'local_fs' | 'backblaze_b2' | 'b2' | 'supabase' | 'firebase' | 'google_drive';
|
|
5
5
|
interface StorageConfig {
|
|
6
6
|
provider: StorageProviderType;
|
|
7
7
|
mode?: StorageMode;
|
|
@@ -19,6 +19,11 @@ interface StorageConfig {
|
|
|
19
19
|
keyId?: string;
|
|
20
20
|
applicationKey?: string;
|
|
21
21
|
accountId?: string;
|
|
22
|
+
googleClientId?: string;
|
|
23
|
+
googleRedirectUri?: string;
|
|
24
|
+
googleAccessToken?: string;
|
|
25
|
+
googleRefreshToken?: string;
|
|
26
|
+
googleRootFolderId?: string;
|
|
22
27
|
}
|
|
23
28
|
interface FileInfo {
|
|
24
29
|
path: string;
|
|
@@ -121,9 +121,10 @@ var SrklClient = class {
|
|
|
121
121
|
const creds = account.credentials || {};
|
|
122
122
|
const sId = (account.serviceId || "").toLowerCase();
|
|
123
123
|
const isB2 = sId.includes("backblaze") || sId === "b2";
|
|
124
|
+
const isGoogle = sId.includes("google_drive") || sId.includes("googledrive") || sId === "google";
|
|
124
125
|
return {
|
|
125
126
|
id: `conn-srkl-${account.id}`,
|
|
126
|
-
provider: isB2 ? "backblaze_b2" : "aws_s3",
|
|
127
|
+
provider: isB2 ? "backblaze_b2" : isGoogle ? "google_drive" : "aws_s3",
|
|
127
128
|
label: `SRKL: ${account.name || (isB2 ? "Backblaze" : "S3")}`,
|
|
128
129
|
role: "redundant_clone",
|
|
129
130
|
enabled: true,
|
|
@@ -137,7 +138,13 @@ var SrklClient = class {
|
|
|
137
138
|
s3SecretAccessKey: creds.secretAccessKey || creds.applicationKey || creds.s3SecretAccessKey,
|
|
138
139
|
s3BucketName: creds.bucketName || creds.s3BucketName,
|
|
139
140
|
s3Region: creds.region || creds.s3Region,
|
|
140
|
-
s3Endpoint: creds.endpoint || creds.s3Endpoint
|
|
141
|
+
s3Endpoint: creds.endpoint || creds.s3Endpoint,
|
|
142
|
+
googleEmail: creds.email || account.credentials?.email || account.name,
|
|
143
|
+
googleRootFolderId: creds.rootFolderId,
|
|
144
|
+
googleRootFolderName: creds.rootFolderName,
|
|
145
|
+
googleAccessToken: creds.accessToken,
|
|
146
|
+
googleAccessTokenExpiresAt: creds.accessTokenExpiresAt,
|
|
147
|
+
googleRefreshToken: creds.refreshToken
|
|
141
148
|
};
|
|
142
149
|
}
|
|
143
150
|
};
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createWorkspace,
|
|
3
3
|
deleteWorkspace,
|
|
4
|
-
deleteWorkspaceFile,
|
|
5
4
|
findUserWorkspace,
|
|
6
5
|
getWorkspaces,
|
|
7
|
-
|
|
8
|
-
readWorkspaceFile,
|
|
9
|
-
updateWorkspace,
|
|
10
|
-
writeWorkspaceFile
|
|
6
|
+
updateWorkspace
|
|
11
7
|
} from "./chunk-6K75XXFD.js";
|
|
12
8
|
import {
|
|
13
9
|
b2DownloadFile,
|
|
@@ -135,58 +131,6 @@ var WorkspaceCloneSyncEngine = class extends SkipoBase3 {
|
|
|
135
131
|
}
|
|
136
132
|
};
|
|
137
133
|
|
|
138
|
-
// src/workspace/RemoteWorkspaceService.ts
|
|
139
|
-
var RemoteWorkspaceService = class {
|
|
140
|
-
type = "remote";
|
|
141
|
-
async findWorkspace(workspaceId, env) {
|
|
142
|
-
return await findUserWorkspace(workspaceId, env);
|
|
143
|
-
}
|
|
144
|
-
async listWorkspaces(userId, env) {
|
|
145
|
-
return await getWorkspaces(userId, env);
|
|
146
|
-
}
|
|
147
|
-
async createWorkspace(userId, data, env) {
|
|
148
|
-
return await createWorkspace(userId, data, env);
|
|
149
|
-
}
|
|
150
|
-
async updateWorkspace(userId, params, env) {
|
|
151
|
-
return await updateWorkspace(userId, params, env);
|
|
152
|
-
}
|
|
153
|
-
async deleteWorkspace(userId, workspaceId, env) {
|
|
154
|
-
return await deleteWorkspace(userId, workspaceId, env);
|
|
155
|
-
}
|
|
156
|
-
async readFile(workspaceId, filePath, env) {
|
|
157
|
-
const { workspace, connection } = await this.findWorkspace(workspaceId, env);
|
|
158
|
-
if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
|
|
159
|
-
const data = await readWorkspaceFile(workspace, connection, filePath);
|
|
160
|
-
if (!data) throw new Error(`File "${filePath}" not found in workspace "${workspaceId}".`);
|
|
161
|
-
return data;
|
|
162
|
-
}
|
|
163
|
-
async writeFile(workspaceId, filePath, content, env) {
|
|
164
|
-
const { workspace, connection } = await this.findWorkspace(workspaceId, env);
|
|
165
|
-
if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
|
|
166
|
-
await writeWorkspaceFile(workspace, connection, filePath, content);
|
|
167
|
-
const size = typeof content === "string" ? new TextEncoder().encode(content).length : content.byteLength;
|
|
168
|
-
return { success: true, path: filePath, sizeBytes: size };
|
|
169
|
-
}
|
|
170
|
-
async deleteFile(workspaceId, filePath, env) {
|
|
171
|
-
const { workspace, connection } = await this.findWorkspace(workspaceId, env);
|
|
172
|
-
if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
|
|
173
|
-
const deleted = await deleteWorkspaceFile(workspace, connection, filePath);
|
|
174
|
-
return { success: deleted };
|
|
175
|
-
}
|
|
176
|
-
async listFiles(workspaceId, subfolder = "", env) {
|
|
177
|
-
const { workspace, connection } = await this.findWorkspace(workspaceId, env);
|
|
178
|
-
if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
|
|
179
|
-
const files = await listWorkspaceFiles(workspace, connection, subfolder);
|
|
180
|
-
return files.map((f) => ({
|
|
181
|
-
path: f.fileName,
|
|
182
|
-
name: f.fileName.split("/").pop() || f.fileName,
|
|
183
|
-
sizeBytes: f.sizeBytes,
|
|
184
|
-
updatedAt: f.uploadTimestamp
|
|
185
|
-
}));
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
var remoteWorkspaceService = new RemoteWorkspaceService();
|
|
189
|
-
|
|
190
134
|
// src/workspace/WorkingWorkspaceService.ts
|
|
191
135
|
import fs2 from "fs";
|
|
192
136
|
import path2 from "path";
|
|
@@ -245,8 +189,10 @@ function setWorkingWorkspaceConfig(cfg) {
|
|
|
245
189
|
}
|
|
246
190
|
return updated;
|
|
247
191
|
}
|
|
248
|
-
function getWorkingWorkspaceDir() {
|
|
249
|
-
const
|
|
192
|
+
function getWorkingWorkspaceDir(workspaceId = "default") {
|
|
193
|
+
const root = getWorkingWorkspaceConfig().workingDirectory;
|
|
194
|
+
const safeId = workspaceId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
195
|
+
const dir = path.join(root, safeId);
|
|
250
196
|
if (!fs.existsSync(dir)) {
|
|
251
197
|
fs.mkdirSync(dir, { recursive: true });
|
|
252
198
|
}
|
|
@@ -331,7 +277,7 @@ async function syncWorkspace(workspaceId) {
|
|
|
331
277
|
}
|
|
332
278
|
const { workspace, user, connection } = match;
|
|
333
279
|
const folderKey = getWorkspaceFolderKey(workspace, user);
|
|
334
|
-
const workingDir = getWorkingWorkspaceDir();
|
|
280
|
+
const workingDir = getWorkingWorkspaceDir(workspaceId);
|
|
335
281
|
const isB2 = workspace.storageProvider === "backblaze_b2" || workspace.storageConfig?.type === "b2";
|
|
336
282
|
const syncedFiles = [];
|
|
337
283
|
let uploadedCount = 0;
|
|
@@ -505,8 +451,8 @@ var WorkingWorkspaceService = class {
|
|
|
505
451
|
}
|
|
506
452
|
workingDirResolver;
|
|
507
453
|
type = "working";
|
|
508
|
-
getWorkingDir() {
|
|
509
|
-
return this.workingDirResolver ? this.workingDirResolver() : getWorkingWorkspaceDir();
|
|
454
|
+
getWorkingDir(workspaceId) {
|
|
455
|
+
return this.workingDirResolver ? this.workingDirResolver(workspaceId) : getWorkingWorkspaceDir(workspaceId);
|
|
510
456
|
}
|
|
511
457
|
async findWorkspace(workspaceId, env) {
|
|
512
458
|
return await findUserWorkspace(workspaceId, env);
|
|
@@ -516,7 +462,7 @@ var WorkingWorkspaceService = class {
|
|
|
516
462
|
}
|
|
517
463
|
async createWorkspace(userId, data, env) {
|
|
518
464
|
const res = await createWorkspace(userId, data, env);
|
|
519
|
-
const dir = this.getWorkingDir();
|
|
465
|
+
const dir = this.getWorkingDir(res.workspace?.id || data?.id || "default");
|
|
520
466
|
try {
|
|
521
467
|
await fs2.promises.mkdir(dir, { recursive: true });
|
|
522
468
|
} catch {
|
|
@@ -531,12 +477,12 @@ var WorkingWorkspaceService = class {
|
|
|
531
477
|
}
|
|
532
478
|
async readFile(_workspaceId, filePath) {
|
|
533
479
|
const cleanRel = filePath.replace(/^\/+/, "");
|
|
534
|
-
const fullPath = path2.resolve(this.getWorkingDir(), cleanRel);
|
|
480
|
+
const fullPath = path2.resolve(this.getWorkingDir(_workspaceId), cleanRel);
|
|
535
481
|
return await fs2.promises.readFile(fullPath);
|
|
536
482
|
}
|
|
537
483
|
async writeFile(_workspaceId, filePath, content) {
|
|
538
484
|
const cleanRel = filePath.replace(/^\/+/, "");
|
|
539
|
-
const fullPath = path2.resolve(this.getWorkingDir(), cleanRel);
|
|
485
|
+
const fullPath = path2.resolve(this.getWorkingDir(_workspaceId), cleanRel);
|
|
540
486
|
await fs2.promises.mkdir(path2.dirname(fullPath), { recursive: true });
|
|
541
487
|
const buffer = typeof content === "string" ? Buffer.from(content) : Buffer.from(content.buffer, content.byteOffset, content.byteLength);
|
|
542
488
|
await fs2.promises.writeFile(fullPath, buffer);
|
|
@@ -553,7 +499,7 @@ var WorkingWorkspaceService = class {
|
|
|
553
499
|
}
|
|
554
500
|
}
|
|
555
501
|
async listFiles(_workspaceId, subfolder = "") {
|
|
556
|
-
const baseDir = path2.resolve(this.getWorkingDir(), subfolder.replace(/^\/+/, ""));
|
|
502
|
+
const baseDir = path2.resolve(this.getWorkingDir(_workspaceId), subfolder.replace(/^\/+/, ""));
|
|
557
503
|
if (!fs2.existsSync(baseDir)) return [];
|
|
558
504
|
const entries = await fs2.promises.readdir(baseDir, { withFileTypes: true });
|
|
559
505
|
return entries.map((e) => {
|
|
@@ -567,7 +513,7 @@ var WorkingWorkspaceService = class {
|
|
|
567
513
|
} catch {
|
|
568
514
|
}
|
|
569
515
|
return {
|
|
570
|
-
path: path2.relative(this.getWorkingDir(), full),
|
|
516
|
+
path: path2.relative(this.getWorkingDir(_workspaceId), full),
|
|
571
517
|
name: e.name,
|
|
572
518
|
isFolder: e.isDirectory(),
|
|
573
519
|
sizeBytes,
|
|
@@ -581,12 +527,46 @@ var WorkingWorkspaceService = class {
|
|
|
581
527
|
};
|
|
582
528
|
var workingWorkspaceService = new WorkingWorkspaceService();
|
|
583
529
|
|
|
530
|
+
// src/workspace/RemoteWorkspaceService.ts
|
|
531
|
+
var RemoteWorkspaceService = class {
|
|
532
|
+
type = "working";
|
|
533
|
+
findWorkspace(id, env) {
|
|
534
|
+
return workingWorkspaceService.findWorkspace(id, env);
|
|
535
|
+
}
|
|
536
|
+
listWorkspaces(userId, env) {
|
|
537
|
+
return workingWorkspaceService.listWorkspaces(userId, env);
|
|
538
|
+
}
|
|
539
|
+
createWorkspace(userId, data, env) {
|
|
540
|
+
return workingWorkspaceService.createWorkspace(userId, data, env);
|
|
541
|
+
}
|
|
542
|
+
updateWorkspace(userId, params, env) {
|
|
543
|
+
return workingWorkspaceService.updateWorkspace(userId, params, env);
|
|
544
|
+
}
|
|
545
|
+
deleteWorkspace(userId, id, env) {
|
|
546
|
+
return workingWorkspaceService.deleteWorkspace(userId, id, env);
|
|
547
|
+
}
|
|
548
|
+
readFile(id, path3, env) {
|
|
549
|
+
return workingWorkspaceService.readFile(id, path3);
|
|
550
|
+
}
|
|
551
|
+
writeFile(id, path3, content, env) {
|
|
552
|
+
return workingWorkspaceService.writeFile(id, path3, content);
|
|
553
|
+
}
|
|
554
|
+
deleteFile(id, path3, env) {
|
|
555
|
+
return workingWorkspaceService.deleteFile(id, path3);
|
|
556
|
+
}
|
|
557
|
+
listFiles(id, subfolder, env) {
|
|
558
|
+
return workingWorkspaceService.listFiles(id, subfolder);
|
|
559
|
+
}
|
|
560
|
+
sync(id, options, env) {
|
|
561
|
+
return workingWorkspaceService.sync(id, options);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
var remoteWorkspaceService = workingWorkspaceService;
|
|
565
|
+
|
|
584
566
|
export {
|
|
585
567
|
WorkspaceFS,
|
|
586
568
|
SyncEngine,
|
|
587
569
|
WorkspaceCloneSyncEngine,
|
|
588
|
-
RemoteWorkspaceService,
|
|
589
|
-
remoteWorkspaceService,
|
|
590
570
|
getDefaultWorkingWorkspaceDir,
|
|
591
571
|
getWorkingWorkspaceConfig,
|
|
592
572
|
setWorkingWorkspaceConfig,
|
|
@@ -595,5 +575,7 @@ export {
|
|
|
595
575
|
getWorkspaceFolderKey,
|
|
596
576
|
syncWorkspace,
|
|
597
577
|
WorkingWorkspaceService,
|
|
598
|
-
workingWorkspaceService
|
|
578
|
+
workingWorkspaceService,
|
|
579
|
+
RemoteWorkspaceService,
|
|
580
|
+
remoteWorkspaceService
|
|
599
581
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
workspaceGraphService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-F6IDQLC2.js";
|
|
4
4
|
import {
|
|
5
5
|
getSrklAccounts,
|
|
6
6
|
srklListBackblazeBuckets
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XHADDA75.js";
|
|
8
8
|
import {
|
|
9
9
|
StorageAdapterFactory
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-R45CEV46.js";
|
|
11
11
|
import {
|
|
12
12
|
b2ListBuckets
|
|
13
13
|
} from "./chunk-4QEMALXF.js";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseStorageAdapter,
|
|
3
|
+
ensureS3DirectoryMarker,
|
|
4
|
+
permanentDeleteS3Folder,
|
|
5
|
+
permanentDeleteS3Object,
|
|
6
|
+
renameS3Folder
|
|
7
|
+
} from "./chunk-R45CEV46.js";
|
|
8
|
+
|
|
9
|
+
// src/storage/S3Adapter.ts
|
|
10
|
+
var S3Client;
|
|
11
|
+
var PutObjectCommand;
|
|
12
|
+
var GetObjectCommand;
|
|
13
|
+
var ListObjectsV2Command;
|
|
14
|
+
var HeadObjectCommand;
|
|
15
|
+
var S3Adapter = class extends BaseStorageAdapter {
|
|
16
|
+
client;
|
|
17
|
+
bucket;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
super(config);
|
|
20
|
+
this.bucket = config.bucket || "";
|
|
21
|
+
}
|
|
22
|
+
async ensureClient() {
|
|
23
|
+
if (this.client) return;
|
|
24
|
+
try {
|
|
25
|
+
const aws = await import("@aws-sdk/client-s3");
|
|
26
|
+
S3Client = aws.S3Client;
|
|
27
|
+
PutObjectCommand = aws.PutObjectCommand;
|
|
28
|
+
GetObjectCommand = aws.GetObjectCommand;
|
|
29
|
+
ListObjectsV2Command = aws.ListObjectsV2Command;
|
|
30
|
+
HeadObjectCommand = aws.HeadObjectCommand;
|
|
31
|
+
this.client = new S3Client({
|
|
32
|
+
region: this.config.region || "us-east-1",
|
|
33
|
+
endpoint: this.config.endpoint,
|
|
34
|
+
credentials: {
|
|
35
|
+
accessKeyId: this.config.accessKey,
|
|
36
|
+
secretAccessKey: this.config.secretKey
|
|
37
|
+
},
|
|
38
|
+
forcePathStyle: Boolean(this.config.endpoint)
|
|
39
|
+
});
|
|
40
|
+
} catch (e) {
|
|
41
|
+
throw new Error(`@aws-sdk/client-s3 failed to initialize: ${e.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async read(pathStr) {
|
|
45
|
+
await this.ensureClient();
|
|
46
|
+
const command = new GetObjectCommand({ Bucket: this.bucket, Key: this.normalizePath(pathStr) });
|
|
47
|
+
const response = await this.client.send(command);
|
|
48
|
+
return response.Body ? await response.Body.transformToByteArray() : new Uint8Array();
|
|
49
|
+
}
|
|
50
|
+
async write(pathStr, data, contentType = "application/octet-stream") {
|
|
51
|
+
await this.ensureClient();
|
|
52
|
+
const body = typeof data === "string" ? Buffer.from(data, "utf-8") : Buffer.from(data);
|
|
53
|
+
const command = new PutObjectCommand({
|
|
54
|
+
Bucket: this.bucket,
|
|
55
|
+
Key: this.normalizePath(pathStr),
|
|
56
|
+
Body: body,
|
|
57
|
+
ContentType: contentType
|
|
58
|
+
});
|
|
59
|
+
await this.client.send(command);
|
|
60
|
+
}
|
|
61
|
+
async delete(pathStr) {
|
|
62
|
+
await this.ensureClient();
|
|
63
|
+
await permanentDeleteS3Object(this.client, this.bucket, this.normalizePath(pathStr));
|
|
64
|
+
}
|
|
65
|
+
async deleteFolder(pathStr) {
|
|
66
|
+
await this.ensureClient();
|
|
67
|
+
return await permanentDeleteS3Folder(this.client, this.bucket, this.normalizePath(pathStr));
|
|
68
|
+
}
|
|
69
|
+
async renameFolder(oldPath, newPath) {
|
|
70
|
+
await this.ensureClient();
|
|
71
|
+
return await renameS3Folder(this.client, this.bucket, this.normalizePath(oldPath), this.normalizePath(newPath));
|
|
72
|
+
}
|
|
73
|
+
async ensureDirectory(pathStr) {
|
|
74
|
+
await this.ensureClient();
|
|
75
|
+
await ensureS3DirectoryMarker(this.client, this.bucket, this.normalizePath(pathStr));
|
|
76
|
+
}
|
|
77
|
+
async exists(pathStr) {
|
|
78
|
+
await this.ensureClient();
|
|
79
|
+
try {
|
|
80
|
+
const command = new HeadObjectCommand({ Bucket: this.bucket, Key: this.normalizePath(pathStr) });
|
|
81
|
+
await this.client.send(command);
|
|
82
|
+
return true;
|
|
83
|
+
} catch (e) {
|
|
84
|
+
if (e.name === "NotFound" || e.$metadata?.httpStatusCode === 404) return false;
|
|
85
|
+
throw e;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async list(pathStr = "") {
|
|
89
|
+
await this.ensureClient();
|
|
90
|
+
const prefix = pathStr ? this.normalizePath(pathStr).replace(/\/+$/, "") + "/" : "";
|
|
91
|
+
const command = new ListObjectsV2Command({ Bucket: this.bucket, Prefix: prefix });
|
|
92
|
+
const response = await this.client.send(command);
|
|
93
|
+
return (response.Contents || []).map((item) => ({
|
|
94
|
+
path: item.Key,
|
|
95
|
+
size: item.Size || 0,
|
|
96
|
+
modifiedAt: item.LastModified ? item.LastModified.getTime() : 0
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
async stat(pathStr) {
|
|
100
|
+
await this.ensureClient();
|
|
101
|
+
const command = new HeadObjectCommand({ Bucket: this.bucket, Key: this.normalizePath(pathStr) });
|
|
102
|
+
const response = await this.client.send(command);
|
|
103
|
+
return {
|
|
104
|
+
path: pathStr,
|
|
105
|
+
size: response.ContentLength || 0,
|
|
106
|
+
modifiedAt: response.LastModified ? response.LastModified.getTime() : 0
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async mkdir(pathStr) {
|
|
110
|
+
await this.ensureDirectory(pathStr);
|
|
111
|
+
}
|
|
112
|
+
async testConnection() {
|
|
113
|
+
const start = performance.now();
|
|
114
|
+
try {
|
|
115
|
+
await this.ensureClient();
|
|
116
|
+
const res = await this.client.send(new ListObjectsV2Command({ Bucket: this.bucket, MaxKeys: 1 }));
|
|
117
|
+
return {
|
|
118
|
+
ok: true,
|
|
119
|
+
latencyMs: Math.round(performance.now() - start),
|
|
120
|
+
details: { bucket: this.bucket, keyCount: res.KeyCount ?? 0 }
|
|
121
|
+
};
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
latencyMs: Math.round(performance.now() - start),
|
|
126
|
+
error: e.message
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
S3Adapter
|
|
134
|
+
};
|