feat(traffic): expose external actor source/response details in monitor
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 7s

This commit is contained in:
OG T
2026-06-07 19:31:27 +08:00
parent b1c9418f40
commit 0b07ae4344
2 changed files with 159 additions and 0 deletions

View File

@@ -91,6 +91,19 @@ function explainAction(action: string) {
return EVENT_LABELS[action] || action;
}
type ExternalActorActivity = {
actorId: string;
events: number;
latestAction: string;
latestSurface: string;
latestSourceIp: string;
latestUserAgent: string;
latestResponseStatus: number | null;
latestResponseSummary: string;
latestReason: string;
latestCreatedAt: number;
};
async function getTrafficSummary(minutes: number) {
const since = new Date(Date.now() - minutes * 60 * 1000);
@@ -213,6 +226,8 @@ async function getTrafficSummary(minutes: number) {
...event,
surface: metadata?.surface,
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",
metadata,
};
});
@@ -252,6 +267,60 @@ async function getTrafficSummary(minutes: number) {
.filter((event) => event.action.includes("ERROR"))
.map((event) => event.action);
const externalActorActivityMap = new Map<string, ExternalActorActivity>();
for (const event of recentEvents) {
if (!event.action.startsWith("EXTERNAL_")) {
continue;
}
const actorId = event.actorId || "agent:unknown";
const isInternal = isInternalActor({
actorType: event.actorType,
actorId: event.actorId,
});
if (isInternal || event.actorType !== "AGENT") {
continue;
}
const existing = externalActorActivityMap.get(actorId);
if (!existing) {
const metadata = asRecordJson(event.metadata);
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";
})();
externalActorActivityMap.set(actorId, {
actorId,
events: 1,
latestAction: event.action,
latestSurface: String(event.surface || "unknown"),
latestSourceIp: normalizedIp,
latestUserAgent: normalizedUa,
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",
latestCreatedAt: event.createdAt.getTime(),
});
} else {
existing.events += 1;
}
}
const externalActorActivities = Array.from(externalActorActivityMap.values())
.sort((left, right) => {
if (right.events !== left.events) return right.events - left.events;
return right.latestCreatedAt - left.latestCreatedAt;
})
.slice(0, 40);
return {
periodMinutes: minutes,
totalEvents: totalRows,
@@ -271,6 +340,7 @@ async function getTrafficSummary(minutes: number) {
recentInternalEvents: recentEvents.filter((event) => !event.action.startsWith("EXTERNAL_")),
conversionSummary,
conversionRates,
externalActorActivities,
externalErrors,
};
}
@@ -487,6 +557,50 @@ export default async function TrafficDashboard({
</div>
</div>
<div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
<h2 className="text-xl font-semibold mb-4"> Actor </h2>
<div className="overflow-auto max-h-96">
<table className="w-full text-sm">
<thead>
<tr className="text-gray-400 border-b border-gray-700">
<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"> IP</th>
<th className="text-left py-2">User-Agent</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">
AGENT AGENT
</td>
</tr>
) : (
summary.externalActorActivities.map((actor) => (
<tr key={actor.actorId} className="border-b border-gray-800">
<td className="py-2 text-gray-300">{actor.actorId}</td>
<td className="py-2 text-emerald-300">{actor.events}</td>
<td className="py-2">
<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.latestSourceIp}</td>
<td className="py-2 text-gray-300">{actor.latestUserAgent}</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>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
<div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
<h2 className="text-xl font-semibold mb-4"></h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 mb-4 text-sm">
@@ -542,6 +656,9 @@ export default async function TrafficDashboard({
<div className="text-gray-400">
actor={event.actorType}:{event.actorId || "unknown"} | entity={event.entityType}/{event.entityId} | surface={String(event.surface || "-")} | {ts}
</div>
<div className="text-gray-500 text-xs mt-1">
response={event.response_status ?? "n/a"} / summary={event.response_summary}
</div>
{event.reason ? <div className="text-gray-500 text-xs mt-1">{event.reason}</div> : null}
{event.metadata ? (
<pre className="text-xs text-gray-500 mt-1 whitespace-pre-wrap">