feat(frontend): Finalize auth refactor, routing, and resolve all code review findings

This commit is contained in:
Yunxiao Xu
2026-02-12 00:11:08 -08:00
parent 7fe020f26c
commit 2545f6df13
17 changed files with 335 additions and 216 deletions

View File

@@ -7,4 +7,18 @@ const api = axios.create({
withCredentials: true, // Crucial for HttpOnly cookies
})
// Add a response interceptor to handle 401s
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Unauthorized - session likely expired
// We can't use useNavigate here as it's not a React component
// But we can redirect to home which will trigger the login view in App.tsx
window.location.href = "/"
}
return Promise.reject(error)
}
)
export default api

View File

@@ -58,7 +58,10 @@ describe("AuthService", () => {
email: "test@example.com",
display_name: "Test User"
}
mockedApi.get.mockResolvedValueOnce({ data: mockUser })
mockedApi.get.mockResolvedValueOnce({
data: mockUser,
headers: { "content-type": "application/json" }
})
const result = await AuthService.getMe()
@@ -66,6 +69,15 @@ describe("AuthService", () => {
expect(result).toEqual(mockUser)
})
it("throws error on invalid non-JSON response", async () => {
mockedApi.get.mockResolvedValueOnce({
data: "<html>Some fallback</html>",
headers: { "content-type": "text/html" }
})
await expect(AuthService.getMe()).rejects.toThrow("Invalid response from server")
})
it("logs out and calls backend", async () => {
mockedApi.post.mockResolvedValueOnce({ data: { detail: "success" } })
await AuthService.logout()

View File

@@ -38,6 +38,11 @@ export const AuthService = {
async getMe(): Promise<UserResponse> {
const response = await api.get<UserResponse>("/auth/me")
// Double check that we got JSON and not an HTML fallback
const contentType = response.headers["content-type"]
if (contentType && !contentType.includes("application/json")) {
throw new Error("Invalid response from server")
}
return response.data
},