87 lines
2.0 KiB
TypeScript
87 lines
2.0 KiB
TypeScript
import { useState, useEffect, useCallback, type ReactNode } from "react";
|
|
import { AuthContext, type AuthUser } from "./AuthTypes";
|
|
|
|
const BASE = "/api";
|
|
|
|
async function fetchMe(token: string) {
|
|
try {
|
|
const res = await fetch(`${BASE}/auth/me`, {
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
});
|
|
if (!res.ok) return null;
|
|
return res.json();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function AuthProvider({ children }: { children: ReactNode }) {
|
|
const [user, setUser] = useState<AuthUser | null>(null);
|
|
const [token, setToken] = useState<string | null>(() => localStorage.getItem("token"));
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
async function validateToken() {
|
|
if (!token) return;
|
|
const u = await fetchMe(token);
|
|
if (cancelled) return;
|
|
if (u) {
|
|
setUser(u);
|
|
} else {
|
|
localStorage.removeItem("token");
|
|
setToken(null);
|
|
}
|
|
}
|
|
|
|
validateToken().finally(() => {
|
|
if (!cancelled) setIsLoading(false);
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [token]);
|
|
|
|
const login = useCallback(async (username: string, password: string) => {
|
|
const res = await fetch(`${BASE}/auth/login`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
if (!res.ok) {
|
|
const text = await res.text().catch(() => "");
|
|
throw new Error(`${res.status}: ${text}`);
|
|
}
|
|
const data = await res.json();
|
|
const newToken = data.access_token;
|
|
localStorage.setItem("token", newToken);
|
|
setToken(newToken);
|
|
}, []);
|
|
|
|
const logout = useCallback(() => {
|
|
localStorage.removeItem("token");
|
|
setToken(null);
|
|
setUser(null);
|
|
}, []);
|
|
|
|
return (
|
|
<AuthContext.Provider
|
|
value={{
|
|
user,
|
|
token,
|
|
isAuthenticated: !!user,
|
|
isLoading,
|
|
login,
|
|
logout,
|
|
}}
|
|
>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
);
|
|
}
|