diff --git a/packages/backend/server/src/core/workspaces/controller.ts b/packages/backend/server/src/core/workspaces/controller.ts index ca314c0a29..de25226503 100644 --- a/packages/backend/server/src/core/workspaces/controller.ts +++ b/packages/backend/server/src/core/workspaces/controller.ts @@ -70,7 +70,12 @@ export class WorkspacesController { // metadata should always exists if body is not null if (metadata) { - res.setHeader('content-type', metadata.contentType); + res.setHeader( + 'content-type', + metadata.contentType.startsWith('application/json') // application/json is reserved for redirect url + ? 'text/json' + : metadata.contentType + ); res.setHeader('last-modified', metadata.lastModified.toUTCString()); res.setHeader('content-length', metadata.contentLength); } else { diff --git a/packages/common/nbstore/src/impls/cloud/blob.ts b/packages/common/nbstore/src/impls/cloud/blob.ts index 8a35edb2c2..7c3711739d 100644 --- a/packages/common/nbstore/src/impls/cloud/blob.ts +++ b/packages/common/nbstore/src/impls/cloud/blob.ts @@ -20,6 +20,8 @@ interface CloudBlobStorageOptions { id: string; } +const SHOULD_MANUAL_REDIRECT = BUILD_CONFIG.isAndroid || BUILD_CONFIG.isIOS; + export class CloudBlobStorage extends BlobStorageBase { static readonly identifier = 'CloudBlobStorage'; override readonly isReadonly = false; @@ -32,7 +34,11 @@ export class CloudBlobStorage extends BlobStorageBase { override async get(key: string, signal?: AbortSignal) { const res = await this.connection.fetch( - '/api/workspaces/' + this.options.id + '/blobs/' + key, + '/api/workspaces/' + + this.options.id + + '/blobs/' + + key + + (SHOULD_MANUAL_REDIRECT ? '?redirect=manual' : ''), { cache: 'default', headers: { @@ -47,7 +53,31 @@ export class CloudBlobStorage extends BlobStorageBase { } try { - const blob = await res.blob(); + const contentType = res.headers.get('content-type'); + + let blob; + + if ( + SHOULD_MANUAL_REDIRECT && + contentType?.startsWith('application/json') + ) { + const json = await res.json(); + if ('url' in json && typeof json.url === 'string') { + const res = await this.connection.fetch(json.url, { + cache: 'default', + headers: { + 'x-affine-version': BUILD_CONFIG.appVersion, + }, + signal, + }); + + blob = await res.blob(); + } else { + throw new Error('Invalid blob response'); + } + } else { + blob = await res.blob(); + } return { key,