@arela/uploader 0.3.0 → 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.
@@ -9,6 +9,8 @@ export class UploadServiceFactory {
9
9
  constructor() {
10
10
  this.apiService = new ApiUploadService();
11
11
  this.supabaseService = new SupabaseUploadService();
12
+ // Cache for target-specific API services
13
+ this._targetServices = new Map();
12
14
  }
13
15
 
14
16
  /**
@@ -54,6 +56,28 @@ export class UploadServiceFactory {
54
56
  throw new Error('No upload service is available');
55
57
  }
56
58
 
59
+ /**
60
+ * Get an API service configured for a specific target
61
+ * @param {string} target - API target: 'agencia', 'cliente', 'ktj931117p55', or 'default'
62
+ * @returns {Promise<ApiUploadService>} API service for the specified target
63
+ */
64
+ async getApiServiceForTarget(target) {
65
+ // Import config dynamically to avoid circular dependency
66
+ const { appConfig } = await import('../../config/config.js');
67
+
68
+ const targetConfig = appConfig.getApiConfig(target);
69
+ if (!targetConfig.baseUrl) {
70
+ throw new Error(`No API URL configured for target '${target}'`);
71
+ }
72
+
73
+ // Create a new service instance with specific config
74
+ // Use setExternalConfig to prevent automatic config refresh from overwriting
75
+ const service = new ApiUploadService();
76
+ service.setExternalConfig(targetConfig.baseUrl, targetConfig.token);
77
+
78
+ return service;
79
+ }
80
+
57
81
  /**
58
82
  * Check if API mode is currently active
59
83
  * @returns {Promise<boolean>} True if API service is available
@@ -152,8 +152,11 @@ export class FileOperations {
152
152
  */
153
153
  static listFilesInDirectory(dirPath, options = {}) {
154
154
  try {
155
- const { excludePattern = /(^|[\/\\])\.|node_modules|\.git/, onlyPdf = false } = options;
156
-
155
+ const {
156
+ excludePattern = /(^|[\/\\])\.|node_modules|\.git/,
157
+ onlyPdf = false,
158
+ } = options;
159
+
157
160
  if (!fs.existsSync(dirPath)) {
158
161
  return [];
159
162
  }
@@ -163,7 +166,7 @@ export class FileOperations {
163
166
 
164
167
  for (const entry of entries) {
165
168
  const fullPath = path.join(dirPath, entry.name);
166
-
169
+
167
170
  // Skip excluded patterns
168
171
  if (excludePattern && excludePattern.test(fullPath)) {
169
172
  continue;
@@ -210,8 +210,9 @@ export class WatchEventHandler {
210
210
  const files = [];
211
211
 
212
212
  // Get all recent files (valid, non-deleted ones)
213
- const validFiles = Array.from(this.recentFiles.entries())
214
- .filter(([_, metadata]) => metadata.event !== 'unlink');
213
+ const validFiles = Array.from(this.recentFiles.entries()).filter(
214
+ ([_, metadata]) => metadata.event !== 'unlink',
215
+ );
215
216
 
216
217
  if (validFiles.length === 0) {
217
218
  logger.debug('[EventHandler] No valid files in batch');
@@ -221,11 +222,13 @@ export class WatchEventHandler {
221
222
  // If sourceDir provided, get ALL files from that directory
222
223
  // This ensures when one file is detected, all related files are processed together
223
224
  if (sourceDir) {
224
- logger.debug(`[EventHandler] Batch: Processing all files in ${sourceDir}`);
225
-
225
+ logger.debug(
226
+ `[EventHandler] Batch: Processing all files in ${sourceDir}`,
227
+ );
228
+
226
229
  for (const [filePath, metadata] of validFiles) {
227
230
  const fileDir = path.dirname(filePath);
228
-
231
+
229
232
  // Only include files from the source directory or its subdirectories
230
233
  if (filePath.startsWith(sourceDir)) {
231
234
  try {
@@ -238,9 +241,11 @@ export class WatchEventHandler {
238
241
  event: metadata.event,
239
242
  type: 'batch',
240
243
  });
241
-
244
+
242
245
  if (files.length >= batchSize) {
243
- logger.debug(`[EventHandler] Batch size limit (${batchSize}) reached`);
246
+ logger.debug(
247
+ `[EventHandler] Batch size limit (${batchSize}) reached`,
248
+ );
244
249
  break;
245
250
  }
246
251
  } catch (error) {
@@ -254,7 +259,7 @@ export class WatchEventHandler {
254
259
  } else {
255
260
  // Fallback: Sort by timestamp (newest first) and take up to batchSize
256
261
  const sortedFiles = validFiles.sort(
257
- ([_, a], [__, b]) => b.timestamp - a.timestamp
262
+ ([_, a], [__, b]) => b.timestamp - a.timestamp,
258
263
  );
259
264
 
260
265
  for (const [filePath, metadata] of sortedFiles) {
@@ -268,7 +273,7 @@ export class WatchEventHandler {
268
273
  event: metadata.event,
269
274
  type: 'batch',
270
275
  });
271
-
276
+
272
277
  if (files.length >= batchSize) {
273
278
  break;
274
279
  }
package/.envbackup DELETED
@@ -1,37 +0,0 @@
1
- # SUPABASE_URL=https://supabase.prod.app.trader-docs.com
2
- # SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UiLCJpYXQiOjE3NDU3MzM2MDAsImV4cCI6MTkwMzUwMDAwMH0.eAoXvaBZzZa31txItwEkoLnplWG10ee-U5GMc265xwk
3
- # SUPABASE_BUCKET=sample
4
-
5
- # PATH_VARS=o:/ExpedienteElectronicko/expediente/archivos/:rfcPatente/:patente-:aduana/:year/:pedimento/ALSJDKHF ASD,KFHJA SDFK,ASLDKJFH
6
-
7
- SUPABASE_URL=http://127.0.0.1:54321
8
- SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
9
- SUPABASE_BUCKET=arela
10
-
11
- # Arela API Configuration
12
- ARELA_API_URL=http://localhost:3010
13
- ARELA_API_TOKEN=f0608f83e5faa4c6be32c19975635c4e9012b31f7249a9f0a9270f54d74ec599
14
-
15
- # Upload Configuration
16
- UPLOAD_BASE_PATH=./sample
17
- UPLOAD_SOURCES=2023|2024
18
-
19
- # RFC Upload Configuration
20
- # Pipe-separated list of RFCs to upload files for
21
- # Example: MMJ0810145N1|ABC1234567XY|DEF9876543ZZ
22
- UPLOAD_RFCS=AKS151005E46|IMS030409FZ0|RDG1107154L7|SHP031226BV2|CSM9301219B4|LIN960124HT8|LME971009SW4|AKM9707151B6|FEL000822AG2|FDM060802J54|MTM9807279B4|AUM9207011CA|MMJ0810145N1|ACC010328EQ6|PED781129JT6|CAD890407NK7|SME140411IK7|JME1903121C2|EIJ110429NF9|PTJ080414TM6|TME050503BM4
23
-
24
- # AKS151005E46|IMS030409FZ0|RDG1107154L7|SHP031226BV2|CSM9301219B4|LIN960124HT8|LME971009SW4|AKM9707151B6|FEL000822AG2|PTJ080414TM6|TME050503BM4
25
- # FDM060802J54|MTM9807279B4|AUM9207011CA|MMJ0810145N1|ACC010328EQ6|PED781129JT6|CAD890407NK7|SME140411IK7|JME1903121C2|EIJ110429NF9
26
-
27
- # Logging and Verbosity
28
- VERBOSE_LOGGING=true # Disable verbose path logging for better performance
29
- BATCH_DELAY=50 # Reduce delay between batches (default: 100ms)
30
- PROGRESS_UPDATE_INTERVAL=20 # Update progress every 20 items (default: 10)
31
-
32
- # Log Buffering
33
- LOG_BUFFER_SIZE=200 # Increase buffer size for fewer I/O ops (default: 100)
34
- LOG_FLUSH_INTERVAL=3000 # Flush logs every 3 seconds (default: 5000ms)
35
-
36
- # Example for maximum performance
37
- # VERBOSE_LOGGING=false BATCH_DELAY=25 LOG_BUFFER_SIZE=500 arela --stats-only /path/to/files
@@ -1,157 +0,0 @@
1
- # Supabase Upload Issue Fix
2
-
3
- ## Problem Description
4
-
5
- When running the CLI with `upload --force-supabase`, the logs displayed that files were being uploaded successfully, but no files appeared in the Supabase bucket.
6
-
7
- ## Root Cause
8
-
9
- The issue was in `/src/commands/UploadCommand.js` in the `#processFile` method. The code was **silently ignoring upload failures** from Supabase:
10
-
11
- ```javascript
12
- // ❌ BEFORE - Upload result was ignored
13
- if (uploadService.getServiceName() === 'Arela API') {
14
- result = await uploadService.upload([fileObject], { ...options, uploadPath });
15
- } else {
16
- // Supabase direct upload
17
- await uploadService.upload([fileObject], { uploadPath });
18
- // Result ignored! No error checking!
19
- }
20
-
21
- logger.info(`SUCCESS: ${path.basename(filePath)} -> ${uploadPath}`);
22
- ```
23
-
24
- Even if the Supabase upload failed and returned `{ success: false, error: "..." }`, the code would:
25
- 1. Ignore the error
26
- 2. Log "SUCCESS"
27
- 3. Continue processing
28
-
29
- This created the illusion that files were being uploaded when they weren't.
30
-
31
- ## Changes Made
32
-
33
- ### 1. Fixed Error Handling in `UploadCommand.js`
34
-
35
- **File:** `/src/commands/UploadCommand.js`
36
-
37
- Now properly captures and validates the Supabase upload result:
38
-
39
- ```javascript
40
- // ✅ AFTER - Upload result is captured and validated
41
- if (uploadService.getServiceName() === 'Arela API') {
42
- result = await uploadService.upload([fileObject], { ...options, uploadPath });
43
- } else {
44
- // Supabase direct upload
45
- const uploadResult = await uploadService.upload([fileObject], { uploadPath });
46
-
47
- // Check if upload was successful
48
- if (!uploadResult.success) {
49
- throw new Error(`Supabase upload failed: ${uploadResult.error}`);
50
- }
51
-
52
- result = { successCount: 1 };
53
- }
54
- ```
55
-
56
- ### 2. Enhanced Logging in `SupabaseUploadService.js`
57
-
58
- **File:** `/src/services/upload/SupabaseUploadService.js`
59
-
60
- Added detailed logging to help diagnose upload issues:
61
-
62
- #### Connection Initialization
63
- ```javascript
64
- console.log(`🔌 Connecting to Supabase: ${appConfig.supabase.url}`);
65
- console.log(`📦 Using bucket: ${this.bucket}`);
66
- console.log(`🧪 Testing bucket access...`);
67
- console.log(`✅ Successfully connected to Supabase bucket: ${this.bucket}`);
68
- ```
69
-
70
- #### Upload Operations
71
- ```javascript
72
- if (error) {
73
- console.error(`❌ Supabase upload failed for ${normalizedPath}:`, error.message);
74
- return { success: false, error: error.message };
75
- }
76
-
77
- console.log(`✅ Supabase upload successful: ${normalizedPath}`);
78
- ```
79
-
80
- #### Better Error Messages
81
- ```javascript
82
- if (!appConfig.supabase.url || !appConfig.supabase.key || !this.bucket) {
83
- const missing = [];
84
- if (!appConfig.supabase.url) missing.push('SUPABASE_URL');
85
- if (!appConfig.supabase.key) missing.push('SUPABASE_KEY');
86
- if (!this.bucket) missing.push('SUPABASE_BUCKET');
87
- throw new Error(`Missing Supabase configuration: ${missing.join(', ')}`);
88
- }
89
- ```
90
-
91
- ## Expected Behavior After Fix
92
-
93
- ### On Successful Upload
94
- ```
95
- 🔌 Connecting to Supabase: https://your-project.supabase.co
96
- 📦 Using bucket: your-bucket-name
97
- 🧪 Testing bucket access...
98
- ✅ Successfully connected to Supabase bucket: your-bucket-name
99
- ✅ Supabase upload successful: 2023/2000601/file.xml
100
- ```
101
-
102
- ### On Failed Upload
103
- ```
104
- ❌ Supabase upload failed for 2023/2000601/file.xml: [error details]
105
- Error: Supabase upload failed: [error details]
106
- ```
107
-
108
- ### On Configuration Error
109
- ```
110
- Error: Missing Supabase configuration: SUPABASE_URL, SUPABASE_KEY
111
- ```
112
-
113
- ## Common Issues to Check
114
-
115
- If uploads still fail after this fix, check:
116
-
117
- 1. **Environment Variables**
118
- ```bash
119
- echo $SUPABASE_URL
120
- echo $SUPABASE_KEY # Should be anon or service_role key
121
- echo $SUPABASE_BUCKET
122
- ```
123
-
124
- 2. **Bucket Permissions**
125
- - Check if the bucket exists in Supabase Storage
126
- - Verify the API key has write permissions to the bucket
127
- - Check bucket policies (public vs private)
128
-
129
- 3. **Network/Firewall**
130
- - Ensure the server can reach Supabase (check firewall rules)
131
- - Test connection: `curl https://your-project.supabase.co`
132
-
133
- 4. **File Size Limits**
134
- - Check Supabase storage limits for your plan
135
- - Verify file sizes are within allowed limits
136
-
137
- 5. **Path Format**
138
- - Ensure paths don't contain invalid characters
139
- - Paths are normalized (forward slashes only)
140
-
141
- ## Testing the Fix
142
-
143
- Run the upload command with verbose logging:
144
-
145
- ```bash
146
- arela upload --force-supabase -v
147
- ```
148
-
149
- You should now see:
150
- - Clear connection status messages
151
- - Per-file upload success/failure messages
152
- - Proper error messages if uploads fail
153
- - Accurate success/error counts in the summary
154
-
155
- ## Migration Notes
156
-
157
- No database migrations required. This is a code-only fix that improves error handling and logging.