Перейти к основному содержимому

Диагностика CMS — порядок проверок

Версия: 1.1 Дата: 25.04.2026 Статус: Готов к обсуждению

Документ описывает алгоритм диагностики при ошибках Decap CMS-редактора. Цель — найти корневую причину до того, как менять рабочую конфигурацию. Документ создан после ряда инцидентов, где каскадные правки конфигов усугубляли ситуацию вместо устранения.

Изменения в v1.1 (25.04.2026): добавлена секция «Ошибка: Path '<folder>' already exists» с корневой причиной (frontend-валидатор validateMetaField Decap CMS, конфликт meta.path + index_file: index с реальным index.md в папке) и проверенным решением (удаление meta: из коллекции).

Главное правило

Decap CMS-редактор (/admin/) работает только на локальном dev-сервере или локальном prod-сервере.

На production-домене (docsys.mamo.market и аналоги) папка /admin/ сознательно удаляется при деплое. Это design-решение для безопасности (CMS не должна быть публично доступна без специальной защиты).

Доказательство — tools/deploy_site.sh:

step "Удаление локальных инструментов из build/"
rm -rf "$REPO/build/admin"
ok "build/admin удалён"

Где работает CMS

СредаURLСтатус
Локальная разработкаhttp://localhost:3000/admin/index.htmlРаботает (если dev-сервер + decap-server запущены)
Локальный prod-просмотрhttp://localhost:5000/admin/index.htmlРаботает (если tools/serve_preview.sh + decap-server запущены)
Production VPShttps://<домен>/admin/Не работает — admin/ удалён при деплое
Shared хостингhttps://<домен>/admin/Не работает — admin/ удалён при деплое

Стек локального окружения

1. Docusaurus dev-сервер (порт 3000)

  • Запуск: npm start или pm2 start ecosystem.config.js --only cms-docs-dev.
  • Сервирует static/admin/config.yml как статику с Content-Type: text/yaml.
  • Сервирует MD-файлы из docs/ через MDX-loader.

2. Decap-server (целевой порт 8083, default 8081)

  • Запуск: pm2 start ecosystem.config.js --only cms-decap-server.
  • Прокси-сервер между Decap CMS frontend и локальным git/file-system backend.
  • В static/admin/config.yml указан proxy_url: http://localhost:8083/api/v1.
  • В ecosystem.config.js env PORT: '8083' принуждает к 8083.
  • Decap-server читает process.env.PORT корректно. Default 8081 — если env не установлен.

3. cms-tools-api (порт 8084)

  • Запуск через PM2.
  • Внутренний API для tools-операций (генерация index.md, добавление пользователей).

Алгоритм диагностики

При ошибке CMS — проверять в этом порядке. Не пропускать шаги.

Шаг 1. URL правильный?

Открыта ли CMS по правильному URL?

  • http://localhost:3000/admin/index.html — правильный URL для dev.
  • http://localhost:5000/admin/index.html — правильный URL для локального prod (serve_preview.sh).
  • http://localhost:3000/admin/ — без index.html Docusaurus может отдать закешированную страницу вместо CMS.
  • http://localhost:3000/admin#/ — то же.
  • https://docsys.mamo.market/admin/ или любой production-URL — admin/ удалён при деплое, nginx возвращает 404 HTML, Decap CMS пытается парсить HTML как YAML и падает с ошибкой YAMLSyntaxError: All collection items must start at the same column at line 1, column 1: <!DOCTYPE html>.

Если URL неправильный — открыть правильный URL и проверить ещё раз. Других проблем искать не нужно.

Шаг 2. Запущен ли Docusaurus dev-сервер на 3000?

ps aux | grep -E 'docusaurus|node.*start' | grep -v grep
curl -sI http://localhost:3000 | head -3

Ожидаемо: процесс docusaurus start или node ... start есть, curl возвращает HTTP/1.1 200 OK.

Если процесса нет — запустить:

pm2 start ecosystem.config.js --only cms-docs-dev

Шаг 3. Запущен ли decap-server на 8083?

ss -tlnp | grep ':8083'
curl -sI http://localhost:8083/api/v1 | head -3

Ожидаемо: LISTEN на 8083, curl возвращает HTTP/1.1 405 Method Not Allowed (это нормально — endpoint требует POST, не GET).

Расширенная проверка ответа:

curl -s -X POST http://localhost:8083/api/v1 \
-H "Content-Type: application/json" \
-d '{"action":"info"}'

Ожидаемый ответ:

