Compare commits
16 Commits
be1d2e3fa9
...
3c5485c449
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c5485c449 | ||
|
|
076e87695d | ||
|
|
83037f3ee2 | ||
|
|
2c20d08127 | ||
|
|
6f560c981b | ||
|
|
a7863b9e87 | ||
|
|
b7891ec200 | ||
|
|
e44a67f5a8 | ||
|
|
6d1df8dcca | ||
|
|
c7cc006212 | ||
|
|
d976c22683 | ||
|
|
7c233b5f77 | ||
|
|
2e758c67fc | ||
|
|
692b17534c | ||
|
|
9c07fce607 | ||
|
|
2dbab85c87 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -161,5 +161,6 @@ target
|
|||||||
**/mutants.out*/
|
**/mutants.out*/
|
||||||
|
|
||||||
.local/
|
.local/
|
||||||
|
.act/
|
||||||
|
|
||||||
certs/
|
certs/
|
||||||
|
|||||||
@@ -80,34 +80,6 @@ mod tests {
|
|||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_exec_file(path: &Path) {
|
|
||||||
write_file(path);
|
|
||||||
let metadata = fs::metadata(path);
|
|
||||||
assert!(metadata.is_ok());
|
|
||||||
let metadata = metadata.ok();
|
|
||||||
assert!(metadata.is_some());
|
|
||||||
let metadata = metadata.unwrap_or_else(|| unreachable!());
|
|
||||||
|
|
||||||
let mut perms = metadata.permissions();
|
|
||||||
perms.set_mode(0o755);
|
|
||||||
let result = fs::set_permissions(path, perms);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_non_exec_file(path: &Path) {
|
|
||||||
write_file(path);
|
|
||||||
let metadata = fs::metadata(path);
|
|
||||||
assert!(metadata.is_ok());
|
|
||||||
let metadata = metadata.ok();
|
|
||||||
assert!(metadata.is_some());
|
|
||||||
let metadata = metadata.unwrap_or_else(|| unreachable!());
|
|
||||||
|
|
||||||
let mut perms = metadata.permissions();
|
|
||||||
perms.set_mode(0o644);
|
|
||||||
let result = fs::set_permissions(path, perms);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn valid_tls_raw_paths(temp_dir: &TempDir) -> (PathBuf, PathBuf, PathBuf) {
|
fn valid_tls_raw_paths(temp_dir: &TempDir) -> (PathBuf, PathBuf, PathBuf) {
|
||||||
let ca_path = temp_dir.path().join("ca.pem");
|
let ca_path = temp_dir.path().join("ca.pem");
|
||||||
let cert_path = temp_dir.path().join("cert.pem");
|
let cert_path = temp_dir.path().join("cert.pem");
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
pub mod ssh;
|
pub mod ssh;
|
||||||
|
|
||||||
pub type AgentClient =
|
pub type AgentClient =
|
||||||
@@ -47,8 +43,6 @@ mod tests {
|
|||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
use crate::config::settings::{
|
use crate::config::settings::{
|
||||||
GrpcSettings, LogSettings, MAuthSettings, Settings, TLSSettings,
|
GrpcSettings, LogSettings, MAuthSettings, Settings, TLSSettings,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::sync::{Arc, Weak};
|
use std::sync::Weak;
|
||||||
|
|
||||||
use nxmesh_proto::{
|
use nxmesh_proto::{ConfigUpdate, MasterMessage, command::Command, master_message::Payload};
|
||||||
AgentMessage, ConfigUpdate, MasterMessage, command::Command, master_message::Payload,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::service::master_handler::{MasterHandlerError, MessageResult};
|
use crate::service::master_handler::{MasterHandlerError, MessageResult};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tracing::{debug, warn};
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{config::settings::NginxSettings, service::master_handler::MasterHandlerError};
|
use crate::{config::settings::NginxSettings, service::master_handler::MasterHandlerError};
|
||||||
|
|
||||||
@@ -120,10 +120,7 @@ impl CommandHandler for CommandHandlerImpl {
|
|||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let error_info = String::from_utf8_lossy(&output.stderr);
|
let error_info = String::from_utf8_lossy(&output.stderr);
|
||||||
return Err(CommandHandlerError::CommandExecutionError(
|
return Err(CommandHandlerError::CommandExecutionError(
|
||||||
std::io::Error::new(
|
std::io::Error::other(format!("Failed to reload nginx: {}", error_info.trim())),
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
format!("Failed to reload nginx: {}", error_info.trim()),
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let success_info = String::from_utf8_lossy(&output.stdout);
|
let success_info = String::from_utf8_lossy(&output.stdout);
|
||||||
@@ -142,10 +139,7 @@ impl CommandHandler for CommandHandlerImpl {
|
|||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let error_info = String::from_utf8_lossy(&output.stderr);
|
let error_info = String::from_utf8_lossy(&output.stderr);
|
||||||
return Err(CommandHandlerError::CommandExecutionError(
|
return Err(CommandHandlerError::CommandExecutionError(
|
||||||
std::io::Error::new(
|
std::io::Error::other(format!("Failed to stop nginx: {}", error_info.trim())),
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
format!("Failed to stop nginx: {}", error_info.trim()),
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let success_info = String::from_utf8_lossy(&output.stdout);
|
let success_info = String::from_utf8_lossy(&output.stdout);
|
||||||
@@ -171,10 +165,10 @@ impl CommandHandler for CommandHandlerImpl {
|
|||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let error_info = String::from_utf8_lossy(&output.stderr);
|
let error_info = String::from_utf8_lossy(&output.stderr);
|
||||||
return Err(CommandHandlerError::CommandExecutionError(
|
return Err(CommandHandlerError::CommandExecutionError(
|
||||||
std::io::Error::new(
|
std::io::Error::other(format!(
|
||||||
std::io::ErrorKind::Other,
|
"Failed to validate nginx config: {}",
|
||||||
format!("Failed to validate nginx config: {}", error_info.trim()),
|
error_info.trim()
|
||||||
),
|
)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let success_info = String::from_utf8_lossy(&output.stdout);
|
let success_info = String::from_utf8_lossy(&output.stdout);
|
||||||
@@ -191,10 +185,10 @@ impl CommandHandler for CommandHandlerImpl {
|
|||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let error_info = String::from_utf8_lossy(&output.stderr);
|
let error_info = String::from_utf8_lossy(&output.stderr);
|
||||||
return Err(CommandHandlerError::CommandExecutionError(
|
return Err(CommandHandlerError::CommandExecutionError(
|
||||||
std::io::Error::new(
|
std::io::Error::other(format!(
|
||||||
std::io::ErrorKind::Other,
|
"Failed to get nginx version: {}",
|
||||||
format!("Failed to get nginx version: {}", error_info.trim()),
|
error_info.trim()
|
||||||
),
|
)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,10 +205,7 @@ impl CommandHandler for CommandHandlerImpl {
|
|||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let error_info = String::from_utf8_lossy(&output.stderr);
|
let error_info = String::from_utf8_lossy(&output.stderr);
|
||||||
return Err(CommandHandlerError::CommandExecutionError(
|
return Err(CommandHandlerError::CommandExecutionError(
|
||||||
std::io::Error::new(
|
std::io::Error::other(format!("Failed to get nginx status: {}", error_info.trim())),
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
format!("Failed to get nginx status: {}", error_info.trim()),
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use fs4::tokio::AsyncFileExt;
|
use fs4::tokio::AsyncFileExt;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::{io::AsyncWriteExt, process::Command};
|
use tokio::io::AsyncWriteExt;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{config::settings::NginxSettings, service::master_handler::MasterHandlerError};
|
use crate::{config::settings::NginxSettings, service::master_handler::MasterHandlerError};
|
||||||
@@ -222,19 +222,19 @@ impl FsHandler for FsHandlerImpl {
|
|||||||
let mut entries = tokio::fs::read_dir(&deployment_dir).await?;
|
let mut entries = tokio::fs::read_dir(&deployment_dir).await?;
|
||||||
let mut candidates: Vec<(std::path::PathBuf, std::time::SystemTime)> = Vec::new();
|
let mut candidates: Vec<(std::path::PathBuf, std::time::SystemTime)> = Vec::new();
|
||||||
while let Some(entry) = entries.next_entry().await? {
|
while let Some(entry) = entries.next_entry().await? {
|
||||||
if entry.file_type().await.map_or(false, |t| t.is_dir()) {
|
if entry.file_type().await.is_ok_and(|t| t.is_dir())
|
||||||
if let Ok(mtime) = entry.metadata().await.and_then(|m| m.modified()) {
|
&& let Ok(mtime) = entry.metadata().await.and_then(|m| m.modified())
|
||||||
candidates.push((entry.path(), mtime));
|
{
|
||||||
}
|
candidates.push((entry.path(), mtime));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sort descending by mtime (newest first)
|
// sort descending by mtime (newest first)
|
||||||
candidates.sort_by(|a, b| b.1.cmp(&a.1));
|
candidates.sort_by_key(|b| std::cmp::Reverse(b.1));
|
||||||
|
|
||||||
for (dir, _) in &candidates {
|
for (dir, _) in &candidates {
|
||||||
let mut dir_entries = tokio::fs::read_dir(dir).await?;
|
let mut dir_entries = tokio::fs::read_dir(dir).await?;
|
||||||
while let Some(file) = dir_entries.next_entry().await? {
|
while let Some(file) = dir_entries.next_entry().await? {
|
||||||
if file.file_type().await.map_or(false, |t| t.is_file()) {
|
if file.file_type().await.is_ok_and(|t| t.is_file()) {
|
||||||
let name = file.file_name().to_string_lossy().to_string();
|
let name = file.file_name().to_string_lossy().to_string();
|
||||||
if name == "nginx.conf" || name.ends_with(".conf") {
|
if name == "nginx.conf" || name.ends_with(".conf") {
|
||||||
let path = file.path().to_string_lossy().to_string();
|
let path = file.path().to_string_lossy().to_string();
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ impl OnConfigUpdateHandler for NginxMasterMessageHandlerImpl {
|
|||||||
// apply reload on the root config
|
// apply reload on the root config
|
||||||
self.command_handler.reload(Some(&root_config_path)).await?;
|
self.command_handler.reload(Some(&root_config_path)).await?;
|
||||||
// persist deployment path so Reload/Test commands survive agent restarts
|
// persist deployment path so Reload/Test commands survive agent restarts
|
||||||
self.fs_handler.save_last_deployment(&root_config_path).await?;
|
self.fs_handler
|
||||||
|
.save_last_deployment(&root_config_path)
|
||||||
|
.await?;
|
||||||
info!("Persisted last deployment path: {}", root_config_path);
|
info!("Persisted last deployment path: {}", root_config_path);
|
||||||
// Reply the master to confirm the config update is successful
|
// Reply the master to confirm the config update is successful
|
||||||
self.master_handler
|
self.master_handler
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ thiserror.workspace = true
|
|||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
|
|
||||||
|
#
|
||||||
|
anyhow = { version = "1.0.102", features = ["backtrace"] }
|
||||||
|
|
||||||
# Web
|
# Web
|
||||||
axum = { workspace = true, features = ["ws"] }
|
axum = { workspace = true, features = ["ws"] }
|
||||||
tower.workspace = true
|
tower.workspace = true
|
||||||
@@ -87,6 +90,7 @@ zip = { workspace = true }
|
|||||||
rust-embed = { version = "8.11.0", features = [] }
|
rust-embed = { version = "8.11.0", features = [] }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
axum-test = "20.0.0"
|
axum-test = "20.0.0"
|
||||||
|
tokio-stream.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio-test.workspace = true
|
tokio-test.workspace = true
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use tonic::transport::Server;
|
|
||||||
|
|
||||||
pub mod ssh;
|
pub mod ssh;
|
||||||
|
|
||||||
|
|||||||
61
apps/nxmesh-master/src/db/entities/access_rule.rs
Normal file
61
apps/nxmesh-master/src/db/entities/access_rule.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "access_rule")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub server_id: Option<Uuid>,
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub r#type: String,
|
||||||
|
pub ip_cidr: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::location_block::Entity",
|
||||||
|
from = "Column::LocationId",
|
||||||
|
to = "super::location_block::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
LocationBlock,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::server_block::Entity",
|
||||||
|
from = "Column::ServerId",
|
||||||
|
to = "super::server_block::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ServerBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::location_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LocationBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::server_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ServerBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
64
apps/nxmesh-master/src/db/entities/agent_config_binding.rs
Normal file
64
apps/nxmesh-master/src/db/entities/agent_config_binding.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "agent_config_binding")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub agent_id: Option<Uuid>,
|
||||||
|
pub group_id: Option<Uuid>,
|
||||||
|
pub config_id: Uuid,
|
||||||
|
pub is_active: bool,
|
||||||
|
pub applied_at: DateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::agent_group::Entity",
|
||||||
|
from = "Column::GroupId",
|
||||||
|
to = "super::agent_group::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
AgentGroup,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::agents::Entity",
|
||||||
|
from = "Column::AgentId",
|
||||||
|
to = "super::agents::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
Agents,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::proxy_config::Entity",
|
||||||
|
from = "Column::ConfigId",
|
||||||
|
to = "super::proxy_config::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ProxyConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::agent_group::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::AgentGroup.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::agents::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Agents.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::proxy_config::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ProxyConfig.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
35
apps/nxmesh-master/src/db/entities/agent_group.rs
Normal file
35
apps/nxmesh-master/src/db/entities/agent_group.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "agent_group")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::agent_config_binding::Entity")]
|
||||||
|
AgentConfigBinding,
|
||||||
|
#[sea_orm(has_many = "super::agents::Entity")]
|
||||||
|
Agents,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::agent_config_binding::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::AgentConfigBinding.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::agents::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Agents.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
@@ -19,9 +19,33 @@ pub struct Model {
|
|||||||
pub labels: Option<Json>,
|
pub labels: Option<Json>,
|
||||||
pub created_at: DateTimeWithTimeZone,
|
pub created_at: DateTimeWithTimeZone,
|
||||||
pub updated_at: DateTimeWithTimeZone,
|
pub updated_at: DateTimeWithTimeZone,
|
||||||
|
pub group_id: Option<Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::agent_config_binding::Entity")]
|
||||||
|
AgentConfigBinding,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::agent_group::Entity",
|
||||||
|
from = "Column::GroupId",
|
||||||
|
to = "super::agent_group::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
AgentGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::agent_config_binding::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::AgentConfigBinding.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::agent_group::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::AgentGroup.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|||||||
37
apps/nxmesh-master/src/db/entities/cache_zone.rs
Normal file
37
apps/nxmesh-master/src/db/entities/cache_zone.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "cache_zone")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub size_limit: String,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(has_many = "super::proxy_setting::Entity")]
|
||||||
|
ProxySetting,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::proxy_setting::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ProxySetting.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
37
apps/nxmesh-master/src/db/entities/config_inheritance.rs
Normal file
37
apps/nxmesh-master/src/db/entities/config_inheritance.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "config_inheritance")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub child_config_id: Uuid,
|
||||||
|
pub parent_config_id: Uuid,
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
pub applied_at: DateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::proxy_config::Entity",
|
||||||
|
from = "Column::ChildConfigId",
|
||||||
|
to = "super::proxy_config::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ProxyConfig2,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::proxy_config::Entity",
|
||||||
|
from = "Column::ParentConfigId",
|
||||||
|
to = "super::proxy_config::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ProxyConfig1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
59
apps/nxmesh-master/src/db/entities/limit_rule.rs
Normal file
59
apps/nxmesh-master/src/db/entities/limit_rule.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "limit_rule")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub location_id: Uuid,
|
||||||
|
pub zone_id: Uuid,
|
||||||
|
pub burst: Option<i32>,
|
||||||
|
pub nodelay: Option<bool>,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::limit_zone::Entity",
|
||||||
|
from = "Column::ZoneId",
|
||||||
|
to = "super::limit_zone::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
LimitZone,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::location_block::Entity",
|
||||||
|
from = "Column::LocationId",
|
||||||
|
to = "super::location_block::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
LocationBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::limit_zone::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LimitZone.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::location_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LocationBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
37
apps/nxmesh-master/src/db/entities/limit_zone.rs
Normal file
37
apps/nxmesh-master/src/db/entities/limit_zone.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "limit_zone")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub key: String,
|
||||||
|
pub rate: String,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::limit_rule::Entity")]
|
||||||
|
LimitRule,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::limit_rule::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LimitRule.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
91
apps/nxmesh-master/src/db/entities/location_block.rs
Normal file
91
apps/nxmesh-master/src/db/entities/location_block.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "location_block")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub server_id: Uuid,
|
||||||
|
pub path_pattern: String,
|
||||||
|
pub proxy_pass_upstream_id: Option<Uuid>,
|
||||||
|
#[sea_orm(column_type = "JsonBinary", nullable)]
|
||||||
|
pub metadata: Option<Json>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::access_rule::Entity")]
|
||||||
|
AccessRule,
|
||||||
|
#[sea_orm(has_many = "super::limit_rule::Entity")]
|
||||||
|
LimitRule,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(has_many = "super::proxy_setting::Entity")]
|
||||||
|
ProxySetting,
|
||||||
|
#[sea_orm(has_many = "super::rewrite_rule::Entity")]
|
||||||
|
RewriteRule,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::server_block::Entity",
|
||||||
|
from = "Column::ServerId",
|
||||||
|
to = "super::server_block::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ServerBlock,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::upstream::Entity",
|
||||||
|
from = "Column::ProxyPassUpstreamId",
|
||||||
|
to = "super::upstream::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
Upstream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::access_rule::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::AccessRule.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::limit_rule::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LimitRule.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::proxy_setting::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ProxySetting.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::rewrite_rule::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::RewriteRule.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::server_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ServerBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::upstream::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Upstream.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
44
apps/nxmesh-master/src/db/entities/log_setting.rs
Normal file
44
apps/nxmesh-master/src/db/entities/log_setting.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "log_setting")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub server_id: Uuid,
|
||||||
|
pub access_log_path: Option<String>,
|
||||||
|
pub error_log_path: Option<String>,
|
||||||
|
pub log_level: Option<String>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::server_block::Entity",
|
||||||
|
from = "Column::ServerId",
|
||||||
|
to = "super::server_block::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ServerBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::server_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ServerBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
@@ -2,5 +2,20 @@
|
|||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub mod access_rule;
|
||||||
|
pub mod agent_config_binding;
|
||||||
|
pub mod agent_group;
|
||||||
pub mod agents;
|
pub mod agents;
|
||||||
|
pub mod cache_zone;
|
||||||
|
pub mod config_inheritance;
|
||||||
|
pub mod limit_rule;
|
||||||
|
pub mod limit_zone;
|
||||||
|
pub mod location_block;
|
||||||
|
pub mod log_setting;
|
||||||
|
pub mod proxy_config;
|
||||||
|
pub mod proxy_setting;
|
||||||
pub mod public_key_revocations;
|
pub mod public_key_revocations;
|
||||||
|
pub mod rewrite_rule;
|
||||||
|
pub mod server_block;
|
||||||
|
pub mod ssl_certificate;
|
||||||
|
pub mod upstream;
|
||||||
|
|||||||
@@ -1,4 +1,19 @@
|
|||||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
pub use super::access_rule::Entity as AccessRule;
|
||||||
|
pub use super::agent_config_binding::Entity as AgentConfigBinding;
|
||||||
|
pub use super::agent_group::Entity as AgentGroup;
|
||||||
pub use super::agents::Entity as Agents;
|
pub use super::agents::Entity as Agents;
|
||||||
|
pub use super::cache_zone::Entity as CacheZone;
|
||||||
|
pub use super::config_inheritance::Entity as ConfigInheritance;
|
||||||
|
pub use super::limit_rule::Entity as LimitRule;
|
||||||
|
pub use super::limit_zone::Entity as LimitZone;
|
||||||
|
pub use super::location_block::Entity as LocationBlock;
|
||||||
|
pub use super::log_setting::Entity as LogSetting;
|
||||||
|
pub use super::proxy_config::Entity as ProxyConfig;
|
||||||
|
pub use super::proxy_setting::Entity as ProxySetting;
|
||||||
pub use super::public_key_revocations::Entity as PublicKeyRevocations;
|
pub use super::public_key_revocations::Entity as PublicKeyRevocations;
|
||||||
|
pub use super::rewrite_rule::Entity as RewriteRule;
|
||||||
|
pub use super::server_block::Entity as ServerBlock;
|
||||||
|
pub use super::ssl_certificate::Entity as SslCertificate;
|
||||||
|
pub use super::upstream::Entity as Upstream;
|
||||||
|
|||||||
46
apps/nxmesh-master/src/db/entities/proxy_config.rs
Normal file
46
apps/nxmesh-master/src/db/entities/proxy_config.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "proxy_config")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub is_template: bool,
|
||||||
|
pub created_at: DateTime,
|
||||||
|
pub updated_at: DateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::agent_config_binding::Entity")]
|
||||||
|
AgentConfigBinding,
|
||||||
|
#[sea_orm(has_many = "super::server_block::Entity")]
|
||||||
|
ServerBlock,
|
||||||
|
#[sea_orm(has_many = "super::upstream::Entity")]
|
||||||
|
Upstream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::agent_config_binding::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::AgentConfigBinding.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::server_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ServerBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::upstream::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Upstream.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
60
apps/nxmesh-master/src/db/entities/proxy_setting.rs
Normal file
60
apps/nxmesh-master/src/db/entities/proxy_setting.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "proxy_setting")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub location_id: Uuid,
|
||||||
|
pub read_timeout: Option<i32>,
|
||||||
|
pub connect_timeout: Option<i32>,
|
||||||
|
pub buffer_size: Option<i32>,
|
||||||
|
pub cache_enabled: Option<bool>,
|
||||||
|
pub cache_zone: Option<Uuid>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::cache_zone::Entity",
|
||||||
|
from = "Column::CacheZone",
|
||||||
|
to = "super::cache_zone::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
CacheZone,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::location_block::Entity",
|
||||||
|
from = "Column::LocationId",
|
||||||
|
to = "super::location_block::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
LocationBlock,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cache_zone::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::CacheZone.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::location_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LocationBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
46
apps/nxmesh-master/src/db/entities/rewrite_rule.rs
Normal file
46
apps/nxmesh-master/src/db/entities/rewrite_rule.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "rewrite_rule")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub location_id: Uuid,
|
||||||
|
pub pattern: String,
|
||||||
|
pub replacement: String,
|
||||||
|
pub flag: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::location_block::Entity",
|
||||||
|
from = "Column::LocationId",
|
||||||
|
to = "super::location_block::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
LocationBlock,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::location_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LocationBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
83
apps/nxmesh-master/src/db/entities/server_block.rs
Normal file
83
apps/nxmesh-master/src/db/entities/server_block.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "server_block")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub config_id: Uuid,
|
||||||
|
pub server_name: Option<Vec<String>>,
|
||||||
|
pub listen_port: i32,
|
||||||
|
pub ssl_enabled: Option<bool>,
|
||||||
|
pub ssl_cert_id: Option<Uuid>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::access_rule::Entity")]
|
||||||
|
AccessRule,
|
||||||
|
#[sea_orm(has_many = "super::location_block::Entity")]
|
||||||
|
LocationBlock,
|
||||||
|
#[sea_orm(has_many = "super::log_setting::Entity")]
|
||||||
|
LogSetting,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::proxy_config::Entity",
|
||||||
|
from = "Column::ConfigId",
|
||||||
|
to = "super::proxy_config::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ProxyConfig,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::ssl_certificate::Entity",
|
||||||
|
from = "Column::SslCertId",
|
||||||
|
to = "super::ssl_certificate::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SslCertificate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::access_rule::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::AccessRule.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::location_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LocationBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::log_setting::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LogSetting.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::proxy_config::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ProxyConfig.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::ssl_certificate::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::SslCertificate.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
29
apps/nxmesh-master/src/db/entities/ssl_certificate.rs
Normal file
29
apps/nxmesh-master/src/db/entities/ssl_certificate.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "ssl_certificate")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub cert_path: String,
|
||||||
|
pub key_path: String,
|
||||||
|
pub expiry_date: DateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::server_block::Entity")]
|
||||||
|
ServerBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::server_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ServerBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
54
apps/nxmesh-master/src/db/entities/upstream.rs
Normal file
54
apps/nxmesh-master/src/db/entities/upstream.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "upstream")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: Uuid,
|
||||||
|
pub config_id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub target_host: String,
|
||||||
|
pub target_port: i32,
|
||||||
|
#[sea_orm(column_type = "JsonBinary", nullable)]
|
||||||
|
pub metadata: Option<Json>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::location_block::Entity")]
|
||||||
|
LocationBlock,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::proxy_config::Entity",
|
||||||
|
from = "Column::ConfigId",
|
||||||
|
to = "super::proxy_config::Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
ProxyConfig,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "Entity",
|
||||||
|
from = "Column::OverrideOfId",
|
||||||
|
to = "Column::Id",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "SetNull"
|
||||||
|
)]
|
||||||
|
SelfRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::location_block::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::LocationBlock.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::proxy_config::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::ProxyConfig.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
41
apps/nxmesh-master/src/routes/api/agents/add_agent.rs
Normal file
41
apps/nxmesh-master/src/routes/api/agents/add_agent.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{Json, extract::State, http::StatusCode, response::IntoResponse};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
routes::api::{agents::dto::AgentInfo, error::AppError},
|
||||||
|
service::agent::{AgentService, CreateAgentRecord},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct CreateAgentRequest {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub ip_address: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_agent_handler(
|
||||||
|
State(agent_service): State<Arc<dyn AgentService>>,
|
||||||
|
Json(body): Json<CreateAgentRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
if body.name.trim().is_empty() {
|
||||||
|
return Err(AppError::BadRequest("name is required".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec = CreateAgentRecord {
|
||||||
|
name: body.name,
|
||||||
|
ip_address: body.ip_address,
|
||||||
|
};
|
||||||
|
|
||||||
|
let agent = agent_service.create(&rec).await.map_err(|err| {
|
||||||
|
error!("Failed to create agent: {}", err);
|
||||||
|
AppError::InternalServerError
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(serde_json::json!({"agent": AgentInfo::from(agent)})),
|
||||||
|
))
|
||||||
|
}
|
||||||
27
apps/nxmesh-master/src/routes/api/agents/delete_agent.rs
Normal file
27
apps/nxmesh-master/src/routes/api/agents/delete_agent.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
use crate::{routes::api::error::AppError, service::agent::AgentService};
|
||||||
|
|
||||||
|
pub async fn delete_agent_handler(
|
||||||
|
State(agent_service): State<Arc<dyn AgentService>>,
|
||||||
|
Path(id): Path<uuid::Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = agent_service.delete(id).await.map_err(|err| {
|
||||||
|
error!("Failed to delete agent: {}", err);
|
||||||
|
AppError::InternalServerError
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if deleted {
|
||||||
|
info!("Agent {} deleted", id);
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
39
apps/nxmesh-master/src/routes/api/agents/dto.rs
Normal file
39
apps/nxmesh-master/src/routes/api/agents/dto.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::service::agent::{AgentRecord, State};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AgentInfo {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub state: State,
|
||||||
|
//
|
||||||
|
pub deployment_mode: Option<String>,
|
||||||
|
pub ip_address: Option<String>,
|
||||||
|
pub last_seen_at: Option<String>,
|
||||||
|
pub labels: Option<serde_json::Value>,
|
||||||
|
//
|
||||||
|
pub number_of_routes: usize,
|
||||||
|
//
|
||||||
|
pub created_at: String,
|
||||||
|
pub updated_at: String,
|
||||||
|
pub is_disabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AgentRecord> for AgentInfo {
|
||||||
|
fn from(record: AgentRecord) -> Self {
|
||||||
|
AgentInfo {
|
||||||
|
id: record.id.to_string(),
|
||||||
|
name: record.name,
|
||||||
|
ip_address: record.ip_address,
|
||||||
|
state: record.state,
|
||||||
|
deployment_mode: record.deployment_mode,
|
||||||
|
last_seen_at: record.last_seen_at,
|
||||||
|
labels: record.labels,
|
||||||
|
number_of_routes: 0, // This will be populated later
|
||||||
|
created_at: record.created_at,
|
||||||
|
updated_at: record.updated_at,
|
||||||
|
is_disabled: matches!(record.state, State::Disabled),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
apps/nxmesh-master/src/routes/api/agents/get_agent.rs
Normal file
52
apps/nxmesh-master/src/routes/api/agents/get_agent.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
routes::api::{agents::dto::AgentInfo, error::AppError},
|
||||||
|
service::agent::AgentService,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct GetAgentsResponse {
|
||||||
|
agents: Vec<AgentInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct GetAgentResponse {
|
||||||
|
agent: AgentInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_agents_handler(
|
||||||
|
State(agent_service): State<Arc<dyn AgentService>>,
|
||||||
|
) -> Result<Json<GetAgentsResponse>, AppError> {
|
||||||
|
let agents = agent_service.list().await.map_err(|err| {
|
||||||
|
error!("Failed to get agents: {}", err);
|
||||||
|
AppError::InternalServerError
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Json(GetAgentsResponse {
|
||||||
|
agents: agents.into_iter().map(AgentInfo::from).collect(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_agent_handler(
|
||||||
|
State(agent_service): State<Arc<dyn AgentService>>,
|
||||||
|
Path(id): Path<uuid::Uuid>,
|
||||||
|
) -> Result<Json<GetAgentResponse>, AppError> {
|
||||||
|
let agent = agent_service.get(id).await.map_err(|err| {
|
||||||
|
error!("Failed to get agent: {}", err);
|
||||||
|
AppError::InternalServerError
|
||||||
|
})?;
|
||||||
|
match agent {
|
||||||
|
Some(agent) => Ok(Json(GetAgentResponse {
|
||||||
|
agent: AgentInfo::from(agent),
|
||||||
|
})),
|
||||||
|
None => Err(AppError::NotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
225
apps/nxmesh-master/src/routes/api/agents/mod.rs
Normal file
225
apps/nxmesh-master/src/routes/api/agents/mod.rs
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
use crate::routes::api::{
|
||||||
|
ApiRouter,
|
||||||
|
agents::{
|
||||||
|
add_agent::add_agent_handler,
|
||||||
|
delete_agent::delete_agent_handler,
|
||||||
|
get_agent::{get_agent_handler, get_agents_handler},
|
||||||
|
update_agent::update_agent_handler,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod add_agent;
|
||||||
|
mod delete_agent;
|
||||||
|
mod dto;
|
||||||
|
mod get_agent;
|
||||||
|
mod update_agent;
|
||||||
|
|
||||||
|
pub async fn get_router() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/agents",
|
||||||
|
axum::routing::get(get_agents_handler).post(add_agent_handler),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/agents/{id}",
|
||||||
|
axum::routing::get(get_agent_handler)
|
||||||
|
.put(update_agent_handler)
|
||||||
|
.delete(delete_agent_handler),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::service::agent::{AgentRecord, MockAgentService, State};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use axum_test::TestServer;
|
||||||
|
|
||||||
|
async fn make_state(mock: MockAgentService) -> axum::Router {
|
||||||
|
use crate::service::proxy::*;
|
||||||
|
let state = crate::routes::api::LocalApiState::from(crate::routes::api::ApiState {
|
||||||
|
agent_service: Arc::new(mock),
|
||||||
|
proxy_service: Arc::new(MockProxyServiceTrait::new()),
|
||||||
|
server_block_service: Arc::new(server_block::MockServerBlockService::new()),
|
||||||
|
upstream_service: Arc::new(upstream::MockUpstreamService::new()),
|
||||||
|
location_block_service: Arc::new(location_block::MockLocationBlockService::new()),
|
||||||
|
access_rule_service: Arc::new(access_rule::MockAccessRuleService::new()),
|
||||||
|
cache_zone_service: Arc::new(cache_zone::MockCacheZoneService::new()),
|
||||||
|
limit_rule_service: Arc::new(limit_rule::MockLimitRuleService::new()),
|
||||||
|
limit_zone_service: Arc::new(limit_zone::MockLimitZoneService::new()),
|
||||||
|
log_setting_service: Arc::new(log_setting::MockLogSettingService::new()),
|
||||||
|
proxy_setting_service: Arc::new(proxy_setting::MockProxySettingService::new()),
|
||||||
|
rewrite_rule_service: Arc::new(rewrite_rule::MockRewriteRuleService::new()),
|
||||||
|
ssl_certificate_service: Arc::new(ssl_certificate::MockSslCertificateService::new()),
|
||||||
|
config_inheritance_service: Arc::new(
|
||||||
|
config_inheritance::MockConfigInheritanceService::new(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
get_router().await.with_state(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_record(id: uuid::Uuid) -> AgentRecord {
|
||||||
|
AgentRecord {
|
||||||
|
id,
|
||||||
|
name: "agent1".to_string(),
|
||||||
|
ip_address: Some("127.0.0.1".to_string()),
|
||||||
|
state: State::Active,
|
||||||
|
deployment_mode: None,
|
||||||
|
last_seen_at: None,
|
||||||
|
labels: None,
|
||||||
|
created_at: "created".to_string(),
|
||||||
|
updated_at: "updated".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_get_agents() {
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock.expect_list().returning(|| Ok(vec![]));
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let response = server.get("/agents").await;
|
||||||
|
assert_eq!(response.status_code(), 200);
|
||||||
|
assert_eq!(response.text(), r#"{"agents":[]}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_get_agent_not_found() {
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock.expect_get().returning(|_| Ok(None));
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
let response = server.get(&format!("/agents/{}", id)).await;
|
||||||
|
assert_eq!(response.status_code(), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_get_agent_found() {
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock
|
||||||
|
.expect_get()
|
||||||
|
.returning(move |_| Ok(Some(make_record(id))));
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let expected = format!(
|
||||||
|
r#"{{"agent":{{"id":"{}","name":"agent1","state":"Active","deployment_mode":null,"ip_address":"127.0.0.1","last_seen_at":null,"labels":null,"number_of_routes":0,"created_at":"created","updated_at":"updated","is_disabled":false}}}}"#,
|
||||||
|
id
|
||||||
|
);
|
||||||
|
let response = server.get(&format!("/agents/{}", id)).await;
|
||||||
|
assert_eq!(response.status_code(), 200);
|
||||||
|
assert_eq!(response.text(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_create_agent() {
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock.expect_create().returning(move |rec| {
|
||||||
|
Ok(AgentRecord {
|
||||||
|
id,
|
||||||
|
name: rec.name.clone(),
|
||||||
|
ip_address: rec.ip_address.clone(),
|
||||||
|
state: State::Active,
|
||||||
|
deployment_mode: None,
|
||||||
|
last_seen_at: None,
|
||||||
|
labels: None,
|
||||||
|
created_at: "created".to_string(),
|
||||||
|
updated_at: "updated".to_string(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let response = server
|
||||||
|
.post("/agents")
|
||||||
|
.json(&serde_json::json!({"name": "new-agent", "ip_address": "10.0.0.1"}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(response.status_code(), 201);
|
||||||
|
let body: serde_json::Value = serde_json::from_str(&response.text()).unwrap();
|
||||||
|
assert_eq!(body["agent"]["name"], "new-agent");
|
||||||
|
assert_eq!(body["agent"]["ip_address"], "10.0.0.1");
|
||||||
|
assert_eq!(body["agent"]["id"], id.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_create_agent_empty_name() {
|
||||||
|
let agent_service_mock = MockAgentService::new();
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let response = server
|
||||||
|
.post("/agents")
|
||||||
|
.json(&serde_json::json!({"name": ""}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(response.status_code(), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_create_agent_missing_name() {
|
||||||
|
let agent_service_mock = MockAgentService::new();
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let response = server.post("/agents").json(&serde_json::json!({})).await;
|
||||||
|
assert_eq!(response.status_code(), 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_update_agent() {
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock.expect_update().returning(move |_, _| {
|
||||||
|
Ok(Some(AgentRecord {
|
||||||
|
id,
|
||||||
|
name: "updated-agent".to_string(),
|
||||||
|
ip_address: Some("10.0.0.2".to_string()),
|
||||||
|
state: State::Inactive,
|
||||||
|
deployment_mode: None,
|
||||||
|
last_seen_at: None,
|
||||||
|
labels: None,
|
||||||
|
created_at: "created".to_string(),
|
||||||
|
updated_at: "updated".to_string(),
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let response = server
|
||||||
|
.put(&format!("/agents/{}", id))
|
||||||
|
.json(&serde_json::json!({"name": "updated-agent", "state": "Inactive"}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(response.status_code(), 200);
|
||||||
|
let body: serde_json::Value = serde_json::from_str(&response.text()).unwrap();
|
||||||
|
assert_eq!(body["agent"]["name"], "updated-agent");
|
||||||
|
assert_eq!(body["agent"]["state"], "Inactive");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_update_agent_not_found() {
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock
|
||||||
|
.expect_update()
|
||||||
|
.returning(|_, _| Ok(None));
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
let response = server
|
||||||
|
.put(&format!("/agents/{}", id))
|
||||||
|
.json(&serde_json::json!({"name": "updated-agent"}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(response.status_code(), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_delete_agent() {
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock.expect_delete().returning(|_| Ok(true));
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
let response = server.delete(&format!("/agents/{}", id)).await;
|
||||||
|
assert_eq!(response.status_code(), 204);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_delete_agent_not_found() {
|
||||||
|
let mut agent_service_mock = MockAgentService::new();
|
||||||
|
agent_service_mock.expect_delete().returning(|_| Ok(false));
|
||||||
|
let server = TestServer::new(make_state(agent_service_mock).await);
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
let response = server.delete(&format!("/agents/{}", id)).await;
|
||||||
|
assert_eq!(response.status_code(), 404);
|
||||||
|
}
|
||||||
|
}
|
||||||
62
apps/nxmesh-master/src/routes/api/agents/update_agent.rs
Normal file
62
apps/nxmesh-master/src/routes/api/agents/update_agent.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
routes::api::{agents::dto::AgentInfo, error::AppError},
|
||||||
|
service::agent::{AgentService, State as AgentState, UpdateAgentRecord},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct UpdateAgentRequest {
|
||||||
|
#[serde(default)]
|
||||||
|
pub name: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub ip_address: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub state: Option<AgentState>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deployment_mode: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub labels: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_agent_handler(
|
||||||
|
State(agent_service): State<Arc<dyn AgentService>>,
|
||||||
|
Path(id): Path<uuid::Uuid>,
|
||||||
|
Json(body): Json<UpdateAgentRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
if let Some(ref name) = body.name
|
||||||
|
&& name.trim().is_empty()
|
||||||
|
{
|
||||||
|
return Err(AppError::BadRequest("name must not be empty".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec = UpdateAgentRecord {
|
||||||
|
name: body.name,
|
||||||
|
ip_address: body.ip_address,
|
||||||
|
state: body.state,
|
||||||
|
deployment_mode: body.deployment_mode,
|
||||||
|
labels: body.labels,
|
||||||
|
};
|
||||||
|
|
||||||
|
let agent = agent_service.update(id, &rec).await.map_err(|err| {
|
||||||
|
error!("Failed to update agent: {}", err);
|
||||||
|
AppError::InternalServerError
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match agent {
|
||||||
|
Some(agent) => Ok((
|
||||||
|
StatusCode::OK,
|
||||||
|
Json(serde_json::json!({"agent": AgentInfo::from(agent)})),
|
||||||
|
)),
|
||||||
|
None => Err(AppError::NotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
55
apps/nxmesh-master/src/routes/api/error.rs
Normal file
55
apps/nxmesh-master/src/routes/api/error.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use axum::{
|
||||||
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
use serde_json::json;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::service::proxy::types::ProxyServiceError;
|
||||||
|
|
||||||
|
impl From<ProxyServiceError> for AppError {
|
||||||
|
fn from(e: ProxyServiceError) -> Self {
|
||||||
|
match e {
|
||||||
|
ProxyServiceError::ConfigNotFound => AppError::NotFound,
|
||||||
|
ProxyServiceError::InvalidConfig(msg) => AppError::BadRequest(msg),
|
||||||
|
ProxyServiceError::RendererNotFound => AppError::InternalServerError,
|
||||||
|
ProxyServiceError::DatabaseError(_) => AppError::InternalServerError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AppError {
|
||||||
|
NotFound,
|
||||||
|
InternalServerError,
|
||||||
|
BadRequest(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_error_response(status: StatusCode, code: &str, msg: &str) -> Response {
|
||||||
|
let body = json!({"code": code, "message": msg});
|
||||||
|
Response::builder()
|
||||||
|
.status(status)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(serde_json::to_string(&body).unwrap_or_default().into())
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
error!("Failed to build error response: {}", err);
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for AppError {
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
match self {
|
||||||
|
AppError::BadRequest(msg) => {
|
||||||
|
make_error_response(StatusCode::BAD_REQUEST, "BAD_REQUEST", &msg)
|
||||||
|
}
|
||||||
|
AppError::NotFound => {
|
||||||
|
make_error_response(StatusCode::NOT_FOUND, "NOT_FOUND", "Not Found")
|
||||||
|
}
|
||||||
|
AppError::InternalServerError => make_error_response(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"INTERNAL_ERROR",
|
||||||
|
"Internal Server Error",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
148
apps/nxmesh-master/src/routes/api/mod.rs
Normal file
148
apps/nxmesh-master/src/routes/api/mod.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{Router, extract::FromRef};
|
||||||
|
|
||||||
|
use crate::service::agent::AgentService;
|
||||||
|
use crate::service::proxy::ProxyServiceTrait;
|
||||||
|
use crate::service::proxy::access_rule::AccessRuleService;
|
||||||
|
use crate::service::proxy::cache_zone::CacheZoneService;
|
||||||
|
use crate::service::proxy::config_inheritance::ConfigInheritanceService;
|
||||||
|
use crate::service::proxy::limit_rule::LimitRuleService;
|
||||||
|
use crate::service::proxy::limit_zone::LimitZoneService;
|
||||||
|
use crate::service::proxy::location_block::LocationBlockService;
|
||||||
|
use crate::service::proxy::log_setting::LogSettingService;
|
||||||
|
use crate::service::proxy::proxy_setting::ProxySettingService;
|
||||||
|
use crate::service::proxy::rewrite_rule::RewriteRuleService;
|
||||||
|
use crate::service::proxy::server_block::ServerBlockService;
|
||||||
|
use crate::service::proxy::ssl_certificate::SslCertificateService;
|
||||||
|
use crate::service::proxy::upstream::UpstreamService;
|
||||||
|
|
||||||
|
mod agents;
|
||||||
|
pub mod error;
|
||||||
|
pub use error::AppError;
|
||||||
|
pub(crate) mod proxy;
|
||||||
|
|
||||||
|
pub struct ApiState {
|
||||||
|
pub agent_service: Arc<dyn AgentService>,
|
||||||
|
pub proxy_service: Arc<dyn ProxyServiceTrait>,
|
||||||
|
pub server_block_service: Arc<dyn ServerBlockService>,
|
||||||
|
pub upstream_service: Arc<dyn UpstreamService>,
|
||||||
|
pub location_block_service: Arc<dyn LocationBlockService>,
|
||||||
|
pub access_rule_service: Arc<dyn AccessRuleService>,
|
||||||
|
pub cache_zone_service: Arc<dyn CacheZoneService>,
|
||||||
|
pub limit_rule_service: Arc<dyn LimitRuleService>,
|
||||||
|
pub limit_zone_service: Arc<dyn LimitZoneService>,
|
||||||
|
pub log_setting_service: Arc<dyn LogSettingService>,
|
||||||
|
pub proxy_setting_service: Arc<dyn ProxySettingService>,
|
||||||
|
pub rewrite_rule_service: Arc<dyn RewriteRuleService>,
|
||||||
|
pub ssl_certificate_service: Arc<dyn SslCertificateService>,
|
||||||
|
pub config_inheritance_service: Arc<dyn ConfigInheritanceService>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LocalApiState(pub Arc<ApiState>);
|
||||||
|
|
||||||
|
impl From<ApiState> for LocalApiState {
|
||||||
|
fn from(api_state: ApiState) -> Self {
|
||||||
|
LocalApiState(Arc::new(api_state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Arc<ApiState>> for LocalApiState {
|
||||||
|
fn from(api_state: Arc<ApiState>) -> Self {
|
||||||
|
LocalApiState(api_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ApiRouter = Router<LocalApiState>;
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn AgentService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn AgentService> {
|
||||||
|
api_state.0.agent_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn ProxyServiceTrait> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn ProxyServiceTrait> {
|
||||||
|
api_state.0.proxy_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn ServerBlockService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn ServerBlockService> {
|
||||||
|
api_state.0.server_block_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn UpstreamService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn UpstreamService> {
|
||||||
|
api_state.0.upstream_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn LocationBlockService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn LocationBlockService> {
|
||||||
|
api_state.0.location_block_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn AccessRuleService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn AccessRuleService> {
|
||||||
|
api_state.0.access_rule_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn CacheZoneService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn CacheZoneService> {
|
||||||
|
api_state.0.cache_zone_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn LimitRuleService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn LimitRuleService> {
|
||||||
|
api_state.0.limit_rule_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn LimitZoneService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn LimitZoneService> {
|
||||||
|
api_state.0.limit_zone_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn LogSettingService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn LogSettingService> {
|
||||||
|
api_state.0.log_setting_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn ProxySettingService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn ProxySettingService> {
|
||||||
|
api_state.0.proxy_setting_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn RewriteRuleService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn RewriteRuleService> {
|
||||||
|
api_state.0.rewrite_rule_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn SslCertificateService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn SslCertificateService> {
|
||||||
|
api_state.0.ssl_certificate_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRef<LocalApiState> for Arc<dyn ConfigInheritanceService> {
|
||||||
|
fn from_ref(api_state: &LocalApiState) -> Arc<dyn ConfigInheritanceService> {
|
||||||
|
api_state.0.config_inheritance_service.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_router(state: impl Into<LocalApiState>) -> Router {
|
||||||
|
ApiRouter::new()
|
||||||
|
.nest("/agents", agents::get_router().await)
|
||||||
|
.nest("/proxy", proxy::get_router().await)
|
||||||
|
.with_state(state.into())
|
||||||
|
}
|
||||||
161
apps/nxmesh-master/src/routes/api/proxy/access_rules.rs
Normal file
161
apps/nxmesh-master/src/routes/api/proxy/access_rules.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::access_rule::{
|
||||||
|
AccessRuleService, CreateAccessRuleParams, UpdateAccessRuleParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::AccessRuleConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct AccessRuleResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub r#type: String,
|
||||||
|
pub ip_cidr: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AccessRuleConfig> for AccessRuleResponse {
|
||||||
|
fn from(c: AccessRuleConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
r#type: c.r#type,
|
||||||
|
ip_cidr: c.ip_cidr,
|
||||||
|
description: c.description,
|
||||||
|
priority: c.priority,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateAccessRuleRequest {
|
||||||
|
pub server_id: Option<Uuid>,
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub r#type: String,
|
||||||
|
pub ip_cidr: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateAccessRuleRequest> for CreateAccessRuleParams {
|
||||||
|
fn from(r: CreateAccessRuleRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
server_id: r.server_id,
|
||||||
|
location_id: r.location_id,
|
||||||
|
r#type: r.r#type,
|
||||||
|
ip_cidr: r.ip_cidr,
|
||||||
|
description: r.description,
|
||||||
|
priority: r.priority,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateAccessRuleRequest {
|
||||||
|
pub server_id: Option<Option<Uuid>>,
|
||||||
|
pub location_id: Option<Option<Uuid>>,
|
||||||
|
pub r#type: Option<String>,
|
||||||
|
pub ip_cidr: Option<String>,
|
||||||
|
pub description: Option<Option<String>>,
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateAccessRuleRequest> for UpdateAccessRuleParams {
|
||||||
|
fn from(r: UpdateAccessRuleRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
server_id: r.server_id,
|
||||||
|
location_id: r.location_id,
|
||||||
|
r#type: r.r#type,
|
||||||
|
ip_cidr: r.ip_cidr,
|
||||||
|
description: r.description,
|
||||||
|
priority: r.priority,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_access_rules_by_server(
|
||||||
|
State(svc): State<Arc<dyn AccessRuleService>>,
|
||||||
|
Path(server_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<AccessRuleResponse>>, AppError> {
|
||||||
|
let rules = svc.list_by_server(server_id).await?;
|
||||||
|
Ok(Json(rules.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_access_rules_by_location(
|
||||||
|
State(svc): State<Arc<dyn AccessRuleService>>,
|
||||||
|
Path(location_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<AccessRuleResponse>>, AppError> {
|
||||||
|
let rules = svc.list_by_location(location_id).await?;
|
||||||
|
Ok(Json(rules.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_access_rule(
|
||||||
|
State(svc): State<Arc<dyn AccessRuleService>>,
|
||||||
|
Json(body): Json<CreateAccessRuleRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let rule = svc.create(body.into()).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(AccessRuleResponse::from(rule))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_access_rule(
|
||||||
|
State(svc): State<Arc<dyn AccessRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<AccessRuleResponse>, AppError> {
|
||||||
|
let rule = svc.get(id).await?;
|
||||||
|
Ok(Json(rule.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_access_rule(
|
||||||
|
State(svc): State<Arc<dyn AccessRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateAccessRuleRequest>,
|
||||||
|
) -> Result<Json<AccessRuleResponse>, AppError> {
|
||||||
|
let rule = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(rule.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_access_rule(
|
||||||
|
State(svc): State<Arc<dyn AccessRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/server-blocks/{server_id}/access-rules",
|
||||||
|
axum::routing::get(list_access_rules_by_server),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/locations/{location_id}/access-rules",
|
||||||
|
axum::routing::get(list_access_rules_by_location),
|
||||||
|
)
|
||||||
|
.route("/access-rules", axum::routing::post(create_access_rule))
|
||||||
|
.route(
|
||||||
|
"/access-rules/{id}",
|
||||||
|
axum::routing::get(get_access_rule)
|
||||||
|
.put(update_access_rule)
|
||||||
|
.delete(delete_access_rule),
|
||||||
|
)
|
||||||
|
}
|
||||||
84
apps/nxmesh-master/src/routes/api/proxy/agents.rs
Normal file
84
apps/nxmesh-master/src/routes/api/proxy/agents.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::{ProxyServiceTrait, types::AgentConfigBinding};
|
||||||
|
|
||||||
|
use super::configs::ProxyConfigResponse;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct AgentConfigResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub agent_id: Option<Uuid>,
|
||||||
|
pub group_id: Option<Uuid>,
|
||||||
|
pub config_id: Uuid,
|
||||||
|
pub is_active: bool,
|
||||||
|
pub applied_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AgentConfigBinding> for AgentConfigResponse {
|
||||||
|
fn from(b: AgentConfigBinding) -> Self {
|
||||||
|
Self {
|
||||||
|
id: b.id,
|
||||||
|
agent_id: b.agent_id,
|
||||||
|
group_id: b.group_id,
|
||||||
|
config_id: b.config_id,
|
||||||
|
is_active: b.is_active,
|
||||||
|
applied_at: b.applied_at.to_rfc3339(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct BindAgentRequest {
|
||||||
|
pub config_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_active_agent_config(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Path(agent_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Option<ProxyConfigResponse>>, AppError> {
|
||||||
|
let config = svc.get_active_agent_config(agent_id).await?;
|
||||||
|
Ok(Json(config.map(Into::into)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bind_agent(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Path(agent_id): Path<Uuid>,
|
||||||
|
Json(body): Json<BindAgentRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let binding = svc.bind_agent(agent_id, body.config_id).await?;
|
||||||
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(AgentConfigResponse::from(binding)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unbind_agent(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Path(agent_id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.unbind_agent(agent_id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new().route(
|
||||||
|
"/agents/{agent_id}/config",
|
||||||
|
axum::routing::get(get_active_agent_config)
|
||||||
|
.post(bind_agent)
|
||||||
|
.delete(unbind_agent),
|
||||||
|
)
|
||||||
|
}
|
||||||
133
apps/nxmesh-master/src/routes/api/proxy/cache_zones.rs
Normal file
133
apps/nxmesh-master/src/routes/api/proxy/cache_zones.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::cache_zone::{
|
||||||
|
CacheZoneService, CreateCacheZoneParams, UpdateCacheZoneParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::CacheZoneConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct CacheZoneResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub size: String,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CacheZoneConfig> for CacheZoneResponse {
|
||||||
|
fn from(c: CacheZoneConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
name: c.name,
|
||||||
|
path: c.path,
|
||||||
|
size: c.size,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateCacheZoneRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub size_limit: String,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateCacheZoneRequest> for CreateCacheZoneParams {
|
||||||
|
fn from(r: CreateCacheZoneRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
name: r.name,
|
||||||
|
path: r.path,
|
||||||
|
size_limit: r.size_limit,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateCacheZoneRequest {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub path: Option<String>,
|
||||||
|
pub size_limit: Option<String>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateCacheZoneRequest> for UpdateCacheZoneParams {
|
||||||
|
fn from(r: UpdateCacheZoneRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
name: r.name,
|
||||||
|
path: r.path,
|
||||||
|
size_limit: r.size_limit,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_cache_zones(
|
||||||
|
State(svc): State<Arc<dyn CacheZoneService>>,
|
||||||
|
) -> Result<Json<Vec<CacheZoneResponse>>, AppError> {
|
||||||
|
let zones = svc.list().await?;
|
||||||
|
Ok(Json(zones.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_cache_zone(
|
||||||
|
State(svc): State<Arc<dyn CacheZoneService>>,
|
||||||
|
Json(body): Json<CreateCacheZoneRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let zone = svc.create(body.into()).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(CacheZoneResponse::from(zone))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cache_zone(
|
||||||
|
State(svc): State<Arc<dyn CacheZoneService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<CacheZoneResponse>, AppError> {
|
||||||
|
let zone = svc.get(id).await?;
|
||||||
|
Ok(Json(zone.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_cache_zone(
|
||||||
|
State(svc): State<Arc<dyn CacheZoneService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateCacheZoneRequest>,
|
||||||
|
) -> Result<Json<CacheZoneResponse>, AppError> {
|
||||||
|
let zone = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(zone.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_cache_zone(
|
||||||
|
State(svc): State<Arc<dyn CacheZoneService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/cache-zones",
|
||||||
|
axum::routing::get(list_cache_zones).post(create_cache_zone),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/cache-zones/{id}",
|
||||||
|
axum::routing::get(get_cache_zone)
|
||||||
|
.put(update_cache_zone)
|
||||||
|
.delete(delete_cache_zone),
|
||||||
|
)
|
||||||
|
}
|
||||||
104
apps/nxmesh-master/src/routes/api/proxy/config_inheritance.rs
Normal file
104
apps/nxmesh-master/src/routes/api/proxy/config_inheritance.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::config_inheritance::{
|
||||||
|
AddInheritanceParams, ConfigInheritanceRecord, ConfigInheritanceService,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct AddParentRequest {
|
||||||
|
pub parent_config_id: Uuid,
|
||||||
|
#[serde(default)]
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct InheritanceRecordResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub child_config_id: Uuid,
|
||||||
|
pub parent_config_id: Uuid,
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
pub applied_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigInheritanceRecord> for InheritanceRecordResponse {
|
||||||
|
fn from(r: ConfigInheritanceRecord) -> Self {
|
||||||
|
Self {
|
||||||
|
id: r.id,
|
||||||
|
child_config_id: r.child_config_id,
|
||||||
|
parent_config_id: r.parent_config_id,
|
||||||
|
priority: r.priority,
|
||||||
|
applied_at: r.applied_at.and_utc().to_rfc3339(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_parents(
|
||||||
|
State(svc): State<Arc<dyn ConfigInheritanceService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<serde_json::Value>, AppError> {
|
||||||
|
let records = svc.list_parents(id).await?;
|
||||||
|
let response: Vec<InheritanceRecordResponse> = records.into_iter().map(Into::into).collect();
|
||||||
|
Ok(Json(serde_json::json!(response)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_parent(
|
||||||
|
State(svc): State<Arc<dyn ConfigInheritanceService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<AddParentRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let record = svc
|
||||||
|
.add(AddInheritanceParams {
|
||||||
|
child_config_id: id,
|
||||||
|
parent_config_id: body.parent_config_id,
|
||||||
|
priority: body.priority,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(InheritanceRecordResponse::from(record)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_parent(
|
||||||
|
State(svc): State<Arc<dyn ConfigInheritanceService>>,
|
||||||
|
Path((id, parent_id)): Path<(Uuid, Uuid)>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.remove(id, parent_id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_children(
|
||||||
|
State(svc): State<Arc<dyn ConfigInheritanceService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<serde_json::Value>, AppError> {
|
||||||
|
let records = svc.list_children(id).await?;
|
||||||
|
let response: Vec<InheritanceRecordResponse> = records.into_iter().map(Into::into).collect();
|
||||||
|
Ok(Json(serde_json::json!(response)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/configs/{id}/parents",
|
||||||
|
axum::routing::get(list_parents).post(add_parent),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/configs/{id}/parents/{parent_id}",
|
||||||
|
axum::routing::delete(remove_parent),
|
||||||
|
)
|
||||||
|
.route("/configs/{id}/children", axum::routing::get(list_children))
|
||||||
|
}
|
||||||
150
apps/nxmesh-master/src/routes/api/proxy/configs.rs
Normal file
150
apps/nxmesh-master/src/routes/api/proxy/configs.rs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::{
|
||||||
|
ProxyServiceTrait,
|
||||||
|
types::{CreateProxyConfigParams, ProxyConfigSummary, UpdateProxyConfigParams},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct ProxyConfigResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub is_template: bool,
|
||||||
|
pub created_at: String,
|
||||||
|
pub updated_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ProxyConfigSummary> for ProxyConfigResponse {
|
||||||
|
fn from(s: ProxyConfigSummary) -> Self {
|
||||||
|
Self {
|
||||||
|
id: s.id,
|
||||||
|
name: s.name,
|
||||||
|
description: s.description,
|
||||||
|
is_template: s.is_template,
|
||||||
|
created_at: s.created_at.to_rfc3339(),
|
||||||
|
updated_at: s.updated_at.to_rfc3339(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateConfigRequest {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub is_template: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateConfigRequest {
|
||||||
|
#[serde(default)]
|
||||||
|
pub name: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub is_template: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct ListConfigsResponse {
|
||||||
|
pub configs: Vec<ProxyConfigResponse>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_configs(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
) -> Result<Json<ListConfigsResponse>, AppError> {
|
||||||
|
let configs = svc.list_configs().await?;
|
||||||
|
Ok(Json(ListConfigsResponse {
|
||||||
|
configs: configs.into_iter().map(Into::into).collect(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_config(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Json(body): Json<CreateConfigRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
if body.name.trim().is_empty() {
|
||||||
|
return Err(AppError::BadRequest("name is required".to_string()));
|
||||||
|
}
|
||||||
|
let config = svc
|
||||||
|
.create_config(CreateProxyConfigParams {
|
||||||
|
name: body.name,
|
||||||
|
description: body.description,
|
||||||
|
is_template: body.is_template,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(ProxyConfigResponse::from(config))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_config(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<serde_json::Value>, AppError> {
|
||||||
|
let config = svc.get_proxy_config(id).await?;
|
||||||
|
Ok(Json(serde_json::to_value(config.id).unwrap_or_default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_config(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateConfigRequest>,
|
||||||
|
) -> Result<Json<ProxyConfigResponse>, AppError> {
|
||||||
|
let config = svc
|
||||||
|
.update_config(
|
||||||
|
id,
|
||||||
|
UpdateProxyConfigParams {
|
||||||
|
name: body.name,
|
||||||
|
description: body.description,
|
||||||
|
is_template: body.is_template,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(Json(config.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_config(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete_config(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn render_config(
|
||||||
|
State(svc): State<Arc<dyn ProxyServiceTrait>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<String, AppError> {
|
||||||
|
let output = svc.render_config(id).await?;
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/configs",
|
||||||
|
axum::routing::get(list_configs).post(create_config),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/configs/{id}",
|
||||||
|
axum::routing::get(get_config)
|
||||||
|
.put(update_config)
|
||||||
|
.delete(delete_config),
|
||||||
|
)
|
||||||
|
.route("/configs/{id}/render", axum::routing::get(render_config))
|
||||||
|
}
|
||||||
141
apps/nxmesh-master/src/routes/api/proxy/limit_rules.rs
Normal file
141
apps/nxmesh-master/src/routes/api/proxy/limit_rules.rs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::limit_rule::{
|
||||||
|
CreateLimitRuleParams, LimitRuleService, UpdateLimitRuleParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::LimitRuleConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct LimitRuleResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub location_id: Uuid,
|
||||||
|
pub zone_id: Uuid,
|
||||||
|
pub burst: Option<i32>,
|
||||||
|
pub nodelay: Option<bool>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LimitRuleConfig> for LimitRuleResponse {
|
||||||
|
fn from(c: LimitRuleConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
location_id: c.location_id,
|
||||||
|
zone_id: c.zone_id,
|
||||||
|
burst: c.burst,
|
||||||
|
nodelay: c.nodelay,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateLimitRuleRequest {
|
||||||
|
pub location_id: Uuid,
|
||||||
|
pub zone_id: Uuid,
|
||||||
|
pub burst: Option<i32>,
|
||||||
|
pub nodelay: Option<bool>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateLimitRuleRequest> for CreateLimitRuleParams {
|
||||||
|
fn from(r: CreateLimitRuleRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
location_id: r.location_id,
|
||||||
|
zone_id: r.zone_id,
|
||||||
|
burst: r.burst,
|
||||||
|
nodelay: r.nodelay,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateLimitRuleRequest {
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub zone_id: Option<Uuid>,
|
||||||
|
pub burst: Option<Option<i32>>,
|
||||||
|
pub nodelay: Option<Option<bool>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateLimitRuleRequest> for UpdateLimitRuleParams {
|
||||||
|
fn from(r: UpdateLimitRuleRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
location_id: r.location_id,
|
||||||
|
zone_id: r.zone_id,
|
||||||
|
burst: r.burst,
|
||||||
|
nodelay: r.nodelay,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_limit_rules_by_location(
|
||||||
|
State(svc): State<Arc<dyn LimitRuleService>>,
|
||||||
|
Path(location_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<LimitRuleResponse>>, AppError> {
|
||||||
|
let rules = svc.list_by_location(location_id).await?;
|
||||||
|
Ok(Json(rules.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_limit_rule(
|
||||||
|
State(svc): State<Arc<dyn LimitRuleService>>,
|
||||||
|
Json(body): Json<CreateLimitRuleRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let rule = svc.create(body.into()).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(LimitRuleResponse::from(rule))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_limit_rule(
|
||||||
|
State(svc): State<Arc<dyn LimitRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<LimitRuleResponse>, AppError> {
|
||||||
|
let rule = svc.get(id).await?;
|
||||||
|
Ok(Json(rule.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_limit_rule(
|
||||||
|
State(svc): State<Arc<dyn LimitRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateLimitRuleRequest>,
|
||||||
|
) -> Result<Json<LimitRuleResponse>, AppError> {
|
||||||
|
let rule = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(rule.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_limit_rule(
|
||||||
|
State(svc): State<Arc<dyn LimitRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/locations/{location_id}/limit-rules",
|
||||||
|
axum::routing::get(list_limit_rules_by_location),
|
||||||
|
)
|
||||||
|
.route("/limit-rules", axum::routing::post(create_limit_rule))
|
||||||
|
.route(
|
||||||
|
"/limit-rules/{id}",
|
||||||
|
axum::routing::get(get_limit_rule)
|
||||||
|
.put(update_limit_rule)
|
||||||
|
.delete(delete_limit_rule),
|
||||||
|
)
|
||||||
|
}
|
||||||
133
apps/nxmesh-master/src/routes/api/proxy/limit_zones.rs
Normal file
133
apps/nxmesh-master/src/routes/api/proxy/limit_zones.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::limit_zone::{
|
||||||
|
CreateLimitZoneParams, LimitZoneService, UpdateLimitZoneParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::LimitZoneConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct LimitZoneResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub key: String,
|
||||||
|
pub rate: String,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LimitZoneConfig> for LimitZoneResponse {
|
||||||
|
fn from(c: LimitZoneConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
name: c.name,
|
||||||
|
key: c.key,
|
||||||
|
rate: c.rate,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateLimitZoneRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub key: String,
|
||||||
|
pub rate: String,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateLimitZoneRequest> for CreateLimitZoneParams {
|
||||||
|
fn from(r: CreateLimitZoneRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
name: r.name,
|
||||||
|
key: r.key,
|
||||||
|
rate: r.rate,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateLimitZoneRequest {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub key: Option<String>,
|
||||||
|
pub rate: Option<String>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateLimitZoneRequest> for UpdateLimitZoneParams {
|
||||||
|
fn from(r: UpdateLimitZoneRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
name: r.name,
|
||||||
|
key: r.key,
|
||||||
|
rate: r.rate,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_limit_zones(
|
||||||
|
State(svc): State<Arc<dyn LimitZoneService>>,
|
||||||
|
) -> Result<Json<Vec<LimitZoneResponse>>, AppError> {
|
||||||
|
let zones = svc.list().await?;
|
||||||
|
Ok(Json(zones.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_limit_zone(
|
||||||
|
State(svc): State<Arc<dyn LimitZoneService>>,
|
||||||
|
Json(body): Json<CreateLimitZoneRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let zone = svc.create(body.into()).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(LimitZoneResponse::from(zone))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_limit_zone(
|
||||||
|
State(svc): State<Arc<dyn LimitZoneService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<LimitZoneResponse>, AppError> {
|
||||||
|
let zone = svc.get(id).await?;
|
||||||
|
Ok(Json(zone.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_limit_zone(
|
||||||
|
State(svc): State<Arc<dyn LimitZoneService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateLimitZoneRequest>,
|
||||||
|
) -> Result<Json<LimitZoneResponse>, AppError> {
|
||||||
|
let zone = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(zone.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_limit_zone(
|
||||||
|
State(svc): State<Arc<dyn LimitZoneService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/limit-zones",
|
||||||
|
axum::routing::get(list_limit_zones).post(create_limit_zone),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/limit-zones/{id}",
|
||||||
|
axum::routing::get(get_limit_zone)
|
||||||
|
.put(update_limit_zone)
|
||||||
|
.delete(delete_limit_zone),
|
||||||
|
)
|
||||||
|
}
|
||||||
145
apps/nxmesh-master/src/routes/api/proxy/locations.rs
Normal file
145
apps/nxmesh-master/src/routes/api/proxy/locations.rs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::location_block::{
|
||||||
|
CreateLocationBlockParams, LocationBlockService, UpdateLocationBlockParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::LocationBlockConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct LocationBlockResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub server_id: Uuid,
|
||||||
|
pub path_pattern: String,
|
||||||
|
pub proxy_pass_upstream_id: Option<Uuid>,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LocationBlockConfig> for LocationBlockResponse {
|
||||||
|
fn from(c: LocationBlockConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
server_id: c.server_id,
|
||||||
|
path_pattern: c.path_pattern,
|
||||||
|
proxy_pass_upstream_id: c.proxy_pass_upstream_id,
|
||||||
|
metadata: c.metadata,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateLocationBlockRequest {
|
||||||
|
pub path_pattern: String,
|
||||||
|
pub proxy_pass_upstream_id: Option<Uuid>,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateLocationBlockRequest> for CreateLocationBlockParams {
|
||||||
|
fn from(r: CreateLocationBlockRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
server_id: Uuid::nil(),
|
||||||
|
path_pattern: r.path_pattern,
|
||||||
|
proxy_pass_upstream_id: r.proxy_pass_upstream_id,
|
||||||
|
metadata: r.metadata,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateLocationBlockRequest {
|
||||||
|
pub server_id: Option<Uuid>,
|
||||||
|
pub path_pattern: Option<String>,
|
||||||
|
pub proxy_pass_upstream_id: Option<Option<Uuid>>,
|
||||||
|
pub metadata: Option<Option<serde_json::Value>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateLocationBlockRequest> for UpdateLocationBlockParams {
|
||||||
|
fn from(r: UpdateLocationBlockRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
server_id: r.server_id,
|
||||||
|
path_pattern: r.path_pattern,
|
||||||
|
proxy_pass_upstream_id: r.proxy_pass_upstream_id,
|
||||||
|
metadata: r.metadata,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_locations(
|
||||||
|
State(svc): State<Arc<dyn LocationBlockService>>,
|
||||||
|
Path(server_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<LocationBlockResponse>>, AppError> {
|
||||||
|
let locations = svc.list_by_server(server_id).await?;
|
||||||
|
Ok(Json(locations.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_location(
|
||||||
|
State(svc): State<Arc<dyn LocationBlockService>>,
|
||||||
|
Path(server_id): Path<Uuid>,
|
||||||
|
Json(body): Json<CreateLocationBlockRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let mut params = CreateLocationBlockParams::from(body);
|
||||||
|
params.server_id = server_id;
|
||||||
|
let location = svc.create(params).await?;
|
||||||
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(LocationBlockResponse::from(location)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_location(
|
||||||
|
State(svc): State<Arc<dyn LocationBlockService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<LocationBlockResponse>, AppError> {
|
||||||
|
let location = svc.get(id).await?;
|
||||||
|
Ok(Json(location.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_location(
|
||||||
|
State(svc): State<Arc<dyn LocationBlockService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateLocationBlockRequest>,
|
||||||
|
) -> Result<Json<LocationBlockResponse>, AppError> {
|
||||||
|
let location = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(location.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_location(
|
||||||
|
State(svc): State<Arc<dyn LocationBlockService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/server-blocks/{server_id}/locations",
|
||||||
|
axum::routing::get(list_locations).post(create_location),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/locations/{id}",
|
||||||
|
axum::routing::get(get_location)
|
||||||
|
.put(update_location)
|
||||||
|
.delete(delete_location),
|
||||||
|
)
|
||||||
|
}
|
||||||
140
apps/nxmesh-master/src/routes/api/proxy/log_settings.rs
Normal file
140
apps/nxmesh-master/src/routes/api/proxy/log_settings.rs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::log_setting::{
|
||||||
|
CreateLogSettingParams, LogSettingService, UpdateLogSettingParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::LogSettingConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct LogSettingResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub access_log_path: Option<String>,
|
||||||
|
pub error_log_path: Option<String>,
|
||||||
|
pub log_level: Option<String>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LogSettingConfig> for LogSettingResponse {
|
||||||
|
fn from(c: LogSettingConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
access_log_path: c.access_log_path,
|
||||||
|
error_log_path: c.error_log_path,
|
||||||
|
log_level: c.log_level,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateLogSettingRequest {
|
||||||
|
pub access_log_path: Option<String>,
|
||||||
|
pub error_log_path: Option<String>,
|
||||||
|
pub log_level: Option<String>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateLogSettingRequest> for CreateLogSettingParams {
|
||||||
|
fn from(r: CreateLogSettingRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
server_id: Uuid::nil(),
|
||||||
|
access_log_path: r.access_log_path,
|
||||||
|
error_log_path: r.error_log_path,
|
||||||
|
log_level: r.log_level,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateLogSettingRequest {
|
||||||
|
pub server_id: Option<Uuid>,
|
||||||
|
pub access_log_path: Option<Option<String>>,
|
||||||
|
pub error_log_path: Option<Option<String>>,
|
||||||
|
pub log_level: Option<Option<String>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateLogSettingRequest> for UpdateLogSettingParams {
|
||||||
|
fn from(r: UpdateLogSettingRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
server_id: r.server_id,
|
||||||
|
access_log_path: r.access_log_path,
|
||||||
|
error_log_path: r.error_log_path,
|
||||||
|
log_level: r.log_level,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_log_settings(
|
||||||
|
State(svc): State<Arc<dyn LogSettingService>>,
|
||||||
|
Path(server_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<LogSettingResponse>>, AppError> {
|
||||||
|
let settings = svc.list_by_server(server_id).await?;
|
||||||
|
Ok(Json(settings.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_log_setting(
|
||||||
|
State(svc): State<Arc<dyn LogSettingService>>,
|
||||||
|
Path(server_id): Path<Uuid>,
|
||||||
|
Json(body): Json<CreateLogSettingRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let mut params = CreateLogSettingParams::from(body);
|
||||||
|
params.server_id = server_id;
|
||||||
|
let setting = svc.create(params).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(LogSettingResponse::from(setting))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_log_setting(
|
||||||
|
State(svc): State<Arc<dyn LogSettingService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<LogSettingResponse>, AppError> {
|
||||||
|
let setting = svc.get(id).await?;
|
||||||
|
Ok(Json(setting.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_log_setting(
|
||||||
|
State(svc): State<Arc<dyn LogSettingService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateLogSettingRequest>,
|
||||||
|
) -> Result<Json<LogSettingResponse>, AppError> {
|
||||||
|
let setting = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(setting.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_log_setting(
|
||||||
|
State(svc): State<Arc<dyn LogSettingService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/server-blocks/{server_id}/log-settings",
|
||||||
|
axum::routing::get(list_log_settings).post(create_log_setting),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/log-settings/{id}",
|
||||||
|
axum::routing::get(get_log_setting)
|
||||||
|
.put(update_log_setting)
|
||||||
|
.delete(delete_log_setting),
|
||||||
|
)
|
||||||
|
}
|
||||||
37
apps/nxmesh-master/src/routes/api/proxy/mod.rs
Normal file
37
apps/nxmesh-master/src/routes/api/proxy/mod.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use crate::routes::api::ApiRouter;
|
||||||
|
|
||||||
|
pub(crate) mod access_rules;
|
||||||
|
pub(crate) mod agents;
|
||||||
|
pub(crate) mod cache_zones;
|
||||||
|
pub(crate) mod config_inheritance;
|
||||||
|
pub(crate) mod configs;
|
||||||
|
pub(crate) mod limit_rules;
|
||||||
|
pub(crate) mod limit_zones;
|
||||||
|
pub(crate) mod locations;
|
||||||
|
pub(crate) mod log_settings;
|
||||||
|
pub(crate) mod proxy_settings;
|
||||||
|
pub(crate) mod rewrite_rules;
|
||||||
|
pub(crate) mod server_blocks;
|
||||||
|
pub(crate) mod ssl_certificates;
|
||||||
|
pub(crate) mod upstreams;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod test_builder;
|
||||||
|
|
||||||
|
pub async fn get_router() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.merge(configs::routes())
|
||||||
|
.merge(agents::routes())
|
||||||
|
.merge(config_inheritance::routes())
|
||||||
|
.merge(server_blocks::routes())
|
||||||
|
.merge(upstreams::routes())
|
||||||
|
.merge(locations::routes())
|
||||||
|
.merge(access_rules::routes())
|
||||||
|
.merge(cache_zones::routes())
|
||||||
|
.merge(limit_rules::routes())
|
||||||
|
.merge(limit_zones::routes())
|
||||||
|
.merge(log_settings::routes())
|
||||||
|
.merge(proxy_settings::routes())
|
||||||
|
.merge(rewrite_rules::routes())
|
||||||
|
.merge(ssl_certificates::routes())
|
||||||
|
}
|
||||||
157
apps/nxmesh-master/src/routes/api/proxy/proxy_settings.rs
Normal file
157
apps/nxmesh-master/src/routes/api/proxy/proxy_settings.rs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::proxy_setting::{
|
||||||
|
CreateProxySettingParams, ProxySettingService, UpdateProxySettingParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::ProxySettingConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct ProxySettingResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub location_id: Uuid,
|
||||||
|
pub read_timeout: Option<i32>,
|
||||||
|
pub connect_timeout: Option<i32>,
|
||||||
|
pub buffer_size: Option<i32>,
|
||||||
|
pub cache_enabled: Option<bool>,
|
||||||
|
pub cache_zone: Option<Uuid>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ProxySettingConfig> for ProxySettingResponse {
|
||||||
|
fn from(c: ProxySettingConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
location_id: c.location_id,
|
||||||
|
read_timeout: c.read_timeout,
|
||||||
|
connect_timeout: c.connect_timeout,
|
||||||
|
buffer_size: c.buffer_size,
|
||||||
|
cache_enabled: c.cache_enabled,
|
||||||
|
cache_zone: c.cache_zone,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateProxySettingRequest {
|
||||||
|
pub read_timeout: Option<i32>,
|
||||||
|
pub connect_timeout: Option<i32>,
|
||||||
|
pub buffer_size: Option<i32>,
|
||||||
|
pub cache_enabled: Option<bool>,
|
||||||
|
pub cache_zone: Option<Uuid>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateProxySettingRequest> for CreateProxySettingParams {
|
||||||
|
fn from(r: CreateProxySettingRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
location_id: Uuid::nil(),
|
||||||
|
read_timeout: r.read_timeout,
|
||||||
|
connect_timeout: r.connect_timeout,
|
||||||
|
buffer_size: r.buffer_size,
|
||||||
|
cache_enabled: r.cache_enabled,
|
||||||
|
cache_zone: r.cache_zone,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateProxySettingRequest {
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub read_timeout: Option<Option<i32>>,
|
||||||
|
pub connect_timeout: Option<Option<i32>>,
|
||||||
|
pub buffer_size: Option<Option<i32>>,
|
||||||
|
pub cache_enabled: Option<Option<bool>>,
|
||||||
|
pub cache_zone: Option<Option<Uuid>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateProxySettingRequest> for UpdateProxySettingParams {
|
||||||
|
fn from(r: UpdateProxySettingRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
location_id: r.location_id,
|
||||||
|
read_timeout: r.read_timeout,
|
||||||
|
connect_timeout: r.connect_timeout,
|
||||||
|
buffer_size: r.buffer_size,
|
||||||
|
cache_enabled: r.cache_enabled,
|
||||||
|
cache_zone: r.cache_zone,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_proxy_settings(
|
||||||
|
State(svc): State<Arc<dyn ProxySettingService>>,
|
||||||
|
Path(location_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<ProxySettingResponse>>, AppError> {
|
||||||
|
let settings = svc.list_by_location(location_id).await?;
|
||||||
|
Ok(Json(settings.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_proxy_setting(
|
||||||
|
State(svc): State<Arc<dyn ProxySettingService>>,
|
||||||
|
Path(location_id): Path<Uuid>,
|
||||||
|
Json(body): Json<CreateProxySettingRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let mut params = CreateProxySettingParams::from(body);
|
||||||
|
params.location_id = location_id;
|
||||||
|
let setting = svc.create(params).await?;
|
||||||
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(ProxySettingResponse::from(setting)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_proxy_setting(
|
||||||
|
State(svc): State<Arc<dyn ProxySettingService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<ProxySettingResponse>, AppError> {
|
||||||
|
let setting = svc.get(id).await?;
|
||||||
|
Ok(Json(setting.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_proxy_setting(
|
||||||
|
State(svc): State<Arc<dyn ProxySettingService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateProxySettingRequest>,
|
||||||
|
) -> Result<Json<ProxySettingResponse>, AppError> {
|
||||||
|
let setting = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(setting.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_proxy_setting(
|
||||||
|
State(svc): State<Arc<dyn ProxySettingService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/locations/{location_id}/proxy-settings",
|
||||||
|
axum::routing::get(list_proxy_settings).post(create_proxy_setting),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/proxy-settings/{id}",
|
||||||
|
axum::routing::get(get_proxy_setting)
|
||||||
|
.put(update_proxy_setting)
|
||||||
|
.delete(delete_proxy_setting),
|
||||||
|
)
|
||||||
|
}
|
||||||
148
apps/nxmesh-master/src/routes/api/proxy/rewrite_rules.rs
Normal file
148
apps/nxmesh-master/src/routes/api/proxy/rewrite_rules.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::rewrite_rule::{
|
||||||
|
CreateRewriteRuleParams, RewriteRuleService, UpdateRewriteRuleParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::RewriteRuleConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct RewriteRuleResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub location_id: Uuid,
|
||||||
|
pub pattern: String,
|
||||||
|
pub replacement: String,
|
||||||
|
pub flag: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RewriteRuleConfig> for RewriteRuleResponse {
|
||||||
|
fn from(c: RewriteRuleConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
location_id: c.location_id,
|
||||||
|
pattern: c.pattern,
|
||||||
|
replacement: c.replacement,
|
||||||
|
flag: c.flag,
|
||||||
|
priority: c.priority,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateRewriteRuleRequest {
|
||||||
|
pub pattern: String,
|
||||||
|
pub replacement: String,
|
||||||
|
pub flag: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateRewriteRuleRequest> for CreateRewriteRuleParams {
|
||||||
|
fn from(r: CreateRewriteRuleRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
location_id: Uuid::nil(),
|
||||||
|
pattern: r.pattern,
|
||||||
|
replacement: r.replacement,
|
||||||
|
flag: r.flag,
|
||||||
|
priority: r.priority,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateRewriteRuleRequest {
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub pattern: Option<String>,
|
||||||
|
pub replacement: Option<String>,
|
||||||
|
pub flag: Option<Option<String>>,
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateRewriteRuleRequest> for UpdateRewriteRuleParams {
|
||||||
|
fn from(r: UpdateRewriteRuleRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
location_id: r.location_id,
|
||||||
|
pattern: r.pattern,
|
||||||
|
replacement: r.replacement,
|
||||||
|
flag: r.flag,
|
||||||
|
priority: r.priority,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_rewrite_rules(
|
||||||
|
State(svc): State<Arc<dyn RewriteRuleService>>,
|
||||||
|
Path(location_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<RewriteRuleResponse>>, AppError> {
|
||||||
|
let rules = svc.list_by_location(location_id).await?;
|
||||||
|
Ok(Json(rules.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_rewrite_rule(
|
||||||
|
State(svc): State<Arc<dyn RewriteRuleService>>,
|
||||||
|
Path(location_id): Path<Uuid>,
|
||||||
|
Json(body): Json<CreateRewriteRuleRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let mut params = CreateRewriteRuleParams::from(body);
|
||||||
|
params.location_id = location_id;
|
||||||
|
let rule = svc.create(params).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(RewriteRuleResponse::from(rule))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_rewrite_rule(
|
||||||
|
State(svc): State<Arc<dyn RewriteRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<RewriteRuleResponse>, AppError> {
|
||||||
|
let rule = svc.get(id).await?;
|
||||||
|
Ok(Json(rule.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_rewrite_rule(
|
||||||
|
State(svc): State<Arc<dyn RewriteRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateRewriteRuleRequest>,
|
||||||
|
) -> Result<Json<RewriteRuleResponse>, AppError> {
|
||||||
|
let rule = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(rule.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_rewrite_rule(
|
||||||
|
State(svc): State<Arc<dyn RewriteRuleService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/locations/{location_id}/rewrite-rules",
|
||||||
|
axum::routing::get(list_rewrite_rules).post(create_rewrite_rule),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/rewrite-rules/{id}",
|
||||||
|
axum::routing::get(get_rewrite_rule)
|
||||||
|
.put(update_rewrite_rule)
|
||||||
|
.delete(delete_rewrite_rule),
|
||||||
|
)
|
||||||
|
}
|
||||||
142
apps/nxmesh-master/src/routes/api/proxy/server_blocks.rs
Normal file
142
apps/nxmesh-master/src/routes/api/proxy/server_blocks.rs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::server_block::{
|
||||||
|
CreateServerBlockParams, ServerBlockService, UpdateServerBlockParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::ServerBlockConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct ServerBlockResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub server_name: Option<Vec<String>>,
|
||||||
|
pub listen_port: i32,
|
||||||
|
pub ssl_enabled: Option<bool>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ServerBlockConfig> for ServerBlockResponse {
|
||||||
|
fn from(c: ServerBlockConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
server_name: c.server_name,
|
||||||
|
listen_port: c.listen_port,
|
||||||
|
ssl_enabled: c.ssl_enabled,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateServerBlockRequest {
|
||||||
|
pub server_name: Option<Vec<String>>,
|
||||||
|
pub listen_port: i32,
|
||||||
|
pub ssl_enabled: Option<bool>,
|
||||||
|
pub ssl_cert_id: Option<Uuid>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateServerBlockRequest> for CreateServerBlockParams {
|
||||||
|
fn from(r: CreateServerBlockRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
config_id: Uuid::nil(),
|
||||||
|
server_name: r.server_name,
|
||||||
|
listen_port: r.listen_port,
|
||||||
|
ssl_enabled: r.ssl_enabled,
|
||||||
|
ssl_cert_id: r.ssl_cert_id,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateServerBlockRequest {
|
||||||
|
pub server_name: Option<Option<Vec<String>>>,
|
||||||
|
pub listen_port: Option<i32>,
|
||||||
|
pub ssl_enabled: Option<Option<bool>>,
|
||||||
|
pub ssl_cert_id: Option<Option<Uuid>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateServerBlockRequest> for UpdateServerBlockParams {
|
||||||
|
fn from(r: UpdateServerBlockRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
server_name: r.server_name,
|
||||||
|
listen_port: r.listen_port,
|
||||||
|
ssl_enabled: r.ssl_enabled,
|
||||||
|
ssl_cert_id: r.ssl_cert_id,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_server_blocks(
|
||||||
|
State(svc): State<Arc<dyn ServerBlockService>>,
|
||||||
|
Path(config_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<ServerBlockResponse>>, AppError> {
|
||||||
|
let blocks = svc.list_by_config(config_id).await?;
|
||||||
|
Ok(Json(blocks.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_server_block(
|
||||||
|
State(svc): State<Arc<dyn ServerBlockService>>,
|
||||||
|
Path(config_id): Path<Uuid>,
|
||||||
|
Json(body): Json<CreateServerBlockRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let mut params = CreateServerBlockParams::from(body);
|
||||||
|
params.config_id = config_id;
|
||||||
|
let block = svc.create(params).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(ServerBlockResponse::from(block))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_server_block(
|
||||||
|
State(svc): State<Arc<dyn ServerBlockService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<ServerBlockResponse>, AppError> {
|
||||||
|
let block = svc.get(id).await?;
|
||||||
|
Ok(Json(block.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_server_block(
|
||||||
|
State(svc): State<Arc<dyn ServerBlockService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateServerBlockRequest>,
|
||||||
|
) -> Result<Json<ServerBlockResponse>, AppError> {
|
||||||
|
let block = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(block.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_server_block(
|
||||||
|
State(svc): State<Arc<dyn ServerBlockService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/configs/{config_id}/server-blocks",
|
||||||
|
axum::routing::get(list_server_blocks).post(create_server_block),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/server-blocks/{id}",
|
||||||
|
axum::routing::get(get_server_block)
|
||||||
|
.put(update_server_block)
|
||||||
|
.delete(delete_server_block),
|
||||||
|
)
|
||||||
|
}
|
||||||
136
apps/nxmesh-master/src/routes/api/proxy/ssl_certificates.rs
Normal file
136
apps/nxmesh-master/src/routes/api/proxy/ssl_certificates.rs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::ssl_certificate::{
|
||||||
|
CreateSslCertificateParams, SslCertificateService, UpdateSslCertificateParams,
|
||||||
|
};
|
||||||
|
use crate::service::proxy::types::SslCertificateConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct SslCertificateResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub cert_path: String,
|
||||||
|
pub key_path: String,
|
||||||
|
pub expiry_date: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SslCertificateConfig> for SslCertificateResponse {
|
||||||
|
fn from(c: SslCertificateConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
name: c.name,
|
||||||
|
cert_path: c.cert_path,
|
||||||
|
key_path: c.key_path,
|
||||||
|
expiry_date: c.expiry_date.to_rfc3339(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateSslCertificateRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub cert_path: String,
|
||||||
|
pub key_path: String,
|
||||||
|
pub expiry_date: chrono::DateTime<chrono::Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateSslCertificateRequest> for CreateSslCertificateParams {
|
||||||
|
fn from(r: CreateSslCertificateRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
name: r.name,
|
||||||
|
cert_path: r.cert_path,
|
||||||
|
key_path: r.key_path,
|
||||||
|
expiry_date: r.expiry_date,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateSslCertificateRequest {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub cert_path: Option<String>,
|
||||||
|
pub key_path: Option<String>,
|
||||||
|
pub expiry_date: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateSslCertificateRequest> for UpdateSslCertificateParams {
|
||||||
|
fn from(r: UpdateSslCertificateRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
name: r.name,
|
||||||
|
cert_path: r.cert_path,
|
||||||
|
key_path: r.key_path,
|
||||||
|
expiry_date: r.expiry_date,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_ssl_certificates(
|
||||||
|
State(svc): State<Arc<dyn SslCertificateService>>,
|
||||||
|
) -> Result<Json<Vec<SslCertificateResponse>>, AppError> {
|
||||||
|
let certs = svc.list().await?;
|
||||||
|
Ok(Json(certs.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_ssl_certificate(
|
||||||
|
State(svc): State<Arc<dyn SslCertificateService>>,
|
||||||
|
Json(body): Json<CreateSslCertificateRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let cert = svc.create(body.into()).await?;
|
||||||
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(SslCertificateResponse::from(cert)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_ssl_certificate(
|
||||||
|
State(svc): State<Arc<dyn SslCertificateService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<SslCertificateResponse>, AppError> {
|
||||||
|
let cert = svc.get(id).await?;
|
||||||
|
Ok(Json(cert.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_ssl_certificate(
|
||||||
|
State(svc): State<Arc<dyn SslCertificateService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateSslCertificateRequest>,
|
||||||
|
) -> Result<Json<SslCertificateResponse>, AppError> {
|
||||||
|
let cert = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(cert.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_ssl_certificate(
|
||||||
|
State(svc): State<Arc<dyn SslCertificateService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/ssl-certificates",
|
||||||
|
axum::routing::get(list_ssl_certificates).post(create_ssl_certificate),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/ssl-certificates/{id}",
|
||||||
|
axum::routing::get(get_ssl_certificate)
|
||||||
|
.put(update_ssl_certificate)
|
||||||
|
.delete(delete_ssl_certificate),
|
||||||
|
)
|
||||||
|
}
|
||||||
178
apps/nxmesh-master/src/routes/api/proxy/test_builder.rs
Normal file
178
apps/nxmesh-master/src/routes/api/proxy/test_builder.rs
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum_test::TestServer;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiState, LocalApiState};
|
||||||
|
use crate::service::proxy::*;
|
||||||
|
use crate::service::proxy::{
|
||||||
|
access_rule::MockAccessRuleService, cache_zone::MockCacheZoneService,
|
||||||
|
config_inheritance::MockConfigInheritanceService, limit_rule::MockLimitRuleService,
|
||||||
|
limit_zone::MockLimitZoneService, location_block::MockLocationBlockService,
|
||||||
|
log_setting::MockLogSettingService, proxy_setting::MockProxySettingService,
|
||||||
|
rewrite_rule::MockRewriteRuleService, server_block::MockServerBlockService,
|
||||||
|
ssl_certificate::MockSslCertificateService, upstream::MockUpstreamService,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct TestProxyApiBuilder {
|
||||||
|
proxy_service: Option<MockProxyServiceTrait>,
|
||||||
|
server_block_service: Option<MockServerBlockService>,
|
||||||
|
upstream_service: Option<MockUpstreamService>,
|
||||||
|
location_block_service: Option<MockLocationBlockService>,
|
||||||
|
access_rule_service: Option<MockAccessRuleService>,
|
||||||
|
cache_zone_service: Option<MockCacheZoneService>,
|
||||||
|
limit_rule_service: Option<MockLimitRuleService>,
|
||||||
|
limit_zone_service: Option<MockLimitZoneService>,
|
||||||
|
log_setting_service: Option<MockLogSettingService>,
|
||||||
|
proxy_setting_service: Option<MockProxySettingService>,
|
||||||
|
rewrite_rule_service: Option<MockRewriteRuleService>,
|
||||||
|
ssl_certificate_service: Option<MockSslCertificateService>,
|
||||||
|
config_inheritance_service: Option<MockConfigInheritanceService>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestProxyApiBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
proxy_service: None,
|
||||||
|
server_block_service: None,
|
||||||
|
upstream_service: None,
|
||||||
|
location_block_service: None,
|
||||||
|
access_rule_service: None,
|
||||||
|
cache_zone_service: None,
|
||||||
|
limit_rule_service: None,
|
||||||
|
limit_zone_service: None,
|
||||||
|
log_setting_service: None,
|
||||||
|
proxy_setting_service: None,
|
||||||
|
rewrite_rule_service: None,
|
||||||
|
ssl_certificate_service: None,
|
||||||
|
config_inheritance_service: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_proxy(mut self, mock: MockProxyServiceTrait) -> Self {
|
||||||
|
self.proxy_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_server_block(mut self, mock: MockServerBlockService) -> Self {
|
||||||
|
self.server_block_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_upstream(mut self, mock: MockUpstreamService) -> Self {
|
||||||
|
self.upstream_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_location_block(mut self, mock: MockLocationBlockService) -> Self {
|
||||||
|
self.location_block_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_access_rule(mut self, mock: MockAccessRuleService) -> Self {
|
||||||
|
self.access_rule_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_cache_zone(mut self, mock: MockCacheZoneService) -> Self {
|
||||||
|
self.cache_zone_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_limit_rule(mut self, mock: MockLimitRuleService) -> Self {
|
||||||
|
self.limit_rule_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_limit_zone(mut self, mock: MockLimitZoneService) -> Self {
|
||||||
|
self.limit_zone_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_log_setting(mut self, mock: MockLogSettingService) -> Self {
|
||||||
|
self.log_setting_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_proxy_setting(mut self, mock: MockProxySettingService) -> Self {
|
||||||
|
self.proxy_setting_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_rewrite_rule(mut self, mock: MockRewriteRuleService) -> Self {
|
||||||
|
self.rewrite_rule_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_ssl_certificate(mut self, mock: MockSslCertificateService) -> Self {
|
||||||
|
self.ssl_certificate_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_config_inheritance(mut self, mock: MockConfigInheritanceService) -> Self {
|
||||||
|
self.config_inheritance_service = Some(mock);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn build(self) -> TestServer {
|
||||||
|
let state = ApiState {
|
||||||
|
proxy_service: Arc::new(
|
||||||
|
self.proxy_service
|
||||||
|
.unwrap_or_else(MockProxyServiceTrait::new),
|
||||||
|
),
|
||||||
|
server_block_service: Arc::new(
|
||||||
|
self.server_block_service
|
||||||
|
.unwrap_or_else(MockServerBlockService::new),
|
||||||
|
),
|
||||||
|
upstream_service: Arc::new(
|
||||||
|
self.upstream_service
|
||||||
|
.unwrap_or_else(MockUpstreamService::new),
|
||||||
|
),
|
||||||
|
location_block_service: Arc::new(
|
||||||
|
self.location_block_service
|
||||||
|
.unwrap_or_else(MockLocationBlockService::new),
|
||||||
|
),
|
||||||
|
access_rule_service: Arc::new(
|
||||||
|
self.access_rule_service
|
||||||
|
.unwrap_or_else(MockAccessRuleService::new),
|
||||||
|
),
|
||||||
|
cache_zone_service: Arc::new(
|
||||||
|
self.cache_zone_service
|
||||||
|
.unwrap_or_else(MockCacheZoneService::new),
|
||||||
|
),
|
||||||
|
limit_rule_service: Arc::new(
|
||||||
|
self.limit_rule_service
|
||||||
|
.unwrap_or_else(MockLimitRuleService::new),
|
||||||
|
),
|
||||||
|
limit_zone_service: Arc::new(
|
||||||
|
self.limit_zone_service
|
||||||
|
.unwrap_or_else(MockLimitZoneService::new),
|
||||||
|
),
|
||||||
|
log_setting_service: Arc::new(
|
||||||
|
self.log_setting_service
|
||||||
|
.unwrap_or_else(MockLogSettingService::new),
|
||||||
|
),
|
||||||
|
proxy_setting_service: Arc::new(
|
||||||
|
self.proxy_setting_service
|
||||||
|
.unwrap_or_else(MockProxySettingService::new),
|
||||||
|
),
|
||||||
|
rewrite_rule_service: Arc::new(
|
||||||
|
self.rewrite_rule_service
|
||||||
|
.unwrap_or_else(MockRewriteRuleService::new),
|
||||||
|
),
|
||||||
|
ssl_certificate_service: Arc::new(
|
||||||
|
self.ssl_certificate_service
|
||||||
|
.unwrap_or_else(MockSslCertificateService::new),
|
||||||
|
),
|
||||||
|
config_inheritance_service: Arc::new(
|
||||||
|
self.config_inheritance_service
|
||||||
|
.unwrap_or_else(MockConfigInheritanceService::new),
|
||||||
|
),
|
||||||
|
// Keep agent_service for ApiState completeness; not used by proxy routes
|
||||||
|
agent_service: Arc::new(crate::service::agent::MockAgentService::new()),
|
||||||
|
};
|
||||||
|
let app = super::get_router()
|
||||||
|
.await
|
||||||
|
.with_state(LocalApiState(Arc::new(state)));
|
||||||
|
TestServer::new(app)
|
||||||
|
}
|
||||||
|
}
|
||||||
144
apps/nxmesh-master/src/routes/api/proxy/upstreams.rs
Normal file
144
apps/nxmesh-master/src/routes/api/proxy/upstreams.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::api::{ApiRouter, AppError};
|
||||||
|
use crate::service::proxy::types::UpstreamConfig;
|
||||||
|
use crate::service::proxy::upstream::{
|
||||||
|
CreateUpstreamParams, UpdateUpstreamParams, UpstreamService,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct UpstreamResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub target_host: String,
|
||||||
|
pub target_port: i32,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpstreamConfig> for UpstreamResponse {
|
||||||
|
fn from(c: UpstreamConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
id: c.id,
|
||||||
|
name: c.name,
|
||||||
|
target_host: c.target_host,
|
||||||
|
target_port: c.target_port,
|
||||||
|
metadata: c.metadata,
|
||||||
|
override_of_id: c.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct CreateUpstreamRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub target_host: String,
|
||||||
|
pub target_port: i32,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreateUpstreamRequest> for CreateUpstreamParams {
|
||||||
|
fn from(r: CreateUpstreamRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
config_id: Uuid::nil(),
|
||||||
|
name: r.name,
|
||||||
|
target_host: r.target_host,
|
||||||
|
target_port: r.target_port,
|
||||||
|
metadata: r.metadata,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct UpdateUpstreamRequest {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub target_host: Option<String>,
|
||||||
|
pub target_port: Option<i32>,
|
||||||
|
pub metadata: Option<Option<serde_json::Value>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateUpstreamRequest> for UpdateUpstreamParams {
|
||||||
|
fn from(r: UpdateUpstreamRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
name: r.name,
|
||||||
|
target_host: r.target_host,
|
||||||
|
target_port: r.target_port,
|
||||||
|
metadata: r.metadata,
|
||||||
|
override_of_id: r.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_upstreams(
|
||||||
|
State(svc): State<Arc<dyn UpstreamService>>,
|
||||||
|
Path(config_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<UpstreamResponse>>, AppError> {
|
||||||
|
let upstreams = svc.list_by_config(config_id).await?;
|
||||||
|
Ok(Json(upstreams.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_upstream(
|
||||||
|
State(svc): State<Arc<dyn UpstreamService>>,
|
||||||
|
Path(config_id): Path<Uuid>,
|
||||||
|
Json(body): Json<CreateUpstreamRequest>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let mut params = CreateUpstreamParams::from(body);
|
||||||
|
params.config_id = config_id;
|
||||||
|
let upstream = svc.create(params).await?;
|
||||||
|
Ok((StatusCode::CREATED, Json(UpstreamResponse::from(upstream))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_upstream(
|
||||||
|
State(svc): State<Arc<dyn UpstreamService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<UpstreamResponse>, AppError> {
|
||||||
|
let upstream = svc.get(id).await?;
|
||||||
|
Ok(Json(upstream.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_upstream(
|
||||||
|
State(svc): State<Arc<dyn UpstreamService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Json(body): Json<UpdateUpstreamRequest>,
|
||||||
|
) -> Result<Json<UpstreamResponse>, AppError> {
|
||||||
|
let upstream = svc.update(id, body.into()).await?;
|
||||||
|
Ok(Json(upstream.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_upstream(
|
||||||
|
State(svc): State<Arc<dyn UpstreamService>>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let deleted = svc.delete(id).await?;
|
||||||
|
if deleted {
|
||||||
|
Ok((StatusCode::NO_CONTENT,))
|
||||||
|
} else {
|
||||||
|
Err(AppError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn routes() -> ApiRouter {
|
||||||
|
ApiRouter::new()
|
||||||
|
.route(
|
||||||
|
"/configs/{config_id}/upstreams",
|
||||||
|
axum::routing::get(list_upstreams).post(create_upstream),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/upstreams/{id}",
|
||||||
|
axum::routing::get(get_upstream)
|
||||||
|
.put(update_upstream)
|
||||||
|
.delete(delete_upstream),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,21 +1,46 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
|
|
||||||
|
pub mod api;
|
||||||
mod frontend;
|
mod frontend;
|
||||||
|
|
||||||
pub async fn get_root_router() -> Router {
|
pub async fn get_root_router(api_state: impl Into<Arc<api::ApiState>>) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.merge(frontend::get_router().await)
|
.merge(frontend::get_router().await)
|
||||||
|
.nest("/api", api::get_router(api_state.into()).await)
|
||||||
.fallback(frontend::get_fallback_handler().await)
|
.fallback(frontend::get_fallback_handler().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::service::agent::MockAgentService;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use axum_test::TestServer;
|
use axum_test::TestServer;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_should_return_index_html_for_root_path() {
|
async fn test_should_return_index_html_for_root_path() {
|
||||||
let router = get_root_router().await;
|
use crate::service::proxy::*;
|
||||||
|
let state = Arc::new(api::ApiState {
|
||||||
|
agent_service: Arc::new(MockAgentService::new()),
|
||||||
|
proxy_service: Arc::new(MockProxyServiceTrait::new()),
|
||||||
|
server_block_service: Arc::new(server_block::MockServerBlockService::new()),
|
||||||
|
upstream_service: Arc::new(upstream::MockUpstreamService::new()),
|
||||||
|
location_block_service: Arc::new(location_block::MockLocationBlockService::new()),
|
||||||
|
access_rule_service: Arc::new(access_rule::MockAccessRuleService::new()),
|
||||||
|
cache_zone_service: Arc::new(cache_zone::MockCacheZoneService::new()),
|
||||||
|
limit_rule_service: Arc::new(limit_rule::MockLimitRuleService::new()),
|
||||||
|
limit_zone_service: Arc::new(limit_zone::MockLimitZoneService::new()),
|
||||||
|
log_setting_service: Arc::new(log_setting::MockLogSettingService::new()),
|
||||||
|
proxy_setting_service: Arc::new(proxy_setting::MockProxySettingService::new()),
|
||||||
|
rewrite_rule_service: Arc::new(rewrite_rule::MockRewriteRuleService::new()),
|
||||||
|
ssl_certificate_service: Arc::new(ssl_certificate::MockSslCertificateService::new()),
|
||||||
|
config_inheritance_service: Arc::new(
|
||||||
|
config_inheritance::MockConfigInheritanceService::new(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
let router = get_root_router(state).await;
|
||||||
let server = TestServer::new(router);
|
let server = TestServer::new(router);
|
||||||
let response = server.get("/").await;
|
let response = server.get("/").await;
|
||||||
assert_eq!(response.status_code(), 200);
|
assert_eq!(response.status_code(), 200);
|
||||||
@@ -23,7 +48,26 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_should_return_index_html_for_nonexistent_path() {
|
async fn test_should_return_index_html_for_nonexistent_path() {
|
||||||
let router = get_root_router().await;
|
use crate::service::proxy::*;
|
||||||
|
let state = Arc::new(api::ApiState {
|
||||||
|
agent_service: Arc::new(MockAgentService::new()),
|
||||||
|
proxy_service: Arc::new(MockProxyServiceTrait::new()),
|
||||||
|
server_block_service: Arc::new(server_block::MockServerBlockService::new()),
|
||||||
|
upstream_service: Arc::new(upstream::MockUpstreamService::new()),
|
||||||
|
location_block_service: Arc::new(location_block::MockLocationBlockService::new()),
|
||||||
|
access_rule_service: Arc::new(access_rule::MockAccessRuleService::new()),
|
||||||
|
cache_zone_service: Arc::new(cache_zone::MockCacheZoneService::new()),
|
||||||
|
limit_rule_service: Arc::new(limit_rule::MockLimitRuleService::new()),
|
||||||
|
limit_zone_service: Arc::new(limit_zone::MockLimitZoneService::new()),
|
||||||
|
log_setting_service: Arc::new(log_setting::MockLogSettingService::new()),
|
||||||
|
proxy_setting_service: Arc::new(proxy_setting::MockProxySettingService::new()),
|
||||||
|
rewrite_rule_service: Arc::new(rewrite_rule::MockRewriteRuleService::new()),
|
||||||
|
ssl_certificate_service: Arc::new(ssl_certificate::MockSslCertificateService::new()),
|
||||||
|
config_inheritance_service: Arc::new(
|
||||||
|
config_inheritance::MockConfigInheritanceService::new(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
let router = get_root_router(state).await;
|
||||||
let server = TestServer::new(router);
|
let server = TestServer::new(router);
|
||||||
let fallback_response = server.get("/nonexistent").await;
|
let fallback_response = server.get("/nonexistent").await;
|
||||||
assert_eq!(fallback_response.status_code(), 200);
|
assert_eq!(fallback_response.status_code(), 200);
|
||||||
|
|||||||
@@ -1,26 +1,120 @@
|
|||||||
use nxmesh_proto::{AgentMessage, MasterMessage, agent_service_server::AgentService};
|
use chrono::Utc;
|
||||||
|
use nxmesh_proto::{
|
||||||
|
AgentMessage, MasterMessage, agent_service_server::AgentService as GrpcAgentService,
|
||||||
|
};
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
use tracing::{error, info};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub mod repo;
|
mod repo;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
pub use types::{AgentRecord, CreateAgentRecord, State, UpdateAgentRecord};
|
||||||
|
|
||||||
|
use crate::service::error::RepoError;
|
||||||
|
use repo::{AgentRepo, AgentRepoImpl};
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait AgentService: Send + Sync + 'static {
|
||||||
|
async fn list(&self) -> Result<Vec<AgentRecord>, RepoError>;
|
||||||
|
async fn get(&self, id: Uuid) -> Result<Option<AgentRecord>, RepoError>;
|
||||||
|
async fn create(&self, rec: &CreateAgentRecord) -> Result<AgentRecord, RepoError>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
rec: &UpdateAgentRecord,
|
||||||
|
) -> Result<Option<AgentRecord>, RepoError>;
|
||||||
|
async fn delete(&self, id: Uuid) -> Result<bool, RepoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AgentServiceImpl {
|
||||||
|
repo: Box<dyn AgentRepo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AgentServiceImpl {
|
||||||
|
pub fn new(db: DatabaseConnection) -> Self {
|
||||||
|
Self {
|
||||||
|
repo: Box::new(AgentRepoImpl::new(db)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl AgentService for AgentServiceImpl {
|
||||||
|
async fn list(&self) -> Result<Vec<AgentRecord>, RepoError> {
|
||||||
|
self.repo.list().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get(&self, id: Uuid) -> Result<Option<AgentRecord>, RepoError> {
|
||||||
|
self.repo.get(id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create(&self, rec: &CreateAgentRecord) -> Result<AgentRecord, RepoError> {
|
||||||
|
self.repo.create(rec).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
rec: &UpdateAgentRecord,
|
||||||
|
) -> Result<Option<AgentRecord>, RepoError> {
|
||||||
|
self.repo.update(id, rec).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: Uuid) -> Result<bool, RepoError> {
|
||||||
|
self.repo.delete(id).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct AgentServerService {}
|
pub struct AgentServerService {}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl AgentService for AgentServerService {
|
impl GrpcAgentService for AgentServerService {
|
||||||
#[doc = " Server streaming response type for the Stream method."]
|
type StreamStream =
|
||||||
type StreamStream = tonic::codec::Streaming<MasterMessage>;
|
tokio_stream::wrappers::ReceiverStream<std::result::Result<MasterMessage, tonic::Status>>;
|
||||||
|
|
||||||
#[doc = " Stream establishes a persistent connection for real-time communication"]
|
|
||||||
#[allow(
|
|
||||||
mismatched_lifetime_syntaxes,
|
|
||||||
clippy::type_complexity,
|
|
||||||
clippy::type_repetition_in_bounds
|
|
||||||
)]
|
|
||||||
async fn stream(
|
async fn stream(
|
||||||
&self,
|
&self,
|
||||||
request: tonic::Request<tonic::Streaming<AgentMessage>>,
|
request: tonic::Request<tonic::Streaming<AgentMessage>>,
|
||||||
) -> Result<tonic::Response<Self::StreamStream>, tonic::Status> {
|
) -> Result<tonic::Response<Self::StreamStream>, tonic::Status> {
|
||||||
todo!()
|
let mut inbound = request.into_inner();
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel::<std::result::Result<MasterMessage, tonic::Status>>(32);
|
||||||
|
let outbound = ReceiverStream::new(rx);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
match inbound.message().await {
|
||||||
|
Ok(Some(msg)) => {
|
||||||
|
info!("Received AgentMessage: {:?}", msg);
|
||||||
|
|
||||||
|
let ack = MasterMessage {
|
||||||
|
timestamp: Utc::now().timestamp_millis(),
|
||||||
|
message_id: Uuid::new_v4().to_string(),
|
||||||
|
payload: None,
|
||||||
|
};
|
||||||
|
if let Err(e) = tx.send(Ok(ack)).await {
|
||||||
|
error!("Failed to send MasterMessage ack: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
info!("Agent closed the outbound stream");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error receiving AgentMessage: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(tonic::Response::new(outbound))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connection_test(
|
async fn connection_test(
|
||||||
|
|||||||
146
apps/nxmesh-master/src/service/agent/repo.rs
Normal file
146
apps/nxmesh-master/src/service/agent/repo.rs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::types::{AgentRecord, CreateAgentRecord, State, UpdateAgentRecord};
|
||||||
|
use crate::{
|
||||||
|
db::entities::agents::{ActiveModel as AgentActiveModel, Entity as Agent},
|
||||||
|
service::error::RepoError,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub(crate) trait AgentRepo: Send + Sync + 'static {
|
||||||
|
async fn list(&self) -> Result<Vec<AgentRecord>, RepoError>;
|
||||||
|
async fn get(&self, id: Uuid) -> Result<Option<AgentRecord>, RepoError>;
|
||||||
|
async fn create(&self, rec: &CreateAgentRecord) -> Result<AgentRecord, RepoError>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
rec: &UpdateAgentRecord,
|
||||||
|
) -> Result<Option<AgentRecord>, RepoError>;
|
||||||
|
async fn delete(&self, id: Uuid) -> Result<bool, RepoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct AgentRepoImpl {
|
||||||
|
db: DatabaseConnection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AgentRepoImpl {
|
||||||
|
pub fn new(db: DatabaseConnection) -> Self {
|
||||||
|
Self { db }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl AgentRepo for AgentRepoImpl {
|
||||||
|
async fn list(&self) -> Result<Vec<AgentRecord>, RepoError> {
|
||||||
|
let agents = Agent::find().all(&self.db).await?;
|
||||||
|
Ok(agents
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| AgentRecord {
|
||||||
|
id: m.id,
|
||||||
|
name: m.name,
|
||||||
|
ip_address: m.ip_address,
|
||||||
|
state: m.state.into(),
|
||||||
|
deployment_mode: m.deployment_mode,
|
||||||
|
last_seen_at: m.last_seen_at.map(|dt| dt.to_string()),
|
||||||
|
labels: m.labels,
|
||||||
|
created_at: m.created_at.to_string(),
|
||||||
|
updated_at: m.updated_at.to_string(),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get(&self, id: Uuid) -> Result<Option<AgentRecord>, RepoError> {
|
||||||
|
let agent = Agent::find_by_id(id).one(&self.db).await?;
|
||||||
|
Ok(agent.map(|m| AgentRecord {
|
||||||
|
id: m.id,
|
||||||
|
name: m.name,
|
||||||
|
ip_address: m.ip_address,
|
||||||
|
state: m.state.into(),
|
||||||
|
deployment_mode: m.deployment_mode,
|
||||||
|
last_seen_at: m.last_seen_at.map(|dt| dt.to_string()),
|
||||||
|
labels: m.labels,
|
||||||
|
created_at: m.created_at.to_string(),
|
||||||
|
updated_at: m.updated_at.to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create(&self, rec: &CreateAgentRecord) -> Result<AgentRecord, RepoError> {
|
||||||
|
let new_agent = AgentActiveModel {
|
||||||
|
id: Set(Uuid::new_v4()),
|
||||||
|
name: Set(rec.name.clone()),
|
||||||
|
ip_address: Set(rec.ip_address.clone()),
|
||||||
|
state: Set(State::Active.into()),
|
||||||
|
deployment_mode: Set(None),
|
||||||
|
last_seen_at: Set(None),
|
||||||
|
labels: Set(None),
|
||||||
|
created_at: Set(chrono::Utc::now().into()),
|
||||||
|
updated_at: Set(chrono::Utc::now().into()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let agent = new_agent.insert(&self.db).await?;
|
||||||
|
|
||||||
|
Ok(AgentRecord {
|
||||||
|
id: agent.id,
|
||||||
|
name: agent.name,
|
||||||
|
ip_address: agent.ip_address,
|
||||||
|
state: agent.state.into(),
|
||||||
|
deployment_mode: agent.deployment_mode,
|
||||||
|
last_seen_at: agent.last_seen_at.map(|dt| dt.to_string()),
|
||||||
|
labels: agent.labels,
|
||||||
|
created_at: agent.created_at.to_string(),
|
||||||
|
updated_at: agent.updated_at.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
rec: &UpdateAgentRecord,
|
||||||
|
) -> Result<Option<AgentRecord>, RepoError> {
|
||||||
|
let existing = match Agent::find_by_id(id).one(&self.db).await? {
|
||||||
|
Some(agent) => agent,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut agent: AgentActiveModel = AgentActiveModel::from(existing);
|
||||||
|
|
||||||
|
if let Some(name) = &rec.name {
|
||||||
|
agent.name = Set(name.clone());
|
||||||
|
}
|
||||||
|
if let Some(ip_address) = &rec.ip_address {
|
||||||
|
agent.ip_address = Set(Some(ip_address.clone()));
|
||||||
|
}
|
||||||
|
if let Some(state) = &rec.state {
|
||||||
|
agent.state = Set(String::from(*state));
|
||||||
|
}
|
||||||
|
if let Some(deployment_mode) = &rec.deployment_mode {
|
||||||
|
agent.deployment_mode = Set(Some(deployment_mode.clone()));
|
||||||
|
}
|
||||||
|
if let Some(labels) = &rec.labels {
|
||||||
|
agent.labels = Set(Some(labels.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
agent.updated_at = Set(chrono::Utc::now().into());
|
||||||
|
|
||||||
|
let updated = agent.update(&self.db).await?;
|
||||||
|
|
||||||
|
Ok(Some(AgentRecord {
|
||||||
|
id: updated.id,
|
||||||
|
name: updated.name,
|
||||||
|
ip_address: updated.ip_address,
|
||||||
|
state: updated.state.into(),
|
||||||
|
deployment_mode: updated.deployment_mode,
|
||||||
|
last_seen_at: updated.last_seen_at.map(|dt| dt.to_string()),
|
||||||
|
labels: updated.labels,
|
||||||
|
created_at: updated.created_at.to_string(),
|
||||||
|
updated_at: updated.updated_at.to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: Uuid) -> Result<bool, RepoError> {
|
||||||
|
let result = Agent::delete_by_id(id).exec(&self.db).await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
60
apps/nxmesh-master/src/service/agent/types.rs
Normal file
60
apps/nxmesh-master/src/service/agent/types.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum State {
|
||||||
|
Active,
|
||||||
|
Inactive,
|
||||||
|
Unreachable,
|
||||||
|
Unknown,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<State> for String {
|
||||||
|
fn from(state: State) -> Self {
|
||||||
|
match state {
|
||||||
|
State::Active => "active".to_string(),
|
||||||
|
State::Inactive => "inactive".to_string(),
|
||||||
|
State::Unreachable => "unreachable".to_string(),
|
||||||
|
State::Unknown => "unknown".to_string(),
|
||||||
|
State::Disabled => "disabled".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for State {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
match s.as_str() {
|
||||||
|
"active" => State::Active,
|
||||||
|
"inactive" => State::Inactive,
|
||||||
|
"unreachable" => State::Unreachable,
|
||||||
|
"unknown" => State::Unknown,
|
||||||
|
"disabled" => State::Disabled,
|
||||||
|
_ => State::Inactive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AgentRecord {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub ip_address: Option<String>,
|
||||||
|
pub state: State,
|
||||||
|
pub deployment_mode: Option<String>,
|
||||||
|
pub last_seen_at: Option<String>,
|
||||||
|
pub labels: Option<serde_json::Value>,
|
||||||
|
pub created_at: String,
|
||||||
|
pub updated_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CreateAgentRecord {
|
||||||
|
pub name: String,
|
||||||
|
pub ip_address: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateAgentRecord {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub ip_address: Option<String>,
|
||||||
|
pub state: Option<State>,
|
||||||
|
pub deployment_mode: Option<String>,
|
||||||
|
pub labels: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
@@ -131,7 +131,7 @@ impl CertificateService for CertificateServiceImpl {
|
|||||||
.collect::<Vec<SanType>>(),
|
.collect::<Vec<SanType>>(),
|
||||||
san_dns
|
san_dns
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|dns| SanType::DnsName(dns))
|
.map(SanType::DnsName)
|
||||||
.collect::<Vec<SanType>>(),
|
.collect::<Vec<SanType>>(),
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
|
|||||||
13
apps/nxmesh-master/src/service/error.rs
Normal file
13
apps/nxmesh-master/src/service/error.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
|
||||||
|
pub enum RepoError {
|
||||||
|
#[error("internal error: {0}")]
|
||||||
|
InternalError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sea_orm::DbErr> for RepoError {
|
||||||
|
fn from(err: sea_orm::DbErr) -> Self {
|
||||||
|
match err {
|
||||||
|
other => RepoError::InternalError(other.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ use crate::{connector::agent::AgentConnectorTrait, service::certificate::Certifi
|
|||||||
|
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
pub mod certificate;
|
pub mod certificate;
|
||||||
|
pub mod error;
|
||||||
|
pub mod proxy;
|
||||||
|
|
||||||
pub async fn start_master_server(
|
pub async fn start_master_server(
|
||||||
settings: crate::config::settings::Settings,
|
settings: crate::config::settings::Settings,
|
||||||
@@ -50,7 +52,60 @@ pub async fn start_master_server(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let axum_router = crate::routes::get_root_router().await;
|
let api_state = crate::routes::api::ApiState {
|
||||||
|
agent_service: Arc::new(crate::service::agent::AgentServiceImpl::new(
|
||||||
|
db_connection.clone(),
|
||||||
|
)),
|
||||||
|
proxy_service: Arc::new(crate::service::proxy::service::ProxyServiceImpl::new(
|
||||||
|
db_connection.clone(),
|
||||||
|
)),
|
||||||
|
server_block_service: Arc::new(
|
||||||
|
crate::service::proxy::server_block::ServerBlockServiceImpl::new(db_connection.clone()),
|
||||||
|
),
|
||||||
|
upstream_service: Arc::new(crate::service::proxy::upstream::UpstreamServiceImpl::new(
|
||||||
|
db_connection.clone(),
|
||||||
|
)),
|
||||||
|
location_block_service: Arc::new(
|
||||||
|
crate::service::proxy::location_block::LocationBlockServiceImpl::new(
|
||||||
|
db_connection.clone(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
access_rule_service: Arc::new(
|
||||||
|
crate::service::proxy::access_rule::AccessRuleServiceImpl::new(db_connection.clone()),
|
||||||
|
),
|
||||||
|
cache_zone_service: Arc::new(
|
||||||
|
crate::service::proxy::cache_zone::CacheZoneServiceImpl::new(db_connection.clone()),
|
||||||
|
),
|
||||||
|
limit_rule_service: Arc::new(
|
||||||
|
crate::service::proxy::limit_rule::LimitRuleServiceImpl::new(db_connection.clone()),
|
||||||
|
),
|
||||||
|
limit_zone_service: Arc::new(
|
||||||
|
crate::service::proxy::limit_zone::LimitZoneServiceImpl::new(db_connection.clone()),
|
||||||
|
),
|
||||||
|
log_setting_service: Arc::new(
|
||||||
|
crate::service::proxy::log_setting::LogSettingServiceImpl::new(db_connection.clone()),
|
||||||
|
),
|
||||||
|
proxy_setting_service: Arc::new(
|
||||||
|
crate::service::proxy::proxy_setting::ProxySettingServiceImpl::new(
|
||||||
|
db_connection.clone(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
rewrite_rule_service: Arc::new(
|
||||||
|
crate::service::proxy::rewrite_rule::RewriteRuleServiceImpl::new(db_connection.clone()),
|
||||||
|
),
|
||||||
|
ssl_certificate_service: Arc::new(
|
||||||
|
crate::service::proxy::ssl_certificate::SslCertificateServiceImpl::new(
|
||||||
|
db_connection.clone(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
config_inheritance_service: Arc::new(
|
||||||
|
crate::service::proxy::config_inheritance::ConfigInheritanceServiceImpl::new(
|
||||||
|
db_connection.clone(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let axum_router = crate::routes::get_root_router(Arc::new(api_state)).await;
|
||||||
|
|
||||||
// Start the HTTP server
|
// Start the HTTP server
|
||||||
let addr = format!("{}:{}", settings.server.bind_address, settings.server.port)
|
let addr = format!("{}:{}", settings.server.bind_address, settings.server.port)
|
||||||
|
|||||||
156
apps/nxmesh-master/src/service/proxy/access_rule/mod.rs
Normal file
156
apps/nxmesh-master/src/service/proxy/access_rule/mod.rs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
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<Uuid>,
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub r#type: String,
|
||||||
|
pub ip_cidr: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateAccessRuleParams {
|
||||||
|
pub server_id: Option<Option<Uuid>>,
|
||||||
|
pub location_id: Option<Option<Uuid>>,
|
||||||
|
pub r#type: Option<String>,
|
||||||
|
pub ip_cidr: Option<String>,
|
||||||
|
pub description: Option<Option<String>>,
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait AccessRuleService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<AccessRuleConfig>;
|
||||||
|
async fn list_by_server(&self, server_id: Uuid) -> ProxyServiceResult<Vec<AccessRuleConfig>>;
|
||||||
|
async fn list_by_location(
|
||||||
|
&self,
|
||||||
|
location_id: Uuid,
|
||||||
|
) -> ProxyServiceResult<Vec<AccessRuleConfig>>;
|
||||||
|
async fn create(&self, params: CreateAccessRuleParams) -> ProxyServiceResult<AccessRuleConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateAccessRuleParams,
|
||||||
|
) -> ProxyServiceResult<AccessRuleConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<AccessRuleConfig> {
|
||||||
|
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<Vec<AccessRuleConfig>> {
|
||||||
|
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<Vec<AccessRuleConfig>> {
|
||||||
|
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<AccessRuleConfig> {
|
||||||
|
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<AccessRuleConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::access_rule::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
116
apps/nxmesh-master/src/service/proxy/cache_zone/mod.rs
Normal file
116
apps/nxmesh-master/src/service/proxy/cache_zone/mod.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
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<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateCacheZoneParams {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub path: Option<String>,
|
||||||
|
pub size_limit: Option<String>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait CacheZoneService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<CacheZoneConfig>;
|
||||||
|
async fn list(&self) -> ProxyServiceResult<Vec<CacheZoneConfig>>;
|
||||||
|
async fn create(&self, params: CreateCacheZoneParams) -> ProxyServiceResult<CacheZoneConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateCacheZoneParams,
|
||||||
|
) -> ProxyServiceResult<CacheZoneConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<CacheZoneConfig> {
|
||||||
|
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<Vec<CacheZoneConfig>> {
|
||||||
|
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<CacheZoneConfig> {
|
||||||
|
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<CacheZoneConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::cache_zone::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
144
apps/nxmesh-master/src/service/proxy/config_inheritance/mod.rs
Normal file
144
apps/nxmesh-master/src/service/proxy/config_inheritance/mod.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
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<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConfigInheritanceRecord {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub child_config_id: Uuid,
|
||||||
|
pub parent_config_id: Uuid,
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
pub applied_at: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait ConfigInheritanceService: Send + Sync + 'static {
|
||||||
|
async fn add(
|
||||||
|
&self,
|
||||||
|
params: AddInheritanceParams,
|
||||||
|
) -> ProxyServiceResult<ConfigInheritanceRecord>;
|
||||||
|
async fn remove(
|
||||||
|
&self,
|
||||||
|
child_config_id: Uuid,
|
||||||
|
parent_config_id: Uuid,
|
||||||
|
) -> ProxyServiceResult<bool>;
|
||||||
|
async fn list_parents(
|
||||||
|
&self,
|
||||||
|
child_config_id: Uuid,
|
||||||
|
) -> ProxyServiceResult<Vec<ConfigInheritanceRecord>>;
|
||||||
|
async fn list_children(
|
||||||
|
&self,
|
||||||
|
parent_config_id: Uuid,
|
||||||
|
) -> ProxyServiceResult<Vec<ConfigInheritanceRecord>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ConfigInheritanceRecord> {
|
||||||
|
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<bool> {
|
||||||
|
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<Vec<ConfigInheritanceRecord>> {
|
||||||
|
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<Vec<ConfigInheritanceRecord>> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
130
apps/nxmesh-master/src/service/proxy/limit_rule/mod.rs
Normal file
130
apps/nxmesh-master/src/service/proxy/limit_rule/mod.rs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
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<i32>,
|
||||||
|
pub nodelay: Option<bool>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateLimitRuleParams {
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub zone_id: Option<Uuid>,
|
||||||
|
pub burst: Option<Option<i32>>,
|
||||||
|
pub nodelay: Option<Option<bool>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait LimitRuleService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<LimitRuleConfig>;
|
||||||
|
async fn list_by_location(&self, location_id: Uuid)
|
||||||
|
-> ProxyServiceResult<Vec<LimitRuleConfig>>;
|
||||||
|
async fn create(&self, params: CreateLimitRuleParams) -> ProxyServiceResult<LimitRuleConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateLimitRuleParams,
|
||||||
|
) -> ProxyServiceResult<LimitRuleConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<LimitRuleConfig> {
|
||||||
|
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<Vec<LimitRuleConfig>> {
|
||||||
|
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<LimitRuleConfig> {
|
||||||
|
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<LimitRuleConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::limit_rule::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
116
apps/nxmesh-master/src/service/proxy/limit_zone/mod.rs
Normal file
116
apps/nxmesh-master/src/service/proxy/limit_zone/mod.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
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<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateLimitZoneParams {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub key: Option<String>,
|
||||||
|
pub rate: Option<String>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait LimitZoneService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<LimitZoneConfig>;
|
||||||
|
async fn list(&self) -> ProxyServiceResult<Vec<LimitZoneConfig>>;
|
||||||
|
async fn create(&self, params: CreateLimitZoneParams) -> ProxyServiceResult<LimitZoneConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateLimitZoneParams,
|
||||||
|
) -> ProxyServiceResult<LimitZoneConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<LimitZoneConfig> {
|
||||||
|
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<Vec<LimitZoneConfig>> {
|
||||||
|
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<LimitZoneConfig> {
|
||||||
|
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<LimitZoneConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::limit_zone::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
206
apps/nxmesh-master/src/service/proxy/location_block/mod.rs
Normal file
206
apps/nxmesh-master/src/service/proxy/location_block/mod.rs
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
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<Uuid>,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateLocationBlockParams {
|
||||||
|
pub server_id: Option<Uuid>,
|
||||||
|
pub path_pattern: Option<String>,
|
||||||
|
pub proxy_pass_upstream_id: Option<Option<Uuid>>,
|
||||||
|
pub metadata: Option<Option<serde_json::Value>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait LocationBlockService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<LocationBlockConfig>;
|
||||||
|
async fn list_by_server(&self, server_id: Uuid)
|
||||||
|
-> ProxyServiceResult<Vec<LocationBlockConfig>>;
|
||||||
|
async fn create(
|
||||||
|
&self,
|
||||||
|
params: CreateLocationBlockParams,
|
||||||
|
) -> ProxyServiceResult<LocationBlockConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateLocationBlockParams,
|
||||||
|
) -> ProxyServiceResult<LocationBlockConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<LocationBlockConfig> {
|
||||||
|
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<LocationBlockConfig> {
|
||||||
|
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<Vec<LocationBlockConfig>> {
|
||||||
|
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<LocationBlockConfig> {
|
||||||
|
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<LocationBlockConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::location_block::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
125
apps/nxmesh-master/src/service/proxy/log_setting/mod.rs
Normal file
125
apps/nxmesh-master/src/service/proxy/log_setting/mod.rs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
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<String>,
|
||||||
|
pub error_log_path: Option<String>,
|
||||||
|
pub log_level: Option<String>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateLogSettingParams {
|
||||||
|
pub server_id: Option<Uuid>,
|
||||||
|
pub access_log_path: Option<Option<String>>,
|
||||||
|
pub error_log_path: Option<Option<String>>,
|
||||||
|
pub log_level: Option<Option<String>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait LogSettingService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<LogSettingConfig>;
|
||||||
|
async fn list_by_server(&self, server_id: Uuid) -> ProxyServiceResult<Vec<LogSettingConfig>>;
|
||||||
|
async fn create(&self, params: CreateLogSettingParams) -> ProxyServiceResult<LogSettingConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateLogSettingParams,
|
||||||
|
) -> ProxyServiceResult<LogSettingConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<LogSettingConfig> {
|
||||||
|
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<Vec<LogSettingConfig>> {
|
||||||
|
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<LogSettingConfig> {
|
||||||
|
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<LogSettingConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::log_setting::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
60
apps/nxmesh-master/src/service/proxy/mod.rs
Normal file
60
apps/nxmesh-master/src/service/proxy/mod.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
use crate::service::proxy::types::{
|
||||||
|
AgentConfigBinding, CreateProxyConfigParams, ProxyConfig, ProxyConfigSummary,
|
||||||
|
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;
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait ProxyServiceTrait: Send + Sync + 'static {
|
||||||
|
async fn get_proxy_config(&self, proxy_id: uuid::Uuid) -> ProxyServiceResult<ProxyConfig>;
|
||||||
|
|
||||||
|
// CRUD
|
||||||
|
async fn list_configs(&self) -> ProxyServiceResult<Vec<ProxyConfigSummary>>;
|
||||||
|
async fn create_config(
|
||||||
|
&self,
|
||||||
|
params: CreateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary>;
|
||||||
|
async fn update_config(
|
||||||
|
&self,
|
||||||
|
id: uuid::Uuid,
|
||||||
|
params: UpdateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary>;
|
||||||
|
async fn delete_config(&self, id: uuid::Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
|
||||||
|
// Render
|
||||||
|
async fn render_config(&self, proxy_id: uuid::Uuid) -> ProxyServiceResult<String>;
|
||||||
|
|
||||||
|
// Binding
|
||||||
|
async fn get_active_agent_config(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<Option<ProxyConfigSummary>>;
|
||||||
|
async fn bind_agent(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
config_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<AgentConfigBinding>;
|
||||||
|
async fn unbind_agent(&self, agent_id: uuid::Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ProxyConfigRenderer: Send + Sync + 'static {
|
||||||
|
fn render(&self, config: &ProxyConfig) -> String;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::service::proxy::types::AccessRuleConfig;
|
||||||
|
|
||||||
|
impl std::fmt::Display for AccessRuleConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{} {};", self.r#type, self.ip_cidr)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
use crate::service::proxy::types::CacheZoneConfig;
|
||||||
|
|
||||||
|
impl std::fmt::Display for CacheZoneConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"proxy_cache_path {} levels=1:2 keys_zone={}:{};",
|
||||||
|
self.path, self.name, self.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
use crate::service::proxy::types::LimitRuleConfig;
|
||||||
|
|
||||||
|
pub struct LimitRuleRender<'a> {
|
||||||
|
pub rule: &'a LimitRuleConfig,
|
||||||
|
pub zone_name: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for LimitRuleRender<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "limit_req zone={}", self.zone_name)?;
|
||||||
|
if let Some(burst) = self.rule.burst {
|
||||||
|
write!(f, " burst={}", burst)?;
|
||||||
|
}
|
||||||
|
if self.rule.nodelay.unwrap_or(false) {
|
||||||
|
write!(f, " nodelay")?;
|
||||||
|
}
|
||||||
|
write!(f, ";")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
use crate::service::proxy::types::LimitZoneConfig;
|
||||||
|
|
||||||
|
impl std::fmt::Display for LimitZoneConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"limit_req_zone {} zone={}:{};",
|
||||||
|
self.key, self.name, self.rate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
use crate::service::proxy::types::LocationBlockConfig;
|
||||||
|
|
||||||
|
pub struct LocationBlockRender<'a> {
|
||||||
|
pub block: &'a LocationBlockConfig,
|
||||||
|
pub upstream_name: Option<&'a str>,
|
||||||
|
pub access_rules: &'a [String],
|
||||||
|
pub rewrite_rules: &'a [String],
|
||||||
|
pub proxy_setting: Option<&'a str>,
|
||||||
|
pub limit_rules: &'a [String],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for LocationBlockRender<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, " location {} {{", self.block.path_pattern)?;
|
||||||
|
|
||||||
|
if let Some(upstream) = self.upstream_name {
|
||||||
|
writeln!(f, " proxy_pass http://{};", upstream)?;
|
||||||
|
writeln!(f, " proxy_set_header Host $host;")?;
|
||||||
|
writeln!(f, " proxy_set_header X-Real-IP $remote_addr;")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(settings) = self.proxy_setting {
|
||||||
|
for line in settings.lines() {
|
||||||
|
if !line.is_empty() {
|
||||||
|
writeln!(f, "{}", line)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for rule in self.access_rules {
|
||||||
|
writeln!(f, " {}", rule)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for rule in self.rewrite_rules {
|
||||||
|
writeln!(f, " {}", rule)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for rule in self.limit_rules {
|
||||||
|
writeln!(f, " {}", rule)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(f, " }}")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use crate::service::proxy::types::LogSettingConfig;
|
||||||
|
|
||||||
|
impl std::fmt::Display for LogSettingConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(ref path) = self.access_log_path {
|
||||||
|
if let Some(ref level) = self.log_level {
|
||||||
|
writeln!(f, " access_log {} {};", path, level)?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " access_log {};", path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref path) = self.error_log_path {
|
||||||
|
if let Some(ref level) = self.log_level {
|
||||||
|
writeln!(f, " error_log {} {};", path, level)?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " error_log {};", path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
11
apps/nxmesh-master/src/service/proxy/nginx/config/mod.rs
Normal file
11
apps/nxmesh-master/src/service/proxy/nginx/config/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
pub(crate) mod access_rule;
|
||||||
|
pub(crate) mod cache_zone;
|
||||||
|
pub(crate) mod limit_rule;
|
||||||
|
pub(crate) mod limit_zone;
|
||||||
|
pub(crate) mod location_block;
|
||||||
|
pub(crate) mod log_setting;
|
||||||
|
pub(crate) mod proxy_setting;
|
||||||
|
pub(crate) mod rewrite_rule;
|
||||||
|
pub(crate) mod server_block;
|
||||||
|
pub(crate) mod ssl_certificate;
|
||||||
|
pub(crate) mod upstream;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
use crate::service::proxy::types::ProxySettingConfig;
|
||||||
|
|
||||||
|
pub struct ProxySettingRender<'a> {
|
||||||
|
pub setting: &'a ProxySettingConfig,
|
||||||
|
pub cache_zone_name: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ProxySettingRender<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(timeout) = self.setting.read_timeout {
|
||||||
|
writeln!(f, " proxy_read_timeout {}s;", timeout)?;
|
||||||
|
}
|
||||||
|
if let Some(timeout) = self.setting.connect_timeout {
|
||||||
|
writeln!(f, " proxy_connect_timeout {}s;", timeout)?;
|
||||||
|
}
|
||||||
|
if let Some(buffer) = self.setting.buffer_size {
|
||||||
|
writeln!(f, " proxy_buffer_size {};", buffer)?;
|
||||||
|
}
|
||||||
|
if self.setting.cache_enabled.unwrap_or(false)
|
||||||
|
&& let Some(zone_name) = self.cache_zone_name
|
||||||
|
{
|
||||||
|
writeln!(f, " proxy_cache {};", zone_name)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
use crate::service::proxy::types::RewriteRuleConfig;
|
||||||
|
|
||||||
|
impl std::fmt::Display for RewriteRuleConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "rewrite {} {}", self.pattern, self.replacement)?;
|
||||||
|
if let Some(ref flag) = self.flag {
|
||||||
|
write!(f, " {}", flag)?;
|
||||||
|
}
|
||||||
|
write!(f, ";")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
use crate::service::proxy::types::ServerBlockConfig;
|
||||||
|
|
||||||
|
pub struct ServerBlockRender<'a> {
|
||||||
|
pub block: &'a ServerBlockConfig,
|
||||||
|
pub ssl_cert: Option<&'a str>,
|
||||||
|
pub locations: &'a [String],
|
||||||
|
pub access_rules: &'a [String],
|
||||||
|
pub log_setting: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ServerBlockRender<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "server {{")?;
|
||||||
|
|
||||||
|
if self.block.ssl_enabled.unwrap_or(false) {
|
||||||
|
writeln!(f, " listen {} ssl;", self.block.listen_port)?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " listen {};", self.block.listen_port)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref names) = self.block.server_name
|
||||||
|
&& !names.is_empty()
|
||||||
|
{
|
||||||
|
writeln!(f, " server_name {};", names.join(" "))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cert) = self.ssl_cert {
|
||||||
|
writeln!(f, "{}", cert)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(log) = self.log_setting {
|
||||||
|
for line in log.lines() {
|
||||||
|
if !line.is_empty() {
|
||||||
|
writeln!(f, "{}", line)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for rule in self.access_rules {
|
||||||
|
writeln!(f, " {}", rule)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for loc in self.locations {
|
||||||
|
writeln!(f, "{}", loc)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use crate::service::proxy::types::SslCertificateConfig;
|
||||||
|
|
||||||
|
impl std::fmt::Display for SslCertificateConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, " ssl_certificate {};", self.cert_path)?;
|
||||||
|
writeln!(f, " ssl_certificate_key {};", self.key_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::service::proxy::types::UpstreamConfig;
|
||||||
|
|
||||||
|
impl std::fmt::Display for UpstreamConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, " server {}:{};", self.target_host, self.target_port)
|
||||||
|
}
|
||||||
|
}
|
||||||
451
apps/nxmesh-master/src/service/proxy/nginx/mod.rs
Normal file
451
apps/nxmesh-master/src/service/proxy/nginx/mod.rs
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
pub(crate) mod config;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::service::proxy::ProxyConfigRenderer;
|
||||||
|
use crate::service::proxy::types::{
|
||||||
|
AccessRuleConfig, CacheZoneConfig, LimitRuleConfig, LimitZoneConfig, LogSettingConfig,
|
||||||
|
OverrideRef, ProxyConfig, ProxySettingConfig, RewriteRuleConfig, SslCertificateConfig,
|
||||||
|
UpstreamConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn resolve_ids<'a, T>(ids: &[OverrideRef], map: &'a HashMap<Uuid, T>) -> Vec<&'a T> {
|
||||||
|
ids.iter().filter_map(|r| map.get(&r.id)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_upstream_name(
|
||||||
|
upstream_id: Option<Uuid>,
|
||||||
|
upstreams: &HashMap<Uuid, UpstreamConfig>,
|
||||||
|
) -> Option<&str> {
|
||||||
|
upstream_id
|
||||||
|
.and_then(|id| upstreams.get(&id))
|
||||||
|
.map(|u| u.name.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_cache_zone_name(
|
||||||
|
zone_id: Option<Uuid>,
|
||||||
|
cache_zones: &HashMap<Uuid, CacheZoneConfig>,
|
||||||
|
) -> Option<&str> {
|
||||||
|
zone_id
|
||||||
|
.and_then(|id| cache_zones.get(&id))
|
||||||
|
.map(|z| z.name.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_limit_zone_name(
|
||||||
|
zone_id: Uuid,
|
||||||
|
limit_zones: &HashMap<Uuid, LimitZoneConfig>,
|
||||||
|
) -> Option<&str> {
|
||||||
|
limit_zones.get(&zone_id).map(|z| z.name.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_access_rules(
|
||||||
|
ids: &[OverrideRef],
|
||||||
|
access_rules: &HashMap<Uuid, AccessRuleConfig>,
|
||||||
|
) -> Vec<String> {
|
||||||
|
resolve_ids(ids, access_rules)
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| r.to_string())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_rewrite_rules(
|
||||||
|
ids: &[OverrideRef],
|
||||||
|
rewrite_rules: &HashMap<Uuid, RewriteRuleConfig>,
|
||||||
|
) -> Vec<String> {
|
||||||
|
resolve_ids(ids, rewrite_rules)
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| r.to_string())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_limit_rules(
|
||||||
|
ids: &[OverrideRef],
|
||||||
|
limit_rules: &HashMap<Uuid, LimitRuleConfig>,
|
||||||
|
limit_zones: &HashMap<Uuid, LimitZoneConfig>,
|
||||||
|
) -> Vec<String> {
|
||||||
|
resolve_ids(ids, limit_rules)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|rule| {
|
||||||
|
let zone_name = resolve_limit_zone_name(rule.zone_id, limit_zones)?;
|
||||||
|
Some(config::limit_rule::LimitRuleRender { rule, zone_name }.to_string())
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_proxy_setting(
|
||||||
|
ids: &[OverrideRef],
|
||||||
|
proxy_settings: &HashMap<Uuid, ProxySettingConfig>,
|
||||||
|
cache_zones: &HashMap<Uuid, CacheZoneConfig>,
|
||||||
|
) -> Option<String> {
|
||||||
|
let setting = resolve_ids(ids, proxy_settings).into_iter().next()?;
|
||||||
|
let cache_zone_name = resolve_cache_zone_name(setting.cache_zone, cache_zones);
|
||||||
|
Some(
|
||||||
|
config::proxy_setting::ProxySettingRender {
|
||||||
|
setting,
|
||||||
|
cache_zone_name,
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_log_setting(
|
||||||
|
ids: &[OverrideRef],
|
||||||
|
log_settings: &HashMap<Uuid, LogSettingConfig>,
|
||||||
|
) -> Option<String> {
|
||||||
|
let setting = resolve_ids(ids, log_settings).into_iter().next()?;
|
||||||
|
Some(setting.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_ssl_cert(
|
||||||
|
ids: &[OverrideRef],
|
||||||
|
ssl_certificates: &HashMap<Uuid, SslCertificateConfig>,
|
||||||
|
) -> Option<String> {
|
||||||
|
let cert = resolve_ids(ids, ssl_certificates).into_iter().next()?;
|
||||||
|
Some(cert.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NginxConfigRenderer;
|
||||||
|
|
||||||
|
impl ProxyConfigRenderer for NginxConfigRenderer {
|
||||||
|
fn render(&self, config: &ProxyConfig) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
// Global cache zones
|
||||||
|
for zone in config.cache_zones.values() {
|
||||||
|
writeln!(output, "{}", zone).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit request zones
|
||||||
|
for zone in config.limit_zones.values() {
|
||||||
|
writeln!(output, "{}", zone).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upstream blocks
|
||||||
|
for upstream in config.upstreams.values() {
|
||||||
|
writeln!(output, "upstream {} {{", upstream.name).ok();
|
||||||
|
writeln!(output, "{}", upstream).ok();
|
||||||
|
writeln!(output, "}}").ok();
|
||||||
|
writeln!(output).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server blocks
|
||||||
|
for sb in config.server_blocks.values() {
|
||||||
|
let access_rules = render_access_rules(&sb.access_rules, &config.access_rules);
|
||||||
|
let ssl_cert = render_ssl_cert(&sb.ssl_certificates, &config.ssl_certificates);
|
||||||
|
let log_setting = render_log_setting(&sb.log_settings, &config.log_settings);
|
||||||
|
|
||||||
|
// Render location blocks for this server
|
||||||
|
let locations: Vec<String> = resolve_ids(&sb.location_blocks, &config.location_blocks)
|
||||||
|
.into_iter()
|
||||||
|
.map(|lb| {
|
||||||
|
let upstream_name =
|
||||||
|
resolve_upstream_name(lb.proxy_pass_upstream_id, &config.upstreams);
|
||||||
|
let lb_access_rules =
|
||||||
|
render_access_rules(&lb.access_rules, &config.access_rules);
|
||||||
|
let lb_rewrite_rules =
|
||||||
|
render_rewrite_rules(&lb.rewrite_rules, &config.rewrite_rules);
|
||||||
|
let lb_proxy_setting = render_proxy_setting(
|
||||||
|
&lb.proxy_settings,
|
||||||
|
&config.proxy_settings,
|
||||||
|
&config.cache_zones,
|
||||||
|
);
|
||||||
|
let lb_limit_rules = render_limit_rules(
|
||||||
|
&lb.limit_rules,
|
||||||
|
&config.limit_rules,
|
||||||
|
&config.limit_zones,
|
||||||
|
);
|
||||||
|
|
||||||
|
config::location_block::LocationBlockRender {
|
||||||
|
block: lb,
|
||||||
|
upstream_name,
|
||||||
|
access_rules: &lb_access_rules,
|
||||||
|
rewrite_rules: &lb_rewrite_rules,
|
||||||
|
proxy_setting: lb_proxy_setting.as_deref(),
|
||||||
|
limit_rules: &lb_limit_rules,
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let server = config::server_block::ServerBlockRender {
|
||||||
|
block: sb,
|
||||||
|
ssl_cert: ssl_cert.as_deref(),
|
||||||
|
locations: &locations,
|
||||||
|
access_rules: &access_rules,
|
||||||
|
log_setting: log_setting.as_deref(),
|
||||||
|
};
|
||||||
|
|
||||||
|
writeln!(output, "{}", server).ok();
|
||||||
|
writeln!(output).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::service::proxy::types::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn make_id() -> uuid::Uuid {
|
||||||
|
uuid::Uuid::new_v4()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn basic_proxy_config() -> ProxyConfig {
|
||||||
|
let upstream_id = make_id();
|
||||||
|
let location_id = make_id();
|
||||||
|
let server_id = make_id();
|
||||||
|
|
||||||
|
ProxyConfig {
|
||||||
|
id: make_id(),
|
||||||
|
name: "test".to_string(),
|
||||||
|
r#type: ProxyType::Nginx,
|
||||||
|
description: None,
|
||||||
|
parent_config_id: None,
|
||||||
|
upstreams: HashMap::from([(
|
||||||
|
upstream_id,
|
||||||
|
UpstreamConfig {
|
||||||
|
id: upstream_id,
|
||||||
|
name: "backend".to_string(),
|
||||||
|
target_host: "127.0.0.1".to_string(),
|
||||||
|
target_port: 3000,
|
||||||
|
metadata: None,
|
||||||
|
override_of_id: None,
|
||||||
|
location_blocks: vec![OverrideRef {
|
||||||
|
id: location_id,
|
||||||
|
override_of_id: None,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
location_blocks: HashMap::from([(
|
||||||
|
location_id,
|
||||||
|
LocationBlockConfig {
|
||||||
|
id: location_id,
|
||||||
|
server_id,
|
||||||
|
path_pattern: "/api".to_string(),
|
||||||
|
proxy_pass_upstream_id: Some(upstream_id),
|
||||||
|
metadata: None,
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
limit_rules: vec![],
|
||||||
|
proxy_settings: vec![],
|
||||||
|
rewrite_rules: vec![],
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
server_blocks: HashMap::from([(
|
||||||
|
server_id,
|
||||||
|
ServerBlockConfig {
|
||||||
|
id: server_id,
|
||||||
|
server_name: Some(vec!["example.com".to_string()]),
|
||||||
|
listen_port: 80,
|
||||||
|
ssl_enabled: Some(false),
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![OverrideRef {
|
||||||
|
id: location_id,
|
||||||
|
override_of_id: None,
|
||||||
|
}],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
access_rules: HashMap::new(),
|
||||||
|
cache_zones: HashMap::new(),
|
||||||
|
limit_rules: HashMap::new(),
|
||||||
|
limit_zones: HashMap::new(),
|
||||||
|
log_settings: HashMap::new(),
|
||||||
|
proxy_settings: HashMap::new(),
|
||||||
|
rewrite_rules: HashMap::new(),
|
||||||
|
ssl_certificates: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_basic_nginx_config() {
|
||||||
|
let config = basic_proxy_config();
|
||||||
|
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("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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_config_with_cache_zone() {
|
||||||
|
let zone_id = make_id();
|
||||||
|
let mut config = basic_proxy_config();
|
||||||
|
config.cache_zones.insert(
|
||||||
|
zone_id,
|
||||||
|
CacheZoneConfig {
|
||||||
|
id: zone_id,
|
||||||
|
name: "mycache".to_string(),
|
||||||
|
path: "/var/cache/nginx".to_string(),
|
||||||
|
size: "10m".to_string(),
|
||||||
|
override_of_id: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let renderer = NginxConfigRenderer;
|
||||||
|
let output = renderer.render(&config);
|
||||||
|
assert!(
|
||||||
|
output.contains("proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m;")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_config_with_access_rules() {
|
||||||
|
let rule_id = make_id();
|
||||||
|
let mut config = basic_proxy_config();
|
||||||
|
|
||||||
|
// Get the server ID from the config
|
||||||
|
let sb_id = *config.server_blocks.keys().next().unwrap();
|
||||||
|
|
||||||
|
let rule = AccessRuleConfig {
|
||||||
|
id: rule_id,
|
||||||
|
r#type: "allow".to_string(),
|
||||||
|
ip_cidr: "192.168.1.0/24".to_string(),
|
||||||
|
description: None,
|
||||||
|
priority: 10,
|
||||||
|
override_of_id: None,
|
||||||
|
};
|
||||||
|
config.access_rules.insert(rule_id, rule);
|
||||||
|
|
||||||
|
// Add access rule ref to the server block
|
||||||
|
let sb = config.server_blocks.get_mut(&sb_id).unwrap();
|
||||||
|
sb.access_rules.push(OverrideRef {
|
||||||
|
id: rule_id,
|
||||||
|
override_of_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let renderer = NginxConfigRenderer;
|
||||||
|
let output = renderer.render(&config);
|
||||||
|
assert!(output.contains("allow 192.168.1.0/24;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_config_empty() {
|
||||||
|
let config = ProxyConfig {
|
||||||
|
id: make_id(),
|
||||||
|
name: "empty".to_string(),
|
||||||
|
r#type: ProxyType::Nginx,
|
||||||
|
description: None,
|
||||||
|
parent_config_id: None,
|
||||||
|
server_blocks: HashMap::new(),
|
||||||
|
upstreams: HashMap::new(),
|
||||||
|
access_rules: HashMap::new(),
|
||||||
|
cache_zones: HashMap::new(),
|
||||||
|
limit_rules: HashMap::new(),
|
||||||
|
limit_zones: HashMap::new(),
|
||||||
|
location_blocks: HashMap::new(),
|
||||||
|
log_settings: HashMap::new(),
|
||||||
|
proxy_settings: HashMap::new(),
|
||||||
|
rewrite_rules: HashMap::new(),
|
||||||
|
ssl_certificates: HashMap::new(),
|
||||||
|
};
|
||||||
|
let renderer = NginxConfigRenderer;
|
||||||
|
let output = renderer.render(&config);
|
||||||
|
assert!(output.is_empty() || output.trim().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_config_with_limit_zone_and_rule() {
|
||||||
|
let mut config = basic_proxy_config();
|
||||||
|
let zone_id = make_id();
|
||||||
|
let location_id = *config.location_blocks.keys().next().unwrap();
|
||||||
|
let rule_id = make_id();
|
||||||
|
|
||||||
|
config.limit_zones.insert(
|
||||||
|
zone_id,
|
||||||
|
LimitZoneConfig {
|
||||||
|
id: zone_id,
|
||||||
|
name: "reqzone".to_string(),
|
||||||
|
key: "$binary_remote_addr".to_string(),
|
||||||
|
rate: "10r/s".to_string(),
|
||||||
|
override_of_id: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
config.limit_rules.insert(
|
||||||
|
rule_id,
|
||||||
|
LimitRuleConfig {
|
||||||
|
id: rule_id,
|
||||||
|
location_id,
|
||||||
|
zone_id,
|
||||||
|
burst: Some(20),
|
||||||
|
nodelay: Some(true),
|
||||||
|
is_deleted: false,
|
||||||
|
override_of_id: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let lb = config.location_blocks.get_mut(&location_id).unwrap();
|
||||||
|
lb.limit_rules.push(OverrideRef {
|
||||||
|
id: rule_id,
|
||||||
|
override_of_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let renderer = NginxConfigRenderer;
|
||||||
|
let output = renderer.render(&config);
|
||||||
|
assert!(output.contains("limit_req_zone $binary_remote_addr zone=reqzone:10r/s;"));
|
||||||
|
assert!(output.contains("limit_req zone=reqzone burst=20 nodelay;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_config_with_ssl() {
|
||||||
|
let mut config = basic_proxy_config();
|
||||||
|
let cert_id = make_id();
|
||||||
|
let sb_id = *config.server_blocks.keys().next().unwrap();
|
||||||
|
|
||||||
|
config.ssl_certificates.insert(
|
||||||
|
cert_id,
|
||||||
|
SslCertificateConfig {
|
||||||
|
id: cert_id,
|
||||||
|
name: "test-cert".to_string(),
|
||||||
|
cert_path: "/etc/ssl/certs/test.pem".to_string(),
|
||||||
|
key_path: "/etc/ssl/private/test.key".to_string(),
|
||||||
|
expiry_date: chrono::Utc::now(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let sb = config.server_blocks.get_mut(&sb_id).unwrap();
|
||||||
|
sb.ssl_enabled = Some(true);
|
||||||
|
sb.ssl_certificates.push(OverrideRef {
|
||||||
|
id: cert_id,
|
||||||
|
override_of_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let renderer = NginxConfigRenderer;
|
||||||
|
let output = renderer.render(&config);
|
||||||
|
assert!(output.contains("listen 80 ssl;"));
|
||||||
|
assert!(output.contains("ssl_certificate /etc/ssl/certs/test.pem;"));
|
||||||
|
assert!(output.contains("ssl_certificate_key /etc/ssl/private/test.key;"));
|
||||||
|
}
|
||||||
|
}
|
||||||
149
apps/nxmesh-master/src/service/proxy/proxy_setting/mod.rs
Normal file
149
apps/nxmesh-master/src/service/proxy/proxy_setting/mod.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
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<i32>,
|
||||||
|
pub connect_timeout: Option<i32>,
|
||||||
|
pub buffer_size: Option<i32>,
|
||||||
|
pub cache_enabled: Option<bool>,
|
||||||
|
pub cache_zone: Option<Uuid>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateProxySettingParams {
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub read_timeout: Option<Option<i32>>,
|
||||||
|
pub connect_timeout: Option<Option<i32>>,
|
||||||
|
pub buffer_size: Option<Option<i32>>,
|
||||||
|
pub cache_enabled: Option<Option<bool>>,
|
||||||
|
pub cache_zone: Option<Option<Uuid>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait ProxySettingService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<ProxySettingConfig>;
|
||||||
|
async fn list_by_location(
|
||||||
|
&self,
|
||||||
|
location_id: Uuid,
|
||||||
|
) -> ProxyServiceResult<Vec<ProxySettingConfig>>;
|
||||||
|
async fn create(
|
||||||
|
&self,
|
||||||
|
params: CreateProxySettingParams,
|
||||||
|
) -> ProxyServiceResult<ProxySettingConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateProxySettingParams,
|
||||||
|
) -> ProxyServiceResult<ProxySettingConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ProxySettingConfig> {
|
||||||
|
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<Vec<ProxySettingConfig>> {
|
||||||
|
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<ProxySettingConfig> {
|
||||||
|
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<ProxySettingConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::proxy_setting::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
624
apps/nxmesh-master/src/service/proxy/repo.rs
Normal file
624
apps/nxmesh-master/src/service/proxy/repo.rs
Normal file
@@ -0,0 +1,624 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, prelude::*};
|
||||||
|
|
||||||
|
use crate::service::proxy::types::{
|
||||||
|
AgentConfigBinding, CreateProxyConfigParams, Mergeable, OverrideRef, ProxyConfig,
|
||||||
|
ProxyConfigSummary, ProxyServiceError, ProxyServiceResult, ProxyType, UpdateProxyConfigParams,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait ProxyRepo: Send + Sync + 'static {
|
||||||
|
async fn get_proxy_raw_config(&self, proxy_id: uuid::Uuid) -> ProxyServiceResult<ProxyConfig>;
|
||||||
|
async fn get_proxy_raw_configs(
|
||||||
|
&self,
|
||||||
|
proxy_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<Vec<ProxyConfig>>;
|
||||||
|
async fn get_merged_proxy_config(
|
||||||
|
&self,
|
||||||
|
proxy_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<ProxyConfig>;
|
||||||
|
|
||||||
|
// CRUD
|
||||||
|
async fn list_proxy_configs(&self) -> ProxyServiceResult<Vec<ProxyConfigSummary>>;
|
||||||
|
async fn create_proxy_config(
|
||||||
|
&self,
|
||||||
|
params: CreateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary>;
|
||||||
|
async fn update_proxy_config(
|
||||||
|
&self,
|
||||||
|
id: uuid::Uuid,
|
||||||
|
params: UpdateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary>;
|
||||||
|
async fn delete_proxy_config(&self, id: uuid::Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
|
||||||
|
// Agent config binding
|
||||||
|
async fn get_active_agent_config(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<Option<ProxyConfigSummary>>;
|
||||||
|
async fn bind_agent_to_config(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
config_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<AgentConfigBinding>;
|
||||||
|
async fn unbind_agent(&self, agent_id: uuid::Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ProxyRepoImpl {
|
||||||
|
db: DatabaseConnection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProxyRepoImpl {
|
||||||
|
pub fn new(db: DatabaseConnection) -> Self {
|
||||||
|
Self { db }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ProxyRepo for ProxyRepoImpl {
|
||||||
|
async fn get_proxy_raw_configs(
|
||||||
|
&self,
|
||||||
|
proxy_config_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<Vec<ProxyConfig>> {
|
||||||
|
let mut proxy_config_id_frontier = vec![proxy_config_id];
|
||||||
|
let mut visited = std::collections::HashSet::new();
|
||||||
|
|
||||||
|
let mut configs: Vec<ProxyConfig> = Vec::new();
|
||||||
|
|
||||||
|
while let Some(current_id) = proxy_config_id_frontier.pop() {
|
||||||
|
if visited.contains(¤t_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited.insert(current_id);
|
||||||
|
//
|
||||||
|
let config = self.get_proxy_raw_config(current_id).await?;
|
||||||
|
//
|
||||||
|
if let Some(parent_ids) = &config.parent_config_id {
|
||||||
|
proxy_config_id_frontier.extend(parent_ids);
|
||||||
|
}
|
||||||
|
configs.push(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(configs)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_proxy_raw_config(&self, proxy_id: uuid::Uuid) -> ProxyServiceResult<ProxyConfig> {
|
||||||
|
let proxy_entity = crate::db::entities::proxy_config::Entity::find_by_id(proxy_id)
|
||||||
|
.one(&self.db)
|
||||||
|
.await?
|
||||||
|
.ok_or(ProxyServiceError::ConfigNotFound)?;
|
||||||
|
|
||||||
|
// Resolve parent configs from config_inheritance
|
||||||
|
let parent_config_id = {
|
||||||
|
let inheritance = crate::db::entities::config_inheritance::Entity::find()
|
||||||
|
.filter(crate::db::entities::config_inheritance::Column::ChildConfigId.eq(proxy_id))
|
||||||
|
.all(&self.db)
|
||||||
|
.await?;
|
||||||
|
if inheritance.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(
|
||||||
|
inheritance
|
||||||
|
.into_iter()
|
||||||
|
.map(|ci| ci.parent_config_id)
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Direct children of proxy_config
|
||||||
|
let server_blocks = crate::db::entities::server_block::Entity::find()
|
||||||
|
.filter(crate::db::entities::server_block::Column::ConfigId.eq(proxy_id))
|
||||||
|
.all(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let upstreams = crate::db::entities::upstream::Entity::find()
|
||||||
|
.filter(crate::db::entities::upstream::Column::ConfigId.eq(proxy_id))
|
||||||
|
.all(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Children of server_blocks
|
||||||
|
let server_block_ids: Vec<uuid::Uuid> = server_blocks.iter().map(|sb| sb.id).collect();
|
||||||
|
|
||||||
|
let (location_blocks, log_settings, server_access_rules) = if server_block_ids.is_empty() {
|
||||||
|
(vec![], vec![], vec![])
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
crate::db::entities::location_block::Entity::find()
|
||||||
|
.filter(
|
||||||
|
crate::db::entities::location_block::Column::ServerId
|
||||||
|
.is_in(server_block_ids.clone()),
|
||||||
|
)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?,
|
||||||
|
crate::db::entities::log_setting::Entity::find()
|
||||||
|
.filter(
|
||||||
|
crate::db::entities::log_setting::Column::ServerId
|
||||||
|
.is_in(server_block_ids.clone()),
|
||||||
|
)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?,
|
||||||
|
crate::db::entities::access_rule::Entity::find()
|
||||||
|
.filter(
|
||||||
|
crate::db::entities::access_rule::Column::ServerId.is_in(server_block_ids),
|
||||||
|
)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Children of location_blocks
|
||||||
|
let location_block_ids: Vec<uuid::Uuid> = location_blocks.iter().map(|lb| lb.id).collect();
|
||||||
|
|
||||||
|
let (location_access_rules, limit_rules, proxy_settings, rewrite_rules) =
|
||||||
|
if location_block_ids.is_empty() {
|
||||||
|
(vec![], vec![], vec![], vec![])
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
crate::db::entities::access_rule::Entity::find()
|
||||||
|
.filter(
|
||||||
|
crate::db::entities::access_rule::Column::LocationId
|
||||||
|
.is_in(location_block_ids.clone()),
|
||||||
|
)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?,
|
||||||
|
crate::db::entities::limit_rule::Entity::find()
|
||||||
|
.filter(
|
||||||
|
crate::db::entities::limit_rule::Column::LocationId
|
||||||
|
.is_in(location_block_ids.clone()),
|
||||||
|
)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?,
|
||||||
|
crate::db::entities::proxy_setting::Entity::find()
|
||||||
|
.filter(
|
||||||
|
crate::db::entities::proxy_setting::Column::LocationId
|
||||||
|
.is_in(location_block_ids.clone()),
|
||||||
|
)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?,
|
||||||
|
crate::db::entities::rewrite_rule::Entity::find()
|
||||||
|
.filter(
|
||||||
|
crate::db::entities::rewrite_rule::Column::LocationId
|
||||||
|
.is_in(location_block_ids),
|
||||||
|
)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Collect referenced IDs for zone and cert lookups
|
||||||
|
let limit_zone_ids: Vec<uuid::Uuid> = limit_rules.iter().map(|lr| lr.zone_id).collect();
|
||||||
|
let cache_zone_ids: Vec<uuid::Uuid> = proxy_settings
|
||||||
|
.iter()
|
||||||
|
.filter_map(|ps| ps.cache_zone)
|
||||||
|
.collect();
|
||||||
|
let ssl_cert_ids: Vec<uuid::Uuid> = server_blocks
|
||||||
|
.iter()
|
||||||
|
.filter_map(|sb| sb.ssl_cert_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let limit_zones = if limit_zone_ids.is_empty() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
crate::db::entities::limit_zone::Entity::find()
|
||||||
|
.filter(crate::db::entities::limit_zone::Column::Id.is_in(limit_zone_ids))
|
||||||
|
.all(&self.db)
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
let cache_zones = if cache_zone_ids.is_empty() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
crate::db::entities::cache_zone::Entity::find()
|
||||||
|
.filter(crate::db::entities::cache_zone::Column::Id.is_in(cache_zone_ids))
|
||||||
|
.all(&self.db)
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
let ssl_certificates = if ssl_cert_ids.is_empty() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
crate::db::entities::ssl_certificate::Entity::find()
|
||||||
|
.filter(crate::db::entities::ssl_certificate::Column::Id.is_in(ssl_cert_ids))
|
||||||
|
.all(&self.db)
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Group child IDs by parent ────────────────────────────────────────
|
||||||
|
|
||||||
|
// location_block IDs grouped by server_id
|
||||||
|
let loc_block_ids_by_server: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for lb in &location_blocks {
|
||||||
|
map.entry(lb.server_id).or_default().push(OverrideRef {
|
||||||
|
id: lb.id,
|
||||||
|
override_of_id: lb.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// log_setting IDs grouped by server_id
|
||||||
|
let log_setting_ids_by_server: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for ls in &log_settings {
|
||||||
|
map.entry(ls.server_id).or_default().push(OverrideRef {
|
||||||
|
id: ls.id,
|
||||||
|
override_of_id: ls.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// server-level access_rule IDs grouped by server_id
|
||||||
|
let server_ar_ids_by_server: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for ar in &server_access_rules {
|
||||||
|
if let Some(sid) = ar.server_id {
|
||||||
|
map.entry(sid).or_default().push(OverrideRef {
|
||||||
|
id: ar.id,
|
||||||
|
override_of_id: ar.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// location-level access_rule IDs grouped by location_id
|
||||||
|
let loc_ar_ids_by_location: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for ar in &location_access_rules {
|
||||||
|
if let Some(lid) = ar.location_id {
|
||||||
|
map.entry(lid).or_default().push(OverrideRef {
|
||||||
|
id: ar.id,
|
||||||
|
override_of_id: ar.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// limit_rule IDs grouped by location_id
|
||||||
|
let lr_ids_by_location: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for lr in &limit_rules {
|
||||||
|
map.entry(lr.location_id).or_default().push(OverrideRef {
|
||||||
|
id: lr.id,
|
||||||
|
override_of_id: lr.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// proxy_setting IDs grouped by location_id
|
||||||
|
let ps_ids_by_location: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for ps in &proxy_settings {
|
||||||
|
map.entry(ps.location_id).or_default().push(OverrideRef {
|
||||||
|
id: ps.id,
|
||||||
|
override_of_id: ps.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// rewrite_rule IDs grouped by location_id
|
||||||
|
let rr_ids_by_location: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for rr in &rewrite_rules {
|
||||||
|
map.entry(rr.location_id).or_default().push(OverrideRef {
|
||||||
|
id: rr.id,
|
||||||
|
override_of_id: rr.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// location_block IDs grouped by proxy_pass_upstream_id (reverse FK)
|
||||||
|
let loc_block_ids_by_upstream: HashMap<uuid::Uuid, Vec<OverrideRef>> = {
|
||||||
|
let mut map: HashMap<_, Vec<_>> = HashMap::new();
|
||||||
|
for lb in &location_blocks {
|
||||||
|
if let Some(up_id) = lb.proxy_pass_upstream_id {
|
||||||
|
map.entry(up_id).or_default().push(OverrideRef {
|
||||||
|
id: lb.id,
|
||||||
|
override_of_id: lb.override_of_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Build child-ID tuples for each config type ───────────────────────
|
||||||
|
|
||||||
|
let location_block_tuples: Vec<(
|
||||||
|
crate::db::entities::location_block::Model,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
)> = location_blocks
|
||||||
|
.into_iter()
|
||||||
|
.map(|lb| {
|
||||||
|
let ar_ids = loc_ar_ids_by_location
|
||||||
|
.get(&lb.id)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let lr_ids = lr_ids_by_location.get(&lb.id).cloned().unwrap_or_default();
|
||||||
|
let ps_ids = ps_ids_by_location.get(&lb.id).cloned().unwrap_or_default();
|
||||||
|
let rr_ids = rr_ids_by_location.get(&lb.id).cloned().unwrap_or_default();
|
||||||
|
(lb, ar_ids, lr_ids, ps_ids, rr_ids)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let server_block_tuples: Vec<(
|
||||||
|
crate::db::entities::server_block::Model,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
)> = server_blocks
|
||||||
|
.into_iter()
|
||||||
|
.map(|sb| {
|
||||||
|
let ar_ids = server_ar_ids_by_server
|
||||||
|
.get(&sb.id)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let lb_ids = loc_block_ids_by_server
|
||||||
|
.get(&sb.id)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let ls_ids = log_setting_ids_by_server
|
||||||
|
.get(&sb.id)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let sc_ids: Vec<OverrideRef> = sb
|
||||||
|
.ssl_cert_id
|
||||||
|
.into_iter()
|
||||||
|
.map(|id| OverrideRef {
|
||||||
|
id,
|
||||||
|
override_of_id: None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
(sb, ar_ids, lb_ids, ls_ids, sc_ids)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let upstream_tuples: Vec<(crate::db::entities::upstream::Model, Vec<OverrideRef>)> =
|
||||||
|
upstreams
|
||||||
|
.into_iter()
|
||||||
|
.map(|u| {
|
||||||
|
let lb_ids = loc_block_ids_by_upstream
|
||||||
|
.get(&u.id)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
(u, lb_ids)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Combine server-level and location-level access rules into one flat list
|
||||||
|
let all_access_rules: Vec<crate::db::entities::access_rule::Model> = {
|
||||||
|
let mut ars =
|
||||||
|
Vec::with_capacity(server_access_rules.len() + location_access_rules.len());
|
||||||
|
ars.extend(server_access_rules);
|
||||||
|
ars.extend(location_access_rules);
|
||||||
|
ars
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ProxyConfig {
|
||||||
|
id: proxy_entity.id,
|
||||||
|
name: proxy_entity.name,
|
||||||
|
r#type: ProxyType::Nginx,
|
||||||
|
description: proxy_entity.description,
|
||||||
|
parent_config_id,
|
||||||
|
server_blocks: server_block_tuples
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.0.id, m.into()))
|
||||||
|
.collect(),
|
||||||
|
upstreams: upstream_tuples
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.0.id, m.into()))
|
||||||
|
.collect(),
|
||||||
|
location_blocks: location_block_tuples
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.0.id, m.into()))
|
||||||
|
.collect(),
|
||||||
|
access_rules: all_access_rules
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.id, m.into()))
|
||||||
|
.collect(),
|
||||||
|
cache_zones: cache_zones.into_iter().map(|m| (m.id, m.into())).collect(),
|
||||||
|
limit_rules: limit_rules.into_iter().map(|m| (m.id, m.into())).collect(),
|
||||||
|
limit_zones: limit_zones.into_iter().map(|m| (m.id, m.into())).collect(),
|
||||||
|
log_settings: log_settings.into_iter().map(|m| (m.id, m.into())).collect(),
|
||||||
|
proxy_settings: proxy_settings
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.id, m.into()))
|
||||||
|
.collect(),
|
||||||
|
rewrite_rules: rewrite_rules
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.id, m.into()))
|
||||||
|
.collect(),
|
||||||
|
ssl_certificates: ssl_certificates
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.id, m.into()))
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_merged_proxy_config(
|
||||||
|
&self,
|
||||||
|
proxy_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<ProxyConfig> {
|
||||||
|
let configs = self.get_proxy_raw_configs(proxy_id).await?;
|
||||||
|
|
||||||
|
// configs is ordered [leaf, ..., root] (most specific first)
|
||||||
|
// self.merge(other) means self overrides other
|
||||||
|
// So start with leaf and merge each ancestor into it
|
||||||
|
let mut iter = configs.into_iter();
|
||||||
|
let mut merged = iter.next().ok_or(ProxyServiceError::ConfigNotFound)?;
|
||||||
|
for config in iter {
|
||||||
|
merged.merge(config);
|
||||||
|
}
|
||||||
|
Ok(merged)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── CRUD ──
|
||||||
|
|
||||||
|
async fn list_proxy_configs(&self) -> ProxyServiceResult<Vec<ProxyConfigSummary>> {
|
||||||
|
use crate::db::entities::proxy_config::Column;
|
||||||
|
use sea_orm::QueryOrder;
|
||||||
|
|
||||||
|
let configs = crate::db::entities::proxy_config::Entity::find()
|
||||||
|
.order_by(Column::UpdatedAt, sea_orm::Order::Desc)
|
||||||
|
.all(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(configs.into_iter().map(Into::into).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_proxy_config(
|
||||||
|
&self,
|
||||||
|
params: CreateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary> {
|
||||||
|
use crate::db::entities::proxy_config::ActiveModel;
|
||||||
|
let now = chrono::Utc::now().naive_utc();
|
||||||
|
let model = ActiveModel {
|
||||||
|
id: Set(uuid::Uuid::new_v4()),
|
||||||
|
name: Set(params.name),
|
||||||
|
description: Set(params.description),
|
||||||
|
is_template: Set(params.is_template),
|
||||||
|
created_at: Set(now),
|
||||||
|
updated_at: Set(now),
|
||||||
|
};
|
||||||
|
let result = model.insert(&self.db).await?;
|
||||||
|
Ok(result.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_proxy_config(
|
||||||
|
&self,
|
||||||
|
id: uuid::Uuid,
|
||||||
|
params: UpdateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary> {
|
||||||
|
use crate::db::entities::proxy_config::ActiveModel;
|
||||||
|
use crate::db::entities::proxy_config::Entity as ProxyConfigEntity;
|
||||||
|
|
||||||
|
let existing = ProxyConfigEntity::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(description) = params.description {
|
||||||
|
model.description = Set(Some(description));
|
||||||
|
}
|
||||||
|
if let Some(is_template) = params.is_template {
|
||||||
|
model.is_template = Set(is_template);
|
||||||
|
}
|
||||||
|
model.updated_at = Set(chrono::Utc::now().naive_utc());
|
||||||
|
|
||||||
|
let result = model.update(&self.db).await?;
|
||||||
|
Ok(result.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_proxy_config(&self, id: uuid::Uuid) -> ProxyServiceResult<bool> {
|
||||||
|
let result = crate::db::entities::proxy_config::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Agent config binding ──
|
||||||
|
|
||||||
|
async fn get_active_agent_config(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<Option<ProxyConfigSummary>> {
|
||||||
|
use crate::db::entities::agent_config_binding::Column;
|
||||||
|
use crate::db::entities::proxy_config::Entity as ProxyConfigEntity;
|
||||||
|
use sea_orm::Condition;
|
||||||
|
|
||||||
|
let binding = crate::db::entities::agent_config_binding::Entity::find()
|
||||||
|
.filter(
|
||||||
|
Condition::all()
|
||||||
|
.add(Column::AgentId.eq(agent_id))
|
||||||
|
.add(Column::IsActive.eq(true)),
|
||||||
|
)
|
||||||
|
.one(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
match binding {
|
||||||
|
Some(b) => {
|
||||||
|
let config = ProxyConfigEntity::find_by_id(b.config_id)
|
||||||
|
.one(&self.db)
|
||||||
|
.await?
|
||||||
|
.ok_or(ProxyServiceError::ConfigNotFound)?;
|
||||||
|
Ok(Some(config.into()))
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bind_agent_to_config(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
config_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<AgentConfigBinding> {
|
||||||
|
use crate::db::entities::agent_config_binding::ActiveModel;
|
||||||
|
use crate::db::entities::agent_config_binding::Column;
|
||||||
|
use sea_orm::Condition;
|
||||||
|
|
||||||
|
// Deactivate existing active binding for this agent
|
||||||
|
if let Some(existing) = crate::db::entities::agent_config_binding::Entity::find()
|
||||||
|
.filter(
|
||||||
|
Condition::all()
|
||||||
|
.add(Column::AgentId.eq(agent_id))
|
||||||
|
.add(Column::IsActive.eq(true)),
|
||||||
|
)
|
||||||
|
.one(&self.db)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
let mut active: ActiveModel = existing.into();
|
||||||
|
active.is_active = Set(false);
|
||||||
|
active.update(&self.db).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let now = chrono::Utc::now().naive_utc();
|
||||||
|
let model = ActiveModel {
|
||||||
|
id: Set(uuid::Uuid::new_v4()),
|
||||||
|
agent_id: Set(Some(agent_id)),
|
||||||
|
group_id: Set(None),
|
||||||
|
config_id: Set(config_id),
|
||||||
|
is_active: Set(true),
|
||||||
|
applied_at: Set(now),
|
||||||
|
};
|
||||||
|
let result = model.insert(&self.db).await?;
|
||||||
|
Ok(result.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unbind_agent(&self, agent_id: uuid::Uuid) -> ProxyServiceResult<bool> {
|
||||||
|
use crate::db::entities::agent_config_binding::ActiveModel;
|
||||||
|
use crate::db::entities::agent_config_binding::Column;
|
||||||
|
use sea_orm::Condition;
|
||||||
|
|
||||||
|
let existing = crate::db::entities::agent_config_binding::Entity::find()
|
||||||
|
.filter(
|
||||||
|
Condition::all()
|
||||||
|
.add(Column::AgentId.eq(agent_id))
|
||||||
|
.add(Column::IsActive.eq(true)),
|
||||||
|
)
|
||||||
|
.one(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(b) = existing {
|
||||||
|
let mut active: ActiveModel = b.into();
|
||||||
|
active.is_active = Set(false);
|
||||||
|
active.update(&self.db).await?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
144
apps/nxmesh-master/src/service/proxy/rewrite_rule/mod.rs
Normal file
144
apps/nxmesh-master/src/service/proxy/rewrite_rule/mod.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
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<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateRewriteRuleParams {
|
||||||
|
pub location_id: Option<Uuid>,
|
||||||
|
pub pattern: Option<String>,
|
||||||
|
pub replacement: Option<String>,
|
||||||
|
pub flag: Option<Option<String>>,
|
||||||
|
pub priority: Option<i32>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait RewriteRuleService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<RewriteRuleConfig>;
|
||||||
|
async fn list_by_location(
|
||||||
|
&self,
|
||||||
|
location_id: Uuid,
|
||||||
|
) -> ProxyServiceResult<Vec<RewriteRuleConfig>>;
|
||||||
|
async fn create(
|
||||||
|
&self,
|
||||||
|
params: CreateRewriteRuleParams,
|
||||||
|
) -> ProxyServiceResult<RewriteRuleConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateRewriteRuleParams,
|
||||||
|
) -> ProxyServiceResult<RewriteRuleConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<RewriteRuleConfig> {
|
||||||
|
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<Vec<RewriteRuleConfig>> {
|
||||||
|
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<RewriteRuleConfig> {
|
||||||
|
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<RewriteRuleConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::rewrite_rule::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
202
apps/nxmesh-master/src/service/proxy/server_block/mod.rs
Normal file
202
apps/nxmesh-master/src/service/proxy/server_block/mod.rs
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
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<Vec<String>>,
|
||||||
|
pub listen_port: i32,
|
||||||
|
pub ssl_enabled: Option<bool>,
|
||||||
|
pub ssl_cert_id: Option<Uuid>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateServerBlockParams {
|
||||||
|
pub server_name: Option<Option<Vec<String>>>,
|
||||||
|
pub listen_port: Option<i32>,
|
||||||
|
pub ssl_enabled: Option<Option<bool>>,
|
||||||
|
pub ssl_cert_id: Option<Option<Uuid>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait ServerBlockService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<ServerBlockConfig>;
|
||||||
|
async fn list_by_config(&self, config_id: Uuid) -> ProxyServiceResult<Vec<ServerBlockConfig>>;
|
||||||
|
async fn create(
|
||||||
|
&self,
|
||||||
|
params: CreateServerBlockParams,
|
||||||
|
) -> ProxyServiceResult<ServerBlockConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateServerBlockParams,
|
||||||
|
) -> ProxyServiceResult<ServerBlockConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ServerBlockConfig> {
|
||||||
|
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<ServerBlockConfig> {
|
||||||
|
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<Vec<ServerBlockConfig>> {
|
||||||
|
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<ServerBlockConfig> {
|
||||||
|
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<ServerBlockConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::server_block::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
96
apps/nxmesh-master/src/service/proxy/service.rs
Normal file
96
apps/nxmesh-master/src/service/proxy/service.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
|
use super::nginx::NginxConfigRenderer;
|
||||||
|
use super::repo::{ProxyRepo, ProxyRepoImpl};
|
||||||
|
use super::types::{
|
||||||
|
AgentConfigBinding, CreateProxyConfigParams, ProxyConfig, ProxyConfigSummary,
|
||||||
|
ProxyServiceError, ProxyServiceResult, ProxyType, UpdateProxyConfigParams,
|
||||||
|
};
|
||||||
|
use super::{ProxyConfigRenderer, ProxyServiceTrait};
|
||||||
|
|
||||||
|
pub struct ProxyServiceImpl {
|
||||||
|
repo: Box<dyn ProxyRepo>,
|
||||||
|
renderers: HashMap<ProxyType, Box<dyn ProxyConfigRenderer>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProxyServiceImpl {
|
||||||
|
pub fn new(db: DatabaseConnection) -> Self {
|
||||||
|
let mut renderers: HashMap<ProxyType, Box<dyn ProxyConfigRenderer>> = HashMap::new();
|
||||||
|
renderers.insert(ProxyType::Nginx, Box::new(NginxConfigRenderer));
|
||||||
|
Self {
|
||||||
|
repo: Box::new(ProxyRepoImpl::new(db)),
|
||||||
|
renderers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ProxyServiceTrait for ProxyServiceImpl {
|
||||||
|
async fn get_proxy_config(&self, proxy_id: uuid::Uuid) -> ProxyServiceResult<ProxyConfig> {
|
||||||
|
self.repo.get_merged_proxy_config(proxy_id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn render_config(&self, proxy_id: uuid::Uuid) -> ProxyServiceResult<String> {
|
||||||
|
let config = self.repo.get_merged_proxy_config(proxy_id).await?;
|
||||||
|
let renderer = self
|
||||||
|
.renderers
|
||||||
|
.get(&config.r#type)
|
||||||
|
.ok_or(ProxyServiceError::RendererNotFound)?;
|
||||||
|
Ok(renderer.render(&config))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_configs(&self) -> ProxyServiceResult<Vec<ProxyConfigSummary>> {
|
||||||
|
self.repo.list_proxy_configs().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_config(
|
||||||
|
&self,
|
||||||
|
params: CreateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary> {
|
||||||
|
self.repo.create_proxy_config(params).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_config(
|
||||||
|
&self,
|
||||||
|
id: uuid::Uuid,
|
||||||
|
params: UpdateProxyConfigParams,
|
||||||
|
) -> ProxyServiceResult<ProxyConfigSummary> {
|
||||||
|
self.repo.update_proxy_config(id, params).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_config(&self, id: uuid::Uuid) -> ProxyServiceResult<bool> {
|
||||||
|
self.repo.delete_proxy_config(id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_active_agent_config(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<Option<ProxyConfigSummary>> {
|
||||||
|
self.repo.get_active_agent_config(agent_id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bind_agent(
|
||||||
|
&self,
|
||||||
|
agent_id: uuid::Uuid,
|
||||||
|
config_id: uuid::Uuid,
|
||||||
|
) -> ProxyServiceResult<AgentConfigBinding> {
|
||||||
|
self.repo.bind_agent_to_config(agent_id, config_id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unbind_agent(&self, agent_id: uuid::Uuid) -> ProxyServiceResult<bool> {
|
||||||
|
self.repo.unbind_agent(agent_id).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProxyServiceImpl {
|
||||||
|
pub async fn render_config(&self, proxy_id: uuid::Uuid) -> ProxyServiceResult<String> {
|
||||||
|
let config = self.repo.get_merged_proxy_config(proxy_id).await?;
|
||||||
|
let renderer = self
|
||||||
|
.renderers
|
||||||
|
.get(&config.r#type)
|
||||||
|
.ok_or(ProxyServiceError::RendererNotFound)?;
|
||||||
|
Ok(renderer.render(&config))
|
||||||
|
}
|
||||||
|
}
|
||||||
123
apps/nxmesh-master/src/service/proxy/ssl_certificate/mod.rs
Normal file
123
apps/nxmesh-master/src/service/proxy/ssl_certificate/mod.rs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
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<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateSslCertificateParams {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub cert_path: Option<String>,
|
||||||
|
pub key_path: Option<String>,
|
||||||
|
pub expiry_date: Option<chrono::DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait SslCertificateService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<SslCertificateConfig>;
|
||||||
|
async fn list(&self) -> ProxyServiceResult<Vec<SslCertificateConfig>>;
|
||||||
|
async fn create(
|
||||||
|
&self,
|
||||||
|
params: CreateSslCertificateParams,
|
||||||
|
) -> ProxyServiceResult<SslCertificateConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateSslCertificateParams,
|
||||||
|
) -> ProxyServiceResult<SslCertificateConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<SslCertificateConfig> {
|
||||||
|
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<Vec<SslCertificateConfig>> {
|
||||||
|
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<SslCertificateConfig> {
|
||||||
|
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<SslCertificateConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::ssl_certificate::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
827
apps/nxmesh-master/src/service/proxy/types.rs
Normal file
827
apps/nxmesh-master/src/service/proxy/types.rs
Normal file
@@ -0,0 +1,827 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ProxyServiceError {
|
||||||
|
#[error("proxy config not found")]
|
||||||
|
ConfigNotFound,
|
||||||
|
#[error("invalid proxy config: {0}")]
|
||||||
|
InvalidConfig(String),
|
||||||
|
#[error("no renderer registered for this proxy type")]
|
||||||
|
RendererNotFound,
|
||||||
|
#[error("database error: {0}")]
|
||||||
|
DatabaseError(#[from] sea_orm::DbErr),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ProxyServiceResult<T> = Result<T, ProxyServiceError>;
|
||||||
|
|
||||||
|
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
|
||||||
|
pub enum ProxyType {
|
||||||
|
Nginx,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Mergeable<T> {
|
||||||
|
// merge with other, self overrides other
|
||||||
|
fn merge(&mut self, other: T);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct OverrideRef {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_override_vecs(
|
||||||
|
mut child: Vec<OverrideRef>,
|
||||||
|
parent: Vec<OverrideRef>,
|
||||||
|
) -> Vec<OverrideRef> {
|
||||||
|
let overridden_ids: std::collections::HashSet<uuid::Uuid> =
|
||||||
|
child.iter().filter_map(|r| r.override_of_id).collect();
|
||||||
|
for item in parent {
|
||||||
|
if !overridden_ids.contains(&item.id) {
|
||||||
|
child.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Overridable {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProxyConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub r#type: ProxyType,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub parent_config_id: Option<Vec<uuid::Uuid>>,
|
||||||
|
//
|
||||||
|
pub server_blocks: HashMap<uuid::Uuid, ServerBlockConfig>,
|
||||||
|
pub upstreams: HashMap<uuid::Uuid, UpstreamConfig>,
|
||||||
|
pub access_rules: HashMap<uuid::Uuid, AccessRuleConfig>,
|
||||||
|
pub cache_zones: HashMap<uuid::Uuid, CacheZoneConfig>,
|
||||||
|
pub limit_rules: HashMap<uuid::Uuid, LimitRuleConfig>,
|
||||||
|
pub limit_zones: HashMap<uuid::Uuid, LimitZoneConfig>,
|
||||||
|
pub location_blocks: HashMap<uuid::Uuid, LocationBlockConfig>,
|
||||||
|
pub log_settings: HashMap<uuid::Uuid, LogSettingConfig>,
|
||||||
|
pub proxy_settings: HashMap<uuid::Uuid, ProxySettingConfig>,
|
||||||
|
pub rewrite_rules: HashMap<uuid::Uuid, RewriteRuleConfig>,
|
||||||
|
pub ssl_certificates: HashMap<uuid::Uuid, SslCertificateConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mergeable<ProxyConfig> for ProxyConfig {
|
||||||
|
fn merge(&mut self, other: ProxyConfig) {
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
macro_rules! merge_overridable_field {
|
||||||
|
($field:ident) => {
|
||||||
|
let overridden: HashSet<uuid::Uuid> = self
|
||||||
|
.$field
|
||||||
|
.values()
|
||||||
|
.filter_map(|v| v.override_of_id())
|
||||||
|
.collect();
|
||||||
|
for (id, value) in other.$field {
|
||||||
|
if !overridden.contains(&id) && !self.$field.contains_key(&id) {
|
||||||
|
self.$field.insert(id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// server_blocks: merge matching entries (field-level), handle overrides
|
||||||
|
{
|
||||||
|
let overridden: HashSet<uuid::Uuid> = self
|
||||||
|
.server_blocks
|
||||||
|
.values()
|
||||||
|
.filter_map(|sb| sb.override_of_id)
|
||||||
|
.collect();
|
||||||
|
for (id, block) in other.server_blocks {
|
||||||
|
if let Some(self_block) = self.server_blocks.get_mut(&id) {
|
||||||
|
self_block.merge(block);
|
||||||
|
} else if !overridden.contains(&id) {
|
||||||
|
self.server_blocks.insert(id, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_overridable_field!(upstreams);
|
||||||
|
merge_overridable_field!(access_rules);
|
||||||
|
merge_overridable_field!(cache_zones);
|
||||||
|
merge_overridable_field!(limit_rules);
|
||||||
|
merge_overridable_field!(limit_zones);
|
||||||
|
merge_overridable_field!(location_blocks);
|
||||||
|
merge_overridable_field!(log_settings);
|
||||||
|
merge_overridable_field!(proxy_settings);
|
||||||
|
merge_overridable_field!(rewrite_rules);
|
||||||
|
|
||||||
|
// ssl_certificates: simple merge (no override_of_id)
|
||||||
|
for (id, cert) in other.ssl_certificates {
|
||||||
|
self.ssl_certificates.entry(id).or_insert(cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ServerBlockConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub server_name: Option<Vec<String>>,
|
||||||
|
pub listen_port: i32,
|
||||||
|
pub ssl_enabled: Option<bool>,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
//
|
||||||
|
pub access_rules: Vec<OverrideRef>,
|
||||||
|
pub location_blocks: Vec<OverrideRef>,
|
||||||
|
pub log_settings: Vec<OverrideRef>,
|
||||||
|
pub ssl_certificates: Vec<OverrideRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl
|
||||||
|
From<(
|
||||||
|
crate::db::entities::server_block::Model,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
)> for ServerBlockConfig
|
||||||
|
{
|
||||||
|
fn from(
|
||||||
|
(model, access_rules, location_blocks, log_settings, ssl_certificates): (
|
||||||
|
crate::db::entities::server_block::Model,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
),
|
||||||
|
) -> Self {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mergeable<ServerBlockConfig> for ServerBlockConfig {
|
||||||
|
fn merge(&mut self, other: ServerBlockConfig) {
|
||||||
|
// self (child) overrides other (parent): keep child's values, fill gaps from parent
|
||||||
|
if self.server_name.is_none() {
|
||||||
|
self.server_name = other.server_name;
|
||||||
|
}
|
||||||
|
// listen_port is non-optional, child always keeps its own
|
||||||
|
if self.ssl_enabled.is_none() {
|
||||||
|
self.ssl_enabled = other.ssl_enabled;
|
||||||
|
}
|
||||||
|
if self.override_of_id.is_none() {
|
||||||
|
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.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.ssl_certificates = merge_override_vecs(
|
||||||
|
std::mem::take(&mut self.ssl_certificates),
|
||||||
|
other.ssl_certificates,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpstreamConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub target_host: String,
|
||||||
|
pub target_port: i32,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
//
|
||||||
|
pub location_blocks: Vec<OverrideRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(crate::db::entities::upstream::Model, Vec<OverrideRef>)> for UpstreamConfig {
|
||||||
|
fn from(
|
||||||
|
(model, location_blocks): (crate::db::entities::upstream::Model, Vec<OverrideRef>),
|
||||||
|
) -> Self {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AccessRuleConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub r#type: String,
|
||||||
|
pub ip_cidr: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::access_rule::Model> for AccessRuleConfig {
|
||||||
|
fn from(model: crate::db::entities::access_rule::Model) -> Self {
|
||||||
|
AccessRuleConfig {
|
||||||
|
id: model.id,
|
||||||
|
r#type: model.r#type,
|
||||||
|
ip_cidr: model.ip_cidr,
|
||||||
|
description: model.description,
|
||||||
|
priority: model.priority,
|
||||||
|
override_of_id: model.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CacheZoneConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub size: String,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::cache_zone::Model> for CacheZoneConfig {
|
||||||
|
fn from(model: crate::db::entities::cache_zone::Model) -> Self {
|
||||||
|
CacheZoneConfig {
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
path: model.path,
|
||||||
|
size: model.size_limit,
|
||||||
|
override_of_id: model.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LimitRuleConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub location_id: uuid::Uuid,
|
||||||
|
pub zone_id: uuid::Uuid,
|
||||||
|
pub burst: Option<i32>,
|
||||||
|
pub nodelay: Option<bool>,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::limit_rule::Model> for LimitRuleConfig {
|
||||||
|
fn from(model: crate::db::entities::limit_rule::Model) -> Self {
|
||||||
|
LimitRuleConfig {
|
||||||
|
id: model.id,
|
||||||
|
location_id: model.location_id,
|
||||||
|
zone_id: model.zone_id,
|
||||||
|
burst: model.burst,
|
||||||
|
nodelay: model.nodelay,
|
||||||
|
is_deleted: model.is_deleted,
|
||||||
|
override_of_id: model.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LimitZoneConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub key: String,
|
||||||
|
pub rate: String,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::limit_zone::Model> for LimitZoneConfig {
|
||||||
|
fn from(model: crate::db::entities::limit_zone::Model) -> Self {
|
||||||
|
LimitZoneConfig {
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
key: model.key,
|
||||||
|
rate: model.rate,
|
||||||
|
override_of_id: model.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocationBlockConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub server_id: uuid::Uuid,
|
||||||
|
pub path_pattern: String,
|
||||||
|
pub proxy_pass_upstream_id: Option<uuid::Uuid>,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
//
|
||||||
|
pub access_rules: Vec<OverrideRef>,
|
||||||
|
pub limit_rules: Vec<OverrideRef>,
|
||||||
|
pub proxy_settings: Vec<OverrideRef>,
|
||||||
|
pub rewrite_rules: Vec<OverrideRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl
|
||||||
|
From<(
|
||||||
|
crate::db::entities::location_block::Model,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
)> for LocationBlockConfig
|
||||||
|
{
|
||||||
|
fn from(
|
||||||
|
(model, access_rules, limit_rules, proxy_settings, rewrite_rules): (
|
||||||
|
crate::db::entities::location_block::Model,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
Vec<OverrideRef>,
|
||||||
|
),
|
||||||
|
) -> Self {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LogSettingConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub access_log_path: Option<String>,
|
||||||
|
pub error_log_path: Option<String>,
|
||||||
|
pub log_level: Option<String>,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::log_setting::Model> for LogSettingConfig {
|
||||||
|
fn from(model: crate::db::entities::log_setting::Model) -> Self {
|
||||||
|
LogSettingConfig {
|
||||||
|
id: model.id,
|
||||||
|
access_log_path: model.access_log_path,
|
||||||
|
error_log_path: model.error_log_path,
|
||||||
|
log_level: model.log_level,
|
||||||
|
override_of_id: model.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SslCertificateConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub cert_path: String,
|
||||||
|
pub key_path: String,
|
||||||
|
pub expiry_date: chrono::DateTime<chrono::Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::ssl_certificate::Model> for SslCertificateConfig {
|
||||||
|
fn from(model: crate::db::entities::ssl_certificate::Model) -> Self {
|
||||||
|
SslCertificateConfig {
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
cert_path: model.cert_path,
|
||||||
|
key_path: model.key_path,
|
||||||
|
expiry_date: model.expiry_date.and_utc(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProxySettingConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub location_id: uuid::Uuid,
|
||||||
|
pub read_timeout: Option<i32>,
|
||||||
|
pub connect_timeout: Option<i32>,
|
||||||
|
pub buffer_size: Option<i32>,
|
||||||
|
pub cache_enabled: Option<bool>,
|
||||||
|
pub cache_zone: Option<uuid::Uuid>,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::proxy_setting::Model> for ProxySettingConfig {
|
||||||
|
fn from(model: crate::db::entities::proxy_setting::Model) -> Self {
|
||||||
|
ProxySettingConfig {
|
||||||
|
id: model.id,
|
||||||
|
location_id: model.location_id,
|
||||||
|
read_timeout: model.read_timeout,
|
||||||
|
connect_timeout: model.connect_timeout,
|
||||||
|
buffer_size: model.buffer_size,
|
||||||
|
cache_enabled: model.cache_enabled,
|
||||||
|
cache_zone: model.cache_zone,
|
||||||
|
override_of_id: model.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RewriteRuleConfig {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub location_id: uuid::Uuid,
|
||||||
|
pub pattern: String,
|
||||||
|
pub replacement: String,
|
||||||
|
pub flag: Option<String>,
|
||||||
|
pub priority: i32,
|
||||||
|
pub override_of_id: Option<uuid::Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::rewrite_rule::Model> for RewriteRuleConfig {
|
||||||
|
fn from(model: crate::db::entities::rewrite_rule::Model) -> Self {
|
||||||
|
RewriteRuleConfig {
|
||||||
|
id: model.id,
|
||||||
|
location_id: model.location_id,
|
||||||
|
pattern: model.pattern,
|
||||||
|
replacement: model.replacement,
|
||||||
|
flag: model.flag,
|
||||||
|
priority: model.priority,
|
||||||
|
override_of_id: model.override_of_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Overridable implementations ──
|
||||||
|
|
||||||
|
impl Overridable for ServerBlockConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for UpstreamConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for AccessRuleConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for CacheZoneConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for LimitRuleConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for LimitZoneConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for LocationBlockConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for LogSettingConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for ProxySettingConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Overridable for RewriteRuleConfig {
|
||||||
|
fn override_of_id(&self) -> Option<uuid::Uuid> {
|
||||||
|
self.override_of_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── CRUD types ──
|
||||||
|
|
||||||
|
pub struct ProxyConfigSummary {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub is_template: bool,
|
||||||
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::proxy_config::Model> for ProxyConfigSummary {
|
||||||
|
fn from(m: crate::db::entities::proxy_config::Model) -> Self {
|
||||||
|
Self {
|
||||||
|
id: m.id,
|
||||||
|
name: m.name,
|
||||||
|
description: m.description,
|
||||||
|
is_template: m.is_template,
|
||||||
|
created_at: m.created_at.and_utc(),
|
||||||
|
updated_at: m.updated_at.and_utc(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CreateProxyConfigParams {
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub is_template: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateProxyConfigParams {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub is_template: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Agent config binding ──
|
||||||
|
|
||||||
|
pub struct AgentConfigBinding {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
pub agent_id: Option<uuid::Uuid>,
|
||||||
|
pub group_id: Option<uuid::Uuid>,
|
||||||
|
pub config_id: uuid::Uuid,
|
||||||
|
pub is_active: bool,
|
||||||
|
pub applied_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::entities::agent_config_binding::Model> for AgentConfigBinding {
|
||||||
|
fn from(m: crate::db::entities::agent_config_binding::Model) -> Self {
|
||||||
|
Self {
|
||||||
|
id: m.id,
|
||||||
|
agent_id: m.agent_id,
|
||||||
|
group_id: m.group_id,
|
||||||
|
config_id: m.config_id,
|
||||||
|
is_active: m.is_active,
|
||||||
|
applied_at: m.applied_at.and_utc(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn make_id() -> uuid::Uuid {
|
||||||
|
uuid::Uuid::new_v4()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_merge_override_vecs_child_overrides_parent() {
|
||||||
|
let parent_id = make_id();
|
||||||
|
let child_id = make_id();
|
||||||
|
let child = vec![OverrideRef {
|
||||||
|
id: child_id,
|
||||||
|
override_of_id: Some(parent_id),
|
||||||
|
}];
|
||||||
|
let parent = vec![OverrideRef {
|
||||||
|
id: parent_id,
|
||||||
|
override_of_id: None,
|
||||||
|
}];
|
||||||
|
let result = merge_override_vecs(child, parent);
|
||||||
|
assert_eq!(result.len(), 1);
|
||||||
|
assert_eq!(result[0].id, child_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_merge_override_vecs_removes_overridden_parent() {
|
||||||
|
let parent_id = make_id();
|
||||||
|
let child_override = make_id();
|
||||||
|
let child = vec![OverrideRef {
|
||||||
|
id: child_override,
|
||||||
|
override_of_id: Some(parent_id),
|
||||||
|
}];
|
||||||
|
let parent = vec![OverrideRef {
|
||||||
|
id: parent_id,
|
||||||
|
override_of_id: None,
|
||||||
|
}];
|
||||||
|
let result = merge_override_vecs(child, parent);
|
||||||
|
assert_eq!(result.len(), 1);
|
||||||
|
assert_eq!(result[0].id, child_override);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_merge_override_vecs_empty_child() {
|
||||||
|
let parent = vec![OverrideRef {
|
||||||
|
id: make_id(),
|
||||||
|
override_of_id: None,
|
||||||
|
}];
|
||||||
|
let result = merge_override_vecs(vec![], parent.clone());
|
||||||
|
assert_eq!(result.len(), 1);
|
||||||
|
assert_eq!(result[0].id, parent[0].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_server_block_merge_child_overrides_parent() {
|
||||||
|
let id = make_id();
|
||||||
|
let mut child = ServerBlockConfig {
|
||||||
|
id,
|
||||||
|
server_name: Some(vec!["child.example.com".to_string()]),
|
||||||
|
listen_port: 443,
|
||||||
|
ssl_enabled: Some(true),
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
let parent = ServerBlockConfig {
|
||||||
|
id,
|
||||||
|
server_name: Some(vec!["parent.example.com".to_string()]),
|
||||||
|
listen_port: 80,
|
||||||
|
ssl_enabled: Some(false),
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
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.listen_port, 443);
|
||||||
|
assert_eq!(child.ssl_enabled, Some(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_server_block_merge_fills_from_parent() {
|
||||||
|
let id = make_id();
|
||||||
|
let mut child = ServerBlockConfig {
|
||||||
|
id,
|
||||||
|
server_name: None,
|
||||||
|
listen_port: 443,
|
||||||
|
ssl_enabled: None,
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
let parent = ServerBlockConfig {
|
||||||
|
id,
|
||||||
|
server_name: Some(vec!["parent.example.com".to_string()]),
|
||||||
|
listen_port: 80,
|
||||||
|
ssl_enabled: Some(false),
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
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.listen_port, 443); // non-optional: child keeps its own
|
||||||
|
assert_eq!(child.ssl_enabled, Some(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_proxy_config_merge_server_block_overrides() {
|
||||||
|
let sb_id = make_id();
|
||||||
|
let child_sb = ServerBlockConfig {
|
||||||
|
id: sb_id,
|
||||||
|
server_name: Some(vec!["child.example.com".to_string()]),
|
||||||
|
listen_port: 443,
|
||||||
|
ssl_enabled: Some(true),
|
||||||
|
override_of_id: Some(make_id()),
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
let parent_sb = ServerBlockConfig {
|
||||||
|
id: sb_id,
|
||||||
|
server_name: Some(vec!["parent.example.com".to_string()]),
|
||||||
|
listen_port: 80,
|
||||||
|
ssl_enabled: Some(false),
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
let parent_id = make_id();
|
||||||
|
let mut child_proxy = ProxyConfig {
|
||||||
|
id: parent_id,
|
||||||
|
name: "child".to_string(),
|
||||||
|
r#type: ProxyType::Nginx,
|
||||||
|
description: None,
|
||||||
|
parent_config_id: None,
|
||||||
|
server_blocks: HashMap::from([(child_sb.id, child_sb)]),
|
||||||
|
upstreams: HashMap::new(),
|
||||||
|
access_rules: HashMap::new(),
|
||||||
|
cache_zones: HashMap::new(),
|
||||||
|
limit_rules: HashMap::new(),
|
||||||
|
limit_zones: HashMap::new(),
|
||||||
|
location_blocks: HashMap::new(),
|
||||||
|
log_settings: HashMap::new(),
|
||||||
|
proxy_settings: HashMap::new(),
|
||||||
|
rewrite_rules: HashMap::new(),
|
||||||
|
ssl_certificates: HashMap::new(),
|
||||||
|
};
|
||||||
|
let parent_proxy = ProxyConfig {
|
||||||
|
id: make_id(),
|
||||||
|
name: "parent".to_string(),
|
||||||
|
r#type: ProxyType::Nginx,
|
||||||
|
description: None,
|
||||||
|
parent_config_id: None,
|
||||||
|
server_blocks: HashMap::from([(parent_sb.id, parent_sb)]),
|
||||||
|
upstreams: HashMap::new(),
|
||||||
|
access_rules: HashMap::new(),
|
||||||
|
cache_zones: HashMap::new(),
|
||||||
|
limit_rules: HashMap::new(),
|
||||||
|
limit_zones: HashMap::new(),
|
||||||
|
location_blocks: HashMap::new(),
|
||||||
|
log_settings: HashMap::new(),
|
||||||
|
proxy_settings: HashMap::new(),
|
||||||
|
rewrite_rules: HashMap::new(),
|
||||||
|
ssl_certificates: HashMap::new(),
|
||||||
|
};
|
||||||
|
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.listen_port, 443);
|
||||||
|
assert_eq!(merged_sb.ssl_enabled, Some(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_proxy_config_merge_adds_parent_server_block() {
|
||||||
|
let child_sb_id = make_id();
|
||||||
|
let parent_sb_id = make_id();
|
||||||
|
let child_sb = ServerBlockConfig {
|
||||||
|
id: child_sb_id,
|
||||||
|
server_name: Some(vec!["child.example.com".to_string()]),
|
||||||
|
listen_port: 443,
|
||||||
|
ssl_enabled: Some(true),
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
let parent_sb = ServerBlockConfig {
|
||||||
|
id: parent_sb_id,
|
||||||
|
server_name: Some(vec!["parent.example.com".to_string()]),
|
||||||
|
listen_port: 80,
|
||||||
|
ssl_enabled: None,
|
||||||
|
override_of_id: None,
|
||||||
|
access_rules: vec![],
|
||||||
|
location_blocks: vec![],
|
||||||
|
log_settings: vec![],
|
||||||
|
ssl_certificates: vec![],
|
||||||
|
};
|
||||||
|
let parent_id = make_id();
|
||||||
|
let mut child_proxy = ProxyConfig {
|
||||||
|
id: parent_id,
|
||||||
|
name: "child".to_string(),
|
||||||
|
r#type: ProxyType::Nginx,
|
||||||
|
description: None,
|
||||||
|
parent_config_id: None,
|
||||||
|
server_blocks: HashMap::from([(child_sb.id, child_sb)]),
|
||||||
|
upstreams: HashMap::new(),
|
||||||
|
access_rules: HashMap::new(),
|
||||||
|
cache_zones: HashMap::new(),
|
||||||
|
limit_rules: HashMap::new(),
|
||||||
|
limit_zones: HashMap::new(),
|
||||||
|
location_blocks: HashMap::new(),
|
||||||
|
log_settings: HashMap::new(),
|
||||||
|
proxy_settings: HashMap::new(),
|
||||||
|
rewrite_rules: HashMap::new(),
|
||||||
|
ssl_certificates: HashMap::new(),
|
||||||
|
};
|
||||||
|
let parent_proxy = ProxyConfig {
|
||||||
|
id: make_id(),
|
||||||
|
name: "parent".to_string(),
|
||||||
|
r#type: ProxyType::Nginx,
|
||||||
|
description: None,
|
||||||
|
parent_config_id: None,
|
||||||
|
server_blocks: HashMap::from([(parent_sb.id, parent_sb)]),
|
||||||
|
upstreams: HashMap::new(),
|
||||||
|
access_rules: HashMap::new(),
|
||||||
|
cache_zones: HashMap::new(),
|
||||||
|
limit_rules: HashMap::new(),
|
||||||
|
limit_zones: HashMap::new(),
|
||||||
|
location_blocks: HashMap::new(),
|
||||||
|
log_settings: HashMap::new(),
|
||||||
|
proxy_settings: HashMap::new(),
|
||||||
|
rewrite_rules: HashMap::new(),
|
||||||
|
ssl_certificates: HashMap::new(),
|
||||||
|
};
|
||||||
|
child_proxy.merge(parent_proxy);
|
||||||
|
assert_eq!(child_proxy.server_blocks.len(), 2);
|
||||||
|
// Both child and parent server blocks should be present
|
||||||
|
assert!(child_proxy.server_blocks.contains_key(&child_sb_id));
|
||||||
|
assert!(child_proxy.server_blocks.contains_key(&parent_sb_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
139
apps/nxmesh-master/src/service/proxy/upstream/mod.rs
Normal file
139
apps/nxmesh-master/src/service/proxy/upstream/mod.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
use sea_orm::{ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, prelude::*};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::service::proxy::types::{ProxyServiceError, ProxyServiceResult, UpstreamConfig};
|
||||||
|
|
||||||
|
pub struct CreateUpstreamParams {
|
||||||
|
pub config_id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub target_host: String,
|
||||||
|
pub target_port: i32,
|
||||||
|
pub metadata: Option<serde_json::Value>,
|
||||||
|
pub override_of_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateUpstreamParams {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub target_host: Option<String>,
|
||||||
|
pub target_port: Option<i32>,
|
||||||
|
pub metadata: Option<Option<serde_json::Value>>,
|
||||||
|
pub override_of_id: Option<Option<Uuid>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, mockall::automock)]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait UpstreamService: Send + Sync + 'static {
|
||||||
|
async fn get(&self, id: Uuid) -> ProxyServiceResult<UpstreamConfig>;
|
||||||
|
async fn list_by_config(&self, config_id: Uuid) -> ProxyServiceResult<Vec<UpstreamConfig>>;
|
||||||
|
async fn create(&self, params: CreateUpstreamParams) -> ProxyServiceResult<UpstreamConfig>;
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
id: Uuid,
|
||||||
|
params: UpdateUpstreamParams,
|
||||||
|
) -> ProxyServiceResult<UpstreamConfig>;
|
||||||
|
async fn delete(&self, id: Uuid) -> ProxyServiceResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<UpstreamConfig> {
|
||||||
|
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<Vec<UpstreamConfig>> {
|
||||||
|
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<UpstreamConfig> {
|
||||||
|
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<UpstreamConfig> {
|
||||||
|
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<bool> {
|
||||||
|
let result = crate::db::entities::upstream::Entity::delete_by_id(id)
|
||||||
|
.exec(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ pub use sea_orm_migration::prelude::*;
|
|||||||
|
|
||||||
mod m20260301_000001_create_agents;
|
mod m20260301_000001_create_agents;
|
||||||
mod m20260301_000002_create_public_key_revokaction;
|
mod m20260301_000002_create_public_key_revokaction;
|
||||||
|
mod m20260620_111325_create_proxy_tables;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ impl MigratorTrait for Migrator {
|
|||||||
vec![
|
vec![
|
||||||
Box::new(m20260301_000001_create_agents::Migration),
|
Box::new(m20260301_000001_create_agents::Migration),
|
||||||
Box::new(m20260301_000002_create_public_key_revokaction::Migration),
|
Box::new(m20260301_000002_create_public_key_revokaction::Migration),
|
||||||
|
Box::new(m20260620_111325_create_proxy_tables::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
902
crates/migration/src/m20260620_111325_create_proxy_tables.rs
Normal file
902
crates/migration/src/m20260620_111325_create_proxy_tables.rs
Normal file
@@ -0,0 +1,902 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(AgentGroup::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AgentGroup::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(AgentGroup::Name).string().not_null())
|
||||||
|
.col(ColumnDef::new(AgentGroup::Description).string())
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Agents::Table)
|
||||||
|
.add_column(ColumnDef::new(Agents::GroupId).uuid().null())
|
||||||
|
.add_foreign_key(
|
||||||
|
TableForeignKey::new()
|
||||||
|
.name("fk_agents_group_id")
|
||||||
|
.from_tbl(Agents::Table)
|
||||||
|
.to_tbl(AgentGroup::Table)
|
||||||
|
.from_col(Agents::GroupId)
|
||||||
|
.to_col(AgentGroup::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(ProxyConfig::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ProxyConfig::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(ProxyConfig::Name).string().not_null())
|
||||||
|
.col(ColumnDef::new(ProxyConfig::Description).string())
|
||||||
|
.col(ColumnDef::new(ProxyConfig::IsTemplate).boolean().not_null())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ProxyConfig::CreatedAt)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ProxyConfig::UpdatedAt)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(SSLCertificate::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(SSLCertificate::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(SSLCertificate::Name).string().not_null())
|
||||||
|
.col(ColumnDef::new(SSLCertificate::CertPath).string().not_null())
|
||||||
|
.col(ColumnDef::new(SSLCertificate::KeyPath).string().not_null())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(SSLCertificate::ExpiryDate)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(CacheZone::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(CacheZone::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(CacheZone::Name).string().not_null())
|
||||||
|
.col(ColumnDef::new(CacheZone::Path).string().not_null())
|
||||||
|
.col(ColumnDef::new(CacheZone::SizeLimit).string().not_null())
|
||||||
|
.col(ColumnDef::new(CacheZone::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_cache_zone_override_of")
|
||||||
|
.from_tbl(CacheZone::Table)
|
||||||
|
.to_tbl(CacheZone::Table)
|
||||||
|
.from_col(CacheZone::OverrideOfId)
|
||||||
|
.to_col(CacheZone::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(LimitZone::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(LimitZone::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(LimitZone::Name).string().not_null())
|
||||||
|
.col(ColumnDef::new(LimitZone::Key).string().not_null())
|
||||||
|
.col(ColumnDef::new(LimitZone::Rate).string().not_null())
|
||||||
|
.col(ColumnDef::new(LimitZone::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_limit_zone_override_of")
|
||||||
|
.from_tbl(LimitZone::Table)
|
||||||
|
.to_tbl(LimitZone::Table)
|
||||||
|
.from_col(LimitZone::OverrideOfId)
|
||||||
|
.to_col(LimitZone::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(AgentConfigBinding::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AgentConfigBinding::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(AgentConfigBinding::AgentId).uuid())
|
||||||
|
.col(ColumnDef::new(AgentConfigBinding::GroupId).uuid())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AgentConfigBinding::ConfigId)
|
||||||
|
.uuid()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AgentConfigBinding::IsActive)
|
||||||
|
.boolean()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AgentConfigBinding::AppliedAt)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_agent_config_binding_agent")
|
||||||
|
.from_tbl(AgentConfigBinding::Table)
|
||||||
|
.to_tbl(Agents::Table)
|
||||||
|
.from_col(AgentConfigBinding::AgentId)
|
||||||
|
.to_col(Agents::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_agent_config_binding_group")
|
||||||
|
.from_tbl(AgentConfigBinding::Table)
|
||||||
|
.to_tbl(AgentGroup::Table)
|
||||||
|
.from_col(AgentConfigBinding::GroupId)
|
||||||
|
.to_col(AgentGroup::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_agent_config_binding_config")
|
||||||
|
.from_tbl(AgentConfigBinding::Table)
|
||||||
|
.to_tbl(ProxyConfig::Table)
|
||||||
|
.from_col(AgentConfigBinding::ConfigId)
|
||||||
|
.to_col(ProxyConfig::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(ConfigInheritance::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ConfigInheritance::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ConfigInheritance::ChildConfigId)
|
||||||
|
.uuid()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ConfigInheritance::ParentConfigId)
|
||||||
|
.uuid()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(ConfigInheritance::Priority).integer())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ConfigInheritance::AppliedAt)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_config_inheritance_child")
|
||||||
|
.from_tbl(ConfigInheritance::Table)
|
||||||
|
.to_tbl(ProxyConfig::Table)
|
||||||
|
.from_col(ConfigInheritance::ChildConfigId)
|
||||||
|
.to_col(ProxyConfig::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_config_inheritance_parent")
|
||||||
|
.from_tbl(ConfigInheritance::Table)
|
||||||
|
.to_tbl(ProxyConfig::Table)
|
||||||
|
.from_col(ConfigInheritance::ParentConfigId)
|
||||||
|
.to_col(ProxyConfig::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(Upstream::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(ColumnDef::new(Upstream::Id).uuid().not_null().primary_key())
|
||||||
|
.col(ColumnDef::new(Upstream::ConfigId).uuid().not_null())
|
||||||
|
.col(ColumnDef::new(Upstream::Name).string().not_null())
|
||||||
|
.col(ColumnDef::new(Upstream::TargetHost).string().not_null())
|
||||||
|
.col(ColumnDef::new(Upstream::TargetPort).integer().not_null())
|
||||||
|
.col(ColumnDef::new(Upstream::Metadata).json_binary())
|
||||||
|
.col(ColumnDef::new(Upstream::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_upstream_config")
|
||||||
|
.from_tbl(Upstream::Table)
|
||||||
|
.to_tbl(ProxyConfig::Table)
|
||||||
|
.from_col(Upstream::ConfigId)
|
||||||
|
.to_col(ProxyConfig::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_upstream_override_of")
|
||||||
|
.from_tbl(Upstream::Table)
|
||||||
|
.to_tbl(Upstream::Table)
|
||||||
|
.from_col(Upstream::OverrideOfId)
|
||||||
|
.to_col(Upstream::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(ServerBlock::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ServerBlock::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(ServerBlock::ConfigId).uuid().not_null())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ServerBlock::ServerName)
|
||||||
|
.array(ColumnType::String(StringLen::None)),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(ServerBlock::ListenPort).integer().not_null())
|
||||||
|
.col(ColumnDef::new(ServerBlock::SslEnabled).boolean())
|
||||||
|
.col(ColumnDef::new(ServerBlock::SslCertId).uuid())
|
||||||
|
.col(ColumnDef::new(ServerBlock::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_server_block_config")
|
||||||
|
.from_tbl(ServerBlock::Table)
|
||||||
|
.to_tbl(ProxyConfig::Table)
|
||||||
|
.from_col(ServerBlock::ConfigId)
|
||||||
|
.to_col(ProxyConfig::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_server_block_ssl_cert")
|
||||||
|
.from_tbl(ServerBlock::Table)
|
||||||
|
.to_tbl(SSLCertificate::Table)
|
||||||
|
.from_col(ServerBlock::SslCertId)
|
||||||
|
.to_col(SSLCertificate::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_server_block_override_of")
|
||||||
|
.from_tbl(ServerBlock::Table)
|
||||||
|
.to_tbl(ServerBlock::Table)
|
||||||
|
.from_col(ServerBlock::OverrideOfId)
|
||||||
|
.to_col(ServerBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(LocationBlock::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(LocationBlock::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(LocationBlock::ServerId).uuid().not_null())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(LocationBlock::PathPattern)
|
||||||
|
.string()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(LocationBlock::ProxyPassUpstreamId).uuid())
|
||||||
|
.col(ColumnDef::new(LocationBlock::Metadata).json_binary())
|
||||||
|
.col(ColumnDef::new(LocationBlock::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_location_block_server")
|
||||||
|
.from_tbl(LocationBlock::Table)
|
||||||
|
.to_tbl(ServerBlock::Table)
|
||||||
|
.from_col(LocationBlock::ServerId)
|
||||||
|
.to_col(ServerBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_location_block_upstream")
|
||||||
|
.from_tbl(LocationBlock::Table)
|
||||||
|
.to_tbl(Upstream::Table)
|
||||||
|
.from_col(LocationBlock::ProxyPassUpstreamId)
|
||||||
|
.to_col(Upstream::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_location_block_override_of")
|
||||||
|
.from_tbl(LocationBlock::Table)
|
||||||
|
.to_tbl(LocationBlock::Table)
|
||||||
|
.from_col(LocationBlock::OverrideOfId)
|
||||||
|
.to_col(LocationBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(AccessRule::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AccessRule::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(AccessRule::ServerId).uuid())
|
||||||
|
.col(ColumnDef::new(AccessRule::LocationId).uuid())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AccessRule::Type)
|
||||||
|
.string()
|
||||||
|
.not_null()
|
||||||
|
.comment("allow or deny"),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AccessRule::IpCidr)
|
||||||
|
.string()
|
||||||
|
.not_null()
|
||||||
|
.comment("IP address or CIDR range. 0.0.0.0/0 means all IPs"),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(AccessRule::Description).string())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(AccessRule::Priority)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
.comment(
|
||||||
|
"Priority of the access rule. Lower number means higher priority.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(AccessRule::IsDeleted).boolean().not_null())
|
||||||
|
.col(ColumnDef::new(AccessRule::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_access_rule_server")
|
||||||
|
.from_tbl(AccessRule::Table)
|
||||||
|
.to_tbl(ServerBlock::Table)
|
||||||
|
.from_col(AccessRule::ServerId)
|
||||||
|
.to_col(ServerBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_access_rule_location")
|
||||||
|
.from_tbl(AccessRule::Table)
|
||||||
|
.to_tbl(LocationBlock::Table)
|
||||||
|
.from_col(AccessRule::LocationId)
|
||||||
|
.to_col(LocationBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_access_rule_override_of")
|
||||||
|
.from_tbl(AccessRule::Table)
|
||||||
|
.to_tbl(AccessRule::Table)
|
||||||
|
.from_col(AccessRule::OverrideOfId)
|
||||||
|
.to_col(AccessRule::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(RewriteRule::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(RewriteRule::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(RewriteRule::LocationId).uuid().not_null())
|
||||||
|
.col(ColumnDef::new(RewriteRule::Pattern).string().not_null())
|
||||||
|
.col(ColumnDef::new(RewriteRule::Replacement).string().not_null())
|
||||||
|
.col(ColumnDef::new(RewriteRule::Flag).string())
|
||||||
|
.col(ColumnDef::new(RewriteRule::Priority).integer().not_null())
|
||||||
|
.col(ColumnDef::new(RewriteRule::IsDeleted).boolean().not_null())
|
||||||
|
.col(ColumnDef::new(RewriteRule::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_rewrite_rule_location")
|
||||||
|
.from_tbl(RewriteRule::Table)
|
||||||
|
.to_tbl(LocationBlock::Table)
|
||||||
|
.from_col(RewriteRule::LocationId)
|
||||||
|
.to_col(LocationBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_rewrite_rule_override_of")
|
||||||
|
.from_tbl(RewriteRule::Table)
|
||||||
|
.to_tbl(RewriteRule::Table)
|
||||||
|
.from_col(RewriteRule::OverrideOfId)
|
||||||
|
.to_col(RewriteRule::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(ProxySetting::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(ProxySetting::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(ProxySetting::LocationId).uuid().not_null())
|
||||||
|
.col(ColumnDef::new(ProxySetting::ReadTimeout).integer())
|
||||||
|
.col(ColumnDef::new(ProxySetting::ConnectTimeout).integer())
|
||||||
|
.col(ColumnDef::new(ProxySetting::BufferSize).integer())
|
||||||
|
.col(ColumnDef::new(ProxySetting::CacheEnabled).boolean())
|
||||||
|
.col(ColumnDef::new(ProxySetting::CacheZone).uuid())
|
||||||
|
.col(ColumnDef::new(ProxySetting::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_proxy_setting_location")
|
||||||
|
.from_tbl(ProxySetting::Table)
|
||||||
|
.to_tbl(LocationBlock::Table)
|
||||||
|
.from_col(ProxySetting::LocationId)
|
||||||
|
.to_col(LocationBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_proxy_setting_cache_zone")
|
||||||
|
.from_tbl(ProxySetting::Table)
|
||||||
|
.to_tbl(CacheZone::Table)
|
||||||
|
.from_col(ProxySetting::CacheZone)
|
||||||
|
.to_col(CacheZone::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_proxy_setting_override_of")
|
||||||
|
.from_tbl(ProxySetting::Table)
|
||||||
|
.to_tbl(ProxySetting::Table)
|
||||||
|
.from_col(ProxySetting::OverrideOfId)
|
||||||
|
.to_col(ProxySetting::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(LimitRule::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(LimitRule::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(LimitRule::LocationId).uuid().not_null())
|
||||||
|
.col(ColumnDef::new(LimitRule::ZoneId).uuid().not_null())
|
||||||
|
.col(ColumnDef::new(LimitRule::Burst).integer())
|
||||||
|
.col(ColumnDef::new(LimitRule::Nodelay).boolean())
|
||||||
|
.col(ColumnDef::new(LimitRule::IsDeleted).boolean().not_null())
|
||||||
|
.col(ColumnDef::new(LimitRule::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_limit_rule_location")
|
||||||
|
.from_tbl(LimitRule::Table)
|
||||||
|
.to_tbl(LocationBlock::Table)
|
||||||
|
.from_col(LimitRule::LocationId)
|
||||||
|
.to_col(LocationBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_limit_rule_zone")
|
||||||
|
.from_tbl(LimitRule::Table)
|
||||||
|
.to_tbl(LimitZone::Table)
|
||||||
|
.from_col(LimitRule::ZoneId)
|
||||||
|
.to_col(LimitZone::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_limit_rule_override_of")
|
||||||
|
.from_tbl(LimitRule::Table)
|
||||||
|
.to_tbl(LimitRule::Table)
|
||||||
|
.from_col(LimitRule::OverrideOfId)
|
||||||
|
.to_col(LimitRule::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(LogSetting::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(LogSetting::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(LogSetting::ServerId).uuid().not_null())
|
||||||
|
.col(ColumnDef::new(LogSetting::AccessLogPath).string())
|
||||||
|
.col(ColumnDef::new(LogSetting::ErrorLogPath).string())
|
||||||
|
.col(ColumnDef::new(LogSetting::LogLevel).string())
|
||||||
|
.col(ColumnDef::new(LogSetting::OverrideOfId).uuid())
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_log_setting_server")
|
||||||
|
.from_tbl(LogSetting::Table)
|
||||||
|
.to_tbl(ServerBlock::Table)
|
||||||
|
.from_col(LogSetting::ServerId)
|
||||||
|
.to_col(ServerBlock::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.name("fk_log_setting_override_of")
|
||||||
|
.from_tbl(LogSetting::Table)
|
||||||
|
.to_tbl(LogSetting::Table)
|
||||||
|
.from_col(LogSetting::OverrideOfId)
|
||||||
|
.to_col(LogSetting::Id)
|
||||||
|
.on_delete(ForeignKeyAction::SetNull)
|
||||||
|
.on_update(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(LogSetting::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(LimitRule::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(ProxySetting::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(RewriteRule::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(AccessRule::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(LocationBlock::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(ServerBlock::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Upstream::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(ConfigInheritance::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(AgentConfigBinding::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(LimitZone::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(CacheZone::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(SSLCertificate::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(ProxyConfig::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Agents::Table)
|
||||||
|
.drop_foreign_key(Alias::new("fk_agents_group_id"))
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Agents::Table)
|
||||||
|
.drop_column(Agents::GroupId)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(AgentGroup::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum Agents {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
GroupId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum AgentGroup {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
Description,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum AgentConfigBinding {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
AgentId,
|
||||||
|
GroupId,
|
||||||
|
ConfigId,
|
||||||
|
IsActive,
|
||||||
|
AppliedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum ProxyConfig {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
Description,
|
||||||
|
IsTemplate,
|
||||||
|
CreatedAt,
|
||||||
|
UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum ConfigInheritance {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
ChildConfigId,
|
||||||
|
ParentConfigId,
|
||||||
|
Priority,
|
||||||
|
AppliedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum SSLCertificate {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
CertPath,
|
||||||
|
KeyPath,
|
||||||
|
ExpiryDate,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum CacheZone {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
Path,
|
||||||
|
SizeLimit,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum LimitZone {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
Key,
|
||||||
|
Rate,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum Upstream {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
ConfigId,
|
||||||
|
Name,
|
||||||
|
TargetHost,
|
||||||
|
TargetPort,
|
||||||
|
Metadata,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum ServerBlock {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
ConfigId,
|
||||||
|
ServerName,
|
||||||
|
ListenPort,
|
||||||
|
SslEnabled,
|
||||||
|
SslCertId,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum LocationBlock {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
ServerId,
|
||||||
|
PathPattern,
|
||||||
|
ProxyPassUpstreamId,
|
||||||
|
Metadata,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum AccessRule {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
ServerId,
|
||||||
|
LocationId,
|
||||||
|
Type,
|
||||||
|
IpCidr,
|
||||||
|
Priority,
|
||||||
|
Description,
|
||||||
|
IsDeleted,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum RewriteRule {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
LocationId,
|
||||||
|
Pattern,
|
||||||
|
Replacement,
|
||||||
|
Flag,
|
||||||
|
Priority,
|
||||||
|
IsDeleted,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum ProxySetting {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
LocationId,
|
||||||
|
ReadTimeout,
|
||||||
|
ConnectTimeout,
|
||||||
|
BufferSize,
|
||||||
|
CacheEnabled,
|
||||||
|
CacheZone,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum LimitRule {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
LocationId,
|
||||||
|
ZoneId,
|
||||||
|
Burst,
|
||||||
|
Nodelay,
|
||||||
|
IsDeleted,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum LogSetting {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
ServerId,
|
||||||
|
AccessLogPath,
|
||||||
|
ErrorLogPath,
|
||||||
|
LogLevel,
|
||||||
|
OverrideOfId,
|
||||||
|
}
|
||||||
@@ -12,114 +12,3 @@ pub use agent::*;
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
#[allow(ambiguous_glob_reexports)]
|
#[allow(ambiguous_glob_reexports)]
|
||||||
pub use tonic_async_interceptor::*;
|
pub use tonic_async_interceptor::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use prost::Message;
|
|
||||||
|
|
||||||
use crate::agent::{
|
|
||||||
AgentMessage, ConfigApplyStatus, ConfigStatus, DeploymentMode, Error, MasterMessage,
|
|
||||||
MetricType, RegistrationRequest, agent_message, master_message,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn agent_message_round_trip_with_registration_payload() {
|
|
||||||
let msg = AgentMessage {
|
|
||||||
agent_id: "agent-1".to_string(),
|
|
||||||
timestamp: 123,
|
|
||||||
payload: Some(agent_message::Payload::Registration(RegistrationRequest {
|
|
||||||
hostname: "node-1".to_string(),
|
|
||||||
ip_address: "127.0.0.1".to_string(),
|
|
||||||
version: "1.0.0".to_string(),
|
|
||||||
capabilities: vec!["reload".to_string(), "metrics".to_string()],
|
|
||||||
labels: std::collections::HashMap::from([
|
|
||||||
("region".to_string(), "dev".to_string()),
|
|
||||||
("tier".to_string(), "edge".to_string()),
|
|
||||||
]),
|
|
||||||
deployment_mode: DeploymentMode::Standalone as i32,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
let encoded = msg.encode_to_vec();
|
|
||||||
let decoded = AgentMessage::decode(encoded.as_slice());
|
|
||||||
assert!(decoded.is_ok());
|
|
||||||
let decoded = decoded.unwrap_or_else(|_| unreachable!());
|
|
||||||
|
|
||||||
assert_eq!(decoded.agent_id, "agent-1");
|
|
||||||
assert_eq!(decoded.timestamp, 123);
|
|
||||||
|
|
||||||
match decoded.payload {
|
|
||||||
Some(agent_message::Payload::Registration(payload)) => {
|
|
||||||
assert_eq!(payload.hostname, "node-1");
|
|
||||||
assert_eq!(payload.ip_address, "127.0.0.1");
|
|
||||||
assert_eq!(payload.version, "1.0.0");
|
|
||||||
assert_eq!(payload.capabilities.len(), 2);
|
|
||||||
assert_eq!(payload.labels.get("region"), Some(&"dev".to_string()));
|
|
||||||
assert_eq!(payload.deployment_mode, DeploymentMode::Standalone as i32);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn master_message_round_trip_with_error_payload() {
|
|
||||||
let msg = MasterMessage {
|
|
||||||
timestamp: 999,
|
|
||||||
payload: Some(master_message::Payload::Error(Error {
|
|
||||||
code: "E_CONFIG_INVALID".to_string(),
|
|
||||||
message: "invalid config".to_string(),
|
|
||||||
details: std::collections::HashMap::from([
|
|
||||||
("file".to_string(), "site.conf".to_string()),
|
|
||||||
("line".to_string(), "42".to_string()),
|
|
||||||
]),
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
let encoded = msg.encode_to_vec();
|
|
||||||
let decoded = MasterMessage::decode(encoded.as_slice());
|
|
||||||
assert!(decoded.is_ok());
|
|
||||||
let decoded = decoded.unwrap_or_else(|_| unreachable!());
|
|
||||||
|
|
||||||
assert_eq!(decoded.timestamp, 999);
|
|
||||||
match decoded.payload {
|
|
||||||
Some(master_message::Payload::Error(err)) => {
|
|
||||||
assert_eq!(err.code, "E_CONFIG_INVALID");
|
|
||||||
assert_eq!(err.message, "invalid config");
|
|
||||||
assert_eq!(err.details.get("line"), Some(&"42".to_string()));
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn enum_integer_mappings_are_stable() {
|
|
||||||
assert_eq!(DeploymentMode::Unspecified as i32, 0);
|
|
||||||
assert_eq!(DeploymentMode::DockerSidecar as i32, 1);
|
|
||||||
assert_eq!(DeploymentMode::KubernetesSidecar as i32, 2);
|
|
||||||
assert_eq!(DeploymentMode::Standalone as i32, 3);
|
|
||||||
|
|
||||||
assert_eq!(ConfigApplyStatus::Unspecified as i32, 0);
|
|
||||||
assert_eq!(ConfigApplyStatus::Pending as i32, 1);
|
|
||||||
assert_eq!(ConfigApplyStatus::Validating as i32, 2);
|
|
||||||
assert_eq!(ConfigApplyStatus::Applying as i32, 3);
|
|
||||||
assert_eq!(ConfigApplyStatus::Success as i32, 4);
|
|
||||||
assert_eq!(ConfigApplyStatus::Failed as i32, 5);
|
|
||||||
assert_eq!(ConfigApplyStatus::RolledBack as i32, 6);
|
|
||||||
|
|
||||||
assert_eq!(MetricType::Unspecified as i32, 0);
|
|
||||||
assert_eq!(MetricType::Gauge as i32, 1);
|
|
||||||
assert_eq!(MetricType::Counter as i32, 2);
|
|
||||||
assert_eq!(MetricType::Histogram as i32, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn config_status_defaults_are_proto3_zero_values() {
|
|
||||||
let status = ConfigStatus::default();
|
|
||||||
|
|
||||||
assert_eq!(status.config_id, "");
|
|
||||||
assert_eq!(status.version, 0);
|
|
||||||
assert_eq!(status.status, ConfigApplyStatus::Unspecified as i32);
|
|
||||||
assert_eq!(status.error_message, "");
|
|
||||||
assert_eq!(status.applied_at, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
2
justfile
2
justfile
@@ -54,7 +54,7 @@ dev-master *ARGS:
|
|||||||
|
|
||||||
dev-agent *ARGS:
|
dev-agent *ARGS:
|
||||||
@echo "🔧 Starting Rust agent..."
|
@echo "🔧 Starting Rust agent..."
|
||||||
cargo watch -w apps/nxmesh-agent -x 'run --bin nxmesh-agent -- {{ ARGS }}'
|
cargo watch -w apps/nxmesh-agent -x 'run --bin nxmesh-agent --release -- {{ ARGS }}'
|
||||||
|
|
||||||
# Start Vite frontend development server
|
# Start Vite frontend development server
|
||||||
dev-frontend:
|
dev-frontend:
|
||||||
|
|||||||
Reference in New Issue
Block a user