deploy(cms-s3): S3 FileStorage provider LIVE on RUVDS — admin↔MinIO split-brain closed
Разбор 500 на /admin/assets/<owner>/delete → корень ACL (app pool RX-only на App_Data после scp-миграции) → полная развязка хранилища CMS: - S3 FileStorage провайдер (MoreThenCms.FileStorage.S3) построен (координация с интерн-сессией) и раскатан LIVE на прод RUVDS: 6 контентных классов web.config → S3/MinIO, кэши остались Local. Смок 7 тенантов 200/301, 0×500, S3-read byte-parity. Upload-гоча: UseChunkEncoding=false (MinIO ⊥ AWSSDK aws-chunked). - Весь локальный контент RUVDS → MinIO (galleries 5.3G, themes 2.7G, maxMind); imageCache-блоат (2.1G) выпилен из бакета themes. - stostayer.old определён как stale-копия (не мигрировать). Новый concept: snolla-admin-appdata-acl-500-after-scp-migration. Трекеры: morethencms-s3-filestorage-provider (LIVE), reconcile-local-assets-to-minio (done). Rollback: web.config.bak-pre-s3-2026-07-03 на хосте. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,47 +1,42 @@
|
||||
---
|
||||
_last_updated_: 2026-07-02T09:45:00Z
|
||||
session_id: 2026-07-02-labtools-pro-and-ru-vds-cutover
|
||||
_last_updated_: 2026-07-03T10:45:00Z
|
||||
session_id: 2026-07-03-ruvds-minio-s3-provider-cutover
|
||||
---
|
||||
|
||||
# Next session handoff
|
||||
|
||||
## Итог сессии — весь snolla-тираж выехал на VDS ✅
|
||||
`emspb.ru` + `labtools.pro` + `labtools.ru` — все LIVE на [[vds-kzntsv]] (`89.253.255.94`) за traefik. RUVDS IIS (`80.64.31.36`) — rollback, живой.
|
||||
## Итог сессии — закрыт split-brain админка↔фронт: RUVDS теперь пишет/читает ассеты из MinIO
|
||||
|
||||
## Recent commits (⚠️ 4 ЛОКАЛЬНЫХ, НЕ ЗАПУШЕНЫ — автопуш не выдавался)
|
||||
- `a9f4f3a6` deploy(labtools): 43e28ba LIVE — cutover labtools.ru done
|
||||
- `ed99f222` deploy(labtools-pro): 7bd9fae LIVE — cutover labtools.pro done
|
||||
- `531d7f0f` deploy(labtools-pro): staging GREEN, stack 19
|
||||
- `253f427a` decommission(emspb): сняты emspb.ru/www SNI-биндинги с RUVDS IIS
|
||||
→ **Первое действие след. сессии если разрешат: `git push`** (4 коммита ждут).
|
||||
Началось с «почему `/admin/assets/<owner>/delete` → 500», раскрутилось в полную развязку хранилища. Всё LIVE и зелёное.
|
||||
|
||||
## Состояние треков
|
||||
| Трек | Статус | Стек | Образ |
|
||||
|---|---|---|---|
|
||||
| `[emspb-web-vds-deploy]` | 🟢 closed (LIVE + RUVDS decommissioned) | 18 | emspb:b6e361a |
|
||||
| `[labtools-pro-web-vds-deploy]` | 🟢 closed (LIVE) | 19 | labtools-pro:7bd9fae |
|
||||
| `[labtools-web-vds-deploy]` | 🟢 closed (LIVE) | 17 | labtools:43e28ba |
|
||||
### 1. Разбор 500 + ACL-фикс ✅
|
||||
`POST /admin/assets/<ownerId>/delete` → 500 = app pool `IIS AppPool\snolla` имел на `C:\sites\snolla\App_Data` только **RX** после scp-миграции 23.05 → `File.Delete` кидал `UnauthorizedAccessException`. NRE-версии исключены проверкой строк `Folders`/`Files` в БД `mssql.kzntsv.site`. Fix: `icacls App_Data /grant "IIS AppPool\snolla:(OI)(CI)(M)" /T` (44293 файла). Concept: `.wiki/concepts/snolla-admin-appdata-acl-500-after-scp-migration.md`.
|
||||
|
||||
Рунбуки: `.wiki/concepts/{emspb,labtools,labtools.pro}-vds-deploy-runbook.md`.
|
||||
### 2. S3 FileStorage-провайдер — построен и LIVE на прод RUVDS ✅ (главное)
|
||||
Провайдера в кодовой базе не было (только Local/Azure). **Координировал разработку с интерн-сессией `MoreThenCms`** (inbox-переписка, вся в `.claude-inbox/.read/`): написал ТЗ → выверил 4 расхождения по ground-truth (физ-объекты MinIO + snolla `storage.js`) → нашёл upload-баг.
|
||||
- **Ключ-конвенция:** bucket=класс, key `<prefix>/<file>` без ведущего слэша, id lowercase **dash-free**. assets→`assets`, galleries→`galleries` (плоско, без `/images`), images/scripts(`js`!)/stylesheets(`css`)/watermarks→bucket `themes`.
|
||||
- **Upload-gotcha (важно):** MinIO отвергает AWSSDK 3.7 aws-chunked → провайдер ставит **`UseChunkEncoding=false`** в PutObjectRequest. `DisablePayloadSigning` НЕ помогает.
|
||||
- **Валидация:** провайдер чистый (без SnollaHost) → гонял standalone-пробой в PowerShell (грузил DLL + зависимости из `stostayer.old\bin`, `$type::new($cfgDict)`), IIS/elevation не нужны. Read/key/byte-parity + self-test put→exists→download→delete — всё ✅.
|
||||
- **Прод-cutover RUVDS 2026-07-03:** 3 DLL в `C:\sites\snolla\bin` (бандл: `~/projects/MoreThenCms/MoreThenCms.FileStorage.S3/deploy/`); `web.config` `<fileStorageClients>` — 6 контентных классов → S3 (креды в конфиге, ACL SYSTEM+Admins), кэши (imageCache/uploadCache/contentCache/_imageCache-Azure) оставлены **Local**. Смок: 7 тенантов 200/301, **0× 500**; S3-read вживую (theme CSS byte-parity).
|
||||
- **⚠️ Новая runtime-зависимость:** RUVDS теперь зависит от `minio.kzntsv.site` (books-vds) для отдачи ВСЕХ ассетов/тем/галерей. Раньше был self-contained (локальный диск). Если MinIO/books-vds ляжет — у RUVDS-тенантов пропадут картинки/css. Учитывать.
|
||||
- **ROLLBACK (мгновенный):** restore `C:\sites\snolla\web.config.bak-pre-s3-2026-07-03` (или IIS-снапшот `pre-s3-cutover-2026-07-03`) + `Restart-WebAppPool snolla` → вернётся Local, MinIO не трогается.
|
||||
- Провайдер закрыт с их стороны (wiki: `filestorage-s3-provider`, `minio-awssdk-chunk-encoding-gotcha` — в репо MoreThenCms). Прогер на приёме, если вылезет edge (theme-subpath / редкие content-type).
|
||||
|
||||
## DNS-факты (на будущее)
|
||||
- Зоны на РАЗНЫХ провайдерах: `emspb.ru`/`labtools.pro` — **reg.ru** (`ns1/ns2.reg.ru`); `labtools.ru` — **Yandex DNS** (`dns1/dns2.yandex.net`). Проверять авторитетный NS того провайдера.
|
||||
- labtools.ru siteUrl = **non-www** (`https://labtools.ru`, sitemap host без www); emspb/labtools.pro = www (R3). Разные тенанты — разный canonical.
|
||||
### 3. Весь локальный контент RUVDS → MinIO ✅
|
||||
rclone на RUVDS-хосте (gap-fill `--ignore-existing`): galleries 5.3 ГБ (301→9984), themes 2.7 ГБ (вкл. watermarks под `themes/<id>/watermarks/`), maxMind→bucket `maxmind`. assets уже были (13.06). Parity чистая (rclone check, все matching). imageCache-блоат (17135 obj / 2.1 ГБ) **выпилен** из бакета `themes` после cutover'а — реальный контент цел (10864 / 671 MiB).
|
||||
- Гоча: `Start-Process`-детач на Win Server Core НЕ переживает закрытие ssh → длинные операции гнать attached ssh + bash `run_in_background`.
|
||||
|
||||
## Открытые follow-up (не блокеры, по решению оператора)
|
||||
- **RUVDS-биндинги labtools.ru/www + labtools.pro/www** ещё на IIS (rollback). Снять как для emspb (`Remove-WebBinding` + backup config) — когда оператор скажет. Ключ: `~/.ssh/ruvds-iis-migration`, `Administrator@80.64.31.36`. См. [[../.wiki/entities/ruvds-iis-host]].
|
||||
- **`emspb.snolla.com`** — НЕ мигрирован (DNS→IIS), биндинг оставлен намеренно. Отдельная миграция если понадобится.
|
||||
- **www.labtools.ru** — на момент wrap ещё split-brain (публичный кэш RUVDS, TTL до 6ч); авторитетные оба уже VDS, LE-cert на www выпущен. Доедет сам. Спот-чек внешним резолвером позже.
|
||||
- **Косметика labtools.pro:** стартовый баннер лога = `labtools.ru (snolla)` (константа app-name из скаффолда). У workshop, не блокер.
|
||||
### 4. stostayer.old — stale, НЕ мигрировать
|
||||
`C:\sites\stostayer.old` (каталог `stostayer`, `old.stostayer.ru`) — старая копия. Текущий stostayer = тенант в каталоге `MoreThenCms` (SiteId `9cf0a8e5`), уже в MinIO. Его локальные ассеты в MinIO лить нельзя (stale + коллизия owner-ID). Юзер: «неросайт, делай что хочешь». Использовался только как источник DLL-зависимостей для пробы.
|
||||
|
||||
## Guards
|
||||
- RUVDS IIS НЕ выводить целиком (общий catch-all, ещё держит ~22 хоста + rollback).
|
||||
- НЕ добавлять `Host(...)` в traefik ДО подтверждения флипа на **всех** авторитетных NS зоны (LE rate-limit).
|
||||
- Стеки на VDS — только через Portainer (env сохранять при PUT, pullImage=false для label-only правок).
|
||||
## Отложено (решения юзера, зафиксированы)
|
||||
- **Локальный IIS на [[windows-recovery-host]]** под мультитенантную админку — поднимать, когда сайты уедут с RUVDS (сам PC жив, серверная роль была декоммишнута — обновить статус в `.wiki/entities/windows-recovery-host` при подъёме).
|
||||
- **Миграция всех сайтов на snolla** — отдельный длинный трек (как labtools/pilonuxt).
|
||||
|
||||
## Инфра-заметки
|
||||
- Сборка образов — на VDS из `git archive <sha>` (обход traefik-499), push с VDS. VERDACCIO_CI_TOKEN + PORTAINER_API_KEY в `pass vds-kzntsv/full-env`.
|
||||
- Env всех snolla-тенантов идентичен (8 секретов) — при новом стеке брать verbatim из соседнего (`GET /api/stacks/<id>|.Env`).
|
||||
## ⚠️ Git — закоммитить+запушить (с разрешения)
|
||||
Прошлый «4 unpushed» — **закрыт** (origin/master..HEAD пусто). НО за эту сессию куча **незакоммиченных** doc-правок: `.tasks/STATUS.md`, `NEXT_SESSION.md`, 2 новых task-файла (`morethencms-s3-filestorage-provider`, `reconcile-local-assets-to-minio`), `.wiki/` (новый concept `snolla-admin-appdata-acl-500-after-scp-migration` + правки `galleries-storage-class-local-not-s3`/`index`/`log`). Коммитнуть+запушить в начале след. сессии, если юзер разрешит.
|
||||
|
||||
## Memory updates за сессию
|
||||
- Дополнена `operator-dns-flip-verify-domain-before-cutover`: проверять АВТОРИТЕТНЫЙ NS (не публичный кэш), гейт = ВСЕ auth-NS согласны (dns1/dns2 расходятся mid-propagation), идентифицировать провайдера зоны.
|
||||
## Ключевые ссылки
|
||||
- Трекеры: `.tasks/morethencms-s3-filestorage-provider.md` (🟢 LIVE), `.tasks/reconcile-local-assets-to-minio.md` (🟢 done).
|
||||
- Концепты: `.wiki/concepts/snolla-admin-appdata-acl-500-after-scp-migration.md`, `galleries-storage-class-local-not-s3.md`.
|
||||
- Хост RUVDS: `pass ruvds-iis/full-env`, ssh key `~/.ssh/ruvds-iis-migration`, я=Administrator (elevation там есть). MinIO creds `pass minio-vds/full-env`, alias endpoint `https://minio.kzntsv.site`.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Admin Task Board
|
||||
_Updated: 2026-07-03 — 🟡 **заведена [morethencms-s3-filestorage-provider].** Разведка «почему /admin/assets/.../delete → 500» на RUVDS IIS: корень = app pool `IIS AppPool\snolla` имел на `App_Data` только RX после scp-миграции → `File.Delete` UnauthorizedAccessException (не MinIO, провайдер `assets`=Local; NRE-версии исключены проверкой строк Folders/Files в БД). Fix применён: `icacls App_Data /grant "IIS AppPool\snolla:(OI)(CI)(M)" /T` (44293 файла, verified). Попутно заменён `labtools-price.pdf` в ОБА хранилища (MinIO ETag→6426ddd0 + локалка, MD5 сверены). Настоящее закрытие split-brain (админка=Local vs фронт=MinIO) → новая таска на S3-провайдер `MoreThenCms.FileStorage.S3` (name approved): ТЗ доставлено в инбокс MoreThenCms, объём=все контентные классы, 1 MinIO, deploy на локальный IIS windows-recovery-host→боевой. DNS-аудит: инфра-хосты не перехватываются (нет 127.0.0.1). Concepts: snolla-admin-appdata-acl-500 + galleries-storage-class-local-not-s3._
|
||||
_Updated: 2026-06-29 (4) — 🟢 **[pilonuxt-deploy-sitemap-taxonomy] closed.** pilonuxt `6b9ae63` LIVE (стек 16) — bump-only sitemap taxonomy: content-api 0.15→0.16, core 0.2→0.4 (taxonomy enum, прокси не менялся). **Tree-check `.output` 5/5** (content-api 0.16.0, core 0.4.0, data 0.9.1 1×, snolla 0, btoa 0). Push словил **499 на .output** → ретрай прошёл (digest `226584ce…`). Acceptance 4/4: `/sitemap.xml` индекс 7 записей с `store-taxonomy-1`; `/sitemap-store-taxonomy-1.xml`→200 57 url абсолютные (35 categories+22 tags, vendors 0=пусто в CMS); v1 цел (products-1/pages-1 200); `taxonomy-999`→404. Регресс `/`/`/catalog` 200, stderr чист. Откат: стек 16→`42f9d64`. Ack→victor/pilorama98.ru._
|
||||
_Updated: 2026-06-29 (3) — 🟢 **[pilonuxt-deploy-sitemap] closed.** pilonuxt `42f9d64` LIVE (стек 16) — sitemap.xml через content-api@0.15.0 (+core@0.2.0). Build из монорепы (форс schema-gen против прод-MSSQL). **Tree-check `.output` 5/5:** content-api 0.15.0, core 0.2.0, data 0.9.1 (1×), snolla 0 (7 grep-хитов = комменты-провенансы, 0 import), btoa 0. Push **напрямую** (499 не случился). Acceptance 3/3: `/sitemap.xml`→200 `<sitemapindex>` 6 источников; `/sitemap-pages-1.xml`→200 `<urlset>` 70 url все абсолютные; `/sitemap-store-products-999.xml`→404. Регресс цел (robots/`/`/`/catalog` 200), stderr чист. Откат: стек 16→`cf2bba2`. Ack→victor/pilorama98.ru._
|
||||
_Updated: 2026-06-29 (2) — 🟢 **[deploy-pilonuxt-forms-api-drop-snolla] closed.** pilonuxt `cf2bba2` LIVE (стек 16) — ADR-0010: формы на `@snollajs/forms-api`, `@snollajs/snolla` дропнут целиком, core@0.1.1. Перед перекатом **tree-check `.output` 4/4** (0× snolla, 0× btoa, 1× data@0.9.1, core 0.1.1 + content-api 0.14 + forms-api 0.1.0). Smoke зелёный: SSR `/`+`/catalog`→200, robots/yandex из БД (ADR-0009 не регресс), форма `?path=/checkout` пустой→422 JSON / `/nope`→404 (valid НЕ слал). Гоча: 422 только с `Accept: application/json`. Notify victor/pilorama98.ru (монореп-inbox)._
|
||||
@@ -39,6 +40,15 @@ _Updated: 2026-05-26 — заведена `bookva-tenant-cutover-prep` ⚪ (7-st
|
||||
|
||||
_Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user decision — 9/24 hostnames live на RUVDS, source IIS оставлен running. `migrate-elasticsearch-to-books-vds` 🟢 closed ранее сегодня. 16 IIS hostnames + LE renewal pipeline + decommission — descoped в Closure note, не отдельные tracker tasks.)_
|
||||
|
||||
## 🟡 [morethencms-s3-filestorage-provider] — S3-провайдер FileStorage для MoreThenCms (MinIO), закрывает split-brain админка↔фронт
|
||||
**Status:** 🟡 in-progress — ТЗ (name `MoreThenCms.FileStorage.S3` одобрен) доставлено в инбокс MoreThenCms 2026-07-03; жду сборку от прогера. Owner-split: код — прогер; координация+deploy+приёмка — admin.
|
||||
**Where I stopped:** Разведка 500 на `/admin/assets/.../delete` → корень RX-only ACL на `App_Data` (пофикшен `icacls …:(M) /T`, 44293 файла). `labtools-price.pdf` заменён в MinIO+локалке (MD5 сверены). ТЗ на провайдер: `~/projects/MoreThenCms/.claude-inbox/2026-07-03T06-40-09Z-admin.md`. Решения: все контентные классы, 1 MinIO (`minio.kzntsv.site`), ключ без ведущего слэша (parity), deploy → локальный IIS windows-recovery-host → боевой. DNS-аудит чист.
|
||||
**Next action:** дождаться `MoreThenCms.FileStorage.S3.dll` → deploy на локальный IIS → сквозняк (upload→объект в MinIO→imgproxy 200, byte-parity) → deploy боевой + переключить `<fileStorageClients>`. См. [morethencms-s3-filestorage-provider.md](morethencms-s3-filestorage-provider.md).
|
||||
**Branch:** n/a (admin ops + external code)
|
||||
<!-- created-by: vitya@.admin-exec / 2026-07-03 / trigger: user-план «сайты→snolla, админка→локальный IIS» -->
|
||||
|
||||
---
|
||||
|
||||
## 🟡 [fix-nl-vds-reality-pq-dest] — Reality-инбаунд 443 на NL VDS (213.176.64.253): server-side done, ждёт client-side
|
||||
**Status:** 🟡 server-side fix + cred-rotation применены и проверены 2026-06-05; остался один client-side шаг (user меняет SNI в v2rayN) + подтверждение → закрыть.
|
||||
**Where I stopped:** ✅ dest `www.intel.com`→`www.microsoft.com` на инбаунде 443 (x-ui.db + restart), сквозной тоннель через реальный :443 (SNI microsoft) = HTTP 204; 32030 жив. ✅ Креды ротированы (root SSH pass / panel user+pass / panel secret-JWT — оригиналы светились в плейнтексте), всё в `pass nl-vds-3xui/full-env`, БД-бэкап на сервере. Root cause: ML-DSA-65 (PQ) × intel/Akamai (HRR), разбор в `.wiki/concepts/reality-pq-mldsa65-dest-incompatibility.md`.
|
||||
|
||||
50
.tasks/morethencms-s3-filestorage-provider.md
Normal file
50
.tasks/morethencms-s3-filestorage-provider.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# [morethencms-s3-filestorage-provider] — S3-провайдер FileStorage для MoreThenCms (MinIO)
|
||||
|
||||
**Status:** 🟢 LIVE на прод RUVDS (2026-07-03). Split-brain закрыт: боевой catch-all `C:\sites\snolla` теперь читает/пишет ассеты/темы/галереи из MinIO. Локальный IIS (windows-recovery-host) — отложен до ухода сайтов с RUVDS (решение user).
|
||||
|
||||
### Прод-cutover RUVDS (2026-07-03)
|
||||
- Backup: `web.config.bak-pre-s3-2026-07-03` + IIS-снапшот `pre-s3-cutover-2026-07-03`. Rollback = restore + recycle (Local вернётся, MinIO не трогается).
|
||||
- 3 DLL (S3+AWSSDK.*) → `C:\sites\snolla\bin`. web.config `<fileStorageClients>`: 6 контентных классов (assets/galleries/images/scripts/stylesheets/watermarks) → S3, креды в конфиге (ACL SYSTEM+Admins); кэши (imageCache/uploadCache/contentCache/_imageCache-Azure) оставлены Local. Правка — точечный regex, UTF-8/BOM сохранён, XML валиден (10 записей).
|
||||
- Pre-flight: RUVDS→minio.kzntsv.site:443 = TCP+HTTPS 200. Egress ок.
|
||||
- Smoke GREEN: 7 тенантов 200/301 (0× 500); S3-read вживую — theme CSS `16ba5cb8…/css/lato.css` → 200/9239b/text/css, byte-parity с MinIO. Write-механика — self-test цикл (ранее).
|
||||
- imageCache-прун бакета `themes` (2.1 ГБ мусора) — фоном bfb8wy94n.
|
||||
**Валидация (standalone-проба, IIS/elevation не нужны):** Read/key/byte-parity ✅ + full self-test `put→exists→download(MD5==)→delete→exists=false` ✅ после upload-фикса. Upload-фикс: **`UseChunkEncoding=false`** в `PutObjectRequest` (MinIO отвергает AWSSDK 3.7 aws-chunked `STREAMING-AWS4-HMAC-SHA256-PAYLOAD`; `DisablePayloadSigning` НЕ помогает). 17/17 юнитов + интеграция. `_selftest/`-мусора нет.
|
||||
|
||||
### Метод валидации (важно — IIS/elevation НЕ нужны)
|
||||
Провайдер чистый (AWSSDK + базовый FileStorageClient, без SnollaHost/Autofac) → грузится standalone в PowerShell: загрузить 3 DLL из `deploy/` + зависимости из `C:\sites\stostayer.old\bin` (резолвер с guard от рекурсии), инстанцировать `AssetsStorage` через `$type::new($cfgDict)`, гонять против real MinIO. Проба: scratchpad `s3-provider-probe.ps1` + `s3-upload-diag.ps1`. stostayer.old НЕ трогается (только его DLL читаются в чужой процесс). Self-test пишет в throwaway `assets/_selftest/` (чистится).
|
||||
**Owner-split:** код — прогер (проект MoreThenCms); координация + deploy + приёмка — admin (я).
|
||||
|
||||
### Ключ-конвенция (ground-truthed 2026-07-03, финал)
|
||||
Провайдер = калька с **Local** (не Azure — Azure в бою не гонялся), ключи по snolla-фронту `packages/core/lib/services/storage.js` (`key = ownerId.toLowerCase()+'/'+storageFilename`).
|
||||
- **assets**: bucket `assets`, ключ `<ownerId:N>/<file>` — ownerId **без дефисов** (админка отдаёт `ToString("N")`, объекты в MinIO без дефисов). НЕ вставлять дефисы.
|
||||
- **themes** (css/images/**js**/watermarks): единый bucket `themes`, ключ `<themeId:N>/{css,images,js,watermarks}/<file>`. scripts=`js` (не `/scripts`!). watermarks НЕ отдельный бакет — они в `App_Data\themes\<themeId>\watermarks\` (Local `WatermarksStorage`), 11 файлов вкл. tandemmebel.ru.
|
||||
- **galleries**: bucket `galleries`, ключ `<siteId:N>/<file>` **плоско** (без `/images`).
|
||||
- Azure separator-баг (`prefix+fileUri` без `/`) прогер поймал → `Trim('/')+"/"+fileUri`.
|
||||
- Configuration net461→net462 (`OpeItcLoc03/MoreThenCms.Configuration 3e79ff2`) — build-only, на прод НЕ едет (шипим только 3 DLL).
|
||||
- Приёмка на локальном IIS: **key-parity + byte-parity** sweep (ключ совпал с существующими + MD5==).
|
||||
|
||||
## Зачем
|
||||
Админка MoreThenCms пишет ассеты на локальный диск IIS (`App_Data`, провайдер `FileStorage.Local`), а боевой фронт snolla читает из MinIO → split-brain (правка в админке не видна на сайте). Плюс отдельный симптом — 500 на delete из-за RX-only ACL (пофикшен 2026-07-03 выдачей Modify, но это лечит только delete, не устраняет раздвоение). Настоящее закрытие — посадить админку на тот же MinIO через S3-провайдер, которого в кодовой базе нет (есть только `Local` и `Azure`).
|
||||
|
||||
## Решения (приняты)
|
||||
- Новая сборка `MoreThenCms.FileStorage.S3` — калька с `MoreThenCms.FileStorage.Azure`, backend `AWSSDK.S3` против MinIO (`ForcePathStyle=true`).
|
||||
- Объём: **все** контентные классы (Assets/Galleries/Images/Scripts/Stylesheets/Watermarks). Кэши (image/upload/content) остаются Local.
|
||||
- Один MinIO на всех: `minio.kzntsv.site` (books-vds, 89.253.255.133), path-style. Bucket = имя класса, ключ = `<prefix>/<file>` **без ведущего слэша** (parity с уже мигрированными объектами).
|
||||
- Deploy target админки: локальный IIS на **windows-recovery-host** (репрпоуз PC; серверная роль была декоммишнута, сам PC жив). DNS-аудит 2026-07-03: инфра-хосты `*.kzntsv.site` резолвятся в реальные IP, перехвата `127.0.0.1` НЕТ — костыль в конфиг не нужен.
|
||||
|
||||
## Where I stopped
|
||||
ТЗ (полное, под новичка, с таблицей префиксов и выделенной граблей «ведущий слэш в ключе S3») лежит в `~/projects/MoreThenCms/.claude-inbox/2026-07-03T06-40-09Z-admin.md`.
|
||||
|
||||
## Next action
|
||||
Дождаться `MoreThenCms.FileStorage.S3.dll` от прогера → развернуть на локальный IIS (windows-recovery-host) → сквозняк: upload из админки → объект в MinIO с корректным ключом+Content-Type → фронт через imgproxy отдаёт 200 → byte-parity (ETag/MD5). Затем deploy на боевой + переключить `<fileStorageClients>` секцию, секреты прокинуть из окружения (не хардкод).
|
||||
|
||||
## Пайплайн
|
||||
ТЗ ✅ → сборка ⏳ (прогер) → deploy-local ⏳ → сквозняк-приёмка ⏳ → deploy-remote ⏳ → close split-brain.
|
||||
|
||||
## Контекст
|
||||
- `.wiki/concepts/snolla-admin-appdata-acl-500-after-scp-migration.md` (500 + split-brain + fix)
|
||||
- `.wiki/concepts/galleries-storage-class-local-not-s3.md` (Local-класс, миграция в S3, префиксы)
|
||||
- Абстракция: `MoreThenCms/FileStorage/FileStorageClient.cs` + `Azure/AzureCloudStorage.cs` (образец)
|
||||
|
||||
**Branch:** n/a (admin ops + external code)
|
||||
<!-- created-by: vitya@.admin-exec / 2026-07-03 / trigger: user-план «сайты→snolla, админка→локальный IIS», нужен S3-провайдер -->
|
||||
28
.tasks/reconcile-local-assets-to-minio.md
Normal file
28
.tasks/reconcile-local-assets-to-minio.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# [reconcile-local-assets-to-minio] — залить всё локальное хранилище RUVDS в MinIO
|
||||
|
||||
**Status:** 🟢 done 2026-07-03 — залито и сверено. Parity чистая: assets 4750/4750 (0 diff checksum), galleries 9984/9984 (долито 9683), themes 27999/27999, maxMind 2/2. Job `b6qb8e10q` exit 0, DONE 10:31:30Z.
|
||||
**Follow-up — ✅ closed 2026-07-03:** per-theme `<themeId>/imageCache/*` (легаси resize-кэш) выпилен из бакета `themes` после S3-cutover'а (17135 объектов / 2.1 ГБ → 0). Реальный контент тем цел (10864 / 671 MiB, счётчик 1:1). Бакет `themes` теперь только реальный контент.
|
||||
|
||||
## Задача
|
||||
Перетащить всё контентное локальное хранилище `C:\sites\snolla\App_Data` (RUVDS IIS) в MinIO (`minio.kzntsv.site`, books-vds). Для уже мигрированных живых сайтов — только verify (все файлы на месте), не перезаписывать. Готовит estate к плану «все сайты → snolla, админка → локальный IIS + S3-провайдер».
|
||||
|
||||
## Разведка (2026-07-03)
|
||||
| класс | диск (файлы/МиБ) | MinIO до | действие |
|
||||
|---|---|---|---|
|
||||
| assets | 4750 / 215.5 | 4750 / 216 | verify (`rclone check --one-way --checksum`) — по кол-ву сходится, ловим drift |
|
||||
| galleries | 9984 / 5522 | 301 (pilorama98) | gap-fill `--ignore-existing` (~9683 файла / 5.4 ГБ) |
|
||||
| themes | 27999 / 2847 | 317 (3 темы из 87) | gap-fill `--ignore-existing` (~2.8 ГБ) |
|
||||
| maxMind | 2 / 34.6 | — | ✅ залито в bucket `maxmind` (по требованию user) verify 0 diff |
|
||||
| uploadCache/contentCache/searchIndexes | кэши/индексы | — | НЕ мигрируем |
|
||||
|
||||
Конвенция ключей — **verbatim** диск→бакет (bucket = имя класса): assets `<ownerId>/<file>`, galleries `<siteId>/…`, themes `<themeId>/{css,images,js}/…`. Проверено на живых объектах.
|
||||
|
||||
## Метод
|
||||
rclone на хосте (env-var remote `min:`), режим gap-fill `--ignore-existing` (живые объекты не трогаются). Логи: `C:\Windows\Temp\mig-*.log`, маркер `mig-DONE.txt`. Скрипт: scratchpad `mig-assets-to-minio.ps1` (+ `mig-maxmind.ps1`).
|
||||
⚠️ Гоча: `Start-Process`-детач на Windows Server Core НЕ переживает закрытие ssh-сессии (процесс убивается с сессией) → запускать attached (ssh держит сессию) либо через Scheduled Task.
|
||||
|
||||
## Where I stopped / Next action
|
||||
Ждать завершения bash-job `b6qb8e10q` → прочитать `mig-galleries-check.log` / `mig-themes-check.log` (post-verify one-way parity) + `mig-assets-check.log` (drift). Файлы «есть в обоих, но различаются» (drift после 13.06) — НЕ перезаписаны, вынести списком user'у (по живым сайтам перезапись — его решение). Финальная сверка counts/size диск vs MinIO. Затем close.
|
||||
|
||||
**Branch:** n/a (admin ops)
|
||||
<!-- created-by: vitya@.admin-exec / 2026-07-03 / trigger: user-задача "перетащить все ассеты RUVDS в minio" -->
|
||||
@@ -61,4 +61,6 @@ curl -sk --resolve imgproxy.kzntsv.site:443:127.0.0.1 'https://imgproxy.kzntsv.s
|
||||
|
||||
⚠️ На стороне snolla `content-api/routes/assets.js` — пустой stub: объекты в S3 есть и отдаются через imgproxy, но inline-картинки контента не отрендерятся, пока роут не реализован (код-таска snolla).
|
||||
|
||||
⚠️ **Админка на S3 НЕ переключена** (класс `assets` в её `<fileStorageClients>` всё ещё Local) → правки ассета через админку RUVDS меняют только локальный `App_Data`, а боевой app читает MinIO — расходятся. Плюс отдельный баг прав на этой же локалке ронял админский delete/upload в 500. Оба — в [[snolla-admin-appdata-acl-500-after-scp-migration]] (там же рецепт ручного зеркалирования в оба хранилища).
|
||||
|
||||
См. задачи `.tasks/STATUS.md` → `[migrate-gallery-originals-to-s3]`, `[migrate-assets-originals-to-s3]`.
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: MoreThenCms admin (RUVDS) — App_Data RX-only после scp-миграции → HTTP 500 на любой записи (delete/upload)
|
||||
status: fixed 2026-07-03 (Modify выдан на весь App_Data)
|
||||
tags: [cms, snolla, morethencms, iis, ruvds, acl, permissions, migration, assets, 500]
|
||||
related: [[galleries-storage-class-local-not-s3]], [[../entities/ruvds-iis-host]], [[../sources/iis-migration-to-ruvds-2026-05-23]]
|
||||
updated: 2026-07-03
|
||||
---
|
||||
|
||||
# App_Data RX-only → 500 на записи из админки
|
||||
|
||||
## Симптом
|
||||
|
||||
`POST /admin/assets/<ownerId>/delete?path=/&filename=<file>` → **HTTP 500**. В IIS-логе
|
||||
(`C:\inetpub\logs\LogFiles\W3SVC1\`, время в **UTC**; сервер MSK=UTC+3) — `sc-status 500
|
||||
sc-substatus 0 sc-win32-status 0` = чистое managed-исключение, не IIS-уровень. В Windows
|
||||
**Application Event Log пусто**, файловых логов CMS нет → ASP.NET глотает исключение default-handler'ом.
|
||||
GET того же URL → 404 (delete только `[HttpPost]`).
|
||||
|
||||
## Root cause — ACL, не код и не MinIO
|
||||
|
||||
App pool `snolla` = **ApplicationPoolIdentity** → процесс бежит как `IIS AppPool\snolla`.
|
||||
После scp-миграции 2026-05-23 bootstrap выдал этому аккаунту на дереве контента **только
|
||||
`(RX)` (Read&Execute)**, без Modify:
|
||||
|
||||
```
|
||||
C:\sites\snolla\App_Data IIS APPPOOL\snolla:(OI)(CI)(RX) ← только чтение
|
||||
...\labtools-price.pdf IIS APPPOOL\snolla:(I)(RX)
|
||||
```
|
||||
|
||||
Провайдер `assets` в `<fileStorageClients>` = `MoreThenCms.FileStorage.Local.AssetsStorage`
|
||||
(**локальный диск**, `App_Data\assets\<ownerId:N>\<storageFilename>`; `basePath=""`) — см.
|
||||
[[galleries-storage-class-local-not-s3]]. Код-путь удаления:
|
||||
`AssetsController.Delete` → `AssetsService.DeleteAsset` (`MoreThenCms/Assets/Services/AssetsService.cs:84`):
|
||||
`FileExists` (RX-чтение ок → true) → **`DeleteFile` = `File.Delete`** под identity с RX →
|
||||
`UnauthorizedAccessException` → необработанное → **500**. DB-delete (строкой ниже) даже не
|
||||
достигается. `RX` не содержит право DELETE, и на родителе нет `DC` (delete-child) → удалять нечем.
|
||||
`BUILTIN\Users:(AD)(WD)` не спасает — виртуальный аккаунт пула в `IIS_IUSRS`, не в `Users`; и AD/WD — создание, не удаление.
|
||||
|
||||
**Дифдиагностика (чем исключены NRE-версии):** в БД `mssql.kzntsv.site` подтверждены обе строки —
|
||||
`Folders` (root-папка owner'а, `Path='/'`, `Discriminator=AssetsFolder`) и `Files`
|
||||
(`StorageClient='assets'`, `StorageFilename=<file>`), поэтому `GetFolderByPath(...).Id` и
|
||||
`asset.Folder` не-null, выполнение доходит именно до `File.Delete`. Единственная падающая операция — физическое удаление.
|
||||
|
||||
**Класс проблемы шире delete:** RX-only убивает ВСЕ Local-записи из админки — upload ассетов,
|
||||
`contentCache`/`uploadCache`/`imageCache`, галереи, watermarks. Всё это классы Local в `App_Data`.
|
||||
|
||||
## Fix (применён 2026-07-03)
|
||||
|
||||
```powershell
|
||||
# на RUVDS IIS (80.64.31.36), от Administrator
|
||||
icacls "C:\sites\snolla\App_Data" /grant "IIS AppPool\snolla:(OI)(CI)(M)" /T /C
|
||||
```
|
||||
|
||||
`(M)`=Modify (включает DELETE+WRITE), `(OI)(CI)`=наследование на файлы+подпапки, `/T`=протянуть
|
||||
на существующие. Результат: **44293 файла, 0 ошибок**. Проверка: на целевом файле теперь
|
||||
`IIS APPPOOL\snolla:(I)(M)`. Рестарт пула не нужен — ACL применяется на лету. Живой delete-тест
|
||||
намеренно НЕ гонялся (удалил бы реальный ассет); доказательство = ACL на точном таргете + протрассированный код-путь.
|
||||
|
||||
## Split-brain: админка ≠ боевой сайт
|
||||
|
||||
Админка MoreThenCms живёт на **RUVDS IIS** и читает/пишет **локальный** `App_Data`. Боевой
|
||||
snolla-app (labtools.ru и др. на [[../entities/vds-kzntsv]]) отдаёт ассеты/галереи из **MinIO**
|
||||
(`minio.kzntsv.site` = books-vds, bucket `assets/<ownerId>/<file>`), см.
|
||||
[[galleries-storage-class-local-not-s3]]. Классы мигрированы в S3 (галереи 12.06, assets 13.06),
|
||||
но **админка на S3 не переключена** → любая правка ассета через админку меняет только локалку IIS
|
||||
и НЕ отражается на боевом сайте (и наоборот). Пока админка не repointed на S3 — правки зеркалить руками в оба места.
|
||||
|
||||
Пример (2026-07-03): замена `labtools-price.pdf` (ownerId `d375c419…`) — новый файл залит
|
||||
**и** в MinIO (`mc cp --attr Content-Type=application/pdf … snm/assets/d375c419…/labtools-price.pdf`,
|
||||
ETag→`6426ddd0…`) **и** на локальный диск IIS (scp, MD5 сверен). Оба слоя = байт-в-байт с исходником.
|
||||
|
||||
## TODO (настоящее закрытие split-brain)
|
||||
|
||||
Переключить админский `<fileStorageClients>`-класс `assets` (и `galleries` и пр.) на S3-провайдер,
|
||||
указывающий на `minio.kzntsv.site` — тогда админка и боевой app работают с одним хранилищем,
|
||||
ручное зеркалирование и ACL на App_Data перестают быть нужны. Это код/конфиг-таска на стороне MoreThenCms.
|
||||
@@ -35,6 +35,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
|
||||
- [iis-migration-2026-05-19-postmortem](concepts/iis-migration-2026-05-19-postmortem.md) — post-mortem миграции CMS на нативный IIS, 2026-05-19
|
||||
- [minio-imgproxy-on-vds](concepts/minio-imgproxy-on-vds.md) — MinIO+imgproxy stack на VDS, миграция с upgrade 2020→2025 + DNS swap pending
|
||||
- [morethencms-null-settingsdata-https-502](concepts/morethencms-null-settingsdata-https-502.md) — MoreThenCms тенант с `Sites.SettingsData=NULL` → 502 только на HTTPS (нет `httpSecure` → KeyNotFound); fix = UPDATE + recycle. maljarka 2026-06-08
|
||||
- [snolla-admin-appdata-acl-500-after-scp-migration](concepts/snolla-admin-appdata-acl-500-after-scp-migration.md) — админский delete/upload ассета → 500: app pool имел на `App_Data` только RX после scp-миграции → `File.Delete` UnauthorizedAccessException. Fix = icacls Modify. + split-brain: админка пишет Local, боевой app читает MinIO
|
||||
- [mssql-container-data-restore](concepts/mssql-container-data-restore.md) — MSSQL контейнер с восстановленными production data — паттерн
|
||||
- [mssql-on-vds](concepts/mssql-on-vds.md) — MSSQL Express 2022 Linux на VDS — миграция + login orphan fix + traefik TCP gotchas
|
||||
- [ocis-on-vds-deploy-recipe](concepts/ocis-on-vds-deploy-recipe.md) — oCIS на VDS — deploy recipe + non-obvious gotchas (UID 1001 vs 1000, basic auth, LibreGraph user-create)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Append-only log of wiki operations (ingests, promotions, lints, migrations).
|
||||
|
||||
## [2026-07-03] ingest | NEW concepts/snolla-admin-appdata-acl-500-after-scp-migration — разведка «почему `/admin/assets/<owner>/delete` → 500» на RUVDS IIS. Корень: app pool `IIS AppPool\snolla` (ApplicationPoolIdentity) имел на `C:\sites\snolla\App_Data` только `(RX)` после scp-миграции 23.05 → `File.Delete` в `AssetsService.DeleteAsset` кидает UnauthorizedAccessException → необработанное 500 (event log пуст, IIS substatus 0/win32 0). NRE-версии исключены проверкой строк в БД (Folders root + Files). Fix: `icacls App_Data /grant "IIS AppPool\snolla:(OI)(CI)(M)" /T` (44293 файла, verified `(I)(M)` на таргете). Класс шире delete — все Local-записи из админки (upload/кэши/галереи). Также: заменён `labtools-price.pdf` (owner d375c419) в ОБА хранилища (MinIO `assets/…` ETag→6426ddd0 + локалка IIS, MD5 сверены). Split-brain админка(Local)↔боевой app(MinIO) — cross-ref [[concepts/galleries-storage-class-local-not-s3]].
|
||||
|
||||
## [2026-06-29] deploy | pilonuxt `cf2bba2` LIVE (стек 16) — ADR-0010: формы переехали с monolith `@snollajs/snolla` на `@snollajs/forms-api`, snolla **дропнут целиком**, core@0.1.1 (Buffer вместо btoa). Тот же build-пайплайн (форс-регенерация клиента + save|ssh load + push с VDS). **Новый обязательный шаг — tree-check `.output/server/node_modules` ПЕРЕД перекатом** (вся сага content-api↔snolla про dual-instance, dev маскирует unmet-deps): `docker run --rm --entrypoint sh <img> -c 'find /app/.output/server/node_modules ...'` → подтвердил 0× snolla, 0× btoa, ровно 1× data@0.9.1, core 0.1.1 + content-api 0.14 + forms-api 0.1.0. Smoke: SSR `/`+`/catalog`→200 (= btoa/snolla-дроп бандл не сломал), robots/yandex из БД (ADR-0009 не регресс), форма `POST /snolla-forms/forms?path=/checkout` пустой→422 JSON (valid НЕ слал — реальное письмо менеджеру), `/nope`→404. **Гоча smoke-харнеса:** forms-api отдаёт 422 только при `Accept: application/json` (иначе `prefersJson(req)` false → ветка `res.status(result.status||404).end()` даёт 404 — это не баг приложения, а отсутствие Accept в curl). Источник: наряд [deploy-pilonuxt-forms-api-drop-snolla]. См. [[concepts/portainer-stack-management-vds]] § Stack redeploy.
|
||||
|
||||
## [2026-06-29] deploy | pilonuxt `40bb383` LIVE (стек 16) — DB-backed staticPages + robots.txt (ADR-0009). Build из монорепы victor/pilorama98.ru: ключевой шаг — **форс-регенерация gitignored-клиента** `apps/web/src/generated/` (`rm -rf` + `yarn workspace nuxt-app schema-gen` против live MSSQL), иначе ensure-schema skip-if-present собрал бы старый клиент без `getStaticPage`/`getRobotsTxt` → server-500. Push с дома повторил traefik-499 на `.output`-слое даже при образе 425МБ (не multi-GB) → канонический fallback `docker save | ssh vds 'docker load'` + push с VDS (registry локален) сработал. Перекат через Portainer PUT стек 16 (`pullImage:true`, байты+UTF8 gotcha #9). Smoke acceptance 6/6 телами: robots полный из БД (Yandex Clean-param + Sitemap, не статика), yandex/google верификации 200, /nope→Nuxt-404, /+/catalog→200. Источник: наряд [deploy-pilonuxt-static-pages-robots]. См. [[concepts/portainer-stack-management-vds]] § Stack redeploy.
|
||||
|
||||
Reference in New Issue
Block a user