From e843a2b637cf24c00930d8da51b95f28ac73af88 Mon Sep 17 00:00:00 2001 From: BilalG1 Date: Tue, 11 Nov 2025 11:06:09 -0800 Subject: [PATCH] svix embedded portal (#1007) https://www.loom.com/share/ade557d34b674ecb9ae1d703b5332c9d ## Summary by CodeRabbit * **New Features** * Added support for inline webhook configuration portal rendering when available * Enhanced webhooks page with improved theming support * **Refactor** * Updated webhook token API to return structured data including optional server URL alongside token --- > [!NOTE] > Enables embedded Svix portal on the Webhooks page when available, updating the token API and shared types to return an optional portal URL and wiring it through the admin app. > > - **Frontend (Dashboard Webhooks page)**: > - Conditionally render Svix `AppPortal` when `svixToken.url` is provided; otherwise fall back to `SvixProvider` with token. > - Integrate theme support (`next-themes`) for portal `darkMode`; import `svix-react` styles. > - **Backend (API)**: > - Update `POST /api/latest/webhooks/svix-token` to return `{ token, url? }`, deriving `url` only when no `STACK_SVIX_SERVER_URL` is set. > - **Shared Types/SDK**: > - Extend `svixTokenAdminReadSchema` to include optional `url`. > - Change admin app `useSvixToken()` to return `{ token, url }` and propagate through implementation. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9f5dc52ecf219a23a927ca0645c933bb2c2329cd. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). Co-authored-by: Konsti Wohlwend --- .../api/latest/webhooks/svix-token/route.tsx | 7 +- .../[projectId]/webhooks/page-client.tsx | 91 +++++++++++-------- .../src/interface/crud/svix-token.ts | 1 + .../apps/implementations/admin-app-impl.ts | 4 +- .../stack-app/apps/interfaces/admin-app.ts | 2 +- 5 files changed, 61 insertions(+), 44 deletions(-) diff --git a/apps/backend/src/app/api/latest/webhooks/svix-token/route.tsx b/apps/backend/src/app/api/latest/webhooks/svix-token/route.tsx index 3433cac4c..f07e2459f 100644 --- a/apps/backend/src/app/api/latest/webhooks/svix-token/route.tsx +++ b/apps/backend/src/app/api/latest/webhooks/svix-token/route.tsx @@ -2,15 +2,20 @@ import { getSvixClient } from "@/lib/webhooks"; import { createCrudHandlers } from "@/route-handlers/crud-handler"; import { svixTokenCrud } from "@stackframe/stack-shared/dist/interface/crud/svix-token"; import { yupObject } from "@stackframe/stack-shared/dist/schema-fields"; +import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env"; import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies"; +const svixServerUrl = getEnvVariable("STACK_SVIX_SERVER_URL", ""); + const appPortalCrudHandlers = createLazyProxy(() => createCrudHandlers(svixTokenCrud, { paramsSchema: yupObject({}), onCreate: async ({ auth }) => { const svix = getSvixClient(); await svix.application.getOrCreate({ uid: auth.project.id, name: auth.project.id }); const result = await svix.authentication.appPortalAccess(auth.project.id, {}); - return { token: result.token }; + // svix embedded app portal is only available on hosted svix. + const url = svixServerUrl ? undefined : result.url; + return { token: result.token, url }; }, })); diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx index b34fe2187..d0dfe5128 100644 --- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx +++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx @@ -17,6 +17,56 @@ import { PageLayout } from "../page-layout"; import { useAdminApp } from "../use-admin-app"; import { getSvixResult } from "./utils"; import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises"; +import { useTheme } from "next-themes"; +import { AppPortal } from "svix-react"; +import "svix-react/style.css"; + + +export default function PageClient() { + const stackAdminApp = useAdminApp(); + const svixToken = stackAdminApp.useSvixToken(); + const { resolvedTheme } = useTheme(); + const [updateCounter, setUpdateCounter] = useState(0); + const [testDialogEndpoint, setTestDialogEndpoint] = useState(null); + + return ( + + + {svixToken.url ? ( +
+ +
+ ) : ( + + setUpdateCounter(x => x + 1)} + onTestRequested={(endpoint) => setTestDialogEndpoint(endpoint)} + /> + {testDialogEndpoint && ( + { + if (!open) { + setTestDialogEndpoint(null); + } + }} + /> + )} + + )} +
+
+ ); +} type Endpoint = { id: string, @@ -157,7 +207,7 @@ function CreateDialog(props: { ); } -export function EndpointEditDialog(props: { +function EndpointEditDialog(props: { open: boolean, onClose: () => void, endpoint: Endpoint, @@ -369,42 +419,3 @@ function Endpoints(props: { updateFn: () => void, onTestRequested: (endpoint: En ); } } - -export default function PageClient() { - const stackAdminApp = useAdminApp(); - const svixToken = stackAdminApp.useSvixToken(); - const [updateCounter, setUpdateCounter] = useState(0); - const [testDialogEndpoint, setTestDialogEndpoint] = useState(null); - - return ( - - - - setUpdateCounter(x => x + 1)} - onTestRequested={(endpoint) => setTestDialogEndpoint(endpoint)} - /> - {testDialogEndpoint && ( - { - if (!open) { - setTestDialogEndpoint(null); - } - }} - /> - )} - - - - ); -} diff --git a/packages/stack-shared/src/interface/crud/svix-token.ts b/packages/stack-shared/src/interface/crud/svix-token.ts index fbdff4e84..c113bfbb6 100644 --- a/packages/stack-shared/src/interface/crud/svix-token.ts +++ b/packages/stack-shared/src/interface/crud/svix-token.ts @@ -3,6 +3,7 @@ import { yupObject, yupString } from "../../schema-fields"; export const svixTokenAdminReadSchema = yupObject({ token: yupString().defined(), + url: yupString().optional(), }).defined(); export const svixTokenAdminCreateSchema = yupObject({}).defined(); diff --git a/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts b/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts index e5554c8a0..4c49dc285 100644 --- a/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts +++ b/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts @@ -416,9 +416,9 @@ export class _StackAdminAppImplIncomplete, deleteProjectPermissionDefinition(permissionId: string): Promise, - useSvixToken(): string, // THIS_LINE_PLATFORM react-like + useSvixToken(): { token: string, url: string | undefined }, // THIS_LINE_PLATFORM react-like sendTestEmail(options: { recipientEmail: string,