mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-09 15:17:59 -05:00
fix(frontend): logout console issues (#11400)
## Changes 🏗️ Fixed the logout errors by removing duplicate redirects. `serverLogout` was calling `redirect("/login")` (which throws `NEXT_REDIRECT`), and then `useSupabaseStore` was also calling `router.refresh()`, causing conflicts. Updated `serverLogout` to return a result object instead of redirecting, and moved the redirect to the client using `router.push("/login")` after logout completes. This removes the `NEXT_REDIRECT` error and ensures a single redirect. <img width="800" height="706" alt="Screenshot 2025-11-18 at 16 14 54" src="https://github.com/user-attachments/assets/38e0e55c-f48d-4b25-a07b-d4729e229c70" /> Also addressed 401 errors during logout. Hooks like `useCredits` were still making API calls after logout, causing "Authorization header is missing" errors. Added a check in `_makeClientRequest` to detect logout-in-progress and suppress authentication errors during that window. This prevents console noise and avoids unnecessary error handling. <img width="800" height="742" alt="Screenshot 2025-11-18 at 16 14 45" src="https://github.com/user-attachments/assets/6fb2270a-97a0-4411-9e5a-9b4b52117af3" /> ## Checklist 📋 ### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Log out of your account - [x] There are no errors showing up on the browser devtools
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
"use client";
|
||||
import { IconLogOut } from "@/components/__legacy__/ui/icons";
|
||||
import { LoadingSpinner } from "@/components/__legacy__/ui/loading";
|
||||
import { toast } from "@/components/molecules/Toast/use-toast";
|
||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTransition } from "react";
|
||||
import { toast } from "@/components/molecules/Toast/use-toast";
|
||||
|
||||
export function AccountLogoutOption() {
|
||||
const router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const supabase = useSupabase();
|
||||
|
||||
@@ -17,7 +15,6 @@ export function AccountLogoutOption() {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
await supabase.logOut();
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
Sentry.captureException(e);
|
||||
toast({
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { IconAutoGPTLogo, IconType } from "@/components/__legacy__/ui/icons";
|
||||
import { useBreakpoint } from "@/lib/hooks/useBreakpoint";
|
||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
|
||||
import { useMemo } from "react";
|
||||
import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "../helpers";
|
||||
import { AccountMenu } from "./AccountMenu/AccountMenu";
|
||||
import { AgentActivityDropdown } from "./AgentActivityDropdown/AgentActivityDropdown";
|
||||
@@ -12,7 +13,6 @@ import { LoginButton } from "./LoginButton";
|
||||
import { MobileNavBar } from "./MobileNavbar/MobileNavBar";
|
||||
import { NavbarLink } from "./NavbarLink";
|
||||
import { Wallet } from "./Wallet/Wallet";
|
||||
import { useGetFlag, Flag } from "@/services/feature-flags/use-get-flag";
|
||||
interface NavbarViewProps {
|
||||
isLoggedIn: boolean;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export const NavbarView = ({ isLoggedIn }: NavbarViewProps) => {
|
||||
<nav className="sticky top-0 z-40 inline-flex h-[60px] w-full items-center border border-white/50 bg-[#f3f4f6]/20 p-3 backdrop-blur-[26px]">
|
||||
{/* Left section */}
|
||||
{!isSmallScreen ? (
|
||||
<div className="flex flex-1 items-center gap-3 gap-5">
|
||||
<div className="flex flex-1 items-center gap-5">
|
||||
{isLoggedIn
|
||||
? linksWithChat.map((link) => (
|
||||
<NavbarLink
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { getWebSocketToken } from "@/lib/supabase/actions";
|
||||
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
|
||||
import { createBrowserClient } from "@supabase/ssr";
|
||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||
import { Key, storage } from "@/services/storage/local-storage";
|
||||
import { IMPERSONATION_HEADER_NAME } from "@/lib/constants";
|
||||
import { ImpersonationState } from "@/lib/impersonation";
|
||||
import { getWebSocketToken } from "@/lib/supabase/actions";
|
||||
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
|
||||
import { environment } from "@/services/environment";
|
||||
import { Key, storage } from "@/services/storage/local-storage";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { createBrowserClient } from "@supabase/ssr";
|
||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||
import type {
|
||||
AddUserCreditsResponse,
|
||||
AnalyticsDetails,
|
||||
@@ -70,7 +71,6 @@ import type {
|
||||
UsersBalanceHistoryResponse,
|
||||
WebSocketNotification,
|
||||
} from "./types";
|
||||
import { environment } from "@/services/environment";
|
||||
|
||||
const isClient = environment.isClientSide();
|
||||
|
||||
@@ -1006,8 +1006,13 @@ export default class BackendAPI {
|
||||
// Dynamic import is required even for client-only functions because helpers.ts
|
||||
// has server-only imports (like getServerSupabase) at the top level. Static imports
|
||||
// would bundle server-only code into the client bundle, causing runtime errors.
|
||||
const { buildClientUrl, buildUrlWithQuery, handleFetchError } =
|
||||
await import("./helpers");
|
||||
const {
|
||||
buildClientUrl,
|
||||
buildUrlWithQuery,
|
||||
handleFetchError,
|
||||
isLogoutInProgress,
|
||||
isAuthenticationError,
|
||||
} = await import("./helpers");
|
||||
|
||||
const payloadAsQuery = ["GET", "DELETE"].includes(method);
|
||||
let url = buildClientUrl(path);
|
||||
@@ -1034,7 +1039,18 @@ export default class BackendAPI {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw await handleFetchError(response);
|
||||
const error = await handleFetchError(response);
|
||||
if (
|
||||
isAuthenticationError(response, error.message) &&
|
||||
isLogoutInProgress()
|
||||
) {
|
||||
console.debug(
|
||||
"Authentication request failed during logout, ignoring:",
|
||||
error.message,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
|
||||
import { Key, storage } from "@/services/storage/local-storage";
|
||||
import { environment } from "@/services/environment";
|
||||
import { IMPERSONATION_HEADER_NAME } from "@/lib/constants";
|
||||
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
|
||||
import { environment } from "@/services/environment";
|
||||
import { Key, storage } from "@/services/storage/local-storage";
|
||||
|
||||
import { GraphValidationErrorResponse } from "./types";
|
||||
|
||||
@@ -221,7 +221,7 @@ export async function parseApiResponse(response: Response): Promise<any> {
|
||||
}
|
||||
}
|
||||
|
||||
function isAuthenticationError(
|
||||
export function isAuthenticationError(
|
||||
response: Response,
|
||||
errorDetail: string,
|
||||
): boolean {
|
||||
@@ -234,7 +234,7 @@ function isAuthenticationError(
|
||||
);
|
||||
}
|
||||
|
||||
function isLogoutInProgress(): boolean {
|
||||
export function isLogoutInProgress(): boolean {
|
||||
if (environment.isServerSide()) return false;
|
||||
|
||||
try {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import type { User } from "@supabase/supabase-js";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getRedirectPath } from "./helpers";
|
||||
import { getServerSupabase } from "./server/getServerSupabase";
|
||||
|
||||
@@ -157,8 +156,7 @@ export async function serverLogout(options: ServerLogoutOptions = {}) {
|
||||
const supabase = await getServerSupabase();
|
||||
|
||||
if (!supabase) {
|
||||
redirect("/login");
|
||||
return;
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -170,14 +168,18 @@ export async function serverLogout(options: ServerLogoutOptions = {}) {
|
||||
|
||||
if (error) {
|
||||
console.error("Error logging out:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Logout error:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
};
|
||||
}
|
||||
|
||||
// Clear all cached data and redirect
|
||||
revalidatePath("/", "layout");
|
||||
redirect("/login");
|
||||
return { success: true };
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,20 +122,16 @@ export const useSupabaseStore = create<SupabaseStoreState>((set, get) => {
|
||||
|
||||
broadcastLogout();
|
||||
|
||||
try {
|
||||
await serverLogout(options);
|
||||
} catch (error) {
|
||||
console.error("Error logging out:", error);
|
||||
} finally {
|
||||
set({
|
||||
user: null,
|
||||
hasLoadedUser: false,
|
||||
isUserLoading: false,
|
||||
});
|
||||
set({
|
||||
user: null,
|
||||
hasLoadedUser: false,
|
||||
isUserLoading: false,
|
||||
});
|
||||
|
||||
if (router) {
|
||||
router.refresh();
|
||||
}
|
||||
const result = await serverLogout(options);
|
||||
|
||||
if (result.success && router) {
|
||||
router.push("/login");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user