From d7bcedddadc6ee3fe6fb97054ab948755ca2c650 Mon Sep 17 00:00:00 2001 From: Luke Policinski Date: Mon, 28 Sep 2026 13:18:25 -0400 Subject: [PATCH] feature: storage check reads the test file back --- hasura/metadata/actions.graphql | 8 ++++++++ hasura/metadata/actions.yaml | 8 ++++++++ src/s3/s3.controller.ts | 29 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/hasura/metadata/actions.graphql b/hasura/metadata/actions.graphql index c9835db06..0f17860de 100644 --- a/hasura/metadata/actions.graphql +++ b/hasura/metadata/actions.graphql @@ -788,6 +788,10 @@ type Query { telemetryStats(includeSelf: Boolean): TelemetryStats! } +type Mutation { + testDownload: TestDownloadResponse +} + type Mutation { testUpload: TestUploadResponse } @@ -1380,6 +1384,10 @@ type TelemetryStats { countries: [TelemetryCountryCount!]! } +type TestDownloadResponse { + error: String +} + type TestUploadResponse { error: String } diff --git a/hasura/metadata/actions.yaml b/hasura/metadata/actions.yaml index 90a9e0d7b..0382003fb 100644 --- a/hasura/metadata/actions.yaml +++ b/hasura/metadata/actions.yaml @@ -1113,6 +1113,13 @@ actions: forward_client_headers: true permissions: - role: administrator + - name: testDownload + definition: + kind: synchronous + handler: '{{HASURA_GRAPHQL_ACTIONS_HOOK}}' + forward_client_headers: true + permissions: + - role: administrator - name: testUpload definition: kind: synchronous @@ -2066,6 +2073,7 @@ custom_types: - name: TelemetryUtilityTypeCount - name: TelemetryUtilitySourceCount - name: TelemetryStats + - name: TestDownloadResponse - name: TestUploadResponse - name: GetTestUploadResponse - name: ApiKeyResponse diff --git a/src/s3/s3.controller.ts b/src/s3/s3.controller.ts index dc3bc1d0c..7db269a84 100644 --- a/src/s3/s3.controller.ts +++ b/src/s3/s3.controller.ts @@ -52,6 +52,35 @@ export class S3Controller { } } + // Reads back the file testUpload wrote, the way a download is served: through + // a presigned link. The browser can't do this itself, since most buckets + // don't allow the panel's site to read them directly. + @HasuraAction() + public async testDownload() { + try { + const response = await fetch( + await this.s3.getPresignedUrl("hello.txt", undefined, 60, "get"), + ); + + if (!response.ok) { + throw new Error(`${response.status} ${response.statusText}`); + } + + if (!(await response.text()).startsWith("world")) { + throw new Error( + "the test file came back different from what was written", + ); + } + + return {}; + } catch (error) { + this.logger.error(`Failed to download file from S3: ${error.message}`); + return { + error: error.message, + }; + } + } + @HasuraAction() public async getTestUploadLink() { try {