last changes

This commit is contained in:
Spynder
2025-05-14 14:42:45 +03:00
parent 177653d84a
commit 042b53e6a4
41 changed files with 14316 additions and 900 deletions

View File

@ -0,0 +1,182 @@
import type { AuthProvider } from "@refinedev/core";
import axios, { AxiosError } from "axios";
import { jwtDecode } from "jwt-decode";
export const TOKEN_KEY = "refine-auth";
interface AuthResponse {
token: string;
user: {
id: number;
name: string;
email: string;
is_admin: boolean;
};
}
interface ErrorResponse {
message: string;
}
class AuthError extends Error {
constructor(message: string) {
super(message);
this.name = "AuthError";
}
}
interface JWTPayload {
user_id: number;
email: string;
is_admin: boolean;
exp: number;
}
export const authProvider: AuthProvider = {
login: async ({ email, password }) => {
try {
const response = await axios.post<AuthResponse>(
`${import.meta.env.VITE_KRBL_API}/auth/login`,
{
email,
password,
}
);
const { token, user } = response.data;
if (token) {
localStorage.setItem(TOKEN_KEY, token);
localStorage.setItem("user", JSON.stringify(user));
return {
success: true,
redirectTo: "/",
};
}
throw new AuthError("Неверный email или пароль");
} catch (error) {
const errorMessage =
(error as AxiosError<ErrorResponse>)?.response?.data?.message ||
"Неверный email или пароль";
return {
success: false,
error: new AuthError(errorMessage),
};
}
},
logout: async () => {
try {
await axios.post(
`${import.meta.env.VITE_KRBL_API}/auth/logout`,
{},
{
headers: {
Authorization: `Bearer ${localStorage.getItem(TOKEN_KEY)}`,
},
}
);
} catch (error) {
console.error("Ошибка при выходе:", error);
}
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem("user");
return {
success: true,
redirectTo: "/login",
};
},
check: async () => {
const token = localStorage.getItem(TOKEN_KEY);
if (!token) {
return {
authenticated: false,
redirectTo: "/login",
};
}
try {
const response = await axios.get(
`${import.meta.env.VITE_KRBL_API}/auth/me`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
if (response.status === 200) {
// Обновляем информацию о пользователе
localStorage.setItem("user", JSON.stringify(response.data));
return {
authenticated: true,
};
}
} catch (error) {
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem("user");
return {
authenticated: false,
redirectTo: "/login",
error: new AuthError("Сессия истекла, пожалуйста, войдите снова"),
};
}
return {
authenticated: false,
redirectTo: "/login",
};
},
getPermissions: async () => {
const token = localStorage.getItem(TOKEN_KEY);
if (!token) return null;
try {
const decoded = jwtDecode<JWTPayload>(token);
if (decoded.is_admin) {
document.body.classList.add("is-admin");
} else {
document.body.classList.remove("is-admin");
}
return decoded.is_admin ? ["admin"] : ["user"];
} catch {
document.body.classList.remove("is-admin");
return ["user"];
}
},
getIdentity: async () => {
const token = localStorage.getItem(TOKEN_KEY);
const user = localStorage.getItem("user");
if (!token || !user) return null;
try {
const decoded = jwtDecode<JWTPayload>(token);
const userData = JSON.parse(user);
return {
...userData,
is_admin: decoded.is_admin, // всегда используем значение из токена
};
} catch {
return null;
}
},
onError: async (error) => {
const status = (error as AxiosError)?.response?.status;
if (status === 401 || status === 403) {
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem("user");
return {
logout: true,
redirectTo: "/login",
error: new AuthError("Сессия истекла, пожалуйста, войдите снова"),
};
}
return { error };
},
};

View File

@ -1,6 +1,6 @@
import dataProvider from "@refinedev/simple-rest";
import { TOKEN_KEY } from "../authProvider";
import { TOKEN_KEY } from "@providers";
import axios from "axios";
import { languageStore } from "../store/LanguageStore";

View File

@ -0,0 +1,24 @@
import i18n from 'i18next'
import {initReactI18next} from 'react-i18next'
import {I18nProvider} from '@refinedev/core'
import translationRU from '../locales/ru/translation.json'
i18n.use(initReactI18next).init({
resources: {
ru: {
translation: translationRU,
},
},
lng: 'ru',
fallbackLng: 'ru',
interpolation: {
escapeValue: false,
},
})
export const i18nProvider: I18nProvider = {
translate: (key, options) => i18n.t(key, options) as string,
changeLocale: (locale: string) => i18n.changeLanguage(locale),
getLocale: () => i18n.language,
}

3
src/providers/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './data'
export * from './authProvider'
export * from './i18nProvider'