raqeb-cli 1.2.0 → 1.3.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.
Files changed (2) hide show
  1. package/lib/interactive.js +315 -10
  2. package/package.json +1 -1
@@ -2,15 +2,42 @@ const readline = require('readline');
2
2
  const chalk = require('chalk');
3
3
  const inquirer = require('inquirer');
4
4
  const Table = require('cli-table3');
5
+ const fs = require('fs');
6
+ const os = require('os');
7
+ const path = require('path');
5
8
 
6
9
  class InteractiveShell {
7
10
  constructor(apiClient) {
8
11
  this.client = apiClient;
9
12
  this.history = [];
10
13
  this.rl = null;
14
+ this.config = this.loadConfig();
15
+ this.userInfo = null;
11
16
  }
12
17
 
13
- start() {
18
+ loadConfig() {
19
+ try {
20
+ const configPath = path.join(os.homedir(), '.raqeb', 'config.json');
21
+ if (fs.existsSync(configPath)) {
22
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
23
+ }
24
+ } catch (error) {
25
+ // Config not found or invalid
26
+ }
27
+ return null;
28
+ }
29
+
30
+ async fetchUserInfo() {
31
+ try {
32
+ const response = await this.client.get('/auth/me');
33
+ this.userInfo = response.data;
34
+ } catch (error) {
35
+ this.userInfo = null;
36
+ }
37
+ }
38
+
39
+ async start() {
40
+ await this.fetchUserInfo();
14
41
  this.showWelcome();
15
42
  this.setupReadline();
16
43
  this.rl.prompt();
@@ -55,10 +82,31 @@ class InteractiveShell {
55
82
  }
56
83
 
57
84
  showWelcome() {
58
- console.log(chalk.cyan('\n╔═══════════════════════════════════════════════════════════╗'));
59
- console.log(chalk.cyan('║ Raqeb CLI - Interactive Mode v1.1.0 ║'));
60
- console.log(chalk.cyan('║ Type / to see all commands, or /help for help ║'));
61
- console.log(chalk.cyan('╚═══════════════════════════════════════════════════════════╝\n'));
85
+ console.clear();
86
+
87
+ // ASCII Logo
88
+ console.log(chalk.cyan.bold(`
89
+ ██████╗ ██████╗ ██████╗ ███████╗██████╗
90
+ ██╔══██╗██╔═══██╗██╔═══██╗██╔════╝██╔══██╗
91
+ ██████╔╝███████║██║ ██║█████╗ ██████╔╝
92
+ ██╔══██╗██╔══██║██║▄▄ ██║██╔══╝ ██╔══██╗
93
+ ██║ ██║██║ ██║╚██████╔╝███████╗██████╔╝
94
+ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══▀▀═╝ ╚══════╝╚═════╝
95
+ `));
96
+
97
+ console.log(chalk.cyan('╔═══════════════════════════════════════════════════════════╗'));
98
+ console.log(chalk.cyan('║ Raqeb CLI - Interactive Mode v1.3.0 ║'));
99
+ console.log(chalk.cyan('╚═══════════════════════════════════════════════════════════╝'));
100
+
101
+ // Show user info if available
102
+ if (this.userInfo) {
103
+ console.log(chalk.gray('\n📊 Session Information:'));
104
+ console.log(chalk.white(` User: ${chalk.green(this.userInfo.email || 'Unknown')}`));
105
+ console.log(chalk.white(` Tenant: ${chalk.green(this.userInfo.tenant_name || this.userInfo.tenant_id || 'Unknown')}`));
106
+ console.log(chalk.white(` Role: ${chalk.yellow(this.userInfo.role || 'user')}`));
107
+ }
108
+
109
+ console.log(chalk.cyan('\n💡 Type / to see all commands, or /help for help\n'));
62
110
  }
63
111
 
64
112
  async handleCommand(input) {
@@ -319,7 +367,34 @@ class InteractiveShell {
319
367
  }
320
368
 
321
369
  async listSecrets() {
322
- console.log(chalk.yellow('\n⚠️ Secrets list not yet implemented\n'));
370
+ try {
371
+ const response = await this.client.get('/service-accounts/secrets');
372
+ const secrets = response.data;
373
+
374
+ if (!secrets || secrets.length === 0) {
375
+ console.log(chalk.yellow('\nNo secrets found\n'));
376
+ return;
377
+ }
378
+
379
+ console.log(chalk.bold('\n🔐 Secrets:\n'));
380
+ const table = new Table({
381
+ head: [chalk.cyan('Name'), chalk.cyan('Created'), chalk.cyan('Updated')],
382
+ style: { head: ['cyan'] }
383
+ });
384
+
385
+ secrets.forEach(secret => {
386
+ table.push([
387
+ secret.name,
388
+ new Date(secret.created_at).toLocaleString(),
389
+ secret.updated_at ? new Date(secret.updated_at).toLocaleString() : 'Never'
390
+ ]);
391
+ });
392
+
393
+ console.log(table.toString());
394
+ console.log(chalk.gray(`\nTotal: ${secrets.length} secret(s)\n`));
395
+ } catch (error) {
396
+ throw new Error(error.response?.data?.detail || error.message);
397
+ }
323
398
  }
324
399
 
325
400
  async getSecret(secretId) {
@@ -344,19 +419,249 @@ class InteractiveShell {
344
419
  }
345
420
 
346
421
  async setSecret() {
347
- console.log(chalk.yellow('\n⚠️ Set secret not yet implemented\n'));
422
+ try {
423
+ const answers = await inquirer.prompt([
424
+ {
425
+ type: 'input',
426
+ name: 'name',
427
+ message: 'Secret name:',
428
+ validate: (value) => value.trim() !== '' || 'Name is required'
429
+ },
430
+ {
431
+ type: 'password',
432
+ name: 'value',
433
+ message: 'Secret value:',
434
+ mask: '*',
435
+ validate: (value) => value.trim() !== '' || 'Value is required'
436
+ },
437
+ {
438
+ type: 'input',
439
+ name: 'description',
440
+ message: 'Description (optional):',
441
+ default: ''
442
+ }
443
+ ]);
444
+
445
+ await this.client.post('/service-accounts/secrets', {
446
+ name: answers.name,
447
+ value: answers.value,
448
+ description: answers.description || undefined
449
+ });
450
+
451
+ console.log(chalk.green(`\n✓ Secret '${answers.name}' created successfully\n`));
452
+ } catch (error) {
453
+ throw new Error(error.response?.data?.detail || error.message);
454
+ }
348
455
  }
349
456
 
350
457
  async deleteSecret(secretName) {
351
- console.log(chalk.yellow('\n⚠️ Delete secret not yet implemented\n'));
458
+ if (!secretName) {
459
+ console.log(chalk.red('\n❌ Secret name required'));
460
+ console.log(chalk.gray('Usage: /secrets delete <name>\n'));
461
+ return;
462
+ }
463
+
464
+ try {
465
+ const confirm = await inquirer.prompt([{
466
+ type: 'confirm',
467
+ name: 'confirmed',
468
+ message: `Delete secret '${secretName}'?`,
469
+ default: false
470
+ }]);
471
+
472
+ if (!confirm.confirmed) {
473
+ console.log(chalk.gray('\nCancelled\n'));
474
+ return;
475
+ }
476
+
477
+ await this.client.delete(`/service-accounts/secrets/${secretName}`);
478
+ console.log(chalk.green(`\n✓ Secret '${secretName}' deleted successfully\n`));
479
+ } catch (error) {
480
+ throw new Error(error.response?.data?.detail || error.message);
481
+ }
352
482
  }
353
483
 
354
484
  async handleKeys(input) {
355
- console.log(chalk.yellow('\n⚠️ API keys management not yet implemented\n'));
485
+ const parts = input.split(' ');
486
+ const subcommand = parts[1];
487
+
488
+ if (!subcommand || subcommand === 'help') {
489
+ console.log(chalk.bold('\n🔑 API Keys Commands:\n'));
490
+ console.log(' /keys list List all API keys');
491
+ console.log(' /keys create Create new API key');
492
+ console.log(' /keys delete <id> Delete API key');
493
+ console.log();
494
+ return;
495
+ }
496
+
497
+ switch (subcommand) {
498
+ case 'list':
499
+ await this.listKeys();
500
+ break;
501
+ case 'create':
502
+ await this.createKey();
503
+ break;
504
+ case 'delete':
505
+ await this.deleteKey(parts[2]);
506
+ break;
507
+ default:
508
+ console.log(chalk.red(`Unknown keys command: ${subcommand}`));
509
+ console.log(chalk.gray('Type /keys for available commands'));
510
+ }
511
+ }
512
+
513
+ async listKeys() {
514
+ try {
515
+ const response = await this.client.get('/service-accounts/api-keys');
516
+ const keys = response.data;
517
+
518
+ if (!keys || keys.length === 0) {
519
+ console.log(chalk.yellow('\nNo API keys found\n'));
520
+ return;
521
+ }
522
+
523
+ console.log(chalk.bold('\n🔑 API Keys:\n'));
524
+ const table = new Table({
525
+ head: [chalk.cyan('ID'), chalk.cyan('Name'), chalk.cyan('Prefix'), chalk.cyan('Created'), chalk.cyan('Last Used')],
526
+ style: { head: ['cyan'] }
527
+ });
528
+
529
+ keys.forEach(key => {
530
+ table.push([
531
+ key.id.substring(0, 8) + '...',
532
+ key.name || 'Unnamed',
533
+ key.api_key_prefix || 'N/A',
534
+ new Date(key.created_at).toLocaleDateString(),
535
+ key.last_used_at ? new Date(key.last_used_at).toLocaleDateString() : 'Never'
536
+ ]);
537
+ });
538
+
539
+ console.log(table.toString());
540
+ console.log(chalk.gray(`\nTotal: ${keys.length} key(s)\n`));
541
+ } catch (error) {
542
+ throw new Error(error.response?.data?.detail || error.message);
543
+ }
544
+ }
545
+
546
+ async createKey() {
547
+ try {
548
+ const answers = await inquirer.prompt([
549
+ {
550
+ type: 'input',
551
+ name: 'name',
552
+ message: 'Key name:',
553
+ validate: (value) => value.trim() !== '' || 'Name is required'
554
+ },
555
+ {
556
+ type: 'checkbox',
557
+ name: 'scopes',
558
+ message: 'Select scopes:',
559
+ choices: [
560
+ { name: 'Read Secrets', value: 'secrets:read', checked: true },
561
+ { name: 'Write Secrets', value: 'secrets:write' },
562
+ { name: 'Read Databases', value: 'databases:read', checked: true },
563
+ { name: 'Write Databases', value: 'databases:write' },
564
+ { name: 'Manage API Keys', value: 'api-keys:manage' }
565
+ ]
566
+ }
567
+ ]);
568
+
569
+ const response = await this.client.post('/service-accounts/api-keys', {
570
+ name: answers.name,
571
+ scopes: answers.scopes
572
+ });
573
+
574
+ console.log(chalk.green('\n✓ API Key Created!\n'));
575
+ console.log(chalk.yellow('⚠️ Save this key now - it won\'t be shown again:\n'));
576
+ console.log(chalk.white.bold(response.data.api_key));
577
+ console.log();
578
+ } catch (error) {
579
+ throw new Error(error.response?.data?.detail || error.message);
580
+ }
581
+ }
582
+
583
+ async deleteKey(keyId) {
584
+ if (!keyId) {
585
+ console.log(chalk.red('\n❌ Key ID required'));
586
+ console.log(chalk.gray('Usage: /keys delete <id>\n'));
587
+ return;
588
+ }
589
+
590
+ try {
591
+ const confirm = await inquirer.prompt([{
592
+ type: 'confirm',
593
+ name: 'confirmed',
594
+ message: `Delete API key '${keyId}'?`,
595
+ default: false
596
+ }]);
597
+
598
+ if (!confirm.confirmed) {
599
+ console.log(chalk.gray('\nCancelled\n'));
600
+ return;
601
+ }
602
+
603
+ await this.client.delete(`/service-accounts/api-keys/${keyId}`);
604
+ console.log(chalk.green(`\n✓ API key deleted successfully\n`));
605
+ } catch (error) {
606
+ throw new Error(error.response?.data?.detail || error.message);
607
+ }
356
608
  }
357
609
 
358
610
  async handleAudit(input) {
359
- console.log(chalk.yellow('\n⚠️ Audit logs not yet implemented\n'));
611
+ const parts = input.split(' ');
612
+ const subcommand = parts[1];
613
+
614
+ if (!subcommand || subcommand === 'help') {
615
+ console.log(chalk.bold('\n📋 Audit Commands:\n'));
616
+ console.log(' /audit logs View recent audit logs');
617
+ console.log(' /audit logs <limit> View last N logs');
618
+ console.log();
619
+ return;
620
+ }
621
+
622
+ switch (subcommand) {
623
+ case 'logs':
624
+ await this.showAuditLogs(parts[2] ? parseInt(parts[2]) : 10);
625
+ break;
626
+ default:
627
+ console.log(chalk.red(`Unknown audit command: ${subcommand}`));
628
+ console.log(chalk.gray('Type /audit for available commands'));
629
+ }
630
+ }
631
+
632
+ async showAuditLogs(limit = 10) {
633
+ try {
634
+ const response = await this.client.get('/audit/logs', {
635
+ params: { limit }
636
+ });
637
+ const logs = response.data;
638
+
639
+ if (!logs || logs.length === 0) {
640
+ console.log(chalk.yellow('\nNo audit logs found\n'));
641
+ return;
642
+ }
643
+
644
+ console.log(chalk.bold(`\n📋 Recent Audit Logs (Last ${limit}):\n`));
645
+ const table = new Table({
646
+ head: [chalk.cyan('Time'), chalk.cyan('User'), chalk.cyan('Action'), chalk.cyan('Resource')],
647
+ style: { head: ['cyan'] },
648
+ colWidths: [20, 25, 20, 30]
649
+ });
650
+
651
+ logs.forEach(log => {
652
+ table.push([
653
+ new Date(log.timestamp).toLocaleString(),
654
+ log.user_email || 'System',
655
+ log.action,
656
+ log.resource_type + '/' + (log.resource_id || 'N/A')
657
+ ]);
658
+ });
659
+
660
+ console.log(table.toString());
661
+ console.log(chalk.gray(`\nShowing ${logs.length} log(s)\n`));
662
+ } catch (error) {
663
+ throw new Error(error.response?.data?.detail || error.message);
664
+ }
360
665
  }
361
666
  }
362
667
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raqeb-cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Raqeb CLI - Command-line tool for Database PAM and Developer Secrets Management",
5
5
  "main": "index.js",
6
6
  "bin": {