use std::path::PathBuf;
use std::sync::{Arc, OnceLock};
use anyhow::{anyhow, Result};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WorkflowWebhookSecret {
NotFound,
NoTrigger,
Secret(Option<String>),
}
#[async_trait::async_trait]
pub trait WebhookIngressHost: Send + Sync {
fn composio_is_configured(&self) -> bool;
fn verify_webhook_signature(&self, raw_body: &[u8], signature: Option<&str>) -> bool;
fn verify_workflow_webhook_signature(
&self,
secret: &str,
raw_body: &[u8],
signature: Option<&str>,
) -> bool;
async fn composio_handle_webhook(&self, payload: &Value) -> Option<usize>;
async fn run_workflow_for_trigger(&self, workflow_id: &str, payload_json: &str)
-> Result<String>;
fn workflow_webhook_secret(&self, workflow_id: &str) -> WorkflowWebhookSecret;
fn auth_token(&self) -> Option<String>;
fn data_dir(&self) -> PathBuf;
async fn ensure_funnel(&self, port: u16) -> Result<String>;
async fn funnel_url(&self, port: u16) -> Option<String>;
}
fn host_slot() -> &'static OnceLock<Arc<dyn WebhookIngressHost>> {
static HOST: OnceLock<Arc<dyn WebhookIngressHost>> = OnceLock::new();
&HOST
}
pub fn set_global_host(host: Arc<dyn WebhookIngressHost>) {
let _ = host_slot().set(host);
}
pub(crate) fn host() -> Result<Arc<dyn WebhookIngressHost>> {
host_slot()
.get()
.cloned()
.ok_or_else(|| anyhow!("webhook-ingress host not initialized"))
}
pub(crate) fn host_opt() -> Option<Arc<dyn WebhookIngressHost>> {
host_slot().get().cloned()
}