init
This commit is contained in:
76
app/api/v1/stats/route.ts
Normal file
76
app/api/v1/stats/route.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { gte, sql } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import { lobsters, tasks } from "@/lib/db/schema";
|
||||
import {
|
||||
redis,
|
||||
getGlobalStats,
|
||||
getRegionStats,
|
||||
getHourlyActivity,
|
||||
} from "@/lib/redis";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const globalStats = await getGlobalStats();
|
||||
const regionStats = await getRegionStats();
|
||||
const hourlyRaw = await getHourlyActivity();
|
||||
|
||||
// Convert hourly data to array format
|
||||
const hourlyActivity = Object.entries(hourlyRaw).map(([hour, count]) => ({
|
||||
hour: hour.split("T")[1] + ":00",
|
||||
count,
|
||||
}));
|
||||
|
||||
const now = Date.now();
|
||||
const fiveMinutesAgo = now - 300_000;
|
||||
const activeLobsters = await redis.zcount(
|
||||
"active:lobsters",
|
||||
fiveMinutesAgo,
|
||||
"+inf"
|
||||
);
|
||||
|
||||
const totalLobstersResult = await db
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(lobsters);
|
||||
const totalLobsters = totalLobstersResult[0]?.count ?? 0;
|
||||
|
||||
const todayStart = new Date();
|
||||
todayStart.setHours(0, 0, 0, 0);
|
||||
|
||||
const tasksTodayResult = await db
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(tasks)
|
||||
.where(gte(tasks.timestamp, todayStart));
|
||||
const tasksToday = tasksTodayResult[0]?.count ?? 0;
|
||||
|
||||
const avgDurationResult = await db
|
||||
.select({ avg: sql<number>`AVG(${tasks.durationMs})` })
|
||||
.from(tasks)
|
||||
.where(gte(tasks.timestamp, todayStart));
|
||||
const avgTaskDuration = avgDurationResult[0]?.avg
|
||||
? Math.round(avgDurationResult[0].avg)
|
||||
: 0;
|
||||
|
||||
// Convert region stats from string values to numbers
|
||||
const regionBreakdown: Record<string, number> = {};
|
||||
for (const [key, val] of Object.entries(regionStats)) {
|
||||
regionBreakdown[key] = parseInt(val, 10) || 0;
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
totalLobsters,
|
||||
activeLobsters,
|
||||
tasksToday,
|
||||
tasksTotal: parseInt(globalStats.total_tasks ?? "0", 10),
|
||||
avgTaskDuration,
|
||||
regionBreakdown,
|
||||
hourlyActivity,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Stats error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user