Compare commits
3 Commits
v1.87.0
...
rf/profile
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8f617f80b | ||
|
|
e2f0584cea | ||
|
|
bdf717c7b5 |
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,39 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [1.87.0](https://github.com/windmill-labs/windmill/compare/v1.86.0...v2.0.0) (2023-04-11)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **frontend:** Add option to return file names ([#1380](https://github.com/windmill-labs/windmill/issues/1380))
|
||||
|
||||
### Features
|
||||
|
||||
* **backend:** add instance events webhook ([f2d3c82](https://github.com/windmill-labs/windmill/commit/f2d3c8208b6daa49f304f355752145de47138a3c))
|
||||
* **backend:** extend cached resolution for go ([dac61d1](https://github.com/windmill-labs/windmill/commit/dac61d1c982576d7589e16ab01c8cc8bad6e1686))
|
||||
* **backend:** Redis based queue ([#1324](https://github.com/windmill-labs/windmill/issues/1324)) ([d45e6c9](https://github.com/windmill-labs/windmill/commit/d45e6c94abed609357b18d4daa7de6b2ea0ba978))
|
||||
* **frontend:** Add option to return file names ([#1380](https://github.com/windmill-labs/windmill/issues/1380)) ([3dabac1](https://github.com/windmill-labs/windmill/commit/3dabac153f302f48210d15ebaec514e72717300f))
|
||||
* **python:** cache dependency resolution ([facb670](https://github.com/windmill-labs/windmill/commit/facb67093ce7d3b0874d0d559fb272ed822ce360))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **backend:** nested deno relative imports ([955a213](https://github.com/windmill-labs/windmill/commit/955a213a504c1f3b8811c930823e87fe7dba101a))
|
||||
* **cli:** overwrite archived scripts ([1f705ca](https://github.com/windmill-labs/windmill/commit/1f705cab2ce8c79829f22fc6af9e06ecba7450b1))
|
||||
* **frontend:** Add missing stopPropagation ([#1394](https://github.com/windmill-labs/windmill/issues/1394)) ([58d4b55](https://github.com/windmill-labs/windmill/commit/58d4b556ebbd76c6f07f1a16d601a9d824b99f7e))
|
||||
* **frontend:** fix app init issue ([d0e0e1f](https://github.com/windmill-labs/windmill/commit/d0e0e1fdf27d9a7fb86c66e43398786b64d8b6b7))
|
||||
* **frontend:** Fix frontend dependencies ([#1379](https://github.com/windmill-labs/windmill/issues/1379)) ([8e9c491](https://github.com/windmill-labs/windmill/commit/8e9c49165060a4a7f831b8be075593f89d867784))
|
||||
* **frontend:** Fix icon picker input ([#1389](https://github.com/windmill-labs/windmill/issues/1389)) ([8a44f8e](https://github.com/windmill-labs/windmill/commit/8a44f8e7796f13698e2a99af9f3772f5e676604b))
|
||||
* **frontend:** Fix mac shortcuts ([#1381](https://github.com/windmill-labs/windmill/issues/1381)) ([41831d5](https://github.com/windmill-labs/windmill/commit/41831d58ed593bb283600b76170f6e76783e0eae))
|
||||
* **frontend:** fix popover configuration to avoid content shift ([#1377](https://github.com/windmill-labs/windmill/issues/1377)) ([2031e1e](https://github.com/windmill-labs/windmill/commit/2031e1ebd0dc020da104ee84a0294c86babcefaf))
|
||||
* **frontend:** remove stopPropagation that was preventing components dnd ([#1378](https://github.com/windmill-labs/windmill/issues/1378)) ([de8dc1e](https://github.com/windmill-labs/windmill/commit/de8dc1e9cd7beea2ce62656e9e7676214f77a110))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* parallelize more operations for deno jobs ([e911869](https://github.com/windmill-labs/windmill/commit/e911869d990956463834ac9ff35c52ba8236e362))
|
||||
|
||||
## [1.86.0](https://github.com/windmill-labs/windmill/compare/v1.85.0...v1.86.0) (2023-04-08)
|
||||
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ COPY .git/ .git/
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$features"
|
||||
|
||||
|
||||
FROM python:3.11.3-slim-buster
|
||||
FROM python:3.11.2-slim-buster
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
ARG APP=/usr/src/app
|
||||
|
||||
10
README.md
10
README.md
@@ -70,10 +70,10 @@ https://user-images.githubusercontent.com/275584/218350457-bc2fdc3b-e667-4da5-a2
|
||||
|
||||
3. Make it flow! You can chain your scripts or scripts made by the community
|
||||
shared on [WindmillHub](https://hub.windmill.dev).
|
||||

|
||||

|
||||
|
||||
4. Build complex UI on top of your scripts and flows.
|
||||

|
||||

|
||||
|
||||
Scripts and flows can also be triggered by a cron schedule '_/5 _ \* \* \*' or
|
||||
through webhooks.
|
||||
@@ -123,7 +123,7 @@ instance from local commands. See
|
||||
### Running scripts locally
|
||||
|
||||
You can run your script locally easily, you simply need to pass the right
|
||||
environment variables for the `wmill` client library to fetch resources and
|
||||
environment variables for the `wmill` client library to fetch resource and
|
||||
variables from your instance if necessary. See more:
|
||||
<https://docs.windmill.dev/docs/advanced/local_development/>
|
||||
|
||||
@@ -315,8 +315,6 @@ it being synced automatically everyday.
|
||||
| DENO_PATH | /usr/bin/deno | The path to the deno binary. | Worker |
|
||||
| PYTHON_PATH | /usr/local/bin/python3 | The path to the python binary. | Worker |
|
||||
| GO_PATH | /usr/bin/go | The path to the go binary. | Worker |
|
||||
| GOPRIVATE | | The GOPRIVATE env variable to use private go modules | Worker |
|
||||
| NETRC | | The netrc content to use a private go registry | Worker |
|
||||
| PIP_INDEX_URL | None | The index url to pass for pip. | Worker |
|
||||
| PIP_EXTRA_INDEX_URL | None | The extra index url to pass to pip. | Worker |
|
||||
| PIP_TRUSTED_HOST | None | The trusted host to pass to pip. | Worker |
|
||||
@@ -334,7 +332,7 @@ it being synced automatically everyday.
|
||||
| INCLUDE_HEADERS | None | Whitelist of headers that are passed to jobs as args (separated by a comma) | Server |
|
||||
| WHITELIST_WORKSPACES | None | Whitelist of workspaces this worker takes job from | Worker |
|
||||
| BLACKLIST_WORKSPACES | None | Blacklist of workspaces this worker takes job from | Worker |
|
||||
| INSTANCE_EVENTS_WEBHOOK | None | Webhook to notify of events such as new user added, signup/invite. Can hook back to windmill to send emails | Server |
|
||||
| NEW_USER_WEBHOOK | None | Webhook to notify of a new user added, signup/invite. Can hook back to windmill to send emails | Server |
|
||||
|
||||
## Run a local dev setup
|
||||
|
||||
|
||||
@@ -18,4 +18,4 @@ rustflags = [
|
||||
rustflags = [
|
||||
"-C", "link-arg=-undefined",
|
||||
"-C", "link-arg=dynamic_lookup",
|
||||
]
|
||||
]
|
||||
|
||||
352
backend/Cargo.lock
generated
352
backend/Cargo.lock
generated
@@ -178,55 +178,6 @@ dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-global-executor"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"futures-lite",
|
||||
"log",
|
||||
"parking",
|
||||
"polling",
|
||||
"rustix",
|
||||
"slab",
|
||||
"socket2",
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.7.0"
|
||||
@@ -263,33 +214,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"crossbeam-utils",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"gloo-timers",
|
||||
"kv-log-macro",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
"wasm-bindgen-futures",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -315,12 +240,6 @@ dependencies = [
|
||||
"uuid 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
|
||||
|
||||
[[package]]
|
||||
name = "async-timer"
|
||||
version = "0.7.4"
|
||||
@@ -340,7 +259,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -367,12 +286,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@@ -381,9 +294,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.14"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e5abc76e6ef57bf438c067d2898481475994102eeb5d662a0b2162d0c2fdcf1"
|
||||
checksum = "349f8ccfd9221ee7d1f3d4b33e1f8319b3a81ed8f61f2ea40b37b859794b4491"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
@@ -414,9 +327,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.3.4"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
|
||||
checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -447,19 +360,6 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bb8"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e9f4fa9768efd269499d8fba693260cfc670891cf6de3adc935588447a77cc8"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"parking_lot 0.11.2",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "better_scoped_tls"
|
||||
version = "0.1.0"
|
||||
@@ -534,21 +434,6 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-lock",
|
||||
"async-task",
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
@@ -716,7 +601,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -735,20 +620,6 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concolor-override"
|
||||
version = "1.0.0"
|
||||
@@ -921,16 +792,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.94"
|
||||
@@ -955,7 +816,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -972,7 +833,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1426,7 +1287,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1535,23 +1396,11 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gosyn"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248a4b26b282ec2e90272f7e7bd4764eaa6378dee13497ef09aa0efebd80cc85"
|
||||
checksum = "1584c4cbcb6f1d97481c19d1951c64f6e1256e5a03c28159ae45431c00511ee7"
|
||||
dependencies = [
|
||||
"strum",
|
||||
"unic-ucd-category",
|
||||
@@ -1945,15 +1794,6 @@ version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37"
|
||||
|
||||
[[package]]
|
||||
name = "kv-log-macro"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.19.9"
|
||||
@@ -2136,7 +1976,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2415,9 +2254,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.50"
|
||||
version = "0.10.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1"
|
||||
checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -2436,7 +2275,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2447,9 +2286,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.85"
|
||||
version = "0.9.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d3d193fb1488ad46ffe3aaabc912cc931d02ee8518fe2959aea8ef52718b0c0"
|
||||
checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -2726,22 +2565,6 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"libc",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
@@ -2827,7 +2650,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
"thiserror",
|
||||
"typify",
|
||||
"unicode-ident",
|
||||
@@ -2847,7 +2670,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_tokenstream",
|
||||
"serde_yaml",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2888,12 +2711,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radix_fmt"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
@@ -2965,27 +2782,6 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ea8c51b5dc1d8e5fd3350ec8167f464ec0995e79f2e90a075b63371500d557f"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"combine",
|
||||
"futures-util",
|
||||
"itoa",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"ryu",
|
||||
"sha1_smol",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@@ -3138,21 +2934,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsmq_async"
|
||||
version = "5.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "086675eeb88ca55fa3b25c0075615d31dd9eb0adee219a9dcd999090cb371cab"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bb8",
|
||||
"lazy_static",
|
||||
"radix_fmt",
|
||||
"rand 0.8.5",
|
||||
"redis",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.6.1"
|
||||
@@ -3262,7 +3043,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#c9546c2419f2e4db7e2987f30f0508dc29b30035"
|
||||
source = "git+https://github.com/RustPython/RustPython#dde92d1269b5fbd09cf5dba5e7817f0e2a96c49f"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-compiler-core",
|
||||
@@ -3271,7 +3052,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#c9546c2419f2e4db7e2987f30f0508dc29b30035"
|
||||
source = "git+https://github.com/RustPython/RustPython#dde92d1269b5fbd09cf5dba5e7817f0e2a96c49f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bstr",
|
||||
@@ -3284,7 +3065,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#c9546c2419f2e4db7e2987f30f0508dc29b30035"
|
||||
source = "git+https://github.com/RustPython/RustPython#dde92d1269b5fbd09cf5dba5e7817f0e2a96c49f"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
"anyhow",
|
||||
@@ -3346,7 +3127,7 @@ dependencies = [
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid 1.3.1",
|
||||
"uuid 1.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3456,9 +3237,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
version = "1.0.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -3485,13 +3266,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.160"
|
||||
version = "1.0.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
||||
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3557,7 +3338,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3611,12 +3392,6 @@ dependencies = [
|
||||
"digest 0.10.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
@@ -3843,7 +3618,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"uuid 1.3.1",
|
||||
"uuid 1.3.0",
|
||||
"webpki-roots",
|
||||
"whoami",
|
||||
]
|
||||
@@ -4129,9 +3904,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.14"
|
||||
version = "2.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5"
|
||||
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4194,7 +3969,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4308,7 +4083,7 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4621,7 +4396,7 @@ dependencies = [
|
||||
"regress",
|
||||
"schemars",
|
||||
"serde_json",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
"thiserror",
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -4637,7 +4412,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.14",
|
||||
"syn 2.0.13",
|
||||
"typify-impl",
|
||||
]
|
||||
|
||||
@@ -4654,7 +4429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13a3aaa69b04e5b66cc27309710a569ea23593612387d67daaf102e73aa974fd"
|
||||
dependencies = [
|
||||
"rand 0.8.5",
|
||||
"uuid 1.3.1",
|
||||
"uuid 1.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4842,9 +4617,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb"
|
||||
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"serde",
|
||||
@@ -4868,16 +4643,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.0.0-alpha.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
@@ -5096,7 +4861,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -5108,7 +4873,6 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"rsa",
|
||||
"rsmq_async",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.6",
|
||||
@@ -5116,7 +4880,6 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-metrics",
|
||||
"tracing",
|
||||
"url",
|
||||
"windmill-api",
|
||||
"windmill-api-client",
|
||||
"windmill-common",
|
||||
@@ -5126,7 +4889,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -5153,7 +4916,6 @@ dependencies = [
|
||||
"regex",
|
||||
"reqwest",
|
||||
"retainer",
|
||||
"rsmq_async",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5184,7 +4946,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
@@ -5194,12 +4956,12 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid 1.3.1",
|
||||
"uuid 1.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -5212,7 +4974,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -5232,12 +4994,12 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid 1.3.1",
|
||||
"uuid 1.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5245,7 +5007,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -5260,7 +5022,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -5273,7 +5035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -5288,7 +5050,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deno_core",
|
||||
@@ -5302,34 +5064,31 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"cron",
|
||||
"futures-core",
|
||||
"hex",
|
||||
"hmac",
|
||||
"lazy_static",
|
||||
"prometheus",
|
||||
"reqwest",
|
||||
"rsmq_async",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sql-builder",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"ulid",
|
||||
"uuid 1.3.1",
|
||||
"uuid 1.3.0",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -5346,13 +5105,12 @@ dependencies = [
|
||||
"prometheus",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"rsmq_async",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid 1.3.1",
|
||||
"uuid 1.3.0",
|
||||
"windmill-api-client",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.87.0"
|
||||
version = "1.86.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -48,8 +48,6 @@ git-version.workspace = true
|
||||
rsa.workspace = true
|
||||
base64.workspace = true
|
||||
sha2.workspace = true
|
||||
rsmq_async.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -143,7 +141,6 @@ sqlx = { version = "^0", features = [
|
||||
dotenv = "^0"
|
||||
ulid = { version = "^1", features = ["uuid"] }
|
||||
futures = "^0"
|
||||
futures-core = "^0"
|
||||
tokio-metrics = "0.1.0"
|
||||
lazy_static = "1.4.0"
|
||||
serde_derive = "1.0.147"
|
||||
@@ -156,7 +153,4 @@ async-stripe = { version = "0.14", features = [
|
||||
] }
|
||||
async_zip = { version = "0.0.11", features = ["full"] }
|
||||
once_cell = "1.17.1"
|
||||
rsmq_async = { version = "5.1.5" }
|
||||
gosyn = "0.2.2"
|
||||
|
||||
[patch.crates-io]
|
||||
|
||||
@@ -103,11 +103,6 @@ fn parse_param(x: Param, cm: &Lrc<SourceMap>, skip_dflt: bool) -> error::Result<
|
||||
Expr::Lit(Lit::Str(Str { value, .. })) => {
|
||||
Some(Value::String(value.to_string()))
|
||||
}
|
||||
Expr::Lit(Lit::Num(Number { value, .. }))
|
||||
if (value == (value as u64) as f64) =>
|
||||
{
|
||||
Some(serde_json::json!(value as u64))
|
||||
}
|
||||
Expr::Lit(Lit::Num(Number { value, .. })) => Some(serde_json::json!(value)),
|
||||
Expr::Lit(Lit::BigInt(BigInt { value, .. })) => Some(serde_json::json!(value)),
|
||||
Expr::Lit(Lit::Bool(Bool { value, .. })) => Some(Value::Bool(value)),
|
||||
|
||||
@@ -3957,6 +3957,76 @@
|
||||
},
|
||||
"query": "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND archived = false AND workspace_id = $2)"
|
||||
},
|
||||
"a1c41bbeb2d64fa1e7dfd2ed053191a1de5d786ae8c22e225e450865ecac94e9": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Uuid",
|
||||
"Bool",
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Timestamptz",
|
||||
"Int8",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Jsonb",
|
||||
{
|
||||
"Custom": {
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies"
|
||||
]
|
||||
},
|
||||
"name": "job_kind"
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Bool",
|
||||
{
|
||||
"Custom": {
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"python3",
|
||||
"deno",
|
||||
"go",
|
||||
"bash"
|
||||
]
|
||||
},
|
||||
"name": "script_lang"
|
||||
}
|
||||
},
|
||||
"Bool",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Uuid"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO queue\n (workspace_id, id, running, parent_job, created_by, permissioned_as, scheduled_for, \n script_hash, script_path, raw_code, raw_lock, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step, language, started_at, same_worker, pre_run_error, email, visible_to_owner, root_job)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, CASE WHEN $3 THEN now() END, $19, $20, $21, $22, $23) RETURNING id"
|
||||
},
|
||||
"a227548b6604c56bfc15eb780bd8ee72a89dc6701a50f5048e928bd87baa7b9a": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -4966,26 +5036,6 @@
|
||||
},
|
||||
"query": "\n INSERT INTO resume_job\n (id, resume_id, job, flow, value, approver)\n VALUES ($1, $2, $3, $4, $5, $6)\n "
|
||||
},
|
||||
"c4fb68ce79c1cec23cadc515285578ca18398504cf703091d06191ff68d95399": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "database_length!",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE workspace_id = $1"
|
||||
},
|
||||
"c59dd666b9a316c027e8c319b80ccbab3a220d93b64357981bd4a03324dad1d0": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -6072,76 +6122,6 @@
|
||||
},
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM schedule WHERE path = $1 AND workspace_id = $2)"
|
||||
},
|
||||
"f014bbaecaa09840c6b66d8b58b942fcf9827a7dfd9450f225fddd2b404c4f36": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Uuid",
|
||||
"Bool",
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Timestamptz",
|
||||
"Int8",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Jsonb",
|
||||
{
|
||||
"Custom": {
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies"
|
||||
]
|
||||
},
|
||||
"name": "job_kind"
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Bool",
|
||||
{
|
||||
"Custom": {
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"python3",
|
||||
"deno",
|
||||
"go",
|
||||
"bash"
|
||||
]
|
||||
},
|
||||
"name": "script_lang"
|
||||
}
|
||||
},
|
||||
"Bool",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Uuid"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO queue\n (workspace_id, id, running, parent_job, created_by, permissioned_as, scheduled_for, \n script_hash, script_path, raw_code, raw_lock, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step, language, started_at, same_worker, pre_run_error, email, visible_to_owner, root_job)\n VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, now()), $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, CASE WHEN $3 THEN now() END, $19, $20, $21, $22, $23) RETURNING id"
|
||||
},
|
||||
"f056b5f3e66a764748925f1bfd3180923fde8c7fdf69088d0e4a5555cc049545": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
|
||||
@@ -49,40 +49,8 @@ async fn main() -> anyhow::Result<()> {
|
||||
.and_then(|x| x.parse::<bool>().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let rsmq_config = std::env::var("REDIS_URL").ok().map(|x| {
|
||||
let url = x.parse::<url::Url>().unwrap();
|
||||
let mut config = rsmq_async::RsmqOptions { ..Default::default() };
|
||||
|
||||
config.host = url.host_str().expect("redis host required").to_owned();
|
||||
config.password = url.password().map(|s| s.to_owned());
|
||||
config.db = url
|
||||
.path_segments()
|
||||
.and_then(|mut segments| segments.next())
|
||||
.and_then(|segment| segment.parse().ok())
|
||||
.unwrap_or(0);
|
||||
config.ns = url
|
||||
.query_pairs()
|
||||
.find(|s| s.0 == "rsmq_namespace")
|
||||
.map(|s| s.1)
|
||||
.unwrap_or(std::borrow::Cow::Borrowed("rsmq"))
|
||||
.into_owned();
|
||||
config.port = url.port().unwrap_or(6379).to_string();
|
||||
|
||||
config
|
||||
});
|
||||
|
||||
let db = windmill_common::connect_db(server_mode).await?;
|
||||
|
||||
let rsmq = if let Some(config) = rsmq_config {
|
||||
let mut rsmq = rsmq_async::MultiplexedRsmq::new(config).await.unwrap();
|
||||
|
||||
let _ = rsmq_async::RsmqConnection::create_queue(&mut rsmq, "main_queue", None, None, None)
|
||||
.await;
|
||||
Some(rsmq)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if server_mode {
|
||||
windmill_api::migrate_db(&db).await?;
|
||||
}
|
||||
@@ -128,8 +96,6 @@ Windmill Community Edition {GIT_VERSION}
|
||||
"PYTHON_PATH",
|
||||
"DENO_PATH",
|
||||
"GO_PATH",
|
||||
"GOPRIVATE",
|
||||
"NETRC",
|
||||
"PIP_INDEX_URL",
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_TRUSTED_HOST",
|
||||
@@ -146,17 +112,16 @@ Windmill Community Edition {GIT_VERSION}
|
||||
"INCLUDE_HEADERS",
|
||||
"WHITELIST_WORKSPACES",
|
||||
"BLACKLIST_WORKSPACES",
|
||||
"INSTANCE_EVENTS_WEBHOOK",
|
||||
"NEW_USER_WEBHOOK",
|
||||
"CLOUD_HOSTED",
|
||||
]);
|
||||
|
||||
if server_mode || num_workers > 0 {
|
||||
let addr = SocketAddr::from((server_bind_address, port));
|
||||
|
||||
let rsmq2 = rsmq.clone();
|
||||
let server_f = async {
|
||||
if server_mode {
|
||||
windmill_api::run_server(db.clone(), rsmq2, addr, rx.resubscribe()).await?;
|
||||
windmill_api::run_server(db.clone(), addr, rx.resubscribe()).await?;
|
||||
}
|
||||
Ok(()) as anyhow::Result<()>
|
||||
};
|
||||
@@ -168,17 +133,15 @@ Windmill Community Edition {GIT_VERSION}
|
||||
rx.resubscribe(),
|
||||
num_workers,
|
||||
base_internal_url.clone(),
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(()) as anyhow::Result<()>
|
||||
};
|
||||
|
||||
let rsmq2 = rsmq.clone();
|
||||
let monitor_f = async {
|
||||
if server_mode {
|
||||
monitor_db(&db, rx.resubscribe(), &base_internal_url, rsmq2);
|
||||
monitor_db(&db, rx.resubscribe(), &base_internal_url);
|
||||
}
|
||||
Ok(()) as anyhow::Result<()>
|
||||
};
|
||||
@@ -218,11 +181,10 @@ fn display_config(envs: Vec<&str>) {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn monitor_db<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 'static>(
|
||||
pub fn monitor_db(
|
||||
db: &Pool<Postgres>,
|
||||
rx: tokio::sync::broadcast::Receiver<()>,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) {
|
||||
let db1 = db.clone();
|
||||
let db2 = db.clone();
|
||||
@@ -230,17 +192,16 @@ pub fn monitor_db<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 'static>
|
||||
let rx2 = rx.resubscribe();
|
||||
let base_internal_url = base_internal_url.to_string();
|
||||
tokio::spawn(async move {
|
||||
windmill_worker::handle_zombie_jobs_periodically(&db1, rx, &base_internal_url, rsmq).await
|
||||
windmill_worker::handle_zombie_jobs_periodically(&db1, rx, &base_internal_url).await
|
||||
});
|
||||
tokio::spawn(async move { windmill_api::delete_expired_items_perdiodically(&db2, rx2).await });
|
||||
}
|
||||
|
||||
pub async fn run_workers<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 'static>(
|
||||
pub async fn run_workers(
|
||||
db: Pool<Postgres>,
|
||||
rx: tokio::sync::broadcast::Receiver<()>,
|
||||
num_workers: i32,
|
||||
base_internal_url: String,
|
||||
rsmq: Option<R>,
|
||||
) -> anyhow::Result<()> {
|
||||
let license_key = std::env::var("LICENSE_KEY").ok();
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -270,7 +231,6 @@ pub async fn run_workers<R: rsmq_async::RsmqConnection + Send + Sync + Clone + '
|
||||
let ip = ip.clone();
|
||||
let rx = rx.resubscribe();
|
||||
let base_internal_url = base_internal_url.clone();
|
||||
let rsmq2 = rsmq.clone();
|
||||
handles.push(tokio::spawn(monitor.instrument(async move {
|
||||
tracing::info!(worker = %worker_name, "starting worker");
|
||||
windmill_worker::run_worker(
|
||||
@@ -281,7 +241,6 @@ pub async fn run_workers<R: rsmq_async::RsmqConnection + Send + Sync + Clone + '
|
||||
&ip,
|
||||
rx,
|
||||
&base_internal_url,
|
||||
rsmq2,
|
||||
)
|
||||
.await
|
||||
})));
|
||||
|
||||
@@ -88,7 +88,7 @@ impl ApiServer {
|
||||
let addr = sock.local_addr().unwrap();
|
||||
drop(sock);
|
||||
|
||||
let task = tokio::task::spawn(windmill_api::run_server(db.clone(), None, addr, rx));
|
||||
let task = tokio::task::spawn(windmill_api::run_server(db.clone(), addr, rx));
|
||||
|
||||
return Self { addr, tx, task };
|
||||
}
|
||||
@@ -656,7 +656,7 @@ def main(error, port):
|
||||
},
|
||||
}))
|
||||
.unwrap();
|
||||
let (_attempts, responses) = [
|
||||
let (attempts, responses) = [
|
||||
/* fail the first step twice */
|
||||
(0x00, None),
|
||||
(0x00, None),
|
||||
@@ -678,20 +678,14 @@ def main(error, port):
|
||||
_ => panic!("expected failure module"),
|
||||
}
|
||||
|
||||
println!("result: {:#?}", result);
|
||||
assert_eq!(server.close().await, attempts);
|
||||
assert_eq!(
|
||||
result
|
||||
.get("from failure module")
|
||||
.unwrap()
|
||||
.get("error")
|
||||
.unwrap()
|
||||
.get("name")
|
||||
.unwrap()
|
||||
.clone(),
|
||||
json!("IndexError")
|
||||
result,
|
||||
json!({
|
||||
"recv": 42,
|
||||
"from failure module": {"error": {"name": "IndexError", "stack": " File \"/tmp/tmp/main/step_0.py\", line 5, in main\n return sock.recv(1)[0]\n", "message": "index out of range"}},
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(result.get("recv").unwrap().clone(), json!(42));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -827,8 +821,9 @@ impl RunJob {
|
||||
|
||||
async fn push(self, db: &Pool<Postgres>) -> Uuid {
|
||||
let RunJob { payload, args } = self;
|
||||
let (uuid, tx) = windmill_queue::push::<rsmq_async::MultiplexedRsmq>(
|
||||
(None, db.begin().await.unwrap()).into(),
|
||||
let tx = db.begin().await.unwrap();
|
||||
let (uuid, tx) = windmill_queue::push(
|
||||
tx,
|
||||
"test-workspace",
|
||||
payload,
|
||||
args,
|
||||
@@ -846,7 +841,8 @@ impl RunJob {
|
||||
)
|
||||
.await
|
||||
.expect("push has to succeed");
|
||||
tx.commit().await.unwrap();
|
||||
|
||||
tx.commit().await.expect("push has to commit");
|
||||
|
||||
uuid
|
||||
}
|
||||
@@ -916,7 +912,7 @@ fn spawn_test_worker(
|
||||
let ip: &str = Default::default();
|
||||
let future = async move {
|
||||
let base_internal_url = format!("http://localhost:{}", port);
|
||||
windmill_worker::run_worker::<rsmq_async::MultiplexedRsmq>(
|
||||
windmill_worker::run_worker(
|
||||
&db,
|
||||
worker_instance,
|
||||
worker_name,
|
||||
@@ -924,7 +920,6 @@ fn spawn_test_worker(
|
||||
ip,
|
||||
rx,
|
||||
&base_internal_url,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
};
|
||||
@@ -2073,7 +2068,7 @@ async fn test_branchall_skip_failure(db: Pool<Postgres>) {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<ErrorResult>(result.get(0).unwrap().clone())
|
||||
serde_json::from_value::<ErrorResult>(result)
|
||||
.unwrap()
|
||||
.error
|
||||
.name,
|
||||
@@ -2110,7 +2105,7 @@ async fn test_branchall_skip_failure(db: Pool<Postgres>) {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<ErrorResult>(result.get(0).unwrap().clone())
|
||||
serde_json::from_value::<ErrorResult>(result)
|
||||
.unwrap()
|
||||
.error
|
||||
.name,
|
||||
|
||||
@@ -70,5 +70,4 @@ async-stripe = { workspace = true, optional = true }
|
||||
lazy_static.workspace = true
|
||||
prometheus.workspace = true
|
||||
async_zip.workspace = true
|
||||
rsmq_async.workspace = true
|
||||
regex.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.87.0
|
||||
version: 1.86.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
|
||||
@@ -37,7 +37,7 @@ use windmill_common::{
|
||||
http_get_from_hub, list_elems_from_hub, not_found_if_none, paginate, Pagination, StripPath,
|
||||
},
|
||||
};
|
||||
use windmill_queue::{push, JobPayload, QueueTransaction, RawCode};
|
||||
use windmill_queue::{push, JobPayload, RawCode};
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
@@ -565,7 +565,6 @@ fn digest(code: &str) -> String {
|
||||
async fn execute_component(
|
||||
OptAuthed(opt_authed): OptAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(payload): Json<ExecuteApp>,
|
||||
) -> Result<String> {
|
||||
@@ -584,7 +583,7 @@ async fn execute_component(
|
||||
};
|
||||
|
||||
let path = path.to_path();
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, db.begin().await?).into();
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
let policy = if let Some(static_fields) = payload.clone().force_viewer_static_fields {
|
||||
let mut hm = HashMap::new();
|
||||
@@ -653,12 +652,8 @@ async fn execute_component(
|
||||
}
|
||||
ExecuteApp { args, raw_code: None, path: Some(path), .. } => {
|
||||
let payload = if path.starts_with("script/") {
|
||||
script_path_to_payload(
|
||||
path.strip_prefix("script/").unwrap(),
|
||||
tx.transaction_mut(),
|
||||
&w_id,
|
||||
)
|
||||
.await?
|
||||
script_path_to_payload(path.strip_prefix("script/").unwrap(), &mut tx, &w_id)
|
||||
.await?
|
||||
} else if path.starts_with("flow/") {
|
||||
JobPayload::Flow(path.strip_prefix("flow/").unwrap().to_string())
|
||||
} else {
|
||||
@@ -691,8 +686,8 @@ async fn execute_component(
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(uuid.to_string())
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use windmill_common::{
|
||||
http_get_from_hub, list_elems_from_hub, not_found_if_none, paginate, Pagination, StripPath,
|
||||
},
|
||||
};
|
||||
use windmill_queue::{push, schedule::push_scheduled_job, JobPayload, QueueTransaction};
|
||||
use windmill_queue::{push, schedule::push_scheduled_job, JobPayload};
|
||||
|
||||
use crate::{
|
||||
db::{UserDB, DB},
|
||||
@@ -180,7 +180,6 @@ async fn create_flow(
|
||||
authed: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(nf): Json<NewFlow>,
|
||||
@@ -188,10 +187,10 @@ async fn create_flow(
|
||||
// cron::Schedule::from_str(&ns.schedule).map_err(|e| error::Error::BadRequest(e.to_string()))?;
|
||||
let authed = maybe_refresh_folders(&nf.path, &w_id, authed, &db).await;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.clone().begin(&authed).await?).into();
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
|
||||
check_path_conflict(tx.transaction_mut(), &w_id, &nf.path).await?;
|
||||
check_schedule_conflict(tx.transaction_mut(), &w_id, &nf.path).await?;
|
||||
check_path_conflict(&mut tx, &w_id, &nf.path).await?;
|
||||
check_schedule_conflict(&mut tx, &w_id, &nf.path).await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO flow (workspace_id, path, summary, description, value, edited_by, edited_at, \
|
||||
@@ -223,11 +222,13 @@ async fn create_flow(
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateFlow { workspace: w_id.clone(), path: nf.path.clone() },
|
||||
);
|
||||
|
||||
let tx = user_db.begin(&authed).await?;
|
||||
let (dependency_job_uuid, mut tx) = push(
|
||||
tx,
|
||||
&w_id,
|
||||
@@ -246,7 +247,6 @@ async fn create_flow(
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE flow SET dependency_job = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
dependency_job_uuid,
|
||||
@@ -286,7 +286,6 @@ async fn check_schedule_conflict<'c>(
|
||||
async fn update_flow(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path((w_id, flow_path)): Path<(String, StripPath)>,
|
||||
@@ -295,9 +294,9 @@ async fn update_flow(
|
||||
let flow_path = flow_path.to_path();
|
||||
let authed = maybe_refresh_folders(&flow_path, &w_id, authed, &db).await;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.clone().begin(&authed).await?).into();
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
|
||||
check_schedule_conflict(tx.transaction_mut(), &w_id, flow_path).await?;
|
||||
check_schedule_conflict(&mut tx, &w_id, flow_path).await?;
|
||||
|
||||
let schema = nf.schema.map(|x| x.0);
|
||||
let old_dep_job = sqlx::query_scalar!(
|
||||
@@ -324,13 +323,13 @@ async fn update_flow(
|
||||
.await?;
|
||||
|
||||
if nf.path != flow_path {
|
||||
check_schedule_conflict(tx.transaction_mut(), &w_id, &nf.path).await?;
|
||||
check_schedule_conflict(&mut tx, &w_id, &nf.path).await?;
|
||||
|
||||
if !authed.is_admin {
|
||||
require_owner_of_path(&w_id, &authed.username, &authed.groups, &flow_path, &db).await?;
|
||||
}
|
||||
|
||||
let mut schedulables: Vec<Schedule> = sqlx::query_as!(
|
||||
let mut schedulables = sqlx::query_as!(
|
||||
Schedule,
|
||||
"UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS true RETURNING *",
|
||||
nf.path,
|
||||
@@ -353,32 +352,32 @@ async fn update_flow(
|
||||
schedulables.push(schedule);
|
||||
}
|
||||
|
||||
for schedule in schedulables.into_iter() {
|
||||
// TODO: Why is this in the loop in the first place? Seems like it's just doing nothing after the first iteration? Should this use schedule.path?
|
||||
clear_schedule(tx.transaction_mut(), flow_path, true).await?;
|
||||
for schedule in schedulables {
|
||||
clear_schedule(&mut tx, flow_path, true).await?;
|
||||
|
||||
if schedule.enabled {
|
||||
tx = push_scheduled_job(tx, schedule).await?;
|
||||
}
|
||||
}
|
||||
|
||||
audit_log(
|
||||
&mut tx,
|
||||
&authed.username,
|
||||
"flows.update",
|
||||
ActionKind::Create,
|
||||
&w_id,
|
||||
Some(&nf.path.to_string()),
|
||||
Some(
|
||||
[Some(("flow", nf.path.as_str()))]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
audit_log(
|
||||
&mut tx,
|
||||
&authed.username,
|
||||
"flows.update",
|
||||
ActionKind::Create,
|
||||
&w_id,
|
||||
Some(&nf.path.to_string()),
|
||||
Some(
|
||||
[Some(("flow", nf.path.as_str()))]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateFlow {
|
||||
@@ -388,6 +387,7 @@ async fn update_flow(
|
||||
},
|
||||
);
|
||||
|
||||
let tx = user_db.begin(&authed).await?;
|
||||
let (dependency_job_uuid, mut tx) = push(
|
||||
tx,
|
||||
&w_id,
|
||||
|
||||
@@ -30,9 +30,7 @@ use windmill_common::{
|
||||
users::username_to_permissioned_as,
|
||||
utils::{not_found_if_none, now_from_db, paginate, require_admin, Pagination, StripPath},
|
||||
};
|
||||
use windmill_queue::{
|
||||
get_queued_job, push, JobKind, JobPayload, QueueTransaction, QueuedJob, RawCode,
|
||||
};
|
||||
use windmill_queue::{get_queued_job, push, JobKind, JobPayload, QueuedJob, RawCode};
|
||||
|
||||
use crate::{
|
||||
db::{UserDB, DB},
|
||||
@@ -66,7 +64,6 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/run/preview_flow", post(run_preview_flow_job))
|
||||
.route("/list", get(list_jobs))
|
||||
.route("/queue/list", get(list_queue_jobs))
|
||||
.route("/queue/count", get(count_queue_jobs))
|
||||
.route("/completed/list", get(list_completed_jobs))
|
||||
.route("/completed/get/:id", get(get_completed_job))
|
||||
.route("/completed/get_result/:id", get(get_completed_job_result))
|
||||
@@ -117,7 +114,6 @@ async fn get_result_by_id(
|
||||
}
|
||||
|
||||
async fn cancel_job_api(
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
OptAuthed(opt_authed): OptAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, id)): Path<(String, Uuid)>,
|
||||
@@ -131,7 +127,7 @@ async fn cancel_job_api(
|
||||
};
|
||||
|
||||
let (mut tx, job_option) =
|
||||
windmill_queue::cancel_job(&username, reason, id, &w_id, tx, rsmq, false).await?;
|
||||
windmill_queue::cancel_job(&username, reason, id, &w_id, tx, false).await?;
|
||||
|
||||
if let Some(id) = job_option {
|
||||
audit_log(
|
||||
@@ -160,7 +156,6 @@ async fn cancel_job_api(
|
||||
}
|
||||
|
||||
async fn force_cancel(
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
OptAuthed(opt_authed): OptAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, id)): Path<(String, Uuid)>,
|
||||
@@ -174,7 +169,7 @@ async fn force_cancel(
|
||||
};
|
||||
|
||||
let (mut tx, job_option) =
|
||||
windmill_queue::cancel_job(&username, reason, id, &w_id, tx, rsmq, true).await?;
|
||||
windmill_queue::cancel_job(&username, reason, id, &w_id, tx, true).await?;
|
||||
|
||||
if let Some(id) = job_option {
|
||||
audit_log(
|
||||
@@ -501,26 +496,6 @@ async fn list_queue_jobs(
|
||||
Ok(Json(jobs))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, FromRow)]
|
||||
struct QueueStats {
|
||||
database_length: i64,
|
||||
}
|
||||
|
||||
async fn count_queue_jobs(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
) -> error::JsonResult<QueueStats> {
|
||||
Ok(Json(
|
||||
sqlx::query_as!(
|
||||
QueueStats,
|
||||
"SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE workspace_id = $1",
|
||||
w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
async fn list_jobs(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
@@ -818,7 +793,6 @@ async fn get_suspended_flow_info<'c>(
|
||||
pub async fn cancel_suspended_job(
|
||||
/* unauthed */
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, job, resume_id, secret)): Path<(String, Uuid, u32, String)>,
|
||||
Query(approver): Query<QueryApprover>,
|
||||
) -> error::Result<String> {
|
||||
@@ -841,7 +815,6 @@ pub async fn cancel_suspended_job(
|
||||
parent_flow,
|
||||
&w_id,
|
||||
tx,
|
||||
rsmq,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
@@ -1235,15 +1208,14 @@ fn decode_payload<D: DeserializeOwned>(t: String) -> anyhow::Result<D> {
|
||||
pub async fn run_flow_by_path(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, flow_path)): Path<(String, StripPath)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
headers: HeaderMap,
|
||||
Json(args): Json<Option<serde_json::Map<String, serde_json::Value>>>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
let flow_path = flow_path.to_path();
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
let scheduled_for = run_query.get_scheduled_for(tx.transaction_mut()).await?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&mut tx).await?;
|
||||
let args = run_query.add_include_headers(headers, args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
@@ -1271,16 +1243,15 @@ pub async fn run_flow_by_path(
|
||||
pub async fn run_job_by_path(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, script_path)): Path<(String, StripPath)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
headers: HeaderMap,
|
||||
Json(args): Json<Option<serde_json::Map<String, serde_json::Value>>>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
let script_path = script_path.to_path();
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
let job_payload = script_path_to_payload(script_path, tx.transaction_mut(), &w_id).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(tx.transaction_mut()).await?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let job_payload = script_path_to_payload(script_path, &mut tx, &w_id).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&mut tx).await?;
|
||||
let args = run_query.add_include_headers(headers, args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
@@ -1422,7 +1393,6 @@ lazy_static::lazy_static! {
|
||||
|
||||
pub async fn run_wait_result_job_by_path_get(
|
||||
authed: Authed,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, script_path)): Path<(String, StripPath)>,
|
||||
@@ -1441,8 +1411,8 @@ pub async fn run_wait_result_job_by_path_get(
|
||||
|
||||
check_queue_too_long(db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?;
|
||||
let script_path = script_path.to_path();
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.clone().begin(&authed).await?).into();
|
||||
let job_payload = script_path_to_payload(script_path, tx.transaction_mut(), &w_id).await?;
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
let job_payload = script_path_to_payload(script_path, &mut tx, &w_id).await?;
|
||||
|
||||
let (uuid, tx) = push(
|
||||
tx,
|
||||
@@ -1477,7 +1447,6 @@ pub async fn run_wait_result_job_by_path_get(
|
||||
pub async fn run_wait_result_job_by_path(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, script_path)): Path<(String, StripPath)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
@@ -1486,8 +1455,8 @@ pub async fn run_wait_result_job_by_path(
|
||||
) -> error::JsonResult<serde_json::Value> {
|
||||
check_queue_too_long(db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?;
|
||||
let script_path = script_path.to_path();
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.clone().begin(&authed).await?).into();
|
||||
let job_payload = script_path_to_payload(script_path, tx.transaction_mut(), &w_id).await?;
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
let job_payload = script_path_to_payload(script_path, &mut tx, &w_id).await?;
|
||||
|
||||
let args = run_query.add_include_headers(headers, args.unwrap_or_default());
|
||||
|
||||
@@ -1524,7 +1493,6 @@ pub async fn run_wait_result_job_by_path(
|
||||
pub async fn run_wait_result_job_by_hash(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, script_hash)): Path<(String, ScriptHash)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
@@ -1534,9 +1502,8 @@ pub async fn run_wait_result_job_by_hash(
|
||||
check_queue_too_long(db, run_query.queue_limit).await?;
|
||||
|
||||
let hash = script_hash.0;
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.clone().begin(&authed).await?).into();
|
||||
let path = get_path_for_hash(tx.transaction_mut(), &w_id, hash).await?;
|
||||
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
let path = get_path_for_hash(&mut tx, &w_id, hash).await?;
|
||||
let args = run_query.add_include_headers(headers, args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
@@ -1572,7 +1539,6 @@ pub async fn run_wait_result_job_by_hash(
|
||||
pub async fn run_wait_result_flow_by_path(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, flow_path)): Path<(String, StripPath)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
@@ -1582,8 +1548,8 @@ pub async fn run_wait_result_flow_by_path(
|
||||
check_queue_too_long(db, run_query.queue_limit).await?;
|
||||
|
||||
let flow_path = flow_path.to_path();
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.clone().begin(&authed).await?).into();
|
||||
let scheduled_for = run_query.get_scheduled_for(tx.transaction_mut()).await?;
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&mut tx).await?;
|
||||
let args = run_query.add_include_headers(headers, args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
@@ -1604,6 +1570,7 @@ pub async fn run_wait_result_flow_by_path(
|
||||
!run_query.invisible_to_owner.unwrap_or(false),
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
run_wait_result(
|
||||
@@ -1634,14 +1601,13 @@ pub async fn script_path_to_payload<'c>(
|
||||
async fn run_preview_job(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
headers: HeaderMap,
|
||||
Json(preview): Json<Preview>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
let scheduled_for = run_query.get_scheduled_for(tx.transaction_mut()).await?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&mut tx).await?;
|
||||
let args = run_query.add_include_headers(headers, preview.args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
@@ -1668,21 +1634,19 @@ async fn run_preview_job(
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok((StatusCode::CREATED, uuid.to_string()))
|
||||
}
|
||||
|
||||
async fn run_preview_flow_job(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
headers: HeaderMap,
|
||||
Json(raw_flow): Json<PreviewFlow>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
let scheduled_for = run_query.get_scheduled_for(tx.transaction_mut()).await?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&mut tx).await?;
|
||||
let args = run_query.add_include_headers(headers, raw_flow.args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
@@ -1704,23 +1668,21 @@ async fn run_preview_flow_job(
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok((StatusCode::CREATED, uuid.to_string()))
|
||||
}
|
||||
|
||||
pub async fn run_job_by_hash(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, script_hash)): Path<(String, ScriptHash)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
headers: HeaderMap,
|
||||
Json(args): Json<Option<serde_json::Map<String, serde_json::Value>>>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
let hash = script_hash.0;
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
let path = get_path_for_hash(tx.transaction_mut(), &w_id, hash).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(tx.transaction_mut()).await?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let path = get_path_for_hash(&mut tx, &w_id, hash).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&mut tx).await?;
|
||||
let args = run_query.add_include_headers(headers, args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
@@ -1742,7 +1704,6 @@ pub async fn run_job_by_hash(
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok((StatusCode::CREATED, uuid.to_string()))
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,6 @@ lazy_static::lazy_static! {
|
||||
|
||||
pub async fn run_server(
|
||||
db: DB,
|
||||
rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
addr: SocketAddr,
|
||||
mut rx: tokio::sync::broadcast::Receiver<()>,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -101,7 +100,6 @@ pub async fn run_server(
|
||||
.on_request(()),
|
||||
)
|
||||
.layer(Extension(db.clone()))
|
||||
.layer(Extension(rsmq))
|
||||
.layer(Extension(user_db))
|
||||
.layer(Extension(auth_cache.clone()))
|
||||
.layer(CookieManagerLayer::new())
|
||||
|
||||
@@ -32,8 +32,7 @@ use windmill_audit::{audit_log, ActionKind};
|
||||
use windmill_common::users::username_to_permissioned_as;
|
||||
use windmill_common::utils::{not_found_if_none, now_from_db};
|
||||
|
||||
use crate::users::{truncate_token, Authed};
|
||||
use crate::webhook_util::{InstanceEvent, WebhookShared};
|
||||
use crate::users::{truncate_token, Authed, NEW_USER_WEBHOOK};
|
||||
use crate::workspaces::invite_user_to_all_auto_invite_worspaces;
|
||||
use crate::{
|
||||
db::{UserDB, DB},
|
||||
@@ -44,7 +43,7 @@ use crate::{BASE_URL, HTTP_CLIENT, IS_SECURE, OAUTH_CLIENTS, SLACK_SIGNING_SECRE
|
||||
use windmill_common::error::{self, to_anyhow, Error};
|
||||
use windmill_common::oauth2::*;
|
||||
|
||||
use windmill_queue::{JobPayload, QueueTransaction};
|
||||
use windmill_queue::JobPayload;
|
||||
|
||||
use std::{fs, str};
|
||||
|
||||
@@ -729,7 +728,6 @@ where
|
||||
async fn slack_command(
|
||||
SlackSig { sig, ts }: SlackSig,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
body: Bytes,
|
||||
) -> error::Result<String> {
|
||||
let form: SlackCommand = serde_urlencoded::from_bytes(&body)
|
||||
@@ -742,7 +740,7 @@ async fn slack_command(
|
||||
}
|
||||
}
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, db.begin().await?).into();
|
||||
let mut tx = db.begin().await?;
|
||||
let settings = sqlx::query_as!(
|
||||
WorkspaceSettings,
|
||||
"SELECT * FROM workspace_settings WHERE slack_team_id = $1",
|
||||
@@ -758,7 +756,7 @@ async fn slack_command(
|
||||
} else {
|
||||
let path = path.strip_prefix("script/").unwrap_or_else(|| path);
|
||||
let script_hash = windmill_common::get_latest_hash_for_path(
|
||||
tx.transaction_mut(),
|
||||
&mut tx,
|
||||
&settings.workspace_id,
|
||||
path,
|
||||
)
|
||||
@@ -790,15 +788,14 @@ async fn slack_command(
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
let url = BASE_URL.to_owned();
|
||||
tx.commit().await?;
|
||||
let url = BASE_URL.to_owned();
|
||||
return Ok(format!(
|
||||
"Job launched. See details at {url}/run/{uuid}?workspace={}",
|
||||
&settings.workspace_id
|
||||
));
|
||||
}
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
return Ok(format!(
|
||||
"workspace not properly configured (did you set the script to trigger in the settings?)"
|
||||
@@ -818,7 +815,6 @@ async fn login_callback(
|
||||
Path(client_name): Path<String>,
|
||||
cookies: Cookies,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Json(callback): Json<OAuthCallback>,
|
||||
) -> error::Result<String> {
|
||||
let client_w_config = &OAUTH_CLIENTS
|
||||
@@ -944,7 +940,14 @@ async fn login_callback(
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
webhook.send_instance_event(InstanceEvent::UserSignupOAuth { email: email.clone() });
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = HTTP_CLIENT
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &email, "event": "oauth_signup"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
Ok("Successfully logged in".to_string())
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,7 @@ use windmill_common::{
|
||||
schedule::Schedule,
|
||||
utils::{not_found_if_none, paginate, Pagination, StripPath},
|
||||
};
|
||||
use windmill_queue::{self, schedule::push_scheduled_job, JobKind, QueueTransaction};
|
||||
use windmill_queue::{self, schedule::push_scheduled_job, JobKind};
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
@@ -79,23 +79,15 @@ async fn create_schedule(
|
||||
authed: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(ns): Json<NewSchedule>,
|
||||
) -> Result<String> {
|
||||
let authed = maybe_refresh_folders(&ns.path, &w_id, authed, &db).await;
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
cron::Schedule::from_str(&ns.schedule).map_err(|e| Error::BadRequest(e.to_string()))?;
|
||||
check_path_conflict(tx.transaction_mut(), &w_id, &ns.path).await?;
|
||||
check_flow_conflict(
|
||||
tx.transaction_mut(),
|
||||
&w_id,
|
||||
&ns.path,
|
||||
ns.is_flow,
|
||||
&ns.script_path,
|
||||
)
|
||||
.await?;
|
||||
check_path_conflict(&mut tx, &w_id, &ns.path).await?;
|
||||
check_flow_conflict(&mut tx, &w_id, &ns.path, ns.is_flow, &ns.script_path).await?;
|
||||
|
||||
let schedule = sqlx::query_as!(
|
||||
Schedule,
|
||||
@@ -147,15 +139,14 @@ async fn edit_schedule(
|
||||
authed: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(es): Json<EditSchedule>,
|
||||
) -> Result<String> {
|
||||
let path = path.to_path();
|
||||
|
||||
let authed = maybe_refresh_folders(&path, &w_id, authed, &db).await;
|
||||
let mut tx: QueueTransaction<'_, rsmq_async::MultiplexedRsmq> =
|
||||
(rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
cron::Schedule::from_str(&es.schedule).map_err(|e| Error::BadRequest(e.to_string()))?;
|
||||
|
||||
@@ -167,7 +158,7 @@ async fn edit_schedule(
|
||||
.fetch_one(&mut tx)
|
||||
.await?;
|
||||
|
||||
clear_schedule(tx.transaction_mut(), path, is_flow).await?;
|
||||
clear_schedule(&mut tx, path, is_flow).await?;
|
||||
let schedule = sqlx::query_as!(
|
||||
Schedule,
|
||||
"UPDATE schedule SET schedule = $1, timezone = $2, args = $3 WHERE path \
|
||||
@@ -182,6 +173,10 @@ async fn edit_schedule(
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("updating schedule in {w_id}: {e}")))?;
|
||||
|
||||
if schedule.enabled {
|
||||
tx = push_scheduled_job(tx, schedule).await?;
|
||||
}
|
||||
|
||||
audit_log(
|
||||
&mut tx,
|
||||
&authed.username,
|
||||
@@ -197,10 +192,6 @@ async fn edit_schedule(
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if schedule.enabled {
|
||||
tx = push_scheduled_job(tx, schedule).await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(path.to_string())
|
||||
@@ -279,12 +270,10 @@ pub async fn preview_schedule(
|
||||
pub async fn set_enabled(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(payload): Json<SetEnabled>,
|
||||
) -> Result<String> {
|
||||
let mut tx: QueueTransaction<'_, rsmq_async::MultiplexedRsmq> =
|
||||
(rsmq, user_db.begin(&authed).await?).into();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let path = path.to_path();
|
||||
let schedule_o = sqlx::query_as!(
|
||||
Schedule,
|
||||
@@ -299,8 +288,11 @@ pub async fn set_enabled(
|
||||
|
||||
let schedule = not_found_if_none(schedule_o, "Schedule", path)?;
|
||||
|
||||
clear_schedule(tx.transaction_mut(), path, schedule.is_flow).await?;
|
||||
clear_schedule(&mut tx, path, schedule.is_flow).await?;
|
||||
|
||||
if payload.enabled {
|
||||
tx = push_scheduled_job(tx, schedule).await?;
|
||||
}
|
||||
audit_log(
|
||||
&mut tx,
|
||||
&authed.username,
|
||||
@@ -311,12 +303,7 @@ pub async fn set_enabled(
|
||||
Some([("enabled", payload.enabled.to_string().as_ref())].into()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if payload.enabled {
|
||||
tx = push_scheduled_job(tx, schedule).await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(format!(
|
||||
"succesfully updated schedule at path {} to status {}",
|
||||
path, payload.enabled
|
||||
|
||||
@@ -44,7 +44,7 @@ use windmill_common::{
|
||||
list_elems_from_hub, not_found_if_none, paginate, require_admin, Pagination, StripPath,
|
||||
},
|
||||
};
|
||||
use windmill_queue::{self, schedule::push_scheduled_job, QueueTransaction};
|
||||
use windmill_queue::{self, schedule::push_scheduled_job};
|
||||
|
||||
const MAX_HASH_HISTORY_LENGTH_STORED: usize = 20;
|
||||
|
||||
@@ -193,7 +193,6 @@ fn hash_script(ns: &NewScript) -> i64 {
|
||||
async fn create_script(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
@@ -201,14 +200,14 @@ async fn create_script(
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let hash = ScriptHash(hash_script(&ns));
|
||||
let authed = maybe_refresh_folders(&ns.path, &w_id, authed, &db).await;
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
if sqlx::query_scalar!(
|
||||
"SELECT 1 FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
hash.0,
|
||||
&w_id
|
||||
)
|
||||
.fetch_optional(tx.transaction_mut())
|
||||
.fetch_optional(&mut tx)
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
@@ -269,7 +268,7 @@ async fn create_script(
|
||||
)));
|
||||
};
|
||||
|
||||
let ps = get_script_by_hash_internal(tx.transaction_mut(), &w_id, p_hash).await?;
|
||||
let ps = get_script_by_hash_internal(&mut tx, &w_id, p_hash).await?;
|
||||
|
||||
if ps.path != ns.path {
|
||||
if !authed.is_admin {
|
||||
@@ -364,7 +363,7 @@ async fn create_script(
|
||||
.await?;
|
||||
|
||||
for schedule in schedulables {
|
||||
clear_schedule(tx.transaction_mut(), &schedule.path, false).await?;
|
||||
clear_schedule(&mut tx, &schedule.path, false).await?;
|
||||
|
||||
if schedule.enabled {
|
||||
tx = push_scheduled_job(tx, schedule).await?;
|
||||
@@ -372,6 +371,36 @@ async fn create_script(
|
||||
}
|
||||
}
|
||||
|
||||
let mut tx = if needs_lock_gen {
|
||||
let dependencies = match ns.language {
|
||||
ScriptLang::Python3 => {
|
||||
windmill_parser_py::parse_python_imports(&ns.content)?.join("\n")
|
||||
}
|
||||
_ => ns.content,
|
||||
};
|
||||
let (_, tx) = windmill_queue::push(
|
||||
tx,
|
||||
&w_id,
|
||||
windmill_queue::JobPayload::Dependencies { hash, dependencies, language: ns.language },
|
||||
serde_json::Map::new(),
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
tx
|
||||
} else {
|
||||
tx
|
||||
};
|
||||
|
||||
if p_hashes.is_some() && !p_hashes.unwrap().is_empty() {
|
||||
audit_log(
|
||||
&mut tx,
|
||||
@@ -386,7 +415,7 @@ async fn create_script(
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateScript {
|
||||
workspace: w_id.clone(),
|
||||
workspace: w_id,
|
||||
path: ns.path.clone(),
|
||||
hash: hash.to_string(),
|
||||
},
|
||||
@@ -411,41 +440,13 @@ async fn create_script(
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateScript {
|
||||
workspace: w_id.clone(),
|
||||
workspace: w_id,
|
||||
path: ns.path.clone(),
|
||||
hash: hash.to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if needs_lock_gen {
|
||||
let dependencies = match ns.language {
|
||||
ScriptLang::Python3 => {
|
||||
windmill_parser_py::parse_python_imports(&ns.content)?.join("\n")
|
||||
}
|
||||
_ => ns.content,
|
||||
};
|
||||
let (_, new_tx) = windmill_queue::push(
|
||||
tx,
|
||||
&w_id,
|
||||
windmill_queue::JobPayload::Dependencies { hash, dependencies, language: ns.language },
|
||||
serde_json::Map::new(),
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
tx = new_tx;
|
||||
}
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok((StatusCode::CREATED, format!("{}", hash)))
|
||||
|
||||
@@ -12,9 +12,8 @@ use crate::{
|
||||
db::{UserDB, DB},
|
||||
folders::get_folders_for_user,
|
||||
utils::require_super_admin,
|
||||
webhook_util::{InstanceEvent, WebhookShared},
|
||||
workspaces::invite_user_to_all_auto_invite_worspaces,
|
||||
COOKIE_DOMAIN, IS_SECURE,
|
||||
COOKIE_DOMAIN, HTTP_CLIENT, IS_SECURE,
|
||||
};
|
||||
use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
|
||||
use axum::{
|
||||
@@ -1038,7 +1037,6 @@ lazy_static! {
|
||||
|
||||
async fn accept_invite(
|
||||
Authed { email, .. }: Authed,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Extension(db): Extension<DB>,
|
||||
Json(nu): Json<AcceptInvite>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
@@ -1084,11 +1082,6 @@ async fn accept_invite(
|
||||
}
|
||||
|
||||
if is_some {
|
||||
webhook.send_instance_event(InstanceEvent::UserJoinedWorkspace {
|
||||
email: email.clone(),
|
||||
workspace: nu.workspace_id.clone(),
|
||||
username: nu.username.clone(),
|
||||
});
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
format!(
|
||||
@@ -1329,7 +1322,6 @@ lazy_static::lazy_static! {
|
||||
async fn create_user(
|
||||
Authed { email, .. }: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Extension(argon2): Extension<Arc<Argon2<'_>>>,
|
||||
Json(mut nu): Json<NewUser>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
@@ -1370,9 +1362,19 @@ async fn create_user(
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = HTTP_CLIENT
|
||||
.post(&new_user_webhook)
|
||||
.json(
|
||||
&serde_json::json!({"email" : &nu.email, "name": &nu.name, "event": "global_add"}),
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
invite_user_to_all_auto_invite_worspaces(&db, &nu.email).await?;
|
||||
|
||||
webhook.send_instance_event(InstanceEvent::UserAdded { email: nu.email.clone() });
|
||||
Ok((StatusCode::CREATED, format!("email {} created", nu.email)))
|
||||
}
|
||||
|
||||
|
||||
@@ -13,26 +13,6 @@ lazy_static::lazy_static! {
|
||||
"Histogram of webhook requests made"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
pub static ref INSTANCE_EVENTS_WEBHOOK: Option<String> = std::env::var("INSTANCE_EVENTS_WEBHOOK").ok();
|
||||
|
||||
}
|
||||
|
||||
pub enum WebhookPayload {
|
||||
WorkspaceEvent(String, WebhookMessage),
|
||||
InstanceEvent(InstanceEvent),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum InstanceEvent {
|
||||
UserSignupOAuth { email: String },
|
||||
UserAdded { email: String },
|
||||
// UserDeleted { email: String },
|
||||
// UserDeletedWorkspace { workspace: String, email: String },
|
||||
UserAddedWorkspace { workspace: String, email: String },
|
||||
UserInvitedWorkspace { workspace: String, email: String },
|
||||
UserJoinedWorkspace { workspace: String, email: String, username: String },
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -66,12 +46,12 @@ pub enum WebhookMessage {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WebhookShared {
|
||||
pub channel: mpsc::UnboundedSender<WebhookPayload>,
|
||||
pub channel: mpsc::UnboundedSender<(String, WebhookMessage)>,
|
||||
}
|
||||
|
||||
impl WebhookShared {
|
||||
pub fn new(mut shutdown_rx: tokio::sync::broadcast::Receiver<()>, db: DB) -> Self {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<WebhookPayload>();
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<(String, WebhookMessage)>();
|
||||
let _process = tokio::spawn(async move {
|
||||
let client = reqwest::Client::builder()
|
||||
// TODO: investigate pool timeouts and such if TCP load is high
|
||||
@@ -86,7 +66,7 @@ impl WebhookShared {
|
||||
biased;
|
||||
_ = shutdown_rx.recv() => break,
|
||||
r = rx.recv() => match r {
|
||||
Some(WebhookPayload::WorkspaceEvent(workspace_id, message)) => {
|
||||
Some((workspace_id, message)) => {
|
||||
let url_guard = match cache.get(&workspace_id).await {
|
||||
Some(guard) => {
|
||||
guard
|
||||
@@ -116,13 +96,6 @@ impl WebhookShared {
|
||||
drop(url_guard);
|
||||
}
|
||||
},
|
||||
Some(WebhookPayload::InstanceEvent(event)) => {
|
||||
if *METRICS_ENABLED { Some(WEBHOOK_REQUEST_COUNT.start_timer()) } else { None };
|
||||
let r = client.post(INSTANCE_EVENTS_WEBHOOK.as_ref().unwrap()).json(&event).send().await;
|
||||
if let Err(e) = r {
|
||||
tracing::error!("Error sending instance event: {}", e);
|
||||
}
|
||||
},
|
||||
None => break,
|
||||
},
|
||||
_ = futures::future::poll_fn(|cx| cache_purge_interval.poll_tick(cx)) => {
|
||||
@@ -137,16 +110,6 @@ impl WebhookShared {
|
||||
}
|
||||
|
||||
pub fn send_message(&self, workspace_id: String, message: WebhookMessage) {
|
||||
let _ = self.channel.send(WebhookPayload::WorkspaceEvent(
|
||||
workspace_id.clone(),
|
||||
message,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn send_instance_event(&self, event: InstanceEvent) {
|
||||
if INSTANCE_EVENTS_WEBHOOK.is_none() {
|
||||
return;
|
||||
}
|
||||
let _ = self.channel.send(WebhookPayload::InstanceEvent(event));
|
||||
let _ = self.channel.send((workspace_id.clone(), message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ use crate::{
|
||||
db::{UserDB, DB},
|
||||
folders::Folder,
|
||||
resources::{Resource, ResourceType},
|
||||
users::{Authed, WorkspaceInvite, VALID_USERNAME},
|
||||
users::{Authed, WorkspaceInvite, NEW_USER_WEBHOOK, VALID_USERNAME},
|
||||
utils::require_super_admin,
|
||||
variables::build_crypt,
|
||||
webhook_util::{InstanceEvent, WebhookShared},
|
||||
HTTP_CLIENT,
|
||||
};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use axum::response::Redirect;
|
||||
@@ -943,7 +943,6 @@ pub async fn invite_user_to_all_auto_invite_worspaces(db: &DB, email: &str) -> R
|
||||
async fn invite_user(
|
||||
Authed { username, is_admin, .. }: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(mut nu): Json<NewWorkspaceInvite>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
@@ -967,10 +966,14 @@ async fn invite_user(
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
webhook.send_instance_event(InstanceEvent::UserInvitedWorkspace {
|
||||
email: nu.email.clone(),
|
||||
workspace: w_id,
|
||||
});
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = &HTTP_CLIENT
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &nu.email, "event": "workspace_invite"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
@@ -981,7 +984,6 @@ async fn invite_user(
|
||||
async fn add_user(
|
||||
Authed { username, is_admin, .. }: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(mut nu): Json<NewWorkspaceUser>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
@@ -1020,10 +1022,14 @@ async fn add_user(
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
webhook.send_instance_event(InstanceEvent::UserAddedWorkspace {
|
||||
workspace: w_id.clone(),
|
||||
email: nu.email.clone(),
|
||||
});
|
||||
if let Some(new_user_webhook) = NEW_USER_WEBHOOK.clone() {
|
||||
let _ = HTTP_CLIENT
|
||||
.post(&new_user_webhook)
|
||||
.json(&serde_json::json!({"email" : &nu.email, "event": "workspace_add"}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| tracing::error!("Error sending new user webhook: {}", e.to_string()));
|
||||
}
|
||||
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
|
||||
@@ -41,8 +41,8 @@ pub struct AuditLog {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub async fn audit_log<'c, E: sqlx::Executor<'c, Database = Postgres>>(
|
||||
db: E,
|
||||
pub async fn audit_log<'c>(
|
||||
db: &mut Transaction<'c, Postgres>,
|
||||
username: &str,
|
||||
operation: &str,
|
||||
action_kind: ActionKind,
|
||||
|
||||
@@ -17,7 +17,7 @@ use serde_json::to_string_pretty;
|
||||
|
||||
use crate::utils::StripPath;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Hash, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Hash)]
|
||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||
#[cfg_attr(
|
||||
feature = "sqlx",
|
||||
|
||||
@@ -52,8 +52,8 @@ pub fn paginate(pagination: Pagination) -> (usize, usize) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "sqlx")]
|
||||
pub async fn now_from_db<'c, E: sqlx::PgExecutor<'c>>(
|
||||
db: E,
|
||||
pub async fn now_from_db<'c>(
|
||||
db: &mut sqlx::Transaction<'c, sqlx::Postgres>,
|
||||
) -> Result<chrono::DateTime<chrono::Utc>> {
|
||||
Ok(sqlx::query_scalar!("SELECT now()")
|
||||
.fetch_one(db)
|
||||
|
||||
@@ -31,6 +31,3 @@ reqwest.workspace = true
|
||||
lazy_static.workspace = true
|
||||
prometheus.workspace = true
|
||||
cron.workspace = true
|
||||
rsmq_async.workspace = true
|
||||
tokio.workspace = true
|
||||
futures-core.workspace = true
|
||||
|
||||
@@ -25,8 +25,6 @@ use windmill_common::{
|
||||
METRICS_ENABLED,
|
||||
};
|
||||
|
||||
use crate::QueueTransaction;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new()
|
||||
.user_agent("windmill/beta")
|
||||
@@ -54,15 +52,13 @@ lazy_static::lazy_static! {
|
||||
|
||||
const MAX_FREE_EXECS: i32 = 1000;
|
||||
const MAX_FREE_CONCURRENT_RUNS: i32 = 15;
|
||||
const RSMQ_MAIN_QUEUE: &'static str = "main_queue";
|
||||
|
||||
pub async fn cancel_job<'c, R: rsmq_async::RsmqConnection + Clone>(
|
||||
pub async fn cancel_job<'c>(
|
||||
username: &str,
|
||||
reason: Option<String>,
|
||||
id: Uuid,
|
||||
w_id: &str,
|
||||
mut tx: Transaction<'c, Postgres>,
|
||||
rsmq: Option<R>,
|
||||
force_rerun: bool,
|
||||
) -> error::Result<(Transaction<'c, Postgres>, Option<Uuid>)> {
|
||||
let job_option = sqlx::query_scalar!(
|
||||
@@ -76,12 +72,6 @@ pub async fn cancel_job<'c, R: rsmq_async::RsmqConnection + Clone>(
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?;
|
||||
if let Some(mut rsmq) = rsmq {
|
||||
rsmq.change_message_visibility(RSMQ_MAIN_QUEUE, &id.to_string(), 0)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e))?;
|
||||
}
|
||||
|
||||
let mut jobs = job_option.map(|j| vec![j]).unwrap_or_default();
|
||||
while !jobs.is_empty() {
|
||||
let p_job = jobs.pop();
|
||||
@@ -101,11 +91,10 @@ pub async fn cancel_job<'c, R: rsmq_async::RsmqConnection + Clone>(
|
||||
Ok((tx, job_option))
|
||||
}
|
||||
|
||||
pub async fn pull<R: rsmq_async::RsmqConnection + Clone>(
|
||||
pub async fn pull(
|
||||
db: &Pool<Postgres>,
|
||||
whitelist_workspaces: Option<Vec<String>>,
|
||||
blacklist_workspaces: Option<Vec<String>>,
|
||||
rsmq: Option<R>,
|
||||
) -> windmill_common::error::Result<Option<QueuedJob>> {
|
||||
let mut workspaces_filter = String::new();
|
||||
if let Some(whitelist) = whitelist_workspaces {
|
||||
@@ -117,9 +106,6 @@ pub async fn pull<R: rsmq_async::RsmqConnection + Clone>(
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
));
|
||||
if let Some(_rsmq) = rsmq {
|
||||
todo!("REDIS: Implement workspace filters for redis");
|
||||
}
|
||||
}
|
||||
if let Some(blacklist) = blacklist_workspaces {
|
||||
workspaces_filter.push_str(&format!(
|
||||
@@ -130,50 +116,16 @@ pub async fn pull<R: rsmq_async::RsmqConnection + Clone>(
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
));
|
||||
if let Some(_rsmq) = rsmq {
|
||||
todo!("REDIS: Implement workspace filters for redis");
|
||||
}
|
||||
}
|
||||
|
||||
let job: Option<QueuedJob> = if let Some(mut rsmq) = rsmq {
|
||||
// TODO: REDIS: Race conditions / replace last_ping
|
||||
let msg = rsmq
|
||||
.pop_message::<Vec<u8>>(RSMQ_MAIN_QUEUE)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e))?;
|
||||
|
||||
if let Some(msg) = msg {
|
||||
let uuid = Uuid::from_bytes_le(
|
||||
msg.message
|
||||
.try_into()
|
||||
.map_err(|_| anyhow::anyhow!("Failed to parsed Redis message"))?,
|
||||
);
|
||||
|
||||
sqlx::query_as::<_, QueuedJob>(
|
||||
"UPDATE queue
|
||||
SET running = true
|
||||
, started_at = coalesce(started_at, now())
|
||||
, last_ping = now()
|
||||
, suspend_until = null
|
||||
WHERE id = $1
|
||||
RETURNING *",
|
||||
)
|
||||
.bind(uuid)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
/* Jobs can be started if they:
|
||||
* - haven't been started before,
|
||||
* running = false
|
||||
* - are flows with a step that needed resume,
|
||||
* suspend_until is non-null
|
||||
* and suspend = 0 when the resume messages are received
|
||||
* or suspend_until <= now() if it has timed out */
|
||||
sqlx::query_as::<_, QueuedJob>(&format!(
|
||||
"UPDATE queue
|
||||
/* Jobs can be started if they:
|
||||
* - haven't been started before,
|
||||
* running = false
|
||||
* - are flows with a step that needed resume,
|
||||
* suspend_until is non-null
|
||||
* and suspend = 0 when the resume messages are received
|
||||
* or suspend_until <= now() if it has timed out */
|
||||
let job: Option<QueuedJob> = sqlx::query_as::<_, QueuedJob>(&format!(
|
||||
"UPDATE queue
|
||||
SET running = true
|
||||
, started_at = coalesce(started_at, now())
|
||||
, last_ping = now()
|
||||
@@ -191,10 +143,9 @@ pub async fn pull<R: rsmq_async::RsmqConnection + Clone>(
|
||||
LIMIT 1
|
||||
)
|
||||
RETURNING *"
|
||||
))
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
};
|
||||
))
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
|
||||
if job.is_some() && *METRICS_ENABLED {
|
||||
QUEUE_PULL_COUNT.inc();
|
||||
@@ -256,11 +207,11 @@ pub async fn get_result_by_id(
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub async fn delete_job<'c, R: rsmq_async::RsmqConnection + Clone + Send>(
|
||||
mut tx: QueueTransaction<'c, R>,
|
||||
pub async fn delete_job(
|
||||
db: &Pool<Postgres>,
|
||||
w_id: &str,
|
||||
job_id: Uuid,
|
||||
) -> windmill_common::error::Result<QueueTransaction<'c, R>> {
|
||||
) -> windmill_common::error::Result<()> {
|
||||
if *METRICS_ENABLED {
|
||||
QUEUE_DELETE_COUNT.inc();
|
||||
}
|
||||
@@ -269,13 +220,13 @@ pub async fn delete_job<'c, R: rsmq_async::RsmqConnection + Clone + Send>(
|
||||
w_id,
|
||||
job_id
|
||||
)
|
||||
.fetch_one(&mut tx)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Error during deletion of job {job_id}: {e}")))?
|
||||
.unwrap_or(0)
|
||||
== 1;
|
||||
tracing::debug!("Job {job_id} deleted: {job_removed}");
|
||||
Ok(tx)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_queued_job<'c>(
|
||||
@@ -294,9 +245,9 @@ pub async fn get_queued_job<'c>(
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
// #[instrument(level = "trace", skip_all)]
|
||||
pub async fn push<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
mut tx: QueueTransaction<'c, R>,
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub async fn push<'c>(
|
||||
mut tx: Transaction<'c, Postgres>,
|
||||
workspace_id: &str,
|
||||
job_payload: JobPayload,
|
||||
args: serde_json::Map<String, serde_json::Value>,
|
||||
@@ -311,7 +262,8 @@ pub async fn push<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
mut same_worker: bool,
|
||||
pre_run_error: Option<&windmill_common::error::Error>,
|
||||
visible_to_owner: bool,
|
||||
) -> Result<(Uuid, QueueTransaction<'c, R>), Error> {
|
||||
) -> Result<(Uuid, Transaction<'c, Postgres>), Error> {
|
||||
let scheduled_for = scheduled_for_o.unwrap_or_else(chrono::Utc::now);
|
||||
let args_json = serde_json::Value::Object(args);
|
||||
let job_id: Uuid = Ulid::new().into();
|
||||
|
||||
@@ -563,7 +515,7 @@ pub async fn push<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
(workspace_id, id, running, parent_job, created_by, permissioned_as, scheduled_for,
|
||||
script_hash, script_path, raw_code, raw_lock, args, job_kind, schedule_path, raw_flow, \
|
||||
flow_status, is_flow_step, language, started_at, same_worker, pre_run_error, email, visible_to_owner, root_job)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, now()), $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, CASE WHEN $3 THEN now() END, $19, $20, $21, $22, $23) \
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, CASE WHEN $3 THEN now() END, $19, $20, $21, $22, $23) \
|
||||
RETURNING id",
|
||||
workspace_id,
|
||||
job_id,
|
||||
@@ -571,7 +523,7 @@ pub async fn push<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
parent_job,
|
||||
user,
|
||||
permissioned_as,
|
||||
scheduled_for_o,
|
||||
scheduled_for,
|
||||
script_hash,
|
||||
script_path.clone(),
|
||||
raw_code,
|
||||
@@ -630,10 +582,6 @@ pub async fn push<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
.instrument(tracing::info_span!("job_run", email = &email))
|
||||
.await?;
|
||||
}
|
||||
if let Some(ref mut rsmq) = tx.rsmq {
|
||||
rsmq.send_message(job_id.to_bytes_le().to_vec(), scheduled_for_o);
|
||||
}
|
||||
|
||||
Ok((uuid, tx))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
*/
|
||||
|
||||
mod jobs;
|
||||
mod queue_transaction;
|
||||
pub mod schedule;
|
||||
|
||||
pub use jobs::*;
|
||||
pub use queue_transaction::*;
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use futures_core::{future::BoxFuture, stream::BoxStream};
|
||||
use rsmq_async::{RedisBytes, RsmqConnection};
|
||||
use sqlx::{Postgres, Transaction};
|
||||
|
||||
pub enum RedisOp {
|
||||
SendMessage(RedisBytes, Option<chrono::DateTime<chrono::Utc>>),
|
||||
DeleteMessage(String),
|
||||
}
|
||||
|
||||
impl RedisOp {
|
||||
pub async fn apply<R: RsmqConnection>(self, rsmq: &mut R) -> Result<(), rsmq_async::RsmqError> {
|
||||
match self {
|
||||
RedisOp::SendMessage(bytes, time) => {
|
||||
rsmq.send_message(
|
||||
"main_queue",
|
||||
bytes,
|
||||
time.map(|t| (t - chrono::Utc::now()).num_seconds())
|
||||
.and_then(|e| e.try_into().ok()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
RedisOp::DeleteMessage(id) => {
|
||||
rsmq.delete_message("main_queue", &id).await?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RedisTransaction<R: RsmqConnection> {
|
||||
rsmq: R,
|
||||
queued_ops: Vec<RedisOp>,
|
||||
}
|
||||
|
||||
impl<R: RsmqConnection> From<R> for RedisTransaction<R> {
|
||||
fn from(value: R) -> Self {
|
||||
Self { rsmq: value, queued_ops: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RsmqConnection> RedisTransaction<R> {
|
||||
pub async fn commit(self) -> Result<(), rsmq_async::RsmqError> {
|
||||
let mut rsmq = self.rsmq;
|
||||
for op in self.queued_ops {
|
||||
op.apply(&mut rsmq).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_message<E: Into<RedisBytes>>(
|
||||
&mut self,
|
||||
bytes: E,
|
||||
delay_until: Option<chrono::DateTime<chrono::Utc>>,
|
||||
) {
|
||||
self.queued_ops
|
||||
.push(RedisOp::SendMessage(bytes.into(), delay_until))
|
||||
}
|
||||
|
||||
pub fn delete_message(&mut self, id: String) {
|
||||
self.queued_ops.push(RedisOp::DeleteMessage(id))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueueTransaction<'c, R: RsmqConnection> {
|
||||
pub rsmq: Option<RedisTransaction<R>>,
|
||||
transaction: Transaction<'c, Postgres>,
|
||||
}
|
||||
|
||||
impl<'c, R: RsmqConnection> From<(Option<R>, Transaction<'c, Postgres>)>
|
||||
for QueueTransaction<'c, R>
|
||||
{
|
||||
fn from(value: (Option<R>, Transaction<'c, Postgres>)) -> Self {
|
||||
Self { rsmq: value.0.map(|e| e.into()), transaction: value.1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, R: RsmqConnection> Debug for QueueTransaction<'c, R> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("QueueTransaction")
|
||||
.field("rsmq", &self.rsmq.as_ref().map(|_| ())) // do not require R: Debug
|
||||
.field("transaction", &self.transaction)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, R: RsmqConnection> QueueTransaction<'c, R> {
|
||||
pub async fn commit(self) -> Result<(), windmill_common::error::Error> {
|
||||
self.transaction.commit().await?;
|
||||
if let Some(rsmq) = self.rsmq {
|
||||
rsmq.commit().await.map_err(|e| anyhow::anyhow!(e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn transaction_mut<'a>(&'a mut self) -> &'a mut Transaction<'c, Postgres> {
|
||||
&mut self.transaction
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b, R: RsmqConnection + Send> sqlx::Executor<'b> for &'b mut QueueTransaction<'c, R> {
|
||||
type Database = Postgres;
|
||||
|
||||
fn fetch_many<'e, 'q: 'e, E: 'q>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxStream<
|
||||
'e,
|
||||
Result<
|
||||
sqlx::Either<
|
||||
<Self::Database as sqlx::Database>::QueryResult,
|
||||
<Self::Database as sqlx::Database>::Row,
|
||||
>,
|
||||
sqlx::Error,
|
||||
>,
|
||||
>
|
||||
where
|
||||
'b: 'e,
|
||||
E: sqlx::Execute<'q, Self::Database>,
|
||||
{
|
||||
self.transaction.fetch_many(query)
|
||||
}
|
||||
|
||||
fn fetch_optional<'e, 'q: 'e, E: 'q>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, Result<Option<<Self::Database as sqlx::Database>::Row>, sqlx::Error>>
|
||||
where
|
||||
'b: 'e,
|
||||
E: sqlx::Execute<'q, Self::Database>,
|
||||
{
|
||||
self.transaction.fetch_optional(query)
|
||||
}
|
||||
|
||||
fn prepare_with<'e, 'q: 'e>(
|
||||
self,
|
||||
sql: &'q str,
|
||||
parameters: &'e [<Self::Database as sqlx::Database>::TypeInfo],
|
||||
) -> BoxFuture<
|
||||
'e,
|
||||
Result<<Self::Database as sqlx::database::HasStatement<'q>>::Statement, sqlx::Error>,
|
||||
>
|
||||
where
|
||||
'b: 'e,
|
||||
{
|
||||
self.transaction.prepare_with(sql, parameters)
|
||||
}
|
||||
|
||||
fn describe<'e, 'q: 'e>(
|
||||
self,
|
||||
sql: &'q str,
|
||||
) -> BoxFuture<'e, Result<sqlx::Describe<Self::Database>, sqlx::Error>>
|
||||
where
|
||||
'b: 'e,
|
||||
{
|
||||
self.transaction.describe(sql)
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,11 @@ use windmill_common::{
|
||||
users::username_to_permissioned_as,
|
||||
utils::{now_from_db, StripPath},
|
||||
};
|
||||
use crate::{QueueTransaction};
|
||||
|
||||
pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
mut tx: QueueTransaction<'c, R>,
|
||||
pub async fn push_scheduled_job<'c>(
|
||||
mut tx: Transaction<'c, Postgres>,
|
||||
schedule: Schedule,
|
||||
) -> Result<QueueTransaction<'c, R>> {
|
||||
) -> Result<Transaction<'c, Postgres>> {
|
||||
let sched = cron::Schedule::from_str(&schedule.schedule)
|
||||
.map_err(|e| error::Error::BadRequest(e.to_string()))?;
|
||||
|
||||
@@ -71,7 +70,7 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
} else {
|
||||
JobPayload::ScriptHash {
|
||||
hash: windmill_common::get_latest_hash_for_path(
|
||||
tx.transaction_mut(),
|
||||
&mut tx,
|
||||
&schedule.workspace_id,
|
||||
&schedule.script_path,
|
||||
)
|
||||
@@ -80,15 +79,7 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
}
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE schedule SET error = NULL WHERE workspace_id = $1 AND path = $2",
|
||||
&schedule.workspace_id,
|
||||
&schedule.path
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
|
||||
let (_, tx) = push(
|
||||
let (_, mut tx) = push(
|
||||
tx,
|
||||
&schedule.workspace_id,
|
||||
payload,
|
||||
@@ -106,7 +97,14 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
Ok(tx) // TODO: Bubble up pushed UUID from here
|
||||
sqlx::query!(
|
||||
"UPDATE schedule SET error = NULL WHERE workspace_id = $1 AND path = $2",
|
||||
&schedule.workspace_id,
|
||||
&schedule.path
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
pub async fn get_schedule_opt<'c>(
|
||||
|
||||
@@ -48,5 +48,4 @@ deno_core.workspace = true
|
||||
const_format.workspace = true
|
||||
git-version.workspace = true
|
||||
dyn-iter.workspace = true
|
||||
once_cell.workspace = true
|
||||
rsmq_async.workspace = true
|
||||
once_cell.workspace = true
|
||||
@@ -6,30 +6,29 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use sqlx::{Pool, Postgres};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use sqlx::{Pool, Postgres, Transaction};
|
||||
use tracing::instrument;
|
||||
use uuid::Uuid;
|
||||
use windmill_common::{
|
||||
error::Error, flow_status::FlowStatusModule, schedule::Schedule, METRICS_ENABLED,
|
||||
};
|
||||
use windmill_queue::{
|
||||
delete_job, schedule::get_schedule_opt, JobKind, QueueTransaction, QueuedJob, CLOUD_HOSTED,
|
||||
};
|
||||
use windmill_queue::{delete_job, schedule::get_schedule_opt, JobKind, QueuedJob, CLOUD_HOSTED};
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub async fn add_completed_job_error<R: rsmq_async::RsmqConnection + Clone + Send>(
|
||||
pub async fn add_completed_job_error(
|
||||
db: &Pool<Postgres>,
|
||||
queued_job: &QueuedJob,
|
||||
logs: String,
|
||||
e: serde_json::Value,
|
||||
metrics: Option<crate::worker::Metrics>,
|
||||
rsmq: Option<R>,
|
||||
) -> Result<serde_json::Value, Error> {
|
||||
if *METRICS_ENABLED {
|
||||
metrics.map(|m| m.worker_execution_failed.inc());
|
||||
}
|
||||
let result = serde_json::json!({ "error": e });
|
||||
let _ = add_completed_job(db, &queued_job, false, false, result.clone(), logs, rsmq).await?;
|
||||
let _ = add_completed_job(db, &queued_job, false, false, result.clone(), logs).await?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -52,14 +51,13 @@ fn flatten_jobs(modules: Vec<FlowStatusModule>) -> Vec<Uuid> {
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub async fn add_completed_job<R: rsmq_async::RsmqConnection + Clone + Send>(
|
||||
pub async fn add_completed_job(
|
||||
db: &Pool<Postgres>,
|
||||
queued_job: &QueuedJob,
|
||||
success: bool,
|
||||
skipped: bool,
|
||||
result: serde_json::Value,
|
||||
logs: String,
|
||||
rsmq: Option<R>,
|
||||
) -> Result<Uuid, Error> {
|
||||
let duration =
|
||||
if queued_job.job_kind == JobKind::Flow || queued_job.job_kind == JobKind::FlowPreview {
|
||||
@@ -91,7 +89,7 @@ pub async fn add_completed_job<R: rsmq_async::RsmqConnection + Clone + Send>(
|
||||
.ok()
|
||||
.flatten()
|
||||
.flatten();
|
||||
let mut tx: QueueTransaction<'_, R> = (rsmq, db.begin().await?).into();
|
||||
let mut tx = db.begin().await?;
|
||||
let job_id = queued_job.id.clone();
|
||||
sqlx::query!(
|
||||
"INSERT INTO completed_job AS cj
|
||||
@@ -161,7 +159,9 @@ pub async fn add_completed_job<R: rsmq_async::RsmqConnection + Clone + Send>(
|
||||
.execute(&mut tx)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Could not add completed job {job_id}: {e}")))?;
|
||||
tx = delete_job(tx, &queued_job.workspace_id, job_id).await?;
|
||||
println!("{:?}", SystemTime::now());
|
||||
|
||||
let _ = delete_job(db, &queued_job.workspace_id, job_id).await?;
|
||||
if !queued_job.is_flow_step
|
||||
&& queued_job.job_kind != JobKind::Flow
|
||||
&& queued_job.job_kind != JobKind::FlowPreview
|
||||
@@ -216,14 +216,14 @@ pub async fn add_completed_job<R: rsmq_async::RsmqConnection + Clone + Send>(
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub async fn schedule_again_if_scheduled<'c, R: rsmq_async::RsmqConnection + Clone + Send + 'c>(
|
||||
mut tx: QueueTransaction<'c, R>,
|
||||
pub async fn schedule_again_if_scheduled<'c>(
|
||||
mut tx: Transaction<'c, Postgres>,
|
||||
db: &Pool<Postgres>,
|
||||
schedule_path: &str,
|
||||
script_path: &str,
|
||||
w_id: &str,
|
||||
) -> windmill_common::error::Result<QueueTransaction<'c, R>> {
|
||||
let schedule = get_schedule_opt(tx.transaction_mut(), w_id, schedule_path)
|
||||
) -> windmill_common::error::Result<Transaction<'c, Postgres>> {
|
||||
let schedule = get_schedule_opt(&mut tx, w_id, schedule_path)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
Error::InternalErr(format!(
|
||||
|
||||
@@ -17,7 +17,7 @@ use windmill_api_client::Client;
|
||||
use windmill_parser_go::parse_go_imports;
|
||||
use std::{
|
||||
borrow::Borrow, collections::HashMap, io, os::unix::process::ExitStatusExt, panic,
|
||||
process::Stdio, time::{Duration}, sync::atomic::Ordering,
|
||||
process::Stdio, time::{Duration, SystemTime}, sync::atomic::Ordering,
|
||||
sync::{Arc},
|
||||
};
|
||||
use tracing::{trace_span, Instrument};
|
||||
@@ -411,8 +411,6 @@ lazy_static::lazy_static! {
|
||||
static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new());
|
||||
static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| String::new());
|
||||
static ref PIP_INDEX_URL: Option<String> = std::env::var("PIP_INDEX_URL").ok();
|
||||
static ref GOPRIVATE: Option<String> = std::env::var("GOPRIVATE").ok();
|
||||
static ref NETRC: Option<String> = std::env::var("NETRC").ok();
|
||||
static ref PIP_EXTRA_INDEX_URL: Option<String> = std::env::var("PIP_EXTRA_INDEX_URL").ok();
|
||||
static ref PIP_TRUSTED_HOST: Option<String> = std::env::var("PIP_TRUSTED_HOST").ok();
|
||||
static ref DENO_AUTH_TOKENS: String = std::env::var("DENO_AUTH_TOKENS")
|
||||
@@ -535,8 +533,8 @@ impl AuthedClient {
|
||||
}
|
||||
|
||||
|
||||
#[tracing::instrument(skip(rsmq), level = "trace")]
|
||||
pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub async fn run_worker(
|
||||
db: &Pool<Postgres>,
|
||||
worker_instance: &str,
|
||||
worker_name: String,
|
||||
@@ -544,8 +542,8 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
ip: &str,
|
||||
mut rx: tokio::sync::broadcast::Receiver<()>,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) {
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if !*DISABLE_NSJAIL {
|
||||
tracing::warn!(
|
||||
@@ -583,33 +581,30 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
.unwrap();
|
||||
|
||||
|
||||
let all_langs = [
|
||||
None,
|
||||
Some(ScriptLang::Python3),
|
||||
Some(ScriptLang::Deno),
|
||||
Some(ScriptLang::Go),
|
||||
Some(ScriptLang::Bash)];
|
||||
|
||||
let worker_execution_duration: HashMap<_, _> = all_langs.clone().into_iter().map(|x| (x.clone(), prometheus::register_histogram!(
|
||||
let worker_execution_duration = prometheus::register_histogram_vec!(
|
||||
prometheus::HistogramOpts::new(
|
||||
"worker_execution_duration",
|
||||
"Duration between receiving a job and completing it",
|
||||
)
|
||||
.const_label("name", &worker_name)
|
||||
.const_label("language", x.map(|x| x.as_str()).unwrap_or("none")))
|
||||
.expect("register prometheus metric"))
|
||||
).collect();
|
||||
.const_label("name", &worker_name),
|
||||
&["workspace_id", "language"],
|
||||
)
|
||||
.expect("register prometheus metric");
|
||||
|
||||
let worker_execution_duration_counter = prometheus::register_counter!(prometheus::opts!(
|
||||
"worker_execution_duration_counter",
|
||||
"Total number of seconds spent executing jobs"
|
||||
)
|
||||
.const_label("name", &worker_name))
|
||||
.expect("register prometheus metric");
|
||||
|
||||
|
||||
let worker_execution_duration_counter: HashMap<_, _> = all_langs.clone().into_iter().map(|x| (x.clone(), prometheus::register_counter!(
|
||||
prometheus::Opts::new(
|
||||
"worker_execution_duration_counter",
|
||||
"Total number of seconds spent executing jobs"
|
||||
)
|
||||
.const_label("name", &worker_name)
|
||||
.const_label("language", x.map(|x| x.as_str()).unwrap_or("none")))
|
||||
.expect("register prometheus metric"))
|
||||
).collect();
|
||||
let worker_sleep_duration = prometheus::register_histogram!(prometheus::HistogramOpts::new(
|
||||
"worker_sleep_duration",
|
||||
"Duration sleeping waiting for job",
|
||||
)
|
||||
.const_label("name", &worker_name),)
|
||||
.expect("register prometheus metric");
|
||||
|
||||
|
||||
let worker_sleep_duration_counter = prometheus::register_counter!(prometheus::opts!(
|
||||
@@ -634,24 +629,19 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
.const_label("name", &worker_name))
|
||||
.expect("register prometheus metric");
|
||||
|
||||
let worker_execution_failed: HashMap<_, _> = all_langs.clone().into_iter().map(|x| (x.clone(), prometheus::register_int_counter!(
|
||||
prometheus::Opts::new(
|
||||
"worker_execution_failed", "Number of failed jobs",
|
||||
)
|
||||
.const_label("name", &worker_name)
|
||||
.const_label("language", x.map(|x| x.as_str()).unwrap_or("none")))
|
||||
.expect("register prometheus metric"))
|
||||
).collect();
|
||||
let worker_execution_failed = prometheus::register_int_counter_vec!(
|
||||
prometheus::Opts::new("worker_execution_failed", "Number of failed jobs",)
|
||||
.const_label("name", &worker_name),
|
||||
&["workspace_id", "language"],
|
||||
)
|
||||
.expect("register prometheus metric");
|
||||
|
||||
|
||||
let worker_execution_count: HashMap<_, _> = all_langs.into_iter().map(|x| (x.clone(), prometheus::register_int_counter!(
|
||||
prometheus::Opts::new(
|
||||
"worker_execution_count", "Number of executed jobs"
|
||||
)
|
||||
.const_label("name", &worker_name)
|
||||
.const_label("language", x.map(|x| x.as_str()).unwrap_or("none")))
|
||||
.expect("register prometheus metric"))
|
||||
).collect();
|
||||
let worker_execution_count = prometheus::register_int_counter_vec!(
|
||||
prometheus::Opts::new("worker_execution_count", "Number of executed jobs",)
|
||||
.const_label("name", &worker_name),
|
||||
&["workspace_id", "language"],
|
||||
)
|
||||
.expect("register prometheus metric");
|
||||
|
||||
let worker_busy: prometheus::IntGauge = prometheus::register_int_gauge!(prometheus::Opts::new(
|
||||
"worker_busy",
|
||||
@@ -700,7 +690,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
if *METRICS_ENABLED {
|
||||
worker_busy.set(0);
|
||||
uptime_metric.inc_by(
|
||||
((start_time.elapsed().as_millis() as f64)/1000.0 - uptime_metric.get())
|
||||
(((Instant::now() - start_time).as_millis() as f64)/1000.0 - uptime_metric.get())
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
@@ -762,8 +752,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
},
|
||||
(job, timer) = {
|
||||
let timer = if *METRICS_ENABLED { Some(worker_pull_duration.start_timer()) } else { None };
|
||||
pull(&db, WHITELIST_WORKSPACES.clone(), BLACKLIST_WORKSPACES.clone(), rsmq.clone()).map(|x| (x, timer))
|
||||
} => {
|
||||
pull(&db, WHITELIST_WORKSPACES.clone(), BLACKLIST_WORKSPACES.clone()).map(|x| (x, timer)) } => {
|
||||
timer.map(|timer| {
|
||||
let duration_pull_s = timer.stop_and_record();
|
||||
worker_pull_duration_counter.inc_by(duration_pull_s);
|
||||
@@ -772,7 +761,6 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
},
|
||||
}
|
||||
}.instrument(trace_span!("worker_get_next_job")).await;
|
||||
|
||||
if do_break {
|
||||
return true;
|
||||
}
|
||||
@@ -781,30 +769,29 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
}
|
||||
match next_job {
|
||||
Ok(Some(job)) => {
|
||||
// println!("{:?}", SystemTime::now());
|
||||
println!("{:?}", SystemTime::now());
|
||||
|
||||
let token = create_token_for_owner_in_bg(&db, &job).await;
|
||||
let language = job.language.clone();
|
||||
let label_values = [
|
||||
&job.workspace_id,
|
||||
job.language.as_ref().map(|l| l.as_str()).unwrap_or(""),
|
||||
];
|
||||
|
||||
let _timer = worker_execution_duration
|
||||
.get(&language)
|
||||
.expect("no timer found")
|
||||
.with_label_values(label_values.as_slice())
|
||||
.start_timer();
|
||||
|
||||
jobs_executed += 1;
|
||||
if *METRICS_ENABLED {
|
||||
worker_execution_count
|
||||
.get(&language)
|
||||
.expect("no timer found")
|
||||
.with_label_values(label_values.as_slice())
|
||||
.inc();
|
||||
}
|
||||
|
||||
let metrics = if *METRICS_ENABLED {
|
||||
Some(Metrics {
|
||||
worker_execution_failed: worker_execution_failed
|
||||
.get(&language)
|
||||
.expect("no timer found").clone(),
|
||||
})
|
||||
} else { None };
|
||||
let metrics = Metrics {
|
||||
worker_execution_failed: worker_execution_failed
|
||||
.with_label_values(label_values.as_slice()),
|
||||
};
|
||||
|
||||
let job_root = job.root_job.map(|x| x.to_string()).unwrap_or_else(|| "none".to_string());
|
||||
tracing::info!(worker = %worker_name, id = %job.id, root_id = %job_root, "fetched job {}, root job: {}", job.id, job_root);
|
||||
@@ -815,6 +802,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
.create(&job_dir)
|
||||
.await
|
||||
.expect("could not create job dir");
|
||||
println!("dir creation {:?}", SystemTime::now());
|
||||
|
||||
let same_worker = job.same_worker;
|
||||
|
||||
@@ -839,6 +827,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
.await
|
||||
.expect("could not create shared dir");
|
||||
}
|
||||
println!("bef token: {:?}", SystemTime::now());
|
||||
|
||||
let authed_client = AuthedClientBackgroundTask { base_internal_url: base_internal_url.to_string(), token: token, workspace: job.workspace_id.to_string(), client: OnceCell::new() };
|
||||
let is_flow = job.job_kind == JobKind::Flow || job.job_kind == JobKind::FlowPreview || job.job_kind == JobKind::FlowDependencies;
|
||||
@@ -852,8 +841,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
&job_dir,
|
||||
metrics.clone(),
|
||||
same_worker_tx.clone(),
|
||||
base_internal_url,
|
||||
rsmq.clone()
|
||||
base_internal_url
|
||||
)
|
||||
.await
|
||||
.err()
|
||||
@@ -863,30 +851,27 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
&authed_client.get_authed().await,
|
||||
job,
|
||||
err,
|
||||
metrics,
|
||||
Some(metrics),
|
||||
false,
|
||||
same_worker_tx.clone(),
|
||||
&worker_dir,
|
||||
base_internal_url,
|
||||
rsmq.clone()
|
||||
base_internal_url
|
||||
)
|
||||
.await;
|
||||
};
|
||||
|
||||
let duration = _timer.stop_and_record();
|
||||
worker_execution_duration_counter
|
||||
.get(&language)
|
||||
.expect("no timer found").inc_by(duration);
|
||||
worker_execution_duration_counter.inc_by(duration);
|
||||
|
||||
if !*KEEP_JOB_DIR && !(is_flow && same_worker) {
|
||||
let _ = tokio::fs::remove_dir_all(job_dir).await;
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
let _timer = if *METRICS_ENABLED { Some(Instant::now()) } else { None };
|
||||
let _timer = if *METRICS_ENABLED { Some(worker_sleep_duration.start_timer()) } else { None };
|
||||
tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE)).await;
|
||||
_timer.map(|timer| {
|
||||
let duration = timer.elapsed().as_secs_f64();
|
||||
let duration = timer.stop_and_record();
|
||||
worker_sleep_duration_counter.inc_by(duration);
|
||||
});
|
||||
}
|
||||
@@ -905,7 +890,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
async fn handle_job_error(
|
||||
db: &Pool<Postgres>,
|
||||
client: &AuthedClient,
|
||||
job: QueuedJob,
|
||||
@@ -915,14 +900,12 @@ async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
same_worker_tx: Sender<Uuid>,
|
||||
worker_dir: &str,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) {
|
||||
let err = match err {
|
||||
Error::JsonErr(err) => err,
|
||||
_ => json!({"message": err.to_string(), "name": "InternalErr"}),
|
||||
};
|
||||
|
||||
let rsmq_2 = rsmq.clone();
|
||||
let update_job_future = || {
|
||||
add_completed_job_error(
|
||||
db,
|
||||
@@ -930,16 +913,15 @@ async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
format!("Unexpected error during job execution:\n{err}"),
|
||||
err.clone(),
|
||||
metrics.clone(),
|
||||
rsmq_2,
|
||||
)
|
||||
};
|
||||
|
||||
let update_job_future = if job.is_flow_step || job.job_kind == JobKind::FlowPreview || job.job_kind == JobKind::Flow {
|
||||
let (flow, job_status_to_update, update_job_future) = if let Some(parent_job_id) = job.parent_job {
|
||||
if job.is_flow_step || job.job_kind == JobKind::FlowPreview || job.job_kind == JobKind::Flow {
|
||||
let (flow, job_status_to_update) = if let Some(parent_job_id) = job.parent_job {
|
||||
let _ = update_job_future().await;
|
||||
(parent_job_id, job.id, None)
|
||||
(parent_job_id, job.id)
|
||||
} else {
|
||||
(job.id, Uuid::nil(), Some(update_job_future))
|
||||
(job.id, Uuid::nil())
|
||||
};
|
||||
|
||||
let updated_flow = update_flow_status_after_job_completion(
|
||||
@@ -956,7 +938,7 @@ async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
worker_dir,
|
||||
None,
|
||||
base_internal_url,
|
||||
rsmq.clone(),
|
||||
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -972,20 +954,15 @@ async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
format!("Unexpected error during flow job error handling:\n{err}"),
|
||||
json!({"message": err.to_string(), "name": "InternalErr"}),
|
||||
metrics.clone(),
|
||||
rsmq,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_job_future
|
||||
} else {
|
||||
Some(update_job_future)
|
||||
};
|
||||
if let Some(f) = update_job_future {
|
||||
let _ = f().await;
|
||||
}
|
||||
if job.parent_job.is_none() {
|
||||
let _ = update_job_future().await;
|
||||
}
|
||||
tracing::error!(job_id = %job.id, "error handling job: {err:#?} {} {} {}", job.id, job.workspace_id, job.created_by);
|
||||
}
|
||||
@@ -1009,20 +986,19 @@ async fn insert_initial_ping(
|
||||
|
||||
|
||||
fn extract_error_value(log_lines: &str, i: i32) -> serde_json::Value {
|
||||
return json!({"message": format!("ExitCode: {i}, last log lines:\n{}", log_lines.to_string().trim().to_string()), "name": "ExecutionErr"});
|
||||
return json!({"message": format!("ExitCode: {i}, last log lines: {}", log_lines.to_string().trim().to_string()), "name": "ExecutionErr"});
|
||||
}
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
async fn handle_queued_job(
|
||||
job: QueuedJob,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
client: &AuthedClientBackgroundTask,
|
||||
worker_name: &str,
|
||||
worker_dir: &str,
|
||||
job_dir: &str,
|
||||
metrics: Option<Metrics>,
|
||||
metrics: Metrics,
|
||||
same_worker_tx: Sender<Uuid>,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) -> windmill_common::error::Result<()> {
|
||||
if job.canceled {
|
||||
return Err(Error::JsonErr(canceled_job_to_result(&job)))?;
|
||||
@@ -1041,14 +1017,13 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
args,
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
rsmq,
|
||||
base_internal_url
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
_ => {
|
||||
let mut logs = "".to_string();
|
||||
// println!("handle queue {:?}", SystemTime::now());
|
||||
println!("handle queue {:?}", SystemTime::now());
|
||||
if let Some(log_str) = &job.logs {
|
||||
logs.push_str(&log_str);
|
||||
}
|
||||
@@ -1108,8 +1083,8 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
let client = &client.get_authed().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
// println!("bef completed job{:?}", SystemTime::now());
|
||||
add_completed_job(db, &job, true, false, r.clone(), logs, rsmq.clone()).await?;
|
||||
println!("bef completed job{:?}", SystemTime::now());
|
||||
add_completed_job(db, &job, true, false, r.clone(), logs).await?;
|
||||
if job.is_flow_step {
|
||||
if let Some(parent_job) = job.parent_job {
|
||||
update_flow_status_after_job_completion(
|
||||
@@ -1120,13 +1095,13 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
&job.workspace_id,
|
||||
true,
|
||||
r,
|
||||
metrics.clone(),
|
||||
Some(metrics.clone()),
|
||||
false,
|
||||
same_worker_tx.clone(),
|
||||
worker_dir,
|
||||
None,
|
||||
base_internal_url,
|
||||
rsmq.clone()
|
||||
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -1161,7 +1136,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
};
|
||||
|
||||
let result =
|
||||
add_completed_job_error(db, &job, logs, error_value, metrics.clone(), rsmq.clone())
|
||||
add_completed_job_error(db, &job, logs, error_value, Some(metrics.clone()))
|
||||
.await?;
|
||||
if job.is_flow_step {
|
||||
if let Some(parent_job) = job.parent_job {
|
||||
@@ -1173,13 +1148,13 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
&job.workspace_id,
|
||||
false,
|
||||
result,
|
||||
metrics,
|
||||
Some(metrics),
|
||||
false,
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
None,
|
||||
base_internal_url,
|
||||
rsmq
|
||||
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -1308,7 +1283,7 @@ mount {{
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
// println!("handle lang job {:?}", SystemTime::now());
|
||||
println!("handle lang job {:?}", SystemTime::now());
|
||||
|
||||
let result: error::Result<serde_json::Value> = match language {
|
||||
None => {
|
||||
@@ -1384,7 +1359,7 @@ mount {{
|
||||
&lang_str,
|
||||
job.id
|
||||
);
|
||||
// println!("handled job: {:?}", SystemTime::now());
|
||||
println!("handled job: {:?}", SystemTime::now());
|
||||
|
||||
result
|
||||
}
|
||||
@@ -1423,7 +1398,6 @@ async fn handle_go_job(
|
||||
} else {
|
||||
(false, false)
|
||||
};
|
||||
|
||||
logs.push_str("\n\n--- GO DEPENDENCIES SETUP ---\n");
|
||||
set_logs(logs, &job.id, db).await;
|
||||
|
||||
@@ -1569,9 +1543,6 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?
|
||||
} else {
|
||||
if let Some(ref netrc) = *NETRC {
|
||||
write_file(&HOME_ENV, ".netrc", netrc).await?;
|
||||
}
|
||||
Command::new(GO_PATH.as_str())
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
@@ -1579,7 +1550,6 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.env("GOPATH", GO_CACHE_DIR)
|
||||
.env("GOPRIVATE", GOPRIVATE.as_ref().unwrap_or(&String::new()))
|
||||
.env("HOME", HOME_ENV.as_str())
|
||||
.args(vec!["run", "main.go"])
|
||||
.stdout(Stdio::piped())
|
||||
@@ -1771,8 +1741,6 @@ async fn handle_deno_job(
|
||||
let import_map = format!(
|
||||
r#"{{
|
||||
"imports": {{
|
||||
"{base_internal_url}/api/w/{w_id}/scripts/raw/p/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
|
||||
"{base_internal_url}": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
|
||||
"/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
|
||||
"./wrapper.ts": "./wrapper.ts",
|
||||
"./main.ts": "./main.ts"{relative_mounts}
|
||||
@@ -2505,7 +2473,6 @@ async fn install_go_dependencies(
|
||||
new_lockfile = true;
|
||||
}
|
||||
}
|
||||
|
||||
let mod_command = if skip_tidy {
|
||||
"download"
|
||||
} else {
|
||||
@@ -2532,11 +2499,8 @@ async fn install_go_dependencies(
|
||||
let mut file = File::open(format!("{job_dir}/go.mod")).await?;
|
||||
file.read_to_string(&mut req_content).await?;
|
||||
req_content.push_str(GO_REQ_SPLITTER);
|
||||
let sum_path = format!("{job_dir}/go.sum");
|
||||
if tokio::fs::metadata(&sum_path).await.is_ok() {
|
||||
let mut file = File::open(sum_path).await?;
|
||||
file.read_to_string(&mut req_content).await?;
|
||||
}
|
||||
let mut file = File::open(format!("{job_dir}/go.sum")).await?;
|
||||
file.read_to_string(&mut req_content).await?;
|
||||
|
||||
if non_dep_job {
|
||||
sqlx::query!(
|
||||
@@ -2991,14 +2955,13 @@ async fn append_logs(job_id: uuid::Uuid, logs: impl AsRef<str>, db: impl Borrow<
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_zombie_jobs_periodically<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
pub async fn handle_zombie_jobs_periodically(
|
||||
db: &Pool<Postgres>,
|
||||
mut rx: tokio::sync::broadcast::Receiver<()>,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) {
|
||||
loop {
|
||||
handle_zombie_jobs(db, base_internal_url, rsmq.clone()).await;
|
||||
handle_zombie_jobs(db, base_internal_url).await;
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(Duration::from_secs(30)) => (),
|
||||
@@ -3010,7 +2973,7 @@ pub async fn handle_zombie_jobs_periodically<R: rsmq_async::RsmqConnection + Sen
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(db: &Pool<Postgres>, base_internal_url: &str, rsmq: Option<R>) {
|
||||
async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str) {
|
||||
if *RESTART_ZOMBIE_JOBS {
|
||||
let restarted = sqlx::query!(
|
||||
"UPDATE queue SET running = false, started_at = null, logs = logs || '\nRestarted job after not receiving job''s ping for too long the ' || now() || '\n\n' WHERE last_ping < now() - ($1 || ' seconds')::interval AND running = true AND job_kind != $2 AND job_kind != $3 AND same_worker = false RETURNING id, workspace_id, last_ping",
|
||||
@@ -3088,7 +3051,6 @@ async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
same_worker_tx_never_used,
|
||||
"",
|
||||
base_internal_url,
|
||||
rsmq.clone()
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -31,13 +31,11 @@ use windmill_common::{
|
||||
type DB = sqlx::Pool<sqlx::Postgres>;
|
||||
|
||||
use windmill_queue::{
|
||||
canceled_job_to_result, get_queued_job, push, JobPayload, QueueTransaction, QueuedJob, RawCode,
|
||||
canceled_job_to_result, get_queued_job, push, JobPayload, QueuedJob, RawCode,
|
||||
};
|
||||
|
||||
// #[instrument(level = "trace", skip_all)]
|
||||
pub async fn update_flow_status_after_job_completion<
|
||||
R: rsmq_async::RsmqConnection + Send + Sync + Clone,
|
||||
>(
|
||||
pub async fn update_flow_status_after_job_completion(
|
||||
db: &DB,
|
||||
client: &AuthedClient,
|
||||
flow: uuid::Uuid,
|
||||
@@ -51,7 +49,6 @@ pub async fn update_flow_status_after_job_completion<
|
||||
worker_dir: &str,
|
||||
stop_early_override: Option<bool>,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) -> error::Result<()> {
|
||||
// this is manual tailrecursion because async_recursion blows up the stack
|
||||
let mut depth = 0;
|
||||
@@ -70,7 +67,6 @@ pub async fn update_flow_status_after_job_completion<
|
||||
stop_early_override,
|
||||
base_internal_url,
|
||||
depth,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
while let Some(nrec) = rec {
|
||||
@@ -90,7 +86,6 @@ pub async fn update_flow_status_after_job_completion<
|
||||
nrec.stop_early_override,
|
||||
base_internal_url,
|
||||
depth,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -104,9 +99,7 @@ pub struct RecUpdateFlowStatusAfterJobCompletion {
|
||||
stop_early_override: Option<bool>,
|
||||
}
|
||||
// #[instrument(level = "trace", skip_all)]
|
||||
pub async fn update_flow_status_after_job_completion_internal<
|
||||
R: rsmq_async::RsmqConnection + Send + Sync + Clone,
|
||||
>(
|
||||
pub async fn update_flow_status_after_job_completion_internal(
|
||||
db: &DB,
|
||||
client: &AuthedClient,
|
||||
flow: uuid::Uuid,
|
||||
@@ -121,12 +114,11 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
stop_early_override: Option<bool>,
|
||||
base_internal_url: &str,
|
||||
depth: u8,
|
||||
rsmq: Option<R>,
|
||||
) -> error::Result<Option<RecUpdateFlowStatusAfterJobCompletion>> {
|
||||
let (should_continue_flow, flow_job, stop_early, skip_if_stop_early, nresult) = {
|
||||
tracing::debug!("UPDATE FLOW STATUS: {flow:?} {success} {result:?} {w_id} {depth}");
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq.clone(), db.begin().await?).into();
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
let old_status_json = sqlx::query_scalar!(
|
||||
"SELECT flow_status FROM queue WHERE id = $1 AND workspace_id = $2",
|
||||
@@ -162,7 +154,7 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
module_status,
|
||||
FlowStatusModule::InProgress { iterator: Some(_), .. }
|
||||
) {
|
||||
compute_skip_loop_failures(flow, old_status.step, tx.transaction_mut())
|
||||
compute_skip_loop_failures(flow, old_status.step, &mut tx)
|
||||
.await?
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
@@ -205,11 +197,9 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
FlowStatusModule::InProgress {
|
||||
branchall: Some(BranchAllStatus { branch, .. }),
|
||||
..
|
||||
} => {
|
||||
compute_skip_branchall_failure(flow, old_status.step, *branch, tx.transaction_mut())
|
||||
.await?
|
||||
.unwrap_or(false)
|
||||
}
|
||||
} => compute_skip_branchall_failure(flow, old_status.step, *branch, &mut tx)
|
||||
.await?
|
||||
.unwrap_or(false),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@@ -471,7 +461,7 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
.context("remove flow status retry")?;
|
||||
}
|
||||
|
||||
let flow_job = get_queued_job(flow, w_id, tx.transaction_mut())
|
||||
let flow_job = get_queued_job(flow, w_id, &mut tx)
|
||||
.await?
|
||||
.ok_or_else(|| Error::InternalErr(format!("requiring flow to be in the queue")))?;
|
||||
|
||||
@@ -513,9 +503,7 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
{
|
||||
true
|
||||
}
|
||||
false if has_failure_module(flow, tx.transaction_mut()).await? && !is_failure_step => {
|
||||
true
|
||||
}
|
||||
false if has_failure_module(flow, &mut tx).await? && !is_failure_step => true,
|
||||
false => false,
|
||||
};
|
||||
|
||||
@@ -560,7 +548,6 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
logs,
|
||||
canceled_job_to_result(&flow_job),
|
||||
metrics.clone(),
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
@@ -571,7 +558,6 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
stop_early && skip_if_stop_early,
|
||||
nresult.clone(),
|
||||
logs,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -585,7 +571,6 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
same_worker_tx.clone(),
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -596,7 +581,6 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
"Unexpected error during flow chaining:\n".to_string(),
|
||||
json!({"message": err.to_string(), "name": "InternalError"}),
|
||||
metrics.clone(),
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await;
|
||||
true
|
||||
@@ -847,7 +831,7 @@ async fn transform_input(
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
pub async fn handle_flow(
|
||||
flow_job: &QueuedJob,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
client: &AuthedClient,
|
||||
@@ -855,7 +839,6 @@ pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
same_worker_tx: Sender<Uuid>,
|
||||
worker_dir: &str,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) -> anyhow::Result<()> {
|
||||
let value = flow_job
|
||||
.raw_flow
|
||||
@@ -879,7 +862,6 @@ pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
rsmq,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
@@ -887,7 +869,7 @@ pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
|
||||
#[async_recursion]
|
||||
// #[instrument(level = "trace", skip_all)]
|
||||
async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
async fn push_next_flow_job(
|
||||
flow_job: &QueuedJob,
|
||||
mut status: FlowStatus,
|
||||
flow: FlowValue,
|
||||
@@ -897,7 +879,6 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
same_worker_tx: Sender<Uuid>,
|
||||
worker_dir: &str,
|
||||
base_internal_url: &str,
|
||||
rsmq: Option<R>,
|
||||
) -> error::Result<()> {
|
||||
let job_root = flow_job
|
||||
.root_job
|
||||
@@ -934,7 +915,6 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
worker_dir,
|
||||
None,
|
||||
base_internal_url,
|
||||
rsmq,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -1105,7 +1085,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
let logs = "Timed out waiting to be resumed".to_string();
|
||||
let result = json!({ "error": {"message": logs, "name": "SuspendedTimeout"}});
|
||||
let _uuid =
|
||||
add_completed_job(db, &flow_job, success, skipped, result, logs, rsmq).await?;
|
||||
add_completed_job(db, &flow_job, success, skipped, result, logs).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
@@ -1306,7 +1286,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
flow.same_worker && module.suspend.is_none() && module.sleep.is_none();
|
||||
|
||||
/* Finally, push the job into the queue */
|
||||
let mut tx = (rsmq.clone(), db.begin().await?).into();
|
||||
let mut tx = db.begin().await?;
|
||||
let mut uuids = vec![];
|
||||
|
||||
let len = match &job_payloads {
|
||||
@@ -1472,7 +1452,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
json!(i),
|
||||
flow_job.id
|
||||
)
|
||||
.execute(db)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
} else {
|
||||
sqlx::query!(
|
||||
@@ -1488,7 +1468,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
json!(i),
|
||||
flow_job.id
|
||||
)
|
||||
.execute(db)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
};
|
||||
|
||||
|
||||
53
benchmarks/example_fake_user.json
Normal file
53
benchmarks/example_fake_user.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"type": "RANDOM",
|
||||
"actions": [
|
||||
{
|
||||
"weight": 1,
|
||||
"action": {
|
||||
"type": "RANDOM",
|
||||
"actions": [
|
||||
{
|
||||
"weight": 1,
|
||||
"action": {
|
||||
"type": "PREVIEW_SCRIPT",
|
||||
"workspace": "demo",
|
||||
"language": "deno",
|
||||
"args": {},
|
||||
"content": "export async function main() { return \"Hello World\"; }"
|
||||
}
|
||||
},
|
||||
{
|
||||
"weight": 1,
|
||||
"action": {
|
||||
"type": "PREVIEW_SCRIPT",
|
||||
"workspace": "demo",
|
||||
"language": "python3",
|
||||
"args": {},
|
||||
"content": "def main(): return \"Hello World\";"
|
||||
}
|
||||
},
|
||||
{
|
||||
"weight": 1,
|
||||
"action": {
|
||||
"type": "PREVIEW_SCRIPT",
|
||||
"workspace": "demo",
|
||||
"language": "go",
|
||||
"args": {},
|
||||
"content": "func main() { return \"Hello World\" }"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"weight": 0.5,
|
||||
"action": {
|
||||
"type": "PREVIEW_SCRIPT",
|
||||
"workspace": "demo",
|
||||
"language": "deno",
|
||||
"args": {},
|
||||
"content": "import { delay } from \"https://deno.land/std@0.131.0/async/delay.ts\"; export async function main() { await delay(1000); return \"Hello World\"; }"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"type": "PREVIEW_SCRIPT",
|
||||
"workspace": "demo",
|
||||
"language": "deno",
|
||||
"args": {},
|
||||
"content": "export async function main() { return \"Hello World\"; }"
|
||||
}
|
||||
@@ -27,60 +27,60 @@ await new Command()
|
||||
"The number of workers to run at once.",
|
||||
{
|
||||
default: 1,
|
||||
},
|
||||
}
|
||||
)
|
||||
.option(
|
||||
"-s --seconds <seconds:number>",
|
||||
"How long to run the benchmark for (in seconds).",
|
||||
{
|
||||
default: 30,
|
||||
},
|
||||
}
|
||||
)
|
||||
.option("--max <max:number>", "Maximum number of operations performed.")
|
||||
.option("-e --email <email:string>", "The email to use to login.")
|
||||
.option("-p --password <password:string>", "The password to use to login.")
|
||||
.env(
|
||||
"WM_TOKEN=<token:string>",
|
||||
"The token to use when talking to the API server. Preferred over manual login.",
|
||||
"The token to use when talking to the API server. Preferred over manual login."
|
||||
)
|
||||
.option(
|
||||
"-t --token <token:string>",
|
||||
"The token to use when talking to the API server. Preferred over manual login.",
|
||||
"The token to use when talking to the API server. Preferred over manual login."
|
||||
)
|
||||
.env(
|
||||
"WM_WORKSPACE=<workspace:string>",
|
||||
"The workspace to spawn scripts from.",
|
||||
"The workspace to spawn scripts from."
|
||||
)
|
||||
.option(
|
||||
"-w --workspace <workspace:string>",
|
||||
"The workspace to spawn scripts from.",
|
||||
{ default: "starter" },
|
||||
{ default: "starter" }
|
||||
)
|
||||
.option("-m --metrics <metrics:string>", "The url to scrape metrics from.", {
|
||||
default: "http://localhost:8001/metrics",
|
||||
})
|
||||
.option(
|
||||
"--export-json <export_json:string>",
|
||||
"If set, exports will be into a JSON file.",
|
||||
"If set, exports will be into a JSON file."
|
||||
)
|
||||
.option(
|
||||
"--export-csv <export_csv:string>",
|
||||
"If set, exports will be into a csv file.",
|
||||
"If set, exports will be into a csv file."
|
||||
)
|
||||
.option(
|
||||
"--export-histograms [histograms...:string]",
|
||||
"Mark metrics (without label) that are reported as histograms to export.",
|
||||
"Mark metrics (without label) that are reported as histograms to export."
|
||||
)
|
||||
.option(
|
||||
"--export-simple [simple...:string]",
|
||||
"Mark metrics (without label) that are reported as simple values.",
|
||||
"Mark metrics (without label) that are reported as simple values."
|
||||
)
|
||||
.option(
|
||||
"--maximum-throughput <maximum_throughput:number>",
|
||||
"Maximum number of jobs/flows to start in one second.",
|
||||
{
|
||||
default: Infinity,
|
||||
},
|
||||
}
|
||||
)
|
||||
.option("--use-flows", "Run flows instead of jobs.")
|
||||
.option("--custom <custom_path:string>", "Use custom actions during bench")
|
||||
@@ -89,11 +89,11 @@ await new Command()
|
||||
"The maximum time in ms to wait for jobs to complete.",
|
||||
{
|
||||
default: 90000,
|
||||
},
|
||||
}
|
||||
)
|
||||
.option(
|
||||
"--continous",
|
||||
"Run the benchmark forever. This effectively disables metric collection & exports. No zombie jobs will be tracked.",
|
||||
"Run the benchmark forever. This effectively disables metric collection & exports. No zombie jobs will be tracked."
|
||||
)
|
||||
.option(
|
||||
"--histogram-buckets [buckets...:string]",
|
||||
@@ -114,7 +114,7 @@ await new Command()
|
||||
"0.01",
|
||||
"0.005",
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
.action(
|
||||
async ({
|
||||
@@ -162,7 +162,7 @@ await new Command()
|
||||
new URL("./scraper.ts", import.meta.url).href,
|
||||
{
|
||||
type: "module",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
metrics_worker.postMessage({
|
||||
@@ -192,8 +192,8 @@ await new Command()
|
||||
zombieTimeout,
|
||||
},
|
||||
null,
|
||||
4,
|
||||
),
|
||||
4
|
||||
)
|
||||
);
|
||||
|
||||
const config = {
|
||||
@@ -245,22 +245,19 @@ await new Command()
|
||||
const updateState = setInterval(async () => {
|
||||
const elapsed = start ? Math.ceil((Date.now() - start) / 1000) : 0;
|
||||
const sum = jobsSent.reduce((a, b) => a + b, 0);
|
||||
const queue_length = (await (await fetch(
|
||||
host + "/api/w/" + config.workspace_id + "/jobs/queue/count",
|
||||
{ headers: { ["Authorization"]: "Bearer " + config.token } },
|
||||
)).json()).database_length;
|
||||
const queue_length = (
|
||||
await windmill.JobService.listQueue({
|
||||
workspace: config.workspace_id,
|
||||
})
|
||||
).length;
|
||||
await Deno.stdout.write(
|
||||
enc(
|
||||
`elapsed: ${elapsed}/${seconds} | jobs sent: ${
|
||||
JSON.stringify(
|
||||
jobsSent,
|
||||
)
|
||||
} (sum: ${sum} thr: ${
|
||||
(sum / elapsed).toFixed(
|
||||
2,
|
||||
)
|
||||
}) | queue: ${queue_length} \r`,
|
||||
),
|
||||
`elapsed: ${elapsed}/${seconds} | jobs sent: ${JSON.stringify(
|
||||
jobsSent
|
||||
)} (sum: ${sum} thr: ${(sum / elapsed).toFixed(
|
||||
2
|
||||
)}) | queue: ${queue_length} \r`
|
||||
)
|
||||
);
|
||||
}, 100);
|
||||
|
||||
@@ -287,7 +284,7 @@ await new Command()
|
||||
|
||||
const sum = jobsSent.reduce((a, b) => a + b, 0);
|
||||
await Deno.stdout.write(
|
||||
enc(" ".padStart(30) + `\rduration: ${seconds} | jobs sent: ${sum}\n`),
|
||||
enc(" ".padStart(30) + `\rduration: ${seconds} | jobs sent: ${sum}\n`)
|
||||
);
|
||||
|
||||
const shutdown_start = Date.now();
|
||||
@@ -305,7 +302,7 @@ await new Command()
|
||||
};
|
||||
worker.addEventListener("message", l);
|
||||
worker.postMessage(
|
||||
Number.isSafeInteger(zombieTimeout) ? zombieTimeout : 90000,
|
||||
Number.isSafeInteger(zombieTimeout) ? zombieTimeout : 90000
|
||||
);
|
||||
});
|
||||
|
||||
@@ -321,10 +318,11 @@ await new Command()
|
||||
console.log("incorrect results: ", incorrect_results);
|
||||
console.log(
|
||||
"queue length:",
|
||||
(await (await fetch(
|
||||
host + "/api/w/" + config.workspace_id + "/jobs/queue/count",
|
||||
{ headers: { ["Authorization"]: "Bearer " + config.token } },
|
||||
)).json()).database_length,
|
||||
(
|
||||
await windmill.JobService.listQueue({
|
||||
workspace: config.workspace_id,
|
||||
})
|
||||
).length
|
||||
);
|
||||
|
||||
metrics_worker!.postMessage("stop");
|
||||
@@ -348,7 +346,7 @@ await new Command()
|
||||
const value = values[i]!;
|
||||
const mean = value.reduce((acc, e) => acc + e, 0) / values.length;
|
||||
const stdev = Math.sqrt(
|
||||
value.reduce((acc, e) => acc + (e - mean) ** 2) / values.length,
|
||||
value.reduce((acc, e) => acc + (e - mean) ** 2) / values.length
|
||||
);
|
||||
obj[name] = { mean, stdev };
|
||||
}
|
||||
@@ -376,6 +374,6 @@ await new Command()
|
||||
f.close();
|
||||
}
|
||||
console.log("done");
|
||||
},
|
||||
}
|
||||
)
|
||||
.parse();
|
||||
|
||||
@@ -13,8 +13,6 @@ const promise = new Promise<{
|
||||
continous: boolean;
|
||||
max_per_worker: number;
|
||||
custom: Action | undefined;
|
||||
server: string;
|
||||
token: string;
|
||||
}>((resolve, _reject) => {
|
||||
self.onmessage = (evt) => {
|
||||
const sharedConfig = evt.data;
|
||||
@@ -26,8 +24,6 @@ const promise = new Promise<{
|
||||
continous: sharedConfig.continous,
|
||||
max_per_worker: sharedConfig.max_per_worker,
|
||||
custom: sharedConfig.custom,
|
||||
server: sharedConfig.server,
|
||||
token: sharedConfig.token,
|
||||
};
|
||||
self.name = "Worker " + sharedConfig.i;
|
||||
resolve(config);
|
||||
@@ -50,20 +46,19 @@ const updateStatusInterval = setInterval(() => {
|
||||
}, 100);
|
||||
|
||||
while (cont) {
|
||||
const queue_length = (await (await fetch(
|
||||
config.server + "/api/w/" + config.workspace_id + "/jobs/queue/count",
|
||||
{ headers: { ["Authorization"]: "Bearer " + config.token } },
|
||||
)).json()).database_length;
|
||||
if (queue_length > 2500) {
|
||||
const queue_length = (
|
||||
await windmill.JobService.listQueue({ workspace: config.workspace_id })
|
||||
).length;
|
||||
if (queue_length > 500) {
|
||||
console.log(
|
||||
`queue length: ${queue_length} > 2500. waiting... `,
|
||||
`queue length: ${queue_length} > 500. waiting... `
|
||||
);
|
||||
await sleep(0.5);
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
(total_spawned * 1000) / (Date.now() - start_time) >
|
||||
config.per_worker_throughput
|
||||
config.per_worker_throughput
|
||||
) {
|
||||
console.log("at maximum throughput. waiting...");
|
||||
await sleep(0.1);
|
||||
@@ -143,13 +138,13 @@ while (outstanding.length > 0 && Date.now() < end_time) {
|
||||
await Deno.stdout.write(
|
||||
enc(
|
||||
`uuid: ${uuid}, queue length: ${
|
||||
(await (await fetch(
|
||||
config.server + "/api/w/" + config.workspace_id +
|
||||
"/jobs/queue/count",
|
||||
{ headers: { ["Authorization"]: "Bearer " + config.token } },
|
||||
)).json()).database_length
|
||||
} \r`,
|
||||
),
|
||||
(
|
||||
await windmill.JobService.listQueue({
|
||||
workspace: config.workspace_id,
|
||||
})
|
||||
).length
|
||||
} \r`
|
||||
)
|
||||
);
|
||||
} else if (!config.useFlows) {
|
||||
r = r as api.CompletedJob;
|
||||
@@ -161,7 +156,7 @@ while (outstanding.length > 0 && Date.now() < end_time) {
|
||||
" != " +
|
||||
uuid +
|
||||
"job: \n" +
|
||||
JSON.stringify(r, null, 2),
|
||||
JSON.stringify(r, null, 2)
|
||||
);
|
||||
incorrect_results++;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import sync from "./sync.ts";
|
||||
import { tryResolveVersion } from "./context.ts";
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
|
||||
export const VERSION = "v1.87.0";
|
||||
export const VERSION = "v1.86.0";
|
||||
|
||||
let command: any = new Command()
|
||||
.name("wmill")
|
||||
|
||||
@@ -142,7 +142,6 @@ export async function handleFile(
|
||||
typed.summary === remote.summary &&
|
||||
typed.is_template === remote.is_template &&
|
||||
typed.kind == remote.kind &&
|
||||
!remote.archived &&
|
||||
remote?.lock == typed.lock?.join("\n") &&
|
||||
JSON.stringify(typed.schema) == JSON.stringify(remote.schema))
|
||||
) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
:8080 {
|
||||
bind {$ADDRESS}
|
||||
reverse_proxy /api/* http://localhost:8000
|
||||
reverse_proxy /api/* http://windmill:8000
|
||||
reverse_proxy /* http://localhost:3000
|
||||
reverse_proxy /ws/* http://lsp:3001 {
|
||||
lb_policy header "Authorization"
|
||||
}
|
||||
}
|
||||
}
|
||||
93
frontend/package-lock.json
generated
93
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.87.0",
|
||||
"version": "1.86.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill",
|
||||
"version": "1.87.0",
|
||||
"version": "1.86.0",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
@@ -29,7 +29,7 @@
|
||||
"svelte-autosize": "^1.0.1",
|
||||
"svelte-chartjs": "^3.1.0",
|
||||
"svelte-portal": "^2.2.0",
|
||||
"svelte-select": "^5.6.0",
|
||||
"svelte-select": "^5.3.1",
|
||||
"svelte-timezone-picker": "^2.0.3",
|
||||
"tailwind-merge": "^1.9.1",
|
||||
"vscode-languageclient": "^8.1.0",
|
||||
@@ -47,21 +47,21 @@
|
||||
"@types/d3-zoom": "^3.0.2",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/vscode": "~1.77.0",
|
||||
"@types/vscode": "~1.76.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"cssnano": "^6.0.0",
|
||||
"d3-dag": "^0.11.5",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"monaco-editor-workers": "0.36.0",
|
||||
"ol": "^7.2.2",
|
||||
"openapi-typescript-codegen": "^0.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pdfjs-dist": "^3.5.141",
|
||||
"pdfjs-dist": "^3.4.120",
|
||||
"postcss": "^8.4.18",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.3",
|
||||
@@ -80,7 +80,7 @@
|
||||
"svelte-preprocess": "^5.0.1",
|
||||
"svelte-range-slider-pips": "^2.1.1",
|
||||
"svelte-splitpanes": "^0.7.3",
|
||||
"svelte2tsx": "^0.6.11",
|
||||
"svelte2tsx": "^0.6.10",
|
||||
"tailwindcss": "^3.2.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.3",
|
||||
@@ -636,9 +636,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
|
||||
"integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz",
|
||||
"integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -1378,9 +1378,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/vscode": {
|
||||
"version": "1.77.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.77.0.tgz",
|
||||
"integrity": "sha512-MWFN5R7a33n8eJZJmdVlifjig3LWUNRrPeO1xemIcZ0ae0TEQuRc7G2xV0LUX78RZFECY1plYBn+dP/Acc3L0Q==",
|
||||
"version": "1.76.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.76.0.tgz",
|
||||
"integrity": "sha512-CQcY3+Fe5hNewHnOEAVYj4dd1do/QHliXaknAEYSXx2KEHUzFibDZSKptCon+HPgK55xx20pR+PBJjf0MomnBA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
@@ -2823,15 +2823,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
|
||||
"integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.37.0.tgz",
|
||||
"integrity": "sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@eslint/eslintrc": "^2.0.2",
|
||||
"@eslint/js": "8.38.0",
|
||||
"@eslint/js": "8.37.0",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
@@ -4735,17 +4735,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pdfjs-dist": {
|
||||
"version": "3.5.141",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.5.141.tgz",
|
||||
"integrity": "sha512-lYIvyi5grtYOIatsfCifIKwxHeAJ8eHyP22DTdvY4pm0yWVSFQnMafpgCPSw8gaNRDDdcHnBVOkqMsyK8SRxZg==",
|
||||
"version": "3.4.120",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.4.120.tgz",
|
||||
"integrity": "sha512-B1hw9ilLG4m/jNeFA0C2A0PZydjxslP8ylU+I4XM7Bzh/xWETo9EiBV848lh0O0hLut7T6lK1V7cpAXv5BhxWw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path2d-polyfill": "^2.0.1",
|
||||
"web-streams-polyfill": "^3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"canvas": "^2.11.0"
|
||||
}
|
||||
@@ -6562,9 +6559,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svelte-select": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.6.0.tgz",
|
||||
"integrity": "sha512-+EDXupp3Lr/hKlXNra1vR1VEGxEEsFxYPs5yDDqQE+xagyAOd8P54IJtU84Z5QN3HYc8l2Hp+tNCCYZoTWWxyQ==",
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.5.2.tgz",
|
||||
"integrity": "sha512-uvTOJyrrD5OnSCAv9A+gLtIeeYHL5PUw3YpnuBkmfp7a3IHUyjgNNM4GIf462Lh9QEEEM3nD0R0E+G/Jp1iq1w==",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.2.1",
|
||||
"svelte-floating-ui": "1.2.8"
|
||||
@@ -6598,9 +6595,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte2tsx": {
|
||||
"version": "0.6.11",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.11.tgz",
|
||||
"integrity": "sha512-rRW/3V/6mcejYWmSqcHpmILOSPsOhLgkbKbrTOz82s2n8TywmIsqj2jYPsiL6HeGoUM/atiTD0YKguW4b7ECog==",
|
||||
"version": "0.6.10",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.10.tgz",
|
||||
"integrity": "sha512-7CtUexhSHppSpLwVX7yI9OxJp7+esCNTuOF/CaVxWTgWp88QF/2/dCbpND/cTZmBxht5cFFQBwoLXXqFLdYe3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dedent-js": "^1.0.1",
|
||||
@@ -7675,9 +7672,9 @@
|
||||
}
|
||||
},
|
||||
"@eslint/js": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
|
||||
"integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz",
|
||||
"integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==",
|
||||
"dev": true
|
||||
},
|
||||
"@floating-ui/core": {
|
||||
@@ -8299,9 +8296,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/vscode": {
|
||||
"version": "1.77.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.77.0.tgz",
|
||||
"integrity": "sha512-MWFN5R7a33n8eJZJmdVlifjig3LWUNRrPeO1xemIcZ0ae0TEQuRc7G2xV0LUX78RZFECY1plYBn+dP/Acc3L0Q==",
|
||||
"version": "1.76.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.76.0.tgz",
|
||||
"integrity": "sha512-CQcY3+Fe5hNewHnOEAVYj4dd1do/QHliXaknAEYSXx2KEHUzFibDZSKptCon+HPgK55xx20pR+PBJjf0MomnBA==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
@@ -9313,15 +9310,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"eslint": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
|
||||
"integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.37.0.tgz",
|
||||
"integrity": "sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@eslint/eslintrc": "^2.0.2",
|
||||
"@eslint/js": "8.38.0",
|
||||
"@eslint/js": "8.37.0",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
@@ -10775,9 +10772,9 @@
|
||||
}
|
||||
},
|
||||
"pdfjs-dist": {
|
||||
"version": "3.5.141",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.5.141.tgz",
|
||||
"integrity": "sha512-lYIvyi5grtYOIatsfCifIKwxHeAJ8eHyP22DTdvY4pm0yWVSFQnMafpgCPSw8gaNRDDdcHnBVOkqMsyK8SRxZg==",
|
||||
"version": "3.4.120",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.4.120.tgz",
|
||||
"integrity": "sha512-B1hw9ilLG4m/jNeFA0C2A0PZydjxslP8ylU+I4XM7Bzh/xWETo9EiBV848lh0O0hLut7T6lK1V7cpAXv5BhxWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"canvas": "^2.11.0",
|
||||
@@ -12020,9 +12017,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"svelte-select": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.6.0.tgz",
|
||||
"integrity": "sha512-+EDXupp3Lr/hKlXNra1vR1VEGxEEsFxYPs5yDDqQE+xagyAOd8P54IJtU84Z5QN3HYc8l2Hp+tNCCYZoTWWxyQ==",
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.5.2.tgz",
|
||||
"integrity": "sha512-uvTOJyrrD5OnSCAv9A+gLtIeeYHL5PUw3YpnuBkmfp7a3IHUyjgNNM4GIf462Lh9QEEEM3nD0R0E+G/Jp1iq1w==",
|
||||
"requires": {
|
||||
"@floating-ui/dom": "^1.2.1",
|
||||
"svelte-floating-ui": "1.2.8"
|
||||
@@ -12046,9 +12043,9 @@
|
||||
}
|
||||
},
|
||||
"svelte2tsx": {
|
||||
"version": "0.6.11",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.11.tgz",
|
||||
"integrity": "sha512-rRW/3V/6mcejYWmSqcHpmILOSPsOhLgkbKbrTOz82s2n8TywmIsqj2jYPsiL6HeGoUM/atiTD0YKguW4b7ECog==",
|
||||
"version": "0.6.10",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.10.tgz",
|
||||
"integrity": "sha512-7CtUexhSHppSpLwVX7yI9OxJp7+esCNTuOF/CaVxWTgWp88QF/2/dCbpND/cTZmBxht5cFFQBwoLXXqFLdYe3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dedent-js": "^1.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.87.0",
|
||||
"version": "1.86.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -26,21 +26,21 @@
|
||||
"@types/d3-zoom": "^3.0.2",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/vscode": "~1.77.0",
|
||||
"@types/vscode": "~1.76.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"cssnano": "^6.0.0",
|
||||
"d3-dag": "^0.11.5",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"monaco-editor-workers": "0.36.0",
|
||||
"ol": "^7.2.2",
|
||||
"openapi-typescript-codegen": "^0.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pdfjs-dist": "^3.5.141",
|
||||
"pdfjs-dist": "^3.4.120",
|
||||
"postcss": "^8.4.18",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.3",
|
||||
@@ -59,7 +59,7 @@
|
||||
"svelte-preprocess": "^5.0.1",
|
||||
"svelte-range-slider-pips": "^2.1.1",
|
||||
"svelte-splitpanes": "^0.7.3",
|
||||
"svelte2tsx": "^0.6.11",
|
||||
"svelte2tsx": "^0.6.10",
|
||||
"tailwindcss": "^3.2.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.3",
|
||||
@@ -89,7 +89,7 @@
|
||||
"svelte-autosize": "^1.0.1",
|
||||
"svelte-chartjs": "^3.1.0",
|
||||
"svelte-portal": "^2.2.0",
|
||||
"svelte-select": "^5.6.0",
|
||||
"svelte-select": "^5.3.1",
|
||||
"svelte-timezone-picker": "^2.0.3",
|
||||
"tailwind-merge": "^1.9.1",
|
||||
"vscode-languageclient": "^8.1.0",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { ResourceService, VariableService } from '$lib/gen'
|
||||
import { getModifierKey, getScriptByPath, sendUserToast } from '$lib/utils'
|
||||
import { getScriptByPath, sendUserToast } from '$lib/utils'
|
||||
|
||||
import {
|
||||
faBroom,
|
||||
@@ -365,12 +365,10 @@
|
||||
{iconOnly}
|
||||
startIcon={{ icon: faBroom }}
|
||||
>
|
||||
Format ({getModifierKey()}+S)
|
||||
Format (Ctrl+S)
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
Format <Kbd class="!text-gray-800">{getModifierKey()}</Kbd> + <Kbd class="!text-gray-800">
|
||||
S
|
||||
</Kbd>
|
||||
Format <Kbd class="!text-gray-800">Ctrl</Kbd> + <Kbd class="!text-gray-800">S</Kbd>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
@@ -217,20 +217,20 @@
|
||||
|
||||
switch (event.key) {
|
||||
case 'Z':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
$flowStore = redo(history)
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 'z':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
$flowStore = undo(history, $flowStore)
|
||||
$selectedIdStore = 'Input'
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 's':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
saveFlow(false)
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
import FlowProgressBar from './flows/FlowProgressBar.svelte'
|
||||
import CapturePayload from './flows/content/CapturePayload.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { getModifierKey } from '$lib/utils'
|
||||
|
||||
let capturePayload: CapturePayload
|
||||
export let previewMode: 'upTo' | 'whole'
|
||||
@@ -76,7 +75,7 @@
|
||||
if (open) {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault()
|
||||
runPreview($previewArgs)
|
||||
}
|
||||
@@ -135,7 +134,7 @@
|
||||
btnClasses="w-full max-w-lg"
|
||||
on:click={() => runPreview($previewArgs)}
|
||||
>
|
||||
Test flow <Kbd class="ml-2">{getModifierKey()}+Enter</Kbd>
|
||||
Test flow <Kbd class="ml-2">Ctrl+Enter</Kbd>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
|
||||
@@ -308,9 +308,6 @@
|
||||
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'}"
|
||||
placeholder={defaultValue ?? ''}
|
||||
bind:value
|
||||
on:pointerdown|stopPropagation={(e) => {
|
||||
dispatch('inputClicked', e)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
properties={schema.properties[argName].properties}
|
||||
itemsType={schema.properties[argName].items}
|
||||
extra={schema.properties[argName]}
|
||||
on:inputClicked
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import { ScriptService, type FlowModule, type InputTransform, type Job } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { getModifierKey, getScriptByPath } from '$lib/utils'
|
||||
import { getScriptByPath } from '$lib/utils'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
@@ -86,7 +86,7 @@
|
||||
runnable={{ summary: mod.summary ?? '', schema, description: '' }}
|
||||
runAction={(_, args) => runTest(args)}
|
||||
schedulable={false}
|
||||
buttonText={`Test (${getModifierKey()}+Enter)`}
|
||||
buttonText="Test (Ctrl+Enter)"
|
||||
detailed={false}
|
||||
topButton
|
||||
bind:args={stepArgs}
|
||||
|
||||
@@ -317,28 +317,16 @@
|
||||
></span
|
||||
>
|
||||
|
||||
<div class="flex flex-row items-center gap-1 w-full">
|
||||
<div class="flex flex-row gap-1 w-full">
|
||||
<select class="grow w-full" {disabled} bind:value={meta.owner}>
|
||||
{#each folders as { name, write }}
|
||||
<option disabled={!write}>{name}{write ? '' : ' (read-only)'}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<Button
|
||||
title="View folder"
|
||||
btnClasses="!p-1.5"
|
||||
variant="border"
|
||||
size="xs"
|
||||
on:click={viewFolder.openDrawer}
|
||||
>
|
||||
<Button variant="border" size="xs" on:click={viewFolder.openDrawer}>
|
||||
<Icon scale={0.8} data={faEye} /></Button
|
||||
>
|
||||
<Button
|
||||
title="New folder"
|
||||
btnClasses="!p-1.5"
|
||||
variant="border"
|
||||
size="xs"
|
||||
on:click={newFolder.openDrawer}
|
||||
>
|
||||
<Button variant="border" size="xs" on:click={newFolder.openDrawer}>
|
||||
<Icon scale={0.8} data={faPlus} /></Button
|
||||
></div
|
||||
>
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: resolvedConfig?.triggerOnAppLoad && runnableComponent?.runComponent()
|
||||
|
||||
let errors: Record<string, string> = {}
|
||||
$: errorsMessage = Object.values(errors)
|
||||
.filter((x) => x != '')
|
||||
@@ -117,7 +119,6 @@
|
||||
{render}
|
||||
{outputs}
|
||||
{extraKey}
|
||||
refreshOnStart={resolvedConfig.triggerOnAppLoad}
|
||||
>
|
||||
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
|
||||
{#if errorsMessage}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
title="sandbox"
|
||||
srcdoc={result
|
||||
? '<scr' + `ipt type="application/javascript" src="/tailwind.js"></script>` + result
|
||||
: ''}
|
||||
: 'No html'}
|
||||
/>
|
||||
{/key}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { AlignWrapper, InputValue } from '../helpers'
|
||||
import { loadIcon } from '../icon'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = 'left'
|
||||
@@ -39,8 +38,6 @@
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.strokeWidth} bind:value={strokeWidth} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper
|
||||
{render}
|
||||
{horizontalAlignment}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import Loader from '../helpers/Loader.svelte'
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -32,8 +31,6 @@
|
||||
$: css = concatCustomCss($app.css?.imagecomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
{#each Object.keys(components['imagecomponent'].initialData.configuration) as key (key)}
|
||||
<ResolveConfig
|
||||
{id}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
import { Point } from 'ol/geom'
|
||||
import { defaults as defaultControls } from 'ol/control'
|
||||
import { findGridItem, initOutput } from '../../editor/appUtils'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
interface Marker {
|
||||
lon: number
|
||||
@@ -194,8 +193,6 @@
|
||||
<InputValue {id} input={configuration.zoom} bind:value={zoom} />
|
||||
<InputValue {id} input={configuration.markers} bind:value={markers} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
{#if render}
|
||||
<div class="relative h-full w-full">
|
||||
<div
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -193,8 +192,6 @@
|
||||
<InputValue {id} input={configuration.source} bind:value={source} />
|
||||
<InputValue {id} input={configuration.zoom} bind:value={zoom} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
{#if render}
|
||||
<div class="relative flex flex-col w-full h-full bg-gray-100">
|
||||
{#if source && zoom}
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
)}
|
||||
>
|
||||
{String(result)}
|
||||
{#if resolvedConfig.tooltip && resolvedConfig.tooltip != ''}
|
||||
{#if resolvedConfig.tooltip != ''}
|
||||
<Tooltip>{resolvedConfig.tooltip}</Tooltip>
|
||||
{/if}
|
||||
{#if resolvedConfig.copyButton && result}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
{placeholder}
|
||||
on:pointerdown|stopPropagation
|
||||
on:keyup={debounce}
|
||||
on:keydown|stopPropagation
|
||||
class={parentClass}
|
||||
{...$$restProps}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type {
|
||||
ConnectedAppInput,
|
||||
@@ -16,20 +16,13 @@
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let recomputeOnInputChanged: boolean
|
||||
export let recomputableByRefreshButton: boolean
|
||||
export let noBackendValue: any = undefined
|
||||
|
||||
const { worldStore, staticExporter, noBackend } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let result: any = undefined
|
||||
|
||||
let result: any = noBackend ? noBackendValue : undefined
|
||||
|
||||
onMount(() => {
|
||||
$staticExporter[id] = () => {
|
||||
return result
|
||||
}
|
||||
})
|
||||
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let outputs = initOutput($worldStore, id, {
|
||||
result: result,
|
||||
result: undefined,
|
||||
loading: false
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onDestroy, onMount } from 'svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let id: string
|
||||
const { initialized } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
onMount(() => {
|
||||
if (!$initialized.initializedComponents.includes(id)) {
|
||||
$initialized.initializedComponents = [...$initialized.initializedComponents, id]
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
$initialized.initializedComponents = $initialized.initializedComponents.filter((c) => c !== id)
|
||||
})
|
||||
</script>
|
||||
@@ -41,15 +41,8 @@
|
||||
$: stateId = $worldStore?.stateId
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
let firstDebounce = true
|
||||
const debounce_ms = 50
|
||||
function debounce(cb: () => Promise<void>) {
|
||||
if (firstDebounce) {
|
||||
firstDebounce = false
|
||||
cb()
|
||||
return
|
||||
}
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import InputValue from './InputValue.svelte'
|
||||
import InitializeComponent from './InitializeComponent.svelte'
|
||||
|
||||
export let componentInput: AppInput
|
||||
export let id: string
|
||||
@@ -26,8 +25,6 @@
|
||||
$: result && outputs && setOutput(result)
|
||||
</script>
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
{#if componentInput.type !== 'runnable'}
|
||||
<InputValue {id} input={componentInput} bind:value={result} />
|
||||
{/if}
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
import { classNames, defaultIfEmptyString, emptySchema, sendUserToast } from '$lib/utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { Bug } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext, onDestroy } from 'svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { AppInputs, Runnable } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, InlineScript } from '../../types'
|
||||
import { computeGlobalContext, eval_like } from './eval'
|
||||
import InputValue from './InputValue.svelte'
|
||||
import RefreshButton from './RefreshButton.svelte'
|
||||
import { selectId } from '../../editor/appUtils'
|
||||
|
||||
// Component props
|
||||
export let id: string
|
||||
@@ -32,10 +31,9 @@
|
||||
export let render: boolean
|
||||
export let outputs: { result: Output<any>; loading: Output<boolean> }
|
||||
export let extraKey = ''
|
||||
export let recomputeOnInputChanged: boolean = true
|
||||
export let recomputeOnInputChanged: boolean = false
|
||||
export let loading = false
|
||||
export let recomputableByRefreshButton: boolean = true
|
||||
export let refreshOnStart: boolean = false
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
@@ -49,31 +47,17 @@
|
||||
mode,
|
||||
stateId,
|
||||
state,
|
||||
componentControl,
|
||||
initialized,
|
||||
selectedComponent,
|
||||
app,
|
||||
connectingInput
|
||||
componentControl
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$runnableComponents[id] = {
|
||||
autoRefresh: autoRefresh && recomputableByRefreshButton,
|
||||
refreshOnStart,
|
||||
cb: async (inlineScript?: InlineScript) => {
|
||||
await executeComponent(true, inlineScript)
|
||||
}
|
||||
}
|
||||
|
||||
if (!$initialized.initializedComponents.includes(id)) {
|
||||
$initialized.initializedComponents = [...$initialized.initializedComponents, id]
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
$initialized.initializedComponents = $initialized.initializedComponents.filter((c) => c !== id)
|
||||
})
|
||||
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
let args: Record<string, any> | undefined = undefined
|
||||
@@ -117,7 +101,7 @@
|
||||
function refreshIfAutoRefresh(_src: string) {
|
||||
const refreshEnabled =
|
||||
autoRefresh && ((recomputeOnInputChanged ?? true) || refreshOn?.length > 0)
|
||||
if (refreshEnabled && $initialized.initialized) {
|
||||
if (refreshEnabled && $worldStore.initialized) {
|
||||
setDebouncedExecute()
|
||||
}
|
||||
}
|
||||
@@ -335,11 +319,6 @@
|
||||
$errorByComponent = $errorByComponent
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputClick(e: CustomEvent) {
|
||||
const event = e as unknown as PointerEvent
|
||||
!$connectingInput.opened && selectId(event, id, selectedComponent, $app)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each Object.entries(fields ?? {}) as [key, v] (key)}
|
||||
@@ -386,11 +365,7 @@
|
||||
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}" style={wrapperStyle}>
|
||||
{#if (autoRefresh || forceSchemaDisplay) && schemaStripped && Object.keys(schemaStripped?.properties ?? {}).length > 0}
|
||||
<div class="px-2 h-fit min-h-0">
|
||||
<LightweightSchemaForm
|
||||
schema={schemaStripped}
|
||||
bind:args
|
||||
on:inputClicked={handleInputClick}
|
||||
/>
|
||||
<LightweightSchemaForm schema={schemaStripped} bind:args />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import RunnableComponent from './RunnableComponent.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
import { sendUserToast } from '$lib/utils'
|
||||
import InitializeComponent from './InitializeComponent.svelte'
|
||||
|
||||
export let componentInput: AppInput | undefined
|
||||
export let id: string
|
||||
@@ -40,7 +39,6 @@
|
||||
export let recomputeIds: string[] = []
|
||||
export let outputs: { result: Output<any>; loading: Output<boolean> }
|
||||
export let extraKey: string | undefined = undefined
|
||||
export let refreshOnStart: boolean = false
|
||||
|
||||
const { staticExporter, noBackend, componentControl, runnableComponents } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -53,9 +51,7 @@
|
||||
result = componentInput?.['value']
|
||||
}
|
||||
onMount(() => {
|
||||
$staticExporter[id] = () => {
|
||||
return result
|
||||
}
|
||||
$staticExporter[id] = () => result
|
||||
})
|
||||
|
||||
function isRunnableDefined(componentInput) {
|
||||
@@ -100,11 +96,9 @@
|
||||
</script>
|
||||
|
||||
{#if componentInput === undefined}
|
||||
<InitializeComponent {id} />
|
||||
<slot />
|
||||
{:else if componentInput.type === 'runnable' && isRunnableDefined(componentInput)}
|
||||
<RunnableComponent
|
||||
{refreshOnStart}
|
||||
{extraKey}
|
||||
bind:loading
|
||||
bind:this={runnableComponent}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -35,8 +34,6 @@
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<Toggle
|
||||
checked={defaultValue}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -42,8 +41,6 @@
|
||||
<InputValue {id} input={configuration.maxDate} bind:value={maxValue} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
{#if inputType === 'date'}
|
||||
<input
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -20,11 +19,11 @@
|
||||
let text: string | undefined = undefined
|
||||
|
||||
let outputs = initOutput($worldStore, id, {
|
||||
result: [] as { name: string; data: string }[] | undefined
|
||||
result: [] as string[] | undefined
|
||||
})
|
||||
|
||||
// Receives Base64 encoded strings from the input component
|
||||
async function handleChange(files: { name: string; data: string }[] | undefined) {
|
||||
async function handleChange(files: string[] | undefined) {
|
||||
outputs?.result.set(files)
|
||||
}
|
||||
|
||||
@@ -35,15 +34,12 @@
|
||||
<InputValue {id} input={configuration.allowMultiple} bind:value={allowMultiple} />
|
||||
<InputValue {id} input={configuration.text} bind:value={text} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
{#if render}
|
||||
<div class="w-full h-full p-1">
|
||||
<FileInput
|
||||
accept={acceptedFileTypes?.length ? acceptedFileTypes?.join(', ') : undefined}
|
||||
multiple={allowMultiple}
|
||||
convertTo="base64"
|
||||
returnFileNames
|
||||
on:change={({ detail }) => {
|
||||
handleChange(detail)
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -56,8 +55,6 @@
|
||||
<InputValue {id} input={configuration.items} bind:value={labels} />
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<div
|
||||
class="app-select w-full"
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -45,8 +44,6 @@
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<input
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -55,8 +54,6 @@
|
||||
bind:value={values[1]}
|
||||
/>
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex items-center w-full gap-1 px-1">
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '../../../../defaults'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -104,8 +103,6 @@
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
<InputValue {id} input={configuration.create} bind:value={create} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<div
|
||||
class="app-select w-full"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -60,7 +59,6 @@
|
||||
input={configuration.defaultValue}
|
||||
bind:value={values[0]}
|
||||
/>
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<div class="flex items-center w-full gap-1 px-1">
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -45,8 +44,6 @@
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
{#if inputType === 'textarea'}
|
||||
<textarea
|
||||
class={twMerge(
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import AlignWrapper from '../../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import CurrencyInput from './CurrencyInput.svelte'
|
||||
import InitializeComponent from '../../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -49,8 +48,6 @@
|
||||
<InputValue {id} input={configuration.currency} bind:value={currency} />
|
||||
<InputValue {id} input={configuration.locale} bind:value={locale} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
{#key isNegativeAllowed}
|
||||
{#key locale}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentContainerHeight: number
|
||||
@@ -27,8 +26,6 @@
|
||||
$: css = concatCustomCss($app.css?.containercomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<div class="w-full h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -37,8 +36,6 @@
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper
|
||||
{horizontalAlignment}
|
||||
{verticalAlignment}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { AlignWrapper } from '../helpers'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let customCss: ComponentCustomCSS<'drawercomponent'> | undefined = undefined
|
||||
export let id: string
|
||||
@@ -71,8 +70,6 @@
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.fillContainer} bind:value={fillContainer} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<Portal target="#app-editor-top-level-drawer">
|
||||
<Drawer let:open bind:this={appDrawer} size="800px" alwaysOpen positionClass="!absolute">
|
||||
<DrawerContent
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentContainerHeight: number
|
||||
@@ -66,8 +65,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<div class="h-full w-full border" on:pointerdown={onFocus}>
|
||||
<Splitpanes {horizontal}>
|
||||
{#each sumedup as paneSize, index (index)}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -79,8 +78,6 @@
|
||||
|
||||
<InputValue {id} input={configuration.tabsKind} bind:value={resolvedConfig.tabsKind} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<div class={resolvedConfig.tabsKind == 'sidebar' ? 'flex gap-4 w-full' : 'w-full'}>
|
||||
{#if !resolvedConfig.tabsKind || resolvedConfig.tabsKind == 'tabs' || (resolvedConfig.tabsKind == 'invisibleOnView' && $mode == 'dnd')}
|
||||
<div bind:clientHeight={tabHeight}>
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
worldStore,
|
||||
app: appStore,
|
||||
summary: summaryStore,
|
||||
initialized: writable({ initialized: false, initializedComponents: [] }),
|
||||
selectedComponent,
|
||||
mode,
|
||||
connectingInput,
|
||||
|
||||
@@ -257,20 +257,20 @@
|
||||
|
||||
switch (event.key) {
|
||||
case 'Z':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
$app = redo(history)
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 'z':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
$app = undo(history, $app)
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 's':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
save()
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
const parentWidth = writable(0)
|
||||
setContext<AppViewerContext>('AppViewerContext', {
|
||||
worldStore: buildWorld(context),
|
||||
initialized: writable({ initialized: false, initializedComponents: [] }),
|
||||
app: appStore,
|
||||
summary: writable(summary),
|
||||
selectedComponent,
|
||||
@@ -172,7 +171,6 @@
|
||||
fields={script.fields}
|
||||
recomputeOnInputChanged={script.recomputeOnInputChanged ?? true}
|
||||
recomputableByRefreshButton={script.autoRefresh ?? false}
|
||||
noBackendValue={script.noBackendValue}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import type { AppViewerContext } from '../types'
|
||||
import { allItems } from '../utils'
|
||||
|
||||
const { runnableComponents, app, initialized } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { runnableComponents, app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let loading: boolean = false
|
||||
let timeout: NodeJS.Timer | undefined = undefined
|
||||
let interval: number | undefined = undefined
|
||||
@@ -14,7 +14,7 @@
|
||||
let firstLoad = false
|
||||
|
||||
$: !firstLoad &&
|
||||
$initialized.initializedComponents?.length ==
|
||||
$worldStore.initializedOutputs ==
|
||||
allItems($app.grid, $app.subgrids).length + $app.hiddenInlineScripts.length &&
|
||||
refresh()
|
||||
$: componentNumber = Object.values($runnableComponents).filter((x) => x.autoRefresh).length
|
||||
@@ -39,19 +39,14 @@
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
let isFirstLoad = false
|
||||
if (!firstLoad) {
|
||||
$initialized.initialized = true
|
||||
$worldStore.initialized = true
|
||||
firstLoad = true
|
||||
isFirstLoad = true
|
||||
}
|
||||
loading = true
|
||||
Promise.all(
|
||||
Object.keys($runnableComponents).map((id) => {
|
||||
if (
|
||||
!$runnableComponents?.[id]?.autoRefresh &&
|
||||
(!isFirstLoad || !$runnableComponents?.[id]?.refreshOnStart)
|
||||
) {
|
||||
if (!$runnableComponents?.[id]?.autoRefresh) {
|
||||
return
|
||||
}
|
||||
return $runnableComponents?.[id]?.cb?.()
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
autoRefresh={hiddenInlineScript.script.autoRefresh}
|
||||
id={`bg_${hiddenInlineScript.index}`}
|
||||
bind:recomputeOnInputChanged={hiddenInlineScript.script.recomputeOnInputChanged}
|
||||
bind:doNotRecomputeOnInputChanged={hiddenInlineScript.script.doNotRecomputeOnInputChanged}
|
||||
doNotRecomputeOnInputChanged={hiddenInlineScript.script.doNotRecomputeOnInputChanged}
|
||||
bind:inlineScript={hiddenInlineScript.script.inlineScript}
|
||||
/>
|
||||
{:else}
|
||||
|
||||
@@ -53,14 +53,13 @@ export function selectId(
|
||||
app: App
|
||||
) {
|
||||
// this ensure handleClickOutside are triggered
|
||||
let event = new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
relatedTarget: e.target
|
||||
})
|
||||
window.dispatchEvent(event)
|
||||
|
||||
window.dispatchEvent(
|
||||
new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
)
|
||||
if (e.shiftKey) {
|
||||
selectedComponent.update((old) => {
|
||||
if (old && old?.[0]) {
|
||||
@@ -454,6 +453,7 @@ export function initOutput<I extends Record<string, any>>(
|
||||
id: string,
|
||||
init: I
|
||||
): Outputtable<I> {
|
||||
world.initializedOutputs += 1
|
||||
if (!world) {
|
||||
return {} as any
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import Badge from '$lib/components/common/badge/Badge.svelte'
|
||||
import Editor from '$lib/components/Editor.svelte'
|
||||
import { emptySchema, getModifierKey, scriptLangToEditorLang } from '$lib/utils'
|
||||
import { emptySchema, isMac, scriptLangToEditorLang } from '$lib/utils'
|
||||
import Popover from '../../../Popover.svelte'
|
||||
import { computeFields } from './utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
@@ -176,7 +176,7 @@
|
||||
Format
|
||||
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<Kbd>{getModifierKey()}</Kbd>
|
||||
<Kbd>{isMac() ? '⌘' : 'CTRL'}</Kbd>
|
||||
<Kbd>S</Kbd>
|
||||
</div>
|
||||
</div>
|
||||
@@ -198,7 +198,7 @@
|
||||
Run
|
||||
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<Kbd>{getModifierKey()}</Kbd>
|
||||
<Kbd>{isMac() ? '⌘' : 'CTRL'}</Kbd>
|
||||
<Kbd>
|
||||
<div class="h-4 flex items-center justify-center">
|
||||
<CornerDownLeft size={10} />
|
||||
|
||||
@@ -31,15 +31,10 @@
|
||||
appInput = clearResultAppInput(appInput)
|
||||
}
|
||||
|
||||
$: {
|
||||
if (appInput.recomputeOnInputChanged === undefined) {
|
||||
appInput.recomputeOnInputChanged = true
|
||||
}
|
||||
//TODO: remove after migration is done
|
||||
if (appInput.doNotRecomputeOnInputChanged != undefined) {
|
||||
appInput.recomputeOnInputChanged = !appInput.doNotRecomputeOnInputChanged
|
||||
appInput.doNotRecomputeOnInputChanged = undefined
|
||||
}
|
||||
if (appInput.recomputeOnInputChanged === undefined) {
|
||||
appInput.recomputeOnInputChanged = true
|
||||
} else if (appInput.doNotRecomputeOnInputChanged == true) {
|
||||
appInput.recomputeOnInputChanged = false
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
}
|
||||
$app = $app
|
||||
$worldStore = $worldStore
|
||||
}
|
||||
$app = $app
|
||||
$worldStore = $worldStore
|
||||
}
|
||||
|
||||
$: $connectingInput && applyConnection()
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
{#if anchor}
|
||||
<Popup
|
||||
ref={anchor}
|
||||
closeOn={[]}
|
||||
options={{ placement: 'bottom' }}
|
||||
bind:open={openPopup}
|
||||
let:close
|
||||
@@ -88,11 +87,7 @@
|
||||
<div class="max-w-xs shadow-[0_10px_40px_-5px_rgba(0,0,0,0.25)] bg-white rounded-md p-2">
|
||||
{#if filteredItems}
|
||||
<input
|
||||
on:keydown={(event) => {
|
||||
if (!['ArrowDown', 'ArrowUp'].includes(event.key)) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
}}
|
||||
on:keydown|stopPropagation
|
||||
bind:value={search}
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
@@ -108,8 +103,8 @@
|
||||
select(label)
|
||||
close()
|
||||
}}
|
||||
class="w-full center-center flex-col font-normal p-1
|
||||
hover:bg-gray-100 focus:bg-gray-100 rounded duration-200
|
||||
class="w-full center-center flex-col font-normal p-1
|
||||
hover:bg-gray-100 focus:bg-gray-100 rounded duration-200
|
||||
{label === componentInput.value ? 'text-blue-600 bg-blue-50 pointer-events-none' : ''}"
|
||||
>
|
||||
<svelte:component this={icon} size={22} />
|
||||
|
||||
@@ -18,16 +18,12 @@
|
||||
export let recomputeOnInputChanged: boolean | undefined = true
|
||||
export let doNotRecomputeOnInputChanged: undefined | boolean = undefined
|
||||
|
||||
//TODO: remove after migration is done
|
||||
$: {
|
||||
if (doNotRecomputeOnInputChanged != undefined) {
|
||||
recomputeOnInputChanged = !doNotRecomputeOnInputChanged
|
||||
doNotRecomputeOnInputChanged = undefined
|
||||
}
|
||||
if (doNotRecomputeOnInputChanged == true) {
|
||||
recomputeOnInputChanged = false
|
||||
}
|
||||
|
||||
if (recomputeOnInputChanged == undefined) {
|
||||
recomputeOnInputChanged = true
|
||||
}
|
||||
if (recomputeOnInputChanged == undefined) {
|
||||
recomputeOnInputChanged = true
|
||||
}
|
||||
|
||||
$: dependencies = getDependencies(fields)
|
||||
|
||||
@@ -130,8 +130,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if frontendDependencies && recomputeOnInputChanged}
|
||||
{#if frontendDependencies && !recomputeOnInputChanged}
|
||||
<div class="w-full">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-xs font-semibold text-slate-800 mb-1">Change on values</div>
|
||||
|
||||
@@ -24,6 +24,8 @@ export type World = {
|
||||
connect: <T>(inputSpec: AppInput, next: (x: T) => void, id?: string) => Input<T>
|
||||
stateId: Writable<number>
|
||||
newOutput: <T>(id: string, name: string, previousValue: T) => Output<T>
|
||||
initializedOutputs: number
|
||||
initialized: boolean
|
||||
}
|
||||
|
||||
export function buildWorld(context: Record<string, any>): Writable<World> {
|
||||
@@ -57,7 +59,9 @@ export function buildWorld(context: Record<string, any>): Writable<World> {
|
||||
outputsById,
|
||||
connect: newWorld.connect,
|
||||
stateId,
|
||||
newOutput
|
||||
newOutput,
|
||||
initializedOutputs: 0,
|
||||
initialized: false
|
||||
})
|
||||
return writableWorld
|
||||
}
|
||||
|
||||
@@ -117,7 +117,6 @@ export type App = {
|
||||
//deprecated and to be removed after migration
|
||||
doNotRecomputeOnInputChanged?: boolean
|
||||
recomputeOnInputChanged?: boolean
|
||||
noBackendValue?: any
|
||||
}>
|
||||
css?: Partial<Record<AppCssItemName, Record<string, ComponentCssProperty>>>
|
||||
subgrids?: Record<string, GridItem[]>
|
||||
@@ -134,23 +133,12 @@ export type AppViewerContext = {
|
||||
worldStore: Writable<World>
|
||||
app: Writable<App>
|
||||
summary: Writable<string>
|
||||
initialized: Writable<{
|
||||
initializedComponents: string[]
|
||||
initialized: boolean
|
||||
}>
|
||||
selectedComponent: Writable<string[] | undefined>
|
||||
mode: Writable<EditorMode>
|
||||
connectingInput: Writable<ConnectingInput>
|
||||
breakpoint: Writable<EditorBreakpoint>
|
||||
runnableComponents: Writable<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
autoRefresh: boolean
|
||||
refreshOnStart?: boolean
|
||||
cb: (inlineScript?: InlineScript) => Promise<void>
|
||||
}
|
||||
>
|
||||
Record<string, { autoRefresh: boolean; cb: (inlineScript?: InlineScript) => Promise<void> }>
|
||||
>
|
||||
staticExporter: Writable<Record<string, () => any>>
|
||||
appPath: string
|
||||
|
||||
@@ -160,17 +160,12 @@ export function toStatic(
|
||||
summary: string
|
||||
): { app: App; summary: string } {
|
||||
const newApp: App = JSON.parse(JSON.stringify(app))
|
||||
allItems(newApp.grid, newApp.subgrids).forEach((x) => {
|
||||
newApp.grid.forEach((x) => {
|
||||
let c: AppComponent = x.data
|
||||
if (c.componentInput?.type == 'runnable') {
|
||||
c.componentInput.value = staticExporter[x.id]()
|
||||
}
|
||||
})
|
||||
|
||||
newApp.hiddenInlineScripts?.forEach((x, i) => {
|
||||
x.noBackendValue = staticExporter[`bg_` + i]()
|
||||
})
|
||||
|
||||
return { app: newApp, summary }
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { ReadFileAs } from './model'
|
||||
|
||||
type ConvertedFile = string | ArrayBuffer | null
|
||||
|
||||
let c = ''
|
||||
export { c as class }
|
||||
export let style = ''
|
||||
@@ -16,7 +14,6 @@
|
||||
export let convertTo: ReadFileAs | undefined = undefined
|
||||
export let hideIcon = false
|
||||
export let iconSize = 36
|
||||
export let returnFileNames = false
|
||||
const dispatch = createEventDispatcher()
|
||||
let input: HTMLInputElement
|
||||
let files: File[] | undefined = undefined
|
||||
@@ -80,12 +77,7 @@
|
||||
|
||||
if (convertTo && files) {
|
||||
const promises = files.map(convertFile)
|
||||
let converted: ConvertedFile[] | { name: string; data: ConvertedFile }[] = await Promise.all(
|
||||
promises
|
||||
)
|
||||
if (returnFileNames) {
|
||||
converted = converted.map((c, i) => ({ name: files![i].name, data: c }))
|
||||
}
|
||||
const converted = await Promise.all(promises)
|
||||
dispatch('change', converted)
|
||||
} else {
|
||||
dispatch('change', files)
|
||||
|
||||
@@ -69,10 +69,8 @@
|
||||
}
|
||||
}
|
||||
function conditionalClosed() {
|
||||
setTimeout(() => {
|
||||
if (isFocusContained()) return
|
||||
closed()
|
||||
}, 0)
|
||||
if (isFocusContained()) return
|
||||
closed()
|
||||
}
|
||||
function openFocusOut() {
|
||||
stateMachine.setState('open-focus-out')
|
||||
@@ -82,12 +80,11 @@
|
||||
}
|
||||
|
||||
function keyDown(event: KeyboardEvent & { currentTarget: EventTarget & Window }) {
|
||||
const modifiers = ['Shift', 'Control', 'Command', 'Alt', 'Meta']
|
||||
const modifiers = ['Shift', 'Control', 'Command', 'Alt']
|
||||
// Prevent closing the popup when the only key pressed is a modifier key
|
||||
if (modifiers.includes(event.key) || $stateMachine.currentState === 'closed') return
|
||||
if (event.key === 'Escape') {
|
||||
;(<HTMLElement>document.activeElement)?.blur()
|
||||
return conditionalClosed()
|
||||
return (<HTMLElement>document.activeElement)?.blur()
|
||||
}
|
||||
|
||||
const prev = ['Up', 'Left']
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg
|
||||
class={$$props.class}
|
||||
class:animate-[spin_5s_linear_infinite]={spin === 'fast'}
|
||||
class:animate-[spin_15s_linear_infinite]={spin === 'medium'}
|
||||
class:animate-[spin_50s_linear_infinite]={spin === 'slow'}
|
||||
class:animate-[spin-counter-clockwise_5s_linear_infinite]={spin === 'fast'}
|
||||
class:animate-[spin-counter-clockwise_15s_linear_infinite]={spin === 'medium'}
|
||||
class:animate-[spin-counter-clockwise_50s_linear_infinite]={spin === 'slow'}
|
||||
version="1.1"
|
||||
id="Calque_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -12,7 +12,7 @@ export {
|
||||
PYTHON_FAILURE_MODULE_CODE
|
||||
}
|
||||
|
||||
export const DENO_INIT_CODE = `// Ctrl/CMD+. to cache dependencies on imports hover, Ctrl/CMD+S to format.
|
||||
export const DENO_INIT_CODE = `// Ctrl+. to cache dependencies on imports hover, Ctrl+S to format.
|
||||
|
||||
// import { toWords } from "npm:number-to-words@1"
|
||||
// import * as wmill from "https://deno.land/x/windmill@v${__pkg__.version}/mod.ts"
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
|
||||
type Tab = 'hubscripts' | 'hubflows' | 'hubapps' | 'workspace'
|
||||
|
||||
let tab: Tab = window.location.hash?.includes('#')
|
||||
? (window.location.hash?.replace('#', '') as Tab)
|
||||
: 'workspace'
|
||||
let tab: Tab = 'workspace'
|
||||
let filter: string = ''
|
||||
|
||||
let flowViewer: Drawer
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
defaultIfEmptyString,
|
||||
displayDaysAgo,
|
||||
emptyString,
|
||||
getModifierKey,
|
||||
sendUserToast
|
||||
} from '$lib/utils'
|
||||
import { FlowService, type Flow, JobService } from '$lib/gen'
|
||||
@@ -65,7 +64,7 @@
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey) {
|
||||
if (isValid) {
|
||||
event.preventDefault()
|
||||
runForm?.run()
|
||||
@@ -114,8 +113,7 @@
|
||||
<Button
|
||||
startIcon={{ icon: faPlay }}
|
||||
disabled={runForm == undefined || !isValid}
|
||||
on:click={() => runForm?.run()}
|
||||
>Run <Kbd class="ml-2">{getModifierKey()}+Enter</Kbd></Button
|
||||
on:click={() => runForm?.run()}>Run <Kbd class="ml-2">Ctrl+Enter</Kbd></Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user