feat: add slice calculator integration and marketplace SLA for grid nodes

This commit is contained in:
mik-tf
2025-09-08 23:47:02 -04:00
parent 138f67e02e
commit 52316319e6
7 changed files with 862 additions and 87 deletions

View File

@@ -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);

View 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();
}
})();

View File

@@ -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 %}

View File

@@ -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>

View File

@@ -150,6 +150,232 @@
"group_slice_price": null,
"last_seen": "2025-09-09T03:03:44.241206636Z",
"health_score": 100.0
},
{
"id": "grid_node_10",
"location": "Unknown, Belgium",
"status": "Online",
"capacity": {
"cpu_cores": 8,
"memory_gb": 15,
"storage_gb": 402559,
"bandwidth_mbps": 1000,
"ssd_storage_gb": 223,
"hdd_storage_gb": 402336,
"ram_gb": 15
},
"used_capacity": {
"cpu_cores": 4,
"memory_gb": 10,
"storage_gb": 137,
"bandwidth_mbps": 0,
"ssd_storage_gb": 137,
"hdd_storage_gb": 0,
"ram_gb": 10
},
"uptime_percentage": 99.0,
"farming_start_date": "2025-08-10T03:10:19.936729614Z",
"last_updated": "2025-09-09T03:10:19.936733378Z",
"utilization_7_day_avg": 65.0,
"slice_formats_supported": [
"1x1",
"2x2",
"4x4"
],
"rental_options": null,
"total_base_slices": 0,
"allocated_base_slices": 0,
"earnings_today_usd": 0.0,
"grid_node_id": "10",
"available_combinations": [],
"slice_allocations": [],
"slice_last_calculated": null,
"marketplace_sla": {
"id": "sla-repair-e3b05c2c-5eb6-4304-93c0-7a9c7348636a",
"name": "Repaired Node SLA",
"uptime_guarantee": 99.8,
"response_time_hours": 24,
"resolution_time_hours": 48,
"penalty_rate": 0.01,
"uptime_guarantee_percentage": 99.8,
"base_slice_price": 0.5,
"bandwidth_guarantee_mbps": 100.0,
"last_updated": "2025-09-09T03:13:53.570999450Z"
},
"slice_pricing": {
"base_price_per_hour": 1.0,
"currency": "USD",
"pricing_multiplier": 1.0
},
"grid_data": {
"capacity": {
"bandwidth_mbps": 1000,
"cpu_cores": 8,
"hdd_storage_gb": 402336,
"memory_gb": 15,
"ram_gb": 15,
"ssd_storage_gb": 223,
"storage_gb": 402559
},
"certification_type": "Diy",
"city": "Unknown",
"country": "Belgium",
"farm_id": 1,
"farm_name": "Freefarm",
"farming_policy_id": 1,
"grid_node_id": 10,
"last_updated": "2025-09-09T03:10:19.936676347Z",
"location": "Unknown, Belgium",
"node_id": 10,
"public_ips": [
"192.168.1.100"
],
"status": "Online",
"total_resources": {
"bandwidth_mbps": 1000,
"cpu_cores": 8,
"hdd_storage_gb": 402336,
"memory_gb": 15,
"ram_gb": 15,
"ssd_storage_gb": 223,
"storage_gb": 402559
},
"uptime": 99.5,
"used_resources": {
"bandwidth_mbps": 0,
"cpu_cores": 4,
"hdd_storage_gb": 0,
"memory_gb": 10,
"ram_gb": 10,
"ssd_storage_gb": 137,
"storage_gb": 137
}
},
"slice_formats": null,
"name": "Grid Node 10",
"region": "Belgium",
"node_type": "MyceliumNode",
"staking_options": null,
"availability_status": "Available",
"node_group_id": null,
"group_assignment_date": null,
"group_slice_format": null,
"group_slice_price": null,
"last_seen": "2025-09-09T03:10:19.936736220Z",
"health_score": 100.0
},
{
"id": "grid_node_12",
"location": "Unknown, Belgium",
"status": "Online",
"capacity": {
"cpu_cores": 8,
"memory_gb": 15,
"storage_gb": 402559,
"bandwidth_mbps": 1000,
"ssd_storage_gb": 223,
"hdd_storage_gb": 402336,
"ram_gb": 15
},
"used_capacity": {
"cpu_cores": 5,
"memory_gb": 12,
"storage_gb": 165,
"bandwidth_mbps": 0,
"ssd_storage_gb": 165,
"hdd_storage_gb": 0,
"ram_gb": 12
},
"uptime_percentage": 99.0,
"farming_start_date": "2025-08-10T03:30:24.732131670Z",
"last_updated": "2025-09-09T03:30:24.732143334Z",
"utilization_7_day_avg": 65.0,
"slice_formats_supported": [
"1x1",
"2x2",
"4x4"
],
"rental_options": null,
"total_base_slices": 0,
"allocated_base_slices": 0,
"earnings_today_usd": 0.0,
"grid_node_id": "12",
"available_combinations": [],
"slice_allocations": [],
"slice_last_calculated": null,
"marketplace_sla": {
"id": "sla-repair-25c05a40-790f-4a80-8ef8-fa12a726c0ff",
"name": "Repaired Node SLA",
"uptime_guarantee": 99.8,
"response_time_hours": 24,
"resolution_time_hours": 48,
"penalty_rate": 0.01,
"uptime_guarantee_percentage": 99.8,
"base_slice_price": 0.5,
"bandwidth_guarantee_mbps": 100.0,
"last_updated": "2025-09-09T03:30:27.416132016Z"
},
"slice_pricing": {
"base_price_per_hour": 1.0,
"currency": "USD",
"pricing_multiplier": 1.0
},
"grid_data": {
"capacity": {
"bandwidth_mbps": 1000,
"cpu_cores": 8,
"hdd_storage_gb": 402336,
"memory_gb": 15,
"ram_gb": 15,
"ssd_storage_gb": 223,
"storage_gb": 402559
},
"certification_type": "Diy",
"city": "Unknown",
"country": "Belgium",
"farm_id": 1,
"farm_name": "Freefarm",
"farming_policy_id": 1,
"grid_node_id": 12,
"last_updated": "2025-09-09T03:30:24.731928799Z",
"location": "Unknown, Belgium",
"node_id": 12,
"public_ips": [
"192.168.1.100"
],
"status": "Online",
"total_resources": {
"bandwidth_mbps": 1000,
"cpu_cores": 8,
"hdd_storage_gb": 402336,
"memory_gb": 15,
"ram_gb": 15,
"ssd_storage_gb": 223,
"storage_gb": 402559
},
"uptime": 99.5,
"used_resources": {
"bandwidth_mbps": 0,
"cpu_cores": 5,
"hdd_storage_gb": 0,
"memory_gb": 12,
"ram_gb": 12,
"ssd_storage_gb": 165,
"storage_gb": 165
}
},
"slice_formats": null,
"name": "Grid Node 12",
"region": "Belgium",
"node_type": "MyceliumNode",
"staking_options": null,
"availability_status": "Available",
"node_group_id": null,
"group_assignment_date": null,
"group_slice_format": null,
"group_slice_price": null,
"last_seen": "2025-09-09T03:30:24.732150580Z",
"health_score": 100.0
}
],
"resource_provider_earnings": [],

