feat: show device status and dedupe agents

This commit is contained in:
2025-08-13 01:15:14 -06:00
parent cd982bbd29
commit 0c055d8608
2 changed files with 32 additions and 3 deletions

View File

@@ -27,6 +27,14 @@ export default function DeviceDetails({ device, onBack }) {
const [softwareSearch, setSoftwareSearch] = useState(""); const [softwareSearch, setSoftwareSearch] = useState("");
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const statusFromHeartbeat = (tsSec, offlineAfter = 15) => {
if (!tsSec) return "Offline";
const now = Date.now() / 1000;
return now - tsSec <= offlineAfter ? "Online" : "Offline";
};
const statusColor = (s) => (s === "Online" ? "#00d18c" : "#ff4f4f");
useEffect(() => { useEffect(() => {
if (!device || !device.hostname) return; if (!device || !device.hostname) return;
const load = async () => { const load = async () => {
@@ -373,6 +381,7 @@ export default function DeviceDetails({ device, onBack }) {
{ label: "Storage", content: renderStorage() }, { label: "Storage", content: renderStorage() },
{ label: "Network", content: renderNetwork() } { label: "Network", content: renderNetwork() }
]; ];
const status = statusFromHeartbeat(agent.last_seen || device?.lastSeen);
return ( return (
<Paper sx={{ m: 2, p: 2, bgcolor: "#1e1e1e" }} elevation={2}> <Paper sx={{ m: 2, p: 2, bgcolor: "#1e1e1e" }} elevation={2}>
@@ -382,7 +391,20 @@ export default function DeviceDetails({ device, onBack }) {
Back Back
</Button> </Button>
)} )}
<Typography variant="h6" sx={{ color: "#58a6ff" }}> <Typography
variant="h6"
sx={{ color: "#58a6ff", display: "flex", alignItems: "center" }}
>
<span
style={{
display: "inline-block",
width: 10,
height: 10,
borderRadius: 10,
background: statusColor(status),
marginRight: 8,
}}
/>
{agent.hostname || "Device Details"} {agent.hostname || "Device Details"}
</Typography> </Typography>
</Box> </Box>

View File

@@ -640,11 +640,18 @@ def on_agent_heartbeat(data):
agent_id = data.get("agent_id") agent_id = data.get("agent_id")
if not agent_id: if not agent_id:
return return
hostname = data.get("hostname")
if hostname:
for aid, info in list(registered_agents.items()):
if aid != agent_id and info.get("hostname") == hostname:
registered_agents.pop(aid, None)
agent_configurations.pop(aid, None)
rec = registered_agents.setdefault(agent_id, {}) rec = registered_agents.setdefault(agent_id, {})
rec["agent_id"] = agent_id rec["agent_id"] = agent_id
if data.get("hostname"): if hostname:
rec["hostname"] = data.get("hostname") rec["hostname"] = hostname
if data.get("agent_operating_system"): if data.get("agent_operating_system"):
rec["agent_operating_system"] = data.get("agent_operating_system") rec["agent_operating_system"] = data.get("agent_operating_system")
rec["last_seen"] = int(data.get("last_seen") or time.time()) rec["last_seen"] = int(data.get("last_seen") or time.time())