generated from muhtadeetaron/nextjs-template
155 lines
3.8 KiB
TypeScript
155 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { create } from "zustand";
|
|
import { LoginForm, RegisterForm, UserData } from "@/types/auth";
|
|
import { API_URL } from "@/lib/auth";
|
|
|
|
// Cookie utilities
|
|
const getCookie = (name: string): string | null => {
|
|
if (typeof document === "undefined") return null;
|
|
|
|
const value = `; ${document.cookie}`;
|
|
const parts = value.split(`; ${name}=`);
|
|
if (parts.length === 2) {
|
|
return parts.pop()?.split(";").shift() || null;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const setCookie = (
|
|
name: string,
|
|
value: string | null,
|
|
days: number = 7
|
|
): void => {
|
|
if (typeof document === "undefined") return;
|
|
|
|
if (value === null) {
|
|
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict; Secure`;
|
|
} else {
|
|
const expires = new Date();
|
|
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
|
document.cookie = `${name}=${value}; expires=${expires.toUTCString()}; path=/; SameSite=Strict; Secure`;
|
|
}
|
|
};
|
|
|
|
interface APIError extends Error {
|
|
response?: any;
|
|
}
|
|
|
|
interface AuthState {
|
|
token: string | null;
|
|
isLoading: boolean;
|
|
hydrated: boolean;
|
|
user: UserData | null;
|
|
error: string | null;
|
|
|
|
login: (form: LoginForm) => Promise<void>;
|
|
register: (form: RegisterForm) => Promise<void>;
|
|
setToken: (token: string | null) => void;
|
|
fetchUser: () => Promise<void>;
|
|
logout: () => void;
|
|
initializeAuth: () => Promise<void>;
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>((set, get) => ({
|
|
token: null,
|
|
isLoading: true,
|
|
hydrated: false,
|
|
error: null,
|
|
user: null,
|
|
|
|
setToken: (newToken) => {
|
|
set({ token: newToken });
|
|
setCookie("authToken", newToken);
|
|
},
|
|
|
|
login: async (form: LoginForm) => {
|
|
set({ isLoading: true, error: null });
|
|
try {
|
|
const response = await fetch(`${API_URL}/auth/login/`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(form),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.message || "Login failed");
|
|
}
|
|
|
|
setCookie("authToken", data.token);
|
|
set({ token: data.token, isLoading: false });
|
|
} catch (err: any) {
|
|
set({
|
|
error: err?.message || "Login failed",
|
|
isLoading: false,
|
|
});
|
|
throw err;
|
|
}
|
|
},
|
|
register: async (form: RegisterForm) => {
|
|
set({ isLoading: true, error: null });
|
|
try {
|
|
const response = await fetch(`${API_URL}/auth/register/`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(form),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
const error: APIError = new Error(
|
|
data?.detail || "Registration failed"
|
|
);
|
|
error.response = data;
|
|
throw error;
|
|
}
|
|
|
|
setCookie("authToken", data.token);
|
|
set({ token: data.token, isLoading: false });
|
|
} catch (err: any) {
|
|
set({
|
|
error: err?.message || "Registration failed",
|
|
isLoading: false,
|
|
});
|
|
throw err;
|
|
}
|
|
},
|
|
|
|
fetchUser: async () => {
|
|
const token = get().token;
|
|
if (!token) return;
|
|
|
|
try {
|
|
const res = await fetch(`${API_URL}/me/profile/`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
|
|
if (!res.ok) throw new Error("Failed to fetch user info");
|
|
|
|
const data: UserData = await res.json();
|
|
console.log(data);
|
|
set({ user: data });
|
|
} catch (err) {
|
|
console.error("Error fetching user:", err);
|
|
get().logout();
|
|
}
|
|
},
|
|
|
|
logout: () => {
|
|
set({ token: null, user: null });
|
|
setCookie("authToken", null);
|
|
},
|
|
|
|
initializeAuth: async () => {
|
|
const storedToken = getCookie("authToken");
|
|
if (storedToken) {
|
|
set({ token: storedToken });
|
|
await get().fetchUser();
|
|
}
|
|
set({ isLoading: false, hydrated: true });
|
|
},
|
|
}));
|