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;