uchill/front_material/api/auth.ts

205 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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;
}
/**
* Вход в систему
*/
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
);
}