Files
EscapistsReintroduced/services/telemetry-worker/src/dashboard-api.js
T
2026-11-01 19:29:54 +01:00

45 lines
3.1 KiB
JavaScript

// Receipt times come from the receiver, never the game server's clock. A late
// queued report is fresh receipt evidence, not proof that the server is online.
export async function dashboardOverview(db, serverId) {
const where = serverId ? ' WHERE server_id = ?' : '';
const values = serverId ? [serverId] : [];
const results = await db.batch([
db.prepare(`SELECT COUNT(*) AS missions, COALESCE(SUM(outcome = 'victory'), 0) AS wins,
COALESCE(SUM(outcome = 'defeat'), 0) AS losses, COALESCE(SUM(terminal = 0), 0) AS unfinished,
COALESCE(SUM(partial), 0) AS partialMissions FROM missions${where}`).bind(...values),
db.prepare(`SELECT COUNT(*) AS servers FROM servers${where}`).bind(...values),
db.prepare(`SELECT COUNT(*) AS events, MAX(received_at) AS lastReceivedAt FROM events${where}`).bind(...values),
db.prepare(`SELECT CASE WHEN terminal = 1 THEN outcome ELSE 'unfinished' END AS outcome,
COUNT(*) AS missions FROM missions${where} GROUP BY terminal, outcome`).bind(...values),
db.prepare(`SELECT lower(json_extract(payload, '$.terrain')) AS terrain, COUNT(*) AS missions,
SUM(outcome = 'victory') AS wins, SUM(outcome = 'defeat') AS losses,
SUM(terminal = 0) AS unfinished FROM missions${where}
GROUP BY lower(json_extract(payload, '$.terrain')) ORDER BY missions DESC, terrain LIMIT 101`).bind(...values),
]);
const totals = {...results[0].results[0], ...results[1].results[0], ...results[2].results[0]};
const completed = totals.wins + totals.losses;
totals.winRate = completed ? Math.round(1000 * totals.wins / completed) / 10 : null;
return {serverId, generatedAt: Math.floor(Date.now() / 1000), totals,
outcomes: results[3].results, maps: results[4].results.slice(0, 100), mapsTruncated: results[4].results.length > 100};
}
export async function dashboardServers(db, {limit, before}, query) {
const result = await db.prepare(`WITH page AS (
SELECT rowid AS cursor, * FROM servers WHERE rowid < ?
AND (? = '' OR instr(lower(name), lower(?)) > 0 OR instr(server_id, lower(?)) > 0)
ORDER BY rowid DESC LIMIT ?
) SELECT s.cursor, s.server_id AS serverId, s.name, s.active, s.registration, s.created_at AS createdAt,
e.received_at AS lastReceivedAt, json_extract(e.payload, '$.occurredAt') AS lastReportedAt,
json_extract(e.payload, '$.type') AS lastEventType, e.mission_id AS lastMissionId,
json_extract(e.payload, '$.terrain') AS terrain,
json_extract(e.payload, '$.connectedPlayers') AS lastReportedPlayers,
COUNT(m.id) AS missions, COALESCE(SUM(m.outcome = 'victory'), 0) AS wins,
COALESCE(SUM(m.outcome = 'defeat'), 0) AS losses,
COALESCE(SUM(m.terminal = 0), 0) AS unfinished, COALESCE(SUM(m.partial), 0) AS partialMissions
FROM page s LEFT JOIN events e ON e.id = (
SELECT id FROM events WHERE server_id = s.server_id ORDER BY received_at DESC, id DESC LIMIT 1)
LEFT JOIN missions m ON m.server_id = s.server_id
GROUP BY s.cursor ORDER BY s.cursor DESC`).bind(before, query, query, query, limit).all();
return {servers: result.results, nextBefore: result.results.length === limit ? result.results.at(-1).cursor : null};
}