Как мы писали фронтенд собственной панели управления хостингом: фреймворк и бэкдоры


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

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

    Выбор фреймворка: почему искали новый


    Предыдущая панель была реализована на собственном фреймворке, написанном на jQuery. Мы сидели на VMManager, он требовал много доработок: по интерфейсу и функционалу, было тяжело сопровождать такой код. Добавление нового функционала в панель со стороны фронта занимало много времени. Понятно, что при желании и на jQuery можно реализовать хороший фреймворк (я до сих пор люблю jQuery) или даже подобие CMS, но это был не оптимальный вариант: начиная скудной документацией по самописному фреймворку и заканчивая не совсем корректной архитектурой самого приложения.

    Старая панель была реализована в виде Single Page Application и на этом его хорошие качества заканчивались. После решения очередной головоломки по добавлению кнопки в список, пришло понимание, что нужна альтернатива. Выбор пал на Vue.

    Почему SPA?


    Single Page Application — идеальный выбор для панели управления. Панель управления в плане рендеринга довольно простая штука, эту работу можно легко доверить браузеру пользователя. К тому же панели не важна SEO-оптимизация, для этого у нас есть основной сайт. Ну и требуемое время на начальную загрузку всех необходимых скриптов пользователи панели воспринимают спокойно в силу специфики самих этих пользователей. Опять же, бекенд у нас получился классическим RestAPI сервисом — для предоставления в будущем открытого API нашим клиентам.

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

    Почему Vue?


    3 года назад Vue был относительно молодым фреймворком, но уже тогда о нем много говорили и писали, и когда вышел релиз версии 2.0, мы решили сделать ставку на него — и не прогадали. Сначала планировали просто постепенно заменять какие-то компоненты написанные на jQuery и Vue это позволял делать легко. Но потом, после того, как были переписаны довольно объемные компоненты, все таки решили, что лучше переписать вообще все приложение на Vue.
    Это бы рискованный шаг и мы решили его сделать по 4 причинам:

    1. Vue — простой декларативный фреймворк, его понимают даже верстальщики. Если что, под него легко найти разработчика или просто научить товарища. А значит у нас не будет проблем с поиском нового разработчика и его вхождением в проект, если меня переедет трамвай (хвала богам, в моем городе их нет).
    2. Vue объективно хорош для написания SPA приложений.
    3. У меня перед глазами был опыт развития React и я предположил, что популярность Vue будет расти так же. Сейчас фреймворк входит в TOP-3 популярных JS-фреймворков (это легко проверить поисковым запросом), уступая только React и Angular. У него хорошая поддержка, развитая экосистема и большое комьюнити.
    4. Скорость разработки. Лично я сразу стал воспринимать Vue как этакий конструктор и разработка на нем идет довольно быстро: если мне нужен, например, компонент выбора даты, скорее всего на Vue он уже существует, свободен в использовании и опробован сообществом. Я просто устанавливаю компонент в проект, пишу тег и все работает. По сути, наша панель состоит на 70-80% из готовых библиотек. Я имею в виду именно использование компонента, а не размер кодовой базы, который можно проверить командой типа: npx intrinsic/loc

    При реализации проекта всегда учитываешь его перспективы, особенно перспективы развития. И то, что в экосистеме Vue уже имеются такие инструменты как Weex, Quasar Framework или Nuxt по мне существенно расширяют горизонты развития нашей панели.

    На Хабре есть замечательная статья о Vue от его создателя, а я расскажу о некоторых особенностях нашего приложения.

    Синхронизация Vuex с сервисом RestAPI


    Часть данных глобального хранилища Vuex в нашем приложении синхронизируется с RestAPI путем обыкновенных запросов по соответствующим адресам. Зачем мы так сделали? Ну хотя бы для того, чтобы основные настройки пользователя не были привязаны к конкретному браузеру конкретного устройства. Можно зайти в нашу панель с компьютера жены или из игрового клуба и при этом получить в то же знакомое окружение, что и было у вас на своей родной машине.

    Кроме того, когда синхронизация была только с localStorage, некоторые браузеры при обновлениях теряли содержимое localStorage — оно полностью удалялось. Да и в последнее время прослеживается какая то тенденция к ужесточению политики хранения данных пользователей в куках, например функция в WebKit Intelligent Tracking Prevention — не ровен час они доберутся и до localStorage.

    Шина событий


    Да, мы используем глобальную шину событий. Как и в любом другом крупном приложении с множеством компонентов, рано или поздно возникает необходимость наладить взаимодействие между не связанными между собой компонентами. Даже через глобальное хранилище. Понятно, что если есть связь родитель-потомок, их взаимодействие стандартно организуется через свойства props в одну сторону и методом $emit в другую, ну или через хранилище, как и описано в рекомендациях Vue.

    Но в документации описана и возможность использования глобальной шины событий. У нас в проекте куча форм с разными наборами полей и в некоторых случаях (их немного, но все из них принципиальные) нужно как-то по особенному реагировать на изменение значения поля. Хранить в глобальном хранилище значения всех полей каждой формы не имеет смысла:

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

    Поэтому я решил использовать механизм шины событий. При этом ничто не мешает использовать свой Event emitter — главное использовать этот механизм аккуратно, только для исключительных ситуаций и тщательно все подчищать за собой.

    Взаимодействие RestAPI с панелью


    Для большей отзывчивости интерфейса в старом jQuery-фреймворке обратная связь от RestAPI к клиентскому приложению эмулировалась через хитрую систему таймеров: она производила опросы RestAPI с определенным интервалом и перерисовывала узлы DOM, которые затронули изменения.

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

    Для решения этой проблемы в новой панели я решил использовать связку из модуля Nchan для веб сервера Nginx и новых возможностей HTML5-интерфейсов — EventSource и WebWorker.

    Модуль Nchan поддерживает отправку сообщений через Websocket, EventSource и Long-Polling. Я провел несколько тестов и решил использовать EventSource: сообщения могут быть только текстовыми и поток сообщений осуществляется только в одну сторону (от сервера). Это полностью решало поставленную задачу.

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

    Бэкдоры: как и зачем я проверяю безопасность компонентов


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

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

    Я всегда проверяю пакеты на наличие хуков preinstall, install и postinstall в поле “scripts” файла “package.json”. Кроме того, использую статические анализаторы пакетов, такие как retire, snyk и команду “audit” пакетного менеджера npm.

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

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

    После того, как пакет прошел анализ, я обязательно фиксирую его версию. Если нужна другая версия — пакет проходит анализ заново. Да, это занимает время, но оно того стоит.

    Пока бэкдоры ни разу не попадали к нам на продакшн.

    Много-много комментариев


    Как я уже говорил, Vue был выбран за простоту и декларативность. В дополнение к этому я пишу много комментариев, практически к каждой строчке: чтобы в случае чего новый разработчик мог легко войти в проект и чтобы я сам легко возвращался к старым кускам кода.

    За что я полюбил новый фронтенд и панель в целом


    Стало проще поддерживать код


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

    Клиенты могут быстро получать то, что запрашивали




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

    Я открыт к вопросам


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

    И, конечно, приглашаю высказать пожелания по улучшению панели.
    Хостинг-технологии
    114,13
    VDS хостинг от разработчиков для разработчиков
    Поделиться публикацией

    Комментарии 14

      0
      А связь между складками при такой схеме получается? Т. е. на одной я что-то от редактировал и это отражается на другой.
        0
        Я думаю что классическое решение это localStorage events
          0
          Получается, но не во всем.
          Вот списки таким образом синхронизируются. Например изменение статуса или добавление/удаление записи, а вот формы пока нет.
          Это же зависит от того, что нам шлет бэк.
          А вообще это, кстати, хорошая идея.
          +1
          Пока бэкдоры ни разу не попадали к нам на продакшн.

          А вот сколько их выявили вообще? Как часто попадаются?
            0
            Не нашли !== Не попадали
              0
              И? Вопрос был просто о найденных бэкдорах во время разработки. Ещё и с примерчиками бы.
              0
              Ну тут неправильно наверное выражена мысль.
              Имелось в виду из известных таких пакетов.
              Конечно, мы не исследуем полностью код устанавливаемых пакетов, потому и используем анализаторы.
              Вполне возможно что и что то лежит себе тихонько, до того момента как попадет в базу этих анализаторов.

              Тут ведь как раз и возникает вопрос — свой репозитарий с меньшим риском, или общий, с большим риском.
              Плюс сюда вопрос затрат.
              Поэтому и ведутся какие то работы по минимизации рисков, это дешевле и быстрее ))
              0
              Во-вторых, все наши формы генерируются динамически и набор полей у любой формы может поменяться кардинально.


              А в каком виде бэк передаёт описание формы, если не секрет?
                0
                Json разумеется.
                  0
                  Коню понятно, что json))

                  Интересовало, json schema или что-то своё? Именно в плане описания полей.
                    0
                    Если зайти в панель и посмотреть на трафик — то там можно увидеть в каком виде передаются формы.
                    На основе этих данных, формы уже рисуются динамически.
                  0
                  Ну как уже отметили — обычный JSON
                  Вот открытая часть API, с примерами.
                  https://vdsina.ru/tech/api
                  0

                  Около месяца являюсь пользователем vdsina. Сейчас там два виртуальных сервера. Взял на пробу, чтобы потренироваться в настройке сервера с нуля для flask.
                  Админка ощущается очень лёгкой и современной, хоть и очень простой в плане дизайна. На работе имею дело с vmmanager/ispmanager. Там всё очень мелкое и выглядещее как нечто из 90-ых. Кто-то скажет "Работает? Вот и не жалуйся." Тут я не соглашусь. Важен и внешний вид и понятность.
                  Всё, что мне было нужно в админке vdsina, я нашёл без проблем, там, где я предполагал это найти. Основные операции с сервером, ssh-ключи, регистрация домена, работа с dns.
                  Почему-то не принимались ключи ecdsa, поддержка поправила, хоть и было довольно поздно. Ещё бы добавить добавление нескольких ssh-ключей одновременно. У меня например 3 ключа: дом, работа, телефон. Добавлял один, остальные в ручную.

                    0
                    Это какое-то издевательство при регистрации не спрашивать пароль у юзера, а генерировать самим, да еще и в открытом виде отправлять на почту? В чем сложность сразу просить пароль или это у вас такой изощренный метод подтверждения почты?

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

                    Самое читаемое