From 85c7f60ce196fc5cbac5467f6033dff8676733ef Mon Sep 17 00:00:00 2001 From: hugocasa Date: Fri, 24 Oct 2025 11:37:03 +0200 Subject: [PATCH] feat: support wildcards in http routes (#6927) --- backend/windmill-api/src/triggers/http/handler.rs | 8 +++++--- backend/windmill-api/src/triggers/http/mod.rs | 5 +++-- .../triggers/http/RouteEditorConfigSection.svelte | 10 ++++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/backend/windmill-api/src/triggers/http/handler.rs b/backend/windmill-api/src/triggers/http/handler.rs index a887dc32a7..5d8898f121 100644 --- a/backend/windmill-api/src/triggers/http/handler.rs +++ b/backend/windmill-api/src/triggers/http/handler.rs @@ -62,7 +62,9 @@ pub async fn increase_trigger_version(tx: &mut PgConnection) -> Result<()> { } pub fn generate_route_path_key(route_path: &str) -> String { - ROUTE_PATH_KEY_RE.replace_all(route_path, "/*").to_string() + ROUTE_PATH_KEY_RE + .replace_all(route_path, "${1}${2}key") + .to_string() } pub async fn route_path_key_exists( @@ -322,7 +324,7 @@ async fn check_if_route_exist( workspace_id: &str, trigger_path: Option<&str>, ) -> Result { - let route_path_key = ROUTE_PATH_KEY_RE.replace_all(&config.route_path, ":key"); + let route_path_key = generate_route_path_key(&config.route_path); let exists = route_path_key_exists( &route_path_key, @@ -340,7 +342,7 @@ async fn check_if_route_exist( )); } - Ok(route_path_key.into_owned()) + Ok(route_path_key) } pub struct HttpTrigger; diff --git a/backend/windmill-api/src/triggers/http/mod.rs b/backend/windmill-api/src/triggers/http/mod.rs index 2c5df24913..55844e0fb1 100644 --- a/backend/windmill-api/src/triggers/http/mod.rs +++ b/backend/windmill-api/src/triggers/http/mod.rs @@ -192,8 +192,9 @@ impl<'de> Deserialize<'de> for HttpConfigRequest { // Regex patterns for route validation lazy_static::lazy_static! { - static ref ROUTE_PATH_KEY_RE: regex::Regex = regex::Regex::new(r"/?:[-\w]+").unwrap(); - static ref VALID_ROUTE_PATH_RE: regex::Regex = regex::Regex::new(r"^:?[-\w]+(/:?[-\w]+)*$").unwrap(); + // Matches named params like :id or wildcards like :* or * + static ref ROUTE_PATH_KEY_RE: regex::Regex = regex::Regex::new(r"(/)?(:|\*)[-\w]+").unwrap(); + static ref VALID_ROUTE_PATH_RE: regex::Regex = regex::Regex::new(r"^(\*[-\w]+$|:?[-\w]+)(/(\*[-\w]+$|:?[-\w]+))*$").unwrap(); } #[derive(Deserialize)] diff --git a/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte b/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte index 94965d696c..8bf579c34e 100644 --- a/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte +++ b/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte @@ -54,7 +54,11 @@ clearTimeout(validateTimeout) } validateTimeout = setTimeout(async () => { - if (!routePath || !method || !/^:?[-\w]+(\/:?[-\w]+)*$/.test(routePath)) { + if ( + !routePath || + !method || + !/^(\*[-\w]+$|:?[-\w]+)(\/(\*[-\w]+$|:?[-\w]+))*$/.test(routePath) + ) { routeError = 'Endpoint not valid' } else if (await routeExists(routePath, method, workspaced_route)) { routeError = 'Endpoint already taken' @@ -140,7 +144,9 @@ bind:value={route_path} error={routeError !== ''} /> -
':myparam' for path params
+
+ Use ':myparam' for path params and '*mywildcard' for wildcards +