From eec74f4e186a7cb7c7a103b0da54c8d8848f2199 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:59:53 -0500 Subject: [PATCH 1/2] fix direct url --- packages/cli/src/main.ts | 3 +++ packages/cli/src/utils.ts | 2 ++ version.txt | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/main.ts b/packages/cli/src/main.ts index b88cd9c3ef..78a3ce9823 100644 --- a/packages/cli/src/main.ts +++ b/packages/cli/src/main.ts @@ -525,15 +525,18 @@ Full guide: https://docs.getinboxzero.com/self-hosting/microsoft-oauth`, if (runWebInDocker) { // Web app runs in Docker: use container hostnames env.DATABASE_URL = `postgresql://${env.POSTGRES_USER}:${env.POSTGRES_PASSWORD}@db:5432/${env.POSTGRES_DB}`; + env.DIRECT_URL = env.DATABASE_URL; env.UPSTASH_REDIS_URL = "http://serverless-redis-http:80"; } else { // Web app runs on host: containers expose ports to localhost env.DATABASE_URL = `postgresql://${env.POSTGRES_USER}:${env.POSTGRES_PASSWORD}@localhost:${postgresPort}/${env.POSTGRES_DB}`; + env.DIRECT_URL = env.DATABASE_URL; env.UPSTASH_REDIS_URL = `http://localhost:${redisPort}`; } } else { // External infrastructure - set placeholders for user to fill in env.DATABASE_URL = "postgresql://user:password@your-host:5432/inboxzero"; + env.DIRECT_URL = env.DATABASE_URL; env.UPSTASH_REDIS_URL = "https://your-redis-url"; env.UPSTASH_REDIS_TOKEN = "your-redis-token"; } diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index e37b48dfd1..0353aefec1 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -50,11 +50,13 @@ export function generateEnvFile(config: { setValue("POSTGRES_PASSWORD", env.POSTGRES_PASSWORD); setValue("POSTGRES_DB", env.POSTGRES_DB); setValue("DATABASE_URL", wrapInQuotes(env.DATABASE_URL)); + setValue("DIRECT_URL", wrapInQuotes(env.DIRECT_URL)); setValue("UPSTASH_REDIS_URL", wrapInQuotes(env.UPSTASH_REDIS_URL)); setValue("UPSTASH_REDIS_TOKEN", env.UPSTASH_REDIS_TOKEN); } else { // External infra - set placeholders setValue("DATABASE_URL", wrapInQuotes(env.DATABASE_URL)); + setValue("DIRECT_URL", wrapInQuotes(env.DIRECT_URL)); setValue("UPSTASH_REDIS_URL", wrapInQuotes(env.UPSTASH_REDIS_URL)); setValue("UPSTASH_REDIS_TOKEN", env.UPSTASH_REDIS_TOKEN); } diff --git a/version.txt b/version.txt index 81e4389812..7ff31a5a05 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.21.30 +v2.21.31 From 18e3dfa82a6772b62bc60bd2939532a0cbe1c261 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Wed, 3 Dec 2025 00:08:51 -0500 Subject: [PATCH 2/2] fix after cookie issue --- .../assistant/ResultDisplay.tsx | 2 +- .../[emailAccountId]/onboarding/page.tsx | 9 +++- apps/web/app/(landing)/welcome/page.tsx | 9 +++- apps/web/app/(landing)/welcome/utms.tsx | 45 ++++++++++++------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx index 910fb2fa5b..7602e21f49 100644 --- a/apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx @@ -128,7 +128,7 @@ export function ResultDisplayContent({ result }: { result: RunRulesResult }) {
{result.actionItems?.length ? ( <> -
Actions taken:
+
Actions:
({ diff --git a/apps/web/app/(app)/[emailAccountId]/onboarding/page.tsx b/apps/web/app/(app)/[emailAccountId]/onboarding/page.tsx index cab181928c..d3bb0769ca 100644 --- a/apps/web/app/(app)/[emailAccountId]/onboarding/page.tsx +++ b/apps/web/app/(app)/[emailAccountId]/onboarding/page.tsx @@ -3,7 +3,10 @@ import type { Metadata } from "next"; import { cookies } from "next/headers"; import { after } from "next/server"; import { OnboardingContent } from "@/app/(app)/[emailAccountId]/onboarding/OnboardingContent"; -import { fetchUserAndStoreUtms } from "@/app/(landing)/welcome/utms"; +import { + extractUtmValues, + fetchUserAndStoreUtms, +} from "@/app/(landing)/welcome/utms"; import { auth } from "@/utils/auth"; export const maxDuration = 300; @@ -25,10 +28,12 @@ export default async function OnboardingPage(props: { const authPromise = auth(); const cookieStore = await cookies(); + const utmValues = extractUtmValues(cookieStore); + after(async () => { const user = await authPromise; if (!user?.user) return; - await fetchUserAndStoreUtms(user.user.id, cookieStore); + await fetchUserAndStoreUtms(user.user.id, utmValues); }); return ( diff --git a/apps/web/app/(landing)/welcome/page.tsx b/apps/web/app/(landing)/welcome/page.tsx index 85593284ab..0168279978 100644 --- a/apps/web/app/(landing)/welcome/page.tsx +++ b/apps/web/app/(landing)/welcome/page.tsx @@ -6,7 +6,10 @@ import { OnboardingForm } from "@/app/(landing)/welcome/form"; import { SquaresPattern } from "@/app/(landing)/home/SquaresPattern"; import { PageHeading, TypographyP } from "@/components/Typography"; import { CardBasic } from "@/components/ui/card"; -import { fetchUserAndStoreUtms } from "@/app/(landing)/welcome/utms"; +import { + extractUtmValues, + fetchUserAndStoreUtms, +} from "@/app/(landing)/welcome/utms"; import { auth } from "@/utils/auth"; export const metadata: Metadata = { @@ -27,10 +30,12 @@ export default async function WelcomePage(props: { const authPromise = auth(); const cookieStore = await cookies(); + const utmValues = extractUtmValues(cookieStore); + after(async () => { const user = await authPromise; if (!user?.user) return; - await fetchUserAndStoreUtms(user.user.id, cookieStore); + await fetchUserAndStoreUtms(user.user.id, utmValues); }); return ( diff --git a/apps/web/app/(landing)/welcome/utms.tsx b/apps/web/app/(landing)/welcome/utms.tsx index 4fbf6c5c6f..0748376231 100644 --- a/apps/web/app/(landing)/welcome/utms.tsx +++ b/apps/web/app/(landing)/welcome/utms.tsx @@ -4,9 +4,31 @@ import type { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension const logger = createScopedLogger("utms"); +type UtmValues = { + utmCampaign?: string; + utmMedium?: string; + utmSource?: string; + utmTerm?: string; + affiliate?: string; +}; + +// Extract UTM values from cookies before passing to after() callback +// This is required because request APIs (cookies/headers) cannot be used +// inside after() in Server Components - only in Server Actions and Route Handlers +// See: https://nextjs.org/docs/app/api-reference/functions/after +export function extractUtmValues(cookies: ReadonlyRequestCookies): UtmValues { + return { + utmCampaign: cookies.get("utm_campaign")?.value, + utmMedium: cookies.get("utm_medium")?.value, + utmSource: cookies.get("utm_source")?.value, + utmTerm: cookies.get("utm_term")?.value, + affiliate: cookies.get("affiliate")?.value, + }; +} + export async function fetchUserAndStoreUtms( userId: string, - cookies: ReadonlyRequestCookies, + utmValues: UtmValues, ) { const user = await prisma.user .findUnique({ @@ -19,26 +41,19 @@ export async function fetchUserAndStoreUtms( }); if (user && !user.utms) { - await storeUtms(userId, cookies); + await storeUtms(userId, utmValues); } } -// `cookies` passed in as we can't do await cookies() in the `after` hook -async function storeUtms(userId: string, cookies: ReadonlyRequestCookies) { +async function storeUtms(userId: string, utmValues: UtmValues) { logger.info("Storing utms", { userId }); - const utmCampaign = cookies.get("utm_campaign"); - const utmMedium = cookies.get("utm_medium"); - const utmSource = cookies.get("utm_source"); - const utmTerm = cookies.get("utm_term"); - const affiliate = cookies.get("affiliate"); - const utms = { - utmCampaign: utmCampaign?.value, - utmMedium: utmMedium?.value, - utmSource: utmSource?.value, - utmTerm: utmTerm?.value, - affiliate: affiliate?.value, + utmCampaign: utmValues.utmCampaign, + utmMedium: utmValues.utmMedium, + utmSource: utmValues.utmSource, + utmTerm: utmValues.utmTerm, + affiliate: utmValues.affiliate, }; try {