@orcapt/cli 1.0.0
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/LICENSE +22 -0
- package/QUICK_START.md +241 -0
- package/README.md +949 -0
- package/bin/orca.js +406 -0
- package/package.json +58 -0
- package/src/commands/db.js +248 -0
- package/src/commands/fetch-doc.js +220 -0
- package/src/commands/kickstart-node.js +431 -0
- package/src/commands/kickstart-python.js +360 -0
- package/src/commands/lambda.js +736 -0
- package/src/commands/login.js +277 -0
- package/src/commands/storage.js +911 -0
- package/src/commands/ui.js +286 -0
- package/src/config.js +62 -0
- package/src/utils/docker-helper.js +357 -0
- package/src/utils/index.js +349 -0
|
@@ -0,0 +1,911 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orca Storage Commands
|
|
3
|
+
* Manage S3-like storage buckets and files via Orca Deploy API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const ora = require('ora');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const https = require('https');
|
|
11
|
+
const http = require('http');
|
|
12
|
+
const { getCredentials } = require('./login');
|
|
13
|
+
const { API_BASE_URL, API_ENDPOINTS } = require('../config');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Make API request to Orca Deploy API
|
|
17
|
+
*/
|
|
18
|
+
function makeApiRequest(method, endpoint, credentials, body = null) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const url = new URL(endpoint, API_BASE_URL);
|
|
21
|
+
const isHttps = url.protocol === 'https:';
|
|
22
|
+
const httpModule = isHttps ? https : http;
|
|
23
|
+
|
|
24
|
+
const options = {
|
|
25
|
+
hostname: url.hostname,
|
|
26
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
27
|
+
path: url.pathname,
|
|
28
|
+
method: method,
|
|
29
|
+
headers: {
|
|
30
|
+
'x-workspace': credentials.workspace,
|
|
31
|
+
'x-token': credentials.token,
|
|
32
|
+
'x-mode': credentials.mode || 'dev',
|
|
33
|
+
'Content-Type': 'application/json'
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const req = httpModule.request(options, (res) => {
|
|
38
|
+
let data = '';
|
|
39
|
+
|
|
40
|
+
res.on('data', (chunk) => {
|
|
41
|
+
data += chunk;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
res.on('end', () => {
|
|
45
|
+
try {
|
|
46
|
+
const response = JSON.parse(data);
|
|
47
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
48
|
+
resolve(response);
|
|
49
|
+
} else {
|
|
50
|
+
reject({ statusCode: res.statusCode, response });
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
reject(new Error(`Invalid response: ${data}`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
req.on('error', (error) => {
|
|
59
|
+
reject(error);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (body) {
|
|
63
|
+
req.write(JSON.stringify(body));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
req.end();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Upload file using multipart/form-data
|
|
72
|
+
*/
|
|
73
|
+
function uploadFileToApi(endpoint, credentials, filePath, bucketName, options = {}) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const url = new URL(endpoint.replace('{bucketName}', bucketName), API_BASE_URL);
|
|
76
|
+
const isHttps = url.protocol === 'https:';
|
|
77
|
+
const httpModule = isHttps ? https : http;
|
|
78
|
+
|
|
79
|
+
const fileStream = fs.createReadStream(filePath);
|
|
80
|
+
const fileName = path.basename(filePath);
|
|
81
|
+
const boundary = `----FormBoundary${Date.now()}`;
|
|
82
|
+
|
|
83
|
+
const folderPath = options.folder || '';
|
|
84
|
+
|
|
85
|
+
// Build multipart form data
|
|
86
|
+
let formData = '';
|
|
87
|
+
|
|
88
|
+
// Add folder_path field
|
|
89
|
+
if (folderPath) {
|
|
90
|
+
formData += `--${boundary}\r\n`;
|
|
91
|
+
formData += `Content-Disposition: form-data; name="folder_path"\r\n\r\n`;
|
|
92
|
+
formData += `${folderPath}\r\n`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Add visibility field
|
|
96
|
+
if (options.visibility) {
|
|
97
|
+
formData += `--${boundary}\r\n`;
|
|
98
|
+
formData += `Content-Disposition: form-data; name="visibility"\r\n\r\n`;
|
|
99
|
+
formData += `${options.visibility}\r\n`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Add generate_url field
|
|
103
|
+
formData += `--${boundary}\r\n`;
|
|
104
|
+
formData += `Content-Disposition: form-data; name="generate_url"\r\n\r\n`;
|
|
105
|
+
formData += `true\r\n`;
|
|
106
|
+
|
|
107
|
+
// Add file field header
|
|
108
|
+
formData += `--${boundary}\r\n`;
|
|
109
|
+
formData += `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`;
|
|
110
|
+
formData += `Content-Type: application/octet-stream\r\n\r\n`;
|
|
111
|
+
|
|
112
|
+
const formDataBuffer = Buffer.from(formData, 'utf8');
|
|
113
|
+
const endBoundary = Buffer.from(`\r\n--${boundary}--\r\n`, 'utf8');
|
|
114
|
+
|
|
115
|
+
const fileStats = fs.statSync(filePath);
|
|
116
|
+
const contentLength = formDataBuffer.length + fileStats.size + endBoundary.length;
|
|
117
|
+
|
|
118
|
+
const requestOptions = {
|
|
119
|
+
hostname: url.hostname,
|
|
120
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
121
|
+
path: url.pathname,
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: {
|
|
124
|
+
'x-workspace': credentials.workspace,
|
|
125
|
+
'x-token': credentials.token,
|
|
126
|
+
'x-mode': credentials.mode || 'dev',
|
|
127
|
+
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
|
128
|
+
'Content-Length': contentLength
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const req = httpModule.request(requestOptions, (res) => {
|
|
133
|
+
let data = '';
|
|
134
|
+
|
|
135
|
+
res.on('data', (chunk) => {
|
|
136
|
+
data += chunk;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
res.on('end', () => {
|
|
140
|
+
try {
|
|
141
|
+
const response = JSON.parse(data);
|
|
142
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
143
|
+
resolve(response);
|
|
144
|
+
} else {
|
|
145
|
+
reject({ statusCode: res.statusCode, response });
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
reject(new Error(`Invalid response: ${data}`));
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
req.on('error', (error) => {
|
|
154
|
+
reject(error);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Write form data header
|
|
158
|
+
req.write(formDataBuffer);
|
|
159
|
+
|
|
160
|
+
// Stream file
|
|
161
|
+
fileStream.on('data', (chunk) => {
|
|
162
|
+
req.write(chunk);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
fileStream.on('end', () => {
|
|
166
|
+
req.write(endBoundary);
|
|
167
|
+
req.end();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
fileStream.on('error', (error) => {
|
|
171
|
+
reject(error);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Format bytes to human readable
|
|
178
|
+
*/
|
|
179
|
+
function formatBytes(bytes) {
|
|
180
|
+
if (bytes === 0) return '0 B';
|
|
181
|
+
const k = 1024;
|
|
182
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
183
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
184
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Check authentication
|
|
189
|
+
*/
|
|
190
|
+
function requireAuth() {
|
|
191
|
+
const credentials = getCredentials();
|
|
192
|
+
if (!credentials) {
|
|
193
|
+
console.log(chalk.red('\nā Not authenticated'));
|
|
194
|
+
console.log(chalk.cyan('Please run:'), chalk.yellow('orca login'), chalk.cyan('first\n'));
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
return credentials;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Bucket Create Command
|
|
202
|
+
*/
|
|
203
|
+
async function bucketCreate(bucketName, options = {}) {
|
|
204
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
205
|
+
console.log(chalk.cyan('šŖ£ Creating Storage Bucket'));
|
|
206
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
207
|
+
|
|
208
|
+
const credentials = requireAuth();
|
|
209
|
+
|
|
210
|
+
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
211
|
+
console.log(chalk.white('Workspace: '), chalk.yellow(credentials.workspace));
|
|
212
|
+
console.log(chalk.white('Visibility: '), chalk.yellow(options.public ? 'public' : 'private'));
|
|
213
|
+
if (options.description) {
|
|
214
|
+
console.log(chalk.white('Description:'), chalk.yellow(options.description));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const spinner = ora('Creating bucket...').start();
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const requestBody = {
|
|
221
|
+
bucket_name: bucketName,
|
|
222
|
+
visibility: options.public ? 'public' : 'private',
|
|
223
|
+
versioning_enabled: options.versioning || false,
|
|
224
|
+
encryption_enabled: options.encryption !== false,
|
|
225
|
+
encryption_type: options.encryptionType || 'AES256'
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
if (options.description) {
|
|
229
|
+
requestBody.description = options.description;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const response = await makeApiRequest(
|
|
233
|
+
'POST',
|
|
234
|
+
API_ENDPOINTS.STORAGE_BUCKET_CREATE,
|
|
235
|
+
credentials,
|
|
236
|
+
requestBody
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
spinner.succeed(chalk.green('ā Bucket created successfully!'));
|
|
240
|
+
|
|
241
|
+
console.log(chalk.cyan('\nš¦ Bucket Details:'));
|
|
242
|
+
console.log(chalk.white(' Name: '), chalk.yellow(response.bucket.bucket_name));
|
|
243
|
+
console.log(chalk.white(' AWS Bucket: '), chalk.gray(response.bucket.aws_bucket_name));
|
|
244
|
+
console.log(chalk.white(' Region: '), chalk.yellow(response.bucket.region));
|
|
245
|
+
console.log(chalk.white(' Status: '), chalk.green(response.bucket.status));
|
|
246
|
+
console.log(chalk.white(' Visibility: '), chalk.yellow(response.bucket.visibility));
|
|
247
|
+
console.log(chalk.white(' Encryption: '), chalk.yellow(response.bucket.encryption_enabled ? 'Enabled' : 'Disabled'));
|
|
248
|
+
|
|
249
|
+
console.log(chalk.cyan('\nš” Next Steps:'));
|
|
250
|
+
console.log(chalk.white(' Upload file: '), chalk.yellow(`orca storage upload ${bucketName} <file-path>`));
|
|
251
|
+
console.log(chalk.white(' List files: '), chalk.yellow(`orca storage files ${bucketName}`));
|
|
252
|
+
console.log('');
|
|
253
|
+
|
|
254
|
+
} catch (error) {
|
|
255
|
+
spinner.fail(chalk.red('ā Failed to create bucket'));
|
|
256
|
+
|
|
257
|
+
if (error.response) {
|
|
258
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
259
|
+
if (error.statusCode === 409) {
|
|
260
|
+
console.log(chalk.yellow(' Bucket name already exists for this workspace'));
|
|
261
|
+
} else if (error.statusCode === 422) {
|
|
262
|
+
console.log(chalk.yellow(' Invalid bucket name or parameters'));
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
266
|
+
}
|
|
267
|
+
console.log('');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Bucket List Command
|
|
274
|
+
*/
|
|
275
|
+
async function bucketList() {
|
|
276
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
277
|
+
console.log(chalk.cyan('š Listing Storage Buckets'));
|
|
278
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
279
|
+
|
|
280
|
+
const credentials = requireAuth();
|
|
281
|
+
console.log(chalk.white('Workspace:'), chalk.yellow(credentials.workspace));
|
|
282
|
+
|
|
283
|
+
const spinner = ora('Fetching buckets...').start();
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
const response = await makeApiRequest(
|
|
287
|
+
'GET',
|
|
288
|
+
API_ENDPOINTS.STORAGE_BUCKET_LIST,
|
|
289
|
+
credentials
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
spinner.succeed(chalk.green(`ā Found ${response.count} bucket(s)`));
|
|
293
|
+
|
|
294
|
+
if (response.count === 0) {
|
|
295
|
+
console.log(chalk.yellow('\nš No buckets found'));
|
|
296
|
+
console.log(chalk.cyan('\nš” Create your first bucket:'));
|
|
297
|
+
console.log(chalk.white(' '), chalk.yellow('orca storage bucket create my-bucket'));
|
|
298
|
+
console.log('');
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.log('');
|
|
303
|
+
console.log(chalk.white('ā'.repeat(100)));
|
|
304
|
+
console.log(
|
|
305
|
+
chalk.white('NAME').padEnd(25),
|
|
306
|
+
chalk.white('FILES').padEnd(10),
|
|
307
|
+
chalk.white('SIZE').padEnd(15),
|
|
308
|
+
chalk.white('VISIBILITY').padEnd(15),
|
|
309
|
+
chalk.white('STATUS').padEnd(15),
|
|
310
|
+
chalk.white('CREATED')
|
|
311
|
+
);
|
|
312
|
+
console.log(chalk.white('ā'.repeat(100)));
|
|
313
|
+
|
|
314
|
+
response.buckets.forEach(bucket => {
|
|
315
|
+
const name = bucket.bucket_name.padEnd(25);
|
|
316
|
+
const files = String(bucket.file_count).padEnd(10);
|
|
317
|
+
const size = bucket.total_size.padEnd(15);
|
|
318
|
+
const visibility = bucket.visibility.padEnd(15);
|
|
319
|
+
const status = bucket.status === 'active'
|
|
320
|
+
? chalk.green(bucket.status.padEnd(15))
|
|
321
|
+
: chalk.yellow(bucket.status.padEnd(15));
|
|
322
|
+
const created = new Date(bucket.created_at).toLocaleDateString();
|
|
323
|
+
|
|
324
|
+
console.log(
|
|
325
|
+
chalk.yellow(name),
|
|
326
|
+
chalk.white(files),
|
|
327
|
+
chalk.cyan(size),
|
|
328
|
+
visibility === 'public ' ? chalk.green(visibility) : chalk.gray(visibility),
|
|
329
|
+
status,
|
|
330
|
+
chalk.gray(created)
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
console.log(chalk.white('ā'.repeat(100)));
|
|
335
|
+
console.log('');
|
|
336
|
+
|
|
337
|
+
} catch (error) {
|
|
338
|
+
spinner.fail(chalk.red('ā Failed to list buckets'));
|
|
339
|
+
|
|
340
|
+
if (error.response) {
|
|
341
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}\n`));
|
|
342
|
+
} else {
|
|
343
|
+
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
344
|
+
}
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* File Upload Command
|
|
351
|
+
*/
|
|
352
|
+
async function fileUpload(bucketName, localPath, options = {}) {
|
|
353
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
354
|
+
console.log(chalk.cyan('š¤ Uploading File'));
|
|
355
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
356
|
+
|
|
357
|
+
const credentials = requireAuth();
|
|
358
|
+
|
|
359
|
+
// Check if file exists
|
|
360
|
+
if (!fs.existsSync(localPath)) {
|
|
361
|
+
console.log(chalk.red(`ā File not found: ${localPath}\n`));
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const fileStats = fs.statSync(localPath);
|
|
366
|
+
const fileSizeMB = (fileStats.size / (1024 * 1024)).toFixed(2);
|
|
367
|
+
const fileName = path.basename(localPath);
|
|
368
|
+
|
|
369
|
+
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
370
|
+
console.log(chalk.white('File: '), chalk.yellow(fileName));
|
|
371
|
+
console.log(chalk.white('Local Path: '), chalk.gray(localPath));
|
|
372
|
+
console.log(chalk.white('Size: '), chalk.yellow(`${fileSizeMB} MB`));
|
|
373
|
+
|
|
374
|
+
if (options.folder) {
|
|
375
|
+
console.log(chalk.white('Folder: '), chalk.yellow(options.folder));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
console.log(chalk.white('Visibility: '), chalk.yellow(options.public ? 'public' : 'private'));
|
|
379
|
+
|
|
380
|
+
const spinner = ora('Uploading file...').start();
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
const uploadOptions = {
|
|
384
|
+
folder: options.folder || '',
|
|
385
|
+
visibility: options.public ? 'public' : 'private'
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const response = await uploadFileToApi(
|
|
389
|
+
API_ENDPOINTS.STORAGE_UPLOAD,
|
|
390
|
+
credentials,
|
|
391
|
+
localPath,
|
|
392
|
+
bucketName,
|
|
393
|
+
uploadOptions
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
spinner.succeed(chalk.green('ā File uploaded successfully!'));
|
|
397
|
+
|
|
398
|
+
console.log(chalk.cyan('\nš File Details:'));
|
|
399
|
+
console.log(chalk.white(' Name: '), chalk.yellow(response.file.file_name));
|
|
400
|
+
console.log(chalk.white(' Key: '), chalk.gray(response.file.file_key));
|
|
401
|
+
console.log(chalk.white(' Size: '), chalk.yellow(response.file.file_size));
|
|
402
|
+
console.log(chalk.white(' Type: '), chalk.yellow(response.file.mime_type));
|
|
403
|
+
console.log(chalk.white(' Visibility: '), chalk.yellow(response.file.visibility));
|
|
404
|
+
|
|
405
|
+
if (response.file.download_url) {
|
|
406
|
+
console.log(chalk.cyan('\nš Download URL (valid for 60 minutes):'));
|
|
407
|
+
console.log(chalk.gray(response.file.download_url));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
console.log(chalk.cyan('\nš” Next Steps:'));
|
|
411
|
+
console.log(chalk.white(' List files: '), chalk.yellow(`orca storage files ${bucketName}`));
|
|
412
|
+
console.log(chalk.white(' Download: '), chalk.yellow(`orca storage download ${bucketName} ${response.file.file_key}`));
|
|
413
|
+
console.log('');
|
|
414
|
+
|
|
415
|
+
} catch (error) {
|
|
416
|
+
spinner.fail(chalk.red('ā Upload failed'));
|
|
417
|
+
|
|
418
|
+
if (error.response) {
|
|
419
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
420
|
+
if (error.statusCode === 404) {
|
|
421
|
+
console.log(chalk.yellow(' Bucket not found'));
|
|
422
|
+
} else if (error.statusCode === 413) {
|
|
423
|
+
console.log(chalk.yellow(' File too large (max 100MB)'));
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
427
|
+
}
|
|
428
|
+
console.log('');
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* File Download Command
|
|
435
|
+
*/
|
|
436
|
+
async function fileDownload(bucketName, fileKey, localPath) {
|
|
437
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
438
|
+
console.log(chalk.cyan('š„ Downloading File'));
|
|
439
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
440
|
+
|
|
441
|
+
const credentials = requireAuth();
|
|
442
|
+
|
|
443
|
+
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
444
|
+
console.log(chalk.white('File Key: '), chalk.yellow(fileKey));
|
|
445
|
+
console.log(chalk.white('Local Path: '), chalk.yellow(localPath || 'current directory'));
|
|
446
|
+
|
|
447
|
+
const spinner = ora('Getting download URL...').start();
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
// Get pre-signed download URL from API
|
|
451
|
+
const encodedFileKey = encodeURIComponent(fileKey);
|
|
452
|
+
const response = await makeApiRequest(
|
|
453
|
+
'GET',
|
|
454
|
+
API_ENDPOINTS.STORAGE_DOWNLOAD.replace('{bucketName}', bucketName).replace('{fileKey}', encodedFileKey),
|
|
455
|
+
credentials
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
spinner.text = 'Downloading file...';
|
|
459
|
+
|
|
460
|
+
// Download file from S3 using pre-signed URL
|
|
461
|
+
const downloadUrl = response.download_url;
|
|
462
|
+
const fileName = response.file.file_name;
|
|
463
|
+
const outputPath = localPath || fileName;
|
|
464
|
+
|
|
465
|
+
// Download file using the pre-signed URL
|
|
466
|
+
await new Promise((resolve, reject) => {
|
|
467
|
+
const urlObj = new URL(downloadUrl);
|
|
468
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
469
|
+
const httpModule = isHttps ? https : http;
|
|
470
|
+
|
|
471
|
+
const fileWriter = fs.createWriteStream(outputPath);
|
|
472
|
+
|
|
473
|
+
httpModule.get(downloadUrl, (res) => {
|
|
474
|
+
if (res.statusCode !== 200) {
|
|
475
|
+
reject(new Error(`Failed to download: ${res.statusCode}`));
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
res.pipe(fileWriter);
|
|
480
|
+
|
|
481
|
+
fileWriter.on('finish', () => {
|
|
482
|
+
fileWriter.close();
|
|
483
|
+
resolve();
|
|
484
|
+
});
|
|
485
|
+
}).on('error', (err) => {
|
|
486
|
+
fs.unlink(outputPath, () => {});
|
|
487
|
+
reject(err);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
fileWriter.on('error', (err) => {
|
|
491
|
+
fs.unlink(outputPath, () => {});
|
|
492
|
+
reject(err);
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
spinner.succeed(chalk.green('ā File downloaded successfully!'));
|
|
497
|
+
|
|
498
|
+
console.log(chalk.cyan('\nš File Details:'));
|
|
499
|
+
console.log(chalk.white(' Name: '), chalk.yellow(response.file.file_name));
|
|
500
|
+
console.log(chalk.white(' Size: '), chalk.yellow(formatBytes(response.file.file_size_bytes)));
|
|
501
|
+
console.log(chalk.white(' Type: '), chalk.yellow(response.file.mime_type));
|
|
502
|
+
console.log(chalk.white(' Location: '), chalk.gray(path.resolve(outputPath)));
|
|
503
|
+
console.log('');
|
|
504
|
+
|
|
505
|
+
} catch (error) {
|
|
506
|
+
spinner.fail(chalk.red('ā Download failed'));
|
|
507
|
+
|
|
508
|
+
if (error.response) {
|
|
509
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
510
|
+
if (error.statusCode === 404) {
|
|
511
|
+
console.log(chalk.yellow(' File or bucket not found'));
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
515
|
+
}
|
|
516
|
+
console.log('');
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Bucket Info Command
|
|
523
|
+
*/
|
|
524
|
+
async function bucketInfo(bucketName) {
|
|
525
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
526
|
+
console.log(chalk.cyan('ā¹ļø Bucket Information'));
|
|
527
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
528
|
+
|
|
529
|
+
const credentials = requireAuth();
|
|
530
|
+
const spinner = ora('Fetching bucket info...').start();
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
const response = await makeApiRequest(
|
|
534
|
+
'GET',
|
|
535
|
+
API_ENDPOINTS.STORAGE_BUCKET_LIST.replace('/list', `/${bucketName}`),
|
|
536
|
+
credentials
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
spinner.succeed(chalk.green('ā Bucket info retrieved'));
|
|
540
|
+
|
|
541
|
+
const bucket = response.bucket;
|
|
542
|
+
|
|
543
|
+
console.log(chalk.cyan('\nš¦ Bucket Details:'));
|
|
544
|
+
console.log(chalk.white(' Name: '), chalk.yellow(bucket.bucket_name));
|
|
545
|
+
console.log(chalk.white(' AWS Bucket: '), chalk.gray(bucket.aws_bucket_name));
|
|
546
|
+
console.log(chalk.white(' Region: '), chalk.yellow(bucket.region));
|
|
547
|
+
console.log(chalk.white(' Status: '), bucket.status === 'active' ? chalk.green(bucket.status) : chalk.yellow(bucket.status));
|
|
548
|
+
console.log(chalk.white(' Visibility: '), bucket.visibility === 'public' ? chalk.green(bucket.visibility) : chalk.gray(bucket.visibility));
|
|
549
|
+
console.log(chalk.white(' Files: '), chalk.yellow(bucket.file_count));
|
|
550
|
+
console.log(chalk.white(' Total Size: '), chalk.cyan(bucket.total_size));
|
|
551
|
+
console.log(chalk.white(' Versioning: '), bucket.versioning_enabled ? chalk.green('Enabled') : chalk.gray('Disabled'));
|
|
552
|
+
console.log(chalk.white(' Encryption: '), bucket.encryption_enabled ? chalk.green(`Enabled (${bucket.encryption_type})`) : chalk.gray('Disabled'));
|
|
553
|
+
|
|
554
|
+
if (bucket.description) {
|
|
555
|
+
console.log(chalk.white(' Description: '), chalk.gray(bucket.description));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
console.log(chalk.white(' Created: '), chalk.gray(new Date(bucket.created_at).toLocaleString()));
|
|
559
|
+
console.log('');
|
|
560
|
+
|
|
561
|
+
} catch (error) {
|
|
562
|
+
spinner.fail(chalk.red('ā Failed to get bucket info'));
|
|
563
|
+
|
|
564
|
+
if (error.response) {
|
|
565
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
566
|
+
if (error.statusCode === 404) {
|
|
567
|
+
console.log(chalk.yellow(' Bucket not found'));
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
571
|
+
}
|
|
572
|
+
console.log('');
|
|
573
|
+
process.exit(1);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Bucket Delete Command
|
|
579
|
+
*/
|
|
580
|
+
async function bucketDelete(bucketName, options = {}) {
|
|
581
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
582
|
+
console.log(chalk.cyan('šļø Deleting Storage Bucket'));
|
|
583
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
584
|
+
|
|
585
|
+
const credentials = requireAuth();
|
|
586
|
+
|
|
587
|
+
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
588
|
+
console.log(chalk.white('Workspace:'), chalk.yellow(credentials.workspace));
|
|
589
|
+
|
|
590
|
+
if (options.force) {
|
|
591
|
+
console.log(chalk.yellow('\nā ļø Force delete enabled - all files will be deleted'));
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const spinner = ora('Deleting bucket...').start();
|
|
595
|
+
|
|
596
|
+
try {
|
|
597
|
+
const endpoint = API_ENDPOINTS.STORAGE_BUCKET_LIST.replace('/list', `/${bucketName}`) +
|
|
598
|
+
(options.force ? '?force=true' : '');
|
|
599
|
+
|
|
600
|
+
await makeApiRequest('DELETE', endpoint, credentials);
|
|
601
|
+
|
|
602
|
+
spinner.succeed(chalk.green('ā Bucket deleted successfully!'));
|
|
603
|
+
console.log('');
|
|
604
|
+
|
|
605
|
+
} catch (error) {
|
|
606
|
+
spinner.fail(chalk.red('ā Failed to delete bucket'));
|
|
607
|
+
|
|
608
|
+
if (error.response) {
|
|
609
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
610
|
+
if (error.statusCode === 404) {
|
|
611
|
+
console.log(chalk.yellow(' Bucket not found'));
|
|
612
|
+
} else if (error.statusCode === 400 && error.response.file_count) {
|
|
613
|
+
console.log(chalk.yellow(` Bucket contains ${error.response.file_count} file(s)`));
|
|
614
|
+
console.log(chalk.cyan(' Use --force to delete anyway:'));
|
|
615
|
+
console.log(chalk.white(' '), chalk.yellow(`orca storage bucket delete ${bucketName} --force`));
|
|
616
|
+
}
|
|
617
|
+
} else {
|
|
618
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
619
|
+
}
|
|
620
|
+
console.log('');
|
|
621
|
+
process.exit(1);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* File List Command
|
|
627
|
+
*/
|
|
628
|
+
async function fileList(bucketName, options = {}) {
|
|
629
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
630
|
+
console.log(chalk.cyan('š Listing Files'));
|
|
631
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
632
|
+
|
|
633
|
+
const credentials = requireAuth();
|
|
634
|
+
|
|
635
|
+
console.log(chalk.white('Bucket:'), chalk.yellow(bucketName));
|
|
636
|
+
if (options.folder) {
|
|
637
|
+
console.log(chalk.white('Folder:'), chalk.yellow(options.folder));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const spinner = ora('Fetching files...').start();
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
let endpoint = API_ENDPOINTS.STORAGE_FILE_LIST.replace('{bucketName}', bucketName);
|
|
644
|
+
const params = [];
|
|
645
|
+
|
|
646
|
+
if (options.folder) {
|
|
647
|
+
params.push(`folder_path=${encodeURIComponent(options.folder)}`);
|
|
648
|
+
}
|
|
649
|
+
if (options.page) {
|
|
650
|
+
params.push(`page=${options.page}`);
|
|
651
|
+
}
|
|
652
|
+
if (options.perPage) {
|
|
653
|
+
params.push(`per_page=${options.perPage}`);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (params.length > 0) {
|
|
657
|
+
endpoint += '?' + params.join('&');
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const response = await makeApiRequest('GET', endpoint, credentials);
|
|
661
|
+
|
|
662
|
+
spinner.succeed(chalk.green(`ā Found ${response.pagination.total} file(s)`));
|
|
663
|
+
|
|
664
|
+
if (response.pagination.total === 0) {
|
|
665
|
+
console.log(chalk.yellow('\nš No files found'));
|
|
666
|
+
console.log(chalk.cyan('\nš” Upload a file:'));
|
|
667
|
+
console.log(chalk.white(' '), chalk.yellow(`orca storage upload ${bucketName} <file-path>`));
|
|
668
|
+
console.log('');
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
console.log('');
|
|
673
|
+
console.log(chalk.white('ā'.repeat(120)));
|
|
674
|
+
console.log(
|
|
675
|
+
chalk.white('NAME').padEnd(40),
|
|
676
|
+
chalk.white('SIZE').padEnd(15),
|
|
677
|
+
chalk.white('TYPE').padEnd(20),
|
|
678
|
+
chalk.white('DOWNLOADS').padEnd(12),
|
|
679
|
+
chalk.white('UPLOADED')
|
|
680
|
+
);
|
|
681
|
+
console.log(chalk.white('ā'.repeat(120)));
|
|
682
|
+
|
|
683
|
+
response.files.forEach(file => {
|
|
684
|
+
const name = (file.file_name.length > 38
|
|
685
|
+
? file.file_name.substring(0, 35) + '...'
|
|
686
|
+
: file.file_name).padEnd(40);
|
|
687
|
+
const size = file.file_size.padEnd(15);
|
|
688
|
+
const type = (file.mime_type.length > 18
|
|
689
|
+
? file.mime_type.substring(0, 15) + '...'
|
|
690
|
+
: file.mime_type).padEnd(20);
|
|
691
|
+
const downloads = String(file.download_count).padEnd(12);
|
|
692
|
+
const uploaded = new Date(file.uploaded_at).toLocaleDateString();
|
|
693
|
+
|
|
694
|
+
console.log(
|
|
695
|
+
chalk.yellow(name),
|
|
696
|
+
chalk.cyan(size),
|
|
697
|
+
chalk.gray(type),
|
|
698
|
+
chalk.white(downloads),
|
|
699
|
+
chalk.gray(uploaded)
|
|
700
|
+
);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
console.log(chalk.white('ā'.repeat(120)));
|
|
704
|
+
|
|
705
|
+
if (response.pagination.last_page > 1) {
|
|
706
|
+
console.log(chalk.gray(`Page ${response.pagination.current_page} of ${response.pagination.last_page}`));
|
|
707
|
+
if (response.pagination.current_page < response.pagination.last_page) {
|
|
708
|
+
console.log(chalk.cyan('Next page: '), chalk.yellow(`orca storage files ${bucketName} --page ${response.pagination.current_page + 1}`));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
console.log('');
|
|
713
|
+
|
|
714
|
+
} catch (error) {
|
|
715
|
+
spinner.fail(chalk.red('ā Failed to list files'));
|
|
716
|
+
|
|
717
|
+
if (error.response) {
|
|
718
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
719
|
+
if (error.statusCode === 404) {
|
|
720
|
+
console.log(chalk.yellow(' Bucket not found'));
|
|
721
|
+
}
|
|
722
|
+
} else {
|
|
723
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
724
|
+
}
|
|
725
|
+
console.log('');
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* File Delete Command
|
|
732
|
+
*/
|
|
733
|
+
async function fileDelete(bucketName, fileKey) {
|
|
734
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
735
|
+
console.log(chalk.cyan('šļø Deleting File'));
|
|
736
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
737
|
+
|
|
738
|
+
const credentials = requireAuth();
|
|
739
|
+
|
|
740
|
+
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
741
|
+
console.log(chalk.white('File Key:'), chalk.yellow(fileKey));
|
|
742
|
+
|
|
743
|
+
const spinner = ora('Deleting file...').start();
|
|
744
|
+
|
|
745
|
+
try {
|
|
746
|
+
const encodedFileKey = encodeURIComponent(fileKey);
|
|
747
|
+
const endpoint = API_ENDPOINTS.STORAGE_FILE_DELETE
|
|
748
|
+
.replace('{bucketName}', bucketName)
|
|
749
|
+
.replace('{fileKey}', encodedFileKey);
|
|
750
|
+
|
|
751
|
+
await makeApiRequest('DELETE', endpoint, credentials);
|
|
752
|
+
|
|
753
|
+
spinner.succeed(chalk.green('ā File deleted successfully!'));
|
|
754
|
+
console.log('');
|
|
755
|
+
|
|
756
|
+
} catch (error) {
|
|
757
|
+
spinner.fail(chalk.red('ā Failed to delete file'));
|
|
758
|
+
|
|
759
|
+
if (error.response) {
|
|
760
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
761
|
+
if (error.statusCode === 404) {
|
|
762
|
+
console.log(chalk.yellow(' File or bucket not found'));
|
|
763
|
+
}
|
|
764
|
+
} else {
|
|
765
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
766
|
+
}
|
|
767
|
+
console.log('');
|
|
768
|
+
process.exit(1);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Permission Add Command
|
|
774
|
+
*/
|
|
775
|
+
async function permissionAdd(bucketName, options = {}) {
|
|
776
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
777
|
+
console.log(chalk.cyan('š Adding Permission'));
|
|
778
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
779
|
+
|
|
780
|
+
const credentials = requireAuth();
|
|
781
|
+
|
|
782
|
+
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
783
|
+
console.log(chalk.white('Target Type: '), chalk.yellow(options.targetType || 'user'));
|
|
784
|
+
console.log(chalk.white('Target ID: '), chalk.yellow(options.targetId || 'N/A'));
|
|
785
|
+
console.log(chalk.white('Resource Type:'), chalk.yellow(options.resourceType || 'bucket'));
|
|
786
|
+
|
|
787
|
+
const spinner = ora('Adding permission...').start();
|
|
788
|
+
|
|
789
|
+
try {
|
|
790
|
+
const requestBody = {
|
|
791
|
+
target_type: options.targetType || 'user',
|
|
792
|
+
target_id: options.targetId,
|
|
793
|
+
resource_type: options.resourceType || 'bucket',
|
|
794
|
+
resource_path: options.resourcePath || null,
|
|
795
|
+
can_read: options.read || false,
|
|
796
|
+
can_write: options.write || false,
|
|
797
|
+
can_delete: options.delete || false,
|
|
798
|
+
can_list: options.list || false,
|
|
799
|
+
valid_until: options.validUntil || null,
|
|
800
|
+
reason: options.reason || null
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
const endpoint = API_ENDPOINTS.STORAGE_PERMISSION_ADD
|
|
804
|
+
.replace('{bucketName}', bucketName);
|
|
805
|
+
|
|
806
|
+
const response = await makeApiRequest('POST', endpoint, credentials, requestBody);
|
|
807
|
+
|
|
808
|
+
spinner.succeed(chalk.green('ā Permission added successfully!'));
|
|
809
|
+
|
|
810
|
+
console.log(chalk.cyan('\nš Permission Details:'));
|
|
811
|
+
console.log(chalk.white(' ID: '), chalk.yellow(response.permission.id));
|
|
812
|
+
console.log(chalk.white(' Target: '), chalk.yellow(`${response.permission.target_type}:${response.permission.target_id || 'all'}`));
|
|
813
|
+
console.log(chalk.white(' Resource: '), chalk.yellow(`${response.permission.resource_type}:${response.permission.resource_path || 'all'}`));
|
|
814
|
+
console.log(chalk.white(' Permissions: '), chalk.green(response.permission.permissions.join(', ')));
|
|
815
|
+
|
|
816
|
+
if (response.permission.valid_until) {
|
|
817
|
+
console.log(chalk.white(' Valid Until: '), chalk.gray(new Date(response.permission.valid_until).toLocaleString()));
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
console.log('');
|
|
821
|
+
|
|
822
|
+
} catch (error) {
|
|
823
|
+
spinner.fail(chalk.red('ā Failed to add permission'));
|
|
824
|
+
|
|
825
|
+
if (error.response) {
|
|
826
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}`));
|
|
827
|
+
if (error.statusCode === 404) {
|
|
828
|
+
console.log(chalk.yellow(' Bucket not found'));
|
|
829
|
+
} else if (error.statusCode === 422) {
|
|
830
|
+
console.log(chalk.yellow(' Invalid permission parameters'));
|
|
831
|
+
}
|
|
832
|
+
} else {
|
|
833
|
+
console.log(chalk.red(`\nā ${error.message}`));
|
|
834
|
+
}
|
|
835
|
+
console.log('');
|
|
836
|
+
process.exit(1);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Permission List Command
|
|
842
|
+
*/
|
|
843
|
+
async function permissionList(bucketName) {
|
|
844
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
845
|
+
console.log(chalk.cyan('š Listing Permissions'));
|
|
846
|
+
console.log(chalk.cyan('============================================================\n'));
|
|
847
|
+
|
|
848
|
+
const credentials = requireAuth();
|
|
849
|
+
console.log(chalk.white('Bucket:'), chalk.yellow(bucketName));
|
|
850
|
+
|
|
851
|
+
const spinner = ora('Fetching permissions...').start();
|
|
852
|
+
|
|
853
|
+
try {
|
|
854
|
+
const endpoint = API_ENDPOINTS.STORAGE_PERMISSION_LIST
|
|
855
|
+
.replace('{bucketName}', bucketName);
|
|
856
|
+
|
|
857
|
+
const response = await makeApiRequest('GET', endpoint, credentials);
|
|
858
|
+
|
|
859
|
+
spinner.succeed(chalk.green(`ā Found ${response.count} permission(s)`));
|
|
860
|
+
|
|
861
|
+
if (response.count === 0) {
|
|
862
|
+
console.log(chalk.yellow('\nš No permissions found'));
|
|
863
|
+
console.log(chalk.cyan('\nš” Add a permission:'));
|
|
864
|
+
console.log(chalk.white(' '), chalk.yellow(`orca storage permission add ${bucketName} --target-type user --target-id USER_ID --read`));
|
|
865
|
+
console.log('');
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
console.log('');
|
|
870
|
+
response.permissions.forEach(perm => {
|
|
871
|
+
const statusIcon = perm.is_valid ? 'ā' : 'ā';
|
|
872
|
+
const statusColor = perm.is_valid ? chalk.green : chalk.red;
|
|
873
|
+
|
|
874
|
+
console.log(statusColor(`${statusIcon} Permission #${perm.id}`));
|
|
875
|
+
console.log(chalk.white(' Target: '), chalk.yellow(`${perm.target_type}:${perm.target_id || 'all'}`));
|
|
876
|
+
console.log(chalk.white(' Resource: '), chalk.yellow(`${perm.resource_type}:${perm.resource_path || 'all'}`));
|
|
877
|
+
console.log(chalk.white(' Actions: '), chalk.green(perm.permissions.join(', ')));
|
|
878
|
+
console.log(chalk.white(' Status: '), statusColor(perm.status));
|
|
879
|
+
|
|
880
|
+
if (perm.valid_until) {
|
|
881
|
+
console.log(chalk.white(' Valid Until:'), chalk.gray(new Date(perm.valid_until).toLocaleString()));
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
console.log('');
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
} catch (error) {
|
|
888
|
+
spinner.fail(chalk.red('ā Failed to list permissions'));
|
|
889
|
+
|
|
890
|
+
if (error.response) {
|
|
891
|
+
console.log(chalk.red(`\nā ${error.response.message || 'Unknown error'}\n`));
|
|
892
|
+
} else {
|
|
893
|
+
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
894
|
+
}
|
|
895
|
+
process.exit(1);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
module.exports = {
|
|
900
|
+
bucketCreate,
|
|
901
|
+
bucketList,
|
|
902
|
+
bucketInfo,
|
|
903
|
+
bucketDelete,
|
|
904
|
+
fileUpload,
|
|
905
|
+
fileDownload,
|
|
906
|
+
fileList,
|
|
907
|
+
fileDelete,
|
|
908
|
+
permissionAdd,
|
|
909
|
+
permissionList
|
|
910
|
+
};
|
|
911
|
+
|