Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export function ResultDisplayContent({ result }: { result: RunRulesResult }) {
<div className="mt-2">
{result.actionItems?.length ? (
<>
<div className="font-medium text-sm mb-1">Actions taken:</div>
<div className="font-medium text-sm mb-1">Actions:</div>
<Actions
actions={
result.actionItems?.map((action) => ({
Expand Down
9 changes: 7 additions & 2 deletions apps/web/app/(app)/[emailAccountId]/onboarding/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 (
Expand Down
9 changes: 7 additions & 2 deletions apps/web/app/(landing)/welcome/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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 (
Expand Down
45 changes: 30 additions & 15 deletions apps/web/app/(landing)/welcome/utms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -19,26 +41,19 @@ export async function fetchUserAndStoreUtms(
});

if (user && !user.utms) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Persisting utms even when all values are undefined makes user.utms truthy and blocks saving real UTMs later. Consider only writing when at least one value is present (or treat an empty object as "no utms").

Suggested change
if (user && !user.utms) {
if (user && !user.utms && Object.values(utmValues).some((v) => v != null && v !== "")) {
await storeUtms(userId, utmValues);
}

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

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 {
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.21.30
v2.21.31
Loading