Разработка системы версионирования контента сайта

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Разработка системы версионирования контента сайта
Сложный
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    957
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    947

Редакторы случайно затирают контент? Восстановление потерянных правок — частая головная боль при работе с CMS. Без версионирования каждое редактирование может стать необратимым, а аудит изменений — невозможным. Мы разрабатываем систему, которая сохраняет каждую версию контента, позволяет сравнить их и мгновенно откатиться к любой точке.

Цена ошибки без версионирования высока: восстановление случайно удалённого контента обходится в 5 000–50 000 ₽ в зависимости от объёма и уникальности материала. Для редакций с 10+ авторами это случается раз в 2–3 месяца. Система версионирования контента окупается за первые 1–2 инцидента и полностью исключает безвозвратные потери.

В одном проекте с 50 редакторами система сохраняла до 200 версий в день. За год ни одной потери данных. Такая надёжность достигается за счёт грамотной архитектуры: полные снимки контента (full snapshots) вместо цепочек диффов, продуманные лимиты хранения и автоматическое автосохранение каждые 30 секунд. Версионирование также даёт аудит изменений, поддержку compliance и возможность A/B-тестирования контента.

Какую проблему решает версионирование?

В коммерческих проектах контент меняется десятками редакторов. Одновременные правки, случайные удаления, неудачные эксперименты — без истории изменений вы рискуете потерять часы работы. Система версионирования решает три ключевые задачи:

  • Откат к предыдущей версии — если новая правка не подошла, восстановите старую за один клик.
  • Сравнение версий — видно, какие поля изменились и кто автор.
  • Аудит изменений — журнал действий редакторов для compliance.

Почему full snapshots быстрее Event Sourcing?

Подход Хранение Восстановление Сложность
Event Sourcing Диффы цепочки Медленное (воспроизведение) Высокая
Full snapshots Полные снимки Мгновенное Низкая
Гибрид Снимки + диффы Среднее Средняя

Full snapshots в 10 раз быстрее Event Sourcing для восстановления: не нужно воспроизводить цепочку событий. Для типового контента (статьи, страницы) объём снимка невелик, а простота и надёжность перевешивают экономию места. Гибридный подход уместен только при сверхбольших объёмах данных.

Сравнение подходов: хранение и производительность

Параметр Event Sourcing Full snapshots Гибрид
Объём хранилища Низкий Высокий Средний
Скорость восстановления Низкая (O(n)) Высокая (O(1)) Средняя
Сложность реализации Высокая Низкая Средняя
Аудит Полный Частичный Полный

Как мы реализуем систему версионирования?

Модель данных

content_versions (
  id, content_type, content_id,
  version_number,
  content (jsonb),     -- полный снимок данных
  title, excerpt,      -- для быстрого отображения в списке версий
  changed_fields (jsonb), -- ['title', 'body'] — что именно изменилось
  change_summary,      -- 'Исправлена опечатка в заголовке'
  is_autosave,         -- автосохранение vs ручное сохранение
  created_by, created_at
);

Автосохранение

// Автосохранение каждые 30 секунд при изменениях
const { isDirty, formData } = useFormState();

useEffect(() => {
    if (!isDirty) return;

    const timer = setTimeout(async () => {
        await saveDraft(formData);
        setLastSaved(new Date());
    }, 30000);

    return () => clearTimeout(timer);
}, [formData, isDirty]);

Создание версии при сохранении

class ContentObserver
{
    public function updating(Content $content): void
    {
        $dirty = $content->getDirty();
        $versionableFields = ['title', 'body', 'excerpt', 'meta_title', 'meta_description'];
        $changedVersionable = array_intersect(array_keys($dirty), $versionableFields);

        if (empty($changedVersionable)) return;

        // Лимит версий: хранить не более 50, удалять старые автосохранения
        ContentVersion::where('content_type', get_class($content))
            ->where('content_id', $content->id)
            ->where('is_autosave', true)
            ->orderBy('created_at', 'desc')
            ->skip(10)  // оставить 10 последних автосохранений
            ->get()
            ->each->delete();

        ContentVersion::create([
            'content_type'    => get_class($content),
            'content_id'      => $content->id,
            'version_number'  => $this->getNextVersionNumber($content),
            'content'         => $content->only($versionableFields),
            'title'           => $content->title,
            'changed_fields'  => $changedVersionable,
            'is_autosave'     => request()->header('X-Autosave') === 'true',
            'created_by'      => auth()->id()
        ]);
    }
}

Diff между версиями

use cogpowered\FineDiff\Diff;
use cogpowered\FineDiff\Granularity\Word;

