directify-cli 1.0.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/.gitattributes +2 -0
- package/README.md +297 -0
- package/package.json +32 -0
- package/src/commands/articles.js +193 -0
- package/src/commands/auth.js +54 -0
- package/src/commands/categories.js +146 -0
- package/src/commands/config.js +27 -0
- package/src/commands/directories.js +35 -0
- package/src/commands/fields.js +41 -0
- package/src/commands/listings.js +243 -0
- package/src/commands/tags.js +138 -0
- package/src/index.js +29 -0
- package/src/utils/api.js +94 -0
- package/src/utils/output.js +65 -0
package/src/utils/api.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
|
|
3
|
+
const config = new Conf({ projectName: 'directify-cli' });
|
|
4
|
+
|
|
5
|
+
const BASE_URL = 'https://directify.app/api';
|
|
6
|
+
|
|
7
|
+
export function getToken() {
|
|
8
|
+
return config.get('token');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function setToken(token) {
|
|
12
|
+
config.set('token', token);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function clearToken() {
|
|
16
|
+
config.delete('token');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getDefaultDirectory() {
|
|
20
|
+
return config.get('directory');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function setDefaultDirectory(id) {
|
|
24
|
+
config.set('directory', id);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function resolveDirectory(opts) {
|
|
28
|
+
const dir = opts.directory || getDefaultDirectory();
|
|
29
|
+
if (!dir) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
'No directory specified. Use --directory <id> or set a default with: directify config set-directory <id>'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function request(method, path, body = null) {
|
|
38
|
+
const token = getToken();
|
|
39
|
+
if (!token) {
|
|
40
|
+
throw new Error('Not authenticated. Run: directify auth login <token>');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const url = `${BASE_URL}${path}`;
|
|
44
|
+
const headers = {
|
|
45
|
+
Authorization: `Bearer ${token}`,
|
|
46
|
+
Accept: 'application/json',
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const options = { method, headers };
|
|
51
|
+
if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
|
|
52
|
+
options.body = JSON.stringify(body);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const res = await fetch(url, options);
|
|
56
|
+
|
|
57
|
+
if (res.status === 401) {
|
|
58
|
+
throw new Error('Authentication failed. Check your token with: directify auth login <token>');
|
|
59
|
+
}
|
|
60
|
+
if (res.status === 403) {
|
|
61
|
+
throw new Error('Access denied. You do not have permission to access this resource.');
|
|
62
|
+
}
|
|
63
|
+
if (res.status === 404) {
|
|
64
|
+
throw new Error('Resource not found.');
|
|
65
|
+
}
|
|
66
|
+
if (res.status === 422) {
|
|
67
|
+
const data = await res.json();
|
|
68
|
+
const errors = data.errors
|
|
69
|
+
? Object.entries(data.errors)
|
|
70
|
+
.map(([field, msgs]) => ` ${field}: ${Array.isArray(msgs) ? msgs.join(', ') : msgs}`)
|
|
71
|
+
.join('\n')
|
|
72
|
+
: data.message || 'Validation failed';
|
|
73
|
+
throw new Error(`Validation error:\n${errors}`);
|
|
74
|
+
}
|
|
75
|
+
if (res.status === 429) {
|
|
76
|
+
throw new Error('Rate limit exceeded. Please wait a moment and try again (limit: 120 requests/minute).');
|
|
77
|
+
}
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
const text = await res.text().catch(() => '');
|
|
80
|
+
throw new Error(`API error (${res.status}): ${text || res.statusText}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (res.status === 204) return null;
|
|
84
|
+
|
|
85
|
+
return res.json();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const api = {
|
|
89
|
+
get: (path) => request('GET', path),
|
|
90
|
+
post: (path, body) => request('POST', path, body),
|
|
91
|
+
put: (path, body) => request('PUT', path, body),
|
|
92
|
+
patch: (path, body) => request('PATCH', path, body),
|
|
93
|
+
delete: (path) => request('DELETE', path),
|
|
94
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export function printJson(data) {
|
|
4
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function printTable(items, columns) {
|
|
8
|
+
if (!items || items.length === 0) {
|
|
9
|
+
console.log(chalk.dim(' No results found.'));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Calculate column widths
|
|
14
|
+
const widths = {};
|
|
15
|
+
for (const col of columns) {
|
|
16
|
+
const key = col.key || col;
|
|
17
|
+
const label = col.label || key;
|
|
18
|
+
widths[key] = Math.max(label.length, ...items.map((item) => String(getValue(item, key) ?? '').length));
|
|
19
|
+
widths[key] = Math.min(widths[key], col.maxWidth || 50);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Header
|
|
23
|
+
const header = columns
|
|
24
|
+
.map((col) => {
|
|
25
|
+
const label = col.label || col.key || col;
|
|
26
|
+
const key = col.key || col;
|
|
27
|
+
return label.toUpperCase().padEnd(widths[key]);
|
|
28
|
+
})
|
|
29
|
+
.join(' ');
|
|
30
|
+
console.log(chalk.bold(header));
|
|
31
|
+
console.log(chalk.dim('-'.repeat(header.length)));
|
|
32
|
+
|
|
33
|
+
// Rows
|
|
34
|
+
for (const item of items) {
|
|
35
|
+
const row = columns
|
|
36
|
+
.map((col) => {
|
|
37
|
+
const key = col.key || col;
|
|
38
|
+
let val = String(getValue(item, key) ?? '');
|
|
39
|
+
if (val.length > (col.maxWidth || 50)) {
|
|
40
|
+
val = val.slice(0, (col.maxWidth || 50) - 3) + '...';
|
|
41
|
+
}
|
|
42
|
+
return val.padEnd(widths[key]);
|
|
43
|
+
})
|
|
44
|
+
.join(' ');
|
|
45
|
+
console.log(row);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(chalk.dim(`\n${items.length} item(s)`));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getValue(obj, path) {
|
|
52
|
+
return path.split('.').reduce((o, k) => (o ? o[k] : undefined), obj);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function printSuccess(message) {
|
|
56
|
+
console.log(chalk.green(`\u2713 ${message}`));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function printError(message) {
|
|
60
|
+
console.error(chalk.red(`\u2717 ${message}`));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function printInfo(message) {
|
|
64
|
+
console.log(chalk.blue(`\u2139 ${message}`));
|
|
65
|
+
}
|