desktop_app/app/core/services/auth_service.py
2025-09-29 02:43:25 +03:00

182 lines
8.5 KiB
Python
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.

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