Как мы встроили React в 20-летний Ruby-монолит, сделали Канбан и ушли в Open Core. Релиз Redmarc v0.5 Beta

от автора

Блог компании ArcFront Авторы: Алекс (arc-core) – Architect, Product & Project Manager, ArcFront. Перевод и адаптация: Елена (loc-elena) – i18n Engineer, ArcFront.


Интернет полон историй безупречного успеха. «Мы взяли модный фреймворк, переписали систему с нуля, и всё стало летать».

Но реальный мир разработки, особенно корпоративной, устроен иначе. Реальный мир – это legacy. Это системы, которые живут десятилетиями, хранят в себе терабайты критических данных, и чья миграция обойдется бизнесу дороже, чем годовой бюджет ИТ-отдела. Одной из таких систем является Redmine.

Мы в команде ArcFront любим Redmine за его гранитную надежность, абсолютную бесплатность и тотальный контроль над данными (никакого vendor lock-in). Но, будем честны, его интерфейс родом из 2006 года заставляет инженеров страдать. Создание задачи занимает секунды. Переключение контекста выбивает из потока. “Стокгольмский синдром” энтерпрайза заставляет нас терпеть боль просто потому, что “так исторически сложилось”.

Мы решили, что хватит.

Вместо того чтобы убегать на платные облачные трекеры, мы совершили хирургическую инъекцию. Мы внедрили молниеносный React SPA прямо в ядро старого Ruby-монолита. Без микросервисов. Без CORS. Без дополнительных серверов и сложной инфраструктуры.

Сегодня мы официально объявляем о завершении первого большого спринта и выпуске Redmarc v0.5 Beta. Это не просто апдейт. Это полноценный продукт, меняющий правила игры, и наш официальный переход к бизнес-модели Open Core.

Запасайтесь кофе. Это лонгрид о том, как заставить старого пса выучить новые трюки: от архитектуры инъекции и парсинга Textile до психологии интерфейсов и выживания в Open Source.


Часть 1. Визуальный продакшен: Что мы наделали

Интерфейс – это там, где живут пользователи. Прежде чем нырять в архитектурный хардкор, давайте посмотрим, ради чего всё затевалось. Мы хотели создать инструмент, в котором инженеры захотят работать.

Swimlanes и Канбан: Поток вместо списков

Самая частая просьба от ПМов и тимлидов – дайте нормальную доску. В v0.5 мы зарелизили не просто Drag & Drop доску, а выкатили полноценные Swimlanes. Ниже представлено изображение Swimlanes с задачами без фильтрации.

Swimlanes / Горизонтальные дорожки

Swimlanes / Горизонтальные дорожки

Теперь нет нужды искать подзадачу конкретной задачи в большом вертикальном списке задач, достаточно перейти на вкладку Swimlanes и на удобных горизонтальных дорожках найти нужный номер задачи сразу со всеми ее подзадачами. Вы можете сгруппировать доску задач по Исполнителю или Типу задачи и мгновенно увидеть, кто перегружен, а кто сидит без дела.

Мы применили паттерн Optimistic UI. Когда вы перетаскиваете карточку из колонки “В работе” в “Тестирование”, статус в интерфейсе меняется мгновенно. Никаких лоадеров на весь экран. Фронтенд создает иллюзию нулевой задержки, а в фоне React отправляет асинхронный запрос PUT /issues/[id].json. Если сервер не принимает статус (например, сработали ограничения Workflow Redmine) – карточка откатится назад с красивым Toast-уведомлением.

Issue Detail Panel и защита Когнитивного Кэша

Сброс визуального контекста – враг продуктивности. В старом трекере каждый клик на задачу открывает новую вкладку. Вы теряете контекст всего спринта.

В Redmarc при клике на карточку плавно выезжает боковая панель.

Issue Detail Panel / Детальная панель задачи

Issue Detail Panel / Детальная панель задачи

Прямо здесь вы можете:

  1. Использовать Inline-редактирование – клик на дату вызывает календарь, клик на статус вызывает дропдаун. Изменения улетают на сервер на лету.

  2. Работать с Custom Fields. Да, мы научили React динамически понимать ваши кастомные поля из базы Redmine и рендерить их в панели.

  3. Читать Activity Journal – мы превратили машинные логи (old_value: 1) в нормальный человеческий язык.

Command Palette и горячие клавиши (Управление без мыши)

Мы, разработчики, ненавидим тянуться за мышкой. Нажмите Ctrl+K в любом месте приложения.

Command Palette

Command Palette

Вылетает модалка в стиле VS Code или Raycast. Начните вводить текст – это поиск по задачам. Введите > – и вы в режиме команд: можно сменить тему, переключить проект, обновить данные.

Command Palette ">" / Меню команд ввод ">"

Command Palette «>» / Меню команд ввод «>»

Нажмите C на клавиатуре – и появится форма новой задачи. Клавиатурно-ориентированный UX экономит часы времени в месяц на каждом сотруднике.

