From d184261027d80f2a718cb8e963fa70e84b04d289 Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Wed, 31 Dec 2025 16:44:18 +0800 Subject: [PATCH] feat: added openapi doc --- apps/api/src/routes/api/openapi.rs | 27 +- .../nginx/upstream/create_upstream.rs | 20 +- .../nginx/upstream/create_upstream_target.rs | 18 +- .../restricted/nginx/upstream/get_upstream.rs | 20 + .../nginx/upstream/get_upstream_target.rs | 18 +- .../nginx/upstream/remove_upstream.rs | 16 +- .../nginx/upstream/remove_upstream_target.rs | 16 +- .../nginx/upstream/update_upstream.rs | 16 +- .../nginx/upstream/update_upstream_target.rs | 15 +- apps/api/swagger.json | 931 ++++++++++++++++++ .../app/generated/api-client/api-client.ts | 273 +++++ .../generated/api-client/tanstack-client.ts | 62 ++ 12 files changed, 1422 insertions(+), 10 deletions(-) diff --git a/apps/api/src/routes/api/openapi.rs b/apps/api/src/routes/api/openapi.rs index f37db25..cc855e3 100644 --- a/apps/api/src/routes/api/openapi.rs +++ b/apps/api/src/routes/api/openapi.rs @@ -3,6 +3,7 @@ pub mod tag { pub const HEALTH_TAG: &str = "Health"; pub const AUTH_TAG: &str = "Authentication"; pub const USER_TAG: &str = "User"; + pub const NGINX_TAG: &str = "Nginx"; } #[derive(utoipa::OpenApi)] @@ -14,6 +15,16 @@ pub mod tag { crate::routes::api::auth::init_admin::init_admin, // User management paths crate::routes::api::restricted::user::me::get_user_info, + // Nginx upstream management + crate::routes::api::restricted::nginx::upstream::create_upstream::create_upstream, + crate::routes::api::restricted::nginx::upstream::create_upstream_target::add_upstream_target, + crate::routes::api::restricted::nginx::upstream::get_upstream::get_upstream_list, + crate::routes::api::restricted::nginx::upstream::get_upstream::get_upstream, + crate::routes::api::restricted::nginx::upstream::get_upstream_target::get_upstream_target, + crate::routes::api::restricted::nginx::upstream::update_upstream::update_upstream, + crate::routes::api::restricted::nginx::upstream::update_upstream_target::update_upstream_target, + crate::routes::api::restricted::nginx::upstream::remove_upstream::remove_upstream, + crate::routes::api::restricted::nginx::upstream::remove_upstream_target::remove_upstream_target, ), components( schemas(crate::routes::api::health::info::HealthInfo), @@ -22,11 +33,25 @@ pub mod tag { schemas(crate::routes::api::auth::init_admin::AdminInitRequest), // User management schemas schemas(crate::routes::api::restricted::user::me::UserInfo), + // Nginx upstream schemas + schemas(crate::routes::api::restricted::nginx::upstream::create_upstream::CreateUpstreamRequestBody), + schemas(crate::routes::api::restricted::nginx::upstream::create_upstream_target::CreateUpstreamTargetInfo), + schemas(crate::routes::api::restricted::nginx::upstream::get_upstream::GetUpstreamParams), + schemas(crate::routes::api::restricted::nginx::upstream::get_upstream_target::GetUpstreamTargetsParams), + schemas(crate::routes::api::restricted::nginx::upstream::info::response::UpstreamTargetInfo), + schemas(crate::routes::api::restricted::nginx::upstream::info::response::UpstreamInfoResponse), + schemas(crate::routes::api::restricted::nginx::upstream::info::response::UpstreamListResponse), + schemas(crate::routes::api::restricted::nginx::upstream::info::response::UpstreamTargetInfoResponse), + schemas(crate::routes::api::restricted::nginx::upstream::update_upstream::UpdateUpstreamRequestBody), + schemas(crate::routes::api::restricted::nginx::upstream::update_upstream_target::UpdateUpstreamTargetRequestBody), + schemas(crate::routes::api::restricted::nginx::upstream::info::response::UpdateUpstreamInfoResponse), + schemas(crate::routes::api::restricted::nginx::upstream::info::response::UpdateUpstreamTargetInfoResponse), ), tags( (name = tag::HEALTH_TAG, description = "Health information API"), (name = tag::AUTH_TAG, description = "Authentication API"), - (name = tag::USER_TAG, description = "User management API") + (name = tag::USER_TAG, description = "User management API"), + (name = tag::NGINX_TAG, description = "Nginx management API") ) )] pub struct ApiDoc; diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream.rs b/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream.rs index 426cf57..0319076 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream.rs @@ -6,7 +6,13 @@ use sea_orm::TransactionTrait; use crate::{ errors::api_error::ApiError, middlewares::request_info::AuthenticatedRequestInfo, - routes::{AppState, api::restricted::nginx::upstream::info::response::UpstreamInfoResponse}, + routes::{ + AppState, + api::{ + openapi::tag::NGINX_TAG, + restricted::nginx::upstream::info::response::UpstreamInfoResponse, + }, + }, services::nginx::info::upstream::UpstreamCreateInfo, }; @@ -75,6 +81,18 @@ impl From for ConcreteCreateUpstreamRequestBody { } #[axum::debug_handler] +#[utoipa::path( + post, + path = "/api/upstreams", + request_body = CreateUpstreamRequestBody, + responses( + (status = 200, description = "Upstream created successfully", body = UpstreamInfoResponse), + (status = 401, description = "Unauthorized"), + (status = 422, description = "Invalid request"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn create_upstream( request_info: AuthenticatedRequestInfo, State(state): State>, diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream_target.rs b/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream_target.rs index c0b31df..9865058 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream_target.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/create_upstream_target.rs @@ -7,7 +7,11 @@ use crate::{ errors::api_error::ApiError, middlewares::request_info::AuthenticatedRequestInfo, routes::{ - AppState, api::restricted::nginx::upstream::info::response::UpstreamTargetInfoResponse, + AppState, + api::{ + openapi::tag::NGINX_TAG, + restricted::nginx::upstream::info::response::UpstreamTargetInfoResponse, + }, }, services::nginx::info::upstream_target::UpstreamTargetCreateInfo, }; @@ -45,6 +49,18 @@ impl From for ConcreteCreateUpstreamTargetInfo { } #[axum::debug_handler] +#[utoipa::path( + post, + path = "/api/upstreams/{upstream_id}/targets", + request_body = CreateUpstreamTargetInfo, + responses( + (status = 200, description = "Upstream target created successfully", body = UpstreamTargetInfoResponse), + (status = 401, description = "Unauthorized"), + (status = 422, description = "Invalid request"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn add_upstream_target( _request_info: AuthenticatedRequestInfo, State(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 index e1cdd95..0d16351 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream.rs @@ -14,6 +14,7 @@ use crate::{ AppState, api::{ helper::pagination::{ExtractPagination, PaginationInfo}, + openapi::tag::NGINX_TAG, restricted::nginx::upstream::info::response::{ UpstreamInfoResponse, UpstreamListResponse, }, @@ -39,6 +40,15 @@ impl From for ConcreteGetUpstreamParams { } } +#[utoipa::path( + get, + path = "/api/upstreams", + responses( + (status = 200, description = "List upstreams", body = UpstreamListResponse), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn get_upstream_list( ExtractPagination(pagination): ExtractPagination, State(state): State>, @@ -60,6 +70,16 @@ pub async fn get_upstream_list( })) } +#[utoipa::path( + get, + path = "/api/upstreams/{upstream_id}", + responses( + (status = 200, description = "Get upstream info", body = UpstreamInfoResponse), + (status = 404, description = "Not found"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn get_upstream( Path(upstream_id): Path, Query(params): Query, diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream_target.rs b/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream_target.rs index 07a4ac5..2e66d1b 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream_target.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/get_upstream_target.rs @@ -10,7 +10,13 @@ use uuid::Uuid; use crate::{ errors::api_error::ApiError, - routes::{AppState, api::restricted::nginx::upstream::info::response::UpstreamTargetInfo}, + routes::{ + AppState, + api::{ + openapi::tag::NGINX_TAG, + restricted::nginx::upstream::info::response::UpstreamTargetInfo, + }, + }, }; #[derive(Serialize, Deserialize, utoipa::ToSchema)] @@ -30,6 +36,16 @@ impl From for ConcreteGetUpstreamTargetsParams { } } +#[utoipa::path( + get, + path = "/api/upstream_targets/{upstream_target_id}", + responses( + (status = 200, description = "Get upstream target info", body = UpstreamTargetInfo), + (status = 404, description = "Not found"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn get_upstream_target( Path(upstream_target_id): Path, Query(params): Query, diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream.rs b/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream.rs index 25de995..656a24e 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream.rs @@ -9,10 +9,22 @@ use sea_orm::TransactionTrait; use uuid::Uuid; use crate::{ - errors::api_error::ApiError, middlewares::request_info::AuthenticatedRequestInfo, - routes::AppState, + errors::api_error::ApiError, + middlewares::request_info::AuthenticatedRequestInfo, + routes::{AppState, api::openapi::tag::NGINX_TAG}, }; +#[utoipa::path( + delete, + path = "/api/upstreams/{upstream_id}", + responses( + (status = 200, description = "Upstream removed successfully", body = ()), + (status = 401, description = "Unauthorized"), + (status = 404, description = "Not found"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn remove_upstream( _request_info: AuthenticatedRequestInfo, Path(upstream_id): Path, diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream_target.rs b/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream_target.rs index f6bf323..40b0266 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream_target.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/remove_upstream_target.rs @@ -9,10 +9,22 @@ use sea_orm::TransactionTrait; use uuid::Uuid; use crate::{ - errors::api_error::ApiError, middlewares::request_info::AuthenticatedRequestInfo, - routes::AppState, + errors::api_error::ApiError, + middlewares::request_info::AuthenticatedRequestInfo, + routes::{AppState, api::openapi::tag::NGINX_TAG}, }; +#[utoipa::path( + delete, + path = "/api/upstream_targets/{upstream_target_id}", + responses( + (status = 200, description = "Upstream target removed successfully", body = ()), + (status = 401, description = "Unauthorized"), + (status = 404, description = "Not found"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn remove_upstream_target( _request_info: AuthenticatedRequestInfo, Path(upstream_target_id): Path, diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream.rs b/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream.rs index 0f496cb..94b23af 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream.rs @@ -13,7 +13,8 @@ use crate::{ errors::api_error::ApiError, middlewares::request_info::AuthenticatedRequestInfo, routes::{ - AppState, api::restricted::nginx::upstream::info::response::UpdateUpstreamInfoResponse, + AppState, api::openapi::tag::NGINX_TAG, + api::restricted::nginx::upstream::info::response::UpdateUpstreamInfoResponse, }, services::nginx::info::upstream::UpdateUpstreamInfo, }; @@ -47,6 +48,19 @@ impl From for UpdateUpstreamInfo { } } +#[utoipa::path( + patch, + path = "/api/upstreams/{upstream_id}", + request_body = UpdateUpstreamRequestBody, + responses( + (status = 200, description = "Upstream updated successfully", body = UpdateUpstreamInfoResponse), + (status = 401, description = "Unauthorized"), + (status = 404, description = "Not found"), + (status = 422, description = "Invalid request"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn update_upstream( _request_info: AuthenticatedRequestInfo, Path(upstream_id): Path, diff --git a/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream_target.rs b/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream_target.rs index 0750475..30c1bb0 100644 --- a/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream_target.rs +++ b/apps/api/src/routes/api/restricted/nginx/upstream/update_upstream_target.rs @@ -13,7 +13,7 @@ use crate::{ errors::api_error::ApiError, middlewares::request_info::AuthenticatedRequestInfo, routes::{ - AppState, + AppState, api::openapi::tag::NGINX_TAG, api::restricted::nginx::upstream::info::response::UpdateUpstreamTargetInfoResponse, }, services::nginx::info::upstream_target::UpdateUpstreamTargetInfo, @@ -40,6 +40,19 @@ impl From for UpdateUpstreamTargetInfo { } } +#[utoipa::path( + patch, + path = "/api/upstream_targets/{upstream_target_id}", + request_body = UpdateUpstreamTargetRequestBody, + responses( + (status = 200, description = "Upstream target updated successfully", body = UpdateUpstreamTargetInfoResponse), + (status = 401, description = "Unauthorized"), + (status = 404, description = "Not found"), + (status = 422, description = "Invalid request"), + (status = 500, description = "Internal server error"), + ), + tag = NGINX_TAG, +)] pub async fn update_upstream_target( _request_info: AuthenticatedRequestInfo, Path(upstream_target_id): Path, diff --git a/apps/api/swagger.json b/apps/api/swagger.json index cf7fddc..b43e131 100644 --- a/apps/api/swagger.json +++ b/apps/api/swagger.json @@ -106,6 +106,357 @@ } } }, + "/api/upstream_targets/{upstream_target_id}": { + "get": { + "tags": [ + "Nginx" + ], + "operationId": "get_upstream_target", + "parameters": [ + { + "name": "upstream_target_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Get upstream target info", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamTargetInfo" + } + } + } + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + } + }, + "delete": { + "tags": [ + "Nginx" + ], + "operationId": "remove_upstream_target", + "parameters": [ + { + "name": "upstream_target_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Upstream target removed successfully", + "content": { + "application/json": { + "schema": { + "default": null + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + } + }, + "patch": { + "tags": [ + "Nginx" + ], + "operationId": "update_upstream_target", + "parameters": [ + { + "name": "upstream_target_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUpstreamTargetRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Upstream target updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUpstreamTargetInfoResponse" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Invalid request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/api/upstreams": { + "get": { + "tags": [ + "Nginx" + ], + "operationId": "get_upstream_list", + "responses": { + "200": { + "description": "List upstreams", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamListResponse" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + }, + "post": { + "tags": [ + "Nginx" + ], + "operationId": "create_upstream", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUpstreamRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Upstream created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamInfoResponse" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "422": { + "description": "Invalid request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/api/upstreams/{upstream_id}": { + "get": { + "tags": [ + "Nginx" + ], + "operationId": "get_upstream", + "parameters": [ + { + "name": "upstream_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Get upstream info", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamInfoResponse" + } + } + } + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + } + }, + "delete": { + "tags": [ + "Nginx" + ], + "operationId": "remove_upstream", + "parameters": [ + { + "name": "upstream_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Upstream removed successfully", + "content": { + "application/json": { + "schema": { + "default": null + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + } + }, + "patch": { + "tags": [ + "Nginx" + ], + "operationId": "update_upstream", + "parameters": [ + { + "name": "upstream_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUpstreamRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Upstream updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUpstreamInfoResponse" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Invalid request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/api/upstreams/{upstream_id}/targets": { + "post": { + "tags": [ + "Nginx" + ], + "operationId": "add_upstream_target", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUpstreamTargetInfo" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Upstream target created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamTargetInfoResponse" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "422": { + "description": "Invalid request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/api/user/me": { "get": { "tags": [ @@ -157,6 +508,102 @@ } } }, + "CreateUpstreamRequestBody": { + "type": "object", + "required": [ + "name", + "protocol", + "upstream_targets" + ], + "properties": { + "algorithm": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "protocol": { + "type": "string" + }, + "sticky_session": { + "type": [ + "boolean", + "null" + ] + }, + "upstream_targets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpstreamTargetInfo" + } + } + } + }, + "CreateUpstreamTargetInfo": { + "type": "object", + "required": [ + "upstream_id", + "host", + "port" + ], + "properties": { + "enabled": { + "type": [ + "boolean", + "null" + ] + }, + "host": { + "type": "string" + }, + "is_backup": { + "type": [ + "boolean", + "null" + ] + }, + "port": { + "type": "integer", + "format": "int64" + }, + "upstream_id": { + "type": "string", + "format": "uuid" + }, + "weight": { + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "GetUpstreamParams": { + "type": "object", + "properties": { + "include_targets": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "GetUpstreamTargetsParams": { + "type": "object", + "properties": { + "include_upstream": { + "type": [ + "boolean", + "null" + ] + } + } + }, "HealthInfo": { "type": "object", "description": "System health information", @@ -212,6 +659,486 @@ } } }, + "PaginationInfo": { + "type": "object", + "description": "Pagination information included in API responses", + "required": [ + "total_items", + "total_pages", + "current_page", + "per_page" + ], + "properties": { + "current_page": { + "type": "integer", + "format": "int32", + "description": "Current page number", + "minimum": 0 + }, + "per_page": { + "type": "integer", + "format": "int32", + "description": "Items per page", + "minimum": 0 + }, + "total_items": { + "type": "integer", + "format": "int64", + "description": "Total number of items", + "minimum": 0 + }, + "total_pages": { + "type": "integer", + "format": "int32", + "description": "Total number of pages", + "minimum": 0 + } + } + }, + "UpdateUpstreamInfoResponse": { + "type": "object", + "required": [ + "id", + "name", + "protocol", + "algorithm", + "sticky_session", + "created_at", + "updated_at", + "upstream_targets" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "created_by": { + "type": [ + "string", + "null" + ], + "format": "uuid" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "protocol": { + "type": "string" + }, + "sticky_session": { + "type": "boolean" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "upstream_targets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpstreamTargetBasicInfo" + } + } + } + }, + "UpdateUpstreamRequestBody": { + "type": "object", + "properties": { + "algorithm": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "protocol": { + "type": [ + "string", + "null" + ] + }, + "sticky_session": { + "type": [ + "boolean", + "null" + ] + }, + "upstream_targets": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/components/schemas/UpstreamTargetBasicUpdateInfo" + } + } + } + }, + "UpdateUpstreamTargetInfoResponse": { + "type": "object", + "required": [ + "id", + "host", + "port", + "enabled", + "is_backup", + "weight", + "created_at", + "updated_at", + "upstream_id" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "is_backup": { + "type": "boolean" + }, + "port": { + "type": "integer", + "format": "int64" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "upstream_id": { + "type": "string", + "format": "uuid" + }, + "weight": { + "type": "integer", + "format": "int32" + } + } + }, + "UpdateUpstreamTargetRequestBody": { + "type": "object", + "properties": { + "enabled": { + "type": [ + "boolean", + "null" + ] + }, + "host": { + "type": [ + "string", + "null" + ] + }, + "is_backup": { + "type": [ + "boolean", + "null" + ] + }, + "port": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "weight": { + "type": [ + "integer", + "null" + ], + "format": "int32" + } + } + }, + "UpstreamBasicInfo": { + "type": "object", + "required": [ + "id", + "name", + "protocol", + "created_at", + "updated_at" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "protocol": { + "type": "string" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, + "UpstreamInfoResponse": { + "type": "object", + "required": [ + "id", + "name", + "protocol", + "algorithm", + "sticky_session", + "created_at", + "updated_at", + "upstream_targets" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "created_by": { + "type": [ + "string", + "null" + ], + "format": "uuid" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "protocol": { + "type": "string" + }, + "sticky_session": { + "type": "boolean" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "upstream_targets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpstreamTargetBasicInfo" + } + } + } + }, + "UpstreamListResponse": { + "type": "object", + "required": [ + "items", + "pagination" + ], + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpstreamInfoResponse" + } + }, + "pagination": { + "$ref": "#/components/schemas/PaginationInfo" + } + } + }, + "UpstreamTargetBasicInfo": { + "type": "object", + "required": [ + "id", + "target_host", + "target_port", + "enabled", + "is_backup", + "weight", + "created_at", + "updated_at" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "is_backup": { + "type": "boolean" + }, + "target_host": { + "type": "string" + }, + "target_port": { + "type": "integer", + "format": "int64" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "weight": { + "type": "integer", + "format": "int32" + } + } + }, + "UpstreamTargetBasicUpdateInfo": { + "type": "object", + "required": [ + "id", + "enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "id": { + "type": "integer", + "format": "int64" + } + } + }, + "UpstreamTargetInfo": { + "type": "object", + "required": [ + "id", + "target_host", + "target_port", + "enabled", + "is_backup", + "weight", + "created_at", + "updated_at", + "upstream_id" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "is_backup": { + "type": "boolean" + }, + "target_host": { + "type": "string" + }, + "target_port": { + "type": "integer", + "format": "int64" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "upstream": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/UpstreamBasicInfo" + } + ] + }, + "upstream_id": { + "type": "string", + "format": "uuid" + }, + "weight": { + "type": "integer", + "format": "int32" + } + } + }, + "UpstreamTargetInfoResponse": { + "type": "object", + "required": [ + "id", + "host", + "port", + "enabled", + "is_backup", + "weight", + "created_at", + "updated_at", + "upstream_id" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "is_backup": { + "type": "boolean" + }, + "port": { + "type": "integer", + "format": "int64" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "upstream_id": { + "type": "string", + "format": "uuid" + }, + "weight": { + "type": "integer", + "format": "int32" + } + } + }, "UserInfo": { "type": "object", "description": "System health information", @@ -245,6 +1172,10 @@ { "name": "User", "description": "User management API" + }, + { + "name": "Nginx", + "description": "Nginx management API" } ] } \ No newline at end of file diff --git a/apps/frontend/app/generated/api-client/api-client.ts b/apps/frontend/app/generated/api-client/api-client.ts index 0b47c95..34dea83 100644 --- a/apps/frontend/app/generated/api-client/api-client.ts +++ b/apps/frontend/app/generated/api-client/api-client.ts @@ -1,6 +1,42 @@ export namespace Schemas { // export type AdminInitRequest = { password: string; setup_secret: string; username: string }; + export type UpstreamBasicInfo = { + created_at: string; + id: string; + name: string; + protocol: string; + updated_at: string; + }; + export type UpstreamTargetInfo = { + created_at: string; + enabled: boolean; + id: string; + is_backup: boolean; + target_host: string; + target_port: number; + updated_at: string; + upstream?: (null | UpstreamBasicInfo) | undefined; + upstream_id: string; + weight: number; + }; + export type CreateUpstreamRequestBody = { + algorithm?: (string | null) | undefined; + name: string; + protocol: string; + sticky_session?: (boolean | null) | undefined; + upstream_targets: Array; + }; + export type CreateUpstreamTargetInfo = { + enabled?: (boolean | null) | undefined; + host: string; + is_backup?: (boolean | null) | undefined; + port: number; + upstream_id: string; + weight?: (number | null) | undefined; + }; + export type GetUpstreamParams = Partial<{ include_targets: boolean | null }>; + export type GetUpstreamTargetsParams = Partial<{ include_upstream: boolean | null }>; export type HealthInfo = { errors?: (Array | null) | undefined; is_initialized: boolean; @@ -9,6 +45,77 @@ export namespace Schemas { version: string; }; export type LoginRequest = { password: string; username: string }; + export type PaginationInfo = { current_page: number; per_page: number; total_items: number; total_pages: number }; + export type UpstreamTargetBasicInfo = { + created_at: string; + enabled: boolean; + id: string; + is_backup: boolean; + target_host: string; + target_port: number; + updated_at: string; + weight: number; + }; + export type UpdateUpstreamInfoResponse = { + algorithm: string; + created_at: string; + created_by?: (string | null) | undefined; + id: string; + name: string; + protocol: string; + sticky_session: boolean; + updated_at: string; + upstream_targets: Array; + }; + export type UpdateUpstreamRequestBody = Partial<{ + algorithm: string | null; + name: string | null; + protocol: string | null; + sticky_session: boolean | null; + upstream_targets: Array | null; + }>; + export type UpdateUpstreamTargetInfoResponse = { + created_at: string; + enabled: boolean; + host: string; + id: string; + is_backup: boolean; + port: number; + updated_at: string; + upstream_id: string; + weight: number; + }; + export type UpdateUpstreamTargetRequestBody = Partial<{ + enabled: boolean | null; + host: string | null; + is_backup: boolean | null; + port: number | null; + weight: number | null; + }>; + export type UpstreamInfoResponse = { + algorithm: string; + created_at: string; + created_by?: (string | null) | undefined; + id: string; + name: string; + protocol: string; + sticky_session: boolean; + updated_at: string; + upstream_targets: Array; + }; + export type UpstreamListResponse = { items: Array; pagination: PaginationInfo }; + export type UpstreamTargetBasicUpdateInfo = { enabled: boolean; id: number }; + export type UpstreamTargetInfoResponse = { + created_at: string; + enabled: boolean; + host: string; + id: string; + is_backup: boolean; + port: number; + updated_at: string; + upstream_id: string; + weight: number; + }; export type UserInfo = { id: string; username: string }; // @@ -42,6 +149,95 @@ export namespace Endpoints { parameters: never; responses: { 200: Schemas.HealthInfo; 404: unknown }; }; + export type get_Get_upstream_target = { + method: "GET"; + path: "/api/upstream_targets/{upstream_target_id}"; + requestFormat: "json"; + parameters: { + path: { upstream_target_id: string }; + }; + responses: { 200: Schemas.UpstreamTargetInfo; 404: unknown; 500: unknown }; + }; + export type delete_Remove_upstream_target = { + method: "DELETE"; + path: "/api/upstream_targets/{upstream_target_id}"; + requestFormat: "json"; + parameters: { + path: { upstream_target_id: string }; + }; + responses: { 200: unknown; 401: unknown; 404: unknown; 500: unknown }; + }; + export type patch_Update_upstream_target = { + method: "PATCH"; + path: "/api/upstream_targets/{upstream_target_id}"; + requestFormat: "json"; + parameters: { + path: { upstream_target_id: string }; + + body: Schemas.UpdateUpstreamTargetRequestBody; + }; + responses: { + 200: Schemas.UpdateUpstreamTargetInfoResponse; + 401: unknown; + 404: unknown; + 422: unknown; + 500: unknown; + }; + }; + export type get_Get_upstream_list = { + method: "GET"; + path: "/api/upstreams"; + requestFormat: "json"; + parameters: never; + responses: { 200: Schemas.UpstreamListResponse; 500: unknown }; + }; + export type post_Create_upstream = { + method: "POST"; + path: "/api/upstreams"; + requestFormat: "json"; + parameters: { + body: Schemas.CreateUpstreamRequestBody; + }; + responses: { 200: Schemas.UpstreamInfoResponse; 401: unknown; 422: unknown; 500: unknown }; + }; + export type get_Get_upstream = { + method: "GET"; + path: "/api/upstreams/{upstream_id}"; + requestFormat: "json"; + parameters: { + path: { upstream_id: string }; + }; + responses: { 200: Schemas.UpstreamInfoResponse; 404: unknown; 500: unknown }; + }; + export type delete_Remove_upstream = { + method: "DELETE"; + path: "/api/upstreams/{upstream_id}"; + requestFormat: "json"; + parameters: { + path: { upstream_id: string }; + }; + responses: { 200: unknown; 401: unknown; 404: unknown; 500: unknown }; + }; + export type patch_Update_upstream = { + method: "PATCH"; + path: "/api/upstreams/{upstream_id}"; + requestFormat: "json"; + parameters: { + path: { upstream_id: string }; + + body: Schemas.UpdateUpstreamRequestBody; + }; + responses: { 200: Schemas.UpdateUpstreamInfoResponse; 401: unknown; 404: unknown; 422: unknown; 500: unknown }; + }; + export type post_Add_upstream_target = { + method: "POST"; + path: "/api/upstreams/{upstream_id}/targets"; + requestFormat: "json"; + parameters: { + body: Schemas.CreateUpstreamTargetInfo; + }; + responses: { 200: Schemas.UpstreamTargetInfoResponse; 401: unknown; 422: unknown; 500: unknown }; + }; export type get_Get_user_info = { method: "GET"; path: "/api/user/me"; @@ -58,11 +254,24 @@ export type EndpointByMethod = { post: { "/api/auth/init_admin": Endpoints.post_Init_admin; "/api/auth/login": Endpoints.post_Login; + "/api/upstreams": Endpoints.post_Create_upstream; + "/api/upstreams/{upstream_id}/targets": Endpoints.post_Add_upstream_target; }; get: { "/api/health/info": Endpoints.get_Get_health_info; + "/api/upstream_targets/{upstream_target_id}": Endpoints.get_Get_upstream_target; + "/api/upstreams": Endpoints.get_Get_upstream_list; + "/api/upstreams/{upstream_id}": Endpoints.get_Get_upstream; "/api/user/me": Endpoints.get_Get_user_info; }; + delete: { + "/api/upstream_targets/{upstream_target_id}": Endpoints.delete_Remove_upstream_target; + "/api/upstreams/{upstream_id}": Endpoints.delete_Remove_upstream; + }; + patch: { + "/api/upstream_targets/{upstream_target_id}": Endpoints.patch_Update_upstream_target; + "/api/upstreams/{upstream_id}": Endpoints.patch_Update_upstream; + }; }; // @@ -70,6 +279,8 @@ export type EndpointByMethod = { // export type PostEndpoints = EndpointByMethod["post"]; export type GetEndpoints = EndpointByMethod["get"]; +export type DeleteEndpoints = EndpointByMethod["delete"]; +export type PatchEndpoints = EndpointByMethod["patch"]; // // @@ -364,6 +575,68 @@ export class ApiClient { } // + // + delete( + path: Path, + ...params: MaybeOptionalArg< + TEndpoint extends { parameters: infer UParams } + ? NotNever extends true + ? UParams & { overrides?: RequestInit; withResponse?: false; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: false; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: false; throwOnStatusError?: boolean } + > + ): Promise, { data: {} }>["data"]>; + + delete( + path: Path, + ...params: MaybeOptionalArg< + TEndpoint extends { parameters: infer UParams } + ? NotNever extends true + ? UParams & { overrides?: RequestInit; withResponse?: true; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: true; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: true; throwOnStatusError?: boolean } + > + ): Promise>; + + delete( + path: Path, + ...params: MaybeOptionalArg + ): Promise { + return this.request("delete", path, ...params); + } + // + + // + patch( + path: Path, + ...params: MaybeOptionalArg< + TEndpoint extends { parameters: infer UParams } + ? NotNever extends true + ? UParams & { overrides?: RequestInit; withResponse?: false; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: false; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: false; throwOnStatusError?: boolean } + > + ): Promise, { data: {} }>["data"]>; + + patch( + path: Path, + ...params: MaybeOptionalArg< + TEndpoint extends { parameters: infer UParams } + ? NotNever extends true + ? UParams & { overrides?: RequestInit; withResponse?: true; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: true; throwOnStatusError?: boolean } + : { overrides?: RequestInit; withResponse?: true; throwOnStatusError?: boolean } + > + ): Promise>; + + patch( + path: Path, + ...params: MaybeOptionalArg + ): Promise { + return this.request("patch", path, ...params); + } + // + // /** * Generic request method with full type-safety for any endpoint diff --git a/apps/frontend/app/generated/api-client/tanstack-client.ts b/apps/frontend/app/generated/api-client/tanstack-client.ts index 0cd843e..5469fe8 100644 --- a/apps/frontend/app/generated/api-client/tanstack-client.ts +++ b/apps/frontend/app/generated/api-client/tanstack-client.ts @@ -43,6 +43,8 @@ const createQueryKey = ( // export type PostEndpoints = EndpointByMethod["post"]; export type GetEndpoints = EndpointByMethod["get"]; +export type DeleteEndpoints = EndpointByMethod["delete"]; +export type PatchEndpoints = EndpointByMethod["patch"]; // // @@ -130,6 +132,66 @@ export class TanstackQueryApiClient { } // + // + delete( + path: Path, + ...params: MaybeOptionalArg + ) { + const queryKey = createQueryKey(path as string, params[0]); + const query = { + /** type-only property if you need easy access to the endpoint params */ + "~endpoint": {} as TEndpoint, + queryKey, + queryFn: {} as "You need to pass .queryOptions to the useQuery hook", + queryOptions: queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const requestParams = { + ...(params[0] || {}), + ...(queryKey[0] || {}), + overrides: { signal }, + withResponse: false as const, + }; + const res = await this.client.delete(path, requestParams as never); + return res as InferResponseData; + }, + queryKey: queryKey, + }), + }; + + return query; + } + // + + // + patch( + path: Path, + ...params: MaybeOptionalArg + ) { + const queryKey = createQueryKey(path as string, params[0]); + const query = { + /** type-only property if you need easy access to the endpoint params */ + "~endpoint": {} as TEndpoint, + queryKey, + queryFn: {} as "You need to pass .queryOptions to the useQuery hook", + queryOptions: queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const requestParams = { + ...(params[0] || {}), + ...(queryKey[0] || {}), + overrides: { signal }, + withResponse: false as const, + }; + const res = await this.client.patch(path, requestParams as never); + return res as InferResponseData; + }, + queryKey: queryKey, + }), + }; + + return query; + } + // + // /** * Generic mutation method with full type-safety for any endpoint; it doesnt require parameters to be passed initially