82 lines
1.8 KiB
TypeScript
82 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState, type ReactNode } from "react";
|
|
import {
|
|
clearTokens,
|
|
getAccessToken,
|
|
getCustomerLoginUrl,
|
|
} from "@/lib/session";
|
|
|
|
const MESHKEE_API_URL =
|
|
process.env.NEXT_PUBLIC_MESHKEE_API_URL?.replace(/\/$/, "") ||
|
|
"https://api.meshkee.com/api/v1";
|
|
|
|
async function verifySession(accessToken: string, signal: AbortSignal) {
|
|
const response = await fetch(`${MESHKEE_API_URL}/auth/me`, {
|
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
signal,
|
|
cache: "no-store",
|
|
});
|
|
return response.ok;
|
|
}
|
|
|
|
export default function RequireAuth({
|
|
children,
|
|
returnPath = "/register",
|
|
}: {
|
|
children: ReactNode;
|
|
returnPath?: string;
|
|
}) {
|
|
const [ready, setReady] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const controller = new AbortController();
|
|
|
|
async function ensureAuth() {
|
|
const token = getAccessToken();
|
|
if (!token) {
|
|
window.location.replace(getCustomerLoginUrl(returnPath));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const ok = await verifySession(token, controller.signal);
|
|
if (controller.signal.aborted) return;
|
|
if (!ok) {
|
|
clearTokens();
|
|
window.location.replace(getCustomerLoginUrl(returnPath));
|
|
return;
|
|
}
|
|
setReady(true);
|
|
} catch {
|
|
if (controller.signal.aborted) return;
|
|
clearTokens();
|
|
window.location.replace(getCustomerLoginUrl(returnPath));
|
|
}
|
|
}
|
|
|
|
void ensureAuth();
|
|
return () => controller.abort();
|
|
}, [returnPath]);
|
|
|
|
if (!ready) {
|
|
return (
|
|
<div
|
|
style={{
|
|
minHeight: "50vh",
|
|
display: "grid",
|
|
placeItems: "center",
|
|
fontSize: 12,
|
|
letterSpacing: "0.2em",
|
|
textTransform: "uppercase",
|
|
color: "#8e8e8e",
|
|
}}
|
|
>
|
|
Checking account…
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return children;
|
|
}
|