feat: wire all tasks together in main with signal handling
Made-with: Cursor
This commit is contained in:
@@ -12,7 +12,7 @@ pub struct BridgeConfig {
|
|||||||
pub control: ControlConfig,
|
pub control: ControlConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct IrcConfig {
|
pub struct IrcConfig {
|
||||||
pub server: String,
|
pub server: String,
|
||||||
#[serde(default = "default_irc_port")]
|
#[serde(default = "default_irc_port")]
|
||||||
|
|||||||
126
src/main.rs
126
src/main.rs
@@ -9,6 +9,128 @@ mod router;
|
|||||||
mod webhook;
|
mod webhook;
|
||||||
mod websocket;
|
mod websocket;
|
||||||
|
|
||||||
fn main() {
|
use std::path::PathBuf;
|
||||||
println!("owncast-irc-bridge");
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use tokio::sync::{mpsc, watch};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(name = "owncast-irc-bridge")]
|
||||||
|
#[command(about = "Bidirectional chat bridge between Owncast and IRC")]
|
||||||
|
struct Cli {
|
||||||
|
/// Path to config file
|
||||||
|
#[arg(short, long, default_value = "config.toml")]
|
||||||
|
config: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_env_filter(
|
||||||
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
||||||
|
)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let cli = Cli::parse();
|
||||||
|
let config = config::BridgeConfig::load(&cli.config)?;
|
||||||
|
let access_token = config.owncast_access_token()?;
|
||||||
|
|
||||||
|
info!("Starting owncast-irc-bridge");
|
||||||
|
|
||||||
|
let start_time = Instant::now();
|
||||||
|
let (shutdown_tx, shutdown_rx) = watch::channel(false);
|
||||||
|
|
||||||
|
let (event_tx, event_rx) = mpsc::channel(256);
|
||||||
|
let (irc_outbound_tx, irc_outbound_rx) = mpsc::channel(256);
|
||||||
|
let (state_tx, state_rx) = mpsc::channel(32);
|
||||||
|
let (control_tx, control_rx) = mpsc::channel(32);
|
||||||
|
|
||||||
|
let api_client = owncast_api::OwncastApiClient::new(
|
||||||
|
config.owncast.url.clone(),
|
||||||
|
access_token,
|
||||||
|
);
|
||||||
|
|
||||||
|
let irc_config = config.irc.clone();
|
||||||
|
let irc_event_tx = event_tx.clone();
|
||||||
|
let irc_shutdown = shutdown_rx.clone();
|
||||||
|
let _irc_handle = tokio::spawn(async move {
|
||||||
|
irc_task::run_irc_task(irc_config, irc_event_tx, irc_outbound_rx, irc_shutdown).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let webhook_port = config.owncast.webhook_port;
|
||||||
|
let webhook_event_tx = event_tx.clone();
|
||||||
|
let _webhook_handle = tokio::spawn(async move {
|
||||||
|
if let Err(e) = webhook::run_webhook_server(webhook_port, webhook_event_tx).await {
|
||||||
|
tracing::error!(error = %e, "Webhook server failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ws_handle = if config.owncast.websocket_enabled {
|
||||||
|
let ws_url = config.owncast.url.clone();
|
||||||
|
let ws_event_tx = event_tx.clone();
|
||||||
|
let ws_shutdown = shutdown_rx.clone();
|
||||||
|
Some(tokio::spawn(async move {
|
||||||
|
websocket::run_websocket_task(ws_url, ws_event_tx, ws_shutdown).await;
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let health_api = owncast_api::OwncastApiClient::new(
|
||||||
|
config.owncast.url.clone(),
|
||||||
|
String::new(),
|
||||||
|
);
|
||||||
|
let health_interval = std::time::Duration::from_secs(config.owncast.health_poll_interval_secs);
|
||||||
|
let health_shutdown = shutdown_rx.clone();
|
||||||
|
let _health_handle = tokio::spawn(async move {
|
||||||
|
health::run_health_poller(&health_api, state_tx, health_interval, health_shutdown).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let control_socket_path = config.control.socket_path.clone();
|
||||||
|
let _control_handle = tokio::spawn(async move {
|
||||||
|
if let Err(e) = control::run_control_socket(&control_socket_path, control_tx).await {
|
||||||
|
tracing::error!(error = %e, "Control socket failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let sig_shutdown_tx = shutdown_tx.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
|
||||||
|
.expect("Failed to register SIGTERM handler");
|
||||||
|
let mut sighup = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::hangup())
|
||||||
|
.expect("Failed to register SIGHUP handler");
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
_ = tokio::signal::ctrl_c() => {
|
||||||
|
info!("SIGINT received, shutting down");
|
||||||
|
let _ = sig_shutdown_tx.send(true);
|
||||||
|
}
|
||||||
|
_ = sigterm.recv() => {
|
||||||
|
info!("SIGTERM received, shutting down");
|
||||||
|
let _ = sig_shutdown_tx.send(true);
|
||||||
|
}
|
||||||
|
_ = sighup.recv() => {
|
||||||
|
info!("SIGHUP received (reconnect not yet wired)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router::run_router(
|
||||||
|
config.bridge,
|
||||||
|
config.owncast.url,
|
||||||
|
api_client,
|
||||||
|
event_rx,
|
||||||
|
irc_outbound_tx,
|
||||||
|
state_rx,
|
||||||
|
control_rx,
|
||||||
|
shutdown_tx,
|
||||||
|
start_time,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
info!("Bridge shutting down");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user