Рубрики
Uncategorized

★ Создание панели мониторинга в реальном времени на базе Laravel, Vue, Pusher и Tailwind (издание 2018 года)

В Spatie у нас есть телевизионный экран у стены, на котором отображается приборная панель. На этой панели мониторинга отображается… Помеченный laravel, php, javascript, vue.

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

Мы открыли нашу панель мониторинга с открытым исходным кодом, чтобы вы могли просмотреть весь исходный код на GitHub . Он построен с Laravel 5.7 , Vue и Попутный ветер CSS .

Эта панель управления не совсем нова. Первоначальная версия была опубликована пару лет назад. В нашей компании нам нравится возиться с новыми вещами. Вот почему мы каждый год проводим большую очистку исходного кода. Мы также обновляем все используемые зависимости и технологии. В этом году мы сосредоточились на обновлении дизайна. Панель мониторинга теперь использует Попутный ветер CSS . В этом сообщении в блоге, которое является обновлением предыдущего , я хотел бы объяснить, как построена эта панель мониторинга.

Вам нужно будет быть знакомым с обоими Laravel и Vue чтобы извлечь максимальную пользу из этого поста. Если вам нужно освежить свои знания Vue, я могу настоятельно рекомендовать Уроки Laracasts по Vue .

История

У нас уже довольно давно есть приборная панель в Spatie. До нашей нынешней версии на базе Laravel мы использовали Dashing , фреймворк для быстрого создания информационных панелей. Фреймворк был создан сотрудниками Shopify и использует Ruby под капотом.

Когда я впервые создал нашу панель мониторинга, несколько лет назад, мы были на перепутье с нашей компанией . В мире PHP не было большого импульса, и мы подумывали о переходе на Ruby. Идея состояла в том, что, играя с Dashing, мы получим некоторый опыт владения языком. Затем появились Composer и Laravel, и мы решили придерживаться PHP (и, учитывая текущее состояние экосистемы PHP, мы совсем не сожалеем об этом выборе).

Когда поддержка Dashing официально прекратилась , я подумал, что сейчас самое подходящее время полностью перестроить панель мониторинга с помощью Laravel и Vue.

Плитки

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

  • Плитка Твиттера, которая показывает все упоминания о цитатах @spatie_be
  • Для каждого члена нашей команды есть специальная плитка. На каждой плитке отображаются задачи для этого участника на текущую неделю. Эти задачи извлекаются из нескольких файлов markdown в частном репозитории на GitHub. В этой плитке есть еще кое-что. Подробнее об этом позже.
  • Некоторая статистика наших многочисленных публичных репозиториев на GitHub. Эти данные поступают с GitHub и API Packagist
  • Командный календарь, который собирает события из календаря Google.
  • Часы с текущей датой. и некоторые погодные условия, извлекаемые из Yahoo Weather API . Мы также показываем температуру внутри нашего офиса. Эти данные получены с датчика температуры (большое спасибо Алекс за добавление этого)
  • В нашем родном городе, Антверпене, есть общая велосипедная система под названием//Bike|/. Велосипедная плитка показывает, сколько велосипедов доступно в ближайших велосипедных точках рядом с нашим офисом.

Чтобы помочь всем оставаться “в зоне”, мы купили всю команду Наушники Bose QuietComfort . Панель мониторинга отображает текущий трек для каждого члена команды на его плитке участника команды. Аватар будет заменен обложкой альбома. Мы используем API last.fm чтобы получить эту информацию.

На плитке члена команды также будет отображаться маленькая корона, когда у кого-то день рождения?

Члены команды регулярно работают из дома. Когда мы не работаем в офисе в течение дня, у нас есть привычка устанавливать наш статус в Slack на “Работа из дома”. Когда член команды установит этот статус в Slack, мы отобразим симпатичный маленький смайлик палатки.

Обзор высокого уровня

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

Каждая плитка является собственным компонентом Vue. Планировщик Laravel по умолчанию используется для периодической выборки некоторых данных из API-интерфейсов Календаря Google , Last.fm и т.д… Когда Laravel получает ответ от любой из этих служб, событие трансляции запускается в Толкатель . Этот мощный сервис использует WebSockets для передачи событий сервера клиентам в режиме реального времени. На стороне клиента мы будем использовать Laravel Echo . Эта библиотека JavaScript очень упрощает обработку этих событий-толкателей. По-прежнему на стороне клиента каждый компонент Vue будет прослушивать входящие события для обновления отображаемых плиток.

