@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.
- package/docs/AUTO_PROCESSING_PIPELINE.md +258 -0
- package/docs/COMPLETE_USAGE_GUIDE.md +1363 -0
- package/docs/DATABASESERVICE_IMPROVEMENTS.md +546 -0
- package/docs/PASO_2_TEST_RESULTS.md +298 -0
- package/docs/PASO_3_PLAN.md +385 -0
- package/docs/PHASE_1_FILE_DETECTION.md +366 -0
- package/docs/PHASE_2_API_INTEGRATION.md +426 -0
- package/docs/PHASE_3_DATABASE_MANAGEMENT.md +480 -0
- package/docs/PHASE_4_FILE_OPERATIONS.md +448 -0
- package/docs/PHASE_5_WATCH_MODE.md +450 -0
- package/docs/PHASE_6_SIGNAL_HANDLING.md +472 -0
- package/docs/PHASE_7_ADVANCED_FEATURES.md +560 -0
- package/docs/PLAN_WATCH_FEATURE.md +417 -0
- package/docs/README.md +480 -0
- package/docs/SCHEMA_ALIGNMENT_SUMMARY.md +301 -0
- package/docs/SMARTWATCH_DATABASE_REFACTORING.md +181 -0
- package/docs/SMART_WATCH_DATABASE_CHANGES.md +502 -0
- package/docs/TESTING_WATCH_MODE.md +212 -0
- package/docs/WATCHER_API_IMPLEMENTATION.md +520 -0
- package/docs/WATCHER_API_INTEGRATION.md +562 -0
- package/docs/WATCHER_SETUP_GUIDE.md +614 -0
- package/docs/WATCH_ARCHITECTURE.md +395 -0
- package/docs/WATCH_AUTO_PIPELINE.md +334 -0
- package/docs/WATCH_CONFIGURATION.md +267 -0
- package/docs/WATCH_USAGE_GUIDE.md +567 -0
- package/docs/commands.md +14 -0
- package/package.json +1 -1
- package/src/commands/IdentifyCommand.js +11 -0
- package/src/config/config.js +2 -2
- package/src/file-detection.js +42 -1
- package/src/scoring/scoring-engine.js +40 -7
- package/src/services/LoggingService.js +5 -3
- package/.vscode/settings.json +0 -1
- package/coverage/IdentifyCommand.js.html +0 -1462
- package/coverage/PropagateCommand.js.html +0 -1507
- package/coverage/PushCommand.js.html +0 -1504
- package/coverage/ScanCommand.js.html +0 -1654
- package/coverage/UploadCommand.js.html +0 -1846
- package/coverage/WatchCommand.js.html +0 -4111
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -191
- package/coverage/lcov-report/IdentifyCommand.js.html +0 -1462
- package/coverage/lcov-report/PropagateCommand.js.html +0 -1507
- package/coverage/lcov-report/PushCommand.js.html +0 -1504
- package/coverage/lcov-report/ScanCommand.js.html +0 -1654
- package/coverage/lcov-report/UploadCommand.js.html +0 -1846
- package/coverage/lcov-report/WatchCommand.js.html +0 -4111
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -191
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -1937
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/docs/API_ENDPOINTS_FOR_DETECTION.md +0 -647
- package/docs/API_RETRY_MECHANISM.md +0 -338
- package/docs/ARELA_IDENTIFY_IMPLEMENTATION.md +0 -489
- package/docs/ARELA_IDENTIFY_QUICKREF.md +0 -186
- package/docs/ARELA_PROPAGATE_IMPLEMENTATION.md +0 -581
- package/docs/ARELA_PROPAGATE_QUICKREF.md +0 -272
- package/docs/ARELA_PUSH_IMPLEMENTATION.md +0 -577
- package/docs/ARELA_PUSH_QUICKREF.md +0 -322
- package/docs/ARELA_SCAN_IMPLEMENTATION.md +0 -373
- package/docs/ARELA_SCAN_QUICKREF.md +0 -139
- package/docs/CROSS_PLATFORM_PATH_HANDLING.md +0 -597
- package/docs/DETECTION_ATTEMPT_TRACKING.md +0 -414
- package/docs/MIGRATION_UPLOADER_TO_FILE_STATS.md +0 -1020
- package/docs/MULTI_LEVEL_DIRECTORY_SCANNING.md +0 -494
- package/docs/QUICK_REFERENCE_API_DETECTION.md +0 -264
- package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +0 -200
- package/docs/STATS_COMMAND_SEQUENCE_DIAGRAM.md +0 -287
- package/docs/STATS_COMMAND_SIMPLE.md +0 -93
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# Smart Watch Database Service Changes
|
|
2
|
+
|
|
3
|
+
## 📋 Resumen de Cambios
|
|
4
|
+
|
|
5
|
+
Se han agregado **13 nuevos métodos** al `DatabaseService.js` para soportar la cola inteligente de procesamiento (Smart Watch Queue).
|
|
6
|
+
|
|
7
|
+
### ✅ Métodos Agregados
|
|
8
|
+
|
|
9
|
+
#### 1. `insertFileToUploader(filePath, options)`
|
|
10
|
+
Inserta un archivo con estado inicial PENDING o READY_TO_UPLOAD.
|
|
11
|
+
|
|
12
|
+
**Parámetros:**
|
|
13
|
+
- `filePath` (string): Ruta completa del archivo
|
|
14
|
+
- `options` (object):
|
|
15
|
+
- `processingStatus` (string): 'PENDING' o 'READY_TO_UPLOAD' (default: 'PENDING')
|
|
16
|
+
- `dependsOnPath` (string): Ruta del directorio padre
|
|
17
|
+
- `documentType` (string): Tipo de documento
|
|
18
|
+
- `size` (number): Tamaño del archivo en bytes
|
|
19
|
+
- `fileExtension` (string): Extensión del archivo
|
|
20
|
+
|
|
21
|
+
**Ejemplo:**
|
|
22
|
+
```javascript
|
|
23
|
+
const file = await databaseService.insertFileToUploader('/path/to/file.pdf', {
|
|
24
|
+
processingStatus: 'PENDING',
|
|
25
|
+
dependsOnPath: '../../Documents/2023',
|
|
26
|
+
size: 1024,
|
|
27
|
+
fileExtension: 'pdf'
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### 2. `getPendingFilesInDirectory(dirPath)`
|
|
32
|
+
Obtiene archivos que esperan por un pedimento simplificado.
|
|
33
|
+
|
|
34
|
+
**Parámetros:**
|
|
35
|
+
- `dirPath` (string): Ruta del directorio
|
|
36
|
+
|
|
37
|
+
**Retorna:** Array de archivos con status PENDING
|
|
38
|
+
|
|
39
|
+
**Ejemplo:**
|
|
40
|
+
```javascript
|
|
41
|
+
const pendingFiles = await databaseService.getPendingFilesInDirectory(
|
|
42
|
+
'../../Documents/2023'
|
|
43
|
+
);
|
|
44
|
+
console.log(`${pendingFiles.length} files waiting for pedimento`);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### 3. `getFilesWithStatus(statuses, limit)`
|
|
48
|
+
Obtiene archivos con ciertos status.
|
|
49
|
+
|
|
50
|
+
**Parámetros:**
|
|
51
|
+
- `statuses` (array): Array de status a buscar
|
|
52
|
+
- `limit` (number): Máximo de resultados (default: 10)
|
|
53
|
+
|
|
54
|
+
**Ejemplo:**
|
|
55
|
+
```javascript
|
|
56
|
+
const ready = await databaseService.getFilesWithStatus(
|
|
57
|
+
['READY_TO_UPLOAD', 'PROCESSING'],
|
|
58
|
+
10
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### 4. `updateFileStatus(filePath, updates)`
|
|
63
|
+
Actualiza el estado y otros campos de un archivo.
|
|
64
|
+
|
|
65
|
+
**Parámetros:**
|
|
66
|
+
- `filePath` (string): Ruta original del archivo
|
|
67
|
+
- `updates` (object): Campos a actualizar
|
|
68
|
+
|
|
69
|
+
**Ejemplo:**
|
|
70
|
+
```javascript
|
|
71
|
+
await databaseService.updateFileStatus(filePath, {
|
|
72
|
+
processing_status: 'UPLOADED',
|
|
73
|
+
upload_attempts: 1
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### 5. `markPedimentoDetected(dirPath, pedimentoPath)`
|
|
78
|
+
Marca un pedimento como detectado y actualiza archivos pendientes automáticamente.
|
|
79
|
+
|
|
80
|
+
**Parámetros:**
|
|
81
|
+
- `dirPath` (string): Ruta del directorio
|
|
82
|
+
- `pedimentoPath` (string): Ruta del archivo pedimento
|
|
83
|
+
|
|
84
|
+
**Comportamiento Automático:**
|
|
85
|
+
- Marca el pedimento como READY_TO_UPLOAD
|
|
86
|
+
- Obtiene todos los PENDING en `dirPath`
|
|
87
|
+
- Actualiza todos los PENDING a READY_TO_UPLOAD
|
|
88
|
+
- Registra el timestamp de detección
|
|
89
|
+
|
|
90
|
+
**Ejemplo:**
|
|
91
|
+
```javascript
|
|
92
|
+
await databaseService.markPedimentoDetected(
|
|
93
|
+
'../../Documents/2023',
|
|
94
|
+
'../../Documents/2023/3429-07-23021611-Simplif.pdf'
|
|
95
|
+
);
|
|
96
|
+
// Automáticamente marca todos los PENDING como READY_TO_UPLOAD
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### 6. `getExpiredWaitingFiles(maxWaitTimeSeconds)`
|
|
100
|
+
Obtiene archivos que esperan demasiado sin pedimento (archivos huérfanos).
|
|
101
|
+
|
|
102
|
+
**Parámetros:**
|
|
103
|
+
- `maxWaitTimeSeconds` (number): Tiempo máximo de espera en segundos
|
|
104
|
+
|
|
105
|
+
**Ejemplo:**
|
|
106
|
+
```javascript
|
|
107
|
+
const expired = await databaseService.getExpiredWaitingFiles(300); // 5 minutos
|
|
108
|
+
if (expired.length > 0) {
|
|
109
|
+
console.log(`Found ${expired.length} orphaned files`);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### 7. `getProcessingStats()`
|
|
114
|
+
Obtiene estadísticas generales de la cola.
|
|
115
|
+
|
|
116
|
+
**Retorna:** Object con conteos por status
|
|
117
|
+
|
|
118
|
+
**Ejemplo:**
|
|
119
|
+
```javascript
|
|
120
|
+
const stats = await databaseService.getProcessingStats();
|
|
121
|
+
console.log(`
|
|
122
|
+
Pending: ${stats.pending}
|
|
123
|
+
Ready: ${stats.readyToUpload}
|
|
124
|
+
Processing: ${stats.processing}
|
|
125
|
+
Uploaded: ${stats.uploaded}
|
|
126
|
+
Failed: ${stats.failed}
|
|
127
|
+
Total: ${stats.total}
|
|
128
|
+
`);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### 8. `getFailedFilesForRetry(maxAttempts)`
|
|
132
|
+
Obtiene archivos fallidos listos para reintentar.
|
|
133
|
+
|
|
134
|
+
**Parámetros:**
|
|
135
|
+
- `maxAttempts` (number): Máximo número de intentos permitidos (default: 3)
|
|
136
|
+
|
|
137
|
+
**Ejemplo:**
|
|
138
|
+
```javascript
|
|
139
|
+
const toRetry = await databaseService.getFailedFilesForRetry(3);
|
|
140
|
+
console.log(`${toRetry.length} files ready for retry`);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### 9. `retryFile(filePath)`
|
|
144
|
+
Reinicia un archivo fallido individual.
|
|
145
|
+
|
|
146
|
+
**Parámetros:**
|
|
147
|
+
- `filePath` (string): Ruta del archivo
|
|
148
|
+
|
|
149
|
+
**Comportamiento:**
|
|
150
|
+
- Cambia status a READY_TO_UPLOAD
|
|
151
|
+
- Reinicia upload_attempts a 0
|
|
152
|
+
- Limpia last_error
|
|
153
|
+
|
|
154
|
+
**Ejemplo:**
|
|
155
|
+
```javascript
|
|
156
|
+
const retried = await databaseService.retryFile('../../Documents/2023/file.pdf');
|
|
157
|
+
console.log(`File retried: ${retried.filename}`);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### 10. `retryFailedFiles(limit, maxAttempts)`
|
|
161
|
+
Reinicia múltiples archivos fallidos de forma eficiente.
|
|
162
|
+
|
|
163
|
+
**Parámetros:**
|
|
164
|
+
- `limit` (number): Cantidad máxima a reintentar (default: 10)
|
|
165
|
+
- `maxAttempts` (number): Máximo número de intentos (default: 3)
|
|
166
|
+
|
|
167
|
+
**Retorna:** Object con `{ retried, skipped, files }`
|
|
168
|
+
|
|
169
|
+
**Ejemplo:**
|
|
170
|
+
```javascript
|
|
171
|
+
const result = await databaseService.retryFailedFiles(10, 3);
|
|
172
|
+
console.log(`✅ Retried: ${result.retried}, ❌ Skipped: ${result.skipped}`);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### 11. `getOverallProgress()`
|
|
176
|
+
Obtiene progreso general con estimación de tiempo.
|
|
177
|
+
|
|
178
|
+
**Retorna:** Object con:
|
|
179
|
+
- `totalFiles`: Total de archivos
|
|
180
|
+
- `processedFiles`: Archivos subidos exitosamente
|
|
181
|
+
- `failedFiles`: Archivos con error
|
|
182
|
+
- `pendingFiles`: Archivos pendientes/procesando
|
|
183
|
+
- `percentComplete`: Porcentaje completado
|
|
184
|
+
- `estimatedTimeRemaining`: ETA estimada
|
|
185
|
+
- `avgProcessingTimeMs`: Tiempo promedio de procesamiento
|
|
186
|
+
|
|
187
|
+
**Ejemplo:**
|
|
188
|
+
```javascript
|
|
189
|
+
const progress = await databaseService.getOverallProgress();
|
|
190
|
+
console.log(`Progress: ${progress.percentComplete}%`);
|
|
191
|
+
console.log(`ETA: ${progress.estimatedTimeRemaining}`);
|
|
192
|
+
console.log(`Success rate: ${
|
|
193
|
+
(progress.processedFiles / progress.totalFiles * 100).toFixed(2)
|
|
194
|
+
}%`);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### 12. `getFileDetails(filePath)`
|
|
198
|
+
Obtiene detalles completos de un archivo.
|
|
199
|
+
|
|
200
|
+
**Parámetros:**
|
|
201
|
+
- `filePath` (string): Ruta del archivo
|
|
202
|
+
|
|
203
|
+
**Retorna:** Object con todos los campos + información adicional
|
|
204
|
+
|
|
205
|
+
**Ejemplo:**
|
|
206
|
+
```javascript
|
|
207
|
+
const details = await databaseService.getFileDetails(filePath);
|
|
208
|
+
console.log(`
|
|
209
|
+
Filename: ${details.filename}
|
|
210
|
+
Status: ${details.processing_status}
|
|
211
|
+
Time in queue: ${details.timeInQueue}
|
|
212
|
+
Attempts: ${details.attempts}
|
|
213
|
+
Has error: ${details.hasError}
|
|
214
|
+
`);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### 13. `searchFiles(filters)`
|
|
218
|
+
Busca archivos por múltiples criterios.
|
|
219
|
+
|
|
220
|
+
**Parámetros:**
|
|
221
|
+
- `filters` (object):
|
|
222
|
+
- `status` (string): Status a buscar
|
|
223
|
+
- `rfc` (string): RFC del remitente
|
|
224
|
+
- `year` (number): Año
|
|
225
|
+
- `dirPath` (string): Ruta del directorio
|
|
226
|
+
- `numPedimento` (string): Número de pedimento (búsqueda parcial)
|
|
227
|
+
- `limit` (number): Máximo de resultados (default: 50)
|
|
228
|
+
|
|
229
|
+
**Ejemplo:**
|
|
230
|
+
```javascript
|
|
231
|
+
const results = await databaseService.searchFiles({
|
|
232
|
+
status: 'FAILED',
|
|
233
|
+
rfc: 'AUM9207011CA',
|
|
234
|
+
year: 2023,
|
|
235
|
+
limit: 50
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## 🔄 Flujo de Procesamiento
|
|
240
|
+
|
|
241
|
+
### Escenario Típico
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
// 1. Archivo nuevo detectado en el directorio
|
|
245
|
+
const file = await databaseService.insertFileToUploader(
|
|
246
|
+
filePath,
|
|
247
|
+
{
|
|
248
|
+
processingStatus: 'PENDING',
|
|
249
|
+
dependsOnPath: dirPath
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
console.log(`✅ Archivo registrado: ${file.filename}`);
|
|
253
|
+
|
|
254
|
+
// 2. Esperando pedimento simplificado
|
|
255
|
+
const pending = await databaseService.getPendingFilesInDirectory(dirPath);
|
|
256
|
+
console.log(`⏳ Esperando: ${pending.length} archivos`);
|
|
257
|
+
|
|
258
|
+
// 3. Se detecta pedimento simplificado
|
|
259
|
+
await databaseService.markPedimentoDetected(dirPath, pedimentoPath);
|
|
260
|
+
// ↓ Automáticamente marca PENDING → READY_TO_UPLOAD
|
|
261
|
+
|
|
262
|
+
// 4. Obtener archivos listos para procesar
|
|
263
|
+
const ready = await databaseService.getFilesWithStatus(['READY_TO_UPLOAD'], 10);
|
|
264
|
+
console.log(`✅ Listos: ${ready.length} archivos`);
|
|
265
|
+
|
|
266
|
+
// 5. Actualizar a PROCESSING
|
|
267
|
+
await databaseService.updateFileStatus(filePath, {
|
|
268
|
+
processing_status: 'PROCESSING'
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// 6. Actualizar a UPLOADED (éxito)
|
|
272
|
+
await databaseService.updateFileStatus(filePath, {
|
|
273
|
+
processing_status: 'UPLOADED',
|
|
274
|
+
upload_attempts: 1
|
|
275
|
+
});
|
|
276
|
+
console.log(`✅ Subido: ${filePath}`);
|
|
277
|
+
|
|
278
|
+
// 7. Si hay error → FAILED
|
|
279
|
+
await databaseService.updateFileStatus(filePath, {
|
|
280
|
+
processing_status: 'FAILED',
|
|
281
|
+
last_error: 'Timeout uploading to server',
|
|
282
|
+
upload_attempts: 1
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// 8. Reintentar más tarde
|
|
286
|
+
const retried = await databaseService.retryFile(filePath);
|
|
287
|
+
console.log(`🔄 Reintentando: ${filePath}`);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## 📊 Estados de Archivo
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
┌─────────┐
|
|
294
|
+
│ PENDING │ ← Archivo nuevo, esperando pedimento simplificado
|
|
295
|
+
└────┬────┘
|
|
296
|
+
│
|
|
297
|
+
│ [Pedimento detectado → markPedimentoDetected()]
|
|
298
|
+
↓
|
|
299
|
+
┌──────────────────┐
|
|
300
|
+
│ READY_TO_UPLOAD │ ← Listo para procesar
|
|
301
|
+
└────┬─────────────┘
|
|
302
|
+
│
|
|
303
|
+
│ [Comienza procesamiento → updateFileStatus('PROCESSING')]
|
|
304
|
+
↓
|
|
305
|
+
┌────────────┐
|
|
306
|
+
│ PROCESSING │ ← Siendo procesado
|
|
307
|
+
└────┬───────┘
|
|
308
|
+
│
|
|
309
|
+
├─→ [Éxito] ┌──────────┐
|
|
310
|
+
│ │ UPLOADED │ ✅ Completado
|
|
311
|
+
│ └──────────┘
|
|
312
|
+
│
|
|
313
|
+
└─→ [Error] ┌────────┐
|
|
314
|
+
│ FAILED │ ❌ Con error
|
|
315
|
+
└───┬────┘
|
|
316
|
+
│
|
|
317
|
+
│ [Reintentar → retryFile()]
|
|
318
|
+
↓
|
|
319
|
+
READY_TO_UPLOAD (reinicio del ciclo)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## 🛡️ Manejo de Errores
|
|
323
|
+
|
|
324
|
+
Todos los métodos incluyen:
|
|
325
|
+
- **Retry lógico** con exponential backoff (hasta 30 segundos)
|
|
326
|
+
- **Error logging** detallado con contexto
|
|
327
|
+
- **Transacciones seguras** (no hay datos parciales)
|
|
328
|
+
- **Validaciones** de parámetros
|
|
329
|
+
- **Recuperación automática** en caso de timeout
|
|
330
|
+
|
|
331
|
+
## 📈 Casos de Uso
|
|
332
|
+
|
|
333
|
+
### Monitoreo en Tiempo Real
|
|
334
|
+
```javascript
|
|
335
|
+
setInterval(async () => {
|
|
336
|
+
const stats = await databaseService.getProcessingStats();
|
|
337
|
+
const progress = await databaseService.getOverallProgress();
|
|
338
|
+
|
|
339
|
+
console.log(`📊 Stats:`);
|
|
340
|
+
console.log(` Progreso: ${progress.percentComplete}%`);
|
|
341
|
+
console.log(` ETA: ${progress.estimatedTimeRemaining}`);
|
|
342
|
+
console.log(` Pendientes: ${progress.pendingFiles}`);
|
|
343
|
+
console.log(` Fallidos: ${progress.failedFiles}`);
|
|
344
|
+
}, 5000); // Cada 5 segundos
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Limpieza de Archivos Huérfanos (Orfanage Cleanup)
|
|
348
|
+
```javascript
|
|
349
|
+
// Ejecutar cada 30 minutos
|
|
350
|
+
setInterval(async () => {
|
|
351
|
+
const expired = await databaseService.getExpiredWaitingFiles(1800); // 30 minutos
|
|
352
|
+
|
|
353
|
+
if (expired.length > 0) {
|
|
354
|
+
console.log(`🗑️ Limpiando ${expired.length} archivos huérfanos...`);
|
|
355
|
+
|
|
356
|
+
for (const file of expired) {
|
|
357
|
+
await databaseService.updateFileStatus(file.original_path, {
|
|
358
|
+
processing_status: 'FAILED',
|
|
359
|
+
last_error: 'Expired waiting for pedimento (30 minutes)'
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}, 1800000); // 30 minutos
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Reintentos Automáticos
|
|
367
|
+
```javascript
|
|
368
|
+
// Ejecutar cada 10 minutos
|
|
369
|
+
setInterval(async () => {
|
|
370
|
+
const result = await databaseService.retryFailedFiles(10, 3);
|
|
371
|
+
|
|
372
|
+
if (result.retried > 0) {
|
|
373
|
+
console.log(`🔄 Retrying ${result.retried} files...`);
|
|
374
|
+
}
|
|
375
|
+
}, 600000); // 10 minutos
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Búsqueda Avanzada
|
|
379
|
+
```javascript
|
|
380
|
+
// Buscar todos los archivos fallidos de un RFC en 2023
|
|
381
|
+
const files = await databaseService.searchFiles({
|
|
382
|
+
rfc: 'AUM9207011CA',
|
|
383
|
+
year: 2023,
|
|
384
|
+
status: 'FAILED'
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
console.log(`Found ${files.length} failed files for RFC ${rfc}`);
|
|
388
|
+
files.forEach(f => {
|
|
389
|
+
console.log(`- ${f.filename}: ${f.last_error}`);
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Dashboard con Estadísticas
|
|
394
|
+
```javascript
|
|
395
|
+
async function getDashboardData() {
|
|
396
|
+
const [stats, progress, failedFiles] = await Promise.all([
|
|
397
|
+
databaseService.getProcessingStats(),
|
|
398
|
+
databaseService.getOverallProgress(),
|
|
399
|
+
databaseService.getFailedFilesForRetry()
|
|
400
|
+
]);
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
stats,
|
|
404
|
+
progress,
|
|
405
|
+
failedCount: failedFiles.length,
|
|
406
|
+
successRate: (
|
|
407
|
+
progress.processedFiles / progress.totalFiles * 100
|
|
408
|
+
).toFixed(2) + '%'
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## 🔗 Integración con SmartWatchService
|
|
414
|
+
|
|
415
|
+
El DatabaseService se usa desde SmartWatchService:
|
|
416
|
+
|
|
417
|
+
```javascript
|
|
418
|
+
// En SmartWatchService.js
|
|
419
|
+
class SmartWatchService {
|
|
420
|
+
async handleNewFile(filePath) {
|
|
421
|
+
const isPedimento = await this.#isPedimentoSimplificado(filePath);
|
|
422
|
+
const parentDir = path.dirname(filePath);
|
|
423
|
+
|
|
424
|
+
if (isPedimento) {
|
|
425
|
+
// Insertar pedimento y marcar dependientes como READY
|
|
426
|
+
await this.databaseService.markPedimentoDetected(parentDir, filePath);
|
|
427
|
+
logger.info(`✅ Pedimento detected, ${pending.length} files ready`);
|
|
428
|
+
} else {
|
|
429
|
+
// Insertar archivo como PENDING
|
|
430
|
+
await this.databaseService.insertFileToUploader(filePath, {
|
|
431
|
+
processingStatus: 'PENDING',
|
|
432
|
+
dependsOnPath: parentDir,
|
|
433
|
+
size: fs.statSync(filePath).size,
|
|
434
|
+
fileExtension: path.extname(filePath).slice(1)
|
|
435
|
+
});
|
|
436
|
+
logger.info(`⏳ File registered as PENDING`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async processReadyFiles() {
|
|
441
|
+
const ready = await this.databaseService.getFilesWithStatus(
|
|
442
|
+
['READY_TO_UPLOAD'],
|
|
443
|
+
10
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
for (const file of ready) {
|
|
447
|
+
try {
|
|
448
|
+
await this.databaseService.updateFileStatus(file.original_path, {
|
|
449
|
+
processing_status: 'PROCESSING'
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const result = await this.uploadService.upload(file);
|
|
453
|
+
|
|
454
|
+
await this.databaseService.updateFileStatus(file.original_path, {
|
|
455
|
+
processing_status: 'UPLOADED',
|
|
456
|
+
upload_attempts: (file.upload_attempts || 0) + 1
|
|
457
|
+
});
|
|
458
|
+
} catch (error) {
|
|
459
|
+
await this.databaseService.updateFileStatus(file.original_path, {
|
|
460
|
+
processing_status: 'FAILED',
|
|
461
|
+
last_error: error.message,
|
|
462
|
+
upload_attempts: (file.upload_attempts || 0) + 1
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
## 📚 Tablas y Campos Soportados
|
|
471
|
+
|
|
472
|
+
### Tabla: `uploader`
|
|
473
|
+
|
|
474
|
+
**Campos utilizados por Smart Watch:**
|
|
475
|
+
- `original_path` (varchar): Ruta completa del archivo
|
|
476
|
+
- `filename` (varchar): Nombre del archivo
|
|
477
|
+
- `file_extension` (varchar): Extensión (.pdf, .xml, etc.)
|
|
478
|
+
- `size` (bigint): Tamaño en bytes
|
|
479
|
+
- `document_type` (varchar): Tipo de documento
|
|
480
|
+
- `processing_status` (varchar): PENDING | READY_TO_UPLOAD | PROCESSING | UPLOADED | FAILED
|
|
481
|
+
- `depends_on_path` (varchar): Referencia al directorio del pedimento
|
|
482
|
+
- `pedimento_detected_at` (timestamp): Cuándo se detectó el pedimento
|
|
483
|
+
- `upload_attempts` (integer): Número de intentos
|
|
484
|
+
- `last_error` (text): Último mensaje de error
|
|
485
|
+
- `created_at` (timestamp): Fecha de creación
|
|
486
|
+
- `updated_at` (timestamp): Última actualización
|
|
487
|
+
- `status` (varchar): Legacy field (pendiente, en_progreso, completado, fallido)
|
|
488
|
+
|
|
489
|
+
## ✅ Validación
|
|
490
|
+
|
|
491
|
+
- Todos los métodos pasan validación de sintaxis Node.js
|
|
492
|
+
- Incluyen error handling robusto con retry logic
|
|
493
|
+
- Compatible con Supabase JS client (v1.x y v2.x)
|
|
494
|
+
- Logs detallados para debugging
|
|
495
|
+
- No hay breaking changes en métodos existentes
|
|
496
|
+
|
|
497
|
+
## 🚀 Próximos Pasos
|
|
498
|
+
|
|
499
|
+
1. **Integración con SmartWatchService** - Usar estos métodos en el watch service
|
|
500
|
+
2. **Estadísticas en API** - Exponer endpoints REST para las estadísticas
|
|
501
|
+
3. **Dashboard** - Crear UI para monitoreo en tiempo real
|
|
502
|
+
4. **Alertas** - Notificaciones cuando hay archivos expirados o con errores
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# 🧪 Testing Manual: Watch Mode - Fase 2
|
|
2
|
+
|
|
3
|
+
## Preparación del Entorno
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Crear directorio de prueba
|
|
7
|
+
mkdir -p /tmp/watch-test
|
|
8
|
+
cd /tmp/watch-test
|
|
9
|
+
|
|
10
|
+
# Crear archivo inicial
|
|
11
|
+
echo "Test document" > test-document.txt
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Test 1: Watch Mode Básico (Strategy: batch)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Terminal 1: Iniciar watch mode
|
|
18
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
19
|
+
node src/index.js watch --directories /tmp/watch-test --strategy batch --debounce 500 --verbose
|
|
20
|
+
|
|
21
|
+
# Terminal 2: Crear archivos de prueba
|
|
22
|
+
cd /tmp/watch-test
|
|
23
|
+
|
|
24
|
+
# Crear un archivo nuevo
|
|
25
|
+
echo "New file content" > new-file.txt
|
|
26
|
+
|
|
27
|
+
# Esperar a ver el evento procesado en Terminal 1
|
|
28
|
+
|
|
29
|
+
# Crear otro archivo
|
|
30
|
+
echo "Another file" > another-file.txt
|
|
31
|
+
|
|
32
|
+
# Modificar archivo existente
|
|
33
|
+
echo "Modified content" >> new-file.txt
|
|
34
|
+
|
|
35
|
+
# Esperar a ver eventos en Terminal 1
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Test 2: Strategy Individual
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Terminal 1: Iniciar con strategy individual
|
|
42
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
43
|
+
node src/index.js watch --directories /tmp/watch-test --strategy individual --debounce 500 --verbose
|
|
44
|
+
|
|
45
|
+
# Terminal 2: Crear archivo
|
|
46
|
+
cd /tmp/watch-test
|
|
47
|
+
echo "Single file test" > single-test.txt
|
|
48
|
+
|
|
49
|
+
# Esperar a ver que SOLO este archivo se procesa
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Test 3: Strategy Full-Structure
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Crear estructura de directorios
|
|
56
|
+
mkdir -p /tmp/watch-test/documents/folder1
|
|
57
|
+
mkdir -p /tmp/watch-test/documents/folder2
|
|
58
|
+
|
|
59
|
+
echo "File 1" > /tmp/watch-test/documents/folder1/file1.txt
|
|
60
|
+
echo "File 2" > /tmp/watch-test/documents/folder2/file2.txt
|
|
61
|
+
|
|
62
|
+
# Terminal 1: Iniciar con strategy full-structure
|
|
63
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
64
|
+
node src/index.js watch --directories /tmp/watch-test/documents --strategy full-structure --debounce 1000 --verbose
|
|
65
|
+
|
|
66
|
+
# Terminal 2: Crear un nuevo archivo en subfolder
|
|
67
|
+
cd /tmp/watch-test/documents/folder1
|
|
68
|
+
echo "New test file" > test-new.txt
|
|
69
|
+
|
|
70
|
+
# Esperar a ver que TODA la estructura se procesa
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Test 4: Multiple Directories
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Crear más directorio de prueba
|
|
77
|
+
mkdir -p /tmp/watch-test2
|
|
78
|
+
echo "Test" > /tmp/watch-test2/test.txt
|
|
79
|
+
|
|
80
|
+
# Terminal 1: Watch múltiples directorios
|
|
81
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
82
|
+
node src/index.js watch --directories /tmp/watch-test,/tmp/watch-test2 --strategy batch --verbose
|
|
83
|
+
|
|
84
|
+
# Terminal 2: Crear archivos en ambas carpetas
|
|
85
|
+
echo "File in test1" > /tmp/watch-test/file1.txt
|
|
86
|
+
echo "File in test2" > /tmp/watch-test2/file2.txt
|
|
87
|
+
|
|
88
|
+
# Esperar a ver eventos de ambos directorios
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Test 5: Dry-Run Mode
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Terminal 1: Iniciar en modo dry-run
|
|
95
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
96
|
+
node src/index.js watch --directories /tmp/watch-test --strategy batch --dry-run --verbose
|
|
97
|
+
|
|
98
|
+
# Terminal 2: Crear archivo
|
|
99
|
+
cd /tmp/watch-test
|
|
100
|
+
echo "Dry run test" > dry-test.txt
|
|
101
|
+
|
|
102
|
+
# Esperar a ver "[DRY RUN]" en los logs - no debe hacer upload real
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Test 6: Debouncing (Verificar que agrupa eventos)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Terminal 1: Iniciar watch con debounce de 2 segundos
|
|
109
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
110
|
+
node src/index.js watch --directories /tmp/watch-test --strategy batch --debounce 2000 --verbose
|
|
111
|
+
|
|
112
|
+
# Terminal 2: Crear múltiples archivos rápidamente
|
|
113
|
+
cd /tmp/watch-test
|
|
114
|
+
for i in {1..5}; do
|
|
115
|
+
echo "File $i" > test-$i.txt
|
|
116
|
+
sleep 0.2 # 200ms entre archivos
|
|
117
|
+
done
|
|
118
|
+
|
|
119
|
+
# Esperar 2 segundos
|
|
120
|
+
# Debería ver un ÚNICO evento que agrupa los 5 archivos
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Test 7: Graceful Shutdown
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Terminal 1: Iniciar watch mode
|
|
127
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
128
|
+
node src/index.js watch --directories /tmp/watch-test --verbose
|
|
129
|
+
|
|
130
|
+
# Terminal 2: Crear archivo mientras está corriendo
|
|
131
|
+
cd /tmp/watch-test
|
|
132
|
+
echo "Test" > test.txt
|
|
133
|
+
|
|
134
|
+
# Terminal 1: Presionar Ctrl+C
|
|
135
|
+
|
|
136
|
+
# Esperar a ver:
|
|
137
|
+
# - "🛑 Received SIGINT, shutting down gracefully..."
|
|
138
|
+
# - "🔴 Stopping WatchService..."
|
|
139
|
+
# - "✅ WatchService stopped"
|
|
140
|
+
# - Resumen final con estadísticas
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Test 8: Ignorar Patrones
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Terminal 1: Iniciar watch ignorando archivos .tmp
|
|
147
|
+
cd /Users/jjfigueroa/gitRepos/arela-uploader
|
|
148
|
+
node src/index.js watch --directories /tmp/watch-test --ignore "*.tmp,*.bak" --verbose
|
|
149
|
+
|
|
150
|
+
# Terminal 2: Crear archivos para ignorar
|
|
151
|
+
cd /tmp/watch-test
|
|
152
|
+
echo "Should be ignored" > file.tmp
|
|
153
|
+
echo "Also ignored" > file.bak
|
|
154
|
+
echo "Should be captured" > file.txt
|
|
155
|
+
|
|
156
|
+
# Esperar: solo file.txt debe generar evento
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Puntos a Verificar en los Tests
|
|
160
|
+
|
|
161
|
+
### Logs Esperados:
|
|
162
|
+
- ✅ "🟢 WATCH MODE STARTED"
|
|
163
|
+
- ✅ "📁 Watching X director(y/ies):"
|
|
164
|
+
- ✅ "⚡ Processing event:"
|
|
165
|
+
- ✅ "📤 [STRATEGY] Upload triggered by:"
|
|
166
|
+
- ✅ "✅ Files queued for upload"
|
|
167
|
+
- ✅ "📊 Stats:"
|
|
168
|
+
|
|
169
|
+
### Eventos Esperados:
|
|
170
|
+
- `add` - Cuando se crea un archivo nuevo
|
|
171
|
+
- `change` - Cuando se modifica un archivo
|
|
172
|
+
- `unlink` - Cuando se elimina un archivo (debería ignorarse)
|
|
173
|
+
|
|
174
|
+
### Debouncing:
|
|
175
|
+
- Múltiples cambios rápidos = 1 evento procesado
|
|
176
|
+
- Esperar = nuevo evento procesado
|
|
177
|
+
|
|
178
|
+
### Graceful Shutdown:
|
|
179
|
+
- Ctrl+C debería cerrar cleanly
|
|
180
|
+
- Ver resumen final con estadísticas
|
|
181
|
+
- Sin errores no capturados
|
|
182
|
+
|
|
183
|
+
## Comandos Útiles para Limpieza
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Limpiar directorio de prueba
|
|
187
|
+
rm -rf /tmp/watch-test
|
|
188
|
+
rm -rf /tmp/watch-test2
|
|
189
|
+
|
|
190
|
+
# Ver archivos siendo creados en tiempo real
|
|
191
|
+
ls -la /tmp/watch-test
|
|
192
|
+
|
|
193
|
+
# Monitorear cambios de archivos en otra terminal
|
|
194
|
+
fswatch /tmp/watch-test # Requiere 'brew install fswatch' en macOS
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Comandos para Desarrollar
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Ejecutar linter
|
|
201
|
+
npm run format
|
|
202
|
+
|
|
203
|
+
# Ver logs en detalle
|
|
204
|
+
node src/index.js watch --directories /tmp/watch-test --verbose --clear-log
|
|
205
|
+
|
|
206
|
+
# Ver solo directorios siendo observados
|
|
207
|
+
node src/index.js watch --directories /tmp/watch-test --verbose | grep "📁"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
**Nota**: Los tests pueden ejecutarse en serie o en paralelo según sea necesario. Cada test es independiente pero usa los mismos directorios (/tmp/watch-test) que pueden necesitar cleanup entre tests.
|