AuthProvider.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. "use client";
  2. import React, { createContext, useContext, useState, useEffect } from "react";
  3. import { User, mockApi } from "@/lib/mockApi";
  4. import { useRouter, usePathname } from "next/navigation";
  5. // Utility functions for client-side cookie management
  6. const getCookie = (name: string) => {
  7. if (typeof document === 'undefined') return null;
  8. const value = `; ${document.cookie}`;
  9. const parts = value.split(`; ${name}=`);
  10. if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
  11. return null;
  12. };
  13. const setCookie = (name: string, value: string, days = 7) => {
  14. if (typeof document === 'undefined') return;
  15. const date = new Date();
  16. date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
  17. const expires = `expires=${date.toUTCString()}`;
  18. document.cookie = `${name}=${value};${expires};path=/`;
  19. };
  20. const deleteCookie = (name: string) => {
  21. if (typeof document === 'undefined') return;
  22. document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
  23. };
  24. interface AuthContextType {
  25. user: User | null;
  26. isLoading: boolean;
  27. login: (token: string, userData: User) => void;
  28. logout: () => void;
  29. }
  30. const AuthContext = createContext<AuthContextType | undefined>(undefined);
  31. export function AuthProvider({ children }: { children: React.ReactNode }) {
  32. const [user, setUser] = useState<User | null>(null);
  33. const [isLoading, setIsLoading] = useState(true);
  34. const router = useRouter();
  35. const pathname = usePathname();
  36. useEffect(() => {
  37. // Check for existing session via cookie
  38. const initAuth = async () => {
  39. const token = getCookie("auth_token");
  40. if (token) {
  41. try {
  42. const validUser = await mockApi.verifySession(token);
  43. if (validUser) {
  44. setUser(validUser);
  45. // Middleware handles redirect, but we can dual-enforce client side
  46. if (pathname === '/login') {
  47. router.replace('/');
  48. }
  49. } else {
  50. deleteCookie("auth_token");
  51. if (pathname !== '/login') router.replace('/login');
  52. }
  53. } catch (error) {
  54. deleteCookie("auth_token");
  55. if (pathname !== '/login') router.replace('/login');
  56. }
  57. } else {
  58. // No token
  59. if (pathname !== '/login') router.replace('/login');
  60. }
  61. setIsLoading(false);
  62. };
  63. initAuth();
  64. }, [pathname, router]);
  65. const login = (token: string, userData: User) => {
  66. setCookie("auth_token", token);
  67. setUser(userData);
  68. router.replace('/');
  69. };
  70. const logout = () => {
  71. deleteCookie("auth_token");
  72. setUser(null);
  73. router.replace('/login');
  74. };
  75. return (
  76. <AuthContext.Provider value={{ user, isLoading, login, logout }}>
  77. {children}
  78. </AuthContext.Provider>
  79. );
  80. }
  81. export function useAuth() {
  82. const context = useContext(AuthContext);
  83. if (context === undefined) {
  84. throw new Error("useAuth must be used within an AuthProvider");
  85. }
  86. return context;
  87. }