From f04426617cb7caf1f50eaa66655f74ea1279e847 Mon Sep 17 00:00:00 2001 From: vitya Date: Fri, 12 Jun 2026 14:50:16 +0300 Subject: [PATCH] =?UTF-8?q?feat(galleries):=20migrate=20pilorama98=20galle?= =?UTF-8?q?ry=20originals=20Local-disk=20=E2=86=92=20S3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snolla galleries 404 (imgproxy "Source unreachable"): legacy storageClient "galleries" = MoreThenCms Local storage class (App_Data\galleries\), never in S3 — products were migrated, galleries weren't. Path A (user pick): new bucket `galleries`, 301 pilorama98 originals (77 MiB) rclone'd from RUVDS IIS → s3://galleries/37e6…/.jpg verbatim. snolla code unchanged (storageClient=bucket is the working convention). imgproxy smoke from books-vds: real obj 200 image/webp, fake guid 404. Other sites unmigrated (scope). Inbox sent to victor/snolla. - close [migrate-gallery-originals-to-s3] (scope pilorama98) - NEW .wiki concept galleries-storage-class-local-not-s3 - fix stale minio-imgproxy-on-vds (pipeline on books-vds since 2026-06-08) Co-Authored-By: Claude Opus 4.8 (1M context) --- .tasks/STATUS.md | 21 +++++-- .../galleries-storage-class-local-not-s3.md | 58 +++++++++++++++++++ .wiki/concepts/minio-imgproxy-on-vds.md | 6 +- .wiki/log.md | 2 + 4 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 .wiki/concepts/galleries-storage-class-local-not-s3.md diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index 2b77bc6..68dc831 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -1,4 +1,5 @@ # Admin Task Board +_Updated: 2026-06-12 (4) — 🟢 **[migrate-gallery-originals-to-s3] closed (scope pilorama98).** Галереи snolla не отдавались (imgproxy 404) т.к. в legacy `storageClient="galleries"` = тип **Local** (диск `App_Data\galleries\`), НЕ S3 — оригиналы никогда туда не заливались (продукты — заливались, bucket `pilorama98`). Развилка A/B закрыта фактами MinIO → **A**: создан bucket `galleries`, залиты 301 файл (77 MiB) pilorama98 (siteId `37e67fc4…`) с RUVDS IIS через rclone → `s3://galleries//.jpg` verbatim (== ровно то, что строит `middleware/galleries.js:47`). Код snolla НЕ менялся. Verify: count+size == source. Smoke с books-vds: реальный объект imgproxy → 200 webp, fake → 404. Уточнение инфра: `imgproxy.kzntsv.site` с 08.06 на **books-vds** (стек 29/30), не на windows-host — `minio-imgproxy-on-vds.md` был stale. Inbox victor/snolla отправлен._ _Updated: 2026-06-12 (3) — 🟢 **bookva-es snapshot done** (не отложен). Portainer stack 37 пересоздан с `path.repo=/snapshots`+bind (PUT API, том цел, epz/products уцелели), repo `kreknin` зарегистрирован, snapshot-блок в `run.sh`. Поймана коллизия basename `snapshots` (оба ES-каталога одноимённы → сливались в один dest-репо, порча обоих) → источник bookva-es = родительский `/usr/docker/bookva-es`. Verified: `BOOKS-VDS backup OK 10m28s`, на kreknin раздельно `snapshots/`(slovo index-41) + `bookva-es/snapshots/`(index-4). Таска `bookva-es-snapshot-repo` 🟢 closed._ _Updated: 2026-06-12 (2) — 🟢 **bookva-* backup gap закрыт.** bookva tenant (db/mongo/es/minio, поднят 26.05) не бэкапился — books-скрипт написан 25.05 до bookva, покрытие не расширили (висело wiki-follow-up #2). Добавлены bookva-db (тот же root pw), bookva-mongo (no-auth), bookva-minio (raw volume) в `books-vds-backup-daily-kreknin/run.sh`; деплой == репо (`.bak-pre-bookva`). Verified green: `BOOKS-VDS backup OK 10m42s`, артефакты на kreknin (bookva-mariadb 257M, bookva-mongo 3.2M, bookva-minio 1.4G). bookva-es отложен → `bookva-es-snapshot-repo` (покрыт slovo-снапшотом, индексы идентичны). Правило в память: новый stateful → бэкап в том же коммите._ _Updated: 2026-06-12 — 🔴→🟢 **MSSQL backup incident + estate backup audit.** VDS daily backup падал с 12.06 (line 108, exit 1): mssql-блок (added 11.06) использовал `WITH ... INIT` → упирался в компрессованный media-header майских `.bak` (Express не пишет COMPRESSION) → молча падал первым cron-запуском. **5 боевых CMS-баз 3 недели без offsite-копии.** Fix: `INIT`→`FORMAT`, прогон verified (MoreThenCms 910M/StayerCalculator 528M/StayerPrice 39M/TireService 4.5M/stostayer 990M на kreknin). Репо-копия `run.sh` синхронизирована (mssql-блока не было). Concept: `.wiki/concepts/mssql-on-vds.md` gotcha#2. **Estate backup-аудит** → `.wiki/concepts/backup-inventory-2026-06.md`: ✅ openwrt UCI backup настроен (cron 03:30 → kreknin, restricted forced-command key); ⚪ заведена `kreknin-self-backup` (#1 SPOF, на потом); nl-vds x-ui.db — user: не нужен; windows-host stale script — user: забыть._ @@ -362,7 +363,9 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## ⚪ [migrate-gallery-originals-to-s3] — Галерейные (blog/gallery) картинки не отдаются ни на dev (imgproxy 404 "Source image is unreachable"), ни на prod (legacy-ресайз 404). Диагностика проведена в victor/snolla против живого pilorama98 + кода MoreThenCms. Нужен доступ к БОЕВОМУ серверу (локальный диск IIS + MinIO/S3), которого нет у snolla-агента — поэтому задача на admin. +## 🟢 [migrate-gallery-originals-to-s3] — closed 2026-06-12 (scope: pilorama98) — путь A: создан bucket `galleries`, залиты 301 файл (77 MiB) legacy-оригиналов pilorama98 с RUVDS IIS `App_Data\galleries\37e6…` → `s3://galleries/37e67fc44b9e4c06a522a26a73bac9b0/.jpg`. Smoke с books-vds: реальный объект через imgproxy → 200 image/webp 32102 B, fake guid → 404. Код snolla НЕ менялся (storageClient=имя бакета — рабочая конвенция). Inbox victor/snolla отправлен. Развилка A/B закрыта фактами MinIO: galleries в legacy = Local storage class (диск), никогда не в S3; A выбран user'ом. + +Галерейные (blog/gallery) картинки не отдаются ни на dev (imgproxy 404 "Source image is unreachable"), ни на prod (legacy-ресайз 404). Диагностика проведена в victor/snolla против живого pilorama98 + кода MoreThenCms. Нужен доступ к БОЕВОМУ серверу (локальный диск IIS + MinIO/S3), которого нет у snolla-агента — поэтому задача на admin. ## Диагноз (доказан, не гипотеза) @@ -412,11 +415,19 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite **weight:** needs-human **notify:** victor/snolla -**Status:** ready -**Where I stopped:** (not started) — диагностика выполнена в victor/snolla, см. тело -**Next action:** 1) В MinIO/S3 проверить: существует ли бакет `galleries` и есть ли в нём объекты `/`; сверить с тем, как лежат рабочие продукты (`s3://pilorama98/products/...`). 2) Залить legacy-оригиналы `App_Data\galleries\\*` в целевой бакет под ключи `/`. 3) Re-smoke imgproxy на одной картинке. +**Status:** done (closed 2026-06-12, scope pilorama98) +**Where I stopped:** мигрировано. MinIO (books-vds стек 30, за `imgproxy.kzntsv.site` с 08.06): bucket `galleries` создан, prefix `37e67fc44b9e4c06a522a26a73bac9b0/` залит (301×.jpg, 77.0 MiB, rclone RUVDS→minio.kzntsv.site, count+size == source). Smoke OK с books-vds (200/404). +**Next action:** — (хвосты в inbox victor/snolla: остальные siteId-папки галерей не мигрированы по скоупу; legacy prod-ресайз остаётся 404 by design). **Branch:** master -**Notify:** victor/snolla +**Notify:** victor/snolla — ✅ inbox `.claude-inbox/2026-06-12T11-47-33Z-admin.md` + +**Closure note (2026-06-12):** +- **Развилка A/B закрыта фактами:** в Web.config legacy `` storageClient `galleries` = тип **Local** (`GalleriesStorage`, диск `App_Data\galleries\`), НЕ S3 → галереи никогда не были в S3 (продукты — да, bucket `pilorama98/products`). Корень доказан. **A** (отдельный bucket `galleries`, key `/`) выбран user'ом → нулевой код-фикс snolla. +- **MinIO state:** bucket'ы до миграции — artmone/books/imgproxytest/manuals/modulair/modules/obsidian/ozon/pilorama98/strapi/test/upload; `galleries` отсутствовал; `pilorama98` = только `products/` (шард по hex, без siteId). Зафиксировано. +- **Залив:** RUVDS IIS `C:\sites\snolla\App_Data\galleries\37e67fc44b9e4c06a522a26a73bac9b0\` (301 файл, dir-name уже lowercase 32-hex == ownerId snolla) → `s3://galleries/37e6…/.jpg` verbatim. rclone v1.74.2 inline s3-remote (env-var config, без записи cred). Verify: 301 объект, 77.012 MiB == source. +- **Smoke (с books-vds, мимо LAN-DNS-перехвата воркстейшна):** подписанный imgproxy-URL реального объекта → 200 image/webp 32102 B; несуществующий guid → 404 (негативный контроль). +- **Wiki-drift зафиксирован:** `minio-imgproxy-on-vds.md` устарел (говорит pipeline на windows-host; фактически imgproxy на books-vds с 08.06). Добавлен concept `galleries-storage-class-local-not-s3`. + --- diff --git a/.wiki/concepts/galleries-storage-class-local-not-s3.md b/.wiki/concepts/galleries-storage-class-local-not-s3.md new file mode 100644 index 0000000..2cb2f64 --- /dev/null +++ b/.wiki/concepts/galleries-storage-class-local-not-s3.md @@ -0,0 +1,58 @@ +--- +title: MoreThenCms galleries — Local storage class, never in S3 (snolla imgproxy 404 root cause) +status: resolved for pilorama98 (2026-06-12); other sites unmigrated +tags: [cms, snolla, morethencms, minio, imgproxy, s3, galleries, migration] +related: [[minio-imgproxy-on-vds]], [[../entities/books-vds]], [[../entities/ruvds-iis-host]] +--- + +# Galleries storage class = Local (disk), not S3 + +## Root cause + +В MoreThenCms `` (Web.config) каждый `storageClient` — это **алиас конфига**, не имя бакета. Алиас `galleries` маппится на тип **`MoreThenCms.FileStorage.Local.GalleriesStorage`** → файлы на локальном диске IIS: + +``` +C:\sites\snolla\App_Data\galleries\\. +``` + +То же у `assets`, `images`, `scripts`, `stylesheets`, `watermarks` — все **Local**. Продукты (e-commerce) — другой механизм, **реально в S3** (bucket `pilorama98`, prefix `products/`, шард по hex, без siteId). + +Переписка **snolla** (Node) наивно строит источник как `s3://${image.storageClient}/${siteId}/${storageFilename}` (`packages/snolla/middleware/galleries.js:47`, `lib/viewModels/index.js:289`) — т.е. ждёт галереи в S3 в бакете с именем `galleries`. Но их там никогда не было → imgproxy «Source image is unreachable» (404). Это НЕ баг билдера URL и НЕ поломка ресайза — просто **данные не мигрированы** из Local-диска в S3. + +Симптом-близнец на legacy-prod (`pilorama98.ru/galleries//images//.jpg`): `original/*` = 200 (читает диск), любой ресайз = 404 — отдельная мёртвая ветка legacy-ресайзера (System.Drawing/image-cache). Целевая архитектура = imgproxy, legacy-ресайз не чинится. + +## Fix recipe (путь A — выбран) + +storageClient как **имя бакета** = рабочая конвенция snolla. Заливаем Local-диск в одноимённый bucket, ключи verbatim — код snolla не меняется: + +```powershell +# на RUVDS IIS (rclone v1.74.2 уже стоит). env-var remote, без записи cred в rclone.conf +$env:RCLONE_CONFIG_MIN_TYPE='s3'; $env:RCLONE_CONFIG_MIN_PROVIDER='Minio' +$env:RCLONE_CONFIG_MIN_ACCESS_KEY_ID=''; $env:RCLONE_CONFIG_MIN_SECRET_ACCESS_KEY='' +$env:RCLONE_CONFIG_MIN_ENDPOINT='https://minio.kzntsv.site' # books-vds S3 API за traefik +& rclone mkdir min:galleries +& rclone copy "C:\sites\snolla\App_Data\galleries\" min:galleries/ --transfers 8 +& rclone size min:galleries/ # сверить count+size с источником +``` + +- `` папки на диске уже lowercase 32-hex == `ownerId` snolla (`String(site.id).replace(-).toLowerCase()`) → ключи совпадают 1:1, трансформаций не надо. +- Креды MinIO: `pass books-vds/full-env` (контейнер `minio`, env `MINIO_ACCESS_KEY`/`MINIO_SECRET_KEY`). imgproxy key/salt — env контейнера `imgproxy`. + +## Smoke (важно: мимо LAN-DNS воркстейшна) + +`imgproxy.kzntsv.site` с воркстейшна резолвится в `127.0.0.1` (LAN-перехват CMS-доменов) — смоук гнать **с самого books-vds** или `--resolve …:89.253.255.133`: + +```bash +curl -sk --resolve imgproxy.kzntsv.site:443:127.0.0.1 'https://imgproxy.kzntsv.site//fill/W/H/ce/0//.jpg)>.webp' +# подпись: base64url( HMAC_SHA256(KEY_bin, SALT_bin + path) ), KEY/SALT = hex из env imgproxy +``` + +Реальный объект → 200 image/webp; несуществующий guid → 404 (негативный контроль). + +## Status / scope + +- **pilorama98** (siteId `37e67fc44b9e4c06a522a26a73bac9b0`): мигрирован 2026-06-12 — 301 файл / 77 MiB, imgproxy 200 verified. +- **Остальные ~40 siteId-папок** в `App_Data\galleries` — НЕ мигрированы (часть пустые). Bucket `galleries` shared; заполнен только префикс pilorama98. Другие сайты в snolla → 404 пока не зальёшь (регрессии нет). +- Если поднимаются другие Local-классы (`assets`/`images`/…) в snolla через imgproxy — их ждёт та же дыра, тот же рецепт. + +См. задачу `.tasks/STATUS.md` → `[migrate-gallery-originals-to-s3]`. diff --git a/.wiki/concepts/minio-imgproxy-on-vds.md b/.wiki/concepts/minio-imgproxy-on-vds.md index d2604e9..43e5e43 100644 --- a/.wiki/concepts/minio-imgproxy-on-vds.md +++ b/.wiki/concepts/minio-imgproxy-on-vds.md @@ -1,12 +1,14 @@ --- title: MinIO + imgproxy stack on vds-kzntsv -status: live as standby — CMS image pipeline остаётся на windows-host (closed-source DLL hardcoded URL) +status: STANDBY/unused for CMS — живой CMS image pipeline переехал на books-vds 2026-06-08 (см. ниже) tags: [vds, minio, imgproxy, migration, ops] -related: [[vds-kzntsv]], [[recovery-architecture-snapshot]], [[mssql-on-vds]] +related: [[vds-kzntsv]], [[recovery-architecture-snapshot]], [[mssql-on-vds]], [[../entities/books-vds]], [[galleries-storage-class-local-not-s3]] --- # MinIO + imgproxy on vds-kzntsv +> ⚠ **Stale для живого pipeline (2026-06-08).** `imgproxy.kzntsv.site` (hardcoded в `MoreThenCms.Modules.Imgproxy.dll`) теперь смотрит на **[[../entities/books-vds]]** (стек 29 imgproxy + стек 30 minio, bucket'ы `pilorama98`, `galleries`, …), а windows-recovery-host декоммишнен. Этот VDS-kzntsv стек — standby/неиспользуемый для CMS. Option C ниже («остаётся на windows-host») историчен. Миграция галерей и рецепт smoke — [[galleries-storage-class-local-not-s3]]. + Запущен 2026-05-22 как часть `minio-imgproxy-vds-migration`. Заодно **upgrade MinIO** `RELEASE.2020-07-13` (5 лет, CVE-куча) → `RELEASE.2025-09-07`. ## Architecture diff --git a/.wiki/log.md b/.wiki/log.md index b6d43b4..b8731ce 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -69,3 +69,5 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations). ## [2026-06-06] decision | nl-vds-3xui: +3 classic Shadowsocks inbounds (ports 32031/32/33) for Outline-app, per-key revocable; protocol e2e-tested OK, RF reachability unverified (SS DPI-blocked in RF); creds in pass; discovered undocumented VLESS:13027. ## [2026-06-10] ingest | concepts/mcp-init-resilience + +## [2026-06-12] migrate | galleries pilorama98 → S3. NEW concepts/galleries-storage-class-local-not-s3.md (root: storageClient `galleries` = Local disk class, never in S3; snolla ждёт s3://galleries// → 404). Путь A: bucket `galleries` создан, 301 файл (77 MiB) с RUVDS IIS залиты verbatim, imgproxy 200 verified с books-vds. UPDATE concepts/minio-imgproxy-on-vds.md (stale-баннер: pipeline на books-vds с 08.06, не windows-host). Task migrate-gallery-originals-to-s3 🟢.