@orcapt/cli 1.0.2 ā 1.0.4
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 +0 -16
- package/bin/orca.js +9 -37
- package/package.json +1 -1
- package/src/commands/db.js +4 -29
- package/src/commands/lambda.js +105 -116
- package/src/commands/storage.js +43 -247
- package/src/utils/errorHandler.js +153 -0
package/README.md
CHANGED
|
@@ -409,22 +409,6 @@ orcapt storage download my-bucket file.txt ./downloads/
|
|
|
409
409
|
orcapt storage delete my-bucket file.txt
|
|
410
410
|
```
|
|
411
411
|
|
|
412
|
-
#### Permission Management
|
|
413
|
-
|
|
414
|
-
```bash
|
|
415
|
-
# Add permission
|
|
416
|
-
orcapt storage permission add my-bucket \
|
|
417
|
-
--target-type=user \
|
|
418
|
-
--target-id=user123 \
|
|
419
|
-
--read \
|
|
420
|
-
--write
|
|
421
|
-
|
|
422
|
-
# List permissions
|
|
423
|
-
orcapt storage permission list my-bucket
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
---
|
|
427
|
-
|
|
428
412
|
### šļø Database Commands
|
|
429
413
|
|
|
430
414
|
Create and manage PostgreSQL databases.
|
package/bin/orca.js
CHANGED
|
@@ -15,17 +15,15 @@ const { login, isLoggedIn, getCredentials, clearCredentials } = require('../src/
|
|
|
15
15
|
const { uiInit, uiStart, uiRemove } = require('../src/commands/ui');
|
|
16
16
|
const { dbCreate, dbList, dbRemove } = require('../src/commands/db');
|
|
17
17
|
const fetchDoc = require('../src/commands/fetch-doc');
|
|
18
|
-
const {
|
|
19
|
-
bucketCreate,
|
|
20
|
-
bucketList,
|
|
18
|
+
const {
|
|
19
|
+
bucketCreate,
|
|
20
|
+
bucketList,
|
|
21
21
|
bucketInfo,
|
|
22
22
|
bucketDelete,
|
|
23
|
-
fileUpload,
|
|
23
|
+
fileUpload,
|
|
24
24
|
fileDownload,
|
|
25
25
|
fileList,
|
|
26
|
-
fileDelete
|
|
27
|
-
permissionAdd,
|
|
28
|
-
permissionList
|
|
26
|
+
fileDelete
|
|
29
27
|
} = require('../src/commands/storage');
|
|
30
28
|
const { lambdaDeploy, lambdaList, lambdaInvoke, lambdaLogs, lambdaRemove, lambdaInfo } = require('../src/commands/lambda');
|
|
31
29
|
|
|
@@ -296,36 +294,7 @@ storageCmd
|
|
|
296
294
|
fileDelete(bucket, fileKey);
|
|
297
295
|
});
|
|
298
296
|
|
|
299
|
-
// Permission commands
|
|
300
|
-
const permissionCmd = storageCmd
|
|
301
|
-
.command('permission')
|
|
302
|
-
.description('Manage bucket permissions');
|
|
303
|
-
|
|
304
|
-
permissionCmd
|
|
305
|
-
.command('add <bucket>')
|
|
306
|
-
.description('Add permission to bucket')
|
|
307
|
-
.option('--target-type <type>', 'Target type (user, workspace, public, api_key)', 'user')
|
|
308
|
-
.option('--target-id <id>', 'Target ID (user ID, workspace ID, etc.)')
|
|
309
|
-
.option('--resource-type <type>', 'Resource type (bucket, folder, file)', 'bucket')
|
|
310
|
-
.option('--resource-path <path>', 'Resource path (for folder/file permissions)')
|
|
311
|
-
.option('--read', 'Grant read permission', false)
|
|
312
|
-
.option('--write', 'Grant write permission', false)
|
|
313
|
-
.option('--delete', 'Grant delete permission', false)
|
|
314
|
-
.option('--list', 'Grant list permission', false)
|
|
315
|
-
.option('--valid-until <date>', 'Permission expiry date (ISO 8601)')
|
|
316
|
-
.option('--reason <text>', 'Reason for granting permission')
|
|
317
|
-
.action((bucket, options) => {
|
|
318
|
-
requireAuth('storage permission add');
|
|
319
|
-
permissionAdd(bucket, options);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
permissionCmd
|
|
323
|
-
.command('list <bucket>')
|
|
324
|
-
.description('List bucket permissions')
|
|
325
|
-
.action((bucket) => {
|
|
326
|
-
requireAuth('storage permission list');
|
|
327
|
-
permissionList(bucket);
|
|
328
|
-
});
|
|
297
|
+
// Permission commands removed as per user request
|
|
329
298
|
|
|
330
299
|
// Ship command - Deploy Docker images to Lambda
|
|
331
300
|
program
|
|
@@ -366,6 +335,7 @@ lambdaCmd
|
|
|
366
335
|
.command('invoke <function-name>')
|
|
367
336
|
.description('Invoke Lambda function')
|
|
368
337
|
.option('--payload <json>', 'JSON payload')
|
|
338
|
+
.option('--path <path>', 'HTTP path to invoke (e.g., health, api/v1/users)')
|
|
369
339
|
.action((functionName, options) => {
|
|
370
340
|
requireAuth('lambda invoke');
|
|
371
341
|
lambdaInvoke(functionName, options);
|
|
@@ -376,6 +346,8 @@ lambdaCmd
|
|
|
376
346
|
.description('Get Lambda function logs')
|
|
377
347
|
.option('--tail', 'Stream logs in real-time')
|
|
378
348
|
.option('--since <time>', 'Show logs since (e.g., 1h, 30m)', '10m')
|
|
349
|
+
.option('--page <number>', 'Page number (default: 1)', '1')
|
|
350
|
+
.option('--per-page <number>', 'Number of logs per page (default: 100, max: 1000)', '100')
|
|
379
351
|
.action((functionName, options) => {
|
|
380
352
|
requireAuth('lambda logs');
|
|
381
353
|
lambdaLogs(functionName, options);
|
package/package.json
CHANGED
package/src/commands/db.js
CHANGED
|
@@ -9,6 +9,7 @@ const https = require('https');
|
|
|
9
9
|
const http = require('http');
|
|
10
10
|
const { getCredentials } = require('./login');
|
|
11
11
|
const { API_BASE_URL, API_ENDPOINTS } = require('../config');
|
|
12
|
+
const { handleError } = require('../utils/errorHandler');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Make API request to orcapt Deploy API
|
|
@@ -109,15 +110,7 @@ async function dbCreate(options) {
|
|
|
109
110
|
|
|
110
111
|
} catch (error) {
|
|
111
112
|
spinner.fail(chalk.red('Failed to create database'));
|
|
112
|
-
|
|
113
|
-
if (error.statusCode === 401) {
|
|
114
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
115
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
116
|
-
} else if (error.response && error.response.detail) {
|
|
117
|
-
console.log(chalk.red(`\nā ${error.response.detail}\n`));
|
|
118
|
-
} else {
|
|
119
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
120
|
-
}
|
|
113
|
+
handleError(error, 'Database creation');
|
|
121
114
|
process.exit(1);
|
|
122
115
|
}
|
|
123
116
|
}
|
|
@@ -169,15 +162,7 @@ async function dbList() {
|
|
|
169
162
|
|
|
170
163
|
} catch (error) {
|
|
171
164
|
spinner.fail(chalk.red('Failed to list databases'));
|
|
172
|
-
|
|
173
|
-
if (error.statusCode === 401) {
|
|
174
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
175
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
176
|
-
} else if (error.response && error.response.detail) {
|
|
177
|
-
console.log(chalk.red(`\nā ${error.response.detail}\n`));
|
|
178
|
-
} else {
|
|
179
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
180
|
-
}
|
|
165
|
+
handleError(error, 'Database listing');
|
|
181
166
|
process.exit(1);
|
|
182
167
|
}
|
|
183
168
|
}
|
|
@@ -225,17 +210,7 @@ async function dbRemove(databaseName) {
|
|
|
225
210
|
|
|
226
211
|
} catch (error) {
|
|
227
212
|
spinner.fail(chalk.red('Failed to delete database'));
|
|
228
|
-
|
|
229
|
-
if (error.statusCode === 401) {
|
|
230
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
231
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
232
|
-
} else if (error.statusCode === 404) {
|
|
233
|
-
console.log(chalk.red(`\nā Database '${databaseName}' not found or doesn't belong to your workspace\n`));
|
|
234
|
-
} else if (error.response && error.response.detail) {
|
|
235
|
-
console.log(chalk.red(`\nā ${error.response.detail}\n`));
|
|
236
|
-
} else {
|
|
237
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
238
|
-
}
|
|
213
|
+
handleError(error, 'Database deletion');
|
|
239
214
|
process.exit(1);
|
|
240
215
|
}
|
|
241
216
|
}
|
package/src/commands/lambda.js
CHANGED
|
@@ -11,6 +11,7 @@ const fs = require('fs');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const { getCredentials } = require('./login');
|
|
13
13
|
const { API_BASE_URL, API_ENDPOINTS } = require('../config');
|
|
14
|
+
const { handleError } = require('../utils/errorHandler');
|
|
14
15
|
const {
|
|
15
16
|
checkDockerInstalled,
|
|
16
17
|
checkDockerImage,
|
|
@@ -193,17 +194,7 @@ async function lambdaDeploy(functionName, options = {}) {
|
|
|
193
194
|
});
|
|
194
195
|
} catch (error) {
|
|
195
196
|
spinner.fail(chalk.red('ā Failed to request ECR credentials'));
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (error.statusCode) {
|
|
199
|
-
console.log(chalk.red('Status Code:'), error.statusCode);
|
|
200
|
-
console.log(chalk.red('Response:'), JSON.stringify(error.response, null, 2));
|
|
201
|
-
} else if (error.message) {
|
|
202
|
-
console.log(chalk.red('Error:'), error.message);
|
|
203
|
-
} else {
|
|
204
|
-
console.log(chalk.red('Error:'), error);
|
|
205
|
-
}
|
|
206
|
-
|
|
197
|
+
handleError(error, 'ECR credentials request');
|
|
207
198
|
console.log();
|
|
208
199
|
console.log(chalk.yellow('š” Troubleshooting tips:'));
|
|
209
200
|
console.log(chalk.white(' 1. Check if backend is running'));
|
|
@@ -289,22 +280,7 @@ async function lambdaDeploy(functionName, options = {}) {
|
|
|
289
280
|
|
|
290
281
|
} catch (error) {
|
|
291
282
|
console.log(chalk.red('\nā Deployment failed'));
|
|
292
|
-
|
|
293
|
-
if (error.statusCode === 401) {
|
|
294
|
-
console.log(chalk.red('Authentication failed'));
|
|
295
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
296
|
-
} else if (error.statusCode === 404) {
|
|
297
|
-
console.log(chalk.red('Endpoint not found'));
|
|
298
|
-
console.log(chalk.yellow('The Lambda API may not be implemented yet.'));
|
|
299
|
-
console.log(chalk.cyan('See STORAGE_LAMBDA_ARCHITECTURE.md for details\n'));
|
|
300
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
301
|
-
console.log(chalk.red('Connection refused'));
|
|
302
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
303
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
304
|
-
} else {
|
|
305
|
-
console.log(chalk.red(`Error: ${error.message}\n`));
|
|
306
|
-
}
|
|
307
|
-
|
|
283
|
+
handleError(error, 'Lambda deployment');
|
|
308
284
|
process.exit(1);
|
|
309
285
|
}
|
|
310
286
|
}
|
|
@@ -360,21 +336,7 @@ async function lambdaList() {
|
|
|
360
336
|
|
|
361
337
|
} catch (error) {
|
|
362
338
|
spinner.fail(chalk.red('Failed to list functions'));
|
|
363
|
-
|
|
364
|
-
if (error.statusCode === 401) {
|
|
365
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
366
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
367
|
-
} else if (error.statusCode === 404) {
|
|
368
|
-
console.log(chalk.red('\nā Endpoint not found'));
|
|
369
|
-
console.log(chalk.yellow('The Lambda API may not be implemented yet.\n'));
|
|
370
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
371
|
-
console.log(chalk.red('\nā Connection refused'));
|
|
372
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
373
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
374
|
-
} else {
|
|
375
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
376
|
-
}
|
|
377
|
-
|
|
339
|
+
handleError(error, 'Lambda listing');
|
|
378
340
|
process.exit(1);
|
|
379
341
|
}
|
|
380
342
|
}
|
|
@@ -403,6 +365,10 @@ async function lambdaInvoke(functionName, options = {}) {
|
|
|
403
365
|
process.exit(1);
|
|
404
366
|
}
|
|
405
367
|
}
|
|
368
|
+
|
|
369
|
+
// Add path (default to 'health' if not provided)
|
|
370
|
+
const path = options.path || 'health';
|
|
371
|
+
console.log(chalk.white('Path: '), chalk.yellow(path));
|
|
406
372
|
|
|
407
373
|
const spinner = ora('Invoking function...').start();
|
|
408
374
|
|
|
@@ -410,11 +376,16 @@ async function lambdaInvoke(functionName, options = {}) {
|
|
|
410
376
|
// Call backend API to invoke Lambda function
|
|
411
377
|
const startTime = Date.now();
|
|
412
378
|
const endpoint = API_ENDPOINTS.LAMBDA_INVOKE.replace('{functionName}', functionName);
|
|
379
|
+
const requestBody = {
|
|
380
|
+
payload,
|
|
381
|
+
path: options.path || 'health' // Default path
|
|
382
|
+
};
|
|
383
|
+
|
|
413
384
|
const response = await makeApiRequest(
|
|
414
385
|
'POST',
|
|
415
386
|
endpoint,
|
|
416
387
|
credentials,
|
|
417
|
-
|
|
388
|
+
requestBody
|
|
418
389
|
);
|
|
419
390
|
|
|
420
391
|
const duration = Date.now() - startTime;
|
|
@@ -433,33 +404,43 @@ async function lambdaInvoke(functionName, options = {}) {
|
|
|
433
404
|
}
|
|
434
405
|
|
|
435
406
|
console.log(chalk.cyan('\nResponse:'));
|
|
436
|
-
|
|
407
|
+
|
|
408
|
+
// Parse and format the response nicely
|
|
409
|
+
const lambdaResponse = response.response || response;
|
|
410
|
+
|
|
411
|
+
// If response has statusCode and body (API Gateway format)
|
|
412
|
+
if (lambdaResponse.statusCode && lambdaResponse.body) {
|
|
413
|
+
console.log(chalk.white('Status Code: '), chalk.green(lambdaResponse.statusCode));
|
|
414
|
+
|
|
415
|
+
// Try to parse body as JSON
|
|
416
|
+
let bodyContent;
|
|
417
|
+
try {
|
|
418
|
+
bodyContent = JSON.parse(lambdaResponse.body);
|
|
419
|
+
console.log(chalk.white('\nBody:'));
|
|
420
|
+
console.log(chalk.green(JSON.stringify(bodyContent, null, 2)));
|
|
421
|
+
} catch (e) {
|
|
422
|
+
// If not JSON, show as string
|
|
423
|
+
console.log(chalk.white('\nBody:'));
|
|
424
|
+
console.log(chalk.green(lambdaResponse.body));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Show headers if available
|
|
428
|
+
if (lambdaResponse.headers && Object.keys(lambdaResponse.headers).length > 0) {
|
|
429
|
+
console.log(chalk.white('\nHeaders:'));
|
|
430
|
+
Object.entries(lambdaResponse.headers).forEach(([key, value]) => {
|
|
431
|
+
console.log(chalk.gray(` ${key}: `), chalk.yellow(value));
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
// Regular response format
|
|
436
|
+
console.log(chalk.green(JSON.stringify(lambdaResponse, null, 2)));
|
|
437
|
+
}
|
|
437
438
|
|
|
438
439
|
console.log(chalk.cyan('\n============================================================\n'));
|
|
439
440
|
|
|
440
441
|
} catch (error) {
|
|
441
442
|
spinner.fail(chalk.red('Invocation failed'));
|
|
442
|
-
|
|
443
|
-
if (error.statusCode === 401) {
|
|
444
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
445
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
446
|
-
} else if (error.statusCode === 404) {
|
|
447
|
-
console.log(chalk.red('\nā Function not found'));
|
|
448
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
449
|
-
} else if (error.statusCode === 500) {
|
|
450
|
-
console.log(chalk.red('\nā Function execution error'));
|
|
451
|
-
if (error.response && error.response.error) {
|
|
452
|
-
console.log(chalk.yellow('Error:'), chalk.white(error.response.error));
|
|
453
|
-
}
|
|
454
|
-
console.log();
|
|
455
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
456
|
-
console.log(chalk.red('\nā Connection refused'));
|
|
457
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
458
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
459
|
-
} else {
|
|
460
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
461
|
-
}
|
|
462
|
-
|
|
443
|
+
handleError(error, 'Lambda invocation');
|
|
463
444
|
process.exit(1);
|
|
464
445
|
}
|
|
465
446
|
}
|
|
@@ -486,11 +467,24 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
486
467
|
|
|
487
468
|
const spinner = ora('Fetching logs...').start();
|
|
488
469
|
const endpoint = API_ENDPOINTS.LAMBDA_LOGS.replace('{functionName}', functionName);
|
|
470
|
+
|
|
471
|
+
// Build query parameters for pagination
|
|
472
|
+
const queryParams = {};
|
|
473
|
+
if (options.page) {
|
|
474
|
+
queryParams.page = parseInt(options.page) || 1;
|
|
475
|
+
}
|
|
476
|
+
if (options.perPage) {
|
|
477
|
+
queryParams.per_page = Math.min(parseInt(options.perPage) || 100, 1000);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
|
481
|
+
const fullEndpoint = queryString ? `${endpoint}?${queryString}` : endpoint;
|
|
482
|
+
|
|
489
483
|
try {
|
|
490
484
|
// Call backend API to get logs
|
|
491
485
|
const response = await makeApiRequest(
|
|
492
486
|
'GET',
|
|
493
|
-
|
|
487
|
+
fullEndpoint,
|
|
494
488
|
credentials
|
|
495
489
|
);
|
|
496
490
|
|
|
@@ -498,12 +492,37 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
498
492
|
|
|
499
493
|
console.log(chalk.cyan('\n============================================================'));
|
|
500
494
|
|
|
495
|
+
// Show sync statistics
|
|
496
|
+
if (response.synced_from_cloudwatch !== undefined || response.skipped_duplicates !== undefined) {
|
|
497
|
+
console.log(chalk.cyan('š Sync Statistics:'));
|
|
498
|
+
if (response.total_cloudwatch_logs !== undefined) {
|
|
499
|
+
console.log(chalk.white(' CloudWatch logs: '), chalk.yellow(response.total_cloudwatch_logs));
|
|
500
|
+
}
|
|
501
|
+
if (response.synced_from_cloudwatch !== undefined) {
|
|
502
|
+
console.log(chalk.white(' Newly synced: '), chalk.green(response.synced_from_cloudwatch));
|
|
503
|
+
}
|
|
504
|
+
if (response.skipped_duplicates !== undefined) {
|
|
505
|
+
console.log(chalk.white(' Skipped (dup): '), chalk.gray(response.skipped_duplicates));
|
|
506
|
+
}
|
|
507
|
+
console.log();
|
|
508
|
+
}
|
|
509
|
+
|
|
501
510
|
if (!response.logs || response.logs.length === 0) {
|
|
502
|
-
console.log(chalk.yellow('No logs found'));
|
|
511
|
+
console.log(chalk.yellow('No logs found in database'));
|
|
503
512
|
console.log(chalk.cyan('\nTry invoking the function first:'));
|
|
504
513
|
console.log(chalk.white(' orcapt lambda invoke'), chalk.cyan(functionName));
|
|
505
514
|
} else {
|
|
506
|
-
|
|
515
|
+
// Show pagination info
|
|
516
|
+
if (response.pagination) {
|
|
517
|
+
const pagination = response.pagination;
|
|
518
|
+
console.log(chalk.cyan('š Pagination:'));
|
|
519
|
+
console.log(chalk.white(' Page: '), chalk.yellow(`${pagination.current_page} / ${pagination.total_pages}`));
|
|
520
|
+
console.log(chalk.white(' Per page: '), chalk.yellow(pagination.per_page));
|
|
521
|
+
console.log(chalk.white(' Total logs: '), chalk.yellow(pagination.total));
|
|
522
|
+
console.log();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
console.log(chalk.green(`ā Showing ${response.logs.length} log entries (page ${response.pagination?.current_page || 1})`));
|
|
507
526
|
console.log(chalk.cyan('============================================================\n'));
|
|
508
527
|
|
|
509
528
|
response.logs.forEach(log => {
|
|
@@ -521,6 +540,21 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
521
540
|
chalk.white(log.message)
|
|
522
541
|
);
|
|
523
542
|
});
|
|
543
|
+
|
|
544
|
+
// Show pagination navigation hints
|
|
545
|
+
if (response.pagination) {
|
|
546
|
+
const pagination = response.pagination;
|
|
547
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
548
|
+
if (pagination.has_prev_page) {
|
|
549
|
+
console.log(chalk.white(' Previous page: '), chalk.cyan(`orcapt lambda logs ${functionName} --page ${pagination.current_page - 1}`));
|
|
550
|
+
}
|
|
551
|
+
if (pagination.has_next_page) {
|
|
552
|
+
console.log(chalk.white(' Next page: '), chalk.cyan(`orcapt lambda logs ${functionName} --page ${pagination.current_page + 1}`));
|
|
553
|
+
}
|
|
554
|
+
if (!pagination.has_prev_page && !pagination.has_next_page) {
|
|
555
|
+
console.log(chalk.gray(' (No more pages)'));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
524
558
|
}
|
|
525
559
|
|
|
526
560
|
console.log(chalk.cyan('\n============================================================'));
|
|
@@ -534,21 +568,7 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
534
568
|
|
|
535
569
|
} catch (error) {
|
|
536
570
|
spinner.fail(chalk.red('Failed to fetch logs'));
|
|
537
|
-
|
|
538
|
-
if (error.statusCode === 401) {
|
|
539
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
540
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
541
|
-
} else if (error.statusCode === 404) {
|
|
542
|
-
console.log(chalk.red('\nā Function not found'));
|
|
543
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
544
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
545
|
-
console.log(chalk.red('\nā Connection refused'));
|
|
546
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
547
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
548
|
-
} else {
|
|
549
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
550
|
-
}
|
|
551
|
-
|
|
571
|
+
handleError(error, 'Lambda logs');
|
|
552
572
|
process.exit(1);
|
|
553
573
|
}
|
|
554
574
|
}
|
|
@@ -595,24 +615,7 @@ async function lambdaRemove(functionName) {
|
|
|
595
615
|
|
|
596
616
|
} catch (error) {
|
|
597
617
|
spinner.fail(chalk.red('Failed to remove function'));
|
|
598
|
-
|
|
599
|
-
if (error.statusCode === 401) {
|
|
600
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
601
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
602
|
-
} else if (error.statusCode === 404) {
|
|
603
|
-
console.log(chalk.red('\nā Function not found'));
|
|
604
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
605
|
-
} else if (error.statusCode === 409) {
|
|
606
|
-
console.log(chalk.red('\nā Conflict'));
|
|
607
|
-
console.log(chalk.yellow('Function may be currently in use. Please try again later.\n'));
|
|
608
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
609
|
-
console.log(chalk.red('\nā Connection refused'));
|
|
610
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
611
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
612
|
-
} else {
|
|
613
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
614
|
-
}
|
|
615
|
-
|
|
618
|
+
handleError(error, 'Lambda removal');
|
|
616
619
|
process.exit(1);
|
|
617
620
|
}
|
|
618
621
|
}
|
|
@@ -706,21 +709,7 @@ async function lambdaInfo(functionName) {
|
|
|
706
709
|
|
|
707
710
|
} catch (error) {
|
|
708
711
|
spinner.fail(chalk.red('Failed to fetch function details'));
|
|
709
|
-
|
|
710
|
-
if (error.statusCode === 401) {
|
|
711
|
-
console.log(chalk.red('\nā Authentication failed'));
|
|
712
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
713
|
-
} else if (error.statusCode === 404) {
|
|
714
|
-
console.log(chalk.red('\nā Function not found'));
|
|
715
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
716
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
717
|
-
console.log(chalk.red('\nā Connection refused'));
|
|
718
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
719
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
720
|
-
} else {
|
|
721
|
-
console.log(chalk.red(`\nā ${error.message}\n`));
|
|
722
|
-
}
|
|
723
|
-
|
|
712
|
+
handleError(error, 'Lambda info');
|
|
724
713
|
process.exit(1);
|
|
725
714
|
}
|
|
726
715
|
}
|
package/src/commands/storage.js
CHANGED
|
@@ -11,6 +11,7 @@ const https = require('https');
|
|
|
11
11
|
const http = require('http');
|
|
12
12
|
const { getCredentials } = require('./login');
|
|
13
13
|
const { API_BASE_URL, API_ENDPOINTS } = require('../config');
|
|
14
|
+
const { handleError } = require('../utils/errorHandler');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Make API request to orcapt Deploy API
|
|
@@ -79,31 +80,31 @@ function uploadFileToApi(endpoint, credentials, filePath, bucketName, options =
|
|
|
79
80
|
const fileStream = fs.createReadStream(filePath);
|
|
80
81
|
const fileName = path.basename(filePath);
|
|
81
82
|
const boundary = `----FormBoundary${Date.now()}`;
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
const folderPath = options.folder || '';
|
|
84
|
-
|
|
85
|
+
|
|
85
86
|
// Build multipart form data
|
|
86
87
|
let formData = '';
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
// Add folder_path field
|
|
89
90
|
if (folderPath) {
|
|
90
91
|
formData += `--${boundary}\r\n`;
|
|
91
92
|
formData += `Content-Disposition: form-data; name="folder_path"\r\n\r\n`;
|
|
92
93
|
formData += `${folderPath}\r\n`;
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
|
|
95
96
|
// Add visibility field
|
|
96
97
|
if (options.visibility) {
|
|
97
98
|
formData += `--${boundary}\r\n`;
|
|
98
99
|
formData += `Content-Disposition: form-data; name="visibility"\r\n\r\n`;
|
|
99
100
|
formData += `${options.visibility}\r\n`;
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
// Add generate_url field
|
|
103
104
|
formData += `--${boundary}\r\n`;
|
|
104
105
|
formData += `Content-Disposition: form-data; name="generate_url"\r\n\r\n`;
|
|
105
106
|
formData += `true\r\n`;
|
|
106
|
-
|
|
107
|
+
|
|
107
108
|
// Add file field header
|
|
108
109
|
formData += `--${boundary}\r\n`;
|
|
109
110
|
formData += `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`;
|
|
@@ -206,7 +207,7 @@ async function bucketCreate(bucketName, options = {}) {
|
|
|
206
207
|
console.log(chalk.cyan('============================================================\n'));
|
|
207
208
|
|
|
208
209
|
const credentials = requireAuth();
|
|
209
|
-
|
|
210
|
+
|
|
210
211
|
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
211
212
|
console.log(chalk.white('Workspace: '), chalk.yellow(credentials.workspace));
|
|
212
213
|
console.log(chalk.white('Visibility: '), chalk.yellow(options.public ? 'public' : 'private'));
|
|
@@ -237,7 +238,7 @@ async function bucketCreate(bucketName, options = {}) {
|
|
|
237
238
|
);
|
|
238
239
|
|
|
239
240
|
spinner.succeed(chalk.green('ā Bucket created successfully!'));
|
|
240
|
-
|
|
241
|
+
|
|
241
242
|
console.log(chalk.cyan('\nš¦ Bucket Details:'));
|
|
242
243
|
console.log(chalk.white(' Name: '), chalk.yellow(response.bucket.bucket_name));
|
|
243
244
|
console.log(chalk.white(' AWS Bucket: '), chalk.gray(response.bucket.aws_bucket_name));
|
|
@@ -245,7 +246,7 @@ async function bucketCreate(bucketName, options = {}) {
|
|
|
245
246
|
console.log(chalk.white(' Status: '), chalk.green(response.bucket.status));
|
|
246
247
|
console.log(chalk.white(' Visibility: '), chalk.yellow(response.bucket.visibility));
|
|
247
248
|
console.log(chalk.white(' Encryption: '), chalk.yellow(response.bucket.encryption_enabled ? 'Enabled' : 'Disabled'));
|
|
248
|
-
|
|
249
|
+
|
|
249
250
|
console.log(chalk.cyan('\nš” Next Steps:'));
|
|
250
251
|
console.log(chalk.white(' Upload file: '), chalk.yellow(`orcapt storage upload ${bucketName} <file-path>`));
|
|
251
252
|
console.log(chalk.white(' List files: '), chalk.yellow(`orcapt storage files ${bucketName}`));
|
|
@@ -253,18 +254,7 @@ async function bucketCreate(bucketName, options = {}) {
|
|
|
253
254
|
|
|
254
255
|
} catch (error) {
|
|
255
256
|
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('');
|
|
257
|
+
handleError(error, 'Bucket creation');
|
|
268
258
|
process.exit(1);
|
|
269
259
|
}
|
|
270
260
|
}
|
|
@@ -316,7 +306,7 @@ async function bucketList() {
|
|
|
316
306
|
const files = String(bucket.file_count).padEnd(10);
|
|
317
307
|
const size = bucket.total_size.padEnd(15);
|
|
318
308
|
const visibility = bucket.visibility.padEnd(15);
|
|
319
|
-
const status = bucket.status === 'active'
|
|
309
|
+
const status = bucket.status === 'active'
|
|
320
310
|
? chalk.green(bucket.status.padEnd(15))
|
|
321
311
|
: chalk.yellow(bucket.status.padEnd(15));
|
|
322
312
|
const created = new Date(bucket.created_at).toLocaleDateString();
|
|
@@ -336,12 +326,7 @@ async function bucketList() {
|
|
|
336
326
|
|
|
337
327
|
} catch (error) {
|
|
338
328
|
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
|
-
}
|
|
329
|
+
handleError(error, 'Bucket listing');
|
|
345
330
|
process.exit(1);
|
|
346
331
|
}
|
|
347
332
|
}
|
|
@@ -370,11 +355,11 @@ async function fileUpload(bucketName, localPath, options = {}) {
|
|
|
370
355
|
console.log(chalk.white('File: '), chalk.yellow(fileName));
|
|
371
356
|
console.log(chalk.white('Local Path: '), chalk.gray(localPath));
|
|
372
357
|
console.log(chalk.white('Size: '), chalk.yellow(`${fileSizeMB} MB`));
|
|
373
|
-
|
|
358
|
+
|
|
374
359
|
if (options.folder) {
|
|
375
360
|
console.log(chalk.white('Folder: '), chalk.yellow(options.folder));
|
|
376
361
|
}
|
|
377
|
-
|
|
362
|
+
|
|
378
363
|
console.log(chalk.white('Visibility: '), chalk.yellow(options.public ? 'public' : 'private'));
|
|
379
364
|
|
|
380
365
|
const spinner = ora('Uploading file...').start();
|
|
@@ -394,19 +379,19 @@ async function fileUpload(bucketName, localPath, options = {}) {
|
|
|
394
379
|
);
|
|
395
380
|
|
|
396
381
|
spinner.succeed(chalk.green('ā File uploaded successfully!'));
|
|
397
|
-
|
|
382
|
+
|
|
398
383
|
console.log(chalk.cyan('\nš File Details:'));
|
|
399
384
|
console.log(chalk.white(' Name: '), chalk.yellow(response.file.file_name));
|
|
400
385
|
console.log(chalk.white(' Key: '), chalk.gray(response.file.file_key));
|
|
401
386
|
console.log(chalk.white(' Size: '), chalk.yellow(response.file.file_size));
|
|
402
387
|
console.log(chalk.white(' Type: '), chalk.yellow(response.file.mime_type));
|
|
403
388
|
console.log(chalk.white(' Visibility: '), chalk.yellow(response.file.visibility));
|
|
404
|
-
|
|
389
|
+
|
|
405
390
|
if (response.file.download_url) {
|
|
406
391
|
console.log(chalk.cyan('\nš Download URL (valid for 60 minutes):'));
|
|
407
392
|
console.log(chalk.gray(response.file.download_url));
|
|
408
393
|
}
|
|
409
|
-
|
|
394
|
+
|
|
410
395
|
console.log(chalk.cyan('\nš” Next Steps:'));
|
|
411
396
|
console.log(chalk.white(' List files: '), chalk.yellow(`orcapt storage files ${bucketName}`));
|
|
412
397
|
console.log(chalk.white(' Download: '), chalk.yellow(`orcapt storage download ${bucketName} ${response.file.file_key}`));
|
|
@@ -414,18 +399,7 @@ async function fileUpload(bucketName, localPath, options = {}) {
|
|
|
414
399
|
|
|
415
400
|
} catch (error) {
|
|
416
401
|
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('');
|
|
402
|
+
handleError(error, 'File upload');
|
|
429
403
|
process.exit(1);
|
|
430
404
|
}
|
|
431
405
|
}
|
|
@@ -469,7 +443,7 @@ async function fileDownload(bucketName, fileKey, localPath) {
|
|
|
469
443
|
const httpModule = isHttps ? https : http;
|
|
470
444
|
|
|
471
445
|
const fileWriter = fs.createWriteStream(outputPath);
|
|
472
|
-
|
|
446
|
+
|
|
473
447
|
httpModule.get(downloadUrl, (res) => {
|
|
474
448
|
if (res.statusCode !== 200) {
|
|
475
449
|
reject(new Error(`Failed to download: ${res.statusCode}`));
|
|
@@ -483,18 +457,18 @@ async function fileDownload(bucketName, fileKey, localPath) {
|
|
|
483
457
|
resolve();
|
|
484
458
|
});
|
|
485
459
|
}).on('error', (err) => {
|
|
486
|
-
fs.unlink(outputPath, () => {});
|
|
460
|
+
fs.unlink(outputPath, () => { });
|
|
487
461
|
reject(err);
|
|
488
462
|
});
|
|
489
463
|
|
|
490
464
|
fileWriter.on('error', (err) => {
|
|
491
|
-
fs.unlink(outputPath, () => {});
|
|
465
|
+
fs.unlink(outputPath, () => { });
|
|
492
466
|
reject(err);
|
|
493
467
|
});
|
|
494
468
|
});
|
|
495
469
|
|
|
496
470
|
spinner.succeed(chalk.green('ā File downloaded successfully!'));
|
|
497
|
-
|
|
471
|
+
|
|
498
472
|
console.log(chalk.cyan('\nš File Details:'));
|
|
499
473
|
console.log(chalk.white(' Name: '), chalk.yellow(response.file.file_name));
|
|
500
474
|
console.log(chalk.white(' Size: '), chalk.yellow(formatBytes(response.file.file_size_bytes)));
|
|
@@ -504,16 +478,7 @@ async function fileDownload(bucketName, fileKey, localPath) {
|
|
|
504
478
|
|
|
505
479
|
} catch (error) {
|
|
506
480
|
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('');
|
|
481
|
+
handleError(error, 'File download');
|
|
517
482
|
process.exit(1);
|
|
518
483
|
}
|
|
519
484
|
}
|
|
@@ -550,26 +515,17 @@ async function bucketInfo(bucketName) {
|
|
|
550
515
|
console.log(chalk.white(' Total Size: '), chalk.cyan(bucket.total_size));
|
|
551
516
|
console.log(chalk.white(' Versioning: '), bucket.versioning_enabled ? chalk.green('Enabled') : chalk.gray('Disabled'));
|
|
552
517
|
console.log(chalk.white(' Encryption: '), bucket.encryption_enabled ? chalk.green(`Enabled (${bucket.encryption_type})`) : chalk.gray('Disabled'));
|
|
553
|
-
|
|
518
|
+
|
|
554
519
|
if (bucket.description) {
|
|
555
520
|
console.log(chalk.white(' Description: '), chalk.gray(bucket.description));
|
|
556
521
|
}
|
|
557
|
-
|
|
522
|
+
|
|
558
523
|
console.log(chalk.white(' Created: '), chalk.gray(new Date(bucket.created_at).toLocaleString()));
|
|
559
524
|
console.log('');
|
|
560
525
|
|
|
561
526
|
} catch (error) {
|
|
562
527
|
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('');
|
|
528
|
+
handleError(error, 'Bucket info');
|
|
573
529
|
process.exit(1);
|
|
574
530
|
}
|
|
575
531
|
}
|
|
@@ -586,7 +542,7 @@ async function bucketDelete(bucketName, options = {}) {
|
|
|
586
542
|
|
|
587
543
|
console.log(chalk.white('Bucket: '), chalk.yellow(bucketName));
|
|
588
544
|
console.log(chalk.white('Workspace:'), chalk.yellow(credentials.workspace));
|
|
589
|
-
|
|
545
|
+
|
|
590
546
|
if (options.force) {
|
|
591
547
|
console.log(chalk.yellow('\nā ļø Force delete enabled - all files will be deleted'));
|
|
592
548
|
}
|
|
@@ -594,9 +550,9 @@ async function bucketDelete(bucketName, options = {}) {
|
|
|
594
550
|
const spinner = ora('Deleting bucket...').start();
|
|
595
551
|
|
|
596
552
|
try {
|
|
597
|
-
const endpoint = API_ENDPOINTS.STORAGE_BUCKET_LIST.replace('/list', `/${bucketName}`) +
|
|
598
|
-
|
|
599
|
-
|
|
553
|
+
const endpoint = API_ENDPOINTS.STORAGE_BUCKET_LIST.replace('/list', `/${bucketName}`) +
|
|
554
|
+
(options.force ? '?force=true' : '');
|
|
555
|
+
|
|
600
556
|
await makeApiRequest('DELETE', endpoint, credentials);
|
|
601
557
|
|
|
602
558
|
spinner.succeed(chalk.green('ā Bucket deleted successfully!'));
|
|
@@ -604,20 +560,7 @@ async function bucketDelete(bucketName, options = {}) {
|
|
|
604
560
|
|
|
605
561
|
} catch (error) {
|
|
606
562
|
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(`orcapt storage bucket delete ${bucketName} --force`));
|
|
616
|
-
}
|
|
617
|
-
} else {
|
|
618
|
-
console.log(chalk.red(`\nā ${error.message}`));
|
|
619
|
-
}
|
|
620
|
-
console.log('');
|
|
563
|
+
handleError(error, 'Bucket deletion');
|
|
621
564
|
process.exit(1);
|
|
622
565
|
}
|
|
623
566
|
}
|
|
@@ -642,7 +585,7 @@ async function fileList(bucketName, options = {}) {
|
|
|
642
585
|
try {
|
|
643
586
|
let endpoint = API_ENDPOINTS.STORAGE_FILE_LIST.replace('{bucketName}', bucketName);
|
|
644
587
|
const params = [];
|
|
645
|
-
|
|
588
|
+
|
|
646
589
|
if (options.folder) {
|
|
647
590
|
params.push(`folder_path=${encodeURIComponent(options.folder)}`);
|
|
648
591
|
}
|
|
@@ -652,7 +595,7 @@ async function fileList(bucketName, options = {}) {
|
|
|
652
595
|
if (options.perPage) {
|
|
653
596
|
params.push(`per_page=${options.perPage}`);
|
|
654
597
|
}
|
|
655
|
-
|
|
598
|
+
|
|
656
599
|
if (params.length > 0) {
|
|
657
600
|
endpoint += '?' + params.join('&');
|
|
658
601
|
}
|
|
@@ -681,12 +624,12 @@ async function fileList(bucketName, options = {}) {
|
|
|
681
624
|
console.log(chalk.white('ā'.repeat(120)));
|
|
682
625
|
|
|
683
626
|
response.files.forEach(file => {
|
|
684
|
-
const name = (file.file_name.length > 38
|
|
685
|
-
? file.file_name.substring(0, 35) + '...'
|
|
627
|
+
const name = (file.file_name.length > 38
|
|
628
|
+
? file.file_name.substring(0, 35) + '...'
|
|
686
629
|
: file.file_name).padEnd(40);
|
|
687
630
|
const size = file.file_size.padEnd(15);
|
|
688
|
-
const type = (file.mime_type.length > 18
|
|
689
|
-
? file.mime_type.substring(0, 15) + '...'
|
|
631
|
+
const type = (file.mime_type.length > 18
|
|
632
|
+
? file.mime_type.substring(0, 15) + '...'
|
|
690
633
|
: file.mime_type).padEnd(20);
|
|
691
634
|
const downloads = String(file.download_count).padEnd(12);
|
|
692
635
|
const uploaded = new Date(file.uploaded_at).toLocaleDateString();
|
|
@@ -701,28 +644,19 @@ async function fileList(bucketName, options = {}) {
|
|
|
701
644
|
});
|
|
702
645
|
|
|
703
646
|
console.log(chalk.white('ā'.repeat(120)));
|
|
704
|
-
|
|
647
|
+
|
|
705
648
|
if (response.pagination.last_page > 1) {
|
|
706
649
|
console.log(chalk.gray(`Page ${response.pagination.current_page} of ${response.pagination.last_page}`));
|
|
707
650
|
if (response.pagination.current_page < response.pagination.last_page) {
|
|
708
651
|
console.log(chalk.cyan('Next page: '), chalk.yellow(`orcapt storage files ${bucketName} --page ${response.pagination.current_page + 1}`));
|
|
709
652
|
}
|
|
710
653
|
}
|
|
711
|
-
|
|
654
|
+
|
|
712
655
|
console.log('');
|
|
713
656
|
|
|
714
657
|
} catch (error) {
|
|
715
658
|
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('');
|
|
659
|
+
handleError(error, 'File listing');
|
|
726
660
|
process.exit(1);
|
|
727
661
|
}
|
|
728
662
|
}
|
|
@@ -755,143 +689,7 @@ async function fileDelete(bucketName, fileKey) {
|
|
|
755
689
|
|
|
756
690
|
} catch (error) {
|
|
757
691
|
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(`orcapt 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
|
-
}
|
|
692
|
+
handleError(error, 'File deletion');
|
|
895
693
|
process.exit(1);
|
|
896
694
|
}
|
|
897
695
|
}
|
|
@@ -904,8 +702,6 @@ module.exports = {
|
|
|
904
702
|
fileUpload,
|
|
905
703
|
fileDownload,
|
|
906
704
|
fileList,
|
|
907
|
-
fileDelete
|
|
908
|
-
permissionAdd,
|
|
909
|
-
permissionList
|
|
705
|
+
fileDelete
|
|
910
706
|
};
|
|
911
707
|
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Error Handler for Orca CLI
|
|
3
|
+
* Handles errors from different API services and formats them for CLI display
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract error message from API response
|
|
10
|
+
* Supports multiple response formats:
|
|
11
|
+
* - databaseasservice: { success: false, error: "message" }
|
|
12
|
+
* - gptclone-api/platform-api: { result: null, message: "message" }
|
|
13
|
+
* - Generic: { detail: "message" } or { message: "message" }
|
|
14
|
+
*/
|
|
15
|
+
function extractErrorMessage(error) {
|
|
16
|
+
if (!error.response) {
|
|
17
|
+
return error.message || 'Unknown error occurred';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const response = error.response;
|
|
21
|
+
|
|
22
|
+
// databaseasservice format: { success: false, error: "message" }
|
|
23
|
+
if (response.error) {
|
|
24
|
+
return response.error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// gptclone-api/platform-api format: { result: null, message: "message" }
|
|
28
|
+
if (response.message) {
|
|
29
|
+
return response.message;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Generic format: { detail: "message" }
|
|
33
|
+
if (response.detail) {
|
|
34
|
+
return response.detail;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Laravel validation errors: { errors: { field: ["message"] } }
|
|
38
|
+
if (response.errors && typeof response.errors === 'object') {
|
|
39
|
+
const errorMessages = [];
|
|
40
|
+
for (const [field, messages] of Object.entries(response.errors)) {
|
|
41
|
+
if (Array.isArray(messages)) {
|
|
42
|
+
errorMessages.push(...messages);
|
|
43
|
+
} else {
|
|
44
|
+
errorMessages.push(messages);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (errorMessages.length > 0) {
|
|
48
|
+
return errorMessages.join(', ');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fallback: try to stringify the response
|
|
53
|
+
if (typeof response === 'string') {
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return 'Unknown error occurred';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get user-friendly error message based on status code
|
|
62
|
+
*/
|
|
63
|
+
function getStatusMessage(statusCode) {
|
|
64
|
+
const messages = {
|
|
65
|
+
400: 'Bad Request - Please check your input parameters',
|
|
66
|
+
401: 'Authentication failed - Your session may have expired',
|
|
67
|
+
403: 'Forbidden - You don\'t have permission to perform this action',
|
|
68
|
+
404: 'Resource not found',
|
|
69
|
+
409: 'Conflict - Resource already exists',
|
|
70
|
+
422: 'Validation error - Please check your input',
|
|
71
|
+
500: 'Internal server error - Please try again later',
|
|
72
|
+
502: 'Bad Gateway - Service temporarily unavailable',
|
|
73
|
+
503: 'Service unavailable - Please try again later',
|
|
74
|
+
504: 'Gateway timeout - Request took too long',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return messages[statusCode] || `HTTP ${statusCode} error`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handle and display error to CLI
|
|
82
|
+
*/
|
|
83
|
+
function handleError(error, context = 'Operation') {
|
|
84
|
+
const statusCode = error.statusCode || error.status || (error.response && error.response.statusCode);
|
|
85
|
+
const errorMessage = extractErrorMessage(error);
|
|
86
|
+
|
|
87
|
+
// Network/connection errors
|
|
88
|
+
if (error.code === 'ECONNREFUSED') {
|
|
89
|
+
console.log(chalk.red(`\nā Connection refused`));
|
|
90
|
+
console.log(chalk.yellow('The API server is not reachable. Please check:'));
|
|
91
|
+
console.log(chalk.white(' 1. Is the backend service running?'));
|
|
92
|
+
console.log(chalk.white(' 2. Is the API URL correct?'));
|
|
93
|
+
console.log(chalk.white(' 3. Check your network connection\n'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
|
|
98
|
+
console.log(chalk.red(`\nā Network error: ${error.message}`));
|
|
99
|
+
console.log(chalk.yellow('Please check your network connection and API URL\n'));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// HTTP status code errors
|
|
104
|
+
if (statusCode) {
|
|
105
|
+
const statusMessage = getStatusMessage(statusCode);
|
|
106
|
+
|
|
107
|
+
console.log(chalk.red(`\nā ${context} failed`));
|
|
108
|
+
console.log(chalk.red(` Status: ${statusCode} - ${statusMessage}`));
|
|
109
|
+
|
|
110
|
+
if (errorMessage && errorMessage !== 'Unknown error occurred') {
|
|
111
|
+
console.log(chalk.yellow(` Error: ${errorMessage}`));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Specific handling for common status codes
|
|
115
|
+
if (statusCode === 401) {
|
|
116
|
+
console.log(chalk.cyan('\nPlease run:'), chalk.white('orcapt login'), chalk.cyan('to authenticate\n'));
|
|
117
|
+
} else if (statusCode === 404) {
|
|
118
|
+
console.log(chalk.yellow('\nThe requested resource was not found or doesn\'t belong to your workspace\n'));
|
|
119
|
+
} else if (statusCode === 422) {
|
|
120
|
+
console.log(chalk.yellow('\nPlease check your input parameters and try again\n'));
|
|
121
|
+
} else if (statusCode >= 500) {
|
|
122
|
+
console.log(chalk.yellow('\nThis appears to be a server-side error. Please try again later.\n'));
|
|
123
|
+
console.log(chalk.gray('If the problem persists, contact support.\n'));
|
|
124
|
+
} else {
|
|
125
|
+
console.log('');
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Generic error
|
|
131
|
+
console.log(chalk.red(`\nā ${context} failed`));
|
|
132
|
+
console.log(chalk.red(` ${errorMessage}\n`));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Format error for programmatic use (returns error object)
|
|
137
|
+
*/
|
|
138
|
+
function formatError(error) {
|
|
139
|
+
return {
|
|
140
|
+
statusCode: error.statusCode || error.status || (error.response && error.response.statusCode),
|
|
141
|
+
message: extractErrorMessage(error),
|
|
142
|
+
code: error.code,
|
|
143
|
+
response: error.response,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
handleError,
|
|
149
|
+
formatError,
|
|
150
|
+
extractErrorMessage,
|
|
151
|
+
getStatusMessage,
|
|
152
|
+
};
|
|
153
|
+
|