From f8b13be650faf3f91a569b6f120b1a8727978aed Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:21:40 +0800 Subject: [PATCH] feat: add Nginx upstream read management routes and handlers --- apps/api/src/routes/api/restricted.rs | 2 + apps/api/src/routes/api/restricted/nginx.rs | 11 ++ .../routes/api/restricted/nginx/upstream.rs | 14 ++ .../restricted/nginx/upstream/get_upstream.rs | 155 ++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 apps/api/src/routes/api/restricted/nginx.rs create mode 100644 apps/api/src/routes/api/restricted/nginx/upstream.rs create mode 100644 apps/api/src/routes/api/restricted/nginx/upstream/get_upstream.rs diff --git a/apps/api/src/routes/api/restricted.rs b/apps/api/src/routes/api/restricted.rs index 17be841..023d8f3 100644 --- a/apps/api/src/routes/api/restricted.rs +++ b/apps/api/src/routes/api/restricted.rs @@ -1,3 +1,4 @@ +pub mod nginx; pub mod user; use std::sync::Arc; @@ -9,6 +10,7 @@ use crate::{middlewares::require_auth::require_auth, routes::AppState}; pub fn get_restricted_router(state: Arc) -> Router { Router::new() .nest("/user", user::get_user_router(state.clone())) + .nest("/nginx", nginx::get_nginx_router(state.clone())) .layer(axum::middleware::from_fn_with_state( state.clone(), require_auth, diff --git a/apps/api/src/routes/api/restricted/nginx.rs b/apps/api/src/routes/api/restricted/nginx.rs new file mode 100644 index 0000000..9f66b9d --- /dev/null +++ b/apps/api/src/routes/api/restricted/nginx.rs @@ -0,0 +1,11 @@ +pub mod upstream; + +use std::sync::Arc; + +use axum::Router; + +use crate::routes::AppState; + +pub fn get_nginx_router(state: Arc) -> Router { + Router::new().nest("/upstream", upstream::get_upstream_router(state.clone())) +} diff --git a/apps/api/src/routes/api/restricted/nginx/upstream.rs b/apps/api/src/routes/api/restricted/nginx/upstream.rs new file mode 100644 index 0000000..1b1f797 --- /dev/null +++ b/apps/api/src/routes/api/restricted/nginx/upstream.rs @@ -0,0 +1,14 @@ +pub mod get_upstream; + +use std::sync::Arc; + +use axum::{Router, routing::get}; + +use crate::routes::AppState; + +pub fn get_upstream_router(state: Arc) -> Router { + Router::new() + .route("upstreams", get(get_upstream::get_upstream_list)) + .route("upstreams/{upstream_id}", get(get_upstream::get_upstream)) + .with_state(state) +} diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream.rs b/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream.rs new file mode 100644 index 0000000..6a475f7 --- /dev/null +++ b/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream.rs @@ -0,0 +1,155 @@ +use std::sync::Arc; + +use axum::{ + Json, + extract::{Path, Query, State}, + response::Result as AxumResult, +}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::{ + errors::service_error::ServiceError, + routes::{ + AppState, + api::helper::pagination::{ExtractPagination, PaginationInfo}, + }, + services::nginx::upstream::GetUpstreamOptions, +}; + +#[derive(Serialize, Deserialize, utoipa::ToSchema)] +pub struct GetUpstreamParams { + pub include_targets: Option, +} + +pub struct ConcreteGetUpstreamParams { + pub include_targets: bool, +} + +impl From for ConcreteGetUpstreamParams { + fn from(params: GetUpstreamParams) -> Self { + Self { + include_targets: params.include_targets.unwrap_or(false), + } + } +} + +#[derive(Serialize, Deserialize, utoipa::ToSchema)] +pub struct UpstreamTargetBasicInfo { + pub id: uuid::Uuid, + pub target_host: String, + pub target_port: i64, + pub enabled: bool, + pub is_backup: bool, + pub weight: i32, + // + pub created_at: DateTime, + pub updated_at: DateTime, +} + +impl From + for UpstreamTargetBasicInfo +{ + fn from(info: crate::services::nginx::info::upstream_target::UpstreamTargetInfo) -> Self { + Self { + id: info.id, + target_host: info.target_host, + target_port: info.target_port, + enabled: info.enabled, + is_backup: info.is_backup, + weight: info.weight as i32, + // + created_at: info.created_at, + updated_at: info.updated_at, + } + } +} + +#[derive(Serialize, Deserialize, utoipa::ToSchema)] +pub struct UpstreamInfoResponse { + pub id: uuid::Uuid, + pub name: String, + pub protocol: String, + pub algorithm: String, + pub sticky_session: bool, + pub created_by: Option, + pub created_at: DateTime, + pub updated_at: DateTime, + // + pub upstream_targets: Vec, +} + +impl From for UpstreamInfoResponse { + fn from(info: crate::services::nginx::info::upstream::UpstreamInfo) -> Self { + Self { + id: info.id, + name: info.name, + protocol: info.protocol, + algorithm: info.algorithm, + sticky_session: info.sticky_session, + created_by: info.created_by, + created_at: info.created_at, + updated_at: info.updated_at, + upstream_targets: info + .upstream_targets + .into_iter() + .map(|t| t.into()) + .collect(), + } + } +} + +#[derive(Serialize, Deserialize, utoipa::ToSchema)] +pub struct UpstreamListResponse { + pub items: Vec, + pub pagination: PaginationInfo, +} + +pub async fn get_upstream_list( + ExtractPagination(pagination): ExtractPagination, + State(state): State>, +) -> AxumResult, ServiceError> { + let upstream_service = &state.service.nginx.get_upstream_service(); + let upstreams = upstream_service + .get_upstreams(Some(pagination.clone().into()), None) + .await?; + + // + Ok(Json(UpstreamListResponse { + items: upstreams.into_iter().map(|u| u.into()).collect(), + pagination: PaginationInfo { + total_items: 0, + total_pages: 0, + current_page: pagination.page, + per_page: pagination.per_page, + }, + })) +} + +pub async fn get_upstream( + Path(upstream_id): Path, + Query(params): Query, + State(_state): State>, +) -> AxumResult, ServiceError> { + let concrete_params: ConcreteGetUpstreamParams = params.into(); + let upstream_service = &_state.service.nginx.get_upstream_service(); + let upstream_info = if concrete_params.include_targets { + upstream_service + .get_upstream( + upstream_id, + Some(GetUpstreamOptions { + include_targets: true, + }), + None, + ) + .await? + } else { + upstream_service + .get_upstream(upstream_id, None, None) + .await? + }; + + // + Ok(Json(upstream_info.into())) +}