Initial codebase structure

- Complete Gemini server implementation with logging
- Add comprehensive documentation (README.md, AGENTS.md)
- Implement certificate management guidelines
- Add .gitignore for security and build artifacts
- All unit tests passing (14/14)
- Ready for production deployment
This commit is contained in:
Jeena 2026-01-15 08:21:37 +09:00
commit 1ed443ff2a
10 changed files with 639 additions and 0 deletions

105
src/main.rs Normal file
View file

@ -0,0 +1,105 @@
mod config;
mod tls;
mod request;
mod server;
mod logging;
use clap::Parser;
use rustls::ServerConfig;
use std::path::Path;
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio_rustls::TlsAcceptor;
use logging::init_logging;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Path to config file
#[arg(short, long)]
config: Option<String>,
/// Directory to serve files from
#[arg(short, long)]
root: Option<String>,
/// Path to certificate file
#[arg(short, long)]
cert: Option<String>,
/// Path to private key file
#[arg(short, long)]
key: Option<String>,
/// Port to listen on
#[arg(short, long)]
port: Option<u16>,
/// Hostname for the server
#[arg(short = 'H', long)]
host: Option<String>,
}
#[tokio::main]
async fn main() {
let args = Args::parse();
// Load config
let config_path = args.config.as_deref().unwrap_or("/etc/pollux/config.toml");
let config = config::load_config(config_path).unwrap_or(config::Config {
root: None,
cert: None,
key: None,
host: None,
port: None,
log_level: None,
});
// Initialize logging
let log_level = config.log_level.as_deref().unwrap_or("info");
init_logging(log_level);
// Merge config with args (args take precedence)
let root = args.root.or(config.root).expect("root is required");
let cert = args.cert.or(config.cert).expect("cert is required");
let key = args.key.or(config.key).expect("key is required");
let host = args.host.or(config.host).unwrap_or_else(|| "0.0.0.0".to_string());
let port = args.port.or(config.port).unwrap_or(1965);
// Validate directory
let dir_path = Path::new(&root);
if !dir_path.exists() || !dir_path.is_dir() {
eprintln!("Error: Directory '{}' does not exist or is not a directory", root);
std::process::exit(1);
}
// Load TLS certificates
let certs = tls::load_certs(&cert).unwrap();
let key = tls::load_private_key(&key).unwrap();
let config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(certs, key).unwrap();
let acceptor = TlsAcceptor::from(Arc::new(config));
let listener = TcpListener::bind(format!("{}:{}", host, port)).await.unwrap();
println!("Server listening on {}:{}", host, port);
loop {
let (stream, _) = listener.accept().await.unwrap();
let acceptor = acceptor.clone();
let dir = root.clone();
let expected_host = host.clone();
if let Ok(stream) = acceptor.accept(stream).await {
if let Err(e) = server::handle_connection(stream, &dir, &expected_host).await {
tracing::error!("Error handling connection: {}", e);
}
}
}
}