@pushler/js 1.0.3 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -8
- package/dist/pushler-ru.cjs +128 -12
- package/dist/pushler-ru.cjs.map +1 -1
- package/dist/pushler-ru.esm.js +128 -12
- package/dist/pushler-ru.esm.js.map +1 -1
- package/dist/pushler-ru.js +128 -12
- package/dist/pushler-ru.js.map +1 -1
- package/dist/pushler-ru.min.js +1 -1
- package/dist/pushler-ru.min.js.map +1 -1
- package/package.json +1 -1
- package/src/PushlerClient.js +123 -7
- package/src/PushlerServer.js +5 -5
package/README.md
CHANGED
|
@@ -23,8 +23,7 @@ import Pushler from '@pushler/js';
|
|
|
23
23
|
|
|
24
24
|
// Создание клиента
|
|
25
25
|
const pushler = Pushler.Create({
|
|
26
|
-
appKey: 'your-app-key'
|
|
27
|
-
apiUrl: 'https://api.pushler.ru' // URL вашего бэкенда для авторизации каналов
|
|
26
|
+
appKey: 'your-app-key'
|
|
28
27
|
});
|
|
29
28
|
|
|
30
29
|
// Обработка событий подключения
|
|
@@ -57,8 +56,7 @@ import Pushler from '@pushler/js';
|
|
|
57
56
|
// Создание серверного клиента
|
|
58
57
|
const server = Pushler.Server({
|
|
59
58
|
appKey: 'your-app-key',
|
|
60
|
-
appSecret: 'your-app-secret'
|
|
61
|
-
apiUrl: 'https://api.pushler.ru'
|
|
59
|
+
appSecret: 'your-app-secret'
|
|
62
60
|
});
|
|
63
61
|
|
|
64
62
|
// Отправка сообщения в канал
|
|
@@ -84,19 +82,44 @@ await server.trigger(
|
|
|
84
82
|
```javascript
|
|
85
83
|
const pushler = Pushler.Create({
|
|
86
84
|
appKey: 'your-app-key', // Обязательно
|
|
87
|
-
apiUrl: 'https://api.pushler.ru', // URL вашего бэкенда для авторизации каналов
|
|
88
85
|
authEndpoint: '/pushler/auth', // Путь для авторизации приватных каналов
|
|
89
86
|
autoConnect: true, // Автоподключение (по умолчанию: true)
|
|
90
87
|
reconnectDelay: 1000, // Задержка переподключения (мс)
|
|
91
|
-
maxReconnectAttempts: 5
|
|
88
|
+
maxReconnectAttempts: 5, // Максимум попыток переподключения
|
|
89
|
+
|
|
90
|
+
// Кастомные заголовки авторизации (опционально)
|
|
91
|
+
authHeaders: {
|
|
92
|
+
'Authorization': 'Bearer your-token'
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// Или динамические заголовки
|
|
96
|
+
getAuthHeaders: () => ({
|
|
97
|
+
'Authorization': `Bearer ${getToken()}`
|
|
98
|
+
})
|
|
92
99
|
});
|
|
93
100
|
```
|
|
94
101
|
|
|
102
|
+
#### Promise-based подключение
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// connect() возвращает Promise
|
|
106
|
+
const { socketId } = await pushler.connect();
|
|
107
|
+
console.log('Подключено! Socket ID:', socketId);
|
|
108
|
+
|
|
109
|
+
// Или с таймаутом
|
|
110
|
+
try {
|
|
111
|
+
await pushler.waitForConnection(10000); // 10 секунд
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('Не удалось подключиться:', error.message);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
95
117
|
#### Методы
|
|
96
118
|
|
|
97
119
|
| Метод | Описание |
|
|
98
120
|
|-------|----------|
|
|
99
|
-
| `connect()` | Подключиться к серверу |
|
|
121
|
+
| `connect()` | Подключиться к серверу (возвращает Promise) |
|
|
122
|
+
| `waitForConnection(timeout?)` | Ожидание подключения с таймаутом |
|
|
100
123
|
| `disconnect()` | Отключиться от сервера |
|
|
101
124
|
| `subscribe(channelName, options?)` | Подписаться на канал |
|
|
102
125
|
| `unsubscribe(channelName)` | Отписаться от канала |
|
|
@@ -134,7 +157,6 @@ const pushler = Pushler.Create({
|
|
|
134
157
|
const server = Pushler.Server({
|
|
135
158
|
appKey: 'your-app-key', // Обязательно
|
|
136
159
|
appSecret: 'your-app-secret', // Обязательно
|
|
137
|
-
apiUrl: 'https://api.pushler.ru', // URL API
|
|
138
160
|
timeout: 10000 // Таймаут запросов (мс)
|
|
139
161
|
});
|
|
140
162
|
```
|
package/dist/pushler-ru.cjs
CHANGED
|
@@ -207,6 +207,8 @@ class PushlerClient {
|
|
|
207
207
|
this.wsUrl = options.wsUrl || `wss://ws.pushler.ru/app/${this.appKey}`;
|
|
208
208
|
this.apiUrl = options.apiUrl;
|
|
209
209
|
this.authEndpoint = options.authEndpoint || '/pushler/auth'; // Путь для авторизации каналов
|
|
210
|
+
this.authHeaders = options.authHeaders || null; // Кастомные заголовки для авторизации
|
|
211
|
+
this.getAuthHeaders = options.getAuthHeaders || null; // Функция для динамических заголовков
|
|
210
212
|
this.autoConnect = options.autoConnect !== false;
|
|
211
213
|
this.reconnectDelay = options.reconnectDelay || 1000;
|
|
212
214
|
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
|
|
@@ -221,6 +223,11 @@ class PushlerClient {
|
|
|
221
223
|
this.reconnectTimer = null;
|
|
222
224
|
this.authTimeout = null;
|
|
223
225
|
|
|
226
|
+
// Promise для ожидания подключения
|
|
227
|
+
this.connectionPromise = null;
|
|
228
|
+
this.connectionResolve = null;
|
|
229
|
+
this.connectionReject = null;
|
|
230
|
+
|
|
224
231
|
// Автоматическое подключение
|
|
225
232
|
if (this.autoConnect) {
|
|
226
233
|
this.connect();
|
|
@@ -256,13 +263,25 @@ class PushlerClient {
|
|
|
256
263
|
|
|
257
264
|
/**
|
|
258
265
|
* Подключение к WebSocket серверу
|
|
266
|
+
* @returns {Promise} Promise, который резолвится при успешном подключении
|
|
259
267
|
*/
|
|
260
268
|
connect() {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return;
|
|
269
|
+
// Если уже подключены - возвращаем резолвленный Promise
|
|
270
|
+
if (this.connectionState === ConnectionStates.CONNECTED) {
|
|
271
|
+
return Promise.resolve({ socketId: this.socketId });
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Если уже идёт подключение - возвращаем существующий Promise
|
|
275
|
+
if (this.connectionState === ConnectionStates.CONNECTING && this.connectionPromise) {
|
|
276
|
+
return this.connectionPromise;
|
|
264
277
|
}
|
|
265
278
|
|
|
279
|
+
// Создаём новый Promise для ожидания подключения
|
|
280
|
+
this.connectionPromise = new Promise((resolve, reject) => {
|
|
281
|
+
this.connectionResolve = resolve;
|
|
282
|
+
this.connectionReject = reject;
|
|
283
|
+
});
|
|
284
|
+
|
|
266
285
|
this.connectionState = ConnectionStates.CONNECTING;
|
|
267
286
|
this.emit(Events.CONNECTION_STATE_CHANGED, this.connectionState);
|
|
268
287
|
|
|
@@ -277,7 +296,66 @@ class PushlerClient {
|
|
|
277
296
|
} catch (error) {
|
|
278
297
|
console.error('PushlerClient: Error creating WebSocket connection:', error);
|
|
279
298
|
this.handleConnectionError(error);
|
|
299
|
+
if (this.connectionReject) {
|
|
300
|
+
this.connectionReject(error);
|
|
301
|
+
this.connectionPromise = null;
|
|
302
|
+
this.connectionResolve = null;
|
|
303
|
+
this.connectionReject = null;
|
|
304
|
+
}
|
|
280
305
|
}
|
|
306
|
+
|
|
307
|
+
return this.connectionPromise;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Ожидание установления соединения
|
|
312
|
+
* @param {number} timeout - Таймаут в мс (по умолчанию 10000)
|
|
313
|
+
* @returns {Promise} Promise с socketId
|
|
314
|
+
*/
|
|
315
|
+
waitForConnection(timeout = 10000) {
|
|
316
|
+
// Если уже подключены
|
|
317
|
+
if (this.connectionState === ConnectionStates.CONNECTED && this.socketId) {
|
|
318
|
+
return Promise.resolve({ socketId: this.socketId });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Если есть активный Promise подключения
|
|
322
|
+
if (this.connectionPromise) {
|
|
323
|
+
return Promise.race([
|
|
324
|
+
this.connectionPromise,
|
|
325
|
+
new Promise((_, reject) =>
|
|
326
|
+
setTimeout(() => reject(new Error('Connection timeout')), timeout)
|
|
327
|
+
)
|
|
328
|
+
]);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Если не подключаемся - начинаем подключение
|
|
332
|
+
if (this.connectionState === ConnectionStates.DISCONNECTED) {
|
|
333
|
+
return this.connect();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Для других состояний создаём Promise, который ждёт connected события
|
|
337
|
+
return new Promise((resolve, reject) => {
|
|
338
|
+
const timeoutId = setTimeout(() => {
|
|
339
|
+
this.off(Events.CONNECTED, onConnected);
|
|
340
|
+
this.off(Events.ERROR, onError);
|
|
341
|
+
reject(new Error('Connection timeout'));
|
|
342
|
+
}, timeout);
|
|
343
|
+
|
|
344
|
+
const onConnected = (data) => {
|
|
345
|
+
clearTimeout(timeoutId);
|
|
346
|
+
this.off(Events.ERROR, onError);
|
|
347
|
+
resolve(data);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const onError = (err) => {
|
|
351
|
+
clearTimeout(timeoutId);
|
|
352
|
+
this.off(Events.CONNECTED, onConnected);
|
|
353
|
+
reject(err);
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
this.on(Events.CONNECTED, onConnected);
|
|
357
|
+
this.on(Events.ERROR, onError);
|
|
358
|
+
});
|
|
281
359
|
}
|
|
282
360
|
|
|
283
361
|
/**
|
|
@@ -367,6 +445,14 @@ class PushlerClient {
|
|
|
367
445
|
|
|
368
446
|
console.log('Connection established with socket ID:', this.socketId);
|
|
369
447
|
|
|
448
|
+
// Резолвим Promise подключения
|
|
449
|
+
if (this.connectionResolve) {
|
|
450
|
+
this.connectionResolve({ socketId: this.socketId });
|
|
451
|
+
this.connectionPromise = null;
|
|
452
|
+
this.connectionResolve = null;
|
|
453
|
+
this.connectionReject = null;
|
|
454
|
+
}
|
|
455
|
+
|
|
370
456
|
// Переподписываемся на все каналы после переподключения
|
|
371
457
|
this.resubscribeAllChannels();
|
|
372
458
|
|
|
@@ -543,6 +629,24 @@ class PushlerClient {
|
|
|
543
629
|
}
|
|
544
630
|
}
|
|
545
631
|
|
|
632
|
+
/**
|
|
633
|
+
* Получение заголовков для авторизации
|
|
634
|
+
*/
|
|
635
|
+
getAuthRequestHeaders() {
|
|
636
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
637
|
+
|
|
638
|
+
// Динамические заголовки имеют приоритет
|
|
639
|
+
if (typeof this.getAuthHeaders === 'function') {
|
|
640
|
+
const dynamicHeaders = this.getAuthHeaders();
|
|
641
|
+
Object.assign(headers, dynamicHeaders);
|
|
642
|
+
} else if (this.authHeaders) {
|
|
643
|
+
// Статические заголовки
|
|
644
|
+
Object.assign(headers, this.authHeaders);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return headers;
|
|
648
|
+
}
|
|
649
|
+
|
|
546
650
|
/**
|
|
547
651
|
* Получение подписи с бэкенда
|
|
548
652
|
*/
|
|
@@ -552,11 +656,15 @@ class PushlerClient {
|
|
|
552
656
|
? this.authEndpoint
|
|
553
657
|
: `${this.apiUrl}${this.authEndpoint}`;
|
|
554
658
|
|
|
659
|
+
const headers = this.getAuthRequestHeaders();
|
|
660
|
+
const hasAuthHeaders = this.authHeaders || this.getAuthHeaders;
|
|
661
|
+
|
|
555
662
|
const response = await fetch(authUrl, {
|
|
556
663
|
method: 'POST',
|
|
557
|
-
headers
|
|
558
|
-
|
|
559
|
-
|
|
664
|
+
headers,
|
|
665
|
+
// Если есть authHeaders, не используем credentials (JWT режим)
|
|
666
|
+
// Если нет — используем cookies
|
|
667
|
+
...(hasAuthHeaders ? {} : { credentials: 'include' }),
|
|
560
668
|
body: JSON.stringify({
|
|
561
669
|
channel_name: channelName,
|
|
562
670
|
socket_id: socketId,
|
|
@@ -566,7 +674,7 @@ class PushlerClient {
|
|
|
566
674
|
});
|
|
567
675
|
|
|
568
676
|
if (!response.ok) {
|
|
569
|
-
const error = await response.json();
|
|
677
|
+
const error = await response.json().catch(() => ({ error: 'Auth request failed' }));
|
|
570
678
|
throw new Error(error.error || 'Failed to get channel signature');
|
|
571
679
|
}
|
|
572
680
|
|
|
@@ -699,6 +807,14 @@ class PushlerClient {
|
|
|
699
807
|
|
|
700
808
|
console.error('PushlerClient connection error:', fullErrorMessage);
|
|
701
809
|
|
|
810
|
+
// Реджектим Promise подключения
|
|
811
|
+
if (this.connectionReject) {
|
|
812
|
+
this.connectionReject(new Error(fullErrorMessage));
|
|
813
|
+
this.connectionPromise = null;
|
|
814
|
+
this.connectionResolve = null;
|
|
815
|
+
this.connectionReject = null;
|
|
816
|
+
}
|
|
817
|
+
|
|
702
818
|
this.emit(Events.ERROR, {
|
|
703
819
|
code: ErrorCodes.CONNECTION_FAILED,
|
|
704
820
|
message: fullErrorMessage,
|
|
@@ -806,11 +922,12 @@ var PushlerClient$1 = PushlerClient;
|
|
|
806
922
|
* Используется на сервере Node.js для отправки событий в каналы
|
|
807
923
|
*/
|
|
808
924
|
class PushlerServer {
|
|
925
|
+
static API_URL = 'https://api.pushler.ru';
|
|
926
|
+
|
|
809
927
|
/**
|
|
810
928
|
* @param {Object} options - Параметры клиента
|
|
811
929
|
* @param {string} options.appKey - Ключ приложения (key_xxx)
|
|
812
930
|
* @param {string} options.appSecret - Секрет приложения (secret_xxx)
|
|
813
|
-
* @param {string} [options.apiUrl='http://localhost:8000/api'] - URL API сервера
|
|
814
931
|
* @param {number} [options.timeout=10000] - Таймаут запроса в мс
|
|
815
932
|
*/
|
|
816
933
|
constructor(options = {}) {
|
|
@@ -823,7 +940,6 @@ class PushlerServer {
|
|
|
823
940
|
|
|
824
941
|
this.appKey = options.appKey;
|
|
825
942
|
this.appSecret = options.appSecret;
|
|
826
|
-
this.apiUrl = (options.apiUrl || 'http://localhost:8000/api').replace(/\/$/, '');
|
|
827
943
|
this.timeout = options.timeout || 10000;
|
|
828
944
|
}
|
|
829
945
|
|
|
@@ -1107,7 +1223,7 @@ class PushlerServer {
|
|
|
1107
1223
|
* @returns {Promise<Object>} Результат запроса
|
|
1108
1224
|
*/
|
|
1109
1225
|
async makeSignedRequest(method, endpoint, data = null) {
|
|
1110
|
-
const url =
|
|
1226
|
+
const url = PushlerServer.API_URL + endpoint;
|
|
1111
1227
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
1112
1228
|
const body = data ? JSON.stringify(data) : '';
|
|
1113
1229
|
const signature = this.generateApiSignature(body, timestamp);
|
|
@@ -1155,7 +1271,7 @@ class PushlerServer {
|
|
|
1155
1271
|
* @returns {Promise<Object>} Результат запроса
|
|
1156
1272
|
*/
|
|
1157
1273
|
async makeHttpRequest(method, endpoint, data = null) {
|
|
1158
|
-
const url =
|
|
1274
|
+
const url = PushlerServer.API_URL + endpoint;
|
|
1159
1275
|
|
|
1160
1276
|
const headers = {
|
|
1161
1277
|
'Content-Type': 'application/json',
|
|
@@ -1196,7 +1312,7 @@ class PushlerServer {
|
|
|
1196
1312
|
* @returns {string}
|
|
1197
1313
|
*/
|
|
1198
1314
|
getApiUrl() {
|
|
1199
|
-
return
|
|
1315
|
+
return PushlerServer.API_URL;
|
|
1200
1316
|
}
|
|
1201
1317
|
}
|
|
1202
1318
|
|