From bbb1639184d39b2804324919aebab7d7829715ce Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 17 Sep 2026 15:09:27 -0400 Subject: [PATCH] debug 2 --- src/routes/api/telemetry/+server.ts | 63 ++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/src/routes/api/telemetry/+server.ts b/src/routes/api/telemetry/+server.ts index 1223c7a..d102991 100644 --- a/src/routes/api/telemetry/+server.ts +++ b/src/routes/api/telemetry/+server.ts @@ -1,27 +1,60 @@ import { json } from '@sveltejs/kit'; -import { getServer } from '$lib/server/fleet/servers.js'; +import { getServer, listServers } from '$lib/server/fleet/servers.js'; import { saveTelemetry } from '$lib/server/fleet/telemetry.js'; import { emit } from '$lib/server/pipeline/bus.js'; export async function POST({ request }: { request: Request }) { console.log('[telemetry] received POST /api/telemetry'); - if (request.headers.get('content-type')?.split(';')[0].trim().toLowerCase() !== 'application/json') { - console.log('[telemetry] rejected 415: Content-Type must be application/json'); - return json({ ok: false, error: 'Content-Type must be application/json' }, { status: 415 }); + // Encode payloads as JSON so script-supplied newlines cannot forge log lines. + // Bound the preview to keep malformed requests from flooding container logs. + const raw = await request.text(); + const context = { + contentType: request.headers.get('content-type'), + payloadPreview: raw.slice(0, 4096), + payloadTruncated: raw.length > 4096 + }; + function reject(status: number, error: string, details: Record = {}) { + console.log(`[telemetry] rejected ${status}: ${JSON.stringify({ error, ...context, ...details })}`); + return json({ ok: false, error, details }, { status }); } - const body = await request.json().catch(() => null); - if (!body || typeof body !== 'object' || Array.isArray(body) - || body.event !== 'round_change' - || typeof body.server !== 'string' || !body.server.length || body.server.length > 255 - || !Number.isSafeInteger(body.round) || body.round < 0 - || !Number.isSafeInteger(body.players) || body.players < 0) { - console.log('[telemetry] rejected 400: invalid round_change payload or malformed JSON'); - return json({ ok: false, error: 'Expected round_change, a server name, and nonnegative integer round and players' }, { status: 400 }); + if (request.headers.get('content-type')?.split(';')[0].trim().toLowerCase() !== 'application/json') { + return reject(415, 'Content-Type must be application/json', { receivedContentType: context.contentType }); + } + let body; + try { + body = JSON.parse(raw); + } catch (error) { + return reject(400, 'Malformed JSON', { parseError: (error as Error).message }); + } + if (!body || typeof body !== 'object' || Array.isArray(body)) { + return reject(400, 'Payload must be a JSON object'); + } + const issues: string[] = []; + if (body.event !== 'round_change') issues.push('event must equal "round_change"'); + if (typeof body.server !== 'string' || !body.server.length || body.server.length > 255) { + issues.push('server must be a registered server name (1–255 characters)'); + } + for (const field of ['round', 'players']) { + if (!Number.isSafeInteger(body[field]) || body[field] < 0) { + issues.push(`${field} must be a nonnegative safe integer (JSON number, not a string)`); + } + } + if (issues.length) { + return reject(400, 'Invalid telemetry payload', { issues }); } const server = getServer(body.server); - if (!server || server.destroyed_at) { - console.log(`[telemetry] rejected 404: unknown or destroyed server ${JSON.stringify(body.server)}`); - return json({ ok: false, error: 'Unknown or destroyed server' }, { status: 404 }); + if (!server) { + const registeredServers = listServers().filter((entry) => !entry.destroyed_at).map((entry) => entry.name); + return reject(404, 'Server is not registered', { + receivedServer: body.server, + hint: 'Send the exact Electric Cherry server name in the server field; matching is case-sensitive.', + registeredServers + }); + } + if (server.destroyed_at) { + return reject(404, 'Server has been destroyed', { + receivedServer: body.server, destroyedAt: server.destroyed_at + }); } const telemetry = { server: body.server as string, round: body.round as number,