@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,520 @@
|
|
|
1
|
+
# Archivo de Ejemplo: watcher.routes.js para arela-api
|
|
2
|
+
|
|
3
|
+
**Ubicación:** `arela-api/src/routes/watcher.routes.js`
|
|
4
|
+
|
|
5
|
+
Este archivo contiene la implementación completa de los endpoints REST para exposer el WatchService.
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import express from 'express';
|
|
9
|
+
import logger from '../services/LoggingService.js';
|
|
10
|
+
|
|
11
|
+
const router = express.Router();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Inicializar rutas del watcher
|
|
15
|
+
* Requiere que watchService esté disponible en el contexto de la app
|
|
16
|
+
* @param {Object} watchService - Instancia de WatchService desde arela-uploader
|
|
17
|
+
*/
|
|
18
|
+
export function initWatcherRoutes(app, watchService) {
|
|
19
|
+
/**
|
|
20
|
+
* GET /api/watcher/status
|
|
21
|
+
* Obtener estado actual del watcher
|
|
22
|
+
*
|
|
23
|
+
* @returns {Object} Estado del watcher incluyendo estadísticas
|
|
24
|
+
*/
|
|
25
|
+
router.get('/status', (req, res) => {
|
|
26
|
+
try {
|
|
27
|
+
const stats = watchService.getStats();
|
|
28
|
+
|
|
29
|
+
res.json({
|
|
30
|
+
isRunning: stats.isRunning,
|
|
31
|
+
autoProcessingEnabled: stats.autoProcessingEnabled,
|
|
32
|
+
watchedDirectories: stats.watchedDirectories,
|
|
33
|
+
activeWatchers: stats.activeWatchers,
|
|
34
|
+
allWatchersReady: watchService.allWatchersReady,
|
|
35
|
+
lastUpdated: new Date().toISOString(),
|
|
36
|
+
stats: {
|
|
37
|
+
filesAdded: stats.filesAdded,
|
|
38
|
+
filesModified: stats.filesModified,
|
|
39
|
+
filesRemoved: stats.filesRemoved,
|
|
40
|
+
uploadsTriggered: stats.uploadsTriggered,
|
|
41
|
+
pipelinesTriggered: stats.pipelinesTriggered,
|
|
42
|
+
errorsEncountered: stats.errorsEncountered,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
} catch (error) {
|
|
46
|
+
logger.error(`Error getting watcher status: ${error.message}`);
|
|
47
|
+
res.status(500).json({
|
|
48
|
+
error: 'Failed to get watcher status',
|
|
49
|
+
details: error.message,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* GET /api/watcher/queue-stats
|
|
56
|
+
* Obtener estadísticas de la cola de procesamiento
|
|
57
|
+
*
|
|
58
|
+
* @returns {Object} Estadísticas de la cola y progreso
|
|
59
|
+
*/
|
|
60
|
+
router.get('/queue-stats', async (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
const queueStats = await watchService.getQueueStats();
|
|
63
|
+
|
|
64
|
+
if (!queueStats) {
|
|
65
|
+
return res.status(500).json({
|
|
66
|
+
error: 'Could not fetch queue stats',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
res.json({
|
|
71
|
+
queue: queueStats.queue,
|
|
72
|
+
progress: queueStats.progress,
|
|
73
|
+
timestamp: queueStats.timestamp,
|
|
74
|
+
metadata: {
|
|
75
|
+
totalCapacity: queueStats.queue.total,
|
|
76
|
+
utilization:
|
|
77
|
+
((queueStats.queue.total - queueStats.queue.pending) /
|
|
78
|
+
queueStats.queue.total) *
|
|
79
|
+
100,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logger.error(`Error getting queue stats: ${error.message}`);
|
|
84
|
+
res.status(500).json({
|
|
85
|
+
error: 'Failed to get queue stats',
|
|
86
|
+
details: error.message,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* GET /api/watcher/directories
|
|
93
|
+
* Obtener lista de directorios monitoreados
|
|
94
|
+
*
|
|
95
|
+
* @returns {Object} Lista de directorios con detalles
|
|
96
|
+
*/
|
|
97
|
+
router.get('/directories', (req, res) => {
|
|
98
|
+
try {
|
|
99
|
+
const directories = Array.from(watchService.watchedDirs).map((dir) => {
|
|
100
|
+
const config = watchService.directoryConfigs.get(dir);
|
|
101
|
+
const isReady = watchService.watcherReady.get(dir) === true;
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
path: dir,
|
|
105
|
+
isReady,
|
|
106
|
+
folderStructure: config?.folderStructure || 'default',
|
|
107
|
+
config: {
|
|
108
|
+
ignored: config?.ignored || 'hidden files, node_modules, .git',
|
|
109
|
+
awaitWriteFinish: config?.awaitWriteFinish || {
|
|
110
|
+
stabilityThreshold: 300,
|
|
111
|
+
pollInterval: 100,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
res.json({
|
|
118
|
+
directories,
|
|
119
|
+
totalDirectories: directories.length,
|
|
120
|
+
readyCount: directories.filter((d) => d.isReady).length,
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.error(`Error getting watched directories: ${error.message}`);
|
|
125
|
+
res.status(500).json({
|
|
126
|
+
error: 'Failed to get watched directories',
|
|
127
|
+
details: error.message,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* POST /api/watcher/start
|
|
134
|
+
* Iniciar el watcher con directorios específicos
|
|
135
|
+
*
|
|
136
|
+
* @param {string[]} directories - Array de rutas a monitorear
|
|
137
|
+
* @param {boolean} autoProcessing - Habilitar procesamiento automático
|
|
138
|
+
* @param {string} folderStructure - Estructura de carpetas (arela, custom, etc)
|
|
139
|
+
* @returns {Object} Estado después de iniciar
|
|
140
|
+
*/
|
|
141
|
+
router.post('/start', async (req, res) => {
|
|
142
|
+
try {
|
|
143
|
+
const {
|
|
144
|
+
directories,
|
|
145
|
+
autoProcessing = true,
|
|
146
|
+
folderStructure = 'arela',
|
|
147
|
+
} = req.body;
|
|
148
|
+
|
|
149
|
+
// Validación
|
|
150
|
+
if (!directories || !Array.isArray(directories) || directories.length === 0) {
|
|
151
|
+
return res.status(400).json({
|
|
152
|
+
error: 'Invalid request',
|
|
153
|
+
details: 'directories must be a non-empty array of strings',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Validar que sean rutas válidas
|
|
158
|
+
const invalidPaths = directories.filter(
|
|
159
|
+
(dir) => typeof dir !== 'string' || dir.trim().length === 0
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (invalidPaths.length > 0) {
|
|
163
|
+
return res.status(400).json({
|
|
164
|
+
error: 'Invalid paths provided',
|
|
165
|
+
details: `${invalidPaths.length} invalid path(s)`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
logger.info(`[API] Starting watcher for ${directories.length} directories`);
|
|
170
|
+
|
|
171
|
+
// Agregar watchers para cada directorio
|
|
172
|
+
for (const dir of directories) {
|
|
173
|
+
await watchService.addWatcher(
|
|
174
|
+
dir,
|
|
175
|
+
{},
|
|
176
|
+
{ folderStructure: folderStructure || 'arela' }
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Habilitar procesamiento automático si se solicita
|
|
181
|
+
if (autoProcessing) {
|
|
182
|
+
watchService.enableAutoProcessing({ batchSize: 10 });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Iniciar watchers
|
|
186
|
+
await watchService.start();
|
|
187
|
+
|
|
188
|
+
// Esperar a que todos los watchers estén listos
|
|
189
|
+
await watchService.waitForWatchersReady();
|
|
190
|
+
|
|
191
|
+
res.json({
|
|
192
|
+
success: true,
|
|
193
|
+
message: 'Watcher started successfully',
|
|
194
|
+
stats: watchService.getStats(),
|
|
195
|
+
directoryCount: directories.length,
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
logger.error(`Error starting watcher: ${error.message}`);
|
|
200
|
+
res.status(500).json({
|
|
201
|
+
error: 'Failed to start watcher',
|
|
202
|
+
details: error.message,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* POST /api/watcher/stop
|
|
209
|
+
* Detener el watcher
|
|
210
|
+
*
|
|
211
|
+
* @returns {Object} Estadísticas finales
|
|
212
|
+
*/
|
|
213
|
+
router.post('/stop', async (req, res) => {
|
|
214
|
+
try {
|
|
215
|
+
logger.info('[API] Stopping watcher');
|
|
216
|
+
|
|
217
|
+
const finalStats = watchService.getStats();
|
|
218
|
+
await watchService.stop('API request');
|
|
219
|
+
|
|
220
|
+
res.json({
|
|
221
|
+
success: true,
|
|
222
|
+
message: 'Watcher stopped successfully',
|
|
223
|
+
finalStats,
|
|
224
|
+
timestamp: new Date().toISOString(),
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
logger.error(`Error stopping watcher: ${error.message}`);
|
|
228
|
+
res.status(500).json({
|
|
229
|
+
error: 'Failed to stop watcher',
|
|
230
|
+
details: error.message,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* POST /api/watcher/enable-auto-processing
|
|
237
|
+
* Habilitar procesamiento automático
|
|
238
|
+
*
|
|
239
|
+
* @param {number} batchSize - Tamaño de lote para procesamiento
|
|
240
|
+
* @returns {Object} Estado actualizado
|
|
241
|
+
*/
|
|
242
|
+
router.post('/enable-auto-processing', (req, res) => {
|
|
243
|
+
try {
|
|
244
|
+
const { batchSize = 10 } = req.body;
|
|
245
|
+
|
|
246
|
+
if (typeof batchSize !== 'number' || batchSize < 1) {
|
|
247
|
+
return res.status(400).json({
|
|
248
|
+
error: 'Invalid batchSize',
|
|
249
|
+
details: 'batchSize must be a positive number',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
logger.info(`[API] Enabling auto-processing with batchSize=${batchSize}`);
|
|
254
|
+
|
|
255
|
+
watchService.enableAutoProcessing({ batchSize });
|
|
256
|
+
|
|
257
|
+
res.json({
|
|
258
|
+
success: true,
|
|
259
|
+
message: 'Auto-processing enabled',
|
|
260
|
+
autoProcessingEnabled: watchService.isAutoProcessingEnabled(),
|
|
261
|
+
batchSize,
|
|
262
|
+
timestamp: new Date().toISOString(),
|
|
263
|
+
});
|
|
264
|
+
} catch (error) {
|
|
265
|
+
logger.error(`Error enabling auto-processing: ${error.message}`);
|
|
266
|
+
res.status(500).json({
|
|
267
|
+
error: 'Failed to enable auto-processing',
|
|
268
|
+
details: error.message,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* POST /api/watcher/disable-auto-processing
|
|
275
|
+
* Deshabilitar procesamiento automático
|
|
276
|
+
*
|
|
277
|
+
* @returns {Object} Estado actualizado
|
|
278
|
+
*/
|
|
279
|
+
router.post('/disable-auto-processing', (req, res) => {
|
|
280
|
+
try {
|
|
281
|
+
logger.info('[API] Disabling auto-processing');
|
|
282
|
+
|
|
283
|
+
watchService.disableAutoProcessing();
|
|
284
|
+
|
|
285
|
+
res.json({
|
|
286
|
+
success: true,
|
|
287
|
+
message: 'Auto-processing disabled',
|
|
288
|
+
autoProcessingEnabled: watchService.isAutoProcessingEnabled(),
|
|
289
|
+
timestamp: new Date().toISOString(),
|
|
290
|
+
});
|
|
291
|
+
} catch (error) {
|
|
292
|
+
logger.error(`Error disabling auto-processing: ${error.message}`);
|
|
293
|
+
res.status(500).json({
|
|
294
|
+
error: 'Failed to disable auto-processing',
|
|
295
|
+
details: error.message,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* GET /api/watcher/pipeline-status
|
|
302
|
+
* Obtener estado de pipelines en ejecución y recientes
|
|
303
|
+
*
|
|
304
|
+
* @returns {Object} Estado de pipelines y recientes
|
|
305
|
+
*/
|
|
306
|
+
router.get('/pipeline-status', (req, res) => {
|
|
307
|
+
try {
|
|
308
|
+
const stats = watchService.getStats();
|
|
309
|
+
|
|
310
|
+
res.json({
|
|
311
|
+
isProcessing: stats.pipelinesTriggered > 0,
|
|
312
|
+
activePipelines: 0, // Valor placeholder - actualizar si se mantiene historial
|
|
313
|
+
recentPipelines: {
|
|
314
|
+
total: stats.pipelinesTriggered,
|
|
315
|
+
successful: stats.pipelinesTriggered - stats.errorsEncountered,
|
|
316
|
+
failed: stats.errorsEncountered,
|
|
317
|
+
},
|
|
318
|
+
errorStats: {
|
|
319
|
+
totalErrors: stats.errorsEncountered,
|
|
320
|
+
lastError: null, // Obtener de errorTracker si es necesario
|
|
321
|
+
},
|
|
322
|
+
timestamp: new Date().toISOString(),
|
|
323
|
+
});
|
|
324
|
+
} catch (error) {
|
|
325
|
+
logger.error(`Error getting pipeline status: ${error.message}`);
|
|
326
|
+
res.status(500).json({
|
|
327
|
+
error: 'Failed to get pipeline status',
|
|
328
|
+
details: error.message,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* GET /api/watcher/health
|
|
335
|
+
* Health check del watcher
|
|
336
|
+
*
|
|
337
|
+
* @returns {Object} Estado de salud
|
|
338
|
+
*/
|
|
339
|
+
router.get('/health', (req, res) => {
|
|
340
|
+
try {
|
|
341
|
+
const stats = watchService.getStats();
|
|
342
|
+
|
|
343
|
+
const health = {
|
|
344
|
+
status: stats.isRunning ? 'healthy' : 'stopped',
|
|
345
|
+
isRunning: stats.isRunning,
|
|
346
|
+
activeWatchers: stats.activeWatchers,
|
|
347
|
+
errors: stats.errorsEncountered,
|
|
348
|
+
uptime: stats.isRunning ? 'tracking' : 'not running',
|
|
349
|
+
timestamp: new Date().toISOString(),
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const statusCode = stats.isRunning && stats.errorsEncountered === 0 ? 200 : 503;
|
|
353
|
+
res.status(statusCode).json(health);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
res.status(500).json({
|
|
356
|
+
status: 'error',
|
|
357
|
+
error: error.message,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Montar el router en la app
|
|
363
|
+
app.use('/api/watcher', router);
|
|
364
|
+
|
|
365
|
+
logger.info('✅ Watcher API routes initialized');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export default router;
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## 📋 Integración en app.js
|
|
374
|
+
|
|
375
|
+
```javascript
|
|
376
|
+
// app.js (o main.js, index.js)
|
|
377
|
+
|
|
378
|
+
import express from 'express';
|
|
379
|
+
import { initWatcherRoutes } from './routes/watcher.routes.js';
|
|
380
|
+
import watchService from '@arela/uploader/services'; // O importar directamente
|
|
381
|
+
|
|
382
|
+
const app = express();
|
|
383
|
+
|
|
384
|
+
// Middleware
|
|
385
|
+
app.use(express.json());
|
|
386
|
+
app.use(express.urlencoded({ extended: true }));
|
|
387
|
+
|
|
388
|
+
// ... otras rutas ...
|
|
389
|
+
|
|
390
|
+
// Inicializar rutas del watcher
|
|
391
|
+
initWatcherRoutes(app, watchService);
|
|
392
|
+
|
|
393
|
+
// Error handling
|
|
394
|
+
app.use((err, req, res, next) => {
|
|
395
|
+
console.error(err);
|
|
396
|
+
res.status(500).json({
|
|
397
|
+
error: 'Internal server error',
|
|
398
|
+
message: err.message,
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Iniciar servidor
|
|
403
|
+
const PORT = process.env.PORT || 3000;
|
|
404
|
+
app.listen(PORT, () => {
|
|
405
|
+
console.log(`🚀 Server running on port ${PORT}`);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
export default app;
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 🧪 Ejemplo de uso desde el frontend
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
// hooks/useWatcherMonitor.js
|
|
417
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
418
|
+
|
|
419
|
+
export const useWatcherMonitor = (pollInterval = 5000) => {
|
|
420
|
+
const [status, setStatus] = useState(null);
|
|
421
|
+
const [queue, setQueue] = useState(null);
|
|
422
|
+
const [directories, setDirectories] = useState([]);
|
|
423
|
+
const [loading, setLoading] = useState(false);
|
|
424
|
+
const [error, setError] = useState(null);
|
|
425
|
+
|
|
426
|
+
// Refrescar datos
|
|
427
|
+
const refresh = useCallback(async () => {
|
|
428
|
+
try {
|
|
429
|
+
setLoading(true);
|
|
430
|
+
setError(null);
|
|
431
|
+
|
|
432
|
+
const [statusRes, queueRes, dirRes] = await Promise.all([
|
|
433
|
+
fetch('/api/watcher/status'),
|
|
434
|
+
fetch('/api/watcher/queue-stats'),
|
|
435
|
+
fetch('/api/watcher/directories'),
|
|
436
|
+
]);
|
|
437
|
+
|
|
438
|
+
if (!statusRes.ok || !queueRes.ok || !dirRes.ok) {
|
|
439
|
+
throw new Error('Failed to fetch watcher data');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
setStatus(await statusRes.json());
|
|
443
|
+
setQueue(await queueRes.json());
|
|
444
|
+
setDirectories(await dirRes.json());
|
|
445
|
+
} catch (err) {
|
|
446
|
+
setError(err.message);
|
|
447
|
+
console.error('Error fetching watcher data:', err);
|
|
448
|
+
} finally {
|
|
449
|
+
setLoading(false);
|
|
450
|
+
}
|
|
451
|
+
}, []);
|
|
452
|
+
|
|
453
|
+
// Poll automático
|
|
454
|
+
useEffect(() => {
|
|
455
|
+
refresh(); // Fetch inicial
|
|
456
|
+
|
|
457
|
+
const interval = setInterval(refresh, pollInterval);
|
|
458
|
+
return () => clearInterval(interval);
|
|
459
|
+
}, [pollInterval, refresh]);
|
|
460
|
+
|
|
461
|
+
// Acciones
|
|
462
|
+
const startWatcher = useCallback(
|
|
463
|
+
async (dirs) => {
|
|
464
|
+
const res = await fetch('/api/watcher/start', {
|
|
465
|
+
method: 'POST',
|
|
466
|
+
headers: { 'Content-Type': 'application/json' },
|
|
467
|
+
body: JSON.stringify({
|
|
468
|
+
directories: dirs,
|
|
469
|
+
autoProcessing: true,
|
|
470
|
+
}),
|
|
471
|
+
});
|
|
472
|
+
if (!res.ok) throw new Error('Failed to start watcher');
|
|
473
|
+
await refresh();
|
|
474
|
+
return res.json();
|
|
475
|
+
},
|
|
476
|
+
[refresh]
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
const stopWatcher = useCallback(async () => {
|
|
480
|
+
const res = await fetch('/api/watcher/stop', { method: 'POST' });
|
|
481
|
+
if (!res.ok) throw new Error('Failed to stop watcher');
|
|
482
|
+
await refresh();
|
|
483
|
+
return res.json();
|
|
484
|
+
}, [refresh]);
|
|
485
|
+
|
|
486
|
+
const enableAutoProcessing = useCallback(async () => {
|
|
487
|
+
const res = await fetch('/api/watcher/enable-auto-processing', {
|
|
488
|
+
method: 'POST',
|
|
489
|
+
headers: { 'Content-Type': 'application/json' },
|
|
490
|
+
body: JSON.stringify({ batchSize: 10 }),
|
|
491
|
+
});
|
|
492
|
+
if (!res.ok) throw new Error('Failed to enable auto-processing');
|
|
493
|
+
await refresh();
|
|
494
|
+
return res.json();
|
|
495
|
+
}, [refresh]);
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
status,
|
|
499
|
+
queue,
|
|
500
|
+
directories,
|
|
501
|
+
loading,
|
|
502
|
+
error,
|
|
503
|
+
refresh,
|
|
504
|
+
startWatcher,
|
|
505
|
+
stopWatcher,
|
|
506
|
+
enableAutoProcessing,
|
|
507
|
+
};
|
|
508
|
+
};
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## 📌 Resumen de cambios necesarios en arela-api
|
|
514
|
+
|
|
515
|
+
1. ✅ Crear `routes/watcher.routes.js` (archivo completo arriba)
|
|
516
|
+
2. ✅ Importar en `app.js` o `index.js`
|
|
517
|
+
3. ✅ Asegurar que WatchService de arela-uploader esté disponible
|
|
518
|
+
4. ✅ Agregar autenticación si es necesario
|
|
519
|
+
5. ✅ Documentar endpoints en Swagger/OpenAPI
|
|
520
|
+
|