vane 0.8.15

A flow-based reverse proxy with multi-layer routing and programmable pipelines.
/* src/plugins/system/httpx.rs */

use crate::{
	common::config::env_loader,
	engine::interfaces::{ExternalApiResponse, MiddlewareOutput, ResolvedInputs},
};
use anyhow::{Result, anyhow};
use fancy_log::{LogLevel, log};
use std::time::Duration;

pub async fn execute(url: &str, name: &str, inputs: ResolvedInputs) -> Result<MiddlewareOutput> {
	log(
		LogLevel::Debug,
		&format!("➜ Executing external HTTP middleware: {name}"),
	);

	// 1. Check Env for TLS Verification Skip
	let skip_tls = env_loader::to_lowercase(&env_loader::get_env(
		"EXTERNAL_HTTPS_CALL_SKIP_TLS_VERIFY",
		"false".to_owned(),
	)) == "true";

	if skip_tls {
		log(
			LogLevel::Debug,
			&format!(
				"⚠ TLS Verification disabled for external plugin '{name}' via EXTERNAL_HTTPS_CALL_SKIP_TLS_VERIFY."
			),
		);
	}

	// 2. Build Client
	let timeout_secs = env_loader::get_env("FLOW_EXECUTION_TIMEOUT_SECS", "10".to_owned())
		.parse::<u64>()
		.unwrap_or(10);

	let client = reqwest::Client::builder()
		.timeout(Duration::from_secs(timeout_secs)) // Runtime timeout
		.danger_accept_invalid_certs(skip_tls)
		.build()
		.map_err(|e| anyhow!("Failed to build HTTP client: {e}"))?;

	// 3. Send POST Request
	let response = match client.post(url).json(&inputs).send().await {
		Ok(r) => r,
		Err(e) => {
			log(
				LogLevel::Error,
				&format!("✗ External HTTP request failed for '{name}': {e}"),
			);
			return Ok(MiddlewareOutput {
				branch: "failure".into(),
				store: None,
			});
		}
	};

	// 4. Validate HTTP Status
	if !response.status().is_success() {
		log(
			LogLevel::Error,
			&format!(
				"✗ External plugin '{}' returned HTTP error: {}",
				name,
				response.status()
			),
		);
		return Ok(MiddlewareOutput {
			branch: "failure".into(),
			store: None,
		});
	}

	// 5. Parse Response Wrapper (ExternalApiResponse)
	let api_response: ExternalApiResponse<MiddlewareOutput> = match response.json().await {
		Ok(r) => r,
		Err(e) => {
			log(
				LogLevel::Error,
				&format!("✗ Failed to parse external API response JSON for '{name}': {e}"),
			);
			return Ok(MiddlewareOutput {
				branch: "failure".into(),
				store: None,
			});
		}
	};

	// 6. Check Logic Status
	if api_response.status == "success" {
		api_response
			.data
			.ok_or_else(|| anyhow!("External API for '{name}' returned success but 'data' is missing."))
	} else {
		let msg = api_response
			.message
			.unwrap_or_else(|| "Unknown error".to_owned());
		log(
			LogLevel::Warn,
			&format!("⚠ External API for '{name}' returned error status: {msg}"),
		);
		Ok(MiddlewareOutput {
			branch: "failure".into(),
			store: None,
		})
	}
}