Compare commits
6 Commits
f4a32af956
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3195b8ddb0 | ||
|
|
c8105bae78 | ||
|
|
466aa891c3 | ||
|
|
cb06e37f60 | ||
|
|
61c4557578 | ||
|
|
a055a0bbef |
76
.gitignore
vendored
Normal file
76
.gitignore
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development
|
||||
.env.test
|
||||
.env.production
|
||||
|
||||
# Cache and temporary files
|
||||
cache/
|
||||
*.enc
|
||||
.cache/
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# IDE and Editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# Build and distribution
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
*.tgz
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
.nyc_output/
|
||||
*.lcov
|
||||
|
||||
# Docker
|
||||
.dockerignore
|
||||
docker-compose.override.yml
|
||||
|
||||
# Miscellaneous
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
.npm
|
||||
.eslintcache
|
||||
.node_repl_history
|
||||
.yarn-integrity
|
||||
@@ -13,10 +13,11 @@ Este endpoint es consumido por el cliente (tienda) para saber si tiene algún co
|
||||
- **Ruta**: `/commands`
|
||||
- **Parámetros (Query)**:
|
||||
- `store_id`: ID de la tienda (Requerido).
|
||||
- `id_cliente`: ID del cliente (Opcional).
|
||||
|
||||
**Ejemplo de Solicitud**:
|
||||
```http
|
||||
GET /v1/commands?store_id=44
|
||||
GET /v1/commands?store_id=44&id_cliente=1
|
||||
```
|
||||
|
||||
**Respuestas**:
|
||||
@@ -43,6 +44,7 @@ Permite a un administrador o sistema central poner en cola una instrucción SQL
|
||||
- `sql` (String): Sentencia SQL a ejecutar (Requerido).
|
||||
- `privilegio` (String): Nivel de permiso, por defecto "READ". Opcional.
|
||||
- `id_user` (Number): ID del usuario que solicita el comando. Opcional.
|
||||
- `id_cliente` (Number): ID del cliente. Opcional.
|
||||
|
||||
**Ejemplo de Solicitud**:
|
||||
```json
|
||||
@@ -50,7 +52,8 @@ Permite a un administrador o sistema central poner en cola una instrucción SQL
|
||||
"store_id": 44,
|
||||
"sql": "SELECT count(*) FROM ventas",
|
||||
"privilegio": "READ",
|
||||
"id_user": 1
|
||||
"id_user": 1,
|
||||
"id_cliente": 1
|
||||
}
|
||||
```
|
||||
|
||||
@@ -151,7 +154,77 @@ GET /v1/commands/user/1?limit=5
|
||||
"fecha_solicitud": "2026-01-13T18:55:00.000Z",
|
||||
"fecha_proceso": "2026-01-13T19:00:00.000Z",
|
||||
"json_response": [ { "count": 150 } ]
|
||||
},
|
||||
...
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Estado de Tiendas (Heartbeat)
|
||||
Devuelve el estado de conectividad de las tiendas basado en su último heartbeat.
|
||||
|
||||
- **Método**: `GET`
|
||||
- **Ruta**: `/stores/status`
|
||||
- **Parámetros (Query)**:
|
||||
- `filter` (String): Filtrar por estado de conectividad. Valores: `online`, `offline`, `all` (Default: `all`).
|
||||
- `id_cliente` (Number): Filtrar por ID de cliente. Opcional.
|
||||
|
||||
**Estados de Conectividad**:
|
||||
- **ONLINE**: Último ping hace menos de 2 minutos (120 segundos)
|
||||
- **DEGRADED**: Último ping entre 2 y 5 minutos (120-300 segundos)
|
||||
- **OFFLINE**: Último ping hace más de 5 minutos (>300 segundos)
|
||||
|
||||
**Ejemplo de Solicitud**:
|
||||
```http
|
||||
GET /v1/stores/status?filter=online
|
||||
GET /v1/stores/status?filter=all&id_cliente=1
|
||||
```
|
||||
|
||||
**Respuesta Exitosa (200 OK)**:
|
||||
```json
|
||||
{
|
||||
"total": 2,
|
||||
"filter_applied": "online",
|
||||
"stores": [
|
||||
{
|
||||
"store_id": 44,
|
||||
"id_cliente": 1,
|
||||
"instance_id": "sql_inyector",
|
||||
"connectivity": "ONLINE",
|
||||
"status": "alive",
|
||||
"last_ping": "2026-02-09T18:15:30.000Z",
|
||||
"seconds_since_ping": 45,
|
||||
"extra_info": {
|
||||
"message": "Consultando comandos",
|
||||
"timestamp": "2026-02-09T18:15:30.000Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"store_id": 370,
|
||||
"id_cliente": 1,
|
||||
"instance_id": "sql_inyector",
|
||||
"connectivity": "OFFLINE",
|
||||
"status": "alive",
|
||||
"last_ping": "2026-02-09T17:50:00.000Z",
|
||||
"seconds_since_ping": 1530,
|
||||
"extra_info": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Notas Importantes**:
|
||||
- Si una tienda **nunca ha enviado un heartbeat**, NO aparecerá en la respuesta de este endpoint.
|
||||
- Solo se muestran tiendas que tienen al menos un registro en `tb_store_process_heartbeat` con `process_name = 'sql_inyector'`.
|
||||
- Para ver tiendas que nunca se han conectado, necesitarías tener una tabla maestra de tiendas y hacer un LEFT JOIN.
|
||||
|
||||
---
|
||||
|
||||
## Códigos de Estado HTTP
|
||||
|
||||
- **200 OK**: Solicitud exitosa
|
||||
- **201 Created**: Recurso creado exitosamente
|
||||
- **204 No Content**: Solicitud exitosa sin contenido
|
||||
- **400 Bad Request**: Parámetros inválidos o faltantes
|
||||
- **404 Not Found**: Recurso no encontrado
|
||||
- **500 Internal Server Error**: Error del servidor
|
||||
|
||||
2
cache/56d162256c40f16086726e9b9c7cdb5c.enc
vendored
2
cache/56d162256c40f16086726e9b9c7cdb5c.enc
vendored
@@ -1 +1 @@
|
||||
U4htzC2uiastLmSNlF35Qp+TVh/tZrvoCCfDzzkiYbvSGCsv3oKdYHD3c70fQF2pAoXR+HhlSqQyiF7VgHHGRQK34YXxwP7yEpkQYFLCYSdImLT/zfMmgte/J9+kj0ysNwiOYMmkOrsVhzW60Tc1IdZcOyqk310oZ9GCi7QsIkS+Ra8M9mpCiYxF2GUErLY3m6FsGbwwTXhZer3bH+eIKR8VixGdn1xVr8pYnPYpN5CBfyMjgOFOYF3cUM/oNmNNA0FdP5kj8IEJyzsgkDHI3hUbW7U1PUwhTRJsRT4i7SxniqJlt6TSHcvJxCC2MuazkWrRtESFfp3S2LKG67GPt0yLWjUxuK1iQB+Ys4xgoe3VtmqFMJOJsExpT6a3ooNFACIYYiRDk4ezgpswMvM0A7pWZFZ05t5pA6WLk37+6Lq6Ol350+Vfs2inBAFlo9mY+rzMBV7jQ4RCP6UCLUeuYG0QbMUQ3DcGQEd2QAnTNTc=
|
||||
H5+Ql2SGk0pf83y/3uR6SGZIJVfDBngT2Gr8SrrJlX29j/NykCSK15CK0GMnTTDeP7VD4lU4h1Piwnabqw69OJYdfTLpQvVoIKSnv+osYRQG1FpPRcOyjdH/iRy8hwR28CJ4vBpYoxEZY0uqKimVkUIseMe94lVMTQvM6+JWfmbFV8Ogkzuvge8jscw6KjUE22HK9k4bgShw/P0dlvPiBp7Sd89isXKe62WHpvd8ozeX6u0g9tFvT6SWr2HEmIod0yNM6ScCXpjvB2iqiV3MLM3Ue9axleXPdvhO04A+rMXwbSeFT9O987wTFuo3xZp3/NU3gqbpDU7jGEArYXyEmGU8NVOGD/9swH8hsdboV0KRjltS4witcK5KdiEhV9aZvwTZHRJ0ktv8XMqt8BlhDoZCfpvUnk48mHZfX14/wiEEx1SSQRi1td2VDnAKBBp5ZBIK35oUmKhVnlPXrj9/xm0vGB1TO6nJ0jB9tlntWmg=
|
||||
140
node_modules/console-control-strings/README.md~
generated
vendored
140
node_modules/console-control-strings/README.md~
generated
vendored
@@ -1,140 +0,0 @@
|
||||
# Console Control Strings
|
||||
|
||||
A library of cross-platform tested terminal/console command strings for
|
||||
doing things like color and cursor positioning. This is a subset of both
|
||||
ansi and vt100. All control codes included work on both Windows & Unix-like
|
||||
OSes, except where noted.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
var consoleControl = require('console-control-strings')
|
||||
|
||||
console.log(consoleControl.color('blue','bgRed', 'bold') + 'hi there' + consoleControl.color('reset'))
|
||||
process.stdout.write(consoleControl.goto(75, 10))
|
||||
```
|
||||
|
||||
## Why Another?
|
||||
|
||||
There are tons of libraries similar to this one. I wanted one that was:
|
||||
|
||||
1. Very clear about compatibility goals.
|
||||
2. Could emit, for instance, a start color code without an end one.
|
||||
3. Returned strings w/o writing to streams.
|
||||
4. Was not weighed down with other unrelated baggage.
|
||||
|
||||
## Functions
|
||||
|
||||
### var code = consoleControl.up(_num = 1_)
|
||||
|
||||
Returns the escape sequence to move _num_ lines up.
|
||||
|
||||
### var code = consoleControl.down(_num = 1_)
|
||||
|
||||
Returns the escape sequence to move _num_ lines down.
|
||||
|
||||
### var code = consoleControl.forward(_num = 1_)
|
||||
|
||||
Returns the escape sequence to move _num_ lines righ.
|
||||
|
||||
### var code = consoleControl.back(_num = 1_)
|
||||
|
||||
Returns the escape sequence to move _num_ lines left.
|
||||
|
||||
### var code = consoleControl.nextLine(_num = 1_)
|
||||
|
||||
Returns the escape sequence to move _num_ lines down and to the beginning of
|
||||
the line.
|
||||
|
||||
### var code = consoleControl.previousLine(_num = 1_)
|
||||
|
||||
Returns the escape sequence to move _num_ lines up and to the beginning of
|
||||
the line.
|
||||
|
||||
### var code = consoleControl.eraseData()
|
||||
|
||||
Returns the escape sequence to erase everything from the current cursor
|
||||
position to the bottom right of the screen. This is line based, so it
|
||||
erases the remainder of the current line and all following lines.
|
||||
|
||||
### var code = consoleControl.eraseLine()
|
||||
|
||||
Returns the escape sequence to erase to the end of the current line.
|
||||
|
||||
### var code = consoleControl.goto(_x_, _y_)
|
||||
|
||||
Returns the escape sequence to move the cursor to the designated position.
|
||||
Note that the origin is _1, 1_ not _0, 0_.
|
||||
|
||||
### var code = consoleControl.gotoSOL()
|
||||
|
||||
Returns the escape sequence to move the cursor to the beginning of the
|
||||
current line. (That is, it returns a carriage return, `\r`.)
|
||||
|
||||
### var code = consoleControl.hideCursor()
|
||||
|
||||
Returns the escape sequence to hide the cursor.
|
||||
|
||||
### var code = consoleControl.showCursor()
|
||||
|
||||
Returns the escape sequence to show the cursor.
|
||||
|
||||
### var code = consoleControl.color(_colors = []_)
|
||||
|
||||
### var code = consoleControl.color(_color1_, _color2_, _…_, _colorn_)
|
||||
|
||||
Returns the escape sequence to set the current terminal display attributes
|
||||
(mostly colors). Arguments can either be a list of attributes or an array
|
||||
of attributes. The difference between passing in an array or list of colors
|
||||
and calling `.color` separately for each one, is that in the former case a
|
||||
single escape sequence will be produced where as in the latter each change
|
||||
will have its own distinct escape sequence. Each attribute can be one of:
|
||||
|
||||
* Reset:
|
||||
* **reset** – Reset all attributes to the terminal default.
|
||||
* Styles:
|
||||
* **bold** – Display text as bold. In some terminals this means using a
|
||||
bold font, in others this means changing the color. In some it means
|
||||
both.
|
||||
* **italic** – Display text as italic. This is not available in most Windows terminals.
|
||||
* **underline** – Underline text. This is not available in most Windows Terminals.
|
||||
* **inverse** – Invert the foreground and background colors.
|
||||
* **stopBold** – Do not display text as bold.
|
||||
* **stopItalic** – Do not display text as italic.
|
||||
* **stopUnderline** – Do not underline text.
|
||||
* **stopInverse** – Do not invert foreground and background.
|
||||
* Colors:
|
||||
* **white**
|
||||
* **black**
|
||||
* **blue**
|
||||
* **cyan**
|
||||
* **green**
|
||||
* **magenta**
|
||||
* **red**
|
||||
* **yellow**
|
||||
* **grey** / **brightBlack**
|
||||
* **brightRed**
|
||||
* **brightGreen**
|
||||
* **brightYellow**
|
||||
* **brightBlue**
|
||||
* **brightMagenta**
|
||||
* **brightCyan**
|
||||
* **brightWhite**
|
||||
* Background Colors:
|
||||
* **bgWhite**
|
||||
* **bgBlack**
|
||||
* **bgBlue**
|
||||
* **bgCyan**
|
||||
* **bgGreen**
|
||||
* **bgMagenta**
|
||||
* **bgRed**
|
||||
* **bgYellow**
|
||||
* **bgGrey** / **bgBrightBlack**
|
||||
* **bgBrightRed**
|
||||
* **bgBrightGreen**
|
||||
* **bgBrightYellow**
|
||||
* **bgBrightBlue**
|
||||
* **bgBrightMagenta**
|
||||
* **bgBrightCyan**
|
||||
* **bgBrightWhite**
|
||||
|
||||
@@ -32,7 +32,7 @@ async function autentificacion(req, res) {
|
||||
|
||||
// Consulta a la nueva tabla soporte_copy en dbasistente
|
||||
const [db_user] = await dbasistente.query(
|
||||
"SELECT CODIGO_SOP, NOMBRE_SOP, USUARIO_SOP, NIVEL_SOP FROM soporte_copy WHERE USUARIO_SOP = ? AND CLAVE_SOP = ? AND ESTADO_SOP = 'V'",
|
||||
"SELECT CODIGO_SOP, NOMBRE_SOP, USUARIO_SOP, NIVEL_SOP FROM soporte_copy WHERE CORREO_SOP = ? AND CLAVE_SOP = ? AND ESTADO_SOP = 'V'",
|
||||
{
|
||||
replacements: [username, password]
|
||||
}
|
||||
|
||||
@@ -248,24 +248,71 @@ async function get_commands_by_user(req, res, dbcentral) {
|
||||
|
||||
async function get_online_stores(req, res, dbcentral) {
|
||||
try {
|
||||
const filter = req.query.filter; // 'online', 'offline', 'all' (default: 'all')
|
||||
const id_cliente = req.query.id_cliente; // Filtro opcional por cliente
|
||||
|
||||
// Construir la consulta base
|
||||
let whereClause = "WHERE h.process_name = 'sql_inyector'";
|
||||
const replacements = [];
|
||||
|
||||
// Filtrar por cliente si se proporciona
|
||||
if (id_cliente) {
|
||||
whereClause += " AND h.id_cliente = ?";
|
||||
replacements.push(id_cliente);
|
||||
}
|
||||
|
||||
// Obtenemos los locales y su último ping
|
||||
// Consideramos "Online" si el ping fue hace menos de 2 minutos
|
||||
// Consideramos "ONLINE" si el ping fue hace menos de 2 minutos
|
||||
const [results] = await dbcentral.query(
|
||||
`SELECT
|
||||
h.store_id,
|
||||
h.id_cliente,
|
||||
h.instance_id,
|
||||
h.last_ping,
|
||||
h.status,
|
||||
IF(TIMESTAMPDIFF(SECOND, h.last_ping, CURRENT_TIMESTAMP) <= 120, 'ONLINE', 'OFFLINE') as connectivity
|
||||
h.extra_info,
|
||||
TIMESTAMPDIFF(SECOND, h.last_ping, CURRENT_TIMESTAMP) as seconds_since_ping,
|
||||
CASE
|
||||
WHEN TIMESTAMPDIFF(SECOND, h.last_ping, CURRENT_TIMESTAMP) <= 120 THEN 'ONLINE'
|
||||
WHEN TIMESTAMPDIFF(SECOND, h.last_ping, CURRENT_TIMESTAMP) <= 300 THEN 'DEGRADED'
|
||||
ELSE 'OFFLINE'
|
||||
END as connectivity
|
||||
FROM tb_store_process_heartbeat h
|
||||
WHERE h.process_name = 'sql_inyector'
|
||||
ORDER BY h.last_ping DESC`
|
||||
${whereClause}
|
||||
ORDER BY h.last_ping DESC`,
|
||||
{ replacements }
|
||||
);
|
||||
|
||||
res.status(200).json(results);
|
||||
// Aplicar filtro de conectividad si se especifica
|
||||
let filteredResults = results;
|
||||
if (filter === 'online') {
|
||||
filteredResults = results.filter(r => r.connectivity === 'ONLINE');
|
||||
} else if (filter === 'offline') {
|
||||
filteredResults = results.filter(r => r.connectivity === 'OFFLINE' || r.connectivity === 'DEGRADED');
|
||||
}
|
||||
|
||||
// Formatear la respuesta
|
||||
const formattedResults = filteredResults.map(store => ({
|
||||
store_id: store.store_id,
|
||||
id_cliente: store.id_cliente,
|
||||
instance_id: store.instance_id,
|
||||
connectivity: store.connectivity,
|
||||
status: store.status,
|
||||
last_ping: store.last_ping,
|
||||
seconds_since_ping: store.seconds_since_ping,
|
||||
extra_info: store.extra_info
|
||||
}));
|
||||
|
||||
res.status(200).json({
|
||||
total: formattedResults.length,
|
||||
filter_applied: filter || 'all',
|
||||
stores: formattedResults
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Error al obtener tiendas online:", error.message);
|
||||
res.status(500).json({
|
||||
error: "Error interno del servidor"
|
||||
error: "Error interno del servidor",
|
||||
message: process.env.NODE_ENV === 'development' ? error.message : 'Error interno'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import dotenv from 'dotenv';
|
||||
const solicitud = {
|
||||
data: [
|
||||
{ dbmane: 'aplicaciones' },
|
||||
{ dbmane: 'asistente_qa' }
|
||||
{ dbmane: 'sial_help' }
|
||||
]
|
||||
};
|
||||
let configPromise = null;
|
||||
@@ -20,8 +20,7 @@ dotenv.config({ path: '../.env' });
|
||||
dotenv.config();
|
||||
const config = await getConfig();
|
||||
const dablog = config.aplicaciones;
|
||||
const dabasistente = config.asistente_qa;
|
||||
|
||||
const dabasistente = config.sial_help;
|
||||
const dbcentral = new Sequelize(
|
||||
dablog.name,
|
||||
dablog.username,
|
||||
|
||||
@@ -30,7 +30,7 @@ app.use(
|
||||
)
|
||||
|
||||
|
||||
app.use(v1);
|
||||
app.use('/v1', v1);
|
||||
|
||||
app.listen(process.env.PORT, () => {
|
||||
console.log('Servidor en funcionamiento en el puerto: ' + process.env.PORT);
|
||||
|
||||
@@ -72,33 +72,33 @@ router.get('/', (req, res) => {
|
||||
});
|
||||
|
||||
|
||||
router.get('/v1/commands', (req, res) => {
|
||||
router.get('/commands', (req, res) => {
|
||||
|
||||
sql_inyec(req, res, dbcentral);
|
||||
|
||||
});
|
||||
|
||||
router.post('/v1/commands/result', (req, res) => {
|
||||
router.post('/commands/result', (req, res) => {
|
||||
save_result_inyec(req, res, dbcentral);
|
||||
});
|
||||
|
||||
router.post('/v1/commands', (req, res) => {
|
||||
router.post('/commands', (req, res) => {
|
||||
create_command(req, res, dbcentral);
|
||||
});
|
||||
|
||||
router.get('/v1/commands/:command_id', (req, res) => {
|
||||
router.get('/commands/:command_id', (req, res) => {
|
||||
get_command_status(req, res, dbcentral);
|
||||
});
|
||||
|
||||
router.get('/v1/commands/user/:id_user', (req, res) => {
|
||||
router.get('/commands/user/:id_user', (req, res) => {
|
||||
get_commands_by_user(req, res, dbcentral);
|
||||
});
|
||||
|
||||
router.get('/v1/stores/status', (req, res) => {
|
||||
router.get('/stores/status', (req, res) => {
|
||||
get_online_stores(req, res, dbcentral);
|
||||
});
|
||||
|
||||
router.post('/v1/autentificacion', (req, res) => {
|
||||
router.post('/autentificacion', (req, res) => {
|
||||
autentificacion(req, res);
|
||||
});
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class CacheCredenciales {
|
||||
} catch (error) {
|
||||
console.error('Error al obtener del caché o desencriptar (posible corrupción):', error);
|
||||
// Si hay un error de desencriptación o parseo, el caché está corrupto; lo eliminamos.
|
||||
await this.eliminar().catch(() => {}); // Intentar eliminar, pero sin fallar si no se puede.
|
||||
await this.eliminar().catch(() => { }); // Intentar eliminar, pero sin fallar si no se puede.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -367,7 +367,7 @@ function procesarCredenciales(credencialesRaw) {
|
||||
* @returns {Promise<object>} - Las credenciales procesadas y listas para usar.
|
||||
* @throws {Error} - Si no se pueden obtener las credenciales.
|
||||
*/
|
||||
export async function ObtenerCredenciales(solicitud, cacheSecretKey = process.env.CACHE_SECRET_KEY || 'a_secret_key_for_caching') {
|
||||
export async function ObtenerCredenciales(solicitud, cacheSecretKey = process.env.SECRET_CACHE_KEY || 'a_secret_key_for_caching') {
|
||||
const cacheName = crypto.createHash('md5').update(JSON.stringify(solicitud)).digest('hex');
|
||||
const cache = new CacheCredenciales(cacheName, cacheSecretKey);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user