@arela/uploader 0.2.4 → 0.2.5
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 +2 -1
- package/src/commands/UploadCommand.js +388 -0
- package/src/config/config.js +173 -0
- package/src/errors/ErrorHandler.js +271 -0
- package/src/errors/ErrorTypes.js +104 -0
- package/src/index-old.js +2650 -0
- package/src/index.js +248 -2594
- package/src/services/DatabaseService.js +953 -0
- package/src/services/LoggingService.js +194 -0
- package/src/services/upload/ApiUploadService.js +147 -0
- package/src/services/upload/BaseUploadService.js +36 -0
- package/src/services/upload/SupabaseUploadService.js +107 -0
- package/src/services/upload/UploadServiceFactory.js +68 -0
- package/src/utils/FileOperations.js +148 -0
- package/src/utils/FileSanitizer.js +99 -0
- package/src/utils/PathDetector.js +196 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* File Sanitization Utility
|
|
5
|
+
* Handles filename sanitization with caching for performance
|
|
6
|
+
*/
|
|
7
|
+
export class FileSanitizer {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.cache = new Map();
|
|
10
|
+
this.patterns = [
|
|
11
|
+
[/[áàâäãåāăą]/gi, 'a'],
|
|
12
|
+
[/[éèêëēĕėę]/gi, 'e'],
|
|
13
|
+
[/[íìîïīĭį]/gi, 'i'],
|
|
14
|
+
[/[óòôöõōŏő]/gi, 'o'],
|
|
15
|
+
[/[úùûüūŭů]/gi, 'u'],
|
|
16
|
+
[/[ñň]/gi, 'n'],
|
|
17
|
+
[/[ç]/gi, 'c'],
|
|
18
|
+
[/[ý]/gi, 'y'],
|
|
19
|
+
[/[멕]/g, 'meok'],
|
|
20
|
+
[/[시]/g, 'si'],
|
|
21
|
+
[/[코]/g, 'ko'],
|
|
22
|
+
[/[용]/g, 'yong'],
|
|
23
|
+
[/[가-힣]/g, 'kr'],
|
|
24
|
+
[/[\u0300-\u036f]/g, ''],
|
|
25
|
+
[/[\\?%*:|"<>[\]~`^]/g, '-'],
|
|
26
|
+
[/[{}]/g, '-'],
|
|
27
|
+
[/[&]/g, 'and'],
|
|
28
|
+
[/[()]/g, ''],
|
|
29
|
+
[/\s+/g, '-'],
|
|
30
|
+
[/-+/g, '-'],
|
|
31
|
+
[/^-+|-+$/g, ''],
|
|
32
|
+
[/^\.+/, ''],
|
|
33
|
+
[/[^\w.-]/g, ''],
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sanitize a filename for safe storage
|
|
39
|
+
* @param {string} fileName - Original filename
|
|
40
|
+
* @returns {string} Sanitized filename
|
|
41
|
+
*/
|
|
42
|
+
sanitizeFileName(fileName) {
|
|
43
|
+
if (this.cache.has(fileName)) {
|
|
44
|
+
return this.cache.get(fileName);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const ext = path.extname(fileName);
|
|
48
|
+
const nameWithoutExt = path.basename(fileName, ext);
|
|
49
|
+
|
|
50
|
+
// If already clean, return as-is
|
|
51
|
+
if (/^[a-zA-Z0-9._-]+$/.test(nameWithoutExt)) {
|
|
52
|
+
const result = fileName;
|
|
53
|
+
this.cache.set(fileName, result);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let sanitized = nameWithoutExt.normalize('NFD');
|
|
58
|
+
|
|
59
|
+
// Apply all sanitization patterns
|
|
60
|
+
for (const [pattern, replacement] of this.patterns) {
|
|
61
|
+
sanitized = sanitized.replace(pattern, replacement);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Additional cleanup
|
|
65
|
+
sanitized = sanitized
|
|
66
|
+
.replace(/~/g, '-')
|
|
67
|
+
.replace(/\s+/g, '-')
|
|
68
|
+
.replace(/\.+/g, '-')
|
|
69
|
+
.replace(/-+/g, '-')
|
|
70
|
+
.replace(/^-+|-+$/g, '');
|
|
71
|
+
|
|
72
|
+
if (!sanitized) {
|
|
73
|
+
sanitized = 'unnamed_file';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result = sanitized + ext;
|
|
77
|
+
this.cache.set(fileName, result);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Clear the sanitization cache
|
|
83
|
+
*/
|
|
84
|
+
clearCache() {
|
|
85
|
+
this.cache.clear();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get cache size for monitoring
|
|
90
|
+
* @returns {number} Number of cached entries
|
|
91
|
+
*/
|
|
92
|
+
getCacheSize() {
|
|
93
|
+
return this.cache.size;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Export singleton instance
|
|
98
|
+
export const fileSanitizer = new FileSanitizer();
|
|
99
|
+
export default fileSanitizer;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Path Detection Utility
|
|
5
|
+
* Extracts year and pedimento information from file paths with caching
|
|
6
|
+
*/
|
|
7
|
+
export class PathDetector {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.cache = new Map();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extract year and pedimento number from file path with caching
|
|
14
|
+
* @param {string} filePath - Full file path
|
|
15
|
+
* @param {string} basePath - Base path to make relative
|
|
16
|
+
* @returns {Object} Detection result with year, pedimento, and detected flag
|
|
17
|
+
*/
|
|
18
|
+
extractYearAndPedimentoFromPath(filePath, basePath) {
|
|
19
|
+
const cacheKey = `${filePath}|${basePath}`;
|
|
20
|
+
|
|
21
|
+
if (this.cache.has(cacheKey)) {
|
|
22
|
+
return this.cache.get(cacheKey);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const detection = this.#performDetection(filePath, basePath);
|
|
26
|
+
this.cache.set(cacheKey, detection);
|
|
27
|
+
|
|
28
|
+
return detection;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Perform the actual path detection
|
|
33
|
+
* @private
|
|
34
|
+
* @param {string} filePath - Full file path
|
|
35
|
+
* @param {string} basePath - Base path to make relative
|
|
36
|
+
* @returns {Object} Detection result
|
|
37
|
+
*/
|
|
38
|
+
#performDetection(filePath, basePath) {
|
|
39
|
+
try {
|
|
40
|
+
const relativePath = path.relative(basePath, filePath);
|
|
41
|
+
const pathParts = relativePath.split(path.sep);
|
|
42
|
+
|
|
43
|
+
let year = null;
|
|
44
|
+
let pedimento = null;
|
|
45
|
+
|
|
46
|
+
// Pattern 1: Direct year/pedimento structure (2024/4023260)
|
|
47
|
+
year = this.#detectDirectPattern(pathParts);
|
|
48
|
+
pedimento = year ? this.#findPedimentoForYear(pathParts, year) : null;
|
|
49
|
+
|
|
50
|
+
// Pattern 2: Named patterns (año2024, ped4023260)
|
|
51
|
+
if (!year || !pedimento) {
|
|
52
|
+
const namedDetection = this.#detectNamedPatterns(pathParts);
|
|
53
|
+
year = year || namedDetection.year;
|
|
54
|
+
pedimento = pedimento || namedDetection.pedimento;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Pattern 3: Loose year detection
|
|
58
|
+
if (!year) {
|
|
59
|
+
year = this.#detectLooseYear(pathParts);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Pattern 4: Loose pedimento detection
|
|
63
|
+
if (!pedimento) {
|
|
64
|
+
pedimento = this.#detectLoosePedimento(pathParts);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
year,
|
|
69
|
+
pedimento,
|
|
70
|
+
detected: !!(year && pedimento)
|
|
71
|
+
};
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return {
|
|
74
|
+
year: null,
|
|
75
|
+
pedimento: null,
|
|
76
|
+
detected: false,
|
|
77
|
+
error: error.message,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Detect direct year/pedimento pattern
|
|
84
|
+
* @private
|
|
85
|
+
* @param {string[]} pathParts - Path components
|
|
86
|
+
* @returns {string|null} Detected year
|
|
87
|
+
*/
|
|
88
|
+
#detectDirectPattern(pathParts) {
|
|
89
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
90
|
+
const yearMatch = pathParts[i].match(/^(202[0-9])$/);
|
|
91
|
+
if (yearMatch) {
|
|
92
|
+
return yearMatch[1];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Find pedimento for a detected year
|
|
100
|
+
* @private
|
|
101
|
+
* @param {string[]} pathParts - Path components
|
|
102
|
+
* @param {string} year - Detected year
|
|
103
|
+
* @returns {string|null} Detected pedimento
|
|
104
|
+
*/
|
|
105
|
+
#findPedimentoForYear(pathParts, year) {
|
|
106
|
+
const yearIndex = pathParts.findIndex(part => part === year);
|
|
107
|
+
if (yearIndex >= 0 && yearIndex < pathParts.length - 1) {
|
|
108
|
+
const nextPart = pathParts[yearIndex + 1];
|
|
109
|
+
const pedimentoMatch = nextPart.match(/^(\d{4,8})$/);
|
|
110
|
+
if (pedimentoMatch) {
|
|
111
|
+
return pedimentoMatch[1];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Detect named patterns (año2024, ped4023260)
|
|
119
|
+
* @private
|
|
120
|
+
* @param {string[]} pathParts - Path components
|
|
121
|
+
* @returns {Object} Detection result with year and pedimento
|
|
122
|
+
*/
|
|
123
|
+
#detectNamedPatterns(pathParts) {
|
|
124
|
+
let year = null;
|
|
125
|
+
let pedimento = null;
|
|
126
|
+
|
|
127
|
+
for (const part of pathParts) {
|
|
128
|
+
if (!year) {
|
|
129
|
+
const namedYearMatch = part.match(/(?:año|year|anio)(\d{4})/i);
|
|
130
|
+
if (namedYearMatch) {
|
|
131
|
+
year = namedYearMatch[1];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!pedimento) {
|
|
136
|
+
const namedPedimentoMatch = part.match(/(?:ped|pedimento|pedi)(\d{4,8})/i);
|
|
137
|
+
if (namedPedimentoMatch) {
|
|
138
|
+
pedimento = namedPedimentoMatch[1];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { year, pedimento };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Detect loose year pattern
|
|
148
|
+
* @private
|
|
149
|
+
* @param {string[]} pathParts - Path components
|
|
150
|
+
* @returns {string|null} Detected year
|
|
151
|
+
*/
|
|
152
|
+
#detectLooseYear(pathParts) {
|
|
153
|
+
for (const part of pathParts) {
|
|
154
|
+
const yearMatch = part.match(/(202[0-9])/);
|
|
155
|
+
if (yearMatch) {
|
|
156
|
+
return yearMatch[1];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Detect loose pedimento pattern
|
|
164
|
+
* @private
|
|
165
|
+
* @param {string[]} pathParts - Path components
|
|
166
|
+
* @returns {string|null} Detected pedimento
|
|
167
|
+
*/
|
|
168
|
+
#detectLoosePedimento(pathParts) {
|
|
169
|
+
for (const part of pathParts) {
|
|
170
|
+
const pedimentoMatch = part.match(/(\d{4,8})/);
|
|
171
|
+
if (pedimentoMatch && pedimentoMatch[1].length >= 4) {
|
|
172
|
+
return pedimentoMatch[1];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Clear the detection cache
|
|
180
|
+
*/
|
|
181
|
+
clearCache() {
|
|
182
|
+
this.cache.clear();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get cache size for monitoring
|
|
187
|
+
* @returns {number} Number of cached entries
|
|
188
|
+
*/
|
|
189
|
+
getCacheSize() {
|
|
190
|
+
return this.cache.size;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Export singleton instance
|
|
195
|
+
export const pathDetector = new PathDetector();
|
|
196
|
+
export default pathDetector;
|