Files
upload-platform/docs/CODE-REFERENCE.md

161 lines
9.1 KiB
Markdown
Raw Permalink 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.
# Справочник кода Upload Platform
Документ описывает актуальную picker-only реализацию. VM upload, backend sessions
и API загрузки относятся к отменённой архитектуре и в текущем коде отсутствуют.
## Архитектура
```text
site/app.py
-> site/templates/index.html
-> dist/file-picker.iife.js
-> upload/frontend/index.js
-> table/on_files_change.js
-> table/on_folder_change.js
-> table/add_file_with_dedup.js
-> table/render.js
-> zip/list_zip_files.js
```
Flask отдаёт страницу и готовый bundle. Все выбранные файлы остаются в памяти браузера;
сервер не принимает содержимое файлов.
## Модель данных
Каждый узел дерева имеет поля:
| Поле | Тип | Назначение |
|---|---|---|
| `id` | `string` | Уникальный идентификатор для DOM-кнопок и поиска. |
| `kind` | `file`, `folder`, `zip` | Тип узла. `file` является leaf. |
| `name` | `string` | Имя отображения в дереве. |
| `path` | `string` | Полный логический путь внутри выбора/архива. |
| `file` | `File \| null` | Исходный или созданный из ZIP browser File для leaf. |
| `children` | `Array` | Дочерние узлы группы. |
| `expanded` | `boolean` | Видимость дочерних строк в таблице. |
Внутреннее состояние picker-а:
| Поле | Назначение |
|---|---|
| `nodes` | Корневые узлы дерева. |
| `fileKeys` | Set ключей `path + NUL + size` для дедупликации. |
| `busy` | Защита от параллельной обработки async change-событий. |
## `upload/frontend/table`
### `add_file_with_dedup.js`
| Функция | Вход | Выход | Побочные эффекты |
|---|---|---|---|
| `addFileWithDedup(state, fileNode)` | state и корневой file/folder/zip-узел | `boolean` | Фильтрует дубли и пустые группы, добавляет узел и ключи в state. |
Дедупликация выполняется рекурсивно. Один и тот же путь с другим размером не
считается дублем.
### `esc.js`
| Функция | Вход | Выход |
|---|---|---|
| `esc(value)` | Любое значение | HTML-экранированная строка |
Используется перед `innerHTML`, `data-path`, `aria-label` и текстом имени.
### `fs.js`
| Функция | Вход | Выход |
|---|---|---|
| `fs(bytes)` | Размер в байтах | Строка в `B`, `KB` или `MB` |
### `rebase_tree.js`
| Функция | Вход | Выход | Побочные эффекты |
|---|---|---|---|
| `rebaseTree(root, prefix)` | Дерево и префикс папки | То же дерево | Рекурсивно меняет `path`; для leaf сохраняет базовое имя `File.name`. |
Модуль является единственной реализацией rebasing для ZIP внутри выбранной папки
и для nested ZIP.
### `on_files_change.js`
| Функция | Вход | Выход | Назначение |
|---|---|---|---|
| `addFiles(state, cfg, files, elements)` | state, `allowedExt`, FileList/Array, DOM | `Promise<void>` | Фильтрует обычные файлы, раскрывает ZIP, вызывает render. |
| `onFilesChange(state, cfg, elements)` | state, config, DOM | async callback | Блокирует повторные события через `busy` и вызывает `addFiles`. |
Ошибка отдельного ZIP пропускается и передаётся в `cfg.onError(error, file)`, если
callback задан. `finally` обязательно освобождает `busy`.
### `on_folder_change.js`
| Функция | Вход | Выход | Назначение |
|---|---|---|---|
| `onFolderChange(state, cfg, elements)` | state, `allowedExt`, folder input и DOM | async callback | Строит дерево по `webkitRelativePath`, раскрывает ZIP и рендерит. |
Корень пути становится folder-узлом. Вложенные папки создаются по сегментам
пути, затем корень проходит общую дедупликацию.
### `render.js`
| Функция | Вход | Выход | Назначение |
|---|---|---|---|
| `renderNode(node, depth, cfg)` | Узел, глубина и render config | HTML | Рекурсивно создаёт строки дерева. Внутренняя функция. |
| `render(state, elements, cfg)` | state, DOM и render config | `void` | Перерисовывает таблицу и счётчик. |
| `findNode(nodes, id)` | Массив узлов и id | `{node, nodes}` или `null` | Ищет узел и его родительский массив. |
| `flattenFiles(nodes, result)` | Дерево и аккумулятор | Массив leaf-файлов | Преобразует дерево для API приложения. |
`render` считает все leaf-файлы, включая свернутые группы. `renderNode` показывает
дочерние строки только при `expanded === true`.
## `upload/frontend/zip`
### `list_zip_files.js`
| Функция | Вход | Выход | Назначение |
|---|---|---|---|
| `extensionAllowed(name, allowedExt)` | Имя и расширения | boolean | Проверяет расширение. Внутренняя. |
| `makeFile(data, name)` | Байты и путь | `File` | Создаёт browser File с базовым именем. Внутренняя. |
| `safeEntryParts(entryName)` | Сырой ZIP-путь | сегменты или null | Отбрасывает traversal и опасные пути. Внутренняя. |
| `node(kind, name, path, children, file)` | Метаданные узла | node | Создаёт узел. Внутренняя. |
| `addPath(root, parts, fileNode)` | Дерево, сегменты, узел | `void` | Создаёт папки и вставляет узел. Внутренняя. |
| `listEntries(data, zipName, allowedExt, depth)` | ZIP bytes и контекст | `Promise<node|null>` | Рекурсивно обрабатывает ZIP. Внутренняя. |
| `listZipFiles(file, allowedExt, limits)` | browser File, расширения и limits | `Promise<node|null>` | Публичная точка входа ZIP-парсера. |
Безопасность: отклоняются абсолютные пути, backslash, пустые сегменты, `.` и
`..`; применяются лимиты `maxEntries`, `maxTotalBytes`, `maxEntryBytes` и
`maxDepth` с безопасными значениями по умолчанию. ZIP без разрешённых leaf-файлов
возвращает `null` и не создаёт пустую группу.
## `upload/frontend/index.js`
`initFilePicker({ mount, allowedExt, labels, layout, limits, onChange, onError })`
строит весь DOM внутри HTMLElement или CSS-селектора `mount`, подключает inputs,
таблицу и обработчики. Возвращаемый API: `pickFiles`, `pickFolder`, `addFiles`,
`getFiles`, `remove`, `clear`, `render`, `destroy`. `getFiles` и `onChange`
возвращают копии массива leaf-файлов. `destroy` снимает слушатели и очищает mount.
IIFE-бандл экспортирует этот API как `FilePicker.initFilePicker`; ESM-бандл
экспортирует функцию напрямую. `fflate` включён в оба bundle, поэтому глобальный
vendor script не требуется.
## Flask и HTML
### `site/app.py`
| Маршрут/объект | Вход | Выход |
|---|---|---|
| `CONFIG` | `config.json` | JSON-конфигурация picker |
| `VERSION` | константа | Версия страницы |
| `index()` / `/` | HTTP GET | HTML с version и config |
| `health()` / `/health` | HTTP GET | `ok`, HTTP 200 |
| `upload_frontend(filename)` | HTTP GET и относительный путь | Совместимый маршрут для исходных frontend-модулей |
| `file_picker_dist(filename)` | HTTP GET и имя bundle | Bundle из `dist` |
### `site/templates/index.html`
Шаблон создаёт mount, подключает IIFE bundle с query-параметром версии для
сброса browser cache и вызывает `FilePicker.initFilePicker`.
Vendor `site/static/vendor/fflate.min.js` не документируется построчно: это
внешняя библиотека, используемая как готовый runtime dependency.