( options: GeneratePresignedUrlOptions )
| 529 | * Generate a presigned URL for direct file upload |
| 530 | */ |
| 531 | export async function generatePresignedUploadUrl( |
| 532 | options: GeneratePresignedUrlOptions |
| 533 | ): Promise<PresignedUrlResponse> { |
| 534 | const { |
| 535 | fileName, |
| 536 | contentType, |
| 537 | fileSize, |
| 538 | context, |
| 539 | userId, |
| 540 | expirationSeconds = 3600, |
| 541 | metadata = {}, |
| 542 | customKey, |
| 543 | } = options |
| 544 | |
| 545 | const allMetadata = { |
| 546 | ...metadata, |
| 547 | originalName: fileName, |
| 548 | uploadedAt: new Date().toISOString(), |
| 549 | purpose: context, |
| 550 | ...(userId && { userId }), |
| 551 | } |
| 552 | |
| 553 | const config = getStorageConfig(context) |
| 554 | |
| 555 | let key: string |
| 556 | if (customKey) { |
| 557 | key = customKey |
| 558 | } else { |
| 559 | const timestamp = Date.now() |
| 560 | const uniqueId = randomBytes(8).toString('hex') |
| 561 | const safeFileName = fileName.replace(/[^a-zA-Z0-9.-]/g, '_') |
| 562 | key = `${context}/${timestamp}-${uniqueId}-${safeFileName}` |
| 563 | } |
| 564 | |
| 565 | if (USE_S3_STORAGE) { |
| 566 | return generateS3PresignedUrl( |
| 567 | key, |
| 568 | contentType, |
| 569 | fileSize, |
| 570 | allMetadata, |
| 571 | config, |
| 572 | expirationSeconds |
| 573 | ) |
| 574 | } |
| 575 | |
| 576 | if (USE_BLOB_STORAGE) { |
| 577 | return generateBlobPresignedUrl(key, contentType, allMetadata, config, expirationSeconds) |
| 578 | } |
| 579 | |
| 580 | throw new Error('Cloud storage not configured. Cannot generate presigned URL for local storage.') |
| 581 | } |
| 582 | |
| 583 | /** |
| 584 | * Generate presigned URL for S3 |
no test coverage detected