Skip to content

Commit 8e0993c

Browse files
authored
feat(storage_client): use dedicated storage host for storage lib (allows >50GB uploads) (#1285)
* feat(storage_client): use dedicated storage host for storage lib (allows >50GB uploads) Transform legacy storage URLs to use the dedicated storage host which disables request buffering and allows uploads up to 500GB. Changes: - Added _transformStorageUrl() method to transform legacy URLs - Legacy format: project-ref.supabase.co/storage/v1 - New format: project-ref.storage.supabase.co/v1 - Only transforms Supabase platform hosts (supabase.co, supabase.in, supabase.red) - Preserves non-platform hosts and localhost URLs unchanged - Added comprehensive tests for URL transformation scenarios This matches the implementation in storage-js PR #230 and enables large file uploads (>50GB) by using the dedicated storage infrastructure. Closes SDK-230 * style: dart fmt
1 parent 0b194e4 commit 8e0993c

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

packages/storage_client/lib/src/storage_client.dart

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class SupabaseStorageClient extends StorageBucketApi {
4242
),
4343
_defaultRetryAttempts = retryAttempts,
4444
super(
45-
url,
45+
_transformStorageUrl(url),
4646
{...Constants.defaultHeaders, ...headers},
4747
httpClient: httpClient,
4848
) {
@@ -51,6 +51,39 @@ class SupabaseStorageClient extends StorageBucketApi {
5151
_log.finest('Initialize with headers: $headers');
5252
}
5353

54+
/// Transforms legacy storage URLs to use the dedicated storage host.
55+
///
56+
/// If legacy URI is used, replace with new storage host (disables request buffering to allow > 50GB uploads).
57+
/// "project-ref.supabase.co/storage/v1" becomes "project-ref.storage.supabase.co/v1"
58+
static String _transformStorageUrl(String url) {
59+
final uri = Uri.parse(url);
60+
final hostname = uri.host;
61+
62+
// Check if it's a Supabase host (supabase.co, supabase.in, or supabase.red)
63+
final isSupabaseHost = RegExp(r'supabase\.(co|in|red)$').hasMatch(hostname);
64+
65+
// If it's a legacy storage URL, transform it
66+
const legacyStoragePrefix = '/storage';
67+
if (isSupabaseHost &&
68+
!hostname.contains('storage.supabase.') &&
69+
uri.path.startsWith(legacyStoragePrefix)) {
70+
// Remove /storage from pathname
71+
final newPath = uri.path.substring(legacyStoragePrefix.length);
72+
// Replace supabase. with storage.supabase. in hostname
73+
final newHostname = hostname.replaceAll('supabase.', 'storage.supabase.');
74+
75+
// Reconstruct the URI
76+
return uri
77+
.replace(
78+
host: newHostname,
79+
path: newPath,
80+
)
81+
.toString();
82+
}
83+
84+
return url;
85+
}
86+
5487
/// Perform file operation in a bucket.
5588
///
5689
/// [id] The bucket id to operate on.

packages/storage_client/test/basic_test.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,60 @@ void main() {
287287
expect(client.headers['X-Client-Info'], 'supabase-dart/0.0.0');
288288
});
289289
});
290+
291+
group('URL Construction', () {
292+
test('should update legacy prod host to new host', () {
293+
const inputUrl = 'https://blah.supabase.co/storage/v1';
294+
const expectedUrl = 'https://blah.storage.supabase.co/v1';
295+
client = SupabaseStorageClient(inputUrl, {
296+
'Authorization': 'Bearer $supabaseKey',
297+
});
298+
expect(client.url, expectedUrl);
299+
});
300+
301+
test('should update legacy staging host to new host', () {
302+
const inputUrl = 'https://blah.supabase.red/storage/v1';
303+
const expectedUrl = 'https://blah.storage.supabase.red/v1';
304+
client = SupabaseStorageClient(inputUrl, {
305+
'Authorization': 'Bearer $supabaseKey',
306+
});
307+
expect(client.url, expectedUrl);
308+
});
309+
310+
test('should accept new host without modification', () {
311+
const inputUrl = 'https://blah.storage.supabase.co/v1';
312+
const expectedUrl = 'https://blah.storage.supabase.co/v1';
313+
client = SupabaseStorageClient(inputUrl, {
314+
'Authorization': 'Bearer $supabaseKey',
315+
});
316+
expect(client.url, expectedUrl);
317+
});
318+
319+
test('should not modify non-platform hosts', () {
320+
const inputUrl = 'https://blah.supabase.co.example.com/storage/v1';
321+
const expectedUrl = 'https://blah.supabase.co.example.com/storage/v1';
322+
client = SupabaseStorageClient(inputUrl, {
323+
'Authorization': 'Bearer $supabaseKey',
324+
});
325+
expect(client.url, expectedUrl);
326+
});
327+
328+
test('should support local host with port without modification', () {
329+
const inputUrl = 'http://localhost:1234/storage/v1';
330+
const expectedUrl = 'http://localhost:1234/storage/v1';
331+
client = SupabaseStorageClient(inputUrl, {
332+
'Authorization': 'Bearer $supabaseKey',
333+
});
334+
expect(client.url, expectedUrl);
335+
});
336+
337+
test('should update legacy supabase.in host to new host', () {
338+
const inputUrl = 'https://blah.supabase.in/storage/v1';
339+
const expectedUrl = 'https://blah.storage.supabase.in/v1';
340+
client = SupabaseStorageClient(inputUrl, {
341+
'Authorization': 'Bearer $supabaseKey',
342+
});
343+
expect(client.url, expectedUrl);
344+
});
345+
});
290346
}

0 commit comments

Comments
 (0)