atendeticket 2.1.7 → 2.1.9
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/TODO.md +15 -0
- package/actions/delete.js +21 -1
- package/appInstaller.js +14 -33
- package/installers/backend/setEnv.js +5 -35
- package/installers/system/installNode.js +6 -4
- package/package.json +1 -1
- package/prompts/installPrompts.js +92 -2
package/TODO.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# TODO List for Installer Script
|
|
2
|
+
|
|
3
|
+
## Completed Tasks
|
|
4
|
+
- [x] Update installPrompts.js to ask 10 specific questions in order
|
|
5
|
+
- [x] Update installNode.js to install Node.js 20.x from nodesource
|
|
6
|
+
- [x] Update installCertbot.js to install via snap
|
|
7
|
+
- [x] Fix deploy_email variable in appInstaller.js
|
|
8
|
+
- [x] Update setupSSL.js to accept email parameter and run for both domains
|
|
9
|
+
- [x] Implement delete.js action according to task commands
|
|
10
|
+
- [x] Implement changeDomain.js action according to task commands
|
|
11
|
+
|
|
12
|
+
## Pending Tasks
|
|
13
|
+
- [ ] Verify all other installers match the task requirements
|
|
14
|
+
- [ ] Test the complete installation flow
|
|
15
|
+
- [ ] Add proper prompting for instancia name in delete and changeDomain actions
|
package/actions/delete.js
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
|
+
const { runCommand } = require('../core/exec');
|
|
1
2
|
const { info, success } = require('../core/logger');
|
|
2
3
|
|
|
3
4
|
async function deleteAction() {
|
|
4
5
|
info('Executando ação de delete...');
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
// Assuming we need to prompt for instancia name, but for now using a placeholder
|
|
8
|
+
// In a real implementation, you'd prompt for the instancia name
|
|
9
|
+
const instancia = 'example'; // This should be prompted or passed as parameter
|
|
10
|
+
|
|
11
|
+
// Remove Redis container
|
|
12
|
+
await runCommand('docker', ['rm', '-f', `redis-${instancia}`]);
|
|
13
|
+
|
|
14
|
+
// Remove nginx sites
|
|
15
|
+
await runCommand('sudo', ['rm', '-f', `/etc/nginx/sites-enabled/${instancia}`]);
|
|
16
|
+
await runCommand('sudo', ['rm', '-f', `/etc/nginx/sites-available/${instancia}`]);
|
|
17
|
+
|
|
18
|
+
// Remove instance directory
|
|
19
|
+
await runCommand('sudo', ['rm', '-rf', `/home/deploy/${instancia}`]);
|
|
20
|
+
|
|
21
|
+
// Delete PM2 processes
|
|
22
|
+
await runCommand('pm2', ['delete', `${instancia}-backend`, `${instancia}-frontend`]);
|
|
23
|
+
await runCommand('pm2', ['save']);
|
|
24
|
+
|
|
25
|
+
success('Instância deletada com sucesso!');
|
|
6
26
|
}
|
|
7
27
|
|
|
8
28
|
module.exports = { delete: deleteAction };
|
package/appInstaller.js
CHANGED
|
@@ -43,42 +43,23 @@ async function runInstaller() {
|
|
|
43
43
|
|
|
44
44
|
// Backend
|
|
45
45
|
await createDatabase(answers.instancia_add, answers.mysql_root_password);
|
|
46
|
-
await createRedis(answers.instancia_add, answers.
|
|
46
|
+
await createRedis(answers.instancia_add, answers.redis_port, answers.mysql_root_password);
|
|
47
47
|
await setBackendEnv(
|
|
48
|
-
'', // node_env
|
|
49
|
-
answers.
|
|
50
|
-
answers.
|
|
51
|
-
|
|
52
|
-
answers.backendPort,
|
|
48
|
+
'production', // node_env
|
|
49
|
+
answers.backend_url,
|
|
50
|
+
answers.frontend_url,
|
|
51
|
+
answers.backend_port,
|
|
53
52
|
'localhost', // db_host
|
|
54
|
-
'postgres', // db_dialect
|
|
55
53
|
answers.instancia_add, // db_user
|
|
56
54
|
answers.mysql_root_password, // db_pass
|
|
57
55
|
answers.instancia_add, // db_name
|
|
58
56
|
5432, // db_port
|
|
59
|
-
'
|
|
60
|
-
1, // import_fallback_file
|
|
61
|
-
1000, // timeout_to_import_message
|
|
62
|
-
3, // app_trialexpiration
|
|
57
|
+
'postgres', // db_dialect
|
|
63
58
|
jwt_secret,
|
|
64
59
|
jwt_refresh_secret,
|
|
65
60
|
redis_uri,
|
|
66
|
-
1, // redis_opt_limiter_max
|
|
67
|
-
3000, // redis_opt_limiter_duration
|
|
68
|
-
8, // flow_menu_cooldown_sec
|
|
69
61
|
answers.max_user, // user_limit
|
|
70
|
-
answers.max_whats
|
|
71
|
-
true, // closed_send_by_me
|
|
72
|
-
'whaticket', // verify_token
|
|
73
|
-
'', // mp_access_token
|
|
74
|
-
'2813216208828642', // facebook_app_id
|
|
75
|
-
'8233912aeade366dd8e2ebef6be256b6', // facebook_app_secret
|
|
76
|
-
'smtp.gmail.com', // smtp_host
|
|
77
|
-
'587', // smtp_port
|
|
78
|
-
'false', // smtp_secure
|
|
79
|
-
'seuemail@gmail.com', // smtp_user
|
|
80
|
-
'suasenha', // smtp_pass
|
|
81
|
-
'Redefinição de senha <seuemail@gmail.com>' // smtp_from
|
|
62
|
+
answers.max_whats // connections_limit
|
|
82
63
|
);
|
|
83
64
|
await installBackendDeps();
|
|
84
65
|
await buildBackend();
|
|
@@ -89,11 +70,11 @@ async function runInstaller() {
|
|
|
89
70
|
|
|
90
71
|
// Frontend
|
|
91
72
|
await setFrontendEnv(
|
|
92
|
-
answers.
|
|
73
|
+
answers.backend_url,
|
|
93
74
|
24, // hours_close_tickets_auto
|
|
94
75
|
'https', // backend_protocol
|
|
95
|
-
answers.
|
|
96
|
-
answers.
|
|
76
|
+
answers.backend_url.replace(/^https?:\/\//, ''), // backend_host
|
|
77
|
+
answers.backend_port, // backend_port_param
|
|
97
78
|
'pt-br', // locale
|
|
98
79
|
'America/Sao_Paulo', // timezone
|
|
99
80
|
'55XXXXXXXXXXX', // number_support
|
|
@@ -111,12 +92,12 @@ async function runInstaller() {
|
|
|
111
92
|
await installFrontendDeps();
|
|
112
93
|
await buildFrontend();
|
|
113
94
|
await startFrontendPM2();
|
|
114
|
-
await setupFrontendNginx(answers.
|
|
95
|
+
await setupFrontendNginx(answers.frontend_url.replace(/^https?:\/\//, ''));
|
|
115
96
|
|
|
116
97
|
// SSL
|
|
117
|
-
const backend_domain = answers.
|
|
118
|
-
const frontend_domain = answers.
|
|
119
|
-
await setupSSL(backend_domain, frontend_domain, answers.
|
|
98
|
+
const backend_domain = answers.backend_url.replace(/^https?:\/\//, '');
|
|
99
|
+
const frontend_domain = answers.frontend_url.replace(/^https?:\/\//, '');
|
|
100
|
+
await setupSSL(backend_domain, frontend_domain, answers.deploy_email);
|
|
120
101
|
|
|
121
102
|
success('Instalação completa do Multizap!');
|
|
122
103
|
}
|
|
@@ -2,7 +2,7 @@ const { info, success } = require('../../core/logger');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
|
-
async function setEnv(node_env, backend_url, frontend_url,
|
|
5
|
+
async function setEnv(node_env, backend_url, frontend_url, port, db_host, db_user, db_pass, db_name, db_port, db_dialect, jwt_secret, jwt_refresh_secret, redis_uri, user_limit, connections_limit) {
|
|
6
6
|
info('Configurando variáveis de ambiente (backend)...');
|
|
7
7
|
|
|
8
8
|
// ensure idempotency
|
|
@@ -16,60 +16,30 @@ async function setEnv(node_env, backend_url, frontend_url, proxy_port, port, db_
|
|
|
16
16
|
frontendUrl = `https://${frontendUrl}`;
|
|
17
17
|
|
|
18
18
|
const envContent = `NODE_ENV=${node_env}
|
|
19
|
+
|
|
19
20
|
BACKEND_URL=${backendUrl}
|
|
20
21
|
FRONTEND_URL=${frontendUrl}
|
|
21
|
-
PROXY_PORT=${proxy_port}
|
|
22
22
|
PORT=${port}
|
|
23
23
|
|
|
24
24
|
DB_HOST=${db_host}
|
|
25
|
-
DB_DIALECT=${db_dialect}
|
|
26
25
|
DB_USER=${db_user}
|
|
27
26
|
DB_PASS=${db_pass}
|
|
28
27
|
DB_NAME=${db_name}
|
|
29
28
|
DB_PORT=${db_port}
|
|
30
|
-
|
|
31
|
-
MASTER_KEY=${master_key}
|
|
32
|
-
|
|
33
|
-
IMPORT_FALLBACK_FILE=${import_fallback_file}
|
|
34
|
-
|
|
35
|
-
TIMEOUT_TO_IMPORT_MESSAGE=${timeout_to_import_message}
|
|
36
|
-
|
|
37
|
-
APP_TRIALEXPIRATION=${app_trialexpiration}
|
|
29
|
+
DB_DIALECT=${db_dialect}
|
|
38
30
|
|
|
39
31
|
JWT_SECRET=${jwt_secret}
|
|
40
32
|
JWT_REFRESH_SECRET=${jwt_refresh_secret}
|
|
41
33
|
|
|
42
|
-
# REDIS CONFIGURADO AUTOMATICAMENTE
|
|
43
34
|
REDIS_URI=${redis_uri}
|
|
44
|
-
REDIS_OPT_LIMITER_MAX=${redis_opt_limiter_max}
|
|
45
|
-
REDIS_OPT_LIMITER_DURATION=${redis_opt_limiter_duration}
|
|
46
|
-
|
|
47
|
-
FLOW_MENU_COOLDOWN_SEC=${flow_menu_cooldown_sec}
|
|
48
35
|
|
|
49
36
|
USER_LIMIT=${user_limit}
|
|
50
|
-
CONNECTIONS_LIMIT=${connections_limit}
|
|
51
|
-
CLOSED_SEND_BY_ME=${closed_send_by_me}
|
|
52
|
-
|
|
53
|
-
VERIFY_TOKEN=${verify_token}
|
|
54
|
-
|
|
55
|
-
#METODOS DE PAGAMENTO
|
|
56
|
-
MP_ACCESS_TOKEN=${mp_access_token}
|
|
57
|
-
|
|
58
|
-
FACEBOOK_APP_ID=${facebook_app_id}
|
|
59
|
-
FACEBOOK_APP_SECRET=${facebook_app_secret}
|
|
60
|
-
|
|
61
|
-
# EMAIL
|
|
62
|
-
SMTP_HOST="${smtp_host}"
|
|
63
|
-
SMTP_PORT="${smtp_port}"
|
|
64
|
-
SMTP_SECURE="${smtp_secure}"
|
|
65
|
-
SMTP_USER="${smtp_user}"
|
|
66
|
-
SMTP_PASS="${smtp_pass}"
|
|
67
|
-
SMTP_FROM="${smtp_from}"`;
|
|
37
|
+
CONNECTIONS_LIMIT=${connections_limit}`;
|
|
68
38
|
|
|
69
39
|
const envPath = path.join(process.cwd(), 'backend', '.env');
|
|
70
40
|
fs.writeFileSync(envPath, envContent);
|
|
71
41
|
|
|
72
|
-
success('Variáveis de ambiente
|
|
42
|
+
success('Variáveis de ambiente do backend configuradas!');
|
|
73
43
|
}
|
|
74
44
|
|
|
75
45
|
module.exports = { setEnv };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const { runCommand } = require('../../core/exec');
|
|
2
|
-
const { info } = require('../../core/logger');
|
|
2
|
+
const { info, success } = require('../../core/logger');
|
|
3
3
|
|
|
4
4
|
async function installNode() {
|
|
5
|
-
info('Instalando Node.js...');
|
|
6
|
-
await runCommand('
|
|
7
|
-
await runCommand('sudo', ['apt', 'install', '-y', 'nodejs'
|
|
5
|
+
info('Instalando Node.js + NPM + NPX...');
|
|
6
|
+
await runCommand('curl', ['-fsSL', 'https://deb.nodesource.com/setup_20.x', '|', 'bash', '-']);
|
|
7
|
+
await runCommand('sudo', ['apt', 'install', '-y', 'nodejs']);
|
|
8
|
+
await runCommand('sudo', ['npm', 'install', '-g', 'npm@latest']);
|
|
9
|
+
success('Node.js + NPM + NPX instalados!');
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
module.exports = { installNode };
|
package/package.json
CHANGED
|
@@ -2,8 +2,98 @@ const inquirer = require("inquirer");
|
|
|
2
2
|
|
|
3
3
|
async function installPrompts() {
|
|
4
4
|
const answers = await inquirer.prompt([
|
|
5
|
-
{
|
|
6
|
-
|
|
5
|
+
{
|
|
6
|
+
type: 'password',
|
|
7
|
+
name: 'mysql_root_password',
|
|
8
|
+
message: '1. Senha do usuário deploy + banco de dados:',
|
|
9
|
+
mask: '*'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
type: 'input',
|
|
13
|
+
name: 'instancia_add',
|
|
14
|
+
message: '2. Nome da instância/empresa (minúsculo, sem espaço, sem caracteres especiais):',
|
|
15
|
+
validate: (input) => {
|
|
16
|
+
if (!input) return 'Este campo é obrigatório';
|
|
17
|
+
if (!/^[a-z0-9]+$/.test(input)) return 'Apenas letras minúsculas e números, sem espaços ou caracteres especiais';
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: 'number',
|
|
23
|
+
name: 'max_whats',
|
|
24
|
+
message: '3. Limite de conexões Whats:',
|
|
25
|
+
validate: (input) => {
|
|
26
|
+
if (input <= 0) return 'Deve ser um número positivo';
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: 'number',
|
|
32
|
+
name: 'max_user',
|
|
33
|
+
message: '4. Limite de usuários:',
|
|
34
|
+
validate: (input) => {
|
|
35
|
+
if (input <= 0) return 'Deve ser um número positivo';
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: 'input',
|
|
41
|
+
name: 'frontend_url',
|
|
42
|
+
message: '5. Domínio do FRONTEND (ex: painel.suaempresa.com):',
|
|
43
|
+
validate: (input) => {
|
|
44
|
+
if (!input) return 'Este campo é obrigatório';
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'input',
|
|
50
|
+
name: 'backend_url',
|
|
51
|
+
message: '6. Domínio do BACKEND (ex: api.suaempresa.com):',
|
|
52
|
+
validate: (input) => {
|
|
53
|
+
if (!input) return 'Este campo é obrigatório';
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'number',
|
|
59
|
+
name: 'frontend_port',
|
|
60
|
+
message: '7. Porta do FRONTEND (ex: 3000):',
|
|
61
|
+
default: 3000,
|
|
62
|
+
validate: (input) => {
|
|
63
|
+
if (input <= 0 || input > 65535) return 'Porta inválida (1-65535)';
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'number',
|
|
69
|
+
name: 'backend_port',
|
|
70
|
+
message: '8. Porta do BACKEND (ex: 4000):',
|
|
71
|
+
default: 4000,
|
|
72
|
+
validate: (input) => {
|
|
73
|
+
if (input <= 0 || input > 65535) return 'Porta inválida (1-65535)';
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'number',
|
|
79
|
+
name: 'redis_port',
|
|
80
|
+
message: '9. Porta do REDIS (ex: 5000):',
|
|
81
|
+
default: 5000,
|
|
82
|
+
validate: (input) => {
|
|
83
|
+
if (input <= 0 || input > 65535) return 'Porta inválida (1-65535)';
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: 'input',
|
|
89
|
+
name: 'deploy_email',
|
|
90
|
+
message: '10. Email para o SSL (Certbot):',
|
|
91
|
+
validate: (input) => {
|
|
92
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
93
|
+
if (!emailRegex.test(input)) return 'Email inválido';
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
7
97
|
]);
|
|
8
98
|
return answers;
|
|
9
99
|
}
|