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 +22 -0
- package/dist/commands/config.d.ts +1 -1
- package/dist/commands/config.js +41 -4
- package/dist/commands/list.js +26 -3
- package/package.json +1 -1
- package/src/commands/config.ts +39 -5
- package/src/commands/list.ts +30 -3
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:
|
package/dist/commands/config.js
CHANGED
|
@@ -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,
|
|
20
|
-
|
|
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
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -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
|
-
|
|
15
|
-
|
|
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
package/src/commands/config.ts
CHANGED
|
@@ -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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/commands/list.ts
CHANGED
|
@@ -12,9 +12,24 @@ export async function listCommand(remotePath: string = '/', options: ListOptions
|
|
|
12
12
|
try {
|
|
13
13
|
const api = createApiClient();
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
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;
|