Перейти к содержанию

Начальные данные и экспорт содержимого

Оглавление серии

Импорт начальных данных и экспорт содержимого решают разные задачи. Fixtures помогают воспроизвести небольшой пример из заранее известных значений. Встроенный export Strapi переносит содержимое экземпляра в поддерживаемом формате. Нельзя передать обычный JSON с тремя статьями команде import и ожидать, что она сама узнает автора, отношения и конфигурацию.

В «Редакционном доме» подготовим импорт fixtures в отдельную пустую локальную CMS и рассмотрим экспорт уже заполненного экземпляра. Команды и скрипт в этой серии не запускались. Результат урока — воспроизводимый порядок наполнения и понимание того, какие сведения переносит каждый механизм, а какие остаются за его границей.

Fixture описывает предметные данные

Файл fixtures/articles.json содержит Анну Лесную, категорию «Вёрстка» и три статьи. У первой есть SEO, resource и подпись обложки. Физический media-файл создаётся отдельной ручной загрузкой: fixture не знает его ID и URL в вашей установке. Такое разделение не позволяет случайно связать Article с чужим media ID из другой базы.

Вначале создаются Author и Category, затем их documentId используются в Article relations. Порядок важен: нельзя связать документ с ещё не существующим объектом, просто написав желаемый идентификатор. В REST connect используются возвращённые IDs, а не придуманные значения из образца ответа. Схемы коллекций должны существовать до создания данных.

Локаль ru также должна быть настроена до импорта. Скрипт явно создаёт status=draft&locale=ru. Он не публикует тексты. Это согласуется с редакционным процессом: наличие исходных данных в файле не является решением о публичной готовности. После наполнения редактор проверит связи, добавит cover и выпустит выбранные документы отдельно.

Скрипт ограничивает область действия

Для запуска читателем нужен временный Import token с find/create Article, Author и Category. Он не должен заменять token сайта. Модуль проверяет явное ALLOW_FIXTURE_IMPORT=yes-local-empty, разрешает только loopback hostname и требует пустые выбранные коллекции. Полный scripts/import-fixtures.mjs приведён ниже.

import { readFile } from 'node:fs/promises';

if (process.env.ALLOW_FIXTURE_IMPORT !== 'yes-local-empty') {
  throw new Error('Разрешите импорт только в отдельную пустую локальную CMS');
}
const base = new URL(process.env.CMS_URL || 'http://127.0.0.1:1337');
if (!['127.0.0.1', 'localhost'].includes(base.hostname)) throw new Error('Local CMS only');
if (!process.env.IMPORT_TOKEN) throw new Error('Missing IMPORT_TOKEN');
const fixture = JSON.parse(await readFile(new URL('../fixtures/articles.json', import.meta.url), 'utf8'));

