account and protected router
This commit is contained in:
0
src/redux/slices/account.ts
Normal file
0
src/redux/slices/account.ts
Normal file
@@ -1,36 +1,63 @@
|
||||
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
|
||||
import axios from '../../axios';
|
||||
|
||||
// 🔹 Функция для декодирования JWT
|
||||
// 🔹 Декодирование JWT
|
||||
function decodeJwt(token: string) {
|
||||
const [, payload] = token.split('.');
|
||||
const json = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
|
||||
return JSON.parse(decodeURIComponent(escape(json)));
|
||||
}
|
||||
|
||||
// 🔹 Типы данных
|
||||
// 🔹 Типы
|
||||
interface AuthState {
|
||||
jwt: string | null;
|
||||
refreshToken: string | null;
|
||||
username: string | null;
|
||||
email: string | null; // <-- добавили email
|
||||
email: string | null;
|
||||
id: string | null;
|
||||
status: 'idle' | 'loading' | 'successful' | 'failed';
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
// 🔹 Инициализация состояния
|
||||
// 🔹 Инициализация состояния с синхронной загрузкой из localStorage
|
||||
const jwtFromStorage = localStorage.getItem('jwt');
|
||||
const refreshTokenFromStorage = localStorage.getItem('refreshToken');
|
||||
|
||||
const initialState: AuthState = {
|
||||
jwt: null,
|
||||
refreshToken: null,
|
||||
jwt: jwtFromStorage || null,
|
||||
refreshToken: refreshTokenFromStorage || null,
|
||||
username: null,
|
||||
email: null, // <-- добавили email
|
||||
email: null,
|
||||
id: null,
|
||||
status: 'idle',
|
||||
error: null,
|
||||
};
|
||||
|
||||
// 🔹 AsyncThunk: Регистрация
|
||||
// Если токен есть, подставляем в axios и декодируем
|
||||
if (jwtFromStorage) {
|
||||
axios.defaults.headers.common['Authorization'] = `Bearer ${jwtFromStorage}`;
|
||||
try {
|
||||
const decoded = decodeJwt(jwtFromStorage);
|
||||
initialState.username =
|
||||
decoded[
|
||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'
|
||||
] || null;
|
||||
initialState.email =
|
||||
decoded[
|
||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'
|
||||
] || null;
|
||||
initialState.id =
|
||||
decoded[
|
||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'
|
||||
] || null;
|
||||
} catch {
|
||||
localStorage.removeItem('jwt');
|
||||
localStorage.removeItem('refreshToken');
|
||||
delete axios.defaults.headers.common['Authorization'];
|
||||
}
|
||||
}
|
||||
|
||||
// 🔹 AsyncThunk-ы (login/register/refresh/whoami) остаются как были
|
||||
export const registerUser = createAsyncThunk(
|
||||
'auth/register',
|
||||
async (
|
||||
@@ -47,7 +74,7 @@ export const registerUser = createAsyncThunk(
|
||||
email,
|
||||
password,
|
||||
});
|
||||
return response.data; // { jwt, refreshToken }
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
err.response?.data?.message || 'Registration failed',
|
||||
@@ -56,7 +83,6 @@ export const registerUser = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// 🔹 AsyncThunk: Логин
|
||||
export const loginUser = createAsyncThunk(
|
||||
'auth/login',
|
||||
async (
|
||||
@@ -68,7 +94,7 @@ export const loginUser = createAsyncThunk(
|
||||
username,
|
||||
password,
|
||||
});
|
||||
return response.data; // { jwt, refreshToken }
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
err.response?.data?.message || 'Login failed',
|
||||
@@ -77,7 +103,6 @@ export const loginUser = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// 🔹 AsyncThunk: Обновление токена
|
||||
export const refreshToken = createAsyncThunk(
|
||||
'auth/refresh',
|
||||
async ({ refreshToken }: { refreshToken: string }, { rejectWithValue }) => {
|
||||
@@ -85,7 +110,7 @@ export const refreshToken = createAsyncThunk(
|
||||
const response = await axios.post('/authentication/refresh', {
|
||||
refreshToken,
|
||||
});
|
||||
return response.data; // { jwt, refreshToken }
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
err.response?.data?.message || 'Refresh token failed',
|
||||
@@ -94,13 +119,12 @@ export const refreshToken = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// 🔹 AsyncThunk: Получение информации о пользователе
|
||||
export const fetchWhoAmI = createAsyncThunk(
|
||||
'auth/whoami',
|
||||
async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await axios.get('/authentication/whoami');
|
||||
return response.data; // { username }
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
err.response?.data?.message || 'Failed to fetch user info',
|
||||
@@ -109,22 +133,6 @@ export const fetchWhoAmI = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// 🔹 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',
|
||||
@@ -134,7 +142,7 @@ const authSlice = createSlice({
|
||||
state.jwt = null;
|
||||
state.refreshToken = null;
|
||||
state.username = null;
|
||||
state.email = null; // <-- очистка email
|
||||
state.email = null;
|
||||
state.id = null;
|
||||
state.status = 'idle';
|
||||
state.error = null;
|
||||
@@ -144,7 +152,7 @@ const authSlice = createSlice({
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
// Регистрация
|
||||
// ----------------- Register -----------------
|
||||
builder.addCase(registerUser.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
state.error = null;
|
||||
@@ -154,7 +162,6 @@ const authSlice = createSlice({
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
|
||||
// 🔸 Декодируем JWT
|
||||
const decoded = decodeJwt(action.payload.jwt);
|
||||
state.username =
|
||||
decoded[
|
||||
@@ -180,7 +187,7 @@ const authSlice = createSlice({
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Логин
|
||||
// ----------------- Login -----------------
|
||||
builder.addCase(loginUser.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
state.error = null;
|
||||
@@ -190,7 +197,6 @@ const authSlice = createSlice({
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
|
||||
// 🔸 Декодируем JWT
|
||||
const decoded = decodeJwt(action.payload.jwt);
|
||||
state.username =
|
||||
decoded[
|
||||
@@ -216,7 +222,7 @@ const authSlice = createSlice({
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Обновление токена
|
||||
// ----------------- Refresh -----------------
|
||||
builder.addCase(refreshToken.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
state.error = null;
|
||||
@@ -226,7 +232,6 @@ const authSlice = createSlice({
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
|
||||
// 🔸 Декодируем JWT
|
||||
const decoded = decodeJwt(action.payload.jwt);
|
||||
state.username =
|
||||
decoded[
|
||||
@@ -252,7 +257,7 @@ const authSlice = createSlice({
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Получение информации о пользователе
|
||||
// ----------------- WhoAmI -----------------
|
||||
builder.addCase(fetchWhoAmI.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
state.error = null;
|
||||
@@ -265,35 +270,6 @@ const authSlice = createSlice({
|
||||
state.status = 'failed';
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Загрузка токенов из localStorage
|
||||
builder.addCase(
|
||||
loadTokensFromLocalStorage.fulfilled,
|
||||
(state, action) => {
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
|
||||
if (action.payload.jwt) {
|
||||
const decoded = decodeJwt(action.payload.jwt);
|
||||
state.username =
|
||||
decoded[
|
||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'
|
||||
] || null;
|
||||
state.email =
|
||||
decoded[
|
||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'
|
||||
] || null;
|
||||
state.id =
|
||||
decoded[
|
||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'
|
||||
] || null;
|
||||
|
||||
axios.defaults.headers.common[
|
||||
'Authorization'
|
||||
] = `Bearer ${action.payload.jwt}`;
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user