|
1 | 1 | import { EventEmitter } from "eventemitter3"; |
2 | 2 | import lighthouse from "@lighthouse-web3/sdk"; |
| 3 | +import { readFileSync } from "fs"; |
3 | 4 | import { AuthenticationManager } from "./auth/AuthenticationManager"; |
4 | 5 | import { ProgressTracker } from "./progress/ProgressTracker"; |
5 | 6 | import { ErrorHandler } from "./errors/ErrorHandler"; |
@@ -217,19 +218,70 @@ export class LighthouseAISDK extends EventEmitter { |
217 | 218 | // Update progress to uploading phase |
218 | 219 | this.progress.updateProgress(operationId, 0, "uploading"); |
219 | 220 |
|
220 | | - // Upload file using Lighthouse SDK |
221 | | - const uploadResponse = await lighthouse.upload( |
222 | | - filePath, |
223 | | - apiKey, |
224 | | - false, // dealStatus - set to false for now |
225 | | - undefined, // endDate |
226 | | - (data: any) => { |
227 | | - // Convert Lighthouse progress format to our format |
228 | | - if (data.loaded !== undefined) { |
229 | | - progressCallback(data.loaded, data.total); |
| 221 | + // Calculate dynamic timeout based on file size (minimum 2 minutes, +30s per MB) |
| 222 | + const fileSizeMB = fileStats.size / (1024 * 1024); |
| 223 | + const dynamicTimeout = Math.max(120000, 120000 + fileSizeMB * 30000); // 2 min base + 30s per MB |
| 224 | + |
| 225 | + // Read file as buffer to avoid fs-extra dependency issues |
| 226 | + const fileBuffer = readFileSync(filePath); |
| 227 | + |
| 228 | + // Upload file using Lighthouse SDK buffer method with timeout |
| 229 | + let uploadResponse; |
| 230 | + try { |
| 231 | + const uploadPromise = lighthouse.uploadBuffer(fileBuffer, apiKey); |
| 232 | + uploadResponse = await this.withTimeout(uploadPromise, dynamicTimeout); |
| 233 | + } catch (error) { |
| 234 | + // Try fallback to direct API call if standard method fails |
| 235 | + const errorMessage = error instanceof Error ? error.message : String(error); |
| 236 | + |
| 237 | + if ( |
| 238 | + errorMessage.includes("ETIMEDOUT") || |
| 239 | + errorMessage.includes("timeout") || |
| 240 | + errorMessage.includes("ENOTFOUND") || |
| 241 | + errorMessage.includes("ECONNREFUSED") |
| 242 | + ) { |
| 243 | + console.warn("Standard upload failed, trying direct API fallback:", errorMessage); |
| 244 | + |
| 245 | + try { |
| 246 | + uploadResponse = await this.uploadViDirectAPI( |
| 247 | + fileBuffer, |
| 248 | + apiKey, |
| 249 | + options.fileName || filePath.split("/").pop() || "file", |
| 250 | + ); |
| 251 | + console.log("Fallback upload successful"); |
| 252 | + } catch (fallbackError) { |
| 253 | + // If both methods fail, provide comprehensive error message |
| 254 | + throw new Error(`Upload failed with both methods: |
| 255 | +
|
| 256 | +Primary error: ${errorMessage} |
| 257 | +Fallback error: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)} |
| 258 | +
|
| 259 | +This could be due to: |
| 260 | +• Network connectivity issues |
| 261 | +• Large file size (current: ${(fileStats.size / 1024 / 1024).toFixed(1)}MB) |
| 262 | +• Lighthouse servers being temporarily unavailable |
| 263 | +• Firewall or proxy blocking the connection |
| 264 | +• Invalid API key |
| 265 | +
|
| 266 | +Try uploading a smaller file or check your network connection.`); |
230 | 267 | } |
231 | | - }, |
232 | | - ); |
| 268 | + } else if (errorMessage.includes("401") || errorMessage.includes("unauthorized")) { |
| 269 | + throw new Error(`Authentication failed. Please check: |
| 270 | +• Your API key is correct and valid |
| 271 | +• Your API key has upload permissions |
| 272 | +• Your API key hasn't expired |
| 273 | +
|
| 274 | +Current API key: ${apiKey.substring(0, 8)}...`); |
| 275 | + } else if (errorMessage.includes("413") || errorMessage.includes("too large")) { |
| 276 | + throw new Error(`File too large (${(fileStats.size / 1024 / 1024).toFixed(1)}MB). |
| 277 | +Maximum file size may be exceeded. Try uploading a smaller file.`); |
| 278 | + } else if (errorMessage.includes("429") || errorMessage.includes("rate limit")) { |
| 279 | + throw new Error(`Rate limit exceeded. Please wait a moment before trying again.`); |
| 280 | + } |
| 281 | + |
| 282 | + // Re-throw original error if we can't classify it |
| 283 | + throw error; |
| 284 | + } |
233 | 285 |
|
234 | 286 | if (!uploadResponse || !uploadResponse.data || !uploadResponse.data.Hash) { |
235 | 287 | throw new Error("Invalid upload response from Lighthouse"); |
@@ -257,6 +309,48 @@ export class LighthouseAISDK extends EventEmitter { |
257 | 309 | }, "uploadFile"); |
258 | 310 | } |
259 | 311 |
|
| 312 | + /** |
| 313 | + * Add timeout wrapper for promises |
| 314 | + */ |
| 315 | + private withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> { |
| 316 | + return Promise.race([ |
| 317 | + promise, |
| 318 | + new Promise<never>((_, reject) => |
| 319 | + setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs), |
| 320 | + ), |
| 321 | + ]); |
| 322 | + } |
| 323 | + |
| 324 | + /** |
| 325 | + * Upload file via direct API call as fallback when SDK fails |
| 326 | + */ |
| 327 | + private async uploadViDirectAPI( |
| 328 | + fileBuffer: Buffer, |
| 329 | + apiKey: string, |
| 330 | + fileName: string, |
| 331 | + ): Promise<any> { |
| 332 | + // This is a fallback method that uses direct HTTP calls to api.lighthouse.storage |
| 333 | + // when the standard SDK fails (usually due to node.lighthouse.storage being down) |
| 334 | + |
| 335 | + const FormData = eval("require")("form-data"); |
| 336 | + const axios = eval("require")("axios"); |
| 337 | + |
| 338 | + const formData = new FormData(); |
| 339 | + formData.append("file", fileBuffer, fileName); |
| 340 | + |
| 341 | + const response = await axios.post("https://api.lighthouse.storage/api/v0/add", formData, { |
| 342 | + headers: { |
| 343 | + ...formData.getHeaders(), |
| 344 | + Authorization: `Bearer ${apiKey}`, |
| 345 | + }, |
| 346 | + timeout: 180000, // 3 minutes |
| 347 | + maxContentLength: Infinity, |
| 348 | + maxBodyLength: Infinity, |
| 349 | + }); |
| 350 | + |
| 351 | + return response; |
| 352 | + } |
| 353 | + |
260 | 354 | /** |
261 | 355 | * Download a file from Lighthouse with comprehensive error handling and progress tracking. |
262 | 356 | * |
|
0 commit comments