Files
admin/cms-admin-assets-root-folders-seed.md
vitya 6f846d0495 docs(.wiki,.tasks): cms-admin-assets-root-folders-seed — DB seed 15 missing root rows
Admin /admin/assets/<siteId>/getList?path= крашился 500 NullReferenceException
в AssetsJsonViewModelBuilder.cs:22 (model.ParentPath на null) для 15 sites без
root AssetsFolder в Folders table (emspb.ru, pilorama98.ru, labtools.pro,
kupimknigi.spb.ru, sestech.ru, aquamax.spb.ru, artmone.pro, priemka-kvartiry.ru,
profund.spb.ru, ics-artmaterials.com, _voda-indigo.ru + 4 sites с NULL Domain).

Root создавался lazy при first upload — sites которые никогда не использовали
admin assets UI остались без root. Frontend (AssetsAppFunc.cs:66-86) делает
proper null-check → 404, только admin view-model builder упустил.

Fix: idempotent SQL seed (WHERE NOT EXISTS), 15 rows inserted. Inserted
FolderId/OwnerId captured в .tasks/...inserted-rows.txt для atomic revert.
Browser-verified user'ом на pilorama98/emspb.

Долгосрочный TODO: null-guard в AssetsJsonViewModelBuilder.Build (CMS code),
требует recompile DLL — отложено до восстановления build env (vds-kzntsv-bootstrap).
snapshot open issue #8 → resolved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:21:13 +03:00

6.8 KiB
Raw Blame History

cms-admin-assets-root-folders-seed

Goal

Для каждого Sites.SiteId в DB MoreThenCms где нет соответствующего root Folders row с Discriminator='AssetsFolder', LoweredPath='/', OwnerId=<SiteId> — добавить недостающий root row. Это unblock'нёт admin /admin/assets/<siteId>/getList?path= который сейчас crash'ит 500 NullReferenceException на пустых assets для 15+ sites (включая emspb.ru, pilorama98.ru, labtools.pro, kupimknigi.spb.ru, sestech.ru, etc.).

Root cause

MoreThenCms.Admin.ViewModels.Builders.AssetsJsonViewModelBuilder.Build (AssetsJsonViewModelBuilder.cs:22) делает new JValue(model.ParentPath) БЕЗ null-check на model. _assetsFoldersService.GetFolderByPath(ctx, ownerId, '') возвращает null когда root folder не существует. Это происходит для sites которые никогда не открывали admin assets UI (root создаётся lazy при first upload, видимо).

Frontend код (AssetsAppFunc.cs:66-86) делает proper null-check → HTTP 404. Только admin view-model builder упустил.

