Files
recflare/apps/datacollection/src/datacollection.app.ts
T
2026-10-13 21:47:34 -04:00

61 lines
2.2 KiB
TypeScript

import { Hono } from 'hono'
import { useWorkersLogger } from 'workers-tagged-logger'
import { withNotFound, withOnError } from '@repo/hono-helpers'
import type { App } from './context'
/**
* DataCollection Worker. The client's telemetry sink: it batches gameplay/analytics events
* and posts them here, and asks on startup how heavily to sample them. Nothing here stores
* or forwards anything — there is no analytics backend behind this server — so both routes
* are acknowledgements.
*
* Neither is auth-gated. Telemetry is fire-and-forget from the client's side and it posts
* before a session is fully established, so a 401 buys nothing and costs a retry loop.
*/
const app = new Hono<App>()
.use(
'*',
// middleware
(c, next) =>
useWorkersLogger(c.env.NAME, {
environment: c.env.ENVIRONMENT,
release: c.env.SENTRY_RELEASE,
})(c, next)
)
.onError(withOnError())
.notFound(withNotFound())
.get('/', async (c) => {
return c.text('hello, world!')
})
// Event drop-off, singular. The client posts one event here and only checks that the
// call succeeded — the response body is never read — so an empty object is a complete
// answer. Events are discarded; nothing collects them.
.post('/data/event', async (c) => {
return c.json({})
})
// The same drop-off for a BATCH of events. Separate route rather than an alias: the
// client sends an array here and the answer is an array, mirroring the request one
// per-event result at a time. Empty says "nothing to report back about any of them".
// Don't collapse the two — a `{}` on this path is not the shape the client's decoder
// expects for a batch.
.post('/data/events', async (c) => {
return c.json([])
})
// Sampling configuration, asked for once per session (`?sessionId=<guid>`). A BARE ARRAY
// of `{ EventType, ShouldBeSampled }` entries (the client decodes a `List<EventDTO>`),
// e.g. `[{ "EventType": "player_join", "ShouldBeSampled": true }]` — not an object: the
// `{}` this once served made the client's decoder throw. Empty says "sample nothing",
// which is the right answer since the events are discarded anyway.
.get('/sampling', async (c) => {
return c.json([])
})
export default app