utn-cli 2.0.48 → 2.0.50

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.
@@ -149,7 +149,7 @@ export async function initBackend() {
149
149
  closeReadLine();
150
150
  }
151
151
 
152
- export async function updateBackend() {
152
+ export async function updateBackend(opciones = { cerrarAlFinalizar: true }) {
153
153
  console.log('Actualizando el proyecto de backend...');
154
154
  const archivosAExcluir = ['InformacionDelModulo.json', 'rutas.js', 'Monitoreo.js', 'API.js', 'NOMBRE_DEL_CANONICO_DEL_PROYECTO.rest'];
155
155
  const directoriodePlantillas = path.join(__dirname, '../templates/backend');
@@ -166,11 +166,16 @@ export async function updateBackend() {
166
166
  fs.renameSync('gitignore', '.gitignore');
167
167
  await inicializarProyectoBackend();
168
168
  console.log('Proyecto de backend actualizado exitosamente.');
169
- closeReadLine();
169
+ if (opciones.cerrarAlFinalizar) {
170
+ closeReadLine();
171
+ }
170
172
  }
171
173
 
172
- export async function addServiceBackend() {
173
- let Servicio = await hacerPreguntaTrim('¿Cuál es el nombre del servicio?: ');
174
+ export async function addServiceBackend(nombreServicio = null, opciones = { cerrarAlFinalizar: true }, tituloServicio = null) {
175
+ let Servicio = nombreServicio;
176
+ if (!Servicio) {
177
+ Servicio = await hacerPreguntaTrim('¿Cuál es el nombre del servicio?: ');
178
+ }
174
179
  Servicio = Servicio.replace(/\s/g, '');
175
180
  const rutaServicioDir = path.join(process.cwd(), 'servicios');
176
181
  const rutaRutaDir = path.join(process.cwd(), 'rutas');
@@ -182,7 +187,7 @@ export async function addServiceBackend() {
182
187
  fs.copyFileSync(templateServicioPath, newServicioPath);
183
188
  } else {
184
189
  console.error(`Error: Template Servicio1.js not found in ${rutaServicioDir}`);
185
- closeReadLine();
190
+ if (opciones.cerrarAlFinalizar) closeReadLine();
186
191
  process.exit(1);
187
192
  }
188
193
 
@@ -192,7 +197,7 @@ export async function addServiceBackend() {
192
197
  fs.copyFileSync(templateRutaPath, newRutaPath);
193
198
  } else {
194
199
  console.error(`Error: Template Servicio1.js not found in ${rutaRutaDir}`);
195
- closeReadLine();
200
+ if (opciones.cerrarAlFinalizar) closeReadLine();
196
201
  process.exit(1);
197
202
  }
198
203
 
@@ -210,10 +215,15 @@ function asignarRutasAExpress(app) {`);
210
215
  fs.writeFileSync(rutaRutasJs, contenidoRutasJs);
211
216
  } else {
212
217
  console.error(`Error: rutas.js not found in ${rutaRutaDir}`);
213
- closeReadLine();
218
+ if (opciones.cerrarAlFinalizar) closeReadLine();
214
219
  process.exit(1);
215
220
  }
216
221
 
222
+ // Reemplazar el título amigable si se proporciona
223
+ if (tituloServicio) {
224
+ reemplazarContenidoEnArchivo(newServicioPath, "const NombreDelServicio = 'Servicio1';", `const NombreDelServicio = '${tituloServicio}';`);
225
+ }
226
+
217
227
  reemplazarContenidoEnArchivo(newServicioPath, 'Servicio1', Servicio);
218
228
  reemplazarContenidoEnArchivo(newRutaPath, 'Servicio1', Servicio);
219
229
 
@@ -280,7 +290,9 @@ export const routes: Routes = [`
280
290
  */
281
291
 
282
292
  console.log(`Servicio ${Servicio} agregado exitosamente.`);
283
- closeReadLine();
293
+ if (opciones.cerrarAlFinalizar) {
294
+ closeReadLine();
295
+ }
284
296
  }
285
297
 
286
298
  export function showBackendVersion() {
@@ -0,0 +1,60 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { addServiceBackend } from './backend.js';
4
+ import { cloneFrontendComponent } from './frontend.js';
5
+ import { closeReadLine, hacerPreguntaTrim } from '../utils/index.js';
6
+
7
+ export async function createComponent() {
8
+ const currentDir = process.cwd();
9
+
10
+ const nombreServicio = await hacerPreguntaTrim('¿Cuál es el nombre del nuevo servicio/componente?: ');
11
+ if (!nombreServicio) {
12
+ console.error('El nombre es requerido.');
13
+ closeReadLine();
14
+ return;
15
+ }
16
+
17
+ const tituloCard = await hacerPreguntaTrim('Ingrese el título para la tarjeta del frontend: ');
18
+ const descripcionCard = await hacerPreguntaTrim('Ingrese la descripción para la tarjeta del frontend: ');
19
+
20
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
21
+ const directories = entries.filter(entry => entry.isDirectory());
22
+
23
+ let foundBackend = false;
24
+ let foundFrontend = false;
25
+
26
+ for (const dir of directories) {
27
+ const dirName = dir.name.toLowerCase();
28
+ const fullPath = path.join(currentDir, dir.name);
29
+
30
+ if (dirName.includes('back')) {
31
+ console.log(`\nAgregando servicio a Backend en: ${dir.name}`);
32
+ process.chdir(fullPath);
33
+ try {
34
+ await addServiceBackend(nombreServicio, { cerrarAlFinalizar: false }, tituloCard);
35
+ foundBackend = true;
36
+ } catch (error) {
37
+ console.error(`Error al agregar servicio en ${dir.name}:`, error);
38
+ }
39
+ process.chdir(currentDir);
40
+ } else if (dirName.includes('front')) {
41
+ console.log(`\nAgregando componente a Frontend en: ${dir.name}`);
42
+ process.chdir(fullPath);
43
+ try {
44
+ await cloneFrontendComponent(nombreServicio, tituloCard, descripcionCard, { cerrarAlFinalizar: false });
45
+ foundFrontend = true;
46
+ } catch (error) {
47
+ console.error(`Error al clonar componente en ${dir.name}:`, error);
48
+ }
49
+ process.chdir(currentDir);
50
+ }
51
+ }
52
+
53
+ if (!foundBackend && !foundFrontend) {
54
+ console.log('No se encontraron carpetas con "back" o "front" para procesar.');
55
+ } else {
56
+ console.log('\nProceso de creación de componente finalizado.');
57
+ }
58
+
59
+ closeReadLine();
60
+ }
@@ -40,7 +40,7 @@ export async function initFrontend() {
40
40
  closeReadLine();
41
41
  }
42
42
 
43
- export async function updateFrontend() {
43
+ export async function updateFrontend(opciones = { cerrarAlFinalizar: true }) {
44
44
  console.log('Actualizando el proyecto de frontend...');
45
45
  const archivosAExcluir = ['app.routes.ts', 'contenedor-principal.component.css', 'contenedor-principal.component.html', 'contenedor-principal.component.ts', '.vscode', 'dist'];
46
46
  const directoriodePlantillas = path.join(__dirname, '../templates/frontend');
@@ -57,19 +57,30 @@ export async function updateFrontend() {
57
57
  fs.renameSync('gitignore', '.gitignore');
58
58
  fs.renameSync('editorconfig', '.editorconfig');
59
59
  console.log('Proyecto de frontend actualizado exitosamente.');
60
- closeReadLine();
60
+ if (opciones.cerrarAlFinalizar) {
61
+ closeReadLine();
62
+ }
61
63
  }
62
64
 
63
- export async function cloneFrontendComponent() {
64
- const nombre = await hacerPreguntaTrim('Ingrese el nombre del nuevo componente (se reemplazará XYZ): ');
65
+ export async function cloneFrontendComponent(nombreComponente = null, tituloCard = null, descripcionCard = null, opciones = { cerrarAlFinalizar: true }) {
66
+ let nombre = nombreComponente;
67
+ if (!nombre) {
68
+ nombre = await hacerPreguntaTrim('Ingrese el nombre del nuevo componente (se reemplazará XYZ): ');
69
+ }
65
70
  if (!nombre) {
66
71
  console.error('El nombre es requerido.');
67
- closeReadLine();
72
+ if (opciones.cerrarAlFinalizar) closeReadLine();
68
73
  process.exit(1);
69
74
  }
70
75
 
71
- const titulo = await hacerPreguntaTrim('Ingrese el título para la tarjeta: ');
72
- const descripcion = await hacerPreguntaTrim('Ingrese la descripción para la tarjeta: ');
76
+ let titulo = tituloCard;
77
+ if (!titulo) {
78
+ titulo = await hacerPreguntaTrim('Ingrese el título para la tarjeta: ');
79
+ }
80
+ let descripcion = descripcionCard;
81
+ if (!descripcion) {
82
+ descripcion = await hacerPreguntaTrim('Ingrese la descripción para la tarjeta: ');
83
+ }
73
84
 
74
85
  const nombreLower = nombre.toLowerCase();
75
86
  const nombreCapitalizado = nombre.charAt(0).toUpperCase() + nombre.slice(1);
@@ -81,13 +92,13 @@ export async function cloneFrontendComponent() {
81
92
 
82
93
  if (!fs.existsSync(rutaFuente)) {
83
94
  console.error(`No se encontró la carpeta plantilla en: ${rutaFuente}`);
84
- closeReadLine();
95
+ if (opciones.cerrarAlFinalizar) closeReadLine();
85
96
  process.exit(1);
86
97
  }
87
98
 
88
99
  if (fs.existsSync(rutaDestino)) {
89
100
  console.error(`La carpeta destino ya existe: ${rutaDestino}`);
90
- closeReadLine();
101
+ if (opciones.cerrarAlFinalizar) closeReadLine();
91
102
  process.exit(1);
92
103
  }
93
104
 
@@ -174,7 +185,9 @@ export async function cloneFrontendComponent() {
174
185
  } else {
175
186
  console.error(`No se encontró el archivo HTML en: ${rutaHtml}`);
176
187
  }
177
- closeReadLine();
188
+ if (opciones.cerrarAlFinalizar) {
189
+ closeReadLine();
190
+ }
178
191
  }
179
192
 
180
193
  export function showFrontendVersion() {
@@ -0,0 +1,76 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { updateBackend } from './backend.js';
4
+ import { updateFrontend } from './frontend.js';
5
+ import { closeReadLine } from '../utils/index.js';
6
+ import { execSync } from 'child_process';
7
+
8
+ export async function runGlobalUpdate() {
9
+ const currentDir = process.cwd();
10
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
11
+
12
+ const directories = entries.filter(entry => entry.isDirectory());
13
+
14
+ if (directories.length === 0) {
15
+ console.log('No se encontraron directorios en la carpeta actual.');
16
+ closeReadLine();
17
+ return;
18
+ }
19
+
20
+ let foundSomething = false;
21
+
22
+ for (const dir of directories) {
23
+ const dirName = dir.name.toLowerCase();
24
+ const fullPath = path.join(currentDir, dir.name);
25
+
26
+ if (dirName.includes('back') || dirName.includes('front')) {
27
+ const type = dirName.includes('back') ? 'Backend' : 'Frontend';
28
+ console.log(`\n=========================================`);
29
+ console.log(`Procesando ${type} en: ${dir.name}`);
30
+ console.log(`=========================================\n`);
31
+
32
+ process.chdir(fullPath);
33
+
34
+ // Execute git pull if it's a git repository
35
+ if (fs.existsSync(path.join(fullPath, '.git'))) {
36
+ console.log(`Ejecutando git pull en ${dir.name}...`);
37
+ try {
38
+ execSync('git pull', { stdio: 'inherit' });
39
+ } catch (error) {
40
+ console.error(`Error al ejecutar git pull en ${dir.name}. Continuando con la actualización...`);
41
+ }
42
+ }
43
+
44
+ try {
45
+ if (dirName.includes('back')) {
46
+ await updateBackend({ cerrarAlFinalizar: false });
47
+ } else if (dirName.includes('front')) {
48
+ await updateFrontend({ cerrarAlFinalizar: false });
49
+
50
+ // Execute build for frontend if package.json exists
51
+ if (fs.existsSync(path.join(process.cwd(), 'package.json'))) {
52
+ console.log(`\nEjecutando sudo npm run build en ${dir.name}...`);
53
+ try {
54
+ execSync('sudo npm run build', { stdio: 'inherit' });
55
+ } catch (error) {
56
+ console.error(`Error al ejecutar build en ${dir.name}. Continuando...`);
57
+ }
58
+ }
59
+ }
60
+ foundSomething = true;
61
+ } catch (error) {
62
+ console.error(`Error actualizando ${type} en ${dir.name}:`, error);
63
+ }
64
+
65
+ process.chdir(currentDir);
66
+ }
67
+ }
68
+
69
+ if (!foundSomething) {
70
+ console.log('No se encontraron carpetas con "back" o "front" en el nombre.');
71
+ } else {
72
+ console.log('\nProceso de actualización global finalizado.');
73
+ }
74
+
75
+ closeReadLine();
76
+ }
package/index.js CHANGED
@@ -7,6 +7,8 @@ import { fileURLToPath } from 'url';
7
7
  import { initDb, showDbVersion } from './commands/db.js';
8
8
  import { initBackend, updateBackend, addServiceBackend, showBackendVersion } from './commands/backend.js';
9
9
  import { initFrontend, updateFrontend, cloneFrontendComponent, showFrontendVersion } from './commands/frontend.js';
10
+ import { runGlobalUpdate } from './commands/update.js';
11
+ import { createComponent } from './commands/createComponent.js';
10
12
  import { runCommit } from './commands/commit.js';
11
13
 
12
14
  const __filename = fileURLToPath(import.meta.url);
@@ -30,6 +32,7 @@ if (packageInfo) {
30
32
 
31
33
  // Define 'db' command
32
34
  program.command('db')
35
+ .alias('bd')
33
36
  .description('Comandos para la gestión de plantillas de base de datos.')
34
37
  .option('--init', 'Inicializa un nuevo proyecto de base de datos.')
35
38
  .option('--version', 'Muestra la versión del módulo de base de datos.')
@@ -88,6 +91,20 @@ program.command('frontend')
88
91
  }
89
92
  });
90
93
 
94
+ // Define 'update' command
95
+ program.command('update')
96
+ .description('Recorre los directorios y actualiza proyectos de backend (si el nombre contiene "back") y frontend (si el nombre contiene "front").')
97
+ .action(async () => {
98
+ await runGlobalUpdate();
99
+ });
100
+
101
+ // Define 'create-component' command
102
+ program.command('create-component')
103
+ .description('Crea un nuevo servicio en el backend y un componente en el frontend simultáneamente.')
104
+ .action(async () => {
105
+ await createComponent();
106
+ });
107
+
91
108
  // Define 'commit' command
92
109
  program.command('commit')
93
110
  .description('Realiza un commit siguiendo el flujo de trabajo predefinido.')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utn-cli",
3
- "version": "2.0.48",
3
+ "version": "2.0.50",
4
4
  "description": "Herramienta CLI unificada para la gestión de plantillas en SIGU.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -58,7 +58,7 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy {
58
58
  this.obtenerUsuariosActuales();
59
59
  this.intervaloUsuarios = setInterval(() => {
60
60
  this.obtenerUsuariosActuales();
61
- }, 30000);
61
+ }, 60000);
62
62
 
63
63
  this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/validarToken').subscribe((datos: any) => {
64
64
  this.TienePermiso = datos.body;
@@ -220,16 +220,24 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy {
220
220
  this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/UsuariosActuales').subscribe((datos: any) => {
221
221
  const data = datos.body;
222
222
 
223
- if (this.UsuariosActuales !== data.UsuariosActuales) {
224
- this.UsuariosActuales = data.UsuariosActuales;
223
+ let actuales = data.UsuariosActuales;
224
+ let activos = data.UsuariosActivos;
225
+
226
+ // Si activos es mayor a actuales, igualamos activos a actuales
227
+ if (activos > actuales) {
228
+ activos = actuales;
229
+ }
230
+
231
+ if (this.UsuariosActuales !== actuales) {
232
+ this.UsuariosActuales = actuales;
225
233
  this.AnimarUsuarios = false;
226
234
  setTimeout(() => {
227
235
  this.AnimarUsuarios = true;
228
236
  }, 50);
229
237
  }
230
238
 
231
- if (this.UsuariosActivos !== data.UsuariosActivos) {
232
- this.UsuariosActivos = data.UsuariosActivos;
239
+ if (this.UsuariosActivos !== activos) {
240
+ this.UsuariosActivos = activos;
233
241
  this.AnimarUsuariosActivos = false;
234
242
  setTimeout(() => {
235
243
  this.AnimarUsuariosActivos = true;
@@ -1,78 +0,0 @@
1
- # Generalidades
2
- Este repositorio proporciona la estructura básica e inicial de la infraestructura de base de datos para el desarrollo de módulos en la UTN. Su principal objetivo es ofrecer un punto de partida sólido para la creación de bases de datos destinadas a los módulos en desarrollo. Siéntase libre de modificarlo según sus necesidades y para que se adapte a su forma de trabajo.
3
-
4
- # Requisitos previos
5
- - Contar con *docker* y *docker-compose* instalado en su computadora.
6
- - Tener instalado el editor de código de su preferencia.
7
- - Contar con algún software para interactuar con bases de datos de tipo MySQL/MariaDB.
8
- - Crear la carpeta *registry.git.utn.ac.cr* en */etc/docker/certs.d* en Linux o en *C:\ProgramData\docker\certs.d* en Windows.
9
- - Solicitar el archivo *ca.cert* y colocarlo en la carpeta creada en el paso anterior.
10
-
11
- # Puesta en marcha del entorno de base de datos (fase inicial)
12
- En una terminal ejecute los siguientes comandos.
13
- - Registro del certificado en *docker*.
14
-
15
- $ docker login registry.git.utn.ac.cr
16
-
17
- - Clone este repositorio en su computadora. No olvide cambiar la ruta https por la ruta correcta.
18
-
19
- $ git clone https://git.utn.ac.cr/sistema-integrado-de-gesti-n-universitaria-sigu/sigu-plantillas/plantillas-base-de-datos.git
20
-
21
- - Ponga a correr el entorno. Ingrese a la carpeta en la que descargó el repositorio.
22
-
23
- $ docker-compose --file docker-compose.yml up -d --build
24
-
25
- - Para apagar el entorno ejecute el siguiente comando.
26
-
27
- $ docker-compose down
28
-
29
- # Resultado (inicial)
30
- Si todo salió bien, tendremos disponible lo siguiente.
31
- - Tres bases de datos para trabajar, nos podremos conectar a ellas haciendo uso de nuestro software favorito estableciendo 127.0.0.1 (no usar localhost) como el *host* y *3307* como el puerto de conexión, el nombre de usuario es *root* y la contraseña es *root*.
32
- - El puerto 3307 corresponde a la primer instancia creada. Esta instancia se usa para almacenar la base de datos de su módulo.
33
- - Si hace uso de los mismos parámetros pero cambia al puerto 3308, se conectará a otra instancia de base de datos, en ésta se semejarán los datos que podría necesitar del servidor cumulodb.utn.ac.cr.
34
- - Si hace uso de los mismos parámetros pero cambia al puerto 3309, se conectará a la imagen de base de datos del *framework*. En *famewotk* se encuentran por ejemplo las tablas SIGU.SIGU_Personas y SIGU.SIGU_Localidades.
35
-
36
- # Hacer uso del entorno de desarrollo
37
- Hasta este momento, hemos configurado el entorno de trabajo y ya todo debería de estar listo para usarse, pero, ¿qué pasa si necesitamos crear las estructuras (base de datos, tablas, vistas, etc) de nuestro módulo?
38
- En los puntos anteriores pusimos ha funcionar nuestro entorno de trabajo, pero, para utilizarlo debemos tener en consideración lo siguiente.
39
-
40
- ## docker-scripts
41
- - En la carpeta docker-scripts se encuentran una serie de archivos de tipo *.sql*, es en estos archivos en donde usted va a definir las diferentes estructuras de su base de datos que necesita su módulo, veamos el uso cada uno de ellos.
42
-
43
- | Archivo | Uso |
44
- |--|--|
45
- | 1-crear estructura.sql | En este archivo van las instrucciones de tipo SQL para crear todas las estructuras de base de datos que necesita su módulo. |
46
- | 2-cambios estructura original.sql | Una vez que su módulo llegó a Producción y si es que se torna necesario hacer cambios a la estructura original del módulo, es en este archivo en donde se escribirán las instrucciones de tipo SQL para crear o modificar las estructuras de base de datos que necesita su módulo. |
47
- | 3-insertar datos de prueba.sql | En este archivo se crean las instrucciones de tipo SQL para llevar a cabo la inserción de los datos de prueba de su módulo. |
48
- | 4-crear eventos y rutinas.sql | En este archivo se crean las instrucciones de tipo SQL para crear los eventos y rutinas (funciones y procedimientos) de su módulo. |
49
- | 5-crear vistas.sql |En este archivo se crean las instrucciones de tipo SQL para crear las vistas de su módulo. |
50
- | 6-calidad.sql | En este archivo se crean las instrucciones de tipo SQL que se necesitan ejecutar en el ambiente de Calidad. |
51
- | 7-pruebas.sql | En este archivo se crean las instrucciones de tipo SQL que se necesitan ejecutar en el ambiente de Pruebas. |
52
- | 8-local.sql | En este archivo se crean las instrucciones de tipo SQL que se necesitan ejecutar en el ambiente de Local. |
53
-
54
- ## cumulodb-scripts
55
- - La carpeta cumulodb-scripts se encuentra bacía de manera predeterminada, en esta carpeta debe colocar el archivo *.sql* que le brinden los compañeros de la Unidad de Datos y que contenga los datos para simular la conexión con el servidor cumulodb.utn.ac.cr.
56
-
57
- # Puesta en marcha del entorno de base de datos (fase de trabajo)
58
- Para este momento debemos haber cumplido con lo siguiente.
59
- - Haber cumplido con la sección de [Requisitos previos](#requisitos-previos).
60
- - Haber ingresado en los diferentes archivos de la carpeta *docker-scripts* las instrucciones de tipo SQL pertinentes.
61
- - Haber pegado en la carpeta cumulodb-scripts el archivo brindado por los compañeros de la Unidad de Datos y que contiene los datos del servidor cumulodb.utn.ac.cr, esto si es que fuera necesario.
62
-
63
- Ahora es necesario volver a crear nuestro entorno de trabajo, pero en esta oportunidad, se creará con las estructuras y bases de datos necesarias, para ello:
64
- - Apague el entorno ejecute el siguiente comando.
65
-
66
- $ docker-compose down
67
-
68
- - Ponga a correr el entorno.
69
-
70
- $ docker-compose --file docker-compose.yml up -d --build
71
-
72
- # Resultado (final)
73
- Si todo salió bien, tendremos disponible lo siguiente.
74
- - Tres bases de datos para trabajar.
75
- - El puerto 3307 corresponde a la primer instancia creada. En esta instancia estará la base de datos y diferentes estructuras creadas en *1-crear estructura.sql* y demás archivos de esa carpeta.
76
- - En el puerto 3308, se conectará a otra instancia de base de datos, en ésta se semejarán los datos que podría necesitar del servidor cumulodb.utn.ac.cr. En esta instancia estará una base de datos llama *DatosFederados* y las tablas suministradas por la Unidad de Datos.
77
- - En el puerto 3309, se conectará a la imagen de base de datos del *framework*. En *famewotk* se encuentran por ejemplo las tablas SIGU.SIGU_Personas y SIGU.SIGU_Localidades.
78
-
@@ -1 +0,0 @@
1
- *