6bd751957c
CI Pipeline / test (20) (pull_request) Successful in 3m0s
CI Pipeline / test (18) (pull_request) Successful in 3m18s
CI Pipeline / e2e (firefox) (pull_request) Successful in 3m20s
CI Pipeline / e2e (chromium) (pull_request) Successful in 3m54s
CI Pipeline / e2e (webkit) (pull_request) Successful in 3m41s
CI Pipeline / performance (pull_request) Successful in 3m3s
CI Pipeline / visual-regression (pull_request) Successful in 7m12s
CI Pipeline / storybook (pull_request) Successful in 1m29s
CI Pipeline / lint (pull_request) Failing after 1m7s
CI Pipeline / build (pull_request) Successful in 1m20s
115 lines
3.2 KiB
JavaScript
115 lines
3.2 KiB
JavaScript
import { NextResponse } from "next/server";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
|
|
const WEB_VITALS_DIR = path.join(process.cwd(), ".next", "web-vitals");
|
|
|
|
// Ensure web-vitals directory exists
|
|
if (!fs.existsSync(WEB_VITALS_DIR)) {
|
|
fs.mkdirSync(WEB_VITALS_DIR, { recursive: true });
|
|
}
|
|
|
|
export async function POST(request) {
|
|
try {
|
|
const { metric, data, url, userAgent, timestamp } = await request.json();
|
|
|
|
// Store the metric data
|
|
const vitalsData = {
|
|
metric,
|
|
data,
|
|
url,
|
|
userAgent,
|
|
timestamp: new Date(timestamp).toISOString(),
|
|
receivedAt: new Date().toISOString(),
|
|
};
|
|
|
|
// Save to file (in production, you would save to a database)
|
|
const filePath = path.join(WEB_VITALS_DIR, `${metric}.json`);
|
|
let existingData = [];
|
|
|
|
if (fs.existsSync(filePath)) {
|
|
try {
|
|
existingData = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
} catch (error) {
|
|
console.warn("Could not parse existing vitals data:", error.message);
|
|
}
|
|
}
|
|
|
|
existingData.push(vitalsData);
|
|
|
|
// Keep only last 100 entries per metric
|
|
if (existingData.length > 100) {
|
|
existingData = existingData.slice(-100);
|
|
}
|
|
|
|
fs.writeFileSync(filePath, JSON.stringify(existingData, null, 2));
|
|
|
|
// Log for monitoring
|
|
console.log(
|
|
`Web Vital received: ${metric} = ${data.value}ms (${data.rating})`,
|
|
);
|
|
|
|
return NextResponse.json({ success: true });
|
|
} catch (error) {
|
|
console.error("Error processing web vital:", error);
|
|
return NextResponse.json(
|
|
{ error: "Internal server error" },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function GET() {
|
|
try {
|
|
const metrics = {};
|
|
|
|
if (fs.existsSync(WEB_VITALS_DIR)) {
|
|
const files = fs.readdirSync(WEB_VITALS_DIR);
|
|
|
|
files.forEach((file) => {
|
|
if (file.endsWith(".json")) {
|
|
const metric = file.replace(".json", "");
|
|
const data = JSON.parse(
|
|
fs.readFileSync(path.join(WEB_VITALS_DIR, file), "utf8"),
|
|
);
|
|
|
|
if (data.length > 0) {
|
|
const values = data
|
|
.map((d) => d.data.value)
|
|
.filter((v) => v !== undefined);
|
|
const ratings = data
|
|
.map((d) => d.data.rating)
|
|
.filter((r) => r !== undefined);
|
|
|
|
metrics[metric] = {
|
|
count: data.length,
|
|
average:
|
|
values.length > 0
|
|
? Math.round(
|
|
values.reduce((a, b) => a + b, 0) / values.length,
|
|
)
|
|
: 0,
|
|
min: values.length > 0 ? Math.min(...values) : 0,
|
|
max: values.length > 0 ? Math.max(...values) : 0,
|
|
goodCount: ratings.filter((r) => r === "good").length,
|
|
needsImprovementCount: ratings.filter(
|
|
(r) => r === "needs-improvement",
|
|
).length,
|
|
poorCount: ratings.filter((r) => r === "poor").length,
|
|
lastUpdated: data[data.length - 1]?.receivedAt,
|
|
};
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
return NextResponse.json({ metrics });
|
|
} catch (error) {
|
|
console.error("Error fetching web vitals:", error);
|
|
return NextResponse.json(
|
|
{ error: "Internal server error" },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|