Сетка

Прежде чем углубиться в код Laravel и Vue, я хотел бы объяснить, как работает система grid. Система сетки позволяет легко указать, где должна располагаться плитка на панели инструментов и какого она должна быть размера.

Это HTML-код фактического представления блейда , который отображает страницу панели мониторинга.


    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

Столбцы сетки обозначаются буквой, а строки – цифрой, как в электронной таблице. Размер и расположение плитки определяются в свойстве положение для каждого компонента, который принимает имя столбца и номер строки. a1 отобразит компонент в первой строке первого столбца. Если вы посмотрите на первый компонент член команды , вы увидите b1:b4 , поэтому, как вы видите на скриншоте панели мониторинга, этот компонент будет отображаться во втором столбце, начиная с первой строки, и он имеет длину 4 позиции.

Наша панель мониторинга использует 5 столбцов и 12 строк. Хотите изменить размер своей панели мониторинга? Нет проблем: просто продолжайте добавлять плитки. Необходимое количество строк и столбцов определяется автоматически.

Большинство современных телевизоров используют соотношение 16: 9, но мы сделали все возможное, чтобы сделать макет полностью адаптивным, чтобы он по-прежнему работал на телевизорах и мониторах с другим соотношением сторон.

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

Компонент подключения к Интернету

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

По умолчанию он не отображается. При отсутствии подключения к Интернету в правом верхнем углу будет отображаться небольшое уведомление. Вот как это выглядит:

Он работает путем прослушивания события, называемого Сердцебиение , который каждую минуту отправляется сервером. Если в течение пары минут он не получит событие, он определит, что наше подключение к Интернету отключено (хотя это также может означать, что у сервера, на котором работает панель мониторинга, возникли проблемы).

Серверная сторона

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

namespace App\Events;

use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

abstract class DashboardEvent implements ShouldBroadcast
{
    public function broadcastOn()
    {
        return new PrivateChannel('dashboard');
    }
}

Это Должен транслировать интерфейс предоставляется Laravel . Все события будут транслироваться на частном канале с именем панель мониторинга . Клиент будет прослушивать все события на этом канале. Использование класса Private Channel гарантирует, что все данные будут отправлены безопасным способом, чтобы никто не мог их прослушивать. Подробнее об этом позже.

Давайте заглянем в каталог приложение/Консоль/Компоненты . Почти вся логика, необходимая серверу для извлечения данных для панели мониторинга, находится здесь. Если вы откроете этот каталог, то увидите, что у каждого компонента есть свой собственный подкаталог. В каждом подкаталоге вы найдете команду Artisan, которую можно запланировать. В нашем примере команда Приложение\Консоль\Компоненты\Отправить сердцебиение класс содержит этот код:

namespace App\Console\Components\Dashboard;

use Illuminate\Console\Command;
use App\Events\Dashboard\Heartbeat;

class SendHeartbeatCommand extends Command
{
    protected $signature = 'dashboard:send-heartbeat';

    protected $description = 'Send a heartbeat to the internet connection tile';

    public function handle()
    {
        $this->info('Sending heartbeat...');

        event(new Heartbeat());

        $this->info('All done!');
    }
}

Единственное, что делает этот код, – это отправляет Сердцебиение – событие. Эта команда запланирована для выполнения каждую минуту в ядре консоли .

Клиентская сторона

Весь код JavaScript, используемый панелью мониторинга, находится в каталоге ресурсы/активы/js . В resources/assets/js/app.js вы видите, что основной экземпляр Vue инициализируется в элементе body:

new Vue({
    el: '#dashboard',

    components: {
        Dashboard,
        Calendar,
        Statistics,
        InternetConnection,
        TeamMember,
        TimeWeather,
        Twitter,
        Uptime,
        Velo,
    },

    created() {
        this.echo = new Echo({
            broadcaster: 'pusher',
            key: window.dashboard.pusherKey,
            cluster: window.dashboard.pusherCluster,
        });
    },
});

