@arela/uploader 0.2.13 → 1.0.0

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 (43) hide show
  1. package/.env.template +66 -0
  2. package/README.md +263 -62
  3. package/docs/API_ENDPOINTS_FOR_DETECTION.md +647 -0
  4. package/docs/QUICK_REFERENCE_API_DETECTION.md +264 -0
  5. package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +200 -0
  6. package/package.json +3 -2
  7. package/scripts/cleanup-ds-store.js +109 -0
  8. package/scripts/cleanup-system-files.js +69 -0
  9. package/scripts/tests/phase-7-features.test.js +415 -0
  10. package/scripts/tests/signal-handling.test.js +275 -0
  11. package/scripts/tests/smart-watch-integration.test.js +554 -0
  12. package/scripts/tests/watch-service-integration.test.js +584 -0
  13. package/src/commands/UploadCommand.js +31 -4
  14. package/src/commands/WatchCommand.js +1342 -0
  15. package/src/config/config.js +270 -2
  16. package/src/document-type-shared.js +2 -0
  17. package/src/document-types/support-document.js +200 -0
  18. package/src/file-detection.js +9 -1
  19. package/src/index.js +163 -4
  20. package/src/services/AdvancedFilterService.js +505 -0
  21. package/src/services/AutoProcessingService.js +749 -0
  22. package/src/services/BenchmarkingService.js +381 -0
  23. package/src/services/DatabaseService.js +1019 -539
  24. package/src/services/ErrorMonitor.js +275 -0
  25. package/src/services/LoggingService.js +419 -1
  26. package/src/services/MonitoringService.js +401 -0
  27. package/src/services/PerformanceOptimizer.js +511 -0
  28. package/src/services/ReportingService.js +511 -0
  29. package/src/services/SignalHandler.js +255 -0
  30. package/src/services/SmartWatchDatabaseService.js +527 -0
  31. package/src/services/WatchService.js +783 -0
  32. package/src/services/upload/ApiUploadService.js +447 -3
  33. package/src/services/upload/MultiApiUploadService.js +233 -0
  34. package/src/services/upload/SupabaseUploadService.js +12 -5
  35. package/src/services/upload/UploadServiceFactory.js +24 -0
  36. package/src/utils/CleanupManager.js +262 -0
  37. package/src/utils/FileOperations.js +44 -0
  38. package/src/utils/WatchEventHandler.js +522 -0
  39. package/supabase/migrations/001_create_initial_schema.sql +366 -0
  40. package/supabase/migrations/002_align_with_arela_api_schema.sql +145 -0
  41. package/.envbackup +0 -37
  42. package/SUPABASE_UPLOAD_FIX.md +0 -157
  43. package/commands.md +0 -14
