refactor: Restructure REST API with /api/v1/ prefix

- Add /api/v1/ prefix for all REST endpoints
- Move WebSocket routes to /ws/ (health, alerts, flows)
- Rename: access_control → acl, service → filter, misc → system
- Restructure filter into /filter/http and /filter/ssh sub-scopes
- Add rate-limit config API (GET/PUT /api/v1/rate-limit/config)
- Wire flow_stats_ws to /ws/flows
- Fix all error responses to JSON format
- Fix double JSON serialization (.json(web::Json(x)) → .json(x))
- Remove old control/ directory

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
DaLaw2 2026-03-19 17:11:55 +08:00
parent 2d9f5f18bf
commit 46e7d18c50
17 changed files with 462 additions and 372 deletions

@ -1 +1 @@
Subproject commit bf27f315f2536025fa9068b9027a151a160bb70d
Subproject commit 4e8b39bbb93641926bba18899b6c63cee187564d

View File

@ -21,7 +21,7 @@ use crate::model::error::Error;
use crate::model::log::ml::MLLog;
use crate::model::log::system::SystemLog;
use crate::utils::logging::Logging;
use crate::web::api::{control, default, health, misc, ml_alert};
use crate::web::api::{acl, filter, rate_limit as rate_limit_api, stats, health as health_api, ml, system as system_api, default, ws};
/// Maps stage name (from config.toml) to (function_name, stage_id)
fn stage_registry() -> HashMap<&'static str, (&'static str, u32)> {
@ -171,10 +171,17 @@ impl System {
.app_data(web::Data::from(health.clone()))
.app_data(web::Data::from(ml_alert.clone()))
.app_data(web::Data::from(flow_statistics.clone()))
.service(control::initialize())
.service(ml_alert::initialize())
.service(health::initialize())
.service(misc::initialize())
.service(
web::scope("/api")
.service(acl::initialize())
.service(filter::initialize())
.service(rate_limit_api::initialize())
.service(stats::initialize())
.service(health_api::initialize())
.service(ml::initialize())
.service(system_api::initialize())
)
.service(ws::initialize())
.default_service(route().to(default::default_route))
})
.bind(format!("0.0.0.0:{}", port))

View File

@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
#[serde(rename_all = "lowercase")]
pub enum ListType {
#[serde(rename = "white_list")]
#[serde(rename = "whitelist")]
White,
#[serde(rename = "black_list")]
#[serde(rename = "blacklist")]
Black,
}

View File

