use anyhow::Result;
use firecloud_storage::QuotaManager;
use std::path::PathBuf;
use tracing::info;
use crate::format::format_bytes;
pub async fn init(data_dir: PathBuf, quota_gb: Option<u64>) -> Result<()> {
println!("🔧 FireCloud Storage Provider Setup");
println!();
let provide_storage = if let Some(_) = quota_gb {
true
} else {
println!("Do you want to provide storage space for other users?");
println!("(You can earn credits and help the network)");
print!("Provide storage? [Y/n]: ");
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
let answer = input.trim().to_lowercase();
answer.is_empty() || answer == "y" || answer == "yes"
};
if !provide_storage {
println!("✓ Storage provider disabled");
println!(" You can still upload and download files normally.");
return Ok(());
}
let quota_bytes = if let Some(gb) = quota_gb {
gb * 1024 * 1024 * 1024
} else {
println!();
println!("How much storage space do you want to provide?");
println!(" 1) 10 GB (recommended for laptops)");
println!(" 2) 50 GB (recommended for desktops)");
println!(" 3) 100 GB (recommended for servers)");
println!(" 4) Custom");
print!("Choice [1-4]: ");
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
let choice = input.trim();
match choice {
"1" | "" => 10 * 1024 * 1024 * 1024,
"2" => 50 * 1024 * 1024 * 1024,
"3" => 100 * 1024 * 1024 * 1024,
"4" => {
print!("Enter storage quota in GB: ");
let mut gb_input = String::new();
std::io::stdin().read_line(&mut gb_input)?;
let gb: u64 = gb_input.trim().parse()
.unwrap_or(10);
gb * 1024 * 1024 * 1024
}
_ => 10 * 1024 * 1024 * 1024,
}
};
let quota_dir = data_dir.join("quota");
std::fs::create_dir_all("a_dir)?;
let quota_manager = QuotaManager::open("a_dir)?;
quota_manager.set_quota_limit(quota_bytes)?;
println!();
println!("✅ Storage provider configured!");
println!(" Quota: {}", format_bytes(quota_bytes));
println!(" Location: {}", data_dir.display());
println!();
println!("Next steps:");
println!(" 1. Start your node: firecloud node");
println!(" 2. Your node will announce storage availability to the network");
println!(" 3. Earn credits by storing chunks for other users!");
Ok(())
}
pub async fn stats(data_dir: PathBuf) -> Result<()> {
let quota_dir = data_dir.join("quota");
std::fs::create_dir_all("a_dir)?;
let quota_manager = QuotaManager::open("a_dir)?;
let quota_stats = quota_manager.get_quota()?;
let used = quota_stats.total_bytes;
let quota = quota_stats.quota_limit;
let available = if quota > used { quota - used } else { 0 };
println!("💾 Storage Statistics");
println!();
println!("Quota: {}", format_bytes(quota));
println!("Used: {} ({:.1}%)",
format_bytes(used),
if quota > 0 {
(used as f64 / quota as f64) * 100.0
} else {
0.0
}
);
println!("Available: {}", format_bytes(available));
println!();
if quota_stats.total_chunks > 0 {
println!("Chunks stored: {}", quota_stats.total_chunks);
} else {
println!("No files stored yet.");
println!("Upload files with: firecloud upload <file>");
}
Ok(())
}
pub async fn list_providers(_data_dir: PathBuf) -> Result<()> {
use firecloud_net::{FireCloudNode, NodeConfig, NodeEvent};
use std::time::Duration;
println!("🔍 Searching for storage providers in the network...");
println!();
let config = NodeConfig {
port: 0, enable_mdns: true,
bootstrap_peers: Vec::new(),
bootstrap_relays: vec![],
};
let mut node = FireCloudNode::new(config).await?;
println!("📡 Node started: {}", node.local_peer_id());
println!("🔍 Discovering peers...");
let mut discovered_peers = Vec::new();
let discovery_timeout = tokio::time::Instant::now() + Duration::from_secs(5);
while tokio::time::Instant::now() < discovery_timeout {
tokio::select! {
_ = tokio::time::sleep(Duration::from_millis(100)) => {}
event = node.poll_event() => {
if let Some(event) = event {
match event {
NodeEvent::PeerDiscovered(peer_id) => {
if peer_id != node.local_peer_id() {
discovered_peers.push(peer_id);
}
}
NodeEvent::Listening(addr) => {
println!("👂 Listening on {}", addr);
}
_ => {}
}
}
}
}
}
if discovered_peers.is_empty() {
println!("❌ No peers found on the network.");
println!();
println!("To see storage providers:");
println!(" 1. Start a node on another device: firecloud node --port 4001");
println!(" 2. Configure it as a storage provider: firecloud storage init --quota 10GB");
println!();
println!("The provider will automatically announce itself via DHT.");
return Ok(());
}
println!("✅ Found {} peer(s)", discovered_peers.len());
println!();
println!("🔍 Querying DHT for storage providers...");
let providers = node.find_storage_providers(10).await?;
if providers.is_empty() {
println!("❌ No storage providers found in the DHT.");
println!();
println!("To become a storage provider:");
println!(" 1. firecloud storage init --quota 10GB");
println!(" 2. firecloud node --port 4001");
println!();
println!("Your node will automatically announce storage availability.");
} else {
println!("✅ Found {} storage provider(s):", providers.len());
println!();
println!("{:<20} {:<15} {:<10}",
"Peer ID", "Available", "Reputation");
println!("{}", "─".repeat(50));
for provider in &providers {
let peer_id_short = format!("{}", provider.peer_id)
.chars()
.take(16)
.collect::<String>();
let available = crate::format::format_bytes(provider.available_space);
let reputation = format!("{:.1}", provider.reputation);
println!("{:<20} {:<15} {:<10}",
peer_id_short, available, reputation);
if !provider.listen_addrs.is_empty() {
let addr_str = provider.listen_addrs.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(", ");
println!(" Addresses: {}", addr_str);
}
}
println!();
println!("💡 Tip: Upload files to distribute chunks across these providers:");
println!(" firecloud upload <file>");
}
Ok(())
}