@super-protocol/sp-cli 0.0.2-alpha.1 → 0.0.2-beta.10
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 +145 -260
- package/dist/commands/account/info.d.ts +21 -0
- package/dist/commands/account/info.js +43 -0
- package/dist/commands/auth/login.d.ts +2 -10
- package/dist/commands/auth/login.js +25 -75
- package/dist/commands/base.d.ts +2 -4
- package/dist/commands/base.js +8 -10
- package/dist/commands/config/add.js +9 -5
- package/dist/commands/config/base.d.ts +4 -3
- package/dist/commands/config/base.js +12 -14
- package/dist/commands/config/create.js +4 -4
- package/dist/commands/config/list.js +2 -2
- package/dist/commands/config/use.js +5 -3
- package/dist/commands/files/download.d.ts +15 -0
- package/dist/commands/files/download.js +53 -0
- package/dist/commands/files/upload.d.ts +18 -0
- package/dist/commands/files/upload.js +77 -0
- package/dist/commands/storage/base.d.ts +13 -0
- package/dist/commands/storage/base.js +123 -0
- package/dist/commands/storage/create.d.ts +11 -0
- package/dist/commands/storage/create.js +53 -0
- package/dist/commands/storage/select.d.ts +9 -0
- package/dist/commands/storage/select.js +46 -0
- package/dist/commands/storage/update.d.ts +14 -0
- package/dist/commands/storage/update.js +187 -0
- package/dist/config/config-file.schema.d.ts +4 -4
- package/dist/config/config-file.schema.js +1 -1
- package/dist/config/config.schema.d.ts +16 -15
- package/dist/config/config.schema.js +2 -10
- package/dist/config/resource.schema.d.ts +31 -0
- package/dist/config/resource.schema.js +14 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +2 -0
- package/dist/lib/container.d.ts +6 -7
- package/dist/lib/container.js +26 -26
- package/dist/managers/account-manager.js +13 -3
- package/dist/managers/config-file-manager.d.ts +1 -1
- package/dist/managers/config-file-manager.js +13 -8
- package/dist/middlewares/auth-middleware.js +5 -1
- package/dist/services/auth.service.d.ts +24 -0
- package/dist/services/auth.service.js +93 -0
- package/dist/services/storage.service.d.ts +71 -0
- package/dist/services/storage.service.js +308 -0
- package/dist/utils/helper.d.ts +5 -0
- package/dist/utils/helper.js +22 -0
- package/dist/utils/progress.d.ts +8 -0
- package/dist/utils/progress.js +27 -0
- package/oclif.manifest.json +368 -131
- package/package.json +12 -6
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { password, select, text } from '@clack/prompts';
|
|
2
|
+
import { StorageType } from '@super-protocol/provider-client';
|
|
3
|
+
import { BaseCommand } from '../../commands/base.js';
|
|
4
|
+
import { StorageService } from '../../services/storage.service.js';
|
|
5
|
+
export class BaseStorageCommand extends BaseCommand {
|
|
6
|
+
currentDir = process.cwd();
|
|
7
|
+
storageService;
|
|
8
|
+
async init() {
|
|
9
|
+
await super.init();
|
|
10
|
+
await this.container.initConfigManager().initProviderClient().build();
|
|
11
|
+
const { providerClient } = this.container;
|
|
12
|
+
this.storageService = new StorageService(providerClient, this.logger);
|
|
13
|
+
}
|
|
14
|
+
async promptS3Credentials() {
|
|
15
|
+
const region = 'us-east-1';
|
|
16
|
+
const readAccessKeyId = this.ensurePromptValue(await text({
|
|
17
|
+
message: 'Read access key ID',
|
|
18
|
+
validate(value) {
|
|
19
|
+
return value ? undefined : 'Read access key ID is required';
|
|
20
|
+
},
|
|
21
|
+
})).trim();
|
|
22
|
+
const readSecretAccessKey = this.ensurePromptValue(await password({
|
|
23
|
+
message: 'Read secret access key',
|
|
24
|
+
validate(value) {
|
|
25
|
+
return value ? undefined : 'Read secret access key is required';
|
|
26
|
+
},
|
|
27
|
+
})).trim();
|
|
28
|
+
const writeAccessKeyId = this.ensurePromptValue(await text({
|
|
29
|
+
message: 'Write access key ID',
|
|
30
|
+
validate(value) {
|
|
31
|
+
return value ? undefined : 'Write access key ID is required';
|
|
32
|
+
},
|
|
33
|
+
})).trim();
|
|
34
|
+
const writeSecretAccessKey = this.ensurePromptValue(await password({
|
|
35
|
+
message: 'Write secret access key',
|
|
36
|
+
validate(value) {
|
|
37
|
+
return value ? undefined : 'Write secret access key is required';
|
|
38
|
+
},
|
|
39
|
+
})).trim();
|
|
40
|
+
return {
|
|
41
|
+
readAccessKeyId,
|
|
42
|
+
readSecretAccessKey,
|
|
43
|
+
region,
|
|
44
|
+
writeAccessKeyId,
|
|
45
|
+
writeSecretAccessKey,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async promptStoragePayload() {
|
|
49
|
+
const storageType = this.ensurePromptValue(await select({
|
|
50
|
+
message: 'Select storage type',
|
|
51
|
+
options: [
|
|
52
|
+
{ label: 'S3', value: StorageType.S3 },
|
|
53
|
+
{ label: 'StorJ', value: StorageType.StorJ },
|
|
54
|
+
],
|
|
55
|
+
}));
|
|
56
|
+
const bucket = this.ensurePromptValue(await text({
|
|
57
|
+
message: 'Bucket name',
|
|
58
|
+
validate(value) {
|
|
59
|
+
return value ? undefined : 'Bucket is required';
|
|
60
|
+
},
|
|
61
|
+
})).trim();
|
|
62
|
+
const prefix = this.ensurePromptValue(await text({
|
|
63
|
+
defaultValue: '/',
|
|
64
|
+
message: 'Prefix',
|
|
65
|
+
validate(value) {
|
|
66
|
+
return value ? undefined : 'Prefix is required';
|
|
67
|
+
},
|
|
68
|
+
})).trim();
|
|
69
|
+
const storage = {
|
|
70
|
+
bucket,
|
|
71
|
+
prefix,
|
|
72
|
+
storageType,
|
|
73
|
+
};
|
|
74
|
+
if (storageType === StorageType.S3) {
|
|
75
|
+
storage.s3Credentials = await this.promptS3Credentials();
|
|
76
|
+
}
|
|
77
|
+
if (storageType === StorageType.StorJ) {
|
|
78
|
+
storage.storjCredentials = await this.promptStorJCredentials();
|
|
79
|
+
}
|
|
80
|
+
return storage;
|
|
81
|
+
}
|
|
82
|
+
async promptStorJCredentials() {
|
|
83
|
+
const readAccessToken = this.ensurePromptValue(await password({
|
|
84
|
+
message: 'Read access token',
|
|
85
|
+
validate(value) {
|
|
86
|
+
return value ? undefined : 'Read access token is required';
|
|
87
|
+
},
|
|
88
|
+
})).trim();
|
|
89
|
+
const writeAccessToken = this.ensurePromptValue(await password({
|
|
90
|
+
message: 'Write access token',
|
|
91
|
+
validate(value) {
|
|
92
|
+
return value ? undefined : 'Write access token is required';
|
|
93
|
+
},
|
|
94
|
+
})).trim();
|
|
95
|
+
return {
|
|
96
|
+
readAccessToken,
|
|
97
|
+
writeAccessToken,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
validateStoragePayload(payload) {
|
|
101
|
+
if (!payload || typeof payload !== 'object') {
|
|
102
|
+
this.error('Storage definition must be an object');
|
|
103
|
+
}
|
|
104
|
+
if (!payload.bucket) {
|
|
105
|
+
this.error('Storage definition missing "bucket"');
|
|
106
|
+
}
|
|
107
|
+
if (!payload.prefix) {
|
|
108
|
+
this.error('Storage definition missing "prefix"');
|
|
109
|
+
}
|
|
110
|
+
if (!payload.storageType) {
|
|
111
|
+
this.error('Storage definition missing "storageType"');
|
|
112
|
+
}
|
|
113
|
+
if (!Object.values(StorageType).includes(payload.storageType)) {
|
|
114
|
+
this.error(`Unsupported storage type: ${payload.storageType}`);
|
|
115
|
+
}
|
|
116
|
+
if (payload.storageType === StorageType.S3 && !payload.s3Credentials) {
|
|
117
|
+
this.error('S3 storage requires "s3Credentials"');
|
|
118
|
+
}
|
|
119
|
+
if (payload.storageType === StorageType.StorJ && !payload.storjCredentials) {
|
|
120
|
+
this.error('StorJ storage requires "storjCredentials"');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseStorageCommand } from './base.js';
|
|
2
|
+
export default class StorageCreate extends BaseStorageCommand<typeof StorageCreate> {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
fromFile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
notDefault: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
private loadStorageFromFile;
|
|
11
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { BaseStorageCommand } from './base.js';
|
|
5
|
+
export default class StorageCreate extends BaseStorageCommand {
|
|
6
|
+
static description = 'Create a new storage entry from file or interactive prompts.';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> storage create',
|
|
9
|
+
'<%= config.bin %> storage create --fromFile ./storage.json',
|
|
10
|
+
'<%= config.bin %> storage create --not-default',
|
|
11
|
+
];
|
|
12
|
+
static flags = {
|
|
13
|
+
fromFile: Flags.string({
|
|
14
|
+
aliases: ['fromFile'],
|
|
15
|
+
char: 'f',
|
|
16
|
+
description: 'Path to a JSON file that contains AddStorageDto payload.',
|
|
17
|
+
}),
|
|
18
|
+
notDefault: Flags.boolean({
|
|
19
|
+
description: 'Skip setting the created storage as default.',
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
async run() {
|
|
23
|
+
const { flags } = await this.parse(StorageCreate);
|
|
24
|
+
const payload = flags.fromFile
|
|
25
|
+
? await this.loadStorageFromFile(flags.fromFile)
|
|
26
|
+
: await this.promptStoragePayload();
|
|
27
|
+
try {
|
|
28
|
+
const storage = await this.storageService.createStorage(payload);
|
|
29
|
+
this.log(`Storage created: ${storage.id}`);
|
|
30
|
+
if (!flags.notDefault) {
|
|
31
|
+
await this.storageService.saveStorage(storage.id);
|
|
32
|
+
this.log('Storage set as default');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async loadStorageFromFile(filePath) {
|
|
40
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
41
|
+
? filePath
|
|
42
|
+
: path.join(this.currentDir, filePath);
|
|
43
|
+
try {
|
|
44
|
+
const fileContent = await fs.readFile(absolutePath, 'utf8');
|
|
45
|
+
const parsed = JSON.parse(fileContent);
|
|
46
|
+
this.validateStoragePayload(parsed);
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
this.error(`Failed to load storage definition: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseStorageCommand } from './base.js';
|
|
2
|
+
export default class StorageSelect extends BaseStorageCommand<typeof StorageSelect> {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { select } from '@clack/prompts';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import { StoragesUndefinedError } from '../../services/storage.service.js';
|
|
4
|
+
import { BaseStorageCommand } from './base.js';
|
|
5
|
+
export default class StorageSelect extends BaseStorageCommand {
|
|
6
|
+
static description = 'Select a storage that will be used by subsequent commands.';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> storage select',
|
|
9
|
+
'<%= config.bin %> storage select --id=2de3e3a4-0000-1111-2222-333344445555',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
id: Flags.string({
|
|
13
|
+
char: 'i',
|
|
14
|
+
description: 'Storage ID to select',
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags } = await this.parse(StorageSelect);
|
|
19
|
+
try {
|
|
20
|
+
let storageId = flags.id;
|
|
21
|
+
if (!storageId) {
|
|
22
|
+
const storages = await this.storageService.requestStorages();
|
|
23
|
+
storageId = this.ensurePromptValue(await select({
|
|
24
|
+
message: 'Select storage',
|
|
25
|
+
options: storages.map(storage => ({
|
|
26
|
+
label: `${storage.id} (${storage.storageType}) ${storage.bucket}/${storage.prefix} `,
|
|
27
|
+
value: storage.id,
|
|
28
|
+
})),
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
if (!storageId) {
|
|
32
|
+
this.error('Storage ID is required');
|
|
33
|
+
}
|
|
34
|
+
await this.storageService.saveStorage(storageId);
|
|
35
|
+
this.log(`Selected storage: ${storageId}`);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof StoragesUndefinedError) {
|
|
39
|
+
this.error('No storages available. Run "sp storage create" first.');
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseStorageCommand } from './base.js';
|
|
2
|
+
export default class StorageUpdate extends BaseStorageCommand<typeof StorageUpdate> {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
fromFile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
private ensureNonEmptyString;
|
|
11
|
+
private ensureUpdatePayload;
|
|
12
|
+
private loadStorageFromFile;
|
|
13
|
+
private promptStorageUpdate;
|
|
14
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { confirm, select, text, } from '@clack/prompts';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import { StorageType } from '@super-protocol/provider-client';
|
|
4
|
+
import fs from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { StoragesUndefinedError } from '../../services/storage.service.js';
|
|
7
|
+
import { BaseStorageCommand } from './base.js';
|
|
8
|
+
export default class StorageUpdate extends BaseStorageCommand {
|
|
9
|
+
static description = 'Update the configuration of an existing storage.';
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> storage update --id=2de3e3a4-0000-1111-2222-333344445555 --fromFile ./storage-update.json',
|
|
12
|
+
'<%= config.bin %> storage update --id=2de3e3a4-0000-1111-2222-333344445555',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
fromFile: Flags.string({
|
|
16
|
+
char: 'f',
|
|
17
|
+
description: 'Path to a JSON file that contains UpdateStorageDto payload.',
|
|
18
|
+
}),
|
|
19
|
+
id: Flags.string({
|
|
20
|
+
char: 'i',
|
|
21
|
+
description: 'Storage ID to update',
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
async run() {
|
|
25
|
+
const { flags } = await this.parse(StorageUpdate);
|
|
26
|
+
try {
|
|
27
|
+
const shouldRequestStorages = !flags.fromFile || !flags.id;
|
|
28
|
+
const storages = shouldRequestStorages ? await this.storageService.requestStorages() : undefined;
|
|
29
|
+
let storageId = flags.id;
|
|
30
|
+
if (!storageId) {
|
|
31
|
+
const options = (storages || []).filter(cp => !cp.isCentralized).map(storage => ({
|
|
32
|
+
label: `${storage.id} (${storage.storageType}) ${storage.bucket}/${storage.prefix} `,
|
|
33
|
+
value: storage.id,
|
|
34
|
+
}));
|
|
35
|
+
if (!options || options.length === 0) {
|
|
36
|
+
throw new StoragesUndefinedError('No storages available to update');
|
|
37
|
+
}
|
|
38
|
+
storageId = this.ensurePromptValue(await select({
|
|
39
|
+
message: 'Select storage to update',
|
|
40
|
+
options,
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
if (!storageId) {
|
|
44
|
+
this.error('Storage ID is required');
|
|
45
|
+
}
|
|
46
|
+
const storageToUpdate = storages?.find(storage => storage.id === storageId);
|
|
47
|
+
if (flags.id && storages && !storageToUpdate) {
|
|
48
|
+
this.error(`Storage with ID ${storageId} not found`);
|
|
49
|
+
}
|
|
50
|
+
const updatePayload = flags.fromFile
|
|
51
|
+
? await this.loadStorageFromFile(flags.fromFile)
|
|
52
|
+
: await this.promptStorageUpdate(storageToUpdate);
|
|
53
|
+
await this.storageService.updateStorage(storageId, updatePayload);
|
|
54
|
+
this.log(`Storage updated: ${storageId}`);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof StoragesUndefinedError) {
|
|
58
|
+
this.error('No storages available. Run "sp storage create" first.');
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
ensureNonEmptyString(value, fieldName) {
|
|
66
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
67
|
+
this.error(`${fieldName} must be a non-empty string`);
|
|
68
|
+
}
|
|
69
|
+
return value.trim();
|
|
70
|
+
}
|
|
71
|
+
ensureUpdatePayload(payload) {
|
|
72
|
+
if (!payload || typeof payload !== 'object') {
|
|
73
|
+
this.error('Storage update definition must be an object');
|
|
74
|
+
}
|
|
75
|
+
const updateFields = ['bucket', 'prefix', 'storageType', 's3Credentials', 'storjCredentials'];
|
|
76
|
+
const hasUpdates = updateFields.some(key => payload[key] !== undefined);
|
|
77
|
+
if (!hasUpdates) {
|
|
78
|
+
this.error('Update payload must include at least one property');
|
|
79
|
+
}
|
|
80
|
+
if (payload.storageType && !Object.values(StorageType).includes(payload.storageType)) {
|
|
81
|
+
this.error(`Unsupported storage type: ${payload.storageType}`);
|
|
82
|
+
}
|
|
83
|
+
if (payload.s3Credentials) {
|
|
84
|
+
const { readAccessKeyId, readSecretAccessKey, region, writeAccessKeyId, writeSecretAccessKey, } = payload.s3Credentials;
|
|
85
|
+
if (!readAccessKeyId || !readSecretAccessKey || !region || !writeAccessKeyId || !writeSecretAccessKey) {
|
|
86
|
+
this.error('S3 credentials must include readAccessKeyId, readSecretAccessKey, region, writeAccessKeyId and writeSecretAccessKey');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (payload.storjCredentials) {
|
|
90
|
+
const { readAccessToken, writeAccessToken } = payload.storjCredentials;
|
|
91
|
+
if (!readAccessToken || !writeAccessToken) {
|
|
92
|
+
this.error('StorJ credentials must include readAccessToken and writeAccessToken');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const sanitized = {};
|
|
96
|
+
if (payload.bucket !== undefined) {
|
|
97
|
+
sanitized.bucket = this.ensureNonEmptyString(payload.bucket, 'Bucket');
|
|
98
|
+
}
|
|
99
|
+
if (payload.prefix !== undefined) {
|
|
100
|
+
sanitized.prefix = this.ensureNonEmptyString(payload.prefix, 'Prefix');
|
|
101
|
+
}
|
|
102
|
+
if (payload.storageType) {
|
|
103
|
+
sanitized.storageType = payload.storageType;
|
|
104
|
+
}
|
|
105
|
+
if (payload.s3Credentials) {
|
|
106
|
+
sanitized.s3Credentials = {
|
|
107
|
+
readAccessKeyId: this.ensureNonEmptyString(payload.s3Credentials.readAccessKeyId, 'S3 credential "readAccessKeyId"'),
|
|
108
|
+
readSecretAccessKey: this.ensureNonEmptyString(payload.s3Credentials.readSecretAccessKey, 'S3 credential "readSecretAccessKey"'),
|
|
109
|
+
region: this.ensureNonEmptyString(payload.s3Credentials.region, 'S3 credential "region"'),
|
|
110
|
+
writeAccessKeyId: this.ensureNonEmptyString(payload.s3Credentials.writeAccessKeyId, 'S3 credential "writeAccessKeyId"'),
|
|
111
|
+
writeSecretAccessKey: this.ensureNonEmptyString(payload.s3Credentials.writeSecretAccessKey, 'S3 credential "writeSecretAccessKey"'),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (payload.storjCredentials) {
|
|
115
|
+
sanitized.storjCredentials = {
|
|
116
|
+
readAccessToken: this.ensureNonEmptyString(payload.storjCredentials.readAccessToken, 'StorJ credential "readAccessToken"'),
|
|
117
|
+
writeAccessToken: this.ensureNonEmptyString(payload.storjCredentials.writeAccessToken, 'StorJ credential "writeAccessToken"'),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return sanitized;
|
|
121
|
+
}
|
|
122
|
+
async loadStorageFromFile(filePath) {
|
|
123
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(this.currentDir, filePath);
|
|
124
|
+
try {
|
|
125
|
+
const fileContent = await fs.readFile(absolutePath, 'utf8');
|
|
126
|
+
const parsed = JSON.parse(fileContent);
|
|
127
|
+
return this.ensureUpdatePayload(parsed);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
this.error(`Failed to load storage update definition: ${error instanceof Error ? error.message : String(error)}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async promptStorageUpdate(existingStorage) {
|
|
134
|
+
const payload = {};
|
|
135
|
+
const bucket = this.ensurePromptValue(await text({
|
|
136
|
+
defaultValue: existingStorage?.bucket ?? '',
|
|
137
|
+
message: 'Bucket name (leave empty to keep current)',
|
|
138
|
+
})).trim();
|
|
139
|
+
if (bucket) {
|
|
140
|
+
payload.bucket = bucket;
|
|
141
|
+
}
|
|
142
|
+
const prefix = this.ensurePromptValue(await text({
|
|
143
|
+
defaultValue: existingStorage?.prefix ?? '',
|
|
144
|
+
message: 'Prefix (leave empty to keep current)',
|
|
145
|
+
})).trim();
|
|
146
|
+
if (prefix) {
|
|
147
|
+
payload.prefix = prefix;
|
|
148
|
+
}
|
|
149
|
+
let newStorageType;
|
|
150
|
+
const shouldChangeType = this.ensurePromptValue(await confirm({
|
|
151
|
+
initialValue: false,
|
|
152
|
+
message: existingStorage
|
|
153
|
+
? `Change storage type? (current: ${existingStorage.storageType})`
|
|
154
|
+
: 'Change storage type?',
|
|
155
|
+
}));
|
|
156
|
+
if (shouldChangeType) {
|
|
157
|
+
newStorageType = this.ensurePromptValue(await select({
|
|
158
|
+
message: 'Select new storage type',
|
|
159
|
+
options: [
|
|
160
|
+
{ label: 'Amazon S3', value: StorageType.S3 },
|
|
161
|
+
{ label: 'StorJ', value: StorageType.StorJ },
|
|
162
|
+
],
|
|
163
|
+
}));
|
|
164
|
+
payload.storageType = newStorageType;
|
|
165
|
+
}
|
|
166
|
+
const effectiveType = newStorageType ?? existingStorage?.storageType;
|
|
167
|
+
const credentialsRequired = Boolean(newStorageType);
|
|
168
|
+
if (effectiveType) {
|
|
169
|
+
const updateCredentials = credentialsRequired || this.ensurePromptValue(await confirm({
|
|
170
|
+
initialValue: false,
|
|
171
|
+
message: `Update ${effectiveType} credentials?`,
|
|
172
|
+
}));
|
|
173
|
+
if (updateCredentials) {
|
|
174
|
+
if (effectiveType === StorageType.S3) {
|
|
175
|
+
payload.s3Credentials = await this.promptS3Credentials();
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
payload.storjCredentials = await this.promptStorJCredentials();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (Object.keys(payload).length === 0) {
|
|
183
|
+
this.error('No changes provided');
|
|
184
|
+
}
|
|
185
|
+
return this.ensureUpdatePayload(payload);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Static } from '
|
|
2
|
-
export declare const configFileManagerSchema:
|
|
3
|
-
configs:
|
|
4
|
-
currentConfig:
|
|
1
|
+
import { Static, Type } from 'typebox';
|
|
2
|
+
export declare const configFileManagerSchema: Type.TObject<{
|
|
3
|
+
configs: Type.TArray<Type.TString>;
|
|
4
|
+
currentConfig: Type.TOptional<Type.TString>;
|
|
5
5
|
}>;
|
|
6
6
|
export type ConfigFileManagerData = Static<typeof configFileManagerSchema>;
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import { Static } from '
|
|
2
|
-
export declare const accountSchema:
|
|
3
|
-
address:
|
|
4
|
-
privateKey:
|
|
1
|
+
import { Static, Type } from 'typebox';
|
|
2
|
+
export declare const accountSchema: Type.TObject<{
|
|
3
|
+
address: Type.TString;
|
|
4
|
+
privateKey: Type.TString;
|
|
5
5
|
}>;
|
|
6
|
-
export declare const authSchema:
|
|
7
|
-
accessKey:
|
|
6
|
+
export declare const authSchema: Type.TObject<{
|
|
7
|
+
accessKey: Type.TString;
|
|
8
8
|
}>;
|
|
9
|
-
export declare const cliConfigSchema:
|
|
10
|
-
account:
|
|
11
|
-
address:
|
|
12
|
-
privateKey:
|
|
9
|
+
export declare const cliConfigSchema: Type.TObject<{
|
|
10
|
+
account: Type.TOptional<Type.TObject<{
|
|
11
|
+
address: Type.TString;
|
|
12
|
+
privateKey: Type.TString;
|
|
13
13
|
}>>;
|
|
14
|
-
auth:
|
|
15
|
-
accessKey:
|
|
14
|
+
auth: Type.TOptional<Type.TObject<{
|
|
15
|
+
accessKey: Type.TString;
|
|
16
16
|
}>>;
|
|
17
|
-
cookies:
|
|
18
|
-
name:
|
|
19
|
-
providerUrl:
|
|
17
|
+
cookies: Type.TOptional<Type.TUnknown>;
|
|
18
|
+
name: Type.TOptional<Type.TString>;
|
|
19
|
+
providerUrl: Type.TOptional<Type.TString>;
|
|
20
|
+
selectedStorage: Type.TOptional<Type.TString>;
|
|
20
21
|
}>;
|
|
21
22
|
export type CliConfig = Static<typeof cliConfigSchema>;
|
|
22
23
|
export type Account = Static<typeof accountSchema>;
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FormatRegistry.Set('url', (value) => {
|
|
3
|
-
try {
|
|
4
|
-
const u = new URL(value);
|
|
5
|
-
return u.protocol === 'http:' || u.protocol === 'https:';
|
|
6
|
-
}
|
|
7
|
-
catch {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
1
|
+
import { Type } from 'typebox';
|
|
11
2
|
export const accountSchema = Type.Object({
|
|
12
3
|
address: Type.String({ pattern: '^0x[a-fA-F0-9]{40}$' }),
|
|
13
4
|
privateKey: Type.String({ pattern: '^0x[a-fA-F0-9]{64}$' }),
|
|
@@ -21,4 +12,5 @@ export const cliConfigSchema = Type.Object({
|
|
|
21
12
|
cookies: Type.Optional(Type.Unknown()),
|
|
22
13
|
name: Type.Optional(Type.String()),
|
|
23
14
|
providerUrl: Type.Optional(Type.String({ format: 'url' })),
|
|
15
|
+
selectedStorage: Type.Optional(Type.String({ maxLength: 36, minLength: 36 })),
|
|
24
16
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ResourceType, StorageType } from '@super-protocol/provider-client';
|
|
2
|
+
import { Type } from 'typebox';
|
|
3
|
+
export declare const storageResourceSchema: Type.TObject<{
|
|
4
|
+
hash: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TObject<{
|
|
5
|
+
algo: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TEnum<typeof import("@super-protocol/dto-js").HashAlgorithm>;
|
|
6
|
+
hash: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
7
|
+
encoding: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TEnum<typeof import("@super-protocol/dto-js").Encoding>;
|
|
8
|
+
}>;
|
|
9
|
+
resource: Type.TObject<{
|
|
10
|
+
credentials: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TUnion<[import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TIntersect<[import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TObject<{
|
|
11
|
+
bucket: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
12
|
+
prefix: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
13
|
+
}>, import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TObject<{
|
|
14
|
+
token: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
15
|
+
}>]>, import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TIntersect<[import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TObject<{
|
|
16
|
+
bucket: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
17
|
+
prefix: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
18
|
+
}>, import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TObject<{
|
|
19
|
+
accessKeyId: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
20
|
+
secretKey: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
21
|
+
endpoint: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
22
|
+
}>]>, import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TObject<{
|
|
23
|
+
bucket: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
24
|
+
prefix: import("@sinclair/typebox", { with: { "resolution-mode": "require" } }).TString;
|
|
25
|
+
}>]>;
|
|
26
|
+
filepath: Type.TString;
|
|
27
|
+
storageType: Type.TEnum<[StorageType.S3, StorageType.FS, StorageType.StorJ]>;
|
|
28
|
+
type: Type.TEnum<[ResourceType.StorageProvider, ResourceType.Url]>;
|
|
29
|
+
}>;
|
|
30
|
+
size: Type.TNumber;
|
|
31
|
+
}>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HashSchema, StorageAccessCredentialsSchema } from '@super-protocol/dto-js';
|
|
2
|
+
import { ResourceType, StorageType } from '@super-protocol/provider-client';
|
|
3
|
+
import { Type } from 'typebox';
|
|
4
|
+
const storageProviderResourceSchema = Type.Object({
|
|
5
|
+
credentials: StorageAccessCredentialsSchema,
|
|
6
|
+
filepath: Type.String({ minLength: 1 }),
|
|
7
|
+
storageType: Type.Enum(StorageType),
|
|
8
|
+
type: Type.Enum(ResourceType),
|
|
9
|
+
});
|
|
10
|
+
export const storageResourceSchema = Type.Object({
|
|
11
|
+
hash: HashSchema,
|
|
12
|
+
resource: storageProviderResourceSchema,
|
|
13
|
+
size: Type.Number({ minimum: 0 }),
|
|
14
|
+
});
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export declare const FILE_ACCESS_PERMS = 384;
|
|
2
2
|
export declare const DIR_ACCESS_PERMS = 448;
|
|
3
3
|
export declare const PROVIDER_URL = "https://api.dp.superprotocol.com";
|
|
4
|
+
export declare const REFRESH_TOKEN_URI = "/api/auth/refresh-access";
|
|
5
|
+
export declare const S3_ENDPOINT = "https://gateway.storjshare.io";
|
package/dist/constants.js
CHANGED
|
@@ -3,3 +3,5 @@ export const FILE_ACCESS_PERMS = 0o600;
|
|
|
3
3
|
// Directory permissions. 'drwx------'
|
|
4
4
|
export const DIR_ACCESS_PERMS = 0o700;
|
|
5
5
|
export const PROVIDER_URL = 'https://api.dp.superprotocol.com';
|
|
6
|
+
export const REFRESH_TOKEN_URI = '/api/auth/refresh-access';
|
|
7
|
+
export const S3_ENDPOINT = 'https://gateway.storjshare.io';
|
package/dist/errors.d.ts
CHANGED
package/dist/errors.js
CHANGED
package/dist/lib/container.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { ProviderClient } from '@super-protocol/provider-client';
|
|
|
3
3
|
import { AccountManager, ConfigFileManager, ConfigManager } from '../managers/index.js';
|
|
4
4
|
interface RuntimeConfig {
|
|
5
5
|
configFile?: string;
|
|
6
|
-
url?: string;
|
|
7
6
|
}
|
|
8
7
|
export declare class AppContainer {
|
|
9
8
|
readonly cwdDir: string;
|
|
@@ -17,7 +16,6 @@ export declare class AppContainer {
|
|
|
17
16
|
private readonly initQueue;
|
|
18
17
|
private logger;
|
|
19
18
|
private runtimeConfigFile?;
|
|
20
|
-
private runtimeProviderUrl?;
|
|
21
19
|
private constructor();
|
|
22
20
|
static initialize(currentDir: string, config: Config): AppContainer;
|
|
23
21
|
get accountManager(): AccountManager;
|
|
@@ -25,13 +23,14 @@ export declare class AppContainer {
|
|
|
25
23
|
get configManager(): ConfigManager;
|
|
26
24
|
static get container(): AppContainer;
|
|
27
25
|
get providerClient(): ProviderClient;
|
|
28
|
-
build(): Promise<
|
|
29
|
-
initAccountManager(): this;
|
|
30
|
-
initConfigFileManager(): this;
|
|
31
|
-
initConfigManager(): this;
|
|
32
|
-
initProviderClient({ enableAuth, enableCookies }?: {
|
|
26
|
+
build(): Promise<this>;
|
|
27
|
+
initAccountManager(rebuild?: boolean): this;
|
|
28
|
+
initConfigFileManager(rebuild?: boolean): this;
|
|
29
|
+
initConfigManager(rebuild?: boolean): this;
|
|
30
|
+
initProviderClient({ enableAuth, enableCookies, rebuild }?: {
|
|
33
31
|
enableAuth?: boolean;
|
|
34
32
|
enableCookies?: boolean;
|
|
33
|
+
rebuild?: boolean;
|
|
35
34
|
}): this;
|
|
36
35
|
setupRuntimeConfig(config: RuntimeConfig): this;
|
|
37
36
|
private queueInit;
|