diff --git a/.gitignore b/.gitignore index 27885185f1..3e6c2f3a75 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ CaddyfileRemoteMalo *.swp **/.idea/ .direnv -.vscode \ No newline at end of file +.vscode diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc672fa06..5f664e6685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## [1.456.0](https://github.com/windmill-labs/windmill/compare/v1.455.2...v1.456.0) (2025-02-01) + + +### Features + +* flow history picker for flow status + load last flow state ([611d5e8](https://github.com/windmill-labs/windmill/commit/611d5e8bf3d64106c3e9698687d2ec3710988517)) + + +### Bug Fixes + +* only start smtp servers if email domain is set ([6d9edc8](https://github.com/windmill-labs/windmill/commit/6d9edc8c2b01c99339c156dc84def13b0a2205a6)) + +## [1.455.2](https://github.com/windmill-labs/windmill/compare/v1.455.1...v1.455.2) (2025-01-31) + + +### Bug Fixes + +* fix worker restart on otel setting set from undefined to null ([#5183](https://github.com/windmill-labs/windmill/issues/5183)) ([940fb03](https://github.com/windmill-labs/windmill/commit/940fb030232ea7944db94bfdf8b30a96f30baa21)) +* improve autoscaling num workers inc ([2638dfc](https://github.com/windmill-labs/windmill/commit/2638dfcf00522af733c49b2c02a78ec4028623e0)) +* improve history and saved inputs rever mechanism ([0b0e564](https://github.com/windmill-labs/windmill/commit/0b0e5640d8fa9682ad0a4349c863b743321be1f4)) + ## [1.455.1](https://github.com/windmill-labs/windmill/compare/v1.455.0...v1.455.1) (2025-01-31) diff --git a/backend/.sqlx/query-fe58ca3cef78c73867d309156df2b74f7127bf6cba25c9a0c5ca5eedd6a19aff.json b/backend/.sqlx/query-fe58ca3cef78c73867d309156df2b74f7127bf6cba25c9a0c5ca5eedd6a19aff.json new file mode 100644 index 0000000000..1188489d89 --- /dev/null +++ b/backend/.sqlx/query-fe58ca3cef78c73867d309156df2b74f7127bf6cba25c9a0c5ca5eedd6a19aff.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM _sqlx_migrations WHERE version=20250131115248", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "fe58ca3cef78c73867d309156df2b74f7127bf6cba25c9a0c5ca5eedd6a19aff" +} diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 3237d15688..a50ea3f309 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -590,9 +590,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", @@ -1552,9 +1552,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.10" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" dependencies = [ "jobserver", "libc", @@ -3072,9 +3072,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" [[package]] name = "dyn-iter" @@ -10123,9 +10123,9 @@ dependencies = [ [[package]] name = "tree-sitter-language" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c199356c799a8945965bb5f2c55b2ad9d9aa7c4b4f6e587fe9dea0bc715e5f9c" +checksum = "38eee4db33814de3d004de9d8d825627ed3320d0989cce0dea30efaf5be4736c" [[package]] name = "triomphe" @@ -10860,7 +10860,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "axum", @@ -10903,7 +10903,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "argon2", @@ -10996,7 +10996,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.455.1" +version = "1.456.0" dependencies = [ "base64 0.22.1", "chrono", @@ -11014,7 +11014,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.455.1" +version = "1.456.0" dependencies = [ "chrono", "serde", @@ -11027,7 +11027,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "serde", @@ -11041,7 +11041,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "async-stream", @@ -11100,7 +11100,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.455.1" +version = "1.456.0" dependencies = [ "regex", "serde", @@ -11114,7 +11114,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "bytes", @@ -11137,7 +11137,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.455.1" +version = "1.456.0" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -11149,7 +11149,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.455.1" +version = "1.456.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -11158,7 +11158,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "lazy_static", @@ -11170,7 +11170,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "serde_json", @@ -11182,7 +11182,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "gosyn", @@ -11194,7 +11194,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "lazy_static", @@ -11206,7 +11206,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -11217,7 +11217,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -11228,7 +11228,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "async-recursion", @@ -11248,7 +11248,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -11265,7 +11265,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "lazy_static", @@ -11277,7 +11277,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "lazy_static", @@ -11295,7 +11295,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "getrandom 0.2.15", @@ -11317,7 +11317,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "serde_json", @@ -11327,7 +11327,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "async-recursion", @@ -11360,7 +11360,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.455.1" +version = "1.456.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -11370,7 +11370,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.455.1" +version = "1.456.0" dependencies = [ "anyhow", "async-recursion", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 2961e18c9c..c174739a84 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.455.1" +version = "1.456.0" authors.workspace = true edition.workspace = true @@ -30,7 +30,7 @@ members = [ ] [workspace.package] -version = "1.455.1" +version = "1.456.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 6b6909ab7b..cdeb8927e5 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -7cc103e5827b1ec91180a5939a9c022a2a1856c6 \ No newline at end of file +15ddca63acd769e4e7355269a239639c50989def \ No newline at end of file diff --git a/backend/migrations/20250131115248_otel_global_settings.up.sql b/backend/migrations/20250131115248_otel_global_settings.up.sql deleted file mode 100644 index b1ce615a6f..0000000000 --- a/backend/migrations/20250131115248_otel_global_settings.up.sql +++ /dev/null @@ -1 +0,0 @@ -INSERT INTO global_settings (name, value) VALUES ('otel', '{}'); diff --git a/backend/migrations/20250131115248_otel_global_settings.down.sql b/backend/migrations/20250131182047_otel_global_settings_v2.down.sql similarity index 100% rename from backend/migrations/20250131115248_otel_global_settings.down.sql rename to backend/migrations/20250131182047_otel_global_settings_v2.down.sql diff --git a/backend/migrations/20250131182047_otel_global_settings_v2.up.sql b/backend/migrations/20250131182047_otel_global_settings_v2.up.sql new file mode 100644 index 0000000000..8227396ebc --- /dev/null +++ b/backend/migrations/20250131182047_otel_global_settings_v2.up.sql @@ -0,0 +1,3 @@ +INSERT INTO global_settings (name, value) +VALUES ('otel', '{}') +ON CONFLICT (name) DO NOTHING; diff --git a/backend/src/main.rs b/backend/src/main.rs index 3e10f03a05..d4459a3371 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -17,13 +17,11 @@ use rand::Rng; use sqlx::{postgres::PgListener, Pool, Postgres}; use std::{ collections::HashMap, + fs::{create_dir_all, DirBuilder}, net::{IpAddr, Ipv4Addr, SocketAddr}, time::Duration, }; -use tokio::{ - fs::{create_dir_all, DirBuilder, File}, - io::AsyncReadExt, -}; +use tokio::{fs::File, io::AsyncReadExt}; use uuid::Uuid; use windmill_api::HTTP_CLIENT; @@ -34,20 +32,20 @@ use windmill_common::{ global_settings::{ BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, - DEFAULT_TAGS_WORKSPACES_SETTING, ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, - EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING, - INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, - KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, - NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING, - PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, - REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, - SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TEAMS_SETTING, - TIMEOUT_WAIT_RESULT_SETTING, + DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS, + EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, + HUB_BASE_URL_SETTING, INDEXER_SETTING, INSTANCE_PYTHON_VERSION_SETTING, + JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, + LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, + NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING, + REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, + RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, + TEAMS_SETTING, TIMEOUT_WAIT_RESULT_SETTING, }, scripts::ScriptLang, stats_ee::schedule_stats, utils::{hostname, rd_string, Mode, GIT_VERSION}, - worker::{reload_custom_tags_setting, HUB_CACHE_DIR, TMP_DIR, WORKER_GROUP}, + worker::{reload_custom_tags_setting, HUB_CACHE_DIR, TMP_DIR, TMP_LOGS_DIR, WORKER_GROUP}, DB, METRICS_ENABLED, }; @@ -73,7 +71,7 @@ use windmill_worker::{ DENO_CACHE_DIR_NPM, GO_BIN_CACHE_DIR, GO_CACHE_DIR, LOCK_CACHE_DIR, PIP_CACHE_DIR, POWERSHELL_CACHE_DIR, PY310_CACHE_DIR, PY311_CACHE_DIR, PY312_CACHE_DIR, PY313_CACHE_DIR, RUST_CACHE_DIR, TAR_PIP_CACHE_DIR, TAR_PY310_CACHE_DIR, TAR_PY311_CACHE_DIR, - TAR_PY312_CACHE_DIR, TAR_PY313_CACHE_DIR, TMP_LOGS_DIR, UV_CACHE_DIR, + TAR_PY312_CACHE_DIR, TAR_PY313_CACHE_DIR, UV_CACHE_DIR, }; use crate::monitor::{ @@ -140,8 +138,8 @@ async fn cache_hub_scripts(file_path: Option) -> anyhow::Result<()> { ) })?; - create_dir_all(HUB_CACHE_DIR).await?; - create_dir_all(BUN_BUNDLE_CACHE_DIR).await?; + create_dir_all(HUB_CACHE_DIR)?; + create_dir_all(BUN_BUNDLE_CACHE_DIR)?; for path in paths.values() { tracing::info!("Caching hub script at {path}"); @@ -152,7 +150,7 @@ async fn cache_hub_scripts(file_path: Option) -> anyhow::Result<()> { .is_some_and(|x| x == &ScriptLang::Deno) { let job_dir = format!("{}/cache_init/{}", TMP_DIR, Uuid::new_v4()); - create_dir_all(&job_dir).await?; + create_dir_all(&job_dir)?; let _ = windmill_worker::generate_deno_lock( &Uuid::nil(), &res.content, @@ -170,7 +168,7 @@ async fn cache_hub_scripts(file_path: Option) -> anyhow::Result<()> { } else if res.language.as_ref().is_some_and(|x| x == &ScriptLang::Bun) { let job_id = Uuid::new_v4(); let job_dir = format!("{}/cache_init/{}", TMP_DIR, job_id); - create_dir_all(&job_dir).await?; + create_dir_all(&job_dir)?; if let Some(lockfile) = res.lockfile { let _ = windmill_worker::prepare_job_dir(&lockfile, &job_dir).await?; let envs = windmill_worker::get_common_bun_proc_envs(None).await; @@ -505,7 +503,6 @@ Windmill Community Edition {GIT_VERSION} DirBuilder::new() .recursive(true) .create("/tmp/windmill") - .await .expect("could not create initial server dir"); #[cfg(feature = "tantivy")] @@ -788,11 +785,12 @@ Windmill Community Edition {GIT_VERSION} }, EXPOSE_METRICS_SETTING => { tracing::info!("Metrics setting changed, restarting"); - // we wait a bit randomly to avoid having all servers and workers shutdown at same time - let rd_delay = rand::rng().random_range(0..40); - tokio::time::sleep(Duration::from_secs(rd_delay)).await; - if let Err(e) = tx.send(()) { - tracing::error!(error = %e, "Could not send killpill to server"); + send_delayed_killpill(&tx, 40, "metrics setting change").await; + }, + EMAIL_DOMAIN_SETTING => { + tracing::info!("Email domain setting changed"); + if server_mode { + send_delayed_killpill(&tx, 4, "email domain setting change").await; } }, EXPOSE_DEBUG_METRICS_SETTING => { @@ -802,29 +800,17 @@ Windmill Community Edition {GIT_VERSION} }, OTEL_SETTING => { tracing::info!("OTEL setting changed, restarting"); - // we wait a bit randomly to avoid having all servers and workers shutdown at same time - let rd_delay = rand::rng().random_range(0..4); - tokio::time::sleep(Duration::from_secs(rd_delay)).await; - if let Err(e) = tx.send(()) { - tracing::error!(error = %e, "Could not send killpill"); - } + send_delayed_killpill(&tx, 4, "OTEL setting change").await; }, REQUEST_SIZE_LIMIT_SETTING => { if server_mode { tracing::info!("Request limit size change detected, killing server expecting to be restarted"); - // we wait a bit randomly to avoid having all servers shutdown at same time - let rd_delay = rand::rng().random_range(0..4); - tokio::time::sleep(Duration::from_secs(rd_delay)).await; - if let Err(e) = tx.send(()) { - tracing::error!(error = %e, "Could not send killpill to server"); - } + send_delayed_killpill(&tx, 4, "request size limit change").await; } }, SAML_METADATA_SETTING => { tracing::info!("SAML metadata change detected, killing server expecting to be restarted"); - if let Err(e) = tx.send(()) { - tracing::error!(error = %e, "Could not send killpill to server"); - } + send_delayed_killpill(&tx, 0, "SAML metadata change").await; }, HUB_BASE_URL_SETTING => { if let Err(e) = reload_hub_base_url_setting(&db, server_mode).await { @@ -1047,7 +1033,6 @@ pub async fn run_workers( DirBuilder::new() .recursive(true) .create(x) - .await .expect("could not create initial worker dir"); } @@ -1098,3 +1083,18 @@ pub async fn run_workers( futures::future::try_join_all(handles).await?; Ok(()) } + +async fn send_delayed_killpill( + tx: &tokio::sync::broadcast::Sender<()>, + max_delay_secs: u64, + context: &str, +) { + // Random delay to avoid all servers/workers shutting down simultaneously + let rd_delay = rand::rng().random_range(0..max_delay_secs); + tracing::info!("Scheduling {context} shutdown in {rd_delay}s"); + tokio::time::sleep(Duration::from_secs(rd_delay)).await; + + if let Err(e) = tx.send(()) { + tracing::error!(error = %e, "Could not send killpill for {context}"); + } +} diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 33488c6356..b77d097ab1 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -1094,7 +1094,7 @@ pub async fn reload_option_setting_with_tracing( } } -async fn load_value_from_global_settings( +pub async fn load_value_from_global_settings( db: &DB, setting_name: &str, ) -> error::Result> { @@ -1107,6 +1107,7 @@ async fn load_value_from_global_settings( .map(|x| x.value); Ok(r) } + pub async fn reload_option_setting( db: &DB, setting_name: &str, diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 611e6426a6..23b2ec744e 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.455.1 + version: 1.456.0 title: Windmill API contact: @@ -4475,7 +4475,7 @@ paths: /w/{workspace}/scripts/delete/p/{path}: post: - summary: delete all scripts at a given path (require admin) + summary: delete script at a given path (require admin) operationId: deleteScriptByPath tags: - script diff --git a/backend/windmill-api/src/db.rs b/backend/windmill-api/src/db.rs index 6186b0ada1..6cff3872e7 100644 --- a/backend/windmill-api/src/db.rs +++ b/backend/windmill-api/src/db.rs @@ -173,6 +173,13 @@ pub async fn migrate(db: &DB) -> Result<(), Error> { let migrator = db.acquire().await?; let mut custom_migrator = CustomMigrator { inner: migrator }; + if let Err(err) = sqlx::query!("DELETE FROM _sqlx_migrations WHERE version=20250131115248") + .execute(db) + .await + { + tracing::info!("Could not remove sqlx migration with version=20250131115248: {err:#}"); + } + match sqlx::migrate!("../migrations") .run_direct(&mut custom_migrator) .await diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 36552f9a57..7ab057292f 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -34,7 +34,11 @@ use http::HeaderValue; use reqwest::Client; #[cfg(feature = "oauth2")] use std::collections::HashMap; +use windmill_common::global_settings::load_value_from_global_settings; +use windmill_common::global_settings::EMAIL_DOMAIN_SETTING; +use windmill_common::worker::HUB_CACHE_DIR; +use std::fs::DirBuilder; use std::time::Duration; use std::{net::SocketAddr, sync::Arc}; use tokio::sync::RwLock; @@ -203,6 +207,13 @@ pub async fn run_server( ) -> anyhow::Result<()> { let user_db = UserDB::new(db.clone()); + for x in [HUB_CACHE_DIR] { + DirBuilder::new() + .recursive(true) + .create(x) + .expect("could not create initial server dir"); + } + #[cfg(feature = "enterprise")] let ext_jwks = ExternalJwks::load().await; let auth_cache = Arc::new(crate::auth::AuthCache::new( @@ -242,16 +253,32 @@ pub async fn run_server( #[cfg(feature = "embedding")] load_embeddings_db(&db); - #[cfg(feature = "smtp")] + let mut start_smtp_server = false; + if let Some(smtp_settings) = + load_value_from_global_settings(&db, EMAIL_DOMAIN_SETTING).await? { - let smtp_server = Arc::new(SmtpServer { - db: db.clone(), - user_db: user_db, - auth_cache: auth_cache.clone(), - base_internal_url: base_internal_url.clone(), - }); - if let Err(err) = smtp_server.start_listener_thread(addr).await { - tracing::error!("Error starting SMTP server: {err:#}"); + if smtp_settings.as_str().unwrap_or("") != "" { + start_smtp_server = true; + } + } + if !start_smtp_server { + tracing::info!("SMTP server not started because email domain is not set"); + } else { + #[cfg(feature = "smtp")] + { + let smtp_server = Arc::new(SmtpServer { + db: db.clone(), + user_db: user_db, + auth_cache: auth_cache.clone(), + base_internal_url: base_internal_url.clone(), + }); + if let Err(err) = smtp_server.start_listener_thread(addr).await { + tracing::error!("Error starting SMTP server: {err:#}"); + } + } + #[cfg(not(feature = "smtp"))] + { + tracing::info!("SMTP server not started because SMTP feature is not enabled"); } } } diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index b603259664..c0159480e7 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -10,6 +10,7 @@ use std::sync::atomic::AtomicBool; use std::sync::Arc; +use std::time::Duration; use crate::db::ApiAuthed; @@ -464,9 +465,11 @@ async fn list_user_usage( require_admin(authed.is_admin, &authed.username)?; } let mut tx = user_db.begin(&authed).await?; - let rows = sqlx::query_as!( - UserWithUsage, - " + let rows = tokio::time::timeout( + Duration::from_secs(300), + sqlx::query_as!( + UserWithUsage, + " SELECT usr.email, usage.executions FROM usr , LATERAL ( @@ -479,10 +482,12 @@ async fn list_user_usage( ) usage WHERE workspace_id = $1 ", - w_id + w_id + ) + .fetch_all(&mut *tx), ) - .fetch_all(&mut *tx) - .await?; + .await + .map_err(|e| Error::InternalErr(format!("Timed out while fetching user usage: {e:#}")))??; tx.commit().await?; Ok(Json(rows)) } diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index 6f33f2efe5..1afd176007 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -469,12 +469,15 @@ pub async fn get_full_hub_script_by_path( let script; if tokio::fs::metadata(&cache_path).await.is_err() { script = get_full_hub_script_by_path_inner(path, http_client, db).await?; - crate::worker::write_file( + if let Err(e) = crate::worker::write_file( HUB_CACHE_DIR, &version, &serde_json::to_string(&script).map_err(to_anyhow)?, - )?; - tracing::info!("wrote hub script {path} to cache"); + ) { + tracing::error!("failed to write hub script {path} to cache: {e}"); + } else { + tracing::info!("wrote hub script {path} to cache"); + } } else { let cache_content = tokio::fs::read_to_string(cache_path).await?; script = serde_json::from_str(&cache_content).unwrap(); diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs index 36d9f14804..241679301f 100644 --- a/backend/windmill-common/src/worker.rs +++ b/backend/windmill-common/src/worker.rs @@ -171,6 +171,8 @@ pub async fn make_pull_query(wc: &WorkerConfig) { } pub const TMP_DIR: &str = "/tmp/windmill"; +pub const TMP_LOGS_DIR: &str = concatcp!(TMP_DIR, "/logs"); + pub const HUB_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "hub"); pub const ROOT_CACHE_DIR: &str = concatcp!(TMP_DIR, "/cache/"); diff --git a/backend/windmill-worker/src/global_cache.rs b/backend/windmill-worker/src/global_cache.rs index 57ded13ee7..8239ab77d4 100644 --- a/backend/windmill-worker/src/global_cache.rs +++ b/backend/windmill-worker/src/global_cache.rs @@ -13,6 +13,9 @@ use windmill_common::error; #[cfg(all(feature = "enterprise", feature = "parquet", unix))] use std::sync::Arc; +#[cfg(all(feature = "enterprise", feature = "parquet"))] +pub const TARGET: &str = const_format::concatcp!(std::env::consts::OS, "_", std::env::consts::ARCH); + #[cfg(all(feature = "enterprise", feature = "parquet", unix))] pub async fn build_tar_and_push( s3_client: Arc, @@ -57,7 +60,7 @@ pub async fn build_tar_and_push( if let Err(e) = s3_client .put( &Path::from(format!( - "/tar/{}/{folder_name}.tar", + "/tar/{TARGET}/{}/{folder_name}.tar", if no_uv { "pip" } else { &python_xyz } )), std::fs::read(&tar_path)?.into(), @@ -100,7 +103,7 @@ pub async fn pull_from_tar( let start = Instant::now(); let tar_path = format!( - "tar/{}/{folder_name}.tar", + "tar/{TARGET}/{}/{folder_name}.tar", if no_uv { "pip".to_owned() } else { python_xyz } ); let bytes = attempt_fetch_bytes(client, &tar_path).await?; diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index 8c134166a0..a48e1cb396 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -632,7 +632,7 @@ pub async fn uv_pip_compile( if *NATIVE_CERT { args.extend(["--native-tls"]); } - tracing::error!("uv args: {:?}", args); + tracing::debug!("uv args: {:?}", args); #[cfg(windows)] let uv_cmd = "uv"; diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 68f044c837..2c2aefe6b3 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -262,8 +262,6 @@ pub async fn create_token_for_owner( Ok(format!("jwt_{}", token)) } -pub const TMP_LOGS_DIR: &str = concatcp!(TMP_DIR, "/logs"); - pub const ROOT_CACHE_NOMOUNT_DIR: &str = concatcp!(TMP_DIR, "/cache_nomount/"); pub const LOCK_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "lock"); diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 42af0ae690..47f2684855 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.455.1"; +export const VERSION = "v1.456.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index 43e5f9d022..f02dbbe841 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -60,7 +60,7 @@ export { // } // }); -export const VERSION = "1.455.1"; +export const VERSION = "1.456.0"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c30f1ed0e3..95f0c35a97 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.455.1", + "version": "1.456.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.455.1", + "version": "1.456.0", "license": "AGPL-3.0", "dependencies": { "@anthropic-ai/sdk": "^0.32.1", diff --git a/frontend/package.json b/frontend/package.json index 5ba2600d9c..3ea5fd90b8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.455.1", + "version": "1.456.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/frontend/src/lib/components/EditableSchemaForm.svelte b/frontend/src/lib/components/EditableSchemaForm.svelte index b32eb988ae..2c8a2af555 100644 --- a/frontend/src/lib/components/EditableSchemaForm.svelte +++ b/frontend/src/lib/components/EditableSchemaForm.svelte @@ -233,7 +233,7 @@ $: !!editTab ? openEditTab() : closeEditTab() - let pannelButtonWidth: number = 0 + let panelButtonWidth: number = 0 export let pannelExtraButtonWidth: number = 0 export function updateJson() { @@ -246,8 +246,8 @@
@@ -265,7 +265,7 @@
0 ? `width: 100%;` - : `width: calc(100% - ${pannelButtonWidth - pannelExtraButtonWidth}px);`} + : `width: calc(100% - ${panelButtonWidth - pannelExtraButtonWidth}px);`} >
@@ -317,7 +317,7 @@ 0 ? 'rounded-tl-none' : '')} + class={twMerge('border rounded-md', panelButtonWidth > 0 ? 'rounded-tl-none' : '')} > {#if editTab !== 'inputEditor'} diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index a0ff007d1b..3db9804bb8 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -1446,6 +1446,7 @@ showCaptureHint.set(true) }} bind:this={flowPreviewButtons} + {loading} /> {:else} @@ -58,7 +63,10 @@
{/if} {#if !collapsable || !collapsed} -
+
{/if} diff --git a/frontend/src/lib/components/details/EmailTriggerPanel.svelte b/frontend/src/lib/components/details/EmailTriggerPanel.svelte index 9b31103702..3687c1e710 100644 --- a/frontend/src/lib/components/details/EmailTriggerPanel.svelte +++ b/frontend/src/lib/components/details/EmailTriggerPanel.svelte @@ -74,6 +74,7 @@ on:updateSchema on:testWithArgs {newItem} + alwaysOpened={true} /> {:else}
diff --git a/frontend/src/lib/components/flows/content/FlowInput.svelte b/frontend/src/lib/components/flows/content/FlowInput.svelte index 8c76412bbd..00f4083f12 100644 --- a/frontend/src/lib/components/flows/content/FlowInput.svelte +++ b/frontend/src/lib/components/flows/content/FlowInput.svelte @@ -308,6 +308,7 @@ savedPreviewArgs = undefined } } + let historicInputs: HistoricInputs | undefined = undefined @@ -348,6 +349,7 @@ {diff} disableDnd={!!previewSchema} on:rejectChange={(e) => { + console.log('rejectChange') rejectChange(e.detail).then(() => { updatePreviewSchema(selectedSchema) }) @@ -422,7 +424,9 @@ size="xs" startIcon={{ icon: X }} shortCut={{ key: 'esc', withoutModifier: true }} - nonCaptureEvent + on:click={() => { + historicInputs?.resetSelected(true) + }} />
{:else} @@ -460,10 +464,11 @@ }} > { - updatePreviewSchemaAndArgs(e.detail ?? undefined) + updatePreviewSchemaAndArgs(e.detail?.args ?? undefined) }} /> diff --git a/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte b/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte index 8d5fff1a74..1ca89b8097 100644 --- a/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte +++ b/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte @@ -10,6 +10,9 @@ import { getContext } from 'svelte' import type { FlowEditorContext } from '../types' import { Play } from 'lucide-svelte' + + export let loading = false + const { selectedId } = getContext('FlowEditorContext') let previewOpen = false let previewMode: 'upTo' | 'whole' = 'whole' @@ -91,20 +94,22 @@ Test flow - - { - previewOpen = false - }} - on:openTriggers={(e) => { - previewOpen = false - dispatch('openTriggers', e.detail) - }} - bind:preventEscape - /> - +{#if !loading} + + { + previewOpen = false + }} + on:openTriggers={(e) => { + previewOpen = false + dispatch('openTriggers', e.detail) + }} + bind:preventEscape + /> + +{/if} diff --git a/frontend/src/lib/components/meltComponents/SideBarTab.svelte b/frontend/src/lib/components/meltComponents/SideBarTab.svelte index 5c35d75a3b..1eaf683566 100644 --- a/frontend/src/lib/components/meltComponents/SideBarTab.svelte +++ b/frontend/src/lib/components/meltComponents/SideBarTab.svelte @@ -35,7 +35,7 @@
diff --git a/frontend/src/lib/components/preview/FlowPreviewStatus.svelte b/frontend/src/lib/components/preview/FlowPreviewStatus.svelte index 6ff7a5cdda..b21bde0038 100644 --- a/frontend/src/lib/components/preview/FlowPreviewStatus.svelte +++ b/frontend/src/lib/components/preview/FlowPreviewStatus.svelte @@ -6,6 +6,7 @@ import { ExternalLinkIcon } from 'lucide-svelte' import type { FlowStatusViewerContext } from '../graph' import { getContext } from 'svelte' + import { truncateRev } from '$lib/utils' export let job: QueuedJob | CompletedJob let { hideJobId } = getContext('FlowStatusViewer') @@ -22,7 +23,7 @@ target="_blank" href="{base}/run/{job?.id}?workspace={job?.workspace_id}" > - {job?.id} + {truncateRev(job?.id, 8)}
diff --git a/frontend/src/lib/components/runs/JobLoader.svelte b/frontend/src/lib/components/runs/JobLoader.svelte index 49d14f46a0..66637842c5 100644 --- a/frontend/src/lib/components/runs/JobLoader.svelte +++ b/frontend/src/lib/components/runs/JobLoader.svelte @@ -28,7 +28,7 @@ export let argFilter: string | undefined export let resultFilter: string | undefined = undefined export let schedulePath: string | undefined = undefined - export let jobKindsCat: string + export let jobKindsCat: string | undefined = undefined export let minTs: string | undefined = undefined export let maxTs: string | undefined = undefined export let jobKinds: string = '' @@ -91,6 +91,9 @@ } function computeJobKinds(jobKindsCat: string | undefined): string { + if (jobKindsCat == undefined && jobKinds != undefined) { + return jobKinds + } if (jobKindsCat == 'all') { let kinds: CompletedJob['job_kind'][] = [ 'script', @@ -103,7 +106,7 @@ 'script_hub', 'flowscript', 'flownode', - 'appscript', + 'appscript' ] return kinds.join(',') } else if (jobKindsCat == 'dependencies') { @@ -120,7 +123,13 @@ let kinds: CompletedJob['job_kind'][] = ['deploymentcallback'] return kinds.join(',') } else { - let kinds: CompletedJob['job_kind'][] = ['script', 'flow', 'flowscript', 'flownode', 'appscript'] + let kinds: CompletedJob['job_kind'][] = [ + 'script', + 'flow', + 'flowscript', + 'flownode', + 'appscript' + ] return kinds.join(',') } } diff --git a/frontend/src/lib/components/schema/InputSelectedBadge.svelte b/frontend/src/lib/components/schema/InputSelectedBadge.svelte index 493235294c..9538fffc95 100644 --- a/frontend/src/lib/components/schema/InputSelectedBadge.svelte +++ b/frontend/src/lib/components/schema/InputSelectedBadge.svelte @@ -7,7 +7,7 @@ export let inputSelected: 'history' | 'captures' | 'saved' | undefined = undefined -
+
diff --git a/frontend/src/lib/components/triggers/TriggersEditorSection.svelte b/frontend/src/lib/components/triggers/TriggersEditorSection.svelte index 251f55b465..f684bf69a0 100644 --- a/frontend/src/lib/components/triggers/TriggersEditorSection.svelte +++ b/frontend/src/lib/components/triggers/TriggersEditorSection.svelte @@ -21,23 +21,26 @@ export let canHavePreprocessor: boolean = false export let hasPreprocessor: boolean = false export let newItem: boolean + export let openForm: boolean = false + export let alwaysOpened: boolean = false + export let showCapture: boolean = false const captureTypeLabels: Record = { - http: 'New custom HTTP route', - websocket: 'New WebSocket trigger', + http: '+ New custom HTTP route', + websocket: '+ New WebSocket trigger', webhook: 'Webhook', - kafka: 'New Kafka trigger', + kafka: '+ New Kafka trigger', email: 'Email trigger', - nats: 'NATS trigger' + nats: '+ New NATS trigger' } const { captureOn } = getContext('TriggerContext') let args: Record = {} + $: collapsed = !openForm const dispatch = createEventDispatcher() - let showCapture = false let init = false $: updateShowCapture(!!$captureOn) function updateShowCapture(show: boolean) { @@ -49,55 +52,57 @@ } -
+
-
- {#if isEditor} - - {/if} - - {#if !noSave} - {@const disabled = newItem || cloudDisabled} - + {#if !collapsed || alwaysOpened} +
+ {#if isEditor} - - {#if disabled} - {#if newItem} - Deploy the runnable to enable trigger creation - {:else if cloudDisabled} - {capitalize(triggerType)} triggers are disabled in the multi-tenant cloud + {/if} + + {#if !noSave} + {@const disabled = newItem || cloudDisabled} + + + + {#if disabled} + {#if newItem} + Deploy the runnable to enable trigger creation + {:else if cloudDisabled} + {capitalize(triggerType)} triggers are disabled in the multi-tenant cloud + {/if} + {:else} + Create new {captureTypeLabels[triggerType].toLowerCase()} {/if} - {:else} - Create new {captureTypeLabels[triggerType].toLowerCase()} - {/if} - - - {/if} -
+ +
+ {/if} +
+ {/if}
{#if isEditor} diff --git a/frontend/src/lib/components/triggers/http/RoutesPanel.svelte b/frontend/src/lib/components/triggers/http/RoutesPanel.svelte index b05d98f1d9..4c215022d4 100644 --- a/frontend/src/lib/components/triggers/http/RoutesPanel.svelte +++ b/frontend/src/lib/components/triggers/http/RoutesPanel.svelte @@ -5,7 +5,7 @@ import { canWrite } from '$lib/utils' import { getContext, onMount } from 'svelte' import Section from '$lib/components/Section.svelte' - import { Alert, Skeleton } from '$lib/components/common' + import { Alert } from '$lib/components/common' import Description from '$lib/components/Description.svelte' import type { TriggerContext } from '$lib/components/triggers' import TriggersEditorSection from '../TriggersEditorSection.svelte' @@ -19,6 +19,8 @@ export let args: Record = {} let routeEditor: RouteEditor + let openForm = true + let dontCloseOnLoad = false $: path && loadTriggers() const { triggersCount, selectedTrigger, defaultValues } = @@ -48,6 +50,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } }) $triggersCount = { ...($triggersCount ?? {}), http_routes_count: httpTriggers?.length } + openForm = httpTriggers?.length === 0 || dontCloseOnLoad } catch (e) { console.error('impossible to load http routes', e) } @@ -61,11 +64,42 @@ bind:this={routeEditor} /> -
+
Routes expose your scripts and flows as HTTP endpoints. Each route can be configured with a specific HTTP method and path. + {#if !newItem && httpTriggers && httpTriggers.length > 0} +
+ {#if !$userStore?.is_admin && !$userStore?.is_super_admin} + + {/if} + +
+ {#each httpTriggers as httpTriggers (httpTriggers.path)} +
+
{httpTriggers.path}
+
+ {httpTriggers.http_method.toUpperCase()} /{httpTriggers.route_path} +
+
+ +
+
+ {/each} +
+
+ {/if} + { routeEditor?.openNew(isFlow, path, e.detail.config) @@ -74,6 +108,7 @@ on:addPreprocessor on:updateSchema on:testWithArgs + bind:showCapture={dontCloseOnLoad} cloudDisabled={false} triggerType="http" {isFlow} @@ -83,43 +118,6 @@ {hasPreprocessor} {newItem} data={{ args }} + bind:openForm /> - {#if !newItem} -
- {#if !$userStore?.is_admin && !$userStore?.is_super_admin} - - {/if} - - {#if httpTriggers} - {#if httpTriggers.length == 0} -
No http routes
- {:else} -
- {#each httpTriggers as httpTriggers (httpTriggers.path)} -
-
{httpTriggers.path}
-
- {httpTriggers.http_method.toUpperCase()} /{httpTriggers.route_path} -
-
- -
-
- {/each} -
- {/if} - {:else} - - {/if} -
- {/if} -
\ No newline at end of file +
diff --git a/frontend/src/lib/components/triggers/kafka/KafkaTriggersPanel.svelte b/frontend/src/lib/components/triggers/kafka/KafkaTriggersPanel.svelte index 5e710676d1..364a976921 100644 --- a/frontend/src/lib/components/triggers/kafka/KafkaTriggersPanel.svelte +++ b/frontend/src/lib/components/triggers/kafka/KafkaTriggersPanel.svelte @@ -6,7 +6,6 @@ import KafkaTriggerEditor from './KafkaTriggerEditor.svelte' import { isCloudHosted } from '$lib/cloud' import Section from '$lib/components/Section.svelte' - import Skeleton from '$lib/components/common/skeleton/Skeleton.svelte' import Description from '$lib/components/Description.svelte' import { Alert } from '$lib/components/common' import type { TriggerContext } from '$lib/components/triggers' @@ -20,6 +19,8 @@ export let hasPreprocessor: boolean = false let kafkaTriggerEditor: KafkaTriggerEditor + let openForm = true + let dontCloseOnLoad = false $: path && loadTriggers() @@ -50,6 +51,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } }) $triggersCount = { ...($triggersCount ?? {}), kafka_count: kafkaTriggers?.length } + openForm = kafkaTriggers?.length === 0 || dontCloseOnLoad } catch (e) { console.error('impossible to load Kafka triggers', e) } @@ -85,6 +87,32 @@ Kafka triggers execute scripts and flows in response to messages published to Kafka topics. + {#if !newItem && kafkaTriggers && kafkaTriggers.length > 0} +
+
+ {#each kafkaTriggers as kafkaTrigger (kafkaTrigger.path)} +
+
{kafkaTrigger.path}
+
+ {kafkaTrigger.kafka_resource_path} +
+
+ +
+
+ {/each} +
+
+ {/if} { saveTrigger(path, e.detail.config) @@ -102,41 +130,8 @@ {canHavePreprocessor} {hasPreprocessor} {newItem} + {openForm} + bind:showCapture={dontCloseOnLoad} /> - - {#if !newItem} - {#if kafkaTriggers} -
- {#if kafkaTriggers.length == 0} -
No Kafka triggers
- {:else} -
- {#each kafkaTriggers as kafkaTrigger (kafkaTrigger.path)} -
-
{kafkaTrigger.path}
-
- {kafkaTrigger.kafka_resource_path} -
-
- -
-
- {/each} -
- {/if} -
- {:else} - - {/if} - {/if}
{/if} diff --git a/frontend/src/lib/components/triggers/nats/NatsTriggersPanel.svelte b/frontend/src/lib/components/triggers/nats/NatsTriggersPanel.svelte index c7950ebacc..12cd385e3a 100644 --- a/frontend/src/lib/components/triggers/nats/NatsTriggersPanel.svelte +++ b/frontend/src/lib/components/triggers/nats/NatsTriggersPanel.svelte @@ -6,7 +6,6 @@ import NatsTriggerEditor from './NatsTriggerEditor.svelte' import { isCloudHosted } from '$lib/cloud' import Section from '$lib/components/Section.svelte' - import Skeleton from '$lib/components/common/skeleton/Skeleton.svelte' import Alert from '$lib/components/common/alert/Alert.svelte' import Description from '$lib/components/Description.svelte' import type { TriggerContext } from '$lib/components/triggers' @@ -20,6 +19,8 @@ export let hasPreprocessor: boolean = false let natsTriggerEditor: NatsTriggerEditor + let openForm = true + let dontCloseOnLoad = false $: path && loadTriggers() @@ -50,6 +51,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } }) $triggersCount = { ...($triggersCount ?? {}), nats_count: natsTriggers?.length } + openForm = natsTriggers?.length === 0 || dontCloseOnLoad } catch (e) { console.error('impossible to load nats triggers', e) } @@ -85,6 +87,34 @@ NATS triggers execute scripts and flows in response to messages published to NATS subjects. + + {#if !newItem && natsTriggers && natsTriggers.length > 0} +
+
+ {#each natsTriggers as natsTrigger (natsTrigger.path)} +
+
{natsTrigger.path}
+
+ {natsTrigger.nats_resource_path} +
+
+ +
+
+ {/each} +
+
+ {/if} + { saveTrigger(path, e.detail.config) @@ -100,41 +130,8 @@ {canHavePreprocessor} {hasPreprocessor} {newItem} + {openForm} + bind:showCapture={dontCloseOnLoad} /> - - {#if !newItem} - {#if natsTriggers} -
- {#if natsTriggers.length == 0} -
No nats triggers
- {:else} -
- {#each natsTriggers as natsTrigger (natsTrigger.path)} -
-
{natsTrigger.path}
-
- {natsTrigger.nats_resource_path} -
-
- -
-
- {/each} -
- {/if} -
- {:else} - - {/if} - {/if}
{/if} diff --git a/frontend/src/lib/components/triggers/webhook/WebhooksPanel.svelte b/frontend/src/lib/components/triggers/webhook/WebhooksPanel.svelte index 948624646b..485a433e97 100644 --- a/frontend/src/lib/components/triggers/webhook/WebhooksPanel.svelte +++ b/frontend/src/lib/components/triggers/webhook/WebhooksPanel.svelte @@ -53,5 +53,6 @@ {canHavePreprocessor} {hasPreprocessor} {newItem} + alwaysOpened={true} /> diff --git a/frontend/src/lib/components/triggers/websocket/WebsocketTriggersPanel.svelte b/frontend/src/lib/components/triggers/websocket/WebsocketTriggersPanel.svelte index 7b633a1262..12142f8343 100644 --- a/frontend/src/lib/components/triggers/websocket/WebsocketTriggersPanel.svelte +++ b/frontend/src/lib/components/triggers/websocket/WebsocketTriggersPanel.svelte @@ -7,7 +7,7 @@ import WebsocketTriggerEditor from './WebsocketTriggerEditor.svelte' import { isCloudHosted } from '$lib/cloud' import Section from '$lib/components/Section.svelte' - import { Alert, Skeleton } from '$lib/components/common' + import { Alert } from '$lib/components/common' import Description from '$lib/components/Description.svelte' import type { TriggerContext } from '$lib/components/triggers' import TriggersEditorSection from '../TriggersEditorSection.svelte' @@ -19,6 +19,8 @@ export let hasPreprocessor: boolean = false let wsTriggerEditor: WebsocketTriggerEditor + let openForm = true + let dontCloseOnLoad = false $: path && loadTriggers() @@ -49,6 +51,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } }) $triggersCount = { ...($triggersCount ?? {}), websocket_count: wsTriggers?.length } + openForm = wsTriggers?.length === 0 || dontCloseOnLoad } catch (e) { console.error('impossible to load WS triggers', e) } @@ -72,6 +75,36 @@ WebSocket triggers allow real-time bidirectional communication between your scripts/flows and external systems. Each trigger creates a unique WebSocket endpoint. + + {#if !newItem && wsTriggers && wsTriggers.length > 0} +
+
+
+ {#each wsTriggers as wsTriggers (wsTriggers.path)} +
+
{wsTriggers.path}
+
+ {wsTriggers.url} +
+
+ +
+
+ {/each} +
+
+
+ {/if} + { @@ -88,43 +121,7 @@ {canHavePreprocessor} {hasPreprocessor} {newItem} + {openForm} /> - - {#if !newItem} -
-
- {#if wsTriggers} - {#if wsTriggers.length == 0} -
No WebSocket triggers
- {:else} -
- {#each wsTriggers as wsTriggers (wsTriggers.path)} -
-
{wsTriggers.path}
-
- {wsTriggers.url} -
-
- -
-
- {/each} -
- {/if} - {:else} - - {/if} -
-
- {/if} {/if} diff --git a/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte index aa26013684..6425c3d5a3 100644 --- a/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte @@ -344,7 +344,7 @@ let stepDetail: FlowModule | string | undefined = undefined let token = 'TOKEN_TO_CREATE' let rightPaneSelected = 'saved_inputs' - + let savedInputsV2: SavedInputsV2 | undefined = undefined let flowHistory: FlowHistory | undefined = undefined @@ -449,7 +449,12 @@
- + { + savedInputsV2?.resetSelected() + }} + {inputSelected} + />
- + { + savedInputsV2?.resetSelected() + }} + {inputSelected} + /> {#if args}