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}"