import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; import axios from "../../axios"; // Типы данных interface AuthState { jwt: string | null; refreshToken: string | null; username: string | null; status: "idle" | "loading" | "successful" | "failed"; error: string | null; } // Инициализация состояния const initialState: AuthState = { jwt: null, refreshToken: null, username: null, status: "idle", error: null, }; // AsyncThunk: Регистрация export const registerUser = createAsyncThunk( "auth/register", async ( { username, email, password }: { username: string; email: string; password: string }, { rejectWithValue } ) => { try { const response = await axios.post("/authentication/register", { username, email, password }); return response.data; // { jwt, refreshToken } } catch (err: any) { return rejectWithValue(err.response?.data?.message || "Registration failed"); } } ); // AsyncThunk: Логин export const loginUser = createAsyncThunk( "auth/login", async ( { username, password }: { username: string; password: string }, { rejectWithValue } ) => { try { const response = await axios.post("/authentication/login", { username, password }); return response.data; // { jwt, refreshToken } } catch (err: any) { return rejectWithValue(err.response?.data?.message || "Login failed"); } } ); // AsyncThunk: Обновление токена export const refreshToken = createAsyncThunk( "auth/refresh", async ({ refreshToken }: { refreshToken: string }, { rejectWithValue }) => { try { const response = await axios.post("/authentication/refresh", { refreshToken }); return response.data; // { username } } catch (err: any) { return rejectWithValue(err.response?.data?.message || "Refresh token failed"); } } ); // AsyncThunk: Получение информации о пользователе export const fetchWhoAmI = createAsyncThunk( "auth/whoami", async (_, { rejectWithValue }) => { try { const response = await axios.get("/authentication/whoami"); return response.data; // { username } } catch (err: any) { return rejectWithValue(err.response?.data?.message || "Failed to fetch user info"); } } ); // AsyncThunk: Загрузка токенов из localStorage export const loadTokensFromLocalStorage = createAsyncThunk( "auth/loadTokens", async (_, { }) => { const jwt = localStorage.getItem("jwt"); const refreshToken = localStorage.getItem("refreshToken"); if (jwt && refreshToken) { axios.defaults.headers.common['Authorization'] = `Bearer ${jwt}`; return { jwt, refreshToken }; } else { return { jwt: null, refreshToken: null }; } } ); // Slice const authSlice = createSlice({ name: "auth", initialState, reducers: { logout: (state) => { state.jwt = null; state.refreshToken = null; state.username = null; state.status = "idle"; state.error = null; localStorage.removeItem("jwt"); localStorage.removeItem("refreshToken"); delete axios.defaults.headers.common['Authorization']; }, }, extraReducers: (builder) => { // Регистрация builder.addCase(registerUser.pending, (state) => { state.status = "loading"; state.error = null; }); builder.addCase(registerUser.fulfilled, (state, action: PayloadAction<{ jwt: string; refreshToken: string }>) => { state.status = "successful"; state.jwt = action.payload.jwt; state.refreshToken = action.payload.refreshToken; axios.defaults.headers.common['Authorization'] = `Bearer ${action.payload.jwt}`; localStorage.setItem("jwt", action.payload.jwt); localStorage.setItem("refreshToken", action.payload.refreshToken); }); builder.addCase(registerUser.rejected, (state, action: PayloadAction) => { state.status = "failed"; state.error = action.payload; }); // Логин builder.addCase(loginUser.pending, (state) => { state.status = "loading"; state.error = null; }); builder.addCase(loginUser.fulfilled, (state, action: PayloadAction<{ jwt: string; refreshToken: string }>) => { state.status = "successful"; state.jwt = action.payload.jwt; state.refreshToken = action.payload.refreshToken; axios.defaults.headers.common['Authorization'] = `Bearer ${action.payload.jwt}`; localStorage.setItem("jwt", action.payload.jwt); localStorage.setItem("refreshToken", action.payload.refreshToken); }); builder.addCase(loginUser.rejected, (state, action: PayloadAction) => { state.status = "failed"; state.error = action.payload; }); // Обновление токена builder.addCase(refreshToken.pending, (state) => { state.status = "loading"; state.error = null; }); builder.addCase(refreshToken.fulfilled, (state, action: PayloadAction<{ username: string }>) => { state.status = "successful"; state.username = action.payload.username; }); builder.addCase(refreshToken.rejected, (state, action: PayloadAction) => { state.status = "failed"; state.error = action.payload; }); // Получение информации о пользователе builder.addCase(fetchWhoAmI.pending, (state) => { state.status = "loading"; state.error = null; }); builder.addCase(fetchWhoAmI.fulfilled, (state, action: PayloadAction<{ username: string }>) => { state.status = "successful"; state.username = action.payload.username; }); builder.addCase(fetchWhoAmI.rejected, (state, action: PayloadAction) => { state.status = "failed"; state.error = action.payload; }); // Загрузка токенов из localStorage builder.addCase(loadTokensFromLocalStorage.fulfilled, (state, action: PayloadAction<{ jwt: string | null; refreshToken: string | null }>) => { state.jwt = action.payload.jwt; state.refreshToken = action.payload.refreshToken; if (action.payload.jwt) { axios.defaults.headers.common['Authorization'] = `Bearer ${action.payload.jwt}`; } }); }, }); export const { logout } = authSlice.actions; export const authReducer = authSlice.reducer;