@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 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
- ### 4. Capturas de Pantalla bajo demanda (Playwright)
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
- ### 5. Captura Automática de Errores (Screenshot + Trace)
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.18",
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 };
@@ -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
- module.exports = { CredentialManager };
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 };
@@ -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 {import('playwright').Page} p.page
44
- * @param {import('playwright').BrowserContext} p.context
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 {import('playwright').Page} page
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) {