Двух-уровневое решение:

  • Краткосрочно (этой task'и): seed missing root rows в DB. Один INSERT per site. Risk: minimal (lookup existing row pattern; используем same shape).
  • Долгосрочно (отдельный task): patch AssetsJsonViewModelBuilder.Build → null-guard → empty response. Требует recompile DLL MoreThenCms.Admin.dll (нет полного build env, рискованно).

Key files

  • MoreThenCms\Assets\Services\AssetsFoldersService.cs:47-54GetFolderByPath query (returns null if not found).
  • MoreThenCms.Web\ViewModels\Builders\AssetsJsonViewModelBuilder.cs:22 — точка падения.
  • MoreThenCms.Web\Admin\Controllers\AssetsController.cs:44-54 — вызов GetList.
  • DB: [MoreThenCms].[dbo].[Folders] (polymorphic, Discriminator='AssetsFolder' для assets).

Discovery (2026-05-19)

  • 15 sites без root AssetsFolder подтверждены через SELECT s.SiteId, s.PrimaryDomain, ... FROM Sites s LEFT JOIN .... См. полный список в Decisions.
  • Sample existing root row: FolderId=1591A38C-..., OwnerId=7EB313DD-F289-..., Path='/', LoweredPath='/', DateCreated=2017-03-13 07:10:15, CreatedById=E4CC416B-.... Тот же shape надо воспроизвести для missing.
  • Затрагивает: emspb.ru (96EBC481-...), pilorama98.ru (37E67FC4-...), labtools.pro, kupimknigi.spb.ru, sestech.ru, aquamax.spb.ru, artmone.pro, priemka-kvartiry.ru, profund.spb.ru, ics-artmaterials.com, _voda-indigo.ru, plus 4 sites с NULL PrimaryDomain.

План

  1. Dry-run: SELECT COUNT(*) FROM Sites s WHERE NOT EXISTS (root for s.SiteId) — должно быть = 15 (sanity check).
  2. Seed: INSERT root row per missing site. Один transaction, no rollback unless count mismatch.
  3. Verify: повторить original probe — root count should be 0 missing.
  4. Test: hit https://emspb.snolla.com/admin/assets/96EBC481-.../getList?path= (через docker exec без auth → 302→login без crash). Затем user-side browser test.

SQL

DECLARE @CreatedById uniqueidentifier = (
    SELECT TOP 1 CreatedById FROM Folders
    WHERE LoweredPath='/' AND Discriminator='AssetsFolder'
);
INSERT INTO Folders (FolderId, OwnerId, Path, LoweredPath, DateCreated, UtcDateCreated, CreatedById, Discriminator)
SELECT NEWID(), s.SiteId, '/', '/', SYSDATETIME(), SYSUTCDATETIME(), @CreatedById, 'AssetsFolder'
FROM Sites s
WHERE NOT EXISTS (
    SELECT 1 FROM Folders f
    WHERE f.OwnerId = s.SiteId
      AND f.LoweredPath = '/'
      AND f.Discriminator = 'AssetsFolder'
);

Atomic revert

Сохранить список inserted FolderId через OUTPUT INSERTED.FolderId в temp file. Если что:

DELETE FROM Folders WHERE FolderId IN (<list>);

Open questions

  • Аналогичная проблема для ImagesFolder/StylesheetsFolder/ScriptsFolder в admin Themes UI? Проверить.
  • DLL patch AssetsJsonViewModelBuilder.Build нужен в долгую — отдельная task. Сейчас обходим seed'ом.

Decisions log

  • 2026-05-19: вынес из cms-port-leak-fix (там был open issue #8 на emspb). User подтвердил воспроизводимость на pilorama98 → не site-specific, общая data issue.
  • 2026-05-19: DB seed применён — 15 rows inserted (single transaction, idempotent NOT EXISTS). Inserted FolderId/OwnerId captured в cms-admin-assets-root-folders-seed.inserted-rows.txt. Server-side verified (probe вернул 302→login вместо 500). User browser-verified на pilorama98/emspb — admin assets открывается, empty list без ошибок.
  • 2026-05-19: DLL recompile отложенAssetsJsonViewModelBuilder.Build нужен null-guard как defensive code, но требует build env (нет gitea/build/registry на recovery host'е). Ждёт vds-kzntsv-bootstrap для восстановления pipeline'а. На текущем seed-only fix'е admin assets работает для всех 15 sites.

Completed steps

  • Diagnostic: stack trace из Application event log → AssetsJsonViewModelBuilder.cs:22 NullRef on model.ParentPath. Source code analysis показал отсутствие null-check (vs proper null-check во frontend AssetsAppFunc.cs:66-86).
  • DB probe schema: Folders table polymorphic с Discriminator, columns FolderId/OwnerId/Path/LoweredPath/DateCreated/CreatedById обязательные.
  • Dry-run: 15 sites без root AssetsFolder (включая emspb/pilorama98/labtools.pro/kupimknigi/sestech/etc), 4 sites с NULL PrimaryDomain тоже в списке.
  • SQL seed через docker exec mssql sqlcmd — INSERT 15 rows с OUTPUT INSERTED.FolderId, INSERTED.OwnerId INTO @Inserted, output saved в .tasks/cms-admin-assets-root-folders-seed.inserted-rows.txt.
  • Verify: missing-root count = 0. Probe wget --header='Host: emspb.snolla.com' на getList URL → 302 Found (auth redirect — normal), нет больше 500.
  • User browser-verified pilorama98 admin assets открывается.
  • Wiki ingest: новый concept cms-admin-assets-root-folder-seed.md, snapshot issue #8 → resolved, cms-server-port-leak-fix sibling-link, log.md + index.md обновлены.

Notes