d-drive-cli 1.0.0 → 1.1.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/README.md CHANGED
@@ -4,10 +4,32 @@ Command-line tool for D-Drive cloud storage.
4
4
 
5
5
  ## Installation
6
6
 
7
+ ### Option 1: NPM (Coming Soon)
7
8
  ```bash
8
9
  npm install -g d-drive-cli
9
10
  ```
10
11
 
12
+ ### Option 2: Local Installation (Current)
13
+ ```bash
14
+ # Clone the repository
15
+ git clone https://github.com/jasonzli-DEV/D-Drive.git
16
+ cd D-Drive/cli
17
+
18
+ # Build and install globally
19
+ npm install
20
+ npm run build
21
+ npm install -g .
22
+
23
+ # Verify installation
24
+ d-drive --version
25
+ ```
26
+
27
+ ### Option 3: Direct from Repository
28
+ ```bash
29
+ # Install directly from GitHub (once published)
30
+ npm install -g jasonzli-DEV/D-Drive/cli
31
+ ```
32
+
11
33
  ## Configuration
12
34
 
13
35
  First, configure your API key:
@@ -3,5 +3,5 @@ interface ConfigOptions {
3
3
  url?: string;
4
4
  list?: boolean;
5
5
  }
6
- export declare function configCommand(options: ConfigOptions): void;
6
+ export declare function configCommand(options: ConfigOptions): Promise<void>;
7
7
  export {};
@@ -5,8 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.configCommand = configCommand;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
+ const ora_1 = __importDefault(require("ora"));
9
+ const axios_1 = __importDefault(require("axios"));
8
10
  const config_1 = require("../config");
