@pushler/vue
v1.1.0
Published
Vue.js SDK for Pushler.ru real-time messaging
Downloads
37
Maintainers
Readme
Pushler Vue SDK
Реактивный Vue.js SDK для работы с Pushler.ru WebSocket сервером.
Установка
npm / yarn / pnpm
npm install @pushler/vue
# или
yarn add @pushler/vue
# или
pnpm add @pushler/vueCDN
<!-- Последняя версия -->
<script src="https://unpkg.com/@pushler/vue"></script>
<!-- Конкретная версия -->
<script src="https://unpkg.com/@pushler/[email protected]/dist/pushler-vue.umd.min.js"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@pushler/vue"></script>Быстрый старт
Использование composable
<script setup>
import { usePushler } from '@pushler/vue';
// Создаём экземпляр Pushler
const pushler = usePushler({
appKey: 'your-app-key',
autoConnect: true, // Автоматическое подключение
});
// Подписка на канал
const channel = pushler.subscribe('my-channel');
// Слушаем события
channel.on('message', (data) => {
console.log('Получено сообщение:', data);
});
channel.on('notification', (data) => {
console.log('Уведомление:', data);
});
</script>
<template>
<div>
<p>Статус: {{ pushler.connectionState }}</p>
<p v-if="pushler.socketId">Socket ID: {{ pushler.socketId }}</p>
<button @click="pushler.connect()" :disabled="pushler.isConnected.value">
Подключиться
</button>
<button @click="pushler.disconnect()" :disabled="!pushler.isConnected.value">
Отключиться
</button>
</div>
</template>Использование Vue Plugin
// main.js
import { createApp } from 'vue';
import { PushlerPlugin } from '@pushler/vue';
import App from './App.vue';
const app = createApp(App);
app.use(PushlerPlugin, {
appKey: 'your-app-key',
autoConnect: true,
});
app.mount('#app');Затем в любом компоненте:
<script setup>
import { inject } from 'vue';
import { PushlerKey } from '@pushler/vue';
const pushler = inject(PushlerKey);
const channel = pushler.subscribe('notifications');
channel.on('new', (data) => {
console.log('Новое уведомление:', data);
});
</script>API Reference
usePushler(options)
Основной composable для работы с WebSocket подключением.
Опции
| Параметр | Тип | По умолчанию | Описание |
|----------|-----|--------------|----------|
| appKey | string | — | Ключ приложения Pushler (обязательный) |
| authEndpoint | string | '/pushler/auth' | Эндпоинт для авторизации приватных каналов |
| autoConnect | boolean | false | Автоматическое подключение при инициализации |
| reconnectDelay | number | 1000 | Задержка между попытками переподключения (мс) |
| maxReconnectAttempts | number | 5 | Максимальное количество попыток переподключения |
Возвращаемые значения
Реактивное состояние
const pushler = usePushler(options);
// Состояние подключения: 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'failed'
pushler.connectionState // Ref<string>
// ID сокета (после подключения)
pushler.socketId // Ref<string | null>
// Ошибка (если есть)
pushler.error // Ref<string | null>
// Количество попыток переподключения
pushler.reconnectAttempts // Ref<number>
// Вычисляемые свойства
pushler.isConnected // ComputedRef<boolean>
pushler.isConnecting // ComputedRef<boolean>
pushler.isReconnecting // ComputedRef<boolean>Методы
// Подключение к серверу
pushler.connect(options?: { appKey? })
// Отключение
pushler.disconnect()
// Подписка на канал
const channel = pushler.subscribe('channel-name', {
signature?: 'pre-signed-signature',
user?: { id: 1, name: 'User' } // для presence-каналов
})
// Отписка от канала
pushler.unsubscribe('channel-name')
// Получение канала
const channel = pushler.channel('channel-name')
// Получение списка каналов
const channels = pushler.getChannels()
// [{ name, fullName, type, subscribed }]
// Подписка на глобальные события
pushler.on('connected', (data) => console.log('Connected:', data.socketId))
pushler.on('message', (msg) => console.log('Message:', msg))
pushler.on('auth_error', (err) => console.error('Auth error:', err))
// Отписка от событий
pushler.off('connected', handler)Канал
const channel = pushler.subscribe('my-channel');
// Свойства канала
channel.name // Полное имя с appKey
channel.originalName // Оригинальное имя
channel.type // 'public' | 'private' | 'presence'
channel.subscribed // boolean
// Методы
channel.on('event-name', callback)
channel.off('event-name', callback)
channel.unsubscribe()Типы каналов
// Публичные каналы (без авторизации)
pushler.subscribe('public-channel')
// Приватные каналы (требуют авторизации через бэкенд)
pushler.subscribe('private-channel')
// SDK автоматически запросит подпись с authEndpoint
// Или можно передать предварительно полученную подпись
pushler.subscribe('private-channel', {
signature: 'hmac-signature-from-backend'
})
// Presence-каналы (информация о присутствии)
pushler.subscribe('presence-chat', {
user: {
id: 123,
name: 'Иван Петров',
avatar: 'https://...'
}
})Безопасность
⚠️ Важно: Секретный ключ
НИКОГДА не храните секретный ключ в клиентском коде!
Секретный ключ (secret) должен храниться только на вашем бэкенде. Для приватных каналов SDK автоматически запрашивает подпись с вашего бэкенда через authEndpoint.
Схема авторизации приватных каналов
┌─────────────┐ 1. subscribe('private-chat') ┌─────────────────┐
│ Browser │ ──────────────────────────────────►│ Pushler WS │
│ (SDK) │ │ Server │
└──────┬──────┘ └────────┬────────┘
│ │
│ 2. POST /pushler/auth │
│ { channel, socket_id } │
▼ │
┌─────────────┐ 3. Проверка авторизации │
│ Ваш │ + генерация подписи (с secret) │
│ Backend │ ───────────────────────────────────────────►│
└─────────────┘ 4. signature │Пример бэкенда для авторизации
// Laravel пример
Route::post('/pushler/auth', function (Request $request) {
$user = auth()->user();
// Проверяем право доступа к каналу
$channelName = $request->channel_name;
if (!$user->canAccessChannel($channelName)) {
return response()->json(['error' => 'Forbidden'], 403);
}
// Генерируем подпись
$socketId = $request->socket_id;
$signature = hash_hmac('sha256', "{$socketId}:{$channelName}", env('PUSHLER_SECRET'));
return response()->json(['signature' => $signature]);
});Кастомные заголовки авторизации
Вы можете передать дополнительные заголовки для запросов авторизации:
// Статические заголовки
const pushler = usePushler({
appKey: 'your-app-key',
authEndpoint: '/api/pushler/auth',
authHeaders: {
'Authorization': 'Bearer your-token',
'X-Custom-Header': 'value'
}
});
// Динамические заголовки (рекомендуется)
const pushler = usePushler({
appKey: 'your-app-key',
authEndpoint: '/api/pushler/auth',
getAuthHeaders: () => ({
'Authorization': `Bearer ${authStore.token}`
})
});При использовании authHeaders или getAuthHeaders SDK не отправляет cookies (credentials: 'include').
Promise-based API
Ожидание подключения
connect() возвращает Promise, который резолвится при успешном подключении:
const pushler = usePushler({ appKey: 'your-app-key' });
// Ожидаем подключения
const { socketId } = await pushler.connect();
console.log('Подключены с socketId:', socketId);
// Теперь можно подписываться на приватные каналы
pushler.subscribe('private-user-123');Метод waitForConnection()
Для случаев, когда нужно дождаться соединения с таймаутом:
try {
await pushler.waitForConnection(10000); // 10 секунд
console.log('Подключены!');
} catch (error) {
console.error('Не удалось подключиться:', error.message);
}usePushlerChannel(pushler, channelName, options)
Composable для работы с отдельным каналом.
<script setup>
import { usePushler, usePushlerChannel } from '@pushler/vue';
const pushler = usePushler({ ... });
const { channel, isSubscribed, events, lastEvent, on, clearEvents } = usePushlerChannel(
pushler,
'notifications',
{
autoSubscribe: true,
maxEvents: 50
}
);
on('new', (data) => {
console.log('Новое уведомление:', data);
});
</script>
<template>
<div>
<p>Подписан: {{ isSubscribed }}</p>
<p>Последнее событие: {{ lastEvent }}</p>
<ul>
<li v-for="event in events" :key="event.timestamp">
{{ event.event }}: {{ event.data }}
</li>
</ul>
<button @click="clearEvents">Очистить</button>
</div>
</template>Константы
import { ConnectionStates, ChannelTypes } from '@pushler/vue';
// Состояния подключения
ConnectionStates.CONNECTING // 'connecting'
ConnectionStates.CONNECTED // 'connected'
ConnectionStates.DISCONNECTED // 'disconnected'
ConnectionStates.RECONNECTING // 'reconnecting'
ConnectionStates.FAILED // 'failed'
// Типы каналов
ChannelTypes.PUBLIC // 'public'
ChannelTypes.PRIVATE // 'private'
ChannelTypes.PRESENCE // 'presence'Примеры
Чат в реальном времени
<script setup>
import { ref } from 'vue';
import { usePushler } from '@pushler/vue';
const pushler = usePushler({
appKey: 'chat-app-key',
autoConnect: true,
});
const messages = ref([]);
const newMessage = ref('');
// Подписка на канал чата
const chatChannel = pushler.subscribe('chat-room-1');
chatChannel.on('message', (data) => {
messages.value.push(data);
});
chatChannel.on('user_joined', (data) => {
messages.value.push({
type: 'system',
text: `${data.name} присоединился к чату`
});
});
async function sendMessage() {
// Отправка через ваш API
await fetch('/api/chat/send', {
method: 'POST',
body: JSON.stringify({
channel: 'chat-room-1',
message: newMessage.value
})
});
newMessage.value = '';
}
</script>Уведомления
<script setup>
import { usePushler } from '@pushler/vue';
import { useNotifications } from './composables/useNotifications';
const pushler = usePushler({ autoConnect: true, ... });
const { show } = useNotifications();
// Приватный канал для уведомлений пользователя
const notificationChannel = pushler.subscribe('private-user-notifications');
notificationChannel.on('new', (data) => {
show({
title: data.title,
message: data.message,
type: data.type || 'info'
});
});
notificationChannel.on('badge_update', (data) => {
// Обновление счётчика непрочитанных
document.title = data.count > 0
? `(${data.count}) Мой сайт`
: 'Мой сайт';
});
</script>Индикатор набора текста (Typing indicator)
<script setup>
import { ref, computed } from 'vue';
import { usePushler } from '@pushler/vue';
const pushler = usePushler({ ... });
const typingUsers = ref(new Set());
const channel = pushler.subscribe('presence-chat', {
user: { id: currentUserId, name: currentUserName }
});
channel.on('typing_start', (data) => {
typingUsers.value.add(data.user_name);
});
channel.on('typing_stop', (data) => {
typingUsers.value.delete(data.user_name);
});
const typingText = computed(() => {
const users = Array.from(typingUsers.value);
if (users.length === 0) return '';
if (users.length === 1) return `${users[0]} печатает...`;
if (users.length === 2) return `${users.join(' и ')} печатают...`;
return `${users.length} человек печатают...`;
});
</script>Использование через CDN
<!DOCTYPE html>
<html>
<head>
<title>Pushler Vue Example</title>
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/@pushler/vue"></script>
</head>
<body>
<div id="app">
<p>Status: {{ connectionState }}</p>
<p v-if="socketId">Socket ID: {{ socketId }}</p>
<button @click="connect" :disabled="isConnected">Connect</button>
<button @click="disconnect" :disabled="!isConnected">Disconnect</button>
</div>
<script>
const { createApp } = Vue;
const { usePushler } = PushlerVue;
createApp({
setup() {
const pushler = usePushler({
appKey: 'your-app-key'
});
return {
connectionState: pushler.connectionState,
socketId: pushler.socketId,
isConnected: pushler.isConnected,
connect: () => pushler.connect(),
disconnect: () => pushler.disconnect()
};
}
}).mount('#app');
</script>
</body>
</html>TypeScript
SDK включает TypeScript определения. Импортируйте типы:
import type {
UsePushlerReturn,
PushlerChannel,
ChannelInfo,
ConnectionState,
ChannelType
} from '@pushler/vue';Лицензия
MIT
