Release v2.6.9
This commit is contained in:
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## 2.6.9 - 2026-08-08
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The connected-clients view no longer exposes or sorts by client IP, so the table stays focused on the player identity, screen, and playback state.
|
||||||
|
- The connected-clients dashboard row renderer now keeps the actions column aligned after removing the IP column, so row updates no longer append a duplicate actions cell.
|
||||||
|
|
||||||
## 2.6.8 - 2026-08-08
|
## 2.6.8 - 2026-08-08
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage",
|
"name": "pulse-signage",
|
||||||
"version": "2.6.8",
|
"version": "2.6.9",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage application with MySQL and media storage",
|
"description": "Pulse Signage application with MySQL and media storage",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ function normalizeRemoteAddress(value) {
|
|||||||
|
|
||||||
function formatPlayerConnectionLabel(deviceId, remoteAddress) {
|
function formatPlayerConnectionLabel(deviceId, remoteAddress) {
|
||||||
const normalizedDeviceId = String(deviceId || '').trim() || 'unknown-player';
|
const normalizedDeviceId = String(deviceId || '').trim() || 'unknown-player';
|
||||||
const normalizedRemoteAddress = normalizeRemoteAddress(remoteAddress);
|
return normalizedDeviceId;
|
||||||
return normalizedRemoteAddress ? `${normalizedDeviceId} (ip ${normalizedRemoteAddress})` : normalizedDeviceId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeProxyBaseUrl(value) {
|
function normalizeProxyBaseUrl(value) {
|
||||||
@@ -359,7 +358,7 @@ async function start() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logBridge(`Player ${formatPlayerConnectionLabel(socket.playerDeviceId, socket.bridgeRemoteAddress)} has disconnected`);
|
logBridge(`Player ${formatPlayerConnectionLabel(socket.playerDeviceId)} has disconnected`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveMediaPath(fileName) {
|
function resolveMediaPath(fileName) {
|
||||||
@@ -828,7 +827,7 @@ async function start() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
playerSockets.set(deviceId, socket);
|
playerSockets.set(deviceId, socket);
|
||||||
logBridge(`Player ${formatPlayerConnectionLabel(deviceId, socket.bridgeRemoteAddress)} has connected`);
|
logBridge(`Player ${formatPlayerConnectionLabel(deviceId)} has connected`);
|
||||||
socket.send(JSON.stringify({ type: 'registered', ok: true, player: player }));
|
socket.send(JSON.stringify({ type: 'registered', ok: true, player: player }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -875,14 +874,12 @@ async function start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!verifyRequestAuth(request)) {
|
if (!verifyRequestAuth(request)) {
|
||||||
const remoteAddress = normalizeRemoteAddress(request && request.socket && request.socket.remoteAddress);
|
logBridge('Player denied with wrong shared secret');
|
||||||
logBridge(remoteAddress ? `Player (ip ${remoteAddress}) denied with wrong shared secret` : 'Player denied with wrong shared secret');
|
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
playersWs.handleUpgrade(request, socket, head, function (ws) {
|
playersWs.handleUpgrade(request, socket, head, function (ws) {
|
||||||
ws.bridgeRemoteAddress = normalizeRemoteAddress(request && request.socket && request.socket.remoteAddress);
|
|
||||||
playersWs.emit('connection', ws, request);
|
playersWs.emit('connection', ws, request);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ async function start() {
|
|||||||
Accept: 'application/json'
|
Accept: 'application/json'
|
||||||
}, authHeaders)
|
}, authHeaders)
|
||||||
});
|
});
|
||||||
|
|
||||||
return Boolean(response && response.ok);
|
return Boolean(response && response.ok);
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+71
-14
@@ -45,6 +45,76 @@ function createPlayerRuntime(options) {
|
|||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function firstHeaderValue(value) {
|
||||||
|
return String(value || '').trim().split(',')[0].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPrivateOrReservedIp(ip) {
|
||||||
|
const normalized = normalizeClientIp(ip);
|
||||||
|
if (!normalized) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lower = normalized.toLowerCase();
|
||||||
|
if (lower === 'localhost' || lower === '::1') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^10\./.test(lower) || /^192\.168\./.test(lower) || /^127\./.test(lower) || /^169\.254\./.test(lower)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^172\.(1[6-9]|2\d|3[0-1])\./.test(lower)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lower.startsWith('fc') || lower.startsWith('fd') || lower.startsWith('fe80:') || lower.startsWith('::ffff:127.')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickForwardedIp(candidates) {
|
||||||
|
const normalizedCandidates = Array.isArray(candidates)
|
||||||
|
? candidates.map(function (candidate) {
|
||||||
|
return normalizeClientIp(candidate);
|
||||||
|
}).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const publicCandidate = normalizedCandidates.find(function (candidate) {
|
||||||
|
return !isPrivateOrReservedIp(candidate);
|
||||||
|
});
|
||||||
|
|
||||||
|
return publicCandidate || normalizedCandidates[0] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRequestIp(request) {
|
||||||
|
const forwardedFor = String(request && request.headers && request.headers['x-forwarded-for'] || '').split(',');
|
||||||
|
const forwardedForIp = pickForwardedIp(forwardedFor);
|
||||||
|
if (forwardedForIp) {
|
||||||
|
return forwardedForIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realIp = pickForwardedIp([firstHeaderValue(request && request.headers && request.headers['x-real-ip'])]);
|
||||||
|
if (realIp) {
|
||||||
|
return realIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const forwarded = firstHeaderValue(request && request.headers && request.headers.forwarded);
|
||||||
|
if (forwarded) {
|
||||||
|
const forwardedMatches = Array.from(forwarded.matchAll(/(?:^|,\s*|;\s*)for=(?:"?\[?)([^"\];,\s]+)/gi)).map(function (match) {
|
||||||
|
return match[1];
|
||||||
|
});
|
||||||
|
const forwardedIp = pickForwardedIp(forwardedMatches);
|
||||||
|
if (forwardedIp) {
|
||||||
|
return forwardedIp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeClientIp(request && request.socket && request.socket.remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
function parseCookies(cookieHeader) {
|
function parseCookies(cookieHeader) {
|
||||||
return String(cookieHeader || '').split(/;\s*/).reduce(function (cookies, pair) {
|
return String(cookieHeader || '').split(/;\s*/).reduce(function (cookies, pair) {
|
||||||
if (!pair) {
|
if (!pair) {
|
||||||
@@ -145,7 +215,6 @@ function createPlayerRuntime(options) {
|
|||||||
const clientName = String(connection.clientName || '').trim();
|
const clientName = String(connection.clientName || '').trim();
|
||||||
const clientId = String(connection.clientId || '').trim();
|
const clientId = String(connection.clientId || '').trim();
|
||||||
const userAgent = String(connection.userAgent || '').trim();
|
const userAgent = String(connection.userAgent || '').trim();
|
||||||
const clientIp = String(connection.clientIp || '').trim();
|
|
||||||
const viewport = connection.viewport && typeof connection.viewport === 'object'
|
const viewport = connection.viewport && typeof connection.viewport === 'object'
|
||||||
? connection.viewport
|
? connection.viewport
|
||||||
: null;
|
: null;
|
||||||
@@ -161,10 +230,6 @@ function createPlayerRuntime(options) {
|
|||||||
labelParts.push(`id ${clientId.slice(-6)}`);
|
labelParts.push(`id ${clientId.slice(-6)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientIp) {
|
|
||||||
labelParts.push(clientIp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (viewport && Number.isFinite(Number(viewport.width)) && Number.isFinite(Number(viewport.height))) {
|
if (viewport && Number.isFinite(Number(viewport.width)) && Number.isFinite(Number(viewport.height))) {
|
||||||
labelParts.push(`${Number(viewport.width)}x${Number(viewport.height)}`);
|
labelParts.push(`${Number(viewport.width)}x${Number(viewport.height)}`);
|
||||||
}
|
}
|
||||||
@@ -198,8 +263,6 @@ function createPlayerRuntime(options) {
|
|||||||
blackout: Boolean(connection.blackout),
|
blackout: Boolean(connection.blackout),
|
||||||
currentSlideId: connection.currentSlide && connection.currentSlide.id ? connection.currentSlide.id : null,
|
currentSlideId: connection.currentSlide && connection.currentSlide.id ? connection.currentSlide.id : null,
|
||||||
currentSlideTitle: connection.currentSlide && connection.currentSlide.title ? connection.currentSlide.title : null,
|
currentSlideTitle: connection.currentSlide && connection.currentSlide.title ? connection.currentSlide.title : null,
|
||||||
clientIp: connection.clientIp || null,
|
|
||||||
remoteAddress: connection.remoteAddress || null,
|
|
||||||
connectedAt: connection.connectedAt ? connection.connectedAt.toISOString() : null,
|
connectedAt: connection.connectedAt ? connection.connectedAt.toISOString() : null,
|
||||||
lastSeenAt: connection.lastSeenAt ? connection.lastSeenAt.toISOString() : null
|
lastSeenAt: connection.lastSeenAt ? connection.lastSeenAt.toISOString() : null
|
||||||
};
|
};
|
||||||
@@ -431,9 +494,6 @@ function createPlayerRuntime(options) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteAddress = request.socket && request.socket.remoteAddress ? request.socket.remoteAddress : null;
|
|
||||||
const forwardedFor = normalizeClientIp(String(request.headers['x-forwarded-for'] || '').split(',')[0]);
|
|
||||||
const normalizedRemoteAddress = normalizeClientIp(remoteAddress);
|
|
||||||
const connectionId = crypto.randomUUID();
|
const connectionId = crypto.randomUUID();
|
||||||
const connection = {
|
const connection = {
|
||||||
id: connectionId,
|
id: connectionId,
|
||||||
@@ -448,9 +508,7 @@ function createPlayerRuntime(options) {
|
|||||||
paused: false,
|
paused: false,
|
||||||
blackout: false,
|
blackout: false,
|
||||||
playerPublicBaseUrl: null,
|
playerPublicBaseUrl: null,
|
||||||
clientIp: forwardedFor || normalizedRemoteAddress,
|
label: 'connected client',
|
||||||
remoteAddress: normalizedRemoteAddress,
|
|
||||||
label: forwardedFor || normalizedRemoteAddress || 'connected client',
|
|
||||||
connectedAt: new Date(),
|
connectedAt: new Date(),
|
||||||
lastSeenAt: new Date()
|
lastSeenAt: new Date()
|
||||||
};
|
};
|
||||||
@@ -491,7 +549,6 @@ function createPlayerRuntime(options) {
|
|||||||
}
|
}
|
||||||
connection.paused = Boolean(payload.paused);
|
connection.paused = Boolean(payload.paused);
|
||||||
connection.blackout = Boolean(payload.blackout);
|
connection.blackout = Boolean(payload.blackout);
|
||||||
connection.clientIp = payload.clientIp ? normalizeClientIp(payload.clientIp) || connection.clientIp : connection.clientIp;
|
|
||||||
connection.currentSlide = payload.currentSlide && typeof payload.currentSlide === 'object' ? {
|
connection.currentSlide = payload.currentSlide && typeof payload.currentSlide === 'object' ? {
|
||||||
id: payload.currentSlide.id || null,
|
id: payload.currentSlide.id || null,
|
||||||
title: payload.currentSlide.title || '',
|
title: payload.currentSlide.title || '',
|
||||||
|
|||||||
@@ -245,8 +245,6 @@
|
|||||||
client && client.slug,
|
client && client.slug,
|
||||||
client && client.screen_slug,
|
client && client.screen_slug,
|
||||||
client && client.screen_name,
|
client && client.screen_name,
|
||||||
client && client.ipAddress,
|
|
||||||
client && client.clientIp,
|
|
||||||
client && client.status,
|
client && client.status,
|
||||||
client && client.currentSlideTitle
|
client && client.currentSlideTitle
|
||||||
];
|
];
|
||||||
@@ -264,7 +262,6 @@
|
|||||||
client: function (client) { return String(client && (getClientDisplayName(client) || client.client_name || client.name || client.clientId) || '').trim(); },
|
client: function (client) { return String(client && (getClientDisplayName(client) || client.client_name || client.name || client.clientId) || '').trim(); },
|
||||||
screen: function (client) { return String(client && (client.screen_name || client.screen_slug) || '').trim(); },
|
screen: function (client) { return String(client && (client.screen_name || client.screen_slug) || '').trim(); },
|
||||||
slide: function (client) { return String(client && client.currentSlideTitle || '').trim(); },
|
slide: function (client) { return String(client && client.currentSlideTitle || '').trim(); },
|
||||||
ip: function (client) { return String(client && client.clientIp || '').trim(); },
|
|
||||||
viewport: function (client) {
|
viewport: function (client) {
|
||||||
var viewport = client && client.viewport;
|
var viewport = client && client.viewport;
|
||||||
if (!viewport || !viewport.width || !viewport.height) {
|
if (!viewport || !viewport.width || !viewport.height) {
|
||||||
@@ -283,12 +280,6 @@
|
|||||||
? [normalizedSortKey]
|
? [normalizedSortKey]
|
||||||
: ['client'];
|
: ['client'];
|
||||||
|
|
||||||
if (sortKeys[0] === 'client') {
|
|
||||||
sortKeys.push('ip');
|
|
||||||
} else if (sortKeys[0] === 'ip') {
|
|
||||||
sortKeys.push('client');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Array.isArray(clients) ? clients.slice() : []).sort(function (leftClient, rightClient) {
|
return (Array.isArray(clients) ? clients.slice() : []).sort(function (leftClient, rightClient) {
|
||||||
for (var index = 0; index < sortKeys.length; index += 1) {
|
for (var index = 0; index < sortKeys.length; index += 1) {
|
||||||
var sortKeyName = sortKeys[index];
|
var sortKeyName = sortKeys[index];
|
||||||
@@ -539,13 +530,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hasActionsColumn) {
|
if (!hasActionsColumn) {
|
||||||
if (row.cells.length > 6) {
|
if (row.cells.length > 5) {
|
||||||
row.deleteCell(row.cells.length - 1);
|
row.deleteCell(row.cells.length - 1);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionCell = row.cells.length > 6 ? row.cells[6] : null;
|
var actionCell = row.cells.length > 5 ? row.cells[5] : null;
|
||||||
if (!actionCell) {
|
if (!actionCell) {
|
||||||
actionCell = row.insertCell(-1);
|
actionCell = row.insertCell(-1);
|
||||||
actionCell.setAttribute('data-label', 'Actions');
|
actionCell.setAttribute('data-label', 'Actions');
|
||||||
@@ -557,8 +548,6 @@
|
|||||||
|
|
||||||
function renderClientRow(client, hasActionsColumn) {
|
function renderClientRow(client, hasActionsColumn) {
|
||||||
var connectedAt = client.connectedAt ? '<div>' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '</div>' + (client.lastSeenAt ? '<div class="subtle connected-updated-secondary">' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '</div>' : '') : '<span class="empty">Unknown</span>';
|
var connectedAt = client.connectedAt ? '<div>' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '</div>' + (client.lastSeenAt ? '<div class="subtle connected-updated-secondary">' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '</div>' : '') : '<span class="empty">Unknown</span>';
|
||||||
var clientIpValue = normalizeDisplayIp(client.clientIp);
|
|
||||||
var clientIp = clientIpValue ? escapeHtml(clientIpValue) : '<span class="empty">Unknown</span>';
|
|
||||||
var viewport = client.viewport && client.viewport.width && client.viewport.height ? escapeHtml(client.viewport.width + 'x' + client.viewport.height) : '<span class="empty">Unknown</span>';
|
var viewport = client.viewport && client.viewport.width && client.viewport.height ? escapeHtml(client.viewport.width + 'x' + client.viewport.height) : '<span class="empty">Unknown</span>';
|
||||||
var clientNameValue = getClientDisplayName(client);
|
var clientNameValue = getClientDisplayName(client);
|
||||||
var clientName = clientNameValue ? escapeHtml(clientNameValue) : '<span class="empty">Unknown</span>';
|
var clientName = clientNameValue ? escapeHtml(clientNameValue) : '<span class="empty">Unknown</span>';
|
||||||
@@ -571,7 +560,6 @@
|
|||||||
'<td data-label="Client"><div>' + clientName + '</div></td>',
|
'<td data-label="Client"><div>' + clientName + '</div></td>',
|
||||||
'<td data-label="Current Screen"><div>' + screenName + '</div></td>',
|
'<td data-label="Current Screen"><div>' + screenName + '</div></td>',
|
||||||
'<td data-label="Current Slide">' + currentSlide + '</td>',
|
'<td data-label="Current Slide">' + currentSlide + '</td>',
|
||||||
'<td data-label="IP">' + clientIp + '</td>',
|
|
||||||
'<td data-label="Viewport">' + viewport + '</td>',
|
'<td data-label="Viewport">' + viewport + '</td>',
|
||||||
'<td data-label="Connected/Updated">' + connectedAt + '</td>',
|
'<td data-label="Connected/Updated">' + connectedAt + '</td>',
|
||||||
actionCell,
|
actionCell,
|
||||||
@@ -580,13 +568,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateClientRowCells(row, client, hasActionsColumn) {
|
function updateClientRowCells(row, client, hasActionsColumn) {
|
||||||
if (!row || !row.cells || row.cells.length < 6) {
|
if (!row || !row.cells || row.cells.length < 5) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var connectedAt = client.connectedAt ? '<div>' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '</div>' + (client.lastSeenAt ? '<div class="subtle connected-updated-secondary">' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '</div>' : '') : '<span class="empty">Unknown</span>';
|
var connectedAt = client.connectedAt ? '<div>' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '</div>' + (client.lastSeenAt ? '<div class="subtle connected-updated-secondary">' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '</div>' : '') : '<span class="empty">Unknown</span>';
|
||||||
var clientIpValue = normalizeDisplayIp(client.clientIp);
|
|
||||||
var clientIp = clientIpValue ? escapeHtml(clientIpValue) : '<span class="empty">Unknown</span>';
|
|
||||||
var viewport = client.viewport && client.viewport.width && client.viewport.height ? escapeHtml(client.viewport.width + 'x' + client.viewport.height) : '<span class="empty">Unknown</span>';
|
var viewport = client.viewport && client.viewport.width && client.viewport.height ? escapeHtml(client.viewport.width + 'x' + client.viewport.height) : '<span class="empty">Unknown</span>';
|
||||||
var clientNameValue = getClientDisplayName(client);
|
var clientNameValue = getClientDisplayName(client);
|
||||||
var clientName = clientNameValue ? escapeHtml(clientNameValue) : '<span class="empty">Unknown</span>';
|
var clientName = clientNameValue ? escapeHtml(clientNameValue) : '<span class="empty">Unknown</span>';
|
||||||
@@ -603,9 +589,8 @@
|
|||||||
setCellHtml(row.cells[0], '<div>' + clientName + '</div>');
|
setCellHtml(row.cells[0], '<div>' + clientName + '</div>');
|
||||||
setCellHtml(row.cells[1], '<div>' + screenName + '</div>');
|
setCellHtml(row.cells[1], '<div>' + screenName + '</div>');
|
||||||
setCellHtml(row.cells[2], currentSlide);
|
setCellHtml(row.cells[2], currentSlide);
|
||||||
setCellHtml(row.cells[3], clientIp);
|
setCellHtml(row.cells[3], viewport);
|
||||||
setCellHtml(row.cells[4], viewport);
|
setCellHtml(row.cells[4], connectedAt);
|
||||||
setCellHtml(row.cells[5], connectedAt);
|
|
||||||
|
|
||||||
syncClientActionCell(row, client, hasActionsColumn);
|
syncClientActionCell(row, client, hasActionsColumn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ module.exports = function registerClientsRoutes(app, deps) {
|
|||||||
client: function (client) { return String(client && (client.client_name || client.name || client.clientId) || '').trim(); },
|
client: function (client) { return String(client && (client.client_name || client.name || client.clientId) || '').trim(); },
|
||||||
screen: function (client) { return String(client && (client.screen_name || client.screen_slug) || '').trim(); },
|
screen: function (client) { return String(client && (client.screen_name || client.screen_slug) || '').trim(); },
|
||||||
slide: function (client) { return String(client && client.currentSlideTitle || '').trim(); },
|
slide: function (client) { return String(client && client.currentSlideTitle || '').trim(); },
|
||||||
ip: function (client) { return String(client && client.clientIp || '').trim(); },
|
|
||||||
viewport: function (client) {
|
viewport: function (client) {
|
||||||
const viewport = client && client.viewport;
|
const viewport = client && client.viewport;
|
||||||
if (!viewport || !viewport.width || !viewport.height) {
|
if (!viewport || !viewport.width || !viewport.height) {
|
||||||
@@ -37,12 +36,6 @@ module.exports = function registerClientsRoutes(app, deps) {
|
|||||||
? [normalizedSortKey]
|
? [normalizedSortKey]
|
||||||
: ['client'];
|
: ['client'];
|
||||||
|
|
||||||
if (sortKeys[0] === 'client') {
|
|
||||||
sortKeys.push('ip');
|
|
||||||
} else if (sortKeys[0] === 'ip') {
|
|
||||||
sortKeys.push('client');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Array.isArray(clients) ? clients.slice() : []).sort(function (leftClient, rightClient) {
|
return (Array.isArray(clients) ? clients.slice() : []).sort(function (leftClient, rightClient) {
|
||||||
for (let index = 0; index < sortKeys.length; index += 1) {
|
for (let index = 0; index < sortKeys.length; index += 1) {
|
||||||
const sortKeyName = sortKeys[index];
|
const sortKeyName = sortKeys[index];
|
||||||
@@ -71,7 +64,6 @@ module.exports = function registerClientsRoutes(app, deps) {
|
|||||||
'deviceId',
|
'deviceId',
|
||||||
'slug',
|
'slug',
|
||||||
'screen_slug',
|
'screen_slug',
|
||||||
'ipAddress',
|
|
||||||
'status'
|
'status'
|
||||||
]);
|
]);
|
||||||
const filteredClients = (data.clients || []).filter(matchesSearch);
|
const filteredClients = (data.clients || []).filter(matchesSearch);
|
||||||
|
|||||||
@@ -88,7 +88,6 @@
|
|||||||
<th data-table-sort-key="client">Client</th>
|
<th data-table-sort-key="client">Client</th>
|
||||||
<th data-table-sort-key="screen">Current Screen Group</th>
|
<th data-table-sort-key="screen">Current Screen Group</th>
|
||||||
<th data-table-sort-key="slide">Current Slide</th>
|
<th data-table-sort-key="slide">Current Slide</th>
|
||||||
<th data-table-sort-key="ip">IP</th>
|
|
||||||
<th data-table-sort-key="viewport">Viewport</th>
|
<th data-table-sort-key="viewport">Viewport</th>
|
||||||
<th data-table-sort-key="connected">Connected/Updated</th>
|
<th data-table-sort-key="connected">Connected/Updated</th>
|
||||||
{{#if (hasPermission currentUser 'clients.allow')}}<th>Actions</th>{{/if}}
|
{{#if (hasPermission currentUser 'clients.allow')}}<th>Actions</th>{{/if}}
|
||||||
@@ -111,7 +110,6 @@
|
|||||||
<span class="empty">No slide currently showing</span>
|
<span class="empty">No slide currently showing</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td data-label="IP">{{#if clientIp}}{{clientIp}}{{else}}<span class="empty">Unknown</span>{{/if}}</td>
|
|
||||||
<td data-label="Viewport">
|
<td data-label="Viewport">
|
||||||
{{#if viewport}}
|
{{#if viewport}}
|
||||||
{{viewport.width}}x{{viewport.height}}
|
{{viewport.width}}x{{viewport.height}}
|
||||||
@@ -169,7 +167,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<tr data-table-search-empty-default><td colspan="{{#if (hasPermission currentUser 'clients.allow')}}7{{else}}6{{/if}}" class="empty">No connected clients yet.</td></tr>
|
<tr data-table-search-empty-default><td colspan="{{#if (hasPermission currentUser 'clients.allow')}}6{{else}}5{{/if}}" class="empty">No connected clients yet.</td></tr>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user