Сами компоненты находятся в каталоге ресурсы/активы/js/компоненты . Это код подключения к Интернету .через внутри этого каталога:




В этом коде очень много всего происходит. Давайте разберемся с этим. Все, что находится между тегами <шаблон> – это HTML-код, который фактически визуализируется. Эта директива v-if гарантирует, что раздел отображается только тогда, когда переменная состояния отключена имеет значение true. Опять же, если у вас возникли проблемы с этим, проверьте серию Vue на Laracasts .

В методе created , который запускается сразу после создания компонента Vue, мы позаботимся о том, чтобы метод экземпляра Vue, называемый determineConnectionStatus , запускался каждую секунду. Эта функция отвечает за определение значения онлайн . Если последнее полученное сердцебиение произошло менее 125 секунд назад, онлайн будет истинным, в противном случае оно будет ложным.

Давайте рассмотрим, как мы можем прослушивать события. В приведенном выше коде вы увидите метод, называемый getEventHandlers . Он ожидает объект, имена свойств которого являются именами событий. Имя события – это полное имя класса события, которое отправляется сервером ( Приложение\События\Подключение к Интернету\Сердцебиение ), но без Приложения\События и с заменой \ на . . Итак, в нашем примере это будет Подключение к Интернету. Сердцебиение . Значением свойства этого объекта должна быть функция, которая будет выполняться всякий раз, когда это событие поступает с сервера.

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

Вы заметили, что компонент использует Эхо микширование? Смешение можно сравнить с чертой характера в мире PHP. Миксин содержит некоторые функции. Каждый компонент, использующий миксин, получит эти функции. Таким образом, как и черта, миксин – это способ объединения кода многократного использования.

Микширование Echo отвечает за добавление мощности Laravel Echo к компоненту. Laravel Echo – это библиотека JavaScript, которая упрощает работу с веб-сайтами. Он будет обрабатывать всю аутентификацию и связь с Pusher. Echo настраивается в app.js

Laravel Echo может работать с несколькими вещателями, здесь мы будем использовать Pusher. Этот ключ является публичной ценностью, необходимой для связи с Pusher.

Давайте вернемся и взглянем на код микширования Echo .

import { forIn } from 'lodash';

export default {
    created() {
        forIn(this.getEventHandlers(), (handler, eventName) => {
            this.$root.echo
                .private('dashboard')
                .listen(`.App.Events.${eventName}`, response => handler(response));
        });
    },
};

Всякий раз, когда создается компонент, использующий миксин, будет выполняться функция created . Он будет обрабатывать вывод getEventHandlers функция из самого компонента. Во-первых, мы создадим полное название события. Затем мы позволим Echo прослушивать события с таким именем на частном канале панель мониторинга . Всякий раз, когда приходит событие с правильным именем, мы собираемся передать ответ обработчику и выполнить его.

Компонент статистики

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

Класс FetchPackagistTotalsCommand , расположенный в приложение/Консоль/Компоненты/Статистика/FetchPackagistTotalsCommand отвечает за получение статистики пакета через API Packagist и преобразование ее в массив. После этого он запустит событие, чтобы сообщить стороне Vue о том, что доступны новые данные.

namespace App\Console\Components\Statistics;

use GuzzleHttp\Client;
use Illuminate\Console\Command;
use Spatie\Packagist\Packagist;
use App\Events\Statistics\PackagistTotalsFetched;

class FetchPackagistTotalsCommand extends Command
{
    protected $signature = 'dashboard:fetch-packagist-totals';

    protected $description = 'Fetch totals for all our PHP packages';

    public function handle()
    {
        $this->info('Fetching packagist totals...');

        $packagist = new Packagist(new Client());

        $totals = collect($packagist->getPackagesByVendor(config('services.packagist.vendor'))['packageNames'])
                ->map(function ($packageName) use ($packagist) {
                    return $packagist->findPackageByName($packageName)['package'];
                })
                ->pipe(function ($packageProperties) {
                    return [
                        'monthly' => $packageProperties->sum('downloads.monthly'),
                        'total' => $packageProperties->sum('downloads.total'),
                    ];
                });
        event(new PackagistTotalsFetched($totals));

        $this->info('All done!');
    }
}

