refactor(auth): separate auth and author responsibilities and centralize auth client creation
- Replace manual axios auth client with createApiClient in auth context - Decouple domain author logic from auth provider - Make AuthorModel extend AuthUser explicitly - Route login/register/logout exclusively through auth package - Derive application-level currentUser from auth identity - Fix provider hierarchy and hook usage across Blog and Profile - Align main.jsx to use base AuthProvider + AuthorProvider layering
This commit is contained in:
96
auth/src/contexts.tsx
Normal file
96
auth/src/contexts.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
||||
import { tokenStore } from "./token";
|
||||
import { createApiClient } from "./axios";
|
||||
import { AuthUser } from "./models";
|
||||
|
||||
interface AuthContextModel {
|
||||
currentUser: AuthUser | null;
|
||||
token: string | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
login(username: string, password: string): Promise<void>;
|
||||
register(username: string, password: string): Promise<void>;
|
||||
logout(): void;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextModel | undefined>(undefined);
|
||||
|
||||
export function AuthProvider({
|
||||
children,
|
||||
authBaseUrl,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
authBaseUrl: string;
|
||||
}) {
|
||||
const [currentUser, setCurrentUser] = useState<AuthUser | null>(null);
|
||||
const [token, setToken] = useState<string | null>(tokenStore.get());
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const auth = createApiClient(authBaseUrl);
|
||||
|
||||
const login = async (username: string, password: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const res = await auth.post("/login", { username, password });
|
||||
const { access_token, user } = res.data;
|
||||
|
||||
tokenStore.set(access_token);
|
||||
setToken(access_token);
|
||||
setCurrentUser(user);
|
||||
} catch (e: any) {
|
||||
setError(e.response?.data?.detail ?? "Login failed");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const register = async (username: string, password: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
await auth.post("/register", { username, password });
|
||||
} catch (e: any) {
|
||||
setError(e.response?.data?.detail ?? "Registration failed");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
tokenStore.clear();
|
||||
setToken(null);
|
||||
setCurrentUser(null);
|
||||
};
|
||||
|
||||
const fetchCurrentUser = async () => {
|
||||
if (!token) return;
|
||||
try {
|
||||
const me = await auth.get("/me");
|
||||
setCurrentUser({ ...me.data });
|
||||
} catch {
|
||||
logout();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchCurrentUser();
|
||||
}, [token]);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{ currentUser, token, loading, error, login, logout, register }}
|
||||
>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuth(): AuthContextModel {
|
||||
const ctx = useContext(AuthContext);
|
||||
if (!ctx) throw new Error("useAuth must be used inside AuthProvider");
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user