@@ -0,0 +1,647 @@
1
+ # Backend API Endpoints for Pedimento Detection and Propagation
2
+
3
+ This document describes the required backend API endpoints needed to support the refactored detection and propagation functionality in the uploader client.
4
+
5
+ ## Overview
6
+
7
+ The uploader client has been refactored to use API endpoints instead of direct Supabase client queries for:
8
+ 1. PDF pedimento detection process
9
+ 2. Arela path propagation to related files
10
+
11
+ This provides better abstraction, security, and control over database operations.
12
+
13
+ ## Required Endpoints
14
+
15
+ ### 1. GET /api/uploader/pdf-records
16
+
17
+ Fetch PDF records that are ready for pedimento detection processing.
18
+
19
+ #### Authentication
20
+ - Requires `x-api-key` header
21
+
22
+ #### Query Parameters
23
+
24
+ | Parameter | Type | Required | Default | Description |
25
+ |-----------|------|----------|---------|-------------|
26
+ | `offset` | integer | No | 0 | Pagination offset for records |
27
+ | `limit` | integer | No | 100 | Maximum number of records to return |
28
+ | `status` | string | No | - | Filter by status field |
29
+ | `file_extension` | string | No | - | Filter by file extension |
30
+ | `is_like_simplificado` | boolean | No | - | Filter records likely to be simplificado |
31
+
32
+ #### Example Request
33
+
34
+ ```bash
35
+ GET /api/uploader/pdf-records?offset=0&limit=100&status=fs-stats&file_extension=pdf&is_like_simplificado=true
36
+ Headers:
37
+ x-api-key: your-api-key-here
38
+ ```
39
+
40
+ #### Response Format
41
+
42
+ **Success (200 OK)**
43
+
44
+ ```json
45
+ [
46
+ {
47
+ "id": "uuid-here",
48
+ "original_path": "/path/to/file.pdf",
49
+ "filename": "file.pdf",
50
+ "file_extension": "pdf",
51
+ "status": "fs-stats"
52
+ },
53
+ ...
54
+ ]
55
+ ```
56
+
57
+ **Error Response**
58
+
59
+ ```json
60
+ {
61
+ "error": "Error message here",
62
+ "message": "Detailed error description"
63
+ }
64
+ ```
65
+
66
+ #### Implementation Notes
67
+
68
+ - Should query the `uploader` table in Supabase
69
+ - Apply filters based on query parameters
70
+ - Order results by `created_at` for consistent pagination
71
+ - Use efficient indexing on frequently filtered columns (`status`, `file_extension`, `is_like_simplificado`)
72
+ - Should support range queries for pagination: `range(offset, offset + limit - 1)`
73
+
74
+ #### SQL Query Example
75
+
76
+ ```sql
77
+ SELECT id, original_path, filename, file_extension, status
78
+ FROM uploader
79
+ WHERE status = 'fs-stats'
80
+ AND file_extension = 'pdf'
81
+ AND is_like_simplificado = true
82
+ ORDER BY created_at
83
+ LIMIT 100 OFFSET 0;
84
+ ```
85
+
86
+ ---
87
+
88
+ ### 2. PATCH /api/uploader/batch-update-detection
89
+
90
+ Batch update detection results for multiple records.
91
+
92
+ #### Authentication
93
+ - Requires `x-api-key` header
94
+
95
+ #### Request Body
96
+
97
+ ```json
98
+ {
99
+ "updates": [
100
+ {
101
+ "id": "uuid-here",
102
+ "status": "detected",
103
+ "document_type": "pedimento-simplificado",
104
+ "num_pedimento": "23 12 3456 7890123",
105
+ "arela_path": "RFC123456789/2023/3456/",
106
+ "year": "2023",
107
+ "rfc": "RFC123456789",
108
+ "message": null
109
+ },
110
+ {
111
+ "id": "uuid-here-2",
112
+ "status": "file-not-found",
113
+ "message": "File no longer exists at original path"
114
+ },
115
+ ...
116
+ ]
117
+ }
118
+ ```
119
+
120
+ #### Update Object Fields
121
+
122
+ | Field | Type | Required | Description |
123
+ |-------|------|----------|-------------|
124
+ | `id` | uuid | Yes | Record ID to update |
125
+ | `status` | string | Yes | New status: 'detected', 'not-detected', 'file-not-found', 'detection-error' |
126
+ | `document_type` | string | No | Detected document type (e.g., 'pedimento-simplificado') |
127
+ | `num_pedimento` | string | No | Detected pedimento number |
128
+ | `arela_path` | string | No | Generated Arela storage path |
129
+ | `year` | string | No | Extracted year from pedimento |
130
+ | `rfc` | string | No | Extracted RFC |
131
+ | `message` | string | No | Error message or additional notes |
132
+
133
+ #### Response Format
134
+
135
+ **Success (200 OK)**
136
+
137
+ ```json
138
+ {
139
+ "success": true,
140
+ "updated": 25,
141
+ "errors": []
142
+ }
143
+ ```
144
+
145
+ **Partial Success (200 OK)**
146
+
147
+ ```json
148
+ {
149
+ "success": true,
150
+ "updated": 23,
151
+ "errors": [
152
+ {
153
+ "id": "uuid-that-failed",
154
+ "message": "Update failed: reason"
155
+ }
156
+ ]
157
+ }
158
+ ```
159
+
160
+ **Error Response (400/500)**
161
+
162
+ ```json
163
+ {
164
+ "success": false,
165
+ "updated": 0,
166
+ "errors": [
167
+ {
168
+ "message": "Validation error or other error description"
169
+ }
170
+ ]
171
+ }
172
+ ```
173
+
174
+ #### Implementation Notes
175
+
176
+ - Should perform batch updates efficiently (use bulk UPDATE or upsert operations)
177
+ - Validate that all required fields are present
178
+ - Handle partial failures gracefully - some updates may succeed while others fail
179
+ - Consider transaction handling for data consistency
180
+ - Log all updates for audit purposes
181
+ - Should handle updates in batches of reasonable size (e.g., 100-500 records)
182
+
183
+ #### SQL Query Example (PostgreSQL)
184
+
185
+ ```sql
186
+ -- Using a CTE (Common Table Expression) for batch updates
187
+ WITH update_data(id, status, document_type, num_pedimento, arela_path, year, rfc, message) AS (
188
+ VALUES
189
+ ('uuid-1'::uuid, 'detected', 'pedimento-simplificado', '23 12 3456 7890123', 'RFC/2023/3456/', '2023', 'RFC123456789', NULL),
190
+ ('uuid-2'::uuid, 'file-not-found', NULL, NULL, NULL, NULL, NULL, 'File no longer exists')
191
+ )
192
+ UPDATE uploader AS u
193
+ SET
194
+ status = ud.status,
195
+ document_type = COALESCE(ud.document_type, u.document_type),
196
+ num_pedimento = COALESCE(ud.num_pedimento, u.num_pedimento),
197
+ arela_path = COALESCE(ud.arela_path, u.arela_path),
198
+ year = COALESCE(ud.year, u.year),
199
+ rfc = COALESCE(ud.rfc, u.rfc),
200
+ message = COALESCE(ud.message, u.message),
201
+ updated_at = NOW()
202
+ FROM update_data AS ud
203
+ WHERE u.id = ud.id;
204
+ ```
205
+
206
+ ---
207
+
208
+ ### 3. POST /api/uploader/propagate-arela-path
209
+
210
+ Execute arela_path propagation process on the backend. This endpoint handles all the logic for finding pedimentos and propagating their arela_path to related files in the same directory.
211
+
212
+ #### Authentication
213
+ - Requires `x-api-key` header
214
+
215
+ #### Request Body
216
+
217
+ ```json
218
+ {
219
+ "years": ["2023", "2024"]
220
+ }
221
+ ```
222
+
223
+ | Field | Type | Required | Description |
224
+ |-------|------|----------|-------------|
225
+ | `years` | array of strings | No | Optional year filter (e.g., ["2023", "2024"]) |
226
+
227
+ #### Example Request
228
+
229
+ ```bash
230
+ POST /api/uploader/propagate-arela-path
231
+ Headers:
232
+ x-api-key: your-api-key-here
233
+ Content-Type: application/json
234
+ Body:
235
+ {
236
+ "years": ["2023", "2024"]
237
+ }
238
+ ```
239
+
240
+ #### Response Format
241
+
242
+ **Success (200 OK)**
243
+
244
+ ```json
245
+ {
246
+ "success": true,
247
+ "processedCount": 150,
248
+ "updatedCount": 1250,
249
+ "errorCount": 2
250
+ }
251
+ ```
252
+
253
+ **Error Response**
254
+
255
+ ```json
256
+ {
257
+ "success": false,
258
+ "processedCount": 0,
259
+ "updatedCount": 0,
260
+ "errorCount": 1,
261
+ "error": "Error message here"
262
+ }
263
+ ```
264
+
265
+ #### Implementation Notes
266
+
267
+ This endpoint should perform all propagation logic server-side:
268
+
269
+ 1. **Find Pedimentos**: Query `pedimento_simplificado` records that have `arela_path` set
270
+ - Filter by `years` if provided
271
+ - Order by `created_at` for consistent processing
272
+
273
+ 2. **For Each Pedimento**:
274
+ - Extract the base directory path from `original_path`
275
+ - Extract the folder part of `arela_path` (remove filename if present)
276
+ - Find all related files in the same directory where `arela_path IS NULL`
277
+ - Update those files with the pedimento's `arela_path` and `year`
278
+
279
+ 3. **Pagination**: Process records in batches to handle large datasets
280
+ - Use cursor-based or offset pagination internally
281
+ - Handle pagination reset when updates change query results
282
+
283
+ 4. **Error Handling**: Continue processing even if some records fail
284
+ - Track errors but don't stop the entire process
285
+ - Return counts of processed, updated, and errors
286
+
287
+ #### SQL Logic Example
288
+
289
+ ```sql
290
+ -- Step 1: Find pedimentos with arela_path
291
+ WITH pedimentos AS (
292
+ SELECT id, original_path, arela_path, year
293
+ FROM uploader
294
+ WHERE document_type = 'pedimento_simplificado'
295
+ AND arela_path IS NOT NULL
296
+ AND ($1::text[] IS NULL OR year = ANY($1::text[]))
297
+ ORDER BY created_at
298
+ )
299
+ -- Step 2: For each pedimento, update related files
300
+ UPDATE uploader u
301
+ SET
302
+ arela_path = p.arela_path,
303
+ year = p.year,
304
+ updated_at = NOW()
305
+ FROM pedimentos p
306
+ WHERE u.arela_path IS NULL
307
+ AND u.original_path LIKE (SELECT regexp_replace(p.original_path, '/[^/]+$', '') || '%')
308
+ AND u.id != p.id;
309
+ ```
310
+
311
+ **Note**: The actual implementation should process pedimentos iteratively with proper pagination and error handling, not in a single query.
312
+
313
+ #### Performance Considerations
314
+
315
+ - Process in batches of 50-100 pedimentos at a time
316
+ - Use batch updates (50-100 files per update query)
317
+ - Add appropriate database indexes on `original_path`, `arela_path`, `document_type`, `year`
318
+ - Consider using background jobs for large datasets
319
+ - Log progress for monitoring
320
+ - Handle transaction boundaries appropriately
321
+
322
+ ---
323
+
324
+ ## RFC-Based Upload Endpoints
325
+
326
+ The following endpoints support RFC-based file upload operations.
327
+
328
+ ### 4. GET /api/uploader/rfc-file-count
329
+
330
+ Get count of files for specified RFCs.
331
+
332
+ #### Authentication
333
+ - Requires `x-api-key` header
334
+
335
+ #### Query Parameters
336
+
337
+ | Parameter | Type | Required | Default | Description |
338
+ |-----------|------|----------|---------|-------------|
339
+ | `rfcs` | string | Yes | - | Comma-separated list of RFC values (e.g., "RFC123,RFC456") |
340
+
341
+ #### Example Request
342
+
343
+ ```bash
344
+ GET /api/uploader/rfc-file-count?rfcs=RFC123456789,RFC987654321
345
+ Headers:
346
+ x-api-key: your-api-key-here
347
+ ```
348
+
349
+ #### Response Format
350
+
351
+ **Success (200 OK)**
352
+
353
+ ```json
354
+ {
355
+ "count": 1250
356
+ }
357
+ ```
358
+
359
+ #### SQL Query Example
360
+
361
+ ```sql
362
+ SELECT COUNT(*) as count
363
+ FROM uploader
364
+ WHERE rfc = ANY($1::text[])
365
+ AND arela_path IS NOT NULL;
366
+ ```
367
+
368
+ ---
369
+
370
+ ### 5. GET /api/uploader/pedimentos-by-rfc
371
+
372
+ Fetch pedimento_simplificado records for specified RFCs.
373
+
374
+ #### Authentication
375
+ - Requires `x-api-key` header
376
+
377
+ #### Query Parameters
378
+
379
+ | Parameter | Type | Required | Default | Description |
380
+ |-----------|------|----------|---------|-------------|
381
+ | `rfcs` | string | Yes | - | Comma-separated list of RFC values |
382
+ | `years` | string | No | - | Optional comma-separated list of years to filter |
383
+ | `offset` | integer | No | 0 | Pagination offset |
384
+ | `limit` | integer | No | 500 | Maximum number of records to return |
385
+
386
+ #### Example Request
387
+
388
+ ```bash
389
+ GET /api/uploader/pedimentos-by-rfc?rfcs=RFC123456789,RFC987654321&years=2023,2024&offset=0&limit=500
390
+ Headers:
391
+ x-api-key: your-api-key-here
392
+ ```
393
+
394
+ #### Response Format
395
+
396
+ **Success (200 OK)**
397
+
398
+ ```json
399
+ [
400
+ {
401
+ "arela_path": "RFC123456789/2023/3456/"
402
+ },
403
+ {
404
+ "arela_path": "RFC123456789/2024/7890/"
405
+ }
406
+ ]
407
+ ```
408
+
409
+ #### SQL Query Example
410
+
411
+ ```sql
412
+ SELECT arela_path
413
+ FROM uploader
414
+ WHERE document_type = 'pedimento_simplificado'
415
+ AND rfc = ANY($1::text[])
416
+ AND arela_path IS NOT NULL
417
+ AND ($2::text[] IS NULL OR year = ANY($2::text[]))
418
+ ORDER BY created_at
419
+ LIMIT $3 OFFSET $4;
420
+ ```
421
+
422
+ ---
423
+
424
+ ### 6. GET /api/uploader/files-for-upload
425
+
426
+ Fetch files ready for upload by arela_path.
427
+
428
+ #### Authentication
429
+ - Requires `x-api-key` header
430
+
431
+ #### Query Parameters
432
+
433
+ | Parameter | Type | Required | Default | Description |
434
+ |-----------|------|----------|---------|-------------|
435
+ | `arela_paths` | string | Yes | - | Pipe-separated list of arela_path values |
436
+ | `offset` | integer | No | 0 | Pagination offset |
437
+ | `limit` | integer | No | 1000 | Maximum number of records to return |
438
+
439
+ #### Example Request
440
+
441
+ ```bash
442
+ GET /api/uploader/files-for-upload?arela_paths=RFC123/2023/3456/|RFC456/2024/7890/&offset=0&limit=1000
443
+ Headers:
444
+ x-api-key: your-api-key-here
445
+ ```
446
+
447
+ #### Response Format
448
+
449
+ **Success (200 OK)**
450
+
451
+ ```json
452
+ [
453
+ {
454
+ "id": "uuid-here",
455
+ "original_path": "/path/to/file.xml",
456
+ "arela_path": "RFC123456789/2023/3456/",
457
+ "filename": "file.xml",
458
+ "rfc": "RFC123456789",
459
+ "document_type": "support_document"
460
+ },
461
+ ...
462
+ ]
463
+ ```
464
+
465
+ #### SQL Query Example
466
+
467
+ ```sql
468
+ SELECT id, original_path, arela_path, filename, rfc, document_type
469
+ FROM uploader
470
+ WHERE arela_path = ANY($1::text[])
471
+ AND status != 'file-uploaded'
472
+ AND original_path IS NOT NULL
473
+ ORDER BY created_at
474
+ LIMIT $2 OFFSET $3;
475
+ ```
476
+
477
+ ---
478
+
479
+ ### 7. PATCH /api/uploader/batch-update-status
480
+
481
+ Batch update file status after upload operations.
482
+
483
+ #### Authentication
484
+ - Requires `x-api-key` header
485
+
486
+ #### Request Body
487
+
488
+ ```json
489
+ {
490
+ "updates": [
491
+ {
492
+ "id": "uuid-here",
493
+ "status": "file-uploaded",
494
+ "message": "Successfully uploaded",
495
+ "processing_status": "UPLOADED"
496
+ },
497
+ {
498
+ "id": "uuid-here-2",
499
+ "status": "upload-error",
500
+ "message": "Upload failed: connection timeout"
501
+ }
502
+ ]
503
+ }
504
+ ```
505
+
506
+ #### Update Object Fields
507
+
508
+ | Field | Type | Required | Description |
509
+ |-------|------|----------|-------------|
510
+ | `id` | uuid | Yes | Record ID to update |
511
+ | `status` | string | Yes | New status value |
512
+ | `message` | string | No | Status message |
513
+ | `processing_status` | string | No | Processing queue status |
514
+
515
+ #### Response Format
516
+
517
+ **Success (200 OK)**
518
+
519
+ ```json
520
+ {
521
+ "success": true,
522
+ "updated": 25,
523
+ "errors": []
524
+ }
525
+ ```
526
+
527
+ **Partial Success (200 OK)**
528
+
529
+ ```json
530
+ {
531
+ "success": true,
532
+ "updated": 23,
533
+ "errors": [
534
+ {
535
+ "id": "uuid-that-failed",
536
+ "message": "Update failed: reason"
537
+ }
538
+ ]
539
+ }
540
+ ```
541
+
542
+ #### SQL Query Example
543
+
544
+ ```sql
545
+ WITH update_data(id, status, message, processing_status) AS (
546
+ VALUES
547
+ ('uuid-1'::uuid, 'file-uploaded', 'Success', 'UPLOADED'),
548
+ ('uuid-2'::uuid, 'upload-error', 'Failed', NULL)
549
+ )
550
+ UPDATE uploader AS u
551
+ SET
552
+ status = COALESCE(ud.status, u.status),
553
+ message = COALESCE(ud.message, u.message),
554
+ processing_status = COALESCE(ud.processing_status, u.processing_status),
555
+ updated_at = NOW()
556
+ FROM update_data AS ud
557
+ WHERE u.id = ud.id;
558
+ ```
559
+
560
+ ---
561
+
562
+ ## Migration Path
563
+
564
+ ### Phase 1: Implementation (Backend)
565
+ 1. Implement all five endpoints in the backend API
566
+ 2. Add proper authentication and authorization
567
+ 3. Add rate limiting and request validation
568
+ 4. Test endpoints with sample data
569
+
570
+ ### Phase 2: Testing (Client)
571
+ 1. Test the refactored client code with the new endpoints
572
+ 2. Performance testing with large datasets
573
+ 3. Verify pagination works correctly
574
+
575
+ ### Phase 3: Deployment
576
+ 1. Deploy backend API changes
577
+ 2. Update client configuration to use API endpoints
578
+ 3. Monitor logs for any issues
579
+ 4. Phase out direct Supabase access completely
580
+
581
+ ## Security Considerations
582
+
583
+ 1. **Authentication**: All endpoints require valid `x-api-key` header
584
+ 2. **Rate Limiting**: Implement rate limiting to prevent abuse
585
+ 3. **Input Validation**: Validate all query parameters and request body data
586
+ 4. **SQL Injection Prevention**: Use parameterized queries or ORM
587
+ 5. **Authorization**: Ensure API keys have appropriate permissions
588
+ 6. **Audit Logging**: Log all data access and modifications
589
+ 7. **Path Traversal**: Validate `basePath` parameter to prevent directory traversal attacks
590
+
591
+ ## Performance Considerations
592
+
593
+ 1. **Indexing**: Add database indexes on commonly filtered columns:
594
+ - `status`
595
+ - `file_extension`
596
+ - `is_like_simplificado`
597
+ - `created_at`
598
+ - `document_type`
599
+ - `arela_path`
600
+ - `original_path` (consider using pg_trgm for LIKE queries)
601
+ - `year`
602
+
603
+ 2. **Batch Size**: Optimize batch sizes for updates (50-100 records per batch for propagation)
604
+
605
+ 3. **Connection Pooling**: Use database connection pooling to handle concurrent requests
606
+
607
+ 4. **Caching**: Consider caching frequently accessed data
608
+
609
+ 5. **Pagination**: Implement efficient pagination with cursors or offset/limit
610
+
611
+ 6. **LIKE Query Optimization**: Use proper indexes or consider full-text search for path matching
612
+
613
+ ## Error Handling
614
+
615
+ Both endpoints should return appropriate HTTP status codes:
616
+
617
+ - **200 OK**: Successful operation
618
+ - **400 Bad Request**: Invalid parameters or request body
619
+ - **401 Unauthorized**: Missing or invalid API key
620
+ - **429 Too Many Requests**: Rate limit exceeded
621
+ - **500 Internal Server Error**: Server-side error
622
+
623
+ ## Testing Checklist
624
+
625
+ - [ ] GET /api/uploader/pdf-records returns correct records with filters
626
+ - [ ] GET /api/uploader/pdf-records handles pagination correctly
627
+ - [ ] GET /api/uploader/pdf-records returns empty array when no records found
628
+ - [ ] PATCH /api/uploader/batch-update-detection successfully updates records
629
+ - [ ] PATCH /api/uploader/batch-update-detection handles partial failures gracefully
630
+ - [ ] PATCH /api/uploader/batch-update-detection validates required fields
631
+ - [ ] GET /api/uploader/pedimentos-for-propagation returns correct pedimento records
632
+ - [ ] GET /api/uploader/pedimentos-for-propagation handles year filtering correctly
633
+ - [ ] GET /api/uploader/related-files finds files in same directory
634
+ - [ ] GET /api/uploader/related-files excludes pedimento record correctly
635
+ - [ ] PATCH /api/uploader/batch-update-arela-path successfully updates arela_path and year
636
+ - [ ] All endpoints require valid authentication
637
+ - [ ] All endpoints handle errors appropriately
638
+ - [ ] Performance testing with 10,000+ records
639
+ - [ ] Rate limiting works as expected
640
+
641
+ ## Example Integration Code
642
+
643
+ The client code has been updated in:
644
+ - `/src/services/upload/ApiUploadService.js` - API methods for detection and propagation
645
+ - `/src/services/DatabaseService.js` - Detection and propagation logic using API
646
+
647
+ See the implementation for reference on how to integrate these endpoints.