From b1c2f90feb217f665a41402486ae5c32897abd82 Mon Sep 17 00:00:00 2001 From: Midka Date: Tue, 27 Feb 2024 13:39:43 +0200 Subject: [PATCH] fix(S3): add support for custom endpoint (#2049) Co-authored-by: Matti Nannt --- .../external-auth-providers/page.mdx | 7 ++++++- .../pages/api/oss-friends/index.ts | 7 ++++--- apps/web/app/health/page.tsx | 12 +++++++++++ packages/lib/storage/service.ts | 21 +++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/apps/formbricks-com/app/docs/self-hosting/external-auth-providers/page.mdx b/apps/formbricks-com/app/docs/self-hosting/external-auth-providers/page.mdx index 92366fa462..7537a7480b 100644 --- a/apps/formbricks-com/app/docs/self-hosting/external-auth-providers/page.mdx +++ b/apps/formbricks-com/app/docs/self-hosting/external-auth-providers/page.mdx @@ -122,6 +122,11 @@ These variables can be provided at the runtime i.e. in your docker-compose file. | NEXTAUTH_SECRET | Secret for NextAuth, used for session signing and encryption. | required | (Generated by the user) | | ENCRYPTION_KEY | Secret for used by Formbricks for data encryption | required | (Generated by the user) | | NEXTAUTH_URL | Location of the auth server. By default, this is the Formbricks docker instance itself. | required | `http://localhost:3000` | +| S3_ACCESS_KEY | Access key for S3. | optional (required if S3 is enabled) | | +| S3_SECRET_KEY | Secret key for S3. | optional (required if S3 is enabled) | | +| S3_REGION | Region for S3. | optional (required if S3 is enabled) | | +| S3_BUCKET | Bucket name for S3. | optional (required if S3 is enabled) | | +| S3_ENDPOINT | Endpoint for S3. | optional (required if S3 is enabled) | | | PRIVACY_URL | URL for privacy policy. | optional | | | TERMS_URL | URL for terms of service. | optional | | | IMPRINT_URL | URL for imprint. | optional | | @@ -155,7 +160,7 @@ These variables can be provided at the runtime i.e. in your docker-compose file. | OIDC_CLIENT_ID | Client ID for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | | | OIDC_CLIENT_SECRET | Secret for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | | | OIDC_ISSUER | Issuer URL for Custom OpenID Connect Provider (should have `.well-known` configured at this) | optional (required if OIDC auth is enabled) | | -| OIDC_SIGNING_ALGORITHM | Signing Algorithm for Custom OpenID Connect Provider | optional | `RS256` | +| OIDC_SIGNING_ALGORITHM | Signing Algorithm for Custom OpenID Connect Provider | optional | `RS256` | ## Build-time Variables diff --git a/apps/formbricks-com/pages/api/oss-friends/index.ts b/apps/formbricks-com/pages/api/oss-friends/index.ts index 2805136d10..bccd11f304 100644 --- a/apps/formbricks-com/pages/api/oss-friends/index.ts +++ b/apps/formbricks-com/pages/api/oss-friends/index.ts @@ -200,9 +200,10 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) href: "https://spark-framework.net", }, { - "name": "Tiledesk", - "description": "The innovative open-source framework for developing LLM-enabled chatbots, Tiledesk empowers developers to create advanced, conversational AI agents.", - "href": "https://tiledesk.com" + name: "Tiledesk", + description: + "The innovative open-source framework for developing LLM-enabled chatbots, Tiledesk empowers developers to create advanced, conversational AI agents.", + href: "https://tiledesk.com", }, { name: "Tolgee", diff --git a/apps/web/app/health/page.tsx b/apps/web/app/health/page.tsx index ef7458ae48..47faf1f105 100644 --- a/apps/web/app/health/page.tsx +++ b/apps/web/app/health/page.tsx @@ -2,6 +2,8 @@ import { CheckBadgeIcon } from "@heroicons/react/24/outline"; import { Metadata } from "next"; import { prisma } from "@formbricks/database"; +import { IS_S3_CONFIGURED } from "@formbricks/lib/constants"; +import { testS3Connection } from "@formbricks/lib/storage/service"; export const dynamic = "force-dynamic"; // no caching @@ -24,8 +26,18 @@ const checkDatabaseConnection = async () => { } }; +const checkS3Connection = async () => { + if (!IS_S3_CONFIGURED) { + // dont try connecting if not in use + return; + } + + await testS3Connection(); +}; + export default async function HealthPage() { await checkDatabaseConnection(); + await checkS3Connection(); return (
diff --git a/packages/lib/storage/service.ts b/packages/lib/storage/service.ts index 317f514395..dd1a3d8325 100644 --- a/packages/lib/storage/service.ts +++ b/packages/lib/storage/service.ts @@ -2,6 +2,7 @@ import { DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, + ListBucketsCommand, ListObjectsCommand, PutObjectCommand, S3Client, @@ -49,6 +50,26 @@ export const getS3Client = () => { return s3ClientInstance; }; +export const testS3Connection = async () => { + try { + const s3Client = getS3Client(); + const cmd = new ListBucketsCommand({}); + const result = await s3Client.send(cmd); + + if (!result.Buckets) { + throw new Error("Access denied to any buckets"); + } + + const bucketNames = result.Buckets.map((b) => b.Name); + + if (!bucketNames.includes(S3_BUCKET_NAME)) { + throw new Error("Access denied to bucket"); + } + } catch (error) { + throw new Error(`S3: ${error}`); + } +}; + const ensureDirectoryExists = async (dirPath: string) => { try { await access(dirPath);