"use client"; // Lightweight client-side session bridge for the Meshkee customer account. // // This storefront has no login form of its own — signing in happens on the // separate customer portal (CUSTOMER_PORTAL_URL). What's implemented here is // the "come back logged in" half of that flow: // // 1. The customer portal sends the shopper back to this site with // `?token=&refreshToken=` in the URL. // 2. On first render we pick those up, store them, and strip them from // the address bar. // 3. From then on we call `GET /auth/me` with the stored access token to // know who's signed in (and to detect an admin, for "مدیریت وبسایت"). // // NOTE: the exact hand-back mechanism isn't published in the public API // docs (auth is plain Bearer-JWT, not a shared cookie), so this is the // standard "redirect back with tokens in the query string" pattern. If // Meshkee's customer portal actually bridges sessions a different way // (shared cookie, postMessage, …), swap the implementation in this file — // everything that reads auth state goes through the hooks below. import { useCallback, useEffect, useState } from "react"; import { API_BASE } from "./meshkee"; const ACCESS_TOKEN_KEY = "meshkee_access_token"; const REFRESH_TOKEN_KEY = "meshkee_refresh_token"; export function getStoredAccessToken() { if (typeof window === "undefined") return null; try { return window.localStorage.getItem(ACCESS_TOKEN_KEY); } catch { return null; } } function storeTokens({ accessToken, refreshToken }) { try { if (accessToken) window.localStorage.setItem(ACCESS_TOKEN_KEY, accessToken); if (refreshToken) window.localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken); } catch { /* localStorage unavailable (private mode, etc.) — session just won't persist */ } } export function clearStoredTokens() { try { window.localStorage.removeItem(ACCESS_TOKEN_KEY); window.localStorage.removeItem(REFRESH_TOKEN_KEY); } catch { /* noop */ } } /** Picks up `?token=&refreshToken=` from a customer-portal hand-back, once. */ function captureTokenFromUrl() { const params = new URLSearchParams(window.location.search); const token = params.get("token") || params.get("accessToken"); const refreshToken = params.get("refreshToken"); if (!token) return false; storeTokens({ accessToken: token, refreshToken }); params.delete("token"); params.delete("accessToken"); params.delete("refreshToken"); const query = params.toString(); const cleanUrl = window.location.pathname + (query ? `?${query}` : "") + window.location.hash; window.history.replaceState({}, "", cleanUrl); return true; } async function fetchCurrentUser(accessToken) { const res = await fetch(`${API_BASE}/auth/me`, { headers: { Authorization: `Bearer ${accessToken}` }, }); if (!res.ok) return null; const data = await res.json().catch(() => null); return data?.user ?? data ?? null; } /** True when the account object looks like a business admin, not a regular customer. */ export function isAdminUser(user) { if (!user) return false; const role = String(user.role || user.type || "").toLowerCase(); return Boolean(user.isAdmin) || role.includes("admin") || role.includes("owner"); } /** * Session state for the header: `{ status: 'loading' | 'guest' | 'in', user, logout }`. */ export function useMeshkeeSession() { const [state, setState] = useState({ status: "loading", user: null }); const load = useCallback(async () => { captureTokenFromUrl(); const token = getStoredAccessToken(); if (!token) { setState({ status: "guest", user: null }); return; } const user = await fetchCurrentUser(token); if (user) { setState({ status: "in", user }); } else { clearStoredTokens(); setState({ status: "guest", user: null }); } }, []); useEffect(() => { load(); }, [load]); const logout = useCallback(() => { clearStoredTokens(); setState({ status: "guest", user: null }); }, []); return { ...state, logout }; }