13 KiB
РЕЗЮМЕ И ТЕХНИЧЕСКОЕ ЗАДАНИЕ: СЛОЙ 2 (ЗАГРУЗКА ЧЕРЕЗ ВМ-БУФЕР)
Документ составлен 2026-09-06 для старта нового рабочего контекста в репозитории upload-platform.
1. ЦЕЛЬ И АРХИТЕКТУРНЫЕ ГРАНИЦЫ
В репозитории upload-platform разрабатывается и изолированно тестируется переиспользуемый встраиваемый модуль, состоящий из двух независимых слоёв:
-
Слой 1 (Выбор файлов / File Picker) — ГОТОВ И ПРОТЕСТИРОВАН (v0.1.13):
- Фронтенд-модуль на Vanilla JS +
fflate. - Поддерживает выбор отдельных файлов, выбор папок (
webkitdirectory), распаковку ZIP и вложенных архивов. - Дедупликация, фильтрация по расширениям, древовидный UI, лимиты (
maxEntries,maxEntryBytes,maxTotalBytes,maxDepth). - Возвращает чистый плоский массив выбранных объектов
Fileчерез методpicker.getFiles(). Не зависит от способа передачи данных.
- Фронтенд-модуль на Vanilla JS +
-
Слой 2 (Транзитная загрузка файлов через ВМ-буфер) — ТЕКУЩАЯ ЗАДАЧА:
- Задача слоя: доставить файлы из браузера в память сессии бэкенд-приложения (Flask).
- Почему нужен буфер: шлюз ingress managed-кластера Kubernetes (
pythonk8s) обрывает входящие HTTP-запросы с телом более 64 КБ. ПрямойPOST /uploadбольших файлов в кластер невозможен. При этом исходящий трафик (egress) из кластера не ограничен. - Решение: Браузер загружает файлы во внешний буфер на ВМ, а бэкенд вытягивает их исходящими
GET-запросами (pull) в оперативную память своей сессии. - Потребители модуля: сервис анонимизации
drhiderи сервис сверки договоров (contractor). Оба сервиса встраивают Слой 1 + Слой 2 как подпапкуupload/без изменения исходного кода модулей (только собственный конфиг) и поверх накладывают свою бизнес-логику (Слой 3: LLM, обфускация, парсинг, diff).
2. ФАКТИЧЕСКОЕ СОСТОЯНИЕ ИНФРАСТРУКТУРЫ (ПРОВЕРЕНО НА ВМ И В K8S)
Проверено 2026-09-06 прямыми командами на ВМ и в кластере:
-
ВМ
5.172.178.213(contracts.kube5s.ru):- Доступ:
ssh -i ~/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213. - В
nginxнастроены WebDAV-буферы:/drhider-upload/\rightarrowalias /var/www/drhider-upload/;, CORS:https://drhider.pythonk8s.dev.nubes.ru./contracts-upload/\rightarrowalias /var/www/contracts-upload/;, CORS:https://contractor.pythonk8s.dev.nubes.ru.
- Проблема текущей реализации на ВМ:
- Nginx WebDAV пишет файлы на диск (корневой раздел).
- Cron каждые 5 минут удаляет файлы старше 30 минут:
find /var/www/drhider-upload -type f -mmin +30 -delete. - По требованию безопасности файлы не должны оседать на диске — обработка и буферизация должны быть строго в оперативной памяти (RAM).
- Доступ:
-
Kubernetes-кластер (
iot-naeel):- Под
drhider: namespace20a75175-a58c-49cb-b8fa-e86367b1a8dc, хостdrhider.pythonk8s.dev.nubes.ru. - Под
contractor: namespaceb4523aba-b5e6-40f1-be56-bb4d2509357c, хостcontractor.pythonk8s.dev.nubes.ru. - Под
uploader-dev(полигон): namespace0e108526-5bb2-4757-9499-3f0bac4f0e83, хостuploader-dev.pythonk8s.dev.nubes.ru(на нём крутитсяupload-platform0.1.13).
- Под
3. ПРИНЦИПИАЛЬНЫЕ АРХИТЕКТУРНЫЕ ИЗМЕНЕНИЯ СЛОЯ 2
Изменение 1: Пофайловый транзит (Streaming / Per-File Transit) вместо батча
- Как было раньше: Браузер в цикле заливал все файлы пачки в буфер на ВМ, и только в самом конце отправлял во Flask один общий запрос
/api/upload_refsсо списком всех ссылок. Буфер был вынужден одновременно хранить все файлы пачки (до сотен мегабайт). - Как должно быть:
- Браузер берет один файл
\rightarrowделаетPUTв буфер на ВМ. - Браузер сразу делает
POST /api/upload_refsдля этого одного файла. - Flask вытягивает его исходящим потоковым
GETпрямо в память сессии (site/session.py) и сразу отправляетDELETEна ВМ. - Файл на ВМ удален. Браузер переходит к следующему файлу.
- Результат: В буфере на ВМ в любой момент времени находится максимум один файл. Общий объем памяти буфера минимален.
- Браузер берет один файл
Изменение 2: Хранение строго в памяти (RAM)
- Никаких временных файлов на диске.
- На бэкенде Flask файлы хранятся в RAM-структуре сессии (
upload/backend/session/state.py). - На ВМ для буфера не должен использоваться сброс на диск (на стороне Nginx требуется проксирование в память или легковесный in-memory микросервис).
Изменение 3: Полная параметризация (Zero Hardcode)
- Прямой IP из браузера поверх HTTPS невозможен (Mixed Content, отсутствие доверенного TLS-сертификата).
- В коде не должно быть зашитых хостов (
contracts.kube5s.ruи т.д.). - Все адреса передаются через конфигурацию:
- Фронтенд:
vmUploadUrl(например,https://contracts.kube5s.ru/drhider-upload/или локальный URL). - Бэкенд:
vmUploadPrefix(для обязательной SSRF-валидации входящих URL перед pull).
- Фронтенд:
Изменение 4: Автономный mock-буфер для тестов в upload-platform
- Тесты в upload-platform не должны зависеть от внешней сети или чужих боевых ВМ.
- В тестовый стенд и dev-сервер site/app.py встраивается локальный легковесный in-memory mock WebDAV (обработка
PUT,GET,DELETE), что позволяет гонять Playwright- и Python-тесты полностью оффлайн.
4. СТРУКТУРА МОДУЛЯ В upload-platform
upload-platform/
├── upload/
│ ├── frontend/
│ │ ├── index.js # Точка входа Слоя 1 (initFilePicker)
│ │ ├── table/ # Логика и UI Слоя 1
│ │ ├── zip/ # Распаковка архивов (fflate)
│ │ └── upload/ # СЛОЙ 2 (ФРОНТЕНД):
│ │ ├── put_to_vm.js # XHR PUT одного файла на буфер с прогрессом и abort
│ │ └── upload_via_vm.js # Пофайловый транзит (PUT -> upload_refs -> repeat)
│ └── backend/ # СЛОЙ 2 (БЭКЕНД):
│ ├── __init__.py
│ ├── upload_refs/
│ │ ├── __init__.py
│ │ ├── blueprint.py # Flask Blueprint (POST /api/upload_refs)
│ │ ├── pull_file.py # Исходящий потоковый GET с ретраями (httpx)
│ │ ├── safe_name.py # Санитизация имен (защита от path traversal)
│ │ └── config.py # Дефолтные параметры (retries, delay)
│ └── session/
│ ├── __init__.py
│ ├── state.py # In-memory хранилище сессий (_sessions, lock, TTL)
│ ├── create_session.py # Создание сессии (UUID)
│ ├── add_file.py # Добавление файла с проверкой лимита памяти
│ ├── get_files.py # Получение списка файлов
│ ├── store_result.py # Сохранение ZIP-результата
│ ├── store_csv.py # Сохранение CSV-результата
│ ├── ttl.py # touch, pause_ttl, resume_ttl
│ ├── cancel.py # Мягкая отмена сессии (Event)
│ └── cleanup.py # Очистка сессии
├── site/
│ ├── app.py # Flask demo (Слой 1 + Слой 2 + local mock buffer)
│ └── templates/index.html # Demo UI со сквозным сценарием выбора и загрузки
├── tests/ # Тесты Слоя 2 (pytest + browser smoke)
├── build.mjs # Сборка бандлов (ESM / IIFE)
├── package.json # Версионирование платформы
└── requirements.txt # Зависимости Python (Flask, httpx, pytest)
5. ПОШАГОВЫЙ ПЛАН РЕАЛИЗАЦИИ В НОВОМ ЧАТЕ
-
Шаг 1. Перенос и адаптация бэкенда Слоя 2:
- Перенести
backend/session/иbackend/upload_refs/из upload/backend в upload-platform/upload/backend. - В
upload-platform/requirements.txtзафиксироватьhttpx>=0.27.0. - Проверить чистоту импортов и типизацию.
- Перенести
-
Шаг 2. Перенос и адаптация фронтенда Слоя 2 на пофайловый транзит:
- Создать upload-platform/upload/frontend/upload/put_to_vm.js (XHR PUT с прогрессом и возможностью
abort). - Создать upload-platform/upload/frontend/upload/upload_via_vm.js с реализацией пофайловой передачи:
- цикл по файлам;
- загрузка файла
kна буфер; - немедленный вызов
/api/upload_refsдля файлаk; - обновление прогресса в UI;
- переход к файлу
k+1.
- Создать upload-platform/upload/frontend/upload/put_to_vm.js (XHR PUT с прогрессом и возможностью
-
Шаг 3. Локальный mock-буфер и интеграция в
site/app.py:- Встроить в upload-platform/site/app.py тестовый Blueprint mock-буфера (
PUT,GET,DELETEв оперативной памяти). - Подключить
create_upload_refs_blueprintк приложению. - Обновить upload-platform/site/templates/index.html, добавив кнопку «Загрузить» и отображение прогресса пофайловой передачи.
- Встроить в upload-platform/site/app.py тестовый Blueprint mock-буфера (
-
Шаг 4. Написание автоматических тестов:
- Python unit-тесты (
pytest):safe_name,session(TTL, лимиты, add/get),upload_refs(SSRF-блокировка, pull с mock-буфера, удаление после pull). - Playwright browser-тесты: сквозной сценарий от выбора файлов в
initFilePickerдо их успешного появления в сессии Flask.
- Python unit-тесты (
-
Шаг 5. Сборка, bump версии и документация:
- Обновить
build.mjsиpackage.json(bump версии). - Написать подробный upload-platform/README.md с инструкцией для агентов, как встроить оба слоя в сторонний сервис за 2 минуты.
- Фиксация коммитом и пуш.
- Обновить