139 lines
4.2 KiB
Python
139 lines
4.2 KiB
Python
"""
|
|
Хранилище сессий в памяти с автоочисткой по 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()
|