Mitchell Magro 1dcfa081e0 s
2025-07-26 18:47:14 +02:00

157 lines
5.0 KiB
TypeScript

import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
// Define the initial state for the authentication slice
interface AuthState {
isLoggedIn: boolean;
authMessage: string;
status: "idle" | "loading" | "succeeded" | "failed";
error: string | null;
}
const initialState: AuthState = {
isLoggedIn: false,
authMessage: "",
status: "idle",
error: null,
};
// Async Thunk for Login
// This handles the API call to your Next.js login Route Handler
export const login = createAsyncThunk(
"auth/login",
async (
{ email, password }: { email: string; password: string },
{ rejectWithValue },
) => {
try {
const response = await fetch("/api/auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (!response.ok) {
// If the server responded with an error status (e.g., 401, 400, 500)
return rejectWithValue(data.message || "Login failed");
}
// On successful login, the backend sets the HTTP-only cookie.
// We'll set a client-side flag (like localStorage) for immediate UI updates,
// though the primary source of truth for auth is the HTTP-only cookie.
if (typeof window !== "undefined") {
// Ensure localStorage access is client-side
localStorage.setItem("userToken", "mock-authenticated"); // For client-side state sync
}
return data.message || "Login successful";
} catch (error) {
// Handle network errors or other unexpected issues
if (error instanceof Error) {
return rejectWithValue(error.message || "Network error during login");
}
}
},
);
// Async Thunk for Logout
// This handles the API call to your Next.js logout Route Handler
export const logout = createAsyncThunk(
"auth/logout",
async (_, { rejectWithValue }) => {
try {
const response = await fetch("/api/auth/logout", {
method: "DELETE",
});
const data = await response.json();
if (!response.ok) {
// If the server responded with an error status
return rejectWithValue(data.message || "Logout failed");
}
if (typeof window !== "undefined") {
// Ensure localStorage access is client-side
localStorage.removeItem("userToken"); // Clear client-side flag
}
return data.message || "Logged out successfully";
} catch (error) {
// Handle network errors
if (error instanceof Error) {
return rejectWithValue(error.message || "Network error during logout");
}
}
},
);
// Create the authentication slice
const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
// Reducer to set an authentication message (e.g., from UI actions)
setAuthMessage: (state, action: PayloadAction<string>) => {
state.authMessage = action.payload;
},
// Reducer to clear the authentication message
clearAuthMessage: (state) => {
state.authMessage = "";
},
// Reducer to initialize login status from client-side storage (e.g., on app load)
// This is useful for cases where middleware might not redirect immediately,
// or for client-side rendering of protected content based on initial state.
initializeAuth: (state) => {
if (typeof window !== "undefined") {
// Ensure this runs only on the client
const userToken = localStorage.getItem("userToken");
state.isLoggedIn = userToken === "mock-authenticated";
}
},
},
extraReducers: (builder) => {
builder
// Login Thunk Reducers
.addCase(login.pending, (state) => {
state.status = "loading";
state.error = null;
state.authMessage = "Attempting login...";
})
.addCase(login.fulfilled, (state, action) => {
state.status = "succeeded";
state.isLoggedIn = true;
state.authMessage = action.payload;
})
.addCase(login.rejected, (state, action) => {
state.status = "failed";
state.isLoggedIn = false;
state.error = action.payload as string;
state.authMessage = action.payload as string; // Display error message
})
// Logout Thunk Reducers
.addCase(logout.pending, (state) => {
state.status = "loading";
state.error = null;
state.authMessage = "Logging out...";
})
.addCase(logout.fulfilled, (state, action) => {
state.status = "succeeded";
state.isLoggedIn = false;
state.authMessage = action.payload;
})
.addCase(logout.rejected, (state, action) => {
state.status = "failed";
state.isLoggedIn = true; // Stay logged in if logout failed
state.error = action.payload as string;
state.authMessage = action.payload as string; // Display error message
});
},
});
export const { setAuthMessage, clearAuthMessage, initializeAuth } =
authSlice.actions;
export default authSlice.reducer;