- Введение
- Зависимости
- Установка
- Настройка
- Важные моменты
- API Методы
- Websockets
- События и слушатели для кастомизации и доработки серверной части
Пакет для быстрой установки чата в laravel проект.
laravel/framework: ^10.0
laravel/passport: ^11.0.0
lcobucci/jwt: ^5.0.0
composer require chequite/chat
Первым делом необходимо развернуть centrifugo. Очень удобно разворачивать через докер:
-
Подтягиваем себе образ centrifugo:
docker pull centrifugo/centrifugo
-
Создаем папку для конфигурационного файла
mkdir /var/centrifugo
-
Создаем файл конфигурации
nano /var/centrifugo/config.json
-
В файл записываем настройки. Для дефолтного использования конфиг будет выглядеть так: {
"allow_subscribe_for_client": true,
"api_key": "5af3d597bd452745a937c0ffda270fab060419e27s93f61d4f48e3409015df9ece",
"token_hmac_secret_key": "8b1482f10d8d47ac838549c8eac07edf3077a00ac6f6886fd5434f896c401cfb8e",
"admin": true,
"admin_password": "MoskowCityB@c9",
"admin_secret": "3fb95b5b93dad7269b7275c525726e86f47e123f1df1158770bfdc27a2fbab1e",
"allowed_origins": ["http://localhost"]
}
allow_subscribe_for_client - Разрешаем подписку на каналы
api_key - ключ для апи запросов, сгенерируйте рандомную строку и вставьте сюда
token_hmac_secret_key - секретный ключ для приложения, тоже генерируется рандомная строка
admin - включаем админ панель для дебага и отслеживания работы центрифуги
admin_password - пароль для входа в админ панель
admin_secret - ключ для запросов под админом
allowed_origins - Разрешенные домены. Необходимо указать все домены с которых будут отправляться запросы. Если не укажете, то будете получать ошибку 403. -
Поднимаем контейнер
docker run -v /var/centrifugo/config.json:/centrifugo/config.json -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json
-
После того как установили пакет и подняли центрифугу, надо добавить в .env файле ключи:
CENTRIFUGO_URL - url вашего centrifugo сервера (Пример http://3.22.83.190:8000, может быть и именованный домен)
CENTRIFUGO_API_KEY - api key, который вы указали в конфиге на сервере centrifugo (Пример 5af3d5s97bd452745a937c0fda270fab060419e2793af61d4f48e3409015df)
CENTRIFUGO_SECRET= секретный ключ, который вы также указали в конфиге на сервере centrifugo (Пример 8b1a482f10d8d47ac83s8549c8e07edf3077a00ac6f6886fd5434f896c401cf)
-
Запускаем миграции для пакета после установки:
php artisan migrate
-
Создаем символьную ссылку на папку storage:
php artisan storage:link
Официальная документация: https://centrifugal.dev/
JwtService::generateJwt($user->id)
- сгенерирует нужный токен
- Когда пользователь открыл чат
- Когда пользователь отправил сообщение
Как читать сообщения при открытом диалоге нужно думать на клиенте, эндпоинт есть ниже
3. Токен центрифуги живет час. Необходимо обновлять его в этом интервале. Я обновлял его при каждой перезагрузке страницы, т.к не силен в клиентской части
- Endpoint:
/api/get-user-info
- Method:
GET
{
"id": 1,
"avatar": "https://your-domain.ru/path/to/file",
"name": "Петр Петров"
}
Parameter | Type | Description |
---|---|---|
id |
integer | ID текущего пользователя |
name |
string | Имя текущего пользователя |
avatar |
string | Аватар текущего пользователя |
- Endpoint:
/api/chats
- Method:
GET
Получить список чатов для текущего пользователя
{
"id": 137,
"companion_name": "Михаил Михайлов",
"avatar": "https://your-domain.ru/path/to/file",
"last_message": {
"text": "Текст сообщения",
"timestamp": "15:33",
"sender_id": 9,
"sender_name": "Сергей Сергеев",
"was_read": true
}
}
Parameter | Type | Description |
---|---|---|
id |
integer | ID собеседника |
companion_name |
string | Имя собеседника |
avatar |
string | Аватар собеседника |
last_message |
object/null | Информация о последнем сообщении в чате |
last_message.text |
string | Текст последнего сообщения |
last_message.timestamp |
string | Время отправки последнего сообщения |
last_message.sender_id |
integer | ID отправителя |
last_message.was_read |
boolean | Сообщение было прочитано |
- Endpoint:
/api/chats
- Method:
POST
Создать чат для текущего пользователя с выбранным пользователем
Parameter | Type | Description |
---|---|---|
companionId |
integer | ID пользователя с которым создаем чат |
{
"chatId": 138
}
Parameter | Type | Description |
---|---|---|
chatId |
integer | ID созданного чата |
- Endpoint:
/api/chats/{chatId}/messages
- Method:
GET
Parameter | Type | Description |
---|---|---|
chatId |
integer | ID чата |
{
"chatId": 138,
"messages": {
{
"text": "Привет",
"was_read": true,
"timestamp": "16:53",
"user": {
"id": 3,
"email": "user@gmail.com",
"avatar": "https://your-domain.ru/path/to/file",
"is_online": false,
"name": "Алексей Алексеев",
"last_seen_at": "24.01.2024 15:30"
},
}
},
"companion": {
"id": 3,
"email": "user@gmail.com",
"avatar": "https://your-domain.ru/path/to/file",
"is_online": false,
"name": "Алексей Алексеев",
"last_seen_at": "24.01.2024 15:30",
}
}
Parameter | Type | Description |
---|---|---|
chatId |
integer | ID чата |
messages |
object | Список сообщений |
messages.* |
object | Информация о сообщении |
messages.*.text |
string | Текст сообщения |
messages.*.was_read |
string | Сообщение было прочитано |
messages.*.timestamp |
string | Дата отправки сообщения |
messages.*.user |
object | Информация об отправителе сообщения |
messages.*.user.id |
integer | ID отправителя |
messages.*.user.email |
string | Email отправителя |
messages.*.user.avatar |
string | Аватар отправителя |
messages.*.user.is_online |
boolean | Статус отправителя |
messages.*.user.name |
string | Имя отправителя |
messages.*.user.last_seen_at |
string | Дата последнего входа(если не онлайн) |
companion |
object | Информация о собеседнике |
companion.id |
integer | ID собеседника |
companion.email |
string | Email собеседника |
companion.avatar |
string | Аватар собеседника |
companion.is_online |
boolean | Статус собеседника |
companion.name |
string | Имя собеседника |
companion.last_seen_at |
string | Дата последнего входа(если не онлайн) |
- Endpoint:
/api/chats/{chatId}/messages
- Method:
POST
Parameter | Type | Description |
---|---|---|
chatId |
integer | ID чата |
{
"text": "Hello world!",
"user": {
"id": 5,
"email": "email@gmail.com",
"avatar": "https://your-domain.ru/path/to/file",
"is_online": false,
"name": "Александр Александров",
"last_seen_at": "25.01.2024 10:15"
},
"was_read": true,
"timestamp": "11:30"
}
Parameter | Type | Description |
---|---|---|
text |
string | Текст сообщения |
user |
object | Информация об отправителе |
user.id |
integer | ID отправителя |
user.email |
string | Email отправителя |
user.avatar |
string | Avatar отправителя |
user.is_online |
boolean | Статус отправителя |
user.name |
string | Имя отправителя |
was_read |
boolean | Было прочитано |
timestamp |
string | Дата отправки |
- Endpoint:
/api/typing
- Method:
POST
Parameter | Type | Description |
---|---|---|
chatId |
integer | ID чата в котором текущий пользователь начал или закончил печатать |
typing |
boolean | true - начал печатать, false - закончил печатать |
{
}
Пустой объект
- Endpoint:
/api/search-users
- Method:
GET
Parameter | Type | Description |
---|---|---|
search |
string | Текст поиска (имя или фамилия) |
{
{
"id": 3,
"email": "email@gmail.com",
"avatar": "https://your-domain.ru/path/to/file",
"is_online": false,
"name": "Филип Филипов",
"last_seen_at": "24.01.2024 19:17"
},
{
"id": 157,
"email": "email2@gmail.com",
"avatar": "https://your-domain.ru/path/to/file",
"is_online": true,
"name": "Филип Сергеев",
"last_seen_at": null
}
}
Parameter | Type | Description |
---|---|---|
* |
object | Информация о найденном пользователе |
*.id |
integer | ID пользователя |
*.email |
string | E-mail пользователя |
*.avatar |
string | Аватар пользователя |
*.is_online |
boolean | Статус пользователя |
*.name |
string | Имя пользователя |
*.last_seen_at |
string | Дата последнего входа (если не онлайн) |
- Endpoint:
/api/update-centrifugo-token
- Method:
GET
{
"centrifugo_token": "ass2jajdwk2kasdkaskgkbkbggb5ib5o412kf2occe3"
}
- Endpoint:
/api/chats/{chatId}/messages/{messageId}
- Method:
POST
Запрос нужен для того, чтобы когда пользователь находится чате и ему приходит сообщение, то надо сообщить серверу, что прочитано
Parameter | Type | Description |
---|---|---|
chatId |
integer | ID чата в котором делаем сообщение прочитанным |
messageId |
integer | ID сообщения которое делаем прочитанным |
{
}
Пустой объект
Для работы на клиенте с вебсокетами я использовал пакет центрифуги: https://github.com/centrifugal/centrifuge-js. По документации легко разобраться. Единственный момент. Нужно передавать centrifuge token при подписках на каналы.
- Channel name:
user-{userId}-chats
Parameter | Type | Description |
---|---|---|
id |
integer | ID собеседника |
companion_name |
string | Имя собеседника |
avatar |
string | Аватар собеседника |
last_message |
object/null | Информация о последнем сообщении в чате |
last_message.text |
string | Текст последнего сообщения |
last_message.timestamp |
string | Время отправки последнего сообщения (Формат 15:45) |
last_message.sender_id |
integer | ID отправителя |
last_message.was_read |
boolean | Сообщение было прочитано |
- Channel name:
user-{chatId}-messages
Parameter | Type | Description |
---|---|---|
text |
string | Текст сообщения |
user |
object | Информация об отправителе |
user.id |
integer | ID отправителя |
user.email |
string | Email отправителя |
user.avatar |
string | Avatar отправителя |
user.is_online |
boolean | Статус отправителя |
user.name |
string | Имя отправителя |
was_read |
boolean | Было прочитано |
timestamp |
string | Дата отправки |
- Channel name:
user-{userId}-status
Parameter | Type | Description |
---|---|---|
isOnline |
boolean | true - онлайн, false - offline |
userId |
integer | ID пользователя, у которого изменился статус |
lastSeen |
string | Нужно для того, чтобы показывать когда пользователь был последний раз, если isOnline = false |
- Channel name:
chat.{chatId}
Parameter | Type | Description |
---|---|---|
chatId |
integer | ID чата |
userId |
integer | ID пользователя, который печатает |
typing |
boolean | true - печатает, false - перестал печатать |
const sub = this.centrifuge.newSubscription(`user-${chatId}-messages`);
sub.on('publication', (response) => {
this.currentChat.messages.push(JSON.parse(response.data))
this.scrollToBottom();
});
sub.subscribe()
-
Ивент обновления чата Dd1\Chat\Events\ChatsUpdated
-
Ивент отправки сообщения/обновления текущего чата Dd1\Chat\Events\MessageSent
-
Ивент набора сообщения Dd1\Chat\Events\TypingEvent
-
Ивент обновления статуса online/offline Dd1\Chat\Events\UserStatusUpdatedEvent
Если необходимо создать новый канал для прослушивания нового события, то просто нужно создать Event и забиндить слушателя Dd1\Chat\Listeners\CentrifugoPushToChannel в ServiceProvider. Слушатель ожидает всегда 2 поля в construct:
Parameter | Type | Description |
---|---|---|
channel |
string | Название канала |
data |
array | Массив с данными, которые будут отправляться по websocket каналу |