Implement comprehensive config validation with graceful error handling
- Replace panic-prone config loading with detailed error messages - Validate config file existence, TOML syntax, required fields - Check filesystem access for root directory and certificate files - Provide actionable error messages explaining how to fix each issue - Exit gracefully with clear guidance instead of cryptic panics - Maintain backward compatibility for valid configurations
This commit is contained in:
parent
b9380483d2
commit
b13c46806c
1 changed files with 94 additions and 16 deletions
110
src/main.rs
110
src/main.rs
|
|
@ -48,27 +48,105 @@ async fn main() {
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
let config_path = args.config.as_deref().unwrap_or("/etc/pollux/config.toml");
|
let config_path = args.config.as_deref().unwrap_or("/etc/pollux/config.toml");
|
||||||
let config = config::load_config(config_path).unwrap_or(config::Config {
|
// Check if config file exists
|
||||||
root: None,
|
if !std::path::Path::new(&config_path).exists() {
|
||||||
cert: None,
|
eprintln!("Error: Config file '{}' not found", config_path);
|
||||||
key: None,
|
eprintln!("Create the config file with required fields:");
|
||||||
bind_host: None,
|
eprintln!(" root = \"/path/to/gemini/content\"");
|
||||||
hostname: None,
|
eprintln!(" cert = \"/path/to/certificate.pem\"");
|
||||||
port: None,
|
eprintln!(" key = \"/path/to/private-key.pem\"");
|
||||||
log_level: None,
|
eprintln!(" bind_host = \"0.0.0.0\"");
|
||||||
max_concurrent_requests: None,
|
eprintln!(" hostname = \"your.domain.com\"");
|
||||||
});
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize logging
|
// Load and parse config
|
||||||
|
let config = match config::load_config(&config_path) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: Failed to parse config file '{}': {}", config_path, e);
|
||||||
|
eprintln!("Check the TOML syntax and ensure all values are properly quoted.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if config.root.is_none() {
|
||||||
|
eprintln!("Error: 'root' field is required in config file");
|
||||||
|
eprintln!("Add: root = \"/path/to/gemini/content\"");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.cert.is_none() {
|
||||||
|
eprintln!("Error: 'cert' field is required in config file");
|
||||||
|
eprintln!("Add: cert = \"/path/to/certificate.pem\"");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.key.is_none() {
|
||||||
|
eprintln!("Error: 'key' field is required in config file");
|
||||||
|
eprintln!("Add: key = \"/path/to/private-key.pem\"");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.hostname.is_none() {
|
||||||
|
eprintln!("Error: 'hostname' field is required in config file");
|
||||||
|
eprintln!("Add: hostname = \"your.domain.com\"");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate filesystem
|
||||||
|
let root_path = std::path::Path::new(config.root.as_ref().unwrap());
|
||||||
|
if !root_path.exists() {
|
||||||
|
eprintln!("Error: Root directory '{}' does not exist", config.root.as_ref().unwrap());
|
||||||
|
eprintln!("Create the directory and add your Gemini files (.gmi, .txt, images)");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
if !root_path.is_dir() {
|
||||||
|
eprintln!("Error: Root path '{}' is not a directory", config.root.as_ref().unwrap());
|
||||||
|
eprintln!("The 'root' field must point to a directory containing your content");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
if let Err(e) = std::fs::read_dir(root_path) {
|
||||||
|
eprintln!("Error: Cannot read root directory '{}': {}", config.root.as_ref().unwrap(), e);
|
||||||
|
eprintln!("Ensure the directory exists and the server user has read permission");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cert_path = std::path::Path::new(config.cert.as_ref().unwrap());
|
||||||
|
if !cert_path.exists() {
|
||||||
|
eprintln!("Error: Certificate file '{}' does not exist", config.cert.as_ref().unwrap());
|
||||||
|
eprintln!("Generate or obtain TLS certificates for your domain");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
if let Err(e) = std::fs::File::open(cert_path) {
|
||||||
|
eprintln!("Error: Cannot read certificate file '{}': {}", config.cert.as_ref().unwrap(), e);
|
||||||
|
eprintln!("Ensure the file exists and the server user has read permission");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_path = std::path::Path::new(config.key.as_ref().unwrap());
|
||||||
|
if !key_path.exists() {
|
||||||
|
eprintln!("Error: Private key file '{}' does not exist", config.key.as_ref().unwrap());
|
||||||
|
eprintln!("Generate or obtain TLS private key for your domain");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
if let Err(e) = std::fs::File::open(key_path) {
|
||||||
|
eprintln!("Error: Cannot read private key file '{}': {}", config.key.as_ref().unwrap(), e);
|
||||||
|
eprintln!("Ensure the file exists and the server user has read permission");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize logging after config validation
|
||||||
let log_level = config.log_level.as_deref().unwrap_or("info");
|
let log_level = config.log_level.as_deref().unwrap_or("info");
|
||||||
init_logging(log_level);
|
init_logging(log_level);
|
||||||
|
|
||||||
// Load configuration from file only
|
// Extract validated config values
|
||||||
let root = config.root.expect("root is required");
|
let root = config.root.unwrap();
|
||||||
let cert_path = config.cert.expect("cert is required");
|
let cert_path = config.cert.unwrap();
|
||||||
let key_path = config.key.expect("key is required");
|
let key_path = config.key.unwrap();
|
||||||
let bind_host = config.bind_host.unwrap_or_else(|| "0.0.0.0".to_string());
|
let bind_host = config.bind_host.unwrap_or_else(|| "0.0.0.0".to_string());
|
||||||
let hostname = config.hostname.expect("hostname is required");
|
let hostname = config.hostname.unwrap();
|
||||||
let port = config.port.unwrap_or(1965);
|
let port = config.port.unwrap_or(1965);
|
||||||
|
|
||||||
// Validate max concurrent requests
|
// Validate max concurrent requests
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue