exponenciacrm 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.
Files changed (2) hide show
  1. package/bin/index.js +150 -0
  2. package/package.json +33 -0
package/bin/index.js ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { execa } = require('execa');
6
+ const ora = require('ora');
7
+ const chalk = require('chalk');
8
+ const open = require('open');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const crypto = require('crypto');
12
+
13
+ const [major] = process.versions.node.split('.').map(Number);
14
+ if (major < 18) {
15
+ console.error(chalk.red(`\nNode.js 18 ou superior é necessário. Você tem a versão ${process.versions.node}.\nAtualize em: https://nodejs.org\n`));
16
+ process.exit(1);
17
+ }
18
+
19
+ async function checkGit() {
20
+ try {
21
+ await execa('git', ['--version']);
22
+ } catch {
23
+ console.error(chalk.red('\nGit não encontrado. Instale em: https://git-scm.com\n'));
24
+ process.exit(1);
25
+ }
26
+ }
27
+
28
+ async function cloneRepo(spinner, cwd) {
29
+ spinner.start('Baixando ExponenciaCRM...');
30
+ await execa('git', [
31
+ 'clone',
32
+ '--depth=1',
33
+ 'https://github.com/bittencourtthulio/ExponenciaCRM',
34
+ '.'
35
+ ], { cwd });
36
+ spinner.succeed('ExponenciaCRM baixado');
37
+ }
38
+
39
+ async function installDeps(spinner, cwd) {
40
+ spinner.start('Instalando dependências...');
41
+ await execa('npm', ['install'], { cwd });
42
+ spinner.succeed('Dependências instaladas');
43
+ }
44
+
45
+ function createEnv(cwd) {
46
+ const secret = crypto.randomUUID();
47
+ const content = `DATABASE_URL="file:./dev.db"\nJWT_SECRET="${secret}"\n`;
48
+ fs.writeFileSync(path.join(cwd, '.env'), content, 'utf-8');
49
+ }
50
+
51
+ async function setupDatabase(spinner, cwd) {
52
+ spinner.start('Configurando banco de dados...');
53
+ await execa('npx', ['prisma', 'migrate', 'dev', '--name', 'init'], { cwd });
54
+ spinner.succeed('Banco de dados configurado');
55
+ }
56
+
57
+ async function seedDatabase(spinner, cwd) {
58
+ spinner.start('Criando usuário admin...');
59
+ await execa('npx', ['prisma', 'db', 'seed'], { cwd });
60
+ spinner.succeed('Usuário admin criado');
61
+ }
62
+
63
+ async function waitForServer() {
64
+ const http = require('http');
65
+ for (let i = 0; i < 60; i++) {
66
+ const ok = await new Promise((resolve) => {
67
+ const req = http.get('http://localhost:3000', (res) => {
68
+ resolve(res.statusCode < 500);
69
+ });
70
+ req.on('error', () => resolve(false));
71
+ req.setTimeout(500, () => { req.destroy(); resolve(false); });
72
+ });
73
+ if (ok) return;
74
+ await new Promise(r => setTimeout(r, 500));
75
+ }
76
+ }
77
+
78
+ async function startServer(cwd) {
79
+ console.log('');
80
+ console.log(chalk.cyan(' Login: ') + 'admin@admin.com.br');
81
+ console.log(chalk.cyan(' Senha: ') + 'admin');
82
+ console.log('');
83
+ console.log(chalk.gray(' Iniciando servidor... o browser abrirá automaticamente.\n'));
84
+ console.log(chalk.gray(' (A primeira inicialização pode levar até 1 minuto)\n'));
85
+ console.log(chalk.gray(' Pressione Ctrl+C para parar o servidor.\n'));
86
+
87
+ const devProcess = execa('npm', ['run', 'dev'], { cwd, stdio: 'inherit' });
88
+
89
+ waitForServer().then(() => {
90
+ console.log(chalk.green('\n ✅ Servidor pronto! Abrindo http://localhost:3000\n'));
91
+ open('http://localhost:3000');
92
+ });
93
+
94
+ await devProcess;
95
+ }
96
+
97
+ async function cleanup(cwd) {
98
+ try {
99
+ const items = fs.readdirSync(cwd);
100
+ for (const item of items) {
101
+ const fullPath = path.join(cwd, item);
102
+ if (fs.statSync(fullPath).isDirectory()) {
103
+ fs.rmSync(fullPath, { recursive: true, force: true });
104
+ } else {
105
+ fs.unlinkSync(fullPath);
106
+ }
107
+ }
108
+ } catch {}
109
+ }
110
+
111
+ async function main() {
112
+ const command = process.argv[2];
113
+
114
+ if (command !== 'init') {
115
+ console.log(chalk.yellow('\nUso: npx exponenciacrm init\n'));
116
+ process.exit(0);
117
+ }
118
+
119
+ const cwd = process.cwd();
120
+ const ignored = new Set(['.DS_Store', '.git', '.gitkeep']);
121
+ const entries = fs.readdirSync(cwd).filter(e => !ignored.has(e));
122
+ if (entries.length > 0) {
123
+ console.error(chalk.red('\nA pasta atual não está vazia. Execute o comando em uma pasta vazia.\n'));
124
+ process.exit(1);
125
+ }
126
+
127
+ console.log('');
128
+ console.log(chalk.bold(' ExponenciaCRM — Setup\n'));
129
+
130
+ const spinner = ora({ color: 'cyan' });
131
+
132
+ try {
133
+ await checkGit();
134
+ await cloneRepo(spinner, cwd);
135
+ await installDeps(spinner, cwd);
136
+ createEnv(cwd);
137
+ await setupDatabase(spinner, cwd);
138
+ await seedDatabase(spinner, cwd);
139
+ await startServer(cwd);
140
+ } catch (err) {
141
+ spinner.fail(chalk.red('Erro durante o setup'));
142
+ console.log(chalk.yellow(' Limpando arquivos parciais...\n'));
143
+ await cleanup(cwd);
144
+ const detail = [err.stderr, err.stdout, err.message].filter(Boolean).join('\n');
145
+ console.error(chalk.red('\n' + detail + '\n'));
146
+ process.exit(1);
147
+ }
148
+ }
149
+
150
+ main();
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "exponenciacrm",
3
+ "version": "1.0.0",
4
+ "description": "CLI para instalar e rodar o ExponenciaCRM com um único comando",
5
+ "main": "bin/index.js",
6
+ "bin": {
7
+ "exponenciacrm": "bin/index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo 'Sem testes definidos'"
11
+ },
12
+ "keywords": [
13
+ "crm",
14
+ "exponencia",
15
+ "imersao"
16
+ ],
17
+ "author": "Thulio Bittencourt",
18
+ "license": "MIT",
19
+ "private": false,
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/bittencourtthulio/exponenciacrm.git"
23
+ },
24
+ "dependencies": {
25
+ "chalk": "4.1.2",
26
+ "execa": "8.0.1",
27
+ "open": "8.4.2",
28
+ "ora": "5.4.1"
29
+ },
30
+ "engines": {
31
+ "node": ">=18"
32
+ }
33
+ }