Рано или поздно у любой ИТ компании (аутсорс или продуктовой) возникает желание организовать собственное пространство, где можно хранить информацию по проектам, сотрудникам, продажам. Вести рабочую переписку и обсуждать задачи/стратегии/документы. Чаще всего, такие компании начинают кодить все сами или пилят что-то для Битрикс24 и тд. В данной серии статей я расскажу о нашем велосипеде — опыте автоматизации процессов. Как положено, почти все self-hosted, opensource и постараемся обойтись почти без кодинга.
В серии статей описан пример реализации инфраструктуры, автор не призывает повторять и не претендует на "правильность" таких подходов. Некоторые части описываемой системы реально и успешно используются в нескольких организациях на протяжении 3-х лет. Автор с радостью принимает предложения по улучшению системы или предложения альтернативных решений. Пожалуйста, не разводите обсуждения типа "кому это нужно" и "что за костыли", кому не нужно, пусть не читает и не мешает комментаторам вести диалог
Итак, что же может захотеть средняя аутсорс компания от подобной системы:
– это наш скромный список. У кого-то он в разы больше, у кого-то наоборот.
Сразу возникает много вопросов, "зачем нам свой GIT сервер?", "зачем сервер чатов?" и тд. На эти и новые вопросы из комментарий я буду отвечать по мере написания статей по конкретному направлению.
Из схемы перед катом и списка выше сразу понятно, что основной частью системы будет являться подсистема управления пользователями. Исторически, да и на практике, LDAP в этих вопросах лидер. LDAP старый, сложный, но очень мощный. Я предпринимал 3 попытки затащить LDAP в разных реализациях типа OpenLDAP и других, более легковесных, но это отнимало много времени, и я решил пока обойтись без него.
Таким образом, нам придется управлять всеми действиями через RESTapi. Возможно, позже я еще раз попробую интегрировать LDAP и напишу дополнение.
В качестве основы всей системы, мы перепробовали множество OpenSource ERP/CRM систем (Odoo, Axellor и другие), пытались адаптировать OpenProject и подобные проекты. Все они по своему хороши, но одним из требований была легковесность и простота доработки. Решили выбрать что-то на PHP, благо их вагон.
И так, мы имеем EspoCRM. Штука молодая, сложно сказать что это "самый лучший" выбор, но она нам понравилась. Без кодинга можно создавать сущности, можно добавлять поля и связи, похожа на CMS, но чуть более расширенная.
После долгого предисловия, Espo будет выполнять следующие задачи:
Тут все как с любым PHP проектом. Весь процесс подробно и с картинками расписано вот тут
После установки мы попадаем вот в такой интерфейс:
И мы имеем готовую к работе CRM. Но нас сейчас интересует управление пользователями.
(Я решил использовать Русскую версию, перевод неплохой и понятный).
Идем в Администрирование -> Управление объектами -> User (Поля).
Видим очень много разных полей. Сейчас страница пользователя (для админа) выглядит вот так:
В зависимости от хотелок, можно добавить такие поля как Skype, Telegram, VK, Facebook и тд. Я добавил День рождения (странно, что его нет по дефолту), Дата начала стажировки, Дата начала обучения, Дата приема на работу, ИНН, Банк и номер расчетного счета (для документов и бухгалтера).
Мы также можем настроить видимость полей, в зависимости от роли или от любого другого поля.
Поля можно добавлять/удалять в любой момент.
Теперь идем в Администрирование -> Управление макетами -> Пользователи -> Детализация.
Видим что-то такое:
Теперь просто мышкой перетягиваем новые поля в нужные места, создаем панели и тд, все по своему вкусу. Сохраняем, и теперь вид страницы пользователя будет как в макете.
На этом настройку полей и шаблонов мы закончим, далее по тексту я буду писать что-то типа "добавим поле" и тд.
Это не мануал по EspoCRM, и я никакого отношения к этому продукту не имею. Я лишь описываю процесс создания интрасети, поэтому что-то описано более подробно, что-то наоборот.
Эту систему можно настраивать долго и беспощадно, можно менять внешний вид и тд. Но сейчас перейдем к следующей подсистеме.
Почта — крайне важный компонент системы. Ваши коллеги не обязательно должны ею активно пользоваться, есть мессенджеры и все такое. Но регистрации в разных сервисах, деловая переписка и тд — должно происходить через email.
В нашем случае — почта это не self-hosted подсистема. Конечно, можно организовать свой сервис, куча готовых решений на рынке. Но мы решили использовать Яндекс.Коннект. У них очень добрые лимиты в 1000 пользователей, достойный web-интерфейс и достаточный RESTapi. Конечно не без рекламы, но в наше время — это нормально.
Организовать почту можно по инструкциям от производителя и затем вернуться к этой статье.
Будем считать, что у нас уже есть настроенный сервис, и теперь нам надо связать почту и портал на espo.
Так как мы в целом не хотим много кодить, и интеграций у нас в будущем будет больше или они могут изменяться, мы решили использовать 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.
global.set('YaConnectToken', 'AgAAAA......');
return msg;
Так мы запишем токен в глобальные переменные и дальнейшем сможем получать его с помощью global.get() в любой функции.
Inject нужен, чтобы заставить функцию выполнится при старте Flow 1. В эту же функцию, мы будем добавлять и другие токены. Не очень удобно, но лучшего варианта я пока не нашел
{
"userName":"testuser",
"firstName":"Test",
"lastName":"User",
"emailAddress":"testuser@mydomain.ru",
"dob":"1988-01-01",
"gender":"male",
"passwordConfirm":"12345678Eiru",
"isActive":true
}
Это будет тестовый аккаунт нашего потенциального юзера.
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:
Ура! Мы сделали первый кусок интеграции!
Теперь нам нужно наконец связать базу пользователей (нашу 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 — мы настроим чуть позже.
{
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 содержит пароль который мы ввели при создании юзера, он приходит только когда мы задаем новый пароль и вводим подтверждение пароля.
Это поле нам нужно, чтобы установить его в новую учетку на почте.
Осталось все это соединить:
const user = msg.payload.entity;
if (msg.payload.event != 'afterSave') { // Если событие не про сохранение пользователя, ничего не делать
return null
}
Мы рассмотрели базовый принцип интеграции через Node-RED. Конечно, мы не учли удаление и редактирование пользователя. Ниже ссылки на готовый Flow, который все учитывает. Его можно просто скопировать и импортировать себе (обновив токены доступа конечно).
EspoCRM можно настроить под свой вкус, цвет и наладить разные процессы, но вместо нее можно использовать любую другую систему, которую вы готовы взять за основу.
В следующих статьях я расскажу:
FAQ:
Q: Зачем вообще Node-RED, если запрос можно сделать прямо из PHP к Яндексу?
A: Имея интеграцию на стороне Node-RED, мы можем менять конфигурацию, добавлять новые сервисы и хуки, на изменения пользователя, без дополнительного кодинга (не считая мелких функций на Node-RED). Процесс деплоя обновлений сводится к одной кнопке.
К сожалению, не доступен сервер mySQL