diff --git a/Cargo.lock b/Cargo.lock index e1520d58..a7225409 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1636,6 +1636,7 @@ dependencies = [ "http 1.1.0", "humantime", "humantime-serde", + "ipnet", "log", "memory-stats", "mime_guess", diff --git a/Cargo.toml b/Cargo.toml index 1710b8c9..a55d7eb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ hostname = "0.3.1" http = "1.1.0" humantime = "2.1.0" humantime-serde = "1.1.1" +ipnet = "2.9.0" log = "0.4.21" memory-stats = { version = "1.1.0", features = ["always_use_statm"] } mime_guess = "2.0.4" diff --git a/TODO.md b/TODO.md index 84791dad..9d3ce55a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,14 @@ # TODO -- [ ] request id proxy plugin -- [ ] allow deny ip proxy plugin - [ ] support etcd or other storage for config - [ ] better error handler - [ ] log rotate - [ ] tls cert auto update - [ ] support validate config before save(web) -- [ ] auto reload config and restart +- [x] allow none upstream for location +- [x] allow deny ip proxy plugin +- [x] auto reload config and restart +- [x] request id proxy plugin - [x] support plugin for proxy and response - [x] authentication for admin page - [x] custom error for pingora error diff --git a/benches/bench.rs b/benches/bench.rs index 5d8d2822..97664c9b 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -106,7 +106,7 @@ fn bench_location_filter(c: &mut Criterion) { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), path: Some("/api".to_string()), ..Default::default() }, @@ -123,7 +123,7 @@ fn bench_location_filter(c: &mut Criterion) { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), path: Some("~/api".to_string()), ..Default::default() }, @@ -139,7 +139,7 @@ fn bench_location_filter(c: &mut Criterion) { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), path: Some("=/api".to_string()), ..Default::default() }, @@ -170,7 +170,7 @@ fn bench_location_rewrite_path(c: &mut Criterion) { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), rewrite: Some("^/users/(.*)$ /$1".to_string()), ..Default::default() }, diff --git a/src/config/load.rs b/src/config/load.rs index ebdb3be5..bd68da05 100644 --- a/src/config/load.rs +++ b/src/config/load.rs @@ -76,6 +76,8 @@ pub enum ProxyPluginCategory { Directory, Mock, RequestId, + IpLimit, + KeyAuth, } #[derive(PartialEq, Debug, Default, Deserialize_repr, Clone, Copy, Serialize_repr)] @@ -148,7 +150,7 @@ impl UpstreamConf { #[derive(Debug, Default, Deserialize, Clone, Serialize)] pub struct LocationConf { - pub upstream: String, + pub upstream: Option, pub path: Option, pub host: Option, pub proxy_headers: Option>, @@ -181,9 +183,10 @@ impl LocationConf { }; validate(&self.proxy_headers)?; validate(&self.headers)?; - if !upstream_names.contains(&self.upstream) { + let upstream = self.upstream.clone().unwrap_or_default(); + if !upstream.is_empty() && !upstream_names.contains(&upstream) { return Err(Error::Invalid { - message: format!("{} upstream is not found(location:{name})", self.upstream), + message: format!("{upstream} upstream is not found(location:{name})"), }); } diff --git a/src/main.rs b/src/main.rs index 2322707e..88247978 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,12 +17,15 @@ use config::{PingapConf, ProxyPluginCategory, ProxyPluginConf}; use log::{error, info, Level}; use pingora::server; use pingora::server::configuration::Opt; +use pingora::services::background::background_service; use proxy::{Server, ServerConf}; use state::get_start_time; use std::error::Error; use std::io::Write; use std::sync::Arc; +use crate::state::AutoRestart; + mod config; mod http_extra; mod plugin; @@ -56,6 +59,9 @@ struct Args { /// Admin server adddr #[arg(long)] admin: Option, + /// Whether this server should try to auto restart + #[arg(long)] + autorestart: bool, } fn new_server_conf(args: &Args, conf: &PingapConf) -> server::configuration::ServerConf { @@ -162,6 +168,12 @@ fn run() -> Result<(), Box> { if let Some(log) = &args.log { new_args.push(format!("--log={log}")); } + if let Some(admin) = &args.admin { + new_args.push(format!("--admin={admin}")); + } + if args.autorestart { + new_args.push("--autorestart".to_string()); + } cmd.args = new_args; state::set_restart_process_command(cmd); } @@ -217,6 +229,10 @@ fn run() -> Result<(), Box> { my_server.add_services(services.bg_services); my_server.add_service(services.lb); } + if args.autorestart { + my_server.add_service(background_service("Auto Restart", AutoRestart {})); + } + info!("server is running"); let _ = get_start_time(); my_server.run_forever(); diff --git a/src/plugin/admin.rs b/src/plugin/admin.rs index 4a80144c..711dcb85 100644 --- a/src/plugin/admin.rs +++ b/src/plugin/admin.rs @@ -31,7 +31,7 @@ use bytesize::ByteSize; use hex::encode; use http::Method; use http::{header, HeaderValue, StatusCode}; -use log::error; +use log::{debug, error}; use memory_stats::memory_stats; use pingora::http::RequestHeader; use pingora::proxy::Session; @@ -93,6 +93,7 @@ pub struct AdminServe { } impl AdminServe { pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new admin server proxy plugin, {value}, {proxy_step:?}"); let arr: Vec<&str> = value.split(' ').collect(); let mut authorization = "".to_string(); if arr.len() >= 2 { diff --git a/src/plugin/compression.rs b/src/plugin/compression.rs index ae29a338..a89288d8 100644 --- a/src/plugin/compression.rs +++ b/src/plugin/compression.rs @@ -18,6 +18,7 @@ use crate::config::{ProxyPluginCategory, ProxyPluginStep}; use crate::state::State; use async_trait::async_trait; use http::HeaderValue; +use log::debug; use once_cell::sync::Lazy; use pingora::proxy::Session; @@ -35,6 +36,8 @@ pub struct Compression { impl Compression { pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new compresson proxy plugin, {value}, {proxy_step:?}"); + let mut levels: [u32; 3] = [0, 0, 0]; let mut support_compression = false; for (index, item) in value.split(' ').enumerate() { diff --git a/src/plugin/directory.rs b/src/plugin/directory.rs index d3453ab9..22365e36 100644 --- a/src/plugin/directory.rs +++ b/src/plugin/directory.rs @@ -19,7 +19,7 @@ use crate::util; use async_trait::async_trait; use bytes::Bytes; use http::{header, HeaderValue, StatusCode}; -use log::error; +use log::{debug, error}; use pingora::proxy::Session; use std::fs::Metadata; use std::os::unix::fs::MetadataExt; @@ -92,6 +92,7 @@ fn get_cacheable_and_headers_from_meta( impl Directory { /// Creates a new directory upstream, which will serve static file of directory. pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new serve static file proxy plugin, {value}, {proxy_step:?}"); let mut new_path = value.to_string(); let mut chunk_size = None; let mut max_age = None; diff --git a/src/plugin/ip_limit.rs b/src/plugin/ip_limit.rs new file mode 100644 index 00000000..32364b9d --- /dev/null +++ b/src/plugin/ip_limit.rs @@ -0,0 +1,100 @@ +// Copyright 2024 Tree xie. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::ProxyPlugin; +use super::Result; +use crate::config::ProxyPluginCategory; +use crate::config::ProxyPluginStep; +use crate::state::State; +use crate::util; +use async_trait::async_trait; +use ipnet::IpNet; +use log::debug; +use pingora::proxy::Session; +use std::net::IpAddr; +use std::str::FromStr; + +pub struct IpLimit { + proxy_step: ProxyPluginStep, + ip_net_list: Vec, + ip_list: Vec, + category: u8, +} + +impl IpLimit { + pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new ip limit proxy plugin, {value}, {proxy_step:?}"); + let arr: Vec<&str> = value.split(' ').collect(); + let ip = arr[0].trim().to_string(); + let mut category = 0; + if arr.len() >= 2 { + let v = arr[1].parse::().unwrap(); + if v > 0 { + category = v; + } + } + let mut ip_net_list = vec![]; + let mut ip_list = vec![]; + for item in ip.split(',') { + if let Ok(value) = IpNet::from_str(item) { + ip_net_list.push(value); + } else { + ip_list.push(item.to_string()); + } + } + Ok(Self { + proxy_step, + ip_list, + ip_net_list, + category, + }) + } +} + +#[async_trait] +impl ProxyPlugin for IpLimit { + #[inline] + fn step(&self) -> ProxyPluginStep { + self.proxy_step + } + #[inline] + fn category(&self) -> ProxyPluginCategory { + ProxyPluginCategory::IpLimit + } + #[inline] + async fn handle(&self, session: &mut Session, ctx: &mut State) -> pingora::Result { + let ip = if let Some(ip) = &ctx.client_ip { + ip.to_string() + } else { + let ip = util::get_client_ip(session); + ctx.client_ip = Some(ip.clone()); + ip + }; + + let found = if self.ip_list.contains(&ip) { + true + } else { + let addr = ip + .parse::() + .map_err(|err| util::new_internal_error(400, err.to_string()))?; + self.ip_net_list.iter().any(|item| item.contains(&addr)) + }; + // deny ip + let allow = if self.category > 0 { !found } else { found }; + if !allow { + return Err(util::new_internal_error(403, "Forbidden".to_string())); + } + return Ok(false); + } +} diff --git a/src/plugin/key_auth.rs b/src/plugin/key_auth.rs new file mode 100644 index 00000000..a43ee849 --- /dev/null +++ b/src/plugin/key_auth.rs @@ -0,0 +1,101 @@ +// Copyright 2024 Tree xie. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::ProxyPlugin; +use super::{Error, Result}; +use crate::config::ProxyPluginCategory; +use crate::config::ProxyPluginStep; +use crate::state::State; +use crate::util; +use async_trait::async_trait; +use http::HeaderName; +use log::debug; +use pingora::proxy::Session; +use std::str::FromStr; +use substring::Substring; + +pub struct KeyAuth { + category: u8, + proxy_step: ProxyPluginStep, + header_name: Option, + query_name: Option, + keys: Vec>, +} + +impl KeyAuth { + pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new key auth proxy plugin, {value}, {proxy_step:?}"); + let arr: Vec<&str> = value.split(' ').collect(); + if arr.len() != 2 { + return Err(Error::Invalid { + message: "Value for key auth is invalid".to_string(), + }); + } + let mut category = 0; + let mut query_name = None; + let mut header_name = None; + let name = arr[0]; + if name.starts_with('?') { + category = 1; + query_name = Some(name.substring(1, name.len()).to_string()); + } else { + header_name = Some(HeaderName::from_str(name).map_err(|e| Error::Invalid { + message: format!("invalid header name, {e}"), + })?); + } + + let keys = arr[1] + .split(',') + .map(|item| item.as_bytes().to_owned()) + .collect(); + + Ok(Self { + category, + keys, + proxy_step, + query_name, + header_name, + }) + } +} + +#[async_trait] +impl ProxyPlugin for KeyAuth { + #[inline] + fn step(&self) -> ProxyPluginStep { + self.proxy_step + } + #[inline] + fn category(&self) -> ProxyPluginCategory { + ProxyPluginCategory::KeyAuth + } + #[inline] + async fn handle(&self, session: &mut Session, _ctx: &mut State) -> pingora::Result { + let value = if self.category == 0 { + self.header_name + .as_ref() + .map(|v| session.get_header_bytes(v)) + } else { + self.query_name.as_ref().map(|name| { + util::get_query_value(session.req_header(), name) + .unwrap_or_default() + .as_bytes() + }) + }; + if value.is_none() || self.keys.contains(&value.unwrap().to_vec()) { + return Err(util::new_internal_error(401, "Key auth fail".to_string())); + } + Ok(false) + } +} diff --git a/src/plugin/limit.rs b/src/plugin/limit.rs index 6213f9ca..d120be21 100644 --- a/src/plugin/limit.rs +++ b/src/plugin/limit.rs @@ -18,6 +18,7 @@ use crate::config::{ProxyPluginCategory, ProxyPluginStep}; use crate::state::State; use crate::util; use async_trait::async_trait; +use log::debug; use pingora::proxy::Session; use pingora_limits::inflight::Inflight; use substring::Substring; @@ -40,6 +41,7 @@ pub struct Limiter { impl Limiter { pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new limit proxy plugin, {value}, {proxy_step:?}"); let (key, max) = value.split_once(' ').ok_or(Error::Invalid { message: value.to_string(), })?; diff --git a/src/plugin/mock.rs b/src/plugin/mock.rs index 6237af70..1ee09e6c 100644 --- a/src/plugin/mock.rs +++ b/src/plugin/mock.rs @@ -19,6 +19,7 @@ use crate::state::State; use async_trait::async_trait; use bytes::Bytes; use http::StatusCode; +use log::debug; use pingora::proxy::Session; use serde::{Deserialize, Serialize}; @@ -39,6 +40,7 @@ pub struct MockResponse { impl MockResponse { /// Creates a new mock response upstream, which will return a mock data. pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new mock proxy plugin, {value}, {proxy_step:?}"); let info: MockInfo = serde_json::from_str(value).map_err(|e| Error::Json { source: e })?; let mut resp = HttpResponse { diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 548d5a04..c74123b7 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -24,6 +24,8 @@ use std::num::ParseIntError; mod admin; mod compression; mod directory; +mod ip_limit; +mod key_auth; mod limit; mod mock; mod request_id; @@ -123,6 +125,14 @@ pub fn init_proxy_plugins(confs: Vec<(String, ProxyPluginConf)>) -> Result<()> { let r = request_id::RequestId::new(&conf.value, step)?; plguins.insert(name, Box::new(r)); } + ProxyPluginCategory::IpLimit => { + let l = ip_limit::IpLimit::new(&conf.value, step)?; + plguins.insert(name, Box::new(l)); + } + ProxyPluginCategory::KeyAuth => { + let k = key_auth::KeyAuth::new(&conf.value, step)?; + plguins.insert(name, Box::new(k)); + } }; } diff --git a/src/plugin/request_id.rs b/src/plugin/request_id.rs index b7f752ca..55681f7c 100644 --- a/src/plugin/request_id.rs +++ b/src/plugin/request_id.rs @@ -19,6 +19,7 @@ use crate::config::ProxyPluginStep; use crate::http_extra::HTTP_HEADER_NAME_X_REQUEST_ID; use crate::state::State; use async_trait::async_trait; +use log::debug; use nanoid::nanoid; use pingora::proxy::Session; use uuid::Uuid; @@ -31,6 +32,7 @@ pub struct RequestId { impl RequestId { pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new request id proxy plugin, {value}, {proxy_step:?}"); let arr: Vec<&str> = value.split(' ').collect(); let algorithm = arr[0].trim().to_string(); let mut size = 8; diff --git a/src/plugin/stats.rs b/src/plugin/stats.rs index 0ee7849e..908ce9ec 100644 --- a/src/plugin/stats.rs +++ b/src/plugin/stats.rs @@ -22,6 +22,7 @@ use async_trait::async_trait; use bytes::Bytes; use bytesize::ByteSize; use http::StatusCode; +use log::debug; use log::error; use memory_stats::memory_stats; use pingora::proxy::Session; @@ -42,6 +43,7 @@ pub struct Stats { impl Stats { pub fn new(value: &str, proxy_step: ProxyPluginStep) -> Result { + debug!("new stats proxy plugin, {value}, {proxy_step:?}"); Ok(Self { proxy_step, path: value.to_string(), diff --git a/src/proxy/location.rs b/src/proxy/location.rs index 3757f8ac..619b6e9d 100644 --- a/src/proxy/location.rs +++ b/src/proxy/location.rs @@ -106,14 +106,15 @@ impl Location { conf: &LocationConf, upstreams: Vec>, ) -> Result { - let up = if conf.upstream.is_empty() { + let upstream = conf.upstream.clone().unwrap_or_default(); + let up = if upstream.is_empty() { Arc::new(new_empty_upstream()) } else { upstreams .iter() - .find(|item| item.name == conf.upstream) + .find(|item| item.name == upstream) .ok_or(Error::Invalid { - message: format!("Upstream({}) not found", conf.upstream), + message: format!("Upstream({upstream}) not found"), })? .clone() }; @@ -136,7 +137,7 @@ impl Location { let path = conf.path.clone().unwrap_or_default(); Ok(Location { - upstream_name: conf.upstream.clone(), + upstream_name: upstream, path_selector: new_path_selector(&path)?, path, hosts, @@ -273,7 +274,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), ..Default::default() }, vec![upstream.clone()], @@ -286,7 +287,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), host: Some("test.com,pingap".to_string()), ..Default::default() }, @@ -301,7 +302,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), path: Some("~/users".to_string()), ..Default::default() }, @@ -316,7 +317,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), path: Some("~^/api".to_string()), ..Default::default() }, @@ -331,7 +332,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), path: Some("/api".to_string()), ..Default::default() }, @@ -346,7 +347,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), path: Some("=/api".to_string()), ..Default::default() }, @@ -375,7 +376,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), rewrite: Some("^/users/(.*)$ /$1".to_string()), ..Default::default() }, @@ -403,7 +404,7 @@ mod tests { let lo = Location::new( "", &LocationConf { - upstream: upstream_name.to_string(), + upstream: Some(upstream_name.to_string()), rewrite: Some("^/users/(.*)$ /$1".to_string()), proxy_headers: Some(vec!["Cache-Control: no-store".to_string()]), headers: Some(vec!["X-Response-Id: pig".to_string()]), diff --git a/src/proxy/logger.rs b/src/proxy/logger.rs index 17a0132e..83504fe0 100644 --- a/src/proxy/logger.rs +++ b/src/proxy/logger.rs @@ -50,6 +50,7 @@ pub enum TagCategory { Context, PayloadSize, PayloadSizeHuman, + RequestId, } #[derive(Debug, Clone)] @@ -209,6 +210,10 @@ impl From<&str> for Parser { category: TagCategory::PayloadSizeHuman, data: None, }), + "{request-id}" => tags.push(Tag { + category: TagCategory::RequestId, + data: None, + }), _ => { if let Some(tag) = format_extra_tag(key) { tags.push(tag); @@ -381,6 +386,11 @@ impl Parser { } } } + TagCategory::RequestId => { + if let Some(key) = &ctx.request_id { + buf.extend(key.as_bytes()); + } + } }; } diff --git a/src/proxy/server.rs b/src/proxy/server.rs index 904ddb42..d1ba1078 100644 --- a/src/proxy/server.rs +++ b/src/proxy/server.rs @@ -93,7 +93,9 @@ impl From for Vec { let mut filter_locations = vec![]; for item in locations.iter() { if valid_locations.contains(&item.0) { - valid_upstreams.push(item.1.upstream.clone()); + if item.1.upstream.is_some() { + valid_upstreams.push(item.1.upstream.clone().unwrap_or_default()); + } filter_locations.push(item.clone()) } } @@ -172,7 +174,8 @@ impl Server { let in_used_upstreams: Vec<_> = conf .locations .iter() - .map(|item| item.1.upstream.clone()) + .filter(|item| item.1.upstream.is_some()) + .map(|item| item.1.upstream.clone().unwrap_or_default()) .collect(); for item in conf.upstreams.iter() { // ignore not in used diff --git a/src/state/process.rs b/src/state/process.rs index 85e46fd8..0277c9ec 100644 --- a/src/state/process.rs +++ b/src/state/process.rs @@ -12,15 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::config::{get_config_hash, get_config_path, load_config}; +use async_trait::async_trait; use log::{error, info}; use once_cell::sync::Lazy; use once_cell::sync::OnceCell; +use pingora::server::ShutdownWatch; +use pingora::services::background::BackgroundService; use std::io; use std::path::PathBuf; use std::process; use std::process::Command; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use tokio::time::interval; static START_TIME: Lazy = Lazy::new(|| { SystemTime::now() @@ -60,6 +65,46 @@ impl RestartProcessCommand { } } +pub struct AutoRestart {} + +fn validate_restart() -> Result> { + let conf = load_config(&get_config_path(), false)?; + conf.validate()?; + if conf.hash().unwrap_or_default() != get_config_hash() { + return Ok(true); + } + Ok(false) +} + +#[async_trait] +impl BackgroundService for AutoRestart { + async fn start(&self, mut shutdown: ShutdownWatch) { + let mut period = interval(Duration::from_secs(60)); + loop { + tokio::select! { + _ = shutdown.changed() => { + break; + } + _ = period.tick() => { + match validate_restart() { + Ok(should_restart) => { + if should_restart { + if let Err(e) = restart() { + error!("Restart fail: {e}"); + } + break; + } + }, + Err(e) => { + error!("auto restart validate fail, {e}"); + } + } + } + } + } + } +} + static CMD: OnceCell = OnceCell::new(); pub fn set_restart_process_command(data: RestartProcessCommand) { diff --git a/web/src/components/form-editor.tsx b/web/src/components/form-editor.tsx index c2e0b729..46ab38ec 100644 --- a/web/src/components/form-editor.tsx +++ b/web/src/components/form-editor.tsx @@ -54,6 +54,9 @@ export enum ProxyPluginCategory { ADMIN = 3, DIRECTORY = 4, MOCK = 5, + REQUEST_ID = 6, + IP_LIMIT = 7, + KEY_AUTH = 8, } export function formatProxyPluginCategory(value: ProxyPluginCategory) { @@ -76,6 +79,15 @@ export function formatProxyPluginCategory(value: ProxyPluginCategory) { case ProxyPluginCategory.MOCK: { return "mock"; } + case ProxyPluginCategory.REQUEST_ID: { + return "requestId"; + } + case ProxyPluginCategory.IP_LIMIT: { + return "ipLimit"; + } + case ProxyPluginCategory.KEY_AUTH: { + return "keyAuth"; + } } } @@ -189,6 +201,42 @@ function FormProxyPluginField({ }); break; } + case ProxyPluginCategory.REQUEST_ID: { + arr.push(...value.split(padding)); + fields.push( + { + label: "The algorithm for genenrate id", + }, + { + label: "The length of id", + }, + ); + break; + } + case ProxyPluginCategory.IP_LIMIT: { + arr.push(...value.split(padding)); + fields.push( + { + label: "The ip list", + }, + { + label: "The limit mode, 0:allow, 1:deny", + }, + ); + break; + } + case ProxyPluginCategory.KEY_AUTH: { + arr.push(...value.split(padding)); + fields.push( + { + label: "The key name", + }, + { + label: "The key value list", + }, + ); + break; + } case ProxyPluginCategory.MOCK: { if (value) { try { diff --git a/web/src/pages/proxy-plugin-info.tsx b/web/src/pages/proxy-plugin-info.tsx index 56713b6a..2a2fce90 100644 --- a/web/src/pages/proxy-plugin-info.tsx +++ b/web/src/pages/proxy-plugin-info.tsx @@ -85,6 +85,21 @@ export default function ProxyPluginInfo() { option: 5, value: ProxyPluginCategory.MOCK, }, + { + label: "Request Id", + option: 6, + value: ProxyPluginCategory.REQUEST_ID, + }, + { + label: "Ip Limit", + option: 7, + value: ProxyPluginCategory.IP_LIMIT, + }, + { + label: "Key Auth", + option: 8, + value: ProxyPluginCategory.KEY_AUTH, + }, ], }, {