Большая часть этого кода должна быть понятна сама по себе. Он также запланирован для периодического запуска . Давайте взглянем на событие PackagistTotalsFetched , которое отправляется:

namespace App\Events\Statistics;

use App\Events\DashboardEvent;

class PackagistTotalsFetched extends DashboardEvent
{
    /** @var int */
    public $monthly;

    /** @var int */
    public $total;

    public function __construct(array $totals)
    {
        $this->monthly = $totals['monthly'];

        $this->total = $totals['total'];
    }
}

При трансляции событий в Laravel также транслируются все общедоступные свойства события. Таким образом, используя этот код, компонент Vue может легко получить значения $ежемесячно и всего $ .

Вот компонент Vue, который отображает плитку на панели инструментов:




Обратите внимание, что в функции getEventHandlers мы обновим переменные состояния всего упаковщиков и упаковщик ежемесячно к значениям, которые мы получаем из Упаковщик итогов выбран -событие.

Темный режим

Новая функция, которую мы добавили в нашу панель мониторинга в этом году, – это темный режим. Когда солнце сядет, будет активирован темный режим. Вот как будет выглядеть приборная панель, когда на улице стемнеет.

.

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

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

namespace App\Console\Components\Dashboard;

use Carbon\Carbon;
use Illuminate\Console\Command;
use App\Events\Dashboard\UpdateAppearance;

class DetermineAppearanceCommand extends Command
{
    protected $signature = 'dashboard:determine-appearance';

    protected $description = 'Determine the looks of the dashboard';

    /** @var float */
    protected $antwerpLat = 51.260197;

    /** @var float */
    protected $antwerpLng = 4.402771;

    public function handle()
    {
        $this->info('Determining dashboard appearance...');

        $appearance = $this->sunIsUp()
            ? 'light-mode'
            : 'dark-mode';

        event(new UpdateAppearance($appearance));

        $this->info('All done!');
    }

    public function sunIsUp(): bool
    {
        $sunriseTimestamp = date_sunrise(
            now()->timestamp,
            SUNFUNCS_RET_TIMESTAMP,
            $this->antwerpLat,
            $this->antwerpLng
        );
        $sunrise = Carbon::createFromTimestamp($sunriseTimestamp);

        $sunsetTimestamp = date_sunset(
            now()->timestamp,
            SUNFUNCS_RET_TIMESTAMP,
            $this->antwerpLat,
            $this->antwerpLng
        );
        $sunset = Carbon::createFromTimestamp($sunsetTimestamp);

        return now()->between($sunrise, $sunset);
    }
}

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

На стороне клиента у нас есть компонент Панель мониторинга , который прослушивает это событие. Он устанавливает либо светлый режим , либо темный режим в качестве класса css на панели инструментов.




В нашем css мы меняем несколько переменных , когда применяется этот темный режим класс.

.dark-mode {
    --text-default: var(--text-default-dark);
    --text-dimmed: var(--text-dimmed-dark);
    --text-invers: var(--text-invers-dark);
    --text-accent: var(--text-accent-dark);

    --bg-screen: var(--bg-screen-dark);
    --bg-tile: var(--bg-tile-dark);
    --bg-warn: var(--bg-warn-dark);
    --bg-error: var(--bg-error-dark);
}

Отображение неработающих сайтов

Еще одна вещь, которую отображает панель мониторинга, – это URL-адреса неработающих клиентских сайтов. За кулисами он использует веб-крючки из О Боже! , служба мониторинга веб-сайтов , которая Маттиас Гениар и я запустил его некоторое время назад.

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

Давайте посмотрим, как это работает за кулисами. При входе в О Боже! перейдите на экран уведомлений, чтобы настроить веб-крючок. Я ввел URL-адрес нашей панели мониторинга (секрет веб-крючка на скриншоте не является настоящим секретом).

Чтобы легко справиться, О Боже! webhook вызывает нашу панель мониторинга, использующую пакет ohdearapp/laravel-oh dear-webhooks В файле маршруты есть маршрут, где О Боже! события будут получены.

Route::ohDearWebhooks('/oh-dear-webhooks');

