Skip to content

Commit 9b476d7

Browse files
committed
update
1 parent 73b5f69 commit 9b476d7

File tree

4 files changed

+157
-2
lines changed

4 files changed

+157
-2
lines changed

backend/src/logParser.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ export class LogParser extends EventEmitter {
2121
requests2xx: 0,
2222
requestsPerSecond: 0,
2323
topIPs: {},
24-
countries: {}
24+
countries: {},
25+
topRouters: {},
26+
topRequestAddrs: {},
27+
topRequestHosts: {}
2528
};
2629
this.lastTimestamp = Date.now();
2730
this.requestsInLastSecond = 0;
@@ -199,6 +202,18 @@ export class LogParser extends EventEmitter {
199202
this.stats.topIPs[log.clientIP] = (this.stats.topIPs[log.clientIP] || 0) + 1;
200203
}
201204

205+
if (log.routerName && log.routerName !== 'unknown') {
206+
this.stats.topRouters[log.routerName] = (this.stats.topRouters[log.routerName] || 0) + 1;
207+
}
208+
209+
if (log.requestAddr && log.requestAddr !== '') {
210+
this.stats.topRequestAddrs[log.requestAddr] = (this.stats.topRequestAddrs[log.requestAddr] || 0) + 1;
211+
}
212+
213+
if (log.requestHost && log.requestHost !== '') {
214+
this.stats.topRequestHosts[log.requestHost] = (this.stats.topRequestHosts[log.requestHost] || 0) + 1;
215+
}
216+
202217
if (log.country && log.countryCode) {
203218
const key = `${log.countryCode}|${log.country}`;
204219
this.stats.countries[key] = (this.stats.countries[key] || 0) + 1;
@@ -230,10 +245,28 @@ export class LogParser extends EventEmitter {
230245
return { country: name, countryCode: code, count };
231246
});
232247

248+
const topRouters = Object.entries(this.stats.topRouters)
249+
.sort(([, a], [, b]) => b - a)
250+
.slice(0, 10)
251+
.map(([router, count]) => ({ router, count }));
252+
253+
const topRequestAddrs = Object.entries(this.stats.topRequestAddrs)
254+
.sort(([, a], [, b]) => b - a)
255+
.slice(0, 10)
256+
.map(([addr, count]) => ({ addr, count }));
257+
258+
const topRequestHosts = Object.entries(this.stats.topRequestHosts)
259+
.sort(([, a], [, b]) => b - a)
260+
.slice(0, 10)
261+
.map(([host, count]) => ({ host, count }));
262+
233263
return {
234264
...this.stats,
235265
topIPs,
236266
topCountries,
267+
topRouters,
268+
topRequestAddrs,
269+
topRequestHosts,
237270
avgResponseTime: Math.round(this.stats.avgResponseTime * 100) / 100
238271
};
239272
}

