@arela/uploader 1.0.7 → 1.0.8
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/.env.template +9 -0
- package/package.json +1 -1
- package/src/commands/IdentifyCommand.js +15 -15
- package/src/commands/PropagateCommand.js +4 -2
- package/src/commands/PushCommand.js +41 -20
- package/src/commands/ScanCommand.js +7 -2
- package/src/config/config.js +2 -2
- package/src/index.js +20 -0
- package/src/services/ScanApiService.js +6 -2
package/.env.template
CHANGED
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
ARELA_API_URL=https://your-arela-api-url.com
|
|
10
10
|
ARELA_API_TOKEN=your-api-token-here
|
|
11
11
|
|
|
12
|
+
# API Agencia - Configura aquí la URL y Token de la agencia activa
|
|
13
|
+
ARELA_API_AGENCIA_URL=https://agencia-api-example.com
|
|
14
|
+
ARELA_API_AGENCIA_TOKEN=your-agencia-api-token-here
|
|
15
|
+
|
|
16
|
+
# API Cliente - Configura aquí la URL y Token del cliente activo
|
|
17
|
+
ARELA_API_CLIENTE_URL=https://cliente-api-example.com
|
|
18
|
+
ARELA_API_CLIENTE_TOKEN=your-cliente-api-token-here
|
|
19
|
+
|
|
20
|
+
|
|
12
21
|
# Supabase Configuration (fallback)
|
|
13
22
|
SUPABASE_URL=https://your-supabase-url.supabase.co
|
|
14
23
|
SUPABASE_KEY=your-supabase-key-here
|
package/package.json
CHANGED
|
@@ -42,23 +42,20 @@ export class IdentifyCommand {
|
|
|
42
42
|
// Validate scan configuration (need same config as scan command)
|
|
43
43
|
appConfig.validateScanConfig();
|
|
44
44
|
|
|
45
|
-
//
|
|
45
|
+
// Determine API target
|
|
46
|
+
const apiTarget = options.api || 'default';
|
|
47
|
+
|
|
48
|
+
// Import ScanApiService dynamically and initialize with target
|
|
46
49
|
const { default: ScanApiService } = await import(
|
|
47
50
|
'../services/ScanApiService.js'
|
|
48
51
|
);
|
|
49
|
-
this.scanApiService = new ScanApiService();
|
|
50
|
-
|
|
51
|
-
// Set API target if specified
|
|
52
|
-
if (options.api) {
|
|
53
|
-
appConfig.setApiTarget(options.api);
|
|
54
|
-
this.scanApiService = new ScanApiService();
|
|
55
|
-
}
|
|
52
|
+
this.scanApiService = new ScanApiService(apiTarget);
|
|
56
53
|
|
|
57
54
|
const scanConfig = appConfig.getScanConfig();
|
|
58
55
|
const batchSize = parseInt(options.batchSize) || 100;
|
|
59
56
|
|
|
60
57
|
logger.info('🔍 Starting arela identify command');
|
|
61
|
-
logger.info(`🎯 API Target: ${
|
|
58
|
+
logger.info(`🎯 API Target: ${apiTarget}`);
|
|
62
59
|
logger.info(`📦 Batch Size: ${batchSize}`);
|
|
63
60
|
|
|
64
61
|
// Fetch all tables for this instance
|
|
@@ -273,7 +270,7 @@ export class IdentifyCommand {
|
|
|
273
270
|
arelaPath: null,
|
|
274
271
|
detectionError:
|
|
275
272
|
'FILE_NOT_FOUND: File does not exist on filesystem. May have been moved or deleted after scan.',
|
|
276
|
-
isPedimento: null,
|
|
273
|
+
isPedimento: null, // Unknown - can't determine
|
|
277
274
|
};
|
|
278
275
|
}
|
|
279
276
|
|
|
@@ -289,7 +286,7 @@ export class IdentifyCommand {
|
|
|
289
286
|
rfc: null,
|
|
290
287
|
arelaPath: null,
|
|
291
288
|
detectionError: `FILE_TOO_LARGE: File size ${(stats.size / 1024 / 1024).toFixed(2)}MB exceeds ${maxSizeBytes / 1024 / 1024}MB limit.`,
|
|
292
|
-
isPedimento: null,
|
|
289
|
+
isPedimento: null, // Unknown - can't determine
|
|
293
290
|
};
|
|
294
291
|
}
|
|
295
292
|
|
|
@@ -306,13 +303,16 @@ export class IdentifyCommand {
|
|
|
306
303
|
rfc: result.rfc,
|
|
307
304
|
arelaPath: result.arelaPath,
|
|
308
305
|
detectionError: result.error,
|
|
309
|
-
isPedimento: true,
|
|
306
|
+
isPedimento: true, // Confirmed pedimento
|
|
310
307
|
};
|
|
311
308
|
}
|
|
312
309
|
|
|
313
310
|
// If no detection, determine if it's definitely not a pedimento
|
|
314
311
|
// This helps avoid re-processing files we know aren't pedimentos
|
|
315
|
-
const isDefinitelyNotPedimento = this.#isDefinitelyNotPedimento(
|
|
312
|
+
const isDefinitelyNotPedimento = this.#isDefinitelyNotPedimento(
|
|
313
|
+
result,
|
|
314
|
+
file,
|
|
315
|
+
);
|
|
316
316
|
|
|
317
317
|
// Build descriptive error message
|
|
318
318
|
let detectionError = null;
|
|
@@ -340,7 +340,7 @@ export class IdentifyCommand {
|
|
|
340
340
|
rfc: result.rfc,
|
|
341
341
|
arelaPath: result.arelaPath,
|
|
342
342
|
detectionError,
|
|
343
|
-
isPedimento: isDefinitelyNotPedimento ? false : null,
|
|
343
|
+
isPedimento: isDefinitelyNotPedimento ? false : null, // false = not pedimento, null = unknown
|
|
344
344
|
};
|
|
345
345
|
} catch (error) {
|
|
346
346
|
logger.warn(
|
|
@@ -367,7 +367,7 @@ export class IdentifyCommand {
|
|
|
367
367
|
rfc: null,
|
|
368
368
|
arelaPath: null,
|
|
369
369
|
detectionError: `${errorCategory}: ${error.message}`,
|
|
370
|
-
isPedimento: null,
|
|
370
|
+
isPedimento: null, // Unknown - error occurred
|
|
371
371
|
};
|
|
372
372
|
}
|
|
373
373
|
}),
|
|
@@ -39,8 +39,10 @@ export class PropagateCommand {
|
|
|
39
39
|
// Step 1: Validate configuration
|
|
40
40
|
await this.#validateConfiguration();
|
|
41
41
|
|
|
42
|
-
// Step 2: Initialize API service
|
|
43
|
-
this.
|
|
42
|
+
// Step 2: Initialize API service with configured target
|
|
43
|
+
const apiTarget = this.options.api || 'default';
|
|
44
|
+
this.scanApiService = new ScanApiService(apiTarget);
|
|
45
|
+
console.log(`🎯 API Target: ${apiTarget}`);
|
|
44
46
|
|
|
45
47
|
// Step 3: Fetch all tables for this instance
|
|
46
48
|
const scanConfig = appConfig.getScanConfig();
|
|
@@ -15,7 +15,8 @@ import appConfig from '../config/config.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export class PushCommand {
|
|
17
17
|
constructor() {
|
|
18
|
-
|
|
18
|
+
// ScanApiService will be initialized in execute() with proper API target
|
|
19
|
+
this.scanApiService = null;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -37,7 +38,6 @@ export class PushCommand {
|
|
|
37
38
|
// Get configuration
|
|
38
39
|
const scanConfig = appConfig.getScanConfig();
|
|
39
40
|
const pushConfig = appConfig.getPushConfig();
|
|
40
|
-
const tableName = scanConfig.tableName;
|
|
41
41
|
|
|
42
42
|
// Override folderStructure from command option if provided
|
|
43
43
|
if (options.folderStructure) {
|
|
@@ -48,26 +48,40 @@ export class PushCommand {
|
|
|
48
48
|
.replace(/\/+$/, '');
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
const
|
|
51
|
+
// Determine API targets for scan (read) and push (upload) operations
|
|
52
|
+
// Priority: explicit scan-api/push-api > source-api/target-api > api > default
|
|
53
|
+
const scanApiTarget =
|
|
54
|
+
options.scanApi || options.sourceApi || options.api || 'default';
|
|
55
|
+
const pushApiTarget =
|
|
56
|
+
options.pushApi || options.targetApi || options.api || scanApiTarget;
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.scanApiService = new ScanApiService(); // Reinitialize with new target
|
|
58
|
-
}
|
|
58
|
+
// Initialize ScanApiService with the scan API target
|
|
59
|
+
this.scanApiService = new ScanApiService(scanApiTarget);
|
|
59
60
|
|
|
60
|
-
// Get upload API configuration
|
|
61
|
+
// Get upload API configuration for the push target
|
|
61
62
|
const uploadApiConfig = appConfig.getApiConfig(pushApiTarget);
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
// Display API configuration
|
|
65
|
+
const isCrossTenant = scanApiTarget !== pushApiTarget;
|
|
66
|
+
if (isCrossTenant) {
|
|
67
|
+
console.log('🔗 Cross-tenant mode enabled:');
|
|
68
|
+
console.log(
|
|
69
|
+
` 📖 Scan API (read): ${scanApiTarget} → ${appConfig.getApiConfig(scanApiTarget).baseUrl}`,
|
|
70
|
+
);
|
|
71
|
+
console.log(
|
|
72
|
+
` 📝 Push API (upload): ${pushApiTarget} → ${uploadApiConfig.baseUrl}`,
|
|
73
|
+
);
|
|
74
|
+
} else {
|
|
75
|
+
console.log(
|
|
76
|
+
`🎯 API Target: ${scanApiTarget} → ${uploadApiConfig.baseUrl}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
67
79
|
console.log(`📦 Fetch Batch Size: ${options.batchSize}`);
|
|
68
80
|
console.log(`📤 Upload Batch Size: ${options.uploadBatchSize}`);
|
|
69
81
|
if (pushConfig.folderStructure) {
|
|
70
|
-
console.log(
|
|
82
|
+
console.log(
|
|
83
|
+
`📁 Folder Structure Prefix: ${pushConfig.folderStructure}`,
|
|
84
|
+
);
|
|
71
85
|
}
|
|
72
86
|
|
|
73
87
|
// Apply filters
|
|
@@ -294,7 +308,6 @@ export class PushCommand {
|
|
|
294
308
|
);
|
|
295
309
|
|
|
296
310
|
// Update counters from API response
|
|
297
|
-
// Note: The CLI endpoint now handles updating the scan table directly
|
|
298
311
|
batchResults.forEach((result) => {
|
|
299
312
|
results.processed++;
|
|
300
313
|
if (result.uploaded) {
|
|
@@ -304,6 +317,17 @@ export class PushCommand {
|
|
|
304
317
|
}
|
|
305
318
|
});
|
|
306
319
|
|
|
320
|
+
// Update scan table via scanApi (supports cross-tenant mode)
|
|
321
|
+
// This is called on scanApi, not pushApi, because scan tables live on scanApi
|
|
322
|
+
try {
|
|
323
|
+
await this.scanApiService.batchUpdateUpload(tableName, batchResults);
|
|
324
|
+
} catch (updateError) {
|
|
325
|
+
logger.error(
|
|
326
|
+
`Failed to update scan table for batch: ${updateError.message}`,
|
|
327
|
+
);
|
|
328
|
+
// Don't fail the entire process, just log the error
|
|
329
|
+
}
|
|
330
|
+
|
|
307
331
|
// Update progress bar
|
|
308
332
|
const elapsed = (Date.now() - results.startTime) / 1000;
|
|
309
333
|
const speed =
|
|
@@ -329,7 +353,7 @@ export class PushCommand {
|
|
|
329
353
|
|
|
330
354
|
/**
|
|
331
355
|
* Upload a batch of files using the new CLI upload endpoint
|
|
332
|
-
*
|
|
356
|
+
* Note: Scan table is updated separately via scanApi after this batch completes
|
|
333
357
|
* @private
|
|
334
358
|
*/
|
|
335
359
|
async #uploadBatchViaCli(tableName, files, uploadApiConfig) {
|
|
@@ -368,8 +392,6 @@ export class PushCommand {
|
|
|
368
392
|
if (!fs.existsSync(file.absolute_path)) {
|
|
369
393
|
result.uploadError =
|
|
370
394
|
'FILE_NOT_FOUND: File does not exist on filesystem';
|
|
371
|
-
// Update the scan table with the error
|
|
372
|
-
await this.scanApiService.batchUpdateUpload(tableName, [result]);
|
|
373
395
|
return result;
|
|
374
396
|
}
|
|
375
397
|
|
|
@@ -377,7 +399,6 @@ export class PushCommand {
|
|
|
377
399
|
const stats = fs.statSync(file.absolute_path);
|
|
378
400
|
if (!stats.isFile()) {
|
|
379
401
|
result.uploadError = 'NOT_A_FILE: Path is not a regular file';
|
|
380
|
-
await this.scanApiService.batchUpdateUpload(tableName, [result]);
|
|
381
402
|
return result;
|
|
382
403
|
}
|
|
383
404
|
|
|
@@ -26,6 +26,7 @@ export class ScanCommand {
|
|
|
26
26
|
* Execute the scan command
|
|
27
27
|
* @param {Object} options - Command options
|
|
28
28
|
* @param {boolean} options.countFirst - Count files first for percentage-based progress
|
|
29
|
+
* @param {string} options.api - API target: 'default', 'agencia', or 'cliente'
|
|
29
30
|
*/
|
|
30
31
|
async execute(options = {}) {
|
|
31
32
|
const startTime = Date.now();
|
|
@@ -34,17 +35,21 @@ export class ScanCommand {
|
|
|
34
35
|
// Validate scan configuration
|
|
35
36
|
appConfig.validateScanConfig();
|
|
36
37
|
|
|
37
|
-
//
|
|
38
|
+
// Determine API target
|
|
39
|
+
const apiTarget = options.api || 'default';
|
|
40
|
+
|
|
41
|
+
// Import ScanApiService dynamically and initialize with target
|
|
38
42
|
const { default: ScanApiService } = await import(
|
|
39
43
|
'../services/ScanApiService.js'
|
|
40
44
|
);
|
|
41
|
-
this.scanApiService = new ScanApiService();
|
|
45
|
+
this.scanApiService = new ScanApiService(apiTarget);
|
|
42
46
|
|
|
43
47
|
const scanConfig = appConfig.getScanConfig();
|
|
44
48
|
// Ensure basePath is absolute for scan operations
|
|
45
49
|
const basePath = PathNormalizer.toAbsolutePath(appConfig.getBasePath());
|
|
46
50
|
|
|
47
51
|
logger.info('🔍 Starting arela scan command');
|
|
52
|
+
logger.info(`🎯 API Target: ${apiTarget}`);
|
|
48
53
|
logger.info(`📦 Company: ${scanConfig.companySlug}`);
|
|
49
54
|
logger.info(`🖥️ Server: ${scanConfig.serverId}`);
|
|
50
55
|
logger.info(`📂 Base Path: ${basePath}`);
|
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.8';
|
|
38
38
|
} catch (error) {
|
|
39
39
|
console.warn('⚠️ Could not read package.json version, using fallback');
|
|
40
|
-
return '1.0.
|
|
40
|
+
return '1.0.8';
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
package/src/index.js
CHANGED
|
@@ -381,6 +381,14 @@ class ArelaUploaderCLI {
|
|
|
381
381
|
'--push-api <target>',
|
|
382
382
|
'API for uploading files: default|agencia|cliente',
|
|
383
383
|
)
|
|
384
|
+
.option(
|
|
385
|
+
'--source-api <target>',
|
|
386
|
+
'Source API for reading data (cross-tenant mode): agencia|cliente',
|
|
387
|
+
)
|
|
388
|
+
.option(
|
|
389
|
+
'--target-api <target>',
|
|
390
|
+
'Target API for uploading files (cross-tenant mode): agencia|cliente',
|
|
391
|
+
)
|
|
384
392
|
.option(
|
|
385
393
|
'-b, --batch-size <size>',
|
|
386
394
|
'Number of files to fetch per batch',
|
|
@@ -406,6 +414,18 @@ class ArelaUploaderCLI {
|
|
|
406
414
|
.option('--show-stats', 'Show performance statistics')
|
|
407
415
|
.action(async (options) => {
|
|
408
416
|
try {
|
|
417
|
+
// Handle cross-tenant mode (source and target APIs)
|
|
418
|
+
// Map source-api/target-api to scan-api/push-api for consistency
|
|
419
|
+
if (options.sourceApi && options.targetApi) {
|
|
420
|
+
appConfig.setCrossTenantTargets(
|
|
421
|
+
options.sourceApi,
|
|
422
|
+
options.targetApi,
|
|
423
|
+
);
|
|
424
|
+
// Also set scan-api and push-api for PushCommand compatibility
|
|
425
|
+
options.scanApi = options.sourceApi;
|
|
426
|
+
options.pushApi = options.targetApi;
|
|
427
|
+
}
|
|
428
|
+
|
|
409
429
|
// Parse comma-separated values
|
|
410
430
|
if (options.rfcs) {
|
|
411
431
|
options.rfcs = options.rfcs
|
|
@@ -10,8 +10,12 @@ import logger from './LoggingService.js';
|
|
|
10
10
|
* Handles API communication for the arela scan command
|
|
11
11
|
*/
|
|
12
12
|
export class ScanApiService {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @param {string|null} apiTarget - API target: 'default', 'agencia', 'cliente', or null (uses active target)
|
|
15
|
+
*/
|
|
16
|
+
constructor(apiTarget = null) {
|
|
17
|
+
this.apiTarget = apiTarget;
|
|
18
|
+
const apiConfig = appConfig.getApiConfig(apiTarget);
|
|
15
19
|
this.baseUrl = apiConfig.baseUrl;
|
|
16
20
|
this.token = apiConfig.token;
|
|
17
21
|
|