182 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import httpx
 | 
						||
import asyncio
 | 
						||
from app.core import config
 | 
						||
from app.core.database import add_session, logout, get_session
 | 
						||
from app.core.localizer import localizer
 | 
						||
 | 
						||
async def login(login, password):
 | 
						||
    """
 | 
						||
    Отправляет запрос на аутентификацию и в случае успеха сохраняет сессию.
 | 
						||
 | 
						||
    :param login: Логин пользователя
 | 
						||
    :param password: Пароль пользователя
 | 
						||
    :return: Кортеж (успех: bool, сообщение: str)
 | 
						||
    """
 | 
						||
    url = f"{config.BASE_URL}/v1/auth/login"
 | 
						||
    
 | 
						||
    try:
 | 
						||
        async with httpx.AsyncClient(http2=True) as client:
 | 
						||
            response = await client.post(url, json={"login": login, "password": password})
 | 
						||
 | 
						||
            if response.status_code == 200:
 | 
						||
                data = response.json()
 | 
						||
                if data.get("status") == "fine":
 | 
						||
                    token_data = data["data"]
 | 
						||
                    add_session(
 | 
						||
                        login=login,
 | 
						||
                        access_token=token_data["access_token"],
 | 
						||
                        refresh_token=token_data["refresh_token"],
 | 
						||
                        user_id=token_data["user_id"]
 | 
						||
                    )
 | 
						||
                    return True, localizer.translate("Успешный вход")
 | 
						||
                else:
 | 
						||
                    return False, data.get("detail", localizer.translate("Неизвестная ошибка ответа"))
 | 
						||
            
 | 
						||
            elif response.status_code in [401]:
 | 
						||
                error_data = response.json()
 | 
						||
                return False, error_data.get("detail", localizer.translate("Неверный логин или пароль"))
 | 
						||
 | 
						||
            elif response.status_code in [403]:
 | 
						||
                error_data = response.json()
 | 
						||
                return False, error_data.get("detail", localizer.translate("Учетная запись пользователя отключена"))
 | 
						||
 | 
						||
            elif response.status_code == 422:
 | 
						||
                return False, localizer.translate("Некорректные данные для входа")
 | 
						||
 | 
						||
            else:
 | 
						||
                return False, f"{localizer.translate('Ошибка сервера')}: {response.status_code}"
 | 
						||
 | 
						||
    except httpx.RequestError as e:
 | 
						||
        return False, f"{localizer.translate('Ошибка сети')}: {e}"
 | 
						||
    except Exception as e:
 | 
						||
        return False, f"{localizer.translate('Произошла ошибка')}: {e}"
 | 
						||
 | 
						||
async def register(login, password, invite=None):
 | 
						||
    """
 | 
						||
    Отправляет запрос на регистрацию нового пользователя.
 | 
						||
 | 
						||
    :param login: Логин
 | 
						||
    :param password: Пароль
 | 
						||
    :param invite: Инвайт-код (опционально)
 | 
						||
    :return: Кортеж (успех: bool, сообщение: str)
 | 
						||
    """
 | 
						||
    url = f"{config.BASE_URL}/v1/auth/register"
 | 
						||
    payload = {"login": login, "password": password}
 | 
						||
    if invite:
 | 
						||
        payload["invite"] = invite
 | 
						||
 | 
						||
    try:
 | 
						||
        async with httpx.AsyncClient(http2=True) as client:
 | 
						||
            response = await client.post(url, json=payload)
 | 
						||
 | 
						||
            if response.status_code == 201:
 | 
						||
                return True, localizer.translate("Регистрация прошла успешно!")
 | 
						||
            
 | 
						||
            error_data = response.json()
 | 
						||
            error_message = error_data.get("detail", localizer.translate("Произошла ошибка"))
 | 
						||
 | 
						||
            if response.status_code == 409:
 | 
						||
                return False, localizer.translate("Этот логин уже занят.")
 | 
						||
            elif response.status_code == 400:
 | 
						||
                return False, localizer.translate("Неверный инвайт-код.")
 | 
						||
            elif response.status_code == 403:
 | 
						||
                return False, localizer.translate("Регистрация в данный момент отключена.")
 | 
						||
            elif response.status_code == 422:
 | 
						||
                 return False, localizer.translate("Данные не прошли валидацию. Проверьте длину логина и пароля.")
 | 
						||
            else:
 | 
						||
                return False, f"{localizer.translate('Ошибка сервера')} ({response.status_code}): {error_message}"
 | 
						||
 | 
						||
    except httpx.RequestError as e:
 | 
						||
        return False, f"{localizer.translate('Ошибка сети')}: {e}"
 | 
						||
    except Exception as e:
 | 
						||
        return False, f"{localizer.translate('Произошла ошибка')}: {e}"
 | 
						||
 | 
						||
 | 
						||
