Files
pulse-signage/src/web/lib/dashboard-state.js
T
2026-08-01 21:41:44 +01:00

131 lines
5.4 KiB
JavaScript

// Helpers for normalizing dashboard state and date values.
const { WebSocket } = require('ws');
function normalizeClientName(value) {
return String(value || '').trim();
}
function enrichScreensWithConnections(screens, connectionsBySlug, onboardingNameBySlug, playerUrlsBySlug) {
return (screens || []).map(function (screen) {
const connectionState = connectionsBySlug[screen.slug] || { count: 0, connections: [] };
return Object.assign({}, screen, {
client_name: onboardingNameBySlug[screen.slug] || null,
player_connection_count: connectionState.count || 0,
player_connections: Array.isArray(connectionState.connections) ? connectionState.connections : [],
player_url: String(playerUrlsBySlug && playerUrlsBySlug[screen.slug] || '').trim() || null
});
});
}
function buildClientRows(screens, connectionsBySlug, onboardingNameBySlug, onboardingNameByDeviceId, formatDashboardDate) {
return (screens || []).flatMap(function (screen) {
const connectionState = connectionsBySlug[screen.slug] || { count: 0, connections: [] };
return (connectionState.connections || []).map(function (connection) {
const deviceId = String(connection.deviceId || '').trim();
return Object.assign({}, connection, {
screen_slug: screen.slug,
screen_name: screen.name,
client_name: (deviceId && onboardingNameByDeviceId && onboardingNameByDeviceId[deviceId]) || connection.clientName || onboardingNameBySlug[screen.slug] || String(connection.clientId || '').trim() || null,
playlist_name: screen.playlist_name || null,
connectedAtLabel: formatDashboardDate(connection.connectedAt),
lastSeenAtLabel: formatDashboardDate(connection.lastSeenAt),
player_url: screen.player_url || String(connection.page || '').trim() || null
});
});
});
}
function compareScreenNames(left, right) {
const leftName = String(left && left.name || '').trim();
const rightName = String(right && right.name || '').trim();
const nameCompare = leftName.localeCompare(rightName, undefined, { sensitivity: 'base', numeric: true });
if (nameCompare !== 0) {
return nameCompare;
}
return String(left && left.slug || '').localeCompare(String(right && right.slug || ''), undefined, { sensitivity: 'base', numeric: true });
}
function createDashboardStateService(options) {
const pool = options && options.pool;
const common = options && options.common;
const playerSnapshotCache = options && options.playerSnapshotCache;
const playerSnapshotSockets = options && options.playerSnapshotSockets;
const ensurePlayerSnapshotSubscription = options && options.ensurePlayerSnapshotSubscription;
const formatDashboardDate = options && options.formatDashboardDate;
if (!pool || !common || !playerSnapshotCache || !playerSnapshotSockets || typeof ensurePlayerSnapshotSubscription !== 'function' || typeof formatDashboardDate !== 'function') {
throw new Error('createDashboardStateService requires the dashboard dependencies.');
}
async function buildDashboardState() {
const data = await common.fetchAdminData(pool);
const screensData = data.screens || [];
const playerUrlsBySlug = typeof common.fetchScreenPlayerUrls === 'function'
? await common.fetchScreenPlayerUrls(pool)
: {};
screensData.forEach(function (screen) {
ensurePlayerSnapshotSubscription(screen.slug);
});
const [onboardingRows] = await pool.query(
`SELECT s.slug, pod.device_id, pod.client_name
FROM d_onboarding_devices pod
JOIN d_screens s ON s.id = pod.screen_id
WHERE pod.client_name IS NOT NULL
AND TRIM(pod.client_name) <> ''`
);
const onboardingNameBySlug = {};
const onboardingNameByDeviceId = {};
onboardingRows.forEach(function (row) {
const clientName = normalizeClientName(row.client_name);
const slug = normalizeClientName(row.slug);
const deviceId = normalizeClientName(row.device_id);
if (slug) {
onboardingNameBySlug[slug] = clientName;
}
if (deviceId) {
onboardingNameByDeviceId[deviceId] = clientName;
}
});
const connectionsBySlug = {};
screensData.forEach(function (screen) {
const cached = playerSnapshotCache.get(String(screen.slug || '').trim());
if (cached && Array.isArray(cached.connections)) {
connectionsBySlug[screen.slug] = cached;
}
});
const screens = enrichScreensWithConnections(data.screens || [], connectionsBySlug, onboardingNameBySlug, playerUrlsBySlug)
.map(function (screen) {
return Object.assign({}, screen, {
player_url: screen.player_url || null
});
})
.sort(compareScreenNames);
const clients = buildClientRows(screens, connectionsBySlug, onboardingNameBySlug, onboardingNameByDeviceId, formatDashboardDate);
const playerServiceConnected = Array.from(playerSnapshotSockets.values()).some(function (socket) {
return socket && socket.readyState === WebSocket.OPEN;
});
return {
playlists: data.playlists || [],
screens: screens,
clients: clients,
slides: data.slides || [],
playerServiceConnected: playerServiceConnected,
connectedClientsCount: screens.reduce(function (total, screen) {
return total + Number(screen.player_connection_count || 0);
}, 0)
};
}
return {
buildDashboardState: buildDashboardState
};
}
module.exports = { createDashboardStateService };