software-development
April 20

Наш новый сайт

Сейчас я буду долго и много хвастаться и никто меня не остановит :)

https://0x08.ru/

https://x0x08.site/

Долгострой

Как-то так получилось, что очень долгое время мы не имели своего сайта. Совсем.

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

С одной стороны просто не было времени, с другой — подгорало и свербило было острое желание создать что-то «эдакое», однозначно демонстрирующее нашу нереальную крутизну в разработке.

Несколько раз собирались что-то наконец сделать и запустить — создавали прототипы, перепробовав кучу разных движков и подходов: Тильду, статичные лендинги, легкие и тяжелые CMS.

Но все это было не то что надо.

Везде оставалось ощущение, что вместо решения столь простой задачи нас пытаются накормить с лопаты говном, еще и за деньги.

Еще у нас на руках был один интересный прототип «с бородой», примерно десятилетней давности на тему построения CMS на технологиях Java Server Faces — т. е. с динамическими XHTML-страницами и возможностью их редактирования «на ходу», без пересборки и перезапуска приложения.

Такого до сих пор больше ни у кого нет (и видимо уже не будет) — нет на свете других таких #банутых, запиливших своими силами полноценную CMS на столь диких корпроративных технологиях.

А все существующие (из обнаруженных) CMS на Java и Portlet-based порталы используют либо банальные парсеры вроде Apache Velocity, либо вообще редактирование файлов на диске.

До нашей степени автоматизации не дошел больше никто из живущих. Поэтому про технологии «под капотом» обязательно расскажу в красках и деталях чуть ниже.

Вообщем было принято решение пойти своим путем и создать свой движок.

Хотя путь и занял суммарно пять лет разработки, думаю результат того стоил и этим проектом мы теперь действительно гордимся.

Бегущие строчки кода на фоне - "заблюренные" исходники самого движка, прокручиваемые в среде разработки.

В качестве основы был взят какой-то открытый одностраничный шаблон сайта-визитки, от которого уже настолько давно ничего не осталось, что я не смог найти ни одной ссылки на автора.

Если кто вдруг заметит знакомые очертания — напишите, добавлю в описание.

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

Это вам не стоковые фото по фотобанкам п#здить.

Фигурные ломаные границы - работа фреймворка Augumented UI.

Технологии

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

Взгляните на скриншот:

Это загруженный проект нашего сайта из среды разработки, обратите внимание на список модулей слева.

Вообщем чтобы не тянуть кота за яйца:

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

У нас действительно своя версия Myfaces, Jetty, Apache Httpclient и еще кучи библиотек — все очень глубоко зачищено от всего лишнего и левого, до такой степени мы #бнулись круты.

Убрано абсолютно все что касалось телеметрии (JMX, MBeans) и технологии SPI. Вся кодовая база всех используемых библиотек переведена на 21ю версию Java, с удалением SecurityManager и вообще всех устаревших API.

Представляете масштаб работы по удалению legacy-кусков из Apache Myfaces и Jetty?

Такого не делал больше никто, по крайней мере для подобных проектов.

Что получилось в итоге

3Мб сборка сервера (jar), 28Мб веб-приложение (war) и 100Мб архив с контентом.

Со всем контентом, включая видео и скриншоты.

Холодный запуск за 3 секунды, 512Мб используемой памяти и стабильная работа на самом говенном и дешевом VDS, который только смогли найти.

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

СMS «Lemmiwinks»

Да мы действительно написали собственную CMS, назвав ее в честь одного из героев мультсериала «South Park» — хомячка со сложной судьбой.

Тяжкий путь этого хомячка в полной мере отражает процесс внедрения наших проектов в крупных компаниях, именно так это и выглядит со стороны.

Ниже я немного расскажу о функционале нашей CMS и как оно вообще все работает.

Чтоб вам стало страшно.

Поддержка локализации

Начнем с самого банального и простого — с локализации.

И сама наша CMS и весь пользовательский контент — мультиязычные.

Вот так выглядят большие текстовые вставки на странице:

 <p class="text-intro">
     <h:panelGroup rendered="#{'en' == localeBean.language}">
         Having so many shiny programs on market,
         somehow there is <strong>always</strong>
         an important feature, missing in existing software.
     </h:panelGroup>
     <h:panelGroup rendered="#{'ru' == localeBean.language}">
          В 21м веке создано и уже работает очень много самого разного ПО,
          но почему-то всегда нехватает лишь <strong>одной кнопки</strong>,
          запускающей необходимый функционал.
      </h:panelGroup>
</p>

А вот так текстовые константы:

<div class="section-title">
      <h2>       
         <h:outputText value="#{siteMessages['site.messages.main.menu.about.tagline']}" />
      </h2>         
</div>

Их содержимое также можно править на ходу, без перезапуска приложения.

Для клиентских страниц ( т.е самого сайта ) язык определяется автоматически из HTTP-заголовка, передаваемого браузером.

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

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

