gonets/SRV/api/v1/auth.py
2025-11-19 15:03:02 +05:00

183 lines
9.1 KiB
Python
Raw Permalink 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.

from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.security import OAuth2PasswordBearer
from services.auth_service import AuthService
from models.auth import UserCreate, UserLogin, UserResponse, Token
from storage.json_storage import JsonStorage
from utils.i18n import get_translations, _
from babel.support import Translations
import logging
import os
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
router = APIRouter()
auth_service = AuthService()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
@router.post("/register", response_model=UserResponse)
async def register(user: UserCreate, translations: Translations = Depends(get_translations)):
"""Регистрирует нового пользователя.
Создаёт нового пользователя на основе предоставленных данных и возвращает
информацию о зарегистрированном пользователе.
Args:
user (UserCreate): Объект с данными нового пользователя (username, password, is_admin).
translations: Объект переводов для текущего языка.
Returns:
UserResponse: Объект с данными зарегистрированного пользователя (id, username, is_admin).
Raises:
HTTPException: Если пользователь уже существует (status_code=400) или произошла
внутренняя ошибка (status_code=500).
"""
try:
db_user = auth_service.register(user)
logger.info(f"Пользователь {user.username} успешно зарегистрирован")
return db_user
except HTTPException as e:
raise e
except Exception as e:
logger.error(f"Error in register: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось зарегистрировать пользователя"))
@router.post("/login", response_model=Token)
async def login(user: UserLogin, request: Request, translations: Translations = Depends(get_translations)):
"""Авторизует пользователя и создаёт сессию.
Проверяет учетные данные пользователя и возвращает токен и session_id для
последующей авторизации.
Args:
user (UserLogin): Объект с данными для входа (username, password).
request (Request): Объект запроса для получения client_id (IP-адрес клиента).
translations: Объект переводов для текущего языка.
Returns:
Token: Объект с access_token и session_id для авторизации.
Raises:
HTTPException: Если учетные данные неверны (status_code=401) или произошла
внутренняя ошибка (status_code=500).
"""
try:
client_id = request.client.host
logger.debug(f"Попытка входа в систему клиента {client_id}")
result = auth_service.login(user, client_id)
logger.info(f"Успешная авторизация для клиента {client_id}, Идентификатор сессии: {result['session_id']}")
return result
except HTTPException as e:
raise e
except Exception as e:
logger.error(f"Ошибка при авторизации: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось авторизовать пользователя"))
@router.get("/me")
async def get_current_user(request: Request, translations: Translations = Depends(get_translations)):
"""Возвращает данные текущего пользователя и токен.
Проверяет активную сессию по client_id и возвращает информацию о пользователе
и токен для дальнейшего использования.
Args:
request (Request): Объект запроса для получения client_id (IP-адрес клиента).
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с ключами "user" (UserResponse), "access_token" (str) и
"token_type" (str, всегда "bearer").
Raises:
HTTPException: Если сессия не найдена или истекла (status_code=401) или
произошла внутренняя ошибка (status_code=500).
"""
try:
client_id = request.client.host
logger.debug(f"Запрос /me для клиента {client_id}")
result = auth_service.get_user_by_session(client_id)
return {
"user": result["user"],
"access_token": result["access_token"],
"token_type": translations.gettext("bearer"),
}
except HTTPException as e:
raise e
except Exception as e:
logger.error(f"Ошибка при get_current_user: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось получить данные пользователя"))
@router.get("/logout")
async def logout(request: Request, translations: Translations = Depends(get_translations)):
"""Выход из системы, удаление сессии.
Удаляет активную сессию пользователя по client_id.
Args:
request (Request): Объект запроса для получения client_id (IP-адрес клиента).
translations: Объект переводов для текущего языка.
Returns:
dict: Пустой словарь или сообщение об успешном выходе.
Raises:
HTTPException: Если сессия не найдена (status_code=401) или произошла
внутренняя ошибка (status_code=500).
"""
try:
client_id = request.client.host
logger.debug(f"Запрос на выход из системы для клиента {client_id}")
return auth_service.logout(client_id)
except HTTPException as e:
raise e
except Exception as e:
logger.error(f"Не удалось выйти из системы: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось выйти из системы"))
@router.get("/check_json")
async def check_users_file(translations: Translations = Depends(get_translations)):
"""Проверяет, существует ли файл users.json и содержит ли он пользователей.
Возвращает информацию о состоянии файла и наличии пользователей.
Returns:
dict: Словарь с ключами:
- "exists" (bool): Существует ли файл.
- "has_users" (bool): Содержит ли файл пользователей.
- "message" (str): Сообщение с описанием результата.
Raises:
HTTPException: Если произошла ошибка при проверке файла (status_code=500).
"""
try:
file_path = "/etc/gonets/users.json"
storage = JsonStorage(file_path=file_path)
if not os.path.exists(file_path):
logger.info(f"Файл {file_path} не существует")
return {
"exists": False,
"has_users": False,
"message": translations.gettext("Файл users.json не существует"),
}
data = storage._read_data()
has_users = len(data.get("users", [])) > 0
if has_users:
logger.info(f"Файл {file_path} существует и содержит пользователей")
return {
"exists": True,
"has_users": True,
"message": translations.gettext("Файл users.json существует и содержит пользователей"),
}
else:
logger.info(f"Файл {file_path} существует, но не содержит пользователей")
return {
"exists": True,
"has_users": False,
"message": translations.gettext("Файл users.json существует, но не содержит пользователей"),
}
except Exception as e:
logger.error(f"Ошибка при проверке файла: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка при проверке файла"))