- fixed some "map sync" bugs with WebSocket installations
- improved WebSocket info on `/setup` page (new live stats, new log viewer,..)
This commit is contained in:
@@ -25,7 +25,6 @@ class Map extends Controller\AccessController {
|
||||
|
||||
// cache keys
|
||||
const CACHE_KEY_INIT = 'CACHED_INIT';
|
||||
const CACHE_KEY_MAP_DATA = 'CACHED.MAP_DATA.%s';
|
||||
const CACHE_KEY_USER_DATA = 'CACHED.USER_DATA.%s';
|
||||
const CACHE_KEY_HISTORY = 'CACHED_MAP_HISTORY_%s';
|
||||
|
||||
@@ -652,6 +651,7 @@ class Map extends Controller\AccessController {
|
||||
protected function broadcastMapAccess(Pathfinder\MapModel $map){
|
||||
$mapAccess = [
|
||||
'id' => $map->_id,
|
||||
'name' => $map->name,
|
||||
'characterIds' => array_map(function ($data){
|
||||
return $data->id;
|
||||
}, $map->getCharactersData())
|
||||
@@ -690,17 +690,18 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
|
||||
$return->data = [
|
||||
'id' => $activeCharacter->_id,
|
||||
'token' => bin2hex(random_bytes(16)), // token for character access
|
||||
'id' => $activeCharacter->_id,
|
||||
'token' => bin2hex(random_bytes(16)), // token for character access
|
||||
'characterData' => $characterData,
|
||||
'mapData' => []
|
||||
'mapData' => []
|
||||
];
|
||||
|
||||
if($maps){
|
||||
foreach($maps as $map){
|
||||
$return->data['mapData'][] = [
|
||||
'id' => $map->_id,
|
||||
'token' => bin2hex(random_bytes(16)) // token for map access
|
||||
'id' => $map->_id,
|
||||
'token' => bin2hex(random_bytes(16)), // token for map access
|
||||
'name' => $map->name
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ class Sso extends Api\User{
|
||||
$this->setLoginCookie($characterModel);
|
||||
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA, $characterModel->_id);
|
||||
$this->setTempCharacterData($characterModel->_id);
|
||||
|
||||
// route to "map"
|
||||
if($rootAlias == 'admin'){
|
||||
|
||||
@@ -388,12 +388,12 @@ class Controller {
|
||||
public function getSessionCharacterData() : array {
|
||||
$data = [];
|
||||
if($user = $this->getUser()){
|
||||
$header = self::getRequestHeaders();
|
||||
$requestedCharacterId = (int)$header['Pf-Character'];
|
||||
$header = self::getRequestHeaders();
|
||||
$requestedCharacterId = (int)$header['Pf-Character'];
|
||||
if( !$this->getF3()->get('AJAX') ){
|
||||
$requestedCharacterId = (int)$_COOKIE['old_char_id'];
|
||||
if(!$requestedCharacterId){
|
||||
$tempCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA);
|
||||
$tempCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA);
|
||||
if((int)$tempCharacterData['ID'] > 0){
|
||||
$requestedCharacterId = (int)$tempCharacterData['ID'];
|
||||
}
|
||||
|
||||
@@ -1557,23 +1557,29 @@ class Setup extends Controller {
|
||||
'class' => 'txt-color-danger'
|
||||
];
|
||||
|
||||
$webSocketStatus = [
|
||||
$statusWeb = [
|
||||
'type' => 'danger',
|
||||
'label' => 'INIT CONNECTION…',
|
||||
'class' => 'txt-color-danger'
|
||||
];
|
||||
|
||||
$statsTcp = [
|
||||
'startup' => 0,
|
||||
'connections' => 0,
|
||||
'maxConnections' => 0
|
||||
];
|
||||
$statsTcp = false;
|
||||
$statsWeb = false;
|
||||
|
||||
$setStats = function(array $stats) use (&$statsTcp, &$statsWeb) {
|
||||
if(!empty($stats['tcpSocket'])){
|
||||
$statsTcp = $stats['tcpSocket'];
|
||||
}
|
||||
if(!empty($stats['webSocket'])){
|
||||
$statsWeb = $stats['webSocket'];
|
||||
}
|
||||
};
|
||||
|
||||
// ping TCP Socket with "healthCheck" task
|
||||
$f3->webSocket(['timeout' => $ttl])
|
||||
->write($task, $healthCheckToken)
|
||||
->then(
|
||||
function($payload) use ($task, $healthCheckToken, &$statusTcp, &$statsTcp) {
|
||||
function($payload) use ($task, $healthCheckToken, &$statusTcp, $setStats) {
|
||||
if(
|
||||
$payload['task'] == $task &&
|
||||
$payload['load'] == $healthCheckToken
|
||||
@@ -1581,24 +1587,26 @@ class Setup extends Controller {
|
||||
$statusTcp['type'] = 'success';
|
||||
$statusTcp['label'] = 'PING OK';
|
||||
$statusTcp['class'] = 'txt-color-success';
|
||||
|
||||
// statistics (e.g. current connection count)
|
||||
if(!empty($payload['stats'])){
|
||||
$statsTcp = $payload['stats'];
|
||||
}
|
||||
}else{
|
||||
$statusTcp['type'] = 'warning';
|
||||
$statusTcp['label'] = is_string($payload['load']) ? $payload['load'] : 'INVALID RESPONSE';
|
||||
$statusTcp['class'] = 'txt-color-warning';
|
||||
}
|
||||
|
||||
// statistics (e.g. current connection count)
|
||||
$setStats((array)$payload['stats']);
|
||||
},
|
||||
function($payload) use (&$statusTcp) {
|
||||
function($payload) use (&$statusTcp, $setStats) {
|
||||
$statusTcp['label'] = $payload['load'];
|
||||
|
||||
// statistics (e.g. current connection count)
|
||||
$setStats((array)$payload['stats']);
|
||||
});
|
||||
|
||||
$socketInformation = [
|
||||
'tcpSocket' => [
|
||||
'label' => 'Socket (intern) [TCP]',
|
||||
'label' => 'TCP-Socket (intern)',
|
||||
'icon' => 'fa-exchange-alt',
|
||||
'status' => $statusTcp,
|
||||
'stats' => $statsTcp,
|
||||
'data' => [
|
||||
@@ -1620,15 +1628,17 @@ class Setup extends Controller {
|
||||
'check' => !empty( $ttl )
|
||||
],[
|
||||
'label' => 'uptime',
|
||||
'value' => Config::formatTimeInterval($statsTcp['startup']),
|
||||
'value' => Config::formatTimeInterval($statsTcp['startup'] ? : 0),
|
||||
'check' => $statsTcp['startup'] > 0
|
||||
]
|
||||
],
|
||||
'token' => $healthCheckToken
|
||||
],
|
||||
'webSocket' => [
|
||||
'label' => 'WebSocket (clients) [HTTP]',
|
||||
'status' => $webSocketStatus,
|
||||
'label' => 'Web-Socket',
|
||||
'icon' => 'fa-random',
|
||||
'status' => $statusWeb,
|
||||
'stats' => $statsWeb,
|
||||
'data' => [
|
||||
[
|
||||
'label' => 'URI',
|
||||
|
||||
@@ -203,9 +203,9 @@ define([], () => {
|
||||
* @param version
|
||||
*/
|
||||
let showVersionInfo = (version) => {
|
||||
console.ok('%c PATHFINDER',
|
||||
'color: #477372; font-size: 25px; margin-left: 10px; line-height: 100px; text-shadow: 1px 1px 0 #212C30; ' +
|
||||
'background: url(https://i.imgur.com/1Gw8mjL.png) no-repeat;');
|
||||
console.ok('%c PATHFINDER',
|
||||
'color: #477372; font-size: 25px; margin-left: 10px; line-height: 50px; text-shadow: 1px 1px 0 #212C30; ' +
|
||||
'background: url(https://i.imgur.com/bhSr6LI.png) no-repeat;');
|
||||
console.pf('Release: %s', version);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
|
||||
define([
|
||||
'app/util'
|
||||
], function(Util){
|
||||
], (Util) => {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
|
||||
};
|
||||
|
||||
let sharedWorker = null;
|
||||
let MsgWorker = null;
|
||||
let characterId = null;
|
||||
@@ -29,17 +25,13 @@ define([
|
||||
* get SharedWorker Script path
|
||||
* @returns {string}
|
||||
*/
|
||||
let getWorkerScript = () => {
|
||||
return '/public/js/' + Util.getVersion() + '/app/worker/map.js';
|
||||
};
|
||||
let getWorkerScript = () => '/public/js/' + Util.getVersion() + '/app/worker/map.js';
|
||||
|
||||
/**
|
||||
* get path to message object
|
||||
* @returns {string}
|
||||
*/
|
||||
let getMessageWorkerObjectPath = () => {
|
||||
return '/public/js/' + Util.getVersion() + '/app/worker/message.js';
|
||||
};
|
||||
let getMessageWorkerObjectPath = () => '/public/js/' + Util.getVersion() + '/app/worker/message.js';
|
||||
|
||||
/**
|
||||
* init (connect) WebSocket within SharedWorker
|
||||
@@ -51,14 +43,14 @@ define([
|
||||
characterId: characterId,
|
||||
});
|
||||
|
||||
sharedWorker.port.postMessage(MsgWorkerInit);
|
||||
sendMessage(MsgWorkerInit);
|
||||
};
|
||||
|
||||
/**
|
||||
* init (start/connect) to "SharedWorker"
|
||||
* init (start/connect) to "SharedWorker" thread
|
||||
* -> set worker events
|
||||
*/
|
||||
let init = (config) => {
|
||||
let init = config => {
|
||||
// set characterId that is connected with this SharedWorker PORT
|
||||
characterId = parseInt(config.characterId);
|
||||
|
||||
@@ -67,9 +59,9 @@ define([
|
||||
MsgWorker = window.MsgWorker;
|
||||
|
||||
// start/connect to "SharedWorker"
|
||||
sharedWorker = new SharedWorker( getWorkerScript(), getMessageWorkerObjectPath() );
|
||||
sharedWorker = new SharedWorker(getWorkerScript(), getMessageWorkerObjectPath());
|
||||
|
||||
sharedWorker.port.addEventListener('message', (e) => {
|
||||
sharedWorker.port.addEventListener('message', e => {
|
||||
let MsgWorkerMessage = e.data;
|
||||
Object.setPrototypeOf(MsgWorkerMessage, MsgWorker.prototype);
|
||||
|
||||
@@ -90,7 +82,7 @@ define([
|
||||
}
|
||||
}, false);
|
||||
|
||||
sharedWorker.onerror = (e) => {
|
||||
sharedWorker.onerror = e => {
|
||||
// could not connect to SharedWorker script -> send error back
|
||||
let MsgWorkerError = new MsgWorker('sw:error');
|
||||
MsgWorkerError.meta({
|
||||
@@ -111,17 +103,49 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* send data to "SharedWorker" thread
|
||||
* @param task
|
||||
* @param data
|
||||
*/
|
||||
let send = (task, data) => {
|
||||
let MsgWorkerSend = new MsgWorker('ws:send');
|
||||
MsgWorkerSend.task(task);
|
||||
MsgWorkerSend.data(data);
|
||||
|
||||
sharedWorker.port.postMessage(MsgWorkerSend);
|
||||
sendMessage(MsgWorkerSend);
|
||||
};
|
||||
|
||||
/**
|
||||
* send close port task to "SharedWorker" thread
|
||||
* -> this removes the port from its port collection and closes it
|
||||
*/
|
||||
let close = () => {
|
||||
let MsgWorkerClose = new MsgWorker('sw:closePort');
|
||||
MsgWorkerClose.task('unsubscribe');
|
||||
sendMessage(MsgWorkerClose);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {window.MsgWorker} MsgWorkerSend
|
||||
*/
|
||||
let sendMessage = MsgWorkerSend => {
|
||||
if(sharedWorker instanceof SharedWorker){
|
||||
if(MsgWorkerSend instanceof window.MsgWorker){
|
||||
sharedWorker.port.postMessage(MsgWorkerSend);
|
||||
}else{
|
||||
console.error('MsgWorkerSend must be instance of window.MsgWorker');
|
||||
}
|
||||
}else{
|
||||
console.error('SharedWorker thread not found');
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getWebSocketURL: getWebSocketURL,
|
||||
init: init,
|
||||
send: send
|
||||
send: send,
|
||||
close: close
|
||||
};
|
||||
});
|
||||
@@ -221,7 +221,7 @@ define([
|
||||
* @param payload
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
let initMapModule = (payload) => {
|
||||
let initMapModule = payload => {
|
||||
|
||||
let initMapModuleExecutor = (resolve, reject) => {
|
||||
// init browser tab change observer, Once the timers are available
|
||||
@@ -248,10 +248,10 @@ define([
|
||||
* @param payloadMapAccessData
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
let initMapWorker = (payloadMapAccessData) => {
|
||||
let initMapWorker = payloadMapAccessData => {
|
||||
|
||||
let initMapWorkerExecutor = (resolve, reject) => {
|
||||
let getPayload = (command) => {
|
||||
let getPayload = command => {
|
||||
return {
|
||||
action: 'initMapWorker',
|
||||
data: {
|
||||
@@ -270,7 +270,7 @@ define([
|
||||
|
||||
// init SharedWorker for maps
|
||||
MapWorker.init({
|
||||
characterId: response.data.id,
|
||||
characterId: response.data.id,
|
||||
callbacks: {
|
||||
onInit: (MsgWorkerMessage) => {
|
||||
Util.setSyncStatus(MsgWorkerMessage.command);
|
||||
@@ -552,6 +552,9 @@ define([
|
||||
// Send map update request on tab close/reload, in order to save map changes that
|
||||
// haven´t been saved through default update trigger
|
||||
window.addEventListener('beforeunload', function(e){
|
||||
// close connection to "SharedWorker"
|
||||
MapWorker.close();
|
||||
|
||||
// save unsaved map changes ...
|
||||
triggerMapUpdatePing();
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ define([
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
splashOverlayClass: 'pf-splash' // class for "splash" overlay
|
||||
splashOverlayClass: 'pf-splash', // class for "splash" overlay
|
||||
webSocketStatsId: 'pf-setup-webSocket-stats', // id for webSocket "stats" panel
|
||||
webSocketRefreshStatsId: 'pf-setup-webSocket-stats-refresh' // class for "reload stats" button
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -42,6 +44,18 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param container
|
||||
* @param selector
|
||||
*/
|
||||
let setCollapseObserver = (container, selector) => {
|
||||
container.find(selector).css({cursor: 'pointer'});
|
||||
container.on('click', selector, function(){
|
||||
$(this).find('.pf-animate-rotate').toggleClass('right');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set page observer
|
||||
*/
|
||||
@@ -52,9 +66,7 @@ define([
|
||||
Util.initPageScroll(body);
|
||||
|
||||
// collapse ---------------------------------------------------------------------------------------------------
|
||||
body.find('[data-toggle="collapse"]').css({cursor: 'pointer'}).on('click', function(){
|
||||
$(this).find('.pf-animate-rotate').toggleClass('right');
|
||||
});
|
||||
setCollapseObserver(body, '[data-toggle="collapse"]');
|
||||
|
||||
// buttons ----------------------------------------------------------------------------------------------------
|
||||
// exclude "download" && "navigation" buttons
|
||||
@@ -128,6 +140,29 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* get WebSockets "subscriptions" <table> HTML
|
||||
* @param subscriptionStats
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
let getWebSocketSubscriptionTable = subscriptionStats => {
|
||||
|
||||
let executor = resolve => {
|
||||
requirejs(['text!templates/modules/subscriptions_table.html', 'mustache'], (template, Mustache) => {
|
||||
let data = {
|
||||
panelId: config.webSocketStatsId,
|
||||
refreshButtonId: config.webSocketRefreshStatsId,
|
||||
subStats: subscriptionStats,
|
||||
channelCount: (Util.getObjVal(subscriptionStats, 'channels') || []).length
|
||||
};
|
||||
|
||||
resolve(Mustache.render(template, data));
|
||||
});
|
||||
};
|
||||
|
||||
return new Promise(executor);
|
||||
};
|
||||
|
||||
/**
|
||||
* perform a basic check if Clients (browser) can connect to the webSocket server
|
||||
*/
|
||||
@@ -158,7 +193,7 @@ define([
|
||||
let socketDangerCount = parseInt(badgeSocketDanger.text()) || 0;
|
||||
|
||||
if(data.uri){
|
||||
let uriRow = webSocketPanel.find('.panel-body table tr');
|
||||
let uriRow = webSocketPanel.find('.panel-body').filter(':first').find('table tr');
|
||||
uriRow.find('td:nth-child(2) kbd').html(data.uri.value);
|
||||
if(data.uri.status){
|
||||
let statusIcon = uriRow.find('td:nth-child(3) i');
|
||||
@@ -206,6 +241,18 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param socket
|
||||
* @param task
|
||||
* @param load
|
||||
*/
|
||||
let sendMessage = (socket, task, load) => {
|
||||
socket.send(JSON.stringify({
|
||||
task: task,
|
||||
load: load
|
||||
}));
|
||||
};
|
||||
|
||||
// try to connect to WebSocket server
|
||||
let socket = new WebSocket(webSocketURI);
|
||||
|
||||
@@ -219,10 +266,7 @@ define([
|
||||
});
|
||||
|
||||
// sent token and check response
|
||||
socket.send(JSON.stringify({
|
||||
task: 'healthCheck',
|
||||
load: tcpSocketPanel.data('token')
|
||||
}));
|
||||
sendMessage(socket, 'healthCheck', tcpSocketPanel.attr('data-token'));
|
||||
|
||||
webSocketPanel.hideLoadingAnimation();
|
||||
};
|
||||
@@ -230,7 +274,7 @@ define([
|
||||
socket.onmessage = (e) => {
|
||||
let response = JSON.parse(e.data);
|
||||
|
||||
if(response === 1){
|
||||
if(Util.getObjVal(response, 'load.isValid') === true){
|
||||
// SUCCESS
|
||||
updateWebSocketPanel({
|
||||
status: {
|
||||
@@ -239,6 +283,23 @@ define([
|
||||
class: 'txt-color-success'
|
||||
}
|
||||
});
|
||||
|
||||
// show subscription stats table
|
||||
getWebSocketSubscriptionTable(Util.getObjVal(response, 'load.subStats')).then(payload => {
|
||||
// remove existing table -> then insert new
|
||||
$('#' + config.webSocketStatsId).remove();
|
||||
$(payload).insertAfter(webSocketPanel).initTooltips();
|
||||
|
||||
let token = Util.getObjVal(response, 'load.token');
|
||||
tcpSocketPanel.attr('data-token', token);
|
||||
|
||||
// set "reload stats" observer
|
||||
$('#' + config.webSocketRefreshStatsId).on('click', function(){
|
||||
$('#' + config.webSocketStatsId).showLoadingAnimation();
|
||||
|
||||
sendMessage(socket, 'healthCheck', token);
|
||||
});
|
||||
});
|
||||
}else{
|
||||
// Got response but INVALID
|
||||
updateWebSocketPanel({
|
||||
@@ -274,6 +335,8 @@ define([
|
||||
});
|
||||
|
||||
webSocketPanel.hideLoadingAnimation();
|
||||
|
||||
$('#' + config.webSocketStatsId).remove();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ let ports = [];
|
||||
let characterPorts = [];
|
||||
|
||||
// init "WebSocket" connection ========================================================================================
|
||||
let initSocket = (uri) => {
|
||||
let initSocket = uri => {
|
||||
let MsgWorkerOpen = new MsgWorker('ws:open');
|
||||
|
||||
if(socket === null){
|
||||
socket = new WebSocket(uri);
|
||||
|
||||
// "WebSocket" open -----------------------------------------------------------------------
|
||||
socket.onopen = (e) => {
|
||||
socket.onopen = e => {
|
||||
MsgWorkerOpen.meta({
|
||||
readyState: socket.readyState
|
||||
});
|
||||
@@ -28,7 +28,7 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// "WebSocket message ---------------------------------------------------------------------
|
||||
socket.onmessage = (e) => {
|
||||
socket.onmessage = e => {
|
||||
let response = JSON.parse(e.data);
|
||||
|
||||
let MsgWorkerSend = new MsgWorker('ws:send');
|
||||
@@ -43,7 +43,7 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// "WebSocket" close ----------------------------------------------------------------------
|
||||
socket.onclose = (closeEvent) => {
|
||||
socket.onclose = closeEvent => {
|
||||
let MsgWorkerClosed = new MsgWorker('ws:closed');
|
||||
MsgWorkerClosed.meta({
|
||||
readyState: socket.readyState,
|
||||
@@ -57,7 +57,7 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// "WebSocket" error ----------------------------------------------------------------------
|
||||
socket.onerror = (e) => {
|
||||
socket.onerror = e => {
|
||||
let MsgWorkerError = new MsgWorker('ws:error');
|
||||
MsgWorkerError.meta({
|
||||
readyState: socket.readyState
|
||||
@@ -75,11 +75,11 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// send message to port(s) ============================================================================================
|
||||
let sendToCurrentPort = (load) => {
|
||||
let sendToCurrentPort = load => {
|
||||
ports[ports.length - 1].postMessage(load);
|
||||
};
|
||||
|
||||
let broadcastPorts = (load) => {
|
||||
let broadcastPorts = load => {
|
||||
// default: sent to all ports
|
||||
let sentToPorts = ports;
|
||||
|
||||
@@ -114,7 +114,7 @@ let addPort = (port, characterId) => {
|
||||
}
|
||||
};
|
||||
|
||||
let getPortsByCharacterIds = (characterIds) => {
|
||||
let getPortsByCharacterIds = characterIds => {
|
||||
let ports = [];
|
||||
|
||||
for(let i = 0; i < characterPorts.length; i++){
|
||||
@@ -128,8 +128,37 @@ let getPortsByCharacterIds = (characterIds) => {
|
||||
return ports;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param port
|
||||
* @returns {int[]}
|
||||
*/
|
||||
let removePort = port => {
|
||||
let characterIds = [];
|
||||
|
||||
// reverse loop required because of array index reset after splice()
|
||||
let i = characterPorts.length;
|
||||
while(i--){
|
||||
if(characterPorts[i].port === port){
|
||||
// collectt all character Ids mapped to the removed port
|
||||
characterIds.push(characterPorts[i].characterId);
|
||||
characterPorts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let j = ports.length;
|
||||
while(j--){
|
||||
if(ports[j] === port){
|
||||
ports.splice(j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// return unique characterIds
|
||||
return [...new Set(characterIds)];
|
||||
};
|
||||
|
||||
// "SharedWorker" connection ==========================================================================================
|
||||
self.addEventListener('connect', (event) => { // jshint ignore:line
|
||||
self.addEventListener('connect', event => { // jshint ignore:line
|
||||
let port = event.ports[0];
|
||||
addPort(port);
|
||||
|
||||
@@ -145,12 +174,18 @@ self.addEventListener('connect', (event) => { // jshint ignore:line
|
||||
initSocket(data.uri);
|
||||
break;
|
||||
case 'ws:send':
|
||||
let MsgSocket = {
|
||||
socket.send(JSON.stringify({
|
||||
task: MsgWorkerMessage.task(),
|
||||
load: MsgWorkerMessage.data()
|
||||
};
|
||||
}));
|
||||
break;
|
||||
case 'sw:closePort':
|
||||
port.close();
|
||||
|
||||
socket.send(JSON.stringify(MsgSocket));
|
||||
socket.send(JSON.stringify({
|
||||
task: MsgWorkerMessage.task(),
|
||||
load: removePort(port)
|
||||
}));
|
||||
break;
|
||||
case 'ws:close':
|
||||
// closeSocket();
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -203,9 +203,9 @@ define([], () => {
|
||||
* @param version
|
||||
*/
|
||||
let showVersionInfo = (version) => {
|
||||
console.ok('%c PATHFINDER',
|
||||
'color: #477372; font-size: 25px; margin-left: 10px; line-height: 100px; text-shadow: 1px 1px 0 #212C30; ' +
|
||||
'background: url(https://i.imgur.com/1Gw8mjL.png) no-repeat;');
|
||||
console.ok('%c PATHFINDER',
|
||||
'color: #477372; font-size: 25px; margin-left: 10px; line-height: 50px; text-shadow: 1px 1px 0 #212C30; ' +
|
||||
'background: url(https://i.imgur.com/bhSr6LI.png) no-repeat;');
|
||||
console.pf('Release: %s', version);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
|
||||
define([
|
||||
'app/util'
|
||||
], function(Util){
|
||||
], (Util) => {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
|
||||
};
|
||||
|
||||
let sharedWorker = null;
|
||||
let MsgWorker = null;
|
||||
let characterId = null;
|
||||
@@ -29,17 +25,13 @@ define([
|
||||
* get SharedWorker Script path
|
||||
* @returns {string}
|
||||
*/
|
||||
let getWorkerScript = () => {
|
||||
return '/public/js/' + Util.getVersion() + '/app/worker/map.js';
|
||||
};
|
||||
let getWorkerScript = () => '/public/js/' + Util.getVersion() + '/app/worker/map.js';
|
||||
|
||||
/**
|
||||
* get path to message object
|
||||
* @returns {string}
|
||||
*/
|
||||
let getMessageWorkerObjectPath = () => {
|
||||
return '/public/js/' + Util.getVersion() + '/app/worker/message.js';
|
||||
};
|
||||
let getMessageWorkerObjectPath = () => '/public/js/' + Util.getVersion() + '/app/worker/message.js';
|
||||
|
||||
/**
|
||||
* init (connect) WebSocket within SharedWorker
|
||||
@@ -51,14 +43,14 @@ define([
|
||||
characterId: characterId,
|
||||
});
|
||||
|
||||
sharedWorker.port.postMessage(MsgWorkerInit);
|
||||
sendMessage(MsgWorkerInit);
|
||||
};
|
||||
|
||||
/**
|
||||
* init (start/connect) to "SharedWorker"
|
||||
* init (start/connect) to "SharedWorker" thread
|
||||
* -> set worker events
|
||||
*/
|
||||
let init = (config) => {
|
||||
let init = config => {
|
||||
// set characterId that is connected with this SharedWorker PORT
|
||||
characterId = parseInt(config.characterId);
|
||||
|
||||
@@ -67,9 +59,9 @@ define([
|
||||
MsgWorker = window.MsgWorker;
|
||||
|
||||
// start/connect to "SharedWorker"
|
||||
sharedWorker = new SharedWorker( getWorkerScript(), getMessageWorkerObjectPath() );
|
||||
sharedWorker = new SharedWorker(getWorkerScript(), getMessageWorkerObjectPath());
|
||||
|
||||
sharedWorker.port.addEventListener('message', (e) => {
|
||||
sharedWorker.port.addEventListener('message', e => {
|
||||
let MsgWorkerMessage = e.data;
|
||||
Object.setPrototypeOf(MsgWorkerMessage, MsgWorker.prototype);
|
||||
|
||||
@@ -90,7 +82,7 @@ define([
|
||||
}
|
||||
}, false);
|
||||
|
||||
sharedWorker.onerror = (e) => {
|
||||
sharedWorker.onerror = e => {
|
||||
// could not connect to SharedWorker script -> send error back
|
||||
let MsgWorkerError = new MsgWorker('sw:error');
|
||||
MsgWorkerError.meta({
|
||||
@@ -111,17 +103,49 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* send data to "SharedWorker" thread
|
||||
* @param task
|
||||
* @param data
|
||||
*/
|
||||
let send = (task, data) => {
|
||||
let MsgWorkerSend = new MsgWorker('ws:send');
|
||||
MsgWorkerSend.task(task);
|
||||
MsgWorkerSend.data(data);
|
||||
|
||||
sharedWorker.port.postMessage(MsgWorkerSend);
|
||||
sendMessage(MsgWorkerSend);
|
||||
};
|
||||
|
||||
/**
|
||||
* send close port task to "SharedWorker" thread
|
||||
* -> this removes the port from its port collection and closes it
|
||||
*/
|
||||
let close = () => {
|
||||
let MsgWorkerClose = new MsgWorker('sw:closePort');
|
||||
MsgWorkerClose.task('unsubscribe');
|
||||
sendMessage(MsgWorkerClose);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {window.MsgWorker} MsgWorkerSend
|
||||
*/
|
||||
let sendMessage = MsgWorkerSend => {
|
||||
if(sharedWorker instanceof SharedWorker){
|
||||
if(MsgWorkerSend instanceof window.MsgWorker){
|
||||
sharedWorker.port.postMessage(MsgWorkerSend);
|
||||
}else{
|
||||
console.error('MsgWorkerSend must be instance of window.MsgWorker');
|
||||
}
|
||||
}else{
|
||||
console.error('SharedWorker thread not found');
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getWebSocketURL: getWebSocketURL,
|
||||
init: init,
|
||||
send: send
|
||||
send: send,
|
||||
close: close
|
||||
};
|
||||
});
|
||||
@@ -221,7 +221,7 @@ define([
|
||||
* @param payload
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
let initMapModule = (payload) => {
|
||||
let initMapModule = payload => {
|
||||
|
||||
let initMapModuleExecutor = (resolve, reject) => {
|
||||
// init browser tab change observer, Once the timers are available
|
||||
@@ -248,10 +248,10 @@ define([
|
||||
* @param payloadMapAccessData
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
let initMapWorker = (payloadMapAccessData) => {
|
||||
let initMapWorker = payloadMapAccessData => {
|
||||
|
||||
let initMapWorkerExecutor = (resolve, reject) => {
|
||||
let getPayload = (command) => {
|
||||
let getPayload = command => {
|
||||
return {
|
||||
action: 'initMapWorker',
|
||||
data: {
|
||||
@@ -270,7 +270,7 @@ define([
|
||||
|
||||
// init SharedWorker for maps
|
||||
MapWorker.init({
|
||||
characterId: response.data.id,
|
||||
characterId: response.data.id,
|
||||
callbacks: {
|
||||
onInit: (MsgWorkerMessage) => {
|
||||
Util.setSyncStatus(MsgWorkerMessage.command);
|
||||
@@ -552,6 +552,9 @@ define([
|
||||
// Send map update request on tab close/reload, in order to save map changes that
|
||||
// haven´t been saved through default update trigger
|
||||
window.addEventListener('beforeunload', function(e){
|
||||
// close connection to "SharedWorker"
|
||||
MapWorker.close();
|
||||
|
||||
// save unsaved map changes ...
|
||||
triggerMapUpdatePing();
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ define([
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
splashOverlayClass: 'pf-splash' // class for "splash" overlay
|
||||
splashOverlayClass: 'pf-splash', // class for "splash" overlay
|
||||
webSocketStatsId: 'pf-setup-webSocket-stats', // id for webSocket "stats" panel
|
||||
webSocketRefreshStatsId: 'pf-setup-webSocket-stats-refresh' // class for "reload stats" button
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -42,6 +44,18 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param container
|
||||
* @param selector
|
||||
*/
|
||||
let setCollapseObserver = (container, selector) => {
|
||||
container.find(selector).css({cursor: 'pointer'});
|
||||
container.on('click', selector, function(){
|
||||
$(this).find('.pf-animate-rotate').toggleClass('right');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set page observer
|
||||
*/
|
||||
@@ -52,9 +66,7 @@ define([
|
||||
Util.initPageScroll(body);
|
||||
|
||||
// collapse ---------------------------------------------------------------------------------------------------
|
||||
body.find('[data-toggle="collapse"]').css({cursor: 'pointer'}).on('click', function(){
|
||||
$(this).find('.pf-animate-rotate').toggleClass('right');
|
||||
});
|
||||
setCollapseObserver(body, '[data-toggle="collapse"]');
|
||||
|
||||
// buttons ----------------------------------------------------------------------------------------------------
|
||||
// exclude "download" && "navigation" buttons
|
||||
@@ -128,6 +140,29 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* get WebSockets "subscriptions" <table> HTML
|
||||
* @param subscriptionStats
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
let getWebSocketSubscriptionTable = subscriptionStats => {
|
||||
|
||||
let executor = resolve => {
|
||||
requirejs(['text!templates/modules/subscriptions_table.html', 'mustache'], (template, Mustache) => {
|
||||
let data = {
|
||||
panelId: config.webSocketStatsId,
|
||||
refreshButtonId: config.webSocketRefreshStatsId,
|
||||
subStats: subscriptionStats,
|
||||
channelCount: (Util.getObjVal(subscriptionStats, 'channels') || []).length
|
||||
};
|
||||
|
||||
resolve(Mustache.render(template, data));
|
||||
});
|
||||
};
|
||||
|
||||
return new Promise(executor);
|
||||
};
|
||||
|
||||
/**
|
||||
* perform a basic check if Clients (browser) can connect to the webSocket server
|
||||
*/
|
||||
@@ -158,7 +193,7 @@ define([
|
||||
let socketDangerCount = parseInt(badgeSocketDanger.text()) || 0;
|
||||
|
||||
if(data.uri){
|
||||
let uriRow = webSocketPanel.find('.panel-body table tr');
|
||||
let uriRow = webSocketPanel.find('.panel-body').filter(':first').find('table tr');
|
||||
uriRow.find('td:nth-child(2) kbd').html(data.uri.value);
|
||||
if(data.uri.status){
|
||||
let statusIcon = uriRow.find('td:nth-child(3) i');
|
||||
@@ -206,6 +241,18 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param socket
|
||||
* @param task
|
||||
* @param load
|
||||
*/
|
||||
let sendMessage = (socket, task, load) => {
|
||||
socket.send(JSON.stringify({
|
||||
task: task,
|
||||
load: load
|
||||
}));
|
||||
};
|
||||
|
||||
// try to connect to WebSocket server
|
||||
let socket = new WebSocket(webSocketURI);
|
||||
|
||||
@@ -219,10 +266,7 @@ define([
|
||||
});
|
||||
|
||||
// sent token and check response
|
||||
socket.send(JSON.stringify({
|
||||
task: 'healthCheck',
|
||||
load: tcpSocketPanel.data('token')
|
||||
}));
|
||||
sendMessage(socket, 'healthCheck', tcpSocketPanel.attr('data-token'));
|
||||
|
||||
webSocketPanel.hideLoadingAnimation();
|
||||
};
|
||||
@@ -230,7 +274,7 @@ define([
|
||||
socket.onmessage = (e) => {
|
||||
let response = JSON.parse(e.data);
|
||||
|
||||
if(response === 1){
|
||||
if(Util.getObjVal(response, 'load.isValid') === true){
|
||||
// SUCCESS
|
||||
updateWebSocketPanel({
|
||||
status: {
|
||||
@@ -239,6 +283,23 @@ define([
|
||||
class: 'txt-color-success'
|
||||
}
|
||||
});
|
||||
|
||||
// show subscription stats table
|
||||
getWebSocketSubscriptionTable(Util.getObjVal(response, 'load.subStats')).then(payload => {
|
||||
// remove existing table -> then insert new
|
||||
$('#' + config.webSocketStatsId).remove();
|
||||
$(payload).insertAfter(webSocketPanel).initTooltips();
|
||||
|
||||
let token = Util.getObjVal(response, 'load.token');
|
||||
tcpSocketPanel.attr('data-token', token);
|
||||
|
||||
// set "reload stats" observer
|
||||
$('#' + config.webSocketRefreshStatsId).on('click', function(){
|
||||
$('#' + config.webSocketStatsId).showLoadingAnimation();
|
||||
|
||||
sendMessage(socket, 'healthCheck', token);
|
||||
});
|
||||
});
|
||||
}else{
|
||||
// Got response but INVALID
|
||||
updateWebSocketPanel({
|
||||
@@ -274,6 +335,8 @@ define([
|
||||
});
|
||||
|
||||
webSocketPanel.hideLoadingAnimation();
|
||||
|
||||
$('#' + config.webSocketStatsId).remove();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ let ports = [];
|
||||
let characterPorts = [];
|
||||
|
||||
// init "WebSocket" connection ========================================================================================
|
||||
let initSocket = (uri) => {
|
||||
let initSocket = uri => {
|
||||
let MsgWorkerOpen = new MsgWorker('ws:open');
|
||||
|
||||
if(socket === null){
|
||||
socket = new WebSocket(uri);
|
||||
|
||||
// "WebSocket" open -----------------------------------------------------------------------
|
||||
socket.onopen = (e) => {
|
||||
socket.onopen = e => {
|
||||
MsgWorkerOpen.meta({
|
||||
readyState: socket.readyState
|
||||
});
|
||||
@@ -28,7 +28,7 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// "WebSocket message ---------------------------------------------------------------------
|
||||
socket.onmessage = (e) => {
|
||||
socket.onmessage = e => {
|
||||
let response = JSON.parse(e.data);
|
||||
|
||||
let MsgWorkerSend = new MsgWorker('ws:send');
|
||||
@@ -43,7 +43,7 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// "WebSocket" close ----------------------------------------------------------------------
|
||||
socket.onclose = (closeEvent) => {
|
||||
socket.onclose = closeEvent => {
|
||||
let MsgWorkerClosed = new MsgWorker('ws:closed');
|
||||
MsgWorkerClosed.meta({
|
||||
readyState: socket.readyState,
|
||||
@@ -57,7 +57,7 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// "WebSocket" error ----------------------------------------------------------------------
|
||||
socket.onerror = (e) => {
|
||||
socket.onerror = e => {
|
||||
let MsgWorkerError = new MsgWorker('ws:error');
|
||||
MsgWorkerError.meta({
|
||||
readyState: socket.readyState
|
||||
@@ -75,11 +75,11 @@ let initSocket = (uri) => {
|
||||
};
|
||||
|
||||
// send message to port(s) ============================================================================================
|
||||
let sendToCurrentPort = (load) => {
|
||||
let sendToCurrentPort = load => {
|
||||
ports[ports.length - 1].postMessage(load);
|
||||
};
|
||||
|
||||
let broadcastPorts = (load) => {
|
||||
let broadcastPorts = load => {
|
||||
// default: sent to all ports
|
||||
let sentToPorts = ports;
|
||||
|
||||
@@ -114,7 +114,7 @@ let addPort = (port, characterId) => {
|
||||
}
|
||||
};
|
||||
|
||||
let getPortsByCharacterIds = (characterIds) => {
|
||||
let getPortsByCharacterIds = characterIds => {
|
||||
let ports = [];
|
||||
|
||||
for(let i = 0; i < characterPorts.length; i++){
|
||||
@@ -128,8 +128,37 @@ let getPortsByCharacterIds = (characterIds) => {
|
||||
return ports;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param port
|
||||
* @returns {int[]}
|
||||
*/
|
||||
let removePort = port => {
|
||||
let characterIds = [];
|
||||
|
||||
// reverse loop required because of array index reset after splice()
|
||||
let i = characterPorts.length;
|
||||
while(i--){
|
||||
if(characterPorts[i].port === port){
|
||||
// collectt all character Ids mapped to the removed port
|
||||
characterIds.push(characterPorts[i].characterId);
|
||||
characterPorts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let j = ports.length;
|
||||
while(j--){
|
||||
if(ports[j] === port){
|
||||
ports.splice(j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// return unique characterIds
|
||||
return [...new Set(characterIds)];
|
||||
};
|
||||
|
||||
// "SharedWorker" connection ==========================================================================================
|
||||
self.addEventListener('connect', (event) => { // jshint ignore:line
|
||||
self.addEventListener('connect', event => { // jshint ignore:line
|
||||
let port = event.ports[0];
|
||||
addPort(port);
|
||||
|
||||
@@ -145,12 +174,18 @@ self.addEventListener('connect', (event) => { // jshint ignore:line
|
||||
initSocket(data.uri);
|
||||
break;
|
||||
case 'ws:send':
|
||||
let MsgSocket = {
|
||||
socket.send(JSON.stringify({
|
||||
task: MsgWorkerMessage.task(),
|
||||
load: MsgWorkerMessage.data()
|
||||
};
|
||||
}));
|
||||
break;
|
||||
case 'sw:closePort':
|
||||
port.close();
|
||||
|
||||
socket.send(JSON.stringify(MsgSocket));
|
||||
socket.send(JSON.stringify({
|
||||
task: MsgWorkerMessage.task(),
|
||||
load: removePort(port)
|
||||
}));
|
||||
break;
|
||||
case 'ws:close':
|
||||
// closeSocket();
|
||||
|
||||
102
public/templates/modules/subscriptions_table.html
Normal file
102
public/templates/modules/subscriptions_table.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<div id="{{panelId}}" class="panel panel-default pricing-big">
|
||||
<div class="panel-heading text-left">
|
||||
<h3 class="panel-title">
|
||||
<i class="fas fa-sitemap fa-rotate-270"></i> Web-Socket stats
|
||||
<span class="pull-right">
|
||||
<kbd title="current subscribed maps">{{channelCount}} maps</kbd>
|
||||
<kbd title="current subscribed unique characters">{{ subStats.countSub }} chars</kbd>
|
||||
<kbd title="current unique connections">{{ subStats.countCon }} con</kbd>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{{#channelCount}}
|
||||
<div class="panel-body no-padding text-left">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="col-xs-1"></td>
|
||||
<td class="col-xs-1 text-right">id</td>
|
||||
<td class="col-xs-2">map name</td>
|
||||
<td class="col-xs-2"></td>
|
||||
<td class="col-xs-2"></td>
|
||||
<td class="col-xs-2"></td>
|
||||
<td class="col-xs-2"></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#subStats.channels}}
|
||||
<tr>
|
||||
<td data-target=".subStats_channel_{{ channelId }}_col" data-toggle="collapse" style="cursor: pointer">
|
||||
<i class="fas fa-fw fa-chevron-right pf-animate-rotate"></i>
|
||||
</td>
|
||||
<td class="text-right"><kbd>{{ channelId }}</kbd></td>
|
||||
<td colspan="3">{{ channelName }}</td>
|
||||
<td class="text-right" title="current subscribed characters"><kbd>{{ countSub }} chars</kbd></td>
|
||||
<td class="text-right" title="current unique map connections"><kbd>{{ countCon }} con</kbd></td>
|
||||
</tr>
|
||||
<tr class="subStats_channel_{{ channelId }}_col collapse">
|
||||
<td class="bg-color bg-color-tealDarkest">
|
||||
<i class="fas fa-fw fa-level-up-alt fa-rotate-90"></i>
|
||||
</td>
|
||||
<td class="bg-color bg-color-tealDarkest"></td>
|
||||
<td class="bg-color bg-color-tealDarkest"></td>
|
||||
<td class="bg-color bg-color-tealDarkest text-right">id</td>
|
||||
<td class="bg-color bg-color-tealDarkest" colspan="2">character name</td>
|
||||
<td class="bg-color bg-color-tealDarkest"></td>
|
||||
</tr>
|
||||
{{#subscriptions}}
|
||||
<tr class="subStats_channel_{{ channelId }}_col collapse">
|
||||
<td class="text-right bg-color bg-color-tealDarker" data-target=".subStats_channel_{{ channelId }}_{{ characterId }}_col" data-toggle="collapse" style="cursor: pointer">
|
||||
<i class="fas fa-fw fa-chevron-right pf-animate-rotate"></i>
|
||||
</td>
|
||||
<td class="bg-color bg-color-tealDarker"></td>
|
||||
<td class="bg-color bg-color-tealDarker"></td>
|
||||
<td class="bg-color bg-color-tealDarker text-right"><kbd>{{ characterId }} {{@index}}</kbd></td>
|
||||
<td class="bg-color bg-color-tealDarker" colspan="2">{{ characterName }}</td>
|
||||
<td class="bg-color bg-color-tealDarker text-right"><kbd>{{ countCon }} con</kbd></td>
|
||||
</tr>
|
||||
|
||||
<tr class="subStats_channel_{{ channelId }}_col collapse">
|
||||
<td class="no-padding" colspan="8">
|
||||
<table class="table subStats_channel_{{ channelId }}_{{ characterId }}_col collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="col-xs-1 bg-color bg-color-tealDarkest text-right">
|
||||
<i class="fas fa-level-up-alt fa-rotate-90"></i>
|
||||
</td>
|
||||
<td class="col-xs-1 bg-color bg-color-tealDarkest"></td>
|
||||
<td class="col-xs-2 bg-color bg-color-tealDarkest">last update</td>
|
||||
<td class="bg-color bg-color-tealDarkest text-right" colspan="3">client</td>
|
||||
<td class="col-xs-2 bg-color bg-color-tealDarkest text-right">id</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#connections}}
|
||||
<tr>
|
||||
<td class="col-xs-1 bg-color bg-color-tealDark"></td>
|
||||
<td class="col-xs-1 bg-color bg-color-tealDark"></td>
|
||||
<td class="col-xs-1 bg-color bg-color-tealDark" title="{{ mTimeSendFormat1 }}" data-container="body"><kbd>{{ mTimeSendFormat2 }}</kbd></td>
|
||||
<td class="bg-color bg-color-tealDark text-right" colspan="3"><kbd>{{ remoteAddress }}</kbd></td>
|
||||
<td class="col-xs-2 bg-color bg-color-tealDark text-right"><kbd>#{{ resourceId }}</kbd></td>
|
||||
</tr>
|
||||
{{/connections}}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{{/subscriptions}}
|
||||
|
||||
{{/subStats.channels}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{/channelCount}}
|
||||
|
||||
<div class="panel-footer btn-group btn-group-justified">
|
||||
<span id="{{ refreshButtonId }}" class="btn btn-default" title="Reload WebSocket stats" data-container="body">
|
||||
<i class="fas fa-fw fa-sync"></i> Reload stats
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -509,7 +509,7 @@
|
||||
|
||||
<check if="{{ @dbInformation.info.dbConfig }}">
|
||||
<tr>
|
||||
<td data-target=".{{ @dbInformation.info.name }}_dbConfig_col" data-toggle="collapse" aria-expanded="false" aria-controls="{{ @dbInformation.info.name }}__dbConfig_col">
|
||||
<td data-target=".{{ @dbInformation.info.name }}_dbConfig_col" data-toggle="collapse">
|
||||
<i class="fas fa-fw fa-chevron-right pf-animate-rotate"></i>
|
||||
</td>
|
||||
<td class="col-sm-3 col-md-3 text-right">required</td>
|
||||
@@ -1016,7 +1016,7 @@
|
||||
<section id="pf-setup-socket">
|
||||
<div class="container">
|
||||
|
||||
<h4><i class="fas fa-fw fa-exchange-alt"></i> Socket configuration <span class="txt-color txt-color-gray">[optional]</span></h4>
|
||||
<h4><i class="fas fa-fw fa-random"></i> Socket configuration <span class="txt-color txt-color-gray">[optional]</span></h4>
|
||||
|
||||
<div class="row text-center">
|
||||
|
||||
@@ -1024,7 +1024,7 @@
|
||||
<div class="col-xs-12 col-md-6 pf-landing-pricing-panel">
|
||||
<div id="pf-setup-{{ @socketType }}" data-token="{{ @socketData.token }}" class="panel panel-default pricing-big" style="position: relative">
|
||||
<div class="panel-heading text-left">
|
||||
<h3 class="panel-title">{{ @socketData.label }}
|
||||
<h3 class="panel-title"><i class="fas {{ @socketData.icon }}"></i> {{ @socketData.label }}
|
||||
<check if="{{ @socketData.stats }}">
|
||||
<span class="pull-right">
|
||||
<kbd title="current active connections">{{ @socketData.stats.connections }} con</kbd>
|
||||
@@ -1074,6 +1074,73 @@
|
||||
</check>
|
||||
</div>
|
||||
|
||||
{* show log data from "stats" array *}
|
||||
<check if="{{ @socketData.stats.logs }}">
|
||||
<div class="panel-body no-padding text-left">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="col-xs-1" data-target=".{{ @socketType }}_log_col" data-toggle="collapse">
|
||||
<i class="fas fa-fw fa-chevron-right pf-animate-rotate"></i>
|
||||
</td>
|
||||
<td>client URI</td>
|
||||
<td class="col-xs-2">file #line</td>
|
||||
<td>message</td>
|
||||
<td class="col-md-1 text-right">type</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<set logsCheck = 'OK' />
|
||||
<repeat group="{{ @socketData.stats.logs }}" value="{{ @logData }}">
|
||||
<set logTypeClass = "" />
|
||||
<check if="{{ in_array('info', @logData.logTypes) }}">
|
||||
<set logTypeClass = "txt-color-info" />
|
||||
</check>
|
||||
<check if="{{ in_array('error', @logData.logTypes) }}">
|
||||
<set logTypeClass = "txt-color-warning" />
|
||||
<set logsCheck = 'ERROR' />
|
||||
</check>
|
||||
|
||||
<tr class="{{ @socketType }}_log_col {{ @logsCheck == 'OK' ? 'collapse' : '' }}">
|
||||
<td class="bg-color bg-color-tealDarker" title="{{ @logData.mTimeFormat1 }}" data-container="body">
|
||||
{{ @logData.mTimeFormat2 }}
|
||||
</td>
|
||||
<td class="bg-color bg-color-tealDarker" title="{{ @logData.remoteAddress }}" data-container="body">
|
||||
<set cellValue = "{{ parse_url(@logData.remoteAddress, PHP_URL_HOST) . parse_url(@logData.remoteAddress, PHP_URL_PATH) }}" />
|
||||
<check if="{{ parse_url(@logData.remoteAddress, PHP_URL_PORT) }}">
|
||||
<set cellValue = "{{ @cellValue . ':' . parse_url(@logData.remoteAddress, PHP_URL_PORT) }}" />
|
||||
</check>
|
||||
|
||||
<check if="{{ @logData.resourceId }}">
|
||||
<set cellValue = "{{ @cellValue . ' #' . @logData.resourceId }}" />
|
||||
</check>
|
||||
|
||||
<check if="{{ @cellValue }}">
|
||||
<true>
|
||||
<kbd>{{ @cellValue}}</kbd>
|
||||
</true>
|
||||
<false>
|
||||
<check if="{{ @logData.action != 'START' }}">
|
||||
<kbd title="send to multiple clients" class="txt-color txt-color-success">broadcast</kbd>
|
||||
</check>
|
||||
</false>
|
||||
</check>
|
||||
</td>
|
||||
<td class="bg-color bg-color-tealDarker" title="{{ @logData.fileName }} #{{ @logData.lineNumber }}" data-container="body">
|
||||
<kbd>{{ mb_strimwidth(pathinfo(@logData.fileName, PATHINFO_FILENAME), 0, 15, '…') }}</kbd>
|
||||
</td>
|
||||
<td class="bg-color bg-color-tealDarker">{{ @logData.message }}</td>
|
||||
<td class="text-right bg-color bg-color-tealDarker" title="{{ implode(@logData.logTypes, ', ') }}" data-placement="right" data-container="body">
|
||||
<i class="fas fa-fw fa-tag txt-color {{ @logTypeClass }}"></i>
|
||||
</td>
|
||||
</tr>
|
||||
</repeat>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</check>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</repeat>
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
&.bg-color-grayDarker { background-color: $gray-darker !important; }
|
||||
&.bg-color-magenta { background-color: $magenta !important; }
|
||||
&.bg-color-tealLighter { background-color: $teal-lighter !important; }
|
||||
&.bg-color-tealDark { background-color: $teal-dark !important; }
|
||||
&.bg-color-tealDarker { background-color: $teal-darker !important; }
|
||||
&.bg-color-tealDarkest { background-color: $teal-darkest !important; }
|
||||
&.bg-color-redLight { background-color: $redLight !important; }
|
||||
|
||||
@@ -711,9 +711,7 @@
|
||||
|
||||
// setup page -----------------------------------------------------------------
|
||||
.pf-body[data-script='setup']{
|
||||
body{
|
||||
user-select: text;
|
||||
}
|
||||
user-select: text;
|
||||
|
||||
.navbar-brand:hover{
|
||||
color: #777; // overwrite default
|
||||
@@ -737,6 +735,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-heading{
|
||||
padding-right: 8px; // overwrite default
|
||||
}
|
||||
}
|
||||
|
||||
// TEST ---
|
||||
|
||||
@@ -545,6 +545,16 @@ table{
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
|
||||
&.collapsing{
|
||||
@include transition( height .01s ease );
|
||||
|
||||
}
|
||||
|
||||
&.collapse.in{
|
||||
display: table;
|
||||
}
|
||||
|
||||
tr{
|
||||
&.collapsing{
|
||||
@include transition( height .01s ease );
|
||||
|
||||
Reference in New Issue
Block a user