Implement reusable upload platform
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
"""Самодостаточное in-memory хранилище сессий."""
|
||||
|
||||
from .add_file import add_file
|
||||
from .cleanup import cleanup
|
||||
from .create_session import create_session
|
||||
from .get_files import file_count, get_files
|
||||
from .state import MAX_FILE_BYTES, MAX_SESSION_BYTES, TTL_SECONDS, configure
|
||||
|
||||
__all__ = [
|
||||
"create_session",
|
||||
"add_file",
|
||||
"get_files",
|
||||
"file_count",
|
||||
"cleanup",
|
||||
"configure",
|
||||
"TTL_SECONDS",
|
||||
"MAX_FILE_BYTES",
|
||||
"MAX_SESSION_BYTES",
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Добавление файла в сессию."""
|
||||
|
||||
from . import state
|
||||
|
||||
|
||||
def add_file(sid: str, filename: str, content: bytes) -> bool:
|
||||
"""Добавить файл, если сессия существует и общий лимит не превышен."""
|
||||
|
||||
with state._lock:
|
||||
session = state._sessions.get(sid)
|
||||
if not session:
|
||||
return False
|
||||
total = sum(len(item_content) for _, item_content in session["files"])
|
||||
if total + len(content) > state.MAX_SESSION_BYTES:
|
||||
return False
|
||||
session["files"].append((filename, content))
|
||||
return True
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Удаление сессии."""
|
||||
|
||||
from .state import _lock, _sessions
|
||||
|
||||
|
||||
def cleanup(sid: str):
|
||||
"""Удалить сессию и остановить её TTL-таймер."""
|
||||
|
||||
with _lock:
|
||||
session = _sessions.pop(sid, None)
|
||||
if session and session.get("timer"):
|
||||
session["timer"].cancel()
|
||||
@@ -0,0 +1,19 @@
|
||||
"""Создание сессии."""
|
||||
|
||||
import threading
|
||||
import uuid
|
||||
|
||||
from .state import _lock, _sessions, _start_timer
|
||||
|
||||
|
||||
def create_session() -> str:
|
||||
"""Создать сессию и вернуть её уникальный идентификатор."""
|
||||
|
||||
sid = uuid.uuid4().hex
|
||||
with _lock:
|
||||
_sessions[sid] = {
|
||||
"files": [],
|
||||
"timer": _start_timer(sid),
|
||||
"cancel": threading.Event(),
|
||||
}
|
||||
return sid
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Чтение файлов сессии."""
|
||||
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from .state import _lock, _sessions
|
||||
|
||||
|
||||
def get_files(sid: str) -> Optional[List[Tuple[str, bytes]]]:
|
||||
"""Вернуть файлы сессии или None, если сессия не найдена."""
|
||||
|
||||
with _lock:
|
||||
session = _sessions.get(sid)
|
||||
return list(session["files"]) if session else None
|
||||
|
||||
|
||||
def file_count(sid: str) -> int:
|
||||
"""Вернуть количество файлов в сессии."""
|
||||
|
||||
with _lock:
|
||||
session = _sessions.get(sid)
|
||||
return len(session["files"]) if session else 0
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Общее состояние сессий, блокировка, лимиты и TTL."""
|
||||
|
||||
import threading
|
||||
|
||||
|
||||
TTL_SECONDS = 30 * 60
|
||||
MAX_FILE_BYTES = 50 * 1024 * 1024
|
||||
MAX_SESSION_BYTES = 500 * 1024 * 1024
|
||||
|
||||
_sessions: dict = {}
|
||||
_lock = threading.Lock()
|
||||
|
||||
|
||||
def _start_timer(sid: str) -> threading.Timer:
|
||||
"""Запустить таймер автоочистки сессии через TTL."""
|
||||
|
||||
def _clean():
|
||||
with _lock:
|
||||
_sessions.pop(sid, None)
|
||||
|
||||
timer = threading.Timer(TTL_SECONDS, _clean)
|
||||
timer.daemon = True
|
||||
timer.start()
|
||||
return timer
|
||||
|
||||
|
||||
def configure(max_file_bytes: int = None, max_session_bytes: int = None,
|
||||
ttl_seconds: int = None):
|
||||
"""Переопределить лимиты и TTL из конфигурации приложения."""
|
||||
|
||||
global MAX_FILE_BYTES, MAX_SESSION_BYTES, TTL_SECONDS
|
||||
if max_file_bytes is not None:
|
||||
MAX_FILE_BYTES = max_file_bytes
|
||||
if max_session_bytes is not None:
|
||||
MAX_SESSION_BYTES = max_session_bytes
|
||||
if ttl_seconds is not None:
|
||||
TTL_SECONDS = ttl_seconds
|
||||
Reference in New Issue
Block a user