import { NextResponse } from "next/server"; import { and, isNotNull, sql } from "drizzle-orm"; import { db } from "@/lib/db"; import { claws } from "@/lib/db/schema"; import { getCacheHeatmap, setCacheHeatmap } from "@/lib/redis"; export async function GET() { try { const cached = await getCacheHeatmap(); if (cached) { return NextResponse.json(JSON.parse(cached)); } const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000); // Query all claws with coordinates, grouped by location const locationRows = await db .select({ city: claws.city, country: claws.country, latitude: claws.latitude, longitude: claws.longitude, count: sql`count(*)`, onlineCount: sql`sum(case when ${claws.lastHeartbeat} >= ${fiveMinutesAgo} then 1 else 0 end)`, }) .from(claws) .where( and( isNotNull(claws.latitude), isNotNull(claws.longitude) ) ) .groupBy( claws.city, claws.country, claws.latitude, claws.longitude ); // Fetch individual claw details for all claws with coordinates const clawDetails = await db .select({ id: claws.id, name: claws.name, city: claws.city, country: claws.country, latitude: claws.latitude, longitude: claws.longitude, lastHeartbeat: claws.lastHeartbeat, }) .from(claws) .where( and( isNotNull(claws.latitude), isNotNull(claws.longitude) ) ); // Group claw details by location key const clawsByLocation = new Map>(); for (const claw of clawDetails) { const key = `${claw.latitude}:${claw.longitude}`; const isOnline = claw.lastHeartbeat ? claw.lastHeartbeat >= fiveMinutesAgo : false; const list = clawsByLocation.get(key) ?? []; list.push({ id: claw.id, name: claw.name, isOnline }); clawsByLocation.set(key, list); } const points = locationRows.map((row) => { const key = `${row.latitude}:${row.longitude}`; const clawList = (clawsByLocation.get(key) ?? []).slice(0, 20); return { lat: Number(row.latitude), lng: Number(row.longitude), weight: row.count, clawCount: row.count, onlineCount: Number(row.onlineCount ?? 0), city: row.city, country: row.country, claws: clawList, }; }); const lastUpdated = new Date().toISOString(); const responseData = { points, lastUpdated }; await setCacheHeatmap(JSON.stringify(responseData)); return NextResponse.json(responseData); } catch (error) { console.error("Heatmap error:", error); return NextResponse.json( { error: "Internal server error" }, { status: 500 } ); } }