""" Хранилище сессий в памяти с автоочисткой по TTL. Использование: sid = create_session() add_file(sid, filename, content_bytes) files = get_files(sid) store_result(sid, zip_bytes) zip_data = get_result(sid) cleanup(sid) """ import uuid import threading import time from typing import Dict, List, Tuple, Optional # ═══════════════════════════════════════════════════════════════════════════ # Данные сессии # ═══════════════════════════════════════════════════════════════════════════ TTL_SECONDS = 30 * 60 # 30 минут _sessions: Dict[str, dict] = {} _lock = threading.Lock() def _start_timer(sid: str): """Запустить таймер автоочистки сессии через TTL.""" def _clean(): with _lock: _sessions.pop(sid, None) timer = threading.Timer(TTL_SECONDS, _clean) timer.daemon = True timer.start() return timer # ═══════════════════════════════════════════════════════════════════════════ # API # ═══════════════════════════════════════════════════════════════════════════ def create_session() -> str: """Создать новую сессию. Returns: Уникальный идентификатор сессии (UUID). """ sid = uuid.uuid4().hex[:12] with _lock: _sessions[sid] = { "files": [], "result": None, "timer": _start_timer(sid), } return sid def add_file(sid: str, filename: str, content: bytes) -> bool: """Добавить файл в сессию. Args: sid: Идентификатор сессии filename: Имя файла content: Бинарное содержимое Returns: True если сессия существует, False если нет. """ with _lock: s = _sessions.get(sid) if not s: return False s["files"].append((filename, content)) return True def get_files(sid: str) -> Optional[List[Tuple[str, bytes]]]: """Получить все файлы сессии. Args: sid: Идентификатор сессии Returns: [(filename, content), ...] или None если сессия не найдена. """ with _lock: s = _sessions.get(sid) return list(s["files"]) if s else None def file_count(sid: str) -> int: """Количество файлов в сессии.""" with _lock: s = _sessions.get(sid) return len(s["files"]) if s else 0 def store_result(sid: str, zip_data: bytes) -> bool: """Сохранить результат обработки. Args: sid: Идентификатор сессии zip_data: ZIP-архив с результатом Returns: True если сессия существует, False если нет. """ with _lock: s = _sessions.get(sid) if not s: return False s["result"] = zip_data return True def get_result(sid: str) -> Optional[bytes]: """Получить результат обработки. Args: sid: Идентификатор сессии Returns: ZIP-архив или None если сессия не найдена/результат не готов. """ with _lock: s = _sessions.get(sid) return s["result"] if s else None def cleanup(sid: str): """Удалить сессию.""" with _lock: s = _sessions.pop(sid, None) if s and s.get("timer"): s["timer"].cancel()