140 lines
4.5 KiB
JavaScript
140 lines
4.5 KiB
JavaScript
function normalizeDeviceId(value) {
|
|
return String(value || '')
|
|
.trim()
|
|
.replace(/[^a-zA-Z0-9_-]/g, '')
|
|
.slice(0, 128);
|
|
}
|
|
|
|
function normalizeBaseUrl(value) {
|
|
return String(value || '').trim().replace(/\/$/, '');
|
|
}
|
|
|
|
function normalizeIdentifier(value) {
|
|
return String(value || '').trim().slice(0, 255);
|
|
}
|
|
|
|
async function columnExists(pool, tableName, columnName) {
|
|
const [rows] = await pool.query(
|
|
`SELECT COUNT(*) AS column_count
|
|
FROM information_schema.COLUMNS
|
|
WHERE TABLE_SCHEMA = DATABASE()
|
|
AND TABLE_NAME = ?
|
|
AND COLUMN_NAME = ?`,
|
|
[tableName, columnName]
|
|
);
|
|
|
|
return Number(rows && rows[0] && rows[0].column_count) > 0;
|
|
}
|
|
|
|
async function fetchPlayerRegistrations(pool) {
|
|
if (!pool) {
|
|
return [];
|
|
}
|
|
|
|
const [rows] = await pool.query(
|
|
`SELECT id, identifier, public_base_url, internal_base_url, last_seen_at, modified_at
|
|
FROM d_players
|
|
ORDER BY modified_at DESC, identifier ASC`
|
|
);
|
|
|
|
return rows;
|
|
}
|
|
|
|
function getConfiguredPlayerIdentifier() {
|
|
return normalizeDeviceId(process.env.PLAYER_IDENTIFIER || process.env.PLAYER_DEVICE_ID || '');
|
|
}
|
|
|
|
async function resolvePlayerRegistration(pool, identifier) {
|
|
const normalizedIdentifier = normalizeDeviceId(identifier);
|
|
if (!pool || !normalizedIdentifier) {
|
|
return null;
|
|
}
|
|
|
|
const hasIdentifierColumn = await columnExists(pool, 'd_players', 'identifier');
|
|
const hasDeviceIdColumn = await columnExists(pool, 'd_players', 'device_id');
|
|
const identifierColumn = hasIdentifierColumn ? 'identifier' : (hasDeviceIdColumn ? 'device_id' : '');
|
|
if (!identifierColumn) {
|
|
return null;
|
|
}
|
|
|
|
const selectIdExpression = hasIdentifierColumn ? 'id' : 'NULL AS id';
|
|
const selectIdentifierExpression = hasIdentifierColumn ? 'identifier' : 'device_id AS identifier';
|
|
const orderByExpression = hasIdentifierColumn ? 'modified_at DESC, id DESC' : 'modified_at DESC';
|
|
|
|
const [rows] = await pool.query(
|
|
`SELECT ${selectIdExpression}, ${selectIdentifierExpression}, public_base_url, internal_base_url, last_seen_at
|
|
FROM d_players
|
|
WHERE ${identifierColumn} = ?
|
|
LIMIT 1`,
|
|
[normalizedIdentifier]
|
|
);
|
|
|
|
if (rows[0]) {
|
|
return rows[0];
|
|
}
|
|
|
|
const [fallbackRows] = await pool.query(
|
|
`SELECT ${selectIdExpression}, ${selectIdentifierExpression}, public_base_url, internal_base_url, last_seen_at
|
|
FROM d_players
|
|
ORDER BY ${orderByExpression}
|
|
LIMIT 1`
|
|
);
|
|
|
|
return fallbackRows[0] || null;
|
|
}
|
|
|
|
async function upsertPlayerRegistration(pool, options) {
|
|
const identifier = normalizeDeviceId(options && (options.identifier || options.deviceId));
|
|
const publicBaseUrl = normalizeBaseUrl(options && options.publicBaseUrl);
|
|
const internalBaseUrl = normalizeBaseUrl(options && options.internalBaseUrl);
|
|
|
|
if (!pool || !identifier) {
|
|
return null;
|
|
}
|
|
|
|
await pool.query(
|
|
`INSERT INTO d_players (identifier, public_base_url, internal_base_url, last_seen_at)
|
|
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
|
ON DUPLICATE KEY UPDATE
|
|
public_base_url = VALUES(public_base_url),
|
|
internal_base_url = VALUES(internal_base_url),
|
|
last_seen_at = CURRENT_TIMESTAMP,
|
|
modified_at = CURRENT_TIMESTAMP`,
|
|
[identifier, publicBaseUrl || null, internalBaseUrl || null]
|
|
);
|
|
|
|
return resolvePlayerRegistration(pool, identifier);
|
|
}
|
|
|
|
async function recordPlayerHeartbeat(pool, options) {
|
|
const identifier = normalizeDeviceId(options && (options.identifier || options.deviceId));
|
|
const publicBaseUrl = normalizeBaseUrl(options && options.publicBaseUrl);
|
|
const internalBaseUrl = normalizeBaseUrl(options && options.internalBaseUrl);
|
|
|
|
if (!pool || !identifier) {
|
|
return null;
|
|
}
|
|
|
|
await pool.query(
|
|
`INSERT INTO d_players (identifier, public_base_url, internal_base_url, last_seen_at)
|
|
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
|
ON DUPLICATE KEY UPDATE
|
|
public_base_url = COALESCE(VALUES(public_base_url), public_base_url),
|
|
internal_base_url = COALESCE(VALUES(internal_base_url), internal_base_url),
|
|
last_seen_at = CURRENT_TIMESTAMP,
|
|
modified_at = CURRENT_TIMESTAMP`,
|
|
[identifier, publicBaseUrl || null, internalBaseUrl || null]
|
|
);
|
|
|
|
return resolvePlayerRegistration(pool, identifier);
|
|
}
|
|
|
|
module.exports = {
|
|
normalizeDeviceId: normalizeDeviceId,
|
|
normalizeIdentifier: normalizeIdentifier,
|
|
getConfiguredPlayerIdentifier: getConfiguredPlayerIdentifier,
|
|
fetchPlayerRegistrations: fetchPlayerRegistrations,
|
|
resolvePlayerRegistration: resolvePlayerRegistration,
|
|
upsertPlayerRegistration: upsertPlayerRegistration,
|
|
recordPlayerHeartbeat: recordPlayerHeartbeat
|
|
}; |