mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-09-11 01:48:42 -06:00
Load device records on startup and improve storage display
This commit is contained in:
@@ -283,25 +283,40 @@ export default function DeviceDetails({ device, onBack }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderStorage = () => {
|
const renderStorage = () => {
|
||||||
const parseNum = (val) => {
|
const toNum = (val) => {
|
||||||
if (val === undefined || val === null) return undefined;
|
if (val === undefined || val === null) return undefined;
|
||||||
const n = Number(String(val).replace(/[^0-9.]/g, ""));
|
const n = Number(val);
|
||||||
return Number.isNaN(n) ? undefined : n;
|
return Number.isNaN(n) ? undefined : n;
|
||||||
};
|
};
|
||||||
|
|
||||||
const rows = (details.storage || []).map((d) => {
|
const rows = (details.storage || []).map((d) => {
|
||||||
const total = parseNum(d.total);
|
const total = toNum(d.total);
|
||||||
const rawFree = parseNum(d.free);
|
let usage = toNum(d.usage);
|
||||||
const freePct = rawFree !== undefined
|
let freePct;
|
||||||
? rawFree <= 100
|
|
||||||
? rawFree
|
if (usage !== undefined) {
|
||||||
: total
|
if (usage <= 1) usage *= 100;
|
||||||
? (rawFree / total) * 100
|
freePct = 100 - usage;
|
||||||
: undefined
|
} else {
|
||||||
: undefined;
|
const freeRaw = toNum(d.free);
|
||||||
let usage = parseNum(d.usage);
|
if (freeRaw !== undefined) {
|
||||||
if ((usage === undefined || Number.isNaN(usage)) && freePct !== undefined) {
|
if (freeRaw > 1 && freeRaw > 100 && total) {
|
||||||
usage = 100 - freePct;
|
freePct = (freeRaw / total) * 100;
|
||||||
|
} else if (freeRaw <= 1) {
|
||||||
|
freePct = freeRaw * 100;
|
||||||
|
} else {
|
||||||
|
freePct = freeRaw;
|
||||||
|
}
|
||||||
|
usage = freePct !== undefined ? 100 - freePct : undefined;
|
||||||
|
} else {
|
||||||
|
const usedRaw = toNum(d.used);
|
||||||
|
if (usedRaw !== undefined && total) {
|
||||||
|
usage = (usedRaw / total) * 100;
|
||||||
|
freePct = 100 - usage;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
drive: d.drive,
|
drive: d.drive,
|
||||||
disk_type: d.disk_type,
|
disk_type: d.disk_type,
|
||||||
@@ -310,8 +325,10 @@ export default function DeviceDetails({ device, onBack }) {
|
|||||||
free: freePct,
|
free: freePct,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!rows.length)
|
if (!rows.length)
|
||||||
return placeholderTable(["Drive Letter", "Disk Type", "Usage", "Total Size", "Free %"]);
|
return placeholderTable(["Drive Letter", "Disk Type", "Usage", "Total Size", "Free %"]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ maxHeight: 400, overflowY: "auto" }}>
|
<Box sx={{ maxHeight: 400, overflowY: "auto" }}>
|
||||||
<Table size="small">
|
<Table size="small">
|
||||||
|
@@ -165,12 +165,7 @@ export default function DeviceList({ onSelectDevice }) {
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{sorted.map((r, i) => (
|
{sorted.map((r, i) => (
|
||||||
<TableRow
|
<TableRow key={r.id || i} hover>
|
||||||
key={r.id || i}
|
|
||||||
hover
|
|
||||||
onClick={() => onSelectDevice && onSelectDevice(r)}
|
|
||||||
sx={{ cursor: onSelectDevice ? "pointer" : "default" }}
|
|
||||||
>
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
<Box
|
<Box
|
||||||
@@ -187,13 +182,21 @@ export default function DeviceList({ onSelectDevice }) {
|
|||||||
{r.status}
|
{r.status}
|
||||||
</Box>
|
</Box>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{r.hostname}</TableCell>
|
<TableCell
|
||||||
|
onClick={() => onSelectDevice && onSelectDevice(r)}
|
||||||
|
sx={{ cursor: onSelectDevice ? "pointer" : "default" }}
|
||||||
|
>
|
||||||
|
{r.hostname}
|
||||||
|
</TableCell>
|
||||||
<TableCell>{timeSince(r.lastSeen)}</TableCell>
|
<TableCell>{timeSince(r.lastSeen)}</TableCell>
|
||||||
<TableCell>{r.os}</TableCell>
|
<TableCell>{r.os}</TableCell>
|
||||||
<TableCell align="right">
|
<TableCell align="right">
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={(e) => openMenu(e, r)}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openMenu(e, r);
|
||||||
|
}}
|
||||||
sx={{ color: "#ccc" }}
|
sx={{ color: "#ccc" }}
|
||||||
>
|
>
|
||||||
<MoreVertIcon fontSize="small" />
|
<MoreVertIcon fontSize="small" />
|
||||||
|
@@ -398,6 +398,36 @@ def init_db():
|
|||||||
|
|
||||||
init_db()
|
init_db()
|
||||||
|
|
||||||
|
|
||||||
|
def load_agents_from_db():
|
||||||
|
"""Populate registered_agents with any devices stored in the database."""
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("SELECT hostname, details FROM device_details")
|
||||||
|
for hostname, details_json in cur.fetchall():
|
||||||
|
try:
|
||||||
|
details = json.loads(details_json or "{}")
|
||||||
|
except Exception:
|
||||||
|
details = {}
|
||||||
|
summary = details.get("summary", {})
|
||||||
|
agent_id = summary.get("agent_id") or hostname
|
||||||
|
registered_agents[agent_id] = {
|
||||||
|
"agent_id": agent_id,
|
||||||
|
"hostname": summary.get("hostname") or hostname,
|
||||||
|
"agent_operating_system": summary.get("operating_system")
|
||||||
|
or summary.get("agent_operating_system")
|
||||||
|
or "-",
|
||||||
|
"last_seen": summary.get("last_seen") or 0,
|
||||||
|
"status": "Offline",
|
||||||
|
}
|
||||||
|
conn.close()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WARN] Failed to load agents from DB: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
load_agents_from_db()
|
||||||
|
|
||||||
@app.route("/api/agents")
|
@app.route("/api/agents")
|
||||||
def get_agents():
|
def get_agents():
|
||||||
"""
|
"""
|
||||||
@@ -481,10 +511,20 @@ def set_device_description(hostname: str):
|
|||||||
|
|
||||||
@app.route("/api/agent/<agent_id>", methods=["DELETE"])
|
@app.route("/api/agent/<agent_id>", methods=["DELETE"])
|
||||||
def delete_agent(agent_id: str):
|
def delete_agent(agent_id: str):
|
||||||
"""Remove an agent from the in-memory registry."""
|
"""Remove an agent from the registry and database."""
|
||||||
if agent_id in registered_agents:
|
info = registered_agents.pop(agent_id, None)
|
||||||
registered_agents.pop(agent_id, None)
|
agent_configurations.pop(agent_id, None)
|
||||||
agent_configurations.pop(agent_id, None)
|
hostname = info.get("hostname") if info else None
|
||||||
|
if hostname:
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("DELETE FROM device_details WHERE hostname = ?", (hostname,))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
if info:
|
||||||
return jsonify({"status": "removed"})
|
return jsonify({"status": "removed"})
|
||||||
return jsonify({"error": "agent not found"}), 404
|
return jsonify({"error": "agent not found"}), 404
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user