@arela/uploader 1.0.24 → 1.1.1

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.
Files changed (80) hide show
  1. package/docs/AUTO_PROCESSING_PIPELINE.md +258 -0
  2. package/docs/COMPLETE_USAGE_GUIDE.md +1363 -0
  3. package/docs/DATABASESERVICE_IMPROVEMENTS.md +546 -0
  4. package/docs/PASO_2_TEST_RESULTS.md +298 -0
  5. package/docs/PASO_3_PLAN.md +385 -0
  6. package/docs/PHASE_1_FILE_DETECTION.md +366 -0
  7. package/docs/PHASE_2_API_INTEGRATION.md +426 -0
  8. package/docs/PHASE_3_DATABASE_MANAGEMENT.md +480 -0
  9. package/docs/PHASE_4_FILE_OPERATIONS.md +448 -0
  10. package/docs/PHASE_5_WATCH_MODE.md +450 -0
  11. package/docs/PHASE_6_SIGNAL_HANDLING.md +472 -0
  12. package/docs/PHASE_7_ADVANCED_FEATURES.md +560 -0
  13. package/docs/PLAN_WATCH_FEATURE.md +417 -0
  14. package/docs/README.md +480 -0
  15. package/docs/SCHEMA_ALIGNMENT_SUMMARY.md +301 -0
  16. package/docs/SMARTWATCH_DATABASE_REFACTORING.md +181 -0
  17. package/docs/SMART_WATCH_DATABASE_CHANGES.md +502 -0
  18. package/docs/TESTING_WATCH_MODE.md +212 -0
  19. package/docs/WATCHER_API_IMPLEMENTATION.md +520 -0
  20. package/docs/WATCHER_API_INTEGRATION.md +562 -0
  21. package/docs/WATCHER_SETUP_GUIDE.md +614 -0
  22. package/docs/WATCH_ARCHITECTURE.md +395 -0
  23. package/docs/WATCH_AUTO_PIPELINE.md +334 -0
  24. package/docs/WATCH_CONFIGURATION.md +267 -0
  25. package/docs/WATCH_USAGE_GUIDE.md +567 -0
  26. package/docs/commands.md +14 -0
  27. package/package.json +1 -1
  28. package/src/commands/IdentifyCommand.js +11 -0
  29. package/src/config/config.js +2 -2
  30. package/src/file-detection.js +42 -1
  31. package/src/scoring/scoring-engine.js +40 -7
  32. package/src/services/LoggingService.js +5 -3
  33. package/.vscode/settings.json +0 -1
  34. package/coverage/IdentifyCommand.js.html +0 -1462
  35. package/coverage/PropagateCommand.js.html +0 -1507
  36. package/coverage/PushCommand.js.html +0 -1504
  37. package/coverage/ScanCommand.js.html +0 -1654
  38. package/coverage/UploadCommand.js.html +0 -1846
  39. package/coverage/WatchCommand.js.html +0 -4111
  40. package/coverage/base.css +0 -224
  41. package/coverage/block-navigation.js +0 -87
  42. package/coverage/favicon.png +0 -0
  43. package/coverage/index.html +0 -191
  44. package/coverage/lcov-report/IdentifyCommand.js.html +0 -1462
  45. package/coverage/lcov-report/PropagateCommand.js.html +0 -1507
  46. package/coverage/lcov-report/PushCommand.js.html +0 -1504
  47. package/coverage/lcov-report/ScanCommand.js.html +0 -1654
  48. package/coverage/lcov-report/UploadCommand.js.html +0 -1846
  49. package/coverage/lcov-report/WatchCommand.js.html +0 -4111
  50. package/coverage/lcov-report/base.css +0 -224
  51. package/coverage/lcov-report/block-navigation.js +0 -87
  52. package/coverage/lcov-report/favicon.png +0 -0
  53. package/coverage/lcov-report/index.html +0 -191
  54. package/coverage/lcov-report/prettify.css +0 -1
  55. package/coverage/lcov-report/prettify.js +0 -2
  56. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  57. package/coverage/lcov-report/sorter.js +0 -210
  58. package/coverage/lcov.info +0 -1937
  59. package/coverage/prettify.css +0 -1
  60. package/coverage/prettify.js +0 -2
  61. package/coverage/sort-arrow-sprite.png +0 -0
  62. package/coverage/sorter.js +0 -210
  63. package/docs/API_ENDPOINTS_FOR_DETECTION.md +0 -647
  64. package/docs/API_RETRY_MECHANISM.md +0 -338
  65. package/docs/ARELA_IDENTIFY_IMPLEMENTATION.md +0 -489
  66. package/docs/ARELA_IDENTIFY_QUICKREF.md +0 -186
  67. package/docs/ARELA_PROPAGATE_IMPLEMENTATION.md +0 -581
  68. package/docs/ARELA_PROPAGATE_QUICKREF.md +0 -272
  69. package/docs/ARELA_PUSH_IMPLEMENTATION.md +0 -577
  70. package/docs/ARELA_PUSH_QUICKREF.md +0 -322
  71. package/docs/ARELA_SCAN_IMPLEMENTATION.md +0 -373
  72. package/docs/ARELA_SCAN_QUICKREF.md +0 -139
  73. package/docs/CROSS_PLATFORM_PATH_HANDLING.md +0 -597
  74. package/docs/DETECTION_ATTEMPT_TRACKING.md +0 -414
  75. package/docs/MIGRATION_UPLOADER_TO_FILE_STATS.md +0 -1020
  76. package/docs/MULTI_LEVEL_DIRECTORY_SCANNING.md +0 -494
  77. package/docs/QUICK_REFERENCE_API_DETECTION.md +0 -264
  78. package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +0 -200
  79. package/docs/STATS_COMMAND_SEQUENCE_DIAGRAM.md +0 -287
  80. package/docs/STATS_COMMAND_SIMPLE.md +0 -93
