mozhost-cli 2.0.0 → 2.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mozhost-cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Command-line interface for MozHost container platform",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -3,12 +3,60 @@ const ora = require('ora');
3
3
  const inquirer = require('inquirer');
4
4
  const api = require('../utils/api');
5
5
 
6
+ // ============================================
7
+ // HELPER FUNCTION - Resolver Nome ou ID
8
+ // ============================================
9
+ async function resolveContainer(identifier) {
10
+ try {
11
+ // Tenta buscar diretamente por ID
12
+ const response = await api.getContainer(identifier);
13
+ return response.container;
14
+ } catch (error) {
15
+ // Se não encontrar (404), busca na lista por nome
16
+ if (error.response?.status === 404 || error.response?.data?.error?.includes('not found')) {
17
+ const listResponse = await api.listContainers();
18
+
19
+ // Procura por nome, ID completo ou ID parcial
20
+ const found = listResponse.containers.find(c =>
21
+ c.name === identifier ||
22
+ c.id === identifier ||
23
+ c.id.startsWith(identifier)
24
+ );
25
+
26
+ if (!found) {
27
+ // Procura containers similares para sugerir
28
+ const similar = listResponse.containers.filter(c =>
29
+ c.name.toLowerCase().includes(identifier.toLowerCase()) ||
30
+ c.id.includes(identifier)
31
+ );
32
+
33
+ if (similar.length > 0) {
34
+ console.log(chalk.yellow('\n💡 Você quis dizer:'));
35
+ similar.forEach(c => {
36
+ console.log(chalk.white(` • ${c.name} ${chalk.gray(`(${c.id.slice(0, 8)})`)}`));
37
+ });
38
+ } else {
39
+ console.log(chalk.gray('\n💡 Use: mozhost ls para ver todos os containers'));
40
+ }
41
+
42
+ throw new Error(`Container "${identifier}" não encontrado`);
43
+ }
44
+
45
+ return found;
46
+ }
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ // ============================================
52
+ // LIST - Listar todos os containers
53
+ // ============================================
6
54
  async function list() {
7
55
  try {
8
56
  const spinner = ora('Carregando containers...').start();
9
-
57
+
10
58
  const response = await api.listContainers();
11
-
59
+
12
60
  spinner.stop();
13
61
 
14
62
  if (response.containers.length === 0) {
@@ -22,7 +70,7 @@ async function list() {
22
70
  response.containers.forEach(container => {
23
71
  const statusColor = container.status === 'running' ? chalk.green : chalk.gray;
24
72
  const statusIcon = container.status === 'running' ? '●' : '○';
25
-
73
+
26
74
  console.log(`${statusColor(statusIcon)} ${chalk.white.bold(container.name)} ${chalk.gray(`(${container.id.slice(0, 8)})`)}`);
27
75
  console.log(` ${chalk.gray('Tipo:')} ${container.type}`);
28
76
  console.log(` ${chalk.gray('Status:')} ${statusColor(container.status)}`);
@@ -51,6 +99,9 @@ async function list() {
51
99
  }
52
100
  }
53
101
 
102
+ // ============================================
103
+ // CREATE - Criar novo container
104
+ // ============================================
54
105
  async function create(options) {
55
106
  try {
56
107
  const { name, type } = options;
@@ -79,85 +130,106 @@ async function create(options) {
79
130
  console.log(chalk.white(` Porta: ${response.container.port}`));
80
131
 
81
132
  console.log(chalk.gray('\nPróximos passos:'));
82
- console.log(chalk.white(` 1. mozhost start ${response.container.id}`));
83
- console.log(chalk.white(` 2. mozhost deploy ${response.container.id}`));
133
+ console.log(chalk.white(` 1. mozhost start ${response.container.name}`));
134
+ console.log(chalk.white(` 2. mozhost deploy ${response.container.name}`));
84
135
 
85
136
  } catch (error) {
86
137
  console.error(chalk.red('\n❌ Erro ao criar container:'));
87
-
138
+
88
139
  if (error.response?.data) {
89
140
  console.error(chalk.red(` ${error.response.data.error || error.response.data.message}`));
90
-
141
+
91
142
  if (error.response.status === 403) {
92
143
  console.log(chalk.yellow('\n💡 Dica: Você atingiu o limite de containers do seu plano'));
93
144
  }
94
-
145
+
95
146
  if (error.response.status === 402) {
96
147
  console.log(chalk.yellow('\n💡 Dica: Você não tem coins suficientes'));
97
148
  }
98
149
  } else {
99
150
  console.error(chalk.red(` ${error.message}`));
100
151
  }
101
-
152
+
102
153
  process.exit(1);
103
154
  }
104
155
  }
105
156
 
106
- async function start(containerId) {
157
+ // ============================================
158
+ // START - Iniciar container
159
+ // ============================================
160
+ async function start(identifier) {
107
161
  try {
108
- const spinner = ora('Iniciando container...').start();
109
-
110
- const response = await api.startContainer(containerId);
162
+ const spinner = ora('Resolvendo container...').start();
163
+ const container = await resolveContainer(identifier);
111
164
 
112
- spinner.succeed(chalk.green(`✅ Container ${response.container.name} iniciado!`));
113
-
114
- const info = await api.getContainer(containerId);
115
- console.log(chalk.cyan(`\n🌐 URL: https://${info.container.domain}`));
165
+ spinner.text = `Iniciando ${container.name}...`;
166
+ const response = await api.startContainer(container.id);
167
+
168
+ spinner.succeed(chalk.green(`✅ Container ${container.name} iniciado!`));
169
+
170
+ console.log(chalk.cyan(`\n🌐 URL: https://${container.domain}`));
116
171
 
117
172
  } catch (error) {
118
173
  console.error(chalk.red('\n❌ Erro ao iniciar container:'));
119
- console.error(chalk.red(` ${error.response?.data?.error || error.message}`));
174
+ console.error(chalk.red(` ${error.message || error.response?.data?.error}`));
120
175
  process.exit(1);
121
176
  }
122
177
  }
123
178
 
124
- async function stop(containerId) {
179
+ // ============================================
180
+ // STOP - Parar container
181
+ // ============================================
182
+ async function stop(identifier) {
125
183
  try {
126
- const spinner = ora('Parando container...').start();
184
+ const spinner = ora('Resolvendo container...').start();
185
+ const container = await resolveContainer(identifier);
127
186
 
128
- const response = await api.stopContainer(containerId);
129
-
130
- spinner.succeed(chalk.green(`✅ Container ${response.container.name} parado!`));
187
+ spinner.text = `Parando ${container.name}...`;
188
+ await api.stopContainer(container.id);
189
+
190
+ spinner.succeed(chalk.green(`✅ Container ${container.name} parado!`));
131
191
 
132
192
  } catch (error) {
133
193
  console.error(chalk.red('\n❌ Erro ao parar container:'));
134
- console.error(chalk.red(` ${error.response?.data?.error || error.message}`));
194
+ console.error(chalk.red(` ${error.message || error.response?.data?.error}`));
135
195
  process.exit(1);
136
196
  }
137
197
  }
138
198
 
139
- async function restart(containerId) {
199
+ // ============================================
200
+ // RESTART - Reiniciar container
201
+ // ============================================
202
+ async function restart(identifier) {
140
203
  try {
141
- const spinner = ora('Reiniciando container...').start();
204
+ const spinner = ora('Resolvendo container...').start();
205
+ const container = await resolveContainer(identifier);
142
206
 
143
- const response = await api.restartContainer(containerId);
144
-
145
- spinner.succeed(chalk.green(`✅ Container ${response.container.name} reiniciado!`));
207
+ spinner.text = `Reiniciando ${container.name}...`;
208
+ await api.restartContainer(container.id);
209
+
210
+ spinner.succeed(chalk.green(`✅ Container ${container.name} reiniciado!`));
146
211
 
147
212
  } catch (error) {
148
213
  console.error(chalk.red('\n❌ Erro ao reiniciar container:'));
149
- console.error(chalk.red(` ${error.response?.data?.error || error.message}`));
214
+ console.error(chalk.red(` ${error.message || error.response?.data?.error}`));
150
215
  process.exit(1);
151
216
  }
152
217
  }
153
218
 
154
- async function deleteContainer(containerId) {
219
+ // ============================================
220
+ // DELETE - Deletar container
221
+ // ============================================
222
+ async function deleteContainer(identifier) {
155
223
  try {
224
+ const spinner = ora('Resolvendo container...').start();
225
+ const container = await resolveContainer(identifier);
226
+ spinner.stop();
227
+
156
228
  const confirm = await inquirer.prompt([
157
229
  {
158
230
  type: 'confirm',
159
231
  name: 'confirmed',
160
- message: chalk.red('Tem certeza que deseja deletar este container?'),
232
+ message: chalk.red(`Tem certeza que deseja deletar "${container.name}"?`),
161
233
  default: false
162
234
  }
163
235
  ]);
@@ -167,70 +239,80 @@ async function deleteContainer(containerId) {
167
239
  return;
168
240
  }
169
241
 
170
- const spinner = ora('Deletando container...').start();
171
-
172
- await api.deleteContainer(containerId);
173
-
174
- spinner.succeed(chalk.green('✅ Container deletado com sucesso!'));
242
+ const deleteSpinner = ora(`Deletando ${container.name}...`).start();
243
+ await api.deleteContainer(container.id);
244
+
245
+ deleteSpinner.succeed(chalk.green(`✅ Container ${container.name} deletado com sucesso!`));
175
246
 
176
247
  } catch (error) {
177
248
  console.error(chalk.red('\n❌ Erro ao deletar container:'));
178
- console.error(chalk.red(` ${error.response?.data?.error || error.message}`));
249
+ console.error(chalk.red(` ${error.message || error.response?.data?.error}`));
179
250
  process.exit(1);
180
251
  }
181
252
  }
182
253
 
183
- async function logs(containerId, options) {
254
+ // ============================================
255
+ // LOGS - Ver logs do container
256
+ // ============================================
257
+ async function logs(identifier, options) {
184
258
  try {
185
- const spinner = ora('Carregando logs...').start();
186
-
187
- const response = await api.getContainerLogs(containerId, options.lines);
259
+ const spinner = ora('Resolvendo container...').start();
260
+ const container = await resolveContainer(identifier);
188
261
 
262
+ spinner.text = `Carregando logs de ${container.name}...`;
263
+ const response = await api.getContainerLogs(container.id, options.lines);
264
+
189
265
  spinner.stop();
190
266
 
191
267
  if (!response.logs || response.logs.length === 0) {
192
- console.log(chalk.yellow('\n⚠️ Nenhum log encontrado'));
268
+ console.log(chalk.yellow(`\n⚠️ Nenhum log encontrado para ${container.name}`));
193
269
  return;
194
270
  }
195
271
 
196
- console.log(chalk.cyan.bold('\n📝 Logs do Container\n'));
272
+ console.log(chalk.cyan.bold(`\n📝 Logs de ${container.name}\n`));
197
273
  response.logs.forEach(line => {
198
274
  console.log(chalk.gray(line));
199
275
  });
200
276
 
201
277
  } catch (error) {
202
278
  console.error(chalk.red('\n❌ Erro ao buscar logs:'));
203
- console.error(chalk.red(` ${error.response?.data?.error || error.message}`));
279
+ console.error(chalk.red(` ${error.message || error.response?.data?.error}`));
204
280
  process.exit(1);
205
281
  }
206
282
  }
207
283
 
208
- async function info(containerId) {
284
+ // ============================================
285
+ // INFO - Informações detalhadas do container
286
+ // ============================================
287
+ async function info(identifier) {
209
288
  try {
210
- const spinner = ora('Carregando informações...').start();
289
+ const spinner = ora('Resolvendo container...').start();
290
+ const container = await resolveContainer(identifier);
211
291
 
212
- const response = await api.getContainer(containerId);
292
+ spinner.text = `Carregando informações de ${container.name}...`;
213
293
 
294
+ // Busca detalhes completos (com stats se disponível)
295
+ const response = await api.getContainer(container.id);
214
296
  spinner.stop();
215
297
 
216
- const container = response.container;
217
-
298
+ const containerData = response.container;
299
+
218
300
  console.log(chalk.cyan.bold('\n📦 Informações do Container\n'));
219
- console.log(chalk.white(` ID: ${container.id}`));
220
- console.log(chalk.white(` Nome: ${container.name}`));
221
- console.log(chalk.white(` Tipo: ${container.type}`));
222
-
223
- const statusColor = container.status === 'running' ? chalk.green : chalk.gray;
224
- console.log(` Status: ${statusColor(container.status)}`);
225
-
226
- console.log(chalk.cyan(` URL: https://${container.domain}`));
227
- console.log(chalk.white(` Porta: ${container.port}`));
228
- console.log(chalk.white(` CPU Limit: ${container.cpu_limit}`));
229
- console.log(chalk.white(` RAM Limit: ${container.memory_limit_mb}MB`));
230
- console.log(chalk.white(` Storage Usado: ${container.storage_used_mb || 0}MB`));
231
- console.log(chalk.white(` Auto Restart: ${container.auto_restart ? 'Sim' : 'Não'}`));
232
- console.log(chalk.gray(` Criado em: ${new Date(container.created_at).toLocaleString('pt-BR')}`));
233
- console.log(chalk.gray(` Atualizado em: ${new Date(container.updated_at).toLocaleString('pt-BR')}`));
301
+ console.log(chalk.white(` ID: ${containerData.id}`));
302
+ console.log(chalk.white(` Nome: ${containerData.name}`));
303
+ console.log(chalk.white(` Tipo: ${containerData.type}`));
304
+
305
+ const statusColor = containerData.status === 'running' ? chalk.green : chalk.gray;
306
+ console.log(` Status: ${statusColor(containerData.status)}`);
307
+
308
+ console.log(chalk.cyan(` URL: https://${containerData.domain}`));
309
+ console.log(chalk.white(` Porta: ${containerData.port}`));
310
+ console.log(chalk.white(` CPU Limit: ${containerData.cpu_limit}`));
311
+ console.log(chalk.white(` RAM Limit: ${containerData.memory_limit_mb}MB`));
312
+ console.log(chalk.white(` Storage Usado: ${containerData.storage_used_mb || 0}MB`));
313
+ console.log(chalk.white(` Auto Restart: ${containerData.auto_restart ? 'Sim' : 'Não'}`));
314
+ console.log(chalk.gray(` Criado em: ${new Date(containerData.created_at).toLocaleString('pt-BR')}`));
315
+ console.log(chalk.gray(` Atualizado em: ${new Date(containerData.updated_at).toLocaleString('pt-BR')}`));
234
316
 
235
317
  if (response.stats) {
236
318
  console.log(chalk.cyan.bold('\n📊 Estatísticas em Tempo Real\n'));
@@ -240,22 +322,28 @@ async function info(containerId) {
240
322
 
241
323
  } catch (error) {
242
324
  console.error(chalk.red('\n❌ Erro ao buscar informações:'));
243
- console.error(chalk.red(` ${error.response?.data?.error || error.message}`));
325
+ console.error(chalk.red(` ${error.message || error.response?.data?.error}`));
244
326
  process.exit(1);
245
327
  }
246
328
  }
247
329
 
248
- async function url(containerId) {
330
+ // ============================================
331
+ // URL - Obter URL do container
332
+ // ============================================
333
+ async function url(identifier) {
249
334
  try {
250
- const response = await api.getContainer(containerId);
251
- console.log(chalk.cyan(`https://${response.container.domain}`));
335
+ const container = await resolveContainer(identifier);
336
+ console.log(chalk.cyan(`https://${container.domain}`));
252
337
  } catch (error) {
253
338
  console.error(chalk.red('❌ Erro ao buscar URL:'));
254
- console.error(chalk.red(` ${error.response?.data?.error || error.message}`));
339
+ console.error(chalk.red(` ${error.message || error.response?.data?.error}`));
255
340
  process.exit(1);
256
341
  }
257
342
  }
258
343
 
344
+ // ============================================
345
+ // EXPORTS
346
+ // ============================================
259
347
  module.exports = {
260
348
  list,
261
349
  create,
@@ -0,0 +1,78 @@
1
+ // mozhost-cli/commands/domain.js
2
+
3
+ const program = require('commander');
4
+ const axios = require('axios');
5
+ const chalk = require('chalk');
6
+
7
+ program
8
+ .command('domain add <container> <domain>')
9
+ .description('Adicionar domínio customizado')
10
+ .action(async (container, domain) => {
11
+ try {
12
+ const res = await axios.post(`${API_URL}/domains`, {
13
+ containerId: container,
14
+ domain
15
+ });
16
+
17
+ console.log(chalk.green('\n✅ Domínio adicionado com sucesso!\n'));
18
+ console.log(chalk.yellow('📋 Configure seu DNS:\n'));
19
+ console.log(` Tipo: ${chalk.bold('A')}`);
20
+ console.log(` Nome: ${chalk.bold('@')} (ou ${chalk.bold(domain)})`);
21
+ console.log(` Valor: ${chalk.bold(res.data.instructions.ip)}`);
22
+ console.log(` TTL: ${chalk.bold('300')}\n`);
23
+ console.log(chalk.cyan('⏳ Aguardando propagação DNS...\n'));
24
+ console.log(`Use ${chalk.bold('mozhost domain status ' + container)} para acompanhar\n`);
25
+
26
+ } catch (error) {
27
+ console.error(chalk.red('❌ Erro:'), error.response?.data?.error || error.message);
28
+ }
29
+ });
30
+
31
+ program
32
+ .command('domain status <container>')
33
+ .description('Ver status dos domínios')
34
+ .action(async (container) => {
35
+ try {
36
+ const res = await axios.get(`${API_URL}/domains/${container}`);
37
+ const domains = res.data;
38
+
39
+ if (domains.length === 0) {
40
+ console.log(chalk.yellow('Nenhum domínio customizado configurado'));
41
+ return;
42
+ }
43
+
44
+ console.log('\n┌─────────────────────────────────────────────────┐');
45
+
46
+ for (const d of domains) {
47
+ const statusIcon = {
48
+ pending: '⏳',
49
+ dns_pending: '🔍',
50
+ ssl_generating: '🔒',
51
+ active: '✅',
52
+ failed: '❌'
53
+ }[d.status];
54
+
55
+ const statusText = {
56
+ pending: 'Aguardando configuração',
57
+ dns_pending: 'DNS detectado, gerando SSL...',
58
+ ssl_generating: 'Gerando certificado SSL...',
59
+ active: 'Ativo',
60
+ failed: 'Falhou - verifique DNS'
61
+ }[d.status];
62
+
63
+ console.log(`│ ${statusIcon} ${chalk.bold(d.domain)}`);
64
+ console.log(`│ Status: ${statusText}`);
65
+ if (d.ssl_expires_at && d.status === 'active') {
66
+ console.log(`│ SSL válido até: ${new Date(d.ssl_expires_at).toLocaleDateString()}`);
67
+ }
68
+ console.log('│');
69
+ }
70
+
71
+ console.log('└─────────────────────────────────────────────────┘\n');
72
+
73
+ } catch (error) {
74
+ console.error(chalk.red('❌ Erro:'), error.message);
75
+ }
76
+ });
77
+
78
+ program.parse(process.argv);