Вот так выглядит шапка дашборда в админке для русской локализации:

А вот ее английская версия:

Чтобы не дублировать информацию, дальше все картинки будут только в русской локали. Но хочу отметить что абсолютно вся система ныне локализована на два языка: русский и английский.

Теперь про функционал.

Редактирование страниц

Это наверное самое интересное и самое крутое что есть в нашей CMS сейчас — основная фича, вокруг которой и выстроена вся система.

Вот так выглядит редактор страницы:

Обратите внимание на цифру 600 справа, это счетчик версий с возможностью отката последних 10ти правок.

Самое крутое тут это сама техническая возможность правки XHTML-шаблонов на лету, со всеми связями и вставками без пересборки и перезапуска приложения.

Реализовано это через виртуальную файловую систему, связанную с хранилищем контента через специальное API.

Вся работа происходит в памяти приложения, фиксация изменений на диске происходит в фоне, поэтому даже отказ диска не повлияет на работу движка и отдачу контента.

Фактически с помощью подобного механизма возможно добавить ключевые фичи CMS в любой Java-проект, где есть рендеринг страниц на стороне сервера:

JSP, JSF, Thymeleaf и тд — везде можно приделать возможность правки шаблонов страниц без перезапуска.

Пишите и мы приделаем функции CMS к любому самому дикому legacy, собранному на коленках пьяными матросами.

Предпросмотр страницы

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

Вот так выглядит превью главной страницы нашего сайта в CMS:

Редактирование стилей

Помимо редактирования XHTML шаблонов, у нас в CMS есть поддержка правки и стилей оформления — CSS.

Причем применяются они сразу после сохранения изменения, без всего адского геммороя с кешированием и устареванием контента — все благодаря грамотной работе с технологией Etag.

Вот так выглядит правка стилей оформления в редакторе нашей CMS:

Автоматическая минификация и препроцессоры воде SCSS у нас тоже используются, но применяются для основного оформления.

Они загружаются уже готовыми, а подобными правками через админку CMS происходит внесение мелких правок в стиле «здесь и сейчас» по требованию заказчика.

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

Редактирование Javascript

Следующая важная фича — редактирование Javascript кода, без которого динамичных сайтов не бывает.

Тут примерно такой же принцип что и со стилями:

большой код лежит отдельно и разработка ведется на Typescript, затем собираются бандлы и выкладываются в виде ресурса

А при необходимости быстрой правки, логика перекрывается мелкими вставками из редактора в админке нашей CMS.

Вот так это выглядит из редактора:

Работа с файлами

Помимо шаблонов, стилей оформления и Javascript-кода, существует и другой контент:

видеоролики, звук, шаблоны документов для скачивания.

Всем этим также необходимо управлять — загружать, обновлять, удалять и добавлять.

За работу со статичными файлами отвечает специальный раздел нашей CMS, вот так это выглядит:

Для фото и видео автоматически запускается предпросмотр, тип MIME хоть и определяется автоматически, но есть возможность его задать вручную — этого столь мелкого, но важного фукнционала почему-то не делают в других популярных CMS.

Работа с архивами

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

Выглядит и работает оно при этом как говно как-то так:

Как видите на скриншоте выше, это всегда дико перегруженный интерфейс с кучей непонятных кнопок, плюс технически сложный функционал самой реализации:

что по-вашему стоит делать с файлами если каталог удален? А как насчет переименования?

Но при этом уходить от концепции «файл-каталог» целиком и полностью не стоит — может получиться как в Magento, плоская таблица, где весь контент надо находить через поиск:

Фантазия для создания имен таких страниц заканчивается уже на втором десятке, а если их пара сотен, тысяча?

Словом проблема имеет место быть и решение не такое простое и очевидное.

Мы решили выйти из ситуации изящно, обратившись к классикам опыту компании id Software и механизму хранения ресурсов в играх Doom и Quake.

В результате получился следующий механизм:

  • Весь контент либо какая-то связанная часть упаковывается в ZIP-архив, в виде файлов и каталогов;
  • Готовый Архив загружается в CMS и его внутренняя структура, в частности полный путь к файлу с учетом каталогов используется как часть ссылки;
  • При необходимости изменений, архив обновляется и загружается заново.

Из интерфейса CMS можно посмотреть содержимое архива с ресурсами:

А вот так выглядит ссылка:

<a href="https://nodejs.org/en" class="btn1"  
   title="Node.js" target="_blank">
  <img class="metro-img lazyload" 
       alt="Node.js"
       data-src="#{ctx}/resources/f/89ef4f72-902c-4cbc-a82e-1d0da3820cee/logos/node-sm.png" 
      />      
</a>

Получается что часть пути к ресурсу это внутренний путь к файлу в архиве.

В результате имеем сразу двух убитых зайцев:

  • Очень простой, понятный и легкий интерфейс
  • Очень мощный и гибкий функционал