@ -1,22 +1,21 @@
use std::net::{SocketAddrV4, SocketAddrV6};
use actix_web::{delete, get, put, web, HttpResponse, Responder, Scope};
use actix_web::{web, HttpResponse, Responder, Scope};
use crate::core::ebpf::access_control::AccessControl;
use crate::model::direction::FlowDirection;
use crate::model::list_type::ListType;
pub fn initialize() -> Scope {
web::scope("/access_control")
.service(get_ipv4_list)
.service(get_ipv6_list)
.service(add_ipv4_list)
.service(add_ipv6_list)
.service(remove_ipv4_list)
.service(remove_ipv6_list)
web::scope("/acl")
.route("/ipv4/{direction}/{list_type}", web::get().to(get_ipv4_list))
.route("/ipv6/{direction}/{list_type}", web::get().to(get_ipv6_list))
.route("/ipv4/{direction}/{list_type}", web::put().to(add_ipv4_list))
.route("/ipv6/{direction}/{list_type}", web::put().to(add_ipv6_list))
.route("/ipv4/{direction}/{list_type}", web::delete().to(remove_ipv4_list))
.route("/ipv6/{direction}/{list_type}", web::delete().to(remove_ipv6_list))
}
#[get("/ipv4/{direction}/{list_type}")]
async fn get_ipv4_list(
path: web::Path<(FlowDirection, ListType)>,
access_control: web::Data<AccessControl>,
@ -26,7 +25,6 @@ async fn get_ipv4_list(
HttpResponse::Ok().json(list)
}
#[get("/ipv6/{direction}/{list_type}")]
async fn get_ipv6_list(
path: web::Path<(FlowDirection, ListType)>,
access_control: web::Data<AccessControl>,
@ -36,7 +34,6 @@ async fn get_ipv6_list(
HttpResponse::Ok().json(list)
}
#[put("/ipv4/{direction}/{list_type}")]
async fn add_ipv4_list(
address: web::Json<SocketAddrV4>,
path: web::Path<(FlowDirection, ListType)>,
@ -46,11 +43,10 @@ async fn add_ipv4_list(
let (direction, list_type) = path.into_inner();
match access_control.add_ipv4_list(direction, list_type, address).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
#[put("/ipv6/{direction}/{list_type}")]
async fn add_ipv6_list(
address: web::Json<SocketAddrV6>,
path: web::Path<(FlowDirection, ListType)>,
@ -60,11 +56,10 @@ async fn add_ipv6_list(
let (direction, list_type) = path.into_inner();
match access_control.add_ipv6_list(direction, list_type, address).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
#[delete("/ipv4/{direction}/{list_type}")]
async fn remove_ipv4_list(
address: web::Json<SocketAddrV4>,
path: web::Path<(FlowDirection, ListType)>,
@ -74,11 +69,10 @@ async fn remove_ipv4_list(
let (direction, list_type) = path.into_inner();
match access_control.remove_ipv4_list(direction, list_type, address).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
#[delete("/ipv6/{direction}/{list_type}")]
async fn remove_ipv6_list(
address: web::Json<SocketAddrV6>,
path: web::Path<(FlowDirection, ListType)>,
@ -88,6 +82,6 @@ async fn remove_ipv6_list(
let (direction, list_type) = path.into_inner();
match access_control.remove_ipv6_list(direction, list_type, address).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}

View File

@ -1,17 +0,0 @@
pub mod access_control;
pub mod protocol_filter;
pub mod statistics;
use actix_web::{web, Scope};
pub fn initialize() -> Scope {
web::scope("/ebpf")
.service(access_control::initialize())
.service(protocol_filter::initialize())
.service(
web::scope("/statistics")
.route("/flows", web::get().to(statistics::get_all_flows))
.route("/flows/top/{n}", web::get().to(statistics::get_top_flows))
.route("/summary", web::get().to(statistics::get_summary)),
)
}

View File

@ -1,251 +0,0 @@
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use actix_web::{delete, get, post, put, web, HttpResponse, Responder, Scope};
use common::model::http_method::HttpMethod;
use crate::core::ebpf::protocol_filter::ProtocolFilter;
pub fn initialize() -> Scope {
web::scope("/service")
.service(get_ipv4_http_service)
.service(get_ipv6_http_service)
.service(add_ipv4_http_service)
.service(add_ipv6_http_service)
.service(remove_ipv4_http_service)
.service(remove_ipv6_http_service)
.service(is_ssh_white_list_enable)
.service(enable_ssh_white_list)
.service(disable_ssh_white_list)
.service(get_ipv4_ssh_service)
.service(get_ipv6_ssh_service)
.service(add_ipv4_ssh_service)
.service(add_ipv6_ssh_service)
.service(remove_ipv4_ssh_service)
.service(remove_ipv6_ssh_service)
.service(get_ipv4_ssh_white_list)
.service(get_ipv6_ssh_white_list)
.service(add_ipv4_ssh_white_list)
.service(add_ipv6_ssh_white_list)
.service(remove_ipv4_ssh_white_list)
.service(remove_ipv6_ssh_white_list)
.service(get_ipv4_ssh_black_list)
.service(get_ipv6_ssh_black_list)
.service(add_ipv4_ssh_black_list)
.service(add_ipv6_ssh_black_list)
.service(remove_ipv4_ssh_black_list)
.service(remove_ipv6_ssh_black_list)
}
#[get("/ipv4/http_service")]
async fn get_ipv4_http_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_http_service().await;
HttpResponse::Ok().json(web::Json(list))
}
#[get("/ipv6/http_service")]
async fn get_ipv6_http_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_http_service().await;
HttpResponse::Ok().json(web::Json(list))
}
#[put("/ipv4/http_service")]
async fn add_ipv4_http_service(
payload: web::Json<(SocketAddrV4, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.add_ipv4_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[put("/ipv6/http_service")]
async fn add_ipv6_http_service(
payload: web::Json<(SocketAddrV6, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.add_ipv6_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv4/http_service")]
async fn remove_ipv4_http_service(
payload: web::Json<(SocketAddrV4, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.remove_ipv4_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv6/http_service")]
async fn remove_ipv6_http_service(
payload: web::Json<(SocketAddrV6, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.remove_ipv6_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[get("/ssh_white_list")]
async fn is_ssh_white_list_enable(service: web::Data<ProtocolFilter>) -> impl Responder {
let enabled = service.is_ssh_white_list_enable().await;
HttpResponse::Ok().json(enabled)
}
#[post("/ssh_white_list/enable")]
async fn enable_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
match service.enable_ssh_white_list().await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[post("/ssh_white_list/disable")]
async fn disable_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
match service.disable_ssh_white_list().await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[get("/ipv4/ssh_service")]
async fn get_ipv4_ssh_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_ssh_service().await;
HttpResponse::Ok().json(web::Json(list))
}
#[get("/ipv6/ssh_service")]
async fn get_ipv6_ssh_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_ssh_service().await;
HttpResponse::Ok().json(web::Json(list))
}
#[put("/ipv4/ssh_service")]
async fn add_ipv4_ssh_service(ip_addr: web::Json<SocketAddrV4>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.add_ipv4_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[put("/ipv6/ssh_service")]
async fn add_ipv6_ssh_service(ip_addr: web::Json<SocketAddrV6>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.add_ipv6_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv4/ssh_service")]
async fn remove_ipv4_ssh_service(ip_addr: web::Json<SocketAddrV4>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.remove_ipv4_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv6/ssh_service")]
async fn remove_ipv6_ssh_service(ip_addr: web::Json<SocketAddrV6>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.remove_ipv6_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[get("/ipv4/ssh_white_list")]
async fn get_ipv4_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_ssh_white_list().await;
HttpResponse::Ok().json(web::Json(list))
}
#[get("/ipv6/ssh_white_list")]
async fn get_ipv6_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_ssh_white_list().await;
HttpResponse::Ok().json(web::Json(list))
}
#[put("/ipv4/ssh_white_list")]
async fn add_ipv4_ssh_white_list(ip_addr: web::Json<Ipv4Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.add_ipv4_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[put("/ipv6/ssh_white_list")]
async fn add_ipv6_ssh_white_list(ip_addr: web::Json<Ipv6Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.add_ipv6_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv4/ssh_white_list")]
async fn remove_ipv4_ssh_white_list(ip_addr: web::Json<Ipv4Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.remove_ipv4_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv6/ssh_white_list")]
async fn remove_ipv6_ssh_white_list(ip_addr: web::Json<Ipv6Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.remove_ipv6_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[get("/ipv4/ssh_black_list")]
async fn get_ipv4_ssh_black_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_ssh_black_list().await;
HttpResponse::Ok().json(web::Json(list))
}
#[get("/ipv6/ssh_black_list")]
async fn get_ipv6_ssh_black_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_ssh_black_list().await;
HttpResponse::Ok().json(web::Json(list))
}
#[put("/ipv4/ssh_black_list")]
async fn add_ipv4_ssh_black_list(ip_addr: web::Json<Ipv4Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.add_ipv4_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[put("/ipv6/ssh_black_list")]
async fn add_ipv6_ssh_black_list(ip_addr: web::Json<Ipv6Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.add_ipv6_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv4/ssh_black_list")]
async fn remove_ipv4_ssh_black_list(ip_addr: web::Json<Ipv4Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.remove_ipv4_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
#[delete("/ipv6/ssh_black_list")]
async fn remove_ipv6_ssh_black_list(ip_addr: web::Json<Ipv6Addr>, service: web::Data<ProtocolFilter>) -> impl Responder {
match service.remove_ipv6_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}

View File

@ -1,19 +0,0 @@
use actix_web::{web, HttpResponse, Responder};
use crate::core::infrastructure::statistics::FlowStatistics;
pub async fn get_all_flows(stats: web::Data<FlowStatistics>) -> impl Responder {
HttpResponse::Ok().json(stats.get_all_flows())
}
pub async fn get_top_flows(
stats: web::Data<FlowStatistics>,
path: web::Path<usize>,
) -> impl Responder {
let n = path.into_inner();
HttpResponse::Ok().json(stats.get_top_flows(n))
}
pub async fn get_summary(stats: web::Data<FlowStatistics>) -> impl Responder {
HttpResponse::Ok().json(stats.get_summary())
}

View File

@ -0,0 +1,288 @@
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use actix_web::{web, HttpResponse, Responder, Scope};
use common::model::http_method::HttpMethod;
use crate::core::ebpf::protocol_filter::ProtocolFilter;
pub fn initialize() -> Scope {
web::scope("/filter")
.service(http_scope())
.service(ssh_scope())
}
fn http_scope() -> Scope {
web::scope("/http")
.route("/ipv4", web::get().to(get_ipv4_http_service))
.route("/ipv6", web::get().to(get_ipv6_http_service))
.route("/ipv4", web::put().to(add_ipv4_http_service))
.route("/ipv6", web::put().to(add_ipv6_http_service))
.route("/ipv4", web::delete().to(remove_ipv4_http_service))
.route("/ipv6", web::delete().to(remove_ipv6_http_service))
}
fn ssh_scope() -> Scope {
web::scope("/ssh")
.route("/ipv4", web::get().to(get_ipv4_ssh_service))
.route("/ipv6", web::get().to(get_ipv6_ssh_service))
.route("/ipv4", web::put().to(add_ipv4_ssh_service))
.route("/ipv6", web::put().to(add_ipv6_ssh_service))
.route("/ipv4", web::delete().to(remove_ipv4_ssh_service))
.route("/ipv6", web::delete().to(remove_ipv6_ssh_service))
.service(ssh_whitelist_scope())
.service(ssh_blacklist_scope())
}
fn ssh_whitelist_scope() -> Scope {
web::scope("/whitelist")
.route("/status", web::get().to(is_ssh_white_list_enable))
.route("/enable", web::post().to(enable_ssh_white_list))
.route("/disable", web::post().to(disable_ssh_white_list))
.route("/ipv4", web::get().to(get_ipv4_ssh_white_list))
.route("/ipv6", web::get().to(get_ipv6_ssh_white_list))
.route("/ipv4", web::put().to(add_ipv4_ssh_white_list))
.route("/ipv6", web::put().to(add_ipv6_ssh_white_list))
.route("/ipv4", web::delete().to(remove_ipv4_ssh_white_list))
.route("/ipv6", web::delete().to(remove_ipv6_ssh_white_list))
}
fn ssh_blacklist_scope() -> Scope {
web::scope("/blacklist")
.route("/ipv4", web::get().to(get_ipv4_ssh_black_list))
.route("/ipv6", web::get().to(get_ipv6_ssh_black_list))
.route("/ipv4", web::put().to(add_ipv4_ssh_black_list))
.route("/ipv6", web::put().to(add_ipv6_ssh_black_list))
.route("/ipv4", web::delete().to(remove_ipv4_ssh_black_list))
.route("/ipv6", web::delete().to(remove_ipv6_ssh_black_list))
}
// --- HTTP service handlers ---
async fn get_ipv4_http_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_http_service().await;
HttpResponse::Ok().json(list)
}
async fn get_ipv6_http_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_http_service().await;
HttpResponse::Ok().json(list)
}
async fn add_ipv4_http_service(
payload: web::Json<(SocketAddrV4, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.add_ipv4_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn add_ipv6_http_service(
payload: web::Json<(SocketAddrV6, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.add_ipv6_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv4_http_service(
payload: web::Json<(SocketAddrV4, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.remove_ipv4_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv6_http_service(
payload: web::Json<(SocketAddrV6, Vec<HttpMethod>)>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
let (addr, methods) = payload.into_inner();
match service.remove_ipv6_http_service(addr, methods).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
// --- SSH service handlers ---
async fn get_ipv4_ssh_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_ssh_service().await;
HttpResponse::Ok().json(list)
}
async fn get_ipv6_ssh_service(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_ssh_service().await;
HttpResponse::Ok().json(list)
}
async fn add_ipv4_ssh_service(
ip_addr: web::Json<SocketAddrV4>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.add_ipv4_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn add_ipv6_ssh_service(
ip_addr: web::Json<SocketAddrV6>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.add_ipv6_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv4_ssh_service(
ip_addr: web::Json<SocketAddrV4>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.remove_ipv4_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv6_ssh_service(
ip_addr: web::Json<SocketAddrV6>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.remove_ipv6_ssh_service(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
// --- SSH whitelist handlers ---
async fn is_ssh_white_list_enable(service: web::Data<ProtocolFilter>) -> impl Responder {
let enabled = service.is_ssh_white_list_enable().await;
HttpResponse::Ok().json(enabled)
}
async fn enable_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
match service.enable_ssh_white_list().await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn disable_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
match service.disable_ssh_white_list().await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn get_ipv4_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_ssh_white_list().await;
HttpResponse::Ok().json(list)
}
async fn get_ipv6_ssh_white_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_ssh_white_list().await;
HttpResponse::Ok().json(list)
}
async fn add_ipv4_ssh_white_list(
ip_addr: web::Json<Ipv4Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.add_ipv4_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn add_ipv6_ssh_white_list(
ip_addr: web::Json<Ipv6Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.add_ipv6_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv4_ssh_white_list(
ip_addr: web::Json<Ipv4Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.remove_ipv4_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv6_ssh_white_list(
ip_addr: web::Json<Ipv6Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.remove_ipv6_ssh_white_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
// --- SSH blacklist handlers ---
async fn get_ipv4_ssh_black_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv4_ssh_black_list().await;
HttpResponse::Ok().json(list)
}
async fn get_ipv6_ssh_black_list(service: web::Data<ProtocolFilter>) -> impl Responder {
let list = service.get_ipv6_ssh_black_list().await;
HttpResponse::Ok().json(list)
}
async fn add_ipv4_ssh_black_list(
ip_addr: web::Json<Ipv4Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.add_ipv4_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn add_ipv6_ssh_black_list(
ip_addr: web::Json<Ipv6Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.add_ipv6_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv4_ssh_black_list(
ip_addr: web::Json<Ipv4Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.remove_ipv4_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}
async fn remove_ipv6_ssh_black_list(
ip_addr: web::Json<Ipv6Addr>,
service: web::Data<ProtocolFilter>,
) -> impl Responder {
match service.remove_ipv6_ssh_black_list(ip_addr.into_inner()).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})),
}
}

View File

@ -1,35 +1,19 @@
use actix_web::{get, web, HttpRequest, HttpResponse, Responder, Scope};
use actix_web::{web, HttpResponse, Responder, Scope};
use crate::core::infrastructure::health::SystemHealth;
use crate::web::websocket::health_websocket;
pub fn initialize() -> Scope {
web::scope("/health")
.service(get_current_metrics)
.service(get_health_status)
.service(websocket_metrics)
.route("/metrics", web::get().to(get_current_metrics))
.route("/status", web::get().to(get_health_status))
}
#[get("/metrics")]
async fn get_current_metrics(health: web::Data<SystemHealth>) -> impl Responder {
let metrics = health.get_current_metrics().await;
HttpResponse::Ok().json(metrics)
}
#[get("/status")]
async fn get_health_status(health: web::Data<SystemHealth>) -> impl Responder {
let status = health.is_system_healthy().await;
HttpResponse::Ok().json(status)
}
#[get("/websocket/metrics")]
async fn websocket_metrics(
req: HttpRequest,
stream: web::Payload,
health: web::Data<SystemHealth>,
) -> impl Responder {
match health_websocket::websocket_system_health(req, stream, health).await {
Ok(response) => response,
Err(err) => HttpResponse::InternalServerError().body(format!("WebSocket error: {}", err)),
}
}

View File

@ -1,14 +0,0 @@
use actix_web::{get, web, HttpResponse, Responder, Scope};
use crate::utils::boot_time::boot_time;
pub fn initialize() -> Scope {
web::scope("/misc")
.service(get_boot_time)
}
#[get("/boot_time")]
async fn get_boot_time() -> impl Responder {
let boot_time = boot_time();
HttpResponse::Ok().json(boot_time)
}

View File

@ -0,0 +1,12 @@
use actix_web::{web, HttpResponse, Responder, Scope};
pub fn initialize() -> Scope {
web::scope("/ml")
.route("/status", web::get().to(get_status))
}
async fn get_status() -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"active": true
}))
}

View File

@ -1,22 +0,0 @@
use crate::core::ml::alert::MLAlert;
use crate::web::websocket::alert_websocket;
use actix_web::{get, web, HttpRequest, HttpResponse, Responder, Scope};
pub fn initialize() -> Scope {
web::scope("/ml")
.service(websocket_alert)
}
#[get("/websocket/alert")]
async fn websocket_alert(
req: HttpRequest,
stream: web::Payload,
ai: web::Data<MLAlert>,
) -> impl Responder {
match alert_websocket::websocket_alert(req, stream, ai).await {
Ok(response) => response,
Err(err) => {
HttpResponse::InternalServerError().json(serde_json::json!({"error": format!("{}", err)}))
}
}
}

View File

@ -1,5 +1,9 @@
pub mod control;
pub mod default;
pub mod misc;
pub mod ml_alert;
pub mod acl;
pub mod filter;
pub mod rate_limit;
pub mod stats;
pub mod health;
pub mod ml;
pub mod system;
pub mod default;
pub mod ws;

View File

@ -0,0 +1,42 @@
use actix_web::{web, HttpResponse, Responder, Scope};
use serde::{Deserialize, Serialize};
use crate::core::ebpf::rate_limit::RateLimitConfig;
#[derive(Serialize, Deserialize)]
pub struct RateLimitSettings {
pub packet_rate: Option<u64>,
pub syn_rate: Option<u64>,
pub udp_rate: Option<u64>,
pub dns_rate: Option<u64>,
pub window_ns: Option<u64>,
}
pub fn initialize() -> Scope {
web::scope("/rate-limit")
.route("/config", web::get().to(get_config))
.route("/config", web::put().to(set_config))
}
async fn get_config() -> impl Responder {
HttpResponse::Ok().json(RateLimitSettings {
packet_rate: Some(common::model::rate_limit::DEFAULT_PACKET_RATE),
syn_rate: Some(common::model::rate_limit::DEFAULT_SYN_RATE),
udp_rate: Some(common::model::rate_limit::DEFAULT_UDP_RATE),
dns_rate: Some(common::model::rate_limit::DEFAULT_DNS_RATE),
window_ns: Some(common::model::rate_limit::DEFAULT_WINDOW_NS),
})
}
async fn set_config(
settings: web::Json<RateLimitSettings>,
config: web::Data<RateLimitConfig>,
) -> impl Responder {
let s = settings.into_inner();
if let Some(v) = s.packet_rate { let _ = config.set_packet_rate(v); }
if let Some(v) = s.syn_rate { let _ = config.set_syn_rate(v); }
if let Some(v) = s.udp_rate { let _ = config.set_udp_rate(v); }
if let Some(v) = s.dns_rate { let _ = config.set_dns_rate(v); }
if let Some(v) = s.window_ns { let _ = config.set_window_ns(v); }
HttpResponse::Ok().json(serde_json::json!({"status": "ok"}))
}

View File

@ -0,0 +1,26 @@
use actix_web::{web, HttpResponse, Responder, Scope};
use crate::core::infrastructure::statistics::FlowStatistics;
pub fn initialize() -> Scope {
web::scope("/stats")
.route("/flows", web::get().to(get_all_flows))
.route("/flows/top/{n}", web::get().to(get_top_flows))
.route("/summary", web::get().to(get_summary))
}
async fn get_all_flows(stats: web::Data<FlowStatistics>) -> impl Responder {
HttpResponse::Ok().json(stats.get_all_flows())
}
async fn get_top_flows(
stats: web::Data<FlowStatistics>,
path: web::Path<usize>,
) -> impl Responder {
let n = path.into_inner();
HttpResponse::Ok().json(stats.get_top_flows(n))
}
async fn get_summary(stats: web::Data<FlowStatistics>) -> impl Responder {
HttpResponse::Ok().json(stats.get_summary())
}

View File

@ -0,0 +1,10 @@
use actix_web::{web, HttpResponse, Responder, Scope};
pub fn initialize() -> Scope {
web::scope("/system")
.route("/boot-time", web::get().to(get_boot_time))
}
async fn get_boot_time() -> impl Responder {
HttpResponse::Ok().json(crate::utils::boot_time::boot_time())
}

View File

@ -0,0 +1,46 @@
use actix_web::{web, HttpRequest, HttpResponse, Responder, Scope};
use crate::core::infrastructure::health::SystemHealth;
use crate::core::infrastructure::statistics::FlowStatistics;
use crate::core::ml::alert::MLAlert;
use crate::web::websocket::{alert_websocket, flow_websocket, health_websocket};
pub fn initialize() -> Scope {
web::scope("/ws")
.route("/health", web::get().to(health_ws))
.route("/alerts", web::get().to(alerts_ws))
.route("/flows", web::get().to(flows_ws))
}
async fn health_ws(
req: HttpRequest,
stream: web::Payload,
health: web::Data<SystemHealth>,
) -> impl Responder {
match health_websocket::websocket_system_health(req, stream, health).await {
Ok(response) => response,
Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": format!("WebSocket error: {}", err)})),
}
}
async fn alerts_ws(
req: HttpRequest,
stream: web::Payload,
ai: web::Data<MLAlert>,
) -> impl Responder {
match alert_websocket::websocket_alert(req, stream, ai).await {
Ok(response) => response,
Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": format!("WebSocket error: {}", err)})),
}
}
async fn flows_ws(
req: HttpRequest,
stream: web::Payload,
stats: web::Data<FlowStatistics>,
) -> impl Responder {
match flow_websocket::flow_stats_ws(req, stream, stats).await {
Ok(response) => response,
Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": format!("WebSocket error: {}", err)})),
}
}