From 7c233b5f776a8b03c27458a823999191e2796821 Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:14:26 +0000 Subject: [PATCH] feat(proxy): implement log setting, proxy setting, rewrite rule, server block, and SSL certificate services - Added LogSettingService with CRUD operations for log settings. - Introduced ProxySettingService for managing proxy settings with CRUD functionality. - Created RewriteRuleService to handle rewrite rules with full CRUD capabilities. - Implemented ServerBlockService for managing server blocks, including relationships with access rules and location blocks. - Added SslCertificateService for managing SSL certificates with create, read, update, and delete operations. - Updated types.rs to include new service configurations and ensure proper merging of overrides. - Enhanced existing tests to cover new functionalities and ensure correctness. --- .../src/service/proxy/access_rule/mod.rs | 155 +++++++++++++ .../src/service/proxy/cache_zone/mod.rs | 115 ++++++++++ .../service/proxy/config_inheritance/mod.rs | 143 ++++++++++++ .../src/service/proxy/limit_rule/mod.rs | 129 +++++++++++ .../src/service/proxy/limit_zone/mod.rs | 115 ++++++++++ .../src/service/proxy/location_block/mod.rs | 205 ++++++++++++++++++ .../src/service/proxy/log_setting/mod.rs | 124 +++++++++++ apps/nxmesh-master/src/service/proxy/mod.rs | 14 +- .../src/service/proxy/nginx/mod.rs | 35 ++- .../src/service/proxy/proxy_setting/mod.rs | 148 +++++++++++++ .../src/service/proxy/rewrite_rule/mod.rs | 143 ++++++++++++ .../src/service/proxy/server_block/mod.rs | 201 +++++++++++++++++ .../src/service/proxy/ssl_certificate/mod.rs | 122 +++++++++++ apps/nxmesh-master/src/service/proxy/types.rs | 79 ++++--- .../src/service/proxy/upstream/mod.rs | 140 ++++++++++++ 15 files changed, 1833 insertions(+), 35 deletions(-) create mode 100644 apps/nxmesh-master/src/service/proxy/access_rule/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/cache_zone/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/config_inheritance/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/limit_rule/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/limit_zone/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/location_block/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/log_setting/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/proxy_setting/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/rewrite_rule/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/server_block/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/ssl_certificate/mod.rs create mode 100644 apps/nxmesh-master/src/service/proxy/upstream/mod.rs diff --git a/apps/nxmesh-master/src/service/proxy/access_rule/mod.rs b/apps/nxmesh-master/src/service/proxy/access_rule/mod.rs new file mode 100644 index 0000000..f6cea0f --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/access_rule/mod.rs @@ -0,0 +1,155 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{AccessRuleConfig, ProxyServiceError, ProxyServiceResult}; + +pub struct CreateAccessRuleParams { + pub server_id: Option, + pub location_id: Option, + pub r#type: String, + pub ip_cidr: String, + pub description: Option, + pub priority: i32, + pub override_of_id: Option, +} + +pub struct UpdateAccessRuleParams { + pub server_id: Option>, + pub location_id: Option>, + pub r#type: Option, + pub ip_cidr: Option, + pub description: Option>, + pub priority: Option, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait AccessRuleService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_server(&self, server_id: Uuid) -> ProxyServiceResult>; + async fn list_by_location( + &self, + location_id: Uuid, + ) -> ProxyServiceResult>; + async fn create(&self, params: CreateAccessRuleParams) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateAccessRuleParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct AccessRuleServiceImpl { + db: DatabaseConnection, +} + +impl AccessRuleServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl AccessRuleService for AccessRuleServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::access_rule; + + let model = access_rule::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list_by_server(&self, server_id: Uuid) -> ProxyServiceResult> { + use crate::db::entities::access_rule; + + let models = access_rule::Entity::find() + .filter(access_rule::Column::ServerId.eq(server_id)) + .all(&self.db) + .await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn list_by_location( + &self, + location_id: Uuid, + ) -> ProxyServiceResult> { + use crate::db::entities::access_rule; + + let models = access_rule::Entity::find() + .filter(access_rule::Column::LocationId.eq(location_id)) + .all(&self.db) + .await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create(&self, params: CreateAccessRuleParams) -> ProxyServiceResult { + use crate::db::entities::access_rule::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + server_id: Set(params.server_id), + location_id: Set(params.location_id), + r#type: Set(params.r#type), + ip_cidr: Set(params.ip_cidr), + description: Set(params.description), + priority: Set(params.priority), + is_deleted: Set(false), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateAccessRuleParams, + ) -> ProxyServiceResult { + use crate::db::entities::access_rule::{ActiveModel, Entity as AccessRuleEntity}; + + let existing = AccessRuleEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(server_id) = params.server_id { + model.server_id = Set(server_id); + } + if let Some(location_id) = params.location_id { + model.location_id = Set(location_id); + } + if let Some(r#type) = params.r#type { + model.r#type = Set(r#type); + } + if let Some(ip_cidr) = params.ip_cidr { + model.ip_cidr = Set(ip_cidr); + } + if let Some(description) = params.description { + model.description = Set(description); + } + if let Some(priority) = params.priority { + model.priority = Set(priority); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::access_rule::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/cache_zone/mod.rs b/apps/nxmesh-master/src/service/proxy/cache_zone/mod.rs new file mode 100644 index 0000000..8965442 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/cache_zone/mod.rs @@ -0,0 +1,115 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{CacheZoneConfig, ProxyServiceError, ProxyServiceResult}; + +pub struct CreateCacheZoneParams { + pub name: String, + pub path: String, + pub size_limit: String, + pub override_of_id: Option, +} + +pub struct UpdateCacheZoneParams { + pub name: Option, + pub path: Option, + pub size_limit: Option, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait CacheZoneService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list(&self) -> ProxyServiceResult>; + async fn create(&self, params: CreateCacheZoneParams) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateCacheZoneParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct CacheZoneServiceImpl { + db: DatabaseConnection, +} + +impl CacheZoneServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl CacheZoneService for CacheZoneServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::cache_zone; + + let model = cache_zone::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list(&self) -> ProxyServiceResult> { + use crate::db::entities::cache_zone; + + let models = cache_zone::Entity::find().all(&self.db).await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create(&self, params: CreateCacheZoneParams) -> ProxyServiceResult { + use crate::db::entities::cache_zone::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + name: Set(params.name), + path: Set(params.path), + size_limit: Set(params.size_limit), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateCacheZoneParams, + ) -> ProxyServiceResult { + use crate::db::entities::cache_zone::{ActiveModel, Entity as CacheZoneEntity}; + + let existing = CacheZoneEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(name) = params.name { + model.name = Set(name); + } + if let Some(path) = params.path { + model.path = Set(path); + } + if let Some(size_limit) = params.size_limit { + model.size_limit = Set(size_limit); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::cache_zone::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/config_inheritance/mod.rs b/apps/nxmesh-master/src/service/proxy/config_inheritance/mod.rs new file mode 100644 index 0000000..57d2308 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/config_inheritance/mod.rs @@ -0,0 +1,143 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::ProxyServiceResult; + +pub struct AddInheritanceParams { + pub child_config_id: Uuid, + pub parent_config_id: Uuid, + pub priority: Option, +} + +pub struct ConfigInheritanceRecord { + pub id: Uuid, + pub child_config_id: Uuid, + pub parent_config_id: Uuid, + pub priority: Option, + pub applied_at: chrono::NaiveDateTime, +} + +#[async_trait::async_trait] +pub trait ConfigInheritanceService: Send + Sync + 'static { + async fn add( + &self, + params: AddInheritanceParams, + ) -> ProxyServiceResult; + async fn remove( + &self, + child_config_id: Uuid, + parent_config_id: Uuid, + ) -> ProxyServiceResult; + async fn list_parents( + &self, + child_config_id: Uuid, + ) -> ProxyServiceResult>; + async fn list_children( + &self, + parent_config_id: Uuid, + ) -> ProxyServiceResult>; +} + +pub(crate) struct ConfigInheritanceServiceImpl { + db: DatabaseConnection, +} + +impl ConfigInheritanceServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl ConfigInheritanceService for ConfigInheritanceServiceImpl { + async fn add( + &self, + params: AddInheritanceParams, + ) -> ProxyServiceResult { + use crate::db::entities::config_inheritance::ActiveModel; + + let now = chrono::Utc::now().naive_utc(); + let model = ActiveModel { + id: Set(Uuid::new_v4()), + child_config_id: Set(params.child_config_id), + parent_config_id: Set(params.parent_config_id), + priority: Set(params.priority), + applied_at: Set(now), + }; + + let result = model.insert(&self.db).await?; + Ok(ConfigInheritanceRecord { + id: result.id, + child_config_id: result.child_config_id, + parent_config_id: result.parent_config_id, + priority: result.priority, + applied_at: result.applied_at, + }) + } + + async fn remove( + &self, + child_config_id: Uuid, + parent_config_id: Uuid, + ) -> ProxyServiceResult { + use crate::db::entities::config_inheritance::{Column, Entity as ConfigInheritanceEntity}; + use sea_orm::Condition; + + let result = ConfigInheritanceEntity::delete_many() + .filter( + Condition::all() + .add(Column::ChildConfigId.eq(child_config_id)) + .add(Column::ParentConfigId.eq(parent_config_id)), + ) + .exec(&self.db) + .await?; + + Ok(result.rows_affected > 0) + } + + async fn list_parents( + &self, + child_config_id: Uuid, + ) -> ProxyServiceResult> { + use crate::db::entities::config_inheritance; + + let results = config_inheritance::Entity::find() + .filter(config_inheritance::Column::ChildConfigId.eq(child_config_id)) + .all(&self.db) + .await?; + + Ok(results + .into_iter() + .map(|m| ConfigInheritanceRecord { + id: m.id, + child_config_id: m.child_config_id, + parent_config_id: m.parent_config_id, + priority: m.priority, + applied_at: m.applied_at, + }) + .collect()) + } + + async fn list_children( + &self, + parent_config_id: Uuid, + ) -> ProxyServiceResult> { + use crate::db::entities::config_inheritance; + + let results = config_inheritance::Entity::find() + .filter(config_inheritance::Column::ParentConfigId.eq(parent_config_id)) + .all(&self.db) + .await?; + + Ok(results + .into_iter() + .map(|m| ConfigInheritanceRecord { + id: m.id, + child_config_id: m.child_config_id, + parent_config_id: m.parent_config_id, + priority: m.priority, + applied_at: m.applied_at, + }) + .collect()) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/limit_rule/mod.rs b/apps/nxmesh-master/src/service/proxy/limit_rule/mod.rs new file mode 100644 index 0000000..2762382 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/limit_rule/mod.rs @@ -0,0 +1,129 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{LimitRuleConfig, ProxyServiceError, ProxyServiceResult}; + +pub struct CreateLimitRuleParams { + pub location_id: Uuid, + pub zone_id: Uuid, + pub burst: Option, + pub nodelay: Option, + pub override_of_id: Option, +} + +pub struct UpdateLimitRuleParams { + pub location_id: Option, + pub zone_id: Option, + pub burst: Option>, + pub nodelay: Option>, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait LimitRuleService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_location(&self, location_id: Uuid) + -> ProxyServiceResult>; + async fn create(&self, params: CreateLimitRuleParams) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateLimitRuleParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct LimitRuleServiceImpl { + db: DatabaseConnection, +} + +impl LimitRuleServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl LimitRuleService for LimitRuleServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::limit_rule; + + let model = limit_rule::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list_by_location( + &self, + location_id: Uuid, + ) -> ProxyServiceResult> { + use crate::db::entities::limit_rule; + + let models = limit_rule::Entity::find() + .filter(limit_rule::Column::LocationId.eq(location_id)) + .all(&self.db) + .await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create(&self, params: CreateLimitRuleParams) -> ProxyServiceResult { + use crate::db::entities::limit_rule::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + location_id: Set(params.location_id), + zone_id: Set(params.zone_id), + burst: Set(params.burst), + nodelay: Set(params.nodelay), + is_deleted: Set(false), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateLimitRuleParams, + ) -> ProxyServiceResult { + use crate::db::entities::limit_rule::{ActiveModel, Entity as LimitRuleEntity}; + + let existing = LimitRuleEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(location_id) = params.location_id { + model.location_id = Set(location_id); + } + if let Some(zone_id) = params.zone_id { + model.zone_id = Set(zone_id); + } + if let Some(burst) = params.burst { + model.burst = Set(burst); + } + if let Some(nodelay) = params.nodelay { + model.nodelay = Set(nodelay); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::limit_rule::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/limit_zone/mod.rs b/apps/nxmesh-master/src/service/proxy/limit_zone/mod.rs new file mode 100644 index 0000000..2003ed4 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/limit_zone/mod.rs @@ -0,0 +1,115 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{LimitZoneConfig, ProxyServiceError, ProxyServiceResult}; + +pub struct CreateLimitZoneParams { + pub name: String, + pub key: String, + pub rate: String, + pub override_of_id: Option, +} + +pub struct UpdateLimitZoneParams { + pub name: Option, + pub key: Option, + pub rate: Option, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait LimitZoneService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list(&self) -> ProxyServiceResult>; + async fn create(&self, params: CreateLimitZoneParams) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateLimitZoneParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct LimitZoneServiceImpl { + db: DatabaseConnection, +} + +impl LimitZoneServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl LimitZoneService for LimitZoneServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::limit_zone; + + let model = limit_zone::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list(&self) -> ProxyServiceResult> { + use crate::db::entities::limit_zone; + + let models = limit_zone::Entity::find().all(&self.db).await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create(&self, params: CreateLimitZoneParams) -> ProxyServiceResult { + use crate::db::entities::limit_zone::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + name: Set(params.name), + key: Set(params.key), + rate: Set(params.rate), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateLimitZoneParams, + ) -> ProxyServiceResult { + use crate::db::entities::limit_zone::{ActiveModel, Entity as LimitZoneEntity}; + + let existing = LimitZoneEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(name) = params.name { + model.name = Set(name); + } + if let Some(key) = params.key { + model.key = Set(key); + } + if let Some(rate) = params.rate { + model.rate = Set(rate); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::limit_zone::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/location_block/mod.rs b/apps/nxmesh-master/src/service/proxy/location_block/mod.rs new file mode 100644 index 0000000..5154818 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/location_block/mod.rs @@ -0,0 +1,205 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{ + LocationBlockConfig, OverrideRef, ProxyServiceError, ProxyServiceResult, +}; + +pub struct CreateLocationBlockParams { + pub server_id: Uuid, + pub path_pattern: String, + pub proxy_pass_upstream_id: Option, + pub metadata: Option, + pub override_of_id: Option, +} + +pub struct UpdateLocationBlockParams { + pub server_id: Option, + pub path_pattern: Option, + pub proxy_pass_upstream_id: Option>, + pub metadata: Option>, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait LocationBlockService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_server(&self, server_id: Uuid) + -> ProxyServiceResult>; + async fn create( + &self, + params: CreateLocationBlockParams, + ) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateLocationBlockParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct LocationBlockServiceImpl { + db: DatabaseConnection, +} + +impl LocationBlockServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } + + async fn build_with_children( + &self, + model: crate::db::entities::location_block::Model, + ) -> ProxyServiceResult { + use crate::db::entities::{access_rule, limit_rule, proxy_setting, rewrite_rule}; + + let access_rules = access_rule::Entity::find() + .filter(access_rule::Column::LocationId.eq(model.id)) + .all(&self.db) + .await? + .into_iter() + .map(|a| OverrideRef { + id: a.id, + override_of_id: a.override_of_id, + }) + .collect(); + + let limit_rules = limit_rule::Entity::find() + .filter(limit_rule::Column::LocationId.eq(model.id)) + .all(&self.db) + .await? + .into_iter() + .map(|l| OverrideRef { + id: l.id, + override_of_id: l.override_of_id, + }) + .collect(); + + let proxy_settings = proxy_setting::Entity::find() + .filter(proxy_setting::Column::LocationId.eq(model.id)) + .all(&self.db) + .await? + .into_iter() + .map(|p| OverrideRef { + id: p.id, + override_of_id: p.override_of_id, + }) + .collect(); + + let rewrite_rules = rewrite_rule::Entity::find() + .filter(rewrite_rule::Column::LocationId.eq(model.id)) + .all(&self.db) + .await? + .into_iter() + .map(|r| OverrideRef { + id: r.id, + override_of_id: r.override_of_id, + }) + .collect(); + + Ok(LocationBlockConfig { + id: model.id, + server_id: model.server_id, + path_pattern: model.path_pattern, + proxy_pass_upstream_id: model.proxy_pass_upstream_id, + metadata: model.metadata, + override_of_id: model.override_of_id, + access_rules, + limit_rules, + proxy_settings, + rewrite_rules, + }) + } +} + +#[async_trait::async_trait] +impl LocationBlockService for LocationBlockServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::location_block; + + let model = location_block::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + self.build_with_children(model).await + } + + async fn list_by_server( + &self, + server_id: Uuid, + ) -> ProxyServiceResult> { + use crate::db::entities::location_block; + + let models = location_block::Entity::find() + .filter(location_block::Column::ServerId.eq(server_id)) + .all(&self.db) + .await?; + + let mut results = Vec::with_capacity(models.len()); + for m in models { + results.push(self.build_with_children(m).await?); + } + Ok(results) + } + + async fn create( + &self, + params: CreateLocationBlockParams, + ) -> ProxyServiceResult { + use crate::db::entities::location_block::ActiveModel; + + let id = Uuid::new_v4(); + let model = ActiveModel { + id: Set(id), + server_id: Set(params.server_id), + path_pattern: Set(params.path_pattern), + proxy_pass_upstream_id: Set(params.proxy_pass_upstream_id), + metadata: Set(params.metadata), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + self.build_with_children(result).await + } + + async fn update( + &self, + id: Uuid, + params: UpdateLocationBlockParams, + ) -> ProxyServiceResult { + use crate::db::entities::location_block::{ActiveModel, Entity as LocationBlockEntity}; + + let existing = LocationBlockEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(server_id) = params.server_id { + model.server_id = Set(server_id); + } + if let Some(path_pattern) = params.path_pattern { + model.path_pattern = Set(path_pattern); + } + if let Some(proxy_pass_upstream_id) = params.proxy_pass_upstream_id { + model.proxy_pass_upstream_id = Set(proxy_pass_upstream_id); + } + if let Some(metadata) = params.metadata { + model.metadata = Set(metadata); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + self.build_with_children(result).await + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::location_block::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/log_setting/mod.rs b/apps/nxmesh-master/src/service/proxy/log_setting/mod.rs new file mode 100644 index 0000000..a0d70f9 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/log_setting/mod.rs @@ -0,0 +1,124 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{LogSettingConfig, ProxyServiceError, ProxyServiceResult}; + +pub struct CreateLogSettingParams { + pub server_id: Uuid, + pub access_log_path: Option, + pub error_log_path: Option, + pub log_level: Option, + pub override_of_id: Option, +} + +pub struct UpdateLogSettingParams { + pub server_id: Option, + pub access_log_path: Option>, + pub error_log_path: Option>, + pub log_level: Option>, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait LogSettingService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_server(&self, server_id: Uuid) -> ProxyServiceResult>; + async fn create(&self, params: CreateLogSettingParams) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateLogSettingParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct LogSettingServiceImpl { + db: DatabaseConnection, +} + +impl LogSettingServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl LogSettingService for LogSettingServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::log_setting; + + let model = log_setting::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list_by_server(&self, server_id: Uuid) -> ProxyServiceResult> { + use crate::db::entities::log_setting; + + let models = log_setting::Entity::find() + .filter(log_setting::Column::ServerId.eq(server_id)) + .all(&self.db) + .await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create(&self, params: CreateLogSettingParams) -> ProxyServiceResult { + use crate::db::entities::log_setting::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + server_id: Set(params.server_id), + access_log_path: Set(params.access_log_path), + error_log_path: Set(params.error_log_path), + log_level: Set(params.log_level), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateLogSettingParams, + ) -> ProxyServiceResult { + use crate::db::entities::log_setting::{ActiveModel, Entity as LogSettingEntity}; + + let existing = LogSettingEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(server_id) = params.server_id { + model.server_id = Set(server_id); + } + if let Some(access_log_path) = params.access_log_path { + model.access_log_path = Set(access_log_path); + } + if let Some(error_log_path) = params.error_log_path { + model.error_log_path = Set(error_log_path); + } + if let Some(log_level) = params.log_level { + model.log_level = Set(log_level); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::log_setting::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/mod.rs b/apps/nxmesh-master/src/service/proxy/mod.rs index 0ca5748..ce6d5b2 100644 --- a/apps/nxmesh-master/src/service/proxy/mod.rs +++ b/apps/nxmesh-master/src/service/proxy/mod.rs @@ -1,10 +1,22 @@ use crate::service::proxy::types::{ AgentConfigBinding, CreateProxyConfigParams, ProxyConfig, ProxyConfigSummary, - ProxyServiceResult, ProxyType, UpdateProxyConfigParams, + ProxyServiceResult, UpdateProxyConfigParams, }; +pub(crate) mod access_rule; +pub(crate) mod cache_zone; +pub(crate) mod config_inheritance; +pub(crate) mod limit_rule; +pub(crate) mod limit_zone; +pub(crate) mod location_block; +pub(crate) mod log_setting; pub(crate) mod nginx; +pub(crate) mod proxy_setting; pub(crate) mod repo; +pub(crate) mod rewrite_rule; +pub(crate) mod server_block; +pub(crate) mod ssl_certificate; +pub(crate) mod upstream; pub mod service; pub mod types; diff --git a/apps/nxmesh-master/src/service/proxy/nginx/mod.rs b/apps/nxmesh-master/src/service/proxy/nginx/mod.rs index 3752abc..96ef86f 100644 --- a/apps/nxmesh-master/src/service/proxy/nginx/mod.rs +++ b/apps/nxmesh-master/src/service/proxy/nginx/mod.rs @@ -187,6 +187,7 @@ impl ProxyConfigRenderer for NginxConfigRenderer { } #[cfg(test)] +#[allow(clippy::unwrap_used)] mod tests { use super::*; use crate::service::proxy::types::*; @@ -271,13 +272,31 @@ mod tests { let renderer = NginxConfigRenderer; let output = renderer.render(&config); - assert!(output.contains("upstream backend {"), "should contain upstream block"); - assert!(output.contains("server 127.0.0.1:3000;"), "should contain upstream server"); + assert!( + output.contains("upstream backend {"), + "should contain upstream block" + ); + assert!( + output.contains("server 127.0.0.1:3000;"), + "should contain upstream server" + ); assert!(output.contains("server {"), "should contain server block"); - assert!(output.contains("listen 80;"), "should contain listen directive"); - assert!(output.contains("server_name example.com;"), "should contain server name"); - assert!(output.contains("location /api {"), "should contain location block"); - assert!(output.contains("proxy_pass http://backend;"), "should contain proxy pass"); + assert!( + output.contains("listen 80;"), + "should contain listen directive" + ); + assert!( + output.contains("server_name example.com;"), + "should contain server name" + ); + assert!( + output.contains("location /api {"), + "should contain location block" + ); + assert!( + output.contains("proxy_pass http://backend;"), + "should contain proxy pass" + ); } #[test] @@ -296,7 +315,9 @@ mod tests { ); let renderer = NginxConfigRenderer; let output = renderer.render(&config); - assert!(output.contains("proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m;")); + assert!( + output.contains("proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m;") + ); } #[test] diff --git a/apps/nxmesh-master/src/service/proxy/proxy_setting/mod.rs b/apps/nxmesh-master/src/service/proxy/proxy_setting/mod.rs new file mode 100644 index 0000000..b6df5c2 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/proxy_setting/mod.rs @@ -0,0 +1,148 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{ProxyServiceError, ProxyServiceResult, ProxySettingConfig}; + +pub struct CreateProxySettingParams { + pub location_id: Uuid, + pub read_timeout: Option, + pub connect_timeout: Option, + pub buffer_size: Option, + pub cache_enabled: Option, + pub cache_zone: Option, + pub override_of_id: Option, +} + +pub struct UpdateProxySettingParams { + pub location_id: Option, + pub read_timeout: Option>, + pub connect_timeout: Option>, + pub buffer_size: Option>, + pub cache_enabled: Option>, + pub cache_zone: Option>, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait ProxySettingService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_location( + &self, + location_id: Uuid, + ) -> ProxyServiceResult>; + async fn create( + &self, + params: CreateProxySettingParams, + ) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateProxySettingParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct ProxySettingServiceImpl { + db: DatabaseConnection, +} + +impl ProxySettingServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl ProxySettingService for ProxySettingServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::proxy_setting; + + let model = proxy_setting::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list_by_location( + &self, + location_id: Uuid, + ) -> ProxyServiceResult> { + use crate::db::entities::proxy_setting; + + let models = proxy_setting::Entity::find() + .filter(proxy_setting::Column::LocationId.eq(location_id)) + .all(&self.db) + .await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create( + &self, + params: CreateProxySettingParams, + ) -> ProxyServiceResult { + use crate::db::entities::proxy_setting::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + location_id: Set(params.location_id), + read_timeout: Set(params.read_timeout), + connect_timeout: Set(params.connect_timeout), + buffer_size: Set(params.buffer_size), + cache_enabled: Set(params.cache_enabled), + cache_zone: Set(params.cache_zone), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateProxySettingParams, + ) -> ProxyServiceResult { + use crate::db::entities::proxy_setting::{ActiveModel, Entity as ProxySettingEntity}; + + let existing = ProxySettingEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(location_id) = params.location_id { + model.location_id = Set(location_id); + } + if let Some(read_timeout) = params.read_timeout { + model.read_timeout = Set(read_timeout); + } + if let Some(connect_timeout) = params.connect_timeout { + model.connect_timeout = Set(connect_timeout); + } + if let Some(buffer_size) = params.buffer_size { + model.buffer_size = Set(buffer_size); + } + if let Some(cache_enabled) = params.cache_enabled { + model.cache_enabled = Set(cache_enabled); + } + if let Some(cache_zone) = params.cache_zone { + model.cache_zone = Set(cache_zone); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::proxy_setting::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/rewrite_rule/mod.rs b/apps/nxmesh-master/src/service/proxy/rewrite_rule/mod.rs new file mode 100644 index 0000000..cf9a8a1 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/rewrite_rule/mod.rs @@ -0,0 +1,143 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{ProxyServiceError, ProxyServiceResult, RewriteRuleConfig}; + +pub struct CreateRewriteRuleParams { + pub location_id: Uuid, + pub pattern: String, + pub replacement: String, + pub flag: Option, + pub priority: i32, + pub override_of_id: Option, +} + +pub struct UpdateRewriteRuleParams { + pub location_id: Option, + pub pattern: Option, + pub replacement: Option, + pub flag: Option>, + pub priority: Option, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait RewriteRuleService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_location( + &self, + location_id: Uuid, + ) -> ProxyServiceResult>; + async fn create( + &self, + params: CreateRewriteRuleParams, + ) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateRewriteRuleParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct RewriteRuleServiceImpl { + db: DatabaseConnection, +} + +impl RewriteRuleServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl RewriteRuleService for RewriteRuleServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::rewrite_rule; + + let model = rewrite_rule::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list_by_location( + &self, + location_id: Uuid, + ) -> ProxyServiceResult> { + use crate::db::entities::rewrite_rule; + + let models = rewrite_rule::Entity::find() + .filter(rewrite_rule::Column::LocationId.eq(location_id)) + .all(&self.db) + .await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create( + &self, + params: CreateRewriteRuleParams, + ) -> ProxyServiceResult { + use crate::db::entities::rewrite_rule::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + location_id: Set(params.location_id), + pattern: Set(params.pattern), + replacement: Set(params.replacement), + flag: Set(params.flag), + priority: Set(params.priority), + is_deleted: Set(false), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateRewriteRuleParams, + ) -> ProxyServiceResult { + use crate::db::entities::rewrite_rule::{ActiveModel, Entity as RewriteRuleEntity}; + + let existing = RewriteRuleEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(location_id) = params.location_id { + model.location_id = Set(location_id); + } + if let Some(pattern) = params.pattern { + model.pattern = Set(pattern); + } + if let Some(replacement) = params.replacement { + model.replacement = Set(replacement); + } + if let Some(flag) = params.flag { + model.flag = Set(flag); + } + if let Some(priority) = params.priority { + model.priority = Set(priority); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::rewrite_rule::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/server_block/mod.rs b/apps/nxmesh-master/src/service/proxy/server_block/mod.rs new file mode 100644 index 0000000..6158bcb --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/server_block/mod.rs @@ -0,0 +1,201 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{ + OverrideRef, ProxyServiceError, ProxyServiceResult, ServerBlockConfig, +}; + +pub struct CreateServerBlockParams { + pub config_id: Uuid, + pub server_name: Option>, + pub listen_port: i32, + pub ssl_enabled: Option, + pub ssl_cert_id: Option, + pub override_of_id: Option, +} + +pub struct UpdateServerBlockParams { + pub server_name: Option>>, + pub listen_port: Option, + pub ssl_enabled: Option>, + pub ssl_cert_id: Option>, + pub override_of_id: Option>, +} + +#[async_trait::async_trait] +pub trait ServerBlockService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_config(&self, config_id: Uuid) -> ProxyServiceResult>; + async fn create( + &self, + params: CreateServerBlockParams, + ) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateServerBlockParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct ServerBlockServiceImpl { + db: DatabaseConnection, +} + +impl ServerBlockServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } + + async fn build_with_children( + &self, + model: crate::db::entities::server_block::Model, + ) -> ProxyServiceResult { + use crate::db::entities::{access_rule, location_block, log_setting}; + + let access_rules = access_rule::Entity::find() + .filter(access_rule::Column::ServerId.eq(model.id)) + .all(&self.db) + .await? + .into_iter() + .map(|a| OverrideRef { + id: a.id, + override_of_id: a.override_of_id, + }) + .collect(); + + let location_blocks = location_block::Entity::find() + .filter(location_block::Column::ServerId.eq(model.id)) + .all(&self.db) + .await? + .into_iter() + .map(|l| OverrideRef { + id: l.id, + override_of_id: l.override_of_id, + }) + .collect(); + + let log_settings = log_setting::Entity::find() + .filter(log_setting::Column::ServerId.eq(model.id)) + .all(&self.db) + .await? + .into_iter() + .map(|l| OverrideRef { + id: l.id, + override_of_id: l.override_of_id, + }) + .collect(); + + let ssl_certificates = model + .ssl_cert_id + .map(|id| { + vec![OverrideRef { + id, + override_of_id: None, + }] + }) + .unwrap_or_default(); + + Ok(ServerBlockConfig { + id: model.id, + server_name: model.server_name, + listen_port: model.listen_port, + ssl_enabled: model.ssl_enabled, + override_of_id: model.override_of_id, + access_rules, + location_blocks, + log_settings, + ssl_certificates, + }) + } +} + +#[async_trait::async_trait] +impl ServerBlockService for ServerBlockServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::server_block; + + let model = server_block::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + self.build_with_children(model).await + } + + async fn list_by_config(&self, config_id: Uuid) -> ProxyServiceResult> { + use crate::db::entities::server_block; + + let models = server_block::Entity::find() + .filter(server_block::Column::ConfigId.eq(config_id)) + .all(&self.db) + .await?; + + let mut results = Vec::with_capacity(models.len()); + for m in models { + results.push(self.build_with_children(m).await?); + } + Ok(results) + } + + async fn create( + &self, + params: CreateServerBlockParams, + ) -> ProxyServiceResult { + use crate::db::entities::server_block::ActiveModel; + + let id = Uuid::new_v4(); + let model = ActiveModel { + id: Set(id), + config_id: Set(params.config_id), + server_name: Set(params.server_name), + listen_port: Set(params.listen_port), + ssl_enabled: Set(params.ssl_enabled), + ssl_cert_id: Set(params.ssl_cert_id), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + self.build_with_children(result).await + } + + async fn update( + &self, + id: Uuid, + params: UpdateServerBlockParams, + ) -> ProxyServiceResult { + use crate::db::entities::server_block::{ActiveModel, Entity as ServerBlockEntity}; + + let existing = ServerBlockEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(server_name) = params.server_name { + model.server_name = Set(server_name); + } + if let Some(listen_port) = params.listen_port { + model.listen_port = Set(listen_port); + } + if let Some(ssl_enabled) = params.ssl_enabled { + model.ssl_enabled = Set(ssl_enabled); + } + if let Some(ssl_cert_id) = params.ssl_cert_id { + model.ssl_cert_id = Set(ssl_cert_id); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + self.build_with_children(result).await + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::server_block::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/ssl_certificate/mod.rs b/apps/nxmesh-master/src/service/proxy/ssl_certificate/mod.rs new file mode 100644 index 0000000..8d1a4f1 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/ssl_certificate/mod.rs @@ -0,0 +1,122 @@ +use chrono::Utc; +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{ProxyServiceError, ProxyServiceResult, SslCertificateConfig}; + +pub struct CreateSslCertificateParams { + pub name: String, + pub cert_path: String, + pub key_path: String, + pub expiry_date: chrono::DateTime, +} + +pub struct UpdateSslCertificateParams { + pub name: Option, + pub cert_path: Option, + pub key_path: Option, + pub expiry_date: Option>, +} + +#[async_trait::async_trait] +pub trait SslCertificateService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list(&self) -> ProxyServiceResult>; + async fn create( + &self, + params: CreateSslCertificateParams, + ) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateSslCertificateParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct SslCertificateServiceImpl { + db: DatabaseConnection, +} + +impl SslCertificateServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl SslCertificateService for SslCertificateServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::ssl_certificate; + + let model = ssl_certificate::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model.into()) + } + + async fn list(&self) -> ProxyServiceResult> { + use crate::db::entities::ssl_certificate; + + let models = ssl_certificate::Entity::find().all(&self.db).await?; + + Ok(models.into_iter().map(Into::into).collect()) + } + + async fn create( + &self, + params: CreateSslCertificateParams, + ) -> ProxyServiceResult { + use crate::db::entities::ssl_certificate::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + name: Set(params.name), + cert_path: Set(params.cert_path), + key_path: Set(params.key_path), + expiry_date: Set(params.expiry_date.naive_utc()), + }; + + let result = model.insert(&self.db).await?; + Ok(result.into()) + } + + async fn update( + &self, + id: Uuid, + params: UpdateSslCertificateParams, + ) -> ProxyServiceResult { + use crate::db::entities::ssl_certificate::{ActiveModel, Entity as SslCertificateEntity}; + + let existing = SslCertificateEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(name) = params.name { + model.name = Set(name); + } + if let Some(cert_path) = params.cert_path { + model.cert_path = Set(cert_path); + } + if let Some(key_path) = params.key_path { + model.key_path = Set(key_path); + } + if let Some(expiry_date) = params.expiry_date { + model.expiry_date = Set(expiry_date.naive_utc()); + } + + let result = model.update(&self.db).await?; + Ok(result.into()) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::ssl_certificate::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +} diff --git a/apps/nxmesh-master/src/service/proxy/types.rs b/apps/nxmesh-master/src/service/proxy/types.rs index 1d65225..f578192 100644 --- a/apps/nxmesh-master/src/service/proxy/types.rs +++ b/apps/nxmesh-master/src/service/proxy/types.rs @@ -34,10 +34,8 @@ pub fn merge_override_vecs( mut child: Vec, parent: Vec, ) -> Vec { - let overridden_ids: std::collections::HashSet = child - .iter() - .filter_map(|r| r.override_of_id) - .collect(); + let overridden_ids: std::collections::HashSet = + child.iter().filter_map(|r| r.override_of_id).collect(); for item in parent { if !overridden_ids.contains(&item.id) { child.push(item); @@ -76,7 +74,8 @@ impl Mergeable for ProxyConfig { macro_rules! merge_overridable_field { ($field:ident) => { - let overridden: HashSet = self.$field + let overridden: HashSet = self + .$field .values() .filter_map(|v| v.override_of_id()) .collect(); @@ -90,7 +89,8 @@ impl Mergeable for ProxyConfig { // server_blocks: merge matching entries (field-level), handle overrides { - let overridden: HashSet = self.server_blocks + let overridden: HashSet = self + .server_blocks .values() .filter_map(|sb| sb.override_of_id) .collect(); @@ -179,18 +179,14 @@ impl Mergeable for ServerBlockConfig { self.override_of_id = other.override_of_id; } - self.access_rules = merge_override_vecs( - std::mem::take(&mut self.access_rules), - other.access_rules, - ); + self.access_rules = + merge_override_vecs(std::mem::take(&mut self.access_rules), other.access_rules); self.location_blocks = merge_override_vecs( std::mem::take(&mut self.location_blocks), other.location_blocks, ); - self.log_settings = merge_override_vecs( - std::mem::take(&mut self.log_settings), - other.log_settings, - ); + self.log_settings = + merge_override_vecs(std::mem::take(&mut self.log_settings), other.log_settings); self.ssl_certificates = merge_override_vecs( std::mem::take(&mut self.ssl_certificates), other.ssl_certificates, @@ -452,34 +448,54 @@ impl From for RewriteRuleConfig { // ── Overridable implementations ── impl Overridable for ServerBlockConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for UpstreamConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for AccessRuleConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for CacheZoneConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for LimitRuleConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for LimitZoneConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for LocationBlockConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for LogSettingConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for ProxySettingConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } impl Overridable for RewriteRuleConfig { - fn override_of_id(&self) -> Option { self.override_of_id } + fn override_of_id(&self) -> Option { + self.override_of_id + } } // ── CRUD types ── @@ -623,7 +639,10 @@ mod tests { }; child.merge(parent); // child keeps its own values (self overrides other) - assert_eq!(child.server_name, Some(vec!["child.example.com".to_string()])); + assert_eq!( + child.server_name, + Some(vec!["child.example.com".to_string()]) + ); assert_eq!(child.listen_port, 443); assert_eq!(child.ssl_enabled, Some(true)); } @@ -655,7 +674,10 @@ mod tests { }; child.merge(parent); // child fills missing optional fields from parent - assert_eq!(child.server_name, Some(vec!["parent.example.com".to_string()])); + assert_eq!( + child.server_name, + Some(vec!["parent.example.com".to_string()]) + ); assert_eq!(child.listen_port, 443); // non-optional: child keeps its own assert_eq!(child.ssl_enabled, Some(false)); } @@ -725,7 +747,10 @@ mod tests { child_proxy.merge(parent_proxy); assert_eq!(child_proxy.server_blocks.len(), 1); let merged_sb = &child_proxy.server_blocks[&sb_id]; - assert_eq!(merged_sb.server_name, Some(vec!["child.example.com".to_string()])); + assert_eq!( + merged_sb.server_name, + Some(vec!["child.example.com".to_string()]) + ); assert_eq!(merged_sb.listen_port, 443); assert_eq!(merged_sb.ssl_enabled, Some(true)); } diff --git a/apps/nxmesh-master/src/service/proxy/upstream/mod.rs b/apps/nxmesh-master/src/service/proxy/upstream/mod.rs new file mode 100644 index 0000000..23a9738 --- /dev/null +++ b/apps/nxmesh-master/src/service/proxy/upstream/mod.rs @@ -0,0 +1,140 @@ +use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*}; +use uuid::Uuid; + +use crate::service::proxy::types::{ + OverrideRef, ProxyServiceError, ProxyServiceResult, UpstreamConfig, +}; + +pub struct CreateUpstreamParams { + pub config_id: Uuid, + pub name: String, + pub target_host: String, + pub target_port: i32, + pub metadata: Option, + pub override_of_id: Option, +} + +pub struct UpdateUpstreamParams { + pub name: Option, + pub target_host: Option, + pub target_port: Option, + pub metadata: Option>, + pub override_of_id: Option>, +} + +fn model_to_config(model: crate::db::entities::upstream::Model) -> UpstreamConfig { + UpstreamConfig { + id: model.id, + name: model.name, + target_host: model.target_host, + target_port: model.target_port, + metadata: model.metadata, + override_of_id: model.override_of_id, + location_blocks: vec![], + } +} + +#[async_trait::async_trait] +pub trait UpstreamService: Send + Sync + 'static { + async fn get(&self, id: Uuid) -> ProxyServiceResult; + async fn list_by_config(&self, config_id: Uuid) -> ProxyServiceResult>; + async fn create(&self, params: CreateUpstreamParams) -> ProxyServiceResult; + async fn update( + &self, + id: Uuid, + params: UpdateUpstreamParams, + ) -> ProxyServiceResult; + async fn delete(&self, id: Uuid) -> ProxyServiceResult; +} + +pub(crate) struct UpstreamServiceImpl { + db: DatabaseConnection, +} + +impl UpstreamServiceImpl { + pub fn new(db: DatabaseConnection) -> Self { + Self { db } + } +} + +#[async_trait::async_trait] +impl UpstreamService for UpstreamServiceImpl { + async fn get(&self, id: Uuid) -> ProxyServiceResult { + use crate::db::entities::upstream; + + let model = upstream::Entity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + Ok(model_to_config(model)) + } + + async fn list_by_config(&self, config_id: Uuid) -> ProxyServiceResult> { + use crate::db::entities::upstream; + + let models = upstream::Entity::find() + .filter(upstream::Column::ConfigId.eq(config_id)) + .all(&self.db) + .await?; + + Ok(models.into_iter().map(model_to_config).collect()) + } + + async fn create(&self, params: CreateUpstreamParams) -> ProxyServiceResult { + use crate::db::entities::upstream::ActiveModel; + + let model = ActiveModel { + id: Set(Uuid::new_v4()), + config_id: Set(params.config_id), + name: Set(params.name), + target_host: Set(params.target_host), + target_port: Set(params.target_port), + metadata: Set(params.metadata), + override_of_id: Set(params.override_of_id), + }; + + let result = model.insert(&self.db).await?; + Ok(model_to_config(result)) + } + + async fn update( + &self, + id: Uuid, + params: UpdateUpstreamParams, + ) -> ProxyServiceResult { + use crate::db::entities::upstream::{ActiveModel, Entity as UpstreamEntity}; + + let existing = UpstreamEntity::find_by_id(id) + .one(&self.db) + .await? + .ok_or(ProxyServiceError::ConfigNotFound)?; + + let mut model: ActiveModel = existing.into(); + if let Some(name) = params.name { + model.name = Set(name); + } + if let Some(target_host) = params.target_host { + model.target_host = Set(target_host); + } + if let Some(target_port) = params.target_port { + model.target_port = Set(target_port); + } + if let Some(metadata) = params.metadata { + model.metadata = Set(metadata); + } + if let Some(override_of_id) = params.override_of_id { + model.override_of_id = Set(override_of_id); + } + + let result = model.update(&self.db).await?; + Ok(model_to_config(result)) + } + + async fn delete(&self, id: Uuid) -> ProxyServiceResult { + let result = crate::db::entities::upstream::Entity::delete_by_id(id) + .exec(&self.db) + .await?; + Ok(result.rows_affected > 0) + } +}