210 lines
6.1 KiB
TypeScript
210 lines
6.1 KiB
TypeScript
/**
|
||
* API модуль для аутентификации
|
||
*/
|
||
|
||
import apiClient from '@/lib/api-client';
|
||
|
||
export interface LoginCredentials {
|
||
email: string;
|
||
password: string;
|
||
}
|
||
|
||
export interface RegisterData {
|
||
email: string;
|
||
password: string;
|
||
password_confirm: string;
|
||
first_name?: string;
|
||
last_name?: string;
|
||
role?: 'mentor' | 'client' | 'parent';
|
||
city?: string;
|
||
timezone?: string;
|
||
}
|
||
|
||
export interface AuthResponse {
|
||
access: string;
|
||
refresh?: string;
|
||
user?: any;
|
||
}
|
||
|
||
export interface User {
|
||
id: number;
|
||
email: string;
|
||
first_name?: string;
|
||
last_name?: string;
|
||
phone?: string;
|
||
role: 'mentor' | 'client' | 'parent';
|
||
is_verified?: boolean;
|
||
avatar_url?: string | null;
|
||
avatar?: string | null;
|
||
telegram_id?: number | null;
|
||
universal_code?: string;
|
||
invitation_link?: string;
|
||
invitation_link_token?: string;
|
||
timezone?: string;
|
||
language?: string;
|
||
city?: string;
|
||
country?: string;
|
||
onboarding_tours_seen?: Record<string, boolean>;
|
||
}
|
||
|
||
/**
|
||
* Вход в систему
|
||
*/
|
||
export async function login(credentials: LoginCredentials): Promise<AuthResponse> {
|
||
console.log('[auth.login] Sending request to /auth/login/', credentials.email);
|
||
const response = await apiClient.post<any>('/auth/login/', credentials);
|
||
console.log('[auth.login] Raw response:', response);
|
||
console.log('[auth.login] response.data:', response.data);
|
||
|
||
// API возвращает { success, message, data: { user, tokens: { access, refresh } } }
|
||
const data = response.data?.data;
|
||
console.log('[auth.login] Parsed data:', data);
|
||
console.log('[auth.login] Tokens:', data?.tokens);
|
||
|
||
if (data?.tokens) {
|
||
const result = {
|
||
access: data.tokens.access,
|
||
refresh: data.tokens.refresh,
|
||
user: data.user
|
||
};
|
||
console.log('[auth.login] Returning:', { ...result, access: result.access?.substring(0, 20) + '...' });
|
||
return result;
|
||
}
|
||
|
||
// Fallback для старого формата
|
||
console.log('[auth.login] Using fallback structure');
|
||
return response.data?.data || response.data;
|
||
}
|
||
|
||
/**
|
||
* Регистрация
|
||
*/
|
||
export async function register(data: RegisterData): Promise<AuthResponse> {
|
||
const response = await apiClient.post<any>('/auth/register/', data);
|
||
// API возвращает { success, message, data: { user, tokens: { access, refresh } } }
|
||
const responseData = response.data?.data;
|
||
if (responseData?.tokens) {
|
||
return {
|
||
access: responseData.tokens.access,
|
||
refresh: responseData.tokens.refresh,
|
||
user: responseData.user
|
||
};
|
||
}
|
||
// Fallback для старого формата
|
||
return response.data?.data || response.data;
|
||
}
|
||
|
||
/**
|
||
* Выход из системы
|
||
*/
|
||
export async function logout(): Promise<void> {
|
||
await apiClient.post('/auth/logout/');
|
||
}
|
||
|
||
/**
|
||
* Получить текущего пользователя
|
||
* Endpoint: GET /api/profile/me/
|
||
*/
|
||
export async function getCurrentUser(): Promise<User> {
|
||
try {
|
||
// Используем ProfileViewSet.me() - возвращает request.user
|
||
console.log('[getCurrentUser] Requesting /profile/me/');
|
||
const response = await apiClient.get<User>('/profile/me/');
|
||
console.log('[getCurrentUser] Success:', response.data);
|
||
return response.data;
|
||
} catch (error: any) {
|
||
console.error('[getCurrentUser] Error with /profile/me/:', error);
|
||
console.error('[getCurrentUser] Error status:', error.response?.status);
|
||
console.error('[getCurrentUser] Error data:', error.response?.data);
|
||
console.error('[getCurrentUser] Error config:', error.config?.url);
|
||
|
||
// Fallback: используем UserViewSet с ID из токена
|
||
const token = typeof window !== 'undefined' ? localStorage.getItem('access_token') : null;
|
||
if (!token) {
|
||
console.error('[getCurrentUser] No token found for fallback');
|
||
throw error;
|
||
}
|
||
|
||
try {
|
||
const payload = JSON.parse(atob(token.split('.')[1]));
|
||
const userId = payload.user_id;
|
||
|
||
if (!userId) {
|
||
console.error('[getCurrentUser] No user_id in token payload:', payload);
|
||
throw error;
|
||
}
|
||
|
||
console.log('[getCurrentUser] Trying fallback /users/' + userId + '/');
|
||
const userResponse = await apiClient.get<User>(`/users/${userId}/`);
|
||
console.log('[getCurrentUser] Fallback success:', userResponse.data);
|
||
return userResponse.data;
|
||
} catch (e) {
|
||
console.error('[getCurrentUser] Fallback error:', e);
|
||
throw error; // Бросаем оригинальную ошибку, а не fallback ошибку
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Обновить access-токен по refresh-токену (запрос без Authorization, чтобы не слать истёкший токен).
|
||
*/
|
||
export async function refreshToken(refresh: string): Promise<{ access: string }> {
|
||
const response = await apiClient.getInstance().post<{ access: string }>(
|
||
'/auth/token/refresh/',
|
||
{ refresh },
|
||
{ __skipAuth: true } as any
|
||
);
|
||
return response.data;
|
||
}
|
||
|
||
/**
|
||
* Смена пароля
|
||
*/
|
||
export async function changePassword(
|
||
oldPassword: string,
|
||
newPassword: string
|
||
): Promise<void> {
|
||
await apiClient.post('/auth/change-password/', {
|
||
old_password: oldPassword,
|
||
new_password: newPassword,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Запрос на сброс пароля
|
||
*/
|
||
export async function requestPasswordReset(data: { email: string }): Promise<void> {
|
||
await apiClient.post('/auth/password-reset/', data);
|
||
}
|
||
|
||
/**
|
||
* Подтверждение email по токену из письма
|
||
*/
|
||
export async function verifyEmail(token: string): Promise<{ success: boolean; message?: string }> {
|
||
const response = await apiClient.post<{ success: boolean; message?: string }>(
|
||
'/auth/verify-email/',
|
||
{ token },
|
||
{ __skipAuth: true } as any
|
||
);
|
||
return response.data;
|
||
}
|
||
|
||
/**
|
||
* Подтверждение сброса пароля (по ссылке из письма)
|
||
*/
|
||
export async function confirmPasswordReset(
|
||
token: string,
|
||
newPassword: string,
|
||
newPasswordConfirm: string
|
||
): Promise<void> {
|
||
await apiClient.post(
|
||
'/auth/password-reset-confirm/',
|
||
{
|
||
token,
|
||
new_password: newPassword,
|
||
new_password_confirm: newPasswordConfirm,
|
||
},
|
||
{ __skipAuth: true } as any
|
||
);
|
||
}
|