"use client"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useCallback, useEffect } from "react"; import { loginUser, registerUser, verifyOtp, resendOtp, forgotPassword, verifyPasswordResetOtp, resetPassword, refreshToken, getStoredTokens, getStoredUser, storeTokens, storeUser, clearAuthData, isTokenExpired, hasValidAuth, } from "@/lib/actions/auth"; import type { LoginInput, RegisterInput, VerifyOtpInput, ResendOtpInput, ForgotPasswordInput, VerifyPasswordResetOtpInput, ResetPasswordInput, } from "@/lib/schema/auth"; import type { User } from "@/lib/models/auth"; import { toast } from "sonner"; export function useAuth() { const router = useRouter(); const queryClient = useQueryClient(); // Get current user from storage const { data: user } = useQuery({ queryKey: ["auth", "user"], queryFn: () => getStoredUser(), staleTime: Infinity, }); // Check if user is authenticated with valid token const isAuthenticated = !!user && hasValidAuth(); // Check if user is admin (check multiple possible field names) const isAdmin = user?.is_admin === true || (user as any)?.isAdmin === true || (user as any)?.is_staff === true || (user as any)?.isStaff === true || (user as any)?.is_superuser === true || (user as any)?.isSuperuser === true; // Login mutation const loginMutation = useMutation({ mutationFn: (input: LoginInput) => loginUser(input), onSuccess: (data) => { if (data.tokens && data.user) { storeTokens(data.tokens); storeUser(data.user); queryClient.setQueryData(["auth", "user"], data.user); queryClient.invalidateQueries({ queryKey: ["auth"] }); } }, }); // Register mutation const registerMutation = useMutation({ mutationFn: (input: RegisterInput) => registerUser(input), }); // Verify OTP mutation const verifyOtpMutation = useMutation({ mutationFn: (input: VerifyOtpInput) => verifyOtp(input), onSuccess: (data) => { if (data.tokens && data.user) { storeTokens(data.tokens); storeUser(data.user); queryClient.setQueryData(["auth", "user"], data.user); queryClient.invalidateQueries({ queryKey: ["auth"] }); } }, }); // Resend OTP mutation const resendOtpMutation = useMutation({ mutationFn: (input: ResendOtpInput) => resendOtp(input), }); // Forgot password mutation const forgotPasswordMutation = useMutation({ mutationFn: (input: ForgotPasswordInput) => forgotPassword(input), }); // Verify password reset OTP mutation const verifyPasswordResetOtpMutation = useMutation({ mutationFn: (input: VerifyPasswordResetOtpInput) => verifyPasswordResetOtp(input), }); // Reset password mutation const resetPasswordMutation = useMutation({ mutationFn: (input: ResetPasswordInput) => resetPassword(input), }); // Refresh token mutation const refreshTokenMutation = useMutation({ mutationFn: (refresh: string) => refreshToken({ refresh }), onSuccess: (tokens) => { storeTokens(tokens); queryClient.invalidateQueries({ queryKey: ["auth"] }); }, onError: () => { // If refresh fails, logout clearAuthData(); queryClient.clear(); }, }); // Logout function const logout = useCallback(() => { clearAuthData(); queryClient.clear(); // Don't redirect here - let components handle redirect as needed }, [queryClient]); // Auto-logout if token is expired or missing useEffect(() => { const checkAuth = () => { const tokens = getStoredTokens(); const storedUser = getStoredUser(); // If user exists but no token or token is expired, logout if (storedUser && (!tokens.access || isTokenExpired(tokens.access))) { // Try to refresh token first if refresh token exists if (tokens.refresh && !isTokenExpired(tokens.refresh)) { refreshTokenMutation.mutate(tokens.refresh, { onError: () => { // If refresh fails, logout clearAuthData(); queryClient.clear(); toast.error("Your session has expired. Please log in again."); }, }); } else { // No valid refresh token, logout immediately clearAuthData(); queryClient.clear(); toast.error("Your session has expired. Please log in again."); } } }; // Check immediately checkAuth(); // Check every 30 seconds const interval = setInterval(checkAuth, 30000); return () => clearInterval(interval); }, [queryClient, refreshTokenMutation]); // Login function const login = useCallback( async (input: LoginInput) => { try { const result = await loginMutation.mutateAsync(input); return result; } catch (error) { throw error; } }, [loginMutation] ); // Register function const register = useCallback( async (input: RegisterInput) => { try { const result = await registerMutation.mutateAsync(input); return result; } catch (error) { throw error; } }, [registerMutation] ); // Verify OTP function const verifyOtpCode = useCallback( async (input: VerifyOtpInput) => { try { const result = await verifyOtpMutation.mutateAsync(input); return result; } catch (error) { throw error; } }, [verifyOtpMutation] ); return { // State user, isAuthenticated, isAdmin, isLoading: loginMutation.isPending || registerMutation.isPending, // Actions login, register, logout, verifyOtp: verifyOtpCode, // Mutations (for direct access if needed) loginMutation, registerMutation, verifyOtpMutation, resendOtpMutation, forgotPasswordMutation, verifyPasswordResetOtpMutation, resetPasswordMutation, refreshTokenMutation, }; }