use std::sync::Arc;
use systemprompt_logging::CliService;
use systemprompt_models::auth::UserRole;
use systemprompt_models::services::{SystemAdmin, SystemAdminConfig};
use systemprompt_models::{AppPaths, Config, ContentConfigRaw};
use systemprompt_users::UserService;
use crate::error::{RuntimeError, RuntimeResult};
#[cfg(feature = "geolocation")]
use systemprompt_analytics::GeoIpReader;
#[cfg(feature = "geolocation")]
pub fn load_geoip_database(config: &Config, show_warnings: bool) -> Option<GeoIpReader> {
let Some(geoip_path) = &config.geoip_database_path else {
if show_warnings {
CliService::warning(
"GeoIP database not configured - geographic data will not be available",
);
CliService::info(" To enable geographic data:");
CliService::info(" 1. Download MaxMind GeoLite2-City database from: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data");
CliService::info(
" 2. Add paths.geoip_database to your profile pointing to the .mmdb file",
);
}
return None;
};
match maxminddb::Reader::open_readfile(geoip_path) {
Ok(reader) => Some(Arc::new(reader)),
Err(e) => {
if show_warnings {
CliService::warning(&format!(
"Could not load GeoIP database from {geoip_path}: {e}"
));
CliService::info(" Geographic data (country/region/city) will not be available.");
CliService::info(
" To fix: Ensure the path is correct and the file is a valid MaxMind .mmdb \
database",
);
}
None
},
}
}
#[cfg(not(feature = "geolocation"))]
pub fn load_geoip_database(
_config: &Config,
_show_warnings: bool,
) -> Option<systemprompt_analytics::GeoIpReader> {
None
}
pub async fn resolve_system_admin(
cfg: &SystemAdminConfig,
users: &UserService,
) -> RuntimeResult<SystemAdmin> {
let user = users.find_by_name(&cfg.username).await?.ok_or_else(|| {
RuntimeError::SystemAdminNotFound {
username: cfg.username.clone(),
}
})?;
if !user.is_active() {
return Err(RuntimeError::SystemAdminInactive {
username: cfg.username.clone(),
});
}
let admin_role = UserRole::Admin.as_str();
if !user.roles.iter().any(|r| r == admin_role) {
return Err(RuntimeError::SystemAdminMissingRole {
username: cfg.username.clone(),
});
}
Ok(SystemAdmin::new(user.id, user.name))
}
pub fn load_content_config(config: &Config, app_paths: &AppPaths) -> Option<Arc<ContentConfigRaw>> {
let content_config_path = app_paths.system().content_config().to_path_buf();
if !content_config_path.exists() {
CliService::warning(&format!(
"Content config not found at: {}",
content_config_path.display()
));
CliService::info(" Landing page detection will not be available.");
return None;
}
let yaml_content = match std::fs::read_to_string(&content_config_path) {
Ok(c) => c,
Err(e) => {
CliService::warning(&format!(
"Could not read content config from {}: {}",
content_config_path.display(),
e
));
CliService::info(" Landing page detection will not be available.");
return None;
},
};
match serde_yaml::from_str::<ContentConfigRaw>(&yaml_content) {
Ok(mut content_cfg) => {
let base_url = config.api_external_url.trim_end_matches('/');
content_cfg.metadata.structured_data.organization.url = base_url.to_string();
let logo = &content_cfg.metadata.structured_data.organization.logo;
if logo.starts_with('/') {
content_cfg.metadata.structured_data.organization.logo =
format!("{base_url}{logo}");
}
Some(Arc::new(content_cfg))
},
Err(e) => {
CliService::warning(&format!(
"Could not parse content config from {}: {}",
content_config_path.display(),
e
));
CliService::info(" Landing page detection will not be available.");
None
},
}
}