async function call(path, method = 'GET', data) {
  const response = await fetch(new URL(path, base), {
    method,
    headers: {
      Authorization: `Bearer ${process.env.IMPORT_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: data ? JSON.stringify({ data }) : undefined,
  });
  if (!response.ok) throw new Error(`Import: HTTP ${response.status}`);
  return response.json();
}
for (const plural of ['articles', 'authors', 'categories']) {
  const { data } = await call(`/api/${plural}?pagination[pageSize]=1&status=draft`);
  if (data.length) throw new Error(`Destination ${plural} is not empty`);
}
const author = await call('/api/authors', 'POST', fixture.author);
const category = await call('/api/categories', 'POST', fixture.category);
for (const article of fixture.articles) {
  await call('/api/articles?status=draft&locale=ru', 'POST', {
    ...article,
    author: { connect: [author.data.documentId] },
    category: { connect: [category.data.documentId] },
  });
}
console.log('Созданы три черновика; публикация выполняется отдельно редактором.');

Проверка empty нужна, чтобы повторный запуск не размножил справочники и статьи. Это не универсальная синхронизация: скрипт не обновляет существующие документы и не умеет продолжить прерванную загрузку с середины. Если первая статья создана, а следующая завершилась ошибкой, новый запуск увидит непустую коллекцию и остановится. Читатель должен разобраться в частичном состоянии, а не обходить защиту удалением проверки.

Запрос find с pageSize один достаточно отвечает на вопрос «есть хотя бы одна запись». Он не оценивает полный объём. Проверка выполняется последовательно до создания Author и Category; поэтому в нормальном пустом сценарии первый POST не начинается, пока не проверены все три коллекции. В реальной системе конкурентный процесс мог бы создать данные после проверки: учебный скрипт предполагает единственного оператора и отдельную лабораторию.

Функция call оборачивает данные в { data }, проверяет HTTP-успех и возвращает JSON. Создание отношений происходит через connect с documentId, полученными от предыдущих POST. Body статьи, SEO и Resources передаются вместе с Article. Здесь обычный объект fixture имеет понятное значение, но не является полной резервной копией базы.

После будущего выполнения ожидаются три drafts, один Author и одна Category. Обложка остаётся непривязанной до ручной загрузки. Английскую локализацию первой статьи создают по десятому уроку; fixture не претендует на заполнение всех языков. Срок жизни Import token заканчивается после работы: его следует отозвать, оставив сайту только чтение.

Встроенный export переносит экземпляр

Когда данные уже внесены, рассмотрим команду встроенного export. Она запускается из проекта Strapi и предлагает формат штатного архива. Для читателя команда выглядит так; путь указывает на собственную локальную резервную папку.

npm run strapi export -- --file ./backups/editorial-house

По умолчанию export создаёт сжатый зашифрованный архив и включает содержимое, связи, assets, schemas и относящуюся к механизму конфигурацию. Admin users и API tokens не экспортируются. Ключ шифрования вводится и хранится отдельно; не помещайте его рядом с архивом в публичный ZIP. Состав описан в Data Export.

Экспорт не заменяет сохранение исходников и зависимостей. Проекту всё ещё нужны подходящие package versions, конфигурация окружения и правила доступа. Архив данных полезен вместе с ними, а не вместо них. Если разработчик прислал только .tar.gz.enc и не сообщил версию модели, получатель не получил полного плана восстановления.

Не называйте fixtures встроенным export. Их можно читать обычным редактором и понимать по смыслу; они создают очень небольшой набор drafts через Content API. Export отражает состояние конкретного экземпляра и импортируется штатным механизмом. Эти формы имеют разные требования к совместимости, существующим данным и управлению секретами.

Импорт требует отдельного назначения

Штатный import применяют к совместимому проекту со схемой подходящего состояния. Документация предупреждает о воздействии на существующие данные назначения. Поэтому сначала создают отдельную лабораторную копию, сохраняют её предыдущее состояние и читают подтверждение операции. В курсе не используем --force, которое могло бы скрыть важный этап понимания последствий. Правила находятся в Data Import.

Для проверки переноса недостаточно найти одну статью. Сравните число документов, связи с Анной и «Вёрсткой», наличие assets, публикацию ru и существование en. Затем создайте новые API tokens и настройте доступ по матрице девятого урока. Отсутствие exported credentials — ожидаемая граница, а не признак повреждённого файла.

Восстановление из export лучше сравнивать по предметным связям, а не только по внутренним numeric IDs. Разные механизмы переноса и версии способны иметь собственные правила идентичности. Для библиотеки важно, что Article «Сетка карточек CSS Grid» относится к Анне и «Вёрстке», имеет нужную локаль и доступный asset. Если интеграция хранит внешний documentId, его поведение при выбранном переносе также проверяют отдельно. Наш курс не обещает произвольную совместимость идентификаторов между любыми способами импорта: сначала определяется конкретный механизм и контрольный набор.

Предметный результат этого урока можно описать без двусмысленности: JSON course fixtures создаёт три черновика только в пустой локальной базе; штатный export сохраняет гораздо более широкий набор данных; штатный import требует подходящего назначения и отдельного разбора его воздействия. Следующая глава соединит эти сведения с файловой копией и изменением схемы, чтобы восстановление означало возврат всего проекта к согласованному состоянию.