| 46 | } |
| 47 | |
| 48 | export async function POST(request: NextRequest) { |
| 49 | try { |
| 50 | const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "unknown" |
| 51 | |
| 52 | const { success } = await ratelimit.limit(ip) |
| 53 | if (!success) { |
| 54 | return new Response(null, { status: 429 }) |
| 55 | } |
| 56 | |
| 57 | const payload: TelemetryPayload = await request.json() |
| 58 | |
| 59 | if (!payload.events || !Array.isArray(payload.events)) { |
| 60 | return new Response(null, { status: 400 }) |
| 61 | } |
| 62 | |
| 63 | const date = payload.context?.date || new Date().toISOString().split("T")[0] |
| 64 | const deviceId = payload.context?.deviceId |
| 65 | const os = VALID_OS.includes(payload.context?.os) ? payload.context.os : "unknown" |
| 66 | const arch = VALID_ARCH.includes(payload.context?.arch) ? payload.context.arch : "unknown" |
| 67 | const version = sanitize(payload.context?.version) || "unknown" |
| 68 | const channel = sanitize(payload.context?.channel) || "unknown" |
| 69 | |
| 70 | const pipeline = redis.pipeline() |
| 71 | |
| 72 | if (deviceId) { |
| 73 | pipeline.sadd(`dau:${date}`, deviceId) |
| 74 | } |
| 75 | |
| 76 | pipeline.hincrby(`os:${date}`, os, 1) |
| 77 | pipeline.hincrby(`arch:${date}`, arch, 1) |
| 78 | pipeline.hincrby(`versions:${date}`, version, 1) |
| 79 | pipeline.hincrby(`channels:${date}`, channel, 1) |
| 80 | |
| 81 | for (const event of payload.events) { |
| 82 | if (!VALID_EVENTS.includes(event.event)) continue |
| 83 | |
| 84 | switch (event.event) { |
| 85 | case "app.started": { |
| 86 | const command = sanitize(event.properties?.command as string) || "unknown" |
| 87 | pipeline.hincrby(`app:${date}`, command, 1) |
| 88 | break |
| 89 | } |
| 90 | case "session.started": { |
| 91 | pipeline.incr(`sessions:${date}`) |
| 92 | break |
| 93 | } |
| 94 | case "session.ended": { |
| 95 | const provider = sanitize(event.properties?.provider as string) |
| 96 | const model = sanitize(event.properties?.model as string) |
| 97 | const duration = event.properties?.duration as number |
| 98 | const messages = event.properties?.messages as number |
| 99 | const tools = event.properties?.tools as number |
| 100 | if (provider) pipeline.hincrby(`session_providers:${date}`, provider, 1) |
| 101 | if (model) pipeline.hincrby(`session_models:${date}`, model, 1) |
| 102 | if (duration) pipeline.incrby(`session_duration:${date}`, Math.round(duration)) |
| 103 | if (messages) pipeline.incrby(`session_messages:${date}`, messages) |
| 104 | if (tools) pipeline.incrby(`session_tools:${date}`, tools) |
| 105 | break |