@arela/uploader 0.2.7 → 0.2.9
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 +67 -13
- package/package.json +1 -1
- package/src/commands/UploadCommand.js +156 -49
- package/src/config/config.js +8 -3
- package/src/services/DatabaseService.js +623 -156
- package/src/services/upload/ApiUploadService.js +70 -7
- package/src/services/upload/SupabaseUploadService.js +2 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Blob } from 'buffer';
|
|
2
2
|
import { FormData } from 'formdata-node';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
import { Agent } from 'http';
|
|
5
|
+
import { Agent as HttpsAgent } from 'https';
|
|
4
6
|
import fetch from 'node-fetch';
|
|
5
7
|
import path from 'path';
|
|
6
8
|
|
|
@@ -16,6 +18,36 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
16
18
|
super();
|
|
17
19
|
this.baseUrl = appConfig.api.baseUrl;
|
|
18
20
|
this.token = appConfig.api.token;
|
|
21
|
+
|
|
22
|
+
// Get API connection settings from config/environment
|
|
23
|
+
const maxApiConnections = parseInt(process.env.MAX_API_CONNECTIONS) || 10;
|
|
24
|
+
const connectionTimeout =
|
|
25
|
+
parseInt(process.env.API_CONNECTION_TIMEOUT) || 60000;
|
|
26
|
+
|
|
27
|
+
// Initialize HTTP agents optimized for multiple API replicas
|
|
28
|
+
this.httpAgent = new Agent({
|
|
29
|
+
keepAlive: true,
|
|
30
|
+
keepAliveMsecs: 30000,
|
|
31
|
+
maxSockets: maxApiConnections, // Match your API replica count
|
|
32
|
+
maxFreeSockets: Math.ceil(maxApiConnections / 2),
|
|
33
|
+
maxTotalSockets: maxApiConnections + 5, // Buffer for peak usage
|
|
34
|
+
timeout: connectionTimeout,
|
|
35
|
+
scheduling: 'fifo', // First-in-first-out scheduling
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.httpsAgent = new HttpsAgent({
|
|
39
|
+
keepAlive: true,
|
|
40
|
+
keepAliveMsecs: 30000,
|
|
41
|
+
maxSockets: maxApiConnections, // Match your API replica count
|
|
42
|
+
maxFreeSockets: Math.ceil(maxApiConnections / 2),
|
|
43
|
+
maxTotalSockets: maxApiConnections + 5, // Buffer for peak usage
|
|
44
|
+
timeout: connectionTimeout,
|
|
45
|
+
scheduling: 'fifo', // First-in-first-out scheduling
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log(
|
|
49
|
+
`🔗 HTTP Agent configured for ${maxApiConnections} concurrent API connections`,
|
|
50
|
+
);
|
|
19
51
|
}
|
|
20
52
|
|
|
21
53
|
/**
|
|
@@ -27,12 +59,31 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
27
59
|
async upload(files, options) {
|
|
28
60
|
const formData = new FormData();
|
|
29
61
|
|
|
30
|
-
// Add files to form data
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
// Add files to form data asynchronously
|
|
63
|
+
for (const file of files) {
|
|
64
|
+
try {
|
|
65
|
+
// Check file size for streaming vs buffer approach
|
|
66
|
+
const stats = await fs.promises.stat(file.path);
|
|
67
|
+
const fileSizeThreshold = 10 * 1024 * 1024; // 10MB threshold
|
|
68
|
+
|
|
69
|
+
if (stats.size > fileSizeThreshold) {
|
|
70
|
+
// Use streaming for large files
|
|
71
|
+
const fileStream = fs.createReadStream(file.path);
|
|
72
|
+
formData.append('files', fileStream, {
|
|
73
|
+
filename: file.name,
|
|
74
|
+
contentType: file.contentType,
|
|
75
|
+
knownLength: stats.size,
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
// Use buffer for smaller files
|
|
79
|
+
const fileBuffer = await fs.promises.readFile(file.path);
|
|
80
|
+
const blob = new Blob([fileBuffer], { type: file.contentType });
|
|
81
|
+
formData.append('files', blob, file.name);
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new Error(`Failed to read file ${file.path}: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
36
87
|
|
|
37
88
|
// Add configuration parameters
|
|
38
89
|
if (appConfig.supabase.bucket) {
|
|
@@ -61,6 +112,7 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
61
112
|
formData.append('clientVersion', appConfig.packageVersion);
|
|
62
113
|
|
|
63
114
|
try {
|
|
115
|
+
const isHttps = this.baseUrl.startsWith('https');
|
|
64
116
|
const response = await fetch(
|
|
65
117
|
`${this.baseUrl}/api/storage/batch-upload-and-process`,
|
|
66
118
|
{
|
|
@@ -69,6 +121,7 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
69
121
|
'x-api-key': this.token,
|
|
70
122
|
},
|
|
71
123
|
body: formData,
|
|
124
|
+
agent: isHttps ? this.httpsAgent : this.httpAgent,
|
|
72
125
|
},
|
|
73
126
|
);
|
|
74
127
|
|
|
@@ -81,8 +134,16 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
81
134
|
|
|
82
135
|
const result = await response.json();
|
|
83
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
|
+
|
|
84
145
|
// Normalize response format to match DatabaseService expectations
|
|
85
|
-
return { success
|
|
146
|
+
return { success, data: result };
|
|
86
147
|
} catch (fetchError) {
|
|
87
148
|
// Return normalized error format
|
|
88
149
|
return { success: false, error: fetchError.message };
|
|
@@ -99,10 +160,12 @@ export class ApiUploadService extends BaseUploadService {
|
|
|
99
160
|
}
|
|
100
161
|
|
|
101
162
|
try {
|
|
163
|
+
const isHttps = this.baseUrl.startsWith('https');
|
|
102
164
|
const response = await fetch(`${this.baseUrl}/api/health`, {
|
|
103
165
|
headers: {
|
|
104
166
|
'x-api-key': this.token,
|
|
105
167
|
},
|
|
168
|
+
agent: isHttps ? this.httpsAgent : this.httpAgent,
|
|
106
169
|
});
|
|
107
170
|
|
|
108
171
|
return response.ok;
|
|
@@ -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
|
/**
|