elestio 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/LICENSE +21 -0
- package/README.md +262 -0
- package/bin/elestio.js +5 -0
- package/package.json +42 -0
- package/src/api.js +105 -0
- package/src/cli.js +828 -0
- package/src/commands/access.js +132 -0
- package/src/commands/actions.js +427 -0
- package/src/commands/auth.js +146 -0
- package/src/commands/backups.js +215 -0
- package/src/commands/billing.js +106 -0
- package/src/commands/cicd.js +403 -0
- package/src/commands/projects.js +145 -0
- package/src/commands/services.js +294 -0
- package/src/commands/templates.js +188 -0
- package/src/commands/volumes.js +117 -0
- package/src/config.js +84 -0
- package/src/utils.js +162 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
import { parseArgs, log, colors, showHelp } from './utils.js';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
const pkg = require('../package.json');
|
|
6
|
+
|
|
7
|
+
// ── Main help ──
|
|
8
|
+
|
|
9
|
+
function printHelp() {
|
|
10
|
+
console.log(`
|
|
11
|
+
${colors.bold}Elestio CLI${colors.reset} v${pkg.version}
|
|
12
|
+
Deploy and manage services on the Elestio DevOps platform.
|
|
13
|
+
|
|
14
|
+
${colors.bold}Usage:${colors.reset} elestio <command> [action] [options]
|
|
15
|
+
|
|
16
|
+
${colors.bold}Auth & Config${colors.reset}
|
|
17
|
+
login Configure credentials (--email, --token)
|
|
18
|
+
whoami Show current user
|
|
19
|
+
config Show/update configuration
|
|
20
|
+
auth test Test authentication
|
|
21
|
+
|
|
22
|
+
${colors.bold}Catalog${colors.reset}
|
|
23
|
+
templates Browse software templates
|
|
24
|
+
sizes List server sizes and pricing
|
|
25
|
+
categories List template categories
|
|
26
|
+
|
|
27
|
+
${colors.bold}Projects${colors.reset}
|
|
28
|
+
projects Manage projects (list, create, edit, delete, members)
|
|
29
|
+
|
|
30
|
+
${colors.bold}Services${colors.reset}
|
|
31
|
+
services List services in a project
|
|
32
|
+
service Show service details
|
|
33
|
+
deploy Deploy a new service
|
|
34
|
+
delete-service Delete a service
|
|
35
|
+
move-service Move a service to another project
|
|
36
|
+
wait <vmID> Wait for a deployment to complete
|
|
37
|
+
|
|
38
|
+
${colors.bold}Server Actions${colors.reset}
|
|
39
|
+
reboot <vmID> Reboot a VM
|
|
40
|
+
reset <vmID> Hard reset a VM
|
|
41
|
+
shutdown <vmID> Shutdown a VM
|
|
42
|
+
poweroff <vmID> Force power off
|
|
43
|
+
poweron <vmID> Power on a VM
|
|
44
|
+
restart-stack <vmID> Restart Docker stack
|
|
45
|
+
lock <vmID> Enable termination protection
|
|
46
|
+
unlock <vmID> Disable termination protection
|
|
47
|
+
resize <vmID> Resize a VM
|
|
48
|
+
|
|
49
|
+
${colors.bold}Networking & Security${colors.reset}
|
|
50
|
+
firewall Manage firewall rules
|
|
51
|
+
ssl Manage SSL/custom domains
|
|
52
|
+
ssh-keys Manage SSH keys
|
|
53
|
+
|
|
54
|
+
${colors.bold}Updates${colors.reset}
|
|
55
|
+
updates Manage auto-updates (system & app)
|
|
56
|
+
|
|
57
|
+
${colors.bold}Monitoring${colors.reset}
|
|
58
|
+
alerts Manage alert rules
|
|
59
|
+
|
|
60
|
+
${colors.bold}Backups & Snapshots${colors.reset}
|
|
61
|
+
backups Manage local/remote backups
|
|
62
|
+
snapshots Manage VM snapshots
|
|
63
|
+
s3-backup Manage S3 external backups
|
|
64
|
+
|
|
65
|
+
${colors.bold}Access${colors.reset}
|
|
66
|
+
credentials <vmID> Get app credentials
|
|
67
|
+
ssh <vmID> Get SSH/web terminal access
|
|
68
|
+
vscode <vmID> Get VSCode web access
|
|
69
|
+
files <vmID> Get file explorer access
|
|
70
|
+
|
|
71
|
+
${colors.bold}Volumes${colors.reset}
|
|
72
|
+
volumes Manage block storage volumes
|
|
73
|
+
|
|
74
|
+
${colors.bold}CI/CD${colors.reset}
|
|
75
|
+
cicd Manage CI/CD targets and pipelines
|
|
76
|
+
|
|
77
|
+
${colors.bold}Billing${colors.reset}
|
|
78
|
+
billing View billing summary and details
|
|
79
|
+
|
|
80
|
+
${colors.bold}Global Options${colors.reset}
|
|
81
|
+
--json Output in JSON format
|
|
82
|
+
--project <id> Specify project ID
|
|
83
|
+
--help, -h Show help
|
|
84
|
+
--version, -v Show version
|
|
85
|
+
`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── Command router ──
|
|
89
|
+
|
|
90
|
+
export async function run(argv) {
|
|
91
|
+
const args = parseArgs(argv);
|
|
92
|
+
const command = args._[0];
|
|
93
|
+
const action = args._[1];
|
|
94
|
+
const json = !!args.json;
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
if (args.version || args.v) {
|
|
98
|
+
console.log(pkg.version);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!command || args.help || args.h) {
|
|
103
|
+
printHelp();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
switch (command) {
|
|
108
|
+
|
|
109
|
+
// ── Auth & Config ──
|
|
110
|
+
|
|
111
|
+
case 'login': {
|
|
112
|
+
const { configure } = await import('./commands/auth.js');
|
|
113
|
+
await configure(args.email || args._[1], args.token || args._[2], json);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
case 'whoami': {
|
|
118
|
+
const { whoami } = await import('./commands/auth.js');
|
|
119
|
+
whoami(json);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
case 'config': {
|
|
124
|
+
const auth = await import('./commands/auth.js');
|
|
125
|
+
if (args.email && args.token) {
|
|
126
|
+
await auth.configure(args.email, args.token, json);
|
|
127
|
+
} else if (args['set-default-project'] || args['default-project']) {
|
|
128
|
+
auth.setDefaultProject(args['set-default-project'] || args['default-project']);
|
|
129
|
+
} else if (args.provider || args.datacenter || args['server-type'] || args.support) {
|
|
130
|
+
auth.setDefaults(args.provider, args.datacenter, args['server-type'], args.support);
|
|
131
|
+
} else if (action === 'test') {
|
|
132
|
+
await auth.testAuth(json);
|
|
133
|
+
} else {
|
|
134
|
+
auth.showConfig(json);
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── Catalog ──
|
|
140
|
+
|
|
141
|
+
case 'templates': {
|
|
142
|
+
const { listTemplates, searchTemplates, findTemplate } = await import('./commands/templates.js');
|
|
143
|
+
if (action === 'search') {
|
|
144
|
+
const results = await searchTemplates(args._[2], args.category);
|
|
145
|
+
if (json) {
|
|
146
|
+
const { outputJson } = await import('./utils.js');
|
|
147
|
+
outputJson(results.map(t => ({ id: t.id, title: t.title, category: t.category })));
|
|
148
|
+
} else {
|
|
149
|
+
console.log(`\n${colors.bold}Search Results (${results.length})${colors.reset}\n`);
|
|
150
|
+
results.forEach(t => console.log(` ${t.id}: ${t.title} [${t.category}]`));
|
|
151
|
+
console.log('');
|
|
152
|
+
}
|
|
153
|
+
} else if (action === 'info') {
|
|
154
|
+
const template = await findTemplate(args._[2]);
|
|
155
|
+
if (!template) throw new Error(`Template "${args._[2]}" not found`);
|
|
156
|
+
if (json) {
|
|
157
|
+
const { outputJson } = await import('./utils.js');
|
|
158
|
+
outputJson(template);
|
|
159
|
+
} else {
|
|
160
|
+
console.log(`\n${colors.bold}${template.title}${colors.reset}`);
|
|
161
|
+
console.log(` ID: ${template.id}`);
|
|
162
|
+
console.log(` Category: ${template.category}`);
|
|
163
|
+
console.log(` Version: ${template.version || template.dockerhub_default_tag || 'latest'}`);
|
|
164
|
+
if (template.description) console.log(` Desc: ${template.description}`);
|
|
165
|
+
console.log('');
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
await listTemplates(args.category, json);
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
case 'sizes': {
|
|
174
|
+
const { listSizes } = await import('./commands/templates.js');
|
|
175
|
+
await listSizes(args.provider, args.country, json);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
case 'categories': {
|
|
180
|
+
const { listCategories } = await import('./commands/templates.js');
|
|
181
|
+
await listCategories(json);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Projects ──
|
|
186
|
+
|
|
187
|
+
case 'projects': {
|
|
188
|
+
const projects = await import('./commands/projects.js');
|
|
189
|
+
switch (action) {
|
|
190
|
+
case 'create':
|
|
191
|
+
await projects.createProject(args._[2] || args.name, args.description, args.emails);
|
|
192
|
+
break;
|
|
193
|
+
case 'edit':
|
|
194
|
+
await projects.editProject(args._[2] || args.id, args.name, args.description, args.emails);
|
|
195
|
+
break;
|
|
196
|
+
case 'delete':
|
|
197
|
+
await projects.deleteProject(args._[2] || args.id, !!args.force);
|
|
198
|
+
break;
|
|
199
|
+
case 'members':
|
|
200
|
+
await projects.listMembers(args._[2] || args.project || args.id, json);
|
|
201
|
+
break;
|
|
202
|
+
case 'add-member':
|
|
203
|
+
await projects.addMember(args._[2] || args.project, args._[3] || args.email, args.role);
|
|
204
|
+
break;
|
|
205
|
+
case 'remove-member':
|
|
206
|
+
await projects.removeMember(args._[2] || args.project, args._[3] || args.member);
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
if (action && action !== 'list') {
|
|
210
|
+
showHelp('projects', {
|
|
211
|
+
'list': 'List all projects',
|
|
212
|
+
'create <name>': 'Create a project',
|
|
213
|
+
'edit <id>': 'Edit a project (--name, --description)',
|
|
214
|
+
'delete <id>': 'Delete a project (--force)',
|
|
215
|
+
'members <projectId>': 'List project members',
|
|
216
|
+
'add-member <projectId> <email>': 'Add a member (--role)',
|
|
217
|
+
'remove-member <projectId> <memberId>': 'Remove a member'
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
await projects.listProjects(json);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── Services ──
|
|
227
|
+
|
|
228
|
+
case 'services': {
|
|
229
|
+
const { listServices } = await import('./commands/services.js');
|
|
230
|
+
await listServices(args.project, json);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
case 'service': {
|
|
235
|
+
const { showService } = await import('./commands/services.js');
|
|
236
|
+
if (!action) throw new Error('Usage: elestio service <vmID>');
|
|
237
|
+
await showService(action, args.project, json);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
case 'deploy': {
|
|
242
|
+
const { deployService } = await import('./commands/services.js');
|
|
243
|
+
if (!action) throw new Error('Usage: elestio deploy <template> [options]');
|
|
244
|
+
await deployService(action, {
|
|
245
|
+
name: args.name, size: args.size, region: args.region,
|
|
246
|
+
provider: args.provider, project: args.project,
|
|
247
|
+
support: args.support, email: args.email,
|
|
248
|
+
version: args.version, dryRun: !!args['dry-run'],
|
|
249
|
+
wait: args.wait !== 'false', timeout: args.timeout ? parseInt(args.timeout) : undefined,
|
|
250
|
+
json, pipelineName: args['pipeline-name']
|
|
251
|
+
});
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
case 'delete-service': {
|
|
256
|
+
const { deleteService } = await import('./commands/services.js');
|
|
257
|
+
if (!action) throw new Error('Usage: elestio delete-service <vmID> --force');
|
|
258
|
+
await deleteService(action, { force: !!args.force, project: args.project, withBackups: !!args['with-backups'] });
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
case 'move-service': {
|
|
263
|
+
const { moveService } = await import('./commands/services.js');
|
|
264
|
+
const vmID = args._[1];
|
|
265
|
+
const targetProject = args._[2] || args.target;
|
|
266
|
+
if (!vmID || !targetProject) throw new Error('Usage: elestio move-service <vmID> <targetProjectId>');
|
|
267
|
+
await moveService(vmID, targetProject, args.project);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
case 'wait': {
|
|
272
|
+
const { waitForDeployment } = await import('./commands/services.js');
|
|
273
|
+
if (!action) throw new Error('Usage: elestio wait <vmID> [--project <id>]');
|
|
274
|
+
await waitForDeployment(action, args.project, args.timeout ? parseInt(args.timeout) : undefined);
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ── Auth ──
|
|
279
|
+
|
|
280
|
+
case 'auth': {
|
|
281
|
+
const auth = await import('./commands/auth.js');
|
|
282
|
+
if (action === 'test' || !action) {
|
|
283
|
+
await auth.testAuth(json);
|
|
284
|
+
} else {
|
|
285
|
+
showHelp('auth', { 'test': 'Test authentication' });
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ── Server Actions ──
|
|
291
|
+
|
|
292
|
+
case 'reboot': {
|
|
293
|
+
const { reboot } = await import('./commands/actions.js');
|
|
294
|
+
if (!action) throw new Error('Usage: elestio reboot <vmID>');
|
|
295
|
+
await reboot(action);
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
case 'reset': {
|
|
300
|
+
const { reset } = await import('./commands/actions.js');
|
|
301
|
+
if (!action) throw new Error('Usage: elestio reset <vmID>');
|
|
302
|
+
await reset(action);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
case 'shutdown': {
|
|
307
|
+
const { shutdown } = await import('./commands/actions.js');
|
|
308
|
+
if (!action) throw new Error('Usage: elestio shutdown <vmID>');
|
|
309
|
+
await shutdown(action, { project: args.project });
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
case 'poweroff': {
|
|
314
|
+
const { poweroff } = await import('./commands/actions.js');
|
|
315
|
+
if (!action) throw new Error('Usage: elestio poweroff <vmID>');
|
|
316
|
+
await poweroff(action);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
case 'poweron': {
|
|
321
|
+
const { poweron } = await import('./commands/actions.js');
|
|
322
|
+
if (!action) throw new Error('Usage: elestio poweron <vmID>');
|
|
323
|
+
await poweron(action);
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
case 'restart-stack': {
|
|
328
|
+
const { restartStack } = await import('./commands/actions.js');
|
|
329
|
+
if (!action) throw new Error('Usage: elestio restart-stack <vmID>');
|
|
330
|
+
await restartStack(action);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
case 'lock': {
|
|
335
|
+
const { lock } = await import('./commands/actions.js');
|
|
336
|
+
if (!action) throw new Error('Usage: elestio lock <vmID>');
|
|
337
|
+
await lock(action);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
case 'unlock': {
|
|
342
|
+
const { unlock } = await import('./commands/actions.js');
|
|
343
|
+
if (!action) throw new Error('Usage: elestio unlock <vmID>');
|
|
344
|
+
await unlock(action);
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
case 'resize': {
|
|
349
|
+
const { resizeServer } = await import('./commands/actions.js');
|
|
350
|
+
if (!action) throw new Error('Usage: elestio resize <vmID> --size <type>');
|
|
351
|
+
await resizeServer(action, args.size || args._[2], { project: args.project, provider: args.provider, region: args.region, cpuRamOnly: args['cpu-ram-only'] !== 'false' });
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
case 'change-version': {
|
|
356
|
+
const { changeVersion } = await import('./commands/actions.js');
|
|
357
|
+
if (!action) throw new Error('Usage: elestio change-version <vmID> <version>');
|
|
358
|
+
await changeVersion(action, args._[2] || args.version);
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ── Firewall ──
|
|
363
|
+
|
|
364
|
+
case 'firewall': {
|
|
365
|
+
const actions = await import('./commands/actions.js');
|
|
366
|
+
const vmID = args._[2];
|
|
367
|
+
switch (action) {
|
|
368
|
+
case 'get':
|
|
369
|
+
if (!vmID) throw new Error('Usage: elestio firewall get <vmID>');
|
|
370
|
+
await actions.getFirewallRules(vmID, json);
|
|
371
|
+
break;
|
|
372
|
+
case 'enable':
|
|
373
|
+
if (!vmID) throw new Error('Usage: elestio firewall enable <vmID> --rules \'[...]\'');
|
|
374
|
+
await actions.enableFirewall(vmID, JSON.parse(args.rules || '[]'));
|
|
375
|
+
break;
|
|
376
|
+
case 'update':
|
|
377
|
+
if (!vmID) throw new Error('Usage: elestio firewall update <vmID> --rules \'[...]\'');
|
|
378
|
+
await actions.updateFirewall(vmID, JSON.parse(args.rules || '[]'));
|
|
379
|
+
break;
|
|
380
|
+
case 'disable':
|
|
381
|
+
if (!vmID) throw new Error('Usage: elestio firewall disable <vmID>');
|
|
382
|
+
await actions.disableFirewall(vmID);
|
|
383
|
+
break;
|
|
384
|
+
default:
|
|
385
|
+
showHelp('firewall', {
|
|
386
|
+
'get <vmID>': 'Show firewall rules',
|
|
387
|
+
'enable <vmID> --rules \'[...]\'': 'Enable firewall with rules',
|
|
388
|
+
'update <vmID> --rules \'[...]\'': 'Update firewall rules',
|
|
389
|
+
'disable <vmID>': 'Disable firewall'
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ── SSL / Custom Domains ──
|
|
396
|
+
|
|
397
|
+
case 'ssl': {
|
|
398
|
+
const actions = await import('./commands/actions.js');
|
|
399
|
+
switch (action) {
|
|
400
|
+
case 'list':
|
|
401
|
+
await actions.listSslDomains(args._[2], json);
|
|
402
|
+
break;
|
|
403
|
+
case 'add':
|
|
404
|
+
await actions.addSslDomain(args._[2], args._[3] || args.domain);
|
|
405
|
+
break;
|
|
406
|
+
case 'remove':
|
|
407
|
+
await actions.removeSslDomain(args._[2], args._[3] || args.domain);
|
|
408
|
+
break;
|
|
409
|
+
default:
|
|
410
|
+
showHelp('ssl', {
|
|
411
|
+
'list <vmID>': 'List custom domains',
|
|
412
|
+
'add <vmID> <domain>': 'Add a custom domain with auto-SSL',
|
|
413
|
+
'remove <vmID> <domain>': 'Remove a custom domain'
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ── SSH Keys ──
|
|
420
|
+
|
|
421
|
+
case 'ssh-keys': {
|
|
422
|
+
const actions = await import('./commands/actions.js');
|
|
423
|
+
switch (action) {
|
|
424
|
+
case 'list':
|
|
425
|
+
await actions.listSshKeys(args._[2], json);
|
|
426
|
+
break;
|
|
427
|
+
case 'add':
|
|
428
|
+
await actions.addSshKey(args._[2], args.name || args._[3], args.key || args._[4]);
|
|
429
|
+
break;
|
|
430
|
+
case 'remove':
|
|
431
|
+
await actions.removeSshKey(args._[2], args.name || args._[3]);
|
|
432
|
+
break;
|
|
433
|
+
default:
|
|
434
|
+
showHelp('ssh-keys', {
|
|
435
|
+
'list <vmID>': 'List SSH keys',
|
|
436
|
+
'add <vmID> --name <name> --key <key>': 'Add an SSH key',
|
|
437
|
+
'remove <vmID> --name <name>': 'Remove an SSH key'
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ── Updates ──
|
|
444
|
+
|
|
445
|
+
case 'updates': {
|
|
446
|
+
const actions = await import('./commands/actions.js');
|
|
447
|
+
switch (action) {
|
|
448
|
+
case 'system-enable':
|
|
449
|
+
await actions.enableSystemAutoUpdate(args._[2], { day: args.day, hour: args.hour, minute: args.minute, securityOnly: args['security-only'] !== 'false' });
|
|
450
|
+
break;
|
|
451
|
+
case 'system-disable':
|
|
452
|
+
await actions.disableSystemAutoUpdate(args._[2]);
|
|
453
|
+
break;
|
|
454
|
+
case 'system-now':
|
|
455
|
+
await actions.runSystemUpdate(args._[2]);
|
|
456
|
+
break;
|
|
457
|
+
case 'app-enable':
|
|
458
|
+
await actions.enableAppAutoUpdate(args._[2], { day: args.day, hour: args.hour, minute: args.minute });
|
|
459
|
+
break;
|
|
460
|
+
case 'app-disable':
|
|
461
|
+
await actions.disableAppAutoUpdate(args._[2]);
|
|
462
|
+
break;
|
|
463
|
+
case 'app-now':
|
|
464
|
+
await actions.runAppUpdate(args._[2]);
|
|
465
|
+
break;
|
|
466
|
+
default:
|
|
467
|
+
showHelp('updates', {
|
|
468
|
+
'system-enable <vmID>': 'Enable OS auto-updates (--day, --hour, --minute)',
|
|
469
|
+
'system-disable <vmID>': 'Disable OS auto-updates',
|
|
470
|
+
'system-now <vmID>': 'Run OS update now',
|
|
471
|
+
'app-enable <vmID>': 'Enable app auto-updates (--day, --hour, --minute)',
|
|
472
|
+
'app-disable <vmID>': 'Disable app auto-updates',
|
|
473
|
+
'app-now <vmID>': 'Run app update now'
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ── Alerts ──
|
|
480
|
+
|
|
481
|
+
case 'alerts': {
|
|
482
|
+
const actions = await import('./commands/actions.js');
|
|
483
|
+
switch (action) {
|
|
484
|
+
case 'get':
|
|
485
|
+
await actions.getAlerts(args._[2], json);
|
|
486
|
+
break;
|
|
487
|
+
case 'enable':
|
|
488
|
+
case 'update':
|
|
489
|
+
await actions.enableAlerts(args._[2], args.rules ? JSON.parse(args.rules) : undefined, args.cycle);
|
|
490
|
+
break;
|
|
491
|
+
case 'disable':
|
|
492
|
+
await actions.disableAlerts(args._[2]);
|
|
493
|
+
break;
|
|
494
|
+
default:
|
|
495
|
+
showHelp('alerts', {
|
|
496
|
+
'get <vmID>': 'Show alert rules',
|
|
497
|
+
'enable <vmID> --rules \'...\'': 'Enable/update alerts',
|
|
498
|
+
'disable <vmID>': 'Disable alerts'
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ── Backups ──
|
|
505
|
+
|
|
506
|
+
case 'backups': {
|
|
507
|
+
const backups = await import('./commands/backups.js');
|
|
508
|
+
switch (action) {
|
|
509
|
+
// Local
|
|
510
|
+
case 'local-list':
|
|
511
|
+
await backups.listLocalBackups(args._[2], json);
|
|
512
|
+
break;
|
|
513
|
+
case 'local-take':
|
|
514
|
+
await backups.takeLocalBackup(args._[2]);
|
|
515
|
+
break;
|
|
516
|
+
case 'local-restore':
|
|
517
|
+
await backups.restoreLocalBackup(args._[2], args._[3] || args.path);
|
|
518
|
+
break;
|
|
519
|
+
case 'local-delete':
|
|
520
|
+
await backups.deleteLocalBackup(args._[2], args._[3] || args.path);
|
|
521
|
+
break;
|
|
522
|
+
// Remote
|
|
523
|
+
case 'remote-list':
|
|
524
|
+
await backups.listRemoteBackups(args._[2], json);
|
|
525
|
+
break;
|
|
526
|
+
case 'remote-take':
|
|
527
|
+
await backups.takeRemoteBackup(args._[2]);
|
|
528
|
+
break;
|
|
529
|
+
case 'remote-restore':
|
|
530
|
+
await backups.restoreRemoteBackup(args._[2], args._[3] || args.snapshot);
|
|
531
|
+
break;
|
|
532
|
+
case 'auto-enable':
|
|
533
|
+
await backups.setupAutoBackups(args._[2], args.path, args.hour);
|
|
534
|
+
break;
|
|
535
|
+
case 'auto-disable':
|
|
536
|
+
await backups.disableAutoBackups(args._[2]);
|
|
537
|
+
break;
|
|
538
|
+
default:
|
|
539
|
+
showHelp('backups', {
|
|
540
|
+
'local-list <vmID>': 'List local backups',
|
|
541
|
+
'local-take <vmID>': 'Take a local backup',
|
|
542
|
+
'local-restore <vmID> <path>': 'Restore a local backup',
|
|
543
|
+
'local-delete <vmID> <path>': 'Delete a local backup',
|
|
544
|
+
'remote-list <vmID>': 'List remote backups',
|
|
545
|
+
'remote-take <vmID>': 'Take a remote backup',
|
|
546
|
+
'remote-restore <vmID> <snapshot>': 'Restore a remote backup',
|
|
547
|
+
'auto-enable <vmID>': 'Setup auto backups (--path, --hour)',
|
|
548
|
+
'auto-disable <vmID>': 'Disable auto backups'
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// ── Snapshots ──
|
|
555
|
+
|
|
556
|
+
case 'snapshots': {
|
|
557
|
+
const backups = await import('./commands/backups.js');
|
|
558
|
+
switch (action) {
|
|
559
|
+
case 'list':
|
|
560
|
+
await backups.listSnapshots(args._[2], json);
|
|
561
|
+
break;
|
|
562
|
+
case 'take':
|
|
563
|
+
await backups.takeSnapshot(args._[2]);
|
|
564
|
+
break;
|
|
565
|
+
case 'restore':
|
|
566
|
+
await backups.restoreSnapshot(args._[2], args._[3] || args.order);
|
|
567
|
+
break;
|
|
568
|
+
case 'delete':
|
|
569
|
+
await backups.deleteSnapshot(args._[2], args._[3] || args.id);
|
|
570
|
+
break;
|
|
571
|
+
case 'auto-enable':
|
|
572
|
+
await backups.enableAutoSnapshots(args._[2]);
|
|
573
|
+
break;
|
|
574
|
+
case 'auto-disable':
|
|
575
|
+
await backups.disableAutoSnapshots(args._[2]);
|
|
576
|
+
break;
|
|
577
|
+
default:
|
|
578
|
+
showHelp('snapshots', {
|
|
579
|
+
'list <vmID>': 'List snapshots',
|
|
580
|
+
'take <vmID>': 'Take a snapshot',
|
|
581
|
+
'restore <vmID> <orderID>': 'Restore a snapshot (0 = most recent)',
|
|
582
|
+
'delete <vmID> <snapshotID>': 'Delete a snapshot',
|
|
583
|
+
'auto-enable <vmID>': 'Enable auto snapshots',
|
|
584
|
+
'auto-disable <vmID>': 'Disable auto snapshots'
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ── S3 External Backups ──
|
|
591
|
+
|
|
592
|
+
case 's3-backup': {
|
|
593
|
+
const backups = await import('./commands/backups.js');
|
|
594
|
+
switch (action) {
|
|
595
|
+
case 'verify':
|
|
596
|
+
await backups.verifyS3Config(args._[2], { apiKey: args.key, secretKey: args.secret, bucketName: args.bucket, endPoint: args.endpoint, prefix: args.prefix, providerType: args.type });
|
|
597
|
+
break;
|
|
598
|
+
case 'enable':
|
|
599
|
+
await backups.enableS3Backup(args._[2], { apiKey: args.key, secretKey: args.secret, bucketName: args.bucket, endPoint: args.endpoint, prefix: args.prefix, providerType: args.type });
|
|
600
|
+
break;
|
|
601
|
+
case 'disable':
|
|
602
|
+
await backups.disableS3Backup(args._[2]);
|
|
603
|
+
break;
|
|
604
|
+
case 'take':
|
|
605
|
+
await backups.takeS3Backup(args._[2]);
|
|
606
|
+
break;
|
|
607
|
+
case 'list':
|
|
608
|
+
await backups.listS3Backups(args._[2], json);
|
|
609
|
+
break;
|
|
610
|
+
case 'restore':
|
|
611
|
+
await backups.restoreS3Backup(args._[2], args._[3] || args.key);
|
|
612
|
+
break;
|
|
613
|
+
case 'delete':
|
|
614
|
+
await backups.deleteS3Backup(args._[2], args._[3] || args.key);
|
|
615
|
+
break;
|
|
616
|
+
default:
|
|
617
|
+
showHelp('s3-backup', {
|
|
618
|
+
'verify <vmID>': 'Verify S3 config (--key, --secret, --bucket, --endpoint)',
|
|
619
|
+
'enable <vmID>': 'Enable S3 backup (--key, --secret, --bucket, --endpoint)',
|
|
620
|
+
'disable <vmID>': 'Disable S3 backup',
|
|
621
|
+
'take <vmID>': 'Take S3 backup now',
|
|
622
|
+
'list <vmID>': 'List S3 backups',
|
|
623
|
+
'restore <vmID> <key>': 'Restore S3 backup',
|
|
624
|
+
'delete <vmID> <key>': 'Delete S3 backup'
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ── Access ──
|
|
631
|
+
|
|
632
|
+
case 'credentials': {
|
|
633
|
+
const { getCredentials } = await import('./commands/access.js');
|
|
634
|
+
if (!action) throw new Error('Usage: elestio credentials <vmID>');
|
|
635
|
+
await getCredentials(action, args.project, json);
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
case 'ssh': {
|
|
640
|
+
const access = await import('./commands/access.js');
|
|
641
|
+
if (!action) throw new Error('Usage: elestio ssh <vmID>');
|
|
642
|
+
if (args.direct) {
|
|
643
|
+
await access.getSSHDirect(action, json);
|
|
644
|
+
} else {
|
|
645
|
+
await access.getSSH(action, args.project, json);
|
|
646
|
+
}
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
case 'vscode': {
|
|
651
|
+
const { getVSCode } = await import('./commands/access.js');
|
|
652
|
+
if (!action) throw new Error('Usage: elestio vscode <vmID>');
|
|
653
|
+
await getVSCode(action, args.project, json);
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
case 'files': {
|
|
658
|
+
const { getFileExplorer } = await import('./commands/access.js');
|
|
659
|
+
if (!action) throw new Error('Usage: elestio files <vmID>');
|
|
660
|
+
await getFileExplorer(action, args.project, json);
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// ── Volumes ──
|
|
665
|
+
|
|
666
|
+
case 'volumes': {
|
|
667
|
+
const volumes = await import('./commands/volumes.js');
|
|
668
|
+
switch (action) {
|
|
669
|
+
case 'create':
|
|
670
|
+
await volumes.createVolume({ name: args.name || args._[2], size: args.size ? parseInt(args.size) : undefined, provider: args.provider, datacenter: args.datacenter, projectId: args.project, serverId: args.server, storageType: args.type });
|
|
671
|
+
break;
|
|
672
|
+
case 'service-list':
|
|
673
|
+
await volumes.getServiceVolumes(args._[2], json);
|
|
674
|
+
break;
|
|
675
|
+
case 'service-create':
|
|
676
|
+
await volumes.createServiceVolume(args._[2], { name: args.name || args._[3], size: args.size ? parseInt(args.size) : undefined, storageType: args.type });
|
|
677
|
+
break;
|
|
678
|
+
case 'resize':
|
|
679
|
+
await volumes.resizeVolume(args._[2], args._[3] || args.volume, parseInt(args.size || args._[4]));
|
|
680
|
+
break;
|
|
681
|
+
case 'detach':
|
|
682
|
+
await volumes.detachVolume(args._[2], args._[3] || args.volume, { keepVolume: args.keep !== 'false' });
|
|
683
|
+
break;
|
|
684
|
+
case 'delete':
|
|
685
|
+
await volumes.deleteServiceVolume(args._[2], args._[3] || args.volume);
|
|
686
|
+
break;
|
|
687
|
+
case 'protect':
|
|
688
|
+
await volumes.setVolumeProtection(args._[2], args._[3] || args.volume, args.disable !== true);
|
|
689
|
+
break;
|
|
690
|
+
default:
|
|
691
|
+
if (action && action !== 'list') {
|
|
692
|
+
showHelp('volumes', {
|
|
693
|
+
'list': 'List volumes in project (--project)',
|
|
694
|
+
'create --name <n> --size <gb>': 'Create a standalone volume',
|
|
695
|
+
'service-list <vmID>': 'List volumes attached to a service',
|
|
696
|
+
'service-create <vmID> --name <n>': 'Create and attach a volume',
|
|
697
|
+
'resize <vmID> <volumeID> --size <gb>': 'Resize a volume',
|
|
698
|
+
'detach <vmID> <volumeID>': 'Detach a volume (--keep false to delete)',
|
|
699
|
+
'delete <vmID> <volumeID>': 'Delete a volume',
|
|
700
|
+
'protect <vmID> <volumeID>': 'Toggle volume protection (--disable)'
|
|
701
|
+
});
|
|
702
|
+
} else {
|
|
703
|
+
await volumes.listVolumes(args.project, json);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ── CI/CD ──
|
|
710
|
+
|
|
711
|
+
case 'cicd': {
|
|
712
|
+
const cicd = await import('./commands/cicd.js');
|
|
713
|
+
switch (action) {
|
|
714
|
+
case 'targets':
|
|
715
|
+
await cicd.getCicdServices(args.project, json);
|
|
716
|
+
break;
|
|
717
|
+
case 'pipelines':
|
|
718
|
+
await cicd.getServicePipelines(args._[2], args.project, json);
|
|
719
|
+
break;
|
|
720
|
+
case 'pipeline-info':
|
|
721
|
+
await cicd.getPipelineDetails(args._[2], args._[3] || args.pipeline, args.project, json);
|
|
722
|
+
break;
|
|
723
|
+
case 'pipeline-restart':
|
|
724
|
+
await cicd.restartPipeline(args._[2], args._[3] || args.pipeline, args.project);
|
|
725
|
+
break;
|
|
726
|
+
case 'pipeline-stop':
|
|
727
|
+
await cicd.stopPipeline(args._[2], args._[3] || args.pipeline, args.project);
|
|
728
|
+
break;
|
|
729
|
+
case 'pipeline-delete':
|
|
730
|
+
await cicd.deletePipeline(args._[2], args._[3] || args.pipeline, args.project, !!args.force);
|
|
731
|
+
break;
|
|
732
|
+
case 'pipeline-resync':
|
|
733
|
+
await cicd.resyncPipeline(args._[2], args._[3] || args.pipeline, args.project);
|
|
734
|
+
break;
|
|
735
|
+
case 'pipeline-logs':
|
|
736
|
+
await cicd.getPipelineLogs(args._[2], args._[3] || args.pipeline, args.project);
|
|
737
|
+
break;
|
|
738
|
+
case 'pipeline-history':
|
|
739
|
+
await cicd.getPipelineHistory(args._[2], args._[3] || args.pipeline, args.project, json);
|
|
740
|
+
break;
|
|
741
|
+
case 'pipeline-log':
|
|
742
|
+
await cicd.viewPipelineLog(args._[2], args.pipeline, args.file, args.project);
|
|
743
|
+
break;
|
|
744
|
+
case 'domains':
|
|
745
|
+
await cicd.listPipelineDomains(args._[2], args._[3] || args.pipeline, args.project, json);
|
|
746
|
+
break;
|
|
747
|
+
case 'domain-add':
|
|
748
|
+
await cicd.addPipelineDomain(args._[2], args.pipeline, args.domain || args._[3], args.project);
|
|
749
|
+
break;
|
|
750
|
+
case 'domain-remove':
|
|
751
|
+
await cicd.removePipelineDomain(args._[2], args.pipeline, args.domain || args._[3], args.project);
|
|
752
|
+
break;
|
|
753
|
+
case 'create': {
|
|
754
|
+
if (args.auto) {
|
|
755
|
+
await cicd.autoCreatePipeline({
|
|
756
|
+
project: args.project, target: args.target, name: args.name,
|
|
757
|
+
mode: args.mode, repo: args.repo, branch: args.branch,
|
|
758
|
+
authId: args['auth-id'], image: args.image, imageTag: args['image-tag'],
|
|
759
|
+
buildDir: args['build-dir'], rootDir: args['root-dir'],
|
|
760
|
+
framework: args.framework, buildCmd: args['build-cmd'],
|
|
761
|
+
runCmd: args['run-cmd'], installCmd: args['install-cmd'],
|
|
762
|
+
nodeVersion: args['node-version'], variables: args.variables
|
|
763
|
+
});
|
|
764
|
+
} else {
|
|
765
|
+
await cicd.createPipeline(args._[2] || args.file);
|
|
766
|
+
}
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
case 'template': {
|
|
770
|
+
const template = cicd.generatePipelineTemplate(args._[2] || args.mode || 'docker');
|
|
771
|
+
console.log(template);
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
case 'registries':
|
|
775
|
+
await cicd.getDockerRegistries(args.project, json);
|
|
776
|
+
break;
|
|
777
|
+
case 'registry-add':
|
|
778
|
+
await cicd.addDockerRegistry(args.project, args.name || args._[2], args.username || args._[3], args.password, args.url);
|
|
779
|
+
break;
|
|
780
|
+
default:
|
|
781
|
+
showHelp('cicd', {
|
|
782
|
+
'targets': 'List CI/CD targets',
|
|
783
|
+
'pipelines <vmID>': 'List pipelines on a target',
|
|
784
|
+
'pipeline-info <vmID> <pipelineID>': 'Show pipeline details',
|
|
785
|
+
'pipeline-restart <vmID> <pipelineID>': 'Restart a pipeline',
|
|
786
|
+
'pipeline-stop <vmID> <pipelineID>': 'Stop a pipeline',
|
|
787
|
+
'pipeline-delete <vmID> <pipelineID>': 'Delete a pipeline (--force)',
|
|
788
|
+
'pipeline-resync <vmID> <pipelineID>': 'Re-sync a pipeline',
|
|
789
|
+
'pipeline-logs <vmID> <pipelineID>': 'View pipeline logs',
|
|
790
|
+
'pipeline-history <vmID> <pipelineID>': 'View build history',
|
|
791
|
+
'create --auto --target <vmID> --name <n>': 'Auto-create pipeline (--mode, --repo)',
|
|
792
|
+
'create <config.json>': 'Create pipeline from config file',
|
|
793
|
+
'template [mode]': 'Generate pipeline config template',
|
|
794
|
+
'domains <vmID> <pipelineID>': 'List pipeline domains',
|
|
795
|
+
'domain-add <vmID> --pipeline <id> --domain <d>': 'Add a domain',
|
|
796
|
+
'domain-remove <vmID> --pipeline <id> --domain <d>': 'Remove a domain',
|
|
797
|
+
'registries': 'List Docker registries',
|
|
798
|
+
'registry-add --name <n> --username <u> --password <p> --url <url>': 'Add Docker registry'
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// ── Billing ──
|
|
805
|
+
|
|
806
|
+
case 'billing': {
|
|
807
|
+
const billing = await import('./commands/billing.js');
|
|
808
|
+
if (action === 'project') {
|
|
809
|
+
await billing.getProjectBilling(args._[2] || args.project, json);
|
|
810
|
+
} else {
|
|
811
|
+
await billing.getBillingSummary(json);
|
|
812
|
+
}
|
|
813
|
+
break;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// ── Unknown ──
|
|
817
|
+
|
|
818
|
+
default:
|
|
819
|
+
log('error', `Unknown command: ${command}`);
|
|
820
|
+
log('info', 'Run "elestio --help" for usage');
|
|
821
|
+
process.exitCode = 1;
|
|
822
|
+
}
|
|
823
|
+
} catch (err) {
|
|
824
|
+
log('error', err.message);
|
|
825
|
+
if (args.debug) console.error(err);
|
|
826
|
+
process.exitCode = 1;
|
|
827
|
+
}
|
|
828
|
+
}
|