Когда, о Боже! обнаруживает, что у одного из сайтов, которые он отслеживает, есть проблема, он вызовет /oh-dear-webhooks . В зависимости от типа проблемы будут срабатывать определенные события . Вот код фактического подписчика события на нашей панели мониторинга.

namespace App\Services\OhDearWebhooks;

use Illuminate\Events\Dispatcher;
use App\Events\Uptime\UptimeCheckFailed;
use App\Events\Uptime\UptimeCheckRecovered;
use OhDear\LaravelWebhooks\OhDearWebhookCall;

class EventSubscriber
{
    public function onUptimeCheckFailed(OhDearWebhookCall $ohDearWebhookCall)
    {
        $site = $ohDearWebhookCall->site();

        event(new UptimeCheckFailed($site['id'], $site['url']));
    }

    public function onUptimeCheckRecovered(OhDearWebhookCall $ohDearWebhookCall)
    {
        $site = $ohDearWebhookCall->site();

        event(new UptimeCheckRecovered($site['id'], $site['url']));
    }

    public function subscribe(Dispatcher $events)
    {
        $events->listen(
            'ohdear-webhooks::uptimeCheckFailed',
            'App\Services\OhDearWebhooks\EventSubscriber@onUptimeCheckFailed'
        );

        $events->listen(
            'ohdear-webhooks::uptimeCheckRecovered',
            'App\Services\OhDearWebhooks\EventSubscriber@onUptimeCheckRecovered'
        );
    }
}

Вы можете видеть это, когда О Боже! приходит событие (например, oh dear-webhooks:: проверка работоспособности не удалась ) мы собираемся запустить новое собственное событие (например, Проверка Времени Безотказной Работы Не удалась ). Наши собственные события расширяют Событие панели мониторинга , что, как объяснено выше, означает, что они будут транслироваться через Pusher в браузер.

Это код компонента Время безотказной работы Vue , который получает события на стороне клиента.




Как только он появится Сбой/| что отображается плитка Время безотказной работы .

– О Боже! веб-крючки в ваших приложениях совсем не сложны. Более подробную информацию вы найдете в пакете webhooks в разделе “О боже! документация .

Безопасность

Поскольку отображается некоторая полусекретная информация (задачи членов нашей команды и события в календаре), мы добавили некоторую безопасность в панель мониторинга. Вот почему вы не можете просто посетить https://dashboard.spatie.be .

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

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

Отображение приборной панели на телевизоре

За нашим телевизором находится Raspberry Pi 2 , на котором отображается приборная панель. Он питается от USB-порта телевизора и имеет небольшой Wifi ключ для подключения к Интернету, поэтому кабели вообще не нужны.

В Pi использовалась ОС по умолчанию Raspian OS . Когда он будет включен, он автоматически запустит Chromium 56 и отобразит содержимое https://dashboard.spatie.be .

Перезагрузка панели мониторинга

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

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

Каждый компонент Vue сохраняет свое собственное состояние в данных . Разве не было бы здорово сохранять эти данные всякий раз, когда они изменяются? Затем его можно будет перезагружать всякий раз, когда включается приборная панель. Состояние Сохранения –микширование, которое используется почти в каждом компоненте Vue, делает именно это.

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

Мы извлекли этот микс в его собственный пакет, чтобы вы могли использовать его в своих проектах: пространственный/vue-сохранить-состояние

Используемые пакеты

Панель мониторинга извлекает данные из различных источников: Google Календарь, Packagist, Lastfm,… Вот список пакетов, используемых для извлечения данных:

Предыдущие итерации

Мы создали нашу информационную панель пару лет назад. Каждый год мы повторяем это. Вот несколько скриншотов с самой первой версии до самой последней.

Заключительные замечания

Я надеюсь, что вам понравился этот небольшой экскурс по коду нашей панели мониторинга. У меня нет намерения создавать полную документацию для панели мониторинга и делать ее защищенной от обезьян, так как для этого потребуется слишком много времени. С другой стороны, я действительно думаю, что если у вас есть некоторый опыт работы с Laravel и Vue, то не так уж сложно создать свою собственную панель мониторинга с помощью нашего кода.

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

Оригинал: “https://dev.to/freekmurze/building-a-realtime-dashboard-powered-by-laravel-vue-pusher-and-tailwind-2018-edition-1efk”