View File

@@ -0,0 +1,15 @@
{
"user_id": "user123@example.com",
"items": [
{
"product_id": "user_user0_example_com_fa373486",
"quantity": 1,
"selected_specifications": {},
"added_at": "2025-09-09T03:32:30.573242755Z",
"updated_at": "2025-09-09T03:32:30.573242755Z"
}
],
"session_id": null,
"created_at": "2025-09-09T03:32:30.573133861Z",
"updated_at": "2025-09-09T03:32:30.573271484Z"
}

View File

@@ -18,7 +18,121 @@
"deleted": null,
"deleted_at": null,
"deletion_reason": null,
"nodes": [],
"nodes": [
{
"id": "grid_node_2007",
"location": "Unknown, Belgium",
"status": "Online",
"capacity": {
"cpu_cores": 24,
"memory_gb": 188,
"storage_gb": 8859,
"bandwidth_mbps": 1000,
"ssd_storage_gb": 476,
"hdd_storage_gb": 8383,
"ram_gb": 188
},
"used_capacity": {
"cpu_cores": 9,
"memory_gb": 55,
"storage_gb": 343,
"bandwidth_mbps": 0,
"ssd_storage_gb": 343,
"hdd_storage_gb": 0,
"ram_gb": 55
},
"uptime_percentage": 99.0,
"farming_start_date": "2025-08-10T03:27:08.942256607Z",
"last_updated": "2025-09-09T03:27:08.942268123Z",
"utilization_7_day_avg": 65.0,
"slice_formats_supported": [
"1x1",
"2x2",
"4x4"
],
"rental_options": null,
"total_base_slices": 0,
"allocated_base_slices": 0,
"earnings_today_usd": 0.0,
"grid_node_id": "2007",
"available_combinations": [],
"slice_allocations": [],
"slice_last_calculated": null,
"marketplace_sla": {
"id": "sla-repair-8fa25e61-493a-46cc-b942-ce8cef923ae6",
"name": "Repaired Node SLA",
"uptime_guarantee": 99.8,
"response_time_hours": 24,
"resolution_time_hours": 48,
"penalty_rate": 0.01,
"uptime_guarantee_percentage": 99.8,
"base_slice_price": 0.5,
"bandwidth_guarantee_mbps": 100.0,
"last_updated": "2025-09-09T03:27:12.489433171Z"
},
"slice_pricing": {
"base_price_per_hour": 1.0,
"currency": "USD",
"pricing_multiplier": 1.0
},
"grid_data": {
"capacity": {
"bandwidth_mbps": 1000,
"cpu_cores": 24,
"hdd_storage_gb": 8383,
"memory_gb": 188,
"ram_gb": 188,
"ssd_storage_gb": 476,
"storage_gb": 8859
},
"certification_type": "Diy",
"city": "Unknown",
"country": "Belgium",
"farm_id": 1,
"farm_name": "Freefarm",
"farming_policy_id": 1,
"grid_node_id": 2007,
"last_updated": "2025-09-09T03:27:08.942100965Z",
"location": "Unknown, Belgium",
"node_id": 2007,
"public_ips": [
"192.168.1.100"
],
"status": "Online",
"total_resources": {
"bandwidth_mbps": 1000,
"cpu_cores": 24,
"hdd_storage_gb": 8383,
"memory_gb": 188,
"ram_gb": 188,
"ssd_storage_gb": 476,
"storage_gb": 8859
},
"uptime": 99.5,
"used_resources": {
"bandwidth_mbps": 0,
"cpu_cores": 9,
"hdd_storage_gb": 0,
"memory_gb": 55,
"ram_gb": 55,
"ssd_storage_gb": 343,
"storage_gb": 343
}
},
"slice_formats": null,
"name": "Grid Node 2007",
"region": "Belgium",
"node_type": "MyceliumNode",
"staking_options": null,
"availability_status": "Available",
"node_group_id": null,
"group_assignment_date": null,
"group_slice_format": null,
"group_slice_price": null,
"last_seen": "2025-09-09T03:27:08.942273898Z",
"health_score": 100.0
}
],
"resource_provider_earnings": [],
"resource_provider_settings": null,
"slice_products": [],
@@ -29,7 +143,91 @@
"active_product_rentals": [],
"resource_provider_rental_earnings": [],
"node_rentals": [],
"node_groups": [],
"node_groups": [
{
"id": "compute",
"name": "Compute",
"description": "General compute workloads",
"node_ids": [],
"group_type": {
"Default": "compute"
},
"updated_at": "2025-09-09T03:26:57.639840151Z",
"created_at": "2025-09-09T03:26:57.639836172Z",
"group_config": {
"group_name": "Compute Nodes",
"max_nodes": 100,
"allocation_strategy": "balanced",
"auto_scaling": true,
"preferred_slice_formats": [
"1x1",
"2x2"
],
"default_pricing": 50.0,
"resource_optimization": {
"cpu_optimization": 0.5,
"memory_optimization": 0.5,
"storage_optimization": 0.5,
"network_optimization": 0.5
}
}
},
{
"id": "storage",
"name": "Storage",
"description": "Storage and data workloads",
"node_ids": [],
"group_type": {
"Default": "storage"
},
"updated_at": "2025-09-09T03:26:57.639846621Z",
"created_at": "2025-09-09T03:26:57.639846284Z",
"group_config": {
"group_name": "Storage Nodes",
"max_nodes": 50,
"allocation_strategy": "storage_optimized",
"auto_scaling": false,
"preferred_slice_formats": [
"1x1"
],
"default_pricing": 30.0,
"resource_optimization": {
"cpu_optimization": 0.3,
"memory_optimization": 0.3,
"storage_optimization": 0.8,
"network_optimization": 0.6
}
}
},
{
"id": "ai-gpu",
"name": "AI/GPU",
"description": "AI and GPU-intensive workloads",
"node_ids": [],
"group_type": {
"Default": "ai-gpu"
},
"updated_at": "2025-09-09T03:26:57.639850994Z",
"created_at": "2025-09-09T03:26:57.639850646Z",
"group_config": {
"group_name": "AI/GPU Nodes",
"max_nodes": 20,
"allocation_strategy": "gpu_optimized",
"auto_scaling": true,
"preferred_slice_formats": [
"4x4",
"8x8"
],
"default_pricing": 200.0,
"resource_optimization": {
"cpu_optimization": 0.8,
"memory_optimization": 0.7,
"storage_optimization": 0.4,
"network_optimization": 0.5
}
}
}
],
"slice_rentals": [],
"slice_assignments": [],
"display_currency": "MC",