Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 13x 13x 13x 13x 13x 13x 13x 13x 10x 10x 13x 13x 13x 13x 13x 13x 13x 13x 13x 1x 1x 13x 13x 13x 13x 13x 13x 13x 13x 13x 1x 1x 1x 1x 1x 1x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 10x 12x 1x 1x 1x 1x 12x 12x 12x 10x 12x 1x 1x 1x 1x 12x 12x 12x 11x 12x 1x 1x 1x 12x 12x 12x 12x 12x 12x 12x 12x 12x 1x 1x 12x 1x 1x 11x 10x 10x 10x 12x 12x 12x | export const dynamic = "force-dynamic";
import { NextResponse } from "next/server";
import { } from "next-auth";
import { getQueryMetrics, getMetricsSummary } from "@/lib/database/metrics";
import { prisma } from "@/lib/prisma";
import { addSecurityHeaders } from "@/lib/security";
import {
withAdmin,
withErrorHandling,
ApiSuccessResponse,
ApiErrorResponse } from "@/lib/api";
import { } from "@/lib/api/middleware";
/**
* GET /api/admin/metrics/database
*
* Returns database query metrics and statistics for admin monitoring.
* Requires admin authentication.
*/
async function handleGet(): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
// Get query metrics from in-memory store
const queryMetrics = getQueryMetrics();
const summary = getMetricsSummary();
// Get additional database statistics
const [
totalProducts,
totalOrders,
totalUsers,
totalReviews,
recentErrors,
recentPerformanceMetrics,
] = await Promise.all([
prisma.product.count(),
prisma.order.count(),
prisma.user.count(),
prisma.review.count(),
prisma.errorLog.findMany({
where: { category: "DATABASE" },
orderBy: { createdAt: "desc" },
take: 10,
select: {
id: true,
message: true,
createdAt: true,
statusCode: true } }),
prisma.performanceMetric.findMany({
where: { type: "database" },
orderBy: { createdAt: "desc" },
take: 20,
select: {
id: true,
name: true,
duration: true,
createdAt: true } }),
]);
// Calculate database health status
const healthStatus = getHealthStatus(summary, queryMetrics);
const responseData = {
health: healthStatus,
summary: {
...summary,
totalQueries: queryMetrics.total,
slowQueryPercentage: queryMetrics.total > 0
? ((queryMetrics.slowQueries / queryMetrics.total) * 100).toFixed(2)
: "0" },
queryMetrics: {
total: queryMetrics.total,
slow: queryMetrics.slowQueries,
warning: queryMetrics.warningQueries,
critical: queryMetrics.criticalQueries,
averageDuration: queryMetrics.averageDuration },
topSlowestQueries: queryMetrics.topSlowest.map(q => ({
duration: q.duration,
timestamp: q.timestamp,
query: q.query.substring(0, 200) + (q.query.length > 200 ? "..." : "") })),
queryPatterns: queryMetrics.queryPatterns,
tableStats: {
products: totalProducts,
orders: totalOrders,
users: totalUsers,
reviews: totalReviews },
recentDatabaseErrors: recentErrors,
recentPerformanceMetrics: recentPerformanceMetrics.map(m => ({
name: m.name,
duration: m.duration,
timestamp: m.createdAt })),
timestamp: new Date().toISOString() };
return addSecurityHeaders(
NextResponse.json({
success: true,
data: responseData })
) as NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>;
}
export const GET = withErrorHandling(withAdmin(handleGet));
/**
* Determine health status based on metrics
*/
function getHealthStatus(
summary: { healthy: boolean; avgQueryTime: number; slowQueryPercentage: number },
metrics: { criticalQueries: number; warningQueries: number }
): {
status: 'healthy' | 'degraded' | 'unhealthy';
message: string;
checks: { name: string; status: 'pass' | 'warn' | 'fail'; value: string }[];
} {
const checks: { name: string; status: 'pass' | 'warn' | 'fail'; value: string }[] = [];
// Check average query time
if (summary.avgQueryTime < 50) {
checks.push({ name: 'Average Query Time', status: 'pass', value: `${summary.avgQueryTime}ms` });
} else if (summary.avgQueryTime < 100) {
checks.push({ name: 'Average Query Time', status: 'warn', value: `${summary.avgQueryTime}ms` });
} else {
checks.push({ name: 'Average Query Time', status: 'fail', value: `${summary.avgQueryTime}ms` });
}
// Check slow query percentage
if (summary.slowQueryPercentage < 5) {
checks.push({ name: 'Slow Query Rate', status: 'pass', value: `${summary.slowQueryPercentage}%` });
} else if (summary.slowQueryPercentage < 10) {
checks.push({ name: 'Slow Query Rate', status: 'warn', value: `${summary.slowQueryPercentage}%` });
} else {
checks.push({ name: 'Slow Query Rate', status: 'fail', value: `${summary.slowQueryPercentage}%` });
}
// Check critical queries
if (metrics.criticalQueries === 0) {
checks.push({ name: 'Critical Queries', status: 'pass', value: `${metrics.criticalQueries}` });
} else if (metrics.criticalQueries < 5) {
checks.push({ name: 'Critical Queries', status: 'warn', value: `${metrics.criticalQueries}` });
} else {
checks.push({ name: 'Critical Queries', status: 'fail', value: `${metrics.criticalQueries}` });
}
// Determine overall status
const hasFailure = checks.some(c => c.status === 'fail');
const hasWarning = checks.some(c => c.status === 'warn');
let status: 'healthy' | 'degraded' | 'unhealthy';
let message: string;
if (hasFailure) {
status = 'unhealthy';
message = 'Database performance issues detected. Review slow queries.';
} else if (hasWarning) {
status = 'degraded';
message = 'Some performance warnings. Monitor closely.';
} else {
status = 'healthy';
message = 'Database performance is optimal.';
}
return { status, message, checks };
}
|