Нажимаете на клавиатуре ? или иконку со знаком вопроса в левом нижнем углу – всплывает модалка с подсказками по всем горячим клавишам (шорткатам), а чтобы не было нужды учить все шорткаты в интерфейсе предусмотрены динамические Tooltips (подсказки при наведении). Постарались реализовать практические полное управление без необходимости использовать мышку.

Эстетика HSL: Slate Studio & Warm Paper

Разработчики смотрят в трекер часами. В v0.5 мы полностью переписали дизайн-систему:

  • Warm Paper (Light) – приятная глазу светлая тема. Не вызывает нагрузки на глаза при долгой работе в интерфейсе и даже при попадании солнца на монитор элементы интерфейса будут видны.

    Warm Paper / Светлая тема

    Warm Paper / Светлая тема
  • Slate Studio (Dark) – темная тема без черных элементов. Нетипичная темная тема, в которую влюбилась вся наша команда.

    Slate Studio / Темная тема

    Slate Studio / Темная тема

Часть 2. Инженерный хардкор: Инъекция паразита

Как мы заставили это работать?

Первая мысль любого фронтендера при виде Redmine: “Давайте поднимем Node.js прокси на порту 3001, будем ходить туда из React, а прокси пусть общается с Redmine”. Мы так и сделали. Это был провал. Настройка CORS, проброс сессионных кук, объяснение сисадминам, что им теперь нужно деплоить и поддерживать еще один Node-сервер… Мы поняли, что этот продукт умрет на этапе установки.

Пивот случился неожиданно. Мы перестали бороться с монолитом и стали его частью.

Redmine – это Ruby on Rails приложение. У него есть штатная архитектура плагинов. Rails-плагин имеет право объявлять свои маршруты (Routes), контроллеры (Controllers) и отдавать статику (Assets).

Вся серверная логика нашего фронтенда уместилась буквально в два файла. Первый – регистрация плагина init.rb:

Redmine::Plugin.register :redmarc do  name 'Redmarc (ArcFront)'  author 'ArcFront Team'  description 'React SPA frontend for Redmine'  version '0.5.0'  url 'https://github.com/ArcFrontDev/Redmarc'end

И единственный контроллер app/controllers/redmarc_controller.rb, который просто отдает сгенерированный Vite’ом index.html:

class RedmarcController < ApplicationController  # Важно: пропускаем штатный рендеринг layout'а Redmine,  # отдавая чистую HTML-оболочку для нашего SPA  def index    render layout: false  endend

А наш Vite (инструмент сборки фронтенда) мы настроили так, чтобы он выплевывал скомпилированный React-бандл прямо в папку assets рубишного плагина:

// vite.config.tsexport default defineConfig({  build: {    outDir: "../../assets", // Пишем бандл прямо в плагин    emptyOutDir: true,    rollupOptions: {      output: {        entryFileNames: `assets/[name].js`,        assetFileNames: `assets/[name].[ext]`,      },    },  },});

Когда сисадмин запускает штатную команду rake redmine:plugins:assets, Redmine сам копирует наш React-код в свою публичную директорию и сам же его серверит.

А как же защита от CSRF? Ведь Redmine (как и любой Rails-бэкенд) отклоняет любые POST/PUT запросы без валидного токена. Элементарно: в index.html мы пробрасываем мета-тег с токеном из сессии Redmine, а в React используем Axios-интерцептор, который подмешивает его в каждый запрос:

// Настройка axios-интерцептораconst csrfToken = document  .querySelector('meta[name="csrf-token"]')  ?.getAttribute("content");if (csrfToken) {  axios.defaults.headers.common["X-CSRF-Token"] = csrfToken;}

Результат:

  1. Никакого CORS. Фронтенд работает на том же домене и порту.

  2. Нативная аутентификация. Браузер сам шлет родную cookie _redmine_session, а наш CSRF-токен защищает запросы к REST API. Redmine уверен, что с ним общается его собственный интерфейс.

  3. Безупречный деплой. Копируй папку, жми restart. Никаких микросервисов.

Бенчмарки: Цифры не врут

Стоило ли оно того? Да. Отказ от рендеринга тяжелого HTML на стороне сервера изменил профиль производительности до неузнаваемости.

Метрика (Навигация в список задач)

Стандартный Redmine UI

Redmarc SPA (React)

Разница

Сетевой Payload

~150 KB (HTML + Assets)

~8 KB (Только JSON)

Меньше в 18 раз

Время отдачи данных (TTFB)

~350 ms (Генерация HTML)

~85 ms (Чистый JSON API)

Быстрее в 4 раза

Сброс контекста (Белый экран)

Да (при каждом клике)

Нет (Мгновенный роутинг)

Бесценно

Рекурсивная пагинация (Обходим limit=100)

Если вы работали с REST API Redmine, вы знаете эту боль. Redmine не отдает больше 100 задач за один запрос. Для стандартного интерфейса со страничной навигацией это норм. Но Канбан-доска должна отображать весь бэклог.

Мы реализовали рекурсивный фетчинг на уровне API-клиента. Вместо кнопок “Загрузить еще”, мы параллельно пускаем промисы, собирая данные по смещениям (offset=0, 100, 200…), пока не упремся в лимит. Благодаря тому, что JSON весит копейки, загрузка и рендеринг 500 задач занимает доли секунды.

