Files
glassmond/components/auth-provider.tsx
T
Alireza HassaniandCursor 98fde02322 Create Glassmond storefront with Meshkee integration
Build the RTL Next.js experience with live catalog, journal, customer interactions, and SEO-ready canonical routes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 00:08:00 +03:30

142 lines
3.3 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import { MESHKEE_API_BASE } from "@/lib/meshkee";
export type AuthUser = {
id?: string | number;
firstName?: string;
lastName?: string;
name?: string;
cellNumber?: string;
email?: string;
role?: string;
roles?: string[];
isAdmin?: boolean;
};
type AuthContextValue = {
user: AuthUser | null;
token: string | null;
loading: boolean;
isAdmin: boolean;
logout: () => void;
};
const AuthContext = createContext<AuthContextValue>({
user: null,
token: null,
loading: true,
isAdmin: false,
logout: () => undefined,
});
const tokenKeys = [
"accessToken",
"meshkeeAccessToken",
"meshkee_access_token",
"meshkee-token",
];
function readToken() {
try {
for (const key of tokenKeys) {
const value = window.localStorage.getItem(key);
if (value) return value;
}
const authValue = window.localStorage.getItem("meshkee-auth");
if (authValue) {
const auth = JSON.parse(authValue) as { accessToken?: string };
if (auth.accessToken) return auth.accessToken;
}
const cookies = document.cookie.split(";").map((item) => item.trim());
for (const key of tokenKeys) {
const prefix = `${key}=`;
const cookie = cookies.find((item) => item.startsWith(prefix));
if (cookie) return decodeURIComponent(cookie.slice(prefix.length));
}
} catch {
return null;
}
return null;
}
function clearTokens() {
try {
tokenKeys.forEach((key) => {
window.localStorage.removeItem(key);
document.cookie = `${key}=; Max-Age=0; path=/; domain=.glassmond.com`;
document.cookie = `${key}=; Max-Age=0; path=/`;
});
window.localStorage.removeItem("meshkee-auth");
} catch {
// External customer portal remains the source of truth for authentication.
}
}
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<AuthUser | null>(null);
const [token, setToken] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const accessToken = readToken();
if (!accessToken) {
setLoading(false);
return;
}
setToken(accessToken);
fetch(`${MESHKEE_API_BASE}/auth/me`, {
headers: { Authorization: `Bearer ${accessToken}` },
})
.then(async (response) => {
if (!response.ok) throw new Error("Unauthorized");
return response.json() as Promise<{ user: AuthUser }>;
})
.then((data) => setUser(data.user))
.catch(() => {
clearTokens();
setToken(null);
})
.finally(() => setLoading(false));
}, []);
const logout = useCallback(() => {
clearTokens();
setUser(null);
setToken(null);
}, []);
const isAdmin = useMemo(() => {
const roles = [user?.role, ...(user?.roles ?? [])]
.filter(Boolean)
.map((role) => String(role).toLowerCase());
return Boolean(
user?.isAdmin ||
roles.some((role) => ["admin", "owner", "superadmin", "business_admin"].includes(role)),
);
}, [user]);
return (
<AuthContext.Provider value={{ user, token, loading, isAdmin, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}