mirror of
https://github.com/fusionpbx/fusionpbx.git
synced 2025-12-30 17:13:49 +00:00
* Initial commit of websockets * Move app_menu to the active_calls websockets * Fix hangup function * Remove connection wait-state on web socket server so events can process * Add timestamp and debug level to console for service debug output * Remove debug exit * Fix typo for ws_client instead of ws_server * Update app_config.php * Fix typo and remove empty function * Remove call to empty function * Fix the menu to point to the correct location * Remove Logging Class * Rename service file * Rename service file * Fix the in progress browser request * Fix browser reload and implement 'active_calls' default values * Add apply_filter function * Create new permission_filter object * In progress active calls now use filter * Add invalid_uuid_exception class * add event_key_filter to honor user permissions * add and_link and or_link for filters * Fix disconnected subscriber and add filters to honor permissions * Add $key and $value for filter * define a service name * catch throwable instead of exception * Add $key and $value for filter and allow returning null * Update permission checks when loading page * Add apply_filter function to honor subscriber permissions * Add create_filter_chain_for function to honor subscriber permissions * Add apply_filter function to honor subscriber permissions * Add apply_filter function to honor subscriber permissions * create interface to allow filterable payload * create interface to define functions required for websocket services * Pass in service class when creating a service token * Allow key/name and return null for filter * Adjust subscriber exceptions to return the ID of the subscriber * Add event filter to filter chain * Add command line options for ip and port for websockets and switch * update service to use is_a syntax * initial commit of base class for websockets system services * initial commit of the system cpu status service * remove extra line feed * fix path on active_calls * initial proof of concept for cpu status updated by websockets * Allow returning null * Use default settings to set the interval for cpu status broadcast * Improve the CPU percent function for Linux systems * Show more debug information * Allow child processes to re-connect to the web socket service * Fix websockets as plural instead of singular * Add class name list-row * Update active_calls.php * Update active_calls.php * Update websocket_client.js * Update app_config.php * Update app_menu.php * Update debian-websockets.service * Update debian-active_calls.service --------- Co-authored-by: FusionPBX <markjcrane@gmail.com>
137 lines
3.4 KiB
JavaScript
137 lines
3.4 KiB
JavaScript
class ws_client {
|
||
constructor(url, token) {
|
||
this.ws = new WebSocket(url);
|
||
this.ws.addEventListener('message', this._onMessage.bind(this));
|
||
this._nextId = 1;
|
||
this._pending = new Map();
|
||
this._eventHandlers = new Map();
|
||
// The token is submitted on every request
|
||
this.token = token;
|
||
}
|
||
|
||
// internal message handler called when event occurs on the socket
|
||
_onMessage(ev) {
|
||
let message;
|
||
let switch_event;
|
||
try {
|
||
//console.log(ev.data);
|
||
message = JSON.parse(ev.data);
|
||
// check for authentication request
|
||
if (message.status_code === 407) {
|
||
console.log('Authentication Required');
|
||
return;
|
||
}
|
||
switch_event = message.payload;
|
||
//console.log('envelope received: ',env);
|
||
} catch (err) {
|
||
console.error('Error parsing JSON data:', err);
|
||
//console.error('Invalid JSON:', ev.data);
|
||
return;
|
||
}
|
||
|
||
// Pull out the request_id first
|
||
const rid = message.request_id ?? null;
|
||
|
||
// If this is the response to a pending request
|
||
if (rid && this._pending.has(rid)) {
|
||
// Destructure with defaults in case they're missing
|
||
const {
|
||
service,
|
||
topic = '',
|
||
status = 'ok',
|
||
code = 200,
|
||
payload = {}
|
||
} = message;
|
||
|
||
const {resolve, reject} = this._pending.get(rid);
|
||
this._pending.delete(rid);
|
||
|
||
if (status === 'ok' && code >= 200 && code < 300) {
|
||
resolve({service, topic, payload, code, message});
|
||
} else {
|
||
const err = new Error(message || `Error ${code}`);
|
||
err.code = code;
|
||
reject(err);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
// Otherwise it's a server‑pushed event…
|
||
// e.g. env.service === 'event' or env.topic is your event name
|
||
this._dispatchEvent(message.service_name, switch_event);
|
||
}
|
||
|
||
// Send a request to the websocket server using JSON string
|
||
request(service, topic = null, payload = {}) {
|
||
const request_id = String(this._nextId++);
|
||
const env = {
|
||
request_id: request_id,
|
||
service,
|
||
...(topic !== null ? {topic} : {}),
|
||
token: this.token,
|
||
payload: payload
|
||
};
|
||
const raw = JSON.stringify(env);
|
||
this.ws.send(raw);
|
||
return new Promise((resolve, reject) => {
|
||
this._pending.set(request_id, {resolve, reject});
|
||
// TODO: get timeout working to reject if no response in X ms
|
||
});
|
||
}
|
||
|
||
subscribe(topic) {
|
||
return this.request('active.calls', topic);
|
||
}
|
||
|
||
unsubscribe(topic) {
|
||
return this.request('active.calls', topic);
|
||
}
|
||
|
||
// register a callback for server-pushes
|
||
onEvent(topic, handler) {
|
||
console.log('registering event listener for ' + topic);
|
||
if (!this._eventHandlers.has(topic)) {
|
||
this._eventHandlers.set(topic, []);
|
||
}
|
||
this._eventHandlers.get(topic).push(handler);
|
||
}
|
||
/**
|
||
* Dispatch a server‑push event envelope to all registered handlers.
|
||
* @param {object} env
|
||
*/
|
||
_dispatchEvent(service, env) {
|
||
// if service==='event', topic carries the real event name:
|
||
let event = (typeof env === 'string')
|
||
? JSON.parse(env)
|
||
: env;
|
||
|
||
// dispatch event handlers
|
||
if (service === 'active.calls') {
|
||
const topic = event.event_name;
|
||
|
||
let handlers = this._eventHandlers.get(topic) || [];
|
||
if (handlers.length === 0) {
|
||
handlers = this._eventHandlers.get('*') || [];
|
||
}
|
||
for (const fn of handlers) {
|
||
try {
|
||
fn(event);
|
||
} catch (err) {
|
||
console.error(`Error in handler for "${topic}":`, err);
|
||
}
|
||
}
|
||
} else {
|
||
const handlers = this._eventHandlers.get(service) || [];
|
||
for (const fn of handlers) {
|
||
try {
|
||
fn(event.data, event);
|
||
} catch (err) {
|
||
console.error(`Error in handler for "${service}":`, err);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|