(url: string)
| 86 | * Pre-signed URLs are returned unchanged. |
| 87 | */ |
| 88 | export function normalizeCloudStorageUrl(url: string): string { |
| 89 | // Don't modify pre-signed URLs |
| 90 | if (isPreSignedUrl(url)) { |
| 91 | return url; |
| 92 | } |
| 93 | |
| 94 | // Handle s3:// scheme |
| 95 | if (url.startsWith('s3://')) { |
| 96 | const match = url.match(/^s3:\/\/([^/]+)\/?(.*)?$/); |
| 97 | if (match) { |
| 98 | const [, bucket, path] = match; |
| 99 | return `https://${bucket}.s3.amazonaws.com/${path || ''}`; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | // Handle gs:// scheme |
| 104 | if (url.startsWith('gs://')) { |
| 105 | const match = url.match(/^gs:\/\/([^/]+)\/?(.*)?$/); |
| 106 | if (match) { |
| 107 | const [, bucket, path] = match; |
| 108 | return `https://storage.googleapis.com/${bucket}/${path || ''}`; |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | // Handle GCS console URLs |
| 113 | // https://console.cloud.google.com/storage/browser/bucket/path |
| 114 | // -> https://storage.googleapis.com/bucket/path |
| 115 | try { |
| 116 | const parsed = new URL(url); |
| 117 | if (parsed.hostname === 'console.cloud.google.com' && |
| 118 | parsed.pathname.startsWith('/storage/browser/')) { |
| 119 | const pathAfterBrowser = parsed.pathname.replace('/storage/browser/', ''); |
| 120 | return `https://storage.googleapis.com/${pathAfterBrowser}`; |
| 121 | } |
| 122 | |
| 123 | // Handle storage.cloud.google.com URLs (alternative GCS domain) |
| 124 | // https://storage.cloud.google.com/bucket/path -> https://storage.googleapis.com/bucket/path |
| 125 | if (parsed.hostname === 'storage.cloud.google.com') { |
| 126 | return `https://storage.googleapis.com${parsed.pathname}`; |
| 127 | } |
| 128 | |
| 129 | // Handle Dropbox share URLs |
| 130 | // https://www.dropbox.com/s/abc123/file.txt?dl=0 -> https://dl.dropboxusercontent.com/s/abc123/file.txt |
| 131 | // https://www.dropbox.com/scl/fi/abc123/file.txt?rlkey=xyz&dl=0 -> https://dl.dropboxusercontent.com/scl/fi/abc123/file.txt?rlkey=xyz |
| 132 | if (parsed.hostname === 'www.dropbox.com' || parsed.hostname === 'dropbox.com') { |
| 133 | // Remove dl parameter and rebuild URL with dl.dropboxusercontent.com |
| 134 | const params = new URLSearchParams(parsed.search); |
| 135 | params.delete('dl'); |
| 136 | const queryString = params.toString(); |
| 137 | const newPath = parsed.pathname + (queryString ? `?${queryString}` : ''); |
| 138 | return `https://dl.dropboxusercontent.com${newPath}`; |
| 139 | } |
| 140 | } catch { |
| 141 | // Invalid URL, return as-is |
| 142 | } |
| 143 | |
| 144 | return url; |
| 145 | } |
no test coverage detected