feat: add conversion-oriented traffic dashboard
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 8s

This commit is contained in:
OG T
2026-06-07 16:27:35 +08:00
parent 54608a3376
commit 5e3b8582eb
2 changed files with 362 additions and 11 deletions

View File

@@ -24,7 +24,16 @@ export async function GET(request: NextRequest) {
const minutes = Math.max(parseInt(query.get("minutes") || "1440", 10), 5);
const since = new Date(Date.now() - minutes * 60 * 1000);
const [summaryRows, actorSummaryRows, externalActorRows, totalRows, latestEvents] = await Promise.all([
const [
summaryRows,
actorSummaryRows,
externalActorRows,
totalRows,
latestEvents,
judgeCompleteRows,
capturedPayoutCount,
releasedPayoutCount,
] = await Promise.all([
prisma.auditEvent.groupBy({
by: ["action"],
where: {
@@ -72,6 +81,29 @@ export async function GET(request: NextRequest) {
createdAt: true,
},
}),
prisma.auditEvent.findMany({
where: {
createdAt: { gte: since },
action: "JUDGE_COMPLETE",
},
select: {
metadata: true,
},
}),
prisma.ledgerEntry.count({
where: {
createdAt: { gte: since },
phase: "CAPTURE",
response_status: "SUCCESS",
},
}),
prisma.ledgerEntry.count({
where: {
createdAt: { gte: since },
phase: "RELEASE",
response_status: "SUCCESS",
},
}),
]);
const actionSummary = Object.fromEntries(
@@ -133,6 +165,44 @@ export async function GET(request: NextRequest) {
{ external: 0, internal: 0 } as Record<string, number>
);
const discoveryEvents =
(actionSummary["EXTERNAL_LIST_OPEN_TASKS"] || 0) +
(actionSummary["EXTERNAL_LIST_OPEN_TASKS_MCP"] || 0);
const claimEvents = actionSummary["EXTERNAL_CLAIM_TASK_SUCCESS"] || 0;
const submitEvents = actionSummary["EXTERNAL_SUBMIT_SOLUTION_SUCCESS"] || 0;
const judgePassEvents = judgeCompleteRows.filter((row) => {
const metadata = asRecordJson(row.metadata);
return metadata?.overall_result === "PASS";
}).length;
const judgeFailEvents = judgeCompleteRows.filter((row) => {
const metadata = asRecordJson(row.metadata);
return metadata?.overall_result === "FAIL";
}).length;
const conversionRate = (numerator: number, denominator: number) => {
if (!denominator) return 0;
return Math.round((numerator / denominator) * 1000) / 10;
};
const externalFunnel = {
discovery_events: discoveryEvents,
claim_events: claimEvents,
submit_events: submitEvents,
judge_pass_events: judgePassEvents,
judge_fail_events: judgeFailEvents,
judge_total_events: judgePassEvents + judgeFailEvents,
payout_captured: capturedPayoutCount,
payout_released: releasedPayoutCount,
};
const conversionRates = {
claim_rate: conversionRate(claimEvents, discoveryEvents),
submit_rate: conversionRate(submitEvents, claimEvents),
pass_rate: conversionRate(judgePassEvents, submitEvents),
payout_rate: conversionRate(capturedPayoutCount, judgePassEvents),
};
const externalEventTypes = Object.entries(actionSummary)
.filter(([action]) => action.startsWith("EXTERNAL_"))
.map(([action, count]) => ({ action, count }));
@@ -172,6 +242,8 @@ export async function GET(request: NextRequest) {
channel_summary: channelSummary,
actor_summary: actorSummary,
external_actor_summary: externalActorSummary,
external_funnel: externalFunnel,
conversion_rates: conversionRates,
external_event_types: externalEventTypes,
internal_event_types: internalEventTypes,
recent_external_events: recentExternalEvents,