9
- function configCommand(options) {
11
+ async function configCommand(options) {
10
12
  if (options.list) {
11
13
  const config = (0, config_1.getAllConfig)();
12
14
  console.log(chalk_1.default.bold('Current Configuration:'));
@@ -16,10 +18,45 @@ function configCommand(options) {
16
18
  return;
17
19
  }
18
20
  if (options.key) {
19
- (0, config_1.setConfig)('apiKey', options.key);
20
- console.log(chalk_1.default.green('✓ API key configured successfully'));
21
+ const spinner = (0, ora_1.default)('Validating API key...').start();
22
+ try {
23
+ // Test the API key by calling the /auth/me endpoint
24
+ const config = (0, config_1.getConfig)();
25
+ const apiUrl = options.url || config.apiUrl || 'http://localhost:5000/api';
26
+ const response = await axios_1.default.get(`${apiUrl}/auth/me`, {
27
+ headers: {
28
+ Authorization: `Bearer ${options.key}`,
29
+ },
30
+ timeout: 10000,
31
+ });
32
+ if (response.status === 200 && response.data) {
33
+ (0, config_1.setConfig)('apiKey', options.key);
34
+ if (options.url) {
35
+ (0, config_1.setConfig)('apiUrl', options.url);
36
+ }
37
+ spinner.succeed(chalk_1.default.green(`✓ API key validated and saved! Authenticated as: ${response.data.user?.discordUsername || 'User'}`));
38
+ }
39
+ else {
40
+ spinner.fail(chalk_1.default.red('✗ Invalid API key'));
41
+ process.exit(1);
42
+ }
43
+ }
44
+ catch (error) {
45
+ spinner.fail(chalk_1.default.red('✗ Failed to validate API key'));
46
+ if (error.response?.status === 401) {
47
+ console.error(chalk_1.default.red('Invalid authentication. Please check your API key.'));
48
+ }
49
+ else if (error.code === 'ECONNREFUSED') {
50
+ console.error(chalk_1.default.red('Cannot connect to API server. Is it running?'));
51
+ console.error(chalk_1.default.gray(`Tried connecting to: ${options.url || (0, config_1.getConfig)().apiUrl}`));
52
+ }
53
+ else {
54
+ console.error(chalk_1.default.red(error.message));
55
+ }
56
+ process.exit(1);
57
+ }
21
58
  }
22
- if (options.url) {
59
+ else if (options.url) {
23
60
  (0, config_1.setConfig)('apiUrl', options.url);
24
61
  console.log(chalk_1.default.green('✓ API URL configured successfully'));
25
62
  }
@@ -11,9 +11,22 @@ async function listCommand(remotePath = '/', options) {
11
11
  const spinner = (0, ora_1.default)('Fetching files...').start();
12
12
  try {
13
13
  const api = (0, api_1.createApiClient)();
14
- const response = await api.get('/files', {
15
- params: { path: remotePath },
16
- });
14
+ // If path is root, don't send parentId (backend returns root-level files)
15
+ // Otherwise, need to find the folder by path first
16
+ let params = {};
17
+ if (remotePath && remotePath !== '/') {
18
+ // First, try to find the folder by path
19
+ const allFilesRes = await api.get('/files');
20
+ const targetFolder = findFolderByPath(allFilesRes.data, remotePath);
21
+ if (targetFolder) {
22
+ params.parentId = targetFolder.id;
23
+ }
24
+ else {
25
+ spinner.fail(chalk_1.default.red(`Folder not found: ${remotePath}`));
26
+ process.exit(1);
27
+ }
28
+ }
29
+ const response = await api.get('/files', { params });
17
30
  const files = response.data;
18
31
  spinner.stop();
19
32
  if (files.length === 0) {
@@ -47,6 +60,16 @@ async function listCommand(remotePath = '/', options) {
47
60
  process.exit(1);
48
61
  }
49
62
  }
63
+ function findFolderByPath(files, targetPath) {
64
+ // Simple path matching - looks for folder by name in the path
65
+ const pathParts = targetPath.split('/').filter(p => p);
66
+ for (const file of files) {
67
+ if (file.type === 'DIRECTORY' && file.name === pathParts[0]) {
68
+ return file;
69
+ }
70
+ }
71
+ return null;
72
+ }
50
73
  function formatFileSize(bytes) {
51
74
  if (bytes === 0)
52
75
  return '0 Bytes';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "d-drive-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "D-Drive CLI tool for developers",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,4 +1,6 @@
1
1
  import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import axios from 'axios';
2
4
  import { getConfig, setConfig, getAllConfig } from '../config';
3
5
 
4
6
  interface ConfigOptions {
@@ -7,7 +9,7 @@ interface ConfigOptions {
7
9
  list?: boolean;
8
10
  }
9
11
 
10
- export function configCommand(options: ConfigOptions) {
12
+ export async function configCommand(options: ConfigOptions) {
11
13
  if (options.list) {
12
14
  const config = getAllConfig();
13
15
  console.log(chalk.bold('Current Configuration:'));
@@ -18,11 +20,43 @@ export function configCommand(options: ConfigOptions) {
18
20
  }
19
21
 
20
22
  if (options.key) {
21
- setConfig('apiKey', options.key);
22
- console.log(chalk.green('✓ API key configured successfully'));
23
- }
23
+ const spinner = ora('Validating API key...').start();
24
+
25
+ try {
26
+ // Test the API key by calling the /auth/me endpoint
27
+ const config = getConfig();
28
+ const apiUrl = options.url || config.apiUrl || 'http://localhost:5000/api';
29
+
30
+ const response = await axios.get(`${apiUrl}/auth/me`, {
31
+ headers: {
32
+ Authorization: `Bearer ${options.key}`,
33
+ },
34
+ timeout: 10000,
35
+ });
24
36
 
25
- if (options.url) {
37
+ if (response.status === 200 && response.data) {
38
+ setConfig('apiKey', options.key);
39
+ if (options.url) {
40
+ setConfig('apiUrl', options.url);
41
+ }
42
+ spinner.succeed(chalk.green(`✓ API key validated and saved! Authenticated as: ${response.data.user?.discordUsername || 'User'}`));
43
+ } else {
44
+ spinner.fail(chalk.red('✗ Invalid API key'));
45
+ process.exit(1);
46
+ }
47
+ } catch (error: any) {
48
+ spinner.fail(chalk.red('✗ Failed to validate API key'));
49
+ if (error.response?.status === 401) {
50
+ console.error(chalk.red('Invalid authentication. Please check your API key.'));
51
+ } else if (error.code === 'ECONNREFUSED') {
52
+ console.error(chalk.red('Cannot connect to API server. Is it running?'));
53
+ console.error(chalk.gray(`Tried connecting to: ${options.url || getConfig().apiUrl}`));
54
+ } else {
55
+ console.error(chalk.red(error.message));
56
+ }
57
+ process.exit(1);
58
+ }
59
+ } else if (options.url) {
26
60
  setConfig('apiUrl', options.url);
27
61
  console.log(chalk.green('✓ API URL configured successfully'));
28
62
  }
@@ -12,9 +12,24 @@ export async function listCommand(remotePath: string = '/', options: ListOptions
12
12
  try {
13
13
  const api = createApiClient();
14
14
 
15
- const response = await api.get('/files', {
16
- params: { path: remotePath },
17
- });
15
+ // If path is root, don't send parentId (backend returns root-level files)
16
+ // Otherwise, need to find the folder by path first
17
+ let params: any = {};
18
+
19
+ if (remotePath && remotePath !== '/') {
20
+ // First, try to find the folder by path
21
+ const allFilesRes = await api.get('/files');
22
+ const targetFolder = findFolderByPath(allFilesRes.data, remotePath);
23
+
24
+ if (targetFolder) {
25
+ params.parentId = targetFolder.id;
26
+ } else {
27
+ spinner.fail(chalk.red(`Folder not found: ${remotePath}`));
28
+ process.exit(1);
29
+ }
30
+ }
31
+
32
+ const response = await api.get('/files', { params });
18
33
 
19
34
  const files = response.data;
20
35
  spinner.stop();
@@ -55,6 +70,18 @@ export async function listCommand(remotePath: string = '/', options: ListOptions
55
70
  }
56
71
  }
57
72
 
73
+ function findFolderByPath(files: any[], targetPath: string): any | null {
74
+ // Simple path matching - looks for folder by name in the path
75
+ const pathParts = targetPath.split('/').filter(p => p);
76
+
77
+ for (const file of files) {
78
+ if (file.type === 'DIRECTORY' && file.name === pathParts[0]) {
79
+ return file;
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+
58
85
  function formatFileSize(bytes: number): string {
59
86
  if (bytes === 0) return '0 Bytes';
60
87
  const k = 1024;