From c883965dde20ead66b1a4dc11f235666e74575a6 Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Wed, 30 Jul 2025 14:11:36 -0700 Subject: [PATCH] make s3 optional --- apps/backend/.env | 8 ++++++++ apps/backend/src/s3.tsx | 35 ++++++++++++++++++++++++----------- docker/server/.env | 6 ++++++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/apps/backend/.env b/apps/backend/.env index a485cf467..72476a6c5 100644 --- a/apps/backend/.env +++ b/apps/backend/.env @@ -50,6 +50,14 @@ STACK_DIRECT_DATABASE_CONNECTION_STRING=# enter your direct (unpooled or session STACK_SVIX_SERVER_URL=# For prod, leave it empty. For local development, use `http://localhost:8113` STACK_SVIX_API_KEY=# enter the API key for the Svix webhook service here. Use `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NTUxNDA2MzksImV4cCI6MTk3MDUwMDYzOSwibmJmIjoxNjU1MTQwNjM5LCJpc3MiOiJzdml4LXNlcnZlciIsInN1YiI6Im9yZ18yM3JiOFlkR3FNVDBxSXpwZ0d3ZFhmSGlyTXUifQ.En8w77ZJWbd0qrMlHHupHUB-4cx17RfzFykseg95SUk` for local development +# S3 +STACK_S3_ENDPOINT= +STACK_S3_REGION= +STACK_S3_ACCESS_KEY_ID= +STACK_S3_SECRET_ACCESS_KEY= +STACK_S3_BUCKET= + + # Misc, optional STACK_ACCESS_TOKEN_EXPIRATION_TIME=# enter the expiration time for the access token here. Optional, don't specify it for default value STACK_SETUP_ADMIN_GITHUB_ID=# enter the account ID of the admin user here, and after running the seed script they will be able to access the internal project in the Stack dashboard. Optional, don't specify it for default value diff --git a/apps/backend/src/s3.tsx b/apps/backend/src/s3.tsx index 79656da97..561c354bd 100644 --- a/apps/backend/src/s3.tsx +++ b/apps/backend/src/s3.tsx @@ -1,20 +1,29 @@ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env"; -import { StatusError } from "@stackframe/stack-shared/dist/utils/errors"; +import { StackAssertionError, StatusError } from "@stackframe/stack-shared/dist/utils/errors"; import { parseBase64Image } from "./lib/images"; -export const s3 = new S3Client({ - region: getEnvVariable("STACK_S3_REGION"), - endpoint: getEnvVariable("STACK_S3_ENDPOINT"), +const S3_REGION = getEnvVariable("STACK_S3_REGION", "us-east-1"); +const S3_ENDPOINT = getEnvVariable("STACK_S3_ENDPOINT", ""); +const S3_BUCKET = getEnvVariable("STACK_S3_BUCKET", ""); +const S3_ACCESS_KEY_ID = getEnvVariable("STACK_S3_ACCESS_KEY_ID", ""); +const S3_SECRET_ACCESS_KEY = getEnvVariable("STACK_S3_SECRET_ACCESS_KEY", ""); + +const HAS_S3 = !!S3_BUCKET && !!S3_ACCESS_KEY_ID && !!S3_SECRET_ACCESS_KEY; + +if (!HAS_S3) { + console.warn("S3 bucket is not configured. File upload features will not be available."); +} + +const s3Client = HAS_S3 ? new S3Client({ + region: S3_REGION, + endpoint: S3_ENDPOINT, forcePathStyle: true, credentials: { - accessKeyId: getEnvVariable("STACK_S3_ACCESS_KEY_ID"), - secretAccessKey: getEnvVariable("STACK_S3_SECRET_ACCESS_KEY"), + accessKeyId: S3_ACCESS_KEY_ID, + secretAccessKey: S3_SECRET_ACCESS_KEY, }, -}); - -export const S3_BUCKET = getEnvVariable("STACK_S3_BUCKET"); -export const S3_ENDPOINT = getEnvVariable("STACK_S3_ENDPOINT"); +}) : undefined; export function getS3PublicUrl(key: string): string { return `${S3_ENDPOINT}/${S3_BUCKET}/${key}`; @@ -29,6 +38,10 @@ export async function uploadBase64Image({ maxBytes?: number, folderName: string, }) { + if (!s3Client) { + throw new StackAssertionError("S3 is not configured"); + } + const { buffer, metadata } = await parseBase64Image(input, { maxBytes }); const key = `${folderName}/${crypto.randomUUID()}.${metadata.format}`; @@ -39,7 +52,7 @@ export async function uploadBase64Image({ Body: buffer, }); - await s3.send(command); + await s3Client.send(command); return { key, diff --git a/docker/server/.env b/docker/server/.env index 7e34dc6a0..c581d7420 100644 --- a/docker/server/.env +++ b/docker/server/.env @@ -32,3 +32,9 @@ STACK_SVIX_API_KEY= STACK_OPENAI_API_KEY=# enter your openai api key if you want to use the openai related features STACK_SKIP_SEED_SCRIPT=# true to skip the seed script + +STACK_S3_ENDPOINT= +STACK_S3_REGION= +STACK_S3_ACCESS_KEY_ID= +STACK_S3_SECRET_ACCESS_KEY= +STACK_S3_BUCKET=