@super-protocol/sp-cli 0.0.2-beta.9 → 0.0.3
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/README.md +134 -65
- package/bin/dev.js +1 -1
- package/dist/commands/account/get-sppi.d.ts +8 -0
- package/dist/commands/account/get-sppi.js +23 -0
- package/dist/commands/account/info.d.ts +1 -0
- package/dist/commands/account/info.js +5 -2
- package/dist/commands/auth/login.d.ts +4 -1
- package/dist/commands/auth/login.js +4 -2
- package/dist/commands/config/add.js +1 -16
- package/dist/commands/config/base.d.ts +1 -0
- package/dist/commands/config/base.js +22 -4
- package/dist/commands/config/create.js +1 -11
- package/dist/commands/config/list.js +20 -19
- package/dist/commands/files/download.js +8 -6
- package/dist/commands/files/upload.js +8 -6
- package/dist/commands/storage/base.js +3 -1
- package/dist/commands/storage/select.d.ts +4 -4
- package/dist/commands/storage/select.js +15 -21
- package/dist/commands/storage/show.d.ts +17 -0
- package/dist/commands/storage/show.js +36 -0
- package/dist/commands/workflows/extend-lease.d.ts +17 -0
- package/dist/commands/workflows/extend-lease.js +94 -0
- package/dist/config/config.schema.d.ts +9 -0
- package/dist/config/config.schema.js +5 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/hooks/finally/shutdown-blockchain.d.ts +3 -0
- package/dist/hooks/finally/shutdown-blockchain.js +8 -0
- package/dist/lib/container.d.ts +1 -0
- package/dist/lib/container.js +23 -1
- package/dist/managers/account-manager.d.ts +1 -0
- package/dist/managers/account-manager.js +17 -13
- package/dist/managers/config-file-manager.d.ts +1 -1
- package/dist/managers/config-file-manager.js +16 -4
- package/dist/services/storage.service.d.ts +3 -1
- package/dist/services/storage.service.js +87 -44
- package/dist/utils/helper.d.ts +1 -0
- package/dist/utils/helper.js +1 -0
- package/dist/utils/progress.js +10 -2
- package/oclif.manifest.json +190 -151
- package/package.json +10 -6
- package/dist/config/resource.schema.d.ts +0 -31
- package/dist/config/resource.schema.js +0 -14
|
@@ -4,17 +4,16 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { StorageType, } from '@super-protocol/dto-js';
|
|
7
|
+
import { StorageResourceSchema, StorageType, } from '@super-protocol/dto-js';
|
|
8
8
|
import { ResourceType, } from '@super-protocol/provider-client';
|
|
9
9
|
import { download, upload } from '@super-protocol/sp-files-addon';
|
|
10
10
|
import * as fs from 'node:fs/promises';
|
|
11
|
-
import { basename, dirname
|
|
11
|
+
import { basename, dirname } from 'node:path';
|
|
12
12
|
import { Value } from 'typebox/value';
|
|
13
13
|
import { Retryable } from 'typescript-retry-decorator';
|
|
14
|
-
import { storageResourceSchema } from '../config/resource.schema.js';
|
|
15
14
|
import { S3_ENDPOINT } from '../constants.js';
|
|
16
15
|
import { ProviderClientError } from '../errors.js';
|
|
17
|
-
import { preparePath, readJsonFile } from '../utils/helper.js';
|
|
16
|
+
import { joinPaths, preparePath, readJsonFile } from '../utils/helper.js';
|
|
18
17
|
export class StorageError extends Error {
|
|
19
18
|
}
|
|
20
19
|
export class StoragesUndefinedError extends StorageError {
|
|
@@ -58,7 +57,7 @@ export class StorageService {
|
|
|
58
57
|
}
|
|
59
58
|
async download(params, progressCb) {
|
|
60
59
|
const resourceFile = await readJsonFile({ path: params.resourcePath });
|
|
61
|
-
if (!Value.Check(
|
|
60
|
+
if (!Value.Check(StorageResourceSchema, resourceFile)) {
|
|
62
61
|
throw new StorageError('Invalid resource file format, please verify resource.json against the schema.');
|
|
63
62
|
}
|
|
64
63
|
const { resource } = resourceFile;
|
|
@@ -67,13 +66,26 @@ export class StorageService {
|
|
|
67
66
|
}
|
|
68
67
|
let localPath = preparePath(params.downloadPath);
|
|
69
68
|
if (resource.filepath) {
|
|
70
|
-
|
|
69
|
+
const sanitizedFilepath = resource.filepath.replaceAll('..', '').replace(/^\/+/, '');
|
|
70
|
+
localPath = joinPaths(localPath, sanitizedFilepath);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
this.logger.info('Starting download');
|
|
74
|
+
const downloadResult = await download(resource, localPath, {
|
|
75
|
+
encryption: resourceFile.encryption,
|
|
76
|
+
progressCallback: progressCb,
|
|
77
|
+
retry: {
|
|
78
|
+
initialDelayMs: 0,
|
|
79
|
+
maxRetries: 2,
|
|
80
|
+
},
|
|
81
|
+
threads: params.maximumConcurrent,
|
|
82
|
+
});
|
|
83
|
+
this.logger.info(downloadResult, 'Downloaded');
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
this.logger.error({ err: error }, 'Download failed with error');
|
|
87
|
+
throw error;
|
|
71
88
|
}
|
|
72
|
-
await download(resource, localPath, {
|
|
73
|
-
encryption: resourceFile.encryption,
|
|
74
|
-
progressCallback: progressCb,
|
|
75
|
-
threads: params.maximumConcurrent,
|
|
76
|
-
});
|
|
77
89
|
}
|
|
78
90
|
async getCentralizedStorage() {
|
|
79
91
|
try {
|
|
@@ -114,6 +126,9 @@ export class StorageService {
|
|
|
114
126
|
throw error_;
|
|
115
127
|
}
|
|
116
128
|
}
|
|
129
|
+
getLabel(storage) {
|
|
130
|
+
return `${storage.isCentralized ? 'Super cloud' : `${storage.storageType} - ${storage.bucket}/${storage.prefix}`} `;
|
|
131
|
+
}
|
|
117
132
|
async hasStorage() {
|
|
118
133
|
try {
|
|
119
134
|
const { data, error } = await this.providerClient.GET('/api/user-settings');
|
|
@@ -182,11 +197,20 @@ export class StorageService {
|
|
|
182
197
|
async saveStorage(selectedStorage) {
|
|
183
198
|
this.logger.info({ selectedStorage }, 'Saving selected storage');
|
|
184
199
|
try {
|
|
185
|
-
|
|
200
|
+
let { data, error } = await this.providerClient.PATCH('/api/user-settings', {
|
|
186
201
|
body: {
|
|
187
202
|
activeStorageId: selectedStorage,
|
|
188
203
|
},
|
|
189
204
|
});
|
|
205
|
+
if (error && error.statusCode === 404) {
|
|
206
|
+
const result = await this.providerClient.POST('/api/user-settings', {
|
|
207
|
+
body: {
|
|
208
|
+
activeStorageId: selectedStorage,
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
error = result.error;
|
|
212
|
+
data = result.data;
|
|
213
|
+
}
|
|
190
214
|
if (error) {
|
|
191
215
|
throw new StorageError(`User settings request error ${error.message}`);
|
|
192
216
|
}
|
|
@@ -225,8 +249,8 @@ export class StorageService {
|
|
|
225
249
|
}
|
|
226
250
|
async upload(params, progressCb) {
|
|
227
251
|
const storage = await this.getCurrentStorage();
|
|
228
|
-
const remotePath =
|
|
229
|
-
const resourceFilePath = preparePath(params.outputPath
|
|
252
|
+
const remotePath = params.remotePath ? params.remotePath.replaceAll('..', '').replace(/^\/+/, '') : generateExternalId();
|
|
253
|
+
const resourceFilePath = preparePath(params.outputPath ?? 'resource.json');
|
|
230
254
|
try {
|
|
231
255
|
await fs.stat(resourceFilePath);
|
|
232
256
|
throw new Error('Output file for resource file already exists');
|
|
@@ -240,32 +264,7 @@ export class StorageService {
|
|
|
240
264
|
if (params.metadataPath) {
|
|
241
265
|
metadata = await readJsonFile({ path: preparePath(params.metadataPath) });
|
|
242
266
|
}
|
|
243
|
-
const writeCredentials = storage
|
|
244
|
-
? {
|
|
245
|
-
bucket: storage.bucket,
|
|
246
|
-
prefix: join(storage.prefix, remotePath),
|
|
247
|
-
token: storage.storjCredentials.writeAccessToken,
|
|
248
|
-
}
|
|
249
|
-
: {
|
|
250
|
-
accessKeyId: storage.s3Credentials.writeAccessKeyId,
|
|
251
|
-
bucket: storage.bucket,
|
|
252
|
-
endpoint: S3_ENDPOINT,
|
|
253
|
-
prefix: join(storage.prefix, remotePath),
|
|
254
|
-
secretKey: storage.s3Credentials.writeSecretAccessKey,
|
|
255
|
-
};
|
|
256
|
-
const readCredentials = storage.storageType === StorageType.StorJ
|
|
257
|
-
? {
|
|
258
|
-
bucket: storage.bucket,
|
|
259
|
-
prefix: join(storage.prefix, remotePath),
|
|
260
|
-
token: storage.storjCredentials.readAccessToken,
|
|
261
|
-
}
|
|
262
|
-
: {
|
|
263
|
-
accessKeyId: storage.s3Credentials.readAccessKeyId,
|
|
264
|
-
bucket: storage.bucket,
|
|
265
|
-
endpoint: S3_ENDPOINT,
|
|
266
|
-
prefix: join(storage.prefix, remotePath),
|
|
267
|
-
secretKey: storage.s3Credentials.readSecretAccessKey,
|
|
268
|
-
};
|
|
267
|
+
const { readCredentials, writeCredentials } = this.getStorageCredentials(storage, remotePath);
|
|
269
268
|
const info = await fs.stat(params.localPath);
|
|
270
269
|
let { localPath } = params;
|
|
271
270
|
let sourcePath;
|
|
@@ -274,6 +273,7 @@ export class StorageService {
|
|
|
274
273
|
localPath = dirname(localPath);
|
|
275
274
|
}
|
|
276
275
|
try {
|
|
276
|
+
this.logger.info('Starting upload');
|
|
277
277
|
const uploadResult = await upload(localPath, {
|
|
278
278
|
credentials: writeCredentials,
|
|
279
279
|
filepath: '',
|
|
@@ -282,6 +282,10 @@ export class StorageService {
|
|
|
282
282
|
}, {
|
|
283
283
|
encryption: params.withEncryption,
|
|
284
284
|
progressCallback: progressCb,
|
|
285
|
+
retry: {
|
|
286
|
+
initialDelayMs: 0,
|
|
287
|
+
maxRetries: 2,
|
|
288
|
+
},
|
|
285
289
|
sourcePath,
|
|
286
290
|
sync: params.sync,
|
|
287
291
|
threads: params.maximumConcurrent,
|
|
@@ -296,11 +300,50 @@ export class StorageService {
|
|
|
296
300
|
this.logger.info(`Resource file was created in ${resourceFilePath}`);
|
|
297
301
|
}
|
|
298
302
|
catch (error) {
|
|
299
|
-
this.logger.error({ err: error }, '
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
+
this.logger.error({ err: error }, 'Something went wrong with upload');
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
getStorageCredentials(storage, remotePath) {
|
|
308
|
+
if (storage.storageType === StorageType.StorJ) {
|
|
309
|
+
const { readAccessToken, writeAccessToken } = storage.storjCredentials || {};
|
|
310
|
+
if (!writeAccessToken || !readAccessToken) {
|
|
311
|
+
throw new StorageError('StorJ credentials are missing or incomplete');
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
readCredentials: {
|
|
315
|
+
bucket: storage.bucket,
|
|
316
|
+
prefix: joinPaths(storage.prefix, remotePath),
|
|
317
|
+
token: readAccessToken,
|
|
318
|
+
}, writeCredentials: {
|
|
319
|
+
bucket: storage.bucket,
|
|
320
|
+
prefix: joinPaths(storage.prefix, remotePath),
|
|
321
|
+
token: writeAccessToken,
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if (storage.storageType === StorageType.S3) {
|
|
326
|
+
const { readAccessKeyId, readSecretAccessKey, writeAccessKeyId, writeSecretAccessKey } = storage.s3Credentials || {};
|
|
327
|
+
if (!writeAccessKeyId || !writeSecretAccessKey || !readAccessKeyId || !readSecretAccessKey) {
|
|
328
|
+
throw new StorageError('S3 credentials are missing or incomplete');
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
readCredentials: {
|
|
332
|
+
accessKeyId: readAccessKeyId,
|
|
333
|
+
bucket: storage.bucket,
|
|
334
|
+
endpoint: S3_ENDPOINT,
|
|
335
|
+
prefix: joinPaths(storage.prefix, remotePath),
|
|
336
|
+
secretKey: readSecretAccessKey,
|
|
337
|
+
}, writeCredentials: {
|
|
338
|
+
accessKeyId: writeAccessKeyId,
|
|
339
|
+
bucket: storage.bucket,
|
|
340
|
+
endpoint: S3_ENDPOINT,
|
|
341
|
+
prefix: joinPaths(storage.prefix, remotePath),
|
|
342
|
+
secretKey: writeSecretAccessKey,
|
|
343
|
+
},
|
|
344
|
+
};
|
|
303
345
|
}
|
|
346
|
+
throw new StorageError(`Unknown storage type: ${storage.storageType}`);
|
|
304
347
|
}
|
|
305
348
|
}
|
|
306
349
|
__decorate([
|
package/dist/utils/helper.d.ts
CHANGED
package/dist/utils/helper.js
CHANGED
|
@@ -7,6 +7,7 @@ export const preparePath = (rawPath) => {
|
|
|
7
7
|
return rawPath;
|
|
8
8
|
return path.join(process.cwd(), rawPath);
|
|
9
9
|
};
|
|
10
|
+
export const joinPaths = (...args) => path.join(...args).replaceAll('\\', '/');
|
|
10
11
|
export const readJsonFile = async (params) => {
|
|
11
12
|
if (!existsSync(params.path)) {
|
|
12
13
|
throw new Error(`File could not be found in ${params.path}`);
|
package/dist/utils/progress.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as p from '@clack/prompts';
|
|
|
2
2
|
export const createProgressPrinter = ({ action, done = 'completed', start, }) => {
|
|
3
3
|
const progressBars = {};
|
|
4
4
|
let lastKey = '';
|
|
5
|
+
let lastProgress = 0;
|
|
5
6
|
const getProgress = (key) => {
|
|
6
7
|
if (key in progressBars) {
|
|
7
8
|
lastKey = key;
|
|
@@ -9,6 +10,7 @@ export const createProgressPrinter = ({ action, done = 'completed', start, }) =>
|
|
|
9
10
|
}
|
|
10
11
|
if (lastKey in progressBars) {
|
|
11
12
|
const progressBar = progressBars[lastKey];
|
|
13
|
+
lastProgress = 0;
|
|
12
14
|
progressBar.stop(`${action} ${lastKey} ${done}`);
|
|
13
15
|
}
|
|
14
16
|
const progressBar = p.progress({ size: 100, style: 'block' });
|
|
@@ -16,11 +18,17 @@ export const createProgressPrinter = ({ action, done = 'completed', start, }) =>
|
|
|
16
18
|
progressBars[key] = progressBar;
|
|
17
19
|
return progressBar;
|
|
18
20
|
};
|
|
19
|
-
const finish = () =>
|
|
21
|
+
const finish = () => {
|
|
22
|
+
if (lastKey && progressBars[lastKey]) {
|
|
23
|
+
lastProgress = 0;
|
|
24
|
+
progressBars[lastKey].stop(`${action} ${lastKey} ${done}`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
20
27
|
return {
|
|
21
28
|
advance(key, num, message) {
|
|
22
29
|
const progress = getProgress(key);
|
|
23
|
-
progress.advance(num, message);
|
|
30
|
+
progress.advance(num - lastProgress, message);
|
|
31
|
+
lastProgress = num;
|
|
24
32
|
},
|
|
25
33
|
finish,
|
|
26
34
|
};
|