From ad400bc6faecb99c2ccaf0e1df05d71a0e713d8f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:55:19 +0000 Subject: [PATCH] feat(server): add S3_DISABLE_CHECKSUMS option for Cloudflare R2 compatibility Co-authored-by: danielalves96 <62755605+danielalves96@users.noreply.github.com> --- apps/server/.env.example | 1 + apps/server/src/config/storage.config.ts | 5 +++++ apps/server/src/env.ts | 1 + apps/server/src/providers/s3-storage.provider.ts | 2 -- apps/server/src/types/storage.ts | 1 + 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/server/.env.example b/apps/server/.env.example index 9ab17e3..b573a94 100644 --- a/apps/server/.env.example +++ b/apps/server/.env.example @@ -15,4 +15,5 @@ DATABASE_URL="file:./palmr.db" # S3_BUCKET_NAME= # S3_FORCE_PATH_STYLE= # S3_REJECT_UNAUTHORIZED=true # Set to false to disable strict SSL certificate validation for self-signed certificates (optional, defaults to true) +# S3_DISABLE_CHECKSUMS=false # Set to true to disable automatic checksums for S3-compatible services that don't support them (e.g., Cloudflare R2) (optional, defaults to false) # PRESIGNED_URL_EXPIRATION=3600 # Duration in seconds for presigned URL expiration (optional, defaults to 3600 seconds / 1 hour) diff --git a/apps/server/src/config/storage.config.ts b/apps/server/src/config/storage.config.ts index 6c719d1..a60c82a 100644 --- a/apps/server/src/config/storage.config.ts +++ b/apps/server/src/config/storage.config.ts @@ -60,6 +60,7 @@ export const storageConfig: StorageConfig = (internalStorageConfig as StorageCon region: env.S3_REGION || "", bucketName: env.S3_BUCKET_NAME || "", forcePathStyle: env.S3_FORCE_PATH_STYLE === "true", + disableChecksums: env.S3_DISABLE_CHECKSUMS === "true", }; if (storageConfig.useSSL && env.S3_REJECT_UNAUTHORIZED === "false") { @@ -91,6 +92,8 @@ export const s3Client = hasValidConfig requestHandler: { requestTimeout: 300000, // 5 minutes timeout for S3 operations }, + // Disable automatic checksums when configured (e.g., for Cloudflare R2 compatibility) + requestChecksumCalculation: storageConfig.disableChecksums ? "WHEN_REQUIRED" : "WHEN_SUPPORTED", }) : null; @@ -145,5 +148,7 @@ export function createPublicS3Client(): S3Client | null { requestHandler: { requestTimeout: 300000, // 5 minutes timeout for S3 operations }, + // Disable automatic checksums when configured (e.g., for Cloudflare R2 compatibility) + requestChecksumCalculation: storageConfig.disableChecksums ? "WHEN_REQUIRED" : "WHEN_SUPPORTED", }); } diff --git a/apps/server/src/env.ts b/apps/server/src/env.ts index 4aa9ad0..e630246 100644 --- a/apps/server/src/env.ts +++ b/apps/server/src/env.ts @@ -12,6 +12,7 @@ const envSchema = z.object({ S3_BUCKET_NAME: z.string().optional(), S3_FORCE_PATH_STYLE: z.union([z.literal("true"), z.literal("false")]).default("false"), S3_REJECT_UNAUTHORIZED: z.union([z.literal("true"), z.literal("false")]).default("true"), + S3_DISABLE_CHECKSUMS: z.union([z.literal("true"), z.literal("false")]).default("false"), // Legacy encryption vars (kept for backward compatibility but not used with S3/Garage) ENCRYPTION_KEY: z.string().optional(), diff --git a/apps/server/src/providers/s3-storage.provider.ts b/apps/server/src/providers/s3-storage.provider.ts index c19eaa5..bc566db 100644 --- a/apps/server/src/providers/s3-storage.provider.ts +++ b/apps/server/src/providers/s3-storage.provider.ts @@ -92,7 +92,6 @@ export class S3StorageProvider implements StorageProvider { return await getSignedUrl(client, command, { expiresIn: expires, - unsignableHeaders: new Set(["x-amz-checksum-crc32"]), }); } @@ -221,7 +220,6 @@ export class S3StorageProvider implements StorageProvider { const url = await getSignedUrl(client, command, { expiresIn: expires, - unsignableHeaders: new Set(["x-amz-checksum-crc32"]), }); return url; } diff --git a/apps/server/src/types/storage.ts b/apps/server/src/types/storage.ts index 884a396..adcac2f 100644 --- a/apps/server/src/types/storage.ts +++ b/apps/server/src/types/storage.ts @@ -25,4 +25,5 @@ export interface StorageConfig { region: string; bucketName: string; forcePathStyle?: boolean; + disableChecksums?: boolean; }