@arela/uploader 1.0.5 → 1.0.6
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/package.json
CHANGED
|
@@ -272,18 +272,17 @@ export class PushCommand {
|
|
|
272
272
|
break;
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
// Upload files in smaller batches
|
|
275
|
+
// Upload files in smaller batches using new CLI upload endpoint
|
|
276
276
|
for (let i = 0; i < files.length; i += uploadBatchSize) {
|
|
277
277
|
const uploadBatch = files.slice(i, i + uploadBatchSize);
|
|
278
|
-
const batchResults = await this.#
|
|
278
|
+
const batchResults = await this.#uploadBatchViaCli(
|
|
279
|
+
tableName,
|
|
279
280
|
uploadBatch,
|
|
280
281
|
uploadApiConfig,
|
|
281
282
|
);
|
|
282
283
|
|
|
283
|
-
// Update
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
// Update counters
|
|
284
|
+
// Update counters from API response
|
|
285
|
+
// Note: The CLI endpoint now handles updating the scan table directly
|
|
287
286
|
batchResults.forEach((result) => {
|
|
288
287
|
results.processed++;
|
|
289
288
|
if (result.uploaded) {
|
|
@@ -317,8 +316,160 @@ export class PushCommand {
|
|
|
317
316
|
}
|
|
318
317
|
|
|
319
318
|
/**
|
|
320
|
-
* Upload a batch of files
|
|
319
|
+
* Upload a batch of files using the new CLI upload endpoint
|
|
320
|
+
* The endpoint updates the CLI scan table directly
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
async #uploadBatchViaCli(tableName, files, uploadApiConfig) {
|
|
324
|
+
const pushConfig = appConfig.getPushConfig();
|
|
325
|
+
const results = [];
|
|
326
|
+
|
|
327
|
+
// Process files one by one (simpler for now, can optimize to true batch later)
|
|
328
|
+
for (const file of files) {
|
|
329
|
+
const result = await this.#uploadFileViaCli(
|
|
330
|
+
tableName,
|
|
331
|
+
file,
|
|
332
|
+
uploadApiConfig,
|
|
333
|
+
pushConfig,
|
|
334
|
+
);
|
|
335
|
+
results.push(result);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return results;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Upload a single file using the CLI upload endpoint
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
async #uploadFileViaCli(tableName, file, uploadApiConfig, pushConfig) {
|
|
346
|
+
const result = {
|
|
347
|
+
id: file.id,
|
|
348
|
+
uploaded: false,
|
|
349
|
+
uploadError: null,
|
|
350
|
+
uploadPath: null,
|
|
351
|
+
uploadedToStorageId: null,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
// Check if file exists
|
|
356
|
+
if (!fs.existsSync(file.absolute_path)) {
|
|
357
|
+
result.uploadError =
|
|
358
|
+
'FILE_NOT_FOUND: File does not exist on filesystem';
|
|
359
|
+
// Update the scan table with the error
|
|
360
|
+
await this.scanApiService.batchUpdateUpload(tableName, [result]);
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Get file stats
|
|
365
|
+
const stats = fs.statSync(file.absolute_path);
|
|
366
|
+
if (!stats.isFile()) {
|
|
367
|
+
result.uploadError = 'NOT_A_FILE: Path is not a regular file';
|
|
368
|
+
await this.scanApiService.batchUpdateUpload(tableName, [result]);
|
|
369
|
+
return result;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Construct upload path using arela_path
|
|
373
|
+
// arela_path format: RFC/Year/Patente/Aduana/Pedimento/
|
|
374
|
+
const uploadPath = `${file.arela_path}${file.file_name}`;
|
|
375
|
+
result.uploadPath = uploadPath;
|
|
376
|
+
|
|
377
|
+
// Create form data for CLI upload endpoint
|
|
378
|
+
const form = new FormData();
|
|
379
|
+
|
|
380
|
+
// Encode fileId and folderStructure in the filename
|
|
381
|
+
// Format: [fileId][folderStructure]filename
|
|
382
|
+
const folderStructure = file.arela_path.endsWith('/')
|
|
383
|
+
? file.arela_path.slice(0, -1)
|
|
384
|
+
: file.arela_path;
|
|
385
|
+
|
|
386
|
+
const encodedFilename = `[${file.id}][${folderStructure}]${file.file_name}`;
|
|
387
|
+
|
|
388
|
+
// Create a read stream with the encoded filename
|
|
389
|
+
const fileStream = fs.createReadStream(file.absolute_path);
|
|
390
|
+
form.append('files', fileStream, {
|
|
391
|
+
filename: encodedFilename,
|
|
392
|
+
contentType: this.#getMimeType(file.file_extension),
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Add required fields for CLI upload
|
|
396
|
+
form.append('tableName', tableName);
|
|
397
|
+
form.append('rfc', file.rfc);
|
|
398
|
+
form.append('bucket', pushConfig.bucket);
|
|
399
|
+
form.append('autoDetect', 'true');
|
|
400
|
+
form.append('autoOrganize', 'false');
|
|
401
|
+
form.append('batchSize', '1');
|
|
402
|
+
form.append('clientVersion', appConfig.packageVersion);
|
|
403
|
+
|
|
404
|
+
// Upload file using new CLI upload endpoint
|
|
405
|
+
const response = await fetch(
|
|
406
|
+
`${uploadApiConfig.baseUrl}/api/storage/cli-upload`,
|
|
407
|
+
{
|
|
408
|
+
method: 'POST',
|
|
409
|
+
headers: {
|
|
410
|
+
'x-api-key': uploadApiConfig.token,
|
|
411
|
+
...form.getHeaders(),
|
|
412
|
+
},
|
|
413
|
+
body: form,
|
|
414
|
+
},
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
if (!response.ok) {
|
|
418
|
+
const errorText = await response.text();
|
|
419
|
+
result.uploadError = `HTTP ${response.status}: ${errorText}`;
|
|
420
|
+
logger.error(`✗ Failed: ${file.file_name} - ${result.uploadError}`);
|
|
421
|
+
return result;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const apiResult = await response.json();
|
|
425
|
+
|
|
426
|
+
// Check response from CLI upload endpoint
|
|
427
|
+
if (apiResult.uploaded && apiResult.uploaded.length > 0) {
|
|
428
|
+
const uploadedFile = apiResult.uploaded[0];
|
|
429
|
+
result.uploaded = true;
|
|
430
|
+
result.uploadedToStorageId = uploadedFile.storageId;
|
|
431
|
+
logger.info(`✓ Uploaded: ${file.file_name} → ${uploadPath}`);
|
|
432
|
+
} else if (apiResult.errors && apiResult.errors.length > 0) {
|
|
433
|
+
const error = apiResult.errors[0];
|
|
434
|
+
result.uploadError = `UPLOAD_FAILED: ${error.error || 'Upload failed'}`;
|
|
435
|
+
logger.error(`✗ Failed: ${file.file_name} - ${result.uploadError}`);
|
|
436
|
+
} else {
|
|
437
|
+
result.uploadError = 'Unknown upload error - no files uploaded';
|
|
438
|
+
logger.error(`✗ Failed: ${file.file_name} - ${result.uploadError}`);
|
|
439
|
+
}
|
|
440
|
+
} catch (error) {
|
|
441
|
+
result.uploadError = `UPLOAD_ERROR: ${error.message}`;
|
|
442
|
+
logger.error(`✗ Error uploading ${file.file_name}:`, error.message);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Get MIME type from file extension
|
|
450
|
+
* @private
|
|
451
|
+
*/
|
|
452
|
+
#getMimeType(extension) {
|
|
453
|
+
const mimeTypes = {
|
|
454
|
+
pdf: 'application/pdf',
|
|
455
|
+
doc: 'application/msword',
|
|
456
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
457
|
+
xls: 'application/vnd.ms-excel',
|
|
458
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
459
|
+
txt: 'text/plain',
|
|
460
|
+
jpg: 'image/jpeg',
|
|
461
|
+
jpeg: 'image/jpeg',
|
|
462
|
+
png: 'image/png',
|
|
463
|
+
gif: 'image/gif',
|
|
464
|
+
xml: 'application/xml',
|
|
465
|
+
};
|
|
466
|
+
return mimeTypes[extension?.toLowerCase()] || 'application/octet-stream';
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Upload a batch of files (legacy - kept for compatibility)
|
|
321
471
|
* @private
|
|
472
|
+
* @deprecated Use #uploadBatchViaCli instead
|
|
322
473
|
*/
|
|
323
474
|
async #uploadBatch(files, uploadApiConfig) {
|
|
324
475
|
const uploadPromises = files.map((file) =>
|
package/src/config/config.js
CHANGED
|
@@ -34,10 +34,10 @@ class Config {
|
|
|
34
34
|
const __dirname = path.dirname(__filename);
|
|
35
35
|
const packageJsonPath = path.resolve(__dirname, '../../package.json');
|
|
36
36
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
37
|
-
return packageJson.version || '1.0.
|
|
37
|
+
return packageJson.version || '1.0.6';
|
|
38
38
|
} catch (error) {
|
|
39
39
|
console.warn('⚠️ Could not read package.json version, using fallback');
|
|
40
|
-
return '1.0.
|
|
40
|
+
return '1.0.6';
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -189,7 +189,7 @@ export class PathNormalizer {
|
|
|
189
189
|
|
|
190
190
|
const basePrefix = 'scan_';
|
|
191
191
|
const prefix = `${basePrefix}${sanitizedCompany}_${sanitizedServer}_`;
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
// Check if even prefix + hash exceeds limit
|
|
194
194
|
if (prefix.length + hash.length > 63) {
|
|
195
195
|
// Company/server names are too long, need to truncate them too
|
|
@@ -197,15 +197,15 @@ export class PathNormalizer {
|
|
|
197
197
|
const halfLength = Math.floor(maxCompanyServerLength / 2);
|
|
198
198
|
const companyLength = halfLength;
|
|
199
199
|
const serverLength = maxCompanyServerLength - companyLength;
|
|
200
|
-
|
|
200
|
+
|
|
201
201
|
const truncatedCompany = sanitizedCompany.substring(0, companyLength);
|
|
202
202
|
const truncatedServer = sanitizedServer.substring(0, serverLength);
|
|
203
|
-
|
|
203
|
+
|
|
204
204
|
tableName = `${basePrefix}${truncatedCompany}_${truncatedServer}_${hash}`;
|
|
205
205
|
} else {
|
|
206
206
|
// Preserve start and end of path, put hash in middle
|
|
207
207
|
const availableSpace = 63 - prefix.length - hash.length - 2; // -2 for underscores around hash
|
|
208
|
-
|
|
208
|
+
|
|
209
209
|
if (availableSpace <= 0 || !sanitizedPath) {
|
|
210
210
|
// If no space for path or path is empty, just use hash
|
|
211
211
|
tableName = `${prefix}${hash}`;
|
|
@@ -217,11 +217,13 @@ export class PathNormalizer {
|
|
|
217
217
|
const halfSpace = Math.floor(availableSpace / 2);
|
|
218
218
|
const startLength = halfSpace;
|
|
219
219
|
const endLength = availableSpace - startLength;
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
// Extract start and end portions of the sanitized path
|
|
222
222
|
const pathStart = sanitizedPath.substring(0, startLength);
|
|
223
|
-
const pathEnd = sanitizedPath.substring(
|
|
224
|
-
|
|
223
|
+
const pathEnd = sanitizedPath.substring(
|
|
224
|
+
sanitizedPath.length - endLength,
|
|
225
|
+
);
|
|
226
|
+
|
|
225
227
|
// Build table name with start, hash, and end
|
|
226
228
|
tableName = `${prefix}${pathStart}_${hash}_${pathEnd}`;
|
|
227
229
|
}
|