Files
LiquidCode_Frontend/src/redux/slices/auth.ts
Виталий Лавшонок 4972836164 formatting
2025-11-04 15:04:59 +03:00

262 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<any>) => {
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<any>) => {
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<any>) => {
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<any>) => {
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;