module.exports = function registerAdminScreenCommandRoutes(app, deps) { const pool = deps.pool; const forwardPlayerCommand = deps.forwardPlayerCommand; const getScreenConnections = deps.getScreenConnections; const isClientNameAvailable = deps.isClientNameAvailable; const withClientNameReservation = deps.withClientNameReservation; const requirePermission = deps.requirePermission; app.post('/admin/screens/:slug/commands', requirePermission('clients.allow'), async function (req, res, next) { try { const slug = String(req.params.slug || '').trim(); const command = String((req.body && req.body.command) || req.query.command || '').trim().toLowerCase(); const connectionId = String((req.body && (req.body.connectionId || req.body.clientId)) || req.query.connectionId || req.query.clientId || '').trim(); const blackoutValue = req.body && Object.prototype.hasOwnProperty.call(req.body, 'blackout') ? req.body.blackout : req.query.blackout; if (!slug) { return res.status(400).json({ error: 'Screen slug is required' }); } if (!command) { return res.status(400).json({ error: 'Command is required' }); } const [screenRows] = await pool.query('SELECT id, name, slug FROM screens WHERE slug = ?', [slug]); if (!screenRows.length) { return res.status(404).json({ error: 'Screen not found' }); } if (command === 'setclientname') { const deviceId = String((req.body && (req.body.deviceId || req.body.clientId)) || req.query.deviceId || req.query.clientId || '').trim(); const clientName = String((req.body && req.body.clientName) || req.query.clientName || '').trim(); if (!deviceId) { return res.status(400).json({ error: 'Device ID is required' }); } if (!clientName) { return res.status(400).json({ error: 'Client name is required' }); } const [currentRows] = await pool.query( `SELECT client_name FROM player_onboarding_devices WHERE device_id = ? LIMIT 1`, [deviceId] ); const onboardingRow = currentRows[0] || null; const currentName = String(onboardingRow && onboardingRow.client_name ? onboardingRow.client_name : '').trim(); if (currentName && currentName.toLowerCase() === clientName.toLowerCase()) { return res.json({ screen: screenRows[0], screenSlug: slug, command: command, connectionId: connectionId || null, deviceId: deviceId, clientName: currentName, ok: true, unchanged: true }); } if (typeof withClientNameReservation !== 'function') { return res.status(500).json({ error: 'Client name reservation is unavailable.' }); } return withClientNameReservation(pool, clientName, async function () { let liveConnections = []; try { const [screenSlugs] = await pool.query('SELECT slug FROM screens ORDER BY slug ASC'); const liveResults = await Promise.all((screenSlugs || []).map(async function (row) { const screenSlug = String(row && row.slug ? row.slug : '').trim(); if (!screenSlug || typeof getScreenConnections !== 'function') { return []; } try { const liveResponse = await getScreenConnections(screenSlug); return Array.isArray(liveResponse && liveResponse.connections) ? liveResponse.connections : []; } catch (_error) { return []; } })); liveConnections = liveResults.flat(); } catch (_error) { liveConnections = []; } const available = await isClientNameAvailable(pool, clientName, deviceId, liveConnections); if (!available) { return res.status(409).json({ error: 'Client name already exists.' }); } if (!onboardingRow) { await forwardPlayerCommand(slug, { command: command, clientName: clientName, clientId: connectionId || deviceId || null, deviceId: deviceId || null }, connectionId || deviceId || undefined); return res.json({ screen: screenRows[0], screenSlug: slug, command: command, connectionId: connectionId || null, deviceId: deviceId, clientName: clientName, ok: true, liveOnly: true }); } const [updateResult] = await pool.query( `UPDATE player_onboarding_devices pod JOIN screens s ON s.id = pod.screen_id SET pod.client_name = ?, pod.modified_at = CURRENT_TIMESTAMP WHERE s.slug = ? AND pod.device_id = ?`, [clientName, slug, deviceId] ); if (!updateResult.affectedRows) { return res.status(404).json({ error: 'Client not found' }); } return res.json({ screen: screenRows[0], screenSlug: slug, command: command, connectionId: connectionId || null, deviceId: deviceId, clientName: clientName, ok: true }); }); } const commandPayload = req.body && typeof req.body === 'object' && !Array.isArray(req.body) ? Object.assign({}, req.body, { command: command }) : { command: command }; if (command === 'blackout' && blackoutValue !== undefined && commandPayload && typeof commandPayload === 'object') { commandPayload.blackout = blackoutValue; } const result = connectionId ? await forwardPlayerCommand(slug, commandPayload, connectionId) : await forwardPlayerCommand(slug, commandPayload); return res.json(Object.assign({ screen: screenRows[0], screenSlug: slug, command: command, connectionId: connectionId || null }, result && typeof result === 'object' ? result : {})); } catch (error) { next(error); } }); };