Files
upload-platform/HISTORY/2026-09-06-layer2-architecture-and-api-boundaries.md
T

88 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-09-06: Архитектурные решения Слоя 2 и границы независимых API слоёв
## 1. Контекст и цели
Зафиксированы ключевые проектные и архитектурные решения по разработке Слоя 2 в репозитории `upload-platform` перед началом реализации:
1. Полная автономность и независимость каждого слоя как отдельного API.
2. Легкость интеграции обоих слоёв (Слой 1 + Слой 2) в целевые сервисы (`drhider`, `contractor`) без переделки исходного кода ядра.
3. Составлено подробное ТЗ и резюме в файле `LAYER2-RESUME.md`.
---
## 2. Архитектурные границы слоёв и API-контракты
### Слой 1: File Picker (v0.1.13 — готов и протестирован)
- **Зона ответственности**: выбор отдельных файлов, выбор папок (`webkitdirectory`), клиентская рекурсивная распаковка ZIP-архивов с помощью `fflate`, дедупликация, фильтрация по расширениям, проверка лимитов, отрисовка древовидного UI.
- **API-контракт**: метод `picker.getFiles()` возвращает плоский массив стандартных объектов `File`. Слой полностью изолирован от транспорта и бэкенда.
### Слой 2: Транзитная доставка через ВМ-буфер в RAM бэкенда (текущая задача)
- **Причина существования**: Ingress-контроллер k8s-кластера (`pythonk8s`) обрывает входящие запросы с телом более 64 КБ. Прямой `POST` больших файлов в кластер невозможен; исходящий трафик (egress) из кластера не ограничен.
- **Принципиальные решения**:
1. **Пофайловый транзит (Per-File Transit)** вместо накопления всей пачки в буфере:
- Файл $k$ отправляется браузером через `PUT` в буфер на ВМ.
- Браузер сразу отправляет `POST /api/upload_refs` бэкенду во Flask для одного файла $k$.
- Бэкенд забирает файл исходящим потоковым `GET` прямо в RAM сессии (`upload/backend/session/state.py`).
- Бэкенд немедленно отправляет `DELETE` на ВМ.
- Файл на ВМ удалён, буфер чист. Браузер переходит к файлу $k+1$.
- В любой момент времени в буфере на ВМ находится максимум один файл.
2. **RAM-only (хранение строго в памяти)**:
- Никакой записи на диск ни на бэкенде, ни на ВМ в постоянном режиме.
3. **Zero Hardcode**:
- Полная параметризация через конфигурацию (`vmUploadUrl` на фронтенде, `vmUploadPrefix` на бэкенде для обязательной SSRF-валидации).
4. **Автономный mock-буфер**:
- In-memory mock WebDAV (`PUT`, `GET`, `DELETE`) внутри тестового стенда `site/app.py` для автономного прогона pytest и Playwright без внешней сети.
- **API-контракт фронтенда**: функция `uploadFilesViaVm(files, options)` с колбэками прогресса по файлу/пачке, завершения файла и поддержкой отмены (`AbortController`).
- **API-контракт бэкенда**: Blueprint `upload_refs` и менеджер сессий `session` (создание сессии, потоковое вытягивание, хранение в RAM, TTL, мягкая отмена).
### Слой 3: Бизнес-логика потребителей (не входит в текущую задачу)
- Сервисы `drhider` (обфускация, LLM) и `contractor` (парсинг, diff договоров) забирают файлы из оперативной памяти сессии через `session.get_files(session_id)`.
- В `upload-platform` Слой 3 эмулируется тестовым callback/эндпоинтом, фиксирующим успешный приём файла из сессии для дальнейшей обработки.
---
## 3. Файловая структура Слоя 2 в `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 # Санитизация имен файлов
│ │ └── config.py # Конфигурация по умолчанию
│ └── session/
│ ├── __init__.py
│ ├── state.py # In-memory хранилище сессий
│ ├── create_session.py
│ ├── add_file.py
│ ├── get_files.py
│ ├── store_result.py
│ ├── store_csv.py
│ ├── ttl.py
│ ├── cancel.py
│ └── cleanup.py
├── site/
│ ├── app.py # Flask demo + local RAM mock buffer
│ └── templates/index.html
├── tests/ # pytest + browser smoke
├── LAYER2-RESUME.md # Техническое задание и сводка
├── build.mjs
├── package.json
└── requirements.txt
```
---
## 4. Зафиксированные факты инфраструктуры
1. **ВМ `5.172.178.213`**: Nginx WebDAV настроен на сброс на диск (`/var/www/drhider-upload/`, `/var/www/contracts-upload/`) с cron-очисткой каждые 5 мин (`find ... -mmin +30 -delete`). Для целевого прода требуется перевод в RAM/in-memory буфер.
2. **Кластер k8s**: неймспейсы `20a75175-a58c-49cb-b8fa-e86367b1a8dc` (`drhider`), `b4523aba-b5e6-40f1-be56-bb4d2509357c` (`contractor`), `0e108526-5bb2-4757-9499-3f0bac4f0e83` (`uploader-dev`).