Поскольку архив с ресурсами готовится вне CMS, нет необходимости реализовывать всю сложную логику работы с контентом:

удаление-добавление-перемещение файлов и каталогов и так далее.

Не надо думать о правах на запись и о свободном месте на диске — операция загрузки ресурсов атомарная, а вся работа с архивом происходит в памяти. Надежность такого решения — как у стального шара, сломать хоть и возможно, но дюже проблематично.

Редактирование текста

Да да, это те самые «resource bundle», которые в нашей CMS стало возможно менять прямо во время работы системы.

Для тех кто не в курсе или далек от разработки поясняю:

Это такие текстовые файлы с расширением .properties, где в виде строк «ключ=значение» хранятся локализованные текстовые константы, отображаемые в интерфейсе приложения.

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

Мы переделали этот механизм и добавили интерфейс для управления этими строками прямо во время работы приложения: добавление, удаление и редактирование.

Получилось офигенно:

На скриншоте не видно, но ширина колонок именяется - можно таскать за вертикальный разделитель между колонками.

Такой функционал позволяет проводить локализацию и правки контента прямо на ходу — на всех поддерживаемых языках.

Да, это тоже можно без особых проблем портировать в ваш проект на Java. Думаю вы уже догадались кому писать.

Работа с входящими запросами

Следующее больное место типичной современной CMS это самая банальная форма обратной связи.

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

Вообще все эти формы обратной связи для CMS это что-то вроде нехорошей и заразной болезни, о которой не принято говорить в приличном обществе.

Есть и ладно, все равно никто не пользуется (типичное заблужение).

Мы вдоволь наелись этого во время работы с чужими CMS, затем говна в вентилятор подкинул закон GDPR, поэтому сделали максимально круто и надежно, используя весь накопленный опыт «битой собаки».

Посмотрите какая красота получилась:

Разумеется на этой форме есть валидация, тоже красивая:

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

А вот так полученные сообщения выглядят из админки:

Есть еще одна фича в нашей CMS, которой нет в других решениях.

Эта фича важна и нужна с точки зрения безопасности, появилась она благодаря нашей долгой практике:

 только собрав лично все проблемы с обработкой и хранением присылаемых файлов можно было до такого додуматься.

Вообщем все присылаемые файлы вне зависимости от их содержимого автоматически архивируются, имя архива формируется псевдослучайным образом из UUID.

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

Вот так выглядит подобный архив:

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

Еще эти архивы в фоне проверяет антивирус, но это так, к слову.

Такой подход полностью исключает все попытки повлиять на физическое хранение присылаемых файлов, риски перезаписи и проблем с их чтением (когда имя приложенного файла в какой-то фантастической кодировке).

Импорт-экспорт данных

Еще одна крайне важная фича, почерпнутая из Atlassian Jira и регулярно применяемая в проектах для наших заказчиков — импортирование и экспортирование всех пользовательских данных.

Скажу сразу:

это сложный но необходимый функционал в любой системе где есть сложная настройка.

Мы прошли весь путь и реализовали всю эту логику целиком.

Вот так выглядит процесс экпорта:

По нажатию кнопки «Экспортировать», в фоне запускается процесс сборки специального архива, содержащего все пользовательские данные:

все страницы, все текстовые константы, все бинарные файлы и метаданные.

По завершению показывается лог выгрузки со статистикой и дается ссылка на скачивание дампа.

Дамп полностью отделяемый, имеет внутри свой формат данных и версию вместо сквозной сериализации-десериализации из JSON, поэтому нет проблем с обратной совместимостью и загрузкой старых версий данных.

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

Все эти метаданные затем отображаются в админке нашей CMS:

Качество

Разумеется нам тоже интересно итоговое качество нашей работы, вот например что говорит проверялка Гугла о нашем сайте:

Не забываем о дешевом VDS-хостинге на дровах

Думаю получилось вполне достойно.

За кадром

Часть функционала осталась за кадром, поэтому буквально по паре строк на каждый.

Telegram

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

SRP

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

Поэтому никаким сниффером отловить такой пароль не получится.

MFA и TOTP

Также используется двухфакторная аутентификация, в качестве второго фактора используется одноразовый пароль, генерируемый с помощью специального приложения вроде Google Authenticator.

Такая же технология применяется в банках для авторизации и входа в современный банк-клиент:

Специальная база данных

Разумеется что задачу хранения данных мы тоже решили «по особому»:

вместо классических реляционных СУБД или популярных NoSQL мы использовали сильно кастомизированную версию Nitrite Database.

В результате остался лишь Key-Value движок и механизм хранения данных на диске.

Планы на будущее

На данный момент мы не планируем использовать нашу замечательную CMS для полноценной коммерческой разработки, в первую очередь из-за сложности верстки на XHTML, но зато давно и успешно применяем ее механизмы в других коммерческих проектах.

Пока же наша CMS это чистая демонстрация возможностей и доказательство нашей неземной крутизны в разработке.