async def refresh_token(access_token: str, refresh_token: str):
 | 
						||
    """
 | 
						||
    Обновляет токен доступа, используя токен обновления.
 | 
						||
 | 
						||
    :param access_token: Истекший токен доступа
 | 
						||
    :param refresh_token: Токен обновления
 | 
						||
    :return: Кортеж (успех: bool, данные: dict | str)
 | 
						||
    """
 | 
						||
    url = f"{config.BASE_URL}/v1/auth/token/refresh"
 | 
						||
    payload = {"access_token": access_token, "refresh_token": refresh_token}
 | 
						||
 | 
						||
    try:
 | 
						||
        async with httpx.AsyncClient(http2=True) as client:
 | 
						||
            response = await client.post(url, json=payload)
 | 
						||
 | 
						||
            if response.status_code == 200:
 | 
						||
                data = response.json()
 | 
						||
                if data.get("status") == "fine":
 | 
						||
                    token_data = data["data"]
 | 
						||
                    # Обновляем сессию с новыми токенами
 | 
						||
                    add_session(
 | 
						||
                        login=None,  # Логин не требуется для обновления
 | 
						||
                        access_token=token_data["access_token"],
 | 
						||
                        refresh_token=token_data["refresh_token"],
 | 
						||
                        update_existing=True
 | 
						||
                    )
 | 
						||
                    return True, token_data
 | 
						||
                else:
 | 
						||
                    return False, data.get("detail", localizer.translate("Unknown error"))
 | 
						||
 | 
						||
            elif response.status_code == 401:
 | 
						||
                return False, localizer.translate("Refresh token is invalid or expired")
 | 
						||
            
 | 
						||
            else:
 | 
						||
                return False, f"{localizer.translate('Server error')}: {response.status_code}"
 | 
						||
 | 
						||
    except httpx.RequestError as e:
 | 
						||
        return False, f"{localizer.translate('Network error')}: {e}"
 | 
						||
    except Exception as e:
 | 
						||
        return False, f"{localizer.translate('An error occurred')}: {e}"
 | 
						||
 | 
						||
 | 
						||
async def get_user_role(access_token: str, login: str):
 | 
						||
    """
 | 
						||
    Получает роль и права пользователя по токену доступа.
 | 
						||
    В случае истечения срока действия токена, пытается его обновить.
 | 
						||
    """
 | 
						||
    url = f"{config.BASE_URL}/v1/user/role"
 | 
						||
    headers = {"Authorization": f"Bearer {access_token}"}
 | 
						||
 | 
						||
    try:
 | 
						||
        async with httpx.AsyncClient(http2=True) as client:
 | 
						||
            response = await client.get(url, headers=headers)
 | 
						||
 | 
						||
            if response.status_code == 200:
 | 
						||
                data = response.json()
 | 
						||
                return (True, data['data']) if data.get("status") == "fine" else (False, data.get("detail", localizer.translate("Unknown error")))
 | 
						||
 | 
						||
            elif response.status_code == 401:
 | 
						||
                # Токен истек, пытаемся обновить
 | 
						||
                session = get_session(login)
 | 
						||
                if not session or not session['refresh_token']:
 | 
						||
                    return False, localizer.translate("No refresh token found")
 | 
						||
 | 
						||
                refresh_success, refresh_data = await refresh_token(access_token, session['refresh_token'])
 | 
						||
                
 | 
						||
                if refresh_success:
 | 
						||
                    # Повторяем запрос с новым токеном
 | 
						||
                    new_access_token = refresh_data['access_token']
 | 
						||
                    headers["Authorization"] = f"Bearer {new_access_token}"
 | 
						||
                    response = await client.get(url, headers=headers)
 | 
						||
                    
 | 
						||
                    if response.status_code == 200:
 | 
						||
                        data = response.json()
 | 
						||
                        return (True, data['data']) if data.get("status") == "fine" else (False, localizer.translate("Failed to get role after refresh"))
 | 
						||
                
 | 
						||
                # Если обновление не удалось, выходим из системы
 | 
						||
                logout(access_token)
 | 
						||
                return False, localizer.translate("Session expired, please log in again")
 | 
						||
 | 
						||
            else:
 | 
						||
                return False, f"{localizer.translate('Server error')}: {response.status_code}"
 | 
						||
 | 
						||
    except httpx.RequestError as e:
 | 
						||
        return False, f"{localizer.translate('Network error')}: {e}"
 | 
						||
    except Exception as e:
 | 
						||
        return False, f"{localizer.translate('An error occurred')}: {e}"
 |