feat: add remote, with rss, cache, discord, and zulip
This commit is contained in:
parent
50ec63a474
commit
83baab68e0
32 changed files with 6615 additions and 40 deletions
149
remote/cache-service/src/main.rs
Normal file
149
remote/cache-service/src/main.rs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use axum::routing::{get, post};
|
||||
use axum::Router;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::info;
|
||||
|
||||
mod api;
|
||||
mod db;
|
||||
mod poller;
|
||||
mod types;
|
||||
mod webhook;
|
||||
|
||||
use types::WebhookTarget;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
||||
.init();
|
||||
|
||||
let port: u16 = std::env::var("NOISEBELL_CACHE_PORT")
|
||||
.unwrap_or_else(|_| "3000".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_CACHE_PORT must be a valid u16")?;
|
||||
|
||||
let pi_address = std::env::var("NOISEBELL_CACHE_PI_ADDRESS")
|
||||
.context("NOISEBELL_CACHE_PI_ADDRESS is required")?;
|
||||
|
||||
let pi_api_key = std::env::var("NOISEBELL_CACHE_PI_API_KEY")
|
||||
.context("NOISEBELL_CACHE_PI_API_KEY is required")?;
|
||||
|
||||
let inbound_api_key = std::env::var("NOISEBELL_CACHE_INBOUND_API_KEY")
|
||||
.context("NOISEBELL_CACHE_INBOUND_API_KEY is required")?;
|
||||
|
||||
let data_dir =
|
||||
std::env::var("NOISEBELL_CACHE_DATA_DIR").unwrap_or_else(|_| "/var/lib/noisebell-cache".into());
|
||||
|
||||
let status_poll_interval_secs: u64 = std::env::var("NOISEBELL_CACHE_STATUS_POLL_INTERVAL_SECS")
|
||||
.unwrap_or_else(|_| "60".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_CACHE_STATUS_POLL_INTERVAL_SECS must be a valid u64")?;
|
||||
|
||||
let info_poll_interval_secs: u64 = std::env::var("NOISEBELL_CACHE_INFO_POLL_INTERVAL_SECS")
|
||||
.unwrap_or_else(|_| "300".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_CACHE_INFO_POLL_INTERVAL_SECS must be a valid u64")?;
|
||||
|
||||
let offline_threshold: u32 = std::env::var("NOISEBELL_CACHE_OFFLINE_THRESHOLD")
|
||||
.unwrap_or_else(|_| "3".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_CACHE_OFFLINE_THRESHOLD must be a valid u32")?;
|
||||
|
||||
let retry_attempts: u32 = std::env::var("NOISEBELL_CACHE_RETRY_ATTEMPTS")
|
||||
.unwrap_or_else(|_| "3".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_CACHE_RETRY_ATTEMPTS must be a valid u32")?;
|
||||
|
||||
let retry_base_delay_secs: u64 = std::env::var("NOISEBELL_CACHE_RETRY_BASE_DELAY_SECS")
|
||||
.unwrap_or_else(|_| "1".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_CACHE_RETRY_BASE_DELAY_SECS must be a valid u64")?;
|
||||
|
||||
let http_timeout_secs: u64 = std::env::var("NOISEBELL_CACHE_HTTP_TIMEOUT_SECS")
|
||||
.unwrap_or_else(|_| "10".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_CACHE_HTTP_TIMEOUT_SECS must be a valid u64")?;
|
||||
|
||||
// Parse outbound webhooks from NOISEBELL_CACHE_WEBHOOK_<n>_URL and _SECRET env vars
|
||||
let mut webhooks = Vec::new();
|
||||
for i in 0.. {
|
||||
let url_key = format!("NOISEBELL_CACHE_WEBHOOK_{i}_URL");
|
||||
match std::env::var(&url_key) {
|
||||
Ok(url) => {
|
||||
let secret_key = format!("NOISEBELL_CACHE_WEBHOOK_{i}_SECRET");
|
||||
let secret = std::env::var(&secret_key).ok();
|
||||
webhooks.push(WebhookTarget { url, secret });
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
info!(
|
||||
port,
|
||||
%pi_address,
|
||||
webhook_count = webhooks.len(),
|
||||
"starting noisebell-cache"
|
||||
);
|
||||
|
||||
let db_path = format!("{data_dir}/noisebell.db");
|
||||
let conn = db::init(&db_path)?;
|
||||
let db = Arc::new(Mutex::new(conn));
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(http_timeout_secs))
|
||||
.build()
|
||||
.context("failed to build HTTP client")?;
|
||||
|
||||
let poller_config = Arc::new(poller::PollerConfig {
|
||||
pi_address,
|
||||
pi_api_key,
|
||||
status_poll_interval: Duration::from_secs(status_poll_interval_secs),
|
||||
info_poll_interval: Duration::from_secs(info_poll_interval_secs),
|
||||
offline_threshold,
|
||||
retry_attempts,
|
||||
retry_base_delay_secs,
|
||||
http_timeout_secs,
|
||||
webhooks: webhooks.clone(),
|
||||
});
|
||||
|
||||
poller::spawn_status_poller(poller_config.clone(), db.clone(), client.clone());
|
||||
poller::spawn_info_poller(poller_config, db.clone(), client.clone());
|
||||
|
||||
let app_state = Arc::new(api::AppState {
|
||||
db,
|
||||
client,
|
||||
inbound_api_key,
|
||||
webhooks,
|
||||
retry_attempts,
|
||||
retry_base_delay_secs,
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
.route("/webhook", post(api::post_webhook))
|
||||
.route("/status", get(api::get_status))
|
||||
.route("/info", get(api::get_info))
|
||||
.route("/history", get(api::get_history))
|
||||
.with_state(app_state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(("0.0.0.0", port))
|
||||
.await
|
||||
.context(format!("failed to bind to 0.0.0.0:{port}"))?;
|
||||
|
||||
info!(port, "listening");
|
||||
|
||||
let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
|
||||
.context("failed to register SIGTERM handler")?;
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(async move {
|
||||
sigterm.recv().await;
|
||||
})
|
||||
.await
|
||||
.context("server error")?;
|
||||
|
||||
info!("shutdown complete");
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue