MotorSignal-App/website/components/ops/operations-infrastructure.tsx

1042 lines
46 KiB
TypeScript

'use client';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import {
OperationsInfrastructureData,
ServiceStatusValue,
ComponentHealthStatus,
OperationsRole,
InfrastructureSpeedtestResult,
SpeedtestMode,
} from './types';
import { normalizeInfrastructureResponse } from '../../lib/ops-data';
import { OperationsActions } from './operations-actions';
interface OperationsInfrastructureProps {
role: OperationsRole;
}
export function OperationsInfrastructure({ role }: OperationsInfrastructureProps) {
const [data, setData] = useState<OperationsInfrastructureData | null>(null);
const [isRefreshing, setIsRefreshing] = useState(false);
const [lastFetchedAt, setLastFetchedAt] = useState<string | null>(null);
const [fetchError, setFetchError] = useState<string | null>(null);
const [copiedSha, setCopiedSha] = useState(false);
const [refreshInterval, setRefreshInterval] = useState<number>(30);
// Speedtest state: dual options (Client to Server vs Server to External)
const [speedtestMode, setSpeedtestMode] = useState<SpeedtestMode>('client_to_server');
const [isRunningSpeedtest, setIsRunningSpeedtest] = useState(false);
const [clientSpeedtestResult, setClientSpeedtestResult] = useState<InfrastructureSpeedtestResult | null>(null);
const [serverSpeedtestResult, setServerSpeedtestResult] = useState<InfrastructureSpeedtestResult | null>(null);
const [speedtestError, setSpeedtestError] = useState<string | null>(null);
const [speedtestProgress, setSpeedtestProgress] = useState<{
percent: number;
currentMbps?: number;
message: string;
}>({ percent: 0, message: '' });
const activeSpeedtestResult = speedtestMode === 'client_to_server' ? clientSpeedtestResult : serverSpeedtestResult;
const speedtestResult = activeSpeedtestResult;
const isFetchingRef = useRef(false);
const pollTimerRef = useRef<NodeJS.Timeout | null>(null);
const fetchData = useCallback(async () => {
if (isFetchingRef.current) return;
isFetchingRef.current = true;
setIsRefreshing(true);
try {
const resp = await fetch('/api/ops/infrastructure', {
method: 'GET',
headers: { accept: 'application/json' },
cache: 'no-store',
});
if (resp.ok) {
const raw = await resp.json();
const normalized = normalizeInfrastructureResponse(raw);
setData(normalized);
setLastFetchedAt(new Date().toLocaleTimeString());
setFetchError(null);
} else if (resp.status === 401 || resp.status === 403) {
setFetchError('Authorization failed. Access restricted.');
} else {
const raw = await resp.json().catch(() => null);
setData(normalizeInfrastructureResponse(raw));
setFetchError('Metrics collector degraded or partially unavailable.');
}
} catch {
setFetchError('Network error reaching infrastructure endpoint.');
} finally {
isFetchingRef.current = false;
setIsRefreshing(false);
}
}, []);
const handleRunClientSpeedtest = async () => {
setSpeedtestProgress({ percent: 10, message: 'Measuring latency to MotorSignal server...' });
// 1. Latency probe (3 rapid samples)
const pings: number[] = [];
let detectedIp: string | undefined;
for (let i = 0; i < 3; i++) {
const t0 = typeof performance !== 'undefined' ? performance.now() : Date.now();
const pingResp = await fetch(`/api/ops/infrastructure/speedtest?action=ping&sample=${i}&t=${Date.now()}`, {
method: 'GET',
headers: { accept: 'application/json' },
cache: 'no-store',
});
if (!pingResp.ok) throw new Error('Client-to-server latency probe failed');
const t1 = typeof performance !== 'undefined' ? performance.now() : Date.now();
pings.push(Math.max(1, t1 - t0));
const pingJson = await pingResp.json().catch(() => null);
if (pingJson?.client_ip) detectedIp = pingJson.client_ip;
}
const minPing = Math.min(...pings);
const latencyMs = Math.round(minPing * 10) / 10;
setSpeedtestProgress({
percent: 30,
message: `Latency: ${latencyMs} ms. Testing download throughput...`,
});
// 2. Download throughput benchmark (2.5 MB bounded payload)
const expectedBytes = 2500000;
const downloadUrl = `/api/ops/infrastructure/speedtest?action=download&bytes=${expectedBytes}&t=${Date.now()}`;
const tDownStart = typeof performance !== 'undefined' ? performance.now() : Date.now();
const downResp = await fetch(downloadUrl, {
method: 'GET',
headers: { accept: 'application/octet-stream' },
cache: 'no-store',
});
if (!downResp.ok) throw new Error('Download throughput test failed');
if (downResp.headers.get('X-Client-IP')) {
detectedIp = downResp.headers.get('X-Client-IP')!;
}
let receivedBytes = 0;
if (downResp.body && typeof downResp.body.getReader === 'function') {
const reader = downResp.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) {
receivedBytes += value.length;
const elapsedSec = Math.max(
0.01,
((typeof performance !== 'undefined' ? performance.now() : Date.now()) - tDownStart) / 1000
);
const currentMbps = Math.round(((receivedBytes * 8) / (elapsedSec * 1000000)) * 100) / 100;
const pct = Math.min(70, 30 + Math.round((receivedBytes / expectedBytes) * 40));
setSpeedtestProgress({
percent: pct,
currentMbps,
message: `Downloading: ${Math.round((receivedBytes / 1024 / 1024) * 10) / 10} MB (${currentMbps} Mbps)...`,
});
}
}
} else if (typeof downResp.arrayBuffer === 'function') {
const buf = await downResp.arrayBuffer();
receivedBytes = buf.byteLength;
} else {
const blob = await downResp.blob();
receivedBytes = blob.size;
}
const tDownEnd = typeof performance !== 'undefined' ? performance.now() : Date.now();
const downDurationSec = Math.max(0.01, (tDownEnd - tDownStart) / 1000);
const downloadMbps = Math.round(((receivedBytes * 8) / (downDurationSec * 1000000)) * 100) / 100;
setSpeedtestProgress({
percent: 75,
currentMbps: downloadMbps,
message: `Download: ${downloadMbps} Mbps. Testing upload throughput...`,
});
// 3. Upload throughput benchmark (1.5 MB bounded payload)
const uploadBytes = 1500000;
const uploadPayload = new Uint8Array(uploadBytes);
for (let i = 0; i < uploadBytes; i += 4096) {
uploadPayload[i] = (i * 37) & 0xff;
}
const tUpStart = typeof performance !== 'undefined' ? performance.now() : Date.now();
const upResp = await fetch(`/api/ops/infrastructure/speedtest?action=upload&t=${Date.now()}`, {
method: 'POST',
headers: { 'Content-Type': 'application/octet-stream' },
body: uploadPayload,
});
if (!upResp.ok) throw new Error('Upload throughput test failed');
const tUpEnd = typeof performance !== 'undefined' ? performance.now() : Date.now();
const upDurationSec = Math.max(0.01, (tUpEnd - tUpStart) / 1000);
const uploadMbps = Math.round(((uploadBytes * 8) / (upDurationSec * 1000000)) * 100) / 100;
const upData = await upResp.json().catch(() => null);
if (upData?.client_ip) detectedIp = upData.client_ip;
setSpeedtestProgress({
percent: 100,
currentMbps: uploadMbps,
message: 'Client-to-server benchmark complete',
});
const result: InfrastructureSpeedtestResult = {
status: 'completed',
tested_at: new Date().toISOString(),
latency_ms: latencyMs,
download_mbps: downloadMbps,
upload_mbps: uploadMbps,
server_target: 'MotorSignal Host Edge',
client_ip: detectedIp ?? 'Operator Browser Link',
mode: 'client_to_server',
direction_label: 'Operator Browser ➔ MotorSignal Server',
};
setClientSpeedtestResult(result);
// Audit recording proxy call (non-blocking)
void fetch('/api/ops/infrastructure/speedtest?action=record_client_result', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
latency_ms: latencyMs,
download_mbps: downloadMbps,
upload_mbps: uploadMbps,
client_ip: detectedIp,
}),
}).catch(() => {});
};
const handleRunServerSpeedtest = async () => {
setSpeedtestProgress({
percent: 30,
message: 'Benchmarking MotorSignal server to upstream internet probe...',
});
const resp = await fetch('/api/ops/infrastructure/speedtest', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mode: 'server_to_external' }),
});
if (!resp.ok) {
const data = await resp.json().catch(() => null);
throw new Error(data?.detail || 'Failed to complete upstream speedtest');
}
const data = (await resp.json()) as InfrastructureSpeedtestResult;
setSpeedtestProgress({
percent: 100,
currentMbps: data.download_mbps,
message: 'Server upstream benchmark complete',
});
const result: InfrastructureSpeedtestResult = {
...data,
mode: 'server_to_external',
direction_label: data.direction_label ?? 'MotorSignal Server ➔ Upstream Edge (1.1.1.1)',
};
setServerSpeedtestResult(result);
};
const handleRunSpeedtest = async () => {
if (isRunningSpeedtest) return;
setIsRunningSpeedtest(true);
setSpeedtestError(null);
setSpeedtestProgress({ percent: 5, message: 'Preparing benchmark...' });
try {
if (speedtestMode === 'client_to_server') {
await handleRunClientSpeedtest();
} else {
await handleRunServerSpeedtest();
}
} catch (err: unknown) {
setSpeedtestError(err instanceof Error ? err.message : 'Network error executing speedtest');
} finally {
setIsRunningSpeedtest(false);
}
};
// Initial fetch and user-selectable visibility-gated polling loop (5s, 10s, 30s)
useEffect(() => {
void fetchData();
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
void fetchData();
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
const pollIntervalMs = refreshInterval ? refreshInterval * 1000 : 30000;
pollTimerRef.current = setInterval(() => {
if (document.visibilityState === 'visible') {
void fetchData();
}
}, pollIntervalMs || 30000);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
if (pollTimerRef.current) {
clearInterval(pollTimerRef.current);
}
};
}, [fetchData, refreshInterval]);
const handleCopySha = (sha: string) => {
if (!sha) return;
navigator.clipboard.writeText(sha).then(() => {
setCopiedSha(true);
setTimeout(() => setCopiedSha(false), 2000);
});
};
const formatBytes = (bytes: number | null): string => {
if (bytes === null || bytes === undefined) return 'N/A';
if (bytes >= 1e12) return `${(bytes / 1e12).toFixed(2)} TB`;
if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(2)} GB`;
if (bytes >= 1e6) return `${(bytes / 1e6).toFixed(1)} MB`;
return `${(bytes / 1e3).toFixed(1)} KB`;
};
const getServiceStatusBadge = (status: ServiceStatusValue) => {
switch (status) {
case 'active':
return {
label: 'Active (Running)',
icon: 'check_circle',
classes: 'bg-emerald-950/60 text-emerald-400 border-emerald-800/60',
};
case 'inactive':
return {
label: 'Inactive (Stopped)',
icon: 'pause_circle',
classes: 'bg-amber-950/60 text-amber-400 border-amber-800/60',
};
case 'failed':
return {
label: 'Failed',
icon: 'cancel',
classes: 'bg-rose-950/60 text-rose-400 border-rose-800/60',
};
case 'unknown':
default:
return {
label: 'Unknown / Unreachable',
icon: 'help',
classes: 'bg-slate-900/60 text-slate-400 border-slate-700/60',
};
}
};
const getComponentHealthBadge = (status: ComponentHealthStatus) => {
switch (status) {
case 'available':
return {
label: 'Available',
icon: 'check_circle',
classes: 'bg-emerald-950/60 text-emerald-400 border-emerald-800/60',
};
case 'degraded':
return {
label: 'Degraded',
icon: 'warning',
classes: 'bg-amber-950/60 text-amber-400 border-amber-800/60',
};
case 'unavailable':
return {
label: 'Unavailable',
icon: 'cancel',
classes: 'bg-rose-950/60 text-rose-400 border-rose-800/60',
};
case 'unknown':
default:
return {
label: 'Unknown',
icon: 'help',
classes: 'bg-slate-900/60 text-slate-400 border-slate-700/60',
};
}
};
const resources = data?.resources;
const services = data?.services ?? [];
const network = data?.network;
const deployment = data?.deployment;
return (
<div className="space-y-8">
{/* Header & Controls */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-6 border-b border-outline-variant/20">
<div>
<div className="flex items-center gap-2 mb-1">
<span className="material-symbols-outlined text-primary-container text-2xl">
dns
</span>
<h1 className="text-xl font-bold tracking-tight text-on-surface">
Infrastructure & Runtime
</h1>
<span className="px-2 py-0.5 rounded text-[11px] font-mono bg-surface-container-high border border-outline-variant/30 text-primary-fixed-dim">
Phase 2 Operations
</span>
</div>
<p className="text-xs text-on-surface-variant">
Host system resources, systemd core services, network totals, and deployment runtime health.
</p>
</div>
<div className="flex items-center gap-3">
{/* Selectable Auto-refresh interval (5s, 10s, 30s) */}
<div className="flex items-center gap-1.5 text-[11px] text-on-surface-variant font-mono bg-surface-container-low px-2 py-1 rounded-lg border border-outline-variant/20">
<span className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse mr-0.5" />
<span className="text-outline">Auto:</span>
{[5, 10, 30].map((sec) => (
<button
key={sec}
type="button"
onClick={() => setRefreshInterval(sec)}
className={`px-1.5 py-0.5 rounded text-[11px] font-semibold transition-colors ${
refreshInterval === sec
? 'bg-primary-container text-on-primary-container font-bold'
: 'text-on-surface-variant hover:text-on-surface'
}`}
title={`Auto-refresh every ${sec} seconds`}
>
{sec}s
</button>
))}
</div>
{/* Last refreshed */}
{lastFetchedAt && (
<span className="text-[11px] text-outline font-mono hidden md:inline">
Updated: {lastFetchedAt}
</span>
)}
{/* Manual Refresh Button */}
<button
type="button"
onClick={() => void fetchData()}
disabled={isRefreshing}
className="flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-semibold bg-surface-container-high hover:bg-surface-container-highest border border-outline-variant/40 text-on-surface disabled:opacity-50 transition-all focus-visible:ring-2 focus-visible:ring-primary-container"
>
<span
className={`material-symbols-outlined text-[16px] ${
isRefreshing ? 'animate-spin' : ''
}`}
>
refresh
</span>
<span>{isRefreshing ? 'Refreshing...' : 'Refresh'}</span>
</button>
</div>
</div>
{/* Optional Warning Banner */}
{fetchError && (
<div className="p-3.5 rounded-lg bg-amber-950/40 border border-amber-800/50 flex items-center gap-2.5 text-xs text-amber-300">
<span className="material-symbols-outlined text-amber-400 text-base">
warning
</span>
<span>{fetchError}</span>
</div>
)}
{/* SECTION 1: System Resources */}
<div>
<div className="flex items-center justify-between mb-3">
<h2 className="text-xs font-bold uppercase tracking-wider text-outline">
Server Resources
</h2>
<span className="text-[10px] text-outline font-mono">
Target Host: Ubuntu VPS
</span>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
{/* CPU Usage */}
<div className="p-4 rounded-xl bg-surface-container-low border border-outline-variant/20 space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs font-medium text-on-surface-variant">CPU Usage</span>
<span className="material-symbols-outlined text-base text-primary-fixed-dim">
memory
</span>
</div>
<div>
<div className="text-2xl font-bold font-mono tracking-tight text-on-surface">
{resources?.cpu_percent !== null && resources?.cpu_percent !== undefined
? `${resources.cpu_percent}%`
: 'Unavailable'}
</div>
<p className="text-[11px] text-outline font-mono mt-0.5">
Load: {resources?.load_avg ? resources.load_avg.join(', ') : 'N/A'}
</p>
</div>
{/* Progress Bar */}
<div className="w-full bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
<div
className="bg-primary-container h-full rounded-full transition-all duration-300"
style={{ width: `${Math.min(100, Math.max(0, resources?.cpu_percent ?? 0))}%` }}
/>
</div>
</div>
{/* Memory (RAM) */}
<div className="p-4 rounded-xl bg-surface-container-low border border-outline-variant/20 space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs font-medium text-on-surface-variant">Memory (RAM)</span>
<span className="material-symbols-outlined text-base text-primary-fixed-dim">
developer_board
</span>
</div>
<div>
<div className="text-xl font-bold font-mono tracking-tight text-on-surface">
{resources?.memory
? `${formatBytes(resources.memory.used)} / ${formatBytes(resources.memory.total)}`
: 'Unavailable'}
</div>
<p className="text-[11px] text-outline font-mono mt-0.5">
{resources?.memory
? `${resources.memory.percent}% used (${formatBytes(resources.memory.available)} free)`
: 'N/A'}
</p>
</div>
{/* Progress Bar */}
<div className="w-full bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
<div
className={`h-full rounded-full transition-all duration-300 ${
(resources?.memory?.percent ?? 0) >= 90
? 'bg-rose-500'
: (resources?.memory?.percent ?? 0) >= 80
? 'bg-amber-500'
: 'bg-primary-container'
}`}
style={{ width: `${Math.min(100, Math.max(0, resources?.memory?.percent ?? 0))}%` }}
/>
</div>
</div>
{/* Swap Memory */}
<div className="p-4 rounded-xl bg-surface-container-low border border-outline-variant/20 space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs font-medium text-on-surface-variant">Swap Space</span>
<span className="material-symbols-outlined text-base text-primary-fixed-dim">
swap_horiz
</span>
</div>
<div>
<div className="text-xl font-bold font-mono tracking-tight text-on-surface">
{resources?.swap
? `${formatBytes(resources.swap.used)} / ${formatBytes(resources.swap.total)}`
: 'Unavailable'}
</div>
<p className="text-[11px] text-outline font-mono mt-0.5">
{resources?.swap ? `${resources.swap.percent}% utilized` : 'N/A'}
</p>
</div>
{/* Progress Bar */}
<div className="w-full bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
<div
className="bg-primary-container/70 h-full rounded-full transition-all duration-300"
style={{ width: `${Math.min(100, Math.max(0, resources?.swap?.percent ?? 0))}%` }}
/>
</div>
</div>
{/* Disk Storage (/) */}
<div className="p-4 rounded-xl bg-surface-container-low border border-outline-variant/20 space-y-3">
<div className="flex items-center justify-between">
<div className="flex items-center gap-1.5">
<span className="text-xs font-medium text-on-surface-variant">Root Disk (/)</span>
{resources?.disk?.critical ? (
<span className="px-1.5 py-0.2 rounded text-[9px] font-mono font-bold bg-rose-950 text-rose-300 border border-rose-800">
CRITICAL
</span>
) : resources?.disk?.warning ? (
<span className="px-1.5 py-0.2 rounded text-[9px] font-mono font-bold bg-amber-950 text-amber-300 border border-amber-800">
WARNING
</span>
) : null}
</div>
<span className="material-symbols-outlined text-base text-primary-fixed-dim">
hard_drive
</span>
</div>
<div>
<div className="text-xl font-bold font-mono tracking-tight text-on-surface">
{resources?.disk
? `${formatBytes(resources.disk.used)} / ${formatBytes(resources.disk.total)}`
: 'Unavailable'}
</div>
<p className="text-[11px] text-outline font-mono mt-0.5">
{resources?.disk
? `${resources.disk.percent}% used (${formatBytes(resources.disk.available)} free)`
: 'N/A'}
</p>
</div>
{/* Progress Bar */}
<div className="w-full bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
<div
className={`h-full rounded-full transition-all duration-300 ${
resources?.disk?.critical
? 'bg-rose-500'
: resources?.disk?.warning
? 'bg-amber-500'
: 'bg-primary-container'
}`}
style={{ width: `${Math.min(100, Math.max(0, resources?.disk?.percent ?? 0))}%` }}
/>
</div>
</div>
{/* Server Uptime */}
<div className="p-4 rounded-xl bg-surface-container-low border border-outline-variant/20 space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs font-medium text-on-surface-variant">Server Uptime</span>
<span className="material-symbols-outlined text-base text-primary-fixed-dim">
schedule
</span>
</div>
<div>
<div className="text-lg font-bold font-mono tracking-tight text-on-surface truncate">
{resources?.uptime_formatted || 'Unavailable'}
</div>
<p className="text-[11px] text-outline font-mono mt-0.5">
{resources?.uptime_seconds
? `${resources.uptime_seconds.toLocaleString()}s total uptime`
: 'N/A'}
</p>
</div>
<div className="text-[10px] text-primary-fixed-dim font-mono">
System running continuously
</div>
</div>
</div>
</div>
{/* SECTION 2: Core Systemd Services */}
<div>
<div className="flex items-center justify-between mb-3">
<h2 className="text-xs font-bold uppercase tracking-wider text-outline">
Core Services (systemd)
</h2>
<span className="text-[10px] text-outline font-mono">
Strict Allowlist: 4 Services
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{services.map((svc) => {
const badge = getServiceStatusBadge(svc.status);
return (
<div
key={svc.name}
className="p-4 rounded-xl bg-surface-container-low border border-outline-variant/20 flex flex-col justify-between space-y-3"
>
<div>
<div className="flex items-start justify-between gap-2">
<div>
<h3 className="text-sm font-semibold text-on-surface">
{svc.display_name}
</h3>
<p className="text-[11px] font-mono text-outline mt-0.5">
{svc.name}.service
</p>
</div>
</div>
<p className="text-xs text-on-surface-variant mt-2 leading-relaxed">
{svc.description}
</p>
</div>
<div className="pt-3 border-t border-outline-variant/10 flex items-center justify-between">
<span className="text-[11px] text-outline">Service Status</span>
<div
className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[11px] font-mono font-medium border ${badge.classes}`}
>
<span className="material-symbols-outlined text-[14px]">
{badge.icon}
</span>
<span>{badge.label}</span>
</div>
</div>
</div>
);
})}
</div>
</div>
{/* SECTION 3 & 4: Network Cumulative Totals & Deployment Health */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Network Cumulative Totals */}
<div className="p-5 rounded-xl bg-surface-container-low border border-outline-variant/20 space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="material-symbols-outlined text-primary-container text-xl">
network_check
</span>
<div>
<h3 className="text-sm font-bold text-on-surface">
Network Cumulative Totals
</h3>
<p className="text-[11px] text-outline">
Aggregated across non-loopback interfaces since host boot
</p>
</div>
</div>
<div className="flex items-center gap-2">
<span className="px-2 py-0.5 rounded text-[10px] font-mono bg-surface-container-high text-primary-fixed-dim border border-outline-variant/30">
Not a rate
</span>
</div>
</div>
<div className="grid grid-cols-2 gap-4 pt-2">
<div className="p-3.5 rounded-lg bg-surface-container-lowest border border-outline-variant/15 space-y-1">
<div className="flex items-center gap-1.5 text-xs text-on-surface-variant">
<span className="material-symbols-outlined text-[15px] text-emerald-400">
download
</span>
<span>Total Received</span>
</div>
<div className="text-xl font-bold font-mono text-on-surface">
{formatBytes(network?.bytes_received ?? null)}
</div>
<p className="text-[10px] text-outline font-mono">
{network?.bytes_received ? `${network.bytes_received.toLocaleString()} bytes` : 'N/A'}
</p>
</div>
<div className="p-3.5 rounded-lg bg-surface-container-lowest border border-outline-variant/15 space-y-1">
<div className="flex items-center gap-1.5 text-xs text-on-surface-variant">
<span className="material-symbols-outlined text-[15px] text-primary-container">
upload
</span>
<span>Total Transmitted</span>
</div>
<div className="text-xl font-bold font-mono text-on-surface">
{formatBytes(network?.bytes_transmitted ?? null)}
</div>
<p className="text-[10px] text-outline font-mono">
{network?.bytes_transmitted ? `${network.bytes_transmitted.toLocaleString()} bytes` : 'N/A'}
</p>
</div>
</div>
{/* Speedtest Suite: Client-to-Server vs Server-to-Upstream */}
<div className="p-4 rounded-lg bg-surface-container-lowest border border-outline-variant/20 space-y-3.5">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div className="space-y-0.5">
<div className="flex items-center gap-1.5 font-bold text-xs text-on-surface">
<span className="material-symbols-outlined text-[16px] text-primary-container">speed</span>
<span>Network Speedtest & Diagnostics</span>
</div>
<p className="text-[10px] text-outline">
Measure real-time latency and throughput across network segments
</p>
</div>
{/* Mode Toggle Tabs */}
<div className="flex items-center p-1 rounded-lg bg-surface-container-high/60 border border-outline-variant/20 gap-1 self-start sm:self-auto">
<button
type="button"
onClick={() => {
if (!isRunningSpeedtest) setSpeedtestMode('client_to_server');
}}
disabled={isRunningSpeedtest}
className={`flex items-center gap-1 px-2.5 py-1 rounded text-xs transition-all ${
speedtestMode === 'client_to_server'
? 'bg-primary-container text-on-primary-container font-semibold shadow-xs'
: 'text-on-surface-variant hover:text-on-surface'
}`}
title="Measure connection from your browser to MotorSignal server"
>
<span className="material-symbols-outlined text-[14px]">laptop_mac</span>
<span>My Connection → MotorSignal</span>
</button>
<button
type="button"
onClick={() => {
if (!isRunningSpeedtest) setSpeedtestMode('server_to_external');
}}
disabled={isRunningSpeedtest}
className={`flex items-center gap-1 px-2.5 py-1 rounded text-xs transition-all ${
speedtestMode === 'server_to_external'
? 'bg-primary-container text-on-primary-container font-semibold shadow-xs'
: 'text-on-surface-variant hover:text-on-surface'
}`}
title="Measure connection from MotorSignal server to upstream internet"
>
<span className="material-symbols-outlined text-[14px]">cloud</span>
<span>MotorSignal → Upstream Server</span>
</button>
</div>
</div>
{/* Direction description banner & trigger */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2.5 p-2.5 rounded-md bg-surface-container-low/70 border border-outline-variant/10 text-[11px]">
<div className="flex items-center gap-2 text-on-surface-variant">
<span className="material-symbols-outlined text-[15px] text-primary-fixed-dim">
{speedtestMode === 'client_to_server' ? 'sync_alt' : 'hub'}
</span>
<span>
{speedtestMode === 'client_to_server' ? (
<span>
<strong className="text-on-surface">Client to Server:</strong> Measures latency, download, and upload between your operator browser and the MotorSignal web host.
</span>
) : (
<span>
<strong className="text-on-surface">Server to External:</strong> Measures server-side network performance from MotorSignal production host to upstream Cloudflare edge.
</span>
)}
</span>
</div>
<button
type="button"
onClick={() => void handleRunSpeedtest()}
disabled={isRunningSpeedtest}
className="shrink-0 flex items-center justify-center gap-1.5 px-3 py-1.5 rounded text-xs font-semibold bg-primary-container text-on-primary-container hover:opacity-90 disabled:opacity-50 transition-all shadow-sm"
>
<span className={`material-symbols-outlined text-[15px] ${isRunningSpeedtest ? 'animate-spin' : ''}`}>
{isRunningSpeedtest ? 'sync' : 'speed'}
</span>
<span>
{isRunningSpeedtest
? 'Testing...'
: speedtestMode === 'client_to_server'
? 'Run Client Speedtest'
: 'Run Server Speedtest'}
</span>
</button>
</div>
{/* Running Progress Card */}
{isRunningSpeedtest && (
<div className="p-3.5 rounded-lg bg-surface-container-low border border-primary-container/30 space-y-2 text-xs font-mono">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 text-primary-fixed-dim font-sans font-medium">
<span className="material-symbols-outlined text-[17px] animate-spin">network_check</span>
<span>
{speedtestMode === 'client_to_server'
? 'Benchmarking Client ➔ MotorSignal Server'
: 'Benchmarking MotorSignal Server ➔ Upstream CDN'}
</span>
</div>
<span className="text-[11px] font-mono text-outline">{speedtestProgress.percent}%</span>
</div>
{/* Progress bar */}
<div className="w-full bg-surface-container-high rounded-full h-1.5 overflow-hidden">
<div
className="bg-primary-container h-1.5 rounded-full transition-all duration-300 ease-out"
style={{ width: `${Math.max(5, speedtestProgress.percent)}%` }}
/>
</div>
<div className="flex items-center justify-between text-[11px] text-outline pt-0.5">
<span className="truncate">{speedtestProgress.message}</span>
{speedtestProgress.currentMbps !== undefined && (
<span className="font-bold text-cyan-400 shrink-0 ml-2">
{speedtestProgress.currentMbps} Mbps
</span>
)}
</div>
</div>
)}
{/* Error banner */}
{speedtestError && (
<div className="p-3 rounded-lg bg-rose-950/40 border border-rose-800/40 text-xs text-rose-300 flex items-center gap-2">
<span className="material-symbols-outlined text-[16px]">error</span>
<span>{speedtestError}</span>
</div>
)}
{/* Result display */}
{activeSpeedtestResult && !isRunningSpeedtest && (
<div className="p-3.5 rounded-lg bg-surface-container-low border border-outline-variant/20 space-y-2.5">
<div className="flex items-center justify-between text-xs">
<div className="flex items-center gap-1.5 font-bold text-on-surface">
<span className="material-symbols-outlined text-[16px] text-emerald-400">check_circle</span>
<span>
{activeSpeedtestResult.mode === 'client_to_server'
? 'Client to Server Result (My Connection → MotorSignal)'
: 'Server to External Result (MotorSignal → Upstream Server)'}
</span>
</div>
<span className="text-[10px] font-mono text-outline">
{new Date(activeSpeedtestResult.tested_at).toLocaleTimeString()}
</span>
</div>
<div className="grid grid-cols-3 gap-2 text-center">
<div className="p-2 rounded bg-surface-container-lowest border border-outline-variant/10">
<span className="block text-[10px] text-outline font-mono">LATENCY</span>
<span className="text-sm font-bold font-mono text-emerald-400">
{activeSpeedtestResult.latency_ms !== null && activeSpeedtestResult.latency_ms !== undefined
? `${activeSpeedtestResult.latency_ms} ms`
: 'N/A'}
</span>
</div>
<div className="p-2 rounded bg-surface-container-lowest border border-outline-variant/10">
<span className="block text-[10px] text-outline font-mono">DOWNLOAD</span>
<span className="text-sm font-bold font-mono text-cyan-400">
{activeSpeedtestResult.download_mbps !== null && activeSpeedtestResult.download_mbps !== undefined
? `${activeSpeedtestResult.download_mbps} Mbps`
: 'N/A'}
</span>
</div>
<div className="p-2 rounded bg-surface-container-lowest border border-outline-variant/10">
<span className="block text-[10px] text-outline font-mono">UPLOAD</span>
<span className="text-sm font-bold font-mono text-primary-container">
{activeSpeedtestResult.upload_mbps !== null && activeSpeedtestResult.upload_mbps !== undefined
? `${activeSpeedtestResult.upload_mbps} Mbps`
: 'N/A'}
</span>
</div>
</div>
<div className="text-[10px] font-mono text-outline flex flex-col sm:flex-row sm:items-center justify-between gap-1 pt-1 border-t border-outline-variant/10">
<span>
Route: {activeSpeedtestResult.direction_label || (activeSpeedtestResult.mode === 'client_to_server' ? 'Browser ➔ MotorSignal Server' : 'MotorSignal Server ➔ Upstream CDN')}
</span>
<div className="flex items-center gap-3">
<span>Target: {activeSpeedtestResult.server_target}</span>
{activeSpeedtestResult.client_ip && <span>IP: {activeSpeedtestResult.client_ip}</span>}
</div>
</div>
</div>
)}
{!activeSpeedtestResult && !isRunningSpeedtest && (
<div className="p-3 rounded-lg bg-surface-container-low/40 border border-outline-variant/10 text-center text-xs text-outline">
No speedtest run yet for{' '}
<span className="font-semibold text-on-surface-variant">
{speedtestMode === 'client_to_server' ? 'My Connection → MotorSignal' : 'MotorSignal → Upstream Server'}
</span>
. Click above to run test.
</div>
)}
</div>
<div className="p-2.5 rounded-lg bg-surface-container-lowest/60 border border-outline-variant/10 text-[11px] text-outline flex items-center gap-2">
<span className="material-symbols-outlined text-[14px] text-primary-fixed-dim">
info
</span>
<span>
Cumulative Total Since Boot across physical network interfaces. Instantaneous bandwidth rates are not tracked.
</span>
</div>
</div>
{/* Deployment Health Foundations */}
<div className="p-5 rounded-xl bg-surface-container-low border border-outline-variant/20 space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="material-symbols-outlined text-primary-container text-xl">
deployed_code
</span>
<div>
<h3 className="text-sm font-bold text-on-surface">
Deployment Health Foundations
</h3>
<p className="text-[11px] text-outline">
Active code revision and subsystem runtime readiness
</p>
</div>
</div>
<span className="px-2 py-0.5 rounded text-[10px] font-mono bg-surface-container-high text-on-surface-variant border border-outline-variant/30">
Foundation
</span>
</div>
<div className="space-y-3 pt-1">
{/* Active Git Revision */}
<div className="p-3 rounded-lg bg-surface-container-lowest border border-outline-variant/15 flex items-center justify-between">
<div>
<div className="text-xs text-on-surface-variant">Active Git Revision</div>
<div className="flex items-center gap-2 mt-1">
<span className="font-mono text-sm font-bold text-on-surface">
{deployment?.short_revision || 'Unavailable'}
</span>
{deployment?.git_revision && (
<button
type="button"
onClick={() => handleCopySha(deployment.git_revision!)}
title="Copy full SHA"
className="p-1 rounded text-outline hover:text-on-surface hover:bg-surface-container transition-colors"
>
<span className="material-symbols-outlined text-[14px]">
{copiedSha ? 'check' : 'content_copy'}
</span>
</button>
)}
</div>
</div>
<div className="flex items-center gap-1.5 font-mono text-[11px]">
<span
className={`w-2 h-2 rounded-full ${
deployment?.revision_status === 'known' ? 'bg-emerald-400' : 'bg-slate-500'
}`}
/>
<span className="capitalize text-outline">
{deployment?.revision_status ?? 'unavailable'}
</span>
</div>
</div>
{/* Health indicators */}
<div className="grid grid-cols-2 gap-3">
<div className="p-3 rounded-lg bg-surface-container-lowest border border-outline-variant/15">
<div className="text-xs text-on-surface-variant mb-1">Website Ingress</div>
{(() => {
const badge = getComponentHealthBadge(deployment?.website_health ?? 'unknown');
return (
<div
className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-mono border ${badge.classes}`}
>
<span className="material-symbols-outlined text-[12px]">{badge.icon}</span>
<span>{badge.label}</span>
</div>
);
})()}
</div>
<div className="p-3 rounded-lg bg-surface-container-lowest border border-outline-variant/15">
<div className="text-xs text-on-surface-variant mb-1">API Readiness</div>
{(() => {
const badge = getComponentHealthBadge(deployment?.api_health ?? 'unknown');
return (
<div
className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-mono border ${badge.classes}`}
>
<span className="material-symbols-outlined text-[12px]">{badge.icon}</span>
<span>{badge.label}</span>
</div>
);
})()}
</div>
</div>
{/* Deployment History Status */}
<div className="p-2.5 rounded-lg bg-surface-container-lowest/60 border border-outline-variant/10 text-[11px] text-outline flex items-center justify-between">
<span className="font-mono">Deployment History:</span>
<span className="text-primary-fixed-dim font-mono">Audited in Phase 2</span>
</div>
</div>
</div>
</div>
{/* Operations Actions & Deployment Controls */}
<div className="mt-8 pt-6 border-t border-outline-variant/20">
<OperationsActions role={role} onActionCompleted={fetchData} />
</div>
</div>
);
}