fuck if i know
This commit is contained in:
+86
-83
@@ -20,8 +20,8 @@ type AuthUser = {
|
||||
|
||||
type AuthContextValue = {
|
||||
user: AuthUser;
|
||||
token: string | null;
|
||||
loading: boolean; // covers initial hydrate + active auth requests
|
||||
token: string | null;
|
||||
loading: boolean;
|
||||
login: (params: { email: string; password: string }) => Promise<void>;
|
||||
register: (params: {
|
||||
email: string;
|
||||
@@ -38,136 +38,138 @@ const TOKEN_KEY = "ballistic_auth_token";
|
||||
const USER_KEY = "ballistic_auth_user";
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [user, setUser] = useState<AuthUser>(null);
|
||||
const [token, setToken] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true); // start true while we hydrate + reuse for calls
|
||||
const [user, setUser] = useState<AuthUser>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
// hydrate from localStorage
|
||||
// Hydrate from localStorage on first load
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (typeof window === "undefined") return;
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const storedToken = window.localStorage.getItem(TOKEN_KEY);
|
||||
const storedUser = window.localStorage.getItem(USER_KEY);
|
||||
const storedToken = window.localStorage.getItem(TOKEN_KEY);
|
||||
const storedUser = window.localStorage.getItem(USER_KEY);
|
||||
|
||||
if (storedToken && storedUser) {
|
||||
setToken(storedToken);
|
||||
if (storedToken) {
|
||||
setToken(storedToken);
|
||||
}
|
||||
|
||||
if (storedUser) {
|
||||
try {
|
||||
setUser(JSON.parse(storedUser));
|
||||
} catch {
|
||||
// bad JSON? wipe it
|
||||
window.localStorage.removeItem(USER_KEY);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
const setSession = useCallback((newToken: string, newUser: AuthUser) => {
|
||||
setToken(newToken);
|
||||
setUser(newUser);
|
||||
const persistAuth = useCallback((nextToken: string | null, nextUser: AuthUser) => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.setItem(TOKEN_KEY, newToken);
|
||||
window.localStorage.setItem(USER_KEY, JSON.stringify(newUser));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearSession = useCallback(() => {
|
||||
setToken(null);
|
||||
setUser(null);
|
||||
if (typeof window !== "undefined") {
|
||||
if (nextToken) {
|
||||
window.localStorage.setItem(TOKEN_KEY, nextToken);
|
||||
} else {
|
||||
window.localStorage.removeItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
if (nextUser) {
|
||||
window.localStorage.setItem(USER_KEY, JSON.stringify(nextUser));
|
||||
} else {
|
||||
window.localStorage.removeItem(USER_KEY);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const register = useCallback(
|
||||
async (params: { email: string; password: string; displayName?: string }) => {
|
||||
const login = useCallback(
|
||||
async ({ email, password }: { email: string; password: string }) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/api/auth/register`, {
|
||||
const res = await fetch(`${API_BASE_URL}/auth/login`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
displayName: params.displayName,
|
||||
}),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(text || "Failed to create account");
|
||||
const text = await res.text().catch(() => res.statusText);
|
||||
throw new Error(text || "Login failed");
|
||||
}
|
||||
|
||||
const data: {
|
||||
token: string;
|
||||
email: string;
|
||||
displayName?: string;
|
||||
role: string;
|
||||
} = await res.json();
|
||||
const data = await res.json();
|
||||
|
||||
setSession(data.token, {
|
||||
email: data.email,
|
||||
displayName: data.displayName ?? null,
|
||||
role: data.role,
|
||||
});
|
||||
// Adjust these to match your backend response shape
|
||||
const nextToken: string = data.token ?? data.accessToken;
|
||||
const nextUser: AuthUser =
|
||||
data.user ??
|
||||
({
|
||||
email: data.email ?? email,
|
||||
displayName: data.displayName ?? null,
|
||||
role: data.role ?? "USER",
|
||||
} as AuthUser);
|
||||
|
||||
setToken(nextToken);
|
||||
setUser(nextUser);
|
||||
persistAuth(nextToken, nextUser);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[setSession],
|
||||
[persistAuth]
|
||||
);
|
||||
|
||||
const login = useCallback(
|
||||
async (params: { email: string; password: string }) => {
|
||||
const register = useCallback(
|
||||
async ({
|
||||
email,
|
||||
password,
|
||||
displayName,
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
displayName?: string;
|
||||
}) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/api/auth/login`, {
|
||||
const res = await fetch(`${API_BASE_URL}/auth/register`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
}),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password, displayName }),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(text || "Failed to log in");
|
||||
const text = await res.text().catch(() => res.statusText);
|
||||
throw new Error(text || "Registration failed");
|
||||
}
|
||||
|
||||
const data: {
|
||||
token: string;
|
||||
email: string;
|
||||
displayName?: string;
|
||||
role: string;
|
||||
} = await res.json();
|
||||
const data = await res.json();
|
||||
|
||||
setSession(data.token, {
|
||||
email: data.email,
|
||||
displayName: data.displayName ?? null,
|
||||
role: data.role,
|
||||
});
|
||||
const nextToken: string = data.token ?? data.accessToken;
|
||||
const nextUser: AuthUser =
|
||||
data.user ??
|
||||
({
|
||||
email: data.email ?? email,
|
||||
displayName: data.displayName ?? displayName ?? null,
|
||||
role: data.role ?? "USER",
|
||||
} as AuthUser);
|
||||
|
||||
setToken(nextToken);
|
||||
setUser(nextUser);
|
||||
persistAuth(nextToken, nextUser);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[setSession],
|
||||
[persistAuth]
|
||||
);
|
||||
|
||||
const logout = useCallback(() => {
|
||||
clearSession();
|
||||
}, [clearSession]);
|
||||
setToken(null);
|
||||
setUser(null);
|
||||
persistAuth(null, null);
|
||||
}, [persistAuth]);
|
||||
|
||||
const getAuthHeaders = useCallback((): HeadersInit => {
|
||||
if (!token) return {};
|
||||
return {
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
return token ? { Authorization: `Bearer ${token}` } : {};
|
||||
}, [token]);
|
||||
|
||||
const value: AuthContextValue = {
|
||||
@@ -183,6 +185,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
|
||||
// 🔑 This is what your useApi hook imports
|
||||
export function useAuth(): AuthContextValue {
|
||||
const ctx = useContext(AuthContext);
|
||||
if (!ctx) {
|
||||
|
||||
Reference in New Issue
Block a user