""" Управление пользователями и их учетными данными МойСклад """ import json import logging import os from pathlib import Path from typing import Dict, Optional from cryptography.fernet import Fernet logger = logging.getLogger(__name__) class UserManager: """Управление пользователями и их учетными данными""" def __init__(self, storage_file: str = None): """ Инициализация менеджера пользователей Args: storage_file: Путь к файлу хранения данных """ # Поддержка Docker volumes data_dir = os.getenv('DATA_DIR', '.') if storage_file is None: storage_file = os.path.join(data_dir, "users.json") self.storage_path = Path(storage_file) self.key_file = Path(data_dir) / ".encryption_key" self.users: Dict[int, Dict] = {} # Создаём data директорию если не существует self.storage_path.parent.mkdir(parents=True, exist_ok=True) self._load_users() # Генерация ключа шифрования self.cipher_key = self._get_or_create_key() self.cipher = Fernet(self.cipher_key) def _get_or_create_key(self) -> bytes: """Получает или создаёт ключ шифрования""" if self.key_file.exists(): with open(self.key_file, "rb") as f: return f.read() else: key = Fernet.generate_key() with open(self.key_file, "wb") as f: f.write(key) logger.info(f"Создан новый ключ шифрования: {self.key_file}") return key def _encrypt(self, text: str) -> str: """Шифрует текст""" return self.cipher.encrypt(text.encode()).decode() def _decrypt(self, encrypted_text: str) -> str: """Расшифровывает текст""" return self.cipher.decrypt(encrypted_text.encode()).decode() def _load_users(self): """Загружает пользователей из файла""" if self.storage_path.exists(): try: with open(self.storage_path, 'r', encoding='utf-8') as f: data = json.load(f) # Конвертируем ключи обратно в int self.users = {int(k): v for k, v in data.items()} logger.info(f"Загружено пользователей: {len(self.users)}") except Exception as e: logger.error(f"Ошибка загрузки пользователей: {e}") self.users = {} else: logger.info("Файл пользователей не найден, создан новый") self.users = {} def _save_users(self): """Сохраняет пользователей в файл""" try: with open(self.storage_path, 'w', encoding='utf-8') as f: json.dump(self.users, f, ensure_ascii=False, indent=2) logger.info(f"Сохранено пользователей: {len(self.users)}") except Exception as e: logger.error(f"Ошибка сохранения пользователей: {e}") def is_user_registered(self, user_id: int) -> bool: """ Проверяет, зарегистрирован ли пользователь Args: user_id: ID пользователя Telegram Returns: True если пользователь зарегистрирован """ return user_id in self.users def register_user( self, user_id: int, moysklad_login: str, moysklad_password: str, username: Optional[str] = None ) -> bool: """ Регистрирует нового пользователя Args: user_id: ID пользователя Telegram moysklad_login: Логин МойСклад moysklad_password: Пароль МойСклад username: Username Telegram (опционально) Returns: True если успешно зарегистрирован """ try: self.users[user_id] = { "username": username, "moysklad_login": self._encrypt(moysklad_login), "moysklad_password": self._encrypt(moysklad_password), "registered_at": None # Можно добавить timestamp } self._save_users() logger.info(f"Пользователь {user_id} зарегистрирован") return True except Exception as e: logger.error(f"Ошибка регистрации пользователя {user_id}: {e}") return False def get_user_credentials(self, user_id: int) -> Optional[Dict[str, str]]: """ Получает учетные данные пользователя Args: user_id: ID пользователя Telegram Returns: Dict с login и password или None """ if user_id not in self.users: return None try: user_data = self.users[user_id] return { "login": self._decrypt(user_data["moysklad_login"]), "password": self._decrypt(user_data["moysklad_password"]) } except Exception as e: logger.error(f"Ошибка получения credentials для {user_id}: {e}") return None def update_credentials( self, user_id: int, moysklad_login: str, moysklad_password: str ) -> bool: """ Обновляет учетные данные пользователя Args: user_id: ID пользователя Telegram moysklad_login: Новый логин МойСклад moysklad_password: Новый пароль МойСклад Returns: True если успешно обновлено """ if user_id not in self.users: return False try: self.users[user_id]["moysklad_login"] = self._encrypt(moysklad_login) self.users[user_id]["moysklad_password"] = self._encrypt(moysklad_password) self._save_users() logger.info(f"Credentials обновлены для пользователя {user_id}") return True except Exception as e: logger.error(f"Ошибка обновления credentials для {user_id}: {e}") return False def delete_user(self, user_id: int) -> bool: """ Удаляет пользователя Args: user_id: ID пользователя Telegram Returns: True если успешно удален """ if user_id in self.users: del self.users[user_id] self._save_users() logger.info(f"Пользователь {user_id} удален") return True return False def get_user_count(self) -> int: """Возвращает количество зарегистрированных пользователей""" return len(self.users)