@@ -0,0 +1,562 @@
1
+ # Integración del Watcher con arela-api
2
+
3
+ ## 📋 Resumen
4
+ Este documento describe cómo exponer la información del WatchService a través de endpoints REST en la arela-api para que el frontend pueda monitorear en tiempo real el estado del uploader en modo watch.
5
+
6
+ ## 🎯 Objetivos
7
+ - Permitir que el frontend vea el estado en tiempo real del watcher
8
+ - Exponer estadísticas de archivos pendientes, listos para subir, procesando, etc.
9
+ - Permitir control remoto del watcher (start/stop/enable-disable auto-processing)
10
+ - Proporcionar información de progreso del pipeline automático
11
+
12
+ ---
13
+
14
+ ## 📡 Endpoints Necesarios
15
+
16
+ ### 1. GET `/api/watcher/status`
17
+ **Descripción:** Obtener estado actual del watcher
18
+
19
+ **Respuesta:**
20
+ ```json
21
+ {
22
+ "isRunning": true,
23
+ "autoProcessingEnabled": true,
24
+ "watchedDirectories": 3,
25
+ "activeWatchers": 3,
26
+ "lastUpdated": "2025-11-17T22:30:00.000Z",
27
+ "stats": {
28
+ "filesAdded": 145,
29
+ "filesModified": 23,
30
+ "filesRemoved": 5,
31
+ "uploadsTriggered": 12,
32
+ "pipelinesTriggered": 12,
33
+ "errorsEncountered": 0
34
+ }
35
+ }
36
+ ```
37
+
38
+ ---
39
+
40
+ ### 2. GET `/api/watcher/queue-stats`
41
+ **Descripción:** Obtener estadísticas de la cola de procesamiento
42
+
43
+ **Respuesta:**
44
+ ```json
45
+ {
46
+ "queue": {
47
+ "pending": 25,
48
+ "readyToUpload": 8,
49
+ "processing": 2,
50
+ "uploaded": 156,
51
+ "failed": 0,
52
+ "total": 191
53
+ },
54
+ "progress": {
55
+ "percentComplete": 82.2,
56
+ "estimatedTimeRemaining": "5 minutes",
57
+ "avgProcessingTimeMs": 3400
58
+ },
59
+ "timestamp": "2025-11-17T22:30:00.000Z"
60
+ }
61
+ ```
62
+
63
+ ---
64
+
65
+ ### 3. GET `/api/watcher/directories`
66
+ **Descripción:** Obtener lista de directorios siendo monitoreados
67
+
68
+ **Respuesta:**
69
+ ```json
70
+ {
71
+ "directories": [
72
+ {
73
+ "path": "/Users/jjfigueroa/Documents/2023",
74
+ "isReady": true,
75
+ "fileCount": 65,
76
+ "folderStructure": "arela"
77
+ },
78
+ {
79
+ "path": "/Users/jjfigueroa/Documents/2024",
80
+ "isReady": true,
81
+ "fileCount": 89,
82
+ "folderStructure": "arela"
83
+ },
84
+ {
85
+ "path": "/Users/jjfigueroa/Downloads",
86
+ "isReady": false,
87
+ "fileCount": 37,
88
+ "folderStructure": "arela"
89
+ }
90
+ ],
91
+ "totalDirectories": 3
92
+ }
93
+ ```
94
+
95
+ ---
96
+
97
+ ### 4. POST `/api/watcher/start`
98
+ **Descripción:** Iniciar el watcher
99
+
100
+ **Body (opcional):**
101
+ ```json
102
+ {
103
+ "directories": ["/path/to/dir1", "/path/to/dir2"],
104
+ "autoProcessing": true,
105
+ "folderStructure": "arela"
106
+ }
107
+ ```
108
+
109
+ **Respuesta:**
110
+ ```json
111
+ {
112
+ "success": true,
113
+ "message": "Watcher started successfully",
114
+ "stats": { /* ver GET /api/watcher/status */ }
115
+ }
116
+ ```
117
+
118
+ ---
119
+
120
+ ### 5. POST `/api/watcher/stop`
121
+ **Descripción:** Detener el watcher
122
+
123
+ **Respuesta:**
124
+ ```json
125
+ {
126
+ "success": true,
127
+ "message": "Watcher stopped successfully",
128
+ "finalStats": { /* estadísticas finales */ }
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ### 6. POST `/api/watcher/enable-auto-processing`
135
+ **Descripción:** Habilitar procesamiento automático
136
+
137
+ **Body:**
138
+ ```json
139
+ {
140
+ "batchSize": 10,
141
+ "timeout": 30000
142
+ }
143
+ ```
144
+
145
+ **Respuesta:**
146
+ ```json
147
+ {
148
+ "success": true,
149
+ "autoProcessingEnabled": true
150
+ }
151
+ ```
152
+
153
+ ---
154
+
155
+ ### 7. POST `/api/watcher/disable-auto-processing`
156
+ **Descripción:** Deshabilitar procesamiento automático
157
+
158
+ **Respuesta:**
159
+ ```json
160
+ {
161
+ "success": true,
162
+ "autoProcessingEnabled": false
163
+ }
164
+ ```
165
+
166
+ ---
167
+
168
+ ### 8. GET `/api/watcher/pipeline-status`
169
+ **Descripción:** Obtener estado de los pipelines en ejecución
170
+
171
+ **Respuesta:**
172
+ ```json
173
+ {
174
+ "isProcessing": false,
175
+ "activePipelines": 0,
176
+ "recentPipelines": [
177
+ {
178
+ "id": "pipeline-1763418030418-ml4dcsgw3",
179
+ "file": "3429-07-23009203-Simplif.pdf",
180
+ "directory": "/Users/jjfigueroa/Documents/2023/230734293009203",
181
+ "startTime": "2025-11-17T22:20:30.000Z",
182
+ "duration": 653,
183
+ "status": "success",
184
+ "steps": {
185
+ "statsOnly": "success",
186
+ "detectPdfs": "success",
187
+ "propagateArelaPath": "success",
188
+ "uploadByRfc": "success"
189
+ }
190
+ }
191
+ ]
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 🔧 Implementación en arela-api
198
+
199
+ ### Archivo: `routes/watcher.routes.js`
200
+
201
+ ```javascript
202
+ import express from 'express';
203
+ import watchService from '../services/WatchService.js'; // Import desde arela-uploader
204
+ import logger from '../services/LoggingService.js';
205
+
206
+ const router = express.Router();
207
+
208
+ /**
209
+ * GET /api/watcher/status
210
+ * Get watcher status and stats
211
+ */
212
+ router.get('/status', (req, res) => {
213
+ try {
214
+ const stats = watchService.getStats();
215
+ res.json({
216
+ isRunning: stats.isRunning,
217
+ autoProcessingEnabled: stats.autoProcessingEnabled,
218
+ watchedDirectories: stats.watchedDirectories,
219
+ activeWatchers: stats.activeWatchers,
220
+ lastUpdated: new Date().toISOString(),
221
+ stats: {
222
+ filesAdded: stats.filesAdded,
223
+ filesModified: stats.filesModified,
224
+ filesRemoved: stats.filesRemoved,
225
+ uploadsTriggered: stats.uploadsTriggered,
226
+ pipelinesTriggered: stats.pipelinesTriggered,
227
+ errorsEncountered: stats.errorsEncountered,
228
+ },
229
+ });
230
+ } catch (error) {
231
+ logger.error(`Error getting watcher status: ${error.message}`);
232
+ res.status(500).json({ error: error.message });
233
+ }
234
+ });
235
+
236
+ /**
237
+ * GET /api/watcher/queue-stats
238
+ * Get queue statistics
239
+ */
240
+ router.get('/queue-stats', async (req, res) => {
241
+ try {
242
+ const queueStats = await watchService.getQueueStats();
243
+ if (!queueStats) {
244
+ return res.status(500).json({ error: 'Could not fetch queue stats' });
245
+ }
246
+ res.json(queueStats);
247
+ } catch (error) {
248
+ logger.error(`Error getting queue stats: ${error.message}`);
249
+ res.status(500).json({ error: error.message });
250
+ }
251
+ });
252
+
253
+ /**
254
+ * GET /api/watcher/directories
255
+ * Get watched directories info
256
+ */
257
+ router.get('/directories', (req, res) => {
258
+ try {
259
+ const directories = Array.from(watchService.watchedDirs).map((dir) => {
260
+ const config = watchService.directoryConfigs.get(dir);
261
+ const isReady = watchService.watcherReady.get(dir) === true;
262
+
263
+ return {
264
+ path: dir,
265
+ isReady,
266
+ folderStructure: config?.folderStructure || 'default',
267
+ };
268
+ });
269
+
270
+ res.json({
271
+ directories,
272
+ totalDirectories: directories.length,
273
+ });
274
+ } catch (error) {
275
+ logger.error(`Error getting watched directories: ${error.message}`);
276
+ res.status(500).json({ error: error.message });
277
+ }
278
+ });
279
+
280
+ /**
281
+ * POST /api/watcher/start
282
+ * Start the watcher
283
+ */
284
+ router.post('/start', async (req, res) => {
285
+ try {
286
+ const { directories, autoProcessing = true, folderStructure = 'arela' } =
287
+ req.body;
288
+
289
+ if (!directories || directories.length === 0) {
290
+ return res.status(400).json({ error: 'No directories specified' });
291
+ }
292
+
293
+ // Add watchers for each directory
294
+ for (const dir of directories) {
295
+ await watchService.addWatcher(dir, {}, { folderStructure });
296
+ }
297
+
298
+ // Enable auto-processing if requested
299
+ if (autoProcessing) {
300
+ watchService.enableAutoProcessing({ batchSize: 10 });
301
+ }
302
+
303
+ // Start watching
304
+ await watchService.start();
305
+
306
+ res.json({
307
+ success: true,
308
+ message: 'Watcher started successfully',
309
+ stats: watchService.getStats(),
310
+ });
311
+ } catch (error) {
312
+ logger.error(`Error starting watcher: ${error.message}`);
313
+ res.status(500).json({ error: error.message });
314
+ }
315
+ });
316
+
317
+ /**
318
+ * POST /api/watcher/stop
319
+ * Stop the watcher
320
+ */
321
+ router.post('/stop', async (req, res) => {
322
+ try {
323
+ const finalStats = watchService.getStats();
324
+ await watchService.stop('API request');
325
+
326
+ res.json({
327
+ success: true,
328
+ message: 'Watcher stopped successfully',
329
+ finalStats,
330
+ });
331
+ } catch (error) {
332
+ logger.error(`Error stopping watcher: ${error.message}`);
333
+ res.status(500).json({ error: error.message });
334
+ }
335
+ });
336
+
337
+ /**
338
+ * POST /api/watcher/enable-auto-processing
339
+ * Enable automatic processing pipeline
340
+ */
341
+ router.post('/enable-auto-processing', (req, res) => {
342
+ try {
343
+ const { batchSize = 10 } = req.body;
344
+
345
+ watchService.enableAutoProcessing({ batchSize });
346
+
347
+ res.json({
348
+ success: true,
349
+ autoProcessingEnabled: watchService.isAutoProcessingEnabled(),
350
+ });
351
+ } catch (error) {
352
+ logger.error(`Error enabling auto-processing: ${error.message}`);
353
+ res.status(500).json({ error: error.message });
354
+ }
355
+ });
356
+
357
+ /**
358
+ * POST /api/watcher/disable-auto-processing
359
+ * Disable automatic processing pipeline
360
+ */
361
+ router.post('/disable-auto-processing', (req, res) => {
362
+ try {
363
+ watchService.disableAutoProcessing();
364
+
365
+ res.json({
366
+ success: true,
367
+ autoProcessingEnabled: watchService.isAutoProcessingEnabled(),
368
+ });
369
+ } catch (error) {
370
+ logger.error(`Error disabling auto-processing: ${error.message}`);
371
+ res.status(500).json({ error: error.message });
372
+ }
373
+ });
374
+
375
+ export default router;
376
+ ```
377
+
378
+ ---
379
+
380
+ ## 🖥️ Integración en arela-api Express App
381
+
382
+ ### Archivo: `app.js` o `index.js`
383
+
384
+ ```javascript
385
+ import express from 'express';
386
+ import watcherRoutes from './routes/watcher.routes.js';
387
+
388
+ const app = express();
389
+
390
+ // ... other middleware ...
391
+
392
+ // Mount watcher routes
393
+ app.use('/api/watcher', watcherRoutes);
394
+
395
+ // ... rest of app ...
396
+ ```
397
+
398
+ ---
399
+
400
+ ## 📦 Importación de WatchService
401
+
402
+ Para que arela-api pueda acceder a WatchService desde arela-uploader, hay dos opciones:
403
+
404
+ ### Opción 1: Exportar como módulo NPM (Recomendado)
405
+ 1. En `arela-uploader/package.json`:
406
+ ```json
407
+ {
408
+ "name": "@arela/uploader",
409
+ "exports": {
410
+ "./services": "./src/services/index.js"
411
+ }
412
+ }
413
+ ```
414
+
415
+ 2. En `arela-uploader/src/services/index.js`:
416
+ ```javascript
417
+ export { default as watchService } from './WatchService.js';
418
+ export { default as databaseService } from './DatabaseService.js';
419
+ export { default as autoProcessingService } from './AutoProcessingService.js';
420
+ ```
421
+
422
+ 3. En arela-api:
423
+ ```bash
424
+ npm install /path/to/arela-uploader
425
+ # o si está publicado en npm
426
+ npm install @arela/uploader
427
+ ```
428
+
429
+ 4. Usar en arela-api:
430
+ ```javascript
431
+ import { watchService } from '@arela/uploader/services';
432
+ ```
433
+
434
+ ### Opción 2: Compartir código vía monorepo
435
+ Usar Lerna o Yarn Workspaces para mantener ambos proyectos en el mismo workspace.
436
+
437
+ ---
438
+
439
+ ## 🎨 Ejemplo de Interfaz Frontend
440
+
441
+ ```javascript
442
+ // hooks/useWatcher.js
443
+ import { useState, useEffect } from 'react';
444
+
445
+ export const useWatcher = () => {
446
+ const [status, setStatus] = useState(null);
447
+ const [queue, setQueue] = useState(null);
448
+ const [loading, setLoading] = useState(false);
449
+ const [error, setError] = useState(null);
450
+
451
+ useEffect(() => {
452
+ const interval = setInterval(async () => {
453
+ try {
454
+ setLoading(true);
455
+ const [statusRes, queueRes] = await Promise.all([
456
+ fetch('/api/watcher/status'),
457
+ fetch('/api/watcher/queue-stats'),
458
+ ]);
459
+
460
+ const statusData = await statusRes.json();
461
+ const queueData = await queueRes.json();
462
+
463
+ setStatus(statusData);
464
+ setQueue(queueData);
465
+ setError(null);
466
+ } catch (err) {
467
+ setError(err.message);
468
+ } finally {
469
+ setLoading(false);
470
+ }
471
+ }, 5000); // Refrescar cada 5 segundos
472
+
473
+ return () => clearInterval(interval);
474
+ }, []);
475
+
476
+ const startWatcher = async (directories) => {
477
+ const res = await fetch('/api/watcher/start', {
478
+ method: 'POST',
479
+ headers: { 'Content-Type': 'application/json' },
480
+ body: JSON.stringify({ directories, autoProcessing: true }),
481
+ });
482
+ return res.json();
483
+ };
484
+
485
+ const stopWatcher = async () => {
486
+ const res = await fetch('/api/watcher/stop', { method: 'POST' });
487
+ return res.json();
488
+ };
489
+
490
+ return {
491
+ status,
492
+ queue,
493
+ loading,
494
+ error,
495
+ startWatcher,
496
+ stopWatcher,
497
+ };
498
+ };
499
+ ```
500
+
501
+ ---
502
+
503
+ ## 🔐 Consideraciones de Seguridad
504
+
505
+ 1. **Autenticación**: Todos los endpoints deben requerir autenticación JWT o similar
506
+ 2. **Autorización**: Solo usuarios con rol `admin` o `uploader` pueden controlar el watcher
507
+ 3. **Rate Limiting**: Implementar rate limiting en los endpoints de control (start/stop)
508
+ 4. **Auditoría**: Registrar todas las acciones de control del watcher
509
+
510
+ Ejemplo con middleware de autenticación:
511
+ ```javascript
512
+ import { verifyAuth } from '../middleware/auth.js';
513
+
514
+ router.get('/status', verifyAuth, (req, res) => {
515
+ // ... endpoint
516
+ });
517
+
518
+ router.post('/start', verifyAuth, requireRole('admin'), async (req, res) => {
519
+ // ... endpoint
520
+ });
521
+ ```
522
+
523
+ ---
524
+
525
+ ## 📊 WebSocket para actualizaciones en tiempo real (Opcional)
526
+
527
+ Para mayor eficiencia, implementar WebSocket en lugar de polling:
528
+
529
+ ```javascript
530
+ // socket.io integration
531
+ io.on('connection', (socket) => {
532
+ socket.on('subscribe-watcher', async () => {
533
+ // Enviar status inicial
534
+ socket.emit('watcher:status', watchService.getStats());
535
+
536
+ // Actualizar cada 5 segundos
537
+ const interval = setInterval(async () => {
538
+ socket.emit('watcher:status', watchService.getStats());
539
+ socket.emit('watcher:queue', await watchService.getQueueStats());
540
+ }, 5000);
541
+
542
+ socket.on('disconnect', () => clearInterval(interval));
543
+ });
544
+ });
545
+ ```
546
+
547
+ ---
548
+
549
+ ## ✅ Checklist de Implementación
550
+
551
+ - [ ] Crear archivo `routes/watcher.routes.js` con endpoints
552
+ - [ ] Integrar rutas en `app.js`
553
+ - [ ] Exportar WatchService desde arela-uploader
554
+ - [ ] Importar en arela-api
555
+ - [ ] Agregar autenticación a endpoints
556
+ - [ ] Agregar validación de inputs
557
+ - [ ] Agregar logging y error handling
558
+ - [ ] Implementar caché en frontend (SWR, React Query)
559
+ - [ ] Agregar WebSocket para actualizaciones real-time (opcional)
560
+ - [ ] Documentar endpoints en OpenAPI/Swagger
561
+ - [ ] Crear tests de integración
562
+