fix traffic observability, tg alerting robustness and prod compose ports
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 7s

This commit is contained in:
OG T
2026-06-07 20:38:19 +08:00
parent 3b665e4cbe
commit 663ad4d3a2
6 changed files with 436 additions and 34 deletions

View File

@@ -190,6 +190,9 @@ export async function GET(request: NextRequest) {
totalRows,
latestEvents,
judgeCompleteRows,
demandPoolCount,
demandPoolNewRows,
demandPoolScopeStats,
] = await Promise.all([
prisma.auditEvent.groupBy({
by: ["action"],
@@ -247,8 +250,48 @@ export async function GET(request: NextRequest) {
metadata: true,
},
}),
prisma.task.count({
where: {
status: "OPEN",
title: {
not: {
startsWith: "GitHub Issue:",
},
},
},
}),
prisma.task.count({
where: {
status: "OPEN",
title: {
not: {
startsWith: "GitHub Issue:",
},
},
created_at: { gte: since },
},
}),
prisma.task.aggregate({
where: {
status: "OPEN",
title: {
not: {
startsWith: "GitHub Issue:",
},
},
},
_avg: {
scope_clarity_score: true,
},
}),
]);
const demandSupply = {
open_task_count: demandPoolCount,
new_open_tasks_in_window: demandPoolNewRows,
avg_scope_clarity: demandPoolScopeStats._avg.scope_clarity_score,
};
let capturedPayoutCount = 0;
let releasedPayoutCount = 0;
try {
@@ -429,9 +472,12 @@ export async function GET(request: NextRequest) {
latest_surface: string;
latest_source_ip: string;
latest_user_agent: string;
latest_task_id: string;
latest_response_status: number | null;
latest_response_summary: string;
latest_reason: string;
latest_payload_summary: string;
latest_request_id: string;
latest_created_at_ms: number;
}> = new Map();
@@ -471,13 +517,34 @@ export async function GET(request: NextRequest) {
latest_surface: normalizedSurface,
latest_source_ip: normalizedIp,
latest_user_agent: normalizedUa,
latest_task_id: taskId,
latest_response_status: responseStatus,
latest_response_summary: responseSummary,
latest_reason: event.reason || "unknown",
latest_payload_summary:
typeof metadata?.payload_summary === "string" ? metadata.payload_summary : "unknown",
latest_request_id:
typeof metadata?.request_id === "string" ? metadata.request_id : "unknown",
latest_created_at_ms: eventAt,
});
} else {
existingActorActivity.events += 1;
if (eventAt > existingActorActivity.latest_created_at_ms) {
existingActorActivity.latest_action = event.action;
existingActorActivity.latest_surface = normalizedSurface;
existingActorActivity.latest_source_ip = normalizedIp;
existingActorActivity.latest_user_agent = normalizedUa;
existingActorActivity.latest_task_id = taskId;
existingActorActivity.latest_response_status = responseStatus;
existingActorActivity.latest_response_summary = responseSummary;
existingActorActivity.latest_reason = event.reason || "unknown";
existingActorActivity.latest_payload_summary =
typeof metadata?.payload_summary === "string" ? metadata.payload_summary : "unknown";
existingActorActivity.latest_request_id =
typeof metadata?.request_id === "string" ? metadata.request_id : "unknown";
existingActorActivity.latest_created_at_ms = eventAt;
}
}
if (
@@ -566,6 +633,7 @@ export async function GET(request: NextRequest) {
period_minutes: minutes,
total_events: totalRows,
action_summary: actionSummary,
demand_supply: demandSupply,
channel_summary: channelSummary,
actor_summary: actorSummary,
external_actor_summary: externalActorSummary,

View File

@@ -98,9 +98,12 @@ type ExternalActorActivity = {
latestSurface: string;
latestSourceIp: string;
latestUserAgent: string;
latestTaskId: string;
latestResponseStatus: number | null;
latestResponseSummary: string;
latestReason: string;
latestPayloadSummary: string;
latestRequestId: string;
latestCreatedAt: number;
};
@@ -116,6 +119,9 @@ async function getTrafficSummary(minutes: number) {
judgeCompleteRows,
capturedPayoutCount,
releasedPayoutCount,
demandPoolCount,
demandPoolNewRows,
demandPoolScopeStats,
] = await Promise.all([
prisma.auditEvent.groupBy({
by: ["action"],
@@ -187,8 +193,48 @@ async function getTrafficSummary(minutes: number) {
response_status: "SUCCESS",
},
}),
prisma.task.count({
where: {
status: "OPEN",
title: {
not: {
startsWith: "GitHub Issue:",
},
},
},
}),
prisma.task.count({
where: {
status: "OPEN",
title: {
not: {
startsWith: "GitHub Issue:",
},
},
created_at: { gte: since },
},
}),
prisma.task.aggregate({
where: {
status: "OPEN",
title: {
not: {
startsWith: "GitHub Issue:",
},
},
},
_avg: {
scope_clarity_score: true,
},
}),
]);
const demandSupply = {
openTaskCount: demandPoolCount,
newOpenTasksInWindow: demandPoolNewRows,
avgScopeClarity: demandPoolScopeStats._avg.scope_clarity_score,
};
const actionSummary = Object.fromEntries(summaryRows.map((row) => [row.action, row._count._all]));
const actorSummary = Object.fromEntries(actorSummaryRows.map((row) => [row.actorType, row._count._all]));
const externalActorSummary = externalActorRows
@@ -228,6 +274,8 @@ async function getTrafficSummary(minutes: number) {
level: metadata?.level,
response_status: typeof metadata?.response_status === "number" ? metadata.response_status : null,
response_summary: typeof metadata?.response_summary === "string" ? metadata.response_summary : "unknown",
payload_summary: typeof metadata?.payload_summary === "string" ? metadata.payload_summary : "unknown",
request_id: typeof metadata?.request_id === "string" ? metadata.request_id : "n/a",
metadata,
};
});
@@ -267,6 +315,9 @@ async function getTrafficSummary(minutes: number) {
.filter((event) => event.action.includes("ERROR"))
.map((event) => event.action);
const demandHealthLabel = demandSupply.openTaskCount > 0 ? "有可接需求" : "無可接需求";
const demandHealthTone = demandSupply.openTaskCount > 0 ? "text-emerald-300" : "text-amber-300";
const externalActorActivityMap = new Map<string, ExternalActorActivity>();
for (const event of recentEvents) {
if (!event.action.startsWith("EXTERNAL_")) {
@@ -293,6 +344,15 @@ async function getTrafficSummary(minutes: number) {
const value = typeof metadata?.user_agent === "string" ? metadata.user_agent : "unknown";
return value.trim().length > 0 ? value.trim() : "unknown";
})();
const taskId = (() => {
const value =
typeof event.entityId === "string"
? event.entityId
: typeof metadata?.task_id === "string"
? metadata.task_id
: "-";
return value;
})();
externalActorActivityMap.set(actorId, {
actorId,
@@ -301,16 +361,54 @@ async function getTrafficSummary(minutes: number) {
latestSurface: String(event.surface || "unknown"),
latestSourceIp: normalizedIp,
latestUserAgent: normalizedUa,
latestTaskId: taskId,
latestResponseStatus:
typeof event.response_status === "number" ? event.response_status : null,
latestResponseSummary: typeof event.response_summary === "string"
? event.response_summary
: "unknown",
latestReason: typeof event.reason === "string" ? event.reason : "unknown",
latestPayloadSummary:
typeof event.payload_summary === "string" ? event.payload_summary : "unknown",
latestRequestId: typeof event.request_id === "string" ? event.request_id : "n/a",
latestCreatedAt: event.createdAt.getTime(),
});
} else {
existing.events += 1;
if (event.createdAt.getTime() > existing.latestCreatedAt) {
const metadata = asRecordJson(event.metadata);
const taskId = (() => {
const value =
typeof event.entityId === "string"
? event.entityId
: typeof metadata?.task_id === "string"
? metadata.task_id
: "-";
return value;
})();
const normalizedIp = (() => {
const value = typeof metadata?.source_ip === "string" ? metadata.source_ip : "unknown";
return value.trim().length > 0 ? value.trim() : "unknown";
})();
const normalizedUa = (() => {
const value = typeof metadata?.user_agent === "string" ? metadata.user_agent : "unknown";
return value.trim().length > 0 ? value.trim() : "unknown";
})();
existing.latestAction = event.action;
existing.latestSurface = String(event.surface || "unknown");
existing.latestSourceIp = normalizedIp;
existing.latestUserAgent = normalizedUa;
existing.latestTaskId = taskId;
existing.latestResponseStatus =
typeof event.response_status === "number" ? event.response_status : null;
existing.latestResponseSummary =
typeof event.response_summary === "string" ? event.response_summary : "unknown";
existing.latestReason = typeof event.reason === "string" ? event.reason : "unknown";
existing.latestPayloadSummary =
typeof event.payload_summary === "string" ? event.payload_summary : "unknown";
existing.latestRequestId = typeof event.request_id === "string" ? event.request_id : "n/a";
existing.latestCreatedAt = event.createdAt.getTime();
}
}
}
@@ -342,6 +440,7 @@ async function getTrafficSummary(minutes: number) {
conversionRates,
externalActorActivities,
externalErrors,
demandSupply,
};
}
@@ -418,8 +517,11 @@ export default async function TrafficDashboard({
}
const summary = await getTrafficSummary(minutes);
const { conversionSummary, conversionRates } = summary;
const { conversionSummary, conversionRates, demandSupply } = summary;
const conversionHints = buildConversionTips(conversionRates, conversionSummary);
const hasClaimStall = summary.externalEventTypes.some((event) => event.action === "EXTERNAL_FUNNEL_CLAIM_STALL");
const demandHealthLabel = demandSupply.openTaskCount > 0 ? "有可接需求" : "無可接需求";
const demandHealthTone = demandSupply.openTaskCount > 0 ? "text-emerald-300" : "text-amber-300";
return (
<div className="min-h-screen bg-gray-950 text-gray-100 p-8 font-sans">
@@ -435,7 +537,7 @@ export default async function TrafficDashboard({
{summary.periodMinutes} `EXTERNAL_*`
</div>
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
<div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
<div className="text-gray-400 text-sm"></div>
<div className="text-3xl font-bold mt-2">{summary.totalEvents}</div>
@@ -456,6 +558,35 @@ export default async function TrafficDashboard({
<div className="text-gray-400 text-sm"></div>
<div className="text-3xl font-bold mt-2 text-amber-300">{conversionSummary.submit_events}</div>
</div>
<div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
<div className="text-gray-400 text-sm"></div>
<div className={`text-3xl font-bold mt-2 ${demandHealthTone}`}>{demandSupply.openTaskCount}</div>
<div className="text-xs text-gray-400 mt-2">{demandHealthLabel}</div>
</div>
</div>
<div className="grid gap-4 lg:grid-cols-2">
<div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
<h2 className="text-xl font-semibold mb-4"></h2>
<div className="space-y-2 text-sm">
<div className="flex justify-between"><span> OPEN </span><span className="text-emerald-300">{demandSupply.openTaskCount}</span></div>
<div className="flex justify-between"><span> {summary.periodMinutes} </span><span className="text-emerald-300">{demandSupply.newOpenTasksInWindow}</span></div>
<div className="flex justify-between"><span></span><span className="text-emerald-300">{demandSupply.avgScopeClarity === null ? "n/a" : `${Number(demandSupply.avgScopeClarity).toFixed(2)}`}</span></div>
</div>
<div className="mt-4 text-xs text-gray-400">
Task.status = OPEN title GitHub Issue: 開頭
</div>
</div>
<div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
<h2 className="text-xl font-semibold mb-4"></h2>
<ul className="text-sm text-gray-200 space-y-2 list-disc list-inside">
<li>1) <span className="text-emerald-300">{demandSupply.openTaskCount}</span> step</li>
<li>2) <span className="font-mono text-xs">/api/open-tasks</span> 3 30~60 </li>
<li>3) GitHub Issue:</li>
<li>4) rewardrequired_stack developer_wallet </li>
</ul>
</div>
</div>
<div className="grid gap-4 lg:grid-cols-2">
@@ -520,6 +651,16 @@ export default async function TrafficDashboard({
<li> PASS JUDGE_COMPLETE + overall_result=PASS CAPTURE</li>
<li>PASS</li>
</ol>
{hasClaimStall && demandSupply.openTaskCount > 0 ? (
<div className="mt-4 p-3 border border-amber-500/40 rounded-lg text-sm text-amber-200">
EXTERNAL_FUNNEL_CLAIM_STALL
<ul className="list-disc list-inside mt-2 space-y-1">
<li> OPEN `scope_clarity_score` reward </li>
<li> 1 `claim_task` 403 API Key/</li>
<li> 200 調</li>
</ul>
</div>
) : null}
<div className="mt-4 text-sm text-gray-400"> 30 </div>
</div>
</div>
@@ -566,15 +707,17 @@ export default async function TrafficDashboard({
<th className="text-left py-2">Actor</th>
<th className="text-left py-2"></th>
<th className="text-left py-2"></th>
<th className="text-left py-2"></th>
<th className="text-left py-2"> IP</th>
<th className="text-left py-2">User-Agent</th>
<th className="text-left py-2">Request-Id</th>
<th className="text-left py-2"></th>
</tr>
</thead>
<tbody>
{summary.externalActorActivities.length === 0 ? (
<tr>
<td colSpan={6} className="text-gray-500 py-3">
<td colSpan={8} className="text-gray-500 py-3">
AGENT AGENT
</td>
</tr>
@@ -587,11 +730,14 @@ export default async function TrafficDashboard({
<div className="text-gray-200">{actor.latestAction}</div>
<div className="text-xs text-gray-500">{actor.latestSurface}</div>
</td>
<td className="py-2 text-gray-300">{actor.latestTaskId}</td>
<td className="py-2 text-gray-300">{actor.latestSourceIp}</td>
<td className="py-2 text-gray-300">{actor.latestUserAgent}</td>
<td className="py-2 text-gray-300">{actor.latestRequestId}</td>
<td className="py-2">
<div className="text-gray-200">{actor.latestResponseStatus ?? "n/a"}</div>
<div className="text-xs text-gray-500">{actor.latestResponseSummary}</div>
<div className="text-xs text-gray-600">{actor.latestPayloadSummary}</div>
</td>
</tr>
))