99 lines
5.1 KiB
JavaScript
99 lines
5.1 KiB
JavaScript
const { createRequestAuthHeaders } = require('#src/request-auth');
|
|
const { fetchPlayerRegistrations } = require('#src/data/player-registry');
|
|
const { renderView } = require('../view');
|
|
|
|
module.exports = function registerOnboardingRoutes(app, deps) {
|
|
const pool = deps.pool;
|
|
const playerInternalBaseUrl = String(deps.playerInternalBaseUrl || '').replace(/\/$/, '');
|
|
const requirePermission = deps.requirePermission;
|
|
|
|
app.get('/pairing', requirePermission('pairing.allow'), async function (req, res, next) {
|
|
try {
|
|
const [screens] = await pool.query('SELECT id, name, slug FROM d_screens ORDER BY name ASC, id ASC');
|
|
res.send(renderView('onboarding/pair', {
|
|
title: 'Pair player',
|
|
active: 'pairing',
|
|
currentUser: req.currentUser,
|
|
clientId: String(req.query.clientId || '').trim(),
|
|
pairingCode: String(req.query.code || '').trim(),
|
|
screens: screens,
|
|
scripts: ['vendor/jsqr/jsQR.js', 'js/onboarding/pair.js?v=' + encodeURIComponent(require('#root/package.json').version)]
|
|
}));
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
app.post('/pairing', requirePermission('pairing.allow'), async function (req, res) {
|
|
const pairingCode = String(req.body && req.body.pairingCode || '').trim();
|
|
const clientId = String(req.body && req.body.clientId || '').trim();
|
|
const clientName = String(req.body && req.body.clientName || '').trim();
|
|
const screenSlug = String(req.body && req.body.screenSlug || '').trim();
|
|
if (!pairingCode || !clientName || !screenSlug) {
|
|
return res.status(400).json({ error: 'All pairing fields are required.' });
|
|
}
|
|
|
|
try {
|
|
let targetBaseUrl = playerInternalBaseUrl;
|
|
let deviceId = '';
|
|
let resolvedClientId = clientId;
|
|
{
|
|
const resolvePath = '/api/onboarding/resolve?pairingCode=' + encodeURIComponent(pairingCode);
|
|
const resolveAuthHeaders = createRequestAuthHeaders({ method: 'GET', pathname: '/api/onboarding/resolve' });
|
|
const resolveResponse = await fetch(`${targetBaseUrl}${resolvePath}`, {
|
|
method: 'GET',
|
|
headers: Object.assign({ Accept: 'application/json' }, resolveAuthHeaders)
|
|
});
|
|
const resolveBody = await resolveResponse.text();
|
|
let resolved = null;
|
|
try { resolved = JSON.parse(resolveBody); } catch (_error) {}
|
|
if (!resolveResponse.ok || !resolved || !resolved.deviceId) {
|
|
const registrations = await fetchPlayerRegistrations(pool);
|
|
const remoteRegistrations = registrations.filter(function (player) {
|
|
const internalUrl = String(player && player.internal_base_url || '').replace(/\/$/, '');
|
|
return internalUrl && internalUrl !== playerInternalBaseUrl;
|
|
});
|
|
if (!remoteRegistrations.length) {
|
|
return res.status(resolveResponse.status || 401).type(resolveResponse.headers.get('content-type') || 'application/json').send(resolveBody || JSON.stringify({ error: 'Unable to resolve kiosk pairing code.' }));
|
|
}
|
|
for (const remoteRegistration of remoteRegistrations) {
|
|
targetBaseUrl = String(remoteRegistration.internal_base_url).replace(/\/$/, '');
|
|
const bridgeResolveHeaders = createRequestAuthHeaders({ method: 'GET', pathname: '/api/onboarding/resolve' });
|
|
const bridgeResolveResponse = await fetch(`${targetBaseUrl}/api/onboarding/resolve?pairingCode=${encodeURIComponent(pairingCode)}`, {
|
|
method: 'GET',
|
|
headers: Object.assign({ Accept: 'application/json' }, bridgeResolveHeaders)
|
|
});
|
|
const bridgeResolveBody = await bridgeResolveResponse.text();
|
|
try { resolved = JSON.parse(bridgeResolveBody); } catch (_error) { resolved = null; }
|
|
if (bridgeResolveResponse.ok && resolved && resolved.deviceId) {
|
|
break;
|
|
}
|
|
}
|
|
if (!resolved || !resolved.deviceId) {
|
|
return res.status(401).json({ error: 'A valid kiosk pairing code is required.' });
|
|
}
|
|
}
|
|
if (resolved && resolved.deviceId) {
|
|
deviceId = String(resolved.deviceId).trim();
|
|
resolvedClientId = String(resolved.clientId || resolvedClientId || '').trim();
|
|
}
|
|
}
|
|
|
|
if (!resolvedClientId) {
|
|
return res.status(400).json({ error: 'Client ID is required.' });
|
|
}
|
|
const payload = { clientId: resolvedClientId, pairingCode: pairingCode, clientName: clientName, screenSlug: screenSlug };
|
|
const authHeaders = createRequestAuthHeaders({ method: 'POST', pathname: '/api/onboarding', body: payload });
|
|
const response = await fetch(`${targetBaseUrl}/api/onboarding`, {
|
|
method: 'POST',
|
|
headers: Object.assign({ 'Content-Type': 'application/json', Accept: 'application/json' }, authHeaders),
|
|
body: JSON.stringify(payload)
|
|
});
|
|
const responseBody = await response.text();
|
|
res.status(response.status).type(response.headers.get('content-type') || 'application/json').send(responseBody);
|
|
} catch (error) {
|
|
res.status(502).json({ error: error && error.message ? error.message : 'Player unavailable.' });
|
|
}
|
|
});
|
|
};
|