From b4a36dbe4c187b05d92b64bb7ffa54ceda96d822 Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Mon, 12 Jan 2026 11:41:55 +0800 Subject: [PATCH] Fix: create location support for proxy pass --- .../nginx/proxy_host/create_location.rs | 92 ++++++++-- apps/api/swagger.json | 89 ++++++++- .../app/generated/api-client/api-client.ts | 173 ++++++++++++++++++ 3 files changed, 335 insertions(+), 19 deletions(-) diff --git a/apps/api/src/routes/api/restricted/nginx/proxy_host/create_location.rs b/apps/api/src/routes/api/restricted/nginx/proxy_host/create_location.rs index d804553..99a038e 100644 --- a/apps/api/src/routes/api/restricted/nginx/proxy_host/create_location.rs +++ b/apps/api/src/routes/api/restricted/nginx/proxy_host/create_location.rs @@ -17,11 +17,36 @@ use crate::{ }; #[derive(serde::Deserialize, utoipa::ToSchema, serde::Serialize)] -pub struct CreateLocationRequestBody { +#[serde(untagged)] +pub enum CreateLocationRequestBody { + // #[serde(rename = "upstream_id")] + UpstreamId(CreateLocationRequestBodyByUpstreamId), + // #[serde(rename = "proxy_pass")] + ProxyPass(CreateLocationRequestBodyByProxyPass), +} + +#[derive(serde::Deserialize, utoipa::ToSchema, serde::Serialize)] +pub struct CreateLocationRequestBodyByUpstreamId { pub path: String, pub match_type: String, pub order: i64, - pub upstream_id: Option, + pub upstream_id: uuid::Uuid, + pub preserve_host_header: Option, + pub allowed_methods: Option>, + pub custom_config: Option, +} + +#[derive(serde::Deserialize, utoipa::ToSchema, serde::Serialize)] +pub struct CreateLocationRequestBodyByProxyPass { + pub path: String, + pub match_type: String, + pub order: i64, + pub proxy_pass_protocol: String, + pub proxy_pass_host: String, + pub proxy_pass_port: i64, + pub preserve_host_header: Option, + pub allowed_methods: Option>, + pub custom_config: Option, } #[cfg(test)] @@ -37,8 +62,9 @@ mod tests { use crate::{ configs::{FromConfig, ProgramSettings}, middlewares::require_auth::mock::REQUEST_AUTH_USER_INVALID_HEADER, - routes::api::restricted::nginx::proxy_host::create_location::CreateLocationRequestBody, - routes::api::restricted::nginx::proxy_host::get_proxy_router, + routes::api::restricted::nginx::proxy_host::{ + create_location::CreateLocationRequestBodyByUpstreamId, get_proxy_router, + }, services::{agent_client::MockAgentService, get_mock_app_service}, }; @@ -117,11 +143,14 @@ mod tests { let router = get_router_with_state(db.clone()); let server = TestServer::new(router).expect("failed to create test server"); - let payload = CreateLocationRequestBody { + let payload = CreateLocationRequestBodyByUpstreamId { path: "/".to_string(), match_type: "prefix".to_string(), order: 1, - upstream_id: None, + upstream_id: up_id, + preserve_host_header: None, + allowed_methods: None, + custom_config: None, }; let res = server @@ -152,11 +181,14 @@ mod tests { let router = get_router_with_state(db.clone()); let server = TestServer::new(router).expect("failed to create test server"); - let payload = CreateLocationRequestBody { + let payload = CreateLocationRequestBodyByUpstreamId { path: "/".to_string(), match_type: "prefix".to_string(), order: 1, - upstream_id: None, + upstream_id: uuid::Uuid::new_v4(), + preserve_host_header: None, + allowed_methods: None, + custom_config: None, }; let res = server @@ -171,18 +203,46 @@ mod tests { impl From<(uuid::Uuid, CreateLocationRequestBody)> for CreateLocationInfo { fn from(val: (uuid::Uuid, CreateLocationRequestBody)) -> Self { + match val.1 { + CreateLocationRequestBody::UpstreamId(body) => Self::from((val.0, body)), + CreateLocationRequestBody::ProxyPass(body) => Self::from((val.0, body)), + } + } +} + +impl From<(uuid::Uuid, CreateLocationRequestBodyByUpstreamId)> for CreateLocationInfo { + fn from((proxy_id, payload): (uuid::Uuid, CreateLocationRequestBodyByUpstreamId)) -> Self { Self { - host_id: val.0, - path: val.1.path, - match_type: val.1.match_type, - order: val.1.order, - upstream_id: val.1.upstream_id, + host_id: proxy_id, + path: payload.path, + match_type: payload.match_type, + order: payload.order, + upstream_id: Some(payload.upstream_id), proxy_pass_protocol: None, proxy_pass_host: None, proxy_pass_port: None, - preserve_host_header: None, - allowed_methods: None, - custom_config: None, + preserve_host_header: payload.preserve_host_header, + allowed_methods: payload.allowed_methods, + custom_config: payload.custom_config, + enabled: true, + } + } +} + +impl From<(uuid::Uuid, CreateLocationRequestBodyByProxyPass)> for CreateLocationInfo { + fn from((proxy_id, payload): (uuid::Uuid, CreateLocationRequestBodyByProxyPass)) -> Self { + Self { + host_id: proxy_id, + path: payload.path, + match_type: payload.match_type, + order: payload.order, + upstream_id: None, + proxy_pass_protocol: Some(payload.proxy_pass_protocol), + proxy_pass_host: Some(payload.proxy_pass_host), + proxy_pass_port: Some(payload.proxy_pass_port), + preserve_host_header: payload.preserve_host_header, + allowed_methods: payload.allowed_methods, + custom_config: payload.custom_config, enabled: true, } } diff --git a/apps/api/swagger.json b/apps/api/swagger.json index fc81dd5..837e753 100644 --- a/apps/api/swagger.json +++ b/apps/api/swagger.json @@ -895,13 +895,41 @@ } }, "CreateLocationRequestBody": { + "oneOf": [ + { + "$ref": "#/components/schemas/CreateLocationRequestBodyByUpstreamId" + }, + { + "$ref": "#/components/schemas/CreateLocationRequestBodyByProxyPass" + } + ] + }, + "CreateLocationRequestBodyByProxyPass": { "type": "object", "required": [ "path", "match_type", - "order" + "order", + "proxy_pass_protocol", + "proxy_pass_host", + "proxy_pass_port" ], "properties": { + "allowed_methods": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "custom_config": { + "type": [ + "string", + "null" + ] + }, "match_type": { "type": "string" }, @@ -912,11 +940,66 @@ "path": { "type": "string" }, - "upstream_id": { + "preserve_host_header": { + "type": [ + "boolean", + "null" + ] + }, + "proxy_pass_host": { + "type": "string" + }, + "proxy_pass_port": { + "type": "integer", + "format": "int64" + }, + "proxy_pass_protocol": { + "type": "string" + } + } + }, + "CreateLocationRequestBodyByUpstreamId": { + "type": "object", + "required": [ + "path", + "match_type", + "order", + "upstream_id" + ], + "properties": { + "allowed_methods": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "custom_config": { "type": [ "string", "null" - ], + ] + }, + "match_type": { + "type": "string" + }, + "order": { + "type": "integer", + "format": "int64" + }, + "path": { + "type": "string" + }, + "preserve_host_header": { + "type": [ + "boolean", + "null" + ] + }, + "upstream_id": { + "type": "string", "format": "uuid" } } diff --git a/apps/frontend/app/generated/api-client/api-client.ts b/apps/frontend/app/generated/api-client/api-client.ts index 1d42ada..04ece1e 100644 --- a/apps/frontend/app/generated/api-client/api-client.ts +++ b/apps/frontend/app/generated/api-client/api-client.ts @@ -1,6 +1,48 @@ export namespace Schemas { // export type AdminInitRequest = { password: string; setup_secret: string; username: string }; + export type CreateLocationReq = { + match_type: string; + order: number; + path: string; + upstream_id?: (string | null) | undefined; + }; + export type CreateLocationRequestBodyByUpstreamId = { + allowed_methods?: (Array | null) | undefined; + custom_config?: (string | null) | undefined; + match_type: string; + order: number; + path: string; + preserve_host_header?: (boolean | null) | undefined; + upstream_id: string; + }; + export type CreateLocationRequestBodyByProxyPass = { + allowed_methods?: (Array | null) | undefined; + custom_config?: (string | null) | undefined; + match_type: string; + order: number; + path: string; + preserve_host_header?: (boolean | null) | undefined; + proxy_pass_host: string; + proxy_pass_port: number; + proxy_pass_protocol: string; + }; + export type CreateLocationRequestBody = CreateLocationRequestBodyByUpstreamId | CreateLocationRequestBodyByProxyPass; + export type CreateProxyRequestBody = { + default_upstream_id?: (string | null) | undefined; + domain: string; + enable_websocket: boolean; + enabled: boolean; + forward_host?: (string | null) | undefined; + forward_port?: (number | null) | undefined; + forward_scheme: string; + listen_port: number; + locations: Array; + meta?: unknown | undefined; + name?: (string | null) | undefined; + preserve_host_header: boolean; + scheme: string; + }; export type UpstreamBasicInfo = { created_at: string; id: string; @@ -44,8 +86,45 @@ export namespace Schemas { up_since: string; version: string; }; + export type LocationInfoResponse = { + created_at: string; + enabled: boolean; + host_id: string; + id: string; + match_type: string; + order: number; + path: string; + updated_at: string; + upstream_id?: (string | null) | undefined; + }; export type LoginRequest = { password: string; username: string }; export type PaginationInfo = { current_page: number; per_page: number; total_items: number; total_pages: number }; + export type ProxyHostUpstreamBasic = { id: string; name: string; protocol: string }; + export type ProxyHostInfoResponse = { + created_at: string; + domain: string; + enable_websocket: boolean; + enabled: boolean; + forward_host?: (string | null) | undefined; + forward_port?: (number | null) | undefined; + forward_scheme: string; + id: string; + listen_port: number; + locations: Array; + name?: (string | null) | undefined; + preserve_host_header: boolean; + scheme: string; + updated_at: string; + upstream?: (null | ProxyHostUpstreamBasic) | undefined; + }; + export type ProxyListResponse = { items: Array; pagination: PaginationInfo }; + export type UpdateLocationRequestBody = Partial<{ + match_type: string | null; + order: number | null; + path: string | null; + upstream_id: string | null; + }>; + export type UpdateProxyRequestBody = Partial<{ domain: string | null; name: string | null }>; export type UpstreamTargetBasicInfo = { created_at: string; enabled: boolean; @@ -149,6 +228,91 @@ export namespace Endpoints { parameters: never; responses: { 200: Schemas.HealthInfo; 404: unknown }; }; + export type get_Get_location = { + method: "GET"; + path: "/api/nginx/locations/{location_id}"; + requestFormat: "json"; + parameters: { + path: { location_id: string }; + }; + responses: { 200: Schemas.LocationInfoResponse; 404: unknown; 500: unknown }; + }; + export type delete_Remove_location = { + method: "DELETE"; + path: "/api/nginx/locations/{location_id}"; + requestFormat: "json"; + parameters: { + path: { location_id: string }; + }; + responses: { 200: unknown; 401: unknown; 404: unknown; 500: unknown }; + }; + export type patch_Update_location = { + method: "PATCH"; + path: "/api/nginx/locations/{location_id}"; + requestFormat: "json"; + parameters: { + path: { location_id: string }; + + body: Schemas.UpdateLocationRequestBody; + }; + responses: { 200: Schemas.LocationInfoResponse; 401: unknown; 404: unknown; 422: unknown; 500: unknown }; + }; + export type get_Get_proxy_list = { + method: "GET"; + path: "/api/nginx/proxy_hosts"; + requestFormat: "json"; + parameters: never; + responses: { 200: Schemas.ProxyListResponse; 500: unknown }; + }; + export type post_Create_proxy = { + method: "POST"; + path: "/api/nginx/proxy_hosts"; + requestFormat: "json"; + parameters: { + body: Schemas.CreateProxyRequestBody; + }; + responses: { 200: Schemas.ProxyHostInfoResponse; 401: unknown; 422: unknown; 500: unknown }; + }; + export type get_Get_proxy = { + method: "GET"; + path: "/api/nginx/proxy_hosts/{proxy_id}"; + requestFormat: "json"; + parameters: { + path: { proxy_id: string }; + }; + responses: { 200: Schemas.ProxyHostInfoResponse; 404: unknown; 500: unknown }; + }; + export type delete_Remove_proxy = { + method: "DELETE"; + path: "/api/nginx/proxy_hosts/{proxy_id}"; + requestFormat: "json"; + parameters: { + path: { proxy_id: string }; + }; + responses: { 200: unknown; 401: unknown; 404: unknown; 500: unknown }; + }; + export type patch_Update_proxy = { + method: "PATCH"; + path: "/api/nginx/proxy_hosts/{proxy_id}"; + requestFormat: "json"; + parameters: { + path: { proxy_id: string }; + + body: Schemas.UpdateProxyRequestBody; + }; + responses: { 200: Schemas.ProxyHostInfoResponse; 401: unknown; 422: unknown; 500: unknown }; + }; + export type post_Create_location = { + method: "POST"; + path: "/api/nginx/proxy_hosts/{proxy_id}/locations"; + requestFormat: "json"; + parameters: { + path: { proxy_id: string }; + + body: Schemas.CreateLocationRequestBody; + }; + responses: { 200: Schemas.LocationInfoResponse; 401: unknown; 422: unknown; 500: unknown }; + }; export type get_Get_upstream_target = { method: "GET"; path: "/api/nginx/upstream_targets/{upstream_target_id}"; @@ -254,21 +418,30 @@ export type EndpointByMethod = { post: { "/api/auth/init_admin": Endpoints.post_Init_admin; "/api/auth/login": Endpoints.post_Login; + "/api/nginx/proxy_hosts": Endpoints.post_Create_proxy; + "/api/nginx/proxy_hosts/{proxy_id}/locations": Endpoints.post_Create_location; "/api/nginx/upstreams": Endpoints.post_Create_upstream; "/api/nginx/upstreams/{upstream_id}/targets": Endpoints.post_Add_upstream_target; }; get: { "/api/health/info": Endpoints.get_Get_health_info; + "/api/nginx/locations/{location_id}": Endpoints.get_Get_location; + "/api/nginx/proxy_hosts": Endpoints.get_Get_proxy_list; + "/api/nginx/proxy_hosts/{proxy_id}": Endpoints.get_Get_proxy; "/api/nginx/upstream_targets/{upstream_target_id}": Endpoints.get_Get_upstream_target; "/api/nginx/upstreams": Endpoints.get_Get_upstream_list; "/api/nginx/upstreams/{upstream_id}": Endpoints.get_Get_upstream; "/api/user/me": Endpoints.get_Get_user_info; }; delete: { + "/api/nginx/locations/{location_id}": Endpoints.delete_Remove_location; + "/api/nginx/proxy_hosts/{proxy_id}": Endpoints.delete_Remove_proxy; "/api/nginx/upstream_targets/{upstream_target_id}": Endpoints.delete_Remove_upstream_target; "/api/nginx/upstreams/{upstream_id}": Endpoints.delete_Remove_upstream; }; patch: { + "/api/nginx/locations/{location_id}": Endpoints.patch_Update_location; + "/api/nginx/proxy_hosts/{proxy_id}": Endpoints.patch_Update_proxy; "/api/nginx/upstream_targets/{upstream_target_id}": Endpoints.patch_Update_upstream_target; "/api/nginx/upstreams/{upstream_id}": Endpoints.patch_Update_upstream; };