199 lines
7.8 KiB
TypeScript
199 lines
7.8 KiB
TypeScript
import { ApiResponse } from './api';
|
||
import { PUBLIC_API_URL } from './api';
|
||
|
||
// Интерфейс для запроса входа
|
||
interface LoginCredentials {
|
||
username: string;
|
||
password: string;
|
||
}
|
||
|
||
// Интерфейс для ответа с токеном
|
||
interface TokenResponse {
|
||
access_token: string;
|
||
token_type: string;
|
||
}
|
||
|
||
// Интерфейс для пользователя
|
||
interface User {
|
||
id: number;
|
||
email: string;
|
||
first_name: string;
|
||
last_name: string;
|
||
is_admin: boolean;
|
||
role?: string;
|
||
}
|
||
|
||
// Константа для ключа в localStorage
|
||
// Используем только один ключ 'token' для хранения токена
|
||
const TOKEN_KEY = 'token';
|
||
|
||
// API для аутентификации
|
||
export const authApi = {
|
||
// Вход в систему
|
||
login: async (email: string, password: string): Promise<ApiResponse<any>> => {
|
||
try {
|
||
// Создаем FormData для отправки данных в формате x-www-form-urlencoded
|
||
const formData = new URLSearchParams();
|
||
formData.append('username', email);
|
||
formData.append('password', password);
|
||
|
||
console.log(`Попытка входа на ${PUBLIC_API_URL}/auth/login с email: ${email}`);
|
||
|
||
// Выполняем запрос напрямую через fetch
|
||
const response = await fetch(`${PUBLIC_API_URL}/auth/login`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/x-www-form-urlencoded',
|
||
},
|
||
body: formData.toString(),
|
||
});
|
||
|
||
const data = await response.json();
|
||
console.log('Ответ сервера при входе:', data);
|
||
|
||
// Обрабатываем ответ
|
||
if (!response.ok) {
|
||
console.error('Ошибка входа, код', response.status, data);
|
||
return {
|
||
success: false,
|
||
error: data.detail || 'Ошибка при авторизации',
|
||
message: 'Не удалось войти в систему',
|
||
};
|
||
}
|
||
|
||
// Проверяем, содержит ли ответ токен
|
||
if (!data.access_token) {
|
||
console.error('Ответ не содержит access_token');
|
||
return {
|
||
success: false,
|
||
error: 'Неверный формат ответа от сервера',
|
||
message: 'Ответ сервера не содержит токен доступа',
|
||
};
|
||
}
|
||
|
||
// Сохраняем токен в localStorage с явным преобразованием в строку
|
||
try {
|
||
localStorage.setItem(TOKEN_KEY, String(data.access_token));
|
||
console.log('Токен сохранен в localStorage:', String(data.access_token).substring(0, 20) + '...');
|
||
|
||
// Проверяем, что токен действительно сохранился
|
||
const savedToken = localStorage.getItem(TOKEN_KEY);
|
||
console.log('Проверка сохранения токена:', savedToken ? 'токен сохранен' : 'токен НЕ сохранен');
|
||
|
||
// Удаляем устаревший ключ, если он есть
|
||
if (localStorage.getItem('auth_token')) {
|
||
localStorage.removeItem('auth_token');
|
||
console.log('Устаревший ключ auth_token удален');
|
||
}
|
||
} catch (storageError) {
|
||
console.error('Ошибка при сохранении токена в localStorage:', storageError);
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
access_token: data.access_token,
|
||
token_type: data.token_type
|
||
}
|
||
};
|
||
} catch (error) {
|
||
console.error('Ошибка при входе:', error);
|
||
return {
|
||
success: false,
|
||
error: error instanceof Error ? error.message : 'Ошибка при авторизации',
|
||
message: 'Не удалось войти в систему'
|
||
};
|
||
}
|
||
},
|
||
|
||
// Получение профиля текущего пользователя
|
||
getProfile: async (): Promise<User | null> => {
|
||
try {
|
||
const token = localStorage.getItem(TOKEN_KEY);
|
||
console.log('Получение профиля с токеном:', token ? token.substring(0, 20) + '...' : 'нет токена');
|
||
|
||
if (!token) {
|
||
console.log('Токен отсутствует, профиль не запрашиваем');
|
||
return null;
|
||
}
|
||
|
||
console.log(`Запрос профиля на ${PUBLIC_API_URL}/users/me`);
|
||
const response = await fetch(`${PUBLIC_API_URL}/users/me`, {
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Content-Type': 'application/json',
|
||
},
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorText = await response.text();
|
||
console.error(`Ошибка получения профиля: HTTP ${response.status}:`, errorText);
|
||
throw new Error(`Ошибка при получении профиля: ${response.status}`);
|
||
}
|
||
|
||
const responseData = await response.json();
|
||
console.log('Полный ответ с профилем пользователя:', responseData);
|
||
|
||
// Защита от неожиданных форматов ответа
|
||
if (!responseData) {
|
||
console.error('Ответ от сервера пуст');
|
||
return null;
|
||
}
|
||
|
||
// Обрабатываем вложенную структуру - пользователь может быть в поле 'user'
|
||
const userData = responseData.user || responseData;
|
||
console.log('Данные пользователя после обработки:', userData);
|
||
|
||
// Проверяем, что полученный объект содержит необходимые поля
|
||
if (!userData || typeof userData !== 'object' || !('id' in userData)) {
|
||
console.error('Полученные данные не содержат необходимых полей пользователя');
|
||
return null;
|
||
}
|
||
|
||
// Если у пользователя нет явного признака администратора,
|
||
// но он имеет роль "admin", устанавливаем is_admin = true
|
||
const userWithAdminCheck = { ...userData };
|
||
|
||
if (userData.role === 'admin' && userData.is_admin === undefined) {
|
||
userWithAdminCheck.is_admin = true;
|
||
console.log('Установлен флаг is_admin=true на основе роли admin');
|
||
}
|
||
|
||
// Убедимся, что is_admin является булевым значением
|
||
if (typeof userWithAdminCheck.is_admin !== 'boolean') {
|
||
userWithAdminCheck.is_admin = Boolean(userWithAdminCheck.is_admin);
|
||
console.log('Преобразовали is_admin в булево значение:', userWithAdminCheck.is_admin);
|
||
}
|
||
|
||
return userWithAdminCheck as User;
|
||
} catch (error) {
|
||
console.error('Ошибка при получении профиля:', error);
|
||
return null;
|
||
}
|
||
},
|
||
|
||
// Выход из системы
|
||
logout: (): void => {
|
||
try {
|
||
localStorage.removeItem(TOKEN_KEY);
|
||
// Удаляем устаревший ключ, если он есть
|
||
if (localStorage.getItem('auth_token')) {
|
||
localStorage.removeItem('auth_token');
|
||
}
|
||
console.log('Токен удален из localStorage');
|
||
} catch (error) {
|
||
console.error('Ошибка при удалении токена:', error);
|
||
}
|
||
},
|
||
|
||
// Проверка, авторизован ли пользователь
|
||
isAuthenticated: (): boolean => {
|
||
try {
|
||
const token = localStorage.getItem(TOKEN_KEY);
|
||
return !!token;
|
||
} catch (error) {
|
||
console.error('Ошибка при проверке токена:', error);
|
||
return false;
|
||
}
|
||
}
|
||
};
|