5fa7a82548
- Add .gitignore and .env.example files for project setup - Create build script for proto compilation with tonic-prost - Generate Cargo.lock with all project dependencies - Configure project structure and ignore patterns for development environment
76 lines
2.3 KiB
Rust
76 lines
2.3 KiB
Rust
use emailks::{
|
|
config::AppConfig, email::EmailSender, pb::email::v1::email_service_server::EmailServiceServer,
|
|
queue::EmailQueue, server::EmailServiceImpl,
|
|
};
|
|
use tonic::transport::Server;
|
|
use tracing::{error, info};
|
|
|
|
const DEFAULT_QUEUE_CAPACITY: usize = 1_000;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(
|
|
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
|
|
)
|
|
.init();
|
|
|
|
let config = AppConfig::from_env()?;
|
|
info!(?config.smtp.host, port = config.smtp.port, "smtp config loaded");
|
|
|
|
let sender = EmailSender::new(config.smtp)?;
|
|
let (queue, worker) = match config.queue_capacity {
|
|
Some(0) => {
|
|
info!("creating unbounded queue by explicit configuration");
|
|
EmailQueue::unbounded()
|
|
}
|
|
Some(cap) => {
|
|
info!(capacity = cap, "creating bounded queue");
|
|
EmailQueue::bounded(cap)
|
|
}
|
|
None => {
|
|
info!(
|
|
capacity = DEFAULT_QUEUE_CAPACITY,
|
|
"creating bounded queue with default capacity"
|
|
);
|
|
EmailQueue::bounded(DEFAULT_QUEUE_CAPACITY)
|
|
}
|
|
};
|
|
|
|
let store = queue.status_store().clone();
|
|
let worker_handle = worker.spawn(move |job| {
|
|
let s = sender.clone();
|
|
async move { s.send_job(&job).await }
|
|
});
|
|
|
|
let addr = config.listen_addr;
|
|
let svc = EmailServiceImpl::new(queue, store);
|
|
|
|
let (health, health_svc) = tonic_health::server::health_reporter();
|
|
health
|
|
.set_serving::<EmailServiceServer<EmailServiceImpl>>()
|
|
.await;
|
|
|
|
info!(%addr, "gRPC server starting");
|
|
Server::builder()
|
|
.add_service(health_svc)
|
|
.add_service(EmailServiceServer::new(svc))
|
|
.serve_with_shutdown(addr, shutdown_signal())
|
|
.await?;
|
|
|
|
info!("server stopped");
|
|
|
|
if let Err(e) = worker_handle.await {
|
|
tracing::error!(error = %e, "worker task panicked");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn shutdown_signal() {
|
|
match tokio::signal::ctrl_c().await {
|
|
Ok(()) => info!("shutdown signal received, draining..."),
|
|
Err(err) => error!(%err, "failed to install CTRL+C handler, shutting down"),
|
|
}
|
|
}
|