frontend/src/components/Dashboard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useWebSocket } from "@/hooks/useWebSocket";
22
import { StatsCards } from "./StatsCards";
33
import { LogTable } from "./LogTable";
44
import { GeoMap } from "./GeoMap";
5+
import { TopListsCards } from "./TopListsCards";
56
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
67
import { Badge } from "@/components/ui/badge";
78
import { Activity, AlertCircle, Server } from "lucide-react";
@@ -114,6 +115,8 @@ export function Dashboard() {
114115
</Card>
115116
</div>
116117

118+
<TopListsCards stats={stats} />
119+
117120
<GeoMap stats={stats} />
118121

119122
<Card>
@@ -130,4 +133,4 @@ export function Dashboard() {
130133
</Card>
131134
</div>
132135
);
133-
}
136+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
2+
import { Badge } from "@/components/ui/badge";
3+
import { Router, Network, ExternalLink, Users } from "lucide-react";
4+
import { Stats } from "@/hooks/useWebSocket";
5+
6+
interface TopListsCardsProps {
7+
stats: Stats | null;
8+
}
9+
10+
export function TopListsCards({ stats }: TopListsCardsProps) {
11+
if (!stats) {
12+
return (
13+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
14+
{[...Array(4)].map((_, i) => (
15+
<Card key={i}>
16+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
17+
<CardTitle className="text-sm font-medium">Loading...</CardTitle>
18+
</CardHeader>
19+
<CardContent>
20+
<div className="h-32 bg-muted animate-pulse rounded" />
21+
</CardContent>
22+
</Card>
23+
))}
24+
</div>
25+
);
26+
}
27+
28+
const topLists = [
29+
{
30+
title: "Top Routers",
31+
description: "Most active routers",
32+
icon: Router,
33+
data: stats.topRouters || [],
34+
color: "text-purple-600",
35+
dataKey: "router"
36+
},
37+
{
38+
title: "Top Request Addresses",
39+
description: "Most requested addresses",
40+
icon: Network,
41+
data: stats.topRequestAddrs || [],
42+
color: "text-cyan-600",
43+
dataKey: "addr"
44+
},
45+
{
46+
title: "Top Request Hosts",
47+
description: "Most requested hosts",
48+
icon: ExternalLink,
49+
data: stats.topRequestHosts || [],
50+
color: "text-emerald-600",
51+
dataKey: "host"
52+
},
53+
{
54+
title: "Top Client IPs",
55+
description: "Most active IP addresses",
56+
icon: Users,
57+
data: stats.topIPs || [],
58+
color: "text-orange-600",
59+
dataKey: "ip"
60+
}
61+
];
62+
63+
return (
64+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
65+
{topLists.map((list, index) => {
66+
const Icon = list.icon;
67+
return (
68+
<Card key={index}>
69+
<CardHeader>
70+
<CardTitle className="flex items-center gap-2">
71+
<Icon className={`h-5 w-5 ${list.color}`} />
72+
{list.title}
73+
</CardTitle>
74+
<CardDescription>{list.description}</CardDescription>
75+
</CardHeader>
76+
<CardContent>
77+
<div className="space-y-2">
78+
{list.data.length === 0 ? (
79+
<div className="text-center text-muted-foreground py-4">
80+
No data available
81+
</div>
82+
) : (
83+
list.data.slice(0, 5).map((item: any, itemIndex) => {
84+
const percentage = stats ? ((item.count / stats.totalRequests) * 100).toFixed(1) : 0;
85+
const value = item[list.dataKey];
86+
const displayValue = value.length > 20 ? `${value.substring(0, 20)}...` : value;
87+
88+
return (
89+
<div key={itemIndex} className="flex items-center justify-between p-2 rounded-lg bg-muted/30">
90+
<div className="flex items-center gap-2 min-w-0 flex-1">
91+
<div className={`w-2 h-2 rounded-full bg-${list.color.split('-')[1]}-${500 - itemIndex * 50}`}
92+
style={{ backgroundColor: `hsl(${220 - itemIndex * 20}, 70%, ${60 - itemIndex * 5}%)` }} />
93+
<span className="font-mono text-xs truncate" title={value}>
94+
{displayValue}
95+
</span>
96+
</div>
97+
<div className="flex items-center gap-2 flex-shrink-0">
98+
<Badge variant="secondary" className="text-xs">
99+
{item.count.toLocaleString()}
100+
</Badge>
101+
<span className="text-xs text-muted-foreground">
102+
{percentage}%
103+
</span>
104+
</div>
105+
</div>
106+
);
107+
})
108+
)}
109+
</div>
110+
</CardContent>
111+
</Card>
112+
);
113+
})}
114+
</div>
115+
);
116+
}

frontend/src/hooks/useWebSocket.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export interface Stats {
3535
requestsPerSecond: number;
3636
topIPs: Array<{ ip: string; count: number }>;
3737
topCountries: Array<{ country: string; countryCode: string; count: number }>;
38+
topRouters: Array<{ router: string; count: number }>;
39+
topRequestAddrs: Array<{ addr: string; count: number }>;
40+
topRequestHosts: Array<{ host: string; count: number }>;
3841
}
3942

4043
interface WebSocketMessage {

0 commit comments

Comments
 (0)