mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 00:02:03 +00:00
e32662169a
* feat: add path name autocomplete with ghost text and folder cycling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: filter out archived/deleted/draft paths from autocomplete Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: show subfolders immediately after Tab-navigating into a folder Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: remove 2-char minimum for suggestions, hide placeholder when suggestions show Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: show LCP ghost text for multiple matches, Enter accepts it for Tab cycling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: suppress Path.svelte Enter dispatch when ghost text is accepted Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: compute LCP inline in Enter handler to avoid reactive timing issues Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Enter picks the first folder and navigates into it Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Enter picks the currently Tab-highlighted folder, not always the first Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: remove stray blank lines in applyCycleOrComplete Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: review fixes — $bindable default, openapi cache description, non-null assertion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: add sqlx query cache for path_autocomplete Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
87 lines
2.7 KiB
Rust
87 lines
2.7 KiB
Rust
/*
|
|
* Author: Ruben Fiszel
|
|
* Copyright: Windmill Labs, Inc 2026
|
|
* This file and its contents are licensed under the AGPLv3 License.
|
|
* Please see the included NOTICE for copyright information and
|
|
* LICENSE-AGPL for a copy of the license.
|
|
*/
|
|
|
|
use std::{
|
|
sync::{Arc, LazyLock},
|
|
time::{Duration, Instant},
|
|
};
|
|
|
|
use axum::{
|
|
extract::{Extension, Path},
|
|
routing::get,
|
|
Json, Router,
|
|
};
|
|
use serde::Serialize;
|
|
use windmill_common::error::JsonResult;
|
|
|
|
use crate::db::{ApiAuthed, DB};
|
|
|
|
// Per-table row cap is inlined into the SQL as `LIMIT 5000`.
|
|
// With 6 tables, the absolute ceiling is ~30k paths pre-dedup.
|
|
/// Final cap applied after dedup/sort.
|
|
const MAX_PATHS: usize = 20_000;
|
|
/// TTL for the per-workspace path list cache.
|
|
const CACHE_TTL: Duration = Duration::from_secs(60);
|
|
|
|
/// Workspace-wide path list cache keyed by workspace_id only.
|
|
/// One entry per workspace shared across all users — autocomplete is a
|
|
/// navigation hint, not an access gate. Saves memory and warms faster.
|
|
static PATHS_CACHE: LazyLock<quick_cache::sync::Cache<String, (Arc<Vec<String>>, Instant)>> =
|
|
LazyLock::new(|| quick_cache::sync::Cache::new(500));
|
|
|
|
pub fn workspaced_service() -> Router {
|
|
Router::new().route("/list_paths", get(list_paths))
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct ListPathsResponse {
|
|
paths: Arc<Vec<String>>,
|
|
}
|
|
|
|
async fn list_paths(
|
|
_authed: ApiAuthed,
|
|
Extension(db): Extension<DB>,
|
|
Path(w_id): Path<String>,
|
|
) -> JsonResult<ListPathsResponse> {
|
|
if let Some((cached, cached_at)) = PATHS_CACHE.get(&w_id) {
|
|
if cached_at.elapsed() < CACHE_TTL {
|
|
return Ok(Json(ListPathsResponse { paths: cached }));
|
|
}
|
|
PATHS_CACHE.remove(&w_id);
|
|
}
|
|
|
|
let mut paths: Vec<String> = sqlx::query_scalar!(
|
|
r#"
|
|
SELECT path AS "path!" FROM (
|
|
(SELECT DISTINCT path FROM script WHERE workspace_id = $1 AND archived = false AND deleted = false AND draft_only IS NOT true LIMIT 5000)
|
|
UNION
|
|
(SELECT path FROM flow WHERE workspace_id = $1 AND archived = false AND draft_only IS NOT true LIMIT 5000)
|
|
UNION
|
|
(SELECT path FROM app WHERE workspace_id = $1 LIMIT 5000)
|
|
UNION
|
|
(SELECT path FROM raw_app WHERE workspace_id = $1 LIMIT 5000)
|
|
UNION
|
|
(SELECT path FROM variable WHERE workspace_id = $1 LIMIT 5000)
|
|
UNION
|
|
(SELECT path FROM resource WHERE workspace_id = $1 LIMIT 5000)
|
|
) t
|
|
"#,
|
|
&w_id,
|
|
)
|
|
.fetch_all(&db)
|
|
.await?;
|
|
|
|
paths.sort_unstable();
|
|
paths.truncate(MAX_PATHS);
|
|
let paths = Arc::new(paths);
|
|
|
|
PATHS_CACHE.insert(w_id, (paths.clone(), Instant::now()));
|
|
|
|
Ok(Json(ListPathsResponse { paths }))
|
|
}
|