feat(storage): S3-совместимое хранилище файлов (SeaweedFS/MinIO) и миграция uploads
- storage.js: абстракция хранилища с драйверами local и s3 (AWS SDK v3), ключи объектов совпадают с текущими путями /uploads/<файл>, поэтому схема БД и URL не меняются - docker-compose.yml: сервис s3 (SeaweedFS, том s3-data, API только на loopback), переменные S3_*/STORAGE_*, restart unless-stopped для app и db - docker-compose.minio.yml: оверрайд S3-сервиса на MinIO (образ из своего зеркала) - server.js/worker.js: чтение и запись файлов только через storage (отдача /uploads/*, миниатюры, share-файлы, zip-отчёты, enhance/apply/rollback, photo-worker), автосоздание бакета, глобальная персистенция загрузок multer - бэкап/восстановление и scripts/backup.sh, restore.sh — через scripts/storage-sync.js - scripts/migrate-to-s3.js: идемпотентная миграция uploads/ в бакет (--dry-run, --verify-only, --delete-local) - админка: блок «Хранилище» в системной информации - .env.example, README.md, AGENTS.md: описание драйверов, переменных и перехода на S3
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
- **Share-ссылки** — публичные страницы-витрины с выбором группы / воспитанника / диапазона дат
|
||||
- **Дашборд** — статистика, активные группы, активность за 14 дней, последние записи, топ воспитанников
|
||||
- **Резервное копирование** — экспорт/импорт полного дампа (БД + файлы) в `tar.gz`
|
||||
- **Хранилище файлов** — локальный каталог `uploads/` или S3-совместимый сервис (`s3`: SeaweedFS, либо MinIO через оверрайд), перенос файлов скриптом миграции
|
||||
- **Настройки** — тексты футера, анти-спам интервал
|
||||
- **Публикация через Tailscale** — приложение открывается по постоянному адресу `https://whatido.<tailnet>.ts.net` без проброса портов, внешнего IP и reverse-proxy
|
||||
|
||||
@@ -20,6 +21,7 @@
|
||||
- Node.js + Express
|
||||
- PostgreSQL (pg)
|
||||
- Multer (загрузка файлов), Tar (бэкапы)
|
||||
- S3-совместимое хранилище (AWS SDK v3): сервис `s3` (SeaweedFS / MinIO)
|
||||
- Lucide (иконки UI)
|
||||
- Docker / Docker Compose
|
||||
- Tailscale (Serve / Funnel) — публикация по HTTPS
|
||||
@@ -244,7 +246,71 @@ docker compose exec cloudflared wg show # есть handshake — VPN подн
|
||||
|
||||
## Хранилище файлов
|
||||
|
||||
Загруженные фото и файлы хранятся в каталоге `uploads/` на хосте и монтируются в контейнер (`./uploads:/app/uploads`). Это даёт прямой доступ к данным из-под хост-системы. Данные БД хранятся в именованном томе `pgdata`.
|
||||
По умолчанию загруженные фото и файлы хранятся в каталоге `uploads/` на хосте и монтируются в контейнер (`./uploads:/app/uploads`) — это драйвер `local`. Данные БД хранятся в именованном томе `pgdata`.
|
||||
|
||||
Дополнительно поддерживается **S3-совместимое хранилище** (сервис `s3` в compose, драйвер `s3`). Все обращения к файлам идут через приложение: URL (`/uploads/...`, `/uploads/thumb/...`, `/api/files/:token`, share-ссылки) и записи в БД (`/uploads/<файл>`) не меняются, поэтому переключение драйвера не требует миграции данных в БД.
|
||||
|
||||
### Сервис `s3`
|
||||
|
||||
```bash
|
||||
docker compose up -d s3 # поднимает S3-хранилище (том s3-data)
|
||||
```
|
||||
|
||||
- **По умолчанию — SeaweedFS** (`chrislusf/seaweedfs`): свободный S3-сервер; API слушает `127.0.0.1:9000` на хосте и `s3:9000` внутри compose-сети.
|
||||
- **MinIO**: официальные свободные образы `minio/minio` удалены из Docker Hub, поэтому MinIO подключается через оверрайд и образ из доступного вам зеркала:
|
||||
|
||||
```bash
|
||||
S3_IMAGE=<ваш-образ-minio> docker compose -f docker-compose.yml -f docker-compose.minio.yml up -d s3
|
||||
```
|
||||
|
||||
Бакет создаётся автоматически при старте приложения (`ensureBucket`) или скриптом миграции. Анонимный доступ к API хранилища закрыт: порт `9000` не публикуется наружу (только loopback), доступ к файлам остаётся через приложение с его аутентификацией и rate limit.
|
||||
|
||||
### Переменные окружения
|
||||
|
||||
| Переменная | По умолчанию | Назначение |
|
||||
|---|---|---|
|
||||
| `STORAGE_DRIVER` | `local` | `local` — файлы в `uploads/`, `s3` — объекты в бакете |
|
||||
| `S3_ENDPOINT` | `http://s3:9000` | Адрес S3 API внутри compose-сети |
|
||||
| `S3_BUCKET` | `whatido` | Бакет для объектов |
|
||||
| `S3_ACCESS_KEY` / `S3_SECRET_KEY` | `whatido` / — | Доступ к хранилищу (для MinIO это root-пользователь) |
|
||||
| `S3_FORCE_PATH_STYLE` | `1` | Path-style адресация (нужна MinIO/SeaweedFS) |
|
||||
| `S3_PREFIX` | — | Необязательный префикс ключей внутри бакета |
|
||||
| `STORAGE_LOCAL_FALLBACK` | `1` | Читать локальный файл, если объекта в S3 ещё нет |
|
||||
| `STORAGE_KEEP_LOCAL` | `0` | Оставлять локальную копию после выгрузки в S3 |
|
||||
| `STORAGE_CACHE_MAX_AGE_HOURS` | `168` | Срок жизни локального кэша оригиналов (для sharp/миниатюр) |
|
||||
|
||||
### Переход на S3 (миграция)
|
||||
|
||||
Порядок не прерывает работу: файлы сначала копируются в бакет, локальные остаются на месте и продолжают использоваться.
|
||||
|
||||
```bash
|
||||
# 1) поднять хранилище
|
||||
docker compose up -d s3
|
||||
|
||||
# 2) предпросмотр и загрузка файлов в бакет (идемпотентно, по размеру объекта)
|
||||
docker compose exec -T app node scripts/migrate-to-s3.js --dry-run
|
||||
docker compose exec -T app node scripts/migrate-to-s3.js
|
||||
|
||||
# 3) проверить, что все объекты на месте (ничего не меняет)
|
||||
docker compose exec -T app node scripts/migrate-to-s3.js --verify-only
|
||||
```
|
||||
|
||||
Дальше включить драйвер `s3` и перезапустить приложение:
|
||||
|
||||
```bash
|
||||
# в .env: STORAGE_DRIVER=s3
|
||||
docker compose up -d app
|
||||
```
|
||||
|
||||
Новые загрузки уходят в бакет (локальная копия удаляется, если `STORAGE_KEEP_LOCAL=0`), старые файлы ещё читаются из `uploads/` благодаря `STORAGE_LOCAL_FALLBACK=1`. Когда всё проверено — удалите локальные копии:
|
||||
|
||||
```bash
|
||||
docker compose exec -T app node scripts/migrate-to-s3.js --delete-local
|
||||
```
|
||||
|
||||
Откат в любой момент: `STORAGE_DRIVER=local` + `docker compose up -d app` (пока локальные копии не удалены).
|
||||
|
||||
Объём и состав хранилища видны в админке: Настройки → Системная информация (блок «Хранилище»).
|
||||
|
||||
## Бэкапы
|
||||
|
||||
@@ -264,6 +330,8 @@ docker compose exec cloudflared wg show # есть handshake — VPN подн
|
||||
|
||||
Форматы не взаимозаменяемы: скриптовый архив содержит `db.sql.gz` + `_uploads/` (перенос на другой хост через `scripts/restore.sh`), а веб-архив из админки — `data.json` + `uploads/` (кнопка «Восстановить»). Если в админку загрузить скриптовый архив, сервер вернёт подсказку, какой инструмент использовать.
|
||||
|
||||
Файлы попадают в бэкап из активного хранилища: при `STORAGE_DRIVER=s3` админ-бэкап и `scripts/backup.sh` выгружают объекты из бакета (`scripts/storage-sync.js export`), а восстановление загружает их обратно (`scripts/storage-sync.js import`). Миниатюры (`.thumbs`) в архив не включаются — они пересоздаются по запросу.
|
||||
|
||||
## Безопасность
|
||||
|
||||
- **Пароль администратора** обязателен (`ADMIN_PASSWORD`); фолбэка на `admin` нет.
|
||||
@@ -299,17 +367,19 @@ docker compose exec cloudflared wg show # есть handshake — VPN подн
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
├── docker-compose.yml # сервисы: app + db + tailscale
|
||||
├── docker-compose.yml # сервисы: app + db + s3 (+ опционально tailscale)
|
||||
├── docker-compose.minio.yml # оверрайд: S3-сервис на MinIO вместо SeaweedFS
|
||||
├── .env.example # шаблон переменных окружения
|
||||
├── Dockerfile # сборка образа (Node 20, генерация TLS-сертификата)
|
||||
├── server.js # Express-приложение
|
||||
├── worker.js # фоновый worker AI-проверки записей
|
||||
├── storage.js # абстракция хранилища: драйверы local и s3
|
||||
├── worker.js # фоновый worker AI-проверки и ИИ-улучшения фото
|
||||
├── certs/ # cert.pem приложения (монтируется в tailscale, в git не хранится)
|
||||
├── db/
|
||||
│ ├── init.sql # схема при первом запуске
|
||||
│ └── migration.sql # миграции существующей БД
|
||||
├── public/ # статика (HTML/CSS/JS админки и витрин)
|
||||
├── scripts/ # вспомогательные скрипты
|
||||
├── uploads/ # загруженные файлы (bind-монт, вне git)
|
||||
├── scripts/ # вспомогательные скрипты (backup/restore/deploy, migrate-to-s3, storage-sync)
|
||||
├── uploads/ # локальные файлы и кэш миниатюр (bind-монт, вне git)
|
||||
└── backups/ # локальные бэкапы
|
||||
```
|
||||
Reference in New Issue
Block a user