Files
pulse-signage/src/web/routes/onboarding.js
T
lzstealth 3960931ebe
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m53s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 34s
Add onboarding weather and template gradients
2026-08-28 20:05:23 +01:00

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.' });
}
});
};