@arela/uploader 0.2.5 → 0.2.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 +2 -2
- package/src/commands/UploadCommand.js +102 -44
- package/src/config/config.js +15 -10
- package/src/errors/ErrorHandler.js +38 -31
- package/src/errors/ErrorTypes.js +2 -2
- package/src/index-old.js +25 -17
- package/src/index.js +124 -49
- package/src/services/DatabaseService.js +372 -185
- package/src/services/LoggingService.js +3 -3
- package/src/services/upload/ApiUploadService.js +16 -10
- package/src/services/upload/BaseUploadService.js +1 -1
- package/src/services/upload/SupabaseUploadService.js +22 -3
- package/src/services/upload/UploadServiceFactory.js +14 -6
- package/src/utils/FileOperations.js +1 -1
- package/src/utils/FileSanitizer.js +1 -1
- package/src/utils/PathDetector.js +9 -7
|
@@ -16,7 +16,7 @@ export class LoggingService {
|
|
|
16
16
|
this.bufferSize = appConfig.performance.logBufferSize;
|
|
17
17
|
this.flushInterval = appConfig.performance.logFlushInterval;
|
|
18
18
|
this.lastFlushTime = Date.now();
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
this.#setupProcessHandlers();
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -52,7 +52,7 @@ export class LoggingService {
|
|
|
52
52
|
try {
|
|
53
53
|
const timestamp = new Date().toISOString();
|
|
54
54
|
const logEntry = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
this.buffer.push(logEntry);
|
|
57
57
|
|
|
58
58
|
// Flush if buffer is full or enough time has passed
|
|
@@ -191,4 +191,4 @@ export class LoggingService {
|
|
|
191
191
|
|
|
192
192
|
// Export singleton instance
|
|
193
193
|
export const logger = new LoggingService();
|
|
194
|
-
export default logger;
|
|
194
|
+
export default logger;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { Blob } from 'buffer';
|
|
2
|
+
import { FormData } from 'formdata-node';
|
|
1
3
|
import fs from 'fs';
|
|
2
4
|
import fetch from 'node-fetch';
|
|
3
5
|
import path from 'path';
|
|
4
|
-
import { FormData } from 'formdata-node';
|
|
5
|
-
import { Blob } from 'buffer';
|
|
6
6
|
|
|
7
7
|
import appConfig from '../../config/config.js';
|
|
8
8
|
import { BaseUploadService } from './BaseUploadService.js';
|
|
@@ -38,7 +38,7 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
38
38
|
if (appConfig.supabase.bucket) {
|
|
39
39
|
formData.append('bucket', appConfig.supabase.bucket);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
if (options.prefix) {
|
|
43
43
|
formData.append('prefix', options.prefix);
|
|
44
44
|
}
|
|
@@ -69,18 +69,18 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
69
69
|
'x-api-key': this.token,
|
|
70
70
|
},
|
|
71
71
|
body: formData,
|
|
72
|
-
}
|
|
72
|
+
},
|
|
73
73
|
);
|
|
74
74
|
|
|
75
75
|
if (!response.ok) {
|
|
76
76
|
const errorText = await response.text();
|
|
77
77
|
throw new Error(
|
|
78
|
-
`API request failed: ${response.status} ${response.statusText} - ${errorText}
|
|
78
|
+
`API request failed: ${response.status} ${response.statusText} - ${errorText}`,
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const result = await response.json();
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
// Normalize response format to match DatabaseService expectations
|
|
85
85
|
return { success: true, data: result };
|
|
86
86
|
} catch (fetchError) {
|
|
@@ -104,7 +104,7 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
104
104
|
'x-api-key': this.token,
|
|
105
105
|
},
|
|
106
106
|
});
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
return response.ok;
|
|
109
109
|
} catch (error) {
|
|
110
110
|
return false;
|
|
@@ -129,12 +129,18 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
129
129
|
#buildFolderStructure(files, options) {
|
|
130
130
|
let combinedStructure = null;
|
|
131
131
|
|
|
132
|
-
if (
|
|
132
|
+
if (
|
|
133
|
+
options.folderStructure &&
|
|
134
|
+
options.autoDetectStructure &&
|
|
135
|
+
files.length > 0
|
|
136
|
+
) {
|
|
133
137
|
// This would require the path detection utility - we'll implement this after creating that module
|
|
134
138
|
console.log(`📁 Custom folder structure: ${options.folderStructure}`);
|
|
135
139
|
return options.folderStructure;
|
|
136
140
|
} else if (options.folderStructure) {
|
|
137
|
-
console.log(
|
|
141
|
+
console.log(
|
|
142
|
+
`📁 Using custom folder structure: ${options.folderStructure}`,
|
|
143
|
+
);
|
|
138
144
|
return options.folderStructure;
|
|
139
145
|
} else if (options.autoDetectStructure && files.length > 0) {
|
|
140
146
|
// Auto-detection logic would go here
|
|
@@ -144,4 +150,4 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
144
150
|
|
|
145
151
|
return null;
|
|
146
152
|
}
|
|
147
|
-
}
|
|
153
|
+
}
|
|
@@ -29,7 +29,24 @@ export class SupabaseUploadService extends BaseUploadService {
|
|
|
29
29
|
throw new Error('Missing Supabase configuration');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
this.client = createClient(appConfig.supabase.url, appConfig.supabase.key
|
|
32
|
+
this.client = createClient(appConfig.supabase.url, appConfig.supabase.key, {
|
|
33
|
+
db: {
|
|
34
|
+
schema: 'public',
|
|
35
|
+
},
|
|
36
|
+
auth: {
|
|
37
|
+
autoRefreshToken: false,
|
|
38
|
+
persistSession: false,
|
|
39
|
+
},
|
|
40
|
+
global: {
|
|
41
|
+
headers: {
|
|
42
|
+
'x-client-info': 'arela-uploader',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
// Increase timeout settings to handle large queries
|
|
46
|
+
realtime: {
|
|
47
|
+
timeout: 60000, // 60 seconds
|
|
48
|
+
},
|
|
49
|
+
});
|
|
33
50
|
|
|
34
51
|
// Test connection
|
|
35
52
|
const { error } = await this.client.storage.from(this.bucket).list('');
|
|
@@ -48,7 +65,9 @@ export class SupabaseUploadService extends BaseUploadService {
|
|
|
48
65
|
await this.#initializeClient();
|
|
49
66
|
|
|
50
67
|
if (files.length !== 1) {
|
|
51
|
-
throw new Error(
|
|
68
|
+
throw new Error(
|
|
69
|
+
'SupabaseUploadService only supports single file uploads',
|
|
70
|
+
);
|
|
52
71
|
}
|
|
53
72
|
|
|
54
73
|
const file = files[0];
|
|
@@ -104,4 +123,4 @@ export class SupabaseUploadService extends BaseUploadService {
|
|
|
104
123
|
await this.#initializeClient();
|
|
105
124
|
return this.client;
|
|
106
125
|
}
|
|
107
|
-
}
|
|
126
|
+
}
|
|
@@ -20,9 +20,11 @@ export class UploadServiceFactory {
|
|
|
20
20
|
async getUploadService(forceSupabase = false) {
|
|
21
21
|
if (forceSupabase) {
|
|
22
22
|
console.log('🔧 Force Supabase mode enabled - skipping API');
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
if (await this.supabaseService.isAvailable()) {
|
|
25
|
-
console.log(
|
|
25
|
+
console.log(
|
|
26
|
+
`✅ Connected to ${this.supabaseService.getServiceName()} (direct mode)`,
|
|
27
|
+
);
|
|
26
28
|
return this.supabaseService;
|
|
27
29
|
} else {
|
|
28
30
|
throw new Error('Supabase service is not available');
|
|
@@ -31,15 +33,21 @@ export class UploadServiceFactory {
|
|
|
31
33
|
|
|
32
34
|
// Try API service first
|
|
33
35
|
if (await this.apiService.isAvailable()) {
|
|
34
|
-
console.log(
|
|
36
|
+
console.log(
|
|
37
|
+
'🌐 API mode enabled - files will be uploaded to Arela API with automatic processing',
|
|
38
|
+
);
|
|
35
39
|
console.log(`✅ Connected to ${this.apiService.getServiceName()}`);
|
|
36
40
|
return this.apiService;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
// Fallback to Supabase
|
|
40
44
|
if (await this.supabaseService.isAvailable()) {
|
|
41
|
-
console.warn(
|
|
42
|
-
|
|
45
|
+
console.warn(
|
|
46
|
+
'⚠️ API connection failed, falling back to direct Supabase upload',
|
|
47
|
+
);
|
|
48
|
+
console.log(
|
|
49
|
+
`✅ Connected to ${this.supabaseService.getServiceName()} (direct mode)`,
|
|
50
|
+
);
|
|
43
51
|
return this.supabaseService;
|
|
44
52
|
}
|
|
45
53
|
|
|
@@ -65,4 +73,4 @@ export class UploadServiceFactory {
|
|
|
65
73
|
|
|
66
74
|
// Export singleton instance
|
|
67
75
|
export const uploadServiceFactory = new UploadServiceFactory();
|
|
68
|
-
export default uploadServiceFactory;
|
|
76
|
+
export default uploadServiceFactory;
|
|
@@ -64,10 +64,10 @@ export class PathDetector {
|
|
|
64
64
|
pedimento = this.#detectLoosePedimento(pathParts);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
return {
|
|
68
|
-
year,
|
|
69
|
-
pedimento,
|
|
70
|
-
detected: !!(year && pedimento)
|
|
67
|
+
return {
|
|
68
|
+
year,
|
|
69
|
+
pedimento,
|
|
70
|
+
detected: !!(year && pedimento),
|
|
71
71
|
};
|
|
72
72
|
} catch (error) {
|
|
73
73
|
return {
|
|
@@ -103,7 +103,7 @@ export class PathDetector {
|
|
|
103
103
|
* @returns {string|null} Detected pedimento
|
|
104
104
|
*/
|
|
105
105
|
#findPedimentoForYear(pathParts, year) {
|
|
106
|
-
const yearIndex = pathParts.findIndex(part => part === year);
|
|
106
|
+
const yearIndex = pathParts.findIndex((part) => part === year);
|
|
107
107
|
if (yearIndex >= 0 && yearIndex < pathParts.length - 1) {
|
|
108
108
|
const nextPart = pathParts[yearIndex + 1];
|
|
109
109
|
const pedimentoMatch = nextPart.match(/^(\d{4,8})$/);
|
|
@@ -133,7 +133,9 @@ export class PathDetector {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
if (!pedimento) {
|
|
136
|
-
const namedPedimentoMatch = part.match(
|
|
136
|
+
const namedPedimentoMatch = part.match(
|
|
137
|
+
/(?:ped|pedimento|pedi)(\d{4,8})/i,
|
|
138
|
+
);
|
|
137
139
|
if (namedPedimentoMatch) {
|
|
138
140
|
pedimento = namedPedimentoMatch[1];
|
|
139
141
|
}
|
|
@@ -193,4 +195,4 @@ export class PathDetector {
|
|
|
193
195
|
|
|
194
196
|
// Export singleton instance
|
|
195
197
|
export const pathDetector = new PathDetector();
|
|
196
|
-
export default pathDetector;
|
|
198
|
+
export default pathDetector;
|