fix(platform): evaluate chat flag after auth for correct redirect (#11873)

Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Nicholas Tindle
2026-01-28 14:58:02 -06:00
committed by GitHub
parent 7df867d645
commit e0dfae5732
9 changed files with 75 additions and 34 deletions

View File

@@ -261,18 +261,36 @@ async def get_onboarding_agents(
return await get_recommended_agents(user_id)
class OnboardingStatusResponse(pydantic.BaseModel):
"""Response for onboarding status check."""
is_onboarding_enabled: bool
is_chat_enabled: bool
@v1_router.get(
"/onboarding/enabled",
summary="Is onboarding enabled",
tags=["onboarding", "public"],
response_model=OnboardingStatusResponse,
)
async def is_onboarding_enabled(
user_id: Annotated[str, Security(get_user_id)],
) -> bool:
# If chat is enabled for user, skip legacy onboarding
if await is_feature_enabled(Flag.CHAT, user_id, False):
return False
return await onboarding_enabled()
) -> OnboardingStatusResponse:
# Check if chat is enabled for user
is_chat_enabled = await is_feature_enabled(Flag.CHAT, user_id, False)
# If chat is enabled, skip legacy onboarding
if is_chat_enabled:
return OnboardingStatusResponse(
is_onboarding_enabled=False,
is_chat_enabled=True,
)
return OnboardingStatusResponse(
is_onboarding_enabled=await onboarding_enabled(),
is_chat_enabled=False,
)
@v1_router.post(

View File

@@ -2,8 +2,9 @@
import { LoadingSpinner } from "@/components/atoms/LoadingSpinner/LoadingSpinner";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { resolveResponse, shouldShowOnboarding } from "@/app/api/helpers";
import { resolveResponse, getOnboardingStatus } from "@/app/api/helpers";
import { getV1OnboardingState } from "@/app/api/__generated__/endpoints/onboarding/onboarding";
import { getHomepageRoute } from "@/lib/constants";
export default function OnboardingPage() {
const router = useRouter();
@@ -11,10 +12,13 @@ export default function OnboardingPage() {
useEffect(() => {
async function redirectToStep() {
try {
// Check if onboarding is enabled
const isEnabled = await shouldShowOnboarding();
if (!isEnabled) {
router.replace("/");
// Check if onboarding is enabled (also gets chat flag for redirect)
const { shouldShowOnboarding, isChatEnabled } =
await getOnboardingStatus();
const homepageRoute = getHomepageRoute(isChatEnabled);
if (!shouldShowOnboarding) {
router.replace(homepageRoute);
return;
}
@@ -22,7 +26,7 @@ export default function OnboardingPage() {
// Handle completed onboarding
if (onboarding.completedSteps.includes("GET_RESULTS")) {
router.replace("/");
router.replace(homepageRoute);
return;
}

View File

@@ -1,8 +1,9 @@
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
import { getHomepageRoute } from "@/lib/constants";
import BackendAPI from "@/lib/autogpt-server-api";
import { NextResponse } from "next/server";
import { revalidatePath } from "next/cache";
import { shouldShowOnboarding } from "@/app/api/helpers";
import { getOnboardingStatus } from "@/app/api/helpers";
// Handle the callback to complete the user session login
export async function GET(request: Request) {
@@ -25,11 +26,15 @@ export async function GET(request: Request) {
const api = new BackendAPI();
await api.createUser();
if (await shouldShowOnboarding()) {
// Get onboarding status from backend (includes chat flag evaluated for this user)
const { shouldShowOnboarding, isChatEnabled } =
await getOnboardingStatus();
if (shouldShowOnboarding) {
next = "/onboarding";
revalidatePath("/onboarding", "layout");
} else {
revalidatePath("/", "layout");
next = getHomepageRoute(isChatEnabled);
revalidatePath(next, "layout");
}
} catch (createUserError) {
console.error("Error creating user:", createUserError);

View File

@@ -1,10 +1,11 @@
"use server";
import { getHomepageRoute } from "@/lib/constants";
import BackendAPI from "@/lib/autogpt-server-api";
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
import { loginFormSchema } from "@/types/auth";
import * as Sentry from "@sentry/nextjs";
import { shouldShowOnboarding } from "../../api/helpers";
import { getOnboardingStatus } from "../../api/helpers";
export async function login(email: string, password: string) {
try {
@@ -36,11 +37,15 @@ export async function login(email: string, password: string) {
const api = new BackendAPI();
await api.createUser();
const onboarding = await shouldShowOnboarding();
// Get onboarding status from backend (includes chat flag evaluated for this user)
const { shouldShowOnboarding, isChatEnabled } = await getOnboardingStatus();
const next = shouldShowOnboarding
? "/onboarding"
: getHomepageRoute(isChatEnabled);
return {
success: true,
onboarding,
next,
};
} catch (err) {
Sentry.captureException(err);

View File

@@ -97,13 +97,8 @@ export function useLoginPage() {
throw new Error(result.error || "Login failed");
}
if (nextUrl) {
router.replace(nextUrl);
} else if (result.onboarding) {
router.replace("/onboarding");
} else {
router.replace(homepageRoute);
}
// Prefer URL's next parameter, then use backend-determined route
router.replace(nextUrl || result.next || homepageRoute);
} catch (error) {
toast({
title:

View File

@@ -5,14 +5,13 @@ import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
import { signupFormSchema } from "@/types/auth";
import * as Sentry from "@sentry/nextjs";
import { isWaitlistError, logWaitlistError } from "../../api/auth/utils";
import { shouldShowOnboarding } from "../../api/helpers";
import { getOnboardingStatus } from "../../api/helpers";
export async function signup(
email: string,
password: string,
confirmPassword: string,
agreeToTerms: boolean,
isChatEnabled: boolean,
) {
try {
const parsed = signupFormSchema.safeParse({
@@ -59,8 +58,9 @@ export async function signup(
await supabase.auth.setSession(data.session);
}
const isOnboardingEnabled = await shouldShowOnboarding();
const next = isOnboardingEnabled
// Get onboarding status from backend (includes chat flag evaluated for this user)
const { shouldShowOnboarding, isChatEnabled } = await getOnboardingStatus();
const next = shouldShowOnboarding
? "/onboarding"
: getHomepageRoute(isChatEnabled);

View File

@@ -108,7 +108,6 @@ export function useSignupPage() {
data.password,
data.confirmPassword,
data.agreeToTerms,
isChatEnabled === true,
);
setIsLoading(false);

View File

@@ -175,9 +175,12 @@ export async function resolveResponse<
return res.data;
}
export async function shouldShowOnboarding() {
const isEnabled = await resolveResponse(getV1IsOnboardingEnabled());
export async function getOnboardingStatus() {
const status = await resolveResponse(getV1IsOnboardingEnabled());
const onboarding = await resolveResponse(getV1OnboardingState());
const isCompleted = onboarding.completedSteps.includes("CONGRATS");
return isEnabled && !isCompleted;
return {
shouldShowOnboarding: status.is_onboarding_enabled && !isCompleted,
isChatEnabled: status.is_chat_enabled,
};
}

View File

@@ -4525,8 +4525,7 @@
"content": {
"application/json": {
"schema": {
"type": "boolean",
"title": "Response Getv1Is Onboarding Enabled"
"$ref": "#/components/schemas/OnboardingStatusResponse"
}
}
}
@@ -8730,6 +8729,19 @@
"title": "OAuthApplicationPublicInfo",
"description": "Public information about an OAuth application (for consent screen)"
},
"OnboardingStatusResponse": {
"properties": {
"is_onboarding_enabled": {
"type": "boolean",
"title": "Is Onboarding Enabled"
},
"is_chat_enabled": { "type": "boolean", "title": "Is Chat Enabled" }
},
"type": "object",
"required": ["is_onboarding_enabled", "is_chat_enabled"],
"title": "OnboardingStatusResponse",
"description": "Response for onboarding status check."
},
"OnboardingStep": {
"type": "string",
"enum": [