/* * Copyright © 2024 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ import getConfig from "next/config"; import { useRouter } from "next/router"; import { useState, useEffect, createContext, ReactNode } from "react"; import { useQueryClient, useQuery, QueryObserverResult, RefetchOptions, UseMutateFunction, } from "react-query"; import { Progress } from "@/app-components/displays/Progress"; import { useLogout } from "@/hooks/entities/auth-hooks"; import { useApiClient } from "@/hooks/useApiClient"; import { useLogoutRedirection, CURRENT_USER_KEY, PUBLIC_PATHS, } from "@/hooks/useAuth"; import { useToast } from "@/hooks/useToast"; import { useTranslate } from "@/hooks/useTranslate"; import { RouterType } from "@/services/types"; import { IUser } from "@/types/user.types"; import { getFromQuery } from "@/utils/URL"; export interface AuthContextValue { user: IUser | undefined; isAuthenticated: boolean; setUser: (data: IUser | undefined) => void; authenticate: (user: IUser) => void; logout: UseMutateFunction; refetchUser: ( options?: RefetchOptions | undefined, ) => Promise>; error: Error | null; } export const AuthContext = createContext(null); AuthContext.displayName = "AuthContext"; export interface AuthProviderProps { children: ReactNode; } const { publicRuntimeConfig } = getConfig(); export const AuthProvider = ({ children }: AuthProviderProps): JSX.Element => { const router = useRouter(); const { logoutRedirection } = useLogoutRedirection(); const [search, setSearch] = useState(""); const hasPublicPath = PUBLIC_PATHS.includes(router.pathname); const { i18n, t } = useTranslate(); const { toast } = useToast(); const [isReady, setIsReady] = useState(false); const queryClient = useQueryClient(); const updateLanguage = (lang: string) => { i18n.changeLanguage(lang); }; const { mutateAsync: logoutSession } = useLogout(); const logout = async () => { queryClient.removeQueries([CURRENT_USER_KEY]); updateLanguage(publicRuntimeConfig.lang.default); await logoutSession(); logoutRedirection(); toast.success(t("message.logout_success")); }; const authRedirection = async (isAuthenticated: boolean) => { if (isAuthenticated) { const redirect = getFromQuery({ search, key: "redirect" }); const nextPage = redirect && decodeURIComponent(redirect); if (nextPage?.startsWith("/")) { router.push(nextPage); } else if (hasPublicPath) router.push(RouterType.HOME); } }; const { apiClient } = useApiClient(); const { data: user, error, isLoading, refetch, } = useQuery({ queryKey: [CURRENT_USER_KEY], queryFn: async () => { return await apiClient.getCurrentSession(); }, onSuccess(data) { updateLanguage(data.language); authRedirection(!!data?.id); }, }); const setUser = (data?: IUser) => { queryClient.setQueryData(CURRENT_USER_KEY, data); }; const authenticate = (user: IUser) => { updateLanguage(user.language); setUser(user); }; const isAuthenticated = !!user; useEffect(() => { const search = location.search; setSearch(search); setIsReady(true); }, []); if (!isReady || isLoading) return ; return ( {children} ); };