use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
pub fn generate_local_config(nap_home: &Path) -> Result<ConfigFiles> {
let lore_config_dir = nap_home.join("lore").join("config");
let local_toml_path = lore_config_dir.join("local.toml");
fs::create_dir_all(&lore_config_dir).context("Failed to create Lore config directory")?;
if local_toml_path.exists() {
tracing::info!("Lore config already exists at {:?}", local_toml_path);
return Ok(ConfigFiles {
config_path: local_toml_path,
config_dir: lore_config_dir,
});
}
tracing::info!("Generating Lore server configuration");
let config = generate_config_toml(nap_home);
fs::write(&local_toml_path, config).context("Failed to write Lore configuration file")?;
tracing::info!("Lore configuration generated at {:?}", local_toml_path);
Ok(ConfigFiles {
config_path: local_toml_path,
config_dir: lore_config_dir,
})
}
fn generate_config_toml(nap_home: &Path) -> String {
let immutable_path = nap_home.join("lore").join("store").join("immutable");
let mutable_path = nap_home.join("lore").join("store").join("mutable");
let _cert_path = nap_home.join("lore").join("certs").join("cert.pem");
let _key_path = nap_home.join("lore").join("certs").join("key.pem");
format!(
r#"
# =============================================================================
# Lore Server Configuration (Generated by NAP SDK)
# =============================================================================
# This configuration is automatically generated by the NAP SDK.
# Manual modifications may be overwritten.
[server]
connection_close_timeout_seconds = 5
runtime_shutdown_timeout_seconds = 25
# Public facing QUIC server settings
[server.quic]
enabled = true
host = "0.0.0.0"
port = 41337
verify_client_certs = false
idle_timeout = 30_000
keep_alive = 500
max_bidi_streams = 8
num_listeners = 10
transport_bits_per_second = 1_073_741_824 # 1 gbit/s
transport_rtt = 100 # 100 ms
handler_timeout_seconds = 50
# gRPC server settings
[server.grpc]
enabled = true
host = "0.0.0.0"
port = 41337
request_handler_timeout_seconds = 50
verify_client_certs = false
# HTTP server settings
[server.http]
enabled = true
host = "0.0.0.0"
port = 41339
max_file_size = 10_485_760 # 10MB
request_timeout_seconds = 300
request_body_timeout_seconds = 3600
available_interval_seconds = 30
available_timeout_seconds = 5
store_health_check = false
# =============================================================================
# Store Configuration
# =============================================================================
[immutable_store]
mode = "local"
[immutable_store.local]
path = "{}"
flush_delay_seconds = 10
[mutable_store]
mode = "local"
[mutable_store.local]
path = "{}"
flush_delay_seconds = 10
[lock_store]
mode = "local"
# =============================================================================
# Tokio Runtime Configuration
# =============================================================================
[tokio]
max_blocking_threads = 512
# =============================================================================
# Telemetry Configuration
# =============================================================================
[telemetry.logger]
enable_otlp = false
format = "json"
output = "stdout"
[telemetry.metrics]
export_interval_millis = 30000
sample_interval_millis = 10000
[telemetry.traces]
sample_rate = 0.05
sample_rate_low_tier = 0.001
# =============================================================================
# Notification Configuration
# =============================================================================
[notification]
mode = "local"
# =============================================================================
# Other Features Configuration
# =============================================================================
[feature]
history_step_size = 100
"#,
immutable_path.display(),
mutable_path.display()
)
}
#[derive(Debug, Clone)]
pub struct ConfigFiles {
pub config_path: std::path::PathBuf,
pub config_dir: std::path::PathBuf,
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_generate_local_config() {
let temp_dir = TempDir::new().unwrap();
let nap_home = temp_dir.path();
let files = generate_local_config(nap_home).unwrap();
assert!(files.config_path.exists());
assert!(files.config_dir.exists());
let content = fs::read_to_string(&files.config_path).unwrap();
assert!(content.contains("[server.quic]"));
assert!(content.contains("port = 41337"));
assert!(content.contains("[immutable_store.local]"));
assert!(content.contains("[mutable_store.local]"));
let files2 = generate_local_config(nap_home).unwrap();
assert_eq!(files.config_path, files2.config_path);
}
#[test]
fn test_config_toml_content() {
let temp_dir = TempDir::new().unwrap();
let nap_home = temp_dir.path();
let config = generate_config_toml(nap_home);
assert!(config.contains("[server.quic]"));
assert!(config.contains("port = 41337"));
assert!(config.contains("[server.grpc]"));
assert!(config.contains("[server.http]"));
assert!(config.contains("port = 41339"));
assert!(config.contains("[immutable_store]"));
assert!(config.contains("mode = \"local\""));
assert!(config.contains("[mutable_store]"));
assert!(config.contains("[lock_store]"));
assert!(config.contains("[telemetry.logger]"));
assert!(config.contains("[telemetry.metrics]"));
assert!(config.contains("[notification]"));
assert!(config.contains("[feature]"));
assert!(config.contains(&nap_home.display().to_string()));
}
}