@arela/uploader 0.2.8 → 0.2.10
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
package/src/config/config.js
CHANGED
|
@@ -28,10 +28,10 @@ class Config {
|
|
|
28
28
|
const __dirname = path.dirname(__filename);
|
|
29
29
|
const packageJsonPath = path.resolve(__dirname, '../../package.json');
|
|
30
30
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
31
|
-
return packageJson.version || '0.2.
|
|
31
|
+
return packageJson.version || '0.2.10';
|
|
32
32
|
} catch (error) {
|
|
33
33
|
console.warn('⚠️ Could not read package.json version, using fallback');
|
|
34
|
-
return '0.2.
|
|
34
|
+
return '0.2.10';
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -72,10 +72,15 @@ class Config {
|
|
|
72
72
|
.map((s) => s.trim())
|
|
73
73
|
.filter(Boolean);
|
|
74
74
|
|
|
75
|
+
const uploadYears = process.env.UPLOAD_YEARS?.split('|')
|
|
76
|
+
.map((s) => s.trim())
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
|
|
75
79
|
return {
|
|
76
80
|
basePath,
|
|
77
81
|
sources,
|
|
78
82
|
rfcs: uploadRfcs,
|
|
83
|
+
years: uploadYears,
|
|
79
84
|
};
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -557,135 +557,226 @@ export class DatabaseService {
|
|
|
557
557
|
const supabase = await this.#getSupabaseClient();
|
|
558
558
|
|
|
559
559
|
logger.info('Phase 3: Starting arela_path and year propagation process...');
|
|
560
|
-
console.log(
|
|
561
|
-
'🔍 Finding pedimento_simplificado records with arela_path and year...',
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
// Get all pedimento_simplificado records that have arela_path
|
|
565
|
-
const { data: pedimentoRecords, error: pedimentoError } = await supabase
|
|
566
|
-
.from('uploader')
|
|
567
|
-
.select('id, original_path, arela_path, filename, year')
|
|
568
|
-
.eq('document_type', 'pedimento_simplificado')
|
|
569
|
-
.not('arela_path', 'is', null);
|
|
570
|
-
|
|
571
|
-
if (pedimentoError) {
|
|
572
|
-
const errorMsg = `Error fetching pedimento records: ${pedimentoError.message}`;
|
|
573
|
-
logger.error(errorMsg);
|
|
574
|
-
throw new Error(errorMsg);
|
|
575
|
-
}
|
|
560
|
+
console.log('🔍 Processing pedimento_simplificado records page by page...');
|
|
576
561
|
|
|
577
|
-
|
|
578
|
-
|
|
562
|
+
// Log year filtering configuration
|
|
563
|
+
if (appConfig.upload.years && appConfig.upload.years.length > 0) {
|
|
564
|
+
logger.info(
|
|
565
|
+
`🗓️ Year filter enabled: ${appConfig.upload.years.join(', ')}`,
|
|
566
|
+
);
|
|
579
567
|
console.log(
|
|
580
|
-
|
|
568
|
+
`🗓️ Year filter enabled: ${appConfig.upload.years.join(', ')}`,
|
|
581
569
|
);
|
|
582
|
-
|
|
570
|
+
} else {
|
|
571
|
+
logger.info('🗓️ No year filter configured - processing all years');
|
|
572
|
+
console.log('🗓️ No year filter configured - processing all years');
|
|
583
573
|
}
|
|
584
574
|
|
|
585
|
-
console.log(
|
|
586
|
-
`📋 Found ${pedimentoRecords.length} pedimento records with arela_path`,
|
|
587
|
-
);
|
|
588
|
-
logger.info(
|
|
589
|
-
`Found ${pedimentoRecords.length} pedimento records with arela_path to process`,
|
|
590
|
-
);
|
|
591
|
-
|
|
592
575
|
let totalProcessed = 0;
|
|
593
576
|
let totalUpdated = 0;
|
|
594
577
|
let totalErrors = 0;
|
|
578
|
+
let offset = 0;
|
|
579
|
+
const pageSize = 50;
|
|
580
|
+
let hasMoreData = true;
|
|
581
|
+
let pageNumber = 1;
|
|
595
582
|
const BATCH_SIZE = 50; // Process files in batches
|
|
596
583
|
|
|
597
|
-
// Process
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
584
|
+
// Process pedimento records page by page for memory efficiency
|
|
585
|
+
while (hasMoreData) {
|
|
586
|
+
logger.info(
|
|
587
|
+
`Fetching and processing pedimento records page ${pageNumber} (records ${offset + 1} to ${offset + pageSize})...`,
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
let query = supabase
|
|
591
|
+
.from('uploader')
|
|
592
|
+
.select('id, original_path, arela_path, filename, year')
|
|
593
|
+
.eq('document_type', 'pedimento_simplificado')
|
|
594
|
+
.not('arela_path', 'is', null);
|
|
601
595
|
|
|
602
|
-
|
|
603
|
-
|
|
596
|
+
// Add year filter if UPLOAD_YEARS is configured
|
|
597
|
+
if (appConfig.upload.years && appConfig.upload.years.length > 0) {
|
|
598
|
+
query = query.in('year', appConfig.upload.years);
|
|
599
|
+
}
|
|
604
600
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
);
|
|
601
|
+
const { data: pedimentoPage, error: pedimentoError } = await query
|
|
602
|
+
.range(offset, offset + pageSize - 1)
|
|
603
|
+
.order('created_at');
|
|
608
604
|
|
|
609
|
-
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
? existingPath
|
|
615
|
-
: existingPath + '/';
|
|
605
|
+
if (pedimentoError) {
|
|
606
|
+
const errorMsg = `Error fetching pedimento records page ${pageNumber}: ${pedimentoError.message}`;
|
|
607
|
+
logger.error(errorMsg);
|
|
608
|
+
throw new Error(errorMsg);
|
|
609
|
+
}
|
|
616
610
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
.is('arela_path', null)
|
|
623
|
-
.neq('id', pedimento.id); // Exclude the pedimento itself
|
|
611
|
+
if (!pedimentoPage || pedimentoPage.length === 0) {
|
|
612
|
+
hasMoreData = false;
|
|
613
|
+
logger.info('No more pedimento records found');
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
624
616
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
);
|
|
629
|
-
totalErrors++;
|
|
630
|
-
continue;
|
|
631
|
-
}
|
|
617
|
+
logger.info(
|
|
618
|
+
`Processing page ${pageNumber}: ${pedimentoPage.length} pedimento records`,
|
|
619
|
+
);
|
|
632
620
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
621
|
+
// Process each pedimento record in the current page
|
|
622
|
+
for (const pedimento of pedimentoPage) {
|
|
623
|
+
try {
|
|
624
|
+
totalProcessed++;
|
|
637
625
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
);
|
|
626
|
+
// Extract base path from original_path (remove filename)
|
|
627
|
+
const basePath = path.dirname(pedimento.original_path);
|
|
641
628
|
|
|
642
|
-
|
|
643
|
-
|
|
629
|
+
logger.info(
|
|
630
|
+
`Processing pedimento: ${pedimento.filename} | Base path: ${basePath} | Year: ${pedimento.year || 'N/A'}`,
|
|
631
|
+
);
|
|
644
632
|
|
|
645
|
-
|
|
646
|
-
const
|
|
647
|
-
const
|
|
648
|
-
|
|
633
|
+
// Extract folder part from existing arela_path
|
|
634
|
+
const existingPath = pedimento.arela_path;
|
|
635
|
+
const folderArelaPath = existingPath.includes('/')
|
|
636
|
+
? existingPath.substring(0, existingPath.lastIndexOf('/')) + '/'
|
|
637
|
+
: existingPath.endsWith('/')
|
|
638
|
+
? existingPath
|
|
639
|
+
: existingPath + '/';
|
|
640
|
+
|
|
641
|
+
// Process related files page by page for memory efficiency
|
|
642
|
+
let relatedFilesFrom = 0;
|
|
643
|
+
const relatedFilesPageSize = 50;
|
|
644
|
+
let hasMoreRelatedFiles = true;
|
|
645
|
+
let relatedFilesPageNumber = 1;
|
|
646
|
+
let totalRelatedFilesProcessed = 0;
|
|
649
647
|
|
|
650
648
|
logger.info(
|
|
651
|
-
`
|
|
649
|
+
`Searching and processing related files in base path: ${basePath}`,
|
|
652
650
|
);
|
|
653
651
|
|
|
654
|
-
|
|
655
|
-
const { error:
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
652
|
+
while (hasMoreRelatedFiles) {
|
|
653
|
+
const { data: relatedFilesPage, error: relatedError } =
|
|
654
|
+
await supabase
|
|
655
|
+
.from('uploader')
|
|
656
|
+
.select('id, filename, original_path')
|
|
657
|
+
.like('original_path', `${basePath}%`)
|
|
658
|
+
.is('arela_path', null)
|
|
659
|
+
.neq('id', pedimento.id) // Exclude the pedimento itself
|
|
660
|
+
.range(
|
|
661
|
+
relatedFilesFrom,
|
|
662
|
+
relatedFilesFrom + relatedFilesPageSize - 1,
|
|
663
|
+
);
|
|
662
664
|
|
|
663
|
-
if (
|
|
665
|
+
if (relatedError) {
|
|
664
666
|
logger.error(
|
|
665
|
-
`Error
|
|
667
|
+
`Error finding related files for ${pedimento.filename}: ${relatedError.message}`,
|
|
666
668
|
);
|
|
667
669
|
totalErrors++;
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (!relatedFilesPage || relatedFilesPage.length === 0) {
|
|
674
|
+
hasMoreRelatedFiles = false;
|
|
675
|
+
if (totalRelatedFilesProcessed === 0) {
|
|
676
|
+
logger.info(`No related files found for ${pedimento.filename}`);
|
|
677
|
+
}
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
logger.info(
|
|
682
|
+
`Processing related files page ${relatedFilesPageNumber}: ${relatedFilesPage.length} files for ${pedimento.filename}`,
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
// Process this page of related files in batches
|
|
686
|
+
const pageFileIds = relatedFilesPage.map((f) => f.id);
|
|
687
|
+
|
|
688
|
+
for (let i = 0; i < pageFileIds.length; i += BATCH_SIZE) {
|
|
689
|
+
const batchIds = pageFileIds.slice(i, i + BATCH_SIZE);
|
|
690
|
+
const batchNumber =
|
|
691
|
+
Math.floor(relatedFilesFrom / BATCH_SIZE) +
|
|
692
|
+
Math.floor(i / BATCH_SIZE) +
|
|
693
|
+
1;
|
|
694
|
+
|
|
695
|
+
logger.info(
|
|
696
|
+
`Updating batch ${batchNumber}: ${batchIds.length} files with arela_path and year...`,
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
try {
|
|
700
|
+
const { error: updateError } = await supabase
|
|
701
|
+
.from('uploader')
|
|
702
|
+
.update({
|
|
703
|
+
arela_path: folderArelaPath,
|
|
704
|
+
year: pedimento.year,
|
|
705
|
+
})
|
|
706
|
+
.in('id', batchIds);
|
|
707
|
+
|
|
708
|
+
if (updateError) {
|
|
709
|
+
logger.error(
|
|
710
|
+
`Error in batch ${batchNumber}: ${updateError.message}`,
|
|
711
|
+
);
|
|
712
|
+
totalErrors++;
|
|
713
|
+
} else {
|
|
714
|
+
totalUpdated += batchIds.length;
|
|
715
|
+
totalRelatedFilesProcessed += batchIds.length;
|
|
716
|
+
logger.info(
|
|
717
|
+
`Successfully updated batch ${batchNumber}: ${batchIds.length} files with arela_path and year`,
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
} catch (batchError) {
|
|
721
|
+
logger.error(
|
|
722
|
+
`Exception in batch ${batchNumber}: ${batchError.message}`,
|
|
723
|
+
);
|
|
724
|
+
totalErrors++;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Check if we need to fetch the next page of related files
|
|
729
|
+
if (relatedFilesPage.length < relatedFilesPageSize) {
|
|
730
|
+
hasMoreRelatedFiles = false;
|
|
731
|
+
logger.info(
|
|
732
|
+
`Completed processing related files for ${pedimento.filename}. Total processed: ${totalRelatedFilesProcessed}`,
|
|
733
|
+
);
|
|
668
734
|
} else {
|
|
669
|
-
|
|
735
|
+
relatedFilesFrom += relatedFilesPageSize;
|
|
736
|
+
relatedFilesPageNumber++;
|
|
670
737
|
logger.info(
|
|
671
|
-
`
|
|
738
|
+
`Page ${relatedFilesPageNumber - 1} complete: ${relatedFilesPage.length} files processed, continuing to next page...`,
|
|
672
739
|
);
|
|
673
740
|
}
|
|
674
|
-
} catch (batchError) {
|
|
675
|
-
logger.error(
|
|
676
|
-
`Exception in batch ${batchNumber}: ${batchError.message}`,
|
|
677
|
-
);
|
|
678
|
-
totalErrors++;
|
|
679
741
|
}
|
|
742
|
+
} catch (error) {
|
|
743
|
+
logger.error(
|
|
744
|
+
`Error processing pedimento ${pedimento.filename}: ${error.message}`,
|
|
745
|
+
);
|
|
746
|
+
totalErrors++;
|
|
680
747
|
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Check if we need to fetch the next page
|
|
751
|
+
if (pedimentoPage.length < pageSize) {
|
|
752
|
+
hasMoreData = false;
|
|
753
|
+
logger.info(
|
|
754
|
+
`Completed processing. Last page ${pageNumber} had ${pedimentoPage.length} records`,
|
|
755
|
+
);
|
|
756
|
+
} else {
|
|
757
|
+
offset += pageSize;
|
|
758
|
+
pageNumber++;
|
|
759
|
+
logger.info(
|
|
760
|
+
`Page ${pageNumber - 1} complete: ${pedimentoPage.length} records processed, moving to next page...`,
|
|
684
761
|
);
|
|
685
|
-
totalErrors++;
|
|
686
762
|
}
|
|
687
763
|
}
|
|
688
764
|
|
|
765
|
+
// Final summary
|
|
766
|
+
if (totalProcessed === 0) {
|
|
767
|
+
logger.info('No pedimento_simplificado records with arela_path found');
|
|
768
|
+
console.log(
|
|
769
|
+
'ℹ️ No pedimento_simplificado records with arela_path found',
|
|
770
|
+
);
|
|
771
|
+
} else {
|
|
772
|
+
console.log(
|
|
773
|
+
`📋 Processed ${totalProcessed} pedimento records across ${pageNumber - 1} pages`,
|
|
774
|
+
);
|
|
775
|
+
logger.info(
|
|
776
|
+
`Processed ${totalProcessed} pedimento records across ${pageNumber - 1} pages`,
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
|
|
689
780
|
const result = {
|
|
690
781
|
processedCount: totalProcessed,
|
|
691
782
|
updatedCount: totalUpdated,
|
|
@@ -743,13 +834,21 @@ export class DatabaseService {
|
|
|
743
834
|
console.log(
|
|
744
835
|
'🎯 Finding pedimento_simplificado records for specified RFCs...',
|
|
745
836
|
);
|
|
837
|
+
|
|
838
|
+
let pedimentoQuery = supabase
|
|
839
|
+
.from('uploader')
|
|
840
|
+
.select('arela_path')
|
|
841
|
+
.eq('document_type', 'pedimento_simplificado')
|
|
842
|
+
.in('rfc', appConfig.upload.rfcs)
|
|
843
|
+
.not('arela_path', 'is', null);
|
|
844
|
+
|
|
845
|
+
// Add year filter if UPLOAD_YEARS is configured
|
|
846
|
+
if (appConfig.upload.years && appConfig.upload.years.length > 0) {
|
|
847
|
+
pedimentoQuery = pedimentoQuery.in('year', appConfig.upload.years);
|
|
848
|
+
}
|
|
849
|
+
|
|
746
850
|
const { data: pedimentoRfcRecords, error: pedimentoRfcError } =
|
|
747
|
-
await
|
|
748
|
-
.from('uploader')
|
|
749
|
-
.select('arela_path')
|
|
750
|
-
.eq('document_type', 'pedimento_simplificado')
|
|
751
|
-
.in('rfc', appConfig.upload.rfcs)
|
|
752
|
-
.not('arela_path', 'is', null);
|
|
851
|
+
await pedimentoQuery;
|
|
753
852
|
|
|
754
853
|
if (pedimentoRfcError) {
|
|
755
854
|
const errorMsg = `Error fetching pedimento RFC records: ${pedimentoRfcError.message}`;
|
|
@@ -1003,13 +1102,25 @@ export class DatabaseService {
|
|
|
1003
1102
|
console.log(
|
|
1004
1103
|
'🎯 Finding pedimento_simplificado documents for specified RFCs with arela_path...',
|
|
1005
1104
|
);
|
|
1006
|
-
|
|
1105
|
+
|
|
1106
|
+
let pedimentoReadyQuery = supabase
|
|
1007
1107
|
.from('uploader')
|
|
1008
1108
|
.select('arela_path')
|
|
1009
1109
|
.eq('document_type', 'pedimento_simplificado')
|
|
1010
1110
|
.in('rfc', uploadRfcs)
|
|
1011
1111
|
.not('arela_path', 'is', null);
|
|
1012
1112
|
|
|
1113
|
+
// Add year filter if UPLOAD_YEARS is configured
|
|
1114
|
+
if (appConfig.upload.years && appConfig.upload.years.length > 0) {
|
|
1115
|
+
pedimentoReadyQuery = pedimentoReadyQuery.in(
|
|
1116
|
+
'year',
|
|
1117
|
+
appConfig.upload.years,
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
const { data: pedimentoRecords, error: pedimentoError } =
|
|
1122
|
+
await pedimentoReadyQuery;
|
|
1123
|
+
|
|
1013
1124
|
if (pedimentoError) {
|
|
1014
1125
|
throw new Error(
|
|
1015
1126
|
`Error querying pedimento_simplificado records: ${pedimentoError.message}`,
|
|
@@ -1041,22 +1152,44 @@ export class DatabaseService {
|
|
|
1041
1152
|
for (let i = 0; i < uniqueArelaPaths.length; i += chunkSize) {
|
|
1042
1153
|
const pathChunk = uniqueArelaPaths.slice(i, i + chunkSize);
|
|
1043
1154
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
.in('arela_path', pathChunk)
|
|
1050
|
-
.neq('status', 'file-uploaded')
|
|
1051
|
-
.not('original_path', 'is', null);
|
|
1155
|
+
// Query with pagination to get all results (Supabase default limit is 1000)
|
|
1156
|
+
let chunkFiles = [];
|
|
1157
|
+
let from = 0;
|
|
1158
|
+
const pageSize = 1000;
|
|
1159
|
+
let hasMoreData = true;
|
|
1052
1160
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1161
|
+
while (hasMoreData) {
|
|
1162
|
+
const { data: pageData, error: chunkError } = await supabase
|
|
1163
|
+
.from('uploader')
|
|
1164
|
+
.select(
|
|
1165
|
+
'id, original_path, arela_path, filename, rfc, document_type, status',
|
|
1166
|
+
)
|
|
1167
|
+
.in('arela_path', pathChunk)
|
|
1168
|
+
.neq('status', 'file-uploaded')
|
|
1169
|
+
.not('original_path', 'is', null)
|
|
1170
|
+
.range(from, from + pageSize - 1);
|
|
1171
|
+
|
|
1172
|
+
if (chunkError) {
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
`Error querying files for arela_paths chunk: ${chunkError.message}`,
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
if (pageData && pageData.length > 0) {
|
|
1179
|
+
chunkFiles = chunkFiles.concat(pageData);
|
|
1180
|
+
|
|
1181
|
+
// Check if we got a full page, indicating there might be more data
|
|
1182
|
+
if (pageData.length < pageSize) {
|
|
1183
|
+
hasMoreData = false;
|
|
1184
|
+
} else {
|
|
1185
|
+
from += pageSize;
|
|
1186
|
+
}
|
|
1187
|
+
} else {
|
|
1188
|
+
hasMoreData = false;
|
|
1189
|
+
}
|
|
1057
1190
|
}
|
|
1058
1191
|
|
|
1059
|
-
if (chunkFiles
|
|
1192
|
+
if (chunkFiles.length > 0) {
|
|
1060
1193
|
allReadyFiles = allReadyFiles.concat(chunkFiles);
|
|
1061
1194
|
}
|
|
1062
1195
|
}
|
|
@@ -1242,17 +1375,36 @@ export class DatabaseService {
|
|
|
1242
1375
|
{ uploadPath: uploadPath },
|
|
1243
1376
|
);
|
|
1244
1377
|
|
|
1245
|
-
//
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1378
|
+
// Check upload result before updating database status
|
|
1379
|
+
if (uploadResult.success) {
|
|
1380
|
+
await supabase
|
|
1381
|
+
.from('uploader')
|
|
1382
|
+
.update({
|
|
1383
|
+
status: 'file-uploaded',
|
|
1384
|
+
message: 'Successfully uploaded to Supabase',
|
|
1385
|
+
})
|
|
1386
|
+
.eq('id', file.id);
|
|
1387
|
+
|
|
1388
|
+
logger.info(`✅ Uploaded: ${file.filename}`);
|
|
1389
|
+
return { success: true, filename: file.filename };
|
|
1390
|
+
} else {
|
|
1391
|
+
await supabase
|
|
1392
|
+
.from('uploader')
|
|
1393
|
+
.update({
|
|
1394
|
+
status: 'upload-error',
|
|
1395
|
+
message: `Upload failed: ${uploadResult.error}`,
|
|
1396
|
+
})
|
|
1397
|
+
.eq('id', file.id);
|
|
1253
1398
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1399
|
+
logger.error(
|
|
1400
|
+
`❌ Upload failed: ${file.filename} - ${uploadResult.error}`,
|
|
1401
|
+
);
|
|
1402
|
+
return {
|
|
1403
|
+
success: false,
|
|
1404
|
+
error: uploadResult.error,
|
|
1405
|
+
filename: file.filename,
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1256
1408
|
} catch (error) {
|
|
1257
1409
|
logger.error(
|
|
1258
1410
|
`❌ Error processing file ${file.filename}: ${error.message}`,
|
|
@@ -1335,23 +1487,188 @@ export class DatabaseService {
|
|
|
1335
1487
|
{ folderStructure: fullFolderStructure },
|
|
1336
1488
|
);
|
|
1337
1489
|
|
|
1338
|
-
if (uploadResult.success) {
|
|
1339
|
-
|
|
1340
|
-
const fileIds = validFiles.map((f) => f.dbRecord.id);
|
|
1341
|
-
await supabase
|
|
1342
|
-
.from('uploader')
|
|
1343
|
-
.update({
|
|
1344
|
-
status: 'file-uploaded',
|
|
1345
|
-
message: 'Successfully uploaded to Arela API (batch)',
|
|
1346
|
-
})
|
|
1347
|
-
.in('id', fileIds);
|
|
1490
|
+
if (uploadResult.success && uploadResult.data) {
|
|
1491
|
+
const apiResult = uploadResult.data;
|
|
1348
1492
|
|
|
1349
|
-
uploaded += validFiles.length;
|
|
1350
1493
|
logger.info(
|
|
1351
|
-
|
|
1494
|
+
`📋 Processing API response: ${apiResult.uploaded?.length || 0} uploaded, ${apiResult.errors?.length || 0} errors`,
|
|
1495
|
+
);
|
|
1496
|
+
|
|
1497
|
+
// Debug logging to understand API response structure
|
|
1498
|
+
logger.debug(
|
|
1499
|
+
`🔍 API Response structure: ${JSON.stringify(apiResult, null, 2)}`,
|
|
1500
|
+
);
|
|
1501
|
+
if (apiResult.uploaded && apiResult.uploaded.length > 0) {
|
|
1502
|
+
logger.debug(
|
|
1503
|
+
`🔍 First uploaded file structure: ${JSON.stringify(apiResult.uploaded[0], null, 2)}`,
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
// Create filename to file mapping for quick lookup
|
|
1508
|
+
const fileNameToRecord = new Map();
|
|
1509
|
+
validFiles.forEach((f) => {
|
|
1510
|
+
fileNameToRecord.set(f.fileData.name, f.dbRecord);
|
|
1511
|
+
});
|
|
1512
|
+
|
|
1513
|
+
// Debug: Log expected filenames
|
|
1514
|
+
logger.debug(
|
|
1515
|
+
`🔍 Expected filenames: ${Array.from(fileNameToRecord.keys()).join(', ')}`,
|
|
1516
|
+
);
|
|
1517
|
+
|
|
1518
|
+
// Handle successfully uploaded files
|
|
1519
|
+
if (apiResult.uploaded && apiResult.uploaded.length > 0) {
|
|
1520
|
+
const successfulFileIds = [];
|
|
1521
|
+
const matchedFilenames = [];
|
|
1522
|
+
|
|
1523
|
+
apiResult.uploaded.forEach((uploadedFile) => {
|
|
1524
|
+
// Try multiple possible property names for filename
|
|
1525
|
+
const possibleFilename =
|
|
1526
|
+
uploadedFile.fileName ||
|
|
1527
|
+
uploadedFile.filename ||
|
|
1528
|
+
uploadedFile.name ||
|
|
1529
|
+
uploadedFile.file_name ||
|
|
1530
|
+
uploadedFile.originalName ||
|
|
1531
|
+
uploadedFile.original_name;
|
|
1532
|
+
|
|
1533
|
+
logger.debug(
|
|
1534
|
+
`🔍 Trying to match uploaded file: ${JSON.stringify(uploadedFile)}`,
|
|
1535
|
+
);
|
|
1536
|
+
|
|
1537
|
+
const dbRecord = fileNameToRecord.get(possibleFilename);
|
|
1538
|
+
if (dbRecord) {
|
|
1539
|
+
successfulFileIds.push(dbRecord.id);
|
|
1540
|
+
matchedFilenames.push(possibleFilename);
|
|
1541
|
+
logger.debug(`✅ Matched file: ${possibleFilename}`);
|
|
1542
|
+
} else {
|
|
1543
|
+
logger.warn(
|
|
1544
|
+
`⚠️ Could not match uploaded file with any known filename: ${JSON.stringify(uploadedFile)}`,
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
// If no individual files matched but API indicates success, use fallback
|
|
1550
|
+
if (
|
|
1551
|
+
successfulFileIds.length === 0 &&
|
|
1552
|
+
apiResult.uploaded.length > 0
|
|
1553
|
+
) {
|
|
1554
|
+
logger.warn(
|
|
1555
|
+
`🔄 Fallback: No individual file matches found, but API indicates ${apiResult.uploaded.length} uploads. Marking all ${validFiles.length} batch files as uploaded.`,
|
|
1556
|
+
);
|
|
1557
|
+
validFiles.forEach((f) => successfulFileIds.push(f.dbRecord.id));
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
if (successfulFileIds.length > 0) {
|
|
1561
|
+
await supabase
|
|
1562
|
+
.from('uploader')
|
|
1563
|
+
.update({
|
|
1564
|
+
status: 'file-uploaded',
|
|
1565
|
+
message: 'Successfully uploaded to Arela API (batch)',
|
|
1566
|
+
})
|
|
1567
|
+
.in('id', successfulFileIds);
|
|
1568
|
+
|
|
1569
|
+
uploaded += successfulFileIds.length;
|
|
1570
|
+
logger.info(
|
|
1571
|
+
`✅ Batch upload successful: ${successfulFileIds.length} files uploaded`,
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// Handle failed files
|
|
1577
|
+
if (apiResult.errors && apiResult.errors.length > 0) {
|
|
1578
|
+
const failedFileIds = [];
|
|
1579
|
+
|
|
1580
|
+
apiResult.errors.forEach((errorInfo) => {
|
|
1581
|
+
// Try multiple possible property names for filename in errors
|
|
1582
|
+
const possibleFilename =
|
|
1583
|
+
errorInfo.fileName ||
|
|
1584
|
+
errorInfo.filename ||
|
|
1585
|
+
errorInfo.name ||
|
|
1586
|
+
errorInfo.file_name ||
|
|
1587
|
+
errorInfo.originalName ||
|
|
1588
|
+
errorInfo.original_name;
|
|
1589
|
+
|
|
1590
|
+
const dbRecord = fileNameToRecord.get(possibleFilename);
|
|
1591
|
+
if (dbRecord) {
|
|
1592
|
+
failedFileIds.push(dbRecord.id);
|
|
1593
|
+
} else {
|
|
1594
|
+
logger.warn(
|
|
1595
|
+
`⚠️ Could not match error file: ${JSON.stringify(errorInfo)}`,
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1600
|
+
if (failedFileIds.length > 0) {
|
|
1601
|
+
await supabase
|
|
1602
|
+
.from('uploader')
|
|
1603
|
+
.update({
|
|
1604
|
+
status: 'upload-error',
|
|
1605
|
+
message: `Upload failed: ${apiResult.errors[0].error}`,
|
|
1606
|
+
})
|
|
1607
|
+
.in('id', failedFileIds);
|
|
1608
|
+
|
|
1609
|
+
errors += failedFileIds.length;
|
|
1610
|
+
logger.error(
|
|
1611
|
+
`❌ Batch upload errors: ${failedFileIds.length} files failed`,
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// Handle any remaining files that weren't in uploaded or errors arrays
|
|
1617
|
+
// Use robust filename extraction for both uploaded and error files
|
|
1618
|
+
const extractFilename = (fileObj) => {
|
|
1619
|
+
return (
|
|
1620
|
+
fileObj.fileName ||
|
|
1621
|
+
fileObj.filename ||
|
|
1622
|
+
fileObj.name ||
|
|
1623
|
+
fileObj.file_name ||
|
|
1624
|
+
fileObj.originalName ||
|
|
1625
|
+
fileObj.original_name
|
|
1626
|
+
);
|
|
1627
|
+
};
|
|
1628
|
+
|
|
1629
|
+
const processedFileNames = new Set([
|
|
1630
|
+
...(apiResult.uploaded || []).map(extractFilename).filter(Boolean),
|
|
1631
|
+
...(apiResult.errors || []).map(extractFilename).filter(Boolean),
|
|
1632
|
+
]);
|
|
1633
|
+
|
|
1634
|
+
const unprocessedFiles = validFiles.filter(
|
|
1635
|
+
(f) => !processedFileNames.has(f.fileData.name),
|
|
1352
1636
|
);
|
|
1637
|
+
if (unprocessedFiles.length > 0) {
|
|
1638
|
+
// Only mark as unprocessed if we haven't already handled all files through fallback logic
|
|
1639
|
+
// If we used fallback (all files marked as uploaded), don't mark any as unprocessed
|
|
1640
|
+
const alreadyHandledCount = uploaded + errors;
|
|
1641
|
+
const shouldMarkUnprocessed =
|
|
1642
|
+
alreadyHandledCount < validFiles.length;
|
|
1643
|
+
|
|
1644
|
+
if (shouldMarkUnprocessed) {
|
|
1645
|
+
const unprocessedIds = unprocessedFiles.map((f) => f.dbRecord.id);
|
|
1646
|
+
await supabase
|
|
1647
|
+
.from('uploader')
|
|
1648
|
+
.update({
|
|
1649
|
+
status: 'upload-error',
|
|
1650
|
+
message: 'File not found in API response',
|
|
1651
|
+
})
|
|
1652
|
+
.in('id', unprocessedIds);
|
|
1653
|
+
|
|
1654
|
+
errors += unprocessedIds.length;
|
|
1655
|
+
logger.warn(
|
|
1656
|
+
`⚠️ Unprocessed files: ${unprocessedIds.length} files not found in API response`,
|
|
1657
|
+
);
|
|
1658
|
+
logger.debug(
|
|
1659
|
+
`🔍 API response uploaded array: ${JSON.stringify(apiResult.uploaded)}`,
|
|
1660
|
+
);
|
|
1661
|
+
logger.debug(
|
|
1662
|
+
`🔍 Expected filenames: ${validFiles.map((f) => f.fileData.name).join(', ')}`,
|
|
1663
|
+
);
|
|
1664
|
+
} else {
|
|
1665
|
+
logger.debug(
|
|
1666
|
+
`✅ All files already handled (uploaded: ${uploaded}, errors: ${errors}), skipping unprocessed marking`,
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1353
1670
|
} else {
|
|
1354
|
-
//
|
|
1671
|
+
// Complete batch failure - mark all files as failed
|
|
1355
1672
|
const fileIds = validFiles.map((f) => f.dbRecord.id);
|
|
1356
1673
|
await supabase
|
|
1357
1674
|
.from('uploader')
|
|
@@ -1363,7 +1680,7 @@ export class DatabaseService {
|
|
|
1363
1680
|
|
|
1364
1681
|
errors += validFiles.length;
|
|
1365
1682
|
logger.error(
|
|
1366
|
-
`❌
|
|
1683
|
+
`❌ Complete batch failure: ${validFiles.length} files - ${uploadResult.error}`,
|
|
1367
1684
|
);
|
|
1368
1685
|
}
|
|
1369
1686
|
}
|
|
@@ -134,8 +134,16 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
134
134
|
|
|
135
135
|
const result = await response.json();
|
|
136
136
|
|
|
137
|
+
// Determine if the operation was successful
|
|
138
|
+
// Success means at least one file was uploaded successfully, even if some failed
|
|
139
|
+
const hasUploads = result.uploaded && result.uploaded.length > 0;
|
|
140
|
+
const hasErrors = result.errors && result.errors.length > 0;
|
|
141
|
+
// Consider it a success if there are uploads, or if there are no errors at all
|
|
142
|
+
const success =
|
|
143
|
+
hasUploads || (!hasErrors && result.stats?.totalFiles > 0);
|
|
144
|
+
|
|
137
145
|
// Normalize response format to match DatabaseService expectations
|
|
138
|
-
return { success
|
|
146
|
+
return { success, data: result };
|
|
139
147
|
} catch (fetchError) {
|
|
140
148
|
// Return normalized error format
|
|
141
149
|
return { success: false, error: fetchError.message };
|
|
@@ -88,10 +88,10 @@ export class SupabaseUploadService extends BaseUploadService {
|
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
if (error) {
|
|
91
|
-
|
|
91
|
+
return { success: false, error: error.message };
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
return data;
|
|
94
|
+
return { success: true, data };
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|