diff --git a/apps/dashboard/src/app/login/auth-actions.ts b/apps/dashboard/src/app/login/auth-actions.ts index d4793802a7b..57631b09f33 100644 --- a/apps/dashboard/src/app/login/auth-actions.ts +++ b/apps/dashboard/src/app/login/auth-actions.ts @@ -44,6 +44,18 @@ export async function doLogin(payload: VerifyLoginPayloadParams) { } const cookieStore = await cookies(); + const utmCookies = cookieStore + .getAll() + .filter((cookie) => { + return cookie.name.startsWith("utm_"); + }) + .reduce( + (acc, cookie) => { + acc[cookie.name] = cookie.value; + return acc; + }, + {} as Record, + ); // forward the request to the API server const res = await fetch(`${API_SERVER_URL}/v2/siwe/login`, { @@ -53,7 +65,7 @@ export async function doLogin(payload: VerifyLoginPayloadParams) { "x-service-api-key": THIRDWEB_API_SECRET, }, // set the createAccount flag to true to create a new account if it does not exist - body: JSON.stringify({ ...payload, createAccount: true }), + body: JSON.stringify({ ...payload, createAccount: true, utm: utmCookies }), }); // if the request failed, log the error and throw an error diff --git a/apps/dashboard/src/middleware.ts b/apps/dashboard/src/middleware.ts index bd28eb3eb7d..799fe8945d7 100644 --- a/apps/dashboard/src/middleware.ts +++ b/apps/dashboard/src/middleware.ts @@ -22,12 +22,40 @@ export const config = { }; export async function middleware(request: NextRequest) { + let cookiesToSet: Record | undefined = undefined; const { pathname } = request.nextUrl; const activeAccount = request.cookies.get(COOKIE_ACTIVE_ACCOUNT)?.value; const authCookie = activeAccount ? request.cookies.get(COOKIE_PREFIX_TOKEN + getAddress(activeAccount)) : null; + // utm collection + // NOTE: this is not working for pages with rewrites in next.config.js - (framer pages) + // if user is already signed in - don't bother capturing utm params + if (!authCookie) { + const searchParamsEntries = request.nextUrl.searchParams.entries(); + const utmParams: Map = new Map(); + for (const param of searchParamsEntries) { + if (param[0].startsWith("utm_")) { + utmParams.set(param[0], param[1]); + } + } + + // if we have utm params, set them as cookies + if (utmParams.size) { + for (const [key, value] of utmParams.entries()) { + // if its already set - don't set it again + if (!request.cookies.get(key)) { + if (!cookiesToSet) { + cookiesToSet = {}; + } + + cookiesToSet[key] = value; + } + } + } + } + // logged in paths if (isLoginRequired(pathname)) { // check if the user is logged in (has a valid auth cookie) @@ -36,12 +64,11 @@ export async function middleware(request: NextRequest) { const searchParamsString = request.nextUrl.searchParams.toString(); // if not logged in, rewrite to login page - return redirect( - request, - "/login", - `next=${encodeURIComponent(`${pathname}${searchParamsString ? `?${searchParamsString}` : ""}`)}`, - false, - ); + return redirect(request, "/login", { + permanent: false, + searchParams: `next=${encodeURIComponent(`${pathname}${searchParamsString ? `?${searchParamsString}` : ""}`)}`, + cookiesToSet, + }); } } @@ -50,7 +77,15 @@ export async function middleware(request: NextRequest) { // if it's the homepage and we have an auth cookie, redirect to the dashboard if (paths.length === 1 && paths[0] === "" && authCookie) { - return redirect(request, "/team"); + return redirect( + request, + "/team", + cookiesToSet + ? { + cookiesToSet, + } + : undefined, + ); } // if the first section of the path is a number, check if it's a valid chain_id and re-write it to the slug @@ -69,6 +104,7 @@ export async function middleware(request: NextRequest) { return redirect( request, `/${chainMetadata.slug}/${paths.slice(1).join("/")}`, + cookiesToSet ? { cookiesToSet } : undefined, ); } } catch { @@ -83,20 +119,18 @@ export async function middleware(request: NextRequest) { // special case for "deployer.thirdweb.eth" // we want to always redirect this to "thirdweb.eth/..." if (paths[0] === "deployer.thirdweb.eth") { - return redirect( - request, - `/thirdweb.eth/${paths.slice(1).join("/")}`, - undefined, - true, - ); + return redirect(request, `/thirdweb.eth/${paths.slice(1).join("/")}`, { + permanent: true, + cookiesToSet, + }); } // if we have exactly 1 path part, we're in the
case -> profile page if (paths.length === 1) { - return rewrite(request, `/profile${pathname}`); + return rewrite(request, `/profile${pathname}`, cookiesToSet); } // if we have more than 1 path part, we're in the
/ case -> publish page if (paths.length > 1) { - return rewrite(request, `/published-contract${pathname}`); + return rewrite(request, `/published-contract${pathname}`, cookiesToSet); } } @@ -108,15 +142,22 @@ export async function middleware(request: NextRequest) { if (firstTeam) { const modifiedPaths = [...paths]; modifiedPaths[1] = firstTeam.slug; - return redirect( - request, - `/${modifiedPaths.join("/")}`, - request.nextUrl.searchParams.toString(), - ); + return redirect(request, `/${modifiedPaths.join("/")}`, { + searchParams: request.nextUrl.searchParams.toString(), + cookiesToSet, + }); } } // END /
/... case // all other cases are handled by the file system router so we just fall through + if (cookiesToSet) { + const defaultResponse = NextResponse.next(); + for (const entry of Object.entries(cookiesToSet)) { + defaultResponse.cookies.set(entry[0], entry[1]); + } + + return defaultResponse; + } } function isPossibleEVMAddress(address: string) { @@ -125,20 +166,46 @@ function isPossibleEVMAddress(address: string) { // utils for rewriting and redirecting with relative paths -function rewrite(request: NextRequest, relativePath: string) { +function rewrite( + request: NextRequest, + relativePath: string, + cookiesToSet: Record | undefined, +) { const url = request.nextUrl.clone(); url.pathname = relativePath; - return NextResponse.rewrite(url); + const res = NextResponse.rewrite(url); + + if (cookiesToSet) { + for (const entry of Object.entries(cookiesToSet)) { + res.cookies.set(entry[0], entry[1]); + } + } + + return res; } function redirect( request: NextRequest, relativePath: string, - searchParams?: string, - permanent = false, + options: + | { + searchParams?: string; + permanent?: boolean; + cookiesToSet?: Record | undefined; + } + | undefined, ) { + const permanent = options?.permanent ?? false; const url = request.nextUrl.clone(); url.pathname = relativePath; - url.search = searchParams ? `?${searchParams}` : ""; - return NextResponse.redirect(url, permanent ? 308 : undefined); + url.search = options?.searchParams ? `?${options.searchParams}` : ""; + const res = NextResponse.redirect(url, permanent ? 308 : undefined); + + if (options?.cookiesToSet) { + for (const entry of Object.entries(options.cookiesToSet)) { + res.cookies.set(entry[0], entry[1]); + } + } + + return res; }