From c8a401aa294aa3526b16cce2e8b7e7ca158ca8fa Mon Sep 17 00:00:00 2001 From: Ben Fornefeld Date: Mon, 10 Nov 2025 16:08:54 +0100 Subject: [PATCH 1/4] add sandbox creation route under /sbx/new --- src/app/sbx/new/route.ts | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/app/sbx/new/route.ts diff --git a/src/app/sbx/new/route.ts b/src/app/sbx/new/route.ts new file mode 100644 index 000000000..6870ba684 --- /dev/null +++ b/src/app/sbx/new/route.ts @@ -0,0 +1,79 @@ +import { SUPABASE_AUTH_HEADERS } from '@/configs/api' +import { AUTH_URLS, PROTECTED_URLS } from '@/configs/urls' +import { l } from '@/lib/clients/logger/logger' +import { createClient } from '@/lib/clients/supabase/server' +import { getDefaultTeam } from '@/server/auth/get-default-team' +import { getSessionInsecure } from '@/server/auth/get-session' +import Sandbox from 'e2b' +import { NextRequest, NextResponse } from 'next/server' +import { serializeError } from 'serialize-error' + +export const GET = async (req: NextRequest) => { + try { + const supabase = await createClient() + const { data, error } = await supabase.auth.getUser() + + if (error || !data.user) { + const params = new URLSearchParams({ + returnTo: req.url, + }) + + return NextResponse.redirect( + new URL(`${AUTH_URLS.SIGN_IN}?${params.toString()}`, req.url) + ) + } + + const defaultTeam = await getDefaultTeam(data.user.id) + + // unexpected state - sign out and redirect to sign-in + if (!defaultTeam) { + await supabase.auth.signOut() + + l.error( + { + key: 'sbx_new:unexpected_state', + user_id: data.user.id, + }, + `No default team found for user: ${data.user.id}` + ) + + return NextResponse.redirect(new URL(req.url).origin) + } + + const session = await getSessionInsecure(supabase) + + if (!session) { + const params = new URLSearchParams({ + returnTo: req.url, + }) + + return NextResponse.redirect( + new URL(`${AUTH_URLS.SIGN_IN}?${params.toString()}`, req.url) + ) + } + + const sbx = await Sandbox.create('base', { + domain: process.env.NEXT_PUBLIC_E2B_DOMAIN, + headers: { + ...SUPABASE_AUTH_HEADERS(session?.access_token, defaultTeam.id), + }, + }) + + const inspectUrl = PROTECTED_URLS.SANDBOX_INSPECT( + defaultTeam.slug, + sbx.sandboxId + ) + + return NextResponse.redirect(new URL(inspectUrl, req.url)) + } catch (error) { + l.warn( + { + key: 'sbx_new:unexpected_error', + error: serializeError(error), + }, + `sbx_new: unexpected error` + ) + + return NextResponse.redirect(new URL(req.url).origin) + } +} From c401c3776f728c5dc5c85b8e47fed76e09887907 Mon Sep 17 00:00:00 2001 From: Ben Fornefeld Date: Mon, 10 Nov 2025 16:20:38 +0100 Subject: [PATCH 2/4] fix: relative returnTo --- src/app/sbx/new/route.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/sbx/new/route.ts b/src/app/sbx/new/route.ts index 6870ba684..968ca042a 100644 --- a/src/app/sbx/new/route.ts +++ b/src/app/sbx/new/route.ts @@ -15,7 +15,7 @@ export const GET = async (req: NextRequest) => { if (error || !data.user) { const params = new URLSearchParams({ - returnTo: req.url, + returnTo: new URL(req.url).pathname, }) return NextResponse.redirect( @@ -44,7 +44,7 @@ export const GET = async (req: NextRequest) => { if (!session) { const params = new URLSearchParams({ - returnTo: req.url, + returnTo: new URL(req.url).pathname, }) return NextResponse.redirect( @@ -55,7 +55,7 @@ export const GET = async (req: NextRequest) => { const sbx = await Sandbox.create('base', { domain: process.env.NEXT_PUBLIC_E2B_DOMAIN, headers: { - ...SUPABASE_AUTH_HEADERS(session?.access_token, defaultTeam.id), + ...SUPABASE_AUTH_HEADERS(session.access_token, defaultTeam.id), }, }) From 575cb021e6cc67e1bc23566e2550232a5e1d4c02 Mon Sep 17 00:00:00 2001 From: Ben Fornefeld Date: Mon, 10 Nov 2025 16:23:31 +0100 Subject: [PATCH 3/4] fix: default team redirect --- src/app/sbx/new/route.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/app/sbx/new/route.ts b/src/app/sbx/new/route.ts index 968ca042a..d4e4e445b 100644 --- a/src/app/sbx/new/route.ts +++ b/src/app/sbx/new/route.ts @@ -25,21 +25,6 @@ export const GET = async (req: NextRequest) => { const defaultTeam = await getDefaultTeam(data.user.id) - // unexpected state - sign out and redirect to sign-in - if (!defaultTeam) { - await supabase.auth.signOut() - - l.error( - { - key: 'sbx_new:unexpected_state', - user_id: data.user.id, - }, - `No default team found for user: ${data.user.id}` - ) - - return NextResponse.redirect(new URL(req.url).origin) - } - const session = await getSessionInsecure(supabase) if (!session) { From d322ab9d9d9e87adf771e1c789091a38fc165dc5 Mon Sep 17 00:00:00 2001 From: Ben Fornefeld Date: Mon, 10 Nov 2025 16:33:42 +0100 Subject: [PATCH 4/4] chore: restructure --- src/app/sbx/new/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/sbx/new/route.ts b/src/app/sbx/new/route.ts index d4e4e445b..6b4cb9e13 100644 --- a/src/app/sbx/new/route.ts +++ b/src/app/sbx/new/route.ts @@ -23,8 +23,6 @@ export const GET = async (req: NextRequest) => { ) } - const defaultTeam = await getDefaultTeam(data.user.id) - const session = await getSessionInsecure(supabase) if (!session) { @@ -37,6 +35,8 @@ export const GET = async (req: NextRequest) => { ) } + const defaultTeam = await getDefaultTeam(data.user.id) + const sbx = await Sandbox.create('base', { domain: process.env.NEXT_PUBLIC_E2B_DOMAIN, headers: {