chore: sincroniza alterações locais

This commit is contained in:
Esdras Renan 2025-10-09 20:38:53 -03:00
parent c3249e523d
commit c2050f311a
7 changed files with 362 additions and 26 deletions

View file

@ -61,6 +61,7 @@ name = "appsdesktop"
version = "0.1.0"
dependencies = [
"chrono",
"get_if_addrs",
"hostname",
"once_cell",
"parking_lot",
@ -372,6 +373,12 @@ dependencies = [
"serde",
]
[[package]]
name = "c_linked_list"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b"
[[package]]
name = "cairo-rs"
version = "0.18.5"
@ -801,7 +808,7 @@ dependencies = [
"dlopen2_derive",
"libc",
"once_cell",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1139,6 +1146,12 @@ dependencies = [
"byteorder",
]
[[package]]
name = "gcc"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
[[package]]
name = "gdk"
version = "0.18.2"
@ -1248,6 +1261,28 @@ dependencies = [
"version_check",
]
[[package]]
name = "get_if_addrs"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7"
dependencies = [
"c_linked_list",
"get_if_addrs-sys",
"libc",
"winapi 0.2.8",
]
[[package]]
name = "get_if_addrs-sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48"
dependencies = [
"gcc",
"libc",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@ -1321,7 +1356,7 @@ dependencies = [
"gobject-sys",
"libc",
"system-deps",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1975,7 +2010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2182,7 +2217,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -4542,7 +4577,7 @@ checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
dependencies = [
"memoffset",
"tempfile",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -4927,6 +4962,12 @@ dependencies = [
"windows-core 0.61.2",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"

View file

@ -23,7 +23,8 @@ tauri-plugin-opener = "2"
tauri-plugin-store = "2.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sysinfo = { version = "0.31", default-features = false, features = ["multithread", "network", "system"] }
sysinfo = { version = "0.31", default-features = false, features = ["multithread", "network", "system", "disk"] }
get_if_addrs = "0.5"
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
once_cell = "1.19"

View file

@ -5,6 +5,7 @@ use chrono::{DateTime, Utc};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use serde::Serialize;
use serde_json::json;
use sysinfo::{Networks, System};
use tauri::async_runtime::{self, JoinHandle};
use tokio::sync::Notify;
@ -100,6 +101,181 @@ fn collect_mac_addresses() -> Vec<String> {
macs
}
#[cfg(target_os = "linux")]
fn collect_serials_platform() -> Vec<String> {
let mut out = Vec::new();
for path in [
"/sys/class/dmi/id/product_uuid",
"/sys/class/dmi/id/product_serial",
"/sys/class/dmi/id/board_serial",
"/etc/machine-id",
] {
if let Ok(raw) = std::fs::read_to_string(path) {
let s = raw.trim().to_string();
if !s.is_empty() && !out.contains(&s) {
out.push(s);
}
}
}
out
}
#[cfg(any(target_os = "windows", target_os = "macos"))]
fn collect_serials_platform() -> Vec<String> {
// Fase 1: sem coleta nativa; será implementada via WMI/ioreg na fase 2.
Vec::new()
}
fn collect_serials() -> Vec<String> {
collect_serials_platform()
}
fn collect_network_addrs() -> Vec<serde_json::Value> {
let mut entries = Vec::new();
if let Ok(ifaces) = get_if_addrs::get_if_addrs() {
for iface in ifaces {
let name = iface.name;
let mac = iface.mac.map(|m| m.to_string());
let addr = iface.ip();
let ip = addr.to_string();
entries.push(json!({
"name": name,
"mac": mac,
"ip": ip,
}));
}
}
entries
}
fn collect_disks(system: &System) -> Vec<serde_json::Value> {
let mut out = Vec::new();
for d in system.disks() {
let name = d.name().to_string_lossy().to_string();
let mount = d.mount_point().to_string_lossy().to_string();
let fs = String::from_utf8_lossy(d.file_system()).to_string();
let total = d.total_space();
let avail = d.available_space();
out.push(json!({
"name": name,
"mountPoint": mount,
"fs": fs,
"totalBytes": total,
"availableBytes": avail,
}));
}
out
}
fn build_inventory_metadata(system: &System) -> serde_json::Value {
let cpu_brand = system
.cpus()
.first()
.map(|cpu| cpu.brand().to_string())
.unwrap_or_default();
let mem_total_bytes = system.total_memory().saturating_mul(1024);
let network = collect_network_addrs();
let disks = collect_disks(system);
let mut inventory = json!({
"cpu": { "brand": cpu_brand },
"memory": { "totalBytes": mem_total_bytes },
"network": network,
"disks": disks,
});
#[cfg(target_os = "linux")]
{
// Softwares instalados (dpkg ou rpm)
let software = collect_software_linux();
if let Some(obj) = inventory.as_object_mut() {
obj.insert("software".into(), software);
}
// Serviços ativos (systemd)
let services = collect_services_linux();
if let Some(obj) = inventory.as_object_mut() {
obj.insert("services".into(), services);
}
}
json!({ "inventory": inventory })
}
#[cfg(target_os = "linux")]
fn collect_software_linux() -> serde_json::Value {
use std::process::Command;
// Tenta dpkg-query primeiro
let dpkg = Command::new("sh")
.arg("-lc")
.arg("dpkg-query -W -f='${binary:Package}\t${Version}\n' 2>/dev/null || true")
.output();
if let Ok(out) = dpkg {
if out.status.success() {
let s = String::from_utf8_lossy(&out.stdout);
let mut items = Vec::new();
for line in s.lines() {
let mut parts = line.split('\t');
let name = parts.next().unwrap_or("").trim();
let version = parts.next().unwrap_or("").trim();
if !name.is_empty() {
items.push(json!({"name": name, "version": version, "source": "dpkg"}));
}
}
return json!(items);
}
}
// Fallback rpm
let rpm = std::process::Command::new("sh")
.arg("-lc")
.arg("rpm -qa --qf '%{NAME}\t%{VERSION}-%{RELEASE}\n' 2>/dev/null || true")
.output();
if let Ok(out) = rpm {
if out.status.success() {
let s = String::from_utf8_lossy(&out.stdout);
let mut items = Vec::new();
for line in s.lines() {
let mut parts = line.split('\t');
let name = parts.next().unwrap_or("").trim();
let version = parts.next().unwrap_or("").trim();
if !name.is_empty() {
items.push(json!({"name": name, "version": version, "source": "rpm"}));
}
}
return json!(items);
}
}
json!([])
}
#[cfg(target_os = "linux")]
fn collect_services_linux() -> serde_json::Value {
use std::process::Command;
let out = Command::new("sh")
.arg("-lc")
.arg("systemctl list-units --type=service --state=running --no-pager --no-legend 2>/dev/null || true")
.output();
if let Ok(out) = out {
if out.status.success() {
let s = String::from_utf8_lossy(&out.stdout);
let mut items = Vec::new();
for line in s.lines() {
// Typical format: UNIT LOAD ACTIVE SUB DESCRIPTION
// We take UNIT and ACTIVE
let cols: Vec<&str> = line.split_whitespace().collect();
if cols.is_empty() { continue; }
let unit = cols.get(0).unwrap_or(&"");
let active = cols.get(2).copied().unwrap_or("");
if !unit.is_empty() {
items.push(json!({"name": unit, "status": active}));
}
}
return json!(items);
}
}
json!([])
}
fn collect_system() -> System {
let mut system = System::new_all();
system.refresh_all();
@ -154,7 +330,7 @@ pub fn collect_profile() -> Result<MachineProfile, AgentError> {
let architecture = std::env::consts::ARCH.to_string();
let mac_addresses = collect_mac_addresses();
let serials: Vec<String> = Vec::new();
let serials: Vec<String> = collect_serials();
if mac_addresses.is_empty() && serials.is_empty() {
return Err(AgentError::MissingIdentifiers);
@ -216,7 +392,7 @@ async fn post_heartbeat(base_url: &str, token: &str, status: Option<String>) ->
hostname: Some(hostname),
os: Some(os),
metrics: Some(metrics),
metadata: None,
metadata: Some(build_inventory_metadata(&system)),
};
let url = format!("{}/api/machines/heartbeat", base_url);