Разбор 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>
5.8 KiB
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)
# на 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.