use crate::domain::error::DomainError;
use crate::domain::node_storage::NodeStorage;
use modkit_macros::domain_model;
use modkit_node_info::NodeInfoCollector;
use nodes_registry_sdk::{Node, NodeSysCap, NodeSysInfo, SysCap};
use std::sync::Arc;
fn is_fallback_uuid(id: &uuid::Uuid) -> bool {
let bytes = id.as_bytes();
bytes[..8] == [0u8; 8]
}
#[domain_model]
#[derive(Clone)]
pub struct Service {
storage: Arc<NodeStorage>,
node_info_collector: Arc<NodeInfoCollector>,
}
impl Service {
pub fn new() -> Self {
let node_info_collector = Arc::new(NodeInfoCollector::new());
let current_node = NodeInfoCollector::create_current_node();
let storage = Arc::new(NodeStorage::new());
let is_fallback = is_fallback_uuid(¤t_node.id);
if is_fallback {
tracing::warn!(
node_id = %current_node.id,
"Hardware UUID detection failed, using fallback UUID (00000000-0000-0000-xxxx-xxxxxxxxxxxx)"
);
} else {
tracing::info!(
node_id = %current_node.id,
hostname = %current_node.hostname,
ip_address = ?current_node.ip_address,
"Initialized node with hardware-based UUID"
);
}
storage.upsert_node(current_node);
Self {
storage,
node_info_collector,
}
}
pub fn get_node(&self, id: uuid::Uuid) -> Result<Node, DomainError> {
self.storage
.get_node(id)
.ok_or(DomainError::NodeNotFound(id))
}
#[must_use]
pub fn list_nodes(&self) -> Vec<Node> {
self.storage.list_nodes()
}
pub fn get_node_sysinfo(&self, node_id: uuid::Uuid) -> Result<NodeSysInfo, DomainError> {
if self.storage.get_node(node_id).is_none() {
return Err(DomainError::NodeNotFound(node_id));
}
if let Some(cached) = self.storage.get_sysinfo(node_id) {
return Ok(cached);
}
let sysinfo = self
.node_info_collector
.collect_sysinfo(node_id)
.map_err(DomainError::from)?;
self.storage.update_sysinfo(node_id, sysinfo.clone());
Ok(sysinfo)
}
pub fn get_node_syscap(
&self,
node_id: uuid::Uuid,
force_refresh: bool,
) -> Result<NodeSysCap, DomainError> {
if self.storage.get_node(node_id).is_none() {
return Err(DomainError::NodeNotFound(node_id));
}
let expired_keys = self.storage.get_expired_syscap_keys(node_id);
let needs_refresh =
force_refresh || !expired_keys.is_empty() || self.storage.get_syscap(node_id).is_none();
if needs_refresh {
let syscap_system = self
.node_info_collector
.collect_syscap(node_id)
.map_err(DomainError::from)?;
self.storage.update_syscap_system(node_id, syscap_system);
}
self.storage
.get_syscap(node_id)
.ok_or(DomainError::SysCapCollectionFailed(
"Failed to get syscap after refresh".to_owned(),
))
}
pub fn set_custom_syscap(
&self,
node_id: uuid::Uuid,
caps: Vec<SysCap>,
) -> Result<(), DomainError> {
if !self.storage.set_custom_syscap(node_id, caps) {
return Err(DomainError::NodeNotFound(node_id));
}
Ok(())
}
pub fn remove_custom_syscap(
&self,
node_id: uuid::Uuid,
keys: Vec<String>,
) -> Result<(), DomainError> {
if !self.storage.remove_custom_syscap(node_id, keys) {
return Err(DomainError::NodeNotFound(node_id));
}
Ok(())
}
pub fn clear_custom_syscap(&self, node_id: uuid::Uuid) -> Result<(), DomainError> {
if !self.storage.clear_custom_syscap(node_id) {
return Err(DomainError::NodeNotFound(node_id));
}
Ok(())
}
}
impl Default for Service {
fn default() -> Self {
Self::new()
}
}