Рендеринг Textile в React

У десятков тысяч компаний базы данных Redmine забиты описаниями задач в разметке Textile (звездочки, плюсики, !image.png!). Когда вы тащите это в React, вы получаете кашу. В v0.5 мы внедрили парсер, который на лету читает старую разметку Textile и рендерит её в безопасный React DOM. Жирный шрифт, код, списки – всё отображается идеально, как будто вы используете современный Markdown.


Часть 3. Sandbox: Потрогать руками за 1 клик

Мы знаем, что фронтендеры не хотят ставить себе на локальную машину Ruby, Postgres и настраивать базу данных только ради того, чтобы посмотреть на React-плагин.

В версии 0.5 мы выкатили полностью автоматизированную песочницу. У нас есть 1-Click Cloud Sandbox (через GitHub Codespaces или Daytona). Один клик в нашем README, и в облаке поднимается контейнер, который сам:

  1. Запускает БД.

  2. Накатывает миграции Redmine.

  3. Инсталлирует плагин Redmarc.

  4. Генерирует фейковые данные (проекты, задачи, статусы), чтобы доска не была пустой.

Или, если вы любите консоль, просто скачайте репозиторий и введите: docker-compose up -d

Через минуту по адресу localhost:8080 у вас будет полностью готовый к тестам стенд с уже забитыми задачами (логин: tester, пароль: password123).


Часть 4. Манифест ArcFront: Переход к Open Core

Мы вложили в Redmarc месяцы бессонных ночей. Продукт дорос до версии 0.5 Beta. Им начинают пользоваться реальные команды. К нам приходят запросы из корпоративного сектора: “Ребята, а добавьте интеграцию с LDAP, дашборды для топ-менеджмента и графики Ганта”.

И здесь мы подошли к той самой черте, где ломается чистый Open Source. Когда бесплатный проект начинает обслуживать тяжелые Enterprise-запросы, мейнтейнеры выгорают, 이슈 висят годами, а проект либо умирает, либо внезапно и со скандалом меняет лицензию на проприетарную (мы все помним драмы вокруг HashiCorp, Redis, Elastic).

Мы не хотим ни того, ни другого. Мы строим долгосрочную историю. Поэтому мы официально заявляем: начиная с релиза v0.5, Redmarc переходит на бизнес-модель Open Core (по пути GitLab).

Что это значит на практике?

  1. Репозиторий, который вы видите сейчас – это Открытое Ядро (Core). Всё, что в нём есть: Канбан, Swimlanes, темы, Command Palette, Drag & Drop – навсегда останется открытым под лицензией MIT. Вы можете форкать, использовать в своих компаниях бесплатно, навсегда. Мы будем бережно поддерживать это ядро: фиксить баги, рефакторить архитектуру, улучшать производительность. Единственное крупное нововведение, которое ждет Core в будущем – это полная локализация на 6 языков (над которой сейчас активно работает Елена).

  2. Больше никаких major-фич в бесплатной версии. Enterprise-функционал: глубокая аналитика, Time Tracking с живым таймером, массовые операции (Bulk Actions), графики Ганта, кастомные борды – будут поставляться в платной версии (Pro/Enterprise). Эти инструменты экономят корпорациям тысячи долларов и сотни часов менеджмента. Мы считаем, что брать за них деньги – это честно.

Архитектура “SPA внутри монолита” позволяет нам сделать это максимально изящно. Мы просто будем использовать Feature Flags внутри React-бандла, подключая тяжелые модули только при наличии лицензии, не разводя хаос в кодовой базе.

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


Итоги

Мы взяли 20-летний монолит, к которому все привыкли, и дали ему лицо, которое он заслуживает. Релиз Redmarc v0.5 Beta доступен прямо сейчас.

Установка по-прежнему занимает 5 минут:

cd /path/to/redmine/pluginsgit clone https://github.com/ArcFrontDev/Redmarc.git redmarcbundle installbundle exec rake redmine:plugins:assets RAILS_ENV=productiontouch tmp/restart.txt

Исходный код (Open Core), песочницы и документация лежат здесь: GitHub ArcFrontDev/Redmarc.

Мы ArcFront – небольшая команда инженеров. Над Redmarc сейчас трудятся:

  • Алекс (arc-core) – Architect, Product & Project Manager, Fullstack Developer & Software Engineer

  • Елена (loc-elena) – i18n Engineer

  • Маркус (sys-markus) – Backend & DevOps

  • Майк (ui-mike) – Frontend, UI/UX

Заходите на GitHub. Ставьте звёзды – они греют нам душу. Поднимайте песочницу, пробуйте Drag & Drop. Пишите в комментарии, если у вас есть мысли по поводу нашей архитектуры или Open Core подхода. Хабр, мы читаем всё!

Давайте вместе делать корпоративный софт, который не хочется закрыть через секунду после открытия.

ссылка на оригинал статьи https://habr.com/ru/articles/1045182/