Рано или поздно у любой ИТ компании (аутсорс или продуктовой) возникает желание организовать собственное пространство, где можно хранить информацию по проектам, сотрудникам, продажам. Вести рабочую переписку и обсуждать задачи/стратегии/документы. Чаще всего, такие компании начинают кодить все сами или пилят что-то для Битрикс24 и тд. В данной серии статей я расскажу о нашем велосипеде — опыте автоматизации процессов. Как положено, почти все self-hosted, opensource и постараемся обойтись почти без кодинга.
Disclamer
В серии статей описан пример реализации инфраструктуры, автор не призывает повторять и не претендует на "правильность" таких подходов. Некоторые части описываемой системы реально и успешно используются в нескольких организациях на протяжении 3-х лет. Автор с радостью принимает предложения по улучшению системы или предложения альтернативных решений. Пожалуйста, не разводите обсуждения типа "кому это нужно" и "что за костыли", кому не нужно, пусть не читает и не мешает комментаторам вести диалог
Итак, что же может захотеть средняя аутсорс компания от подобной системы:
- База данных сотрудников с распределением прав доступа к информации
- Конечно GIT сервер, пользователи которого, будут связанны с пунктом 1
- Обязательно Chat-Server (Messenger если хотите) и конечно с пользователями из пункта 1
- Корпоративная почта, по любому с пользователями из пункта 1
- Организация процесса найма/подбора кандидатов для HR
- Трекинг проектов и задач
- Организация процесса продаж
- Организация процесса обучения и стажировок
- Роад-мап разработчиков и других сотрудников
- Геймификация некоторых процессов (для души)
– это наш скромный список. У кого-то он в разы больше, у кого-то наоборот.
Сразу возникает много вопросов, "зачем нам свой GIT сервер?", "зачем сервер чатов?" и тд. На эти и новые вопросы из комментарий я буду отвечать по мере написания статей по конкретному направлению.
Из схемы перед катом и списка выше сразу понятно, что основной частью системы будет являться подсистема управления пользователями. Исторически, да и на практике, LDAP в этих вопросах лидер. LDAP старый, сложный, но очень мощный. Я предпринимал 3 попытки затащить LDAP в разных реализациях типа OpenLDAP и других, более легковесных, но это отнимало много времени, и я решил пока обойтись без него.
Таким образом, нам придется управлять всеми действиями через RESTapi. Возможно, позже я еще раз попробую интегрировать LDAP и напишу дополнение.
В качестве основы всей системы, мы перепробовали множество OpenSource ERP/CRM систем (Odoo, Axellor и другие), пытались адаптировать OpenProject и подобные проекты. Все они по своему хороши, но одним из требований была легковесность и простота доработки. Решили выбрать что-то на PHP, благо их вагон.
И так, мы имеем EspoCRM. Штука молодая, сложно сказать что это "самый лучший" выбор, но она нам понравилась. Без кодинга можно создавать сущности, можно добавлять поля и связи, похожа на CMS, но чуть более расширенная.
Наконец к делу. Подготавливаем платформу
После долгого предисловия, Espo будет выполнять следующие задачи:
- Управление пользователями
- Портал для сотрудников с новостями и другой информацией
- Организация HR процесса
- Ведение стажеров и обучающихся
- Таск-трекер
- Организация процесса продаж (CRM все таки :))
Устанавливаем платформу
Тут все как с любым PHP проектом. Весь процесс подробно и с картинками расписано вот тут
После установки мы попадаем вот в такой интерфейс:
И мы имеем готовую к работе CRM. Но нас сейчас интересует управление пользователями.
(Я решил использовать Русскую версию, перевод неплохой и понятный).
Идем в Администрирование -> Управление объектами -> User (Поля).
Видим очень много разных полей. Сейчас страница пользователя (для админа) выглядит вот так:
В зависимости от хотелок, можно добавить такие поля как Skype, Telegram, VK, Facebook и тд. Я добавил День рождения (странно, что его нет по дефолту), Дата начала стажировки, Дата начала обучения, Дата приема на работу, ИНН, Банк и номер расчетного счета (для документов и бухгалтера).
Мы также можем настроить видимость полей, в зависимости от роли или от любого другого поля.
Поля можно добавлять/удалять в любой момент.
Теперь идем в Администрирование -> Управление макетами -> Пользователи -> Детализация.
Видим что-то такое:
Теперь просто мышкой перетягиваем новые поля в нужные места, создаем панели и тд, все по своему вкусу. Сохраняем, и теперь вид страницы пользователя будет как в макете.
На этом настройку полей и шаблонов мы закончим, далее по тексту я буду писать что-то типа "добавим поле" и тд.
Это не мануал по EspoCRM, и я никакого отношения к этому продукту не имею. Я лишь описываю процесс создания интрасети, поэтому что-то описано более подробно, что-то наоборот.
Эту систему можно настраивать долго и беспощадно, можно менять внешний вид и тд. Но сейчас перейдем к следующей подсистеме.
Почта
Почта — крайне важный компонент системы. Ваши коллеги не обязательно должны ею активно пользоваться, есть мессенджеры и все такое. Но регистрации в разных сервисах, деловая переписка и тд — должно происходить через email.
В нашем случае — почта это не self-hosted подсистема. Конечно, можно организовать свой сервис, куча готовых решений на рынке. Но мы решили использовать Яндекс.Коннект. У них очень добрые лимиты в 1000 пользователей, достойный web-интерфейс и достаточный RESTapi. Конечно не без рекламы, но в наше время — это нормально.
Организовать почту можно по инструкциям от производителя и затем вернуться к этой статье.
Будем считать, что у нас уже есть настроенный сервис, и теперь нам надо связать почту и портал на espo.
Так как мы в целом не хотим много кодить, и интеграций у нас в будущем будет больше или они могут изменяться, мы решили использовать Node-RED.
Node-RED
Некоторым покажется странным использование такой штуки, но это действительно крутой продукт, позволяющий сделать очень много интеграций за короткий промежуток времени.
У Node-RED конечно есть минусы, основные: отсутствие разграничения прав, и он не подходит для высоко-нагруженных систем. Последнее нас не волнует, так как система не будет нагруженной, а вот первое означает, что изменять конфигурацию системы сможет только тот, у кого есть логин и пароль от админки.
Ставим Node-RED по инструкции с оф. сайта, проверяем, что все работает и открываем доки на API Яндекс.Коннекта.
Нам нужно зарегистрировать новое приложение и сгенерировать ему API Token.
В платформах выбираем "Веб-сервисы" и нажимаем кнопку-ссылку "Подставить URL для разработки".
Далее, настраиваем права данному приложению.
Нас сейчас интересует только Яндекс.Коннект Directory API, и настроить права можно, к примеру, вот так:
(минимально необходимо только Управление пользователями и Чтение данных о сотрудниках)
Сохраняем и видим что-то вот такое:
Теперь нам нужно получить токен, с которым и будем делать запросы. Добрые ребята из Яндекса предусмотрели Отладочный токен, который очень легко получить и можно смело использовать для запросов.
Открываем в браузере ссылку https://oauth.yandex.ru/authorize?response_type=token&client_id=<ID>
, разрешаем доступ приложению и получаем заветный токен
Сохраняем токен в надежном месте.
Теперь, наконец, переходим к интеграции через Node-RED.
- Открываем http://localhost:1880, видим Flow 1
- Перетаскиваем из палитры inject и function
- Связываем как на картинке ниже
- Открываем настройки inject и ставим галочку "Inject once after 0.1 seconds, then"
- Открываем function и пишем такой код
global.set('YaConnectToken', 'AgAAAA......');
return msg;
Так мы запишем токен в глобальные переменные и дальнейшем сможем получать его с помощью global.get() в любой функции.
Inject нужен, чтобы заставить функцию выполнится при старте Flow 1. В эту же функцию, мы будем добавлять и другие токены. Не очень удобно, но лучшего варианта я пока не нашел
Node-RED + Яндекс.Коннект
- Кидаем на Flow вот такие ноды: inject, function, http request
- Соединяем в том же порядке
- Открываем inject, выбираем в выпадающем меню, слева от поля ввода, JSON и пишем вот такой текст
{ "userName":"testuser", "firstName":"Test", "lastName":"User", "emailAddress":"testuser@mydomain.ru", "dob":"1988-01-01", "gender":"male", "passwordConfirm":"12345678Eiru", "isActive":true }
Это будет тестовый аккаунт нашего потенциального юзера.
- Открываем ноду function и пишем следующий код:
const TOKEN = global.get('YaConnectToken'); const user = msg.payload; // Получаем данные о юзере из предыдущей ноды const body = { 'department_id': 1, // ID отдела в Яндекс.Коннект, отдел по умолчанию 1 'is_admin': false, // Новый пользователь не админ 'nickname': user.userName, 'name': { 'first': user.firstName, 'last': user.lastName }, 'birthday': user.dob, // 'YYYY-MM-DD', 'gender': user.gender, //'male/female/null' 'password': user.passwordConfirm, 'is_dismissed': user.isActive, 'position': user.title // Опционально } return { headers: { 'Authorization': 'OAuth ' + TOKEN, //'X-Org-ID': 1234 // Нужно если у Вас больше одной организации. Подробнее тут: https://wilix.org/l/wlwrtj }, payload: body }
Тут мы подготавливаем тело запроса по инструкции и устанавливаем заголовок с токеном авторизации.
Открываем ноду http request, выбираем тип запроса POST, и в url вставляем такой адрес
https://api.directory.yandex.net/v6/users/
Нажимаем кнопку Deploy в правом верхнем углу и тыкаем на вкладыш слева от нового inject (это заставит ноду отправить JSON из inject в function и запустит весь процесс)
Если все прошло удачно, на выходе http request ноды, мы получим msg со statusCode 200 или 201 и примерно такой payload:
Ура! Мы сделали первый кусок интеграции!
Интеграция EspoCRM с Node-RED
Теперь нам нужно наконец связать базу пользователей (нашу CRM) с Node-RED и в последствии с Яндексом.
Тут от нас потребуется немного PHP, но я уже все подготовил.
Достаточно создать файл UserSaved.php в директории EspoCRM по пути custom/Espo/Custom/Hooks/User
и скопировать туда вот этот код:
<?php
namespace Espo\Custom\Hooks\User;
use Espo\ORM\Entity;
class UserSaved extends \Espo\Core\Hooks\Base {
public function afterSave(Entity $entity, array $options = []) {
$entityValues = $entity->getValues();
unset($entityValues['password']);
$entityValues['isNew'] = $entity->isNew();
$data = array(
'event' => 'afterSave',
'entity' => $entityValues,
);
$this->_callRed($data);
}
public function afterRemove(Entity $entity, array $options = []) {
$entityValues = $entity->getValues();
unset($entityValues['password']);
$data = array(
'event' => 'afterRemove',
'entity' => $entityValues
);
$this->_callRed($data);
}
private function _callRed($data) {
$data_string = json_encode($data);
$ch = curl_init('__NODE_RED_ENDPOINT__');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
}
Тут мы объявляем Hook на сохранение и удаление объекта пользователя. (можно почитать в доке) Каждый раз, когда кто-то сохраняет/создает/удаляет пользователя в Espo, будет отправляться запрос на сервер Node-RED. Наверное не самый безопасный способ, но если сервер Node-RED и EspoCRM находятся на одной физической (или виртуальной) машине, можно использовать localhost и уже будет чуть секурнее.
NODE_RED_ENDPOINT — мы настроим чуть позже.
- Переключаемся обратно на Node-RED и перетягиваем из палитры следующие ноды: http in, http response, debug
- Открываем http in и настраиваем как на картинке
- Соединяем ноды вот так
- Возвращаемся к файлу UserSaved.php и меняем NODE_RED_ENDPOINT на АДРЕС_NODE_RED/user-event
- Сохраняем файл
- Нажимаем Deploy в Node-RED
- В EspoCRM идем в Администрирование -> Очистить кэш
- В EspoCRM, в левой панели открываем "Пользователи" и создаем нового юзера
- Если все прошло правильно, создастся новый юзер и в консоли Node-RED (справа нажать на вкладку с жуком) мы увидим подобный JSON
{ id: "5e430b3c59783cb41" name: "User Test" deleted: false isAdmin: false userName: "usertest" type: "regular" password: "GVhrB......" passwordConfirm: "oRM1..." authMethod: null salutationName: "Mr." firstName: "User" lastName: "Test" isActive: true isPortalUser: false isSuperAdmin: false title: "Frontend developer" emailAddress: null phoneNumber: null sendAccessInfo: false gender: "Male" createdAt: "2020-02-11 20:14:52" modifiedAt: "2020-02-11 20:14:52" dob: null inn: null emailAddressIsOptedOut: null phoneNumberIsOptedOut: null emailAddressData: array[0] phoneNumberData: array[0] defaultTeamId: null defaultTeamName: null teamsIds: array[0] teamsNames: object teamsColumns: object rolesIds: array[0] rolesNames: object portalsIds: array[0] portalsNames: object portalRolesIds: array[0] portalRolesNames: object createdById: "1" isNew: true }
Поле isNew сообщает нам, новый ли это пользователь или уже существующий
Поле passwordConfirm содержит пароль который мы ввели при создании юзера, он приходит только когда мы задаем новый пароль и вводим подтверждение пароля.
Это поле нам нужно, чтобы установить его в новую учетку на почте.
Осталось все это соединить:
- Переделываем схему как на картинке (самые первые 2 ноды не трогаем)
- Открываем ноду function и меняем строку 2 на вот это
const user = msg.payload.entity; if (msg.payload.event != 'afterSave') { // Если событие не про сохранение пользователя, ничего не делать return null }
- Нажимаем Deploy
- Идем в Espo и опять создаем нового юзера (предыдущего можно удалить)
- Если все хорошо, то в консоли Node-RED видим JSON от Яндекса с данными нового аккаунта
- Радуемся :)
Итог
Мы рассмотрели базовый принцип интеграции через Node-RED. Конечно, мы не учли удаление и редактирование пользователя. Ниже ссылки на готовый Flow, который все учитывает. Его можно просто скопировать и импортировать себе (обновив токены доступа конечно).
EspoCRM можно настроить под свой вкус, цвет и наладить разные процессы, но вместо нее можно использовать любую другую систему, которую вы готовы взять за основу.
В следующих статьях я расскажу:
- как прикрутить self-hosted GIT (не GitLab :))
- как прикрутить self-hosted чат аля Slack
- как мы организовали процесс подбора персонала для HR и как ловим и сохраняем лидов с сайта.
- и как это все синхронизируется с базой пользователей.
FAQ:
Q: Зачем вообще Node-RED, если запрос можно сделать прямо из PHP к Яндексу?
A: Имея интеграцию на стороне Node-RED, мы можем менять конфигурацию, добавлять новые сервисы и хуки, на изменения пользователя, без дополнительного кодинга (не считая мелких функций на Node-RED). Процесс деплоя обновлений сводится к одной кнопке.