v7.0.0: API upload/process/download, session.py, фронт без обработки — всё на бэкенде
Deploy drhider / validate (push) Waiting to run
Deploy drhider / validate (push) Waiting to run
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
Хранилище сессий в памяти с автоочисткой по 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()
|
||||
Reference in New Issue
Block a user