elestio 1.0.3 → 1.2.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/LICENSE +21 -21
- package/README.md +523 -279
- package/bin/elestio.js +5 -5
- package/package.json +49 -42
- package/src/api.js +143 -105
- package/src/cli.js +32 -838
- package/src/commands/access.js +224 -132
- package/src/commands/actions.js +427 -427
- package/src/commands/auth.js +146 -146
- package/src/commands/backups.js +215 -215
- package/src/commands/billing.js +106 -106
- package/src/commands/cicd-template.js +363 -0
- package/src/commands/cicd.js +509 -486
- package/src/commands/clusters.js +589 -0
- package/src/commands/projects.js +145 -145
- package/src/commands/services.js +306 -294
- package/src/commands/templates.js +188 -188
- package/src/commands/volumes.js +117 -117
- package/src/config.js +104 -84
- package/src/constants.js +76 -0
- package/src/deployment.js +33 -0
- package/src/payloads/pipeline.js +191 -0
- package/src/payloads/server.js +130 -0
- package/src/registry.js +715 -0
- package/src/router.js +188 -0
- package/src/templates/elestio-config.js +188 -0
- package/src/utils.js +174 -162
package/src/utils.js
CHANGED
|
@@ -1,162 +1,174 @@
|
|
|
1
|
-
// ── ANSI Colors ──
|
|
2
|
-
|
|
3
|
-
export const colors = {
|
|
4
|
-
reset: '\x1b[0m',
|
|
5
|
-
bold: '\x1b[1m',
|
|
6
|
-
dim: '\x1b[2m',
|
|
7
|
-
red: '\x1b[31m',
|
|
8
|
-
green: '\x1b[32m',
|
|
9
|
-
yellow: '\x1b[33m',
|
|
10
|
-
blue: '\x1b[34m',
|
|
11
|
-
magenta: '\x1b[35m',
|
|
12
|
-
cyan: '\x1b[36m',
|
|
13
|
-
white: '\x1b[37m',
|
|
14
|
-
gray: '\x1b[90m'
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// ── Logging ──
|
|
18
|
-
|
|
19
|
-
export function log(type, message) {
|
|
20
|
-
const prefix = {
|
|
21
|
-
info: `${colors.blue}[INFO]${colors.reset}`,
|
|
22
|
-
success: `${colors.green}[SUCCESS]${colors.reset}`,
|
|
23
|
-
error: `${colors.red}[ERROR]${colors.reset}`,
|
|
24
|
-
warn: `${colors.yellow}[WARN]${colors.reset}`,
|
|
25
|
-
debug: `${colors.gray}[DEBUG]${colors.reset}`
|
|
26
|
-
};
|
|
27
|
-
console.log(`${prefix[type] || ''} ${message}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ── Argument parsing ──
|
|
31
|
-
|
|
32
|
-
export function parseArgs(argv) {
|
|
33
|
-
const args = { _: [] };
|
|
34
|
-
let i = 0;
|
|
35
|
-
|
|
36
|
-
while (i < argv.length) {
|
|
37
|
-
const arg = argv[i];
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
1
|
+
// ── ANSI Colors ──
|
|
2
|
+
|
|
3
|
+
export const colors = {
|
|
4
|
+
reset: '\x1b[0m',
|
|
5
|
+
bold: '\x1b[1m',
|
|
6
|
+
dim: '\x1b[2m',
|
|
7
|
+
red: '\x1b[31m',
|
|
8
|
+
green: '\x1b[32m',
|
|
9
|
+
yellow: '\x1b[33m',
|
|
10
|
+
blue: '\x1b[34m',
|
|
11
|
+
magenta: '\x1b[35m',
|
|
12
|
+
cyan: '\x1b[36m',
|
|
13
|
+
white: '\x1b[37m',
|
|
14
|
+
gray: '\x1b[90m'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// ── Logging ──
|
|
18
|
+
|
|
19
|
+
export function log(type, message) {
|
|
20
|
+
const prefix = {
|
|
21
|
+
info: `${colors.blue}[INFO]${colors.reset}`,
|
|
22
|
+
success: `${colors.green}[SUCCESS]${colors.reset}`,
|
|
23
|
+
error: `${colors.red}[ERROR]${colors.reset}`,
|
|
24
|
+
warn: `${colors.yellow}[WARN]${colors.reset}`,
|
|
25
|
+
debug: `${colors.gray}[DEBUG]${colors.reset}`
|
|
26
|
+
};
|
|
27
|
+
console.log(`${prefix[type] || ''} ${message}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── Argument parsing ──
|
|
31
|
+
|
|
32
|
+
export function parseArgs(argv) {
|
|
33
|
+
const args = { _: [] };
|
|
34
|
+
let i = 0;
|
|
35
|
+
|
|
36
|
+
while (i < argv.length) {
|
|
37
|
+
const arg = argv[i];
|
|
38
|
+
|
|
39
|
+
// Everything after `--` is positional, never parsed as a flag.
|
|
40
|
+
if (arg === '--') {
|
|
41
|
+
args._.push(...argv.slice(i + 1));
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// `--key=value` is the unambiguous form: it is the only way to pass a value
|
|
46
|
+
// that itself starts with a dash (passwords, negative numbers).
|
|
47
|
+
if (arg.startsWith('--') && arg.includes('=')) {
|
|
48
|
+
const eq = arg.indexOf('=');
|
|
49
|
+
args[arg.slice(2, eq)] = arg.slice(eq + 1);
|
|
50
|
+
i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (arg.startsWith('--') || (arg.startsWith('-') && arg.length === 2)) {
|
|
55
|
+
const key = arg.startsWith('--') ? arg.slice(2) : arg.slice(1);
|
|
56
|
+
const next = argv[i + 1];
|
|
57
|
+
|
|
58
|
+
if (next !== undefined && !looksLikeFlag(next)) {
|
|
59
|
+
args[key] = next;
|
|
60
|
+
i += 2;
|
|
61
|
+
} else {
|
|
62
|
+
args[key] = true;
|
|
63
|
+
i++;
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
args._.push(arg);
|
|
67
|
+
i++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return args;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* `-p` and `--port` are flags; `-5`, `-p@ssw0rd` and `-` are values.
|
|
76
|
+
* Values that are genuinely ambiguous must use the `--key=value` form.
|
|
77
|
+
*/
|
|
78
|
+
function looksLikeFlag(token) {
|
|
79
|
+
return /^--[a-zA-Z]/.test(token) || /^-[a-zA-Z]$/.test(token);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Table formatting ──
|
|
83
|
+
|
|
84
|
+
export function formatTable(data, columns) {
|
|
85
|
+
if (!data || data.length === 0) return 'No data';
|
|
86
|
+
|
|
87
|
+
const widths = {};
|
|
88
|
+
columns.forEach(col => { widths[col.key] = col.label.length; });
|
|
89
|
+
|
|
90
|
+
data.forEach(row => {
|
|
91
|
+
columns.forEach(col => {
|
|
92
|
+
const val = String(row[col.key] ?? '');
|
|
93
|
+
widths[col.key] = Math.max(widths[col.key], val.length);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const header = columns.map(col => col.label.padEnd(widths[col.key])).join(' ');
|
|
98
|
+
const separator = columns.map(col => '-'.repeat(widths[col.key])).join(' ');
|
|
99
|
+
const rows = data.map(row =>
|
|
100
|
+
columns.map(col => String(row[col.key] ?? '').padEnd(widths[col.key])).join(' ')
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return [header, separator, ...rows].join('\n');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Helpers ──
|
|
107
|
+
|
|
108
|
+
export function formatBytes(bytes) {
|
|
109
|
+
if (bytes === 0) return '0 B';
|
|
110
|
+
const k = 1024;
|
|
111
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
112
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
113
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function formatPrice(pricePerHour) {
|
|
117
|
+
const hourly = parseFloat(pricePerHour);
|
|
118
|
+
if (!Number.isFinite(hourly)) return 'N/A';
|
|
119
|
+
return `$${(hourly * 24 * 30).toFixed(2)}/mo`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function validateServerName(name) {
|
|
123
|
+
if (!name) return { valid: false, error: 'Server name is required' };
|
|
124
|
+
if (name.length < 3) return { valid: false, error: 'Server name must be at least 3 characters' };
|
|
125
|
+
if (name.length > 50) return { valid: false, error: 'Server name must be at most 50 characters' };
|
|
126
|
+
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(name) && name.length > 1) {
|
|
127
|
+
return { valid: false, error: 'Server name must be lowercase alphanumeric with hyphens, cannot start/end with hyphen' };
|
|
128
|
+
}
|
|
129
|
+
return { valid: true };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function sleep(ms) {
|
|
133
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function formatService(svc) {
|
|
137
|
+
const status = svc.status === 'running'
|
|
138
|
+
? `${colors.green}running${colors.reset}`
|
|
139
|
+
: `${colors.yellow}${svc.status}${colors.reset}`;
|
|
140
|
+
|
|
141
|
+
const deployment = svc.deploymentStatus === 'Deployed'
|
|
142
|
+
? `${colors.green}Deployed${colors.reset}`
|
|
143
|
+
: `${colors.yellow}${svc.deploymentStatus}${colors.reset}`;
|
|
144
|
+
|
|
145
|
+
return `
|
|
146
|
+
${colors.bold}${svc.displayName}${colors.reset} (${svc.templateName || 'CI/CD'})
|
|
147
|
+
Status: ${status} | Deployment: ${deployment}
|
|
148
|
+
IP: ${svc.ipv4 || 'pending'}
|
|
149
|
+
URL: https://${svc.cname || 'pending'}/
|
|
150
|
+
vmID: ${svc.vmID} | serverID: ${svc.id}
|
|
151
|
+
Size: ${svc.serverType} (${svc.cores} cores, ${svc.ramGB}GB RAM)
|
|
152
|
+
Provider: ${svc.provider} / ${svc.datacenter || 'N/A'}
|
|
153
|
+
Cost: ${formatPrice(svc.pricePerHour)}`.trim();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function showHelp(command, subcommands) {
|
|
157
|
+
console.log(`\n${colors.bold}Usage:${colors.reset} elestio ${command} <action> [options]\n`);
|
|
158
|
+
console.log(`${colors.bold}Actions:${colors.reset}`);
|
|
159
|
+
Object.entries(subcommands).forEach(([name, desc]) => {
|
|
160
|
+
console.log(` ${colors.cyan}${name.padEnd(25)}${colors.reset} ${desc}`);
|
|
161
|
+
});
|
|
162
|
+
console.log('');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function truncate(str, maxLen = 30) {
|
|
166
|
+
if (!str) return '';
|
|
167
|
+
return str.length > maxLen ? str.slice(0, maxLen - 3) + '...' : str;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── JSON output helper ──
|
|
171
|
+
|
|
172
|
+
export function outputJson(data) {
|
|
173
|
+
console.log(JSON.stringify(data, null, 2));
|
|
174
|
+
}
|