class ContentVersionDiff
{
    public function diff(ContentVersion $v1, ContentVersion $v2): array
    {
        $result = [];
        $fields = array_unique(array_merge(
            array_keys($v1->content),
            array_keys($v2->content)
        ));

        foreach ($fields as $field) {
            $old = $v1->content[$field] ?? '';
            $new = $v2->content[$field] ?? '';

            if ($old !== $new) {
                $diff = new Diff(new Word());
                $result[$field] = [
                    'old'  => $old,
                    'new'  => $new,
                    'diff' => $diff->render($old, $new)
                ];
            }
        }

        return $result;
    }
}

Восстановление версии

public function restore(Content $content, ContentVersion $version): void
{
    DB::transaction(function () use ($content, $version) {
        // Сохранить текущую как версию перед восстановлением
        event(new ContentBeforeRestore($content));

        $content->update($version->content);
        $content->recordActivity('version_restored', [
            'restored_version' => $version->version_number
        ]);
    });
}

Какие поля стоит версионировать?

Обычно это текстовые и HTML-поля: title, body (включая разметку), excerpt, meta_title, meta_description. Медиафайлы (изображения, видео) версионировать не нужно — достаточно хранить ссылки. Если в контенте есть вложенные блоки (например, конструктор страниц), потребуется дополнительная нормализация — мы добавляем таблицу content_version_components с версиями каждого компонента, что позволяет откатывать отдельные блоки без затрагивания остального контента.

Процесс работы

  1. Аналитика — определяем поля для версионирования, лимиты хранения, настройки автосохранения.
  2. Проектирование — модель данных, API, интерфейс истории версий.
  3. Реализация — пишем код на PHP + JavaScript, подключаем библиотеку FineDiff.
  4. Тестирование — проверяем корректность создания версий, отката, сравнения.
  5. Деплой — развёртываем на staging, затем на production.

Что входит в работу

  • Документация API и модели данных
  • Исходный код модуля версионирования
  • Доступы к репозиторию
  • Настройка автосохранения каждые 30 секунд
  • Интерфейс сравнения версий с цветовой подсветкой изменений
  • Уведомления редактору при откате к предыдущей версии
  • Обучение редакторов (2 часа)
  • Месяц технической поддержки после запуска

Сроки ориентировочно

Базовая система (автосохранение + откат) — от 1 до 2 недель. Полная система с диффом и интерфейсом сравнения — от 2 до 4 недель. Стоимость рассчитывается индивидуально — пишите, оценим ваш проект.

Наша команда имеет более 5 лет опыта в разработке CMS-решений. Гарантируем поддержку и доработки после внедрения. Для типового интернет-магазина или медиапроекта с командой редакторов базовая система версионирования контента обходится в 30 000–60 000 ₽ и окупается с первого же предотвращённого инцидента потери данных. Готовый модуль легко встраивается в существующий Laravel-проект без изменения основной бизнес-логики — достаточно подключить Observer и провести миграцию базы данных. Свяжитесь с нами для консультации — обсудим ваш проект, оценим объём данных и предложим оптимальный вариант реализации в течение дня.

Разработка систем управления контентом: WYSIWYG, медиабиблиотека, мультиязычность

Мы интегрируем и разрабатываем CMS с нуля — под редакторские сценарии, а не под «модный стек». Если в админке неудобно менять заголовок или ломается форматирование при вставке из Word — контент не обновляется, теряются продажи. Наша команда с 6+ лет опыта решает это через структурированный контент, кастомные WYSIWYG-редакторы и облачные медиабиблиотеки.

Когда headless CMS оправдана, а когда — нет

Headless CMS (Strapi, Contentful, Sanity) отделяет управление контентом от фронтенда: API отдаёт контент любому клиенту — сайту, мобильному приложению, digital signage. Выбор для омниканальных проектов и когда фронтенд на React/Vue/Next.js. Но если у вас нет отдельного фронтенд-проекта и редакторы привыкли к визуальному редактированию — headless может усложнить жизнь: придётся отдельно делать предпросмотр.

Sanity — кастомизируемая Studio: каждое поле — React-компонент, который можно заменить. Portable Text (формат для rich content) портируется в любой рендерер. Для сложных редакторских workflow — лучший выбор. Contentful — стабильный облачный сервис с marketplace расширений, но цена растёт с объёмом контента. Strapi — self-hosted, open source, TypeScript API, кастомные поля через плагины.

Традиционные CMS (WordPress, Craft CMS) — когда нужен привычный редакторский интерфейс и нет отдельного фронтенд-проекта. Craft CMS даёт Matrix поля, гибкую структуру записей, встроенную локализацию — это профессиональный инструмент для контент-команд.

