contests
This commit is contained in:
@@ -1,25 +1,36 @@
|
||||
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
|
||||
import axios from '../../axios';
|
||||
|
||||
// Типы данных
|
||||
// 🔹 Функция для декодирования 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
|
||||
id: string | null;
|
||||
status: 'idle' | 'loading' | 'successful' | 'failed';
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
// Инициализация состояния
|
||||
// 🔹 Инициализация состояния
|
||||
const initialState: AuthState = {
|
||||
jwt: null,
|
||||
refreshToken: null,
|
||||
username: null,
|
||||
email: null, // <-- добавили email
|
||||
id: null,
|
||||
status: 'idle',
|
||||
error: null,
|
||||
};
|
||||
|
||||
// AsyncThunk: Регистрация
|
||||
// 🔹 AsyncThunk: Регистрация
|
||||
export const registerUser = createAsyncThunk(
|
||||
'auth/register',
|
||||
async (
|
||||
@@ -45,7 +56,7 @@ export const registerUser = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// AsyncThunk: Логин
|
||||
// 🔹 AsyncThunk: Логин
|
||||
export const loginUser = createAsyncThunk(
|
||||
'auth/login',
|
||||
async (
|
||||
@@ -66,7 +77,7 @@ export const loginUser = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// AsyncThunk: Обновление токена
|
||||
// 🔹 AsyncThunk: Обновление токена
|
||||
export const refreshToken = createAsyncThunk(
|
||||
'auth/refresh',
|
||||
async ({ refreshToken }: { refreshToken: string }, { rejectWithValue }) => {
|
||||
@@ -74,7 +85,7 @@ export const refreshToken = createAsyncThunk(
|
||||
const response = await axios.post('/authentication/refresh', {
|
||||
refreshToken,
|
||||
});
|
||||
return response.data; // { username }
|
||||
return response.data; // { jwt, refreshToken }
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
err.response?.data?.message || 'Refresh token failed',
|
||||
@@ -83,7 +94,7 @@ export const refreshToken = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// AsyncThunk: Получение информации о пользователе
|
||||
// 🔹 AsyncThunk: Получение информации о пользователе
|
||||
export const fetchWhoAmI = createAsyncThunk(
|
||||
'auth/whoami',
|
||||
async (_, { rejectWithValue }) => {
|
||||
@@ -98,10 +109,10 @@ export const fetchWhoAmI = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// AsyncThunk: Загрузка токенов из localStorage
|
||||
// 🔹 AsyncThunk: Загрузка токенов из localStorage
|
||||
export const loadTokensFromLocalStorage = createAsyncThunk(
|
||||
'auth/loadTokens',
|
||||
async (_, {}) => {
|
||||
async () => {
|
||||
const jwt = localStorage.getItem('jwt');
|
||||
const refreshToken = localStorage.getItem('refreshToken');
|
||||
|
||||
@@ -114,7 +125,7 @@ export const loadTokensFromLocalStorage = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// Slice
|
||||
// 🔹 Slice
|
||||
const authSlice = createSlice({
|
||||
name: 'auth',
|
||||
initialState,
|
||||
@@ -123,6 +134,8 @@ const authSlice = createSlice({
|
||||
state.jwt = null;
|
||||
state.refreshToken = null;
|
||||
state.username = null;
|
||||
state.email = null; // <-- очистка email
|
||||
state.id = null;
|
||||
state.status = 'idle';
|
||||
state.error = null;
|
||||
localStorage.removeItem('jwt');
|
||||
@@ -136,118 +149,145 @@ const authSlice = createSlice({
|
||||
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(registerUser.fulfilled, (state, action) => {
|
||||
state.status = 'successful';
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
|
||||
// 🔸 Декодируем 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}`;
|
||||
localStorage.setItem('jwt', action.payload.jwt);
|
||||
localStorage.setItem('refreshToken', action.payload.refreshToken);
|
||||
});
|
||||
builder.addCase(registerUser.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Логин
|
||||
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(loginUser.fulfilled, (state, action) => {
|
||||
state.status = 'successful';
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
|
||||
// 🔸 Декодируем 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}`;
|
||||
localStorage.setItem('jwt', action.payload.jwt);
|
||||
localStorage.setItem('refreshToken', action.payload.refreshToken);
|
||||
});
|
||||
builder.addCase(loginUser.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Обновление токена
|
||||
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(refreshToken.fulfilled, (state, action) => {
|
||||
state.status = 'successful';
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
|
||||
// 🔸 Декодируем 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}`;
|
||||
localStorage.setItem('jwt', action.payload.jwt);
|
||||
localStorage.setItem('refreshToken', action.payload.refreshToken);
|
||||
});
|
||||
builder.addCase(refreshToken.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Получение информации о пользователе
|
||||
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;
|
||||
},
|
||||
);
|
||||
builder.addCase(fetchWhoAmI.fulfilled, (state, action) => {
|
||||
state.status = 'successful';
|
||||
state.username = action.payload.username;
|
||||
});
|
||||
builder.addCase(fetchWhoAmI.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
|
||||
// Загрузка токенов из localStorage
|
||||
builder.addCase(
|
||||
loadTokensFromLocalStorage.fulfilled,
|
||||
(
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
jwt: string | null;
|
||||
refreshToken: string | null;
|
||||
}>,
|
||||
) => {
|
||||
(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}`;
|
||||
|
||||
@@ -6,9 +6,16 @@ import axios from '../../axios';
|
||||
// =====================
|
||||
|
||||
export interface Mission {
|
||||
missionId: number;
|
||||
id: number;
|
||||
authorId: number;
|
||||
name: string;
|
||||
sortOrder: number;
|
||||
difficulty: number;
|
||||
tags: string[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
timeLimitMilliseconds: number;
|
||||
memoryLimitBytes: number;
|
||||
statements: null;
|
||||
}
|
||||
|
||||
export interface Member {
|
||||
|
||||
Reference in New Issue
Block a user