Skip to content
Clone
563bb99
77 lines 2.4 KB Rust
use serde::Deserialize;
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct ImportConfig {
pub enabled: bool,
pub token_secret: String,
pub allowed_hosts: Vec<String>,
pub allow_insecure_tls: bool,
pub max_concurrent_jobs: usize,
pub clone_timeout_secs: u64,
pub api_timeout_secs: u64,
pub poll_interval_secs: u64,
pub max_attempts: i32,
pub max_repo_bytes: u64,
pub stale_lock_secs: u64,
}
impl Default for ImportConfig {
fn default() -> Self {
Self {
enabled: false,
token_secret: String::new(),
allowed_hosts: Vec::new(),
allow_insecure_tls: false,
max_concurrent_jobs: 2,
clone_timeout_secs: 3600,
api_timeout_secs: 30,
poll_interval_secs: 3,
max_attempts: 5,
max_repo_bytes: 0,
stale_lock_secs: 1800,
}
}
}
impl ImportConfig {
pub fn validate(&self) -> Result<(), String> {
if !self.enabled {
return Ok(());
}
if self.token_secret.trim().is_empty() {
return Err("import.token_secret must not be empty when import is enabled".to_string());
}
if self.max_concurrent_jobs == 0
|| self.clone_timeout_secs == 0
|| self.api_timeout_secs == 0
|| self.poll_interval_secs == 0
|| self.stale_lock_secs == 0
{
return Err("import limits and intervals must be greater than zero".to_string());
}
if self.max_attempts <= 0 {
return Err("import.max_attempts must be greater than zero".to_string());
}
for host in &self.allowed_hosts {
let normalized = host.trim().to_ascii_lowercase();
if normalized.is_empty() || normalized.contains("://") || normalized.contains('/') {
return Err(format!(
"import.allowed_hosts entry `{host}` must be a bare host name"
));
}
}
Ok(())
}
pub fn host_allowed(&self, host: &str) -> bool {
let host = host.trim().trim_end_matches('.').to_ascii_lowercase();
if host.is_empty() {
return false;
}
self.allowed_hosts.iter().any(|entry| {
let entry = entry.trim().trim_end_matches('.').to_ascii_lowercase();
!entry.is_empty() && (host == entry || host.ends_with(&format!(".{entry}")))
})
}
}