{"repo":"cms-docs","publish_modes":["simple"],"type":"local_fs"}

Шаг 4. Свежие ошибки в логах?

tail -30 /home/alex/.pm2/logs/cms-decap-server-error.log
tail -30 /home/alex/.pm2/logs/cms-docs-dev-error.log

Типичные ошибки в логах decap-server:

  • Error: listen EADDRINUSE: address already in use :::8081 — decap-server пытается слушать 8081 (default), но порт занят. Реальная причина в этой машине — Apache. Решение в шаге 5.
  • Error: connect ECONNREFUSED — decap-server упал, не слушает.

Шаг 5. Decap-server crash loop с EADDRINUSE на 8081

Симптомы: в логах EADDRINUSE, decap-server рестартится много раз подряд (видно в pm2 describe cms-decap-server поле restarts:).

Причина: Apache на этой машине слушает 8081, decap-server тоже пытается на 8081 (default). Env-переменная PORT=8083 из ecosystem.config.js не подхватывается PM2 при простом restart или даже restart --update-env в PM2 v6.0.x — restart запускает процесс с уже сохранённым snapshot env.

Решение (рабочее, проверено 25.04.2026):

PM2=/home/alex/.nvm/versions/node/v24.14.1/bin/pm2

# 1. Проверить, кто занял 8081
ss -tlnp | grep ':8081' # часто Apache

# 2. Полностью удалить процесс — НЕ просто restart
$PM2 delete cms-decap-server

# 3. Стартовать заново через ecosystem.config.js
cd /home/alex/sites/cms-docs
$PM2 start ecosystem.config.js --only cms-decap-server

# 4. Сохранить pm2 dump (чтобы переживало reboot)
$PM2 save

# 5. Проверить
ss -tlnp | grep ':8083'
curl -X POST http://localhost:8083/api/v1 \
-H "Content-Type: application/json" \
-d '{"action":"info"}'

Ожидаемое: LISTEN на 8083, curl отдаёт корректный JSON.

Почему pm2 restart --update-env не работает: в PM2 v6.0.x команда restart запускает процесс с уже сохранённым snapshot env, в котором PORT отсутствует. Только delete + новый start ecosystem.config.js создаёт fresh process с правильной env.

Путь к PM2 в этом окружении: pm2 не в PATH; полный путь /home/alex/.nvm/versions/node/v24.14.1/bin/pm2.

Шаг 6. CMS Config Errors про buttons

Симптомы: в браузерной консоли ошибка про неподдерживаемое значение в editor_components → buttons.

Причина: обновление Decap CMS изменило список allowed values для buttons.

Решение: см. Руководство по установке → раздел проблем после обновления Decap CMS.

Шаг 7. Только если все шаги выше пройдены — смотреть в документы

Только если все 6 шагов выше дали зелёный — проблема может быть в самих документах. Проверить:

  • Frontmatter валиден (YAML парсится, парные ---, кодировка UTF-8).
  • Frontmatter содержит draft: false (Decap CMS schema требует).
  • Schema-совместим с cms-config.json и _project_.json.
  • MDX-safe (нет голых <digit в plain-тексте).
  • Кодировка UTF-8 без BOM.
  • Между H1 и шапкой документа — пустая строка.

Подробности — в Правила оформления документов.

Типичные ошибки и их корневые причины

Ошибка: Failed to load entry: TypeError: Failed to fetch + YAMLSyntaxError: All collection items must start at the same column at line 1, column 1: <!DOCTYPE html>

Причина: CMS открыта на production-URL или на dev-URL без index.html. Сервер возвращает HTML (404 или закешированную страницу), Decap CMS пытается парсить HTML как YAML.

Решение: Открывать CMS только по локальному URL с index.html в конце пути:

  • http://localhost:3000/admin/index.html (dev);
  • http://localhost:5000/admin/index.html (prod через serve_preview.sh).

Что НЕ является причиной:

  • Формат документов;
  • Frontmatter;
  • YAML в config.yml (он валиден).

Не нужно править документы или конфиги, чтобы устранить эту ошибку. Достаточно сменить URL.

Ошибка: EADDRINUSE в логах decap-server, CMS не работает локально

См. шаг 5 алгоритма выше.

Ошибка: Failed to fetch без YAML-ошибки

Причина: decap-server не запущен или не слушает на 8083.

Решение: см. шаг 3 алгоритма выше.

Ошибка: Path '<folder>' already exists при Publish существующего документа

