@super-protocol/sp-cli 0.0.2-alpha.1 → 0.0.2-beta.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/README.md +115 -12
- package/dist/commands/auth/login.d.ts +1 -0
- package/dist/commands/auth/login.js +19 -0
- package/dist/commands/storage/base.d.ts +8 -0
- package/dist/commands/storage/base.js +12 -0
- package/dist/commands/storage/create.d.ts +15 -0
- package/dist/commands/storage/create.js +169 -0
- package/dist/commands/storage/select.d.ts +9 -0
- package/dist/commands/storage/select.js +44 -0
- package/dist/commands/storage/update.d.ts +16 -0
- package/dist/commands/storage/update.js +239 -0
- package/dist/config/config.schema.d.ts +1 -0
- package/dist/config/config.schema.js +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +2 -0
- package/dist/lib/container.d.ts +2 -1
- package/dist/lib/container.js +5 -5
- package/dist/middlewares/auth-middleware.js +5 -1
- package/dist/services/storage.service.d.ts +34 -0
- package/dist/services/storage.service.js +113 -0
- package/oclif.manifest.json +242 -1
- package/package.json +7 -2
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { confirm, input, password, select, } from '@inquirer/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 { StoragesEmptyError } 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.requestStorage() : undefined;
|
|
29
|
+
let storageId = flags.id;
|
|
30
|
+
if (!storageId) {
|
|
31
|
+
const choices = (storages || []).filter(cp => !cp.isCentralized).map(storage => ({
|
|
32
|
+
name: `${storage.id} (${storage.storageType}) ${storage.bucket}/${storage.prefix} `,
|
|
33
|
+
value: storage.id,
|
|
34
|
+
}));
|
|
35
|
+
if (!choices || choices.length === 0) {
|
|
36
|
+
throw new StoragesEmptyError('No storages available to update');
|
|
37
|
+
}
|
|
38
|
+
storageId = await select({
|
|
39
|
+
choices,
|
|
40
|
+
message: 'Select storage to update',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (!storageId) {
|
|
44
|
+
this.error('Storage ID is required');
|
|
45
|
+
}
|
|
46
|
+
const storageToUpdate = storages?.find(storage => storage.id === storageId);
|
|
47
|
+
const updatePayload = flags.fromFile
|
|
48
|
+
? await this.loadStorageFromFile(flags.fromFile)
|
|
49
|
+
: await this.promptStorageUpdate(storageToUpdate);
|
|
50
|
+
await this.storageService.updateStorage(storageId, updatePayload);
|
|
51
|
+
this.log(`Storage updated: ${storageId}`);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error instanceof StoragesEmptyError) {
|
|
55
|
+
this.error('No storages available. Run "sp storage create" first.');
|
|
56
|
+
}
|
|
57
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
ensureNonEmptyString(value, fieldName) {
|
|
61
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
62
|
+
this.error(`${fieldName} must be a non-empty string`);
|
|
63
|
+
}
|
|
64
|
+
return value.trim();
|
|
65
|
+
}
|
|
66
|
+
ensureUpdatePayload(payload) {
|
|
67
|
+
if (!payload || typeof payload !== 'object') {
|
|
68
|
+
this.error('Storage update definition must be an object');
|
|
69
|
+
}
|
|
70
|
+
const updateFields = ['bucket', 'prefix', 'storageType', 's3Credentials', 'storjCredentials'];
|
|
71
|
+
const hasUpdates = updateFields.some(key => payload[key] !== undefined);
|
|
72
|
+
if (!hasUpdates) {
|
|
73
|
+
this.error('Update payload must include at least one property');
|
|
74
|
+
}
|
|
75
|
+
if (payload.storageType && !Object.values(StorageType).includes(payload.storageType)) {
|
|
76
|
+
this.error(`Unsupported storage type: ${payload.storageType}`);
|
|
77
|
+
}
|
|
78
|
+
if (payload.s3Credentials) {
|
|
79
|
+
const { readAccessKeyId, readSecretAccessKey, region, writeAccessKeyId, writeSecretAccessKey, } = payload.s3Credentials;
|
|
80
|
+
if (!readAccessKeyId || !readSecretAccessKey || !region || !writeAccessKeyId || !writeSecretAccessKey) {
|
|
81
|
+
this.error('S3 credentials must include readAccessKeyId, readSecretAccessKey, region, writeAccessKeyId and writeSecretAccessKey');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (payload.storjCredentials) {
|
|
85
|
+
const { readAccessToken, writeAccessToken } = payload.storjCredentials;
|
|
86
|
+
if (!readAccessToken || !writeAccessToken) {
|
|
87
|
+
this.error('StorJ credentials must include readAccessToken and writeAccessToken');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const sanitized = {};
|
|
91
|
+
if (payload.bucket !== undefined) {
|
|
92
|
+
sanitized.bucket = this.ensureNonEmptyString(payload.bucket, 'Bucket');
|
|
93
|
+
}
|
|
94
|
+
if (payload.prefix !== undefined) {
|
|
95
|
+
sanitized.prefix = this.ensureNonEmptyString(payload.prefix, 'Prefix');
|
|
96
|
+
}
|
|
97
|
+
if (payload.storageType) {
|
|
98
|
+
sanitized.storageType = payload.storageType;
|
|
99
|
+
}
|
|
100
|
+
if (payload.s3Credentials) {
|
|
101
|
+
sanitized.s3Credentials = {
|
|
102
|
+
readAccessKeyId: this.ensureNonEmptyString(payload.s3Credentials.readAccessKeyId, 'S3 credential "readAccessKeyId"'),
|
|
103
|
+
readSecretAccessKey: this.ensureNonEmptyString(payload.s3Credentials.readSecretAccessKey, 'S3 credential "readSecretAccessKey"'),
|
|
104
|
+
region: this.ensureNonEmptyString(payload.s3Credentials.region, 'S3 credential "region"'),
|
|
105
|
+
writeAccessKeyId: this.ensureNonEmptyString(payload.s3Credentials.writeAccessKeyId, 'S3 credential "writeAccessKeyId"'),
|
|
106
|
+
writeSecretAccessKey: this.ensureNonEmptyString(payload.s3Credentials.writeSecretAccessKey, 'S3 credential "writeSecretAccessKey"'),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (payload.storjCredentials) {
|
|
110
|
+
sanitized.storjCredentials = {
|
|
111
|
+
readAccessToken: this.ensureNonEmptyString(payload.storjCredentials.readAccessToken, 'StorJ credential "readAccessToken"'),
|
|
112
|
+
writeAccessToken: this.ensureNonEmptyString(payload.storjCredentials.writeAccessToken, 'StorJ credential "writeAccessToken"'),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return sanitized;
|
|
116
|
+
}
|
|
117
|
+
async loadStorageFromFile(filePath) {
|
|
118
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(this.currentDir, filePath);
|
|
119
|
+
try {
|
|
120
|
+
const fileContent = await fs.readFile(absolutePath, 'utf8');
|
|
121
|
+
const parsed = JSON.parse(fileContent);
|
|
122
|
+
return this.ensureUpdatePayload(parsed);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
this.error(`Failed to load storage update definition: ${error instanceof Error ? error.message : String(error)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async promptS3Credentials() {
|
|
129
|
+
const region = await input({
|
|
130
|
+
message: 'AWS region',
|
|
131
|
+
validate(value) {
|
|
132
|
+
return value ? true : 'Region is required';
|
|
133
|
+
},
|
|
134
|
+
}).then(value => value.trim());
|
|
135
|
+
const readAccessKeyId = await input({
|
|
136
|
+
message: 'Read access key ID',
|
|
137
|
+
validate(value) {
|
|
138
|
+
return value ? true : 'Read access key ID is required';
|
|
139
|
+
},
|
|
140
|
+
}).then(value => value.trim());
|
|
141
|
+
const readSecretAccessKey = await password({
|
|
142
|
+
message: 'Read secret access key',
|
|
143
|
+
validate(value) {
|
|
144
|
+
return value ? true : 'Read secret access key is required';
|
|
145
|
+
},
|
|
146
|
+
}).then(value => value.trim());
|
|
147
|
+
const writeAccessKeyId = await input({
|
|
148
|
+
message: 'Write access key ID',
|
|
149
|
+
validate(value) {
|
|
150
|
+
return value ? true : 'Write access key ID is required';
|
|
151
|
+
},
|
|
152
|
+
}).then(value => value.trim());
|
|
153
|
+
const writeSecretAccessKey = await password({
|
|
154
|
+
message: 'Write secret access key',
|
|
155
|
+
validate(value) {
|
|
156
|
+
return value ? true : 'Write secret access key is required';
|
|
157
|
+
},
|
|
158
|
+
}).then(value => value.trim());
|
|
159
|
+
return {
|
|
160
|
+
readAccessKeyId,
|
|
161
|
+
readSecretAccessKey,
|
|
162
|
+
region,
|
|
163
|
+
writeAccessKeyId,
|
|
164
|
+
writeSecretAccessKey,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async promptStorageUpdate(existingStorage) {
|
|
168
|
+
const payload = {};
|
|
169
|
+
const bucket = await input({
|
|
170
|
+
default: existingStorage?.bucket ?? '',
|
|
171
|
+
message: 'Bucket name (leave empty to keep current)',
|
|
172
|
+
}).then(value => value.trim());
|
|
173
|
+
if (bucket) {
|
|
174
|
+
payload.bucket = bucket;
|
|
175
|
+
}
|
|
176
|
+
const prefix = await input({
|
|
177
|
+
default: existingStorage?.prefix ?? '',
|
|
178
|
+
message: 'Prefix (leave empty to keep current)',
|
|
179
|
+
}).then(value => value.trim());
|
|
180
|
+
if (prefix) {
|
|
181
|
+
payload.prefix = prefix;
|
|
182
|
+
}
|
|
183
|
+
let newStorageType;
|
|
184
|
+
const shouldChangeType = await confirm({
|
|
185
|
+
default: false,
|
|
186
|
+
message: existingStorage
|
|
187
|
+
? `Change storage type? (current: ${existingStorage.storageType})`
|
|
188
|
+
: 'Change storage type?',
|
|
189
|
+
});
|
|
190
|
+
if (shouldChangeType) {
|
|
191
|
+
newStorageType = await select({
|
|
192
|
+
choices: [
|
|
193
|
+
{ name: 'Amazon S3', value: StorageType.S3 },
|
|
194
|
+
{ name: 'StorJ', value: StorageType.StorJ },
|
|
195
|
+
],
|
|
196
|
+
message: 'Select new storage type',
|
|
197
|
+
});
|
|
198
|
+
payload.storageType = newStorageType;
|
|
199
|
+
}
|
|
200
|
+
const effectiveType = newStorageType ?? existingStorage?.storageType;
|
|
201
|
+
const credentialsRequired = Boolean(newStorageType);
|
|
202
|
+
if (effectiveType) {
|
|
203
|
+
const updateCredentials = credentialsRequired || await confirm({
|
|
204
|
+
default: false,
|
|
205
|
+
message: `Update ${effectiveType} credentials?`,
|
|
206
|
+
});
|
|
207
|
+
if (updateCredentials) {
|
|
208
|
+
if (effectiveType === StorageType.S3) {
|
|
209
|
+
payload.s3Credentials = await this.promptS3Credentials();
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
payload.storjCredentials = await this.promptStorJCredentials();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (Object.keys(payload).length === 0) {
|
|
217
|
+
this.error('No changes provided');
|
|
218
|
+
}
|
|
219
|
+
return this.ensureUpdatePayload(payload);
|
|
220
|
+
}
|
|
221
|
+
async promptStorJCredentials() {
|
|
222
|
+
const readAccessToken = await password({
|
|
223
|
+
message: 'Read access token',
|
|
224
|
+
validate(value) {
|
|
225
|
+
return value ? true : 'Read access token is required';
|
|
226
|
+
},
|
|
227
|
+
}).then(value => value.trim());
|
|
228
|
+
const writeAccessToken = await password({
|
|
229
|
+
message: 'Write access token',
|
|
230
|
+
validate(value) {
|
|
231
|
+
return value ? true : 'Write access token is required';
|
|
232
|
+
},
|
|
233
|
+
}).then(value => value.trim());
|
|
234
|
+
return {
|
|
235
|
+
readAccessToken,
|
|
236
|
+
writeAccessToken,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -17,6 +17,7 @@ export declare const cliConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
17
17
|
cookies: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnknown>;
|
|
18
18
|
name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
19
19
|
providerUrl: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
20
|
+
selectedStorage: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
20
21
|
}>;
|
|
21
22
|
export type CliConfig = Static<typeof cliConfigSchema>;
|
|
22
23
|
export type Account = Static<typeof accountSchema>;
|
|
@@ -21,4 +21,5 @@ export const cliConfigSchema = Type.Object({
|
|
|
21
21
|
cookies: Type.Optional(Type.Unknown()),
|
|
22
22
|
name: Type.Optional(Type.String()),
|
|
23
23
|
providerUrl: Type.Optional(Type.String({ format: 'url' })),
|
|
24
|
+
selectedStorage: Type.Optional(Type.String({ maxLength: 36, minLength: 36 })),
|
|
24
25
|
});
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
package/dist/errors.d.ts
CHANGED
package/dist/errors.js
CHANGED
package/dist/lib/container.d.ts
CHANGED
|
@@ -29,9 +29,10 @@ export declare class AppContainer {
|
|
|
29
29
|
initAccountManager(): this;
|
|
30
30
|
initConfigFileManager(): this;
|
|
31
31
|
initConfigManager(): this;
|
|
32
|
-
initProviderClient({ enableAuth, enableCookies }?: {
|
|
32
|
+
initProviderClient({ enableAuth, enableCookies, rebuild }?: {
|
|
33
33
|
enableAuth?: boolean;
|
|
34
34
|
enableCookies?: boolean;
|
|
35
|
+
rebuild?: boolean;
|
|
35
36
|
}): this;
|
|
36
37
|
setupRuntimeConfig(config: RuntimeConfig): this;
|
|
37
38
|
private queueInit;
|
package/dist/lib/container.js
CHANGED
|
@@ -104,10 +104,10 @@ export class AppContainer {
|
|
|
104
104
|
this._configManager = configManager;
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
|
-
initProviderClient({ enableAuth = true, enableCookies = true } = {}) {
|
|
107
|
+
initProviderClient({ enableAuth = true, enableCookies = true, rebuild = false } = {}) {
|
|
108
108
|
this.initConfigManager();
|
|
109
109
|
return this.queueInit('providerClient', async () => {
|
|
110
|
-
if (this._providerClient) {
|
|
110
|
+
if (this._providerClient && !rebuild) {
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
await this.waitFor('configManager');
|
|
@@ -143,7 +143,7 @@ export class AppContainer {
|
|
|
143
143
|
providerClient.use(authMiddleware);
|
|
144
144
|
}
|
|
145
145
|
this._providerClient = providerClient;
|
|
146
|
-
});
|
|
146
|
+
}, rebuild);
|
|
147
147
|
}
|
|
148
148
|
setupRuntimeConfig(config) {
|
|
149
149
|
if (this.initQueue.size > 0) {
|
|
@@ -160,8 +160,8 @@ export class AppContainer {
|
|
|
160
160
|
}
|
|
161
161
|
return this;
|
|
162
162
|
}
|
|
163
|
-
queueInit(name, task) {
|
|
164
|
-
if (!this.initQueue.has(name)) {
|
|
163
|
+
queueInit(name, task, force = false) {
|
|
164
|
+
if (!this.initQueue.has(name) || force) {
|
|
165
165
|
const promise = task();
|
|
166
166
|
this.initQueue.set(name, promise);
|
|
167
167
|
this.initPromises.push(promise);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { decode } from 'jsonwebtoken';
|
|
2
|
+
import { REFRESH_TOKEN_URI } from '../constants.js';
|
|
2
3
|
import { AuthorizationRequiredError } from '../errors.js';
|
|
3
4
|
function isAccessKeyExpired(accessKey) {
|
|
4
5
|
try {
|
|
@@ -23,7 +24,7 @@ export async function createAuthMiddleware(configManager, providerClient, logger
|
|
|
23
24
|
logger.info('Performed refresh token');
|
|
24
25
|
let refreshResult;
|
|
25
26
|
try {
|
|
26
|
-
refreshResult = await providerClient.POST(
|
|
27
|
+
refreshResult = await providerClient.POST(REFRESH_TOKEN_URI);
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
30
|
logger.error({ err: error }, 'failure refresh token');
|
|
@@ -76,6 +77,9 @@ export async function createAuthMiddleware(configManager, providerClient, logger
|
|
|
76
77
|
}
|
|
77
78
|
const middleware = {
|
|
78
79
|
async onRequest({ request }) {
|
|
80
|
+
if (request.url.endsWith(REFRESH_TOKEN_URI)) {
|
|
81
|
+
return request;
|
|
82
|
+
}
|
|
79
83
|
const accessToken = await ensureAccessKey();
|
|
80
84
|
if (accessToken) {
|
|
81
85
|
request.headers.set('Authorization', `Bearer ${accessToken}`);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AddStorageDto, ProviderClient, StorageResponseDto, UpdateStorageDto } from '@super-protocol/provider-client';
|
|
2
|
+
import pino from 'pino';
|
|
3
|
+
import { IConfigManager } from '../interfaces/config-manager.interface.js';
|
|
4
|
+
export declare class StorageError extends Error {
|
|
5
|
+
}
|
|
6
|
+
export declare class StoragesEmptyError extends StorageError {
|
|
7
|
+
}
|
|
8
|
+
export declare class StorageCreateError extends StorageError {
|
|
9
|
+
}
|
|
10
|
+
export declare class StorageUpdateError extends StorageError {
|
|
11
|
+
}
|
|
12
|
+
export declare class StorageService {
|
|
13
|
+
private readonly configManager;
|
|
14
|
+
private readonly providerClient;
|
|
15
|
+
private readonly logger;
|
|
16
|
+
constructor(configManager: IConfigManager, providerClient: ProviderClient, logger: pino.BaseLogger);
|
|
17
|
+
createStorage(storage: AddStorageDto): Promise<StorageResponseDto>;
|
|
18
|
+
hasStorage(): Promise<boolean>;
|
|
19
|
+
initStorage(): Promise<{
|
|
20
|
+
bucket: string;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
id: string;
|
|
23
|
+
isCentralized: boolean;
|
|
24
|
+
prefix: string;
|
|
25
|
+
s3Credentials?: import("@super-protocol/provider-client").components["schemas"]["S3CredentialsResponseDto"];
|
|
26
|
+
storageType: import("@super-protocol/provider-client").components["schemas"]["StorageType"];
|
|
27
|
+
storjCredentials?: import("@super-protocol/provider-client").components["schemas"]["StorJCredentialsResponseDto"];
|
|
28
|
+
updatedAt: string;
|
|
29
|
+
userId: string;
|
|
30
|
+
}>;
|
|
31
|
+
requestStorage(): Promise<StorageResponseDto[]>;
|
|
32
|
+
saveStorage(selectedStorage: string): Promise<void>;
|
|
33
|
+
updateStorage(id: string, storage: UpdateStorageDto): Promise<StorageResponseDto>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ProviderClientError } from '../errors.js';
|
|
2
|
+
export class StorageError extends Error {
|
|
3
|
+
}
|
|
4
|
+
export class StoragesEmptyError extends StorageError {
|
|
5
|
+
}
|
|
6
|
+
export class StorageCreateError extends StorageError {
|
|
7
|
+
}
|
|
8
|
+
export class StorageUpdateError extends StorageError {
|
|
9
|
+
}
|
|
10
|
+
export class StorageService {
|
|
11
|
+
configManager;
|
|
12
|
+
providerClient;
|
|
13
|
+
logger;
|
|
14
|
+
constructor(configManager, providerClient, logger) {
|
|
15
|
+
this.configManager = configManager;
|
|
16
|
+
this.providerClient = providerClient;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
}
|
|
19
|
+
async createStorage(storage) {
|
|
20
|
+
this.logger.info({ storageType: storage.storageType }, 'Creating storage');
|
|
21
|
+
try {
|
|
22
|
+
const { data, error } = await this.providerClient.POST('/api/storages', {
|
|
23
|
+
body: storage,
|
|
24
|
+
});
|
|
25
|
+
if (error && error.message) {
|
|
26
|
+
this.logger.error({ err: error }, 'Failed to create storage');
|
|
27
|
+
throw new StorageCreateError(error.message);
|
|
28
|
+
}
|
|
29
|
+
if (!data) {
|
|
30
|
+
this.logger.error('Provider returned empty storage response');
|
|
31
|
+
throw new StorageCreateError('Incorrect response');
|
|
32
|
+
}
|
|
33
|
+
this.logger.info({ storageId: data.id }, 'Storage created successfully');
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
this.logger.error({ err: error }, 'Storage creation failed');
|
|
38
|
+
const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
|
|
39
|
+
throw error_;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async hasStorage() {
|
|
43
|
+
return Boolean(await this.configManager.get('selectedStorage'));
|
|
44
|
+
}
|
|
45
|
+
async initStorage() {
|
|
46
|
+
this.logger.info('Requesting storage initialization');
|
|
47
|
+
try {
|
|
48
|
+
const result = await this.providerClient.POST('/api/storages/centralized');
|
|
49
|
+
if (result.error) {
|
|
50
|
+
this.logger.error({ err: result.error }, 'Failed to initialize storage');
|
|
51
|
+
throw new StorageCreateError(result.error.message);
|
|
52
|
+
}
|
|
53
|
+
this.logger.info('Storage initialized successfully');
|
|
54
|
+
return result.data;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
this.logger.error({ err: error }, 'Storage initialization failed');
|
|
58
|
+
const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
|
|
59
|
+
throw error_;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async requestStorage() {
|
|
63
|
+
let data;
|
|
64
|
+
this.logger.info('Requesting available storages');
|
|
65
|
+
try {
|
|
66
|
+
const result = await this.providerClient.GET('/api/storages');
|
|
67
|
+
data = result.data;
|
|
68
|
+
if (result.error) {
|
|
69
|
+
this.logger.error({ err: result.error }, 'Failed to fetch storages');
|
|
70
|
+
throw new StorageError(result.error.message);
|
|
71
|
+
}
|
|
72
|
+
if (!data || !data?.length) {
|
|
73
|
+
this.logger.warn('Storages list is empty');
|
|
74
|
+
throw new StoragesEmptyError('Storages is empty please create or import first one');
|
|
75
|
+
}
|
|
76
|
+
this.logger.debug({ count: data.length }, 'Received storages list');
|
|
77
|
+
return data;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
this.logger.error({ err: error }, 'Failed to request storages');
|
|
81
|
+
const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
|
|
82
|
+
throw error_;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async saveStorage(selectedStorage) {
|
|
86
|
+
this.logger.info({ selectedStorage }, 'Saving selected storage');
|
|
87
|
+
await this.configManager.set('selectedStorage', selectedStorage);
|
|
88
|
+
}
|
|
89
|
+
async updateStorage(id, storage) {
|
|
90
|
+
this.logger.info({ storageId: id }, 'Updating storage');
|
|
91
|
+
try {
|
|
92
|
+
const { data, error } = await this.providerClient.PATCH('/api/storages/{id}', {
|
|
93
|
+
body: storage,
|
|
94
|
+
params: { path: { id } },
|
|
95
|
+
});
|
|
96
|
+
if (error) {
|
|
97
|
+
this.logger.error({ err: error, storageId: id }, 'Failed to update storage');
|
|
98
|
+
throw new StorageUpdateError(error?.message);
|
|
99
|
+
}
|
|
100
|
+
if (!data) {
|
|
101
|
+
this.logger.error({ storageId: id }, 'Provider returned empty storage update response');
|
|
102
|
+
throw new StorageUpdateError('Incorrect response');
|
|
103
|
+
}
|
|
104
|
+
this.logger.info({ storageId: data.id }, 'Storage updated successfully');
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
this.logger.error({ err: error, storageId: id }, 'Failed to update storage');
|
|
109
|
+
const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
|
|
110
|
+
throw error_;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|