--- 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]`.