@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.
Files changed (43) hide show
  1. package/README.md +134 -65
  2. package/bin/dev.js +1 -1
  3. package/dist/commands/account/get-sppi.d.ts +8 -0
  4. package/dist/commands/account/get-sppi.js +23 -0
  5. package/dist/commands/account/info.d.ts +1 -0
  6. package/dist/commands/account/info.js +5 -2
  7. package/dist/commands/auth/login.d.ts +4 -1
  8. package/dist/commands/auth/login.js +4 -2
  9. package/dist/commands/config/add.js +1 -16
  10. package/dist/commands/config/base.d.ts +1 -0
  11. package/dist/commands/config/base.js +22 -4
  12. package/dist/commands/config/create.js +1 -11
  13. package/dist/commands/config/list.js +20 -19
  14. package/dist/commands/files/download.js +8 -6
  15. package/dist/commands/files/upload.js +8 -6
  16. package/dist/commands/storage/base.js +3 -1
  17. package/dist/commands/storage/select.d.ts +4 -4
  18. package/dist/commands/storage/select.js +15 -21
  19. package/dist/commands/storage/show.d.ts +17 -0
  20. package/dist/commands/storage/show.js +36 -0
  21. package/dist/commands/workflows/extend-lease.d.ts +17 -0
  22. package/dist/commands/workflows/extend-lease.js +94 -0
  23. package/dist/config/config.schema.d.ts +9 -0
  24. package/dist/config/config.schema.js +5 -0
  25. package/dist/constants.d.ts +3 -0
  26. package/dist/constants.js +3 -0
  27. package/dist/hooks/finally/shutdown-blockchain.d.ts +3 -0
  28. package/dist/hooks/finally/shutdown-blockchain.js +8 -0
  29. package/dist/lib/container.d.ts +1 -0
  30. package/dist/lib/container.js +23 -1
  31. package/dist/managers/account-manager.d.ts +1 -0
  32. package/dist/managers/account-manager.js +17 -13
  33. package/dist/managers/config-file-manager.d.ts +1 -1
  34. package/dist/managers/config-file-manager.js +16 -4
  35. package/dist/services/storage.service.d.ts +3 -1
  36. package/dist/services/storage.service.js +87 -44
  37. package/dist/utils/helper.d.ts +1 -0
  38. package/dist/utils/helper.js +1 -0
  39. package/dist/utils/progress.js +10 -2
  40. package/oclif.manifest.json +190 -151
  41. package/package.json +10 -6
  42. package/dist/config/resource.schema.d.ts +0 -31
  43. 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, join } from 'node:path';
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(storageResourceSchema, resourceFile)) {
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
- localPath = join(localPath, resource.filepath);
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
- const { data, error } = await this.providerClient.POST('/api/user-settings', {
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 = `${params.remotePath || generateExternalId()}`;
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.storageType === StorageType.StorJ
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 }, 'Somethong went wrong with upload');
300
- console.log(error);
301
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
302
- throw error_;
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([
@@ -1,5 +1,6 @@
1
1
  export declare const getConfigName: (name: string) => string;
2
2
  export declare const preparePath: (rawPath: string) => string;
3
+ export declare const joinPaths: (...args: string[]) => string;
3
4
  export type ReadJsonFileParams = {
4
5
  path: string;
5
6
  };
@@ -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}`);
@@ -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 = () => progressBars[lastKey].stop(`${action} ${lastKey} ${done}`);
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
  };