mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Revert "hotfix(frontend): Show auth modal on 401" (#8287)
This commit is contained in:
parent
a8e15be13f
commit
da4bf5bb29
@ -6,7 +6,7 @@ import * as AuthHook from "#/context/auth-context";
|
||||
|
||||
// Mock the useAuthUrl hook
|
||||
vi.mock("#/hooks/use-auth-url", () => ({
|
||||
useAuthUrl: () => "https://gitlab.com/oauth/authorize",
|
||||
useAuthUrl: () => "https://gitlab.com/oauth/authorize"
|
||||
}));
|
||||
|
||||
describe("AuthModal", () => {
|
||||
@ -16,8 +16,7 @@ describe("AuthModal", () => {
|
||||
providersAreSet: false,
|
||||
setProvidersAreSet: vi.fn(),
|
||||
providerTokensSet: [],
|
||||
setProviderTokensSet: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
setProviderTokensSet: vi.fn()
|
||||
});
|
||||
});
|
||||
|
||||
@ -29,12 +28,8 @@ describe("AuthModal", () => {
|
||||
it("should render the GitHub and GitLab buttons", () => {
|
||||
render(<AuthModal githubAuthUrl="mock-url" appMode="saas" />);
|
||||
|
||||
const githubButton = screen.getByRole("button", {
|
||||
name: "GITHUB$CONNECT_TO_GITHUB",
|
||||
});
|
||||
const gitlabButton = screen.getByRole("button", {
|
||||
name: "GITLAB$CONNECT_TO_GITLAB",
|
||||
});
|
||||
const githubButton = screen.getByRole("button", { name: "GITHUB$CONNECT_TO_GITHUB" });
|
||||
const gitlabButton = screen.getByRole("button", { name: "GITLAB$CONNECT_TO_GITLAB" });
|
||||
|
||||
expect(githubButton).toBeInTheDocument();
|
||||
expect(gitlabButton).toBeInTheDocument();
|
||||
@ -45,9 +40,7 @@ describe("AuthModal", () => {
|
||||
const mockUrl = "https://github.com/login/oauth/authorize";
|
||||
render(<AuthModal githubAuthUrl={mockUrl} appMode="saas" />);
|
||||
|
||||
const githubButton = screen.getByRole("button", {
|
||||
name: "GITHUB$CONNECT_TO_GITHUB",
|
||||
});
|
||||
const githubButton = screen.getByRole("button", { name: "GITHUB$CONNECT_TO_GITHUB" });
|
||||
await user.click(githubButton);
|
||||
|
||||
expect(window.location.href).toBe(mockUrl);
|
||||
|
||||
@ -12,6 +12,7 @@ import { ConversationPanel } from "#/components/features/conversation-panel/conv
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { AuthProvider } from "#/context/auth-context";
|
||||
import { clickOnEditButton } from "./utils";
|
||||
import { queryClientConfig } from "#/query-client-config";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
|
||||
describe("ConversationPanel", () => {
|
||||
@ -23,7 +24,7 @@ describe("ConversationPanel", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const renderConversationPanel = (config?: object) =>
|
||||
const renderConversationPanel = (config?: QueryClientConfig) =>
|
||||
renderWithProviders(<RouterStub />, {
|
||||
preloadedState: {
|
||||
metrics: {
|
||||
|
||||
@ -73,7 +73,7 @@ describe("HomeScreen", () => {
|
||||
screen.getByTestId("task-suggestions");
|
||||
});
|
||||
|
||||
it("should have responsive layout for mobile and desktop screens", () => {
|
||||
it("should have responsive layout for mobile and desktop screens", async () => {
|
||||
renderHomeScreen();
|
||||
|
||||
const mainContainer = screen
|
||||
|
||||
@ -6,7 +6,6 @@ interface AuthContextType {
|
||||
setProviderTokensSet: (tokens: Provider[]) => void;
|
||||
providersAreSet: boolean;
|
||||
setProvidersAreSet: (status: boolean) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
interface AuthContextProps extends React.PropsWithChildren {
|
||||
@ -24,24 +23,19 @@ function AuthProvider({
|
||||
const [providerTokensSet, setProviderTokensSet] = React.useState<Provider[]>(
|
||||
initialProviderTokens,
|
||||
);
|
||||
|
||||
const [providersAreSet, setProvidersAreSet] = React.useState<boolean>(
|
||||
initialProvidersAreSet,
|
||||
);
|
||||
|
||||
const clear = React.useCallback(() => {
|
||||
setProviderTokensSet([]);
|
||||
setProvidersAreSet(false);
|
||||
}, []);
|
||||
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
providerTokensSet,
|
||||
setProviderTokensSet,
|
||||
providersAreSet,
|
||||
setProvidersAreSet,
|
||||
clear,
|
||||
}),
|
||||
[providerTokensSet, providersAreSet, clear],
|
||||
[providerTokensSet],
|
||||
);
|
||||
|
||||
return <AuthContext value={value}>{children}</AuthContext>;
|
||||
|
||||
@ -11,10 +11,10 @@ import { hydrateRoot } from "react-dom/client";
|
||||
import { Provider } from "react-redux";
|
||||
import posthog from "posthog-js";
|
||||
import "./i18n";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import store from "./store";
|
||||
import { AuthProvider } from "./context/auth-context";
|
||||
import { queryClient } from "./query-client-config";
|
||||
import { queryClientConfig } from "./query-client-config";
|
||||
import OpenHands from "./api/open-hands";
|
||||
import { displayErrorToast } from "./utils/custom-toast-handlers";
|
||||
|
||||
@ -59,6 +59,8 @@ async function prepareApp() {
|
||||
}
|
||||
}
|
||||
|
||||
export const queryClient = new QueryClient(queryClientConfig);
|
||||
|
||||
prepareApp().then(() =>
|
||||
startTransition(() => {
|
||||
hydrateRoot(
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import React from "react";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { useConfig } from "./use-config";
|
||||
import { useAuth } from "#/context/auth-context";
|
||||
import { useIsOnTosPage } from "#/hooks/use-is-on-tos-page";
|
||||
|
||||
export const useIsAuthed = () => {
|
||||
const { providersAreSet } = useAuth();
|
||||
const { data: config } = useConfig();
|
||||
const isOnTosPage = useIsOnTosPage();
|
||||
|
||||
const appMode = config?.APP_MODE;
|
||||
const appMode = React.useMemo(() => config?.APP_MODE, [config]);
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["user", "authenticated", appMode],
|
||||
queryKey: ["user", "authenticated", providersAreSet, appMode],
|
||||
queryFn: () => OpenHands.authenticate(appMode!),
|
||||
enabled: !!appMode && !isOnTosPage,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
|
||||
@ -1,10 +1,29 @@
|
||||
import React from "react";
|
||||
import { generateAuthUrl } from "#/utils/generate-auth-url";
|
||||
import { GetConfigResponse } from "#/api/open-hands.types";
|
||||
import { useAuth } from "#/context/auth-context";
|
||||
|
||||
interface UseAuthUrlConfig {
|
||||
appMode: GetConfigResponse["APP_MODE"] | null;
|
||||
identityProvider: string;
|
||||
}
|
||||
|
||||
export const useAuthUrl = (config: UseAuthUrlConfig) =>
|
||||
generateAuthUrl(config.identityProvider, new URL(window.location.href));
|
||||
export const useAuthUrl = (config: UseAuthUrlConfig) => {
|
||||
const { providersAreSet } = useAuth();
|
||||
|
||||
return React.useMemo(() => {
|
||||
if (config.appMode === "saas" && !providersAreSet) {
|
||||
try {
|
||||
return generateAuthUrl(
|
||||
config.identityProvider,
|
||||
new URL(window.location.href),
|
||||
);
|
||||
} catch (e) {
|
||||
// In test environment, window.location.href might not be a valid URL
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [providersAreSet, config.appMode, config.identityProvider]);
|
||||
};
|
||||
|
||||
@ -1,27 +1,17 @@
|
||||
import { QueryCache, MutationCache, QueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
QueryClientConfig,
|
||||
QueryCache,
|
||||
MutationCache,
|
||||
} from "@tanstack/react-query";
|
||||
import i18next from "i18next";
|
||||
import { AxiosError } from "axios";
|
||||
import { I18nKey } from "./i18n/declaration";
|
||||
import { retrieveAxiosErrorMessage } from "./utils/retrieve-axios-error-message";
|
||||
import { displayErrorToast } from "./utils/custom-toast-handlers";
|
||||
|
||||
const handle401Error = (error: AxiosError, queryClient: QueryClient) => {
|
||||
if (error?.response?.status === 401 || error?.status === 401) {
|
||||
queryClient.invalidateQueries({ queryKey: ["user", "authenticated"] });
|
||||
}
|
||||
};
|
||||
|
||||
const shownErrors = new Set<string>();
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
export const queryClientConfig: QueryClientConfig = {
|
||||
queryCache: new QueryCache({
|
||||
onError: (error, query) => {
|
||||
const isAuthQuery =
|
||||
query.queryKey[0] === "user" && query.queryKey[1] === "authenticated";
|
||||
if (!isAuthQuery) {
|
||||
handle401Error(error, queryClient);
|
||||
}
|
||||
|
||||
if (!query.meta?.disableToast) {
|
||||
const errorMessage = retrieveAxiosErrorMessage(error);
|
||||
|
||||
@ -38,12 +28,10 @@ export const queryClient = new QueryClient({
|
||||
}),
|
||||
mutationCache: new MutationCache({
|
||||
onError: (error, _, __, mutation) => {
|
||||
handle401Error(error, queryClient);
|
||||
|
||||
if (!mutation?.meta?.disableToast) {
|
||||
const message = retrieveAxiosErrorMessage(error);
|
||||
displayErrorToast(message || i18next.t(I18nKey.ERROR$GENERIC));
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@ -21,7 +21,6 @@ import { useBalance } from "#/hooks/query/use-balance";
|
||||
import { SetupPaymentModal } from "#/components/features/payment/setup-payment-modal";
|
||||
import { displaySuccessToast } from "#/utils/custom-toast-handlers";
|
||||
import { useIsOnTosPage } from "#/hooks/use-is-on-tos-page";
|
||||
import { useAuth } from "#/context/auth-context";
|
||||
|
||||
export function ErrorBoundary() {
|
||||
const error = useRouteError();
|
||||
@ -57,7 +56,6 @@ export function ErrorBoundary() {
|
||||
}
|
||||
|
||||
export default function MainApp() {
|
||||
const { clear } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
const tosPageStatus = useIsOnTosPage();
|
||||
@ -73,8 +71,6 @@ export default function MainApp() {
|
||||
isError: authError,
|
||||
} = useIsAuthed();
|
||||
|
||||
// When on TOS page, we don't make any API calls, so we need to handle this case
|
||||
const userIsAuthed = tosPageStatus ? false : !!isAuthed && !authError;
|
||||
// Always call the hook, but we'll only use the result when not on TOS page
|
||||
const gitHubAuthUrl = useGitHubAuthUrl({
|
||||
appMode: config.data?.APP_MODE || null,
|
||||
@ -129,11 +125,8 @@ export default function MainApp() {
|
||||
}
|
||||
}, [error?.status, pathname, tosPageStatus]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!userIsAuthed && !isFetchingAuth && authError) {
|
||||
clear();
|
||||
}
|
||||
}, [userIsAuthed, isFetchingAuth, authError, clear]);
|
||||
// When on TOS page, we don't make any API calls, so we need to handle this case
|
||||
const userIsAuthed = tosPageStatus ? false : !!isAuthed && !authError;
|
||||
|
||||
// Only show the auth modal if:
|
||||
// 1. User is not authenticated
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user