From 75e2d99e44c836429e00c2201412cbad8c1f804f Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Fri, 5 Jun 2026 01:38:40 +0200 Subject: [PATCH] fix: gate per-user draft-only rows in listings on include_draft_only flag --- backend/windmill-api-flows/src/flows.rs | 17 ++++++++++++----- backend/windmill-api-scripts/src/scripts.rs | 18 ++++++++++++------ backend/windmill-api/src/apps.rs | 16 +++++++++++----- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/backend/windmill-api-flows/src/flows.rs b/backend/windmill-api-flows/src/flows.rs index 7bc40d83e5..5a2f09df2d 100644 --- a/backend/windmill-api-flows/src/flows.rs +++ b/backend/windmill-api-flows/src/flows.rs @@ -219,11 +219,18 @@ async fn list_flows( tx.commit().await?; // Draft-only rows: drafts the authed user has at paths with no - // deployed flow. Concatenated after the deployed page so the home - // page surfaces them too. Fields not in the draft JSON fall back to - // sensible defaults. Only included when no filters narrow the list - // and we're on page 0 — keeps pagination semantics clean. - if offset == 0 + // deployed flow. Gated on the same `include_draft_only` flag that + // controls deployed `draft_only` rows above so picker callers + // (workspace pickers, script selectors, ...) get the deployed + // listing only — the home page opts in explicitly. + // + // Concatenated after the deployed page so the home page surfaces + // them too. Fields not in the draft JSON fall back to sensible + // defaults. Skipped when filters narrow the list or we're past + // page 0 — keeps pagination semantics clean. + if lq.include_draft_only.unwrap_or(false) + && !authed.is_operator + && offset == 0 && lq.path_start.is_none() && lq.path_exact.is_none() && lq.edited_by.is_none() diff --git a/backend/windmill-api-scripts/src/scripts.rs b/backend/windmill-api-scripts/src/scripts.rs index 15de7441dc..c7faf5df40 100644 --- a/backend/windmill-api-scripts/src/scripts.rs +++ b/backend/windmill-api-scripts/src/scripts.rs @@ -329,12 +329,18 @@ async fn list_scripts( tx.commit().await?; // Draft-only rows: drafts the authed user has at paths with no - // deployed script. Concatenated after the deployed page so the home - // page surfaces them too. Fields not in the draft JSON fall back to - // sensible defaults (the editor can still open the draft from - // /scripts/edit/{path}). Only included when no other filters narrow - // the list — pagination beyond page 0 also skips them. - if offset == 0 + // deployed script. Gated on the same `include_draft_only` flag + // that controls deployed `draft_only` rows above so picker callers + // (workspace pickers, script selectors, ...) get the deployed + // listing only — the home page opts in explicitly. + // + // Concatenated after the deployed page so the home page surfaces + // them too. Fields not in the draft JSON fall back to sensible + // defaults. Skipped when filters narrow the list or we're past + // page 0 — keeps pagination semantics clean. + if lq.include_draft_only.unwrap_or(false) + && !authed.is_operator + && offset == 0 && lq.path_start.is_none() && lq.path_exact.is_none() && lq.created_by.is_none() diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index ad282a55fc..e5187afe5b 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -451,11 +451,17 @@ async fn list_apps( tx.commit().await?; // Draft-only rows: drafts (of either `app` or `raw_app` kind) the - // authed user has at paths with no deployed app. Concatenated after - // the deployed page so the home page surfaces them too. Fields not - // in the draft JSON fall back to defaults. Only included when no - // filters narrow the list and we're on page 0. - if offset == 0 + // authed user has at paths with no deployed app. Gated on the same + // `include_draft_only` flag that controls deployed `draft_only` + // rows above so picker callers get the deployed listing only — the + // home page opts in explicitly. + // + // Concatenated after the deployed page so the home page surfaces + // them too. Fields not in the draft JSON fall back to defaults. + // Skipped when filters narrow the list or we're past page 0. + if lq.include_draft_only.unwrap_or(false) + && !authed.is_operator + && offset == 0 && lq.path_start.is_none() && lq.path_exact.is_none() && lq.label.is_none()