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