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:
@@ -238,6 +238,7 @@ function renderSystemInfo(si) {
|
||||
const photos = si.photos || {};
|
||||
const files = si.files || {};
|
||||
const uploads = si.uploads || {};
|
||||
const storage = si.storage || {};
|
||||
const disk = si.disk;
|
||||
|
||||
let tablesHtml = '';
|
||||
@@ -283,6 +284,13 @@ function renderSystemInfo(si) {
|
||||
<div class="sys-item"><span class="sys-label">Файлов</span><span class="sys-value">${uploads.count || 0}</span></div>
|
||||
<div class="sys-item"><span class="sys-label">Размер</span><span class="sys-value">${esc(uploads.size || '0 B')}</span></div>
|
||||
</div>
|
||||
<div class="sys-card">
|
||||
<div class="sys-title">Хранилище</div>
|
||||
<div class="sys-item"><span class="sys-label">Драйвер</span><span class="sys-value">${storage.driver === 's3' ? 'S3' : 'Локальное'}</span></div>
|
||||
${storage.bucket ? `<div class="sys-item"><span class="sys-label">Бакет</span><span class="sys-value">${esc(storage.bucket)}${storage.prefix ? '/' + esc(storage.prefix) : ''}</span></div>` : ''}
|
||||
<div class="sys-item"><span class="sys-label">Объектов</span><span class="sys-value">${storage.count || 0}</span></div>
|
||||
<div class="sys-item"><span class="sys-label">Размер</span><span class="sys-value">${esc(storage.size || '0 B')}</span></div>
|
||||
</div>
|
||||
${diskHtml || '<div class="sys-card"><div class="sys-title">Диск</div><div class="sys-meta">Недоступно</div></div>'}
|
||||
`;
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"full": "e4704c2dc69361318d9aab490128f30c4d875bda",
|
||||
"short": "e4704c2",
|
||||
"date": "2026-09-24T11:21:58+03:00"
|
||||
"full": "8bf54fb95d8d338f35bd6e628e4e21100a5bf371",
|
||||
"short": "8bf54fb",
|
||||
"date": "2026-09-24T13:11:31+03:00"
|
||||
}
|
||||
Reference in New Issue
Block a user