zeroant-common
Version:
Common modules for zeroant
133 lines • 5.45 kB
JavaScript
import { AddonPlugin } from 'zeroant-factory/addon.plugin';
import { v4 } from 'uuid';
import { WebSocket } from 'ws';
import { ErrorCode, ErrorDescription, WebSocketCloseCode } from '../constants.js';
import { PubSocketConfig } from '../config/pubSocket.config.js';
import { TtlUtils } from 'zeroant-util/ttl.util';
import { BadRequest } from 'zeroant-response/clientErrors/badRequest.clientError';
import { PubSocketPayload } from '../dto/socket.payload.js';
export class PubSocket extends AddonPlugin {
connections = {};
pingInterval = TtlUtils.oneMinute;
retryInterval = TtlUtils.oneSecond * 3;
indefiniteRetryInterval = TtlUtils.oneMinute * 3;
maxRetry = 5;
reconnectTimer = {};
pingingTimer = {};
shutingDown = false;
_options;
get enabled() {
return this.context.config.addons.lazyGet(PubSocketConfig).usePub;
}
async initialize() {
if (!this.enabled) {
return;
}
this.debug('info', 'Enabled');
const options = this.context.config.addons.lazyGet(PubSocketConfig).options;
this._options = options;
const uuid = v4();
for (const url of this._options.url) {
this.createSub(uuid, url, this._options.key[0]);
}
}
to(sub) {
const service = {
send: (topic, data) => {
if (sub === null || sub === undefined) {
throw new BadRequest(ErrorCode.INVALID_PAYLOAD, ErrorDescription.INVALID_PAYLOAD, 'Invalid payload for pub socket send event please provide a valid subscriber');
}
const payload = new PubSocketPayload(topic, data, sub);
for (const socket of Object.values(this.connections)) {
this.debug('debug', 'socket sending message', socket.uuid, socket.readyState);
if (socket.readyState === socket.OPEN) {
socket.send(payload.toBuffer());
}
}
},
broadcast: (topic, data) => {
if (topic === null || topic === undefined) {
throw new BadRequest(ErrorCode.INVALID_PAYLOAD, ErrorDescription.INVALID_PAYLOAD, 'Invalid payload for pub socket broadcast event please provide a valid topic');
}
const payload = new PubSocketPayload(topic, data);
for (const socket of Object.values(this.connections)) {
if (socket.readyState === socket.OPEN) {
socket.send(payload.toBuffer());
}
}
}
};
return service;
}
createSub(uuid, url, key, retries = 0) {
const websocket = new WebSocket(url, {
auth: `pub:${key}`
});
websocket.uuid = uuid;
websocket.sub = key;
websocket.retries = retries;
websocket.onclose = (event) => {
if (this.shutingDown) {
return;
}
this.debug('info', 'Closed', event.code.toString(), event.reason);
this.tryClearPingingInterval(uuid);
this.tryClearReconnectInterval(uuid);
if (event.code !== WebSocketCloseCode.SERVICE_RESTART) {
websocket.retries += 1;
}
if (websocket.retries < this.maxRetry) {
this.debug('info', 'Retrying next', websocket.retries, 'in', this.retryInterval, 'ms');
setTimeout(() => {
this.createSub(uuid, url, key, websocket.retries);
}, this.retryInterval);
}
else if (websocket.retries >= this.maxRetry) {
this.debug('info', 'Retrying exhausted waiting for', this.indefiniteRetryInterval, 'ms', 'to next retries');
setTimeout(() => {
this.createSub(uuid, url, key, 0);
}, this.indefiniteRetryInterval);
}
};
websocket.onerror = (event) => {
if (this.shutingDown) {
return;
}
this.debug('error', 'OnError', event.message);
};
websocket.onopen = (event) => {
if (this.shutingDown) {
this.debug('info', 'ShutingDown', 'No Action initiated');
return;
}
this.tryClearReconnectInterval(uuid);
this.pingingTimer[uuid] = setInterval(() => {
this.debug('info', 'Pinging', `websocket client ${uuid}`);
websocket.ping();
}, this.pingInterval);
};
this.connections[uuid] = websocket;
}
tryClearReconnectInterval(uuid) {
if (this.reconnectTimer[uuid] !== undefined) {
clearTimeout(this.reconnectTimer[uuid]);
}
}
tryClearPingingInterval(uuid) {
if (this.pingingTimer[uuid] !== undefined) {
clearInterval(this.pingingTimer[uuid]);
}
}
close() {
this.shutingDown = true;
for (const [uuid, socket] of Object.entries(this.connections)) {
this.tryClearPingingInterval(uuid);
this.tryClearReconnectInterval(uuid);
if (socket.readyState === socket.OPEN || socket.readyState === socket.CONNECTING) {
socket.close();
}
}
this.debug('info', 'Stopped');
}
}
//# sourceMappingURL=pubSocket.plugin.js.map