mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-04 16:03:06 +00:00
Merge branch 'main' into alp/teams_scripts
This commit is contained in:
+1
-1
@@ -7,4 +7,4 @@ CaddyfileRemoteMalo
|
||||
*.swp
|
||||
**/.idea/
|
||||
.direnv
|
||||
.vscode
|
||||
.vscode
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM _sqlx_migrations WHERE version=20250131115248",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fe58ca3cef78c73867d309156df2b74f7127bf6cba25c9a0c5ca5eedd6a19aff"
|
||||
}
|
||||
Generated
+33
-33
@@ -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",
|
||||
|
||||
+2
-2
@@ -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 <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
7cc103e5827b1ec91180a5939a9c022a2a1856c6
|
||||
15ddca63acd769e4e7355269a239639c50989def
|
||||
@@ -1 +0,0 @@
|
||||
INSERT INTO global_settings (name, value) VALUES ('otel', '{}');
|
||||
@@ -0,0 +1,3 @@
|
||||
INSERT INTO global_settings (name, value)
|
||||
VALUES ('otel', '{}')
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
+41
-41
@@ -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<String>) -> 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<String>) -> 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<String>) -> 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}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1094,7 +1094,7 @@ pub async fn reload_option_setting_with_tracing<T: FromStr + DeserializeOwned>(
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_value_from_global_settings(
|
||||
pub async fn load_value_from_global_settings(
|
||||
db: &DB,
|
||||
setting_name: &str,
|
||||
) -> error::Result<Option<serde_json::Value>> {
|
||||
@@ -1107,6 +1107,7 @@ async fn load_value_from_global_settings(
|
||||
.map(|x| x.value);
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
pub async fn reload_option_setting<T: FromStr + DeserializeOwned>(
|
||||
db: &DB,
|
||||
setting_name: &str,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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/");
|
||||
|
||||
@@ -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<dyn ObjectStore>,
|
||||
@@ -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?;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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");
|
||||
|
||||
+1
-1
@@ -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<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.455.1";
|
||||
export const VERSION = "1.456.0";
|
||||
|
||||
const command = new Command()
|
||||
.name("wmill")
|
||||
|
||||
Generated
+2
-2
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.455.1",
|
||||
"version": "1.456.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -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 @@
|
||||
<div class="relative z-[100000]">
|
||||
<div
|
||||
class="absolute"
|
||||
style="right: calc({editPanelSize}% - 2px - {pannelExtraButtonWidth}px); top: 0px;"
|
||||
bind:clientWidth={pannelButtonWidth}
|
||||
style="right: calc({editPanelSize}% - 1px - {pannelExtraButtonWidth}px); top: 0px;"
|
||||
bind:clientWidth={panelButtonWidth}
|
||||
>
|
||||
<slot name="openEditTab" />
|
||||
</div>
|
||||
@@ -265,7 +265,7 @@
|
||||
<div
|
||||
style={editPanelSize > 0
|
||||
? `width: 100%;`
|
||||
: `width: calc(100% - ${pannelButtonWidth - pannelExtraButtonWidth}px);`}
|
||||
: `width: calc(100% - ${panelButtonWidth - pannelExtraButtonWidth}px);`}
|
||||
>
|
||||
<slot name="addProperty" />
|
||||
</div>
|
||||
@@ -317,7 +317,7 @@
|
||||
<Pane
|
||||
bind:size={editPanelSize}
|
||||
minSize={noPreview ? 100 : 50}
|
||||
class={twMerge('border rounded-md', pannelButtonWidth > 0 ? 'rounded-tl-none' : '')}
|
||||
class={twMerge('border rounded-md', panelButtonWidth > 0 ? 'rounded-tl-none' : '')}
|
||||
>
|
||||
{#if editTab !== 'inputEditor'}
|
||||
<slot name="extraTab" />
|
||||
|
||||
@@ -1446,6 +1446,7 @@
|
||||
showCaptureHint.set(true)
|
||||
}}
|
||||
bind:this={flowPreviewButtons}
|
||||
{loading}
|
||||
/>
|
||||
<Button
|
||||
loading={loadingDraft}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import { HistoryIcon } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import PopoverV2 from '$lib/components/meltComponents/Popover.svelte'
|
||||
import HistoricInputs from './HistoricInputs.svelte'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { JobService } from '$lib/gen'
|
||||
|
||||
export let path: string
|
||||
export let selected: string | undefined = undefined
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function loadInitial() {
|
||||
let jobs = await JobService.listJobs({
|
||||
workspace: $workspaceStore!,
|
||||
scriptPathExact: path,
|
||||
jobKinds: ['flow', 'flowpreview'].join(','),
|
||||
page: 1,
|
||||
perPage: 1
|
||||
})
|
||||
if (jobs.length > 0) {
|
||||
dispatch('select', { jobId: jobs[0].id, initial: true })
|
||||
}
|
||||
}
|
||||
|
||||
$: $workspaceStore && loadInitial()
|
||||
</script>
|
||||
|
||||
<PopoverV2 closeButton={false}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<HistoryIcon size={14} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-2 h-[400px] overflow-hidden w-80 border shadow-sm">
|
||||
<HistoricInputs
|
||||
on:select={(e) => {
|
||||
if (e.detail) {
|
||||
dispatch('select', { jobId: e.detail?.jobId, initial: false })
|
||||
} else {
|
||||
dispatch('unselect')
|
||||
}
|
||||
}}
|
||||
{selected}
|
||||
runnableId={path}
|
||||
runnableType={'FlowPath'}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</PopoverV2>
|
||||
@@ -16,6 +16,7 @@
|
||||
import InputSelectedBadge from './schema/InputSelectedBadge.svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import JsonInputs from './JsonInputs.svelte'
|
||||
import FlowHistoryJobPicker from './FlowHistoryJobPicker.svelte'
|
||||
|
||||
export let previewMode: 'upTo' | 'whole'
|
||||
export let open: boolean
|
||||
@@ -54,6 +55,11 @@
|
||||
} = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let renderCount: number = 0
|
||||
let initial: boolean = false
|
||||
let schemaFormWithArgPicker: SchemaFormWithArgPicker | undefined = undefined
|
||||
let currentJobId: string | undefined = undefined
|
||||
|
||||
function extractFlow(previewMode: 'upTo' | 'whole'): OpenFlow {
|
||||
if (previewMode === 'whole') {
|
||||
return $flowStore
|
||||
@@ -74,6 +80,9 @@
|
||||
args: Record<string, any>,
|
||||
restartedFrom: RestartedFrom | undefined
|
||||
) {
|
||||
if (initial) {
|
||||
initial = false
|
||||
}
|
||||
try {
|
||||
lastPreviewFlow = JSON.stringify($flowStore)
|
||||
jobProgressReset()
|
||||
@@ -89,6 +98,7 @@
|
||||
isRunning = false
|
||||
jobId = undefined
|
||||
}
|
||||
schemaFormWithArgPicker?.refreshHistory()
|
||||
}
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
@@ -159,8 +169,6 @@
|
||||
}
|
||||
|
||||
$: selectedJobStep !== undefined && onSelectedJobStepChange()
|
||||
|
||||
let renderCount: number = 0
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
@@ -324,6 +332,7 @@
|
||||
<div class="overflow-y-auto grow flex flex-col pt-4">
|
||||
<div class="border-b">
|
||||
<SchemaFormWithArgPicker
|
||||
bind:this={schemaFormWithArgPicker}
|
||||
runnableId={initialPath == '' ? $pathStore : initialPath}
|
||||
runnableType={'FlowPath'}
|
||||
previewArgs={$previewArgs}
|
||||
@@ -335,7 +344,10 @@
|
||||
{jsonView}
|
||||
>
|
||||
<div class="w-full flex flex-row justify-between">
|
||||
<InputSelectedBadge {inputSelected} />
|
||||
<InputSelectedBadge
|
||||
on:click={() => schemaFormWithArgPicker?.resetSelected()}
|
||||
{inputSelected}
|
||||
/>
|
||||
<div class="flex flex-row gap-2">
|
||||
<Toggle
|
||||
bind:checked={jsonView}
|
||||
@@ -384,8 +396,43 @@
|
||||
{/if}
|
||||
</SchemaFormWithArgPicker>
|
||||
</div>
|
||||
<div class="pt-4 flex flex-col grow">
|
||||
<div class="pt-4 flex flex-col grow relative">
|
||||
<div
|
||||
class="absolute top-[22px] right-2 border p-1.5 hover:bg-surface-hover rounded-md center-center"
|
||||
>
|
||||
<FlowHistoryJobPicker
|
||||
on:select={(e) => {
|
||||
if (!currentJobId) {
|
||||
currentJobId = jobId
|
||||
}
|
||||
const detail = e.detail
|
||||
initial = detail.initial
|
||||
jobId = detail.jobId
|
||||
}}
|
||||
on:unselect={() => {
|
||||
jobId = currentJobId
|
||||
currentJobId = undefined
|
||||
}}
|
||||
path={initialPath == '' ? $pathStore : initialPath}
|
||||
/>
|
||||
</div>
|
||||
{#if jobId}
|
||||
{#if initial}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
on:click={() => {
|
||||
initial = false
|
||||
}}
|
||||
class="cursor-pointer h-full hover:bg-gray-500/20 dark:hover:bg-gray-500/20 dark:bg-gray-500/80 rounded bg-gray-500/40 absolute top-0 left-0 w-full z-50"
|
||||
>
|
||||
<div class="text-center text-primary text-lg py-2 pt-20"
|
||||
><span class="font-bold border p-2 bg-surface-secondary rounded-md"
|
||||
>Previous run of this flow from history</span
|
||||
></div
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
<FlowStatusViewer
|
||||
hideDownloadInGraph={customUi?.downloadLogs === false}
|
||||
wideResults
|
||||
|
||||
@@ -933,6 +933,8 @@
|
||||
<Tab value="graph"><span class="font-semibold text-md">Graph</span></Tab>
|
||||
<Tab value="sequence"><span class="font-semibold">Details</span></Tab>
|
||||
</Tabs>
|
||||
{:else}
|
||||
<div class="h-[30px]" />
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="{selected != 'sequence' ? 'hidden' : ''} max-w-7xl mx-auto">
|
||||
|
||||
@@ -1,66 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { InputService, type RunnableType, type Job } from '$lib/gen/index.js'
|
||||
import { workspaceStore } from '$lib/stores.js'
|
||||
import { type RunnableType, type Job } from '$lib/gen/index.js'
|
||||
import { sendUserToast } from '$lib/utils.js'
|
||||
import JobSchemaPicker from '$lib/components/schema/JobSchemaPicker.svelte'
|
||||
import RunningJobSchemaPicker from '$lib/components/schema/RunningJobSchemaPicker.svelte'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import JobLoader from './runs/JobLoader.svelte'
|
||||
import { DataTable } from '$lib/components/table'
|
||||
import { clickOutside } from '$lib/utils'
|
||||
import InfiniteList from './InfiniteList.svelte'
|
||||
import HistoricList from './HistoricList.svelte'
|
||||
|
||||
export let runnableId: string | undefined = undefined
|
||||
export let runnableType: RunnableType | undefined = undefined
|
||||
export let loading: boolean = false
|
||||
export let selected: string | undefined = undefined
|
||||
|
||||
let historicList: HistoricList | undefined = undefined
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let jobs: Job[] = []
|
||||
let hasMoreCurrentRuns = false
|
||||
let page = 1
|
||||
let infiniteList: InfiniteList | undefined = undefined
|
||||
let loadInputsPageFn: ((page: number, perPage: number) => Promise<any>) | undefined = undefined
|
||||
|
||||
let cachedArgs: Record<string, any> = {}
|
||||
function initLoadInputs() {
|
||||
loadInputsPageFn = async (page: number, perPage: number) => {
|
||||
const inputs = await InputService.getInputHistory({
|
||||
workspace: $workspaceStore!,
|
||||
runnableId,
|
||||
runnableType,
|
||||
page,
|
||||
perPage,
|
||||
includePreview: true
|
||||
})
|
||||
|
||||
const inputsWithPayload = await Promise.all(
|
||||
inputs.map(async (input) => {
|
||||
if (cachedArgs[input.id]) {
|
||||
return {
|
||||
...input,
|
||||
payloadData: cachedArgs[input.id]
|
||||
}
|
||||
}
|
||||
const payloadData = await loadArgsFromHistory(input.id, undefined, false)
|
||||
if (payloadData === 'WINDMILL_TOO_BIG') {
|
||||
return {
|
||||
...input,
|
||||
payloadData: 'WINDMILL_TOO_BIG',
|
||||
getFullPayload: () => loadArgsFromHistory(input.id, undefined, true)
|
||||
}
|
||||
}
|
||||
cachedArgs[input.id] = payloadData
|
||||
return {
|
||||
...input,
|
||||
payloadData
|
||||
}
|
||||
})
|
||||
)
|
||||
return inputsWithPayload
|
||||
}
|
||||
infiniteList?.setLoader(loadInputsPageFn)
|
||||
}
|
||||
|
||||
async function handleSelected(data: any) {
|
||||
if (selected === data.id) {
|
||||
@@ -70,39 +27,16 @@
|
||||
selected = data.id
|
||||
if (data.payloadData === 'WINDMILL_TOO_BIG') {
|
||||
const fullPayload = await data.getFullPayload?.()
|
||||
dispatch('select', fullPayload)
|
||||
dispatch('select', { args: fullPayload, jobId: data.id })
|
||||
} else {
|
||||
dispatch('select', structuredClone(data.payloadData))
|
||||
dispatch('select', { args: structuredClone(data.payloadData), jobId: data.id })
|
||||
}
|
||||
}
|
||||
|
||||
let selected: string | undefined = undefined
|
||||
|
||||
onDestroy(() => {
|
||||
resetSelected(true)
|
||||
})
|
||||
|
||||
async function getPropPickerElements(): Promise<HTMLElement[]> {
|
||||
return Array.from(
|
||||
document.querySelectorAll('[data-schema-picker], [data-schema-picker] *')
|
||||
) as HTMLElement[]
|
||||
}
|
||||
|
||||
async function loadArgsFromHistory(
|
||||
id: string | undefined,
|
||||
input: boolean | undefined,
|
||||
allowLarge: boolean
|
||||
): Promise<any> {
|
||||
if (!id) return
|
||||
const payloadData = await InputService.getArgsFromHistoryOrSavedInput({
|
||||
jobOrInputId: id,
|
||||
workspace: $workspaceStore!,
|
||||
input,
|
||||
allowLarge
|
||||
})
|
||||
return payloadData
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape' && selected) {
|
||||
resetSelected(true)
|
||||
@@ -120,9 +54,7 @@
|
||||
let jobHovered: string | undefined = undefined
|
||||
|
||||
export function refresh() {
|
||||
if (infiniteList) {
|
||||
infiniteList.loadData('refresh')
|
||||
}
|
||||
historicList?.refresh()
|
||||
}
|
||||
|
||||
export function resetSelected(dispatchEvent?: boolean) {
|
||||
@@ -132,8 +64,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: !loading && refresh()
|
||||
$: $workspaceStore && runnableId && runnableType && infiniteList && initLoadInputs()
|
||||
function getJobKinds(runnableType: RunnableType | undefined) {
|
||||
if (runnableType === 'FlowPath') {
|
||||
return 'flow,flowpreview'
|
||||
} else if (runnableType === 'ScriptPath') {
|
||||
return 'script,preview'
|
||||
} else if (runnableType === 'ScriptHash') {
|
||||
return 'script,preview'
|
||||
}
|
||||
return 'all'
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
@@ -143,7 +83,7 @@
|
||||
bind:jobs
|
||||
path={runnableId}
|
||||
isSkipped={false}
|
||||
jobKindsCat="all"
|
||||
jobKinds={getJobKinds(runnableType)}
|
||||
user={null}
|
||||
label={null}
|
||||
folder={null}
|
||||
@@ -159,15 +99,7 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="h-full w-full flex flex-col gap-4"
|
||||
use:clickOutside={{ capture: false, exclude: getPropPickerElements }}
|
||||
on:click_outside={() => {
|
||||
if (selected) {
|
||||
resetSelected(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="h-full max-h-full min-h-0 w-full flex flex-col gap-4">
|
||||
<div class="grow-0" data-schema-picker>
|
||||
<DataTable size="xs" bind:currentPage={page} hasMore={hasMoreCurrentRuns} tableFixed={true}>
|
||||
{#if loading && (jobs == undefined || jobs?.length == 0)}
|
||||
@@ -201,32 +133,13 @@
|
||||
</div>
|
||||
|
||||
<div class="min-h-0 grow" data-schema-picker>
|
||||
<InfiniteList
|
||||
bind:this={infiniteList}
|
||||
selectedItemId={selected}
|
||||
<HistoricList
|
||||
bind:this={historicList}
|
||||
on:error={(e) => handleError(e.detail)}
|
||||
on:select={(e) => handleSelected(e.detail)}
|
||||
>
|
||||
<svelte:fragment slot="columns">
|
||||
<colgroup>
|
||||
<col class="w-8" />
|
||||
<col class="w-16" />
|
||||
<col />
|
||||
</colgroup>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment let:item let:hover>
|
||||
<JobSchemaPicker
|
||||
job={item}
|
||||
selected={selected === item.id}
|
||||
hovering={hover}
|
||||
payloadData={item.payloadData}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="empty">
|
||||
<div class="text-center text-tertiary text-xs py-2">
|
||||
{runnableId ? 'No previous inputs' : 'Save draft to see previous runs'}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</InfiniteList>
|
||||
{runnableId}
|
||||
{runnableType}
|
||||
{selected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<script lang="ts">
|
||||
import { InputService, type RunnableType } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { onDestroy } from 'svelte'
|
||||
import InfiniteList from './InfiniteList.svelte'
|
||||
import JobSchemaPicker from './schema/JobSchemaPicker.svelte'
|
||||
|
||||
export let runnableId: string | undefined
|
||||
export let runnableType: RunnableType | undefined
|
||||
export let selected: string | undefined = undefined
|
||||
|
||||
let infiniteList: InfiniteList | undefined = undefined
|
||||
let loadInputsPageFn: ((page: number, perPage: number) => Promise<any>) | undefined = undefined
|
||||
|
||||
export function refresh() {
|
||||
if (infiniteList) {
|
||||
infiniteList.loadData('refresh')
|
||||
}
|
||||
}
|
||||
let cachedArgs: Record<string, any> = {}
|
||||
|
||||
let interval: NodeJS.Timeout | undefined = undefined
|
||||
function initLoadInputs() {
|
||||
interval && clearInterval(interval)
|
||||
interval = setInterval(() => {
|
||||
refresh()
|
||||
}, 10000)
|
||||
loadInputsPageFn = async (page: number, perPage: number) => {
|
||||
const inputs = await InputService.getInputHistory({
|
||||
workspace: $workspaceStore!,
|
||||
runnableId,
|
||||
runnableType,
|
||||
page,
|
||||
perPage,
|
||||
includePreview: true
|
||||
})
|
||||
|
||||
const inputsWithPayload = await Promise.all(
|
||||
inputs.map(async (input) => {
|
||||
if (cachedArgs[input.id]) {
|
||||
return {
|
||||
...input,
|
||||
payloadData: cachedArgs[input.id]
|
||||
}
|
||||
}
|
||||
const payloadData = await loadArgsFromHistory(input.id, undefined, false)
|
||||
if (payloadData === 'WINDMILL_TOO_BIG') {
|
||||
return {
|
||||
...input,
|
||||
payloadData: 'WINDMILL_TOO_BIG',
|
||||
getFullPayload: () => loadArgsFromHistory(input.id, undefined, true)
|
||||
}
|
||||
}
|
||||
cachedArgs[input.id] = payloadData
|
||||
return {
|
||||
...input,
|
||||
payloadData
|
||||
}
|
||||
})
|
||||
)
|
||||
return inputsWithPayload
|
||||
}
|
||||
infiniteList?.setLoader(loadInputsPageFn)
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
interval && clearInterval(interval)
|
||||
})
|
||||
|
||||
async function loadArgsFromHistory(
|
||||
id: string | undefined,
|
||||
input: boolean | undefined,
|
||||
allowLarge: boolean
|
||||
): Promise<any> {
|
||||
if (!id) return
|
||||
const payloadData = await InputService.getArgsFromHistoryOrSavedInput({
|
||||
jobOrInputId: id,
|
||||
workspace: $workspaceStore!,
|
||||
input,
|
||||
allowLarge
|
||||
})
|
||||
return payloadData
|
||||
}
|
||||
|
||||
$: $workspaceStore && runnableId && runnableType && infiniteList && initLoadInputs()
|
||||
</script>
|
||||
|
||||
<InfiniteList bind:this={infiniteList} selectedItemId={selected} on:error on:select>
|
||||
<svelte:fragment slot="columns">
|
||||
<colgroup>
|
||||
<col class="w-8" />
|
||||
<col class="w-16" />
|
||||
<col />
|
||||
</colgroup>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment let:item let:hover>
|
||||
<JobSchemaPicker
|
||||
job={item}
|
||||
selected={selected === item.id}
|
||||
hovering={hover}
|
||||
payloadData={item.payloadData}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="empty">
|
||||
<div class="text-center text-tertiary text-xs py-2">
|
||||
{runnableId ? 'No previous inputs' : 'Save draft to see previous runs'}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</InfiniteList>
|
||||
@@ -22,9 +22,11 @@
|
||||
let deleteItemFn: ((id: any) => Promise<any>) | undefined = undefined
|
||||
|
||||
export async function loadData(loadOption: 'refresh' | 'forceRefresh' | 'loadMore' = 'loadMore') {
|
||||
// console.log('loadData', loadOption, length, items?.length)
|
||||
|
||||
if (!loadInputs) return
|
||||
loading = true
|
||||
hasMore = hasMore
|
||||
hasMore = length === perPage * page
|
||||
|
||||
if (hasMore && loadOption === 'loadMore') {
|
||||
page++
|
||||
@@ -160,7 +162,7 @@
|
||||
|
||||
@keyframes greenHighlight {
|
||||
0% {
|
||||
background-color: rgba(70, 255, 138, 0.5);
|
||||
background-color: rgba(70, 255, 138, 0.4);
|
||||
box-shadow: 0 0 15px rgb(34 197 94 / 0.3);
|
||||
}
|
||||
100% {
|
||||
@@ -170,7 +172,7 @@
|
||||
}
|
||||
|
||||
:global(.animate-slideIn) {
|
||||
animation: greenHighlight 2s ease-out forwards;
|
||||
animation: greenHighlight 1s ease-out forwards;
|
||||
will-change: transform, opacity, background-color, box-shadow;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { Edit, Trash2, Save, Eye } from 'lucide-svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { Cell } from './table/index'
|
||||
import { clickOutside } from '$lib/utils'
|
||||
import SaveInputsButton from '$lib/components/SaveInputsButton.svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import InfiniteList from './InfiniteList.svelte'
|
||||
@@ -133,11 +132,6 @@
|
||||
resetSelected(true)
|
||||
})
|
||||
|
||||
async function getPropPickerElements(): Promise<HTMLElement[]> {
|
||||
const elements = document.querySelectorAll('[data-schema-picker], [data-schema-picker] *')
|
||||
return elements ? (Array.from(elements) as HTMLElement[]) : []
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape' && isEditing) {
|
||||
setEditing(null)
|
||||
@@ -191,13 +185,7 @@
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<div
|
||||
class="w-full flex flex-col gap-1 h-full overflow-y-auto"
|
||||
use:clickOutside={{ capture: false, exclude: getPropPickerElements }}
|
||||
on:click_outside={() => {
|
||||
resetSelected(true)
|
||||
}}
|
||||
>
|
||||
<div class="w-full flex flex-col gap-1 h-full overflow-y-auto">
|
||||
{#if !noButton}
|
||||
<div>
|
||||
<Popover class="w-full" placement="bottom" disablePopup={runnableId && previewArgs}>
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
export let isValid: boolean
|
||||
export let args: object
|
||||
|
||||
export function resetSelected() {
|
||||
historicInputs?.resetSelected(true)
|
||||
savedInputsPicker?.resetSelected(true)
|
||||
}
|
||||
|
||||
let savedArgs: any = undefined
|
||||
let runnableType: RunnableType | undefined = undefined
|
||||
let savedInputsPicker: SavedInputsPicker | undefined = undefined
|
||||
@@ -90,7 +95,7 @@
|
||||
{runnableType}
|
||||
on:select={(e) => {
|
||||
if (e.detail) savedInputsPicker?.resetSelected()
|
||||
selectArgs(e.detail, e.detail ? 'history' : undefined)
|
||||
selectArgs(e.detail?.args, e.detail ? 'history' : undefined)
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
@@ -19,6 +19,16 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export function refreshHistory() {
|
||||
historicInputs?.refresh()
|
||||
}
|
||||
|
||||
export function resetSelected() {
|
||||
historicInputs?.resetSelected(true)
|
||||
savedInputsPicker?.resetSelected(true)
|
||||
captureTable?.resetSelected(true)
|
||||
}
|
||||
|
||||
const getDropdownItems = () => {
|
||||
return [
|
||||
{
|
||||
@@ -71,23 +81,24 @@
|
||||
let dropdownItems: any
|
||||
let rightPanelOpen = false
|
||||
|
||||
let savedInputsPicker: SavedInputsPicker | undefined = undefined
|
||||
let loading = false
|
||||
let captureTable: CaptureTable | undefined = undefined
|
||||
let historicInputs: HistoricInputs | undefined = undefined
|
||||
$: selectedTab, (dropdownItems = getDropdownItems())
|
||||
|
||||
let inputPanelSize = 70
|
||||
</script>
|
||||
|
||||
<div class="h-fit">
|
||||
<div class="relative z-[100000] w-full">
|
||||
<div class="absolute" style="right: calc({100 - inputPanelSize}%); top: -1px;">
|
||||
<SideBarTab expandRight={rightPanelOpen} {dropdownItems} fullMenu={true} noTrigger={true} />
|
||||
</div>
|
||||
</div>
|
||||
<Splitpanes class={!rightPanelOpen ? 'splitter-hidden' : ''}>
|
||||
<Pane class="!overflow-visible" size={70} minSize={30}>
|
||||
<div class="relative w-full h-fit pr-12 pb-4 overflow-hidden" bind:clientHeight={rightHeight}>
|
||||
<div class="absolute right-[1px] -top-[1px] z-50">
|
||||
<SideBarTab
|
||||
expandRight={rightPanelOpen}
|
||||
{dropdownItems}
|
||||
fullMenu={true}
|
||||
noTrigger={true}
|
||||
/>
|
||||
</div>
|
||||
<Pane bind:size={inputPanelSize} class="!overflow-visible" minSize={30}>
|
||||
<div class="relative w-full h-fit pr-12 pb-4" bind:clientHeight={rightHeight}>
|
||||
<slot />
|
||||
</div>
|
||||
</Pane>
|
||||
@@ -108,7 +119,7 @@
|
||||
{runnableId}
|
||||
{runnableType}
|
||||
on:select={(e) => {
|
||||
dispatch('select', { payload: e.detail, type: 'history' })
|
||||
dispatch('select', { payload: e.detail?.args, type: 'history' })
|
||||
}}
|
||||
/>
|
||||
</FlowInputEditor>
|
||||
@@ -119,6 +130,7 @@
|
||||
{runnableId}
|
||||
{runnableType}
|
||||
{previewArgs}
|
||||
bind:this={savedInputsPicker}
|
||||
on:select={(e) => {
|
||||
dispatch('select', { payload: e.detail, type: 'saved' })
|
||||
}}
|
||||
@@ -138,6 +150,7 @@
|
||||
on:select={(e) => {
|
||||
dispatch('select', { payload: e.detail, type: 'captures' })
|
||||
}}
|
||||
bind:this={captureTable}
|
||||
isFlow={true}
|
||||
headless={true}
|
||||
addButton={false}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { enterpriseLicense } from '$lib/stores'
|
||||
import { AlertTriangle, ChevronDown, ChevronRight } from 'lucide-svelte'
|
||||
import { AlertTriangle, ChevronRight } from 'lucide-svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { slide } from 'svelte/transition'
|
||||
|
||||
export let label: string | undefined = undefined
|
||||
export let tooltip: string | undefined = undefined
|
||||
@@ -14,6 +15,7 @@
|
||||
export let collapsable: boolean = false
|
||||
export let collapsed: boolean = true
|
||||
export let headless: boolean = false
|
||||
export let animate: boolean = false
|
||||
</script>
|
||||
|
||||
<div class={twMerge('w-full flex flex-col', wrapperClass)}>
|
||||
@@ -27,11 +29,14 @@
|
||||
>
|
||||
{#if collapsable}
|
||||
<button class="flex items-center gap-1" on:click={() => (collapsed = !collapsed)}>
|
||||
{#if collapsed}
|
||||
<ChevronRight size={16} />
|
||||
{:else}
|
||||
<ChevronDown size={16} />
|
||||
{/if}
|
||||
<ChevronRight
|
||||
size={16}
|
||||
class={twMerge(
|
||||
'transition',
|
||||
collapsed ? '' : 'rotate-90',
|
||||
animate ? 'duration-200' : 'duration-0'
|
||||
)}
|
||||
/>
|
||||
{label}
|
||||
</button>
|
||||
{:else}
|
||||
@@ -58,7 +63,10 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if !collapsable || !collapsed}
|
||||
<div class={twMerge('grow min-h-0', $$props.class)}>
|
||||
<div
|
||||
class={twMerge('grow min-h-0', $$props.class)}
|
||||
transition:slide={animate ? { duration: 200 } : { duration: 0 }}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
on:updateSchema
|
||||
on:testWithArgs
|
||||
{newItem}
|
||||
alwaysOpened={true}
|
||||
/>
|
||||
{:else}
|
||||
<div>
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
savedPreviewArgs = undefined
|
||||
}
|
||||
}
|
||||
let historicInputs: HistoricInputs | undefined = undefined
|
||||
</script>
|
||||
|
||||
<!-- Add svelte:window to listen for keyboard events -->
|
||||
@@ -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)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
@@ -460,10 +464,11 @@
|
||||
}}
|
||||
>
|
||||
<HistoricInputs
|
||||
bind:this={historicInputs}
|
||||
runnableId={initialPath ?? undefined}
|
||||
runnableType={$pathStore ? 'FlowPath' : undefined}
|
||||
on:select={(e) => {
|
||||
updatePreviewSchemaAndArgs(e.detail ?? undefined)
|
||||
updatePreviewSchemaAndArgs(e.detail?.args ?? undefined)
|
||||
}}
|
||||
/>
|
||||
</FlowInputEditor>
|
||||
|
||||
@@ -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>('FlowEditorContext')
|
||||
let previewOpen = false
|
||||
let previewMode: 'upTo' | 'whole' = 'whole'
|
||||
@@ -91,20 +94,22 @@
|
||||
Test flow
|
||||
</Button>
|
||||
|
||||
<Drawer bind:open={previewOpen} alwaysOpen size="75%" {preventEscape}>
|
||||
<FlowPreviewContent
|
||||
bind:this={flowPreviewContent}
|
||||
open={previewOpen}
|
||||
bind:previewMode
|
||||
bind:job
|
||||
bind:jobId
|
||||
on:close={() => {
|
||||
previewOpen = false
|
||||
}}
|
||||
on:openTriggers={(e) => {
|
||||
previewOpen = false
|
||||
dispatch('openTriggers', e.detail)
|
||||
}}
|
||||
bind:preventEscape
|
||||
/>
|
||||
</Drawer>
|
||||
{#if !loading}
|
||||
<Drawer bind:open={previewOpen} alwaysOpen size="75%" {preventEscape}>
|
||||
<FlowPreviewContent
|
||||
bind:this={flowPreviewContent}
|
||||
open={previewOpen}
|
||||
bind:previewMode
|
||||
bind:job
|
||||
bind:jobId
|
||||
on:close={() => {
|
||||
previewOpen = false
|
||||
}}
|
||||
on:openTriggers={(e) => {
|
||||
previewOpen = false
|
||||
dispatch('openTriggers', e.detail)
|
||||
}}
|
||||
bind:preventEscape
|
||||
/>
|
||||
</Drawer>
|
||||
{/if}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="flex flex-col relative !overflow-visible {expandRight ? 'pr-2 -mr-2' : ''}"
|
||||
class="flex flex-col relative !overflow-visible w-[33px]"
|
||||
on:mouseenter={handleMouseEnter}
|
||||
on:mouseleave={handleMouseLeave}
|
||||
>
|
||||
|
||||
@@ -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<FlowStatusViewerContext>('FlowStatusViewer')
|
||||
@@ -22,7 +23,7 @@
|
||||
target="_blank"
|
||||
href="{base}/run/{job?.id}?workspace={job?.workspace_id}"
|
||||
>
|
||||
{job?.id}
|
||||
{truncateRev(job?.id, 8)}
|
||||
<ExternalLinkIcon size={14} class="inline mb-1 ml-1" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -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(',')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
export let inputSelected: 'history' | 'captures' | 'saved' | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div class="min-h-[34px]">
|
||||
<div class="min-h-[38px]">
|
||||
<div
|
||||
class="rounded-md flex flex-row gap-2 items-center py-1 px-2 w-fit {classes['info']
|
||||
.bgClass} {inputSelected ? '' : 'hidden'}"
|
||||
@@ -24,7 +24,7 @@
|
||||
size="xs2"
|
||||
startIcon={{ icon: X }}
|
||||
shortCut={{ key: 'esc', withoutModifier: true }}
|
||||
nonCaptureEvent
|
||||
on:click
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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<CaptureTriggerKind, string> = {
|
||||
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>('TriggerContext')
|
||||
|
||||
let args: Record<string, any> = {}
|
||||
$: collapsed = !openForm
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let showCapture = false
|
||||
let init = false
|
||||
$: updateShowCapture(!!$captureOn)
|
||||
function updateShowCapture(show: boolean) {
|
||||
@@ -49,55 +52,57 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Section label={captureTypeLabels[triggerType]}>
|
||||
<Section label={captureTypeLabels[triggerType]} collapsable={!alwaysOpened} bind:collapsed>
|
||||
<svelte:fragment slot="action">
|
||||
<div class="flex flex-row grow w-min-0 gap-2 items-center justify-end">
|
||||
{#if isEditor}
|
||||
<Button
|
||||
size="xs2"
|
||||
on:click={() => {
|
||||
showCapture = !showCapture
|
||||
}}
|
||||
variant="border"
|
||||
color="light"
|
||||
endIcon={{
|
||||
icon: ChevronDown,
|
||||
classes: twMerge('transition', showCapture ? 'rotate-180' : '')
|
||||
}}
|
||||
>
|
||||
Test trigger
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
{#if !noSave}
|
||||
{@const disabled = newItem || cloudDisabled}
|
||||
<Popover notClickable>
|
||||
{#if !collapsed || alwaysOpened}
|
||||
<div class="flex flex-row grow w-min-0 gap-2 items-center justify-end">
|
||||
{#if isEditor}
|
||||
<Button
|
||||
size="xs2"
|
||||
{disabled}
|
||||
startIcon={{ icon: Save }}
|
||||
on:click={() => {
|
||||
dispatch('saveTrigger', {
|
||||
config: args
|
||||
})
|
||||
showCapture = !showCapture
|
||||
}}
|
||||
variant="border"
|
||||
color="light"
|
||||
endIcon={{
|
||||
icon: ChevronDown,
|
||||
classes: twMerge('transition', showCapture ? 'rotate-180' : '')
|
||||
}}
|
||||
>
|
||||
Save
|
||||
Test trigger
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
{#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}
|
||||
<Popover notClickable>
|
||||
<Button
|
||||
size="xs2"
|
||||
{disabled}
|
||||
startIcon={{ icon: Save }}
|
||||
on:click={() => {
|
||||
dispatch('saveTrigger', {
|
||||
config: args
|
||||
})
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
{#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}
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
{#if isEditor}
|
||||
|
||||
@@ -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<string, any> = {}
|
||||
|
||||
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}
|
||||
/>
|
||||
|
||||
<div class="flex flex-col gap-8">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Description link="https://www.windmill.dev/docs/core_concepts/http_routing">
|
||||
Routes expose your scripts and flows as HTTP endpoints. Each route can be configured with a
|
||||
specific HTTP method and path.
|
||||
</Description>
|
||||
{#if !newItem && httpTriggers && httpTriggers.length > 0}
|
||||
<Section label="Routes">
|
||||
{#if !$userStore?.is_admin && !$userStore?.is_super_admin}
|
||||
<Alert title="Only workspace admins can create routes" type="warning" size="xs" />
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each httpTriggers as httpTriggers (httpTriggers.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{httpTriggers.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{httpTriggers.http_method.toUpperCase()} /{httpTriggers.route_path}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => routeEditor?.openEdit(httpTriggers.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if httpTriggers.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
|
||||
<TriggersEditorSection
|
||||
on:saveTrigger={(e) => {
|
||||
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}
|
||||
<Section label="Routes">
|
||||
{#if !$userStore?.is_admin && !$userStore?.is_super_admin}
|
||||
<Alert title="Only workspace admins can create routes" type="warning" size="xs" />
|
||||
{/if}
|
||||
|
||||
{#if httpTriggers}
|
||||
{#if httpTriggers.length == 0}
|
||||
<div class="text-xs text-secondary text-center"> No http routes </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each httpTriggers as httpTriggers (httpTriggers.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{httpTriggers.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{httpTriggers.http_method.toUpperCase()} /{httpTriggers.route_path}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => routeEditor?.openEdit(httpTriggers.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if httpTriggers.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
</Section>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 @@
|
||||
<Description link="https://www.windmill.dev/docs/core_concepts/kafka_triggers">
|
||||
Kafka triggers execute scripts and flows in response to messages published to Kafka topics.
|
||||
</Description>
|
||||
{#if !newItem && kafkaTriggers && kafkaTriggers.length > 0}
|
||||
<Section label="Kafka triggers">
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each kafkaTriggers as kafkaTrigger (kafkaTrigger.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{kafkaTrigger.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{kafkaTrigger.kafka_resource_path}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => kafkaTriggerEditor?.openEdit(kafkaTrigger.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if kafkaTrigger.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
<TriggersEditorSection
|
||||
on:saveTrigger={(e) => {
|
||||
saveTrigger(path, e.detail.config)
|
||||
@@ -102,41 +130,8 @@
|
||||
{canHavePreprocessor}
|
||||
{hasPreprocessor}
|
||||
{newItem}
|
||||
{openForm}
|
||||
bind:showCapture={dontCloseOnLoad}
|
||||
/>
|
||||
|
||||
{#if !newItem}
|
||||
{#if kafkaTriggers}
|
||||
<Section label="Kafka triggers">
|
||||
{#if kafkaTriggers.length == 0}
|
||||
<div class="text-xs text-secondary text-center"> No Kafka triggers </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each kafkaTriggers as kafkaTrigger (kafkaTrigger.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{kafkaTrigger.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{kafkaTrigger.kafka_resource_path}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => kafkaTriggerEditor?.openEdit(kafkaTrigger.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if kafkaTrigger.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</Section>
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -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 @@
|
||||
<Description link="https://www.windmill.dev/docs/core_concepts/nats_triggers">
|
||||
NATS triggers execute scripts and flows in response to messages published to NATS subjects.
|
||||
</Description>
|
||||
|
||||
{#if !newItem && natsTriggers && natsTriggers.length > 0}
|
||||
<Section label="NATS Triggers">
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each natsTriggers as natsTrigger (natsTrigger.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{natsTrigger.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{natsTrigger.nats_resource_path}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => natsTriggerEditor?.openEdit(natsTrigger.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if natsTrigger.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
|
||||
<TriggersEditorSection
|
||||
on:saveTrigger={(e) => {
|
||||
saveTrigger(path, e.detail.config)
|
||||
@@ -100,41 +130,8 @@
|
||||
{canHavePreprocessor}
|
||||
{hasPreprocessor}
|
||||
{newItem}
|
||||
{openForm}
|
||||
bind:showCapture={dontCloseOnLoad}
|
||||
/>
|
||||
|
||||
{#if !newItem}
|
||||
{#if natsTriggers}
|
||||
<Section label="NATS Triggers">
|
||||
{#if natsTriggers.length == 0}
|
||||
<div class="text-xs text-secondary text-center"> No nats triggers </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each natsTriggers as natsTrigger (natsTrigger.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{natsTrigger.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{natsTrigger.nats_resource_path}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => natsTriggerEditor?.openEdit(natsTrigger.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if natsTrigger.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</Section>
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -53,5 +53,6 @@
|
||||
{canHavePreprocessor}
|
||||
{hasPreprocessor}
|
||||
{newItem}
|
||||
alwaysOpened={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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.
|
||||
</Description>
|
||||
|
||||
{#if !newItem && wsTriggers && wsTriggers.length > 0}
|
||||
<Section label="WebSockets">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each wsTriggers as wsTriggers (wsTriggers.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{wsTriggers.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{wsTriggers.url}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => wsTriggerEditor?.openEdit(wsTriggers.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if wsTriggers.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
|
||||
<TriggersEditorSection
|
||||
on:applyArgs
|
||||
on:saveTrigger={(e) => {
|
||||
@@ -88,43 +121,7 @@
|
||||
{canHavePreprocessor}
|
||||
{hasPreprocessor}
|
||||
{newItem}
|
||||
{openForm}
|
||||
/>
|
||||
|
||||
{#if !newItem}
|
||||
<Section label="WebSockets">
|
||||
<div class="flex flex-col gap-4">
|
||||
{#if wsTriggers}
|
||||
{#if wsTriggers.length == 0}
|
||||
<div class="text-xs text-secondary text-center"> No WebSocket triggers </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each wsTriggers as wsTriggers (wsTriggers.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{wsTriggers.path}</div>
|
||||
<div class="col-span-2 truncate">
|
||||
{wsTriggers.url}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => wsTriggerEditor?.openEdit(wsTriggers.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if wsTriggers.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -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
|
||||
</script>
|
||||
|
||||
@@ -449,7 +449,12 @@
|
||||
|
||||
<div class="flex flex-col align-left">
|
||||
<div class="flex flex-row justify-between">
|
||||
<InputSelectedBadge {inputSelected} />
|
||||
<InputSelectedBadge
|
||||
on:click={() => {
|
||||
savedInputsV2?.resetSelected()
|
||||
}}
|
||||
{inputSelected}
|
||||
/>
|
||||
<Toggle
|
||||
bind:checked={jsonView}
|
||||
label="JSON View"
|
||||
@@ -525,6 +530,7 @@
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="save_inputs">
|
||||
<SavedInputsV2
|
||||
bind:this={savedInputsV2}
|
||||
{jsonView}
|
||||
flowPath={flow?.path}
|
||||
{isValid}
|
||||
|
||||
@@ -496,6 +496,8 @@
|
||||
|
||||
let token = 'TOKEN_TO_CREATE'
|
||||
let rightPaneSelected = 'saved_inputs'
|
||||
|
||||
let savedInputsV2: SavedInputsV2 | undefined = undefined
|
||||
</script>
|
||||
|
||||
<MoveDrawer
|
||||
@@ -652,7 +654,12 @@
|
||||
|
||||
<div class="flex flex-col align-left">
|
||||
<div class="flex flex-row justify-between">
|
||||
<InputSelectedBadge {inputSelected} />
|
||||
<InputSelectedBadge
|
||||
on:click={() => {
|
||||
savedInputsV2?.resetSelected()
|
||||
}}
|
||||
{inputSelected}
|
||||
/>
|
||||
<Toggle
|
||||
bind:checked={jsonView}
|
||||
label="JSON View"
|
||||
@@ -715,6 +722,7 @@
|
||||
<svelte:fragment slot="save_inputs">
|
||||
{#if args}
|
||||
<SavedInputsV2
|
||||
bind:this={savedInputsV2}
|
||||
scriptPath={script?.path}
|
||||
scriptHash={topHash}
|
||||
{isValid}
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.455.1"
|
||||
wmill_pg = ">=1.455.1"
|
||||
wmill = ">=1.456.0"
|
||||
wmill_pg = ">=1.456.0"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.455.1
|
||||
version: 1.456.0
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'WindmillClient.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.455.1'
|
||||
ModuleVersion = '1.456.0'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.455.1"
|
||||
version = "1.456.0"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.455.1"
|
||||
version = "1.456.0"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@windmill/windmill",
|
||||
"version": "1.455.1",
|
||||
"version": "1.456.0",
|
||||
"exports": "./src/index.ts",
|
||||
"publish": {
|
||||
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.455.1",
|
||||
"version": "1.456.0",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.455.1
|
||||
1.456.0
|
||||
|
||||
Reference in New Issue
Block a user