forked from syntaxbullet/aurorabot
refactor: rename web/ to api/ to better reflect its purpose
The web/ folder contains the REST API, WebSocket server, and OAuth routes — not a web frontend. Renaming to api/ clarifies this distinction since the actual web frontend lives in panel/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
85
api/src/routes/stats.routes.ts
Normal file
85
api/src/routes/stats.routes.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @fileoverview Statistics endpoints for Aurora API.
|
||||
* Provides dashboard statistics and activity aggregation data.
|
||||
*/
|
||||
|
||||
import type { RouteContext, RouteModule } from "./types";
|
||||
import { jsonResponse, errorResponse, withErrorHandling } from "./utils";
|
||||
|
||||
// Cache for activity stats (heavy aggregation)
|
||||
let activityPromise: Promise<import("@shared/modules/dashboard/dashboard.types").ActivityData[]> | null = null;
|
||||
let lastActivityFetch: number = 0;
|
||||
const ACTIVITY_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
/**
|
||||
* Stats routes handler.
|
||||
*
|
||||
* Endpoints:
|
||||
* - GET /api/stats - Full dashboard statistics
|
||||
* - GET /api/stats/activity - Activity aggregation with caching
|
||||
*/
|
||||
async function handler(ctx: RouteContext): Promise<Response | null> {
|
||||
const { pathname, method } = ctx;
|
||||
|
||||
/**
|
||||
* @route GET /api/stats
|
||||
* @description Returns comprehensive dashboard statistics including
|
||||
* bot info, user counts, economy data, and leaderboards.
|
||||
* @response 200 - Full dashboard stats object
|
||||
* @response 500 - Error fetching statistics
|
||||
*/
|
||||
if (pathname === "/api/stats" && method === "GET") {
|
||||
return withErrorHandling(async () => {
|
||||
// Import the stats function from wherever it's defined
|
||||
// This will be passed in during initialization
|
||||
const { getFullDashboardStats } = await import("./stats.helper.ts");
|
||||
const stats = await getFullDashboardStats();
|
||||
return jsonResponse(stats);
|
||||
}, "fetch dashboard stats");
|
||||
}
|
||||
|
||||
/**
|
||||
* @route GET /api/stats/activity
|
||||
* @description Returns activity aggregation data with 5-minute caching.
|
||||
* Heavy query, results are cached to reduce database load.
|
||||
* @response 200 - Array of activity data points
|
||||
* @response 500 - Error fetching activity statistics
|
||||
*
|
||||
* @example
|
||||
* // Response
|
||||
* [
|
||||
* { "date": "2024-02-08", "commands": 150, "users": 25 },
|
||||
* { "date": "2024-02-07", "commands": 200, "users": 30 }
|
||||
* ]
|
||||
*/
|
||||
if (pathname === "/api/stats/activity" && method === "GET") {
|
||||
return withErrorHandling(async () => {
|
||||
const now = Date.now();
|
||||
|
||||
// If we have a valid cache, return it
|
||||
if (activityPromise && (now - lastActivityFetch < ACTIVITY_CACHE_TTL)) {
|
||||
const data = await activityPromise;
|
||||
return jsonResponse(data);
|
||||
}
|
||||
|
||||
// Otherwise, trigger a new fetch (deduplicated by the promise)
|
||||
if (!activityPromise || (now - lastActivityFetch >= ACTIVITY_CACHE_TTL)) {
|
||||
activityPromise = (async () => {
|
||||
const { dashboardService } = await import("@shared/modules/dashboard/dashboard.service");
|
||||
return await dashboardService.getActivityAggregation();
|
||||
})();
|
||||
lastActivityFetch = now;
|
||||
}
|
||||
|
||||
const activity = await activityPromise;
|
||||
return jsonResponse(activity);
|
||||
}, "fetch activity stats");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export const statsRoutes: RouteModule = {
|
||||
name: "stats",
|
||||
handler
|
||||
};
|
||||
Reference in New Issue
Block a user