d-drive-cli 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,48 @@ 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
+ // Normalize and 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
+ // Accept keys entered with or without the `dd_` prefix, and strip any accidental "Bearer " prefix
27
+ const rawKey = options.key.replace(/^Bearer\s+/i, '').trim();
28
+ const normalizedKey = rawKey.startsWith('dd_') ? rawKey : `dd_${rawKey}`;
29
+ const response = await axios_1.default.get(`${apiUrl}/auth/me`, {
30
+ headers: {
31
+ Authorization: `Bearer ${normalizedKey}`,
32
+ },
33
+ timeout: 10000,
34
+ });
35
+ if (response.status === 200 && response.data) {
36
+ (0, config_1.setConfig)('apiKey', normalizedKey);
37
+ if (options.url) {
38
+ (0, config_1.setConfig)('apiUrl', options.url);
39
+ }
40
+ spinner.succeed(chalk_1.default.green(`✓ API key validated and saved! Authenticated as: ${response.data.user?.discordUsername || 'User'}`));
41
+ }
42
+ else {
43
+ spinner.fail(chalk_1.default.red('✗ Invalid API key'));
44
+ process.exit(1);
45
+ }
46
+ }
47
+ catch (error) {
48
+ spinner.fail(chalk_1.default.red('✗ Failed to validate API key'));
49
+ if (error.response?.status === 401) {
50
+ console.error(chalk_1.default.red('Invalid authentication. Please check your API key.'));
51
+ }
52
+ else if (error.code === 'ECONNREFUSED') {
53
+ console.error(chalk_1.default.red('Cannot connect to API server. Is it running?'));
54
+ console.error(chalk_1.default.gray(`Tried connecting to: ${options.url || (0, config_1.getConfig)().apiUrl}`));
55
+ }
56
+ else {
57
+ console.error(chalk_1.default.red(error.message));
58
+ }
59
+ process.exit(1);
60
+ }
21
61
  }
22
- if (options.url) {
62
+ else if (options.url) {
23
63
  (0, config_1.setConfig)('apiUrl', options.url);
24
64
  console.log(chalk_1.default.green('✓ API URL configured successfully'));
25
65
  }
@@ -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.1",
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,47 @@ 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
+ // Normalize and 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
+ // Accept keys entered with or without the `dd_` prefix, and strip any accidental "Bearer " prefix
31
+ const rawKey = options.key.replace(/^Bearer\s+/i, '').trim();
32
+ const normalizedKey = rawKey.startsWith('dd_') ? rawKey : `dd_${rawKey}`;
33
+
34
+ const response = await axios.get(`${apiUrl}/auth/me`, {
35
+ headers: {
36
+ Authorization: `Bearer ${normalizedKey}`,
37
+ },
38
+ timeout: 10000,
39
+ });
24
40
 
25
- if (options.url) {
41
+ if (response.status === 200 && response.data) {
42
+ setConfig('apiKey', normalizedKey);
43
+ if (options.url) {
44
+ setConfig('apiUrl', options.url);
45
+ }
46
+ spinner.succeed(chalk.green(`✓ API key validated and saved! Authenticated as: ${response.data.user?.discordUsername || 'User'}`));
47
+ } else {
48
+ spinner.fail(chalk.red('✗ Invalid API key'));
49
+ process.exit(1);
50
+ }
51
+ } catch (error: any) {
52
+ spinner.fail(chalk.red('✗ Failed to validate API key'));
53
+ if (error.response?.status === 401) {
54
+ console.error(chalk.red('Invalid authentication. Please check your API key.'));
55
+ } else if (error.code === 'ECONNREFUSED') {
56
+ console.error(chalk.red('Cannot connect to API server. Is it running?'));
57
+ console.error(chalk.gray(`Tried connecting to: ${options.url || getConfig().apiUrl}`));
58
+ } else {
59
+ console.error(chalk.red(error.message));
60
+ }
61
+ process.exit(1);
62
+ }
63
+ } else if (options.url) {
26
64
  setConfig('apiUrl', options.url);
27
65
  console.log(chalk.green('✓ API URL configured successfully'));
28
66
  }
@@ -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;