@openfactu/cli 0.0.7 → 0.0.8

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.
@@ -0,0 +1,474 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerInstallScriptCommand = registerInstallScriptCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const ora_1 = __importDefault(require("ora"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const logger_1 = require("../utils/logger");
11
+ const REPO_URL = 'https://github.com/OpenFactu/platform.git';
12
+ function registerInstallScriptCommand(program) {
13
+ program
14
+ .command('install:script')
15
+ .description('Generar script de instalacion standalone (sin CLI)')
16
+ .option('-o, --output <file>', 'Archivo de salida', './install-openfactu.sh')
17
+ .option('--tag <tag>', 'Version fija en el script')
18
+ .option('--dir <dir>', 'Directorio por defecto')
19
+ .option('-m, --monitoring', 'Incluir opcion de monitoreo en el script')
20
+ .option('-s, --service', 'Incluir opcion de servicio systemd en el script')
21
+ .action(async (opts) => {
22
+ console.log();
23
+ console.log(chalk_1.default.bold.white(' OpenFactu — Generar Script de Instalacion'));
24
+ console.log(chalk_1.default.dim(' ────────────────────────────────────'));
25
+ console.log();
26
+ try {
27
+ const tag = opts.tag || '${TAG:-latest}';
28
+ const defaultDir = opts.dir || '$HOME/openfactu';
29
+ const includeMonitoring = opts.monitoring || false;
30
+ const includeService = opts.service || false;
31
+ if (!opts.output.includes('.sh')) {
32
+ opts.output += '.sh';
33
+ }
34
+ const script = generateInstallScript({
35
+ repoUrl: REPO_URL,
36
+ tag,
37
+ defaultDir,
38
+ includeMonitoring,
39
+ includeService,
40
+ });
41
+ const spinner = (0, ora_1.default)('Generando script...').start();
42
+ fs_1.default.writeFileSync(opts.output, script);
43
+ fs_1.default.chmodSync(opts.output, 0o755);
44
+ spinner.succeed(`Script generado: ${chalk_1.default.cyan(opts.output)}`);
45
+ const fileSize = fs_1.default.statSync(opts.output).size;
46
+ logger_1.log.info(`Tamano: ${(fileSize / 1024).toFixed(1)}KB`);
47
+ logger_1.log.blank();
48
+ logger_1.log.dim(' Para usar el script:');
49
+ logger_1.log.dim(` chmod +x ${opts.output}`);
50
+ logger_1.log.dim(` ./${opts.output}`);
51
+ logger_1.log.blank();
52
+ logger_1.log.dim(' O remotamente:');
53
+ logger_1.log.dim(` curl -sL <url> | bash`);
54
+ logger_1.log.blank();
55
+ }
56
+ catch (err) {
57
+ logger_1.log.error(err.message);
58
+ process.exitCode = 1;
59
+ }
60
+ });
61
+ }
62
+ function generateInstallScript(options) {
63
+ const { repoUrl, tag, defaultDir, includeMonitoring, includeService } = options;
64
+ return `#!/bin/bash
65
+ # ============================================================
66
+ # OpenFactu - Script de Instalacion Automatica
67
+ # Generado por @openfactu/cli
68
+ # ============================================================
69
+
70
+ set -e
71
+
72
+ # Colors
73
+ RED='\\033[0;31m'
74
+ GREEN='\\033[0;32m'
75
+ YELLOW='\\033[1;33m'
76
+ BLUE='\\033[0;34m'
77
+ NC='\\033[0m'
78
+
79
+ # Config
80
+ REPO_URL="${repoUrl}"
81
+ TAG="${tag}"
82
+ INSTALL_DIR="${defaultDir}"
83
+ MONITORING=false
84
+ SERVICE=false
85
+ SKIP_DOCKER=false
86
+
87
+ # ============================================================
88
+ # Functions
89
+ # ============================================================
90
+
91
+ log_info() {
92
+ echo -e "\${BLUE}[INFO]\${NC} $1"
93
+ }
94
+
95
+ log_success() {
96
+ echo -e "\${GREEN}[OK]\${NC} $1"
97
+ }
98
+
99
+ log_warn() {
100
+ echo -e "\${YELLOW}[WARN]\${NC} $1"
101
+ }
102
+
103
+ log_error() {
104
+ echo -e "\${RED}[ERROR]\${NC} $1"
105
+ }
106
+
107
+ check_command() {
108
+ if ! command -v $1 &> /dev/null; then
109
+ return 1
110
+ fi
111
+ return 0
112
+ }
113
+
114
+ generate_password() {
115
+ # Solo alfanumericos: '$' rompe la interpolacion del .env en Docker Compose
116
+ # y '@ # % /' rompen el DATABASE_URL. Ver generatePassword() en la CLI.
117
+ openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 24
118
+ }
119
+
120
+ # ============================================================
121
+ # Banner
122
+ # ============================================================
123
+
124
+ echo ""
125
+ echo " ╔══════════════════════════════════════════╗"
126
+ echo " ║ OpenFactu - Instalador Automatico ║"
127
+ echo " ╚══════════════════════════════════════════╝"
128
+ echo ""
129
+
130
+ # ============================================================
131
+ # Parse arguments
132
+ # ============================================================
133
+
134
+ while [[ $# -gt 0 ]]; do
135
+ case $1 in
136
+ --tag)
137
+ TAG="$2"
138
+ shift 2
139
+ ;;
140
+ --dir)
141
+ INSTALL_DIR="$2"
142
+ shift 2
143
+ ;;
144
+ --monitoring)
145
+ MONITORING=true
146
+ shift
147
+ ;;
148
+ --service)
149
+ SERVICE=true
150
+ shift
151
+ ;;
152
+ --skip-docker)
153
+ SKIP_DOCKER=true
154
+ shift
155
+ ;;
156
+ --help|-h)
157
+ echo "Uso: $0 [opciones]"
158
+ echo ""
159
+ echo "Opciones:"
160
+ echo " --tag <version> Version a instalar (default: latest)"
161
+ echo " --dir <ruta> Directorio de instalacion"
162
+ echo " --monitoring Incluir stack de monitoreo"
163
+ echo " --service Instalar como servicio systemd"
164
+ echo " --skip-docker Solo descargar, no usar Docker"
165
+ echo " --help, -h Mostrar esta ayuda"
166
+ exit 0
167
+ ;;
168
+ *)
169
+ log_error "Opcion desconocida: $1"
170
+ exit 1
171
+ ;;
172
+ esac
173
+ done
174
+
175
+ # ============================================================
176
+ # Pre-flight checks
177
+ # ============================================================
178
+
179
+ log_info "Verificando requisitos..."
180
+
181
+ # Check OS
182
+ if [[ "$OSTYPE" == "linux-gnu"* ]]; then
183
+ log_success "Sistema: Linux"
184
+ elif [[ "$OSTYPE" == "darwin"* ]]; then
185
+ log_success "Sistema: macOS"
186
+ else
187
+ log_warn "Sistema no verificado: $OSTYPE"
188
+ fi
189
+
190
+ # Check Git
191
+ if check_command git; then
192
+ log_success "Git: $(git --version)"
193
+ else
194
+ log_error "Git no esta instalado"
195
+ log_info "Instala Git: https://git-scm.com/downloads"
196
+ exit 1
197
+ fi
198
+
199
+ # Check Docker
200
+ if check_command docker; then
201
+ log_success "Docker: $(docker --version)"
202
+ if docker compose version &> /dev/null || docker-compose --version &> /dev/null; then
203
+ log_success "Docker Compose: disponible"
204
+ else
205
+ log_error "Docker Compose no esta instalado"
206
+ exit 1
207
+ fi
208
+ else
209
+ if [ "$SKIP_DOCKER" = false ]; then
210
+ log_warn "Docker no esta instalado"
211
+ log_info "Instala Docker: https://docs.docker.com/get-docker/"
212
+ log_info "Usa --skip-docker para solo descargar el codigo"
213
+ exit 1
214
+ fi
215
+ fi
216
+
217
+ # Check disk space
218
+ AVAILABLE_GB=$(df -BG "$HOME" | tail -1 | awk '{print $4}' | tr -d 'G')
219
+ if [ "$AVAILABLE_GB" -lt 5 ]; then
220
+ log_error "Espacio insuficiente: \${AVAILABLE_GB}GB disponibles (minimo 5GB)"
221
+ exit 1
222
+ fi
223
+ log_success "Espacio en disco: \${AVAILABLE_GB}GB disponibles"
224
+
225
+ echo ""
226
+
227
+ # ============================================================
228
+ # Interactive prompts (if not all options provided)
229
+ # ============================================================
230
+
231
+ if [ "$TAG" = "\${TAG:-latest}" ]; then
232
+ read -p "Version a instalar (dejar vacio para latest): " USER_TAG
233
+ if [ -n "$USER_TAG" ]; then
234
+ TAG="$USER_TAG"
235
+ fi
236
+ fi
237
+
238
+ read -p "Directorio de instalacion [$INSTALL_DIR]: " USER_DIR
239
+ if [ -n "$USER_DIR" ]; then
240
+ INSTALL_DIR="$USER_DIR"
241
+ fi
242
+
243
+ if [ "$SKIP_DOCKER" = false ]; then
244
+ if [ "$MONITORING" = false ]; then
245
+ read -p "Incluir stack de monitoreo (Grafana, Prometheus)? [y/N]: " MON_ANSWER
246
+ if [[ "$MON_ANSWER" =~ ^[Yy]$ ]]; then
247
+ MONITORING=true
248
+ fi
249
+ fi
250
+
251
+ ${includeService ? ` if [ "$SERVICE" = false ]; then
252
+ read -p "Instalar como servicio systemd (auto-start)? [y/N]: " SVC_ANSWER
253
+ if [[ "$SVC_ANSWER" =~ ^[Yy]$ ]]; then
254
+ SERVICE=true
255
+ fi
256
+ fi` : ''}
257
+ fi
258
+
259
+ echo ""
260
+
261
+ # ============================================================
262
+ # Resolve tag
263
+ # ============================================================
264
+
265
+ if [ "$TAG" = "latest" ]; then
266
+ log_info "Obteniendo ultima version..."
267
+ LATEST=$(curl -s https://api.github.com/repos/OpenFactu/platform/releases | grep -m1 '"tag_name"' | cut -d'"' -f4)
268
+ if [ -n "$LATEST" ]; then
269
+ TAG="$LATEST"
270
+ log_success "Version seleccionada: $TAG"
271
+ else
272
+ TAG="main"
273
+ log_warn "No se pudo obtener la ultima version, usando main"
274
+ fi
275
+ fi
276
+
277
+ # ============================================================
278
+ # Clone repository
279
+ # ============================================================
280
+
281
+ log_info "Descargando OpenFactu..."
282
+
283
+ if [ -d "$INSTALL_DIR" ]; then
284
+ if [ "$(ls -A $INSTALL_DIR)" ]; then
285
+ log_warn "El directorio $INSTALL_DIR no esta vacio"
286
+ read -p "Continuar? [y/N]: " OVERWRITE
287
+ if [[ ! "$OVERWRITE" =~ ^[Yy]$ ]]; then
288
+ log_info "Instalacion cancelada"
289
+ exit 0
290
+ fi
291
+ fi
292
+ else
293
+ mkdir -p "$INSTALL_DIR"
294
+ fi
295
+
296
+ IS_TAG=false
297
+ if [[ "$TAG" == v* ]]; then
298
+ IS_TAG=true
299
+ fi
300
+
301
+ if [ "$IS_TAG" = true ]; then
302
+ git clone --depth 1 --branch "$TAG" "$REPO_URL" "$INSTALL_DIR" 2>/dev/null || {
303
+ log_warn "Clon por tag fallo, intentando metodo alternativo..."
304
+ git clone "$REPO_URL" "$INSTALL_DIR"
305
+ cd "$INSTALL_DIR"
306
+ git checkout "$TAG"
307
+ }
308
+ else
309
+ git clone --branch "$TAG" "$REPO_URL" "$INSTALL_DIR" 2>/dev/null || {
310
+ git clone "$REPO_URL" "$INSTALL_DIR"
311
+ cd "$INSTALL_DIR"
312
+ git checkout "$TAG"
313
+ }
314
+ fi
315
+
316
+ log_success "Codigo descargado en $INSTALL_DIR"
317
+
318
+ # ============================================================
319
+ # Generate .env
320
+ # ============================================================
321
+
322
+ log_info "Generando configuracion..."
323
+
324
+ cd "$INSTALL_DIR"
325
+
326
+ if [ -f ".env.example" ] && [ ! -f ".env" ]; then
327
+ cp .env.example .env
328
+ fi
329
+
330
+ DB_PASSWORD=$(generate_password)
331
+ JWT_SECRET=$(generate_password)
332
+ ADMIN_PASSWORD=$(generate_password)
333
+
334
+ cat >> .env << EOF
335
+
336
+ # Generated by installer
337
+ POSTGRES_USER=openfactu
338
+ POSTGRES_PASSWORD=$DB_PASSWORD
339
+ POSTGRES_DB=openfactudb
340
+ DATABASE_URL=postgresql://openfactu:$DB_PASSWORD@db:5432/openfactudb
341
+ JWT_SECRET=$JWT_SECRET
342
+ SESSION_SECRET=$(generate_password)
343
+ NODE_ENV=production
344
+ HOST=localhost
345
+ CORS_ORIGIN=http://localhost:8080
346
+ VITE_API_URL=http://localhost:3000
347
+ ADMIN_EMAIL=admin@openfactu.local
348
+ ADMIN_PASSWORD=$ADMIN_PASSWORD
349
+ EOF
350
+
351
+ log_success "Configuracion generada"
352
+ echo ""
353
+ log_warn "Guarda estas credenciales:"
354
+ echo " DB Password: $DB_PASSWORD"
355
+ echo " Admin: admin@openfactu.local / $ADMIN_PASSWORD"
356
+ echo ""
357
+
358
+ # ============================================================
359
+ # Docker setup
360
+ # ============================================================
361
+
362
+ if [ "$SKIP_DOCKER" = false ]; then
363
+ log_info "Construyendo contenedores Docker..."
364
+
365
+ if command -v docker &> /dev/null && (docker compose version &> /dev/null || docker-compose --version &> /dev/null); then
366
+ DOCKER_CMD="docker compose"
367
+ if ! docker compose version &> /dev/null; then
368
+ DOCKER_CMD="docker-compose"
369
+ fi
370
+
371
+ # Build
372
+ $DOCKER_CMD build || {
373
+ log_warn "Build fallo, puedes ejecutarlo manualmente despues"
374
+ }
375
+
376
+ # Up
377
+ COMPOSE_FLAGS="-f docker-compose.yml"
378
+ if [ -f "docker-compose.prod.yml" ]; then
379
+ COMPOSE_FLAGS="-f docker-compose.prod.yml"
380
+ fi
381
+
382
+ if [ "$MONITORING" = true ] && [ -f "docker-compose.monitoring.yml" ]; then
383
+ COMPOSE_FLAGS="$COMPOSE_FLAGS -f docker-compose.monitoring.yml"
384
+ fi
385
+
386
+ log_info "Levantando servicios..."
387
+ $DOCKER_CMD $COMPOSE_FLAGS up -d
388
+
389
+ log_success "Servicios levantados"
390
+
391
+ # Wait for services
392
+ log_info "Esperando que los servicios inicien..."
393
+ sleep 10
394
+
395
+ # Health check
396
+ if curl -s http://localhost:3000/api/health &> /dev/null; then
397
+ log_success "API esta operativa"
398
+ else
399
+ log_warn "API aun no responde, puede tardar unos segundos"
400
+ fi
401
+ else
402
+ log_error "Docker no disponible"
403
+ fi
404
+ fi
405
+
406
+ # ============================================================
407
+ # Install systemd service
408
+ # ============================================================
409
+
410
+ ${includeService ? `if [ "$SERVICE" = true ]; then
411
+ if [ -d "/run/systemd/system" ]; then
412
+ log_info "Instalando servicio systemd..."
413
+
414
+ DOCKER_CMD="docker compose"
415
+ if ! docker compose version &> /dev/null; then
416
+ DOCKER_CMD="docker-compose"
417
+ fi
418
+
419
+ cat > /tmp/openfactu.service << SVCEOF
420
+ [Unit]
421
+ Description=OpenFactu ERP Platform
422
+ After=docker.service network-online.target
423
+ Requires=docker.service
424
+
425
+ [Service]
426
+ Type=oneshot
427
+ RemainAfterExit=yes
428
+ WorkingDirectory=$INSTALL_DIR
429
+ ExecStart=$DOCKER_CMD -f docker-compose.yml up -d
430
+ ExecStop=$DOCKER_CMD -f docker-compose.yml down
431
+ Restart=on-failure
432
+ RestartSec=30
433
+
434
+ [Install]
435
+ WantedBy=multi-user.target
436
+ SVCEOF
437
+
438
+ sudo mv /tmp/openfactu.service /etc/systemd/system/openfactu.service
439
+ sudo systemctl daemon-reload
440
+ sudo systemctl enable openfactu
441
+
442
+ log_success "Servicio systemd instalado"
443
+ log_info "Iniciar con: sudo systemctl start openfactu"
444
+ else
445
+ log_warn "systemd no disponible, omitiendo servicio"
446
+ fi
447
+ fi` : ''}
448
+
449
+ # ============================================================
450
+ # Summary
451
+ # ============================================================
452
+
453
+ echo ""
454
+ echo " ╔══════════════════════════════════════════╗"
455
+ echo " ║ Instalacion Completada! ║"
456
+ echo " ╚══════════════════════════════════════════╝"
457
+ echo ""
458
+ echo " Directorio: $INSTALL_DIR"
459
+ echo " Version: $TAG"
460
+ echo ""
461
+ echo " Proximos pasos:"
462
+ echo " cd $INSTALL_DIR"
463
+ echo " openfactu setup # Configurar base de datos"
464
+ echo " openfactu deploy # Configurar acceso externo"
465
+ echo " openfactu doctor # Verificar instalacion"
466
+ echo ""
467
+ if [ "$SKIP_DOCKER" = false ]; then
468
+ echo " URLs:"
469
+ echo " Web: http://localhost:8080"
470
+ echo " API: http://localhost:3000"
471
+ echo ""
472
+ fi
473
+ `;
474
+ }