Skip to content

Commit 4055364

Browse files
committed
updates
1 parent 6e299b1 commit 4055364

File tree

5 files changed

+415
-269
lines changed

5 files changed

+415
-269
lines changed

relay/src/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { initRelayUser, getRelayUser } from "./utils/relay-user";
1818
import SQLiteStore from "./utils/sqlite-store";
1919
import S3Store from "./utils/s3-store";
2020
import { loggers } from "./utils/logger";
21+
import { StatsTracker } from "./utils/stats-tracker";
2122
import {
2223
config,
2324
ipfsConfig,
@@ -249,6 +250,10 @@ async function initializeServer() {
249250
// Fix per rate limiting con proxy
250251
app.set("trust proxy", 1);
251252

253+
// Stats Tracker Initialize
254+
const statsTracker = new StatsTracker();
255+
app.set("statsTracker", statsTracker);
256+
252257
// ===== ROOT HEALTH CHECK ENDPOINTS (for load balancers, k8s probes) =====
253258
// Note: /health endpoint with full details is registered later after initialization
254259
// Use /healthz for minimal health checks during startup
@@ -502,6 +507,14 @@ async function initializeServer() {
502507
app.set("gunInstance", gun);
503508
// Store the gun storage adapter for stats access
504509
app.set("gunStore", store);
510+
511+
// Hook Stats Tracker to Gun's wire peers
512+
gun.on("hi", (peer: any) => {
513+
if (!peer || !peer.wire) return;
514+
const addr = peer.url || peer.id || "unknown";
515+
statsTracker.patchSocket(peer.wire, addr);
516+
});
517+
505518
// Start wormhole cleanup scheduler for orphaned transfer cleanup
506519
if (wormholeConfig.enabled) {
507520
startWormholeCleanup(gun);

relay/src/public/dashboard/src/views/Charts.tsx

Lines changed: 115 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,23 @@ import {
1111
ResponsiveContainer,
1212
AreaChart,
1313
Area,
14+
PieChart,
15+
Pie,
16+
Cell
1417
} from "recharts";
1518

16-
interface ChartData {
17-
connections?: number[];
18-
requests?: number[];
19-
storage?: number[];
20-
labels?: string[];
19+
interface MetricPoint {
20+
ts: number;
21+
v: number;
2122
}
2223

2324
function Charts() {
2425
const { isAuthenticated, getAuthHeaders } = useAuth();
2526
const [loading, setLoading] = useState(true);
2627
const [stats, setStats] = useState<Record<string, any>>({});
27-
const [history, setHistory] = useState<any[]>([]);
28+
29+
// Local history for metrics not historically tracked by backend
30+
const [localHistory, setLocalHistory] = useState<any[]>([]);
2831

2932
const loadStats = useCallback(async () => {
3033
try {
@@ -33,17 +36,16 @@ function Charts() {
3336
const data = await response.json();
3437
setStats(data);
3538

36-
setHistory((prev) => {
39+
setLocalHistory((prev) => {
3740
const now = new Date().toLocaleTimeString();
3841
const newPoint = {
3942
time: now,
4043
memory: (data.memory?.heapUsed || 0) / 1024 / 1024,
41-
peers: data.peers?.count || 0,
42-
damIn: data.dam?.in?.count || 0,
43-
damOut: data.dam?.out?.count || 0,
44+
peers: data.connectedPeers || 0,
45+
totalMsgs: data.totalMessages || 0,
4446
};
4547
const newHistory = [...prev, newPoint];
46-
return newHistory.slice(-20);
48+
return newHistory.slice(-30);
4749
});
4850
}
4951
} catch (error) {
@@ -56,43 +58,63 @@ function Charts() {
5658
useEffect(() => {
5759
if (isAuthenticated) {
5860
loadStats();
59-
const interval = setInterval(loadStats, 5000);
61+
const interval = setInterval(loadStats, 2000);
6062
return () => clearInterval(interval);
6163
}
6264
}, [isAuthenticated, loadStats]);
6365

6466
const formatBytes = (bytes: number) => {
65-
if (!bytes) return "0 B";
67+
if (!bytes && bytes !== 0) return "0 B";
6668
const k = 1024;
6769
const sizes = ["B", "KB", "MB", "GB"];
6870
const i = Math.floor(Math.log(bytes) / Math.log(k));
6971
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
7072
};
7173

72-
const formatUptime = (seconds: number) => {
73-
if (!seconds) return "-";
74+
const formatUptime = (ms: number) => {
75+
if (!ms) return "-";
76+
const seconds = Math.floor(ms / 1000);
7477
const days = Math.floor(seconds / 86400);
7578
const hours = Math.floor((seconds % 86400) / 3600);
7679
const mins = Math.floor((seconds % 3600) / 60);
7780
return `${days}d ${hours}h ${mins}m`;
7881
};
7982

80-
if (loading && history.length === 0) {
83+
if (loading && localHistory.length === 0) {
8184
return (
8285
<div className="flex justify-center p-8">
8386
<span className="loading loading-spinner loading-lg"></span>
8487
</div>
8588
);
8689
}
8790

91+
// Map backend history into chart-friendly formats
92+
const msgHistoryData = (stats.msgHistory || []).map((pt: MetricPoint) => ({
93+
time: new Date(pt.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
94+
rate: pt.v
95+
}));
96+
97+
const byteHistoryData = (stats.byteHistory || []).map((pt: MetricPoint) => ({
98+
time: new Date(pt.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
99+
rate: pt.v
100+
}));
101+
102+
const opMixData = [
103+
{ name: 'PUT', value: stats.putCount || 0 },
104+
{ name: 'GET', value: stats.getCount || 0 },
105+
{ name: 'ACK', value: stats.ackCount || 0 },
106+
{ name: 'ERR', value: stats.errorCount || 0 },
107+
];
108+
const COLORS = ['#00ffe5', '#00b4ff', '#00ff8c', '#ff3a5c'];
109+
88110
return (
89111
<div className="flex flex-col gap-6 max-w-6xl">
90112
{/* Header */}
91113
<div className="card bg-base-100 shadow">
92114
<div className="card-body flex-row items-center justify-between flex-wrap gap-4">
93115
<div>
94-
<h2 className="card-title text-2xl">📊 Charts & Metrics</h2>
95-
<p className="text-base-content/70">Real-time system performance monitoring</p>
116+
<h2 className="card-title text-2xl">📊 Live Telemetry Charts</h2>
117+
<p className="text-base-content/70">Real-time GunDB protocol monitoring</p>
96118
</div>
97119
<div className="flex gap-2">
98120
<span className="badge badge-ghost">{new Date().toLocaleTimeString()}</span>
@@ -105,51 +127,62 @@ function Charts() {
105127
<div className="stats stats-vertical lg:stats-horizontal shadow w-full">
106128
<div className="stat">
107129
<div className="stat-title">Uptime</div>
108-
<div className="stat-value text-lg">{formatUptime(stats.up?.time / 1000)}</div>
130+
<div className="stat-value text-lg">{formatUptime(stats.uptime)}</div>
109131
</div>
110132
<div className="stat">
111133
<div className="stat-title">Heap Used</div>
112134
<div className="stat-value text-lg">{formatBytes(stats.memory?.heapUsed)}</div>
113135
</div>
114136
<div className="stat">
115-
<div className="stat-title">CPU User</div>
137+
<div className="stat-title">CPU System</div>
116138
<div className="stat-value text-lg">
117-
{stats.cpu?.user ? (stats.cpu.user / 1000000).toFixed(2) + "s" : "-"}
139+
{stats.cpu?.system ? (stats.cpu.system / 1000000).toFixed(2) + "s" : "-"}
118140
</div>
119141
</div>
120142
<div className="stat">
121-
<div className="stat-title">Version</div>
122-
<div className="stat-value text-lg">{stats.version || "1.0.0"}</div>
143+
<div className="stat-title">Total Messages</div>
144+
<div className="stat-value text-lg text-primary">{stats.totalMessages?.toLocaleString() || 0}</div>
123145
</div>
124146
</div>
125147

126148
{/* Charts Grid */}
127149
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
128-
{/* Memory Usage Chart */}
129-
<div className="card bg-base-100 shadow">
150+
{/* Message Rate Chart */}
151+
<div className="card bg-base-100 shadow border border-primary/20">
130152
<div className="card-body">
131-
<h3 className="card-title text-lg">🧠 Memory Usage (MB)</h3>
132-
<div className="h-72">
153+
<h3 className="card-title text-lg text-primary">⚡ Message Rate (msg/s)</h3>
154+
<div className="h-64">
133155
<ResponsiveContainer width="100%" height="100%">
134-
<AreaChart data={history}>
135-
<CartesianGrid strokeDasharray="3 3" stroke="#444" />
136-
<XAxis dataKey="time" stroke="#888" fontSize={12} />
137-
<YAxis stroke="#888" fontSize={12} />
156+
<AreaChart data={msgHistoryData}>
157+
<CartesianGrid strokeDasharray="3 3" stroke="#444" opacity={0.3} />
158+
<XAxis dataKey="time" stroke="#888" fontSize={10} tick={{fill: '#2a6070'}} />
159+
<YAxis stroke="#888" fontSize={10} tick={{fill: '#2a6070'}} />
138160
<Tooltip
139-
contentStyle={{
140-
backgroundColor: "#2a2a2a",
141-
border: "none",
142-
borderRadius: "8px",
143-
}}
144-
itemStyle={{ color: "#fff" }}
161+
contentStyle={{ backgroundColor: "#050f14", border: "1px solid #00ffe5", borderRadius: "4px" }}
162+
itemStyle={{ color: "#7ecfdf" }}
145163
/>
146-
<Area
147-
type="monotone"
148-
dataKey="memory"
149-
stroke="#8884d8"
150-
fill="#8884d8"
151-
fillOpacity={0.3}
164+
<Area type="monotone" dataKey="rate" stroke="#00ffe5" fill="#00ffe5" fillOpacity={0.15} isAnimationActive={false} />
165+
</AreaChart>
166+
</ResponsiveContainer>
167+
</div>
168+
</div>
169+
</div>
170+
171+
{/* Bandwidth Chart */}
172+
<div className="card bg-base-100 shadow border border-secondary/20">
173+
<div className="card-body">
174+
<h3 className="card-title text-lg text-secondary">🌊 Bandwidth (bytes/s)</h3>
175+
<div className="h-64">
176+
<ResponsiveContainer width="100%" height="100%">
177+
<AreaChart data={byteHistoryData}>
178+
<CartesianGrid strokeDasharray="3 3" stroke="#444" opacity={0.3} />
179+
<XAxis dataKey="time" stroke="#888" fontSize={10} tick={{fill: '#2a6070'}} />
180+
<YAxis stroke="#888" fontSize={10} tick={{fill: '#2a6070'}} />
181+
<Tooltip
182+
contentStyle={{ backgroundColor: "#050f14", border: "1px solid #00b4ff", borderRadius: "4px" }}
183+
itemStyle={{ color: "#7ecfdf" }}
152184
/>
185+
<Area type="monotone" dataKey="rate" stroke="#00b4ff" fill="#00b4ff" fillOpacity={0.15} isAnimationActive={false} />
153186
</AreaChart>
154187
</ResponsiveContainer>
155188
</div>
@@ -159,65 +192,57 @@ function Charts() {
159192
{/* Peers Chart */}
160193
<div className="card bg-base-100 shadow">
161194
<div className="card-body">
162-
<h3 className="card-title text-lg">👥 Connected Peers</h3>
163-
<div className="h-72">
195+
<h3 className="card-title text-lg">👥 Connected Peers Trend</h3>
196+
<div className="h-64">
164197
<ResponsiveContainer width="100%" height="100%">
165-
<LineChart data={history}>
166-
<CartesianGrid strokeDasharray="3 3" stroke="#444" />
167-
<XAxis dataKey="time" stroke="#888" fontSize={12} />
168-
<YAxis allowDecimals={false} stroke="#888" fontSize={12} />
198+
<LineChart data={localHistory}>
199+
<CartesianGrid strokeDasharray="3 3" stroke="#444" opacity={0.3} />
200+
<XAxis dataKey="time" stroke="#888" fontSize={10} />
201+
<YAxis allowDecimals={false} stroke="#888" fontSize={10} />
169202
<Tooltip
170-
contentStyle={{
171-
backgroundColor: "#2a2a2a",
172-
border: "none",
173-
borderRadius: "8px",
174-
}}
175-
itemStyle={{ color: "#fff" }}
203+
contentStyle={{ backgroundColor: "#050f14", border: "1px solid #00ff8c", borderRadius: "4px" }}
204+
itemStyle={{ color: "#7ecfdf" }}
176205
/>
177-
<Line type="step" dataKey="peers" stroke="#82ca9d" strokeWidth={2} />
206+
<Line type="stepAfter" dataKey="peers" stroke="#00ff8c" strokeWidth={2} isAnimationActive={false} />
178207
</LineChart>
179208
</ResponsiveContainer>
180209
</div>
181210
</div>
182211
</div>
183-
</div>
184212

185-
{/* Full Width Chart */}
186-
<div className="card bg-base-100 shadow">
187-
<div className="card-body">
188-
<h3 className="card-title text-lg">📡 DAM Request Traffic</h3>
189-
<div className="h-72">
190-
<ResponsiveContainer width="100%" height="100%">
191-
<AreaChart data={history}>
192-
<CartesianGrid strokeDasharray="3 3" stroke="#444" />
193-
<XAxis dataKey="time" stroke="#888" fontSize={12} />
194-
<YAxis stroke="#888" fontSize={12} />
195-
<Tooltip
196-
contentStyle={{ backgroundColor: "#2a2a2a", border: "none", borderRadius: "8px" }}
197-
itemStyle={{ color: "#fff" }}
198-
/>
199-
<Legend />
200-
<Area
201-
type="monotone"
202-
dataKey="damIn"
203-
name="Incoming (In)"
204-
stroke="#ffc658"
205-
fill="#ffc658"
206-
stackId="1"
207-
/>
208-
<Area
209-
type="monotone"
210-
dataKey="damOut"
211-
name="Outgoing (Out)"
212-
stroke="#ff7300"
213-
fill="#ff7300"
214-
stackId="1"
215-
/>
216-
</AreaChart>
217-
</ResponsiveContainer>
213+
{/* Operation Mix (Doughnut) */}
214+
<div className="card bg-base-100 shadow">
215+
<div className="card-body items-center">
216+
<h3 className="card-title text-lg w-full text-left">🔄 Operation Mix</h3>
217+
<div className="h-64 w-full">
218+
<ResponsiveContainer width="100%" height="100%">
219+
<PieChart>
220+
<Pie
221+
data={opMixData}
222+
cx="50%"
223+
cy="50%"
224+
innerRadius={60}
225+
outerRadius={90}
226+
paddingAngle={2}
227+
dataKey="value"
228+
stroke="none"
229+
>
230+
{opMixData.map((entry, index) => (
231+
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
232+
))}
233+
</Pie>
234+
<Tooltip
235+
contentStyle={{ backgroundColor: "#050f14", border: "1px solid #333", borderRadius: "4px" }}
236+
itemStyle={{ color: "#fff" }}
237+
/>
238+
<Legend verticalAlign="bottom" height={36} />
239+
</PieChart>
240+
</ResponsiveContainer>
241+
</div>
218242
</div>
219243
</div>
220244
</div>
245+
221246
</div>
222247
);
223248
}

0 commit comments

Comments
 (0)