@gnpdev/rpa-tools 1.0.18 → 1.0.20
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/README.md +32 -12
- package/package.json +3 -2
- package/src/capture.js +64 -0
- package/src/credentials.js +56 -35
- package/src/errorCapture.js +4 -4
- package/src/index.d.ts +6 -0
- package/src/index.js +35 -1
- package/src/logger.js +25 -25
- package/src/watcher.js +1 -1
package/README.md
CHANGED
|
@@ -77,8 +77,8 @@ import { logger } from './lib/rpa.js';
|
|
|
77
77
|
logger.info('Procesando orden');
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
### 3. Gestión de Credenciales
|
|
81
|
-
Recupera de forma segura el `usuario`, `password` e `idUsuario` de una aplicación específica vinculada al bot.
|
|
80
|
+
### 3. Gestión de Credenciales y Estado de Aplicaciones
|
|
81
|
+
Recupera de forma segura el `usuario`, `password` e `idUsuario` de una aplicación específica vinculada al bot. También permite actualizar el estado y observaciones (útil para fallos de login).
|
|
82
82
|
|
|
83
83
|
```javascript
|
|
84
84
|
import { rpa, logger } from './lib/rpa.js';
|
|
@@ -90,9 +90,34 @@ if (credentials) {
|
|
|
90
90
|
const { usuario, password, idUsuario } = credentials;
|
|
91
91
|
// usar en el login de la web
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
// En caso de fallo de login o error en la aplicación
|
|
95
|
+
await rpa.updateAppStatus('Portal CRM', false, 'Credenciales inválidas o bloqueo de cuenta');
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 4. Verificar Estado del Bot (Kill Switch)
|
|
99
|
+
...
|
|
100
|
+
```javascript
|
|
101
|
+
import { rpa, logger } from './lib/rpa.js';
|
|
102
|
+
|
|
103
|
+
const active = await rpa.isActive();
|
|
104
|
+
...
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 5. Capturas de Pantalla
|
|
108
|
+
Existen tres formas de realizar capturas de pantalla:
|
|
109
|
+
|
|
110
|
+
#### A. Captura Manual Estructurada
|
|
111
|
+
Captura la página actual y la guarda con una estructura de carpetas: `capturas/id_bot/fecha/nombre.webp`.
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
import { rpa } from './lib/rpa.js';
|
|
115
|
+
|
|
116
|
+
// Captura la página actual con el nombre 'dashboard_principal'
|
|
117
|
+
const key = await rpa.capturePage(page, 'dashboard_principal');
|
|
93
118
|
```
|
|
94
119
|
|
|
95
|
-
|
|
120
|
+
#### B. Monitoreo en Tiempo Real (Debug)
|
|
96
121
|
Activa el watcher pasando la instancia de `page`. El bot detectará cambios en la tabla de la DB para tomar screenshots automáticamente.
|
|
97
122
|
|
|
98
123
|
```javascript
|
|
@@ -102,20 +127,15 @@ import { rpa } from './lib/rpa.js';
|
|
|
102
127
|
const browser = await chromium.launch();
|
|
103
128
|
const page = await browser.newPage();
|
|
104
129
|
|
|
105
|
-
// Inicia el monitoreo (polling cada 3s por defecto)
|
|
106
130
|
rpa.watchDebugFlag(page);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// Al terminar el proceso del bot
|
|
110
|
-
rpa.destroy();
|
|
111
|
-
|
|
112
|
-
// Sugerencia: limpiar al desconectar
|
|
113
|
-
browser.on('disconnected', () => rpa.destroy());
|
|
114
131
|
```
|
|
115
132
|
|
|
116
|
-
|
|
133
|
+
#### C. Captura Automática de Errores (Screenshot + Trace)
|
|
134
|
+
...
|
|
117
135
|
Captura el estado completo del bot cuando ocurre una excepción. **`captureError` detecta automáticamente el paso actual** definido con `step()`.
|
|
118
136
|
|
|
137
|
+
> **Nota:** El soporte de `tracing` (archivos .zip de depuración) es exclusivo de **Playwright**. En **Puppeteer** se capturará el error y el screenshot, pero el `traceKey` será nulo.
|
|
138
|
+
|
|
119
139
|
```javascript
|
|
120
140
|
import { chromium } from 'playwright';
|
|
121
141
|
import { rpa, logger, step } from './lib/rpa.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gnpdev/rpa-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Libreria para logs y screenshot de bots",
|
|
5
5
|
"author": "Sergio Antonio Trujillo del Valle",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"minio": "^8.0.7",
|
|
21
21
|
"pino": "^10.3.1",
|
|
22
22
|
"pino-pretty": "^13.1.3",
|
|
23
|
-
"sharp": "^0.34.5"
|
|
23
|
+
"sharp": "^0.34.5",
|
|
24
|
+
"pg": "^8.11.0"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@commitlint/cli": "^20.5.0",
|
package/src/capture.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const sharp = require('sharp');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Gestor de capturas de pantalla de la página actual.
|
|
6
|
+
*/
|
|
7
|
+
class PageCapturer {
|
|
8
|
+
/**
|
|
9
|
+
* @param {object} params
|
|
10
|
+
* @param {string} params.botId - UUID del bot
|
|
11
|
+
* @param {import('minio').Client} params.minioClient - Cliente MinIO
|
|
12
|
+
* @param {string} params.bucket - Bucket de MinIO
|
|
13
|
+
* @param {import('pino').Logger} [params.logger] - Instancia de Logger
|
|
14
|
+
*/
|
|
15
|
+
constructor({ botId, minioClient, bucket, logger }) {
|
|
16
|
+
this.botId = botId;
|
|
17
|
+
this.minioClient = minioClient;
|
|
18
|
+
this.bucket = bucket;
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Captura la página actual y la sube a MinIO con una ruta estructurada.
|
|
24
|
+
* Estructura: capturas/{botId}/{YYYY-MM-DD}/{nombre}.webp
|
|
25
|
+
*
|
|
26
|
+
* @param {any} page - Instancia de Page (Playwright o Puppeteer)
|
|
27
|
+
* @param {string} nombre - Nombre del elemento/captura
|
|
28
|
+
* @returns {Promise<string|null>} - Key de la captura en MinIO
|
|
29
|
+
*/
|
|
30
|
+
async capture(page, nombre) {
|
|
31
|
+
try {
|
|
32
|
+
const now = new Date();
|
|
33
|
+
const date = now.toISOString().slice(0, 10);
|
|
34
|
+
const key = `capturas/${this.botId}/${date}/${nombre}.webp`;
|
|
35
|
+
|
|
36
|
+
const buffer = await page.screenshot({
|
|
37
|
+
type: 'jpeg',
|
|
38
|
+
quality: 80,
|
|
39
|
+
fullPage: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const finalBuffer = await sharp(buffer)
|
|
43
|
+
.webp({ quality: 70 })
|
|
44
|
+
.toBuffer();
|
|
45
|
+
|
|
46
|
+
await this.minioClient.putObject(this.bucket, key, finalBuffer, finalBuffer.length, {
|
|
47
|
+
'Content-Type': 'image/webp',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (this.logger) {
|
|
51
|
+
this.logger.info({ key, nombre }, 'Captura de pantalla guardada exitosamente');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return key;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (this.logger) {
|
|
57
|
+
this.logger.error({ err, nombre }, 'Error al capturar pantalla');
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { PageCapturer };
|
package/src/credentials.js
CHANGED
|
@@ -1,35 +1,56 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Gestor de credenciales de aplicaciones para bots.
|
|
5
|
-
*/
|
|
6
|
-
class CredentialManager {
|
|
7
|
-
/**
|
|
8
|
-
* @param {object} params
|
|
9
|
-
* @param {import('pg').Pool} params.pool - Pool de pg
|
|
10
|
-
* @param {string} params.botId - UUID del bot
|
|
11
|
-
*/
|
|
12
|
-
constructor({ pool, botId }) {
|
|
13
|
-
this.pool = pool;
|
|
14
|
-
this.botId = botId;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Obtiene las credenciales de una aplicación.
|
|
19
|
-
* @param {string} nombre - Nombre de la aplicación
|
|
20
|
-
* @returns {Promise<{usuario: string, password: string, idUsuario: string}|null>}
|
|
21
|
-
*/
|
|
22
|
-
async getAppCredentials(nombre) {
|
|
23
|
-
const { rows } = await this.pool.query(
|
|
24
|
-
`SELECT usuario, password, "idUsuario"
|
|
25
|
-
FROM bots.tb_aplicaciones_bots
|
|
26
|
-
WHERE nombre = $1 AND bot_id = $2
|
|
27
|
-
LIMIT 1`,
|
|
28
|
-
[nombre, this.botId]
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
return rows.length > 0 ? rows[0] : null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gestor de credenciales de aplicaciones para bots.
|
|
5
|
+
*/
|
|
6
|
+
class CredentialManager {
|
|
7
|
+
/**
|
|
8
|
+
* @param {object} params
|
|
9
|
+
* @param {import('pg').Pool} params.pool - Pool de pg
|
|
10
|
+
* @param {string} params.botId - UUID del bot
|
|
11
|
+
*/
|
|
12
|
+
constructor({ pool, botId }) {
|
|
13
|
+
this.pool = pool;
|
|
14
|
+
this.botId = botId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Obtiene las credenciales de una aplicación.
|
|
19
|
+
* @param {string} nombre - Nombre de la aplicación
|
|
20
|
+
* @returns {Promise<{usuario: string, password: string, idUsuario: string}|null>}
|
|
21
|
+
*/
|
|
22
|
+
async getAppCredentials(nombre) {
|
|
23
|
+
const { rows } = await this.pool.query(
|
|
24
|
+
`SELECT usuario, password, "idUsuario"
|
|
25
|
+
FROM bots.tb_aplicaciones_bots
|
|
26
|
+
WHERE nombre = $1 AND bot_id = $2
|
|
27
|
+
LIMIT 1`,
|
|
28
|
+
[nombre, this.botId]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return rows.length > 0 ? rows[0] : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Actualiza el estado y las observaciones de una aplicación.
|
|
36
|
+
* @param {string} nombre - Nombre de la aplicación
|
|
37
|
+
* @param {boolean} status - Estado (true/false)
|
|
38
|
+
* @param {string} observations - Observaciones/Errores
|
|
39
|
+
* @returns {Promise<boolean>}
|
|
40
|
+
*/
|
|
41
|
+
async updateAppStatus(nombre, status, observations) {
|
|
42
|
+
try {
|
|
43
|
+
await this.pool.query(
|
|
44
|
+
`UPDATE bots.tb_aplicaciones_bots
|
|
45
|
+
SET status = $1, observations = $2
|
|
46
|
+
WHERE nombre = $3 AND bot_id = $4`,
|
|
47
|
+
[status, observations, nombre, this.botId]
|
|
48
|
+
);
|
|
49
|
+
return true;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { CredentialManager };
|
package/src/errorCapture.js
CHANGED
|
@@ -35,13 +35,13 @@ function errorPrefix(botId) {
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Captura el estado completo cuando ocurre un error:
|
|
38
|
-
* - Screenshot WebP de la página actual
|
|
39
|
-
* - Trace de Playwright (.zip con video, red, consola, DOM snapshots)
|
|
38
|
+
* - Screenshot WebP de la página actual (Playwright/Puppeteer)
|
|
39
|
+
* - Trace de Playwright (.zip con video, red, consola, DOM snapshots) [Opcional]
|
|
40
40
|
* - Registro en bots.tb_error_bots
|
|
41
41
|
*
|
|
42
42
|
* @param {object} p
|
|
43
|
-
* @param {
|
|
44
|
-
* @param {
|
|
43
|
+
* @param {any} p.page - Instancia de Page (Playwright o Puppeteer)
|
|
44
|
+
* @param {any} [p.context] - BrowserContext (solo Playwright para tracing)
|
|
45
45
|
* @param {Error} p.err
|
|
46
46
|
* @param {string} p.botId - UUID del bot
|
|
47
47
|
* @param {import('pg').Pool} p.pool
|
package/src/index.d.ts
CHANGED
|
@@ -74,6 +74,12 @@ export interface RpaTools {
|
|
|
74
74
|
*/
|
|
75
75
|
getCredentials: (nombre: string) => Promise<{ usuario: string; password: string; idUsuario: string } | null>;
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Consulta si el bot está activo en la base de datos (columna 'estado').
|
|
79
|
+
* @returns {Promise<boolean>}
|
|
80
|
+
*/
|
|
81
|
+
isActive: () => Promise<boolean>;
|
|
82
|
+
|
|
77
83
|
/**
|
|
78
84
|
* Captura screenshot, trace y registra error en base de datos.
|
|
79
85
|
*/
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const { createMinioClient, ensureBucket } = require('./storage');
|
|
|
4
4
|
const { ScreenshotWatcher } = require('./watcher');
|
|
5
5
|
const { CredentialManager } = require('./credentials');
|
|
6
6
|
const { captureError } = require('./errorCapture');
|
|
7
|
+
const { PageCapturer } = require('./capture');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Factory principal de la librería.
|
|
@@ -14,7 +15,7 @@ const { captureError } = require('./errorCapture');
|
|
|
14
15
|
* @param {object} opts.minio - config de MinIO
|
|
15
16
|
* @param {object} [opts.log] - opciones de Pino
|
|
16
17
|
*
|
|
17
|
-
* @returns {{ logger, watchDebugFlag, getCredentials, destroy }}
|
|
18
|
+
* @returns {{ logger, state, step, watchDebugFlag, getCredentials, updateAppStatus, isActive, capturePage, captureError, destroy }}
|
|
18
19
|
*/
|
|
19
20
|
async function createRpaTools(opts = {}) {
|
|
20
21
|
const {
|
|
@@ -45,6 +46,7 @@ async function createRpaTools(opts = {}) {
|
|
|
45
46
|
|
|
46
47
|
const watcher = new ScreenshotWatcher({ botId, pool: db, minioClient, bucket, logger });
|
|
47
48
|
const credentials = new CredentialManager({ botId, pool: db });
|
|
49
|
+
const capturer = new PageCapturer({ botId, minioClient, bucket, logger });
|
|
48
50
|
|
|
49
51
|
const state = { currentStep: 'Inicio' };
|
|
50
52
|
|
|
@@ -55,8 +57,40 @@ async function createRpaTools(opts = {}) {
|
|
|
55
57
|
state.currentStep = name;
|
|
56
58
|
logger.info(name + ' - Nuevo paso');
|
|
57
59
|
},
|
|
60
|
+
/**
|
|
61
|
+
* Inicia el polling que activa/desactiva screenshots según bot_debug_config.
|
|
62
|
+
* @param {any} page - Objeto Page de Playwright o Puppeteer
|
|
63
|
+
* @param {number} [pollMs=3000] - Intervalo de polling en ms
|
|
64
|
+
*/
|
|
58
65
|
watchDebugFlag: (page, pollMs) => watcher.watch(page, pollMs),
|
|
59
66
|
getCredentials: (nombre) => credentials.getAppCredentials(nombre),
|
|
67
|
+
updateAppStatus: (nombre, status, observations) => credentials.updateAppStatus(nombre, status, observations),
|
|
68
|
+
/**
|
|
69
|
+
* Consulta si el bot está activo en la base de datos (columna 'estado').
|
|
70
|
+
* @returns {Promise<boolean>}
|
|
71
|
+
*/
|
|
72
|
+
isActive: async () => {
|
|
73
|
+
try {
|
|
74
|
+
const { rows } = await db.query(
|
|
75
|
+
'SELECT estado FROM bots.tb_bots WHERE id = $1',
|
|
76
|
+
[botId]
|
|
77
|
+
);
|
|
78
|
+
return rows.length > 0 ? rows[0].estado : false;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
logger.error({ err }, 'Error consultando estado en isActive');
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
/**
|
|
85
|
+
* Captura el estado de la página actual y lo sube a MinIO.
|
|
86
|
+
* @param {any} page - Instancia de Page
|
|
87
|
+
* @param {string} nombre - Nombre del elemento/captura
|
|
88
|
+
*/
|
|
89
|
+
capturePage: (page, nombre) => capturer.capture(page, nombre),
|
|
90
|
+
/**
|
|
91
|
+
* Captura screenshot y registra error en base de datos.
|
|
92
|
+
* Soporta Playwright (con tracing opcional) y Puppeteer.
|
|
93
|
+
*/
|
|
60
94
|
captureError: ({ page, context, err, step }) => captureError({
|
|
61
95
|
page,
|
|
62
96
|
context,
|
package/src/logger.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const pino = require('pino');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Crea una instancia de Pino ya configurada con el botId como campo base.
|
|
6
|
-
* @param {string} botId
|
|
7
|
-
* @param {object} opts
|
|
8
|
-
* @param {'trace'|'debug'|'info'|'warn'|'error'} [opts.level='info']
|
|
9
|
-
* @param {boolean} [opts.pretty=false] - activar pino-pretty (solo en dev)
|
|
10
|
-
* @param {object} [opts.pinoOptions] - opciones adicionales de Pino
|
|
11
|
-
*/
|
|
12
|
-
function createLogger(botId, opts = {}) {
|
|
13
|
-
const { level = 'info', pretty = false, pinoOptions = {} } = opts;
|
|
14
|
-
|
|
15
|
-
return pino({
|
|
16
|
-
level,
|
|
17
|
-
base: { botId },
|
|
18
|
-
timestamp: pino.stdTimeFunctions.isoTime,
|
|
19
|
-
transport: pretty
|
|
20
|
-
? { target: 'pino-pretty', options: { colorize: true } }
|
|
21
|
-
: undefined,
|
|
22
|
-
...pinoOptions,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
const pino = require('pino');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Crea una instancia de Pino ya configurada con el botId como campo base.
|
|
6
|
+
* @param {string} botId
|
|
7
|
+
* @param {object} opts
|
|
8
|
+
* @param {'trace'|'debug'|'info'|'warn'|'error'} [opts.level='info']
|
|
9
|
+
* @param {boolean} [opts.pretty=false] - activar pino-pretty (solo en dev)
|
|
10
|
+
* @param {object} [opts.pinoOptions] - opciones adicionales de Pino
|
|
11
|
+
*/
|
|
12
|
+
function createLogger(botId, opts = {}) {
|
|
13
|
+
const { level = 'info', pretty = false, pinoOptions = {} } = opts;
|
|
14
|
+
|
|
15
|
+
return pino({
|
|
16
|
+
level,
|
|
17
|
+
base: { botId },
|
|
18
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
19
|
+
transport: pretty
|
|
20
|
+
? { target: 'pino-pretty', options: { colorize: true } }
|
|
21
|
+
: undefined,
|
|
22
|
+
...pinoOptions,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
26
|
module.exports = { createLogger };
|
package/src/watcher.js
CHANGED
|
@@ -35,7 +35,7 @@ class ScreenshotWatcher {
|
|
|
35
35
|
* Inicia el polling que activa/desactiva screenshots según bot_debug_config.
|
|
36
36
|
* Llama una sola vez al arrancar el bot.
|
|
37
37
|
*
|
|
38
|
-
* @param {
|
|
38
|
+
* @param {any} page - Objeto Page de Playwright o Puppeteer
|
|
39
39
|
* @param {number} [pollMs=3000] - cada cuántos ms revisar la BD
|
|
40
40
|
*/
|
|
41
41
|
watch(page, pollMs = 3000) {
|