Симптом: при попытке Publish или Save существующего документа CMS показывает ошибку вида Path 'project-slug/folder' already exists и блокирует сохранение. В логах decap-server при этом нет новых записей — запрос даже не уходит на backend, ошибка генерируется во frontend-валидации Decap CMS.

Корневая причина: в коллекции одновременно используются три параметра — nested, meta.path и index_file: index — а в папке проекта рядом с обычными документами лежит index.md. Frontend-валидатор Decap CMS (validateMetaField в decap-cms-core/src/actions/entries.ts) собирает кандидат-путь как <folder>/<meta.path>/index.md, находит реальный index.md соседом редактируемого документа и считает это коллизией путей.

Подтверждение в исходниках Decap CMS v3:

  • Текст ошибки в decap-cms-locales/src/en/index.js:

    pathExists: "Path '%{path}' already exists"
  • Логика валидации: decap-cms-core/src/actions/entries.ts (функция validateMetaField):

    if (existingEntryPath && existingEntryPath !== draftPath) {
    return getPathError(value, 'pathExists', t);
    }
  • Резолв кандидат-пути: decap-cms-core/src/reducers/entryDraft.js (функция selectCustomPath) собирает значение customPath:

    customPath = join(collection.folder, meta.path, `${index_file}.${extension}`)
  • Гейт срабатывания: decap-cms-core/src/reducers/collections.ts (функция selectHasMetaPath) — проверка только на наличие meta.path, параметр nested тут роли не играет.

Известный baseline-issue в апстриме:

Решение (минимально инвазивное, проверено 25.04.2026):

В static/admin/config.yml коллекции docs удалить строку meta: целиком:

collections:
- name: docs
folder: docs
create: true
nested:
depth: 10
slug: "{{slug}}"
extension: md
format: frontmatter
# meta: { path: { widget: string, label: Path, index_file: index } } ← удалено
fields:
- { label: Заголовок, name: title, widget: string }
- { label: Черновик, name: draft, widget: boolean, default: false, required: false }
- { label: Содержимое, name: body, widget: markdown }

Поле meta.pathопциональное. По официальной документации Decap оно нужно только для возможности перемещать документы между папками через CMS-редактор. Для штатной правки тела документа оно не требуется.

После правки config.yml:

  1. Сохранить файл (tools/build_auth.sh и Docusaurus dev-сервер не требуют перезапуска — config.yml отдаётся как статика).
  2. В браузере на странице CMS — Ctrl+Shift+R (хард-релоад), чтобы браузер взял новый config.yml.
  3. Проверить — Publish существующего документа должен пройти.

Что сохраняется и что теряется:

  • ✅ Сохраняется: nested: depth: 10 (навигация по дереву папок), открытие документов, чтение, сохранение в исходное имя файла.
  • ✅ Сохраняется: index.md как специальный файл-страница папки в Docusaurus.
  • ❌ Теряется: возможность перемещать документ между папками через поле Path в CMS-редакторе. Перенос — только через файловый менеджер или git mv.

Если нужна возможность перемещения через CMS:

Единственный обходной путь, известный в апстриме — разнести index.md-файлы и обычные документы по разным коллекциям: одна nested коллекция для индексов, отдельная folder-коллекция для leaf-документов. Это существенное изменение архитектуры, эскалируется владельцу системы.

Что НЕ является причиной этой ошибки:

  • Формат документа, frontmatter, MDX-парсер.
  • Кэш браузера или Service Worker.
  • Состояние decap-server (8083) — запрос до него не доходит.
  • Версия Decap CMS (3.x) — это поведение присутствует во всех релизах после введения meta.path.

Ошибка: вход в CMS не работает (логин/пароль не принимается)

Причина: CREDENTIALS_HASH в static/admin/index.html не соответствует паролю в cms-config.json.

Решение: обновить пароль через tools/add_user.sh --admin <newpassword>. Скрипт атомарно обновит и cms-config.json, и static/admin/index.html.

Запрещено редактировать CREDENTIALS_HASH руками.

Путь развёртывания CMS на production (если бизнес-требование)

Если CMS на production действительно нужна:

  1. Закомментировать rm -rf "$REPO/build/admin" в tools/deploy_site.sh.
  2. Защитить /admin/ через nginx basic auth, client cert, или OAuth-прокси.
  3. Реализовать proxy для /admin/api/v1/* к decap-server (или git-gateway backend).
  4. Передеплоить.

Это противоречит изначальному design-решению. Эскалируется к автору cms-docs системы.

Связанная документация