misc_pterodactyl-panel/resources/scripts/plugins/Websocket.ts
2022-11-25 13:25:03 -07:00

106 lines
3.2 KiB
TypeScript

import Sockette from 'sockette';
import { EventEmitter } from 'events';
export class Websocket extends EventEmitter {
// Timer instance for this socket.
private timer: any = null;
// The backoff for the timer, in milliseconds.
private backoff = 5000;
// The socket instance being tracked.
private socket: Sockette | null = null;
// The URL being connected to for the socket.
private url: string | null = null;
// The authentication token passed along with every request to the Daemon.
// By default this token expires every 15 minutes and must therefore be
// refreshed at a pretty continuous interval. The socket server will respond
// with "token expiring" and "token expired" events when approaching 3 minutes
// and 0 minutes to expiry.
private token = '';
// Connects to the websocket instance and sets the token for the initial request.
connect(url: string): this {
this.url = url;
this.socket = new Sockette(`${this.url}`, {
onmessage: e => {
try {
const { event, args } = JSON.parse(e.data);
args ? this.emit(event, ...args) : this.emit(event);
} catch (ex) {
console.warn('Failed to parse incoming websocket message.', ex);
}
},
onopen: () => {
// Clear the timers, we managed to connect just fine.
this.timer && clearTimeout(this.timer);
this.backoff = 5000;
this.emit('SOCKET_OPEN');
this.authenticate();
},
onreconnect: () => {
this.emit('SOCKET_RECONNECT');
this.authenticate();
},
onclose: () => this.emit('SOCKET_CLOSE'),
onerror: error => this.emit('SOCKET_ERROR', error),
});
this.timer = setTimeout(() => {
this.backoff = this.backoff + 2500 >= 20000 ? 20000 : this.backoff + 2500;
this.socket && this.socket.close();
clearTimeout(this.timer);
// Re-attempt connecting to the socket.
this.connect(url);
}, this.backoff);
return this;
}
// Sets the authentication token to use when sending commands back and forth
// between the websocket instance.
setToken(token: string, isUpdate = false): this {
this.token = token;
if (isUpdate) {
this.authenticate();
}
return this;
}
authenticate() {
if (this.url && this.token) {
this.send('auth', this.token);
}
}
close(code?: number, reason?: string) {
this.url = null;
this.token = '';
this.socket && this.socket.close(code, reason);
}
open() {
this.socket && this.socket.open();
}
reconnect() {
this.socket && this.socket.reconnect();
}
send(event: string, payload?: string | string[]) {
this.socket &&
this.socket.send(
JSON.stringify({
event,
args: Array.isArray(payload) ? payload : [payload],
}),
);
}
}