From ea9019e6bd20e338ce349fa6f5016121bde3cc5c Mon Sep 17 00:00:00 2001 From: FloridaStyle Date: Thu, 23 Jul 2026 12:16:45 -0400 Subject: [PATCH] =?UTF-8?q?fix(storage):=20Supabase=20signed=20URLs=20?= =?UTF-8?q?=E2=80=94=20prepend=20/storage/v1=20(#2565)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SupabaseStorage.getSignedUrl built the download URL as `${projectUrl}${signedURL}`, but Supabase's sign API returns `signedURL` relative to the Storage API root (/object/sign//?token=...), so the generated link dropped /storage/v1 and returned 404. Now prepends `${projectUrl}/storage/v1`, tolerating an already-absolute URL or a value that already carries the prefix. `gbrain files signed-url` links resolve again. Co-authored-by: Claude Opus 4.8 --- src/core/storage/supabase.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/storage/supabase.ts b/src/core/storage/supabase.ts index ace1fb7f8..ef82d4d75 100644 --- a/src/core/storage/supabase.ts +++ b/src/core/storage/supabase.ts @@ -195,7 +195,14 @@ export class SupabaseStorage implements StorageBackend { throw new Error(`Supabase signed URL failed: ${res.status} ${body}`); } const result = await res.json() as { signedURL: string }; - return `${this.projectUrl}${result.signedURL}`; + // Supabase returns `signedURL` relative to the Storage API root, e.g. + // "/object/sign//?token=...". Prepend projectUrl + "/storage/v1" + // (not just projectUrl) or the link 404s. Tolerate an already-absolute URL or a + // value that already carries the /storage/v1 prefix. + const signed = result.signedURL; + if (/^https?:\/\//.test(signed)) return signed; + if (signed.startsWith('/storage/v1')) return `${this.projectUrl}${signed}`; + return `${this.projectUrl}/storage/v1${signed.startsWith('/') ? '' : '/'}${signed}`; } async getUrl(path: string): Promise {