#[cfg(feature = "store-redis")] use crate::manager::AppState; #[cfg(feature = "store-redis")] use axum::{Json, extract::State}; use serde::{Deserialize, Serialize}; #[cfg(feature = "store-redis")] use std::sync::Arc; #[derive(Debug, Serialize, Deserialize)] pub struct BlacklistEntry { pub target: String, #[serde(rename = "type")] pub entry_type: String, pub reason: String, pub created_at: String, pub expires_at: Option, } #[derive(Serialize)] pub struct BlacklistResponse { pub items: Vec, pub total: usize, } #[derive(Deserialize)] #[allow(dead_code)] pub struct AddBlacklistRequest { pub target: String, #[serde(rename = "type")] pub entry_type: String, pub reason: String, pub duration_secs: Option, } #[cfg(feature = "store-redis")] pub async fn list_blacklist(State(state): State>) -> Json { let mut conn = match state.redis_client.get_multiplexed_async_connection().await { Ok(c) => c, Err(_) => { return Json(BlacklistResponse { items: vec![], total: 0, }); }, }; let members: Vec = match redis::cmd("SMEMBERS") .arg("rampart:blacklist") .query_async(&mut conn) .await { Ok(m) => m, Err(_) => { return Json(BlacklistResponse { items: vec![], total: 0, }); }, }; let items: Vec = members .into_iter() .map(|target| BlacklistEntry { target, entry_type: "ip".to_string(), reason: "manual".to_string(), created_at: chrono::Utc::now().to_rfc3339(), expires_at: None, }) .collect(); let total = items.len(); Json(BlacklistResponse { items, total }) } #[cfg(feature = "store-redis")] pub async fn add_blacklist( State(state): State>, Json(req): Json, ) -> Json { let mut conn = match state.redis_client.get_multiplexed_async_connection().await { Ok(c) => c, Err(_) => return Json(serde_json::json!({"error": "redis unavailable"})), }; let _: () = redis::cmd("SADD") .arg("rampart:blacklist") .arg(&req.target) .query_async(&mut conn) .await .unwrap_or_default(); tracing::info!("Added to blacklist: {} ({})", req.target, req.reason); Json(serde_json::json!({ "status": "added", "target": req.target, "reason": req.reason })) }