From 37307fee5959ba1915d857ed1ab358b287583afe Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:25:34 +0100 Subject: [PATCH 1/3] update nix flake (#7275) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 57fa428e72..b1dcc73104 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1753694789, - "narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=", + "lastModified": 1764517877, + "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "dc9637876d0dcc8c9e5e22986b857632effeb727", + "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", "type": "github" }, "original": { @@ -35,11 +35,11 @@ }, "nixpkgs-claude": { "locked": { - "lastModified": 1763421233, - "narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=", + "lastModified": 1764517877, + "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648", + "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", "type": "github" }, "original": { @@ -93,11 +93,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1753757591, - "narHash": "sha256-3okLvry8fRWZhJZP75pPC9P6U1dcu84VOCPhPLXYozI=", + "lastModified": 1764643237, + "narHash": "sha256-6Ezx9DqVv5UZ7DBK9rcNwBuQUENFyWPS7M09I+FvNao=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "b372cf71b4125d420d7648cbd898ab8f5c355be2", + "rev": "e66d6b924ac59e6c722f69332f6540ea57c69233", "type": "github" }, "original": { From 2a841bb0e319883174a1de49d47b1fb97423191c Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:26:58 +0100 Subject: [PATCH 2/3] fix: Prevent running git sync on promotion mode repos for forks (#7276) --- backend/ee-repo-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 0993363464..3d7c874ddb 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -2e6cce1bcb9deb750276ffbde4c7ca1f22e70c62 \ No newline at end of file +3fb9528570c5477a38bf2ec134ecb2b5b318b809 From 60d5ea857991b370c0049f7cff8cb4611a87620e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 2 Dec 2025 20:45:30 +0100 Subject: [PATCH 3/3] fix: get flow by id doesn't require path (#7278) --- ...19ffac7e82c985d59a2e20e5c616461dbfe7b.json | 24 ---- backend/windmill-api/openapi.yaml | 6 +- backend/windmill-api/src/flows.rs | 113 ++++++++++++++---- .../components/flows/FlowHistoryInner.svelte | 4 +- .../(root)/(logged)/flows/add/+page.svelte | 1 - 5 files changed, 92 insertions(+), 56 deletions(-) delete mode 100644 backend/.sqlx/query-79464d5ef46a05ff9c05a4f1f4419ffac7e82c985d59a2e20e5c616461dbfe7b.json diff --git a/backend/.sqlx/query-79464d5ef46a05ff9c05a4f1f4419ffac7e82c985d59a2e20e5c616461dbfe7b.json b/backend/.sqlx/query-79464d5ef46a05ff9c05a4f1f4419ffac7e82c985d59a2e20e5c616461dbfe7b.json deleted file mode 100644 index 1cdb56d8c0..0000000000 --- a/backend/.sqlx/query-79464d5ef46a05ff9c05a4f1f4419ffac7e82c985d59a2e20e5c616461dbfe7b.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT flow.path FROM flow\n LEFT JOIN flow_version\n ON flow_version.path = flow.path AND flow_version.workspace_id = flow.workspace_id\n WHERE flow.path = $1 AND flow.workspace_id = $2 AND flow_version.id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "79464d5ef46a05ff9c05a4f1f4419ffac7e82c985d59a2e20e5c616461dbfe7b" -} diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index c2dba6b9e6..16b9158989 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -6741,7 +6741,7 @@ paths: items: type: string - /w/{workspace}/flows/get/v/{version}/p/{path}: + /w/{workspace}/flows/get/v/{version}: get: summary: get flow version operationId: getFlowVersion @@ -6752,7 +6752,6 @@ paths: required: true schema: type: number - - $ref: "#/components/parameters/ScriptPath" tags: - flow responses: @@ -6763,7 +6762,7 @@ paths: schema: $ref: "#/components/schemas/Flow" - /w/{workspace}/flows/history_update/v/{version}/p/{path}: + /w/{workspace}/flows/history_update/v/{version}: post: summary: update flow history operationId: updateFlowHistory @@ -6774,7 +6773,6 @@ paths: required: true schema: type: number - - $ref: "#/components/parameters/ScriptPath" requestBody: description: Flow deployment message required: true diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 98130e2d2d..88d4f08fe9 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -69,10 +69,8 @@ pub fn workspaced_service() -> Router { "/list_paths_from_workspace_runnable/:runnable_kind/*path", get(list_paths_from_workspace_runnable), ) - .route( - "/history_update/v/:version/p/*path", - post(update_flow_history), - ) + .route("/history_update/v/:version", post(update_flow_history)) + .route("/get/v/:version", get(get_flow_version_by_id)) .route("/get/v/:version/p/*path", get(get_flow_version)) .route( "/toggle_workspace_error_handler/*path", @@ -709,6 +707,73 @@ async fn get_flow_version( Ok(Json(flow)) } +async fn get_flow_version_by_id( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, version)): Path<(String, i64)>, +) -> JsonResult { + let mut tx = user_db.begin(&authed).await?; + + // First, fetch the path to perform authorization check early + let path: Option = sqlx::query_scalar( + "SELECT path FROM flow_version WHERE id = $1 AND workspace_id = $2", + ) + .bind(version) + .bind(&w_id) + .fetch_optional(&mut *tx) + .await?; + + let path = not_found_if_none( + path, + "Flow version", + format!("{} in workspace {}", version, w_id), + )?; + + // Perform authorization check before fetching full data + check_scopes(&authed, || format!("flows:read:{}", path))?; + + // Now fetch the full flow data with INNER JOIN to ensure flow exists + let flow = sqlx::query_as::<_, Flow>( + "SELECT + flow.workspace_id, + flow.path, + flow.summary, + flow.description, + flow.archived, + flow.extra_perms, + flow.draft_only, + flow.dedicated_worker, + flow.tag, + flow.ws_error_handler_muted, + flow.timeout, + flow.visible_to_runner_only, + flow.on_behalf_of_email, + flow_version.schema, + flow_version.value, + flow_version.created_at as edited_at, + flow_version.created_by as edited_by + FROM flow + INNER JOIN flow_version + ON flow_version.path = flow.path + AND flow_version.workspace_id = flow.workspace_id + WHERE flow_version.id = $1 AND flow.workspace_id = $2", + ) + .bind(version) + .bind(&w_id) + .fetch_optional(&mut *tx) + .await?; + + tx.commit().await?; + + let flow = not_found_if_none( + flow, + "Flow", + format!("for version {} (flow may have been deleted)", version), + )?; + + Ok(Json(flow)) +} + #[derive(Deserialize)] pub struct FlowHistoryUpdate { pub deployment_msg: String, @@ -717,42 +782,42 @@ pub struct FlowHistoryUpdate { async fn update_flow_history( authed: ApiAuthed, Extension(user_db): Extension, - Path((w_id, version, path)): Path<(String, i64, StripPath)>, + Path((w_id, version)): Path<(String, i64)>, Json(history_update): Json, ) -> Result<()> { - let path = path.to_path(); - check_scopes(&authed, || format!("flows:write:{}", path))?; let mut tx = user_db.begin(&authed).await?; - let path_o = sqlx::query_scalar!( - "SELECT flow.path FROM flow - LEFT JOIN flow_version - ON flow_version.path = flow.path AND flow_version.workspace_id = flow.workspace_id - WHERE flow.path = $1 AND flow.workspace_id = $2 AND flow_version.id = $3", - path, - w_id, - version + + // Fetch path and perform authorization check early + let path: Option = sqlx::query_scalar( + "SELECT path FROM flow_version WHERE workspace_id = $1 AND id = $2", ) + .bind(&w_id) + .bind(version) .fetch_optional(&mut *tx) .await?; - if path_o.is_none() { - tx.commit().await?; - return Err(Error::NotFound( - format!("Flow version {version} for path {path} not found").to_string(), - )); - } + let path = not_found_if_none( + path, + "Flow version", + format!("{} in workspace {}", version, w_id), + )?; + // Perform authorization check before any modifications + check_scopes(&authed, || format!("flows:write:{}", path))?; + + // Insert or update deployment metadata sqlx::query!( "INSERT INTO deployment_metadata (workspace_id, path, flow_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg", - w_id, - path_o.unwrap(), + &w_id, + path, version, history_update.deployment_msg, ) .fetch_optional(&mut *tx) .await?; + tx.commit().await?; - return Ok(()); + Ok(()) } async fn update_flow( diff --git a/frontend/src/lib/components/flows/FlowHistoryInner.svelte b/frontend/src/lib/components/flows/FlowHistoryInner.svelte index 8084ad216c..c378105499 100644 --- a/frontend/src/lib/components/flows/FlowHistoryInner.svelte +++ b/frontend/src/lib/components/flows/FlowHistoryInner.svelte @@ -29,8 +29,7 @@ async function loadFlow(version: number) { selected = await FlowService.getFlowVersion({ workspace: $workspaceStore!, - version, - path + version }) } @@ -54,7 +53,6 @@ await FlowService.updateFlowHistory({ workspace: $workspaceStore!, version, - path, requestBody: { deployment_msg: deploymentMsgUpdate! } diff --git a/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte b/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte index af9d000561..7e33983040 100644 --- a/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte @@ -113,7 +113,6 @@ if (templateId) { template = await FlowService.getFlowVersion({ workspace: $workspaceStore!, - path: templatePath, version: parseInt(templateId) }) } else {