Release 2.6.7
This commit is contained in:
+138
-14
@@ -11,6 +11,7 @@ module.exports = function registerManageRoutes(app, deps) {
|
||||
const getScreenDeleteBlockMessage = deps.getScreenDeleteBlockMessage;
|
||||
const getScreenConnections = deps.getScreenConnections;
|
||||
const forwardPlayerCommand = deps.forwardPlayerCommand;
|
||||
const forwardPlayerCommandToBaseUrl = deps.forwardPlayerCommandToBaseUrl;
|
||||
const requirePermission = deps.requirePermission;
|
||||
|
||||
const SCREEN_NAME_MAX_LENGTH = 255;
|
||||
@@ -25,13 +26,106 @@ module.exports = function registerManageRoutes(app, deps) {
|
||||
return text.slice(0, limit);
|
||||
}
|
||||
|
||||
async function fetchAllScreenSlugs() {
|
||||
const [rows] = await pool.query('SELECT slug FROM d_screens ORDER BY slug ASC');
|
||||
function normalizeBaseUrl(value) {
|
||||
return String(value || '').trim().replace(/\/$/, '');
|
||||
}
|
||||
|
||||
function normalizeExplicitPlayerBaseUrl(value) {
|
||||
return String(value || '').trim().replace(/\/$/, '');
|
||||
}
|
||||
|
||||
async function fetchPlayerRegistrations() {
|
||||
if (typeof common.fetchPlayerRegistrations !== 'function') {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const registrations = await common.fetchPlayerRegistrations(pool);
|
||||
return Array.isArray(registrations) ? registrations : [];
|
||||
} catch (_error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function isRecentPlayerRegistration(player, staleSeconds) {
|
||||
const lastSeenAt = player && player.last_seen_at;
|
||||
const lastSeenAtValue = lastSeenAt instanceof Date ? lastSeenAt.getTime() : new Date(lastSeenAt).getTime();
|
||||
const cutoffTime = Date.now() - Math.max(30, Number(staleSeconds || 60)) * 1000;
|
||||
|
||||
return Number.isFinite(lastSeenAtValue) && lastSeenAtValue >= cutoffTime;
|
||||
}
|
||||
|
||||
async function fetchLiveConnectionsForScreen(slug) {
|
||||
if (typeof getScreenConnections !== 'function') {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await getScreenConnections(slug);
|
||||
return Array.isArray(response && response.connections) ? response.connections : [];
|
||||
} catch (_error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function resolvePlayerBaseUrlsForConnections(connections) {
|
||||
const connectionList = Array.isArray(connections) ? connections : [];
|
||||
const seen = new Set();
|
||||
const registrations = await fetchPlayerRegistrations();
|
||||
|
||||
return connectionList.map(function (connection) {
|
||||
const selectedPublicBaseUrl = normalizeExplicitPlayerBaseUrl(connection && connection.playerPublicBaseUrl);
|
||||
if (!selectedPublicBaseUrl || seen.has(selectedPublicBaseUrl)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
seen.add(selectedPublicBaseUrl);
|
||||
const matchedPlayer = registrations.find(function (player) {
|
||||
return normalizeExplicitPlayerBaseUrl(player && player.public_base_url) === selectedPublicBaseUrl;
|
||||
}) || null;
|
||||
|
||||
return normalizeExplicitPlayerBaseUrl(matchedPlayer && matchedPlayer.internal_base_url) || selectedPublicBaseUrl;
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
async function forwardPlayerCommandForConnections(slug, connections, commandPayload) {
|
||||
const targetBaseUrls = await resolvePlayerBaseUrlsForConnections(connections);
|
||||
if (targetBaseUrls.length && typeof forwardPlayerCommandToBaseUrl === 'function') {
|
||||
const results = await Promise.allSettled(targetBaseUrls.map(function (targetBaseUrl) {
|
||||
return forwardPlayerCommandToBaseUrl(targetBaseUrl, slug, commandPayload);
|
||||
}));
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
sent: results.filter(function (result) {
|
||||
return result.status === 'fulfilled';
|
||||
}).length,
|
||||
results: results
|
||||
};
|
||||
}
|
||||
|
||||
return forwardPlayerCommand(slug, commandPayload);
|
||||
}
|
||||
|
||||
async function fetchAllScreenTargets() {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT s.slug
|
||||
FROM d_screens s
|
||||
WHERE s.slug IS NOT NULL
|
||||
ORDER BY s.slug ASC`
|
||||
);
|
||||
return (rows || [])
|
||||
.map(function (row) {
|
||||
return String(row && row.slug ? row.slug : '').trim();
|
||||
return {
|
||||
slug: String(row && row.slug ? row.slug : '').trim(),
|
||||
playerId: '',
|
||||
publicBaseUrl: '',
|
||||
internalBaseUrl: ''
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
.filter(function (row) {
|
||||
return Boolean(row.slug);
|
||||
});
|
||||
}
|
||||
|
||||
app.post('/commands', requirePermission('dashboard.allow'), async function (req, res, next) {
|
||||
@@ -52,8 +146,8 @@ module.exports = function registerManageRoutes(app, deps) {
|
||||
return res.status(400).json({ error: 'Unsupported command' });
|
||||
}
|
||||
|
||||
const slugs = await fetchAllScreenSlugs();
|
||||
if (!slugs.length) {
|
||||
const targets = await fetchAllScreenTargets();
|
||||
if (!targets.length) {
|
||||
await broadcastDashboardState();
|
||||
return res.json({ ok: true, command: command, sent: 0 });
|
||||
}
|
||||
@@ -70,7 +164,22 @@ module.exports = function registerManageRoutes(app, deps) {
|
||||
}
|
||||
: 'reload';
|
||||
|
||||
const sentCount = await notifyPlayerScreens(slugs, payload);
|
||||
const results = await Promise.allSettled(targets.map(async function (target) {
|
||||
const liveConnections = await fetchLiveConnectionsForScreen(target.slug);
|
||||
if (liveConnections.length) {
|
||||
return forwardPlayerCommandForConnections(target.slug, liveConnections, payload);
|
||||
}
|
||||
|
||||
return forwardPlayerCommand(target.slug, payload);
|
||||
}));
|
||||
const sentCount = results.reduce(function (total, result) {
|
||||
if (result.status !== 'fulfilled') {
|
||||
return total;
|
||||
}
|
||||
|
||||
const sentValue = Number(result.value && typeof result.value.sent === 'number' ? result.value.sent : 1);
|
||||
return total + (Number.isFinite(sentValue) && sentValue > 0 ? sentValue : 1);
|
||||
}, 0);
|
||||
await broadcastDashboardState();
|
||||
|
||||
return res.json({
|
||||
@@ -149,13 +258,28 @@ module.exports = function registerManageRoutes(app, deps) {
|
||||
await notifyPlayerScreens([previousSlug], 'refresh');
|
||||
}
|
||||
if (previousSlug && previousSlug !== slug) {
|
||||
const playerBaseUrl = typeof common.fetchPlayerPublicBaseUrl === 'function'
|
||||
? await common.fetchPlayerPublicBaseUrl(pool)
|
||||
: '';
|
||||
await forwardPlayerCommand(previousSlug, {
|
||||
command: 'redirect',
|
||||
url: playerBaseUrl ? `${playerBaseUrl}/screen/${encodeURIComponent(slug)}` : `/screen/${encodeURIComponent(slug)}`
|
||||
});
|
||||
const previousScreenTargets = await pool.query(
|
||||
`SELECT s.slug, s.player_id, p.public_base_url, p.internal_base_url
|
||||
FROM d_screens s
|
||||
LEFT JOIN d_players p ON p.device_id = s.player_id
|
||||
WHERE s.slug = ?
|
||||
LIMIT 1`,
|
||||
[previousSlug]
|
||||
);
|
||||
const previousTargetRow = previousScreenTargets[0] && previousScreenTargets[0][0] || null;
|
||||
const previousInternalBaseUrl = normalizeBaseUrl(previousTargetRow && previousTargetRow.internal_base_url) || '';
|
||||
const previousPublicBaseUrl = normalizeBaseUrl(previousTargetRow && previousTargetRow.public_base_url) || '';
|
||||
if (previousInternalBaseUrl && typeof forwardPlayerCommandToBaseUrl === 'function') {
|
||||
await forwardPlayerCommandToBaseUrl(previousInternalBaseUrl, previousSlug, {
|
||||
command: 'redirect',
|
||||
url: previousPublicBaseUrl ? `${previousPublicBaseUrl}/screen/${encodeURIComponent(slug)}` : `/screen/${encodeURIComponent(slug)}`
|
||||
});
|
||||
} else {
|
||||
await forwardPlayerCommand(previousSlug, {
|
||||
command: 'redirect',
|
||||
url: previousPublicBaseUrl ? `${previousPublicBaseUrl}/screen/${encodeURIComponent(slug)}` : `/screen/${encodeURIComponent(slug)}`
|
||||
});
|
||||
}
|
||||
}
|
||||
redirectAfterSave(req, res, '/screens?edit=' + screen.id, {
|
||||
closeUrl: '/screens',
|
||||
|
||||
Reference in New Issue
Block a user