feat: add slice calculator integration and marketplace SLA for grid nodes
This commit is contained in:
@@ -1301,25 +1301,83 @@ impl ResourceProviderService {
|
||||
// Fetch node data from grid
|
||||
let grid_data = self.grid_service.fetch_node_data(grid_node_id).await?;
|
||||
|
||||
// Create FarmNode from grid data
|
||||
let node = FarmNodeBuilder::new()
|
||||
.id(format!("grid_node_{}", grid_node_id))
|
||||
.name(format!("Grid Node {}", grid_node_id))
|
||||
.location(format!("{}, {}",
|
||||
if grid_data.city.is_empty() { "Unknown City" } else { &grid_data.city },
|
||||
if grid_data.country.is_empty() { "Unknown Country" } else { &grid_data.country }
|
||||
))
|
||||
.status(NodeStatus::Online)
|
||||
.capacity(grid_data.total_resources.clone())
|
||||
.used_capacity(grid_data.used_resources.clone())
|
||||
.uptime_percentage(99.0) // Default uptime
|
||||
.earnings_today_usd(Decimal::ZERO)
|
||||
.health_score(100.0)
|
||||
.region(if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() })
|
||||
.node_type("MyceliumNode".to_string())
|
||||
.grid_node_id(grid_node_id)
|
||||
.grid_data(grid_data)
|
||||
.build()?;
|
||||
// FIX: Calculate slice data using the slice calculator
|
||||
let total_base_slices = self.slice_calculator.calculate_max_base_slices(&grid_data.total_resources);
|
||||
|
||||
// Generate unique node ID for SLA
|
||||
let node_id = format!("grid_node_{}", grid_node_id);
|
||||
let node_id_for_sla = node_id.clone();
|
||||
|
||||
// Create FarmNode from grid data with proper slice calculations
|
||||
let mut node = FarmNode {
|
||||
id: node_id,
|
||||
name: if grid_data.farm_name.is_empty() { format!("Grid Node {}", grid_node_id) } else { grid_data.farm_name.clone() },
|
||||
location: {
|
||||
let city = if grid_data.city.is_empty() { "Unknown" } else { &grid_data.city };
|
||||
let country = if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country };
|
||||
if city == "Unknown" {
|
||||
country.to_string()
|
||||
} else {
|
||||
format!("{}, {}", city, country)
|
||||
}
|
||||
},
|
||||
status: NodeStatus::Online, // Assume online if we can fetch from grid
|
||||
capacity: grid_data.total_resources.clone(),
|
||||
used_capacity: grid_data.used_resources.clone(),
|
||||
uptime_percentage: 99.8, // Clean uptime value for grid nodes
|
||||
farming_start_date: Utc::now() - chrono::Duration::days(30), // Default farming start
|
||||
last_updated: grid_data.last_updated,
|
||||
last_seen: Some(Utc::now()),
|
||||
health_score: 98.5,
|
||||
utilization_7_day_avg: 65.0, // Default utilization
|
||||
slice_formats_supported: vec!["1x1".to_string(), "2x2".to_string(), "4x4".to_string()],
|
||||
rental_options: None,
|
||||
earnings_today_usd: Decimal::ZERO,
|
||||
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
||||
node_type: "MyceliumNode".to_string(),
|
||||
slice_formats: None, // Not used in new slice system
|
||||
staking_options: None,
|
||||
availability_status: crate::models::user::NodeAvailabilityStatus::Available,
|
||||
grid_node_id: Some(grid_node_id.to_string()),
|
||||
grid_data: Some(serde_json::to_value(&grid_data).unwrap_or_default()),
|
||||
node_group_id: None,
|
||||
group_assignment_date: None,
|
||||
group_slice_format: None,
|
||||
group_slice_price: None,
|
||||
|
||||
// FIX: Marketplace SLA field with clean uptime value
|
||||
marketplace_sla: Some(MarketplaceSLA {
|
||||
id: format!("sla-{}", node_id_for_sla),
|
||||
name: "Standard Marketplace SLA".to_string(),
|
||||
uptime_guarantee: 99.8,
|
||||
response_time_hours: 24,
|
||||
resolution_time_hours: 48,
|
||||
penalty_rate: 0.01,
|
||||
uptime_guarantee_percentage: 99.8,
|
||||
bandwidth_guarantee_mbps: grid_data.total_resources.bandwidth_mbps as f32,
|
||||
base_slice_price: SlicePricing::default().base_price_per_hour,
|
||||
last_updated: Utc::now(),
|
||||
}),
|
||||
|
||||
// FIX: Automatic slice management fields
|
||||
total_base_slices: total_base_slices as i32,
|
||||
allocated_base_slices: 0,
|
||||
slice_allocations: Vec::new(),
|
||||
available_combinations: Vec::new(), // Will be calculated below
|
||||
slice_pricing: Some(serde_json::to_value(&SlicePricing::default()).unwrap_or_default()),
|
||||
slice_last_calculated: Some(Utc::now()),
|
||||
};
|
||||
|
||||
// FIX: Generate initial slice combinations
|
||||
let combinations = self.slice_calculator.generate_slice_combinations(
|
||||
node.total_base_slices as u32,
|
||||
node.allocated_base_slices as u32,
|
||||
&node,
|
||||
user_email
|
||||
);
|
||||
node.available_combinations = combinations.iter()
|
||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||
.collect();
|
||||
|
||||
// Save to persistent storage
|
||||
let mut persistent_data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
||||
@@ -1356,26 +1414,82 @@ impl ResourceProviderService {
|
||||
}
|
||||
let grid_data = self.grid_service.fetch_node_data(grid_node_id).await.map_err(|e| e.to_string())?;
|
||||
|
||||
// Build node
|
||||
let node = FarmNodeBuilder::new()
|
||||
.id(node_id)
|
||||
.name(format!("Grid Node {}", grid_node_id))
|
||||
.location(format!("{}, {}",
|
||||
if grid_data.city.is_empty() { "Unknown City" } else { &grid_data.city },
|
||||
if grid_data.country.is_empty() { "Unknown Country" } else { &grid_data.country }
|
||||
))
|
||||
.status(NodeStatus::Online)
|
||||
.capacity(grid_data.total_resources.clone())
|
||||
.used_capacity(grid_data.used_resources.clone())
|
||||
.uptime_percentage(99.0)
|
||||
.earnings_today_usd(Decimal::ZERO)
|
||||
.health_score(100.0)
|
||||
.region(if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() })
|
||||
.node_type("MyceliumNode".to_string())
|
||||
.grid_node_id(grid_node_id)
|
||||
.grid_data(grid_data)
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
// FIX: Calculate slice data using the slice calculator (same as add_node_from_grid)
|
||||
let total_base_slices = self.slice_calculator.calculate_max_base_slices(&grid_data.total_resources);
|
||||
|
||||
// Generate unique node ID for SLA
|
||||
let node_id_for_sla = node_id.clone();
|
||||
|
||||
// Build node with proper slice calculations
|
||||
let mut node = FarmNode {
|
||||
id: node_id,
|
||||
name: if grid_data.farm_name.is_empty() { format!("Grid Node {}", grid_node_id) } else { grid_data.farm_name.clone() },
|
||||
location: {
|
||||
let city = if grid_data.city.is_empty() { "Unknown" } else { &grid_data.city };
|
||||
let country = if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country };
|
||||
if city == "Unknown" {
|
||||
country.to_string()
|
||||
} else {
|
||||
format!("{}, {}", city, country)
|
||||
}
|
||||
},
|
||||
status: NodeStatus::Online, // Assume online if we can fetch from grid
|
||||
capacity: grid_data.total_resources.clone(),
|
||||
used_capacity: grid_data.used_resources.clone(),
|
||||
uptime_percentage: 99.8, // Clean uptime value for grid nodes
|
||||
farming_start_date: Utc::now() - chrono::Duration::days(30), // Default farming start
|
||||
last_updated: grid_data.last_updated,
|
||||
last_seen: Some(Utc::now()),
|
||||
health_score: 98.5,
|
||||
utilization_7_day_avg: 65.0, // Default utilization
|
||||
slice_formats_supported: vec!["1x1".to_string(), "2x2".to_string(), "4x4".to_string()],
|
||||
rental_options: None,
|
||||
earnings_today_usd: Decimal::ZERO,
|
||||
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
||||
node_type: "MyceliumNode".to_string(),
|
||||
slice_formats: None, // Not used in new slice system
|
||||
staking_options: None,
|
||||
availability_status: crate::models::user::NodeAvailabilityStatus::Available,
|
||||
grid_node_id: Some(grid_node_id.to_string()),
|
||||
grid_data: Some(serde_json::to_value(&grid_data).unwrap_or_default()),
|
||||
node_group_id: None,
|
||||
group_assignment_date: None,
|
||||
group_slice_format: None,
|
||||
group_slice_price: None,
|
||||
|
||||
// FIX: Marketplace SLA field with clean uptime value
|
||||
marketplace_sla: Some(MarketplaceSLA {
|
||||
id: format!("sla-{}", node_id_for_sla),
|
||||
name: "Standard Marketplace SLA".to_string(),
|
||||
uptime_guarantee: 99.8,
|
||||
response_time_hours: 24,
|
||||
resolution_time_hours: 48,
|
||||
penalty_rate: 0.01,
|
||||
uptime_guarantee_percentage: 99.8,
|
||||
bandwidth_guarantee_mbps: grid_data.total_resources.bandwidth_mbps as f32,
|
||||
base_slice_price: SlicePricing::default().base_price_per_hour,
|
||||
last_updated: Utc::now(),
|
||||
}),
|
||||
|
||||
// FIX: Automatic slice management fields
|
||||
total_base_slices: total_base_slices as i32,
|
||||
allocated_base_slices: 0,
|
||||
slice_allocations: Vec::new(),
|
||||
available_combinations: Vec::new(), // Will be calculated below
|
||||
slice_pricing: Some(serde_json::to_value(&SlicePricing::default()).unwrap_or_default()),
|
||||
slice_last_calculated: Some(Utc::now()),
|
||||
};
|
||||
|
||||
// FIX: Generate initial slice combinations
|
||||
let combinations = self.slice_calculator.generate_slice_combinations(
|
||||
node.total_base_slices as u32,
|
||||
node.allocated_base_slices as u32,
|
||||
&node,
|
||||
user_email
|
||||
);
|
||||
node.available_combinations = combinations.iter()
|
||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||
.collect();
|
||||
|
||||
persistent_data.nodes.push(node.clone());
|
||||
added_nodes.push(node);
|
||||
@@ -1472,6 +1586,50 @@ impl ResourceProviderService {
|
||||
// Apply slice formats if provided
|
||||
if !slice_formats.is_empty() { node.slice_formats = Some(slice_formats.clone()); }
|
||||
|
||||
// FIX: Calculate and set slice data
|
||||
let total_base_slices = self.slice_calculator.calculate_max_base_slices(&node.capacity);
|
||||
node.total_base_slices = total_base_slices as i32;
|
||||
node.allocated_base_slices = 0;
|
||||
node.slice_allocations = Vec::new();
|
||||
|
||||
// Generate slice combinations
|
||||
let combinations = self.slice_calculator.generate_slice_combinations(
|
||||
total_base_slices,
|
||||
0, // No allocated slices yet
|
||||
&node,
|
||||
user_email
|
||||
);
|
||||
node.available_combinations = combinations.iter()
|
||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||
.collect();
|
||||
node.slice_last_calculated = Some(Utc::now());
|
||||
|
||||
// Set marketplace SLA if not already set
|
||||
if node.marketplace_sla.is_none() {
|
||||
node.marketplace_sla = Some(MarketplaceSLA {
|
||||
id: format!("sla-{}", node.id),
|
||||
name: "Standard Marketplace SLA".to_string(),
|
||||
uptime_guarantee: 99.8,
|
||||
response_time_hours: 24,
|
||||
resolution_time_hours: 48,
|
||||
penalty_rate: 0.01,
|
||||
uptime_guarantee_percentage: 99.8,
|
||||
bandwidth_guarantee_mbps: node.capacity.bandwidth_mbps as f32,
|
||||
base_slice_price: SlicePricing::default().base_price_per_hour,
|
||||
last_updated: Utc::now(),
|
||||
});
|
||||
}
|
||||
|
||||
// Set default slice pricing if not already set
|
||||
if node.slice_pricing.is_none() {
|
||||
let default_pricing = SlicePricing {
|
||||
base_price_per_hour: rust_decimal::Decimal::try_from(0.50).unwrap_or_default(),
|
||||
currency: "USD".to_string(),
|
||||
pricing_multiplier: rust_decimal::Decimal::from(1),
|
||||
};
|
||||
node.slice_pricing = Some(serde_json::to_value(&default_pricing).unwrap_or_default());
|
||||
}
|
||||
|
||||
persistent_data.nodes.push(node.clone());
|
||||
added_nodes.push(node);
|
||||
}
|
||||
@@ -1554,6 +1712,34 @@ impl ResourceProviderService {
|
||||
node.slice_formats = Some(slice_formats.clone());
|
||||
}
|
||||
|
||||
// SLICE CALCULATION FIX: Calculate and set slice data
|
||||
let total_base_slices = self.slice_calculator.calculate_max_base_slices(&node.capacity);
|
||||
node.total_base_slices = total_base_slices as i32;
|
||||
node.allocated_base_slices = 0;
|
||||
node.slice_allocations = Vec::new();
|
||||
|
||||
// Generate slice combinations
|
||||
let combinations = self.slice_calculator.generate_slice_combinations(
|
||||
total_base_slices,
|
||||
0, // No allocated slices yet
|
||||
&node,
|
||||
user_email
|
||||
);
|
||||
node.available_combinations = combinations.iter()
|
||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||
.collect();
|
||||
node.slice_last_calculated = Some(Utc::now());
|
||||
|
||||
// Set default slice pricing if not already set
|
||||
if node.slice_pricing.is_none() {
|
||||
let default_pricing = crate::services::slice_calculator::SlicePricing {
|
||||
base_price_per_hour: rust_decimal::Decimal::try_from(0.50).unwrap_or_default(),
|
||||
currency: "USD".to_string(),
|
||||
pricing_multiplier: rust_decimal::Decimal::from(1),
|
||||
};
|
||||
node.slice_pricing = Some(serde_json::to_value(&default_pricing).unwrap_or_default());
|
||||
}
|
||||
|
||||
// Push to in-memory and batch list
|
||||
persistent_data.nodes.push(node.clone());
|
||||
added_nodes.push(node);
|
||||
@@ -1654,6 +1840,34 @@ impl ResourceProviderService {
|
||||
node.slice_formats = Some(slice_formats.clone());
|
||||
}
|
||||
|
||||
// SLICE CALCULATION FIX: Calculate and set slice data for individual pricing method
|
||||
let total_base_slices = self.slice_calculator.calculate_max_base_slices(&node.capacity);
|
||||
node.total_base_slices = total_base_slices as i32;
|
||||
node.allocated_base_slices = 0;
|
||||
node.slice_allocations = Vec::new();
|
||||
|
||||
// Generate slice combinations
|
||||
let combinations = self.slice_calculator.generate_slice_combinations(
|
||||
total_base_slices,
|
||||
0, // No allocated slices yet
|
||||
&node,
|
||||
user_email
|
||||
);
|
||||
node.available_combinations = combinations.iter()
|
||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||
.collect();
|
||||
node.slice_last_calculated = Some(Utc::now());
|
||||
|
||||
// Set default slice pricing if not already set
|
||||
if node.slice_pricing.is_none() {
|
||||
let default_pricing = crate::services::slice_calculator::SlicePricing {
|
||||
base_price_per_hour: rust_decimal::Decimal::try_from(0.50).unwrap_or_default(),
|
||||
currency: "USD".to_string(),
|
||||
pricing_multiplier: rust_decimal::Decimal::from(1),
|
||||
};
|
||||
node.slice_pricing = Some(serde_json::to_value(&default_pricing).unwrap_or_default());
|
||||
}
|
||||
|
||||
persistent_data.nodes.push(node.clone());
|
||||
added_nodes.push(node);
|
||||
}
|
||||
@@ -1721,6 +1935,50 @@ impl ResourceProviderService {
|
||||
node.slice_formats = Some(slice_formats);
|
||||
}
|
||||
|
||||
// FIX: Calculate and set slice data
|
||||
let total_base_slices = self.slice_calculator.calculate_max_base_slices(&node.capacity);
|
||||
node.total_base_slices = total_base_slices as i32;
|
||||
node.allocated_base_slices = 0;
|
||||
node.slice_allocations = Vec::new();
|
||||
|
||||
// Generate slice combinations
|
||||
let combinations = self.slice_calculator.generate_slice_combinations(
|
||||
total_base_slices,
|
||||
0, // No allocated slices yet
|
||||
&node,
|
||||
user_email
|
||||
);
|
||||
node.available_combinations = combinations.iter()
|
||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||
.collect();
|
||||
node.slice_last_calculated = Some(Utc::now());
|
||||
|
||||
// Set marketplace SLA if not already set
|
||||
if node.marketplace_sla.is_none() {
|
||||
node.marketplace_sla = Some(MarketplaceSLA {
|
||||
id: format!("sla-{}", node.id),
|
||||
name: "Standard Marketplace SLA".to_string(),
|
||||
uptime_guarantee: 99.8,
|
||||
response_time_hours: 24,
|
||||
resolution_time_hours: 48,
|
||||
penalty_rate: 0.01,
|
||||
uptime_guarantee_percentage: 99.8,
|
||||
bandwidth_guarantee_mbps: node.capacity.bandwidth_mbps as f32,
|
||||
base_slice_price: SlicePricing::default().base_price_per_hour,
|
||||
last_updated: Utc::now(),
|
||||
});
|
||||
}
|
||||
|
||||
// Set default slice pricing if not already set
|
||||
if node.slice_pricing.is_none() {
|
||||
let default_pricing = SlicePricing {
|
||||
base_price_per_hour: rust_decimal::Decimal::try_from(0.50).unwrap_or_default(),
|
||||
currency: "USD".to_string(),
|
||||
pricing_multiplier: rust_decimal::Decimal::from(1),
|
||||
};
|
||||
node.slice_pricing = Some(serde_json::to_value(&default_pricing).unwrap_or_default());
|
||||
}
|
||||
|
||||
// Add node to user's data
|
||||
let mut persistent_data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
||||
|
||||
@@ -1767,41 +2025,53 @@ impl ResourceProviderService {
|
||||
None
|
||||
};
|
||||
|
||||
// Create the node
|
||||
let node = FarmNode {
|
||||
id: format!("grid_node_{}", grid_node_id),
|
||||
name: format!("Grid Node {}", grid_node_id),
|
||||
location: format!("{}, {}",
|
||||
if grid_data.city.is_empty() { "Unknown" } else { &grid_data.city },
|
||||
if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }
|
||||
),
|
||||
// FIX: Calculate slice data using the slice calculator
|
||||
let node_capacity = crate::models::user::NodeCapacity {
|
||||
cpu_cores: grid_data.total_resources.cpu_cores,
|
||||
memory_gb: grid_data.total_resources.memory_gb,
|
||||
storage_gb: grid_data.total_resources.storage_gb,
|
||||
bandwidth_mbps: grid_data.total_resources.bandwidth_mbps,
|
||||
ssd_storage_gb: grid_data.total_resources.ssd_storage_gb,
|
||||
hdd_storage_gb: grid_data.total_resources.hdd_storage_gb,
|
||||
ram_gb: grid_data.total_resources.ram_gb,
|
||||
};
|
||||
|
||||
let total_base_slices = self.slice_calculator.calculate_max_base_slices(&node_capacity);
|
||||
let node_id = format!("grid_node_{}", grid_node_id);
|
||||
let bandwidth_mbps = node_capacity.bandwidth_mbps; // Store before move
|
||||
|
||||
// Create the node with proper slice calculations
|
||||
let mut node = FarmNode {
|
||||
id: node_id.clone(),
|
||||
name: if grid_data.farm_name.is_empty() { format!("Grid Node {}", grid_node_id) } else { grid_data.farm_name.clone() },
|
||||
location: {
|
||||
let city = if grid_data.city.is_empty() { "Unknown" } else { &grid_data.city };
|
||||
let country = if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country };
|
||||
if city == "Unknown" {
|
||||
country.to_string()
|
||||
} else {
|
||||
format!("{}, {}", city, country)
|
||||
}
|
||||
},
|
||||
status: crate::models::user::NodeStatus::Online,
|
||||
capacity: crate::models::user::NodeCapacity {
|
||||
cpu_cores: grid_data.total_resources.cpu_cores,
|
||||
memory_gb: grid_data.total_resources.memory_gb,
|
||||
storage_gb: grid_data.total_resources.storage_gb,
|
||||
bandwidth_mbps: grid_data.total_resources.bandwidth_mbps,
|
||||
ssd_storage_gb: grid_data.total_resources.ssd_storage_gb,
|
||||
hdd_storage_gb: grid_data.total_resources.hdd_storage_gb,
|
||||
ram_gb: grid_data.total_resources.ram_gb,
|
||||
},
|
||||
capacity: node_capacity,
|
||||
used_capacity: crate::models::user::NodeCapacity {
|
||||
cpu_cores: 0,
|
||||
memory_gb: 0,
|
||||
storage_gb: 0,
|
||||
bandwidth_mbps: 0,
|
||||
ssd_storage_gb: 0,
|
||||
hdd_storage_gb: 0,
|
||||
ram_gb: 0,
|
||||
cpu_cores: grid_data.used_resources.cpu_cores,
|
||||
memory_gb: grid_data.used_resources.memory_gb,
|
||||
storage_gb: grid_data.used_resources.storage_gb,
|
||||
bandwidth_mbps: grid_data.used_resources.bandwidth_mbps,
|
||||
ssd_storage_gb: grid_data.used_resources.ssd_storage_gb,
|
||||
hdd_storage_gb: grid_data.used_resources.hdd_storage_gb,
|
||||
ram_gb: grid_data.used_resources.ram_gb,
|
||||
},
|
||||
uptime_percentage: 99.0,
|
||||
uptime_percentage: 99.8, // Clean uptime value for grid nodes
|
||||
farming_start_date: grid_data.last_updated,
|
||||
last_updated: grid_data.last_updated,
|
||||
utilization_7_day_avg: 0.0,
|
||||
slice_formats_supported: if slice_formats.is_empty() { Vec::new() } else { slice_formats.clone() },
|
||||
utilization_7_day_avg: 65.0, // Default utilization
|
||||
slice_formats_supported: if slice_formats.is_empty() { vec!["1x1".to_string(), "2x2".to_string(), "4x4".to_string()] } else { slice_formats.clone() },
|
||||
earnings_today_usd: Decimal::ZERO,
|
||||
last_seen: Some(Utc::now()),
|
||||
health_score: 100.0,
|
||||
health_score: 98.5,
|
||||
region: if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string(),
|
||||
node_type: "MyceliumNode".to_string(),
|
||||
slice_formats: if slice_formats.is_empty() { None } else { Some(slice_formats.clone()) },
|
||||
@@ -1815,17 +2085,40 @@ impl ResourceProviderService {
|
||||
group_slice_format: None,
|
||||
group_slice_price: None,
|
||||
|
||||
// NEW: Marketplace SLA field (None for basic grid nodes)
|
||||
marketplace_sla: None,
|
||||
// FIX: Marketplace SLA field with proper data
|
||||
marketplace_sla: Some(MarketplaceSLA {
|
||||
id: format!("sla-{}", node_id),
|
||||
name: "Standard Marketplace SLA".to_string(),
|
||||
uptime_guarantee: 99.8,
|
||||
response_time_hours: 24,
|
||||
resolution_time_hours: 48,
|
||||
penalty_rate: 0.01,
|
||||
uptime_guarantee_percentage: 99.8,
|
||||
bandwidth_guarantee_mbps: bandwidth_mbps as f32,
|
||||
base_slice_price: SlicePricing::default().base_price_per_hour,
|
||||
last_updated: Utc::now(),
|
||||
}),
|
||||
|
||||
total_base_slices: 0,
|
||||
// FIX: Automatic slice management fields with proper calculations
|
||||
total_base_slices: total_base_slices as i32,
|
||||
allocated_base_slices: 0,
|
||||
slice_allocations: Vec::new(),
|
||||
available_combinations: Vec::new(),
|
||||
slice_pricing: Some(serde_json::to_value(&crate::services::slice_calculator::SlicePricing::default()).unwrap_or_default()),
|
||||
slice_last_calculated: None,
|
||||
available_combinations: Vec::new(), // Will be calculated below
|
||||
slice_pricing: Some(serde_json::to_value(&SlicePricing::default()).unwrap_or_default()),
|
||||
slice_last_calculated: Some(Utc::now()),
|
||||
};
|
||||
|
||||
// FIX: Generate initial slice combinations
|
||||
let combinations = self.slice_calculator.generate_slice_combinations(
|
||||
node.total_base_slices as u32,
|
||||
node.allocated_base_slices as u32,
|
||||
&node,
|
||||
user_email
|
||||
);
|
||||
node.available_combinations = combinations.iter()
|
||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||
.collect();
|
||||
|
||||
// Save to persistent storage
|
||||
let mut persistent_data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
||||
|
||||
|
50
src/static/js/dashboard-layout-init.js
Normal file
50
src/static/js/dashboard-layout-init.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Dashboard Layout Initialization - CSP-compliant external script
|
||||
* Handles messaging system initialization and other dashboard-wide functionality
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Initialize messaging system with proper error handling
|
||||
*/
|
||||
function initializeMessagingSystem() {
|
||||
try {
|
||||
// Check if MessagingSystem is available
|
||||
if (typeof MessagingSystem !== 'undefined') {
|
||||
// Create global messaging system instance if it doesn't exist
|
||||
if (!window.messagingSystem) {
|
||||
console.log('🔧 Creating MessagingSystem instance for dashboard');
|
||||
window.messagingSystem = new MessagingSystem();
|
||||
} else {
|
||||
console.log('✅ MessagingSystem already initialized');
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ MessagingSystem class not available - messaging features may not work');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to initialize messaging system:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize dashboard layout components
|
||||
*/
|
||||
function initializeDashboard() {
|
||||
// Initialize messaging system
|
||||
initializeMessagingSystem();
|
||||
|
||||
// Add any other dashboard-wide initialization here
|
||||
console.log('✅ Dashboard layout initialized');
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initializeDashboard);
|
||||
} else {
|
||||
// DOM is already ready
|
||||
initializeDashboard();
|
||||
}
|
||||
|
||||
})();
|
@@ -224,15 +224,5 @@
|
||||
{% block scripts %}
|
||||
<script src="/static/js/dashboard_layout.js"></script>
|
||||
<script src="/static/js/messaging-system.js"></script>
|
||||
<script>
|
||||
// Dynamic user detection for messaging system
|
||||
try {
|
||||
// Initialize messaging system with dynamic user detection
|
||||
if (window.MessagingSystem) {
|
||||
window.MessagingSystem.initializeWithDynamicUser();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize messaging system:', error);
|
||||
}
|
||||
</script>
|
||||
<script src="/static/js/dashboard-layout-init.js"></script>
|
||||
{% endblock %}
|
@@ -303,9 +303,12 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-outline-primary" data-action="node.view" data-node-id="{{ node.id }}">
|
||||
<button class="btn btn-outline-primary" onclick="viewNodeDetails('{{ node.id }}')" title="View Details">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-danger" onclick="deleteNodeConfiguration('{{ node.id }}')" title="Delete">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
Reference in New Issue
Block a user