utn-cli 2.0.32 → 2.0.34
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 +1 -1
- package/templates/backend/rutas/API.js +7 -0
- package/templates/backend/rutas/Servicio1.js +21 -0
- package/templates/backend/rutas/misc.js +17 -0
- package/templates/backend/servicios/API.js +27 -0
- package/templates/backend/servicios/Nucleo/Miscelaneas.js +219 -36
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component.ts +0 -5
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component.css +37 -0
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component.html +17 -0
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component.ts +54 -0
- package/templates/frontend/src/app/Componentes/Nucleo/tabla/tabla.component.html +1 -1
- package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.html +3 -0
- package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.ts +6 -1
package/package.json
CHANGED
|
@@ -4,6 +4,13 @@ const Router = express.Router();
|
|
|
4
4
|
const Miscelaneo = require('../servicios/Nucleo/Miscelaneas.js');
|
|
5
5
|
const { moitoreo } = require('../servicios/Nucleo/Monitoreo.js');
|
|
6
6
|
|
|
7
|
+
// Router.post("/DescargarArchivo", async (solicitud, respuesta) => {
|
|
8
|
+
// if (await Miscelaneo.validarIdentificadorAPI(solicitud.headers)) {
|
|
9
|
+
// return Miscelaneo.descargarArchivo(respuesta, solicitud.body);
|
|
10
|
+
// }
|
|
11
|
+
// return respuesta.status(401).json();
|
|
12
|
+
// });
|
|
13
|
+
|
|
7
14
|
Router.post('/monitoreo/', async (solicitud, respuesta, next) => {
|
|
8
15
|
try {
|
|
9
16
|
if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
@@ -4,6 +4,7 @@ const Router = express.Router();
|
|
|
4
4
|
const Servicio1 = require('../servicios/Servicio1.js');
|
|
5
5
|
const Miscelaneo = require('../servicios/Nucleo/Miscelaneas.js');
|
|
6
6
|
const ManejadorDeErrores = require('../servicios/Nucleo/ManejadorDeErrores.js');
|
|
7
|
+
// const API = require('../servicios/API.js')
|
|
7
8
|
|
|
8
9
|
Router.get('/funcionDeEjemplo', async (solicitud, respuesta, next) => {
|
|
9
10
|
try {
|
|
@@ -22,6 +23,26 @@ Router.get('/funcionDeEjemplo', async (solicitud, respuesta, next) => {
|
|
|
22
23
|
}
|
|
23
24
|
});
|
|
24
25
|
|
|
26
|
+
// Router.get("/descargarArchivo/:ArchivoId", async (solicitud, respuesta, next) => {
|
|
27
|
+
// try {
|
|
28
|
+
// if (await Miscelaneo.validarToken(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
29
|
+
// try {
|
|
30
|
+
// let Datos = { ArchivoId: solicitud.params.ArchivoId };
|
|
31
|
+
// Datos.Token = solicitud.headers.authorization;
|
|
32
|
+
// await API.DescargarArchivo(Datos, respuesta);
|
|
33
|
+
// return;
|
|
34
|
+
// } catch (error) {
|
|
35
|
+
// const MensajeDeError = "No fue posible obtener el archivo";
|
|
36
|
+
// console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
|
|
37
|
+
// return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
|
|
38
|
+
// }
|
|
39
|
+
// }
|
|
40
|
+
// return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
|
|
41
|
+
// } catch (error) {
|
|
42
|
+
// next(error)
|
|
43
|
+
// }
|
|
44
|
+
// });
|
|
45
|
+
|
|
25
46
|
// Router.get("/listar/:PaginadorAccion/:PaginadorIndice/:PaginadorTamanio/:PaginadorFiltro/:PaginadorColumnaParaFiltrar/:PaginadorColumnasParaFiltrar/:ColumnaParaOrdenar/:TipoDeOrden", async (solicitud, respuesta, next) => {
|
|
26
47
|
// try {
|
|
27
48
|
// if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
@@ -277,6 +277,23 @@ Router.post('/reporteDeIncidencia/:Datos', async (solicitud, respuesta, next) =>
|
|
|
277
277
|
}
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
+
Router.post('/reporteDeSugerencia/:Datos', async (solicitud, respuesta, next) => {
|
|
281
|
+
try {
|
|
282
|
+
if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
283
|
+
try {
|
|
284
|
+
return respuesta.json({ body: await Miscelaneo.reporteDeSugerencia(solicitud, solicitud.params.Datos), error: undefined });
|
|
285
|
+
} catch (error) {
|
|
286
|
+
const MensajeDeError = 'No fue posible reportar la incidencia';
|
|
287
|
+
console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
|
|
288
|
+
return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
|
|
292
|
+
} catch (error) {
|
|
293
|
+
next(error);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
280
297
|
Router.get('/cerrarSesion', async (solicitud, respuesta, next) => {
|
|
281
298
|
try {
|
|
282
299
|
if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const Miscelaneo = require('./../servicios/Nucleo/Miscelaneas.js');
|
|
2
|
+
const { Readable } = require('stream');
|
|
2
3
|
|
|
3
4
|
class API {
|
|
4
5
|
|
|
@@ -6,6 +7,32 @@ class API {
|
|
|
6
7
|
|
|
7
8
|
};
|
|
8
9
|
|
|
10
|
+
// async DescargarArchivo(Datos, Respuesta) {
|
|
11
|
+
// let URL = undefined;
|
|
12
|
+
// switch (process.env.ENV) {
|
|
13
|
+
// case 'desarrollo':
|
|
14
|
+
// URL = 'http://dgdh-oferentes-backend-desarrollo.181.193.85.44.nip.io';
|
|
15
|
+
// break;
|
|
16
|
+
// case 'calidad':
|
|
17
|
+
// URL = 'http://dgdh-oferentes-backend-calidad.181.193.85.44.nip.io';
|
|
18
|
+
// break;
|
|
19
|
+
// case 'pruebas':
|
|
20
|
+
// URL = 'http://dgdh-oferentes-backend-pruebas.181.193.85.44.nip.io';
|
|
21
|
+
// break;
|
|
22
|
+
// case 'production':
|
|
23
|
+
// URL = 'https://dgdh-oferentes-backend.sigu.utn.ac.cr';
|
|
24
|
+
// break;
|
|
25
|
+
// default:
|
|
26
|
+
// URL = 'http://localhost:81';
|
|
27
|
+
// }
|
|
28
|
+
// URL += '/api/DescargarArchivo';
|
|
29
|
+
// let resultado = await Miscelaneo.consumirBackend(URL, Datos, 'stream');
|
|
30
|
+
// Respuesta.setHeader('Content-Type', resultado.headers.get('content-type'));
|
|
31
|
+
// Respuesta.setHeader('Content-Disposition', resultado.headers.get('content-disposition'));
|
|
32
|
+
// const nodeStream = Readable.fromWeb(resultado.body);
|
|
33
|
+
// nodeStream.pipe(Respuesta);
|
|
34
|
+
// };
|
|
35
|
+
|
|
9
36
|
// async cursosPorPersonaAnioYPeriodo(Cuerpo) {
|
|
10
37
|
// let URL = undefined;
|
|
11
38
|
// switch (process.env.ENV) {
|
|
@@ -68,7 +68,7 @@ class Miscelaneo {
|
|
|
68
68
|
obtenerNombreCanonicoDelModulo() {
|
|
69
69
|
return this.NombreCanonicoDelModulo;
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
obtenerVersionDelModulo() {
|
|
73
73
|
return this.Version;
|
|
74
74
|
}
|
|
@@ -351,7 +351,7 @@ class Miscelaneo {
|
|
|
351
351
|
});
|
|
352
352
|
await ejecutarConsultaSIGU("INSERT INTO `SIGU`.`SIGU_SolicitudesDeRestablecimientoDeClave` VALUES (?, ?, ?, NOW(4), ?)"
|
|
353
353
|
, [CorreoElectronico[0]['Identificador'], Solicitud.body.Identificacion
|
|
354
|
-
|
|
354
|
+
, SolicitudTextual, LastUser]);
|
|
355
355
|
await envioDeCorreo(CorreoElectronico[0]['CorreoElectronico'], "Solicitud de restablecimiento de clave",
|
|
356
356
|
"<p>Estimada persona usuaria,<br /><br />"
|
|
357
357
|
+ "Se ha realizado una solicitud de cambio de clave para su cuenta de acceso a SIGU.</p>"
|
|
@@ -364,49 +364,222 @@ class Miscelaneo {
|
|
|
364
364
|
return;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
|
|
367
368
|
async Autenticar(Solicitud) {
|
|
368
369
|
const crypto = require('crypto');
|
|
369
370
|
const bcrypt = require('bcryptjs');
|
|
370
371
|
const jwt = require('jsonwebtoken');
|
|
371
372
|
const LastUser = await this.generarLastUser(Solicitud);
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
, [
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
373
|
+
const ConexionSigu = await crearObjetoConexionSIGU();
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
const resultados = await ConexionSigu.query("SELECT `Identificador`, `Clave` FROM `SIGU`.`SIGU_Personas` WHERE `Activo` = TRUE AND `Identificacion` = ?", [Solicitud.body.Identificacion]);
|
|
377
|
+
const Identificador = resultados[0][0]['Identificador'];
|
|
378
|
+
const Resultado = await bcrypt.compare(crypto.createHash('md5').update(Solicitud.body.Clave).digest("hex"), resultados[0][0]['Clave']);
|
|
379
|
+
|
|
380
|
+
if (Resultado) {
|
|
381
|
+
console.log("La clave brindada para el usuario", Solicitud.body.Identificacion, "coincide");
|
|
382
|
+
const Token = await jwt.sign({ Identificador: Identificador, uid: Identificador }, await this.palabraSecretaParaTokens(), { expiresIn: '2h' });
|
|
383
|
+
await ConexionSigu.query("INSERT INTO `SIGU`.`SIGU_Sesiones` VALUES (?, ?, ?, NOW(4), ?) ON DUPLICATE KEY UPDATE `Token` = ?, `LastUser` = ?", [Identificador, Solicitud.headers.host.trim(), Token, LastUser, Token, LastUser]
|
|
384
|
+
);
|
|
385
|
+
await ConexionSigu.query("DELETE FROM `SIGU`.`SIGU_SesionesFallidas` WHERE `Identificador` = ?", [Identificador]);
|
|
386
|
+
const permisos = await ConexionSigu.query("\
|
|
387
|
+
WITH RECURSIVE`ModulosJerarquia` AS( \
|
|
388
|
+
SELECT`Nombre`, `Padre` \
|
|
389
|
+
FROM`SIGU`.`SIGU_ModulosV2` \
|
|
390
|
+
WHERE`Nombre` COLLATE utf8mb4_spanish_ci IN(\
|
|
391
|
+
SELECT`Modulo` \
|
|
392
|
+
FROM`SIGU`.`SIGU_PermisosV2` \
|
|
393
|
+
WHERE`Nombre` LIKE '%Público%' \
|
|
394
|
+
) \
|
|
395
|
+
UNION ALL \
|
|
396
|
+
SELECT`m`.`Nombre`, `m`.`Padre` \
|
|
397
|
+
FROM`SIGU`.`SIGU_ModulosV2` `m` \
|
|
398
|
+
INNER JOIN`ModulosJerarquia` `mj` \
|
|
399
|
+
ON`mj`.`Padre` COLLATE utf8mb4_spanish_ci = \
|
|
400
|
+
`m`.`Nombre` COLLATE utf8mb4_spanish_ci \
|
|
401
|
+
) \
|
|
402
|
+
SELECT DISTINCT \
|
|
403
|
+
`p`.`PermisoId` \
|
|
404
|
+
FROM`ModulosJerarquia` `mj` \
|
|
405
|
+
JOIN`SIGU`.`SIGU_PermisosV2` `p` \
|
|
406
|
+
ON`p`.`Modulo` COLLATE utf8mb4_spanish_ci = \
|
|
407
|
+
`mj`.`Nombre` COLLATE utf8mb4_spanish_ci; \
|
|
408
|
+
");
|
|
409
|
+
|
|
410
|
+
for (const permiso of permisos[0]) {
|
|
411
|
+
await ConexionSigu.query(" \
|
|
412
|
+
INSERT INTO `SIGU`.`SIGU_PermisosPersonasV2` \
|
|
413
|
+
(`PermisoId`, `Identificador`, `LastUpdate`, `LastUser`) \
|
|
414
|
+
VALUES (?, ?, NOW(4), USER()) \
|
|
415
|
+
ON DUPLICATE KEY UPDATE \
|
|
416
|
+
`LastUser` = USER(), \
|
|
417
|
+
`LastUpdate` = NOW(4);", [permiso.PermisoId, Identificador]);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// OBTENER IP DEL USUARIO
|
|
421
|
+
const ipUsuario = (Solicitud.headers['x-forwarded-for'] || '').split(',').shift() || Solicitud.socket?.remoteAddress || Solicitud.connection?.remoteAddress || '-';
|
|
422
|
+
|
|
423
|
+
// VERIFICAR SI LA IP YA EXISTE
|
|
424
|
+
const ipExiste = await ConexionSigu.query("\
|
|
425
|
+
SELECT COUNT(*) AS Total \
|
|
426
|
+
FROM `SIGU`.`SIGU_DireccionesUsadasPorLosUsuarios` \
|
|
427
|
+
WHERE `Identificador` = ? AND `DireccionUsadaPorElUsuario` = ?", [Identificador, ipUsuario]);
|
|
428
|
+
if (ipExiste[0][0].Total === 0) {
|
|
429
|
+
console.log("IP nueva detectada para el usuario, iniciando 2FA");
|
|
430
|
+
|
|
431
|
+
// GENERAR CODIGO 2FA
|
|
432
|
+
let Codigo2FA = await ConexionSigu.query("SELECT UUID() AS `Dato`");
|
|
433
|
+
Codigo2FA = Codigo2FA[0][0].Dato;
|
|
434
|
+
|
|
435
|
+
// GUARDAR CODIGO 2FA
|
|
436
|
+
await ConexionSigu.query(" \
|
|
437
|
+
REPLACE INTO `SIGU`.`SIGU_CodigosDe2FAParaLosUsuarios` \
|
|
438
|
+
VALUES (?, ?, NOW(4), ?)", [
|
|
439
|
+
Identificador,
|
|
440
|
+
Codigo2FA,
|
|
441
|
+
LastUser
|
|
442
|
+
]);
|
|
443
|
+
|
|
444
|
+
// OBTENER CORREO
|
|
445
|
+
const CorreoElectronico = await ConexionSigu.query(
|
|
446
|
+
"SELECT `CorreoElectronico` \
|
|
447
|
+
FROM `SIGU`.`SIGU_CorreosPersona` \
|
|
448
|
+
WHERE `Identificador` = ? \
|
|
449
|
+
AND `Principal` = TRUE"
|
|
450
|
+
, [Identificador]);
|
|
451
|
+
|
|
452
|
+
// ENVIAR CORREO
|
|
453
|
+
console.log(CorreoElectronico[0][0].CorreoElectronico)
|
|
454
|
+
await envioDeCorreo(
|
|
455
|
+
CorreoElectronico[0][0].CorreoElectronico,
|
|
456
|
+
"Código de verificación 2FA",
|
|
457
|
+
"<p>Hemos recibido su solicitud de acceso para 2FA.</p>" +
|
|
458
|
+
"<p>Para continuar con el proceso, por favor utilice el siguiente código único de verificación:</p>" +
|
|
459
|
+
"<h2>" + Codigo2FA + "</h2>" +
|
|
460
|
+
"<p>Si usted no inició este proceso, puede ignorar este mensaje.</p>"
|
|
461
|
+
);
|
|
462
|
+
return { Requiere2FA: true };
|
|
463
|
+
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// SI LA IP YA EXISTE CONTINUA
|
|
467
|
+
await ConexionSigu.query("\
|
|
468
|
+
INSERT INTO `SIGU`.`SIGU_DireccionesUsadasPorLosUsuarios` \
|
|
469
|
+
(`DireccionUsadaPorElUsuario`, `Identificador`, `LastUpdate`, `LastUser`) \
|
|
470
|
+
VALUES (?, ?, NOW(4), ?) \
|
|
471
|
+
ON DUPLICATE KEY UPDATE `LastUpdate` = NOW(4), `LastUser` = ?;", [
|
|
472
|
+
ipUsuario,
|
|
473
|
+
Identificador,
|
|
474
|
+
LastUser,
|
|
475
|
+
LastUser
|
|
476
|
+
]);
|
|
477
|
+
return { Token, Dominio: ((process.env.ENV || 'local') === 'production' ? '.sigu.utn.ac.cr' : '.181.193.85.44.nip.io') };
|
|
478
|
+
|
|
402
479
|
} else {
|
|
403
|
-
|
|
404
|
-
|
|
480
|
+
console.log("La clave brindada para el usuario", Solicitud.body.Identificacion, "no conincide");
|
|
481
|
+
|
|
482
|
+
const Resultados2 = await ConexionSigu.query("SELECT COUNT(*) AS `Total` FROM `SIGU`.`SIGU_ClavesTemporalesDeLasPersonas` WHERE `Identificador` = ? AND `Clave` = ?", [Identificador, Solicitud.body.Clave]);
|
|
483
|
+
|
|
484
|
+
if (Resultados2[0][0]['Total'] > 0) {
|
|
485
|
+
console.log("La clave brindada para el usuario", Solicitud.body.Identificacion, "no conincide, pero coincide la clave temporal");
|
|
486
|
+
await ConexionSigu.query("UPDATE `SIGU`.`SIGU_Personas` SET `Clave` = ?, `LastUpdate` = NOW(4), `LastUser` = ? WHERE `Identificacion` = ?"
|
|
487
|
+
, [await bcrypt.hash(require('crypto').createHash('md5').update(Solicitud.body.Clave).digest("hex"), 10), LastUser, Solicitud.body.Identificacion]);
|
|
488
|
+
|
|
489
|
+
await ConexionSigu.query("DELETE FROM `SIGU`.`SIGU_ClavesTemporalesDeLasPersonas`WHERE `Identificador` = ?", [Identificador]);
|
|
490
|
+
return await this.Autenticar(Solicitud);
|
|
491
|
+
|
|
492
|
+
} else {
|
|
493
|
+
await ConexionSigu.query("INSERT INTO `SIGU`.`SIGU_SesionesFallidas` VALUES (?, ?, NOW(4))"
|
|
494
|
+
, [Identificador, Solicitud.headers.host.trim()]);
|
|
495
|
+
}
|
|
405
496
|
}
|
|
497
|
+
} catch (error) {
|
|
498
|
+
console.log(error);
|
|
499
|
+
return;
|
|
500
|
+
} finally {
|
|
501
|
+
if (ConexionSigu) await ConexionSigu.end();
|
|
406
502
|
}
|
|
407
503
|
return;
|
|
408
504
|
}
|
|
409
505
|
|
|
506
|
+
async Verificar2FA(Solicitud) {
|
|
507
|
+
const jwt = require('jsonwebtoken');
|
|
508
|
+
const ConexionSigu = await crearObjetoConexionSIGU();
|
|
509
|
+
const LastUser = await this.generarLastUser(Solicitud);
|
|
510
|
+
try {
|
|
511
|
+
const { Identificacion, Codigo } = Solicitud.body;
|
|
512
|
+
const resultados = await ConexionSigu.query(
|
|
513
|
+
"SELECT `Identificador` FROM `SIGU`.`SIGU_Personas` WHERE `Activo` = TRUE AND `Identificacion` = ?", [Identificacion]);
|
|
514
|
+
|
|
515
|
+
if (!resultados[0].length) {
|
|
516
|
+
return { error: "Usuario no encontrado" };
|
|
517
|
+
}
|
|
518
|
+
const Identificador = resultados[0][0]['Identificador'];
|
|
519
|
+
// Validar código 2FA
|
|
520
|
+
const codigoValido = await ConexionSigu.query(" \
|
|
521
|
+
SELECT COUNT(*) AS Total \
|
|
522
|
+
FROM `SIGU`.`SIGU_CodigosDe2FAParaLosUsuarios` \
|
|
523
|
+
WHERE `Identificador` = ? \
|
|
524
|
+
AND `CodigoDe2FAParaElUsuario` = ?", [Identificador, Codigo]);
|
|
525
|
+
|
|
526
|
+
if (codigoValido[0][0].Total === 0) {
|
|
527
|
+
// Eliminar código usado cuando falle
|
|
528
|
+
await ConexionSigu.query(" \
|
|
529
|
+
DELETE FROM `SIGU`.`SIGU_CodigosDe2FAParaLosUsuarios` \
|
|
530
|
+
WHERE `Identificador` = ? ", [Identificador]);
|
|
531
|
+
return { error: "Código inválido" };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Eliminar código usado
|
|
535
|
+
await ConexionSigu.query(" \
|
|
536
|
+
DELETE FROM `SIGU`.`SIGU_CodigosDe2FAParaLosUsuarios` WHERE `Identificador` = ?", [Identificador]);
|
|
537
|
+
|
|
538
|
+
// Obtener IP del usuario
|
|
539
|
+
const ipUsuario = (Solicitud.headers['x-forwarded-for'] || '').split(',').shift() || Solicitud.socket?.remoteAddress || Solicitud.connection?.remoteAddress || '-';
|
|
540
|
+
|
|
541
|
+
// Guardar IP autorizada
|
|
542
|
+
await ConexionSigu.query(" \
|
|
543
|
+
INSERT INTO `SIGU`.`SIGU_DireccionesUsadasPorLosUsuarios` (`DireccionUsadaPorElUsuario`, `Identificador`, `LastUpdate`, `LastUser`) \
|
|
544
|
+
VALUES (?, ?, NOW(4), ?) \
|
|
545
|
+
ON DUPLICATE KEY UPDATE `LastUpdate` = NOW(4), `LastUser` = ?;", [
|
|
546
|
+
ipUsuario,
|
|
547
|
+
Identificador,
|
|
548
|
+
LastUser,
|
|
549
|
+
LastUser
|
|
550
|
+
]);
|
|
551
|
+
|
|
552
|
+
// Generar token
|
|
553
|
+
const Token = await jwt.sign(
|
|
554
|
+
{ Identificador: Identificador, uid: Identificador },
|
|
555
|
+
await this.palabraSecretaParaTokens(),
|
|
556
|
+
{ expiresIn: '2h' }
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
// Registrar sesión
|
|
560
|
+
await ConexionSigu.query(" \
|
|
561
|
+
INSERT INTO `SIGU`.`SIGU_Sesiones` \
|
|
562
|
+
VALUES (?, ?, ?, NOW(4), ?) \
|
|
563
|
+
ON DUPLICATE KEY UPDATE `Token` = ?, `LastUser` = ?", [
|
|
564
|
+
Identificador,
|
|
565
|
+
Solicitud.headers.host.trim(),
|
|
566
|
+
Token,
|
|
567
|
+
LastUser,
|
|
568
|
+
Token,
|
|
569
|
+
LastUser
|
|
570
|
+
]);
|
|
571
|
+
return {
|
|
572
|
+
Token,
|
|
573
|
+
Dominio: ((process.env.ENV || 'local') === 'production' ? '.sigu.utn.ac.cr' : '.181.193.85.44.nip.io')
|
|
574
|
+
};
|
|
575
|
+
} catch (error) {
|
|
576
|
+
console.log(error);
|
|
577
|
+
return { error: "Error verificando 2FA" };
|
|
578
|
+
} finally {
|
|
579
|
+
if (ConexionSigu) await ConexionSigu.end();
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
410
583
|
async ListadoDePaisesParaCrearCuenta() {
|
|
411
584
|
const Resultado = await ejecutarConsultaSIGU("SELECT REPLACE(MID(`COLUMN_TYPE`, 6, CHAR_LENGTH(`COLUMN_TYPE`) - 6), \"'\", '') AS `Datos` FROM `information_schema`.`COLUMNS`\
|
|
412
585
|
WHERE `TABLE_SCHEMA` = 'SIGU' AND `TABLE_NAME` = 'SIGU_Personas' AND\
|
|
@@ -538,11 +711,21 @@ class Miscelaneo {
|
|
|
538
711
|
+ "<p><b>Detalle de la incidencia: </b>" + Datos.detalle + "</p><br />"
|
|
539
712
|
+ "<p><b>Resultado esperado: </b>" + Datos.resultado + "</p><br />"
|
|
540
713
|
+ "<p><b>Información de contacto: </b>" + Datos.concato + "</p><br />"
|
|
541
|
-
+ "<p><b>Información del usuario: </b>" + await this.
|
|
714
|
+
+ "<p><b>Información del usuario: </b>" + await this.obtenerDatosDelUsuario(Solicitud.headers.authorization) + "</p><br />"
|
|
542
715
|
, [DatosDelArchivo.rutaDeArchivo]);
|
|
543
716
|
return;
|
|
544
717
|
}
|
|
545
718
|
|
|
719
|
+
async reporteDeSugerencia(Solicitud, Datos) {
|
|
720
|
+
// const DatosDelArchivo = await this.cargarArchivo(Solicitud, Datos);
|
|
721
|
+
await envioDeCorreo('msavatar@utn.ac.cr', "Reporte de sugerencia",
|
|
722
|
+
"<p><b>Sistema: </b>" + this.NombreCanonicoDelModulo + "</p><br />"
|
|
723
|
+
+ "<p><b>Asunto: </b>Reporte de sugerencia</p><br />"
|
|
724
|
+
+ "<p><b>Detalle de la sugerencia: </b>" + Datos.detalle + "</p><br />"
|
|
725
|
+
+ "<p><b>Información del usuario: </b>" + await this.obtenerDatosDelUsuario(Solicitud.headers.authorization) + "</p><br />");
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
546
729
|
async cerrarSesion(Token) {
|
|
547
730
|
let Resultado = undefined;
|
|
548
731
|
try {
|
|
@@ -1058,7 +1241,7 @@ class Miscelaneo {
|
|
|
1058
1241
|
if (!Resultado) {
|
|
1059
1242
|
throw new ManejadorDeErrores(ManejadorDeErrores.mensajeDeErrorVerificacionDeToken(), ManejadorDeErrores.obtenerNumeroDeLinea(), encabezadoDeAutorizacion);
|
|
1060
1243
|
}
|
|
1061
|
-
Resultado.token = token;
|
|
1244
|
+
// Resultado.token = token;
|
|
1062
1245
|
const DatosDeLaPersona = await ejecutarConsultaSIGU("SELECT `Identificacion`, `Nombre`, `PrimerApellido`,\
|
|
1063
1246
|
`SegundoApellido`, CONCAT(`FechaNacimiento`) AS `FechaDeNacimiento`\
|
|
1064
1247
|
FROM `SIGU`.`SIGU_Personas` WHERE `Identificador` = ?", [Resultado.uid]);
|
|
@@ -25,15 +25,10 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
|
25
25
|
|
|
26
26
|
export class ReporteDeIncidenciasComponent implements OnInit {
|
|
27
27
|
formulario!: FormGroup;
|
|
28
|
-
|
|
29
28
|
archivos: File[] = [];
|
|
30
|
-
|
|
31
29
|
readonly dialogRef = inject(MatDialogRef<ReporteDeIncidenciasComponent>);
|
|
32
|
-
|
|
33
30
|
constructor(private fb: FormBuilder, private datosGlobalesService: DatosGlobalesService, private http: HttpClient) { }
|
|
34
|
-
|
|
35
31
|
@ViewChild('EntradDeArchivo') EntradDeArchivo!: ElementRef<HTMLInputElement>;
|
|
36
|
-
|
|
37
32
|
Archivo: any;
|
|
38
33
|
|
|
39
34
|
ngOnInit(): void {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.contenedor {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.fila {
|
|
7
|
+
width: 100%;
|
|
8
|
+
min-width: 500px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.campo {
|
|
12
|
+
width: 100%;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.zona-archivo {
|
|
16
|
+
width: 90%;
|
|
17
|
+
height: 10vh;
|
|
18
|
+
margin-left: 4%;
|
|
19
|
+
border: 2px dashed #3498db;
|
|
20
|
+
padding: 1%;
|
|
21
|
+
align-items: center;
|
|
22
|
+
display: flex;
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
transition: background 0.3s;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.texto {
|
|
28
|
+
width: 100%;
|
|
29
|
+
text-align: center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.texto-clic {
|
|
33
|
+
color: #007bff;
|
|
34
|
+
font-weight: bold;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
text-decoration: underline;
|
|
37
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<h2 mat-dialog-title>Reporte de la sugerencia</h2>
|
|
2
|
+
<mat-dialog-content>
|
|
3
|
+
<div class="contenedor">
|
|
4
|
+
<form [formGroup]="formulario">
|
|
5
|
+
<div class="fila">
|
|
6
|
+
<mat-form-field class="campo">
|
|
7
|
+
<mat-label>Detalle de la sugerencia</mat-label>
|
|
8
|
+
<textarea matInput formControlName="detalle" required></textarea>
|
|
9
|
+
</mat-form-field>
|
|
10
|
+
</div>
|
|
11
|
+
</form>
|
|
12
|
+
</div>
|
|
13
|
+
</mat-dialog-content>
|
|
14
|
+
<mat-dialog-actions>
|
|
15
|
+
<button mat-button (click)="Cerrar()">Cerrar</button>
|
|
16
|
+
<button mat-button (click)="Enviar()">Enviar</button>
|
|
17
|
+
</mat-dialog-actions>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core';
|
|
2
|
+
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
3
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
4
|
+
import { MatInputModule } from '@angular/material/input';
|
|
5
|
+
import { ReactiveFormsModule } from '@angular/forms';
|
|
6
|
+
import { MatDialogActions, MatDialogContent, MatDialogRef, MatDialogTitle } from '@angular/material/dialog';
|
|
7
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
8
|
+
import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
9
|
+
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
10
|
+
|
|
11
|
+
@Component({
|
|
12
|
+
selector: 'app-reporte-de-sugerencias',
|
|
13
|
+
templateUrl: './reporte-de-sugerencias.component.html',
|
|
14
|
+
styleUrls: ['./reporte-de-sugerencias.component.css'],
|
|
15
|
+
imports: [
|
|
16
|
+
MatFormFieldModule,
|
|
17
|
+
MatInputModule,
|
|
18
|
+
ReactiveFormsModule,
|
|
19
|
+
MatDialogContent,
|
|
20
|
+
MatButtonModule,
|
|
21
|
+
MatDialogTitle,
|
|
22
|
+
MatDialogActions
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export class ReporteDeSugerenciasComponent implements OnInit {
|
|
27
|
+
formulario!: FormGroup;
|
|
28
|
+
readonly dialogRef = inject(MatDialogRef<ReporteDeSugerenciasComponent>);
|
|
29
|
+
constructor(private fb: FormBuilder, private datosGlobalesService: DatosGlobalesService, private http: HttpClient) { }
|
|
30
|
+
|
|
31
|
+
ngOnInit(): void {
|
|
32
|
+
this.formulario = this.fb.group({
|
|
33
|
+
detalle: ['', Validators.required]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Cerrar(): void {
|
|
38
|
+
this.dialogRef.close();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Enviar() {
|
|
42
|
+
const Datos = JSON.stringify(this.formulario.value);
|
|
43
|
+
this.http.post(this.datosGlobalesService.ObtenerURL() + 'misc/reporteDeSugerencia/' + Datos, {})
|
|
44
|
+
.subscribe({
|
|
45
|
+
next: (data: any) => {
|
|
46
|
+
alert('Mensaje enviado');
|
|
47
|
+
},
|
|
48
|
+
error: (error) => {
|
|
49
|
+
console.error('Ocurrió un error al informar de la sugerencia:', error);
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
this.Cerrar();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
<ng-container matColumnDef="expand">
|
|
97
97
|
<th mat-header-cell *matHeaderCellDef aria-label="Fila expandible"></th>
|
|
98
98
|
<td mat-cell *matCellDef="let element">
|
|
99
|
-
<button mat-icon-button aria-label="
|
|
99
|
+
<button mat-icon-button aria-label="Expandir fila"
|
|
100
100
|
(click)="(elementoExpandible = elementoExpandible === element ? null : element); $event.stopPropagation()">
|
|
101
101
|
@if (elementoExpandible === element) {
|
|
102
102
|
<mat-icon>keyboard_arrow_up</mat-icon>
|
|
@@ -85,6 +85,9 @@
|
|
|
85
85
|
<button class="botonDeNavegacion" matTooltip="Reporte" mat-button (click)="irASoporte()">
|
|
86
86
|
<mat-icon>support_agent</mat-icon>
|
|
87
87
|
</button>
|
|
88
|
+
<button class="botonDeNavegacion" matTooltip="Sugerencias" mat-button (click)="irASugerencias()">
|
|
89
|
+
<mat-icon>lightbulb</mat-icon>
|
|
90
|
+
</button>
|
|
88
91
|
</div>
|
|
89
92
|
</div>
|
|
90
93
|
</div>
|
|
@@ -6,9 +6,10 @@ import { Location, CommonModule } from '@angular/common';
|
|
|
6
6
|
import { MatIconModule } from '@angular/material/icon';
|
|
7
7
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
8
8
|
import { MatDialog } from '@angular/material/dialog';
|
|
9
|
-
import { ReporteDeIncidenciasComponent } from '../../../Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component'
|
|
9
|
+
import { ReporteDeIncidenciasComponent } from '../../../Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component';
|
|
10
10
|
import { MensajesComponent } from '../../../Componentes/Nucleo/mensajes/mensajes.component';
|
|
11
11
|
import { MensajeConfirmacionHTMLComponent } from '../../../Componentes/Nucleo/mensaje-confirmacion-html/mensaje-confirmacion-html';
|
|
12
|
+
import { ReporteDeSugerenciasComponent } from '../../../Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component';
|
|
12
13
|
|
|
13
14
|
@Component({
|
|
14
15
|
selector: 'app-contenedor-componentes',
|
|
@@ -189,4 +190,8 @@ export class ContenedorComponentesComponent {
|
|
|
189
190
|
window.location.href = datos.body;
|
|
190
191
|
})
|
|
191
192
|
}
|
|
193
|
+
|
|
194
|
+
irASugerencias(): void {
|
|
195
|
+
this.dialog.open(ReporteDeSugerenciasComponent);
|
|
196
|
+
}
|
|
192
197
|
}
|