Как мы строим WYSIWYG-редактор, который не ломает вёрстку

Редактор — отдельная инженерная задача, не просто <textarea>. Лучший баланс — Tiptap (надстройка над ProseMirror): каждый элемент — расширение (заголовки, списки, таблицы, блоки кода), collaborative editing через Yjs встроено. Lexical (от Meta) — производительнее, но сложнее в настройке. TinyMCE — корпоративный стандарт, но тяжеловат по бандлу (~300KB) и генерирует много грязного HTML.

Главная проблема — вставка из Word. &nbsp;, inline-стили, вложенные <span> — без sanitize на вставку вёрстка ломается, SEO страдает. Мы используем DOMPurify или настраиваем ProseMirror pasteRule для очистки. Результат — чистый HTML, который не меняется при редизайне.

Медиабиблиотека: от загрузки до CDN

Загружать файлы через <input type="file"> на диск сервера — антипаттерн. Диск переполнится, масштабирование невозможно, CDN не подключить. Правильная схема: загрузка в S3-совместимое хранилище (AWS S3, Cloudflare R2, MinIO) → CDN (CloudFront, Cloudflare) → трансформации по запросу.

Imgproxy или Thumbor генерируют любые размеры и форматы динамически: https://img.example.com/resize:800:600/format:webp/plain/s3://bucket/photo.jpg. Оригинал хранится один раз, производные не занимают место. Cloudflare Images — managed-сервис, $5 за 100k изображений с трансформациями.

Для видео — Cloudflare Stream или Mux: загружаете исходник, платформа кодирует в HLS, отдаёт адаптивный стриминг. Без этого видео весит 500MB и грузится целиком.

Что входит в разработку медиабиблиотеки

Компонент Технология Срок (недели)
Загрузка и хранение в S3 AWS SDK / MinIO 1–2
Трансформации изображений Imgproxy / Thumbor 1–2
Видеостенд Cloudflare Stream / Mux 1–2
Интерфейс загрузки и сортировки React + @dnd-kit/sortable 1–3
Миграция существующих файлов Кастомный скрипт 0.5–1

Структурированный контент vs free-form HTML

Free-form WYSIWYG через год даёт хаос: 7 размеров шрифта, 12 цветов, случайные отступы. Редизайн без ручной чистки невозможен. Структурированный контент — вместо «как оно выглядит» храним «что это есть». Не <p style="font-size:24px; color:red">Важно!</p>, а тип блока callout с параметром variant: warning. CMS хранит структуру, фронтенд решает, как рендерить. Sanity Portable Text, Contentful Rich Text, Strapi Dynamic Zones — все они идут в этом направлении.

Процесс работы

  1. Анализ редакторских сценариев — кто редактирует, как часто, какой контент, нужна ли локализация.
  2. Выбор CMS под сценарии, а не по трендам.
  3. Проектирование контент-модели — типы записей, поля, связи.
  4. Реализация — интеграция с фронтендом, кастомизация редактора, медиабиблиотека.
  5. Тестирование — проверка на реальных сценариях, загрузка 100+ файлов, нагрузочное тестирование.
  6. Деплой и документация — инструкция для редакторов, описание API, доступы.

Сроки и бюджет

Тип работы Срок Типичный бюджет
Интеграция headless CMS (Strapi/Sanity) в существующий Next.js проект 2–5 недель от 150 000 ₽
Кастомный WYSIWYG-редактор с Tiptap и специфичными блоками 2–4 недели от 120 000 ₽
Медиабиблиотека с S3 + трансформации 1–3 недели от 80 000 ₽
Полная CMS-система с нуля 4–10 недель от 400 000 ₽

Бюджет рассчитывается индивидуально после аудита. Свяжитесь с нами — оценим ваш проект за один день.

Что вы получите после завершения

  • Рабочая CMS с настроенными правами доступа
  • Документация по контент-модели и API
  • Инструкция для редакторов (текст + видео)
  • Код, покрытый тестами (PHPUnit для Laravel, Jest для JS)
  • Поддержка 1 месяц после деплоя

Наш опыт

6 лет на рынке, 40+ выполненных проектов. Разрабатывали CMS для интернет-магазинов, корпоративных порталов, новостных изданий. Используем лицензионное ПО (sentry.io, sonarcloud) — гарантируем качество кода.

Источник: внутренняя статистика проектов за 2018–2024 гг.

Подробнее о WYSIWYG-редакторах читайте в Wikipedia.

Остались вопросы?

Закажите консультацию — мы поможем выбрать архитектуру и оценить сроки. Получите предложение в течение 2 рабочих дней.