Add "FTP"

2026-04-12 20:46:39 +00:00
parent 7197ae52e4
commit 4787a1a761

64
FTP.md Normal file

@@ -0,0 +1,64 @@
# FTP/SFTP Галерея
## Настройка
В settings.json → `ftpConfig`:
```json
{
"protocol": "sftp", // "ftp" или "sftp"
"host": "212.113.122.5",
"port": 22, // 21 для FTP, 22 для SFTP
"user": "reaspekt",
"password": "...", // маскируется в API-ответе
"remotePath": "public/newsletter_2026",
"baseUrl": "https://email-files.vipavenue.ru/newsletter_2026"
}
```
Библиотеки: `basic-ftp` (FTP), `ssh2-sftp-client` (SFTP).
## Загрузка картинок
### Из конструктора (drag-and-drop)
1. Перетаскиваешь картинку на поле `src` блока
2. `handleImageDrop()` → если FTP настроен и у письма есть дата → загружает на FTP
3. Иначе → загружает локально (`/api/upload-image`)
4. URL подставляется в поле блока
### Из галереи (модал)
1. Кнопка FTP-галереи на поле картинки
2. Открывается модал с миниатюрами из папки `{remotePath}/{дата-письма}/`
3. Можно выбрать существующую картинку или загрузить новую
### Пакетная загрузка
- `<input type="file" multiple>` — выбор нескольких файлов
- Загрузка последовательная (`for...of` с await), не параллельная
- `event.target.value = ''` после загрузки — для повторного выбора тех же файлов
- Прогресс: `ftpUploadProgress` / `ftpUploadPercent`
## Безопасность FTP
- **Path traversal**: `folder` проверяется на `..` и абсолютные пути
- **Имя файла**: `fileName.replace(/[^a-zA-Z0-9а-яА-ЯёЁ_-]/g, '_')`
- **Макс размер**: 20 МБ (base64 в body)
- **Типы файлов**: png, jpg, gif, webp
## Автонумерация картинок
Функция `applyAutoImageNumbers()`:
1. Берёт `imageBaseUrl` и `imageExt` из настроек
2. Проходит по всем блокам с пустыми полями `src`
3. Присваивает последовательные номера: `{baseUrl}image__01_.png`, `image__02_.png`, ...
## Кэширование галереи
`ftpGalleryCache` — Map `{folder: files[]}` в памяти клиента. При загрузке новой картинки — файл добавляется в кэш без повторного запроса к FTP.
## Модал галереи
- Размер: `max-width: 1100px`
- Сетка миниатюр: `max-height: 55vh`, миниатюры `height: 100px`
- Клик на миниатюру → копирует URL в поле блока
- Кнопка удаления на каждой миниатюре (с подтверждением)
- Кнопка "Обновить" для перезагрузки списка