Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/resources/scripts/plugins/Websocket.ts
10258 views
1
import Sockette from 'sockette';
2
import { EventEmitter } from 'events';
3
4
export class Websocket extends EventEmitter {
5
// The socket instance being tracked.
6
private socket: Sockette | null = null;
7
8
// The URL being connected to for the socket.
9
private url: string | null = null;
10
11
// The authentication token passed along with every request to the Daemon.
12
// By default this token expires every 15 minutes and must therefore be
13
// refreshed at a pretty continuous interval. The socket server will respond
14
// with "token expiring" and "token expired" events when approaching 3 minutes
15
// and 0 minutes to expiry.
16
private token = '';
17
18
// Connects to the websocket instance and sets the token for the initial request.
19
connect(url: string): this {
20
this.url = url;
21
22
this.socket = new Sockette(`${this.url}`, {
23
timeout: 1000,
24
maxAttempts: 20,
25
onmessage: (e) => {
26
try {
27
const { event, args } = JSON.parse(e.data);
28
args ? this.emit(event, ...args) : this.emit(event);
29
} catch (ex) {
30
console.warn('Failed to parse incoming websocket message.', ex);
31
}
32
},
33
onopen: () => {
34
this.emit('SOCKET_OPEN');
35
this.authenticate();
36
},
37
onreconnect: (evt) => {
38
// We return code 4409 from Wings when a server is suspended. We've
39
// gone ahead and reserved 4400 as well here for future expansion without
40
// having to loop back around.
41
//
42
// If either of those codes is returned go ahead and abort here. Unfortunately
43
// the underlying sockette logic always calls reconnect for any code that isn't
44
// 1000/1001/1003, which is painful but we can just stop the flow here.
45
// @ts-expect-error code is actually present here.
46
if (evt.code === 4409 || evt.code === 4400) {
47
this.close(1000);
48
} else {
49
this.emit('SOCKET_RECONNECT');
50
}
51
},
52
onclose: () => this.emit('SOCKET_CLOSE'),
53
onerror: (error) => this.emit('SOCKET_ERROR', error),
54
onmaximum: () => this.emit('SOCKET_CONNECT_ERROR'),
55
});
56
57
return this;
58
}
59
60
// Sets the authentication token to use when sending commands back and forth
61
// between the websocket instance.
62
setToken(token: string, isUpdate = false): this {
63
this.token = token;
64
65
if (isUpdate) {
66
this.authenticate();
67
}
68
69
return this;
70
}
71
72
authenticate() {
73
if (this.url && this.token) {
74
this.send('auth', this.token);
75
}
76
}
77
78
close(code?: number, reason?: string) {
79
this.url = null;
80
this.token = '';
81
this.socket?.close(code, reason);
82
}
83
84
open() {
85
this.socket?.open();
86
}
87
88
reconnect() {
89
this.socket?.reconnect();
90
}
91
92
send(event: string, payload?: string | string[]) {
93
this.socket?.json({ event, args: Array.isArray(payload) ? payload : [payload] });
94
}
95
}
96
97