use clap::Parser; use sqlx::postgres::PgPoolOptions; use std::sync::Arc; use tracing::{info, warn}; mod handlers; mod sync; mod warmup; mod worker; #[derive(Parser)] #[command(name = "sidelinja-worker", about = "Jobbkø-worker for Sidelinja")] struct Cli { /// PostgreSQL connection string #[arg( long, env = "DATABASE_URL", default_value = "postgres://sidelinja:localdev@localhost:5432/sidelinja" )] database_url: String, /// AI Gateway base URL #[arg( long, env = "AI_GATEWAY_URL", default_value = "http://localhost:4000/v1" )] ai_gateway_url: String, /// AI Gateway API-nøkkel (LiteLLM master key) #[arg(long, env = "AI_GATEWAY_KEY", default_value = "")] ai_gateway_key: String, /// Maks samtidige jobber #[arg(long, default_value = "3")] max_concurrent: usize, /// Polling-intervall i sekunder #[arg(long, default_value = "1")] poll_interval: u64, /// SpacetimeDB URL #[arg(long, env = "SPACETIMEDB_URL", default_value = "http://localhost:3000")] spacetimedb_url: String, /// SpacetimeDB modulnavn #[arg(long, env = "SPACETIMEDB_MODULE", default_value = "sidelinja-realtime")] spacetimedb_module: String, /// Sync-intervall i sekunder (SpacetimeDB → PG) #[arg(long, default_value = "1")] sync_interval: u64, /// Maks meldinger per kanal ved oppvarming #[arg(long, default_value = "100")] warmup_limit: i64, } #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "sidelinja_worker=info,sqlx=warn".into()), ) .json() .init(); let cli = Cli::parse(); info!( max_concurrent = cli.max_concurrent, poll_interval_s = cli.poll_interval, "Starter sidelinja-worker" ); let pool = PgPoolOptions::new() .max_connections(cli.max_concurrent as u32 + 2) .connect(&cli.database_url) .await?; info!("Tilkoblet PostgreSQL"); let registry = Arc::new(handlers::build_registry( reqwest::Client::new(), cli.ai_gateway_url, cli.ai_gateway_key, cli.spacetimedb_url.clone(), cli.spacetimedb_module.clone(), )); let registered: Vec<&str> = registry.keys().map(|k| k.as_str()).collect(); info!(?registered, "Registrerte jobbtyper"); // Oppvarming: last PG-data inn i SpacetimeDB let http = reqwest::Client::new(); if let Err(e) = warmup::run( &pool, &http, &cli.spacetimedb_url, &cli.spacetimedb_module, cli.warmup_limit, ).await { warn!(error = %e, "Oppvarming feilet — fortsetter uten historikk i SpacetimeDB"); } // Spawn sync-worker som parallell task let sync_pool = pool.clone(); let spacetimedb_url = cli.spacetimedb_url.clone(); let spacetimedb_module = cli.spacetimedb_module.clone(); let sync_interval = cli.sync_interval; tokio::spawn(async move { sync::run(sync_pool, http, spacetimedb_url, spacetimedb_module, sync_interval).await; }); worker::run(pool, registry, cli.max_concurrent, cli.poll_interval).await }