119 lines
4.0 KiB
Rust
119 lines
4.0 KiB
Rust
use std::net::SocketAddr;
|
|||
|
|
use std::sync::Arc;
|
||
|
|
use tokio::net::TcpListener;
|
||
|
|
|
||
|
|
use axum::Router;
|
||
|
|
use sea_orm::Database;
|
||
|
|
use tracing_subscriber::EnvFilter;
|
||
|
|
|
||
|
|
use crate::shared::config::CONFIG;
|
||
|
|
use crate::shared::database::get_redis_conn;
|
||
|
|
use crate::shared::state::AppState;
|
||
|
|
|
||
|
|
pub struct Application {
|
||
|
|
pub port: u16,
|
||
|
|
router: Router,
|
||
|
|
listener: TcpListener,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Application {
|
||
|
|
pub async fn build() -> anyhow::Result<Self> {
|
||
|
|
// Initialize tracing. Default to warn/error globally unless RUST_LOG is explicitly set.
|
||
|
|
let env_filter = match std::env::var("RUST_LOG") {
|
||
|
|
Ok(filter) => EnvFilter::new(filter).add_directive("html5ever=error".parse()?),
|
||
|
|
Err(_) => EnvFilter::new("warn,html5ever=error"),
|
||
|
|
};
|
||
|
|
|
||
|
|
tracing_subscriber::fmt().with_env_filter(env_filter).init();
|
||
|
|
|
||
|
|
// Initialize OpenTelemetry metrics
|
||
|
|
crate::shared::observability::metrics::init_otel_metrics();
|
||
|
|
|
||
|
|
tracing::info!("🚀 Scraper starting up...");
|
||
|
|
tracing::info!(" Environment: {}", CONFIG.environment);
|
||
|
|
|
||
|
|
// Log thread configuration
|
||
|
|
let worker_threads = std::thread::available_parallelism()
|
||
|
|
.map(|n| n.get())
|
||
|
|
.unwrap_or(1);
|
||
|
|
tracing::info!(
|
||
|
|
" Tokio Worker Threads: (Defaulting to CPU cores: {})",
|
||
|
|
worker_threads
|
||
|
|
);
|
||
|
|
|
||
|
|
// Redis
|
||
|
|
let _ = get_redis_conn().await;
|
||
|
|
|
||
|
|
// Browser Pool
|
||
|
|
tracing::info!("Initializing browser pool...");
|
||
|
|
let browser_config = crate::shared::browser::BrowserPoolConfig::default();
|
||
|
|
match crate::shared::browser::pool::init_browser_pool(browser_config).await {
|
||
|
|
Ok(_) => tracing::info!("✓ Browser pool initialized"),
|
||
|
|
Err(e) => tracing::error!("⚠️ Failed to initialize browser pool: {}", e),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Database
|
||
|
|
let mut opt = sea_orm::ConnectOptions::new(CONFIG.database_url.clone());
|
||
|
|
opt.max_connections(20)
|
||
|
|
.min_connections(1)
|
||
|
|
.connect_timeout(std::time::Duration::from_secs(
|
||
|
|
CONFIG.db.connect_timeout_seconds,
|
||
|
|
))
|
||
|
|
.idle_timeout(std::time::Duration::from_secs(
|
||
|
|
CONFIG.db.idle_timeout_seconds,
|
||
|
|
))
|
||
|
|
.acquire_timeout(std::time::Duration::from_secs(
|
||
|
|
CONFIG.db.acquire_timeout_seconds,
|
||
|
|
))
|
||
|
|
.max_lifetime(std::time::Duration::from_secs(
|
||
|
|
CONFIG.db.max_lifetime_seconds,
|
||
|
|
))
|
||
|
|
.sqlx_logging(CONFIG.log_level == "debug");
|
||
|
|
|
||
|
|
let db = Database::connect(opt)
|
||
|
|
.await
|
||
|
|
.map_err(|e| anyhow::anyhow!("Failed to connect to database: {}", e))?;
|
||
|
|
tracing::info!("✓ SeaORM database connection established");
|
||
|
|
|
||
|
|
// Schema & Seeding
|
||
|
|
if let Err(e) = crate::shared::database::setup::init(&db).await {
|
||
|
|
tracing::error!("Failed to init DB schema: {}", e);
|
||
|
|
}
|
||
|
|
|
||
|
|
// App State components
|
||
|
|
let db_arc = Arc::new(db);
|
||
|
|
let image_processing_semaphore = Arc::new(tokio::sync::Semaphore::new(
|
||
|
|
CONFIG.image_processing_concurrency,
|
||
|
|
));
|
||
|
|
let event_bus = Arc::new(crate::shared::events::bus::EventBus::new());
|
||
|
|
|
||
|
|
let redis_pool = crate::shared::database::redis_pool()
|
||
|
|
.map_err(|e| anyhow::anyhow!("Failed to init Redis pool: {}", e))?;
|
||
|
|
|
||
|
|
let app_state = Arc::new(AppState {
|
||
|
|
redis_pool,
|
||
|
|
db: db_arc.clone(),
|
||
|
|
image_processing_semaphore,
|
||
|
|
event_bus: event_bus.clone(),
|
||
|
|
});
|
||
|
|
|
||
|
|
let app = crate::app::build_router(app_state, db_arc.clone()).await?;
|
||
|
|
|
||
|
|
// Listener
|
||
|
|
let port = CONFIG.server_port;
|
||
|
|
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||
|
|
let listener = TcpListener::bind(&addr).await?;
|
||
|
|
tracing::info!("Server listening on {}", listener.local_addr()?);
|
||
|
|
|
||
|
|
Ok(Self {
|
||
|
|
port,
|
||
|
|
router: app,
|
||
|
|
listener,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
pub async fn run(self) -> std::io::Result<()> {
|
||
|
|
axum::serve(self.listener, self.router.into_make_service()).await
|
||
|
|
}
|
||
|
|
}
|