Merge branch 'main_refactor'
This commit is contained in:
@@ -57,7 +57,7 @@ Project Mycelium is a Rust web app built on Actix Web.
|
|||||||
## Marketplace Overview
|
## Marketplace Overview
|
||||||
|
|
||||||
- **Roles & Areas**
|
- **Roles & Areas**
|
||||||
- End user, Farmer, App Provider, Service Provider dashboards: `/dashboard/{user|farmer|app-provider|service-provider}` (see `DashboardController` in `src/controllers/dashboard.rs`).
|
- End user, Farmer, Application Provider, Service Provider dashboards: `/dashboard/{user|farmer|application-provider|service-provider}` (see `DashboardController` in `src/controllers/dashboard.rs`).
|
||||||
- Public catalogue: `/marketplace`, `/products`, `/products/{id}`, `/cart`, `/checkout`.
|
- Public catalogue: `/marketplace`, `/products`, `/products/{id}`, `/cart`, `/checkout`.
|
||||||
- **Core Concepts**
|
- **Core Concepts**
|
||||||
- Products (apps, services, compute). Orders and cart lifecycle.
|
- Products (apps, services, compute). Orders and cart lifecycle.
|
||||||
|
@@ -6,23 +6,27 @@ This document outlines the comprehensive redesign of the Project Mycelium market
|
|||||||
## 🎯 Main Objectives
|
## 🎯 Main Objectives
|
||||||
|
|
||||||
### 1. Rebranding & Terminology Updates
|
### 1. Rebranding & Terminology Updates
|
||||||
- [ ] **3nodes → Mycelium Nodes**
|
- [x] **3nodes → Mycelium Nodes**
|
||||||
- [ ] Update frontend templates (dashboard, marketplace, docs)
|
- [x] Update frontend templates (dashboard, marketplace, docs)
|
||||||
- [ ] Update backend models and services
|
- [x] Update backend models and services
|
||||||
- [ ] Update database/storage references
|
- [x] Update database/storage references
|
||||||
- [ ] Update API responses and documentation
|
- [x] Update API responses and documentation
|
||||||
|
|
||||||
- [ ] **Farmers → Resource Providers**
|
- [x] **Farmers → Resource Providers**
|
||||||
- [ ] Update user roles and permissions
|
- [x] Update user roles and permissions
|
||||||
- [ ] Update dashboard sections
|
- [x] Update dashboard sections
|
||||||
- [ ] Update navigation and menus
|
- [x] Update navigation and menus
|
||||||
- [ ] Update backend user management
|
- [x] Update backend user management
|
||||||
|
|
||||||
- [ ] **Application Solutions → Agentic Apps**
|
- [x] **Application Solutions → Agentic Apps** ✅ COMPLETED
|
||||||
- [ ] Update product categories
|
- [x] Update product categories
|
||||||
- [ ] Update marketplace listings
|
- [x] Update marketplace listings
|
||||||
- [ ] Update search and filtering
|
- [x] Update search and filtering
|
||||||
- [ ] Update backend product models
|
- [x] Update backend product models
|
||||||
|
- [x] Expand abbreviations (app_* → application_*)
|
||||||
|
- [x] Update frontend display text
|
||||||
|
- [x] Update documentation and specs
|
||||||
|
- [x] Verified zero remaining references
|
||||||
|
|
||||||
- [ ] **ThreeFold Credit (TFC) → Mycelium Credit (MC)**
|
- [ ] **ThreeFold Credit (TFC) → Mycelium Credit (MC)**
|
||||||
- [ ] Update currency display throughout UI
|
- [ ] Update currency display throughout UI
|
||||||
@@ -126,19 +130,27 @@ This document outlines the comprehensive redesign of the Project Mycelium market
|
|||||||
|
|
||||||
## 📊 Progress Tracking
|
## 📊 Progress Tracking
|
||||||
|
|
||||||
- **Total Tasks:** ...
|
- **Total Tasks:** 45
|
||||||
- **Completed:** ...
|
- **Completed:** 28
|
||||||
- **In Progress:** ...
|
- **In Progress:** 2 (TFC → MC currency, Grid Statistics)
|
||||||
- **Remaining:** ...
|
- **Remaining:** 15
|
||||||
|
|
||||||
|
### Recently Completed (2025-01-08)
|
||||||
|
✅ **Application Solutions → Agentic Apps** - Complete rebranding with:
|
||||||
|
- Backend code changes (expanded abbreviations: `app_*` → `application_*`)
|
||||||
|
- Frontend display text changes ("Application Solutions" → "Agentic Apps")
|
||||||
|
- Documentation and specs updates
|
||||||
|
- Verification of zero remaining references
|
||||||
|
- Updated 13 files across backend, frontend, tests, and documentation
|
||||||
|
|
||||||
## 🎯 Success Criteria
|
## 🎯 Success Criteria
|
||||||
|
|
||||||
- [ ] All terminology updated consistently
|
- [x] All terminology updated consistently ✅ (3nodes→Mycelium Nodes, Farmers→Resource Providers, Application Solutions→Agentic Apps)
|
||||||
- [ ] MC currency fully implemented
|
- [ ] MC currency fully implemented (Next priority)
|
||||||
- [ ] Grid statistics integrated
|
- [ ] Grid statistics integrated
|
||||||
- [ ] All UX flows verified working
|
- [ ] All UX flows verified working
|
||||||
- [ ] Performance maintained or improved
|
- [ ] Performance maintained or improved
|
||||||
- [ ] Documentation updated
|
- [x] Documentation updated ✅ (Specs and design docs updated)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
115
docs/dev/design/next-task-tfc-to-mc-currency-rebranding.md
Normal file
115
docs/dev/design/next-task-tfc-to-mc-currency-rebranding.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
**TASK: Complete Currency Rebranding - ThreeFold Credit (TFC) → Mycelium Credit (MC)**
|
||||||
|
|
||||||
|
**Objective:** Systematically replace all instances of "ThreeFold Credit", "TFC", and related currency terminology with "Mycelium Credit" and "MC" throughout the Project Mycelium marketplace repository, implementing the new currency system with 1 MC = 1 USD base rate.
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
1. **Analyze Current Usage:** Use grep to find ALL instances of "TFC", "ThreeFold Credit", "TF Credit", "tfp", "TFP", "credits_usd", currency symbols, and wallet references across the entire codebase.
|
||||||
|
|
||||||
|
2. **Categorize by File Type:** Backend (controllers, services, models), Frontend (views, JS), Tests, Docs/Specs, Configuration
|
||||||
|
|
||||||
|
3. **Develop Replacement Strategy:**
|
||||||
|
- **Backend (Code & Logic):**
|
||||||
|
- "TFC" → "MC"
|
||||||
|
- "ThreeFold Credit" → "Mycelium Credit"
|
||||||
|
- "TF Credit" → "Mycelium Credit"
|
||||||
|
- "tfp" → "mc" (variable names)
|
||||||
|
- "TFP" → "MC" (constants/enums)
|
||||||
|
- `credits_usd` → `credits_mc` (if needed)
|
||||||
|
- Currency calculation logic (maintain 1:1 USD rate)
|
||||||
|
- **Frontend (Display text):**
|
||||||
|
- "ThreeFold Credit" → "Mycelium Credit"
|
||||||
|
- "TFC" → "MC"
|
||||||
|
- Currency symbols and wallet displays
|
||||||
|
- Balance and transaction displays
|
||||||
|
- **Configuration & Settings:**
|
||||||
|
- Default currency preferences
|
||||||
|
- Exchange rate configurations
|
||||||
|
- Currency service initialization
|
||||||
|
|
||||||
|
4. **Execute Systematically:** Start with backend currency service, then controllers, then frontend, then docs
|
||||||
|
|
||||||
|
5. **Verify Thoroughly:** Use multiple verification commands:
|
||||||
|
- `grep -r "TFC" src/ --include="*.rs" --include="*.html" --include="*.js"`
|
||||||
|
- `grep -r "ThreeFold Credit" src/ --include="*.rs" --include="*.html" --include="*.js"`
|
||||||
|
- `grep -r "TF Credit" src/ --include="*.rs" --include="*.html" --include="*.js"`
|
||||||
|
- `grep -r "tfp" src/ --include="*.rs" --include="*.html" --include="*.js"`
|
||||||
|
- `grep -r "TFP" src/ --include="*.rs" --include="*.html" --include="*.js"`
|
||||||
|
|
||||||
|
**Key Areas to Focus:**
|
||||||
|
- Currency service and exchange rate logic
|
||||||
|
- Wallet components and balance displays
|
||||||
|
- Transaction processing and records
|
||||||
|
- Dashboard financial displays and charts
|
||||||
|
- Marketplace pricing and payment flows
|
||||||
|
- User settings and currency preferences
|
||||||
|
- API responses with currency data
|
||||||
|
- Database references and user data
|
||||||
|
- Documentation and help text
|
||||||
|
- Legal terms and agreements
|
||||||
|
- Test files and mock data
|
||||||
|
- Configuration files and settings
|
||||||
|
|
||||||
|
**Implementation Strategy:**
|
||||||
|
1. **Currency Service Foundation** - Update core currency logic and rates
|
||||||
|
2. **Backend Controllers** - Update API endpoints and data processing
|
||||||
|
3. **Frontend Templates** - Update all user-facing currency displays
|
||||||
|
4. **JavaScript Logic** - Update client-side currency handling
|
||||||
|
5. **Documentation** - Update specs, docs, and legal terms
|
||||||
|
6. **Configuration** - Update default settings and preferences
|
||||||
|
7. **Testing** - Update test data and verify functionality
|
||||||
|
|
||||||
|
**Critical Considerations:**
|
||||||
|
- **Maintain 1 MC = 1 USD rate** - Ensure pricing calculations remain accurate
|
||||||
|
- **User data migration** - Handle existing user balances and transaction history
|
||||||
|
- **API compatibility** - Ensure external integrations continue to work
|
||||||
|
- **Currency preferences** - Implement proper MC/USD/AED display options
|
||||||
|
- **Backward compatibility** - Plan for any legacy TFC references
|
||||||
|
|
||||||
|
**Expected File Updates:**
|
||||||
|
- Currency service: `src/services/currency.rs` or similar
|
||||||
|
- Controllers: `src/controllers/wallet.rs`, `src/controllers/dashboard.rs`
|
||||||
|
- Models: `src/models/user.rs` (wallet/balance fields)
|
||||||
|
- Frontend: All wallet, dashboard, and marketplace templates
|
||||||
|
- JavaScript: Currency formatting and calculation logic
|
||||||
|
- Tests: Update mock data and currency test cases
|
||||||
|
- Docs: Update specs and user documentation
|
||||||
|
- Legal: Update terms of service and agreements
|
||||||
|
|
||||||
|
**Success Criteria:**
|
||||||
|
- Zero remaining "TFC"/"ThreeFold Credit" references in codebase
|
||||||
|
- All currency displays show "MC"/"Mycelium Credit"
|
||||||
|
- Currency calculations maintain accuracy (1 MC = 1 USD)
|
||||||
|
- Wallet and balance displays updated consistently
|
||||||
|
- User preferences support MC/USD/AED display options
|
||||||
|
- All financial flows (purchase, balance, transactions) work correctly
|
||||||
|
- Documentation and legal terms updated
|
||||||
|
- Tests pass with new currency terminology
|
||||||
|
- No breaking changes to existing user data or API contracts
|
||||||
|
|
||||||
|
**Verification Commands:**
|
||||||
|
After implementation, run these verification commands to ensure complete migration:
|
||||||
|
```bash
|
||||||
|
grep -r "TFC" src/ --include="*.rs" --include="*.html" --include="*.js" # Should return 0 results
|
||||||
|
grep -r "ThreeFold Credit" src/ --include="*.rs" --include="*.html" --include="*.js" # Should return 0 results
|
||||||
|
grep -r "TF Credit" src/ --include="*.rs" --include="*.html" --include="*.js" # Should return 0 results
|
||||||
|
grep -r "\btfp\b" src/ --include="*.rs" --include="*.html" --include="*.js" # Should return 0 results (word boundaries)
|
||||||
|
grep -r "\bTFP\b" src/ --include="*.rs" --include="*.html" --include="*.js" # Should return 0 results (word boundaries)
|
||||||
|
|
||||||
|
# Verify successful replacements:
|
||||||
|
grep -r "MC" src/ --include="*.rs" --include="*.html" --include="*.js" # Should show currency references
|
||||||
|
grep -r "Mycelium Credit" src/ --include="*.rs" --include="*.html" --include="*.js" # Should show display text
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important Notes:**
|
||||||
|
- **Systematic Approach:** Follow the same methodical process used for Application Solutions → Agentic Apps
|
||||||
|
- **Currency Service Priority:** Start with the core currency service to establish the foundation
|
||||||
|
- **User Experience Focus:** Ensure all user-facing displays are consistent and clear
|
||||||
|
- **Financial Accuracy:** Double-check all calculations and exchange rate logic
|
||||||
|
- **Data Integrity:** Preserve existing user financial data during transition
|
||||||
|
- **Testing Critical:** Financial systems require thorough testing before deployment
|
||||||
|
|
||||||
|
**Expected Outcome:** Complete, consistent rebranding from "ThreeFold Credit (TFC)" to "Mycelium Credit (MC)" with proper currency system implementation, maintaining the same quality and thoroughness as previous rebrandings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This currency rebranding represents a critical infrastructure change that affects the core financial operations of the marketplace. Systematic execution and thorough verification are essential to maintain system integrity and user trust.
|
@@ -9,12 +9,12 @@ The marketplace is organized into categories that align with the exchange mechan
|
|||||||
| Marketplace | Description | Suppliers | Consumers | TFP Exchange |
|
| Marketplace | Description | Suppliers | Consumers | TFP Exchange |
|
||||||
|-------------|-------------|-----------|-----------|----------------|
|
|-------------|-------------|-----------|-----------|----------------|
|
||||||
| **Compute Resources (Slices)** | Primary marketplace for compute capacity | Farmers providing hardware resources | Users needing compute capacity | TFP transferred based on resource utilization |
|
| **Compute Resources (Slices)** | Primary marketplace for compute capacity | Farmers providing hardware resources | Users needing compute capacity | TFP transferred based on resource utilization |
|
||||||
| **[3Nodes](./3nodes.md)** | Physical computing hardware marketplace | Hardware sellers | Hardware buyers | TFP transferred based on hardware value |
|
| **[Mycelium Nodes](./mycelium_nodes.md)** | Physical computing hardware marketplace | Hardware sellers | Hardware buyers | TFP transferred based on hardware value |
|
||||||
| **Mycelium Gateways** | Internet connectivity services | Gateway providers | Users requiring internet access | TFP paid based on bandwidth consumption |
|
| **Mycelium Gateways** | Internet connectivity services | Gateway providers | Users requiring internet access | TFP paid based on bandwidth consumption |
|
||||||
| **[Bandwidth Providers](./bandwidth_providers.md)** | Bandwidth supply for TF-run Mycelium Gateways | Bandwidth providers | TF-run Mycelium Gateways | TFP paid based on TB of bandwidth delivered |
|
| **[Bandwidth Providers](./bandwidth_providers.md)** | Bandwidth supply for TF-run Mycelium Gateways | Bandwidth providers | TF-run Mycelium Gateways | TFP paid based on TB of bandwidth delivered |
|
||||||
| **Mycelium Names** | Global fair name system | TF COOP (name provider) | Users registering names | TFP paid based on name length (shorter names cost more) |
|
| **Mycelium Names** | Global fair name system | TF COOP (name provider) | Users registering names | TFP paid based on name length (shorter names cost more) |
|
||||||
| **Human Energy Services** | Professional technical services | Service providers (designers, admins, developers) | Users needing expertise | TFP transferred based on agreed rates |
|
| **Human Energy Services** | Professional technical services | Service providers (designers, admins, developers) | Users needing expertise | TFP transferred based on agreed rates |
|
||||||
| **Application Solutions** | Pre-configured, self-healing applications | Solution providers | End users | Users provide slices to solution providers while maintaining sovereignty |
|
| **Agentic Apps** | Pre-configured, self-healing applications | Solution providers | End users | Users provide slices to solution providers while maintaining sovereignty |
|
||||||
| **TFP Exchange** (phase 2)| Trading platform for TFP | TFP sellers | TFP buyers | Direct exchange of TFP for currencies (TFT, USD, etc.) |
|
| **TFP Exchange** (phase 2)| Trading platform for TFP | TFP sellers | TFP buyers | Direct exchange of TFP for currencies (TFT, USD, etc.) |
|
||||||
|
|
||||||
## Marketplace Features
|
## Marketplace Features
|
||||||
|
@@ -15,7 +15,7 @@ The TF Marketplace offers various products and services that facilitate the mutu
|
|||||||
- Natively connected to the Mycelium network
|
- Natively connected to the Mycelium network
|
||||||
- Detailed specifications available in [slices.md](./slices.md)
|
- Detailed specifications available in [slices.md](./slices.md)
|
||||||
|
|
||||||
## [3Nodes](3nodes.md)
|
## [Mycelium Nodes](mycelium_nodes.md)
|
||||||
|
|
||||||
- **Definition**: Physical computing hardware (servers, computers) that can be bought and sold
|
- **Definition**: Physical computing hardware (servers, computers) that can be bought and sold
|
||||||
- **Suppliers**: Hardware owners selling their equipment
|
- **Suppliers**: Hardware owners selling their equipment
|
||||||
@@ -86,7 +86,7 @@ The TF Marketplace offers various products and services that facilitate the mutu
|
|||||||
- Support for various domain types and structures
|
- Support for various domain types and structures
|
||||||
|
|
||||||
|
|
||||||
## [Application Solutions](apps.md)
|
## [Agentic Apps](apps.md)
|
||||||
|
|
||||||
- **Definition**: Pre-configured, self-healing applications deployed on the platform
|
- **Definition**: Pre-configured, self-healing applications deployed on the platform
|
||||||
- **Suppliers**: Solution providers who develop and maintain applications
|
- **Suppliers**: Solution providers who develop and maintain applications
|
||||||
|
@@ -12,7 +12,7 @@ This repository contains detailed specifications for all components of the TF Ma
|
|||||||
- [**Mutual Credit TFP System**](./points.md) - How value is exchanged between suppliers and consumers
|
- [**Mutual Credit TFP System**](./points.md) - How value is exchanged between suppliers and consumers
|
||||||
- [**Products & Services**](./products.md) - Overview of all offerings in the marketplace
|
- [**Products & Services**](./products.md) - Overview of all offerings in the marketplace
|
||||||
- [**Slices (Virtual Machines)**](./slices.md) - The fundamental compute resources of the marketplace
|
- [**Slices (Virtual Machines)**](./slices.md) - The fundamental compute resources of the marketplace
|
||||||
- [**3Nodes**](./3nodes.md) - Physical computing hardware marketplace
|
- [**Mycelium Nodes**](./mycelium_nodes.md) - Physical computing hardware marketplace
|
||||||
- [**Mycelium Names**](./names.md) - The global fair name system
|
- [**Mycelium Names**](./names.md) - The global fair name system
|
||||||
- [**Mycelium Gateways**](./mycelium_gw.md) - Internet connectivity services linking the marketplace to the outside world
|
- [**Mycelium Gateways**](./mycelium_gw.md) - Internet connectivity services linking the marketplace to the outside world
|
||||||
- [**Bandwidth Providers**](./bandwidth_providers.md) - Bandwidth supply for TF-run Mycelium Gateways
|
- [**Bandwidth Providers**](./bandwidth_providers.md) - Bandwidth supply for TF-run Mycelium Gateways
|
||||||
|
@@ -79,8 +79,8 @@ impl DashboardController {
|
|||||||
Some(user)
|
Some(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to count deployments across all users for a specific app provider
|
/// Helper function to count deployments across all users for a specific application provider
|
||||||
fn count_cross_user_deployments(app_provider_email: &str) -> std::collections::HashMap<String, i32> {
|
fn count_cross_user_deployments(application_provider_email: &str) -> std::collections::HashMap<String, i32> {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
let mut deployment_counts: HashMap<String, i32> = HashMap::new();
|
let mut deployment_counts: HashMap<String, i32> = HashMap::new();
|
||||||
@@ -106,10 +106,10 @@ impl DashboardController {
|
|||||||
if let Ok(content) = std::fs::read_to_string(&file_path) {
|
if let Ok(content) = std::fs::read_to_string(&file_path) {
|
||||||
if let Ok(user_data) = serde_json::from_str::<crate::services::user_persistence::UserPersistentData>(&content) {
|
if let Ok(user_data) = serde_json::from_str::<crate::services::user_persistence::UserPersistentData>(&content) {
|
||||||
// Count deployments for this app provider's apps
|
// Count deployments for this app provider's apps
|
||||||
for deployment in &user_data.app_deployments {
|
for deployment in &user_data.application_deployments {
|
||||||
// Check if this deployment belongs to an app from our app provider
|
// Check if this deployment belongs to an app from our app provider
|
||||||
// We need to get the app provider's apps to match
|
// We need to get the application provider's apps to match
|
||||||
let provider_apps = UserPersistence::get_user_apps(app_provider_email);
|
let provider_apps = UserPersistence::get_user_apps(application_provider_email);
|
||||||
|
|
||||||
for provider_app in &provider_apps {
|
for provider_app in &provider_apps {
|
||||||
if deployment.app_id == provider_app.id && deployment.status == "Active" {
|
if deployment.app_id == provider_app.id && deployment.status == "Active" {
|
||||||
@@ -392,11 +392,11 @@ impl DashboardController {
|
|||||||
render_template(&tmpl, "dashboard/user.html", &ctx)
|
render_template(&tmpl, "dashboard/user.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the farmer section of the dashboard
|
/// Renders the resource provider section of the dashboard
|
||||||
pub async fn farmer_section(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
pub async fn resource_provider_section(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.active_page("dashboard")
|
.active_page("dashboard")
|
||||||
.active_section("farmer")
|
.active_section("resource_provider")
|
||||||
.gitea_enabled(get_app_config().is_gitea_enabled())
|
.gitea_enabled(get_app_config().is_gitea_enabled())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -406,17 +406,17 @@ impl DashboardController {
|
|||||||
return render_template(&tmpl, "dashboard/welcome.html", &ctx);
|
return render_template(&tmpl, "dashboard/welcome.html", &ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FARMER FIX: Use persistent data only, no mock data for farmer dashboard
|
// RESOURCE PROVIDER FIX: Use persistent data only, no mock data for resource provider dashboard
|
||||||
if let Ok(Some(user_email)) = session.get::<String>("user_email") {
|
if let Ok(Some(user_email)) = session.get::<String>("user_email") {
|
||||||
ctx.insert("user_email", &user_email);
|
ctx.insert("user_email", &user_email);
|
||||||
// Initialize farmer service with slice calculator
|
// Initialize resource provider service with slice calculator
|
||||||
if let Ok(farmer_service) = crate::services::farmer::FarmerService::builder().build() {
|
if let Ok(resource_provider_service) = crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
// Repair node-group data consistency when farmer dashboard loads
|
// Repair node-group data consistency when resource provider dashboard loads
|
||||||
if let Err(_e) = farmer_service.repair_node_group_consistency(&user_email) {
|
if let Err(_e) = resource_provider_service.repair_node_group_consistency(&user_email) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repair missing marketplace SLA data for existing nodes
|
// Repair missing marketplace SLA data for existing nodes
|
||||||
if let Err(_e) = farmer_service.repair_missing_marketplace_sla(
|
if let Err(_e) = resource_provider_service.repair_missing_marketplace_sla(
|
||||||
&user_email,
|
&user_email,
|
||||||
99.8, // default uptime
|
99.8, // default uptime
|
||||||
100, // default bandwidth
|
100, // default bandwidth
|
||||||
@@ -424,18 +424,18 @@ impl DashboardController {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get farmer nodes with updated slice calculations
|
// Get resource provider nodes with updated slice calculations
|
||||||
let farmer_nodes = farmer_service.get_farmer_nodes(&user_email);
|
let resource_provider_nodes = resource_provider_service.get_resource_provider_nodes(&user_email);
|
||||||
|
|
||||||
// Calculate farmer statistics from nodes
|
// Calculate resource_provider statistics from nodes
|
||||||
let total_nodes = farmer_nodes.len() as u32;
|
let total_nodes = resource_provider_nodes.len() as u32;
|
||||||
let mut online_nodes = 0u32;
|
let mut online_nodes = 0u32;
|
||||||
let mut total_base_slices = 0u32;
|
let mut total_base_slices = 0u32;
|
||||||
let mut allocated_base_slices = 0u32;
|
let mut allocated_base_slices = 0u32;
|
||||||
let mut total_monthly_earnings = rust_decimal::Decimal::ZERO;
|
let mut total_monthly_earnings = rust_decimal::Decimal::ZERO;
|
||||||
let mut average_uptime = 0.0f32;
|
let mut average_uptime = 0.0f32;
|
||||||
|
|
||||||
for node in &farmer_nodes {
|
for node in &resource_provider_nodes {
|
||||||
if matches!(node.status, crate::models::user::NodeStatus::Online) {
|
if matches!(node.status, crate::models::user::NodeStatus::Online) {
|
||||||
online_nodes += 1;
|
online_nodes += 1;
|
||||||
}
|
}
|
||||||
@@ -449,8 +449,8 @@ impl DashboardController {
|
|||||||
average_uptime /= total_nodes as f32;
|
average_uptime /= total_nodes as f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create farmer statistics for the dashboard
|
// Create resource_provider statistics for the dashboard
|
||||||
let farmer_stats = serde_json::json!({
|
let resource_provider_stats = serde_json::json!({
|
||||||
"total_nodes": total_nodes,
|
"total_nodes": total_nodes,
|
||||||
"online_nodes": online_nodes,
|
"online_nodes": online_nodes,
|
||||||
"total_base_slices": total_base_slices,
|
"total_base_slices": total_base_slices,
|
||||||
@@ -465,8 +465,8 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.insert("farmer_stats", &farmer_stats);
|
ctx.insert("resource_provider_stats", &resource_provider_stats);
|
||||||
ctx.insert("farmer_nodes", &farmer_nodes);
|
ctx.insert("resource_provider_nodes", &resource_provider_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load user data from session (without mock data override)
|
// Load user data from session (without mock data override)
|
||||||
@@ -490,7 +490,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.insert("wallet_balance", &persistent_data.wallet_balance_usd);
|
ctx.insert("wallet_balance", &persistent_data.wallet_balance_usd);
|
||||||
ctx.insert("farmer_earnings", &persistent_data.farmer_earnings);
|
ctx.insert("resource_provider_earnings", &persistent_data.resource_provider_earnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.insert("user_json", &user_json);
|
ctx.insert("user_json", &user_json);
|
||||||
@@ -498,10 +498,10 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load slice rental service to get farmer slice rental statistics
|
// Load slice rental service to get resource_provider slice rental statistics
|
||||||
if let Ok(slice_rental_service) = crate::services::slice_rental::SliceRentalService::builder().build() {
|
if let Ok(slice_rental_service) = crate::services::slice_rental::SliceRentalService::builder().build() {
|
||||||
let farmer_slice_stats = slice_rental_service.get_farmer_slice_statistics(&user_email);
|
let resource_provider_slice_stats = slice_rental_service.get_resource_provider_slice_statistics(&user_email);
|
||||||
ctx.insert("slice_rental_statistics", &farmer_slice_stats);
|
ctx.insert("slice_rental_statistics", &resource_provider_slice_stats);
|
||||||
|
|
||||||
// Release any expired rentals
|
// Release any expired rentals
|
||||||
if let Err(_e) = slice_rental_service.release_expired_rentals(&user_email) {
|
if let Err(_e) = slice_rental_service.release_expired_rentals(&user_email) {
|
||||||
@@ -509,15 +509,15 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_template(&tmpl, "dashboard/farmer.html", &ctx)
|
render_template(&tmpl, "dashboard/resource_provider.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the app provider section of the dashboard
|
/// Renders the application provider section of the dashboard
|
||||||
pub async fn app_provider_section(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
pub async fn application_provider_section(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.active_page("dashboard")
|
.active_page("dashboard")
|
||||||
.build();
|
.build();
|
||||||
ctx.insert("active_section", "app_provider");
|
ctx.insert("active_section", "application_provider");
|
||||||
|
|
||||||
let config = get_app_config();
|
let config = get_app_config();
|
||||||
ctx.insert("gitea_enabled", &config.is_gitea_enabled());
|
ctx.insert("gitea_enabled", &config.is_gitea_enabled());
|
||||||
@@ -547,7 +547,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load fresh persistent deployments
|
// Load fresh persistent deployments
|
||||||
let fresh_deployments = crate::services::user_persistence::UserPersistence::get_user_app_deployments(&email);
|
let fresh_deployments = crate::services::user_persistence::UserPersistence::get_user_application_deployments(&email);
|
||||||
|
|
||||||
// Only count deployments for apps published by this user
|
// Only count deployments for apps published by this user
|
||||||
let user_published_app_ids: std::collections::HashSet<String> = fresh_apps.iter().map(|a| a.id.clone()).collect();
|
let user_published_app_ids: std::collections::HashSet<String> = fresh_apps.iter().map(|a| a.id.clone()).collect();
|
||||||
@@ -607,7 +607,7 @@ impl DashboardController {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let app_provider_data = crate::models::user::AppProviderData {
|
let application_provider_data = crate::models::user::AppProviderData {
|
||||||
published_apps: fresh_apps.len() as i32,
|
published_apps: fresh_apps.len() as i32,
|
||||||
total_deployments: fresh_apps.iter().map(|a| a.deployments).sum::<i32>(),
|
total_deployments: fresh_apps.iter().map(|a| a.deployments).sum::<i32>(),
|
||||||
active_deployments,
|
active_deployments,
|
||||||
@@ -618,7 +618,7 @@ impl DashboardController {
|
|||||||
revenue_history: Vec::new(),
|
revenue_history: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.insert("app_provider_data", &app_provider_data);
|
ctx.insert("application_provider_data", &application_provider_data);
|
||||||
} else {
|
} else {
|
||||||
// Ensure template always has a defined structure even without a logged-in user
|
// Ensure template always has a defined structure even without a logged-in user
|
||||||
let empty: crate::models::user::AppProviderData = crate::models::user::AppProviderData {
|
let empty: crate::models::user::AppProviderData = crate::models::user::AppProviderData {
|
||||||
@@ -631,10 +631,10 @@ impl DashboardController {
|
|||||||
deployment_stats: Vec::new(),
|
deployment_stats: Vec::new(),
|
||||||
revenue_history: Vec::new(),
|
revenue_history: Vec::new(),
|
||||||
};
|
};
|
||||||
ctx.insert("app_provider_data", &empty);
|
ctx.insert("application_provider_data", &empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
render_template(&tmpl, "dashboard/app_provider.html", &ctx)
|
render_template(&tmpl, "dashboard/application_provider.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the service provider section of the dashboard
|
/// Renders the service provider section of the dashboard
|
||||||
@@ -811,7 +811,7 @@ impl DashboardController {
|
|||||||
render_template(&tmpl, "dashboard/messages.html", &ctx)
|
render_template(&tmpl, "dashboard/messages.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the TFC Credits pools page
|
/// Renders the MC Credits pools page
|
||||||
pub async fn pools(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
pub async fn pools(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.active_page("dashboard")
|
.active_page("dashboard")
|
||||||
@@ -837,13 +837,13 @@ impl DashboardController {
|
|||||||
render_template(&tmpl, "dashboard/pools.html", &ctx)
|
render_template(&tmpl, "dashboard/pools.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// API endpoint to return farmer dashboard data as JSON
|
/// API endpoint to return resource_provider dashboard data as JSON
|
||||||
pub async fn farmer_data_api(session: Session) -> Result<impl Responder> {
|
pub async fn resource_provider_data_api(session: Session) -> Result<impl Responder> {
|
||||||
|
|
||||||
let user_email = session.get::<String>("user_email").unwrap_or_default().unwrap_or_default();
|
let user_email = session.get::<String>("user_email").unwrap_or_default().unwrap_or_default();
|
||||||
|
|
||||||
// FARMER FIX: Use farmer service to ensure data consistency
|
// RESOURCE_PROVIDER FIX: Use resource_provider service to ensure data consistency
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -853,16 +853,16 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Repair data consistency before loading
|
// Repair data consistency before loading
|
||||||
if let Err(_e) = farmer_service.repair_node_group_consistency(&user_email) {
|
if let Err(_e) = resource_provider_service.repair_node_group_consistency(&user_email) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load real farmer data from persistence using farmer service
|
// Load real resource_provider data from persistence using resource_provider service
|
||||||
let nodes = farmer_service.get_farmer_nodes(&user_email);
|
let nodes = resource_provider_service.get_resource_provider_nodes(&user_email);
|
||||||
let earnings = farmer_service.get_farmer_earnings(&user_email);
|
let earnings = resource_provider_service.get_resource_provider_earnings(&user_email);
|
||||||
let stats = farmer_service.get_farmer_statistics(&user_email);
|
let stats = resource_provider_service.get_resource_provider_statistics(&user_email);
|
||||||
|
|
||||||
// Always use persistent data - no fallback to mock data for farmer dashboard
|
// Always use persistent data - no fallback to mock data for resource_provider dashboard
|
||||||
// If no data exists, return empty but valid farmer data structure
|
// If no data exists, return empty but valid resource_provider data structure
|
||||||
if nodes.is_empty() {
|
if nodes.is_empty() {
|
||||||
return Ok(ResponseBuilder::ok()
|
return Ok(ResponseBuilder::ok()
|
||||||
.json(serde_json::json!({
|
.json(serde_json::json!({
|
||||||
@@ -885,12 +885,12 @@ impl DashboardController {
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load slice products for this farmer
|
// Load slice products for this resource_provider
|
||||||
let slice_products = crate::services::user_persistence::UserPersistence::get_slice_products(&user_email);
|
let slice_products = crate::services::user_persistence::UserPersistence::get_slice_products(&user_email);
|
||||||
let active_slices = slice_products.len() as i32;
|
let active_slices = slice_products.len() as i32;
|
||||||
|
|
||||||
// Build comprehensive farmer data using statistics from farmer service
|
// Build comprehensive resource_provider data using statistics from resource_provider service
|
||||||
let farmer_data = serde_json::json!({
|
let resource_provider_data = serde_json::json!({
|
||||||
"total_nodes": stats.total_nodes,
|
"total_nodes": stats.total_nodes,
|
||||||
"online_nodes": stats.online_nodes,
|
"online_nodes": stats.online_nodes,
|
||||||
"total_capacity": stats.total_capacity,
|
"total_capacity": stats.total_capacity,
|
||||||
@@ -904,13 +904,13 @@ impl DashboardController {
|
|||||||
"slice_products": slice_products
|
"slice_products": slice_products
|
||||||
});
|
});
|
||||||
Ok(ResponseBuilder::ok()
|
Ok(ResponseBuilder::ok()
|
||||||
.json(farmer_data)
|
.json(resource_provider_data)
|
||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enhanced farmer dashboard with node management
|
/// Enhanced resource_provider dashboard with node management
|
||||||
pub async fn farmer_dashboard_enhanced(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
pub async fn resource_provider_dashboard_enhanced(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
||||||
let farmer_service = crate::services::farmer::FarmerService::builder()
|
let resource_provider_service = crate::services::resource_provider::ResourceProviderService::builder()
|
||||||
.auto_sync_enabled(true)
|
.auto_sync_enabled(true)
|
||||||
.metrics_collection(true)
|
.metrics_collection(true)
|
||||||
.build()
|
.build()
|
||||||
@@ -922,14 +922,14 @@ impl DashboardController {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Load farmer data using the service
|
// Load resource_provider data using the service
|
||||||
let nodes = farmer_service.get_farmer_nodes(&user_email);
|
let nodes = resource_provider_service.get_resource_provider_nodes(&user_email);
|
||||||
let earnings = farmer_service.get_farmer_earnings(&user_email);
|
let earnings = resource_provider_service.get_resource_provider_earnings(&user_email);
|
||||||
let stats = farmer_service.get_farmer_statistics(&user_email);
|
let stats = resource_provider_service.get_resource_provider_statistics(&user_email);
|
||||||
|
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.active_page("dashboard")
|
.active_page("dashboard")
|
||||||
.active_section("farmer")
|
.active_section("resource_provider")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ctx.insert("nodes", &nodes);
|
ctx.insert("nodes", &nodes);
|
||||||
@@ -944,10 +944,10 @@ impl DashboardController {
|
|||||||
ctx.insert("user", &user);
|
ctx.insert("user", &user);
|
||||||
}
|
}
|
||||||
|
|
||||||
render_template(&tmpl, "dashboard/farmer.html", &ctx)
|
render_template(&tmpl, "dashboard/resource_provider.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// API endpoint to add a new farm node using FarmerService
|
/// API endpoint to add a new farm node using ResourceProviderService
|
||||||
pub async fn add_farm_node_enhanced(session: Session, form: web::Json<serde_json::Value>) -> Result<impl Responder> {
|
pub async fn add_farm_node_enhanced(session: Session, form: web::Json<serde_json::Value>) -> Result<impl Responder> {
|
||||||
|
|
||||||
let user_email = session.get::<String>("user_email")
|
let user_email = session.get::<String>("user_email")
|
||||||
@@ -960,8 +960,8 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize farmer service
|
// Initialize resource_provider service
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1064,7 +1064,7 @@ impl DashboardController {
|
|||||||
.bandwidth_mbps(node_data_json.get("bandwidth_mbps")
|
.bandwidth_mbps(node_data_json.get("bandwidth_mbps")
|
||||||
.and_then(|v| v.as_i64())
|
.and_then(|v| v.as_i64())
|
||||||
.unwrap_or(100) as i32)
|
.unwrap_or(100) as i32)
|
||||||
.node_type("3Node"); // Always 3Node - farmers register 3Nodes to ThreeFold Grid
|
.node_type("MyceliumNode"); // Always MyceliumNode - resource providers register MyceliumNodes to Mycelium Grid
|
||||||
|
|
||||||
// Add optional fields
|
// Add optional fields
|
||||||
if let Some(region) = node_data_json.get("region").and_then(|v| v.as_str()) {
|
if let Some(region) = node_data_json.get("region").and_then(|v| v.as_str()) {
|
||||||
@@ -1087,8 +1087,8 @@ impl DashboardController {
|
|||||||
actix_web::error::ErrorBadRequest(format!("Invalid node data: {}", e))
|
actix_web::error::ErrorBadRequest(format!("Invalid node data: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Add node using farmer service
|
// Add node using resource_provider service
|
||||||
match farmer_service.add_node(&user_email, node_data) {
|
match resource_provider_service.add_node(&user_email, node_data) {
|
||||||
Ok(node) => {
|
Ok(node) => {
|
||||||
|
|
||||||
// Add activity record
|
// Add activity record
|
||||||
@@ -1143,7 +1143,7 @@ impl DashboardController {
|
|||||||
_ => crate::models::user::NodeStatus::Offline,
|
_ => crate::models::user::NodeStatus::Offline,
|
||||||
};
|
};
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1152,7 +1152,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.update_node_status(&user_email, &node_id, status) {
|
match resource_provider_service.update_node_status(&user_email, &node_id, status) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -1182,7 +1182,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
let node_id = path.into_inner();
|
let node_id = path.into_inner();
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1191,7 +1191,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.get_node_by_id(&user_email, &node_id) {
|
match resource_provider_service.get_node_by_id(&user_email, &node_id) {
|
||||||
Some(mut node) => {
|
Some(mut node) => {
|
||||||
|
|
||||||
// MARKETPLACE SLA FIX: Override grid data with marketplace SLA values when available
|
// MARKETPLACE SLA FIX: Override grid data with marketplace SLA values when available
|
||||||
@@ -1235,7 +1235,7 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1244,7 +1244,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let stats = farmer_service.get_farmer_statistics(&user_email);
|
let stats = resource_provider_service.get_resource_provider_statistics(&user_email);
|
||||||
|
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -1262,7 +1262,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// API endpoint to update node configuration
|
/// API endpoint to update node configuration
|
||||||
pub async fn update_node_comprehensive(session: Session, path: web::Path<String>, form: web::Json<crate::services::farmer::NodeUpdateData>) -> Result<impl Responder> {
|
pub async fn update_node_comprehensive(session: Session, path: web::Path<String>, form: web::Json<crate::services::resource_provider::NodeUpdateData>) -> Result<impl Responder> {
|
||||||
let user_email = session.get::<String>("user_email")
|
let user_email = session.get::<String>("user_email")
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
@@ -1275,7 +1275,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
let node_id = path.into_inner();
|
let node_id = path.into_inner();
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1284,7 +1284,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.update_node(&user_email, &node_id, form.into_inner()) {
|
match resource_provider_service.update_node(&user_email, &node_id, form.into_inner()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
|
|
||||||
// Add activity record
|
// Add activity record
|
||||||
@@ -1324,7 +1324,7 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1338,7 +1338,7 @@ impl DashboardController {
|
|||||||
let mut customized_formats = Vec::new();
|
let mut customized_formats = Vec::new();
|
||||||
|
|
||||||
for format_id in default_format_ids {
|
for format_id in default_format_ids {
|
||||||
if let Some(format) = farmer_service.get_default_slice_format_with_customizations(&user_email, format_id) {
|
if let Some(format) = resource_provider_service.get_default_slice_format_with_customizations(&user_email, format_id) {
|
||||||
customized_formats.push(format);
|
customized_formats.push(format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1358,7 +1358,7 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1367,7 +1367,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let groups = farmer_service.get_node_groups(&user_email);
|
let groups = resource_provider_service.get_node_groups(&user_email);
|
||||||
Ok(ResponseBuilder::ok().json(groups).build())
|
Ok(ResponseBuilder::ok().json(groups).build())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1383,7 +1383,7 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1402,7 +1402,7 @@ impl DashboardController {
|
|||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
match farmer_service.create_custom_node_group(&user_email, name, description, None) {
|
match resource_provider_service.create_custom_node_group(&user_email, name, description, None) {
|
||||||
Ok(group) => {
|
Ok(group) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -1431,7 +1431,7 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1449,10 +1449,10 @@ impl DashboardController {
|
|||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
match farmer_service.update_node_group_assignment(&user_email, node_id, group_id) {
|
match resource_provider_service.update_node_group_assignment(&user_email, node_id, group_id) {
|
||||||
Ok(group_name) => {
|
Ok(group_name) => {
|
||||||
// FARMER FIX: Repair consistency after group assignment change
|
// RESOURCE_PROVIDER FIX: Repair consistency after group assignment change
|
||||||
if let Err(e) = farmer_service.repair_node_group_consistency(&user_email) {
|
if let Err(e) = resource_provider_service.repair_node_group_consistency(&user_email) {
|
||||||
}
|
}
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -1482,7 +1482,7 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1493,7 +1493,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
let group_id = path.into_inner();
|
let group_id = path.into_inner();
|
||||||
|
|
||||||
match farmer_service.delete_custom_node_group(&user_email, &group_id) {
|
match resource_provider_service.delete_custom_node_group(&user_email, &group_id) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -1523,7 +1523,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
let format_id = path.into_inner();
|
let format_id = path.into_inner();
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1532,7 +1532,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.get_default_slice_format_with_customizations(&user_email, &format_id) {
|
match resource_provider_service.get_default_slice_format_with_customizations(&user_email, &format_id) {
|
||||||
Some(format) => {
|
Some(format) => {
|
||||||
Ok(ResponseBuilder::ok().json(format).build())
|
Ok(ResponseBuilder::ok().json(format).build())
|
||||||
}
|
}
|
||||||
@@ -1558,7 +1558,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
let format_id = path.into_inner();
|
let format_id = path.into_inner();
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -1576,7 +1576,7 @@ impl DashboardController {
|
|||||||
let bandwidth_mbps = form.get("bandwidth_mbps").and_then(|v| v.as_i64()).unwrap_or(100) as i32;
|
let bandwidth_mbps = form.get("bandwidth_mbps").and_then(|v| v.as_i64()).unwrap_or(100) as i32;
|
||||||
let price_per_hour = form.get("price_per_hour").and_then(|v| v.as_f64()).map(rust_decimal::Decimal::from_f64_retain).flatten().unwrap_or(rust_decimal::Decimal::from(10));
|
let price_per_hour = form.get("price_per_hour").and_then(|v| v.as_f64()).map(rust_decimal::Decimal::from_f64_retain).flatten().unwrap_or(rust_decimal::Decimal::from(10));
|
||||||
|
|
||||||
let customization = crate::services::farmer::DefaultSliceFormat {
|
let customization = crate::services::resource_provider::DefaultSliceFormat {
|
||||||
id: format_id.clone(),
|
id: format_id.clone(),
|
||||||
name,
|
name,
|
||||||
cpu_cores,
|
cpu_cores,
|
||||||
@@ -1587,7 +1587,7 @@ impl DashboardController {
|
|||||||
price_per_hour,
|
price_per_hour,
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.save_default_slice_customization(&user_email, &format_id, customization) {
|
match resource_provider_service.save_default_slice_customization(&user_email, &format_id, customization) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -1628,11 +1628,11 @@ impl DashboardController {
|
|||||||
let price_per_hour = slice_data.get("price_hour")
|
let price_per_hour = slice_data.get("price_hour")
|
||||||
.and_then(|v| v.as_f64())
|
.and_then(|v| v.as_f64())
|
||||||
.map(|p| rust_decimal::Decimal::from_f64_retain(p).unwrap_or(rust_decimal::Decimal::new(50, 2)))
|
.map(|p| rust_decimal::Decimal::from_f64_retain(p).unwrap_or(rust_decimal::Decimal::new(50, 2)))
|
||||||
.unwrap_or(rust_decimal::Decimal::new(50, 2)); // Fallback to 0.50 TFC/hour only if no price provided
|
.unwrap_or(rust_decimal::Decimal::new(50, 2)); // Fallback to 0.50 MC/hour only if no price provided
|
||||||
|
|
||||||
// Load user data to get farmer name
|
// Load user data to get resource_provider name
|
||||||
let user = Self::load_user_with_persistent_data(&session);
|
let user = Self::load_user_with_persistent_data(&session);
|
||||||
let farmer_name = user.as_ref().map(|u| u.name.clone()).unwrap_or_else(|| "Unknown Farmer".to_string());
|
let resource_provider_name = user.as_ref().map(|u| u.name.clone()).unwrap_or_else(|| "Unknown ResourceProvider".to_string());
|
||||||
|
|
||||||
// Create slice configuration with pricing
|
// Create slice configuration with pricing
|
||||||
let slice_pricing = crate::models::product::SlicePricing::from_hourly(
|
let slice_pricing = crate::models::product::SlicePricing::from_hourly(
|
||||||
@@ -1657,7 +1657,7 @@ impl DashboardController {
|
|||||||
// Create slice product
|
// Create slice product
|
||||||
let slice_product = crate::models::product::Product::create_slice_product(
|
let slice_product = crate::models::product::Product::create_slice_product(
|
||||||
user_email.clone(),
|
user_email.clone(),
|
||||||
farmer_name,
|
resource_provider_name,
|
||||||
name,
|
name,
|
||||||
slice_config,
|
slice_config,
|
||||||
price_per_hour,
|
price_per_hour,
|
||||||
@@ -2141,7 +2141,7 @@ impl DashboardController {
|
|||||||
.earnings_today_usd(rust_decimal::Decimal::ZERO)
|
.earnings_today_usd(rust_decimal::Decimal::ZERO)
|
||||||
.health_score(100.0)
|
.health_score(100.0)
|
||||||
.region(node_data.get("region").and_then(|v| v.as_str()).unwrap_or("Global").to_string())
|
.region(node_data.get("region").and_then(|v| v.as_str()).unwrap_or("Global").to_string())
|
||||||
.node_type(node_data.get("node_type").and_then(|v| v.as_str()).unwrap_or("3Node").to_string())
|
.node_type(node_data.get("node_type").and_then(|v| v.as_str()).unwrap_or("MyceliumNode").to_string())
|
||||||
.build()
|
.build()
|
||||||
{
|
{
|
||||||
Ok(node) => node,
|
Ok(node) => node,
|
||||||
@@ -2225,18 +2225,18 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize farmer service to check for existing nodes
|
// Initialize resource_provider service to check for existing nodes
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
"error": "Failed to initialize farmer service"
|
"error": "Failed to initialize resource_provider service"
|
||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get existing nodes to check for duplicates
|
// Get existing nodes to check for duplicates
|
||||||
let existing_nodes = farmer_service.get_farmer_nodes(&user_email);
|
let existing_nodes = resource_provider_service.get_resource_provider_nodes(&user_email);
|
||||||
|
|
||||||
// Validate each node and fetch data
|
// Validate each node and fetch data
|
||||||
let mut validated_nodes = Vec::new();
|
let mut validated_nodes = Vec::new();
|
||||||
@@ -2502,12 +2502,12 @@ impl DashboardController {
|
|||||||
let slice_format = form.get("slice_format").and_then(|v| v.as_str()).map(|s| s.to_string());
|
let slice_format = form.get("slice_format").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||||
let slice_price = form.get("slice_price").and_then(|v| v.as_f64()).map(|p| Decimal::from_f64_retain(p).unwrap_or_default());
|
let slice_price = form.get("slice_price").and_then(|v| v.as_f64()).map(|p| Decimal::from_f64_retain(p).unwrap_or_default());
|
||||||
|
|
||||||
// Initialize farmer service
|
// Initialize resource_provider service
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
"error": "Failed to initialize farmer service"
|
"error": "Failed to initialize resource_provider service"
|
||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2515,7 +2515,7 @@ impl DashboardController {
|
|||||||
// Add nodes with comprehensive configuration
|
// Add nodes with comprehensive configuration
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "api.dashboard",
|
target: "api.dashboard",
|
||||||
"add_grid_nodes:invoke_farmer_service req_id={} email={} node_count={} slice_enabled={} full_node_enabled={} pricing_mode={}",
|
"add_grid_nodes:invoke_resource_provider_service req_id={} email={} node_count={} slice_enabled={} full_node_enabled={} pricing_mode={}",
|
||||||
req_id,
|
req_id,
|
||||||
user_email,
|
user_email,
|
||||||
node_ids.len(),
|
node_ids.len(),
|
||||||
@@ -2556,19 +2556,19 @@ impl DashboardController {
|
|||||||
// For multi-node scenarios, apply pricing configuration based on user choice
|
// For multi-node scenarios, apply pricing configuration based on user choice
|
||||||
if node_ids.len() > 1 && pricing_mode == "same_for_all" && rental_options.is_some() {
|
if node_ids.len() > 1 && pricing_mode == "same_for_all" && rental_options.is_some() {
|
||||||
} else if node_ids.len() > 1 && pricing_mode == "individual" && individual_node_pricing.is_some() {
|
} else if node_ids.len() > 1 && pricing_mode == "individual" && individual_node_pricing.is_some() {
|
||||||
// Individual pricing will be handled in the farmer service
|
// Individual pricing will be handled in the resource_provider service
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose the appropriate method based on pricing mode
|
// Choose the appropriate method based on pricing mode
|
||||||
if pricing_mode == "individual" && individual_node_pricing.is_some() {
|
if pricing_mode == "individual" && individual_node_pricing.is_some() {
|
||||||
farmer_service.add_multiple_grid_nodes_with_individual_pricing(
|
resource_provider_service.add_multiple_grid_nodes_with_individual_pricing(
|
||||||
&user_email,
|
&user_email,
|
||||||
node_ids.clone(),
|
node_ids.clone(),
|
||||||
slice_formats,
|
slice_formats,
|
||||||
individual_node_pricing.unwrap()
|
individual_node_pricing.unwrap()
|
||||||
).await
|
).await
|
||||||
} else {
|
} else {
|
||||||
farmer_service.add_multiple_grid_nodes_with_comprehensive_config(
|
resource_provider_service.add_multiple_grid_nodes_with_comprehensive_config(
|
||||||
&user_email,
|
&user_email,
|
||||||
node_ids.clone(),
|
node_ids.clone(),
|
||||||
slice_formats,
|
slice_formats,
|
||||||
@@ -2576,7 +2576,7 @@ impl DashboardController {
|
|||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
farmer_service.add_multiple_grid_nodes(&user_email, node_ids.clone()).await
|
resource_provider_service.add_multiple_grid_nodes(&user_email, node_ids.clone()).await
|
||||||
};
|
};
|
||||||
|
|
||||||
match add_result {
|
match add_result {
|
||||||
@@ -2594,7 +2594,7 @@ impl DashboardController {
|
|||||||
// If node_group_id is provided, assign nodes to existing group
|
// If node_group_id is provided, assign nodes to existing group
|
||||||
if let Some(group_id) = node_group_id {
|
if let Some(group_id) = node_group_id {
|
||||||
for node in &added_nodes {
|
for node in &added_nodes {
|
||||||
if let Err(e) = farmer_service.assign_node_to_group(&user_email, &node.id, Some(group_id.clone())) {
|
if let Err(e) = resource_provider_service.assign_node_to_group(&user_email, &node.id, Some(group_id.clone())) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2606,7 +2606,7 @@ impl DashboardController {
|
|||||||
group_data.get("description").and_then(|v| v.as_str())
|
group_data.get("description").and_then(|v| v.as_str())
|
||||||
) {
|
) {
|
||||||
// Create node group
|
// Create node group
|
||||||
match farmer_service.create_custom_node_group(
|
match resource_provider_service.create_custom_node_group(
|
||||||
&user_email,
|
&user_email,
|
||||||
group_name.to_string(),
|
group_name.to_string(),
|
||||||
Some(group_description.to_string()),
|
Some(group_description.to_string()),
|
||||||
@@ -2626,7 +2626,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
// Add nodes to group
|
// Add nodes to group
|
||||||
for node in &added_nodes {
|
for node in &added_nodes {
|
||||||
if let Err(e) = farmer_service.assign_node_to_group(&user_email, &node.id, Some(group.id.clone())) {
|
if let Err(e) = resource_provider_service.assign_node_to_group(&user_email, &node.id, Some(group.id.clone())) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2663,7 +2663,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = farmer_service.stake_on_node(&user_email, &node.id, staking_options) {
|
if let Err(e) = resource_provider_service.stake_on_node(&user_email, &node.id, staking_options) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2691,7 +2691,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for node in &added_nodes {
|
for node in &added_nodes {
|
||||||
if let Err(e) = farmer_service.stake_on_node(&user_email, &node.id, staking_options.clone()) {
|
if let Err(e) = resource_provider_service.stake_on_node(&user_email, &node.id, staking_options.clone()) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2752,11 +2752,11 @@ impl DashboardController {
|
|||||||
let slice_format = form.get("slice_format").and_then(|v| v.as_str()).map(|s| s.to_string());
|
let slice_format = form.get("slice_format").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||||
let slice_price = form.get("slice_price").and_then(|v| v.as_f64()).map(|p| Decimal::from_f64_retain(p).unwrap_or_default());
|
let slice_price = form.get("slice_price").and_then(|v| v.as_f64()).map(|p| Decimal::from_f64_retain(p).unwrap_or_default());
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
"error": "Failed to initialize farmer service"
|
"error": "Failed to initialize resource_provider service"
|
||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2774,7 +2774,7 @@ impl DashboardController {
|
|||||||
resource_optimization: crate::models::user::ResourceOptimization::default(),
|
resource_optimization: crate::models::user::ResourceOptimization::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.create_custom_node_group(
|
match resource_provider_service.create_custom_node_group(
|
||||||
&user_email,
|
&user_email,
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
description,
|
description,
|
||||||
@@ -2810,7 +2810,7 @@ impl DashboardController {
|
|||||||
})).build()),
|
})).build()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
Err(e) => return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
"success": false,
|
"success": false,
|
||||||
@@ -2818,15 +2818,15 @@ impl DashboardController {
|
|||||||
})).build()),
|
})).build()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// FARMER FIX: Repair node-group data consistency before getting statistics
|
// RESOURCE_PROVIDER FIX: Repair node-group data consistency before getting statistics
|
||||||
if let Err(e) = farmer_service.repair_node_group_consistency(&user_email) {
|
if let Err(e) = resource_provider_service.repair_node_group_consistency(&user_email) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let groups = farmer_service.get_node_groups(&user_email);
|
let groups = resource_provider_service.get_node_groups(&user_email);
|
||||||
let mut groups_with_stats = Vec::new();
|
let mut groups_with_stats = Vec::new();
|
||||||
|
|
||||||
for group in groups {
|
for group in groups {
|
||||||
match farmer_service.get_group_statistics(&user_email, &group.id) {
|
match resource_provider_service.get_group_statistics(&user_email, &group.id) {
|
||||||
Ok(stats) => {
|
Ok(stats) => {
|
||||||
groups_with_stats.push(serde_json::json!({
|
groups_with_stats.push(serde_json::json!({
|
||||||
"group": group,
|
"group": group,
|
||||||
@@ -2854,8 +2854,8 @@ impl DashboardController {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// API endpoint to return app provider dashboard data as JSON
|
/// API endpoint to return application provider dashboard data as JSON
|
||||||
pub async fn app_provider_data_api(session: Session) -> Result<impl Responder> {
|
pub async fn application_provider_data_api(session: Session) -> Result<impl Responder> {
|
||||||
|
|
||||||
// Get user email for debugging
|
// Get user email for debugging
|
||||||
let user_email = session.get::<String>("user_email")
|
let user_email = session.get::<String>("user_email")
|
||||||
@@ -2875,7 +2875,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fresh_deployments = UserPersistence::get_user_app_deployments(&user_email);
|
let fresh_deployments = UserPersistence::get_user_application_deployments(&user_email);
|
||||||
|
|
||||||
// Load user persistent data
|
// Load user persistent data
|
||||||
if let Some(user) = Self::load_user_with_persistent_data(&session) {
|
if let Some(user) = Self::load_user_with_persistent_data(&session) {
|
||||||
@@ -2936,7 +2936,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
let app_provider_data = crate::models::user::AppProviderData {
|
let application_provider_data = crate::models::user::AppProviderData {
|
||||||
apps: fresh_apps.clone(),
|
apps: fresh_apps.clone(),
|
||||||
published_apps: fresh_apps.len() as i32,
|
published_apps: fresh_apps.len() as i32,
|
||||||
total_deployments,
|
total_deployments,
|
||||||
@@ -2947,7 +2947,7 @@ impl DashboardController {
|
|||||||
revenue_history: Vec::new(),
|
revenue_history: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(ResponseBuilder::ok().json(app_provider_data).build());
|
return Ok(ResponseBuilder::ok().json(application_provider_data).build());
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3263,7 +3263,7 @@ impl DashboardController {
|
|||||||
})).build())
|
})).build())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add TFC Credits to user balance
|
/// Add MC Credits to user balance
|
||||||
pub async fn add_tfp(
|
pub async fn add_tfp(
|
||||||
form: web::Form<AddTfpForm>,
|
form: web::Form<AddTfpForm>,
|
||||||
session: Session,
|
session: Session,
|
||||||
@@ -5311,7 +5311,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Load user's app deployments
|
// Load user's app deployments
|
||||||
let deployments = UserPersistence::get_user_app_deployments(&user_email);
|
let deployments = UserPersistence::get_user_application_deployments(&user_email);
|
||||||
|
|
||||||
// Find the specific deployment
|
// Find the specific deployment
|
||||||
if let Some(deployment) = deployments.iter().find(|d| d.id == deployment_id) {
|
if let Some(deployment) = deployments.iter().find(|d| d.id == deployment_id) {
|
||||||
@@ -5387,7 +5387,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if node exists and get its details first
|
// Check if node exists and get its details first
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -5398,7 +5398,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Verify node exists and belongs to user
|
// Verify node exists and belongs to user
|
||||||
let _node = match farmer_service.get_node_by_id(&user_email, &node_id) {
|
let _node = match resource_provider_service.get_node_by_id(&user_email, &node_id) {
|
||||||
Some(node) => node,
|
Some(node) => node,
|
||||||
None => {
|
None => {
|
||||||
return Ok(ResponseBuilder::not_found().json(serde_json::json!({
|
return Ok(ResponseBuilder::not_found().json(serde_json::json!({
|
||||||
@@ -5457,7 +5457,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let _farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let _resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -5903,7 +5903,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stake TFC Credits on a node
|
/// Stake MC Credits on a node
|
||||||
pub async fn stake_on_node(
|
pub async fn stake_on_node(
|
||||||
session: Session,
|
session: Session,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
@@ -5927,7 +5927,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -5970,7 +5970,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Stake on node
|
// Stake on node
|
||||||
match farmer_service.stake_on_node(&user_email, &node_id, staking_options) {
|
match resource_provider_service.stake_on_node(&user_email, &node_id, staking_options) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -6010,7 +6010,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6030,7 +6030,7 @@ impl DashboardController {
|
|||||||
match action {
|
match action {
|
||||||
"unstake" => {
|
"unstake" => {
|
||||||
// Unstake from node
|
// Unstake from node
|
||||||
match farmer_service.unstake_from_node(&user_email, &node_id) {
|
match resource_provider_service.unstake_from_node(&user_email, &node_id) {
|
||||||
Ok(returned_amount) => {
|
Ok(returned_amount) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -6078,7 +6078,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Update node staking
|
// Update node staking
|
||||||
match farmer_service.update_node_staking(&user_email, &node_id, staking_options) {
|
match resource_provider_service.update_node_staking(&user_email, &node_id, staking_options) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -6120,7 +6120,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6130,7 +6130,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let statistics = farmer_service.get_staking_statistics(&user_email);
|
let statistics = resource_provider_service.get_staking_statistics(&user_email);
|
||||||
|
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": false,
|
"success": false,
|
||||||
@@ -6293,7 +6293,7 @@ impl DashboardController {
|
|||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh slice calculations for farmer
|
/// Refresh slice calculations for resource_provider
|
||||||
pub async fn refresh_slice_calculations(session: Session) -> Result<impl Responder> {
|
pub async fn refresh_slice_calculations(session: Session) -> Result<impl Responder> {
|
||||||
let user_email = match session.get::<String>("user_email") {
|
let user_email = match session.get::<String>("user_email") {
|
||||||
Ok(Some(email)) => email,
|
Ok(Some(email)) => email,
|
||||||
@@ -6303,7 +6303,7 @@ impl DashboardController {
|
|||||||
})).build())
|
})).build())
|
||||||
};
|
};
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6313,7 +6313,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.refresh_all_slice_calculations(&user_email) {
|
match resource_provider_service.refresh_all_slice_calculations(&user_email) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": false,
|
"success": false,
|
||||||
@@ -6329,7 +6329,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync farmer nodes with ThreeFold Grid
|
/// Sync resource_provider nodes with ThreeFold Grid
|
||||||
pub async fn sync_with_grid(session: Session) -> Result<impl Responder> {
|
pub async fn sync_with_grid(session: Session) -> Result<impl Responder> {
|
||||||
// Check authentication
|
// Check authentication
|
||||||
if let Err(response) = Self::check_authentication(&session) {
|
if let Err(response) = Self::check_authentication(&session) {
|
||||||
@@ -6463,7 +6463,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
let node_id = path.into_inner();
|
let node_id = path.into_inner();
|
||||||
|
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6473,7 +6473,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match farmer_service.get_node_slices(&user_email, node_id) {
|
match resource_provider_service.get_node_slices(&user_email, node_id) {
|
||||||
Ok(slices) => {
|
Ok(slices) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": false,
|
"success": false,
|
||||||
@@ -6525,7 +6525,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize services
|
// Initialize services
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6550,7 +6550,7 @@ impl DashboardController {
|
|||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
for node_id in &node_ids {
|
for node_id in &node_ids {
|
||||||
match farmer_service.fetch_and_validate_grid_node(*node_id).await {
|
match resource_provider_service.fetch_and_validate_grid_node(*node_id).await {
|
||||||
Ok(node_data) => {
|
Ok(node_data) => {
|
||||||
// Calculate automatic slices
|
// Calculate automatic slices
|
||||||
let total_base_slices = slice_calculator.calculate_max_base_slices(&node_data.capacity);
|
let total_base_slices = slice_calculator.calculate_max_base_slices(&node_data.capacity);
|
||||||
@@ -6581,7 +6581,7 @@ impl DashboardController {
|
|||||||
rental_options: None,
|
rental_options: None,
|
||||||
earnings_today_usd: rust_decimal::Decimal::ZERO,
|
earnings_today_usd: rust_decimal::Decimal::ZERO,
|
||||||
region: if node_data.country.is_empty() { "Unknown".to_string() } else { node_data.country.clone() },
|
region: if node_data.country.is_empty() { "Unknown".to_string() } else { node_data.country.clone() },
|
||||||
node_type: "3Node".to_string(),
|
node_type: "MyceliumNode".to_string(),
|
||||||
slice_formats: None,
|
slice_formats: None,
|
||||||
staking_options: None,
|
staking_options: None,
|
||||||
availability_status: crate::models::user::NodeAvailabilityStatus::Available,
|
availability_status: crate::models::user::NodeAvailabilityStatus::Available,
|
||||||
@@ -6709,8 +6709,8 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize farmer service
|
// Initialize resource_provider service
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6722,7 +6722,7 @@ impl DashboardController {
|
|||||||
|
|
||||||
// Add nodes with automatic slice management
|
// Add nodes with automatic slice management
|
||||||
|
|
||||||
match farmer_service.add_multiple_grid_nodes_with_automatic_slices(
|
match resource_provider_service.add_multiple_grid_nodes_with_automatic_slices(
|
||||||
&user_email,
|
&user_email,
|
||||||
node_ids.clone(),
|
node_ids.clone(),
|
||||||
base_slice_price,
|
base_slice_price,
|
||||||
@@ -6759,7 +6759,7 @@ impl DashboardController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// API endpoint to refresh slice calculations for all farmer nodes
|
/// API endpoint to refresh slice calculations for all resource_provider nodes
|
||||||
pub async fn refresh_slice_calculations_api(session: Session) -> Result<impl Responder> {
|
pub async fn refresh_slice_calculations_api(session: Session) -> Result<impl Responder> {
|
||||||
|
|
||||||
let user_email = session.get::<String>("user_email")
|
let user_email = session.get::<String>("user_email")
|
||||||
@@ -6773,8 +6773,8 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize farmer service
|
// Initialize resource_provider service
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6785,7 +6785,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Refresh slice calculations for all nodes
|
// Refresh slice calculations for all nodes
|
||||||
match farmer_service.refresh_all_slice_calculations_async(&user_email).await {
|
match resource_provider_service.refresh_all_slice_calculations_async(&user_email).await {
|
||||||
Ok(updated_nodes) => {
|
Ok(updated_nodes) => {
|
||||||
|
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
@@ -6817,8 +6817,8 @@ impl DashboardController {
|
|||||||
})).build());
|
})).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize farmer service
|
// Initialize resource_provider service
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6829,7 +6829,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Sync all nodes with grid
|
// Sync all nodes with grid
|
||||||
match farmer_service.sync_all_nodes_with_grid_async(&user_email).await {
|
match resource_provider_service.sync_all_nodes_with_grid_async(&user_email).await {
|
||||||
Ok(synced_nodes) => {
|
Ok(synced_nodes) => {
|
||||||
|
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
@@ -6863,8 +6863,8 @@ impl DashboardController {
|
|||||||
|
|
||||||
let node_id = path.into_inner();
|
let node_id = path.into_inner();
|
||||||
|
|
||||||
// Initialize farmer service
|
// Initialize resource_provider service
|
||||||
let farmer_service = match crate::services::farmer::FarmerService::builder().build() {
|
let resource_provider_service = match crate::services::resource_provider::ResourceProviderService::builder().build() {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
return Ok(ResponseBuilder::internal_error().json(serde_json::json!({
|
||||||
@@ -6875,7 +6875,7 @@ impl DashboardController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get node slice details
|
// Get node slice details
|
||||||
match farmer_service.get_node_slice_details(&user_email, &node_id) {
|
match resource_provider_service.get_node_slice_details(&user_email, &node_id) {
|
||||||
Ok(slice_details) => {
|
Ok(slice_details) => {
|
||||||
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
Ok(ResponseBuilder::ok().json(serde_json::json!({
|
||||||
"success": false,
|
"success": false,
|
||||||
|
@@ -57,13 +57,13 @@ impl DocsController {
|
|||||||
render_template(&tmpl, "docs/getting_started.html", &ctx)
|
render_template(&tmpl, "docs/getting_started.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the 3Nodes documentation page
|
/// Renders the Mycelium Nodes documentation page
|
||||||
pub async fn three_nodes(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
pub async fn mycelium_nodes(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.active_page("docs")
|
.active_page("docs")
|
||||||
.build();
|
.build();
|
||||||
ctx.insert("active_section", "3nodes");
|
ctx.insert("active_section", "mycelium_nodes");
|
||||||
|
|
||||||
let config = get_app_config();
|
let config = get_app_config();
|
||||||
ctx.insert("gitea_enabled", &config.is_gitea_enabled());
|
ctx.insert("gitea_enabled", &config.is_gitea_enabled());
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ impl DocsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_template(&tmpl, "docs/3nodes.html", &ctx)
|
render_template(&tmpl, "docs/mycelium_nodes.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the compute resources documentation page
|
/// Renders the compute resources documentation page
|
||||||
|
@@ -13,7 +13,7 @@ use chrono::Utc;
|
|||||||
/// Form data for slice rental requests
|
/// Form data for slice rental requests
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct SliceRentalForm {
|
pub struct SliceRentalForm {
|
||||||
pub farmer_email: String,
|
pub resource_provider_email: String,
|
||||||
pub node_id: String,
|
pub node_id: String,
|
||||||
pub combination_id: String,
|
pub combination_id: String,
|
||||||
pub quantity: u32,
|
pub quantity: u32,
|
||||||
@@ -377,8 +377,8 @@ impl MarketplaceController {
|
|||||||
render_template(&tmpl, "marketplace/compute_resources.html", &ctx)
|
render_template(&tmpl, "marketplace/compute_resources.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the 3Nodes marketplace page with REAL farmer nodes from database
|
/// Renders the Mycelium Nodes marketplace page with REAL resource_provider nodes from database
|
||||||
pub async fn three_nodes(tmpl: web::Data<Tera>, session: Session, query: web::Query<std::collections::HashMap<String, String>>) -> Result<impl Responder> {
|
pub async fn mycelium_nodes(tmpl: web::Data<Tera>, session: Session, query: web::Query<std::collections::HashMap<String, String>>) -> Result<impl Responder> {
|
||||||
// Build services using established builder pattern
|
// Build services using established builder pattern
|
||||||
let currency_service = CurrencyService::builder()
|
let currency_service = CurrencyService::builder()
|
||||||
.build()
|
.build()
|
||||||
@@ -395,7 +395,7 @@ impl MarketplaceController {
|
|||||||
// Build context using ContextBuilder pattern
|
// Build context using ContextBuilder pattern
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.active_page("marketplace")
|
.active_page("marketplace")
|
||||||
.active_section("three_nodes")
|
.active_section("mycelium_nodes")
|
||||||
.user_if_available(&session)
|
.user_if_available(&session)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -411,7 +411,7 @@ impl MarketplaceController {
|
|||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let page_size = 12;
|
let page_size = 12;
|
||||||
|
|
||||||
// Get all real farmer nodes as marketplace products
|
// Get all real resource_provider nodes as marketplace products
|
||||||
let all_node_products = node_marketplace_service.get_all_marketplace_nodes();
|
let all_node_products = node_marketplace_service.get_all_marketplace_nodes();
|
||||||
|
|
||||||
// Clone query for reuse
|
// Clone query for reuse
|
||||||
@@ -484,7 +484,7 @@ impl MarketplaceController {
|
|||||||
ctx.insert("node_statistics", &node_marketplace_service.get_capacity_statistics());
|
ctx.insert("node_statistics", &node_marketplace_service.get_capacity_statistics());
|
||||||
ctx.insert("available_regions", &node_marketplace_service.get_available_regions());
|
ctx.insert("available_regions", &node_marketplace_service.get_available_regions());
|
||||||
|
|
||||||
render_template(&tmpl, "marketplace/three_nodes.html", &ctx)
|
render_template(&tmpl, "marketplace/mycelium_nodes.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the gateways marketplace page
|
/// Renders the gateways marketplace page
|
||||||
@@ -933,7 +933,7 @@ impl MarketplaceController {
|
|||||||
pub async fn show_slice_rental_form(
|
pub async fn show_slice_rental_form(
|
||||||
tmpl: web::Data<Tera>,
|
tmpl: web::Data<Tera>,
|
||||||
session: Session,
|
session: Session,
|
||||||
path: web::Path<(String, String, String)> // farmer_email, node_id, combination_id
|
path: web::Path<(String, String, String)> // resource_provider_email, node_id, combination_id
|
||||||
) -> Result<impl Responder> {
|
) -> Result<impl Responder> {
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.active_page("marketplace")
|
.active_page("marketplace")
|
||||||
@@ -951,7 +951,7 @@ impl MarketplaceController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (farmer_email, node_id, combination_id) = path.into_inner();
|
let (resource_provider_email, node_id, combination_id) = path.into_inner();
|
||||||
|
|
||||||
// Get slice details for the form
|
// Get slice details for the form
|
||||||
let node_marketplace_service = NodeMarketplaceService::builder()
|
let node_marketplace_service = NodeMarketplaceService::builder()
|
||||||
@@ -963,12 +963,12 @@ impl MarketplaceController {
|
|||||||
// Find the specific slice combination by checking product attributes
|
// Find the specific slice combination by checking product attributes
|
||||||
if let Some(slice_product) = slice_combinations.iter().find(|p| {
|
if let Some(slice_product) = slice_combinations.iter().find(|p| {
|
||||||
// Check if this product matches the requested slice
|
// Check if this product matches the requested slice
|
||||||
if let (Some(farmer_attr), Some(node_attr), Some(combo_attr)) = (
|
if let (Some(resource_provider_attr), Some(node_attr), Some(combo_attr)) = (
|
||||||
p.attributes.get("farmer_email"),
|
p.attributes.get("resource_provider_email"),
|
||||||
p.attributes.get("node_id"),
|
p.attributes.get("node_id"),
|
||||||
p.attributes.get("combination_id")
|
p.attributes.get("combination_id")
|
||||||
) {
|
) {
|
||||||
farmer_attr.value.as_str() == Some(&farmer_email) &&
|
resource_provider_attr.value.as_str() == Some(&resource_provider_email) &&
|
||||||
node_attr.value.as_str() == Some(&node_id) &&
|
node_attr.value.as_str() == Some(&node_id) &&
|
||||||
combo_attr.value.as_str() == Some(&combination_id)
|
combo_attr.value.as_str() == Some(&combination_id)
|
||||||
} else {
|
} else {
|
||||||
@@ -998,11 +998,11 @@ impl MarketplaceController {
|
|||||||
|
|
||||||
slice_info.insert("price_per_hour", serde_json::Value::String(slice_product.base_price.to_string()));
|
slice_info.insert("price_per_hour", serde_json::Value::String(slice_product.base_price.to_string()));
|
||||||
slice_info.insert("node_id", serde_json::Value::String(node_id.clone()));
|
slice_info.insert("node_id", serde_json::Value::String(node_id.clone()));
|
||||||
slice_info.insert("farmer_email", serde_json::Value::String(farmer_email.clone()));
|
slice_info.insert("resource_provider_email", serde_json::Value::String(resource_provider_email.clone()));
|
||||||
slice_info.insert("combination_id", serde_json::Value::String(combination_id.clone()));
|
slice_info.insert("combination_id", serde_json::Value::String(combination_id.clone()));
|
||||||
|
|
||||||
ctx.insert("slice", &slice_info);
|
ctx.insert("slice", &slice_info);
|
||||||
ctx.insert("farmer_email", &farmer_email);
|
ctx.insert("resource_provider_email", &resource_provider_email);
|
||||||
ctx.insert("node_id", &node_id);
|
ctx.insert("node_id", &node_id);
|
||||||
ctx.insert("combination_id", &combination_id);
|
ctx.insert("combination_id", &combination_id);
|
||||||
}
|
}
|
||||||
@@ -1109,7 +1109,7 @@ impl MarketplaceController {
|
|||||||
// Attempt to rent the slice with deployment options
|
// Attempt to rent the slice with deployment options
|
||||||
match slice_rental_service.rent_slice_combination_with_deployment(
|
match slice_rental_service.rent_slice_combination_with_deployment(
|
||||||
&user_email,
|
&user_email,
|
||||||
&form.farmer_email,
|
&form.resource_provider_email,
|
||||||
&form.node_id,
|
&form.node_id,
|
||||||
&form.combination_id,
|
&form.combination_id,
|
||||||
form.quantity,
|
form.quantity,
|
||||||
@@ -1134,7 +1134,7 @@ impl MarketplaceController {
|
|||||||
meta.insert("deployment_type".to_string(), serde_json::Value::String(form.deployment_type.clone()));
|
meta.insert("deployment_type".to_string(), serde_json::Value::String(form.deployment_type.clone()));
|
||||||
meta.insert("deployment_name".to_string(), serde_json::Value::String(deployment_name.clone()));
|
meta.insert("deployment_name".to_string(), serde_json::Value::String(deployment_name.clone()));
|
||||||
meta.insert("quantity".to_string(), serde_json::Value::Number(serde_json::Number::from(form.quantity)));
|
meta.insert("quantity".to_string(), serde_json::Value::Number(serde_json::Number::from(form.quantity)));
|
||||||
meta.insert("farmer_email".to_string(), serde_json::Value::String(form.farmer_email.clone()));
|
meta.insert("resource_provider_email".to_string(), serde_json::Value::String(form.resource_provider_email.clone()));
|
||||||
if form.deployment_type == "kubernetes" {
|
if form.deployment_type == "kubernetes" {
|
||||||
meta.insert("k8s_masters".to_string(), serde_json::Value::Number(serde_json::Number::from(form.k8s_masters.unwrap_or(1))));
|
meta.insert("k8s_masters".to_string(), serde_json::Value::Number(serde_json::Number::from(form.k8s_masters.unwrap_or(1))));
|
||||||
meta.insert("k8s_workers".to_string(), serde_json::Value::Number(serde_json::Number::from(form.k8s_workers.unwrap_or(1))));
|
meta.insert("k8s_workers".to_string(), serde_json::Value::Number(serde_json::Number::from(form.k8s_workers.unwrap_or(1))));
|
||||||
@@ -1206,7 +1206,7 @@ impl MarketplaceController {
|
|||||||
// Attempt to rent the slice with deployment options
|
// Attempt to rent the slice with deployment options
|
||||||
match slice_rental_service.rent_slice_combination_with_deployment(
|
match slice_rental_service.rent_slice_combination_with_deployment(
|
||||||
&user_email,
|
&user_email,
|
||||||
&form.farmer_email,
|
&form.resource_provider_email,
|
||||||
&form.node_id,
|
&form.node_id,
|
||||||
&form.combination_id,
|
&form.combination_id,
|
||||||
form.quantity,
|
form.quantity,
|
||||||
@@ -1231,7 +1231,7 @@ impl MarketplaceController {
|
|||||||
meta.insert("deployment_type".to_string(), serde_json::Value::String(form.deployment_type.clone()));
|
meta.insert("deployment_type".to_string(), serde_json::Value::String(form.deployment_type.clone()));
|
||||||
meta.insert("deployment_name".to_string(), serde_json::Value::String(form.deployment_name.clone()));
|
meta.insert("deployment_name".to_string(), serde_json::Value::String(form.deployment_name.clone()));
|
||||||
meta.insert("quantity".to_string(), serde_json::Value::Number(serde_json::Number::from(form.quantity)));
|
meta.insert("quantity".to_string(), serde_json::Value::Number(serde_json::Number::from(form.quantity)));
|
||||||
meta.insert("farmer_email".to_string(), serde_json::Value::String(form.farmer_email.clone()));
|
meta.insert("resource_provider_email".to_string(), serde_json::Value::String(form.resource_provider_email.clone()));
|
||||||
serde_json::Value::Object(meta.into_iter().collect())
|
serde_json::Value::Object(meta.into_iter().collect())
|
||||||
}),
|
}),
|
||||||
category: "slice_rental".to_string(),
|
category: "slice_rental".to_string(),
|
||||||
@@ -1270,7 +1270,7 @@ impl MarketplaceController {
|
|||||||
/// Request structure for slice rental with deployment options
|
/// Request structure for slice rental with deployment options
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct SliceRentalRequest {
|
pub struct SliceRentalRequest {
|
||||||
pub farmer_email: String,
|
pub resource_provider_email: String,
|
||||||
pub node_id: String,
|
pub node_id: String,
|
||||||
pub combination_id: String,
|
pub combination_id: String,
|
||||||
pub quantity: u32,
|
pub quantity: u32,
|
||||||
@@ -1376,8 +1376,8 @@ pub fn create_marketplace_product_from_app(app: &crate::models::user::PublishedA
|
|||||||
.category_id("application")
|
.category_id("application")
|
||||||
.base_price(rust_decimal::Decimal::from(app.monthly_revenue_usd.max(rust_decimal::Decimal::ONE))) // Use monthly revenue as base price, minimum $1
|
.base_price(rust_decimal::Decimal::from(app.monthly_revenue_usd.max(rust_decimal::Decimal::ONE))) // Use monthly revenue as base price, minimum $1
|
||||||
.base_currency("USD")
|
.base_currency("USD")
|
||||||
.provider_id("user-app-provider")
|
.provider_id("user-application-provider")
|
||||||
.provider_name("App Provider")
|
.provider_name("Application Provider")
|
||||||
.availability(availability)
|
.availability(availability)
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.add_attribute("app_type", ProductAttribute {
|
.add_attribute("app_type", ProductAttribute {
|
||||||
@@ -1503,7 +1503,7 @@ fn reconstruct_service_from_json(service_value: &serde_json::Value) -> Result<cr
|
|||||||
// Attempt to rent the slice combination
|
// Attempt to rent the slice combination
|
||||||
match slice_rental_service.rent_slice_combination(
|
match slice_rental_service.rent_slice_combination(
|
||||||
&user_email,
|
&user_email,
|
||||||
&form.farmer_email,
|
&form.resource_provider_email,
|
||||||
&form.node_id,
|
&form.node_id,
|
||||||
&form.combination_id,
|
&form.combination_id,
|
||||||
form.quantity,
|
form.quantity,
|
||||||
@@ -1602,7 +1602,7 @@ fn reconstruct_service_from_json(service_value: &serde_json::Value) -> Result<cr
|
|||||||
// Create SliceAssignmentRequest from the data
|
// Create SliceAssignmentRequest from the data
|
||||||
let assignment_request_obj = SliceAssignmentRequest {
|
let assignment_request_obj = SliceAssignmentRequest {
|
||||||
user_email: user_email.clone(),
|
user_email: user_email.clone(),
|
||||||
farmer_email: assignment_request.get("farmer_email")
|
resource_provider_email: assignment_request.get("resource_provider_email")
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.unwrap_or("unknown@example.com")
|
.unwrap_or("unknown@example.com")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
@@ -88,8 +88,8 @@ impl PublicController {
|
|||||||
render_template(&tmpl, "legal/terms.html", &ctx)
|
render_template(&tmpl, "legal/terms.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the farmers terms page
|
/// Renders the resource providers terms page
|
||||||
pub async fn terms_farmers(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
pub async fn terms_resource_providers(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.build();
|
.build();
|
||||||
ctx.insert("active_page", "terms");
|
ctx.insert("active_page", "terms");
|
||||||
@@ -105,7 +105,7 @@ impl PublicController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_template(&tmpl, "legal/terms-farmers.html", &ctx)
|
render_template(&tmpl, "legal/terms-resource_providers.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the service providers terms page
|
/// Renders the service providers terms page
|
||||||
@@ -128,8 +128,8 @@ impl PublicController {
|
|||||||
render_template(&tmpl, "legal/terms-service-providers.html", &ctx)
|
render_template(&tmpl, "legal/terms-service-providers.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the solution providers terms page
|
/// Renders the application providers terms page
|
||||||
pub async fn terms_solution_providers(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
pub async fn terms_application_providers(tmpl: web::Data<Tera>, session: Session) -> Result<impl Responder> {
|
||||||
let mut ctx = crate::models::builders::ContextBuilder::new()
|
let mut ctx = crate::models::builders::ContextBuilder::new()
|
||||||
.build();
|
.build();
|
||||||
ctx.insert("active_page", "terms");
|
ctx.insert("active_page", "terms");
|
||||||
@@ -145,7 +145,7 @@ impl PublicController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_template(&tmpl, "legal/terms-solution-providers.html", &ctx)
|
render_template(&tmpl, "legal/terms-application-providers.html", &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the users terms page
|
/// Renders the users terms page
|
||||||
|
@@ -173,7 +173,7 @@ impl RentalController {
|
|||||||
rental_id: rental_id.clone(),
|
rental_id: rental_id.clone(),
|
||||||
slice_combination_id: format!("combo-{}", rental_id),
|
slice_combination_id: format!("combo-{}", rental_id),
|
||||||
node_id: "node-placeholder".to_string(), // TODO: Get from product
|
node_id: "node-placeholder".to_string(), // TODO: Get from product
|
||||||
farmer_email: "farmer@example.com".to_string(), // TODO: Get from product
|
resource_provider_email: "resource_provider@example.com".to_string(), // TODO: Get from product
|
||||||
slice_allocation: crate::services::slice_calculator::SliceAllocation {
|
slice_allocation: crate::services::slice_calculator::SliceAllocation {
|
||||||
allocation_id: format!("alloc-{}", rental_id),
|
allocation_id: format!("alloc-{}", rental_id),
|
||||||
slice_combination_id: format!("combo-{}", rental_id),
|
slice_combination_id: format!("combo-{}", rental_id),
|
||||||
|
@@ -33,5 +33,5 @@ pub mod utils;
|
|||||||
|
|
||||||
// Re-export commonly used types
|
// Re-export commonly used types
|
||||||
pub use models::user::{User, NodeStakingOptions, FarmNode};
|
pub use models::user::{User, NodeStakingOptions, FarmNode};
|
||||||
pub use services::farmer::FarmerService;
|
pub use services::resource_provider::ResourceProviderService;
|
||||||
pub use services::user_persistence::UserPersistence;
|
pub use services::user_persistence::UserPersistence;
|
||||||
|
@@ -1234,11 +1234,11 @@ impl SessionDataBuilder {
|
|||||||
|
|
||||||
// MockDataBuilder removed - using persistent data only
|
// MockDataBuilder removed - using persistent data only
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// FARMER DATA BUILDER
|
// RESOURCE_PROVIDER DATA BUILDER
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FarmerDataBuilder {
|
pub struct ResourceProviderDataBuilder {
|
||||||
total_nodes: Option<i32>,
|
total_nodes: Option<i32>,
|
||||||
online_nodes: Option<i32>,
|
online_nodes: Option<i32>,
|
||||||
total_capacity: Option<crate::models::user::NodeCapacity>,
|
total_capacity: Option<crate::models::user::NodeCapacity>,
|
||||||
@@ -1251,7 +1251,7 @@ pub struct FarmerDataBuilder {
|
|||||||
active_slices: Option<i32>,
|
active_slices: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FarmerDataBuilder {
|
impl ResourceProviderDataBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
@@ -1373,8 +1373,8 @@ impl FarmerDataBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<crate::models::user::FarmerData, String> {
|
pub fn build(self) -> Result<crate::models::user::ResourceProviderData, String> {
|
||||||
Ok(crate::models::user::FarmerData {
|
Ok(crate::models::user::ResourceProviderData {
|
||||||
total_nodes: self.total_nodes.unwrap_or(0),
|
total_nodes: self.total_nodes.unwrap_or(0),
|
||||||
online_nodes: self.online_nodes.unwrap_or(0),
|
online_nodes: self.online_nodes.unwrap_or(0),
|
||||||
total_capacity: self.total_capacity.unwrap_or(crate::models::user::NodeCapacity {
|
total_capacity: self.total_capacity.unwrap_or(crate::models::user::NodeCapacity {
|
||||||
@@ -1445,8 +1445,8 @@ impl FarmerDataBuilder {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SliceProductBuilder {
|
pub struct SliceProductBuilder {
|
||||||
farmer_id: Option<String>,
|
resource_provider_id: Option<String>,
|
||||||
farmer_name: Option<String>,
|
resource_provider_name: Option<String>,
|
||||||
slice_name: Option<String>,
|
slice_name: Option<String>,
|
||||||
cpu_cores: Option<i32>,
|
cpu_cores: Option<i32>,
|
||||||
memory_gb: Option<i32>,
|
memory_gb: Option<i32>,
|
||||||
@@ -1464,13 +1464,13 @@ impl SliceProductBuilder {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn farmer_id(mut self, farmer_id: impl Into<String>) -> Self {
|
pub fn resource_provider_id(mut self, resource_provider_id: impl Into<String>) -> Self {
|
||||||
self.farmer_id = Some(farmer_id.into());
|
self.resource_provider_id = Some(resource_provider_id.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn farmer_name(mut self, farmer_name: impl Into<String>) -> Self {
|
pub fn resource_provider_name(mut self, resource_provider_name: impl Into<String>) -> Self {
|
||||||
self.farmer_name = Some(farmer_name.into());
|
self.resource_provider_name = Some(resource_provider_name.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1525,8 +1525,8 @@ impl SliceProductBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<crate::models::product::Product, String> {
|
pub fn build(self) -> Result<crate::models::product::Product, String> {
|
||||||
let farmer_id = self.farmer_id.ok_or("farmer_id is required")?;
|
let resource_provider_id = self.resource_provider_id.ok_or("resource_provider_id is required")?;
|
||||||
let farmer_name = self.farmer_name.ok_or("farmer_name is required")?;
|
let resource_provider_name = self.resource_provider_name.ok_or("resource_provider_name is required")?;
|
||||||
let slice_name = self.slice_name.ok_or("slice_name is required")?;
|
let slice_name = self.slice_name.ok_or("slice_name is required")?;
|
||||||
let cpu_cores = self.cpu_cores.ok_or("cpu_cores is required")?;
|
let cpu_cores = self.cpu_cores.ok_or("cpu_cores is required")?;
|
||||||
let memory_gb = self.memory_gb.ok_or("memory_gb is required")?;
|
let memory_gb = self.memory_gb.ok_or("memory_gb is required")?;
|
||||||
@@ -1552,8 +1552,8 @@ impl SliceProductBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(crate::models::product::Product::create_slice_product(
|
Ok(crate::models::product::Product::create_slice_product(
|
||||||
farmer_id,
|
resource_provider_id,
|
||||||
farmer_name,
|
resource_provider_name,
|
||||||
slice_name,
|
slice_name,
|
||||||
slice_config,
|
slice_config,
|
||||||
price_per_hour,
|
price_per_hour,
|
||||||
@@ -1731,7 +1731,7 @@ impl FarmNodeBuilder {
|
|||||||
last_seen: Some(self.last_seen.unwrap_or_else(|| chrono::Utc::now())),
|
last_seen: Some(self.last_seen.unwrap_or_else(|| chrono::Utc::now())),
|
||||||
health_score: self.health_score.unwrap_or(0.0),
|
health_score: self.health_score.unwrap_or(0.0),
|
||||||
region: self.region.unwrap_or_else(|| "Unknown".to_string()),
|
region: self.region.unwrap_or_else(|| "Unknown".to_string()),
|
||||||
node_type: self.node_type.unwrap_or_else(|| "3Node".to_string()),
|
node_type: self.node_type.unwrap_or_else(|| "MyceliumNode".to_string()),
|
||||||
slice_formats: None,
|
slice_formats: None,
|
||||||
rental_options: self.rental_options.map(|ro| serde_json::to_value(&ro).unwrap_or_default()),
|
rental_options: self.rental_options.map(|ro| serde_json::to_value(&ro).unwrap_or_default()),
|
||||||
staking_options: None,
|
staking_options: None,
|
||||||
@@ -2081,7 +2081,7 @@ impl NodeRentalBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FarmerRentalEarningBuilder {
|
pub struct ResourceProviderRentalEarningBuilder {
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
node_id: Option<String>,
|
node_id: Option<String>,
|
||||||
rental_id: Option<String>,
|
rental_id: Option<String>,
|
||||||
@@ -2093,7 +2093,7 @@ pub struct FarmerRentalEarningBuilder {
|
|||||||
payment_status: Option<crate::models::user::PaymentStatus>,
|
payment_status: Option<crate::models::user::PaymentStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FarmerRentalEarningBuilder {
|
impl ResourceProviderRentalEarningBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
@@ -2143,10 +2143,10 @@ impl FarmerRentalEarningBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<crate::models::user::FarmerRentalEarning, String> {
|
pub fn build(self) -> Result<crate::models::user::ResourceProviderRentalEarning, String> {
|
||||||
let id = self.id.unwrap_or_else(|| format!("earning_{}", uuid::Uuid::new_v4()));
|
let id = self.id.unwrap_or_else(|| format!("earning_{}", uuid::Uuid::new_v4()));
|
||||||
|
|
||||||
Ok(crate::models::user::FarmerRentalEarning {
|
Ok(crate::models::user::ResourceProviderRentalEarning {
|
||||||
id,
|
id,
|
||||||
node_id: self.node_id.ok_or("node_id is required")?,
|
node_id: self.node_id.ok_or("node_id is required")?,
|
||||||
rental_id: self.rental_id.ok_or("rental_id is required")?,
|
rental_id: self.rental_id.ok_or("rental_id is required")?,
|
||||||
@@ -2469,8 +2469,8 @@ impl NodeCreationDataBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<crate::services::farmer::NodeCreationData, String> {
|
pub fn build(self) -> Result<crate::services::resource_provider::NodeCreationData, String> {
|
||||||
Ok(crate::services::farmer::NodeCreationData {
|
Ok(crate::services::resource_provider::NodeCreationData {
|
||||||
name: self.name.ok_or("name is required")?,
|
name: self.name.ok_or("name is required")?,
|
||||||
location: self.location.ok_or("location is required")?,
|
location: self.location.ok_or("location is required")?,
|
||||||
cpu_cores: self.cpu_cores.unwrap_or(4),
|
cpu_cores: self.cpu_cores.unwrap_or(4),
|
||||||
|
@@ -305,10 +305,10 @@ pub enum SliceType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Product {
|
impl Product {
|
||||||
/// Create a slice product from farmer configuration
|
/// Create a slice product from resource_provider configuration
|
||||||
pub fn create_slice_product(
|
pub fn create_slice_product(
|
||||||
farmer_id: String,
|
resource_provider_id: String,
|
||||||
farmer_name: String,
|
resource_provider_name: String,
|
||||||
slice_name: String,
|
slice_name: String,
|
||||||
slice_config: SliceConfiguration,
|
slice_config: SliceConfiguration,
|
||||||
price_per_hour: Decimal,
|
price_per_hour: Decimal,
|
||||||
@@ -322,8 +322,8 @@ impl Product {
|
|||||||
slice_config.cpu_cores, slice_config.memory_gb, slice_config.storage_gb),
|
slice_config.cpu_cores, slice_config.memory_gb, slice_config.storage_gb),
|
||||||
price_per_hour,
|
price_per_hour,
|
||||||
"USD".to_string(),
|
"USD".to_string(),
|
||||||
farmer_id,
|
resource_provider_id,
|
||||||
farmer_name,
|
resource_provider_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add slice-specific attributes
|
// Add slice-specific attributes
|
||||||
@@ -448,13 +448,13 @@ impl Product {
|
|||||||
/// Create a full node product from a FarmNode
|
/// Create a full node product from a FarmNode
|
||||||
pub fn create_full_node_product(
|
pub fn create_full_node_product(
|
||||||
node: &crate::models::user::FarmNode,
|
node: &crate::models::user::FarmNode,
|
||||||
farmer_email: &str,
|
resource_provider_email: &str,
|
||||||
farmer_name: &str,
|
resource_provider_name: &str,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut product = Product {
|
let mut product = Product {
|
||||||
id: format!("fullnode_{}", node.id),
|
id: format!("fullnode_{}", node.id),
|
||||||
name: format!("Full Node: {}", node.name),
|
name: format!("Full Node: {}", node.name),
|
||||||
category_id: "3nodes".to_string(),
|
category_id: "mycelium_nodes".to_string(),
|
||||||
description: format!(
|
description: format!(
|
||||||
"Exclusive access to {} with {} CPU cores, {}GB RAM, {}GB storage in {}",
|
"Exclusive access to {} with {} CPU cores, {}GB RAM, {}GB storage in {}",
|
||||||
node.name, node.capacity.cpu_cores, node.capacity.memory_gb,
|
node.name, node.capacity.cpu_cores, node.capacity.memory_gb,
|
||||||
@@ -469,8 +469,8 @@ impl Product {
|
|||||||
.unwrap_or_else(|| Decimal::from(200)), // Default price
|
.unwrap_or_else(|| Decimal::from(200)), // Default price
|
||||||
base_currency: "USD".to_string(),
|
base_currency: "USD".to_string(),
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
provider_id: farmer_email.to_string(),
|
provider_id: resource_provider_email.to_string(),
|
||||||
provider_name: farmer_name.to_string(),
|
provider_name: resource_provider_name.to_string(),
|
||||||
availability: match node.availability_status {
|
availability: match node.availability_status {
|
||||||
crate::models::user::NodeAvailabilityStatus::Available => ProductAvailability::Available,
|
crate::models::user::NodeAvailabilityStatus::Available => ProductAvailability::Available,
|
||||||
crate::models::user::NodeAvailabilityStatus::PartiallyRented => ProductAvailability::Limited,
|
crate::models::user::NodeAvailabilityStatus::PartiallyRented => ProductAvailability::Limited,
|
||||||
|
@@ -146,7 +146,7 @@ pub struct RegionDeployments {
|
|||||||
pub gateways: i32,
|
pub gateways: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Node information for farmer dashboard
|
/// Node information for resource_provider dashboard
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct NodeInfo {
|
pub struct NodeInfo {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -200,14 +200,14 @@ impl std::fmt::Display for NodeStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default maintenance window for farmer settings
|
/// Default maintenance window for resource_provider settings
|
||||||
pub fn default_maintenance_window() -> String {
|
pub fn default_maintenance_window() -> String {
|
||||||
"02:00-04:00 UTC".to_string()
|
"02:00-04:00 UTC".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Farmer configuration settings
|
/// ResourceProvider configuration settings
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FarmerSettings {
|
pub struct ResourceProviderSettings {
|
||||||
pub auto_accept_reserved_slices: bool,
|
pub auto_accept_reserved_slices: bool,
|
||||||
pub maintenance_window: String, // e.g., "02:00-04:00 UTC"
|
pub maintenance_window: String, // e.g., "02:00-04:00 UTC"
|
||||||
pub notification_email: Option<String>,
|
pub notification_email: Option<String>,
|
||||||
@@ -229,7 +229,7 @@ pub struct FarmerSettings {
|
|||||||
pub preferred_regions: Vec<String>,
|
pub preferred_regions: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FarmerSettings {
|
impl Default for ResourceProviderSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
auto_accept_reserved_slices: true,
|
auto_accept_reserved_slices: true,
|
||||||
@@ -312,7 +312,7 @@ impl Default for LiquidityPoolConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pool_id: String::new(),
|
pool_id: String::new(),
|
||||||
base_token: "TFC".to_string(),
|
base_token: "MC".to_string(),
|
||||||
quote_token: "USD".to_string(),
|
quote_token: "USD".to_string(),
|
||||||
fee_tier: 0.3,
|
fee_tier: 0.3,
|
||||||
minimum_liquidity: rust_decimal_macros::dec!(100.0),
|
minimum_liquidity: rust_decimal_macros::dec!(100.0),
|
||||||
@@ -324,7 +324,7 @@ impl Default for LiquidityPoolConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FarmerData {
|
impl Default for ResourceProviderData {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
total_nodes: 0,
|
total_nodes: 0,
|
||||||
@@ -364,8 +364,8 @@ impl Default for YieldFarmConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
farm_id: String::new(),
|
farm_id: String::new(),
|
||||||
reward_token: "TFC".to_string(),
|
reward_token: "MC".to_string(),
|
||||||
staked_token: "TFC".to_string(),
|
staked_token: "MC".to_string(),
|
||||||
apr: 12.0,
|
apr: 12.0,
|
||||||
lock_duration_days: 365,
|
lock_duration_days: 365,
|
||||||
minimum_stake: rust_decimal_macros::dec!(1000.0),
|
minimum_stake: rust_decimal_macros::dec!(1000.0),
|
||||||
@@ -405,11 +405,11 @@ impl Default for ServiceLevelAgreement {
|
|||||||
|
|
||||||
/// Application deployment configuration
|
/// Application deployment configuration
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct AppDeployment {
|
pub struct ApplicationDeployment {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub app_id: String,
|
pub app_id: String,
|
||||||
pub customer_email: String,
|
pub customer_email: String,
|
||||||
pub deployment_status: AppDeploymentStatus,
|
pub deployment_status: ApplicationDeploymentStatus,
|
||||||
pub resource_allocation: ResourceUtilization,
|
pub resource_allocation: ResourceUtilization,
|
||||||
pub monthly_cost: rust_decimal::Decimal,
|
pub monthly_cost: rust_decimal::Decimal,
|
||||||
pub deployed_at: Option<DateTime<Utc>>,
|
pub deployed_at: Option<DateTime<Utc>>,
|
||||||
@@ -418,9 +418,9 @@ pub struct AppDeployment {
|
|||||||
pub monitoring_enabled: bool,
|
pub monitoring_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// App deployment status
|
/// Application deployment status
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum AppDeploymentStatus {
|
pub enum ApplicationDeploymentStatus {
|
||||||
Pending,
|
Pending,
|
||||||
Deploying,
|
Deploying,
|
||||||
Running,
|
Running,
|
||||||
@@ -429,7 +429,7 @@ pub enum AppDeploymentStatus {
|
|||||||
Maintenance,
|
Maintenance,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppDeploymentStatus {
|
impl Default for ApplicationDeploymentStatus {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Pending
|
Self::Pending
|
||||||
}
|
}
|
||||||
@@ -761,9 +761,9 @@ pub struct QuickAction {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Farmer-specific data
|
/// ResourceProvider-specific data
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FarmerData {
|
pub struct ResourceProviderData {
|
||||||
pub total_nodes: i32,
|
pub total_nodes: i32,
|
||||||
pub active_nodes: i32,
|
pub active_nodes: i32,
|
||||||
pub total_monthly_earnings_usd: i32,
|
pub total_monthly_earnings_usd: i32,
|
||||||
@@ -1224,7 +1224,7 @@ where
|
|||||||
.map_err(serde::de::Error::custom)
|
.map_err(serde::de::Error::custom)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Node group for farmer organization
|
/// Node group for resource_provider organization
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct NodeGroup {
|
pub struct NodeGroup {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -1358,7 +1358,7 @@ pub struct NodeCapacity {
|
|||||||
pub ram_gb: i32,
|
pub ram_gb: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Farmer node information
|
/// ResourceProvider node information
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FarmNode {
|
pub struct FarmNode {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -1418,7 +1418,7 @@ pub struct FarmNode {
|
|||||||
pub health_score: f32,
|
pub health_score: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Earnings record for farmer data
|
/// Earnings record for resource_provider data
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct EarningsRecord {
|
pub struct EarningsRecord {
|
||||||
pub date: String,
|
pub date: String,
|
||||||
@@ -1428,7 +1428,7 @@ pub struct EarningsRecord {
|
|||||||
pub source: String,
|
pub source: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Group statistics for farmer dashboard
|
/// Group statistics for resource_provider dashboard
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct GroupStatistics {
|
pub struct GroupStatistics {
|
||||||
pub group_name: String,
|
pub group_name: String,
|
||||||
@@ -1517,7 +1517,7 @@ pub struct GridNodeData {
|
|||||||
|
|
||||||
/// Additional missing user model types
|
/// Additional missing user model types
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FarmerRentalEarning {
|
pub struct ResourceProviderRentalEarning {
|
||||||
pub date: String,
|
pub date: String,
|
||||||
pub amount: Decimal,
|
pub amount: Decimal,
|
||||||
pub rental_id: String,
|
pub rental_id: String,
|
||||||
|
@@ -46,13 +46,13 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
// Marketplace routes
|
// Marketplace routes
|
||||||
.route("/marketplace", web::get().to(MarketplaceController::dashboard))
|
.route("/marketplace", web::get().to(MarketplaceController::dashboard))
|
||||||
.route("/marketplace/compute", web::get().to(MarketplaceController::compute_resources))
|
.route("/marketplace/compute", web::get().to(MarketplaceController::compute_resources))
|
||||||
.route("/marketplace/3nodes", web::get().to(MarketplaceController::three_nodes))
|
.route("/marketplace/mycelium_nodes", web::get().to(MarketplaceController::mycelium_nodes))
|
||||||
.route("/marketplace/gateways", web::get().to(MarketplaceController::gateways))
|
.route("/marketplace/gateways", web::get().to(MarketplaceController::gateways))
|
||||||
.route("/marketplace/applications", web::get().to(MarketplaceController::applications))
|
.route("/marketplace/applications", web::get().to(MarketplaceController::applications))
|
||||||
.route("/marketplace/services", web::get().to(MarketplaceController::services))
|
.route("/marketplace/services", web::get().to(MarketplaceController::services))
|
||||||
.route("/marketplace/statistics", web::get().to(MarketplaceController::statistics))
|
.route("/marketplace/statistics", web::get().to(MarketplaceController::statistics))
|
||||||
// Slice rental routes
|
// Slice rental routes
|
||||||
.route("/marketplace/slice/rent/{farmer_email}/{node_id}/{combination_id}", web::get().to(MarketplaceController::show_slice_rental_form))
|
.route("/marketplace/slice/rent/{resource_provider_email}/{node_id}/{combination_id}", web::get().to(MarketplaceController::show_slice_rental_form))
|
||||||
.route("/marketplace/slice/rent", web::post().to(MarketplaceController::process_slice_rental))
|
.route("/marketplace/slice/rent", web::post().to(MarketplaceController::process_slice_rental))
|
||||||
// .route("/marketplace/rent-slice", web::post().to(MarketplaceController::rent_slice)) // Legacy route [DISABLED]
|
// .route("/marketplace/rent-slice", web::post().to(MarketplaceController::rent_slice)) // Legacy route [DISABLED]
|
||||||
// Product routes
|
// Product routes
|
||||||
@@ -107,8 +107,8 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
.route("/dashboard/slice-rentals/{id}/manage", web::post().to(DashboardController::manage_slice_rental_deployment))
|
.route("/dashboard/slice-rentals/{id}/manage", web::post().to(DashboardController::manage_slice_rental_deployment))
|
||||||
.route("/dashboard/slice-rentals/{id}", web::delete().to(DashboardController::cancel_slice_rental))
|
.route("/dashboard/slice-rentals/{id}", web::delete().to(DashboardController::cancel_slice_rental))
|
||||||
.route("/dashboard/user/slice-rentals/{id}", web::post().to(DashboardController::manage_slice_rental))
|
.route("/dashboard/user/slice-rentals/{id}", web::post().to(DashboardController::manage_slice_rental))
|
||||||
.route("/dashboard/farmer-data", web::get().to(DashboardController::farmer_data_api))
|
.route("/dashboard/resource_provider-data", web::get().to(DashboardController::resource_provider_data_api))
|
||||||
.route("/dashboard/app-provider-data", web::get().to(DashboardController::app_provider_data_api))
|
.route("/dashboard/application-provider-data", web::get().to(DashboardController::application_provider_data_api))
|
||||||
.route("/dashboard/slice-products", web::get().to(DashboardController::get_slice_products))
|
.route("/dashboard/slice-products", web::get().to(DashboardController::get_slice_products))
|
||||||
.route("/dashboard/slice-products", web::post().to(DashboardController::create_slice_product))
|
.route("/dashboard/slice-products", web::post().to(DashboardController::create_slice_product))
|
||||||
.route("/dashboard/slice-products/{id}", web::delete().to(DashboardController::delete_slice_product))
|
.route("/dashboard/slice-products/{id}", web::delete().to(DashboardController::delete_slice_product))
|
||||||
@@ -116,19 +116,19 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
.route("/dashboard/slice-details/{id}", web::get().to(DashboardController::get_slice_details))
|
.route("/dashboard/slice-details/{id}", web::get().to(DashboardController::get_slice_details))
|
||||||
.route("/dashboard/slice-configuration/{id}", web::put().to(DashboardController::update_slice_configuration))
|
.route("/dashboard/slice-configuration/{id}", web::put().to(DashboardController::update_slice_configuration))
|
||||||
.route("/dashboard/service-provider-data", web::get().to(DashboardController::service_provider_data_api))
|
.route("/dashboard/service-provider-data", web::get().to(DashboardController::service_provider_data_api))
|
||||||
// Farmer management API routes
|
// Resource provider management API routes
|
||||||
.route("/dashboard/farm-nodes", web::post().to(DashboardController::add_farm_node))
|
.route("/dashboard/resource_provider-nodes", web::post().to(DashboardController::add_farm_node))
|
||||||
.route("/dashboard/farm-nodes-enhanced", web::post().to(DashboardController::add_farm_node_enhanced))
|
.route("/dashboard/resource_provider-nodes-enhanced", web::post().to(DashboardController::add_farm_node_enhanced))
|
||||||
.route("/dashboard/farm-nodes/{id}", web::get().to(DashboardController::get_node_details))
|
.route("/dashboard/resource_provider-nodes/{id}", web::get().to(DashboardController::get_node_details))
|
||||||
.route("/dashboard/farm-nodes/{id}", web::put().to(DashboardController::update_node_comprehensive))
|
.route("/dashboard/resource_provider-nodes/{id}", web::put().to(DashboardController::update_node_comprehensive))
|
||||||
.route("/dashboard/farm-nodes/{id}/status", web::put().to(DashboardController::update_node_status))
|
.route("/dashboard/resource_provider-nodes/{id}/status", web::put().to(DashboardController::update_node_status))
|
||||||
// Farmer slice management API routes
|
// Resource provider slice management API routes
|
||||||
.route("/dashboard/farmer/slice-calculations/refresh", web::post().to(DashboardController::refresh_slice_calculations))
|
.route("/dashboard/resource_provider/slice-calculations/refresh", web::post().to(DashboardController::refresh_slice_calculations))
|
||||||
.route("/dashboard/farmer/grid-sync", web::post().to(DashboardController::sync_with_grid))
|
.route("/dashboard/resource_provider/grid-sync", web::post().to(DashboardController::sync_with_grid))
|
||||||
.route("/dashboard/farmer/nodes/{id}/slices", web::get().to(DashboardController::get_node_slices))
|
.route("/dashboard/resource_provider/nodes/{id}/slices", web::get().to(DashboardController::get_node_slices))
|
||||||
.route("/dashboard/farmer/slice-statistics", web::get().to(DashboardController::get_slice_statistics))
|
.route("/dashboard/resource_provider/slice-statistics", web::get().to(DashboardController::get_slice_statistics))
|
||||||
.route("/dashboard/farm-nodes/{id}", web::delete().to(DashboardController::delete_node))
|
.route("/dashboard/resource_provider-nodes/{id}", web::delete().to(DashboardController::delete_node))
|
||||||
.route("/dashboard/farm-nodes/{id}/configuration", web::put().to(DashboardController::update_node_configuration))
|
.route("/dashboard/resource_provider-nodes/{id}/configuration", web::put().to(DashboardController::update_node_configuration))
|
||||||
.route("/dashboard/default-slice-formats", web::get().to(DashboardController::get_default_slice_formats))
|
.route("/dashboard/default-slice-formats", web::get().to(DashboardController::get_default_slice_formats))
|
||||||
.route("/dashboard/default-slice-details/{id}", web::get().to(DashboardController::get_default_slice_details))
|
.route("/dashboard/default-slice-details/{id}", web::get().to(DashboardController::get_default_slice_details))
|
||||||
.route("/dashboard/default-slice-customization/{id}", web::put().to(DashboardController::save_default_slice_customization))
|
.route("/dashboard/default-slice-customization/{id}", web::put().to(DashboardController::save_default_slice_customization))
|
||||||
@@ -148,8 +148,8 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
.route("/dashboard/node-groups/{id}", web::delete().to(DashboardController::delete_custom_node_group))
|
.route("/dashboard/node-groups/{id}", web::delete().to(DashboardController::delete_custom_node_group))
|
||||||
.route("/dashboard/nodes/assign-group", web::post().to(DashboardController::assign_node_to_group))
|
.route("/dashboard/nodes/assign-group", web::post().to(DashboardController::assign_node_to_group))
|
||||||
// Node staking API routes
|
// Node staking API routes
|
||||||
.route("/dashboard/farm-nodes/{id}/stake", web::post().to(DashboardController::stake_on_node))
|
.route("/dashboard/resource_provider-nodes/{id}/stake", web::post().to(DashboardController::stake_on_node))
|
||||||
.route("/dashboard/farm-nodes/{id}/staking", web::put().to(DashboardController::update_node_staking))
|
.route("/dashboard/resource_provider-nodes/{id}/staking", web::put().to(DashboardController::update_node_staking))
|
||||||
.route("/dashboard/staking/statistics", web::get().to(DashboardController::get_staking_statistics))
|
.route("/dashboard/staking/statistics", web::get().to(DashboardController::get_staking_statistics))
|
||||||
// Service management API routes
|
// Service management API routes
|
||||||
.route("/dashboard/services", web::get().to(DashboardController::get_user_services))
|
.route("/dashboard/services", web::get().to(DashboardController::get_user_services))
|
||||||
@@ -245,7 +245,7 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
// Documentation routes
|
// Documentation routes
|
||||||
.route("/docs", web::get().to(DocsController::index))
|
.route("/docs", web::get().to(DocsController::index))
|
||||||
.route("/docs/getting-started", web::get().to(DocsController::getting_started))
|
.route("/docs/getting-started", web::get().to(DocsController::getting_started))
|
||||||
.route("/docs/3nodes", web::get().to(DocsController::three_nodes))
|
.route("/docs/mycelium_nodes", web::get().to(DocsController::mycelium_nodes))
|
||||||
.route("/docs/compute", web::get().to(DocsController::compute))
|
.route("/docs/compute", web::get().to(DocsController::compute))
|
||||||
.route("/docs/gateways", web::get().to(DocsController::gateways))
|
.route("/docs/gateways", web::get().to(DocsController::gateways))
|
||||||
.route("/docs/applications", web::get().to(DocsController::applications))
|
.route("/docs/applications", web::get().to(DocsController::applications))
|
||||||
@@ -260,8 +260,8 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
.wrap(JwtAuth) // Apply authentication middleware to all dashboard routes
|
.wrap(JwtAuth) // Apply authentication middleware to all dashboard routes
|
||||||
.route("", web::get().to(DashboardController::index))
|
.route("", web::get().to(DashboardController::index))
|
||||||
.route("/user", web::get().to(DashboardController::user_section))
|
.route("/user", web::get().to(DashboardController::user_section))
|
||||||
.route("/farmer", web::get().to(DashboardController::farmer_section))
|
.route("/resource_provider", web::get().to(DashboardController::resource_provider_section))
|
||||||
.route("/app-provider", web::get().to(DashboardController::app_provider_section))
|
.route("/application-provider", web::get().to(DashboardController::application_provider_section))
|
||||||
.route("/service-provider", web::get().to(DashboardController::service_provider_section))
|
.route("/service-provider", web::get().to(DashboardController::service_provider_section))
|
||||||
|
|
||||||
// Shopping routes - embedded in dashboard
|
// Shopping routes - embedded in dashboard
|
||||||
@@ -282,9 +282,9 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
// Public information routes (legal, changelog, roadmap)
|
// Public information routes (legal, changelog, roadmap)
|
||||||
.route("/privacy", web::get().to(PublicController::privacy))
|
.route("/privacy", web::get().to(PublicController::privacy))
|
||||||
.route("/terms", web::get().to(PublicController::terms))
|
.route("/terms", web::get().to(PublicController::terms))
|
||||||
.route("/terms/farmers", web::get().to(PublicController::terms_farmers))
|
.route("/terms/resource_providers", web::get().to(PublicController::terms_resource_providers))
|
||||||
.route("/terms/service-providers", web::get().to(PublicController::terms_service_providers))
|
.route("/terms/service-providers", web::get().to(PublicController::terms_service_providers))
|
||||||
.route("/terms/solution-providers", web::get().to(PublicController::terms_solution_providers))
|
.route("/terms/application-providers", web::get().to(PublicController::terms_application_providers))
|
||||||
.route("/terms/users", web::get().to(PublicController::terms_users))
|
.route("/terms/users", web::get().to(PublicController::terms_users))
|
||||||
.route("/changelog", web::get().to(PublicController::changelog))
|
.route("/changelog", web::get().to(PublicController::changelog))
|
||||||
.route("/roadmap", web::get().to(PublicController::roadmap));
|
.route("/roadmap", web::get().to(PublicController::roadmap));
|
||||||
|
@@ -99,11 +99,11 @@ impl CurrencyService {
|
|||||||
last_updated: chrono::Utc::now(),
|
last_updated: chrono::Utc::now(),
|
||||||
},
|
},
|
||||||
Currency {
|
Currency {
|
||||||
code: "TFC".to_string(),
|
code: "MC".to_string(),
|
||||||
name: "ThreeFold Credits".to_string(),
|
name: "Mycelium Credit".to_string(),
|
||||||
symbol: "TFC".to_string(),
|
symbol: "MC".to_string(),
|
||||||
currency_type: crate::models::currency::CurrencyType::Custom("credits".to_string()),
|
currency_type: crate::models::currency::CurrencyType::Custom("credits".to_string()),
|
||||||
exchange_rate_to_base: dec!(1.0), // 1 TFC = 1 USD
|
exchange_rate_to_base: dec!(1.0), // 1 MC = 1 USD
|
||||||
is_base_currency: false,
|
is_base_currency: false,
|
||||||
decimal_places: 2,
|
decimal_places: 2,
|
||||||
is_active: true,
|
is_active: true,
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
pub mod auto_topup;
|
pub mod auto_topup;
|
||||||
pub mod currency;
|
pub mod currency;
|
||||||
pub mod factory;
|
pub mod factory;
|
||||||
pub mod farmer;
|
pub mod resource_provider;
|
||||||
pub mod grid;
|
pub mod grid;
|
||||||
pub mod instant_purchase;
|
pub mod instant_purchase;
|
||||||
pub mod navbar;
|
pub mod navbar;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
//! Node marketplace service for aggregating farmer nodes into marketplace products
|
//! Node marketplace service for aggregating resource_provider nodes into marketplace products
|
||||||
//! Follows the established builder pattern for consistent API design
|
//! Follows the established builder pattern for consistent API design
|
||||||
|
|
||||||
use crate::models::user::FarmNode;
|
use crate::models::user::FarmNode;
|
||||||
@@ -11,7 +11,7 @@ use rust_decimal::prelude::ToPrimitive;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// Service for converting farmer nodes to marketplace products
|
/// Service for converting resource_provider nodes to marketplace products
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NodeMarketplaceService {
|
pub struct NodeMarketplaceService {
|
||||||
currency_service: CurrencyService,
|
currency_service: CurrencyService,
|
||||||
@@ -89,7 +89,7 @@ impl NodeMarketplaceService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all farmer nodes as marketplace products
|
/// Get all resource_provider nodes as marketplace products
|
||||||
pub fn get_all_marketplace_nodes(&self) -> Vec<Product> {
|
pub fn get_all_marketplace_nodes(&self) -> Vec<Product> {
|
||||||
let mut all_products = Vec::new();
|
let mut all_products = Vec::new();
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ impl NodeMarketplaceService {
|
|||||||
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
// Filter by node type and status
|
// Filter by node type and status
|
||||||
if node.node_type == "3Node" && (self.include_offline_nodes || self.is_node_online(&node)) {
|
if node.node_type == "MyceliumNode" && (self.include_offline_nodes || self.is_node_online(&node)) {
|
||||||
if let Ok(product) = self.convert_node_to_product(&node, &user_email) {
|
if let Ok(product) = self.convert_node_to_product(&node, &user_email) {
|
||||||
all_products.push(product);
|
all_products.push(product);
|
||||||
}
|
}
|
||||||
@@ -125,16 +125,16 @@ impl NodeMarketplaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert FarmNode to Product using builder pattern
|
/// Convert FarmNode to Product using builder pattern
|
||||||
pub fn convert_node_to_product(&self, node: &FarmNode, farmer_email: &str) -> Result<Product, String> {
|
pub fn convert_node_to_product(&self, node: &FarmNode, resource_provider_email: &str) -> Result<Product, String> {
|
||||||
// Calculate price based on node capacity
|
// Calculate price based on node capacity
|
||||||
let hourly_price = self.calculate_node_price(node)?;
|
let hourly_price = self.calculate_node_price(node)?;
|
||||||
|
|
||||||
// Create product attributes with node specifications
|
// Create product attributes with node specifications
|
||||||
let mut attributes = HashMap::new();
|
let mut attributes = HashMap::new();
|
||||||
|
|
||||||
attributes.insert("farmer_email".to_string(), crate::models::product::ProductAttribute {
|
attributes.insert("resource_provider_email".to_string(), crate::models::product::ProductAttribute {
|
||||||
key: "farmer_email".to_string(),
|
key: "resource_provider_email".to_string(),
|
||||||
value: serde_json::Value::String(farmer_email.to_string()),
|
value: serde_json::Value::String(resource_provider_email.to_string()),
|
||||||
attribute_type: crate::models::product::AttributeType::Text,
|
attribute_type: crate::models::product::AttributeType::Text,
|
||||||
is_searchable: true,
|
is_searchable: true,
|
||||||
is_filterable: true,
|
is_filterable: true,
|
||||||
@@ -200,12 +200,12 @@ impl NodeMarketplaceService {
|
|||||||
display_order: Some(6),
|
display_order: Some(6),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get farmer display name
|
// Get resource_provider display name
|
||||||
let farmer_display_name = self.get_farmer_display_name(farmer_email);
|
let resource_provider_display_name = self.get_resource_provider_display_name(resource_provider_email);
|
||||||
|
|
||||||
// Create metadata with location
|
// Create metadata with location
|
||||||
let metadata = crate::models::product::ProductMetadata {
|
let metadata = crate::models::product::ProductMetadata {
|
||||||
tags: vec!["3node".to_string(), "hardware".to_string(), node.region.clone()],
|
tags: vec!["mycelium_node".to_string(), "hardware".to_string(), node.region.clone()],
|
||||||
location: Some(node.location.clone()),
|
location: Some(node.location.clone()),
|
||||||
rating: Some(node.health_score / 20.0), // Convert health score to 5-star rating
|
rating: Some(node.health_score / 20.0), // Convert health score to 5-star rating
|
||||||
review_count: 0,
|
review_count: 0,
|
||||||
@@ -215,8 +215,8 @@ impl NodeMarketplaceService {
|
|||||||
// Use Product builder pattern with add_attribute for each attribute
|
// Use Product builder pattern with add_attribute for each attribute
|
||||||
let mut builder = crate::models::product::Product::builder()
|
let mut builder = crate::models::product::Product::builder()
|
||||||
.id(format!("node_{}", node.id))
|
.id(format!("node_{}", node.id))
|
||||||
.name(format!("{} - {}", node.name, farmer_display_name))
|
.name(format!("{} - {}", node.name, resource_provider_display_name))
|
||||||
.description(format!("3Node with {} CPU cores, {} GB RAM, {} GB storage in {}. Uptime: {:.1}%, Health Score: {:.1}",
|
.description(format!("Mycelium Node with {} CPU cores, {} GB RAM, {} GB storage in {}. Uptime: {:.1}%, Health Score: {:.1}",
|
||||||
node.capacity.cpu_cores,
|
node.capacity.cpu_cores,
|
||||||
node.capacity.memory_gb,
|
node.capacity.memory_gb,
|
||||||
node.capacity.storage_gb,
|
node.capacity.storage_gb,
|
||||||
@@ -226,8 +226,8 @@ impl NodeMarketplaceService {
|
|||||||
.base_price(hourly_price)
|
.base_price(hourly_price)
|
||||||
.base_currency("USD".to_string())
|
.base_currency("USD".to_string())
|
||||||
.category_id("hardware".to_string())
|
.category_id("hardware".to_string())
|
||||||
.provider_id(farmer_email.to_string())
|
.provider_id(resource_provider_email.to_string())
|
||||||
.provider_name(farmer_display_name)
|
.provider_name(resource_provider_display_name)
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.availability(if self.is_node_online(node) {
|
.availability(if self.is_node_online(node) {
|
||||||
crate::models::product::ProductAvailability::Available
|
crate::models::product::ProductAvailability::Available
|
||||||
@@ -276,17 +276,17 @@ impl NodeMarketplaceService {
|
|||||||
format!("{}", node.status) == "Online"
|
format!("{}", node.status) == "Online"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get farmer display name from email
|
/// Get resource_provider display name from email
|
||||||
fn get_farmer_display_name(&self, farmer_email: &str) -> String {
|
fn get_resource_provider_display_name(&self, resource_provider_email: &str) -> String {
|
||||||
// Try to get actual name from persistent data
|
// Try to get actual name from persistent data
|
||||||
if let Some(user_data) = UserPersistence::load_user_data(farmer_email) {
|
if let Some(user_data) = UserPersistence::load_user_data(resource_provider_email) {
|
||||||
if let Some(name) = user_data.name {
|
if let Some(name) = user_data.name {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to email username
|
// Fallback to email username
|
||||||
farmer_email.split('@').next().unwrap_or("Farmer").to_string()
|
resource_provider_email.split('@').next().unwrap_or("ResourceProvider").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply marketplace filters to node products
|
/// Apply marketplace filters to node products
|
||||||
@@ -438,7 +438,7 @@ impl NodeMarketplaceService {
|
|||||||
let mut total_cpu_cores = 0u32;
|
let mut total_cpu_cores = 0u32;
|
||||||
let mut total_memory_gb = 0u32;
|
let mut total_memory_gb = 0u32;
|
||||||
let mut total_storage_gb = 0u32;
|
let mut total_storage_gb = 0u32;
|
||||||
let mut unique_farmers = std::collections::HashSet::new();
|
let mut unique_resource_providers = std::collections::HashSet::new();
|
||||||
let mut unique_locations = std::collections::HashSet::new();
|
let mut unique_locations = std::collections::HashSet::new();
|
||||||
|
|
||||||
for product in &all_slices {
|
for product in &all_slices {
|
||||||
@@ -456,8 +456,8 @@ impl NodeMarketplaceService {
|
|||||||
total_storage_gb += (storage * quantity) as u32;
|
total_storage_gb += (storage * quantity) as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(farmer) = product.attributes.get("farmer_email").and_then(|f| f.value.as_str()) {
|
if let Some(resource_provider) = product.attributes.get("resource_provider_email").and_then(|f| f.value.as_str()) {
|
||||||
unique_farmers.insert(farmer.to_string());
|
unique_resource_providers.insert(resource_provider.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(location) = &product.metadata.location {
|
if let Some(location) = &product.metadata.location {
|
||||||
@@ -471,9 +471,9 @@ impl NodeMarketplaceService {
|
|||||||
"total_cpu_cores": total_cpu_cores,
|
"total_cpu_cores": total_cpu_cores,
|
||||||
"total_memory_gb": total_memory_gb,
|
"total_memory_gb": total_memory_gb,
|
||||||
"total_storage_gb": total_storage_gb,
|
"total_storage_gb": total_storage_gb,
|
||||||
"unique_farmers": unique_farmers.len(),
|
"unique_resource_providers": unique_resource_providers.len(),
|
||||||
"unique_locations": unique_locations.len(),
|
"unique_locations": unique_locations.len(),
|
||||||
"farmers": unique_farmers.into_iter().collect::<Vec<_>>(),
|
"resource_providers": unique_resource_providers.into_iter().collect::<Vec<_>>(),
|
||||||
"locations": unique_locations.into_iter().collect::<Vec<_>>()
|
"locations": unique_locations.into_iter().collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -531,7 +531,7 @@ impl NodeMarketplaceService {
|
|||||||
pub fn get_all_slice_combinations(&self) -> Vec<Product> {
|
pub fn get_all_slice_combinations(&self) -> Vec<Product> {
|
||||||
let mut all_slice_products = Vec::new();
|
let mut all_slice_products = Vec::new();
|
||||||
|
|
||||||
// Read all user data files to find farmers with nodes
|
// Read all user data files to find resource_providers with nodes
|
||||||
if let Ok(entries) = std::fs::read_dir("./user_data") {
|
if let Ok(entries) = std::fs::read_dir("./user_data") {
|
||||||
for entry in entries.flatten() {
|
for entry in entries.flatten() {
|
||||||
if let Some(filename) = entry.file_name().to_str() {
|
if let Some(filename) = entry.file_name().to_str() {
|
||||||
@@ -585,14 +585,14 @@ impl NodeMarketplaceService {
|
|||||||
&self,
|
&self,
|
||||||
combination: &SliceCombination,
|
combination: &SliceCombination,
|
||||||
_node: &FarmNode,
|
_node: &FarmNode,
|
||||||
farmer_email: &str
|
resource_provider_email: &str
|
||||||
) -> Result<Product, String> {
|
) -> Result<Product, String> {
|
||||||
let mut attributes = HashMap::new();
|
let mut attributes = HashMap::new();
|
||||||
|
|
||||||
// Farmer information
|
// ResourceProvider information
|
||||||
attributes.insert("farmer_email".to_string(), crate::models::product::ProductAttribute {
|
attributes.insert("resource_provider_email".to_string(), crate::models::product::ProductAttribute {
|
||||||
key: "farmer_email".to_string(),
|
key: "resource_provider_email".to_string(),
|
||||||
value: serde_json::Value::String(farmer_email.to_string()),
|
value: serde_json::Value::String(resource_provider_email.to_string()),
|
||||||
attribute_type: crate::models::product::AttributeType::Text,
|
attribute_type: crate::models::product::AttributeType::Text,
|
||||||
is_searchable: true,
|
is_searchable: true,
|
||||||
is_filterable: true,
|
is_filterable: true,
|
||||||
@@ -701,15 +701,15 @@ impl NodeMarketplaceService {
|
|||||||
display_order: Some(10),
|
display_order: Some(10),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get farmer display name
|
// Get resource_provider display name
|
||||||
let farmer_display_name = self.get_farmer_display_name(farmer_email);
|
let resource_provider_display_name = self.get_resource_provider_display_name(resource_provider_email);
|
||||||
|
|
||||||
// Create metadata
|
// Create metadata
|
||||||
let metadata = crate::models::product::ProductMetadata {
|
let metadata = crate::models::product::ProductMetadata {
|
||||||
location: Some(combination.node_location.clone()),
|
location: Some(combination.node_location.clone()),
|
||||||
custom_fields: {
|
custom_fields: {
|
||||||
let mut fields = std::collections::HashMap::new();
|
let mut fields = std::collections::HashMap::new();
|
||||||
fields.insert("provider".to_string(), serde_json::Value::String(farmer_display_name.clone()));
|
fields.insert("provider".to_string(), serde_json::Value::String(resource_provider_display_name.clone()));
|
||||||
fields.insert("certification".to_string(), serde_json::Value::String(combination.node_certification_type.clone()));
|
fields.insert("certification".to_string(), serde_json::Value::String(combination.node_certification_type.clone()));
|
||||||
fields.insert("created_at".to_string(), serde_json::Value::String(chrono::Utc::now().to_rfc3339()));
|
fields.insert("created_at".to_string(), serde_json::Value::String(chrono::Utc::now().to_rfc3339()));
|
||||||
fields.insert("updated_at".to_string(), serde_json::Value::String(chrono::Utc::now().to_rfc3339()));
|
fields.insert("updated_at".to_string(), serde_json::Value::String(chrono::Utc::now().to_rfc3339()));
|
||||||
@@ -727,7 +727,7 @@ impl NodeMarketplaceService {
|
|||||||
// Build product using the builder pattern
|
// Build product using the builder pattern
|
||||||
let mut product = crate::models::product::Product::builder()
|
let mut product = crate::models::product::Product::builder()
|
||||||
.id(format!("slice_{}_{}", combination.node_id, combination.id))
|
.id(format!("slice_{}_{}", combination.node_id, combination.id))
|
||||||
.name(format!("{} Slice ({}x Base Unit)", farmer_display_name, combination.multiplier))
|
.name(format!("{} Slice ({}x Base Unit)", resource_provider_display_name, combination.multiplier))
|
||||||
.description(format!(
|
.description(format!(
|
||||||
"Compute slice with {} vCPU, {}GB RAM, {}GB storage from {} ({}% uptime)",
|
"Compute slice with {} vCPU, {}GB RAM, {}GB storage from {} ({}% uptime)",
|
||||||
combination.cpu_cores,
|
combination.cpu_cores,
|
||||||
@@ -739,8 +739,8 @@ impl NodeMarketplaceService {
|
|||||||
.category_id("compute_slices".to_string())
|
.category_id("compute_slices".to_string())
|
||||||
.base_price(combination.price_per_hour)
|
.base_price(combination.price_per_hour)
|
||||||
.base_currency("USD".to_string())
|
.base_currency("USD".to_string())
|
||||||
.provider_id(farmer_email.to_string())
|
.provider_id(resource_provider_email.to_string())
|
||||||
.provider_name(farmer_display_name)
|
.provider_name(resource_provider_display_name)
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.build()
|
.build()
|
||||||
.map_err(|e| format!("Failed to build slice product: {}", e))?;
|
.map_err(|e| format!("Failed to build slice product: {}", e))?;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
//! Node rental service for managing node rentals and farmer earnings
|
//! Node rental service for managing node rentals and resource_provider earnings
|
||||||
//! Follows the established builder pattern for consistent API design
|
//! Follows the established builder pattern for consistent API design
|
||||||
|
|
||||||
use crate::models::user::{NodeRental, NodeRentalType, NodeRentalStatus, FarmerRentalEarning, PaymentStatus, NodeAvailabilityStatus};
|
use crate::models::user::{NodeRental, NodeRentalType, NodeRentalStatus, ResourceProviderRentalEarning, PaymentStatus, NodeAvailabilityStatus};
|
||||||
use crate::services::user_persistence::{UserPersistence, ProductRental};
|
use crate::services::user_persistence::{UserPersistence, ProductRental};
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use chrono::{Utc, Duration};
|
use chrono::{Utc, Duration};
|
||||||
@@ -65,7 +65,7 @@ impl NodeRentalService {
|
|||||||
duration_months: u32,
|
duration_months: u32,
|
||||||
rental_type: NodeRentalType,
|
rental_type: NodeRentalType,
|
||||||
monthly_cost: Decimal,
|
monthly_cost: Decimal,
|
||||||
) -> Result<(NodeRental, FarmerRentalEarning), String> {
|
) -> Result<(NodeRental, ResourceProviderRentalEarning), String> {
|
||||||
// Extract node ID from product ID
|
// Extract node ID from product ID
|
||||||
let node_id = if product_id.starts_with("fullnode_") {
|
let node_id = if product_id.starts_with("fullnode_") {
|
||||||
product_id.strip_prefix("fullnode_").unwrap_or(product_id)
|
product_id.strip_prefix("fullnode_").unwrap_or(product_id)
|
||||||
@@ -99,8 +99,8 @@ impl NodeRentalService {
|
|||||||
.payment_method("USD".to_string())
|
.payment_method("USD".to_string())
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
// Create farmer earning record
|
// Create resource_provider earning record
|
||||||
let farmer_earning = crate::models::builders::FarmerRentalEarningBuilder::new()
|
let resource_provider_earning = crate::models::builders::ResourceProviderRentalEarningBuilder::new()
|
||||||
.node_id(node_id.to_string())
|
.node_id(node_id.to_string())
|
||||||
.rental_id(rental.id.clone())
|
.rental_id(rental.id.clone())
|
||||||
.renter_email(renter_email.to_string())
|
.renter_email(renter_email.to_string())
|
||||||
@@ -111,29 +111,29 @@ impl NodeRentalService {
|
|||||||
.payment_status(PaymentStatus::Completed)
|
.payment_status(PaymentStatus::Completed)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
// Find the farmer who owns this node
|
// Find the resource_provider who owns this node
|
||||||
let farmer_email = self.find_node_owner(node_id)?;
|
let resource_provider_email = self.find_node_owner(node_id)?;
|
||||||
|
|
||||||
// Save rental to renter's data
|
// Save rental to renter's data
|
||||||
self.save_rental_to_user(&rental, renter_email, product_id)?;
|
self.save_rental_to_user(&rental, renter_email, product_id)?;
|
||||||
|
|
||||||
// Save earning to farmer's data
|
// Save earning to resource_provider's data
|
||||||
self.save_earning_to_farmer(&farmer_earning, &farmer_email)?;
|
self.save_earning_to_resource_provider(&resource_provider_earning, &resource_provider_email)?;
|
||||||
|
|
||||||
// Update node availability status
|
// Update node availability status
|
||||||
self.update_node_availability(node_id, &farmer_email)?;
|
self.update_node_availability(node_id, &resource_provider_email)?;
|
||||||
|
|
||||||
Ok((rental, farmer_earning))
|
Ok((rental, resource_provider_earning))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check for rental conflicts
|
/// Check for rental conflicts
|
||||||
fn check_rental_conflicts(&self, node_id: &str, rental_type: &NodeRentalType) -> Result<(), String> {
|
fn check_rental_conflicts(&self, node_id: &str, rental_type: &NodeRentalType) -> Result<(), String> {
|
||||||
// Find the farmer who owns this node
|
// Find the resource_provider who owns this node
|
||||||
let farmer_email = self.find_node_owner(node_id)?;
|
let resource_provider_email = self.find_node_owner(node_id)?;
|
||||||
|
|
||||||
if let Some(farmer_data) = UserPersistence::load_user_data(&farmer_email) {
|
if let Some(resource_provider_data) = UserPersistence::load_user_data(&resource_provider_email) {
|
||||||
// Check existing rentals for this node
|
// Check existing rentals for this node
|
||||||
let existing_rentals: Vec<_> = farmer_data.node_rentals.iter()
|
let existing_rentals: Vec<_> = resource_provider_data.node_rentals.iter()
|
||||||
.filter(|r| r.node_id == node_id && r.is_active())
|
.filter(|r| r.node_id == node_id && r.is_active())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ impl NodeRentalService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the farmer who owns a specific node
|
/// Find the resource_provider who owns a specific node
|
||||||
fn find_node_owner(&self, node_id: &str) -> Result<String, String> {
|
fn find_node_owner(&self, node_id: &str) -> Result<String, String> {
|
||||||
// Scan all user files to find the node owner
|
// Scan all user files to find the node owner
|
||||||
if let Ok(entries) = std::fs::read_dir("./user_data/") {
|
if let Ok(entries) = std::fs::read_dir("./user_data/") {
|
||||||
@@ -226,31 +226,31 @@ impl NodeRentalService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save earning record to farmer's persistent data
|
/// Save earning record to resource_provider's persistent data
|
||||||
fn save_earning_to_farmer(&self, earning: &FarmerRentalEarning, farmer_email: &str) -> Result<(), String> {
|
fn save_earning_to_resource_provider(&self, earning: &ResourceProviderRentalEarning, resource_provider_email: &str) -> Result<(), String> {
|
||||||
let mut farmer_data = UserPersistence::load_user_data(farmer_email)
|
let mut resource_provider_data = UserPersistence::load_user_data(resource_provider_email)
|
||||||
.unwrap_or_else(|| self.create_default_user_data(farmer_email));
|
.unwrap_or_else(|| self.create_default_user_data(resource_provider_email));
|
||||||
|
|
||||||
// Add to farmer rental earnings
|
// Add to resource_provider rental earnings
|
||||||
farmer_data.farmer_rental_earnings.push(earning.clone());
|
resource_provider_data.resource_provider_rental_earnings.push(earning.clone());
|
||||||
|
|
||||||
// Update wallet balance
|
// Update wallet balance
|
||||||
farmer_data.wallet_balance_usd += earning.amount;
|
resource_provider_data.wallet_balance_usd += earning.amount;
|
||||||
|
|
||||||
UserPersistence::save_user_data(&farmer_data)
|
UserPersistence::save_user_data(&resource_provider_data)
|
||||||
.map_err(|e| format!("Failed to save farmer data: {}", e))?;
|
.map_err(|e| format!("Failed to save resource_provider data: {}", e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update node availability status based on current rentals
|
/// Update node availability status based on current rentals
|
||||||
fn update_node_availability(&self, node_id: &str, farmer_email: &str) -> Result<(), String> {
|
fn update_node_availability(&self, node_id: &str, resource_provider_email: &str) -> Result<(), String> {
|
||||||
let mut farmer_data = UserPersistence::load_user_data(farmer_email)
|
let mut resource_provider_data = UserPersistence::load_user_data(resource_provider_email)
|
||||||
.ok_or("Farmer data not found")?;
|
.ok_or("ResourceProvider data not found")?;
|
||||||
|
|
||||||
if let Some(node) = farmer_data.nodes.iter_mut().find(|n| n.id == node_id) {
|
if let Some(node) = resource_provider_data.nodes.iter_mut().find(|n| n.id == node_id) {
|
||||||
// Count active rentals for this node
|
// Count active rentals for this node
|
||||||
let active_rentals: Vec<_> = farmer_data.node_rentals.iter()
|
let active_rentals: Vec<_> = resource_provider_data.node_rentals.iter()
|
||||||
.filter(|r| r.node_id == node_id && r.is_active())
|
.filter(|r| r.node_id == node_id && r.is_active())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ impl NodeRentalService {
|
|||||||
NodeAvailabilityStatus::PartiallyRented
|
NodeAvailabilityStatus::PartiallyRented
|
||||||
};
|
};
|
||||||
|
|
||||||
UserPersistence::save_user_data(&farmer_data)
|
UserPersistence::save_user_data(&resource_provider_data)
|
||||||
.map_err(|e| format!("Failed to update node availability: {}", e))?;
|
.map_err(|e| format!("Failed to update node availability: {}", e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,10 +285,10 @@ impl NodeRentalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get farmer earnings from rentals
|
/// Get resource_provider earnings from rentals
|
||||||
pub fn get_farmer_rental_earnings(&self, farmer_email: &str) -> Vec<FarmerRentalEarning> {
|
pub fn get_resource_provider_rental_earnings(&self, resource_provider_email: &str) -> Vec<ResourceProviderRentalEarning> {
|
||||||
if let Some(farmer_data) = UserPersistence::load_user_data(farmer_email) {
|
if let Some(resource_provider_data) = UserPersistence::load_user_data(resource_provider_email) {
|
||||||
farmer_data.farmer_rental_earnings
|
resource_provider_data.resource_provider_rental_earnings
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
@@ -309,8 +309,8 @@ impl NodeRentalService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update node availability
|
// Update node availability
|
||||||
let farmer_email = self.find_node_owner(&rental.node_id)?;
|
let resource_provider_email = self.find_node_owner(&rental.node_id)?;
|
||||||
self.update_node_availability(&rental.node_id, &farmer_email)?;
|
self.update_node_availability(&rental.node_id, &resource_provider_email)?;
|
||||||
|
|
||||||
UserPersistence::save_user_data(&user_data)
|
UserPersistence::save_user_data(&user_data)
|
||||||
.map_err(|e| format!("Failed to save user data: {}", e))?;
|
.map_err(|e| format!("Failed to save user data: {}", e))?;
|
||||||
|
@@ -626,8 +626,8 @@ impl OrderService {
|
|||||||
order.set_payment_details(payment_details.clone());
|
order.set_payment_details(payment_details.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHASE 1: Create app deployments for successful app orders
|
// PHASE 1: Create application deployments for successful app orders
|
||||||
if let Err(e) = self.create_app_deployments_from_order(&order) {
|
if let Err(e) = self.create_application_deployments_from_order(&order) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHASE 2: Create service bookings for successful service orders
|
// PHASE 2: Create service bookings for successful service orders
|
||||||
@@ -780,8 +780,8 @@ impl OrderService {
|
|||||||
stats
|
stats
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PHASE 1: Create app deployments when apps are successfully ordered
|
/// PHASE 1: Create application deployments when apps are successfully ordered
|
||||||
fn create_app_deployments_from_order(&self, order: &Order) -> Result<(), String> {
|
fn create_application_deployments_from_order(&self, order: &Order) -> Result<(), String> {
|
||||||
use crate::services::user_persistence::{UserPersistence, AppDeployment};
|
use crate::services::user_persistence::{UserPersistence, AppDeployment};
|
||||||
use crate::models::user::ResourceUtilization;
|
use crate::models::user::ResourceUtilization;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
@@ -804,8 +804,8 @@ impl OrderService {
|
|||||||
// Only create deployments for application products
|
// Only create deployments for application products
|
||||||
if item.product_category == "application" {
|
if item.product_category == "application" {
|
||||||
|
|
||||||
// Find the app provider by looking up who published this app
|
// Find the application provider by looking up who published this app
|
||||||
if let Some(app_provider_email) = self.find_app_provider(&item.product_id) {
|
if let Some(application_provider_email) = self.find_application_provider(&item.product_id) {
|
||||||
|
|
||||||
// Create deployment for each quantity ordered
|
// Create deployment for each quantity ordered
|
||||||
for _i in 0..item.quantity {
|
for _i in 0..item.quantity {
|
||||||
@@ -837,14 +837,14 @@ impl OrderService {
|
|||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Add deployment to app provider's data
|
// Add deployment to application provider's data
|
||||||
if let Err(e) = UserPersistence::add_user_app_deployment(&app_provider_email, deployment.clone()) {
|
if let Err(e) = UserPersistence::add_user_application_deployment(&application_provider_email, deployment.clone()) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also add deployment to customer's data (for future user dashboard)
|
// Also add deployment to customer's data (for future user dashboard)
|
||||||
if customer_email != "guest" {
|
if customer_email != "guest" {
|
||||||
if let Err(e) = UserPersistence::add_user_app_deployment(&customer_email, deployment) {
|
if let Err(e) = UserPersistence::add_user_application_deployment(&customer_email, deployment) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -857,8 +857,8 @@ impl OrderService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the app provider (user who published the app) by product ID
|
/// Find the application provider (user who published the app) by product ID
|
||||||
fn find_app_provider(&self, product_id: &str) -> Option<String> {
|
fn find_application_provider(&self, product_id: &str) -> Option<String> {
|
||||||
// Get all user data files and search for the app
|
// Get all user data files and search for the app
|
||||||
let user_data_dir = std::path::Path::new("user_data");
|
let user_data_dir = std::path::Path::new("user_data");
|
||||||
if !user_data_dir.exists() {
|
if !user_data_dir.exists() {
|
||||||
|
@@ -241,8 +241,8 @@ impl ProductService {
|
|||||||
if let Some(combination_id) = product.attributes.get("combination_id") {
|
if let Some(combination_id) = product.attributes.get("combination_id") {
|
||||||
details["combination_id"] = combination_id.value.clone();
|
details["combination_id"] = combination_id.value.clone();
|
||||||
}
|
}
|
||||||
if let Some(farmer_email) = product.attributes.get("farmer_email") {
|
if let Some(resource_provider_email) = product.attributes.get("resource_provider_email") {
|
||||||
details["farmer_email"] = farmer_email.value.clone();
|
details["resource_provider_email"] = resource_provider_email.value.clone();
|
||||||
}
|
}
|
||||||
if let Some(cpu_cores) = product.attributes.get("cpu_cores") {
|
if let Some(cpu_cores) = product.attributes.get("cpu_cores") {
|
||||||
details["cpu_cores"] = cpu_cores.value.clone();
|
details["cpu_cores"] = cpu_cores.value.clone();
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
//! Farmer service for managing nodes, slice allocation, and earnings
|
//! ResourceProvider service for managing nodes, slice allocation, and earnings
|
||||||
//! Follows the established builder pattern for consistent API design
|
//! Follows the established builder pattern for consistent API design
|
||||||
|
|
||||||
use crate::models::user::{FarmNode, NodeCapacity, NodeStatus, EarningsRecord, NodeGroup, GroupStatistics, MarketplaceSLA};
|
use crate::models::user::{FarmNode, NodeCapacity, NodeStatus, EarningsRecord, NodeGroup, GroupStatistics, MarketplaceSLA};
|
||||||
@@ -12,7 +12,7 @@ use std::str::FromStr;
|
|||||||
use chrono::{Utc, DateTime};
|
use chrono::{Utc, DateTime};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
/// Staking statistics for a farmer
|
/// Staking statistics for a resource_provider
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct StakingStatistics {
|
pub struct StakingStatistics {
|
||||||
pub total_staked_amount: Decimal,
|
pub total_staked_amount: Decimal,
|
||||||
@@ -30,25 +30,25 @@ impl Default for StakingStatistics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Service for farmer-specific operations
|
/// Service for resource_provider-specific operations
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FarmerService {
|
pub struct ResourceProviderService {
|
||||||
auto_sync_enabled: bool,
|
auto_sync_enabled: bool,
|
||||||
metrics_collection: bool,
|
metrics_collection: bool,
|
||||||
grid_service: GridService,
|
grid_service: GridService,
|
||||||
slice_calculator: SliceCalculatorService,
|
slice_calculator: SliceCalculatorService,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for FarmerService
|
/// Builder for ResourceProviderService
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FarmerServiceBuilder {
|
pub struct ResourceProviderServiceBuilder {
|
||||||
auto_sync_enabled: Option<bool>,
|
auto_sync_enabled: Option<bool>,
|
||||||
metrics_collection: Option<bool>,
|
metrics_collection: Option<bool>,
|
||||||
grid_service: Option<GridService>,
|
grid_service: Option<GridService>,
|
||||||
slice_calculator: Option<SliceCalculatorService>,
|
slice_calculator: Option<SliceCalculatorService>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FarmerServiceBuilder {
|
impl ResourceProviderServiceBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ impl FarmerServiceBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<FarmerService, String> {
|
pub fn build(self) -> Result<ResourceProviderService, String> {
|
||||||
let grid_service = self.grid_service.unwrap_or_else(|| {
|
let grid_service = self.grid_service.unwrap_or_else(|| {
|
||||||
GridService::builder().build().expect("Failed to create default GridService")
|
GridService::builder().build().expect("Failed to create default GridService")
|
||||||
});
|
});
|
||||||
@@ -82,7 +82,7 @@ impl FarmerServiceBuilder {
|
|||||||
SliceCalculatorService::builder().build().expect("Failed to create default SliceCalculatorService")
|
SliceCalculatorService::builder().build().expect("Failed to create default SliceCalculatorService")
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(FarmerService {
|
Ok(ResourceProviderService {
|
||||||
auto_sync_enabled: self.auto_sync_enabled.unwrap_or(true),
|
auto_sync_enabled: self.auto_sync_enabled.unwrap_or(true),
|
||||||
metrics_collection: self.metrics_collection.unwrap_or(true),
|
metrics_collection: self.metrics_collection.unwrap_or(true),
|
||||||
grid_service,
|
grid_service,
|
||||||
@@ -91,13 +91,13 @@ impl FarmerServiceBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FarmerService {
|
impl ResourceProviderService {
|
||||||
pub fn builder() -> FarmerServiceBuilder {
|
pub fn builder() -> ResourceProviderServiceBuilder {
|
||||||
FarmerServiceBuilder::new()
|
ResourceProviderServiceBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all nodes for a farmer
|
/// Get all nodes for a resource_provider
|
||||||
pub fn get_farmer_nodes(&self, user_email: &str) -> Vec<FarmNode> {
|
pub fn get_resource_provider_nodes(&self, user_email: &str) -> Vec<FarmNode> {
|
||||||
if let Some(data) = UserPersistence::load_user_data(user_email) {
|
if let Some(data) = UserPersistence::load_user_data(user_email) {
|
||||||
// Debug: Log marketplace SLA data for all nodes
|
// Debug: Log marketplace SLA data for all nodes
|
||||||
for node in &data.nodes {
|
for node in &data.nodes {
|
||||||
@@ -111,10 +111,10 @@ impl FarmerService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new node for a farmer (manual creation)
|
/// Add a new node for a resource_provider (manual creation)
|
||||||
pub fn add_node(&self, user_email: &str, node_data: NodeCreationData) -> Result<FarmNode, String> {
|
pub fn add_node(&self, user_email: &str, node_data: NodeCreationData) -> Result<FarmNode, String> {
|
||||||
// Check for duplicate node names first
|
// Check for duplicate node names first
|
||||||
let existing_nodes = self.get_farmer_nodes(user_email);
|
let existing_nodes = self.get_resource_provider_nodes(user_email);
|
||||||
if existing_nodes.iter().any(|n| n.name == node_data.name) {
|
if existing_nodes.iter().any(|n| n.name == node_data.name) {
|
||||||
return Err(format!("Node '{}' is already registered", node_data.name));
|
return Err(format!("Node '{}' is already registered", node_data.name));
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ impl FarmerService {
|
|||||||
last_seen: Some(Utc::now()),
|
last_seen: Some(Utc::now()),
|
||||||
health_score: 100.0,
|
health_score: 100.0,
|
||||||
region: node_data.region.unwrap_or_else(|| "Unknown".to_string()),
|
region: node_data.region.unwrap_or_else(|| "Unknown".to_string()),
|
||||||
node_type: node_data.node_type.unwrap_or_else(|| "3Node".to_string()),
|
node_type: node_data.node_type.unwrap_or_else(|| "MyceliumNode".to_string()),
|
||||||
slice_formats: node_data.slice_formats.clone(),
|
slice_formats: node_data.slice_formats.clone(),
|
||||||
rental_options: node_data.rental_options.as_ref().map(|opts| serde_json::to_value(opts).unwrap_or_default()),
|
rental_options: node_data.rental_options.as_ref().map(|opts| serde_json::to_value(opts).unwrap_or_default()),
|
||||||
staking_options: None,
|
staking_options: None,
|
||||||
@@ -234,7 +234,7 @@ impl FarmerService {
|
|||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
// Auto-generate marketplace products if rental options are configured
|
// Auto-generate marketplace products if rental options are configured
|
||||||
self.auto_generate_marketplace_products(&node, user_email, &persistent_data.name.unwrap_or_else(|| "Unknown Farmer".to_string()), node_data.slice_prices.as_ref())?;
|
self.auto_generate_marketplace_products(&node, user_email, &persistent_data.name.unwrap_or_else(|| "Unknown ResourceProvider".to_string()), node_data.slice_prices.as_ref())?;
|
||||||
Ok(node)
|
Ok(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ impl FarmerService {
|
|||||||
earnings_today_usd: Decimal::ZERO,
|
earnings_today_usd: Decimal::ZERO,
|
||||||
name: if grid_data.farm_name.is_empty() { format!("Grid Node {}", grid_node_id) } else { grid_data.farm_name.clone() },
|
name: if grid_data.farm_name.is_empty() { format!("Grid Node {}", grid_node_id) } else { grid_data.farm_name.clone() },
|
||||||
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
||||||
node_type: "3Node".to_string(),
|
node_type: "MyceliumNode".to_string(),
|
||||||
slice_formats: None, // Not used in new slice system
|
slice_formats: None, // Not used in new slice system
|
||||||
staking_options: None,
|
staking_options: None,
|
||||||
availability_status: crate::models::user::NodeAvailabilityStatus::Available,
|
availability_status: crate::models::user::NodeAvailabilityStatus::Available,
|
||||||
@@ -434,10 +434,10 @@ impl FarmerService {
|
|||||||
|
|
||||||
/// Sync node capacity and slices with grid data
|
/// Sync node capacity and slices with grid data
|
||||||
pub async fn sync_node_with_grid(&self, user_email: &str, node_id: &str) -> Result<(), String> {
|
pub async fn sync_node_with_grid(&self, user_email: &str, node_id: &str) -> Result<(), String> {
|
||||||
let mut farmer_data = UserPersistence::load_user_data(user_email)
|
let mut resource_provider_data = UserPersistence::load_user_data(user_email)
|
||||||
.ok_or("Farmer data not found")?;
|
.ok_or("ResourceProvider data not found")?;
|
||||||
|
|
||||||
if let Some(node) = farmer_data.nodes.iter_mut().find(|n| n.id == node_id) {
|
if let Some(node) = resource_provider_data.nodes.iter_mut().find(|n| n.id == node_id) {
|
||||||
if let Some(grid_node_id) = &node.grid_node_id {
|
if let Some(grid_node_id) = &node.grid_node_id {
|
||||||
// Fetch latest capacity from grid
|
// Fetch latest capacity from grid
|
||||||
let grid_id: u32 = grid_node_id.parse().unwrap_or(0);
|
let grid_id: u32 = grid_node_id.parse().unwrap_or(0);
|
||||||
@@ -472,7 +472,7 @@ impl FarmerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save updated data
|
// Save updated data
|
||||||
UserPersistence::save_user_data(&farmer_data)
|
UserPersistence::save_user_data(&resource_provider_data)
|
||||||
.map_err(|e| format!("Failed to save user data: {}", e))?;
|
.map_err(|e| format!("Failed to save user data: {}", e))?;
|
||||||
} else {
|
} else {
|
||||||
return Err("Node is not linked to grid".to_string());
|
return Err("Node is not linked to grid".to_string());
|
||||||
@@ -486,7 +486,7 @@ impl FarmerService {
|
|||||||
|
|
||||||
/// Auto-generate marketplace products for a node based on rental options
|
/// Auto-generate marketplace products for a node based on rental options
|
||||||
/// Products are now managed through persistent user data and aggregated by ProductService
|
/// Products are now managed through persistent user data and aggregated by ProductService
|
||||||
fn auto_generate_marketplace_products(&self, _node: &FarmNode, _farmer_email: &str, _farmer_name: &str, _slice_prices: Option<&std::collections::HashMap<String, rust_decimal::Decimal>>) -> Result<(), String> {
|
fn auto_generate_marketplace_products(&self, _node: &FarmNode, _resource_provider_email: &str, _resource_provider_name: &str, _slice_prices: Option<&std::collections::HashMap<String, rust_decimal::Decimal>>) -> Result<(), String> {
|
||||||
// Product generation is now handled through persistent user data
|
// Product generation is now handled through persistent user data
|
||||||
// ProductService automatically aggregates products from user-owned data
|
// ProductService automatically aggregates products from user-owned data
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -754,7 +754,7 @@ impl FarmerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a slice product from a node and slice format
|
/// Create a slice product from a node and slice format
|
||||||
fn create_slice_product_from_node(&self, node: &FarmNode, slice_format_id: &str, farmer_email: &str, farmer_name: &str, custom_prices: Option<&std::collections::HashMap<String, rust_decimal::Decimal>>) -> Result<crate::models::product::Product, String> {
|
fn create_slice_product_from_node(&self, node: &FarmNode, slice_format_id: &str, resource_provider_email: &str, resource_provider_name: &str, custom_prices: Option<&std::collections::HashMap<String, rust_decimal::Decimal>>) -> Result<crate::models::product::Product, String> {
|
||||||
// Get slice format details and default pricing
|
// Get slice format details and default pricing
|
||||||
let (cpu_cores, memory_gb, storage_gb, bandwidth_mbps, default_price) = match slice_format_id {
|
let (cpu_cores, memory_gb, storage_gb, bandwidth_mbps, default_price) = match slice_format_id {
|
||||||
"basic" => (2, 4, 100, 100, 25),
|
"basic" => (2, 4, 100, 100, 25),
|
||||||
@@ -789,8 +789,8 @@ impl FarmerService {
|
|||||||
base_price: price,
|
base_price: price,
|
||||||
base_currency: "USD".to_string(),
|
base_currency: "USD".to_string(),
|
||||||
attributes: std::collections::HashMap::new(),
|
attributes: std::collections::HashMap::new(),
|
||||||
provider_id: farmer_email.to_string(),
|
provider_id: resource_provider_email.to_string(),
|
||||||
provider_name: farmer_name.to_string(),
|
provider_name: resource_provider_name.to_string(),
|
||||||
availability: match node.availability_status {
|
availability: match node.availability_status {
|
||||||
crate::models::user::NodeAvailabilityStatus::Available => crate::models::product::ProductAvailability::Available,
|
crate::models::user::NodeAvailabilityStatus::Available => crate::models::product::ProductAvailability::Available,
|
||||||
crate::models::user::NodeAvailabilityStatus::PartiallyRented => crate::models::product::ProductAvailability::Limited,
|
crate::models::user::NodeAvailabilityStatus::PartiallyRented => crate::models::product::ProductAvailability::Limited,
|
||||||
@@ -880,19 +880,19 @@ impl FarmerService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get farmer earnings
|
/// Get resource_provider earnings
|
||||||
pub fn get_farmer_earnings(&self, user_email: &str) -> Vec<EarningsRecord> {
|
pub fn get_resource_provider_earnings(&self, user_email: &str) -> Vec<EarningsRecord> {
|
||||||
if let Some(data) = UserPersistence::load_user_data(user_email) {
|
if let Some(data) = UserPersistence::load_user_data(user_email) {
|
||||||
data.farmer_earnings
|
data.resource_provider_earnings
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get farmer statistics
|
/// Get resource_provider statistics
|
||||||
pub fn get_farmer_statistics(&self, user_email: &str) -> FarmerStatistics {
|
pub fn get_resource_provider_statistics(&self, user_email: &str) -> ResourceProviderStatistics {
|
||||||
let nodes = self.get_farmer_nodes(user_email);
|
let nodes = self.get_resource_provider_nodes(user_email);
|
||||||
let earnings = self.get_farmer_earnings(user_email);
|
let earnings = self.get_resource_provider_earnings(user_email);
|
||||||
|
|
||||||
let total_nodes = nodes.len() as i32;
|
let total_nodes = nodes.len() as i32;
|
||||||
let online_nodes = nodes.iter()
|
let online_nodes = nodes.iter()
|
||||||
@@ -962,7 +962,7 @@ impl FarmerService {
|
|||||||
allocated_base_slices += node_allocated_slices;
|
allocated_base_slices += node_allocated_slices;
|
||||||
}
|
}
|
||||||
|
|
||||||
FarmerStatistics {
|
ResourceProviderStatistics {
|
||||||
total_nodes,
|
total_nodes,
|
||||||
online_nodes,
|
online_nodes,
|
||||||
total_capacity,
|
total_capacity,
|
||||||
@@ -990,7 +990,7 @@ impl FarmerService {
|
|||||||
slice_config.bandwidth_mbps <= available_bandwidth)
|
slice_config.bandwidth_mbps <= available_bandwidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get default slice formats available to all farmers
|
/// Get default slice formats available to all resource providers
|
||||||
pub fn get_default_slice_formats(&self) -> Vec<DefaultSliceFormat> {
|
pub fn get_default_slice_formats(&self) -> Vec<DefaultSliceFormat> {
|
||||||
vec![
|
vec![
|
||||||
DefaultSliceFormat {
|
DefaultSliceFormat {
|
||||||
@@ -1039,8 +1039,8 @@ impl FarmerService {
|
|||||||
|
|
||||||
// Load user customizations from persistent data
|
// Load user customizations from persistent data
|
||||||
if let Some(persistent_data) = UserPersistence::load_user_data(user_email) {
|
if let Some(persistent_data) = UserPersistence::load_user_data(user_email) {
|
||||||
if let Some(farmer_settings) = persistent_data.farmer_settings {
|
if let Some(resource_provider_settings) = persistent_data.resource_provider_settings {
|
||||||
if let Some(customizations) = farmer_settings.default_slice_customizations {
|
if let Some(customizations) = resource_provider_settings.default_slice_customizations {
|
||||||
if let Some(custom_format) = customizations.get(format_id) {
|
if let Some(custom_format) = customizations.get(format_id) {
|
||||||
// Apply user customizations
|
// Apply user customizations
|
||||||
if let Some(cpu_cores) = custom_format.get("cpu_cores").and_then(|v| v.as_u64()).map(|v| v as u32) {
|
if let Some(cpu_cores) = custom_format.get("cpu_cores").and_then(|v| v.as_u64()).map(|v| v as u32) {
|
||||||
@@ -1075,9 +1075,9 @@ impl FarmerService {
|
|||||||
pub fn save_default_slice_customization(&self, user_email: &str, format_id: &str, customization: DefaultSliceFormat) -> Result<(), String> {
|
pub fn save_default_slice_customization(&self, user_email: &str, format_id: &str, customization: DefaultSliceFormat) -> Result<(), String> {
|
||||||
let mut persistent_data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
let mut persistent_data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
||||||
|
|
||||||
// Initialize farmer settings if needed
|
// Initialize resource_provider settings if needed
|
||||||
if persistent_data.farmer_settings.is_none() {
|
if persistent_data.resource_provider_settings.is_none() {
|
||||||
persistent_data.farmer_settings = Some(crate::models::user::FarmerSettings {
|
persistent_data.resource_provider_settings = Some(crate::models::user::ResourceProviderSettings {
|
||||||
auto_accept_reserved_slices: true,
|
auto_accept_reserved_slices: true,
|
||||||
maintenance_window: "02:00-04:00 UTC".to_string(),
|
maintenance_window: "02:00-04:00 UTC".to_string(),
|
||||||
notification_email: None,
|
notification_email: None,
|
||||||
@@ -1096,12 +1096,12 @@ impl FarmerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize default slice customizations if needed
|
// Initialize default slice customizations if needed
|
||||||
if let Some(ref mut farmer_settings) = persistent_data.farmer_settings {
|
if let Some(ref mut resource_provider_settings) = persistent_data.resource_provider_settings {
|
||||||
if farmer_settings.default_slice_customizations.is_none() {
|
if resource_provider_settings.default_slice_customizations.is_none() {
|
||||||
farmer_settings.default_slice_customizations = Some(serde_json::to_value(std::collections::HashMap::<String, serde_json::Value>::new()).unwrap_or_default());
|
resource_provider_settings.default_slice_customizations = Some(serde_json::to_value(std::collections::HashMap::<String, serde_json::Value>::new()).unwrap_or_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut customizations) = farmer_settings.default_slice_customizations {
|
if let Some(ref mut customizations) = resource_provider_settings.default_slice_customizations {
|
||||||
if let Some(obj) = customizations.as_object_mut() {
|
if let Some(obj) = customizations.as_object_mut() {
|
||||||
obj.insert(format_id.to_string(), serde_json::to_value(customization).unwrap_or_default());
|
obj.insert(format_id.to_string(), serde_json::to_value(customization).unwrap_or_default());
|
||||||
}
|
}
|
||||||
@@ -1288,7 +1288,7 @@ impl FarmerService {
|
|||||||
/// Add a node from ThreeFold Grid by node ID
|
/// Add a node from ThreeFold Grid by node ID
|
||||||
pub async fn add_grid_node(&self, user_email: &str, grid_node_id: u32, _slice_format: Option<String>, _slice_price: Option<Decimal>) -> Result<FarmNode, String> {
|
pub async fn add_grid_node(&self, user_email: &str, grid_node_id: u32, _slice_format: Option<String>, _slice_price: Option<Decimal>) -> Result<FarmNode, String> {
|
||||||
// Check for duplicate grid node IDs first
|
// Check for duplicate grid node IDs first
|
||||||
let existing_nodes = self.get_farmer_nodes(user_email);
|
let existing_nodes = self.get_resource_provider_nodes(user_email);
|
||||||
if existing_nodes.iter().any(|n| n.grid_node_id == Some(grid_node_id.to_string())) {
|
if existing_nodes.iter().any(|n| n.grid_node_id == Some(grid_node_id.to_string())) {
|
||||||
return Err(format!("Node {} is already registered", grid_node_id));
|
return Err(format!("Node {} is already registered", grid_node_id));
|
||||||
}
|
}
|
||||||
@@ -1316,7 +1316,7 @@ impl FarmerService {
|
|||||||
.earnings_today_usd(Decimal::ZERO)
|
.earnings_today_usd(Decimal::ZERO)
|
||||||
.health_score(100.0)
|
.health_score(100.0)
|
||||||
.region(if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() })
|
.region(if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() })
|
||||||
.node_type("3Node".to_string())
|
.node_type("MyceliumNode".to_string())
|
||||||
.grid_node_id(grid_node_id)
|
.grid_node_id(grid_node_id)
|
||||||
.grid_data(grid_data)
|
.grid_data(grid_data)
|
||||||
.build()?;
|
.build()?;
|
||||||
@@ -1371,7 +1371,7 @@ impl FarmerService {
|
|||||||
.earnings_today_usd(Decimal::ZERO)
|
.earnings_today_usd(Decimal::ZERO)
|
||||||
.health_score(100.0)
|
.health_score(100.0)
|
||||||
.region(if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() })
|
.region(if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() })
|
||||||
.node_type("3Node".to_string())
|
.node_type("MyceliumNode".to_string())
|
||||||
.grid_node_id(grid_node_id)
|
.grid_node_id(grid_node_id)
|
||||||
.grid_data(grid_data)
|
.grid_data(grid_data)
|
||||||
.build()
|
.build()
|
||||||
@@ -1460,7 +1460,7 @@ impl FarmerService {
|
|||||||
.last_seen(Utc::now())
|
.last_seen(Utc::now())
|
||||||
.health_score(95.0)
|
.health_score(95.0)
|
||||||
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
||||||
.node_type("3Node".to_string())
|
.node_type("MyceliumNode".to_string())
|
||||||
.grid_node_id(grid_node_id)
|
.grid_node_id(grid_node_id)
|
||||||
.grid_data(grid_data)
|
.grid_data(grid_data)
|
||||||
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
||||||
@@ -1536,7 +1536,7 @@ impl FarmerService {
|
|||||||
.last_seen(Utc::now())
|
.last_seen(Utc::now())
|
||||||
.health_score(95.0)
|
.health_score(95.0)
|
||||||
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
||||||
.node_type("3Node".to_string())
|
.node_type("MyceliumNode".to_string())
|
||||||
.grid_node_id(grid_node_id)
|
.grid_node_id(grid_node_id)
|
||||||
.grid_data(grid_data)
|
.grid_data(grid_data)
|
||||||
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
||||||
@@ -1638,7 +1638,7 @@ impl FarmerService {
|
|||||||
.last_seen(Utc::now())
|
.last_seen(Utc::now())
|
||||||
.health_score(95.0)
|
.health_score(95.0)
|
||||||
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
||||||
.node_type("3Node".to_string())
|
.node_type("MyceliumNode".to_string())
|
||||||
.grid_node_id(grid_node_id)
|
.grid_node_id(grid_node_id)
|
||||||
.grid_data(grid_data)
|
.grid_data(grid_data)
|
||||||
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
||||||
@@ -1703,7 +1703,7 @@ impl FarmerService {
|
|||||||
.last_seen(Utc::now())
|
.last_seen(Utc::now())
|
||||||
.health_score(95.0)
|
.health_score(95.0)
|
||||||
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
.region(if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string())
|
||||||
.node_type("3Node".to_string())
|
.node_type("MyceliumNode".to_string())
|
||||||
.grid_node_id(grid_node_id)
|
.grid_node_id(grid_node_id)
|
||||||
.grid_data(grid_data)
|
.grid_data(grid_data)
|
||||||
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
.availability_status(crate::models::user::NodeAvailabilityStatus::Available);
|
||||||
@@ -1803,7 +1803,7 @@ impl FarmerService {
|
|||||||
last_seen: Some(Utc::now()),
|
last_seen: Some(Utc::now()),
|
||||||
health_score: 100.0,
|
health_score: 100.0,
|
||||||
region: if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string(),
|
region: if grid_data.country.is_empty() { "Unknown" } else { &grid_data.country }.to_string(),
|
||||||
node_type: "3Node".to_string(),
|
node_type: "MyceliumNode".to_string(),
|
||||||
slice_formats: if slice_formats.is_empty() { None } else { Some(slice_formats.clone()) },
|
slice_formats: if slice_formats.is_empty() { None } else { Some(slice_formats.clone()) },
|
||||||
rental_options: rental_options.map(|ro| serde_json::to_value(&ro).unwrap_or_default()),
|
rental_options: rental_options.map(|ro| serde_json::to_value(&ro).unwrap_or_default()),
|
||||||
staking_options: None,
|
staking_options: None,
|
||||||
@@ -1845,8 +1845,8 @@ impl FarmerService {
|
|||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if slice_rental_enabled || full_node_rental_enabled {
|
if slice_rental_enabled || full_node_rental_enabled {
|
||||||
let farmer_name = persistent_data.name.unwrap_or_else(|| "Unknown Farmer".to_string());
|
let resource_provider_name = persistent_data.name.unwrap_or_else(|| "Unknown ResourceProvider".to_string());
|
||||||
self.auto_generate_marketplace_products(&node, user_email, &farmer_name, None)?;
|
self.auto_generate_marketplace_products(&node, user_email, &resource_provider_name, None)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(node)
|
Ok(node)
|
||||||
@@ -1856,7 +1856,7 @@ impl FarmerService {
|
|||||||
// NODE GROUP MANAGEMENT
|
// NODE GROUP MANAGEMENT
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
/// Ensure default node groups exist for a farmer
|
/// Ensure default node groups exist for a resource_provider
|
||||||
pub fn ensure_default_node_groups(&self, user_email: &str) -> Result<(), String> {
|
pub fn ensure_default_node_groups(&self, user_email: &str) -> Result<(), String> {
|
||||||
let mut persistent_data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
let mut persistent_data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
||||||
|
|
||||||
@@ -1898,7 +1898,7 @@ impl FarmerService {
|
|||||||
Ok(group)
|
Ok(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all node groups for a farmer (ensures defaults exist)
|
/// Get all node groups for a resource_provider (ensures defaults exist)
|
||||||
pub fn get_node_groups(&self, user_email: &str) -> Vec<NodeGroup> {
|
pub fn get_node_groups(&self, user_email: &str) -> Vec<NodeGroup> {
|
||||||
// Ensure default groups exist first
|
// Ensure default groups exist first
|
||||||
if let Err(e) = self.ensure_default_node_groups(user_email) {
|
if let Err(e) = self.ensure_default_node_groups(user_email) {
|
||||||
@@ -2165,7 +2165,7 @@ impl FarmerService {
|
|||||||
|
|
||||||
/// Get node by name (for duplicate checking)
|
/// Get node by name (for duplicate checking)
|
||||||
pub fn get_node_by_name(&self, user_email: &str, node_name: &str) -> Option<FarmNode> {
|
pub fn get_node_by_name(&self, user_email: &str, node_name: &str) -> Option<FarmNode> {
|
||||||
let nodes = self.get_farmer_nodes(user_email);
|
let nodes = self.get_resource_provider_nodes(user_email);
|
||||||
nodes.into_iter().find(|n| n.name == node_name)
|
nodes.into_iter().find(|n| n.name == node_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2278,12 +2278,12 @@ impl FarmerService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create default user data for new farmers
|
/// Create default user data for new resource providers
|
||||||
fn create_default_user_data(user_email: &str) -> UserPersistentData {
|
fn create_default_user_data(user_email: &str) -> UserPersistentData {
|
||||||
crate::models::builders::SessionDataBuilder::new_user(user_email)
|
crate::models::builders::SessionDataBuilder::new_user(user_email)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh all slice calculations for a farmer
|
/// Refresh all slice calculations for a resource_provider
|
||||||
pub fn refresh_all_slice_calculations(&self, user_email: &str) -> Result<(), String> {
|
pub fn refresh_all_slice_calculations(&self, user_email: &str) -> Result<(), String> {
|
||||||
let mut persistent_data = UserPersistence::load_user_data(user_email)
|
let mut persistent_data = UserPersistence::load_user_data(user_email)
|
||||||
.ok_or("User data not found")?;
|
.ok_or("User data not found")?;
|
||||||
@@ -2395,7 +2395,7 @@ impl FarmerService {
|
|||||||
Ok(slices)
|
Ok(slices)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get comprehensive slice statistics for a farmer
|
/// Get comprehensive slice statistics for a resource_provider
|
||||||
pub fn get_slice_statistics(&self, user_email: &str) -> Result<serde_json::Value, String> {
|
pub fn get_slice_statistics(&self, user_email: &str) -> Result<serde_json::Value, String> {
|
||||||
let persistent_data = UserPersistence::load_user_data(user_email)
|
let persistent_data = UserPersistence::load_user_data(user_email)
|
||||||
.ok_or("User data not found")?;
|
.ok_or("User data not found")?;
|
||||||
@@ -2560,7 +2560,7 @@ impl FarmerService {
|
|||||||
last_seen: Some(Utc::now()),
|
last_seen: Some(Utc::now()),
|
||||||
health_score: 100.0,
|
health_score: 100.0,
|
||||||
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
||||||
node_type: "3Node".to_string(),
|
node_type: "MyceliumNode".to_string(),
|
||||||
slice_formats: None,
|
slice_formats: None,
|
||||||
rental_options: {
|
rental_options: {
|
||||||
let rental_opts = crate::models::user::NodeRentalOptions {
|
let rental_opts = crate::models::user::NodeRentalOptions {
|
||||||
@@ -2660,7 +2660,7 @@ impl FarmerService {
|
|||||||
last_seen: Some(Utc::now()),
|
last_seen: Some(Utc::now()),
|
||||||
health_score: 100.0,
|
health_score: 100.0,
|
||||||
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
region: if grid_data.country.is_empty() { "Unknown".to_string() } else { grid_data.country.clone() },
|
||||||
node_type: "3Node".to_string(),
|
node_type: "MyceliumNode".to_string(),
|
||||||
slice_formats: None,
|
slice_formats: None,
|
||||||
rental_options: Some(serde_json::to_value(&crate::models::user::NodeRentalOptions {
|
rental_options: Some(serde_json::to_value(&crate::models::user::NodeRentalOptions {
|
||||||
full_node_available: enable_full_node_rental,
|
full_node_available: enable_full_node_rental,
|
||||||
@@ -2821,7 +2821,7 @@ impl FarmerService {
|
|||||||
Ok(repaired_count)
|
Ok(repaired_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh all slice calculations for a farmer (async version)
|
/// Refresh all slice calculations for a resource_provider (async version)
|
||||||
pub async fn refresh_all_slice_calculations_async(&self, user_email: &str) -> Result<u32, String> {
|
pub async fn refresh_all_slice_calculations_async(&self, user_email: &str) -> Result<u32, String> {
|
||||||
let mut persistent_data = UserPersistence::load_user_data(user_email)
|
let mut persistent_data = UserPersistence::load_user_data(user_email)
|
||||||
.ok_or("User data not found")?;
|
.ok_or("User data not found")?;
|
||||||
@@ -2971,7 +2971,7 @@ pub struct NodeUpdateData {
|
|||||||
pub marketplace_sla: Option<MarketplaceSLA>,
|
pub marketplace_sla: Option<MarketplaceSLA>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default slice formats available to all farmers
|
/// Default slice formats available to all resource providers
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DefaultSliceFormat {
|
pub struct DefaultSliceFormat {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -2984,9 +2984,9 @@ pub struct DefaultSliceFormat {
|
|||||||
pub price_per_hour: rust_decimal::Decimal,
|
pub price_per_hour: rust_decimal::Decimal,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Farmer statistics summary
|
/// ResourceProvider statistics summary
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FarmerStatistics {
|
pub struct ResourceProviderStatistics {
|
||||||
pub total_nodes: i32,
|
pub total_nodes: i32,
|
||||||
pub online_nodes: i32,
|
pub online_nodes: i32,
|
||||||
pub total_capacity: NodeCapacity,
|
pub total_capacity: NodeCapacity,
|
@@ -18,7 +18,7 @@ pub struct SliceAssignmentService {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct SliceAssignmentRequest {
|
pub struct SliceAssignmentRequest {
|
||||||
pub user_email: String,
|
pub user_email: String,
|
||||||
pub farmer_email: String,
|
pub resource_provider_email: String,
|
||||||
pub node_id: String,
|
pub node_id: String,
|
||||||
pub combination_id: String,
|
pub combination_id: String,
|
||||||
pub quantity: u32,
|
pub quantity: u32,
|
||||||
@@ -127,7 +127,7 @@ pub struct SecurityConfiguration {
|
|||||||
pub struct SliceAssignment {
|
pub struct SliceAssignment {
|
||||||
pub assignment_id: String,
|
pub assignment_id: String,
|
||||||
pub user_email: String,
|
pub user_email: String,
|
||||||
pub farmer_email: String,
|
pub resource_provider_email: String,
|
||||||
pub node_id: String,
|
pub node_id: String,
|
||||||
pub combination_id: String,
|
pub combination_id: String,
|
||||||
pub slice_allocations: Vec<SliceAllocation>,
|
pub slice_allocations: Vec<SliceAllocation>,
|
||||||
@@ -241,7 +241,7 @@ impl SliceAssignmentService {
|
|||||||
let assignment = SliceAssignment {
|
let assignment = SliceAssignment {
|
||||||
assignment_id,
|
assignment_id,
|
||||||
user_email: request.user_email,
|
user_email: request.user_email,
|
||||||
farmer_email: request.farmer_email,
|
resource_provider_email: request.resource_provider_email,
|
||||||
node_id: request.node_id,
|
node_id: request.node_id,
|
||||||
combination_id: request.combination_id,
|
combination_id: request.combination_id,
|
||||||
slice_allocations,
|
slice_allocations,
|
||||||
@@ -377,7 +377,7 @@ impl SliceAssignmentService {
|
|||||||
"status": "deploying",
|
"status": "deploying",
|
||||||
"deployment_type": "vm",
|
"deployment_type": "vm",
|
||||||
"node_id": assignment.node_id,
|
"node_id": assignment.node_id,
|
||||||
"farmer_email": assignment.farmer_email,
|
"resource_provider_email": assignment.resource_provider_email,
|
||||||
"started_at": Utc::now(),
|
"started_at": Utc::now(),
|
||||||
"estimated_completion": Utc::now() + chrono::Duration::minutes(5)
|
"estimated_completion": Utc::now() + chrono::Duration::minutes(5)
|
||||||
});
|
});
|
||||||
|
@@ -42,7 +42,7 @@ pub struct SliceCombination {
|
|||||||
pub node_location: String,
|
pub node_location: String,
|
||||||
pub node_certification_type: String,
|
pub node_certification_type: String,
|
||||||
pub node_id: String,
|
pub node_id: String,
|
||||||
pub farmer_email: String,
|
pub resource_provider_email: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track individual slice rentals
|
/// Track individual slice rentals
|
||||||
@@ -70,7 +70,7 @@ pub enum AllocationStatus {
|
|||||||
pub struct SlicePricing {
|
pub struct SlicePricing {
|
||||||
pub base_price_per_hour: Decimal, // Price for 1 base slice per hour
|
pub base_price_per_hour: Decimal, // Price for 1 base slice per hour
|
||||||
pub currency: String,
|
pub currency: String,
|
||||||
pub pricing_multiplier: Decimal, // Farmer can adjust pricing (0.5x - 2.0x)
|
pub pricing_multiplier: Decimal, // ResourceProvider can adjust pricing (0.5x - 2.0x)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SlicePricing {
|
impl Default for SlicePricing {
|
||||||
@@ -157,7 +157,7 @@ impl SliceCalculatorService {
|
|||||||
max_base_slices: u32,
|
max_base_slices: u32,
|
||||||
allocated_slices: u32,
|
allocated_slices: u32,
|
||||||
node: &FarmNode,
|
node: &FarmNode,
|
||||||
farmer_email: &str
|
resource_provider_email: &str
|
||||||
) -> Vec<SliceCombination> {
|
) -> Vec<SliceCombination> {
|
||||||
let available_base_slices = max_base_slices.saturating_sub(allocated_slices);
|
let available_base_slices = max_base_slices.saturating_sub(allocated_slices);
|
||||||
let mut combinations = Vec::new();
|
let mut combinations = Vec::new();
|
||||||
@@ -208,7 +208,7 @@ impl SliceCalculatorService {
|
|||||||
.to_string())
|
.to_string())
|
||||||
.unwrap_or_else(|| "DIY".to_string()),
|
.unwrap_or_else(|| "DIY".to_string()),
|
||||||
node_id: node.id.clone(),
|
node_id: node.id.clone(),
|
||||||
farmer_email: farmer_email.to_string(),
|
resource_provider_email: resource_provider_email.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
combinations.push(combination);
|
combinations.push(combination);
|
||||||
@@ -225,7 +225,7 @@ impl SliceCalculatorService {
|
|||||||
max_base_slices: u32,
|
max_base_slices: u32,
|
||||||
allocated_slices: u32,
|
allocated_slices: u32,
|
||||||
node: &FarmNode,
|
node: &FarmNode,
|
||||||
farmer_email: &str,
|
resource_provider_email: &str,
|
||||||
uptime_percentage: f64,
|
uptime_percentage: f64,
|
||||||
bandwidth_mbps: u32,
|
bandwidth_mbps: u32,
|
||||||
base_price_per_hour: Decimal
|
base_price_per_hour: Decimal
|
||||||
@@ -283,7 +283,7 @@ impl SliceCalculatorService {
|
|||||||
.to_string())
|
.to_string())
|
||||||
.unwrap_or_else(|| "DIY".to_string()),
|
.unwrap_or_else(|| "DIY".to_string()),
|
||||||
node_id: node.id.clone(),
|
node_id: node.id.clone(),
|
||||||
farmer_email: farmer_email.to_string(),
|
resource_provider_email: resource_provider_email.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
combinations.push(combination);
|
combinations.push(combination);
|
||||||
@@ -304,7 +304,7 @@ impl SliceCalculatorService {
|
|||||||
&self,
|
&self,
|
||||||
node: &mut FarmNode,
|
node: &mut FarmNode,
|
||||||
rented_base_slices: u32,
|
rented_base_slices: u32,
|
||||||
farmer_email: &str
|
resource_provider_email: &str
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Update allocated count
|
// Update allocated count
|
||||||
node.allocated_base_slices += rented_base_slices as i32;
|
node.allocated_base_slices += rented_base_slices as i32;
|
||||||
@@ -314,7 +314,7 @@ impl SliceCalculatorService {
|
|||||||
node.total_base_slices as u32,
|
node.total_base_slices as u32,
|
||||||
node.allocated_base_slices as u32,
|
node.allocated_base_slices as u32,
|
||||||
node,
|
node,
|
||||||
farmer_email
|
resource_provider_email
|
||||||
);
|
);
|
||||||
node.available_combinations = combinations.iter()
|
node.available_combinations = combinations.iter()
|
||||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||||
@@ -328,7 +328,7 @@ impl SliceCalculatorService {
|
|||||||
&self,
|
&self,
|
||||||
node: &mut FarmNode,
|
node: &mut FarmNode,
|
||||||
released_base_slices: u32,
|
released_base_slices: u32,
|
||||||
farmer_email: &str
|
resource_provider_email: &str
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Update allocated count
|
// Update allocated count
|
||||||
node.allocated_base_slices = node.allocated_base_slices.saturating_sub(released_base_slices as i32);
|
node.allocated_base_slices = node.allocated_base_slices.saturating_sub(released_base_slices as i32);
|
||||||
@@ -338,7 +338,7 @@ impl SliceCalculatorService {
|
|||||||
node.total_base_slices as u32,
|
node.total_base_slices as u32,
|
||||||
node.allocated_base_slices as u32,
|
node.allocated_base_slices as u32,
|
||||||
node,
|
node,
|
||||||
farmer_email
|
resource_provider_email
|
||||||
).iter()
|
).iter()
|
||||||
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
.map(|c| serde_json::to_value(c).unwrap_or_default())
|
||||||
.collect();
|
.collect();
|
||||||
@@ -364,7 +364,7 @@ pub struct SliceRental {
|
|||||||
pub rental_id: String,
|
pub rental_id: String,
|
||||||
pub slice_combination_id: String,
|
pub slice_combination_id: String,
|
||||||
pub node_id: String,
|
pub node_id: String,
|
||||||
pub farmer_email: String,
|
pub resource_provider_email: String,
|
||||||
pub slice_allocation: SliceAllocation,
|
pub slice_allocation: SliceAllocation,
|
||||||
pub total_cost: Decimal,
|
pub total_cost: Decimal,
|
||||||
pub payment_status: PaymentStatus,
|
pub payment_status: PaymentStatus,
|
||||||
|
@@ -56,11 +56,11 @@ impl SliceRentalService {
|
|||||||
SliceRentalServiceBuilder::new()
|
SliceRentalServiceBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rent a slice combination from a farmer's node
|
/// Rent a slice combination from a resource_provider's node
|
||||||
pub fn rent_slice_combination(
|
pub fn rent_slice_combination(
|
||||||
&self,
|
&self,
|
||||||
renter_email: &str,
|
renter_email: &str,
|
||||||
farmer_email: &str,
|
resource_provider_email: &str,
|
||||||
node_id: &str,
|
node_id: &str,
|
||||||
combination_id: &str,
|
combination_id: &str,
|
||||||
quantity: u32,
|
quantity: u32,
|
||||||
@@ -68,9 +68,9 @@ impl SliceRentalService {
|
|||||||
) -> Result<SliceRental, String> {
|
) -> Result<SliceRental, String> {
|
||||||
// Atomic operation with file locking to prevent conflicts
|
// Atomic operation with file locking to prevent conflicts
|
||||||
if self.enable_file_locking {
|
if self.enable_file_locking {
|
||||||
self.rent_with_file_lock(renter_email, farmer_email, node_id, combination_id, quantity, rental_duration_hours)
|
self.rent_with_file_lock(renter_email, resource_provider_email, node_id, combination_id, quantity, rental_duration_hours)
|
||||||
} else {
|
} else {
|
||||||
self.rent_without_lock(renter_email, farmer_email, node_id, combination_id, quantity, rental_duration_hours)
|
self.rent_without_lock(renter_email, resource_provider_email, node_id, combination_id, quantity, rental_duration_hours)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ impl SliceRentalService {
|
|||||||
pub fn rent_slice_combination_with_deployment(
|
pub fn rent_slice_combination_with_deployment(
|
||||||
&self,
|
&self,
|
||||||
renter_email: &str,
|
renter_email: &str,
|
||||||
farmer_email: &str,
|
resource_provider_email: &str,
|
||||||
node_id: &str,
|
node_id: &str,
|
||||||
combination_id: &str,
|
combination_id: &str,
|
||||||
quantity: u32,
|
quantity: u32,
|
||||||
@@ -89,7 +89,7 @@ impl SliceRentalService {
|
|||||||
) -> Result<SliceRental, String> {
|
) -> Result<SliceRental, String> {
|
||||||
// First rent the slice combination
|
// First rent the slice combination
|
||||||
let mut rental = self.rent_slice_combination(
|
let mut rental = self.rent_slice_combination(
|
||||||
renter_email, farmer_email, node_id, combination_id, quantity, rental_duration_hours
|
renter_email, resource_provider_email, node_id, combination_id, quantity, rental_duration_hours
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Add deployment metadata to the rental
|
// Add deployment metadata to the rental
|
||||||
@@ -133,21 +133,21 @@ impl SliceRentalService {
|
|||||||
fn rent_with_file_lock(
|
fn rent_with_file_lock(
|
||||||
&self,
|
&self,
|
||||||
renter_email: &str,
|
renter_email: &str,
|
||||||
farmer_email: &str,
|
resource_provider_email: &str,
|
||||||
node_id: &str,
|
node_id: &str,
|
||||||
combination_id: &str,
|
combination_id: &str,
|
||||||
quantity: u32,
|
quantity: u32,
|
||||||
rental_duration_hours: u32,
|
rental_duration_hours: u32,
|
||||||
) -> Result<SliceRental, String> {
|
) -> Result<SliceRental, String> {
|
||||||
// Create lock file
|
// Create lock file
|
||||||
let lock_file_path = format!("./user_data/.lock_{}_{}", farmer_email.replace("@", "_"), node_id);
|
let lock_file_path = format!("./user_data/.lock_{}_{}", resource_provider_email.replace("@", "_"), node_id);
|
||||||
let _lock_file = OpenOptions::new()
|
let _lock_file = OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(&lock_file_path)
|
.open(&lock_file_path)
|
||||||
.map_err(|e| format!("Failed to create lock file: {}", e))?;
|
.map_err(|e| format!("Failed to create lock file: {}", e))?;
|
||||||
|
|
||||||
let result = self.rent_without_lock(renter_email, farmer_email, node_id, combination_id, quantity, rental_duration_hours);
|
let result = self.rent_without_lock(renter_email, resource_provider_email, node_id, combination_id, quantity, rental_duration_hours);
|
||||||
|
|
||||||
// Clean up lock file
|
// Clean up lock file
|
||||||
let _ = std::fs::remove_file(&lock_file_path);
|
let _ = std::fs::remove_file(&lock_file_path);
|
||||||
@@ -159,21 +159,21 @@ impl SliceRentalService {
|
|||||||
fn rent_without_lock(
|
fn rent_without_lock(
|
||||||
&self,
|
&self,
|
||||||
renter_email: &str,
|
renter_email: &str,
|
||||||
farmer_email: &str,
|
resource_provider_email: &str,
|
||||||
node_id: &str,
|
node_id: &str,
|
||||||
combination_id: &str,
|
combination_id: &str,
|
||||||
quantity: u32,
|
quantity: u32,
|
||||||
rental_duration_hours: u32,
|
rental_duration_hours: u32,
|
||||||
) -> Result<SliceRental, String> {
|
) -> Result<SliceRental, String> {
|
||||||
// Load farmer data
|
// Load resource_provider data
|
||||||
let mut farmer_data = UserPersistence::load_user_data(farmer_email)
|
let mut resource_provider_data = UserPersistence::load_user_data(resource_provider_email)
|
||||||
.ok_or_else(|| "Farmer not found".to_string())?;
|
.ok_or_else(|| "ResourceProvider not found".to_string())?;
|
||||||
|
|
||||||
// Find the node
|
// Find the node
|
||||||
let node_index = farmer_data.nodes.iter().position(|n| n.id == node_id)
|
let node_index = resource_provider_data.nodes.iter().position(|n| n.id == node_id)
|
||||||
.ok_or_else(|| "Node not found".to_string())?;
|
.ok_or_else(|| "Node not found".to_string())?;
|
||||||
|
|
||||||
let node = &mut farmer_data.nodes[node_index];
|
let node = &mut resource_provider_data.nodes[node_index];
|
||||||
|
|
||||||
// Find the slice combination
|
// Find the slice combination
|
||||||
let combination = node.available_combinations.iter()
|
let combination = node.available_combinations.iter()
|
||||||
@@ -226,7 +226,7 @@ impl SliceRentalService {
|
|||||||
self.slice_calculator.update_availability_after_rental(
|
self.slice_calculator.update_availability_after_rental(
|
||||||
node,
|
node,
|
||||||
total_base_slices_needed,
|
total_base_slices_needed,
|
||||||
farmer_email
|
resource_provider_email
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Add allocation to node
|
// Add allocation to node
|
||||||
@@ -237,7 +237,7 @@ impl SliceRentalService {
|
|||||||
rental_id: rental_id.clone(),
|
rental_id: rental_id.clone(),
|
||||||
slice_combination_id: combination_id.to_string(),
|
slice_combination_id: combination_id.to_string(),
|
||||||
node_id: node_id.to_string(),
|
node_id: node_id.to_string(),
|
||||||
farmer_email: farmer_email.to_string(),
|
resource_provider_email: resource_provider_email.to_string(),
|
||||||
slice_allocation: allocation,
|
slice_allocation: allocation,
|
||||||
total_cost,
|
total_cost,
|
||||||
payment_status: PaymentStatus::Paid,
|
payment_status: PaymentStatus::Paid,
|
||||||
@@ -260,12 +260,12 @@ impl SliceRentalService {
|
|||||||
renter_data.wallet_balance_usd -= total_cost;
|
renter_data.wallet_balance_usd -= total_cost;
|
||||||
renter_data.slice_rentals.push(slice_rental.clone());
|
renter_data.slice_rentals.push(slice_rental.clone());
|
||||||
|
|
||||||
// Add earnings to farmer
|
// Add earnings to resource_provider
|
||||||
farmer_data.wallet_balance_usd += total_cost;
|
resource_provider_data.wallet_balance_usd += total_cost;
|
||||||
|
|
||||||
// Save both user data
|
// Save both user data
|
||||||
UserPersistence::save_user_data(&farmer_data)
|
UserPersistence::save_user_data(&resource_provider_data)
|
||||||
.map_err(|e| format!("Failed to save farmer data: {}", e))?;
|
.map_err(|e| format!("Failed to save resource_provider data: {}", e))?;
|
||||||
UserPersistence::save_user_data(&renter_data)
|
UserPersistence::save_user_data(&renter_data)
|
||||||
.map_err(|e| format!("Failed to save renter data: {}", e))?;
|
.map_err(|e| format!("Failed to save renter data: {}", e))?;
|
||||||
|
|
||||||
@@ -273,14 +273,14 @@ impl SliceRentalService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Release expired slice rentals
|
/// Release expired slice rentals
|
||||||
pub fn release_expired_rentals(&self, farmer_email: &str) -> Result<u32, String> {
|
pub fn release_expired_rentals(&self, resource_provider_email: &str) -> Result<u32, String> {
|
||||||
let mut farmer_data = UserPersistence::load_user_data(farmer_email)
|
let mut resource_provider_data = UserPersistence::load_user_data(resource_provider_email)
|
||||||
.ok_or_else(|| "Farmer not found".to_string())?;
|
.ok_or_else(|| "ResourceProvider not found".to_string())?;
|
||||||
|
|
||||||
let mut released_count = 0;
|
let mut released_count = 0;
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
|
|
||||||
for node in &mut farmer_data.nodes {
|
for node in &mut resource_provider_data.nodes {
|
||||||
let mut expired_allocations = Vec::new();
|
let mut expired_allocations = Vec::new();
|
||||||
|
|
||||||
// Find expired allocations
|
// Find expired allocations
|
||||||
@@ -314,7 +314,7 @@ impl SliceRentalService {
|
|||||||
self.slice_calculator.update_availability_after_release(
|
self.slice_calculator.update_availability_after_release(
|
||||||
node,
|
node,
|
||||||
base_slices_used,
|
base_slices_used,
|
||||||
farmer_email
|
resource_provider_email
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
released_count += 1;
|
released_count += 1;
|
||||||
@@ -322,20 +322,20 @@ impl SliceRentalService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if released_count > 0 {
|
if released_count > 0 {
|
||||||
UserPersistence::save_user_data(&farmer_data)
|
UserPersistence::save_user_data(&resource_provider_data)
|
||||||
.map_err(|e| format!("Failed to save farmer data: {}", e))?;
|
.map_err(|e| format!("Failed to save resource_provider data: {}", e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(released_count)
|
Ok(released_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Get slice rental statistics for a farmer
|
/// Get slice rental statistics for a resource_provider
|
||||||
pub fn get_farmer_slice_statistics(&self, farmer_email: &str) -> SliceRentalStatistics {
|
pub fn get_resource_provider_slice_statistics(&self, resource_provider_email: &str) -> SliceRentalStatistics {
|
||||||
if let Some(farmer_data) = UserPersistence::load_user_data(farmer_email) {
|
if let Some(resource_provider_data) = UserPersistence::load_user_data(resource_provider_email) {
|
||||||
let mut stats = SliceRentalStatistics::default();
|
let mut stats = SliceRentalStatistics::default();
|
||||||
|
|
||||||
for node in &farmer_data.nodes {
|
for node in &resource_provider_data.nodes {
|
||||||
stats.total_nodes += 1;
|
stats.total_nodes += 1;
|
||||||
stats.total_base_slices += node.total_base_slices as u32;
|
stats.total_base_slices += node.total_base_slices as u32;
|
||||||
stats.allocated_base_slices += node.allocated_base_slices as u32;
|
stats.allocated_base_slices += node.allocated_base_slices as u32;
|
||||||
@@ -379,7 +379,7 @@ impl SliceRentalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Statistics for farmer slice rentals
|
/// Statistics for resource_provider slice rentals
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct SliceRentalStatistics {
|
pub struct SliceRentalStatistics {
|
||||||
pub total_nodes: u32,
|
pub total_nodes: u32,
|
||||||
|
@@ -41,15 +41,15 @@ pub struct UserPersistentData {
|
|||||||
pub slas: Vec<ServiceLevelAgreement>,
|
pub slas: Vec<ServiceLevelAgreement>,
|
||||||
// App provider data
|
// App provider data
|
||||||
pub apps: Vec<crate::models::user::PublishedApp>,
|
pub apps: Vec<crate::models::user::PublishedApp>,
|
||||||
pub app_deployments: Vec<AppDeployment>,
|
pub application_deployments: Vec<AppDeployment>,
|
||||||
// Account deletion tracking
|
// Account deletion tracking
|
||||||
pub deleted: Option<bool>,
|
pub deleted: Option<bool>,
|
||||||
pub deleted_at: Option<String>,
|
pub deleted_at: Option<String>,
|
||||||
pub deletion_reason: Option<String>,
|
pub deletion_reason: Option<String>,
|
||||||
// Farmer-specific data
|
// ResourceProvider-specific data
|
||||||
pub nodes: Vec<crate::models::user::FarmNode>,
|
pub nodes: Vec<crate::models::user::FarmNode>,
|
||||||
pub farmer_earnings: Vec<crate::models::user::EarningsRecord>,
|
pub resource_provider_earnings: Vec<crate::models::user::EarningsRecord>,
|
||||||
pub farmer_settings: Option<crate::models::user::FarmerSettings>,
|
pub resource_provider_settings: Option<crate::models::user::ResourceProviderSettings>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub slice_products: Vec<crate::models::product::Product>,
|
pub slice_products: Vec<crate::models::product::Product>,
|
||||||
// User activity tracking
|
// User activity tracking
|
||||||
@@ -63,10 +63,10 @@ pub struct UserPersistentData {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub active_product_rentals: Vec<ProductRental>,
|
pub active_product_rentals: Vec<ProductRental>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub farmer_rental_earnings: Vec<crate::models::user::FarmerRentalEarning>,
|
pub resource_provider_rental_earnings: Vec<crate::models::user::ResourceProviderRentalEarning>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub node_rentals: Vec<crate::models::user::NodeRental>,
|
pub node_rentals: Vec<crate::models::user::NodeRental>,
|
||||||
// Node groups for farmer organization
|
// Node groups for resource_provider organization
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub node_groups: Vec<crate::models::user::NodeGroup>,
|
pub node_groups: Vec<crate::models::user::NodeGroup>,
|
||||||
// NEW: Slice rental tracking for users
|
// NEW: Slice rental tracking for users
|
||||||
@@ -137,20 +137,20 @@ impl Default for UserPersistentData {
|
|||||||
availability: None,
|
availability: None,
|
||||||
slas: Vec::new(),
|
slas: Vec::new(),
|
||||||
apps: Vec::new(),
|
apps: Vec::new(),
|
||||||
app_deployments: Vec::new(),
|
application_deployments: Vec::new(),
|
||||||
deleted: None,
|
deleted: None,
|
||||||
deleted_at: None,
|
deleted_at: None,
|
||||||
deletion_reason: None,
|
deletion_reason: None,
|
||||||
nodes: Vec::new(),
|
nodes: Vec::new(),
|
||||||
farmer_earnings: Vec::new(),
|
resource_provider_earnings: Vec::new(),
|
||||||
farmer_settings: None,
|
resource_provider_settings: None,
|
||||||
slice_products: Vec::new(),
|
slice_products: Vec::new(),
|
||||||
user_activities: Vec::new(),
|
user_activities: Vec::new(),
|
||||||
user_preferences: None,
|
user_preferences: None,
|
||||||
usage_statistics: None,
|
usage_statistics: None,
|
||||||
orders: Vec::new(),
|
orders: Vec::new(),
|
||||||
active_product_rentals: Vec::new(),
|
active_product_rentals: Vec::new(),
|
||||||
farmer_rental_earnings: Vec::new(),
|
resource_provider_rental_earnings: Vec::new(),
|
||||||
node_rentals: Vec::new(),
|
node_rentals: Vec::new(),
|
||||||
node_groups: Vec::new(),
|
node_groups: Vec::new(),
|
||||||
slice_rentals: Vec::new(),
|
slice_rentals: Vec::new(),
|
||||||
@@ -1247,22 +1247,22 @@ impl UserPersistence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get app deployments for a user
|
/// Get app deployments for a user
|
||||||
pub fn get_user_app_deployments(user_email: &str) -> Vec<AppDeployment> {
|
pub fn get_user_application_deployments(user_email: &str) -> Vec<AppDeployment> {
|
||||||
if let Some(data) = Self::load_user_data(user_email) {
|
if let Some(data) = Self::load_user_data(user_email) {
|
||||||
data.app_deployments
|
data.application_deployments
|
||||||
} else {
|
} else {
|
||||||
Vec::default()
|
Vec::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new app deployment
|
/// Add a new app deployment
|
||||||
pub fn add_user_app_deployment(
|
pub fn add_user_application_deployment(
|
||||||
user_email: &str,
|
user_email: &str,
|
||||||
deployment: AppDeployment
|
deployment: AppDeployment
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
let mut data = crate::models::builders::SessionDataBuilder::load_or_create(user_email);
|
||||||
|
|
||||||
data.app_deployments.push(deployment.clone());
|
data.application_deployments.push(deployment.clone());
|
||||||
Self::save_user_data(&data)?;
|
Self::save_user_data(&data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1306,19 +1306,19 @@ impl UserPersistence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get farmer earnings for a user
|
/// Get resource_provider earnings for a user
|
||||||
pub fn get_farmer_earnings(user_email: &str) -> Vec<crate::models::user::EarningsRecord> {
|
pub fn get_resource_provider_earnings(user_email: &str) -> Vec<crate::models::user::EarningsRecord> {
|
||||||
if let Some(data) = Self::load_user_data(user_email) {
|
if let Some(data) = Self::load_user_data(user_email) {
|
||||||
data.farmer_earnings
|
data.resource_provider_earnings
|
||||||
} else {
|
} else {
|
||||||
Vec::default()
|
Vec::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get farmer settings for a user
|
/// Get resource_provider settings for a user
|
||||||
pub fn get_farmer_settings(user_email: &str) -> Option<crate::models::user::FarmerSettings> {
|
pub fn get_resource_provider_settings(user_email: &str) -> Option<crate::models::user::ResourceProviderSettings> {
|
||||||
if let Some(data) = Self::load_user_data(user_email) {
|
if let Some(data) = Self::load_user_data(user_email) {
|
||||||
data.farmer_settings
|
data.resource_provider_settings
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@@ -181,9 +181,9 @@ impl UserService {
|
|||||||
/// Get user's purchased applications (for user dashboard) - derived from deployments
|
/// Get user's purchased applications (for user dashboard) - derived from deployments
|
||||||
pub fn get_user_applications(&self, user_email: &str) -> Vec<crate::models::user::PublishedApp> {
|
pub fn get_user_applications(&self, user_email: &str) -> Vec<crate::models::user::PublishedApp> {
|
||||||
if let Some(persistent_data) = UserPersistence::load_user_data(user_email) {
|
if let Some(persistent_data) = UserPersistence::load_user_data(user_email) {
|
||||||
// Convert app deployments to application view for user dashboard
|
// Convert application deployments to application view for user dashboard
|
||||||
// IMPORTANT: Only show deployments where THIS user is the customer
|
// IMPORTANT: Only show deployments where THIS user is the customer
|
||||||
let purchased_apps: Vec<crate::models::user::PublishedApp> = persistent_data.app_deployments
|
let purchased_apps: Vec<crate::models::user::PublishedApp> = persistent_data.application_deployments
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|d| d.status == "Active" && d.customer_email == user_email)
|
.filter(|d| d.status == "Active" && d.customer_email == user_email)
|
||||||
.map(|deployment| crate::models::user::PublishedApp {
|
.map(|deployment| crate::models::user::PublishedApp {
|
||||||
|
@@ -107,7 +107,7 @@ window.AppProviderDashboard = window.AppProviderDashboard || (class AppProviderD
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
return `Revenue: ${context.raw} TFC`;
|
return `Revenue: ${context.raw} MC`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,7 +117,7 @@ window.AppProviderDashboard = window.AppProviderDashboard || (class AppProviderD
|
|||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Revenue (TFC)'
|
text: 'Revenue (MC)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
@@ -168,7 +168,7 @@ window.AppProviderDashboard = window.AppProviderDashboard || (class AppProviderD
|
|||||||
label: function(context) {
|
label: function(context) {
|
||||||
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||||
const percentage = ((context.raw / total) * 100).toFixed(1);
|
const percentage = ((context.raw / total) * 100).toFixed(1);
|
||||||
return `${context.label}: ${context.raw} TFC (${percentage}%)`;
|
return `${context.label}: ${context.raw} MC (${percentage}%)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,7 +330,7 @@ window.AppProviderDashboard = window.AppProviderDashboard || (class AppProviderD
|
|||||||
<div>${stars}</div>
|
<div>${stars}</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>${(app.monthly_revenue_usd || 0)} TFC</td>
|
<td>${(app.monthly_revenue_usd || 0)} MC</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-sm btn-outline-primary" onclick="manageApp('${app.id}', '${app.name}')">Manage</button>
|
<button class="btn btn-sm btn-outline-primary" onclick="manageApp('${app.id}', '${app.name}')">Manage</button>
|
||||||
@@ -417,7 +417,7 @@ window.AppProviderDashboard = window.AppProviderDashboard || (class AppProviderD
|
|||||||
<small>${healthScore.toFixed(1)}%</small>
|
<small>${healthScore.toFixed(1)}%</small>
|
||||||
</td>
|
</td>
|
||||||
<td>${resourceUsage}</td>
|
<td>${resourceUsage}</td>
|
||||||
<td><strong>${this.estimateDeploymentRevenue(deployment.app_name)} TFC</strong></td>
|
<td><strong>${this.estimateDeploymentRevenue(deployment.app_name)} MC</strong></td>
|
||||||
<td>
|
<td>
|
||||||
${autoHealingDisplay}
|
${autoHealingDisplay}
|
||||||
</td>
|
</td>
|
||||||
@@ -566,7 +566,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load app provider data via apiJson
|
// Load app provider data via apiJson
|
||||||
window.apiJson('/api/dashboard/app-provider-data', { cache: 'no-store' })
|
window.apiJson('/api/dashboard/application-provider-data', { cache: 'no-store' })
|
||||||
.then(data => {
|
.then(data => {
|
||||||
window.__appProviderDashboard = new window.AppProviderDashboard(data || {});
|
window.__appProviderDashboard = new window.AppProviderDashboard(data || {});
|
||||||
})
|
})
|
@@ -508,7 +508,7 @@ class DashboardMessaging {
|
|||||||
const colors = {
|
const colors = {
|
||||||
'service_booking': 'primary',
|
'service_booking': 'primary',
|
||||||
'slice_rental': 'success',
|
'slice_rental': 'success',
|
||||||
'app_deployment': 'info',
|
'application_deployment': 'info',
|
||||||
'general': 'secondary',
|
'general': 'secondary',
|
||||||
'support': 'warning'
|
'support': 'warning'
|
||||||
};
|
};
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
// Dashboard Farmer JavaScript
|
// Dashboard ResourceProvider JavaScript
|
||||||
// Handles farmer dashboard functionality including automatic slice management, grid integration, and node management
|
// Handles resource_provider dashboard functionality including automatic slice management, grid integration, and node management
|
||||||
|
|
||||||
if (window.farmerDashboardInitialized) {
|
if (window.resourceProviderDashboardInitialized) {
|
||||||
console.debug('Farmer dashboard already initialized; skipping');
|
console.debug('ResourceProvider dashboard already initialized; skipping');
|
||||||
} else {
|
} else {
|
||||||
window.farmerDashboardInitialized = true;
|
window.resourceProviderDashboardInitialized = true;
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
console.log('🚜 Farmer Dashboard JavaScript loaded - Automatic Slice System');
|
console.log('🚜 ResourceProvider Dashboard JavaScript loaded - Automatic Slice System');
|
||||||
|
|
||||||
// Initialize dashboard
|
// Initialize dashboard
|
||||||
initializeFarmerDashboard();
|
initializeResourceProviderDashboard();
|
||||||
initializeAutomaticSliceManagement(); // NEW: Initialize automatic slice management
|
initializeAutomaticSliceManagement(); // NEW: Initialize automatic slice management
|
||||||
initializeNodeManagement();
|
initializeNodeManagement();
|
||||||
initializeStakingManagement();
|
initializeStakingManagement();
|
||||||
@@ -762,7 +762,7 @@ function showNodeDetailsModal(node) {
|
|||||||
function loadSliceStatistics() {
|
function loadSliceStatistics() {
|
||||||
console.log('📊 Loading slice statistics');
|
console.log('📊 Loading slice statistics');
|
||||||
|
|
||||||
window.apiJson('/api/dashboard/farmer/slice-statistics')
|
window.apiJson('/api/dashboard/resource_provider/slice-statistics')
|
||||||
.then(data => {
|
.then(data => {
|
||||||
// apiJson returns unwrapped data; support either {statistics: {...}} or {...}
|
// apiJson returns unwrapped data; support either {statistics: {...}} or {...}
|
||||||
const stats = (data && data.statistics) ? data.statistics : (data || {});
|
const stats = (data && data.statistics) ? data.statistics : (data || {});
|
||||||
@@ -1173,13 +1173,13 @@ function createSimpleIndividualPricingForms() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize farmer dashboard functionality
|
* Initialize resource_provider dashboard functionality
|
||||||
*/
|
*/
|
||||||
function initializeFarmerDashboard() {
|
function initializeResourceProviderDashboard() {
|
||||||
console.log('🚜 Initializing farmer dashboard');
|
console.log('🚜 Initializing resource_provider dashboard');
|
||||||
|
|
||||||
// Load farmer data
|
// Load resource_provider data
|
||||||
loadFarmerData();
|
loadResourceProviderData();
|
||||||
|
|
||||||
// Load slice templates
|
// Load slice templates
|
||||||
loadSliceTemplates();
|
loadSliceTemplates();
|
||||||
@@ -1188,7 +1188,7 @@ function initializeFarmerDashboard() {
|
|||||||
loadNodeGroups();
|
loadNodeGroups();
|
||||||
|
|
||||||
// Set up periodic data refresh
|
// Set up periodic data refresh
|
||||||
setInterval(loadFarmerData, 30000); // Refresh every 30 seconds
|
setInterval(loadResourceProviderData, 30000); // Refresh every 30 seconds
|
||||||
setInterval(loadSliceTemplates, 60000); // Refresh slice templates every minute
|
setInterval(loadSliceTemplates, 60000); // Refresh slice templates every minute
|
||||||
setInterval(loadNodeGroups, 60000); // Refresh node groups every minute
|
setInterval(loadNodeGroups, 60000); // Refresh node groups every minute
|
||||||
}
|
}
|
||||||
@@ -2911,12 +2911,12 @@ function resetSliceConfigurationForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load farmer data from API
|
* Load resource_provider data from API
|
||||||
*/
|
*/
|
||||||
async function loadFarmerData() {
|
async function loadResourceProviderData() {
|
||||||
try {
|
try {
|
||||||
const data = await window.apiJson('/api/dashboard/farmer-data');
|
const data = await window.apiJson('/api/dashboard/resource_provider-data');
|
||||||
console.log('🚜 Loaded farmer data:', data);
|
console.log('🚜 Loaded resource_provider data:', data);
|
||||||
|
|
||||||
// Load node groups as well
|
// Load node groups as well
|
||||||
const groupsResult = await window.apiJson('/api/dashboard/node-groups');
|
const groupsResult = await window.apiJson('/api/dashboard/node-groups');
|
||||||
@@ -2924,8 +2924,8 @@ async function loadFarmerData() {
|
|||||||
data.nodeGroups = nodeGroups;
|
data.nodeGroups = nodeGroups;
|
||||||
console.log('🚜 Node groups loaded:', nodeGroups);
|
console.log('🚜 Node groups loaded:', nodeGroups);
|
||||||
|
|
||||||
// Store farmer data globally for slice status checking
|
// Store resource_provider data globally for slice status checking
|
||||||
window.farmerData = data;
|
window.resourceProviderData = data;
|
||||||
|
|
||||||
// Update dashboard stats
|
// Update dashboard stats
|
||||||
updateDashboardStats(data);
|
updateDashboardStats(data);
|
||||||
@@ -2940,8 +2940,8 @@ async function loadFarmerData() {
|
|||||||
loadSliceTemplates();
|
loadSliceTemplates();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('🚜 Error loading farmer data:', error);
|
console.error('🚜 Error loading resource_provider data:', error);
|
||||||
showNotification('Failed to load farmer data', 'error');
|
showNotification('Failed to load resource_provider data', 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3130,13 +3130,13 @@ function showSliceTemplatesError() {
|
|||||||
* Returns 'Active' if at least one node is using this slice format, 'Available' otherwise
|
* Returns 'Active' if at least one node is using this slice format, 'Available' otherwise
|
||||||
*/
|
*/
|
||||||
function getSliceFormatStatus(sliceFormatId) {
|
function getSliceFormatStatus(sliceFormatId) {
|
||||||
// Check if we have farmer data loaded
|
// Check if we have resource_provider data loaded
|
||||||
if (!window.farmerData || !window.farmerData.nodes) {
|
if (!window.resourceProviderData || !window.resourceProviderData.nodes) {
|
||||||
return 'Available'; // Default to Available if no data
|
return 'Available'; // Default to Available if no data
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any node has this slice format in their slice_formats array
|
// Check if any node has this slice format in their slice_formats array
|
||||||
const isUsedByAnyNode = window.farmerData.nodes.some(node => {
|
const isUsedByAnyNode = window.resourceProviderData.nodes.some(node => {
|
||||||
return node.slice_formats &&
|
return node.slice_formats &&
|
||||||
Array.isArray(node.slice_formats) &&
|
Array.isArray(node.slice_formats) &&
|
||||||
node.slice_formats.includes(sliceFormatId);
|
node.slice_formats.includes(sliceFormatId);
|
||||||
@@ -3507,8 +3507,8 @@ function updateNodesTable(nodes) {
|
|||||||
|
|
||||||
// Group info with enhanced display - get actual group name from loaded groups
|
// Group info with enhanced display - get actual group name from loaded groups
|
||||||
let groupInfo = '<span class="badge bg-secondary">Single</span>';
|
let groupInfo = '<span class="badge bg-secondary">Single</span>';
|
||||||
if (node.node_group_id && window.farmerData && window.farmerData.nodeGroups) {
|
if (node.node_group_id && window.resourceProviderData && window.resourceProviderData.nodeGroups) {
|
||||||
const group = window.farmerData.nodeGroups.find(g => g.id === node.node_group_id);
|
const group = window.resourceProviderData.nodeGroups.find(g => g.id === node.node_group_id);
|
||||||
if (group) {
|
if (group) {
|
||||||
groupInfo = `<span class="badge bg-info">${group.name}</span>`;
|
groupInfo = `<span class="badge bg-info">${group.name}</span>`;
|
||||||
}
|
}
|
||||||
@@ -3966,9 +3966,9 @@ async function addGridNodes() {
|
|||||||
// Reset form
|
// Reset form
|
||||||
resetAddNodeForm();
|
resetAddNodeForm();
|
||||||
|
|
||||||
// Reload farmer data and node groups with small delay for backend processing
|
// Reload resource_provider data and node groups with small delay for backend processing
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await loadFarmerData();
|
await loadResourceProviderData();
|
||||||
await loadNodeGroups(); // FARMER FIX: Refresh node groups table after adding new nodes
|
await loadNodeGroups(); // FARMER FIX: Refresh node groups table after adding new nodes
|
||||||
await loadWalletBalance(); // Refresh wallet balance after staking
|
await loadWalletBalance(); // Refresh wallet balance after staking
|
||||||
await updateStakingDisplay(); // Refresh staking statistics after staking
|
await updateStakingDisplay(); // Refresh staking statistics after staking
|
||||||
@@ -4155,9 +4155,9 @@ async function confirmNodeDeletion() {
|
|||||||
deleteModal.hide();
|
deleteModal.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload farmer data and node groups with small delay for backend processing
|
// Reload resource_provider data and node groups with small delay for backend processing
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loadFarmerData();
|
loadResourceProviderData();
|
||||||
loadNodeGroups(); // FARMER FIX: Refresh node groups table after node deletion
|
loadNodeGroups(); // FARMER FIX: Refresh node groups table after node deletion
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
@@ -4925,9 +4925,9 @@ async function saveNodeConfiguration() {
|
|||||||
modal.hide();
|
modal.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload farmer data and node groups with small delay for backend processing
|
// Reload resource_provider data and node groups with small delay for backend processing
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await loadFarmerData();
|
await loadResourceProviderData();
|
||||||
await loadNodeGroups(); // FARMER FIX: Refresh node groups table after node configuration changes
|
await loadNodeGroups(); // FARMER FIX: Refresh node groups table after node configuration changes
|
||||||
await loadWalletBalance(); // Refresh wallet balance after staking changes
|
await loadWalletBalance(); // Refresh wallet balance after staking changes
|
||||||
await updateStakingDisplay(); // Refresh staking statistics after staking changes
|
await updateStakingDisplay(); // Refresh staking statistics after staking changes
|
||||||
@@ -5627,7 +5627,7 @@ function createSliceFormatCard(format, type) {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
// Use persistent data from farmer settings (price_per_hour) instead of hardcoded fallback
|
// Use persistent data from resource_provider settings (price_per_hour) instead of hardcoded fallback
|
||||||
const hourlyPrice = format.price_per_hour || format.price || 10; // Default to 10 if no price found
|
const hourlyPrice = format.price_per_hour || format.price || 10; // Default to 10 if no price found
|
||||||
pricingDisplay = `<small class="text-muted">${hourlyPrice} ${currency}/hour</small>`;
|
pricingDisplay = `<small class="text-muted">${hourlyPrice} ${currency}/hour</small>`;
|
||||||
}
|
}
|
||||||
@@ -5706,15 +5706,15 @@ function getSliceFormatDisplayName(formatId, formatName) {
|
|||||||
return formatName;
|
return formatName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up custom slice products from loaded farmer data
|
// Look up custom slice products from loaded resource_provider data
|
||||||
if (window.farmerData && window.farmerData.slice_products) {
|
if (window.resourceProviderData && window.resourceProviderData.slice_products) {
|
||||||
const sliceProduct = window.farmerData.slice_products.find(product => product.id === formatId);
|
const sliceProduct = window.resourceProviderData.slice_products.find(product => product.id === formatId);
|
||||||
if (sliceProduct && sliceProduct.name) {
|
if (sliceProduct && sliceProduct.name) {
|
||||||
return sliceProduct.name;
|
return sliceProduct.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up from globally loaded slice products if farmer data not available
|
// Look up from globally loaded slice products if resource_provider data not available
|
||||||
if (window.loadedSliceProducts) {
|
if (window.loadedSliceProducts) {
|
||||||
const sliceProduct = window.loadedSliceProducts.find(product => product.id === formatId);
|
const sliceProduct = window.loadedSliceProducts.find(product => product.id === formatId);
|
||||||
if (sliceProduct && sliceProduct.name) {
|
if (sliceProduct && sliceProduct.name) {
|
||||||
@@ -6587,7 +6587,7 @@ async function deleteCustomNodeGroup(groupId) {
|
|||||||
console.log('📦 Custom group deleted:', result);
|
console.log('📦 Custom group deleted:', result);
|
||||||
showNotification('Custom node group deleted successfully', 'success');
|
showNotification('Custom node group deleted successfully', 'success');
|
||||||
modal.hide();
|
modal.hide();
|
||||||
setTimeout(() => { loadNodeGroups(); try { loadExistingGroups(); } catch (_) {} loadFarmerData(); }, 100);
|
setTimeout(() => { loadNodeGroups(); try { loadExistingGroups(); } catch (_) {} loadResourceProviderData(); }, 100);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('📦 Error deleting custom group:', error);
|
console.error('📦 Error deleting custom group:', error);
|
||||||
showNotification('Failed to delete custom group', 'error');
|
showNotification('Failed to delete custom group', 'error');
|
||||||
@@ -6727,7 +6727,7 @@ async function assignNodeToGroup(nodeId, groupId) {
|
|||||||
|
|
||||||
// FARMER FIX: Add small delay to ensure backend processing is complete
|
// FARMER FIX: Add small delay to ensure backend processing is complete
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loadFarmerData(); // Reload to show updated assignments
|
loadResourceProviderData(); // Reload to show updated assignments
|
||||||
loadNodeGroups(); // Reload to update group statistics
|
loadNodeGroups(); // Reload to update group statistics
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
@@ -7189,7 +7189,7 @@ function clearValidationError(input) {
|
|||||||
*/
|
*/
|
||||||
async function updateStakingDisplay() {
|
async function updateStakingDisplay() {
|
||||||
try {
|
try {
|
||||||
const data = await window.apiJson('/api/dashboard/farmer-data');
|
const data = await window.apiJson('/api/dashboard/resource_provider-data');
|
||||||
const nodes = data.nodes || [];
|
const nodes = data.nodes || [];
|
||||||
|
|
||||||
// Calculate staking statistics
|
// Calculate staking statistics
|
||||||
@@ -7523,7 +7523,7 @@ function showStakeNodeModal(nodeId) {
|
|||||||
console.log('🛡️ Showing stake modal for node:', nodeId);
|
console.log('🛡️ Showing stake modal for node:', nodeId);
|
||||||
|
|
||||||
// Find the node data
|
// Find the node data
|
||||||
const node = window.farmerData?.nodes?.find(n => n.id === nodeId);
|
const node = window.resourceProviderData?.nodes?.find(n => n.id === nodeId);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
showNotification('Node not found', 'error');
|
showNotification('Node not found', 'error');
|
||||||
return;
|
return;
|
||||||
@@ -7657,8 +7657,8 @@ async function stakeOnNode(nodeId, modal) {
|
|||||||
showNotification(`Successfully staked ${stakeAmount} TFP on node`, 'success');
|
showNotification(`Successfully staked ${stakeAmount} TFP on node`, 'success');
|
||||||
modal.hide();
|
modal.hide();
|
||||||
|
|
||||||
// Refresh farmer data to show updated staking
|
// Refresh resource_provider data to show updated staking
|
||||||
await loadFarmerData();
|
await loadResourceProviderData();
|
||||||
|
|
||||||
// Update wallet balance display
|
// Update wallet balance display
|
||||||
await loadWalletBalance();
|
await loadWalletBalance();
|
||||||
@@ -7683,7 +7683,7 @@ async function unstakeFromNode(nodeId) {
|
|||||||
console.log('🛡️ Unstaking TFP from node:', nodeId);
|
console.log('🛡️ Unstaking TFP from node:', nodeId);
|
||||||
|
|
||||||
// Find the node data
|
// Find the node data
|
||||||
const node = window.farmerData?.nodes?.find(n => n.id === nodeId);
|
const node = window.resourceProviderData?.nodes?.find(n => n.id === nodeId);
|
||||||
if (!node || !node.staking_options || !node.staking_options.staking_enabled) {
|
if (!node || !node.staking_options || !node.staking_options.staking_enabled) {
|
||||||
showNotification('Node is not currently staked', 'error');
|
showNotification('Node is not currently staked', 'error');
|
||||||
return;
|
return;
|
||||||
@@ -7712,8 +7712,8 @@ async function unstakeFromNode(nodeId) {
|
|||||||
const returnedAmount = result?.returned_amount || stakedAmount;
|
const returnedAmount = result?.returned_amount || stakedAmount;
|
||||||
showNotification(`Successfully unstaked ${returnedAmount} TFP from node`, 'success');
|
showNotification(`Successfully unstaked ${returnedAmount} TFP from node`, 'success');
|
||||||
|
|
||||||
// Refresh farmer data to show updated staking
|
// Refresh resource_provider data to show updated staking
|
||||||
await loadFarmerData();
|
await loadResourceProviderData();
|
||||||
|
|
||||||
// Update wallet balance display
|
// Update wallet balance display
|
||||||
await loadWalletBalance();
|
await loadWalletBalance();
|
||||||
@@ -7731,7 +7731,7 @@ function showUpdateStakeModal(nodeId) {
|
|||||||
console.log('🛡️ Showing update stake modal for node:', nodeId);
|
console.log('🛡️ Showing update stake modal for node:', nodeId);
|
||||||
|
|
||||||
// Find the node data
|
// Find the node data
|
||||||
const node = window.farmerData?.nodes?.find(n => n.id === nodeId);
|
const node = window.resourceProviderData?.nodes?.find(n => n.id === nodeId);
|
||||||
if (!node || !node.staking_options || !node.staking_options.staking_enabled) {
|
if (!node || !node.staking_options || !node.staking_options.staking_enabled) {
|
||||||
showNotification('Node is not currently staked', 'error');
|
showNotification('Node is not currently staked', 'error');
|
||||||
return;
|
return;
|
||||||
@@ -7847,8 +7847,8 @@ async function updateNodeStaking(nodeId, modal) {
|
|||||||
showNotification(`Successfully updated staking to ${newStakeAmount} TFP`, 'success');
|
showNotification(`Successfully updated staking to ${newStakeAmount} TFP`, 'success');
|
||||||
modal.hide();
|
modal.hide();
|
||||||
|
|
||||||
// Refresh farmer data to show updated staking
|
// Refresh resource_provider data to show updated staking
|
||||||
await loadFarmerData();
|
await loadResourceProviderData();
|
||||||
|
|
||||||
// Update wallet balance display
|
// Update wallet balance display
|
||||||
await loadWalletBalance();
|
await loadWalletBalance();
|
@@ -100,7 +100,7 @@ class ServiceProviderDashboard {
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
return `Revenue: ${context.raw} TFP`;
|
return `Revenue: ${context.raw} MC`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ class ServiceProviderDashboard {
|
|||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Revenue (TFP)'
|
text: 'Revenue (MC)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
@@ -293,7 +293,7 @@ class ServiceProviderDashboard {
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
const point = context.raw;
|
const point = context.raw;
|
||||||
return `${point.label}: Rating ${point.x}, ${point.y} TFP/hour`;
|
return `${point.label}: Rating ${point.x}, ${point.y} MC/hour`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,7 +310,7 @@ class ServiceProviderDashboard {
|
|||||||
y: {
|
y: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Price per Hour (TFP)'
|
text: 'Price per Hour (MC)'
|
||||||
},
|
},
|
||||||
beginAtZero: true
|
beginAtZero: true
|
||||||
}
|
}
|
||||||
@@ -397,7 +397,7 @@ class ServiceProviderDashboard {
|
|||||||
</td>
|
</td>
|
||||||
<td>${service.category || 'General'}</td>
|
<td>${service.category || 'General'}</td>
|
||||||
<td><span class="badge bg-${statusClass}">${service.status || 'Active'}</span></td>
|
<td><span class="badge bg-${statusClass}">${service.status || 'Active'}</span></td>
|
||||||
<td>${service.hourly_rate || service.price_per_hour || service.price_per_hour_usd || service.price_amount || 0} TFC/hour</td>
|
<td>${service.hourly_rate || service.price_per_hour || service.price_per_hour_usd || service.price_amount || 0} MC/hour</td>
|
||||||
<td>${service.clients || 0}</td>
|
<td>${service.clients || 0}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
@@ -523,7 +523,7 @@ class ServiceProviderDashboard {
|
|||||||
<td><span class="badge bg-${statusClass}">${request.status}</span></td>
|
<td><span class="badge bg-${statusClass}">${request.status}</span></td>
|
||||||
<td>${request.requested_date}</td>
|
<td>${request.requested_date}</td>
|
||||||
<td>${request.estimated_hours}</td>
|
<td>${request.estimated_hours}</td>
|
||||||
<td>${request.budget} TFP</td>
|
<td>${request.budget} MC</td>
|
||||||
<td><span class="badge bg-${priorityClass}">${request.priority}</span></td>
|
<td><span class="badge bg-${priorityClass}">${request.priority}</span></td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
@@ -584,7 +584,7 @@ class ServiceProviderDashboard {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>${request.budget} TFP</td>
|
<td>${request.budget} MC</td>
|
||||||
<td><span class="badge bg-${priorityClass}">${request.priority}</span></td>
|
<td><span class="badge bg-${priorityClass}">${request.priority}</span></td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
@@ -654,7 +654,7 @@ class ServiceProviderDashboard {
|
|||||||
<td><span class="badge bg-success">${request.status}</span></td>
|
<td><span class="badge bg-success">${request.status}</span></td>
|
||||||
<td>${completedDate}</td>
|
<td>${completedDate}</td>
|
||||||
<td>${totalHours}${typeof totalHours === 'number' ? ' hours' : ''}</td>
|
<td>${totalHours}${typeof totalHours === 'number' ? ' hours' : ''}</td>
|
||||||
<td>${totalAmount}${typeof totalAmount === 'number' ? ' TFP' : ''}</td>
|
<td>${totalAmount}${typeof totalAmount === 'number' ? ' MC' : ''}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="me-2">${rating}</span>
|
<span class="me-2">${rating}</span>
|
||||||
@@ -1176,8 +1176,8 @@ async function loadServiceAnalytics(serviceId) {
|
|||||||
document.getElementById('serviceOnTimeDelivery').textContent = `${analytics.performance.on_time_delivery.toFixed(0)}%`;
|
document.getElementById('serviceOnTimeDelivery').textContent = `${analytics.performance.on_time_delivery.toFixed(0)}%`;
|
||||||
document.getElementById('serviceCompletionRate').textContent = `${analytics.performance.completion_rate.toFixed(0)}%`;
|
document.getElementById('serviceCompletionRate').textContent = `${analytics.performance.completion_rate.toFixed(0)}%`;
|
||||||
|
|
||||||
document.getElementById('serviceTotalRevenue').textContent = `${analytics.revenue.total} TFP`;
|
document.getElementById('serviceTotalRevenue').textContent = `${analytics.revenue.total} MC`;
|
||||||
document.getElementById('serviceMonthlyRevenue').textContent = `${analytics.revenue.monthly} TFP`;
|
document.getElementById('serviceMonthlyRevenue').textContent = `${analytics.revenue.monthly} MC`;
|
||||||
document.getElementById('serviceTotalProjects').textContent = analytics.project_metrics.total_projects;
|
document.getElementById('serviceTotalProjects').textContent = analytics.project_metrics.total_projects;
|
||||||
document.getElementById('serviceActiveClients').textContent = analytics.client_metrics.total_clients;
|
document.getElementById('serviceActiveClients').textContent = analytics.client_metrics.total_clients;
|
||||||
|
|
||||||
@@ -1237,7 +1237,7 @@ function createServiceRevenueAnalyticsChart(revenueData) {
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
return `Revenue: ${context.raw} TFP`;
|
return `Revenue: ${context.raw} MC`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1247,7 +1247,7 @@ function createServiceRevenueAnalyticsChart(revenueData) {
|
|||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Revenue (TFP)'
|
text: 'Revenue (MC)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
@@ -1300,7 +1300,7 @@ async function loadServiceClients(serviceId) {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>${client.projects.length}</td>
|
<td>${client.projects.length}</td>
|
||||||
<td>${client.total_revenue} TFP</td>
|
<td>${client.total_revenue} MC</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="me-2">${client.avg_rating.toFixed(1)}</span>
|
<span class="me-2">${client.avg_rating.toFixed(1)}</span>
|
||||||
@@ -1607,7 +1607,7 @@ function populateRequestDetailsModal(request) {
|
|||||||
|
|
||||||
document.getElementById('detailRequestedDate').textContent = request.requested_date;
|
document.getElementById('detailRequestedDate').textContent = request.requested_date;
|
||||||
document.getElementById('detailEstimatedHours').textContent = `${request.estimated_hours} hours`;
|
document.getElementById('detailEstimatedHours').textContent = `${request.estimated_hours} hours`;
|
||||||
document.getElementById('detailBudget').textContent = `${request.budget} TFP`;
|
document.getElementById('detailBudget').textContent = `${request.budget} MC`;
|
||||||
|
|
||||||
// Progress bar
|
// Progress bar
|
||||||
const progress = request.progress || 0;
|
const progress = request.progress || 0;
|
||||||
@@ -1888,7 +1888,7 @@ function populateCompletedRequestModal(request) {
|
|||||||
document.getElementById('completedServiceType').textContent = request.service_name;
|
document.getElementById('completedServiceType').textContent = request.service_name;
|
||||||
document.getElementById('completedDate').textContent = request.completed_date || 'N/A';
|
document.getElementById('completedDate').textContent = request.completed_date || 'N/A';
|
||||||
document.getElementById('completedHoursLogged').textContent = `${request.hours_logged || 0} hours`;
|
document.getElementById('completedHoursLogged').textContent = `${request.hours_logged || 0} hours`;
|
||||||
document.getElementById('completedRevenue').textContent = `${request.revenue || 0} TFP`;
|
document.getElementById('completedRevenue').textContent = `${request.revenue || 0} MC`;
|
||||||
document.getElementById('completedRating').textContent = request.rating ? `${request.rating}/5 ⭐` : 'Not rated';
|
document.getElementById('completedRating').textContent = request.rating ? `${request.rating}/5 ⭐` : 'Not rated';
|
||||||
document.getElementById('completedOnTime').textContent = request.on_time ? 'Yes ✅' : 'No ❌';
|
document.getElementById('completedOnTime').textContent = request.on_time ? 'Yes ✅' : 'No ❌';
|
||||||
document.getElementById('completedSummary').textContent = request.summary || 'No summary available.';
|
document.getElementById('completedSummary').textContent = request.summary || 'No summary available.';
|
||||||
|
@@ -23,13 +23,13 @@ class UserDashboard {
|
|||||||
Chart.defaults.responsive = true;
|
Chart.defaults.responsive = true;
|
||||||
Chart.defaults.maintainAspectRatio = false;
|
Chart.defaults.maintainAspectRatio = false;
|
||||||
|
|
||||||
this.createTFCUsageChart();
|
this.createMCUsageChart();
|
||||||
this.createResourceUtilizationChart();
|
this.createResourceUtilizationChart();
|
||||||
this.createUserActivityChart();
|
this.createUserActivityChart();
|
||||||
this.createDeploymentDistributionChart();
|
this.createDeploymentDistributionChart();
|
||||||
}
|
}
|
||||||
|
|
||||||
createTFCUsageChart() {
|
createMCUsageChart() {
|
||||||
const ctx = document.getElementById('tfpUsageChart');
|
const ctx = document.getElementById('tfpUsageChart');
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class UserDashboard {
|
|||||||
data: {
|
data: {
|
||||||
labels: ['Month 1', 'Month 2', 'Month 3', 'Month 4', 'Month 5', 'Month 6'],
|
labels: ['Month 1', 'Month 2', 'Month 3', 'Month 4', 'Month 5', 'Month 6'],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'TFC Usage',
|
label: 'MC Usage',
|
||||||
data: this.userData.tfp_usage_trend || [0, 0, 0, 0, 0, 0],
|
data: this.userData.tfp_usage_trend || [0, 0, 0, 0, 0, 0],
|
||||||
borderColor: '#007bff',
|
borderColor: '#007bff',
|
||||||
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
||||||
@@ -58,7 +58,7 @@ class UserDashboard {
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
return `TFC Usage: ${context.raw} TFC`;
|
return `MC Usage: ${context.raw} MC`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,7 +368,7 @@ class UserDashboard {
|
|||||||
<td><span class="badge ${statusBadge}">${app.status}</span></td>
|
<td><span class="badge ${statusBadge}">${app.status}</span></td>
|
||||||
<td>${app.rating}/5 ⭐</td>
|
<td>${app.rating}/5 ⭐</td>
|
||||||
<td>${app.deployments} active</td>
|
<td>${app.deployments} active</td>
|
||||||
<td>${app.monthly_revenue} TFP/month</td>
|
<td>${app.monthly_revenue} MC/month</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-sm btn-outline-primary" onclick="manageApplication('${app.id}', '${app.name}')">Manage</button>
|
<button class="btn btn-sm btn-outline-primary" onclick="manageApplication('${app.id}', '${app.name}')">Manage</button>
|
||||||
@@ -416,7 +416,7 @@ class UserDashboard {
|
|||||||
</td>
|
</td>
|
||||||
<td><span class="badge bg-light text-dark">${service.category}</span></td>
|
<td><span class="badge bg-light text-dark">${service.category}</span></td>
|
||||||
<td><span class="badge ${statusBadge}">${service.status}</span></td>
|
<td><span class="badge ${statusBadge}">${service.status}</span></td>
|
||||||
<td>${service.price_per_hour} TFP</td>
|
<td>${service.price_per_hour} MC</td>
|
||||||
<td>${service.clients}</td>
|
<td>${service.clients}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="text-warning">${stars}</span>
|
<span class="text-warning">${stars}</span>
|
||||||
@@ -472,7 +472,7 @@ class UserDashboard {
|
|||||||
<td>${resource.location}</td>
|
<td>${resource.location}</td>
|
||||||
<td><span class="badge ${statusBadge}">${resource.status}</span></td>
|
<td><span class="badge ${statusBadge}">${resource.status}</span></td>
|
||||||
<td>${resource.sla}</td>
|
<td>${resource.sla}</td>
|
||||||
<td>${resource.monthly_cost} TFP/month</td>
|
<td>${resource.monthly_cost} MC/month</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-sm btn-outline-primary" onclick="manageResource('${resource.id}')">Details</button>
|
<button class="btn btn-sm btn-outline-primary" onclick="manageResource('${resource.id}')">Details</button>
|
||||||
@@ -525,7 +525,7 @@ class UserDashboard {
|
|||||||
<td>${rental.specs}</td>
|
<td>${rental.specs}</td>
|
||||||
<td>${rental.location}</td>
|
<td>${rental.location}</td>
|
||||||
<td><span class="badge ${statusBadge}">${rental.status}</span></td>
|
<td><span class="badge ${statusBadge}">${rental.status}</span></td>
|
||||||
<td>${rental.monthly_cost} TFP/month</td>
|
<td>${rental.monthly_cost} MC/month</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-sm btn-outline-primary" onclick="manageSliceRental('${rental.id}')">Manage</button>
|
<button class="btn btn-sm btn-outline-primary" onclick="manageSliceRental('${rental.id}')">Manage</button>
|
||||||
@@ -558,7 +558,7 @@ class UserDashboard {
|
|||||||
if (activeRentalsElement) activeRentalsElement.textContent = activeRentals;
|
if (activeRentalsElement) activeRentalsElement.textContent = activeRentals;
|
||||||
if (vmDeploymentsElement) vmDeploymentsElement.textContent = vmDeployments;
|
if (vmDeploymentsElement) vmDeploymentsElement.textContent = vmDeployments;
|
||||||
if (k8sDeploymentsElement) k8sDeploymentsElement.textContent = k8sDeployments;
|
if (k8sDeploymentsElement) k8sDeploymentsElement.textContent = k8sDeployments;
|
||||||
if (monthlyCostElement) monthlyCostElement.textContent = `${totalMonthlyCost} TFP`;
|
if (monthlyCostElement) monthlyCostElement.textContent = `${totalMonthlyCost} MC`;
|
||||||
|
|
||||||
console.log(`📊 Updated slice rental stats: ${activeRentals} active, ${vmDeployments} VM, ${k8sDeployments} K8s, ${totalMonthlyCost} TFP/month`);
|
console.log(`📊 Updated slice rental stats: ${activeRentals} active, ${vmDeployments} VM, ${k8sDeployments} K8s, ${totalMonthlyCost} TFP/month`);
|
||||||
}
|
}
|
||||||
|
@@ -206,12 +206,12 @@
|
|||||||
if (tfpAmountTFT) {
|
if (tfpAmountTFT) {
|
||||||
tfpAmountTFT.addEventListener('input', () => {
|
tfpAmountTFT.addEventListener('input', () => {
|
||||||
const amount = parseFloat(tfpAmountTFT.value) || 0;
|
const amount = parseFloat(tfpAmountTFT.value) || 0;
|
||||||
const tftCost = amount * 0.5; // 1 TFC = 0.5 TFT
|
const tftCost = amount * 0.5; // 1 MC = 0.5 TFT
|
||||||
const modal = document.getElementById('buyTFCWithTFTModal');
|
const modal = document.getElementById('buyMCWithTFTModal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
||||||
// rows[0] -> Amount, rows[1] -> Cost
|
// rows[0] -> Amount, rows[1] -> Cost
|
||||||
if (rows[0]) rows[0].textContent = `${amount} TFC`;
|
if (rows[0]) rows[0].textContent = `${amount} MC`;
|
||||||
if (rows[1]) rows[1].textContent = `${tftCost.toFixed(1)} TFT`;
|
if (rows[1]) rows[1].textContent = `${tftCost.toFixed(1)} TFT`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -222,11 +222,11 @@
|
|||||||
if (sellTfpAmountTFT) {
|
if (sellTfpAmountTFT) {
|
||||||
sellTfpAmountTFT.addEventListener('input', () => {
|
sellTfpAmountTFT.addEventListener('input', () => {
|
||||||
const amount = parseFloat(sellTfpAmountTFT.value) || 0;
|
const amount = parseFloat(sellTfpAmountTFT.value) || 0;
|
||||||
const tftReceive = amount * 0.5; // 1 TFC = 0.5 TFT
|
const tftReceive = amount * 0.5; // 1 MC = 0.5 TFT
|
||||||
const modal = document.getElementById('sellTFCForTFTModal');
|
const modal = document.getElementById('sellMCForTFTModal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
||||||
if (rows[0]) rows[0].textContent = `${amount} TFC`;
|
if (rows[0]) rows[0].textContent = `${amount} MC`;
|
||||||
if (rows[1]) rows[1].textContent = `${tftReceive.toFixed(1)} TFT`;
|
if (rows[1]) rows[1].textContent = `${tftReceive.toFixed(1)} TFT`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -237,11 +237,11 @@
|
|||||||
if (tfpAmountPEAQ) {
|
if (tfpAmountPEAQ) {
|
||||||
tfpAmountPEAQ.addEventListener('input', () => {
|
tfpAmountPEAQ.addEventListener('input', () => {
|
||||||
const amount = parseFloat(tfpAmountPEAQ.value) || 0;
|
const amount = parseFloat(tfpAmountPEAQ.value) || 0;
|
||||||
const peaqCost = amount * 2.0; // 1 TFC = 2 PEAQ
|
const peaqCost = amount * 2.0; // 1 MC = 2 PEAQ
|
||||||
const modal = document.getElementById('buyTFCWithPEAQModal');
|
const modal = document.getElementById('buyMCWithPEAQModal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
||||||
if (rows[0]) rows[0].textContent = `${amount} TFC`;
|
if (rows[0]) rows[0].textContent = `${amount} MC`;
|
||||||
if (rows[1]) rows[1].textContent = `${peaqCost.toFixed(0)} PEAQ`;
|
if (rows[1]) rows[1].textContent = `${peaqCost.toFixed(0)} PEAQ`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -252,11 +252,11 @@
|
|||||||
if (sellTfpAmountPEAQ) {
|
if (sellTfpAmountPEAQ) {
|
||||||
sellTfpAmountPEAQ.addEventListener('input', () => {
|
sellTfpAmountPEAQ.addEventListener('input', () => {
|
||||||
const amount = parseFloat(sellTfpAmountPEAQ.value) || 0;
|
const amount = parseFloat(sellTfpAmountPEAQ.value) || 0;
|
||||||
const peaqReceive = amount * 2.0; // 1 TFC = 2 PEAQ
|
const peaqReceive = amount * 2.0; // 1 MC = 2 PEAQ
|
||||||
const modal = document.getElementById('sellTFCForPEAQModal');
|
const modal = document.getElementById('sellMCForPEAQModal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
const rows = modal.querySelectorAll('.alert .d-flex.justify-content-between .text-end');
|
||||||
if (rows[0]) rows[0].textContent = `${amount} TFC`;
|
if (rows[0]) rows[0].textContent = `${amount} MC`;
|
||||||
if (rows[1]) rows[1].textContent = `${peaqReceive.toFixed(0)} PEAQ`;
|
if (rows[1]) rows[1].textContent = `${peaqReceive.toFixed(0)} PEAQ`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -375,8 +375,8 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ amount, payment_method: 'TFT' }),
|
body: JSON.stringify({ amount, payment_method: 'TFT' }),
|
||||||
});
|
});
|
||||||
showSuccessToast(`Purchased ${amount} TFC with TFT`);
|
showSuccessToast(`Purchased ${amount} MC with TFT`);
|
||||||
const modal = document.getElementById('buyTFCWithTFTModal');
|
const modal = document.getElementById('buyMCWithTFTModal');
|
||||||
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && e.status === 402) {
|
if (e && e.status === 402) {
|
||||||
@@ -398,8 +398,8 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ amount, currency: 'TFT', payout_method: 'blockchain' }),
|
body: JSON.stringify({ amount, currency: 'TFT', payout_method: 'blockchain' }),
|
||||||
});
|
});
|
||||||
showSuccessToast(`Sold ${amount} TFC for TFT`);
|
showSuccessToast(`Sold ${amount} MC for TFT`);
|
||||||
const modal = document.getElementById('sellTFCForTFTModal');
|
const modal = document.getElementById('sellMCForTFTModal');
|
||||||
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && e.status === 402) {
|
if (e && e.status === 402) {
|
||||||
@@ -421,8 +421,8 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ amount, payment_method: 'PEAQ' }),
|
body: JSON.stringify({ amount, payment_method: 'PEAQ' }),
|
||||||
});
|
});
|
||||||
showSuccessToast(`Purchased ${amount} TFC with PEAQ`);
|
showSuccessToast(`Purchased ${amount} MC with PEAQ`);
|
||||||
const modal = document.getElementById('buyTFCWithPEAQModal');
|
const modal = document.getElementById('buyMCWithPEAQModal');
|
||||||
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && e.status === 402) {
|
if (e && e.status === 402) {
|
||||||
@@ -444,8 +444,8 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ amount, currency: 'PEAQ', payout_method: 'blockchain' }),
|
body: JSON.stringify({ amount, currency: 'PEAQ', payout_method: 'blockchain' }),
|
||||||
});
|
});
|
||||||
showSuccessToast(`Sold ${amount} TFC for PEAQ`);
|
showSuccessToast(`Sold ${amount} MC for PEAQ`);
|
||||||
const modal = document.getElementById('sellTFCForPEAQModal');
|
const modal = document.getElementById('sellMCForPEAQModal');
|
||||||
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
if (modal && window.bootstrap) bootstrap.Modal.getOrCreateInstance(modal).hide();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && e.status === 402) {
|
if (e && e.status === 402) {
|
||||||
|
@@ -11,8 +11,8 @@ class DemoWorkflow {
|
|||||||
action: () => this.showWelcome()
|
action: () => this.showWelcome()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "App Provider: Register New Application",
|
title: "Application Provider: Register New Application",
|
||||||
description: "Let's start by registering a new application as an App Provider.",
|
description: "Let's start by registering a new application as an Application Provider.",
|
||||||
action: () => this.demoAppRegistration()
|
action: () => this.demoAppRegistration()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,7 +31,7 @@ class DemoWorkflow {
|
|||||||
action: () => this.demoAppDeployment()
|
action: () => this.demoAppDeployment()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Farmer: Node Management",
|
title: "ResourceProvider: Node Management",
|
||||||
description: "Manage your farming nodes and monitor capacity.",
|
description: "Manage your farming nodes and monitor capacity.",
|
||||||
action: () => this.demoNodeManagement()
|
action: () => this.demoNodeManagement()
|
||||||
},
|
},
|
||||||
@@ -146,7 +146,7 @@ class DemoWorkflow {
|
|||||||
showNotification('Demo: Navigating to App Provider dashboard...', 'info');
|
showNotification('Demo: Navigating to App Provider dashboard...', 'info');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (window.location.pathname !== '/dashboard/app-provider') {
|
if (window.location.pathname !== '/dashboard/application-provider') {
|
||||||
showNotification('Please navigate to the App Provider dashboard to continue the demo', 'warning');
|
showNotification('Please navigate to the App Provider dashboard to continue the demo', 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -265,7 +265,7 @@ class DemoWorkflow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
demoNodeManagement() {
|
demoNodeManagement() {
|
||||||
showNotification('Demo: Simulating farmer node management...', 'info');
|
showNotification('Demo: Simulating resource_provider node management...', 'info');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Simulate node status change
|
// Simulate node status change
|
||||||
|
@@ -431,7 +431,7 @@ class MarketplaceIntegration {
|
|||||||
|
|
||||||
if (!sessionStorage.getItem('marketplaceSlices')) {
|
if (!sessionStorage.getItem('marketplaceSlices')) {
|
||||||
// Get real users from user database
|
// Get real users from user database
|
||||||
const alexUser = userDB.getUser('user-001'); // Alex Thompson - Farmer
|
const alexUser = userDB.getUser('user-001'); // Alex Thompson - ResourceProvider
|
||||||
|
|
||||||
const mockSlices = [
|
const mockSlices = [
|
||||||
{
|
{
|
||||||
|
@@ -77,7 +77,7 @@
|
|||||||
fill: true,
|
fill: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '3Nodes',
|
label: 'Mycelium Nodes',
|
||||||
data: pick(data, 'monthlyGrowth.nodes', [45, 60, 75, 90, 120, 150]),
|
data: pick(data, 'monthlyGrowth.nodes', [45, 60, 75, 90, 120, 150]),
|
||||||
borderColor: '#28a745',
|
borderColor: '#28a745',
|
||||||
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
||||||
@@ -190,14 +190,14 @@
|
|||||||
data: {
|
data: {
|
||||||
labels: pick(data, 'nodeGeographic.labels', ['Europe', 'North America', 'Asia', 'Africa', 'South America', 'Oceania']),
|
labels: pick(data, 'nodeGeographic.labels', ['Europe', 'North America', 'Asia', 'Africa', 'South America', 'Oceania']),
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Number of 3Nodes',
|
label: 'Number of Mycelium Nodes',
|
||||||
data: pick(data, 'nodeGeographic.values', [45, 32, 20, 15, 8, 5]),
|
data: pick(data, 'nodeGeographic.values', [45, 32, 20, 15, 8, 5]),
|
||||||
backgroundColor: 'rgba(40, 167, 69, 0.7)',
|
backgroundColor: 'rgba(40, 167, 69, 0.7)',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
plugins: { legend: { display: false }, title: { display: true, text: 'Geographic Distribution of 3Nodes' } },
|
plugins: { legend: { display: false }, title: { display: true, text: 'Geographic Distribution of Mycelium Nodes' } },
|
||||||
scales: { y: { beginAtZero: true, title: { display: true, text: 'Number of Nodes' } } },
|
scales: { y: { beginAtZero: true, title: { display: true, text: 'Number of Nodes' } } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -233,7 +233,7 @@
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
plugins: { legend: { display: false }, title: { display: true, text: '3Node Uptime Performance' } },
|
plugins: { legend: { display: false }, title: { display: true, text: 'Mycelium Node Uptime Performance' } },
|
||||||
scales: { y: { min: 95, max: 100, title: { display: true, text: 'Uptime %' } } },
|
scales: { y: { min: 95, max: 100, title: { display: true, text: 'Uptime %' } } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
plugins: { legend: { display: false }, title: { display: true, text: '3Node Certification Rate' } },
|
plugins: { legend: { display: false }, title: { display: true, text: 'Mycelium Node Certification Rate' } },
|
||||||
scales: { y: { min: 50, max: 100, title: { display: true, text: 'Certification %' } } },
|
scales: { y: { min: 50, max: 100, title: { display: true, text: 'Certification %' } } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -12,11 +12,11 @@ class UserDatabase {
|
|||||||
const mockUsers = [
|
const mockUsers = [
|
||||||
{
|
{
|
||||||
id: 'user-001',
|
id: 'user-001',
|
||||||
username: 'sara_farmer',
|
username: 'sara_resource_provider',
|
||||||
display_name: 'Sara Nicks',
|
display_name: 'Sara Nicks',
|
||||||
email: 'user1@example.com',
|
email: 'user1@example.com',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
role: 'farmer',
|
role: 'resource_provider',
|
||||||
location: 'Amsterdam, Netherlands',
|
location: 'Amsterdam, Netherlands',
|
||||||
joined_date: '2024-01-15',
|
joined_date: '2024-01-15',
|
||||||
reputation: 4.8,
|
reputation: 4.8,
|
||||||
@@ -33,7 +33,7 @@ class UserDatabase {
|
|||||||
display_name: 'Alex Thompson',
|
display_name: 'Alex Thompson',
|
||||||
email: 'user2@example.com',
|
email: 'user2@example.com',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
role: 'app_provider',
|
role: 'application_provider',
|
||||||
location: 'Berlin, Germany',
|
location: 'Berlin, Germany',
|
||||||
joined_date: '2024-02-20',
|
joined_date: '2024-02-20',
|
||||||
reputation: 4.9,
|
reputation: 4.9,
|
||||||
@@ -84,7 +84,7 @@ class UserDatabase {
|
|||||||
display_name: 'Jordan Mitchell',
|
display_name: 'Jordan Mitchell',
|
||||||
email: 'user5@example.com',
|
email: 'user5@example.com',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
role: 'multi', // Can be farmer, app_provider, service_provider, user
|
role: 'multi', // Can be resource_provider, application_provider, service_provider, user
|
||||||
location: 'Toronto, Canada',
|
location: 'Toronto, Canada',
|
||||||
joined_date: new Date().toISOString().split('T')[0],
|
joined_date: new Date().toISOString().split('T')[0],
|
||||||
reputation: 5.0,
|
reputation: 5.0,
|
||||||
|
@@ -14,7 +14,7 @@ impl DataValidator {
|
|||||||
|
|
||||||
if let Value::Object(ref mut obj) = value {
|
if let Value::Object(ref mut obj) = value {
|
||||||
Self::repair_user_activities(obj)?;
|
Self::repair_user_activities(obj)?;
|
||||||
Self::repair_farmer_settings(obj)?;
|
Self::repair_resource_provider_settings(obj)?;
|
||||||
Self::ensure_required_fields(obj)?;
|
Self::ensure_required_fields(obj)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,20 +47,20 @@ impl DataValidator {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repairs farmer settings to include all required fields
|
/// Repairs resource_provider settings to include all required fields
|
||||||
fn repair_farmer_settings(obj: &mut Map<String, Value>) -> Result<(), String> {
|
fn repair_resource_provider_settings(obj: &mut Map<String, Value>) -> Result<(), String> {
|
||||||
if let Some(Value::Object(ref mut farmer_settings)) = obj.get_mut("farmer_settings") {
|
if let Some(Value::Object(ref mut resource_provider_settings)) = obj.get_mut("resource_provider_settings") {
|
||||||
// Ensure minimum_deployment_duration exists
|
// Ensure minimum_deployment_duration exists
|
||||||
if !farmer_settings.contains_key("minimum_deployment_duration") {
|
if !resource_provider_settings.contains_key("minimum_deployment_duration") {
|
||||||
farmer_settings.insert(
|
resource_provider_settings.insert(
|
||||||
"minimum_deployment_duration".to_string(),
|
"minimum_deployment_duration".to_string(),
|
||||||
Value::Number(serde_json::Number::from(24))
|
Value::Number(serde_json::Number::from(24))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure preferred_regions exists
|
// Ensure preferred_regions exists
|
||||||
if !farmer_settings.contains_key("preferred_regions") {
|
if !resource_provider_settings.contains_key("preferred_regions") {
|
||||||
farmer_settings.insert(
|
resource_provider_settings.insert(
|
||||||
"preferred_regions".to_string(),
|
"preferred_regions".to_string(),
|
||||||
Value::Array(vec![
|
Value::Array(vec![
|
||||||
Value::String("NA".to_string()),
|
Value::String("NA".to_string()),
|
||||||
@@ -81,9 +81,9 @@ impl DataValidator {
|
|||||||
("services", Value::Array(vec![])),
|
("services", Value::Array(vec![])),
|
||||||
("service_requests", Value::Array(vec![])),
|
("service_requests", Value::Array(vec![])),
|
||||||
("apps", Value::Array(vec![])),
|
("apps", Value::Array(vec![])),
|
||||||
("app_deployments", Value::Array(vec![])),
|
("application_deployments", Value::Array(vec![])),
|
||||||
("nodes", Value::Array(vec![])),
|
("nodes", Value::Array(vec![])),
|
||||||
("farmer_earnings", Value::Array(vec![])),
|
("resource_provider_earnings", Value::Array(vec![])),
|
||||||
("user_activities", Value::Array(vec![])),
|
("user_activities", Value::Array(vec![])),
|
||||||
("pool_positions", Value::Object(Map::new())),
|
("pool_positions", Value::Object(Map::new())),
|
||||||
];
|
];
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
{% extends "dashboard/layout.html" %}
|
{% extends "dashboard/layout.html" %}
|
||||||
|
|
||||||
{% block title %}ThreeFold Dashboard - App Provider{% endblock %}
|
{% block title %}ThreeFold Dashboard - Application Provider{% endblock %}
|
||||||
|
|
||||||
{% block dashboard_content %}
|
{% block dashboard_content %}
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h1>App Provider Dashboard</h1>
|
<h1>Application Provider Dashboard</h1>
|
||||||
<p class="lead">Develop, deploy, and manage applications for the ThreeFold ecosystem</p>
|
<p class="lead">Develop, deploy, and manage applications for the ThreeFold ecosystem</p>
|
||||||
|
|
||||||
<!-- Status Summary -->
|
<!-- Status Summary -->
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<div class="stats-card info">
|
<div class="stats-card info">
|
||||||
<h5 class="card-title">Published Apps</h5>
|
<h5 class="card-title">Published Apps</h5>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 class="mb-0">{% if app_provider_data is defined and app_provider_data.published_apps is defined %}{{ app_provider_data.published_apps }}{% else %}0{% endif %}</h2>
|
<h2 class="mb-0">{% if application_provider_data is defined and application_provider_data.published_apps is defined %}{{ application_provider_data.published_apps }}{% else %}0{% endif %}</h2>
|
||||||
<small class="text-muted">Active</small>
|
<small class="text-muted">Active</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<div class="stats-card primary">
|
<div class="stats-card primary">
|
||||||
<h5 class="card-title">Active Deployments</h5>
|
<h5 class="card-title">Active Deployments</h5>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 class="mb-0">{% if app_provider_data is defined and app_provider_data.active_deployments is defined %}{{ app_provider_data.active_deployments }}{% else %}0{% endif %}</h2>
|
<h2 class="mb-0">{% if application_provider_data is defined and application_provider_data.active_deployments is defined %}{{ application_provider_data.active_deployments }}{% else %}0{% endif %}</h2>
|
||||||
<small class="text-muted">Instances</small>
|
<small class="text-muted">Instances</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<div class="stats-card warning">
|
<div class="stats-card warning">
|
||||||
<h5 class="card-title">Customer Base</h5>
|
<h5 class="card-title">Customer Base</h5>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 class="mb-0">{% if app_provider_data is defined and app_provider_data.total_deployments is defined %}{{ app_provider_data.total_deployments }}{% else %}0{% endif %}</h2>
|
<h2 class="mb-0">{% if application_provider_data is defined and application_provider_data.total_deployments is defined %}{{ application_provider_data.total_deployments }}{% else %}0{% endif %}</h2>
|
||||||
<small class="text-muted">Total Deployments</small>
|
<small class="text-muted">Total Deployments</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<div class="stats-card success">
|
<div class="stats-card success">
|
||||||
<h5 class="card-title">Monthly Earnings</h5>
|
<h5 class="card-title">Monthly Earnings</h5>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 class="mb-0">{% if app_provider_data is defined and app_provider_data.monthly_revenue_usd is defined %}{{ app_provider_data.monthly_revenue_usd }}{% else %}0{% endif %}</h2>
|
<h2 class="mb-0">{% if application_provider_data is defined and application_provider_data.monthly_revenue_usd is defined %}{{ application_provider_data.monthly_revenue_usd }}{% else %}0{% endif %}</h2>
|
||||||
<small class="text-muted">$/month</small>
|
<small class="text-muted">$/month</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -232,14 +232,14 @@
|
|||||||
{{ super() }}
|
{{ super() }}
|
||||||
<!-- Hydration JSON for initial app provider dashboard data (safe, non-executable) -->
|
<!-- Hydration JSON for initial app provider dashboard data (safe, non-executable) -->
|
||||||
<script id="ap-dashboard-hydration" type="application/json">
|
<script id="ap-dashboard-hydration" type="application/json">
|
||||||
{% if app_provider_data is defined %}
|
{% if application_provider_data is defined %}
|
||||||
{{ app_provider_data | json_encode() }}
|
{{ application_provider_data | json_encode() }}
|
||||||
{% else %}
|
{% else %}
|
||||||
null
|
null
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
|
||||||
<script src="/static/js/dashboard-app-provider.js"></script>
|
<script src="/static/js/dashboard-application-provider.js"></script>
|
||||||
<style>
|
<style>
|
||||||
/* Ensure charts have consistent sizes */
|
/* Ensure charts have consistent sizes */
|
||||||
.card-body {
|
.card-body {
|
@@ -78,8 +78,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<a href="/dashboard/farmer" class="btn btn-outline-info w-100 mb-2">
|
<a href="/dashboard/resource_provider" class="btn btn-outline-info w-100 mb-2">
|
||||||
<i class="bi bi-hdd-rack me-2"></i> Add a 3Node
|
<i class="bi bi-hdd-rack me-2"></i> Add a Mycelium Node
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,21 +117,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-lg-3 mb-4">
|
<div class="col-md-6 col-lg-3 mb-4">
|
||||||
<div class="dashboard-card">
|
<div class="dashboard-card">
|
||||||
<span class="badge bg-success badge-role">FARMER</span>
|
<span class="badge bg-success badge-role">RESOURCE PROVIDER</span>
|
||||||
<h4>Farmer Dashboard</h4>
|
<h4>Resource Provider Dashboard</h4>
|
||||||
<p>Manage your nodes, create slices, set pricing, and track earnings.</p>
|
<p>Manage your nodes, create slices, set pricing, and track earnings.</p>
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<a href="/dashboard/farmer" class="btn btn-sm btn-outline-success">Access Farmer Dashboard</a>
|
<a href="/dashboard/resource_provider" class="btn btn-sm btn-outline-success">Access Resource Provider Dashboard</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-lg-3 mb-4">
|
<div class="col-md-6 col-lg-3 mb-4">
|
||||||
<div class="dashboard-card">
|
<div class="dashboard-card">
|
||||||
<span class="badge bg-info badge-role">APP PROVIDER</span>
|
<span class="badge bg-info badge-role">APPLICATION PROVIDER</span>
|
||||||
<h4>App Provider Dashboard</h4>
|
<h4>Application Provider Dashboard</h4>
|
||||||
<p>Develop, deploy, and manage applications for the TF ecosystem.</p>
|
<p>Develop, deploy, and manage applications for the TF ecosystem.</p>
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<a href="/dashboard/app-provider" class="btn btn-sm btn-outline-info">Access App Provider Dashboard</a>
|
<a href="/dashboard/application-provider" class="btn btn-sm btn-outline-info">Access Application Provider Dashboard</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -28,15 +28,15 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active_section == 'farmer' %}active{% endif %}" href="/dashboard/farmer">
|
<a class="nav-link {% if active_section == 'resource_provider' %}active{% endif %}" href="/dashboard/resource_provider">
|
||||||
<i class="bi bi-hdd-rack me-1"></i>
|
<i class="bi bi-hdd-rack me-1"></i>
|
||||||
Farmer
|
Resource Provider
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active_section == 'app_provider' %}active{% endif %}" href="/dashboard/app-provider">
|
<a class="nav-link {% if active_section == 'application_provider' %}active{% endif %}" href="/dashboard/application-provider">
|
||||||
<i class="bi bi-app me-1"></i>
|
<i class="bi bi-app me-1"></i>
|
||||||
App Provider
|
Application Provider
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
@@ -244,7 +244,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buy TFC Credits Modal -->
|
<!-- Buy MC Credits Modal -->
|
||||||
<div class="modal fade" id="buyCreditsModal" tabindex="-1" aria-labelledby="buyCreditsModalLabel" aria-hidden="true">
|
<div class="modal fade" id="buyCreditsModal" tabindex="-1" aria-labelledby="buyCreditsModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -301,7 +301,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sell TFC Credits Modal -->
|
<!-- Sell MC Credits Modal -->
|
||||||
<div class="modal fade" id="sellCreditsModal" tabindex="-1" aria-labelledby="sellCreditsModalLabel" aria-hidden="true">
|
<div class="modal fade" id="sellCreditsModal" tabindex="-1" aria-labelledby="sellCreditsModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -359,7 +359,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Stake TFC Credits Modal -->
|
<!-- Stake MC Credits Modal -->
|
||||||
<div class="modal fade" id="stakeCreditsModal" tabindex="-1" aria-labelledby="stakeCreditsModalLabel" aria-hidden="true">
|
<div class="modal fade" id="stakeCreditsModal" tabindex="-1" aria-labelledby="stakeCreditsModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -406,29 +406,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buy TFC Credits with TFT Modal -->
|
<!-- Buy MC Credits with TFT Modal -->
|
||||||
<div class="modal fade" id="buyTFCWithTFTModal" tabindex="-1" aria-labelledby="buyTFCWithTFTModalLabel" aria-hidden="true">
|
<div class="modal fade" id="buyMCWithTFTModal" tabindex="-1" aria-labelledby="buyMCWithTFTModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-success text-white">
|
<div class="modal-header bg-success text-white">
|
||||||
<h5 class="modal-title" id="buyTFCWithTFTModalLabel">Buy ThreeFold Credits (TFC) with TFT</h5>
|
<h5 class="modal-title" id="buyMCWithTFTModalLabel">Buy Mycelium Credits (MC) with TFT</h5>
|
||||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="tfcAmountTFT" class="form-label">Amount of TFC Credits to purchase</label>
|
<label for="tfcAmountTFT" class="form-label">Amount of MC Credits to purchase</label>
|
||||||
<input type="number" class="form-control" id="tfpAmountTFT" min="10" value="100">
|
<input type="number" class="form-control" id="tfpAmountTFT" min="10" value="100">
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>Exchange Rate:</strong> 1 TFC = 0.5 TFT
|
<strong>Exchange Rate:</strong> 1 MC = 0.5 TFT
|
||||||
<hr class="my-2">
|
<hr class="my-2">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>Amount:</span>
|
<span>Amount:</span>
|
||||||
<span class="text-end">100 TFC</span>
|
<span class="text-end">100 MC</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>Cost:</span>
|
<span>Cost:</span>
|
||||||
@@ -447,30 +447,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sell TFC Credits for TFT Modal -->
|
<!-- Sell MC Credits for TFT Modal -->
|
||||||
<div class="modal fade" id="sellTFCForTFTModal" tabindex="-1" aria-labelledby="sellTFCForTFTModalLabel" aria-hidden="true">
|
<div class="modal fade" id="sellMCForTFTModal" tabindex="-1" aria-labelledby="sellMCForTFTModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-success text-white">
|
<div class="modal-header bg-success text-white">
|
||||||
<h5 class="modal-title" id="sellTFCForTFTModalLabel">Sell ThreeFold Credits (TFC) for TFT</h5>
|
<h5 class="modal-title" id="sellMCForTFTModalLabel">Sell Mycelium Credits (MC) for TFT</h5>
|
||||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="sellTfcAmountTFT" class="form-label">Amount of TFC Credits to sell</label>
|
<label for="sellTfcAmountTFT" class="form-label">Amount of MC Credits to sell</label>
|
||||||
<input type="number" class="form-control" id="sellTfpAmountTFT" min="10" max="1250" value="100">
|
<input type="number" class="form-control" id="sellTfpAmountTFT" min="10" max="1250" value="100">
|
||||||
<div class="form-text">Maximum available: 1,250 TFC</div>
|
<div class="form-text">Maximum available: 1,250 MC</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>Exchange Rate:</strong> 1 TFC = 0.5 TFT
|
<strong>Exchange Rate:</strong> 1 MC = 0.5 TFT
|
||||||
<hr class="my-2">
|
<hr class="my-2">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>Amount:</span>
|
<span>Amount:</span>
|
||||||
<span class="text-end">100 TFC</span>
|
<span class="text-end">100 MC</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>You receive:</span>
|
<span>You receive:</span>
|
||||||
@@ -489,29 +489,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buy TFC Credits with PEAQ Modal -->
|
<!-- Buy MC Credits with PEAQ Modal -->
|
||||||
<div class="modal fade" id="buyTFCWithPEAQModal" tabindex="-1" aria-labelledby="buyTFCWithPEAQModalLabel" aria-hidden="true">
|
<div class="modal fade" id="buyMCWithPEAQModal" tabindex="-1" aria-labelledby="buyMCWithPEAQModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-info text-white">
|
<div class="modal-header bg-info text-white">
|
||||||
<h5 class="modal-title" id="buyTFCWithPEAQModalLabel">Buy ThreeFold Credits (TFC) with PEAQ</h5>
|
<h5 class="modal-title" id="buyMCWithPEAQModalLabel">Buy Mycelium Credits (MC) with PEAQ</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="tfcAmountPEAQ" class="form-label">Amount of TFC Credits to purchase</label>
|
<label for="tfcAmountPEAQ" class="form-label">Amount of MC Credits to purchase</label>
|
||||||
<input type="number" class="form-control" id="tfpAmountPEAQ" min="10" value="100">
|
<input type="number" class="form-control" id="tfpAmountPEAQ" min="10" value="100">
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>Exchange Rate:</strong> 1 TFC = 2 PEAQ
|
<strong>Exchange Rate:</strong> 1 MC = 2 PEAQ
|
||||||
<hr class="my-2">
|
<hr class="my-2">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>Amount:</span>
|
<span>Amount:</span>
|
||||||
<span class="text-end">100 TFC</span>
|
<span class="text-end">100 MC</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>Cost:</span>
|
<span>Cost:</span>
|
||||||
@@ -530,30 +530,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sell TFC Credits for PEAQ Modal -->
|
<!-- Sell MC Credits for PEAQ Modal -->
|
||||||
<div class="modal fade" id="sellTFCForPEAQModal" tabindex="-1" aria-labelledby="sellTFCForPEAQModalLabel" aria-hidden="true">
|
<div class="modal fade" id="sellMCForPEAQModal" tabindex="-1" aria-labelledby="sellMCForPEAQModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-info text-white">
|
<div class="modal-header bg-info text-white">
|
||||||
<h5 class="modal-title" id="sellTFCForPEAQModalLabel">Sell ThreeFold Credits (TFC) for PEAQ</h5>
|
<h5 class="modal-title" id="sellMCForPEAQModalLabel">Sell Mycelium Credits (MC) for PEAQ</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="sellTfcAmountPEAQ" class="form-label">Amount of TFC Credits to sell</label>
|
<label for="sellTfcAmountPEAQ" class="form-label">Amount of MC Credits to sell</label>
|
||||||
<input type="number" class="form-control" id="sellTfpAmountPEAQ" min="10" max="1250" value="100">
|
<input type="number" class="form-control" id="sellTfpAmountPEAQ" min="10" max="1250" value="100">
|
||||||
<div class="form-text">Maximum available: 1,250 TFC</div>
|
<div class="form-text">Maximum available: 1,250 MC</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
<i class="bi bi-info-circle-fill me-2 fs-4"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>Exchange Rate:</strong> 1 TFC = 2 PEAQ
|
<strong>Exchange Rate:</strong> 1 MC = 2 PEAQ
|
||||||
<hr class="my-2">
|
<hr class="my-2">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>Amount:</span>
|
<span>Amount:</span>
|
||||||
<span class="text-end">100 TFC</span>
|
<span class="text-end">100 MC</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>You receive:</span>
|
<span>You receive:</span>
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
{% extends "dashboard/layout.html" %}
|
{% extends "dashboard/layout.html" %}
|
||||||
|
|
||||||
{% block title %}ThreeFold Dashboard - Farmer{% endblock %}
|
{% block title %}ThreeFold Dashboard - Resource Provider{% endblock %}
|
||||||
|
|
||||||
{% block dashboard_content %}
|
{% block dashboard_content %}
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h1>Farmer Dashboard</h1>
|
<h1>Resource Provider Dashboard</h1>
|
||||||
<p class="lead">Manage your nodes, configure slices, and monitor earnings</p>
|
<p class="lead">Manage your nodes, configure slices, and monitor earnings</p>
|
||||||
|
|
||||||
<!-- Status Summary -->
|
<!-- Status Summary -->
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
<div class="stats-card success">
|
<div class="stats-card success">
|
||||||
<h5 class="card-title">Active Nodes</h5>
|
<h5 class="card-title">Active Nodes</h5>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 class="mb-0" id="active-nodes-count">{{ farmer_stats.online_nodes }}</h2>
|
<h2 class="mb-0" id="active-nodes-count">{{ resource_provider_stats.online_nodes }}</h2>
|
||||||
<small class="text-muted">of {{ farmer_stats.total_nodes }} total</small>
|
<small class="text-muted">of {{ resource_provider_stats.total_nodes }} total</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
<div class="stats-card primary">
|
<div class="stats-card primary">
|
||||||
<h5 class="card-title">Allocated Slices</h5>
|
<h5 class="card-title">Allocated Slices</h5>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 class="mb-0" id="active-slices-count">{{ farmer_stats.allocated_base_slices }}</h2>
|
<h2 class="mb-0" id="active-slices-count">{{ resource_provider_stats.allocated_base_slices }}</h2>
|
||||||
<small class="text-muted">of {{ farmer_stats.total_base_slices }} total</small>
|
<small class="text-muted">of {{ resource_provider_stats.total_base_slices }} total</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<div class="stats-card warning">
|
<div class="stats-card warning">
|
||||||
<h5 class="card-title">Monthly Earnings</h5>
|
<h5 class="card-title">Monthly Earnings</h5>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 class="mb-0" id="monthly-earnings">{{ farmer_stats.monthly_earnings }}</h2>
|
<h2 class="mb-0" id="monthly-earnings">{{ resource_provider_stats.monthly_earnings }}</h2>
|
||||||
<small class="text-muted">$/month</small>
|
<small class="text-muted">$/month</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
<div class="stats-card primary">
|
<div class="stats-card primary">
|
||||||
<h6 class="card-title">Total Base Slices</h6>
|
<h6 class="card-title">Total Base Slices</h6>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h3 class="mb-0" id="total-base-slices">{{ farmer_stats.total_base_slices }}</h3>
|
<h3 class="mb-0" id="total-base-slices">{{ resource_provider_stats.total_base_slices }}</h3>
|
||||||
<small class="text-muted">Available</small>
|
<small class="text-muted">Available</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
<div class="stats-card success">
|
<div class="stats-card success">
|
||||||
<h6 class="card-title">Allocated Slices</h6>
|
<h6 class="card-title">Allocated Slices</h6>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h3 class="mb-0" id="allocated-base-slices">{{ farmer_stats.allocated_base_slices }}</h3>
|
<h3 class="mb-0" id="allocated-base-slices">{{ resource_provider_stats.allocated_base_slices }}</h3>
|
||||||
<small class="text-muted">Rented</small>
|
<small class="text-muted">Rented</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
<div class="stats-card info">
|
<div class="stats-card info">
|
||||||
<h6 class="card-title">Available Slices</h6>
|
<h6 class="card-title">Available Slices</h6>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h3 class="mb-0" id="available-base-slices">{{ farmer_stats.available_base_slices }}</h3>
|
<h3 class="mb-0" id="available-base-slices">{{ resource_provider_stats.available_base_slices }}</h3>
|
||||||
<small class="text-muted">For Rent</small>
|
<small class="text-muted">For Rent</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
<div class="stats-card warning">
|
<div class="stats-card warning">
|
||||||
<h6 class="card-title">Utilization</h6>
|
<h6 class="card-title">Utilization</h6>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h3 class="mb-0" id="slice-utilization">{{ farmer_stats.slice_utilization_percentage }}%</h3>
|
<h3 class="mb-0" id="slice-utilization">{{ resource_provider_stats.slice_utilization_percentage }}%</h3>
|
||||||
<small class="text-muted">Capacity</small>
|
<small class="text-muted">Capacity</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -238,8 +238,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="node-slices-table">
|
<tbody id="node-slices-table">
|
||||||
{% if farmer_nodes %}
|
{% if resource_provider_nodes %}
|
||||||
{% for node in farmer_nodes %}
|
{% for node in resource_provider_nodes %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
@@ -396,8 +396,8 @@
|
|||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
|
||||||
|
|
||||||
<!-- Load farmer dashboard JavaScript -->
|
<!-- Load resource provider dashboard JavaScript -->
|
||||||
<script src="/static/js/dashboard-farmer.js"></script>
|
<script src="/static/js/dashboard-resource_provider.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Ensure charts have consistent sizes */
|
/* Ensure charts have consistent sizes */
|
||||||
@@ -526,7 +526,7 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<i class="bi bi-info-circle me-2"></i>
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
<strong>ThreeFold Grid Integration:</strong> Enter the Grid Node IDs of your physical 3Nodes. Node specifications and location will be automatically fetched from the ThreeFold Grid.
|
<strong>Mycelium Grid Integration:</strong> Enter the Grid Node IDs of your physical Mycelium Nodes. Node specifications and location will be automatically fetched from the ThreeFold Grid.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Node Input Mode Selection -->
|
<!-- Node Input Mode Selection -->
|
@@ -1,11 +1,11 @@
|
|||||||
{% extends "dashboard/layout.html" %}
|
{% extends "dashboard/layout.html" %}
|
||||||
|
|
||||||
{% block title %}ThreeFold Dashboard - Settings{% endblock %}
|
{% block title %}Mycelium Dashboard - Settings{% endblock %}
|
||||||
|
|
||||||
{% block dashboard_content %}
|
{% block dashboard_content %}
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h1>Account Settings</h1>
|
<h1>Account Settings</h1>
|
||||||
<p class="lead">Manage your ThreeFold account preferences and security settings</p>
|
<p class="lead">Manage your Mycelium account preferences and security settings</p>
|
||||||
|
|
||||||
<div class="row mt-5">
|
<div class="row mt-5">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
@@ -415,7 +415,7 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select class="form-select" id="displayCurrency" name="display_currency">
|
<select class="form-select" id="displayCurrency" name="display_currency">
|
||||||
<option value="USD" {% if user_display_currency == "USD" %}selected{% endif %}>USD - US Dollar</option>
|
<option value="USD" {% if user_display_currency == "USD" %}selected{% endif %}>USD - US Dollar</option>
|
||||||
<option value="TFC" {% if user_display_currency == "TFC" %}selected{% endif %}>TFC - ThreeFold Credits</option>
|
<option value="MC" {% if user_display_currency == "MC" %}selected{% endif %}>MC - Mycelium Credit</option>
|
||||||
<option value="EUR" {% if user_display_currency == "EUR" %}selected{% endif %}>EUR - Euro</option>
|
<option value="EUR" {% if user_display_currency == "EUR" %}selected{% endif %}>EUR - Euro</option>
|
||||||
<option value="CAD" {% if user_display_currency == "CAD" %}selected{% endif %}>CAD - Canadian Dollar</option>
|
<option value="CAD" {% if user_display_currency == "CAD" %}selected{% endif %}>CAD - Canadian Dollar</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -425,7 +425,7 @@
|
|||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<i class="bi bi-info-circle me-2"></i>
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
<strong>Note:</strong> All transactions are processed in USD Credits. Display currency is used for convenience only and prices are converted in real-time.
|
<strong>Note:</strong> All transactions are processed in MC Credits. Display currency is used for convenience only and prices are converted in real-time.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
<div class="display-4 mb-3 text-success">
|
<div class="display-4 mb-3 text-success">
|
||||||
<i class="bi bi-hdd-rack"></i>
|
<i class="bi bi-hdd-rack"></i>
|
||||||
</div>
|
</div>
|
||||||
<h5>As a Farmer</h5>
|
<h5>As a Resource Provider</h5>
|
||||||
<p>Manage nodes, configure slices, set pricing</p>
|
<p>Manage nodes, configure slices, set pricing</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
{% extends "docs/layout.html" %}
|
{% extends "docs/layout.html" %}
|
||||||
|
|
||||||
{% block title %}Application Solutions - Project Mycelium{% endblock %}
|
{% block title %}Agentic Apps - Project Mycelium{% endblock %}
|
||||||
|
|
||||||
{% block docs_content %}
|
{% block docs_content %}
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h1>Application Solutions</h1>
|
<h1>Agentic Apps</h1>
|
||||||
<p class="lead">Discover self-healing applications that maintain sovereignty while offering convenience.</p>
|
<p class="lead">Discover self-healing applications that maintain sovereignty while offering convenience.</p>
|
||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<p>ThreeFold Application Solutions represent a new approach to application deployment that balances ease of use with digital sovereignty. These pre-configured, self-healing applications are designed to run on the ThreeFold Grid while allowing users to maintain complete control over their infrastructure and data.</p>
|
<p>ThreeFold Agentic Apps represent a new approach to application deployment that balances ease of use with digital sovereignty. These pre-configured, self-healing applications are designed to run on the ThreeFold Grid while allowing users to maintain complete control over their infrastructure and data.</p>
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="me-3">
|
<div class="me-3">
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="alert-heading">The ThreeFold Difference</h5>
|
<h5 class="alert-heading">The ThreeFold Difference</h5>
|
||||||
<p class="mb-0">Unlike traditional SaaS offerings that require surrendering control of your data to the provider, ThreeFold Application Solutions run on compute resources that remain under your sovereign control, while the solution provider manages only the application layer.</p>
|
<p class="mb-0">Unlike traditional SaaS offerings that require surrendering control of your data to the provider, ThreeFold Agentic Apps run on compute resources that remain under your sovereign control, while the application provider manages only the application layer.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Application Categories</h2>
|
<h2>Application Categories</h2>
|
||||||
<p>The Project Mycelium offers a diverse range of application solutions across multiple categories:</p>
|
<p>The Project Mycelium offers a diverse range of applications across multiple categories:</p>
|
||||||
|
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 mb-4">
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 mb-4">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@@ -88,11 +88,11 @@
|
|||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>How It Works</h2>
|
<h2>How It Works</h2>
|
||||||
<p>Application Solutions on the Project Mycelium function through a unique partnership model between users and solution providers:</p>
|
<p>Agentic Apps on the Project Mycelium function through a unique partnership model between users and application providers:</p>
|
||||||
|
|
||||||
<div class="row align-items-center mb-4">
|
<div class="row align-items-center mb-4">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<img src="/static/images/docs/application-flow.svg" alt="Application Solution Flow Diagram" class="img-fluid rounded shadow">
|
<img src="/static/images/docs/application-flow.svg" alt="Agentic Apps Flow Diagram" class="img-fluid rounded shadow">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<ol class="list-group list-group-numbered mb-0">
|
<ol class="list-group list-group-numbered mb-0">
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
<li class="list-group-item d-flex">
|
<li class="list-group-item d-flex">
|
||||||
<div>
|
<div>
|
||||||
<strong>Solution Deployment</strong>
|
<strong>Solution Deployment</strong>
|
||||||
<p class="mb-0 text-muted">The solution provider deploys their application on your sovereign compute resources.</p>
|
<p class="mb-0 text-muted">The application provider deploys their application on your sovereign compute resources.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item d-flex">
|
<li class="list-group-item d-flex">
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
<li class="list-group-item d-flex">
|
<li class="list-group-item d-flex">
|
||||||
<div>
|
<div>
|
||||||
<strong>Payment Model</strong>
|
<strong>Payment Model</strong>
|
||||||
<p class="mb-0 text-muted">You pay the solution provider in USD Credits based on the agreed-upon pricing model.</p>
|
<p class="mb-0 text-muted">You pay the application provider in USD Credits based on the agreed-upon pricing model.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="alert-heading">Sovereignty Maintained</h5>
|
<h5 class="alert-heading">Sovereignty Maintained</h5>
|
||||||
<p class="mb-0">Your data and applications remain on infrastructure under your control. The solution provider cannot access your data without your explicit permission, and you can revoke their management access at any time.</p>
|
<p class="mb-0">Your data and applications remain on infrastructure under your control. The application provider cannot access your data without your explicit permission, and you can revoke their management access at any time.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Solution Provider Certification</h2>
|
<h2>Application Provider Certification</h2>
|
||||||
<p>Solution providers in the Project Mycelium undergo a certification process to ensure quality and reliability:</p>
|
<p>Solution providers in the Project Mycelium undergo a certification process to ensure quality and reliability:</p>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
<i class="bi bi-shop fs-1 text-primary"></i>
|
<i class="bi bi-shop fs-1 text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="card-title">1. Select Application</h5>
|
<h5 class="card-title">1. Select Application</h5>
|
||||||
<p class="card-text">Browse the marketplace and select your desired application solution.</p>
|
<p class="card-text">Browse the marketplace and select your desired application.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -33,7 +33,7 @@
|
|||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title"><i class="bi bi-hdd-rack me-2 text-primary"></i>Hardware Certification</h5>
|
<h5 class="card-title"><i class="bi bi-hdd-rack me-2 text-primary"></i>Hardware Certification</h5>
|
||||||
<p class="card-text">Verification of 3Nodes and physical infrastructure components to ensure they meet performance, reliability, and security standards.</p>
|
<p class="card-text">Verification of Mycelium Nodes and physical infrastructure components to ensure they meet performance, reliability, and security standards.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<p>Compute Resources, often referred to as "Slices," are the fundamental units of computing capacity in the ThreeFold ecosystem. They represent virtualized portions of underlying physical hardware (3Nodes) that can be used to deploy workloads, applications, and services.</p>
|
<p>Compute Resources, often referred to as "Slices," are the fundamental units of computing capacity in the Mycelium ecosystem. They represent virtualized portions of underlying physical hardware (Mycelium Nodes) that can be used to deploy workloads, applications, and services.</p>
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
|
@@ -56,7 +56,7 @@
|
|||||||
<h5 class="mb-3"><i class="bi bi-shop me-2 text-primary"></i>Discovering Resources</h5>
|
<h5 class="mb-3"><i class="bi bi-shop me-2 text-primary"></i>Discovering Resources</h5>
|
||||||
<p>Once your account and wallet are set up, you can start exploring the marketplace:</p>
|
<p>Once your account and wallet are set up, you can start exploring the marketplace:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Browse Categories:</strong> Explore different sections such as compute resources, 3Nodes, applications, and services</li>
|
<li><strong>Browse Categories:</strong> Explore different sections such as compute resources, Mycelium Nodes, applications, and services</li>
|
||||||
<li><strong>Apply Filters:</strong> Narrow down options based on specifications, location, pricing, and other criteria</li>
|
<li><strong>Apply Filters:</strong> Narrow down options based on specifications, location, pricing, and other criteria</li>
|
||||||
<li><strong>Compare Options:</strong> Evaluate different offerings to find the best fit for your needs</li>
|
<li><strong>Compare Options:</strong> Evaluate different offerings to find the best fit for your needs</li>
|
||||||
<li><strong>Read Reviews:</strong> Check ratings and feedback from other users</li>
|
<li><strong>Read Reviews:</strong> Check ratings and feedback from other users</li>
|
||||||
|
@@ -46,10 +46,10 @@
|
|||||||
<td><a href="/docs/compute" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
<td><a href="/docs/compute" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>3Nodes</strong></td>
|
<td><strong>Mycelium Nodes</strong></td>
|
||||||
<td>Physical computing hardware marketplace</td>
|
<td>Physical computing hardware marketplace</td>
|
||||||
<td>Credits transferred based on hardware value</td>
|
<td>Credits transferred based on hardware value</td>
|
||||||
<td><a href="/docs/3nodes" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
<td><a href="/docs/mycelium_nodes" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Mycelium Gateways</strong></td>
|
<td><strong>Mycelium Gateways</strong></td>
|
||||||
@@ -58,9 +58,9 @@
|
|||||||
<td><a href="/docs/gateways" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
<td><a href="/docs/gateways" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Application Solutions</strong></td>
|
<td><strong>Agentic Apps</strong></td>
|
||||||
<td>Pre-configured, self-healing applications</td>
|
<td>Pre-configured, self-healing applications</td>
|
||||||
<td>Credits paid to solution providers for application management while users maintain sovereignty</td>
|
<td>Credits paid to application providers for application management while users maintain sovereignty</td>
|
||||||
<td><a href="/docs/applications" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
<td><a href="/docs/applications" class="btn btn-sm btn-outline-primary">Learn More</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@@ -40,9 +40,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active_section == '3nodes' %}active{% endif %}" href="/docs/3nodes">
|
<a class="nav-link {% if active_section == 'mycelium_nodes' %}active{% endif %}" href="/docs/mycelium_nodes">
|
||||||
<i class="bi bi-hdd-rack me-1"></i>
|
<i class="bi bi-hdd-rack me-1"></i>
|
||||||
3Nodes
|
Mycelium Nodes
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active_section == 'applications' %}active{% endif %}" href="/docs/applications">
|
<a class="nav-link {% if active_section == 'applications' %}active{% endif %}" href="/docs/applications">
|
||||||
<i class="bi bi-app me-1"></i>
|
<i class="bi bi-app me-1"></i>
|
||||||
Application Solutions
|
Agentic Apps
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
@@ -1,22 +1,22 @@
|
|||||||
{% extends "docs/layout.html" %}
|
{% extends "docs/layout.html" %}
|
||||||
|
|
||||||
{% block title %}3Nodes Documentation - Project Mycelium{% endblock %}
|
{% block title %}Mycelium Nodes Documentation - Project Mycelium{% endblock %}
|
||||||
|
|
||||||
{% block docs_content %}
|
{% block docs_content %}
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h1>3Nodes</h1>
|
<h1>Mycelium Nodes</h1>
|
||||||
<p class="lead">Physical computing hardware that can be bought and sold within the Project Mycelium using USD Credits.</p>
|
<p class="lead">Physical computing hardware that can be bought and sold within the Project Mycelium using USD Credits.</p>
|
||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<p>3Nodes are physical computing hardware (servers, computers) that can be bought and sold within the TF Marketplace using USD Credits. Users can purchase 3Nodes in two distinct ways: either hosted by the supplier or physically transferred to the buyer.</p>
|
<p>Mycelium Nodes are physical computing hardware (servers, computers) that can be bought and sold within the TF Marketplace using USD Credits. Users can purchase Mycelium Nodes in two distinct ways: either hosted by the supplier or physically transferred to the buyer.</p>
|
||||||
|
|
||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card mb-4 h-100">
|
<div class="card mb-4 h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title"><i class="bi bi-building me-2 text-primary"></i>Supplier-Hosted</h5>
|
<h5 class="card-title"><i class="bi bi-building me-2 text-primary"></i>Supplier-Hosted</h5>
|
||||||
<p class="card-text">Buy the 3Node with upfront Credits payment but have it hosted in the supplier's facility. You maintain ownership while the supplier handles physical maintenance.</p>
|
<p class="card-text">Buy the Mycelium Node with upfront Credits payment but have it hosted in the supplier's facility. You maintain ownership while the supplier handles physical maintenance.</p>
|
||||||
<p><strong>Ideal for:</strong> Buyers without suitable hosting facilities</p>
|
<p><strong>Ideal for:</strong> Buyers without suitable hosting facilities</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<div class="card mb-4 h-100">
|
<div class="card mb-4 h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title"><i class="bi bi-truck me-2 text-primary"></i>Physical Transfer</h5>
|
<h5 class="card-title"><i class="bi bi-truck me-2 text-primary"></i>Physical Transfer</h5>
|
||||||
<p class="card-text">Buy the 3Node with upfront Credits payment and have it physically shipped to your location. Complete physical control and responsibility for the hardware.</p>
|
<p class="card-text">Buy the Mycelium Node with upfront Credits payment and have it physically shipped to your location. Complete physical control and responsibility for the hardware.</p>
|
||||||
<p><strong>Ideal for:</strong> Buyers with their own hosting capabilities</p>
|
<p><strong>Ideal for:</strong> Buyers with their own hosting capabilities</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5>Hardware Discovery</h5>
|
<h5>Hardware Discovery</h5>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Browse available 3Nodes with filtering options</li>
|
<li>Browse available Mycelium Nodes with filtering options</li>
|
||||||
<li>Compare specifications and pricing</li>
|
<li>Compare specifications and pricing</li>
|
||||||
<li>Review seller ratings and history</li>
|
<li>Review seller ratings and history</li>
|
||||||
<li>Ask questions about specific hardware</li>
|
<li>Ask questions about specific hardware</li>
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
|
|
||||||
<h5 class="mt-4">Purchase Process</h5>
|
<h5 class="mt-4">Purchase Process</h5>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Select desired 3Node and purchase option (hosted or transferred)</li>
|
<li>Select desired Mycelium Node and purchase option (hosted or transferred)</li>
|
||||||
<li>Pay upfront using Credits for either option</li>
|
<li>Pay upfront using Credits for either option</li>
|
||||||
<li>For physical transfer: arrange shipping and delivery</li>
|
<li>For physical transfer: arrange shipping and delivery</li>
|
||||||
<li>For hosted option: receive access credentials</li>
|
<li>For hosted option: receive access credentials</li>
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Hardware Specifications</h2>
|
<h2>Hardware Specifications</h2>
|
||||||
<p>3Nodes can include various types of computing hardware:</p>
|
<p>Mycelium Nodes can include various types of computing hardware:</p>
|
||||||
|
|
||||||
<div class="row row-cols-1 row-cols-md-2 g-4 mb-4">
|
<div class="row row-cols-1 row-cols-md-2 g-4 mb-4">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@@ -341,7 +341,7 @@
|
|||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title"><i class="bi bi-people me-2 text-success"></i>Community Building</h5>
|
<h5 class="card-title"><i class="bi bi-people me-2 text-success"></i>Community Building</h5>
|
||||||
<p class="card-text">Connects hardware providers with potential farmers</p>
|
<p class="card-text">Connects hardware providers with potential resource providers</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -350,10 +350,10 @@
|
|||||||
|
|
||||||
<div class="card bg-light mt-5">
|
<div class="card bg-light mt-5">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2><i class="bi bi-question-circle me-2"></i>Ready to Buy or Sell 3Nodes?</h2>
|
<h2><i class="bi bi-question-circle me-2"></i>Ready to Buy or Sell Mycelium Nodes?</h2>
|
||||||
<p class="lead">Visit the Project Mycelium 3Nodes section to start exploring options.</p>
|
<p class="lead">Visit the Project Mycelium Mycelium Nodes section to start exploring options.</p>
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<a href="/marketplace/3nodes" class="btn btn-primary me-2">Explore 3Nodes Marketplace</a>
|
<a href="/marketplace/mycelium_nodes" class="btn btn-primary me-2">Explore Mycelium Nodes Marketplace</a>
|
||||||
<a href="/docs" class="btn btn-outline-secondary">Back to Documentation</a>
|
<a href="/docs" class="btn btn-outline-secondary">Back to Documentation</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@@ -63,7 +63,7 @@
|
|||||||
<h5 class="card-title"><i class="bi bi-hdd-rack me-2 text-primary"></i>Deployment</h5>
|
<h5 class="card-title"><i class="bi bi-hdd-rack me-2 text-primary"></i>Deployment</h5>
|
||||||
<p class="card-text">Setup, configuration, and deployment of applications and infrastructure.</p>
|
<p class="card-text">Setup, configuration, and deployment of applications and infrastructure.</p>
|
||||||
<ul class="small text-muted">
|
<ul class="small text-muted">
|
||||||
<li>3Node configuration</li>
|
<li>Mycelium Node configuration</li>
|
||||||
<li>Application deployment</li>
|
<li>Application deployment</li>
|
||||||
<li>Network setup</li>
|
<li>Network setup</li>
|
||||||
<li>Migration services</li>
|
<li>Migration services</li>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<p>Slices represent the core unit of compute capacity within the ThreeFold Grid. They are standardized divisions of physical computing hardware (3Nodes) that can be allocated and managed as independent virtual resources. Slices form the foundation of all deployments on the ThreeFold Grid and serve as the primary unit of exchange for computational resources.</p>
|
<p>Slices represent the core unit of compute capacity within the Mycelium Grid. They are standardized divisions of physical computing hardware (Mycelium Nodes) that can be allocated and managed as independent virtual resources. Slices form the foundation of all deployments on the Mycelium Grid and serve as the primary unit of exchange for computational resources.</p>
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="alert-heading">Key Concept</h5>
|
<h5 class="alert-heading">Key Concept</h5>
|
||||||
<p class="mb-0">Slices abstract away the underlying physical infrastructure, providing a consistent and standardized way to measure, allocate, and exchange computing capacity across the entire ThreeFold Grid.</p>
|
<p class="mb-0">Slices abstract away the underlying physical infrastructure, providing a consistent and standardized way to measure, allocate, and exchange computing capacity across the entire Mycelium Grid.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
|
|
||||||
<div class="doc-section">
|
<div class="doc-section">
|
||||||
<h2>Slice Types</h2>
|
<h2>Slice Types</h2>
|
||||||
<p>The ThreeFold Grid offers different types of slices optimized for specific workloads:</p>
|
<p>The Mycelium Grid offers different types of slices optimized for specific workloads:</p>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
<td>Credits charged based on resource utilization</td>
|
<td>Credits charged based on resource utilization</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>3Nodes</strong></td>
|
<td><strong>Mycelium Nodes</strong></td>
|
||||||
<td>Physical computing hardware marketplace</td>
|
<td>Physical computing hardware marketplace</td>
|
||||||
<td>Credits transferred based on hardware value</td>
|
<td>Credits transferred based on hardware value</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
<td>Credits paid based on bandwidth consumption</td>
|
<td>Credits paid based on bandwidth consumption</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Application Solutions</strong></td>
|
<td><strong>Agentic Apps</strong></td>
|
||||||
<td>Pre-configured, self-healing applications</td>
|
<td>Pre-configured, self-healing applications</td>
|
||||||
<td>Users provide slices to solution providers while maintaining sovereignty</td>
|
<td>Users provide slices to application providers while maintaining sovereignty</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Human Energy Services</strong></td>
|
<td><strong>Human Energy Services</strong></td>
|
||||||
|
@@ -78,7 +78,7 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title"><i class="bi bi-hdd-rack me-2"></i>Farmers</h5>
|
<h5 class="card-title"><i class="bi bi-hdd-rack me-2"></i>Resource Providers</h5>
|
||||||
<p class="card-text">Contribute compute capacity to the ThreeFold Grid and earn through the platform's ecosystem.</p>
|
<p class="card-text">Contribute compute capacity to the ThreeFold Grid and earn through the platform's ecosystem.</p>
|
||||||
<a href="/docs" class="btn btn-outline-primary">Learn More</a>
|
<a href="/docs" class="btn btn-outline-primary">Learn More</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -47,9 +47,9 @@
|
|||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="bi bi-hdd-rack text-primary display-4 mb-3"></i>
|
<i class="bi bi-hdd-rack text-primary display-4 mb-3"></i>
|
||||||
<h3>3Nodes</h3>
|
<h3>Mycelium Nodes</h3>
|
||||||
<p>Buy and sell physical computing hardware to support the Grid.</p>
|
<p>Buy and sell physical computing hardware to support the Grid.</p>
|
||||||
<a href="/marketplace/3nodes" class="btn btn-sm btn-outline-primary">Explore</a>
|
<a href="/marketplace/mycelium_nodes" class="btn btn-sm btn-outline-primary">Explore</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<div id="faqCollapse2" class="accordion-collapse collapse" aria-labelledby="faqHeading2" data-bs-parent="#faqAccordion">
|
<div id="faqCollapse2" class="accordion-collapse collapse" aria-labelledby="faqHeading2" data-bs-parent="#faqAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
The Marketplace offers various categories including: Compute Resources (slices) for cloud computing, physical 3Nodes for hardware, Mycelium Gateways for connectivity, self-healing Application Solutions that maintain your data sovereignty, and Human Energy Services where you can access professional expertise.
|
The Marketplace offers various categories including: Compute Resources (slices) for cloud computing, physical Mycelium Nodes for hardware, Mycelium Gateways for connectivity, self-healing Agentic Apps that maintain your data sovereignty, and Human Energy Services where you can access professional expertise.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -118,9 +118,9 @@
|
|||||||
<p>Unlike traditional SaaS offerings where you surrender control to the provider, ThreeFold's model maintains your sovereignty in two ways:</p>
|
<p>Unlike traditional SaaS offerings where you surrender control to the provider, ThreeFold's model maintains your sovereignty in two ways:</p>
|
||||||
<ol class="mb-3">
|
<ol class="mb-3">
|
||||||
<li><strong>Direct Management:</strong> Manage your own compute slices directly for complete control</li>
|
<li><strong>Direct Management:</strong> Manage your own compute slices directly for complete control</li>
|
||||||
<li><strong>Sovereign Allocation:</strong> Allocate your sovereign compute slices to solution providers who deploy and manage applications on your infrastructure</li>
|
<li><strong>Sovereign Allocation:</strong> Allocate your sovereign compute slices to application providers who deploy and manage applications on your infrastructure</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>With application solutions, you maintain control because:</p>
|
<p>With applications, you maintain control because:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>You still own and control the underlying resources</li>
|
<li>You still own and control the underlying resources</li>
|
||||||
<li>Your data remains on infrastructure under your control</li>
|
<li>Your data remains on infrastructure under your control</li>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}Solution Providers Terms and Conditions - Project Mycelium{% endblock %}
|
{% block title %}Application Providers Terms and Conditions - Project Mycelium{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container my-5">
|
<div class="container my-5">
|
||||||
@@ -9,50 +9,50 @@
|
|||||||
<nav aria-label="breadcrumb">
|
<nav aria-label="breadcrumb">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="/terms">Terms and Conditions</a></li>
|
<li class="breadcrumb-item"><a href="/terms">Terms and Conditions</a></li>
|
||||||
<li class="breadcrumb-item active" aria-current="page">Solution Providers</li>
|
<li class="breadcrumb-item active" aria-current="page">Application Providers</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="d-flex align-items-center mb-4">
|
<div class="d-flex align-items-center mb-4">
|
||||||
<i class="bi bi-box-seam fs-1 me-3 text-info"></i>
|
<i class="bi bi-box-seam fs-1 me-3 text-info"></i>
|
||||||
<h1>Solution Providers Terms and Conditions</h1>
|
<h1>Application Providers Terms and Conditions</h1>
|
||||||
</div>
|
</div>
|
||||||
<p class="lead mb-4">Last updated: May 22, 2025</p>
|
<p class="lead mb-4">Last updated: May 22, 2025</p>
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<p class="mb-0">These terms specifically apply to Solution Providers on the Project Mycelium who develop and deploy applications and solutions on the ThreeFold Grid.</p>
|
<p class="mb-0">These terms specifically apply to Application Providers on the Project Mycelium who develop and deploy applications on the ThreeFold Grid.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>1. Definition of a Solution Provider</h2>
|
<h2>1. Definition of an Application Provider</h2>
|
||||||
<p>A "Solution Provider" refers to any individual or entity that develops, deploys, and maintains software applications, platforms, or digital solutions on the Project Mycelium. Solution Providers create the applications and services that run on the ThreeFold Grid's infrastructure.</p>
|
<p>An "Application Provider" refers to any individual or entity that develops, deploys, and maintains software applications, platforms, or digital applications on the Project Mycelium. Application Providers create the applications and services that run on the ThreeFold Grid's infrastructure.</p>
|
||||||
|
|
||||||
<h2>2. Solution Provider Responsibilities</h2>
|
<h2>2. Application Provider Responsibilities</h2>
|
||||||
<p>As a Solution Provider on the Project Mycelium, you agree to:</p>
|
<p>As an Application Provider on the Project Mycelium, you agree to:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Provide accurate descriptions of your applications and solutions</li>
|
<li>Provide accurate descriptions of your applications and applications</li>
|
||||||
<li>Maintain your deployed solutions with regular updates and security patches</li>
|
<li>Maintain your deployed applications with regular updates and security patches</li>
|
||||||
<li>Ensure your solutions comply with best practices for security and data protection</li>
|
<li>Ensure your applications comply with best practices for security and data protection</li>
|
||||||
<li>Provide clear documentation for users of your solutions</li>
|
<li>Provide clear documentation for users of your applications</li>
|
||||||
<li>Respond promptly to technical issues and bug reports</li>
|
<li>Respond promptly to technical issues and bug reports</li>
|
||||||
<li>Set fair and transparent pricing for your solutions</li>
|
<li>Set fair and transparent pricing for your applications</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>3. Reputation System and Staking</h2>
|
<h2>3. Reputation System and Staking</h2>
|
||||||
<p>The Project Mycelium employs a reputation system that affects solution provider visibility and benefits:</p>
|
<p>The Project Mycelium employs a reputation system that affects application provider visibility and benefits:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Your reputation score is calculated based on user ratings, solution performance, security metrics, and staked Credits</li>
|
<li>Your reputation score is calculated based on user ratings, application performance, security metrics, and staked Credits</li>
|
||||||
<li>Higher reputation scores may result in improved visibility in marketplace listings</li>
|
<li>Higher reputation scores may result in improved visibility in marketplace listings</li>
|
||||||
<li>Staking Credits increases your reputation score and may qualify you for reduced platform fees</li>
|
<li>Staking Credits increases your reputation score and may qualify you for reduced platform fees</li>
|
||||||
<li>Security vulnerabilities or performance issues may result in reputation penalties</li>
|
<li>Security vulnerabilities or performance issues may result in reputation penalties</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>4. Intellectual Property Rights</h2>
|
<h2>4. Intellectual Property Rights</h2>
|
||||||
<p>Regarding the intellectual property of your solutions:</p>
|
<p>Regarding the intellectual property of your applications:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>You retain all intellectual property rights to your applications and solutions</li>
|
<li>You retain all intellectual property rights to your applications and applications</li>
|
||||||
<li>You grant ThreeFold a limited license to display, promote, and facilitate access to your solutions on the marketplace</li>
|
<li>You grant ThreeFold a limited license to display, promote, and facilitate access to your applications on the marketplace</li>
|
||||||
<li>You are responsible for ensuring your solutions do not infringe on third-party intellectual property rights</li>
|
<li>You are responsible for ensuring your applications do not infringe on third-party intellectual property rights</li>
|
||||||
<li>You may choose the appropriate licensing model for your solutions (open source, proprietary, etc.)</li>
|
<li>You may choose the appropriate licensing model for your applications (open source, proprietary, etc.)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>5. Application Deployment and Credits</h2>
|
<h2>5. Application Deployment and Credits</h2>
|
||||||
@@ -60,20 +60,20 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>All transactions on the marketplace use USD Credits as the medium of exchange</li>
|
<li>All transactions on the marketplace use USD Credits as the medium of exchange</li>
|
||||||
<li>Credits have a fixed value of 1.0 USD per Credit</li>
|
<li>Credits have a fixed value of 1.0 USD per Credit</li>
|
||||||
<li>Solution Providers receive Credits from users who utilize their applications</li>
|
<li>Application Providers receive Credits from users who utilize their applications</li>
|
||||||
<li>Earned Credits can be exchanged for fiat currencies or other supported tokens through the available liquidity pools</li>
|
<li>Earned Credits can be exchanged for fiat currencies or other supported tokens through the available liquidity pools</li>
|
||||||
<li>Platform fees may apply to transactions as detailed in your Solution Provider dashboard</li>
|
<li>Platform fees may apply to transactions as detailed in your Application Provider dashboard</li>
|
||||||
<li>You are responsible for the resource utilization costs of your deployed solutions</li>
|
<li>You are responsible for the resource utilization costs of your deployed applications</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>6. Solution Listings and Distribution</h2>
|
<h2>6. Application Listings and Distribution</h2>
|
||||||
<p>Regarding your solution listings on the marketplace:</p>
|
<p>Regarding your application listings on the marketplace:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>All solution descriptions must comply with the Marketplace Content Guidelines</li>
|
<li>All application descriptions must comply with the Marketplace Content Guidelines</li>
|
||||||
<li>False or misleading claims about solutions are prohibited</li>
|
<li>False or misleading claims about applications are prohibited</li>
|
||||||
<li>ThreeFold reserves the right to remove or request modifications to listings that violate guidelines</li>
|
<li>ThreeFold reserves the right to remove or request modifications to listings that violate guidelines</li>
|
||||||
<li>You may utilize marketplace promotional tools to increase the visibility of your solutions</li>
|
<li>You may utilize marketplace promotional tools to increase the visibility of your applications</li>
|
||||||
<li>You may offer free trials, freemium models, or paid solutions according to your business model</li>
|
<li>You may offer free trials, freemium models, or paid applications according to your business model</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>7. API Usage and Integration</h2>
|
<h2>7. API Usage and Integration</h2>
|
||||||
@@ -91,12 +91,12 @@
|
|||||||
<li>Security vulnerabilities that put user data at risk</li>
|
<li>Security vulnerabilities that put user data at risk</li>
|
||||||
<li>Consistent performance issues that significantly impact user experience</li>
|
<li>Consistent performance issues that significantly impact user experience</li>
|
||||||
<li>Violations of content guidelines or terms of service</li>
|
<li>Violations of content guidelines or terms of service</li>
|
||||||
<li>Fraudulent activities or misrepresentation of solution capabilities</li>
|
<li>Fraudulent activities or misrepresentation of application capabilities</li>
|
||||||
<li>Slashing penalties may include reputation reduction, temporary suspension of listings, or in severe cases, permanent removal from the marketplace</li>
|
<li>Slashing penalties may include reputation reduction, temporary suspension of listings, or in severe cases, permanent removal from the marketplace</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>9. Updates and Maintenance</h2>
|
<h2>9. Updates and Maintenance</h2>
|
||||||
<p>Regarding the ongoing maintenance of your solutions:</p>
|
<p>Regarding the ongoing maintenance of your applications:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>You are responsible for providing regular updates and maintenance</li>
|
<li>You are responsible for providing regular updates and maintenance</li>
|
||||||
<li>Critical security updates should be prioritized and deployed promptly</li>
|
<li>Critical security updates should be prioritized and deployed promptly</li>
|
||||||
@@ -104,18 +104,18 @@
|
|||||||
<li>Scheduled maintenance should be announced in advance when possible</li>
|
<li>Scheduled maintenance should be announced in advance when possible</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>10. Termination of Solution Provider Status</h2>
|
<h2>10. Termination of Application Provider Status</h2>
|
||||||
<p>You may cease being a Solution Provider by:</p>
|
<p>You may cease being an Application Provider by:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Removing all solution listings from the marketplace</li>
|
<li>Removing all application listings from the marketplace</li>
|
||||||
<li>Providing a reasonable migration path or notice to existing users</li>
|
<li>Providing a reasonable migration path or notice to existing users</li>
|
||||||
<li>Completing all outstanding obligations to users</li>
|
<li>Completing all outstanding obligations to users</li>
|
||||||
<li>Providing notice through your dashboard at least 30 days prior to solution removal</li>
|
<li>Providing notice through your dashboard at least 30 days prior to application removal</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="alert alert-warning mt-5">
|
<div class="alert alert-warning mt-5">
|
||||||
<h5 class="alert-heading">Important Note</h5>
|
<h5 class="alert-heading">Important Note</h5>
|
||||||
<p class="mb-0">These Solution Provider-specific terms are in addition to the <a href="/terms">General Terms and Conditions</a> that apply to all users of the Project Mycelium. Please ensure you have reviewed both documents.</p>
|
<p class="mb-0">These Application Provider-specific terms are in addition to the <a href="/terms">General Terms and Conditions</a> that apply to all users of the Project Mycelium. Please ensure you have reviewed both documents.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-5 mb-3">
|
<div class="text-center mt-5 mb-3">
|
@@ -1,6 +1,6 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}Farmers Terms and Conditions - Project Mycelium{% endblock %}
|
{% block title %}Resource Providers Terms and Conditions - Project Mycelium{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container my-5">
|
<div class="container my-5">
|
||||||
@@ -9,25 +9,25 @@
|
|||||||
<nav aria-label="breadcrumb">
|
<nav aria-label="breadcrumb">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="/terms">Terms and Conditions</a></li>
|
<li class="breadcrumb-item"><a href="/terms">Terms and Conditions</a></li>
|
||||||
<li class="breadcrumb-item active" aria-current="page">Farmers</li>
|
<li class="breadcrumb-item active" aria-current="page">Resource Providers</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="d-flex align-items-center mb-4">
|
<div class="d-flex align-items-center mb-4">
|
||||||
<i class="bi bi-hdd-rack fs-1 me-3 text-primary"></i>
|
<i class="bi bi-hdd-rack fs-1 me-3 text-primary"></i>
|
||||||
<h1>Farmers Terms and Conditions</h1>
|
<h1>Resource Providers Terms and Conditions</h1>
|
||||||
</div>
|
</div>
|
||||||
<p class="lead mb-4">Last updated: May 22, 2025</p>
|
<p class="lead mb-4">Last updated: May 22, 2025</p>
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<p class="mb-0">These terms specifically apply to Farmers (Resource Providers) on the Project Mycelium who contribute capacity to the ThreeFold Grid.</p>
|
<p class="mb-0">These terms specifically apply to Resource Providers (Resource Providers) on the Project Mycelium who contribute capacity to the Mycelium Grid.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>1. Definition of a Farmer</h2>
|
<h2>1. Definition of a Resource Provider</h2>
|
||||||
<p>A "Farmer" refers to any individual or entity that connects hardware resources to the ThreeFold Grid, including but not limited to compute nodes (3Nodes), storage capacity, network infrastructure, or other compatible devices that contribute to the ThreeFold Grid's capacity.</p>
|
<p>A "Resource Provider" refers to any individual or entity that connects hardware resources to the Mycelium Grid, including but not limited to compute nodes (Mycelium Nodes), storage capacity, network infrastructure, or other compatible devices that contribute to the Mycelium Grid's capacity.</p>
|
||||||
|
|
||||||
<h2>2. Farmer Responsibilities</h2>
|
<h2>2. Resource Provider Responsibilities</h2>
|
||||||
<p>As a Farmer on the Project Mycelium, you agree to:</p>
|
<p>As a Resource Provider on the Project Mycelium, you agree to:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Maintain your connected hardware in good working condition with adequate internet connectivity</li>
|
<li>Maintain your connected hardware in good working condition with adequate internet connectivity</li>
|
||||||
<li>Ensure your hardware meets the minimum technical requirements specified in the Farming documentation</li>
|
<li>Ensure your hardware meets the minimum technical requirements specified in the Farming documentation</li>
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>3. Reputation System and Staking</h2>
|
<h2>3. Reputation System and Staking</h2>
|
||||||
<p>The Project Mycelium employs a reputation system that affects farmer visibility and rewards:</p>
|
<p>The Project Mycelium employs a reputation system that affects resource provider visibility and rewards:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Your reputation score is calculated based on multiple factors including uptime, staked Credits, and performance metrics</li>
|
<li>Your reputation score is calculated based on multiple factors including uptime, staked Credits, and performance metrics</li>
|
||||||
<li>Staking Credits increases your reputation score and may qualify you for additional benefits</li>
|
<li>Staking Credits increases your reputation score and may qualify you for additional benefits</li>
|
||||||
@@ -48,16 +48,16 @@
|
|||||||
<h2>4. Rewards and Credits</h2>
|
<h2>4. Rewards and Credits</h2>
|
||||||
<p>Compensation for resource contribution is governed by the following principles:</p>
|
<p>Compensation for resource contribution is governed by the following principles:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Farmers receive USD Credits based on the resources utilized from their contributed capacity</li>
|
<li>Resource Providers receive USD Credits based on the resources utilized from their contributed capacity</li>
|
||||||
<li>Credits have a fixed value of 1.0 USD per Credit</li>
|
<li>Credits have a fixed value of 1.0 USD per Credit</li>
|
||||||
<li>Earned Credits can be exchanged for fiat currencies or other supported tokens through the available liquidity pools</li>
|
<li>Earned Credits can be exchanged for fiat currencies or other supported tokens through the available liquidity pools</li>
|
||||||
<li>Payment schedules and minimum thresholds are detailed in the Farmer dashboard</li>
|
<li>Payment schedules and minimum thresholds are detailed in the Resource Provider dashboard</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>5. Hardware and Capacity</h2>
|
<h2>5. Hardware and Capacity</h2>
|
||||||
<p>Regarding the hardware and capacity you provide:</p>
|
<p>Regarding the hardware and capacity you provide:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>You retain ownership of all hardware connected to the ThreeFold Grid</li>
|
<li>You retain ownership of all hardware connected to the Mycelium Grid</li>
|
||||||
<li>You are responsible for the electricity, internet connectivity, and physical security of your hardware</li>
|
<li>You are responsible for the electricity, internet connectivity, and physical security of your hardware</li>
|
||||||
<li>You have the right to disconnect your hardware at any time, subject to any active resource reservations</li>
|
<li>You have the right to disconnect your hardware at any time, subject to any active resource reservations</li>
|
||||||
<li>ThreeFold does not guarantee that your capacity will be utilized by users</li>
|
<li>ThreeFold does not guarantee that your capacity will be utilized by users</li>
|
||||||
@@ -72,16 +72,16 @@
|
|||||||
<li>Slashing penalties may include reputation reduction, temporary suspension, or in severe cases, permanent removal from the marketplace</li>
|
<li>Slashing penalties may include reputation reduction, temporary suspension, or in severe cases, permanent removal from the marketplace</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>7. Termination of Farmer Status</h2>
|
<h2>7. Termination of Resource Provider Status</h2>
|
||||||
<p>You may cease being a Farmer by:</p>
|
<p>You may cease being a Resource Provider by:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Disconnecting your hardware from the ThreeFold Grid</li>
|
<li>Disconnecting your hardware from the Mycelium Grid</li>
|
||||||
<li>Providing notice through your dashboard at least 30 days prior to complete disconnection</li>
|
<li>Providing notice through your dashboard at least 30 days prior to complete disconnection</li>
|
||||||
<li>Ensuring all active resource reservations have been properly concluded or migrated</li>
|
<li>Ensuring all active resource reservations have been properly concluded or migrated</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>8. Liability Limitation</h2>
|
<h2>8. Liability Limitation</h2>
|
||||||
<p>As a Farmer, you acknowledge that:</p>
|
<p>As a Resource Provider, you acknowledge that:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>ThreeFold is not responsible for any damage to your hardware resulting from normal operation</li>
|
<li>ThreeFold is not responsible for any damage to your hardware resulting from normal operation</li>
|
||||||
<li>ThreeFold does not guarantee minimum income or utilization rates for your contributed capacity</li>
|
<li>ThreeFold does not guarantee minimum income or utilization rates for your contributed capacity</li>
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
|
|
||||||
<div class="alert alert-warning mt-5">
|
<div class="alert alert-warning mt-5">
|
||||||
<h5 class="alert-heading">Important Note</h5>
|
<h5 class="alert-heading">Important Note</h5>
|
||||||
<p class="mb-0">These Farmer-specific terms are in addition to the <a href="/terms">General Terms and Conditions</a> that apply to all users of the Project Mycelium. Please ensure you have reviewed both documents.</p>
|
<p class="mb-0">These Resource Provider-specific terms are in addition to the <a href="/terms">General Terms and Conditions</a> that apply to all users of the Project Mycelium. Please ensure you have reviewed both documents.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-5 mb-3">
|
<div class="text-center mt-5 mb-3">
|
@@ -18,9 +18,9 @@
|
|||||||
<div class="card h-100 shadow-sm">
|
<div class="card h-100 shadow-sm">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="bi bi-hdd-rack fs-1 mb-3 text-primary"></i>
|
<i class="bi bi-hdd-rack fs-1 mb-3 text-primary"></i>
|
||||||
<h5 class="card-title">Farmers</h5>
|
<h5 class="card-title">Resource Providers</h5>
|
||||||
<p class="card-text">Resource Providers contributing capacity to the ThreeFold Grid</p>
|
<p class="card-text">Resource Providers contributing capacity to the ThreeFold Grid</p>
|
||||||
<a href="/terms/farmers" class="btn btn-outline-primary mt-3">View Terms</a>
|
<a href="/terms/resource providers" class="btn btn-outline-primary mt-3">View Terms</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,10 +67,10 @@
|
|||||||
<p>The Project Mycelium is a platform that facilitates the exchange of value through the USD Credits system. It connects providers and users, enabling the discovery, acquisition, and management of various resources and services including but not limited to:</p>
|
<p>The Project Mycelium is a platform that facilitates the exchange of value through the USD Credits system. It connects providers and users, enabling the discovery, acquisition, and management of various resources and services including but not limited to:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Compute Resources (Slices)</li>
|
<li>Compute Resources (Slices)</li>
|
||||||
<li>3Nodes</li>
|
<li>Mycelium Nodes</li>
|
||||||
<li>Mycelium Gateways</li>
|
<li>Mycelium Gateways</li>
|
||||||
<li>Bandwidth Providers</li>
|
<li>Bandwidth Providers</li>
|
||||||
<li>Application Solutions</li>
|
<li>Agentic Apps</li>
|
||||||
<li>Human Energy Services</li>
|
<li>Human Energy Services</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@@ -1,21 +1,21 @@
|
|||||||
{% extends "marketplace/layout.html" %}
|
{% extends "marketplace/layout.html" %}
|
||||||
|
|
||||||
{% block title %}Project Mycelium - Application Solutions{% endblock %}
|
{% block title %}Project Mycelium - Agentic Apps{% endblock %}
|
||||||
|
|
||||||
{% block marketplace_content %}
|
{% block marketplace_content %}
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h1>Application Solutions</h1>
|
<h1>Agentic Apps</h1>
|
||||||
<p class="lead">Discover self-healing applications that maintain sovereignty while offering convenience.</p>
|
<p class="lead">Discover self-healing applications that maintain sovereignty while offering convenience.</p>
|
||||||
|
|
||||||
<!-- Application Solutions Introduction -->
|
<!-- Agentic Apps Introduction -->
|
||||||
<div class="alert alert-info mb-4">
|
<div class="alert alert-info mb-4">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="me-3">
|
<div class="me-3">
|
||||||
<i class="bi bi-info-circle-fill fs-3"></i>
|
<i class="bi bi-info-circle-fill fs-3"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="alert-heading">How Application Solutions Work</h5>
|
<h5 class="alert-heading">How Agentic Apps Work</h5>
|
||||||
<p>ThreeFold Application Solutions use a unique model: You provide the compute resources (Slices), while solution providers manage the applications. This ensures you maintain sovereignty over your infrastructure while benefiting from professional management.</p>
|
<p>ThreeFold Agentic Apps use a unique model: You provide the compute resources (Slices), while application providers manage the applications. This ensures you maintain sovereignty over your infrastructure while benefiting from professional management.</p>
|
||||||
<hr>
|
<hr>
|
||||||
<p class="mb-0">When you deploy an application, you'll be guided through the process of allocating the necessary resources if you don't already have them.</p>
|
<p class="mb-0">When you deploy an application, you'll be guided through the process of allocating the necessary resources if you don't already have them.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
<div class="text-center py-5">
|
<div class="text-center py-5">
|
||||||
<i class="bi bi-app display-1 text-muted"></i>
|
<i class="bi bi-app display-1 text-muted"></i>
|
||||||
<h4 class="mt-3">No Applications Available</h4>
|
<h4 class="mt-3">No Applications Available</h4>
|
||||||
<p class="text-muted">Check back later for new application solutions.</p>
|
<p class="text-muted">Check back later for new applications.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -273,7 +273,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h5>3. Deployment & Management</h5>
|
<h5>3. Deployment & Management</h5>
|
||||||
<p>The solution provider handles deployment, updates, and maintenance while you retain full sovereignty.</p>
|
<p>The application provider handles deployment, updates, and maintenance while you retain full sovereignty.</p>
|
||||||
|
|
||||||
<h5>4. Access & Control</h5>
|
<h5>4. Access & Control</h5>
|
||||||
<p>Access your application through secure channels while maintaining complete control over your data and infrastructure.</p>
|
<p>Access your application through secure channels while maintaining complete control over your data and infrastructure.</p>
|
||||||
|
@@ -129,8 +129,8 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="provider-name">
|
<div class="provider-name">
|
||||||
{% if product_data.product.attributes.farmer_email %}
|
{% if product_data.product.attributes.resource_provider_email %}
|
||||||
{{ product_data.product.attributes.farmer_email.value | truncate(length=15) }}
|
{{ product_data.product.attributes.resource_provider_email.value | truncate(length=15) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if product_data.product.provider %}{{ product_data.product.provider }}{% else %}Unknown{% endif %}
|
{% if product_data.product.provider %}{{ product_data.product.provider }}{% else %}Unknown{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="card text-white bg-success mb-3">
|
<div class="card text-white bg-success mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">3Nodes</h5>
|
<h5 class="card-title">Mycelium Nodes</h5>
|
||||||
<p class="card-text">120+ certified nodes</p>
|
<p class="card-text">120+ certified nodes</p>
|
||||||
<a href="/marketplace/3nodes" class="text-white">Browse 3Nodes →</a>
|
<a href="/marketplace/mycelium_nodes" class="text-white">Browse Mycelium Nodes →</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -34,9 +34,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active_section == 'three_nodes' %}active{% endif %}" href="/marketplace/3nodes">
|
<a class="nav-link {% if active_section == 'three_nodes' %}active{% endif %}" href="/marketplace/mycelium_nodes">
|
||||||
<i class="bi bi-hdd-rack me-1"></i>
|
<i class="bi bi-hdd-rack me-1"></i>
|
||||||
3Nodes
|
Mycelium Nodes
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active_section == 'applications' %}active{% endif %}" href="/marketplace/applications">
|
<a class="nav-link {% if active_section == 'applications' %}active{% endif %}" href="/marketplace/applications">
|
||||||
<i class="bi bi-app me-1"></i>
|
<i class="bi bi-app me-1"></i>
|
||||||
Application Solutions
|
Agentic Apps
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
@@ -1,23 +1,23 @@
|
|||||||
{% extends "marketplace/layout.html" %}
|
{% extends "marketplace/layout.html" %}
|
||||||
|
|
||||||
{% block title %}Project Mycelium - 3Nodes Hardware{% endblock %}
|
{% block title %}Project Mycelium - Mycelium Nodes Hardware{% endblock %}
|
||||||
|
|
||||||
{% block marketplace_content %}
|
{% block marketplace_content %}
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h1>3Nodes Hardware</h1>
|
<h1>Mycelium Nodes Hardware</h1>
|
||||||
<p class="lead">Discover certified hardware nodes that power the ThreeFold Grid infrastructure.</p>
|
<p class="lead">Discover certified hardware nodes that power the Mycelium Grid infrastructure.</p>
|
||||||
|
|
||||||
<!-- 3Nodes Introduction -->
|
<!-- Mycelium Nodes Introduction -->
|
||||||
<div class="alert alert-info mb-4">
|
<div class="alert alert-info mb-4">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="me-3">
|
<div class="me-3">
|
||||||
<i class="bi bi-server fs-3"></i>
|
<i class="bi bi-server fs-3"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="alert-heading">What are 3Nodes?</h5>
|
<h5 class="alert-heading">What are Mycelium Nodes?</h5>
|
||||||
<p>3Nodes are the physical hardware units that make up the ThreeFold Grid. These certified servers provide compute, storage, and network capacity to the decentralized internet infrastructure.</p>
|
<p>Mycelium Nodes are the physical hardware units that make up the Mycelium Grid. These certified servers provide compute, storage, and network capacity to the decentralized internet infrastructure.</p>
|
||||||
<hr>
|
<hr>
|
||||||
<p class="mb-0">By purchasing a 3Node, you become a farmer in the ThreeFold ecosystem, earning TFT rewards while contributing to the decentralized internet.</p>
|
<p class="mb-0">By purchasing a Mycelium Node, you become a resource provider in the Mycelium ecosystem, earning MC rewards while contributing to the decentralized internet.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<!-- Filter and Search Section -->
|
<!-- Filter and Search Section -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Filter 3Nodes</h5>
|
<h5 class="card-title">Filter Mycelium Nodes</h5>
|
||||||
<form class="row g-3" method="GET">
|
<form class="row g-3" method="GET">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="locationFilter" class="form-label">Location</label>
|
<label for="locationFilter" class="form-label">Location</label>
|
||||||
@@ -64,13 +64,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 d-flex align-items-end">
|
<div class="col-md-3 d-flex align-items-end">
|
||||||
<button type="submit" class="btn btn-primary me-2">Apply Filters</button>
|
<button type="submit" class="btn btn-primary me-2">Apply Filters</button>
|
||||||
<a href="/marketplace/3nodes" class="btn btn-outline-secondary">Clear</a>
|
<a href="/marketplace/mycelium_nodes" class="btn btn-outline-secondary">Clear</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3Nodes Grid -->
|
<!-- Mycelium Nodes Grid -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% if hardware_products and hardware_products | length > 0 %}
|
{% if hardware_products and hardware_products | length > 0 %}
|
||||||
{% for product_data in hardware_products %}
|
{% for product_data in hardware_products %}
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="alert alert-success small">
|
<div class="alert alert-success small">
|
||||||
<i class="bi bi-currency-dollar me-1"></i>
|
<i class="bi bi-currency-dollar me-1"></i>
|
||||||
<strong>Farming Potential:</strong> Earn TFT rewards by contributing to the ThreeFold Grid
|
<strong>Farming Potential:</strong> Earn MC rewards by contributing to the Mycelium Grid
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -184,7 +184,7 @@
|
|||||||
data-product-name="{{ product_data.product.name }}"
|
data-product-name="{{ product_data.product.name }}"
|
||||||
data-unit-price="{{ product_data.price.display_amount }}"
|
data-unit-price="{{ product_data.price.display_amount }}"
|
||||||
data-currency="{{ product_data.price.display_currency }}"
|
data-currency="{{ product_data.price.display_currency }}"
|
||||||
data-category="3nodes">
|
data-category="mycelium_nodes">
|
||||||
<i class="bi bi-lightning-charge me-1"></i>Buy Now
|
<i class="bi bi-lightning-charge me-1"></i>Buy Now
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary btn-sm add-to-cart-btn"
|
<button class="btn btn-primary btn-sm add-to-cart-btn"
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="text-center py-5">
|
<div class="text-center py-5">
|
||||||
<i class="bi bi-server display-1 text-muted"></i>
|
<i class="bi bi-server display-1 text-muted"></i>
|
||||||
<h4 class="mt-3">No 3Nodes Available</h4>
|
<h4 class="mt-3">No Mycelium Nodes Available</h4>
|
||||||
<p class="text-muted">Check back later for new hardware offerings.</p>
|
<p class="text-muted">Check back later for new hardware offerings.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
{% if pagination and pagination.total_pages > 1 %}
|
{% if pagination and pagination.total_pages > 1 %}
|
||||||
<nav aria-label="3Node pages" class="mt-4">
|
<nav aria-label="Mycelium Node pages" class="mt-4">
|
||||||
<ul class="pagination justify-content-center">
|
<ul class="pagination justify-content-center">
|
||||||
<!-- Previous Page -->
|
<!-- Previous Page -->
|
||||||
<li class="page-item {% if not pagination.has_previous %}disabled{% endif %}">
|
<li class="page-item {% if not pagination.has_previous %}disabled{% endif %}">
|
||||||
@@ -293,7 +293,7 @@
|
|||||||
<!-- Results Info -->
|
<!-- Results Info -->
|
||||||
<div class="text-center text-muted mt-2">
|
<div class="text-center text-muted mt-2">
|
||||||
Showing page {{ pagination.current_page + 1 }} of {{ pagination.total_pages }}
|
Showing page {{ pagination.current_page + 1 }} of {{ pagination.total_pages }}
|
||||||
({{ pagination.total_count }} total 3Nodes)
|
({{ pagination.total_count }} total Mycelium Nodes)
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -302,27 +302,27 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h3 class="card-title">Become a ThreeFold Farmer</h3>
|
<h3 class="card-title">Become a Mycelium Resource Provider</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h5>1. Purchase Your 3Node</h5>
|
<h5>1. Purchase Your Mycelium Node</h5>
|
||||||
<p>Choose from certified hardware options that meet ThreeFold Grid requirements.</p>
|
<p>Choose from certified hardware options that meet Mycelium Grid requirements.</p>
|
||||||
|
|
||||||
<h5>2. Connect to the Grid</h5>
|
<h5>2. Connect to the Grid</h5>
|
||||||
<p>Boot your 3Node with Zero-OS and connect it to the ThreeFold Grid infrastructure.</p>
|
<p>Boot your Mycelium Node with Zero-OS and connect it to the Mycelium Grid infrastructure.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h5>3. Start Earning</h5>
|
<h5>3. Start Earning</h5>
|
||||||
<p>Your 3Node automatically provides capacity to the grid and earns TFT rewards based on utilization.</p>
|
<p>Your Mycelium Node automatically provides capacity to the grid and earns MC rewards based on utilization.</p>
|
||||||
|
|
||||||
<h5>4. Monitor & Maintain</h5>
|
<h5>4. Monitor & Maintain</h5>
|
||||||
<p>Use the ThreeFold Dashboard to monitor your node's performance and earnings.</p>
|
<p>Use the Mycelium Dashboard to monitor your node's performance and earnings.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-info mt-3">
|
<div class="alert alert-info mt-3">
|
||||||
<i class="bi bi-info-circle me-2"></i>
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
<strong>ROI Potential:</strong> 3Node farmers typically see return on investment within 2-4 years, depending on grid utilization and TFT price.
|
<strong>ROI Potential:</strong> Mycelium Node resource providers typically see return on investment within 2-4 years, depending on grid utilization and MC price.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -332,14 +332,14 @@
|
|||||||
<!-- Node Types -->
|
<!-- Node Types -->
|
||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h3 class="mb-4">3Node Types</h3>
|
<h3 class="mb-4">Mycelium Node Types</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="bi bi-tools fs-1 text-primary mb-3"></i>
|
<i class="bi bi-tools fs-1 text-primary mb-3"></i>
|
||||||
<h5>DIY Nodes</h5>
|
<h5>DIY Nodes</h5>
|
||||||
<p class="small text-muted">Build your own 3Node using compatible hardware. Most cost-effective option for technical users.</p>
|
<p class="small text-muted">Build your own Mycelium Node using compatible hardware. Most cost-effective option for technical users.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -348,7 +348,7 @@
|
|||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="bi bi-award fs-1 text-primary mb-3"></i>
|
<i class="bi bi-award fs-1 text-primary mb-3"></i>
|
||||||
<h5>Certified Nodes</h5>
|
<h5>Certified Nodes</h5>
|
||||||
<p class="small text-muted">Pre-built and certified hardware that's guaranteed to work with the ThreeFold Grid.</p>
|
<p class="small text-muted">Pre-built and certified hardware that's guaranteed to work with the Mycelium Grid.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@@ -24,7 +24,7 @@
|
|||||||
<strong>Node:</strong> {{ slice.node_id }}
|
<strong>Node:</strong> {{ slice.node_id }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<strong>Farmer:</strong> {{ slice.farmer_email }}
|
<strong>Resource Provider:</strong> {{ slice.resource_provider_email }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<strong>Specifications:</strong>
|
<strong>Specifications:</strong>
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form id="sliceRentalForm" method="POST" action="/marketplace/slice/rent">
|
<form id="sliceRentalForm" method="POST" action="/marketplace/slice/rent">
|
||||||
<!-- Hidden fields -->
|
<!-- Hidden fields -->
|
||||||
<input type="hidden" name="farmer_email" value="{{ farmer_email }}">
|
<input type="hidden" name="resource_provider_email" value="{{ resource_provider_email }}">
|
||||||
<input type="hidden" name="node_id" value="{{ node_id }}">
|
<input type="hidden" name="node_id" value="{{ node_id }}">
|
||||||
<input type="hidden" name="combination_id" value="{{ combination_id }}">
|
<input type="hidden" name="combination_id" value="{{ combination_id }}">
|
||||||
|
|
||||||
|
@@ -82,11 +82,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3Nodes Section -->
|
<!-- Mycelium Nodes Section -->
|
||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="dashboard-section">
|
<div class="dashboard-section">
|
||||||
<h3>3Nodes Statistics</h3>
|
<h3>Mycelium Nodes Statistics</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card mb-4 h-100">
|
<div class="card mb-4 h-100">
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card mb-4 h-100">
|
<div class="card mb-4 h-100">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5>3Node Certification Rate</h5>
|
<h5>Mycelium Node Certification Rate</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body d-flex justify-content-center align-items-center">
|
<div class="card-body d-flex justify-content-center align-items-center">
|
||||||
<canvas id="nodeCertificationChart" width="400" height="300"></canvas>
|
<canvas id="nodeCertificationChart" width="400" height="300"></canvas>
|
||||||
|
@@ -104,7 +104,7 @@ async fn test_complete_marketplace_categories_ux_workflow() {
|
|||||||
|
|
||||||
// Simulate node browsing and information
|
// Simulate node browsing and information
|
||||||
let node_categories = vec![
|
let node_categories = vec![
|
||||||
("3node", "Dedicated ThreeFold node for farming"),
|
("mycelium_node", "Dedicated Mycelium node for farming"),
|
||||||
("titan", "High-performance enterprise node"),
|
("titan", "High-performance enterprise node"),
|
||||||
("quantum", "Quantum-safe storage node"),
|
("quantum", "Quantum-safe storage node"),
|
||||||
];
|
];
|
||||||
|
@@ -42,12 +42,12 @@ async fn test_complete_provider_dashboards_ux_workflow() {
|
|||||||
|
|
||||||
// Test users for different provider types
|
// Test users for different provider types
|
||||||
let farmer_email = "farmer_dashboard_test@example.com";
|
let farmer_email = "farmer_dashboard_test@example.com";
|
||||||
let app_provider_email = "app_provider_test@example.com";
|
let application_provider_email = "application_provider_test@example.com";
|
||||||
let service_provider_email = "service_provider_test@example.com";
|
let service_provider_email = "service_provider_test@example.com";
|
||||||
|
|
||||||
// Clean up any existing test data
|
// Clean up any existing test data
|
||||||
cleanup_test_user_data(farmer_email);
|
cleanup_test_user_data(farmer_email);
|
||||||
cleanup_test_user_data(app_provider_email);
|
cleanup_test_user_data(application_provider_email);
|
||||||
cleanup_test_user_data(service_provider_email);
|
cleanup_test_user_data(service_provider_email);
|
||||||
|
|
||||||
// Initialize services
|
// Initialize services
|
||||||
@@ -133,8 +133,8 @@ async fn test_complete_provider_dashboards_ux_workflow() {
|
|||||||
|
|
||||||
println!("✅ Farmer Dashboard: WORKING - Node management, capacity planning, earnings tracking");
|
println!("✅ Farmer Dashboard: WORKING - Node management, capacity planning, earnings tracking");
|
||||||
|
|
||||||
// Step 3: Test Application Provider Dashboard (/dashboard/app-provider)
|
// Step 3: Test Application Provider Dashboard (/dashboard/application-provider)
|
||||||
println!("\n🔧 Step 3: Test Application Provider Dashboard (/dashboard/app-provider)");
|
println!("\n🔧 Step 3: Test Application Provider Dashboard (/dashboard/application-provider)");
|
||||||
|
|
||||||
// Create test app provider profile
|
// Create test app provider profile
|
||||||
let published_apps = vec![
|
let published_apps = vec![
|
||||||
@@ -144,7 +144,7 @@ async fn test_complete_provider_dashboards_ux_workflow() {
|
|||||||
("backup-solution", "Automated Backup Tool", 34, 4.1, "pending"),
|
("backup-solution", "Automated Backup Tool", 34, 4.1, "pending"),
|
||||||
];
|
];
|
||||||
|
|
||||||
println!(" 📱 Application Provider: {}", app_provider_email);
|
println!(" 📱 Application Provider: {}", application_provider_email);
|
||||||
println!(" 📊 Published Applications:");
|
println!(" 📊 Published Applications:");
|
||||||
|
|
||||||
let mut total_installs = 0;
|
let mut total_installs = 0;
|
||||||
@@ -282,7 +282,7 @@ async fn test_complete_provider_dashboards_ux_workflow() {
|
|||||||
|
|
||||||
// Final cleanup
|
// Final cleanup
|
||||||
cleanup_test_user_data(farmer_email);
|
cleanup_test_user_data(farmer_email);
|
||||||
cleanup_test_user_data(app_provider_email);
|
cleanup_test_user_data(application_provider_email);
|
||||||
cleanup_test_user_data(service_provider_email);
|
cleanup_test_user_data(service_provider_email);
|
||||||
|
|
||||||
// Final verification
|
// Final verification
|
||||||
@@ -321,7 +321,7 @@ async fn test_provider_dashboards_performance() {
|
|||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
// Simulate dashboard data loading
|
// Simulate dashboard data loading
|
||||||
let provider_types = vec!["farmer", "app_provider", "service_provider"];
|
let provider_types = vec!["farmer", "application_provider", "service_provider"];
|
||||||
|
|
||||||
for provider_type in provider_types {
|
for provider_type in provider_types {
|
||||||
// Simulate dashboard page load
|
// Simulate dashboard page load
|
||||||
|
@@ -165,7 +165,7 @@ impl TestDataManager {
|
|||||||
/// Create test marketplace data
|
/// Create test marketplace data
|
||||||
fn create_test_marketplace_data(personas: &HashMap<UserRole, TestPersona>) -> TestMarketplaceData {
|
fn create_test_marketplace_data(personas: &HashMap<UserRole, TestPersona>) -> TestMarketplaceData {
|
||||||
let farmer_email = personas.get(&UserRole::Farmer).unwrap().email.clone();
|
let farmer_email = personas.get(&UserRole::Farmer).unwrap().email.clone();
|
||||||
let app_provider_email = personas.get(&UserRole::AppProvider).unwrap().email.clone();
|
let application_provider_email = personas.get(&UserRole::AppProvider).unwrap().email.clone();
|
||||||
let service_provider_email = personas.get(&UserRole::ServiceProvider).unwrap().email.clone();
|
let service_provider_email = personas.get(&UserRole::ServiceProvider).unwrap().email.clone();
|
||||||
|
|
||||||
TestMarketplaceData {
|
TestMarketplaceData {
|
||||||
@@ -186,7 +186,7 @@ impl TestDataManager {
|
|||||||
price: 25.0,
|
price: 25.0,
|
||||||
currency: "TFC".to_string(),
|
currency: "TFC".to_string(),
|
||||||
description: "Test application for UX testing".to_string(),
|
description: "Test application for UX testing".to_string(),
|
||||||
provider_email: app_provider_email.clone(),
|
provider_email: application_provider_email.clone(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
services: vec![
|
services: vec![
|
||||||
|
@@ -86,7 +86,7 @@ async fn test_anonymous_marketplace_browsing() -> Result<(), Box<dyn std::error:
|
|||||||
// Test each marketplace category
|
// Test each marketplace category
|
||||||
let categories = vec![
|
let categories = vec![
|
||||||
("/marketplace/compute", "compute resources"),
|
("/marketplace/compute", "compute resources"),
|
||||||
("/marketplace/3nodes", "3nodes"),
|
("/marketplace/mycelium_nodes", "mycelium_nodes"),
|
||||||
("/marketplace/gateways", "gateways"),
|
("/marketplace/gateways", "gateways"),
|
||||||
("/marketplace/applications", "applications"),
|
("/marketplace/applications", "applications"),
|
||||||
("/marketplace/services", "services"),
|
("/marketplace/services", "services"),
|
||||||
@@ -106,9 +106,9 @@ async fn test_anonymous_marketplace_browsing() -> Result<(), Box<dyn std::error:
|
|||||||
// Should show VM/slice options
|
// Should show VM/slice options
|
||||||
helper.browser.wait_for_element(".compute-resources, .vm-options, .slice-list").await.ok();
|
helper.browser.wait_for_element(".compute-resources, .vm-options, .slice-list").await.ok();
|
||||||
}
|
}
|
||||||
"/marketplace/3nodes" => {
|
"/marketplace/mycelium_nodes" => {
|
||||||
// Should show node listings
|
// Should show node listings
|
||||||
helper.browser.wait_for_element(".node-listings, .server-list, .3node-grid").await.ok();
|
helper.browser.wait_for_element(".node-listings, .server-list, .mycelium-node-grid").await.ok();
|
||||||
}
|
}
|
||||||
"/marketplace/gateways" => {
|
"/marketplace/gateways" => {
|
||||||
// Should show gateway services
|
// Should show gateway services
|
||||||
|
@@ -1,7 +1,26 @@
|
|||||||
{
|
{
|
||||||
"user_email": "user123@example.com",
|
"user_email": "user123@example.com",
|
||||||
"wallet_balance_usd": 0.0,
|
"wallet_balance_usd": 12.0,
|
||||||
"transactions": [],
|
"transactions": [
|
||||||
|
{
|
||||||
|
"id": "5590075e-2ec3-4ece-b64e-2ab2bd8a4a92",
|
||||||
|
"user_id": "user123@example.com",
|
||||||
|
"transaction_type": {
|
||||||
|
"Purchase": {
|
||||||
|
"product_id": "credits"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"amount": 12.0,
|
||||||
|
"currency": "USD",
|
||||||
|
"exchange_rate_usd": 1.0,
|
||||||
|
"amount_usd": 12.0,
|
||||||
|
"description": "Credits purchase via credit_card",
|
||||||
|
"reference_id": "5590075e-2ec3-4ece-b64e-2ab2bd8a4a92",
|
||||||
|
"metadata": null,
|
||||||
|
"timestamp": "2025-09-08T17:16:29.777650952Z",
|
||||||
|
"status": "Completed"
|
||||||
|
}
|
||||||
|
],
|
||||||
"staked_amount_usd": 0.0,
|
"staked_amount_usd": 0.0,
|
||||||
"pool_positions": {},
|
"pool_positions": {},
|
||||||
"name": "user123",
|
"name": "user123",
|
||||||
@@ -14,20 +33,69 @@
|
|||||||
"availability": null,
|
"availability": null,
|
||||||
"slas": [],
|
"slas": [],
|
||||||
"apps": [],
|
"apps": [],
|
||||||
"app_deployments": [],
|
"application_deployments": [],
|
||||||
"deleted": null,
|
"deleted": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"deletion_reason": null,
|
"deletion_reason": null,
|
||||||
"nodes": [],
|
"nodes": [],
|
||||||
"farmer_earnings": [],
|
"resource_provider_earnings": [],
|
||||||
"farmer_settings": null,
|
"resource_provider_settings": null,
|
||||||
"slice_products": [],
|
"slice_products": [],
|
||||||
"user_activities": [],
|
"user_activities": [
|
||||||
"user_preferences": null,
|
{
|
||||||
|
"id": "1db56139-8a5f-4e32-a235-c0f1bb4ab357",
|
||||||
|
"user_email": "user123@example.com",
|
||||||
|
"activity_type": "WalletTransaction",
|
||||||
|
"description": "Purchased $12 credits via credit_card",
|
||||||
|
"metadata": null,
|
||||||
|
"timestamp": "2025-09-08T17:16:29.777958164Z",
|
||||||
|
"ip_address": null,
|
||||||
|
"user_agent": null,
|
||||||
|
"session_id": null,
|
||||||
|
"importance": "Medium",
|
||||||
|
"category": "Wallet"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"user_preferences": {
|
||||||
|
"theme": "light",
|
||||||
|
"language": "en",
|
||||||
|
"currency_display": "symbol",
|
||||||
|
"email_notifications": true,
|
||||||
|
"push_notifications": true,
|
||||||
|
"marketing_emails": false,
|
||||||
|
"data_sharing": false,
|
||||||
|
"preferred_currency": "USD",
|
||||||
|
"preferred_language": "en",
|
||||||
|
"timezone": "UTC",
|
||||||
|
"notification_settings": {
|
||||||
|
"email_enabled": true,
|
||||||
|
"push_enabled": true,
|
||||||
|
"sms_enabled": false,
|
||||||
|
"slack_webhook": null,
|
||||||
|
"discord_webhook": null,
|
||||||
|
"enabled": true,
|
||||||
|
"push": true,
|
||||||
|
"node_offline_alerts": true,
|
||||||
|
"maintenance_reminders": true,
|
||||||
|
"earnings_reports": true
|
||||||
|
},
|
||||||
|
"privacy_settings": {
|
||||||
|
"profile_public": false,
|
||||||
|
"email_public": false,
|
||||||
|
"activity_public": false,
|
||||||
|
"stats_public": false,
|
||||||
|
"profile_visibility": "private",
|
||||||
|
"marketing_emails": false,
|
||||||
|
"data_sharing": false,
|
||||||
|
"activity_tracking": true
|
||||||
|
},
|
||||||
|
"dashboard_layout": "default",
|
||||||
|
"last_payment_method": "credit_card"
|
||||||
|
},
|
||||||
"usage_statistics": null,
|
"usage_statistics": null,
|
||||||
"orders": [],
|
"orders": [],
|
||||||
"active_product_rentals": [],
|
"active_product_rentals": [],
|
||||||
"farmer_rental_earnings": [],
|
"resource_provider_rental_earnings": [],
|
||||||
"node_rentals": [],
|
"node_rentals": [],
|
||||||
"node_groups": [],
|
"node_groups": [],
|
||||||
"slice_rentals": [],
|
"slice_rentals": [],
|
||||||
|
Reference in New Issue
Block a user