mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-26 00:03:59 +00:00
Merge branch 'main' into python-uv
This commit is contained in:
@@ -1,5 +1,38 @@
|
||||
# Changelog
|
||||
|
||||
## [1.416.2](https://github.com/windmill-labs/windmill/compare/v1.416.1...v1.416.2) (2024-11-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* apply NO_PROXY and HTTP_PROXY, HTTPS_PROXY more consistently ([567d621](https://github.com/windmill-labs/windmill/commit/567d6216d2631a90fbe59ec6142c38b3b352eea7))
|
||||
|
||||
## [1.416.1](https://github.com/windmill-labs/windmill/compare/v1.416.0...v1.416.1) (2024-11-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **prometheus:** fix incorrect worker_busy set to 1 ([53f9136](https://github.com/windmill-labs/windmill/commit/53f9136658b9fc1795793d82408c1f1f04adcf06))
|
||||
|
||||
## [1.416.0](https://github.com/windmill-labs/windmill/compare/v1.415.2...v1.416.0) (2024-11-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* private hub user accessible url setting ([#4617](https://github.com/windmill-labs/windmill/issues/4617)) ([79edf89](https://github.com/windmill-labs/windmill/commit/79edf89bd17827d5f1d946739385327b6c0520bf))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** improve tag selector for workspace script drawer ([66f6985](https://github.com/windmill-labs/windmill/commit/66f69859ad2de51aef5a133df4ab4397d0f61ccf))
|
||||
|
||||
## [1.415.2](https://github.com/windmill-labs/windmill/compare/v1.415.1...v1.415.2) (2024-11-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **s3:** align s3 handler additional creds providers ([984c6dd](https://github.com/windmill-labs/windmill/commit/984c6dd10c63097eb195883c4d8a9681ab1b49e0))
|
||||
|
||||
## [1.415.1](https://github.com/windmill-labs/windmill/compare/v1.415.0...v1.415.1) (2024-10-31)
|
||||
|
||||
|
||||
|
||||
Generated
+114
-114
@@ -132,9 +132,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@@ -166,9 +166,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -441,7 +441,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"swc_macros_common",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -588,7 +588,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -636,7 +636,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -678,7 +678,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -799,9 +799,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.47.0"
|
||||
version = "1.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8776850becacbd3a82a4737a9375ddb5c6832a51379f24443a98e61513f852c"
|
||||
checksum = "ded855583fa1d22e88fe39fd6062b062376e50a8211989e07cf5e38d52eb3453"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -821,9 +821,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.48.0"
|
||||
version = "1.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0007b5b8004547133319b6c4e87193eee2a0bcb3e4c18c75d09febe9dab7b383"
|
||||
checksum = "9177ea1192e6601ae16c7273385690d88a7ed386a00b74a6bc894d12103cd933"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -843,9 +843,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.47.0"
|
||||
version = "1.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fffaa356e7f1c725908b75136d53207fa714e348f365671df14e95a60530ad3"
|
||||
checksum = "823ef553cf36713c97453e2ddff1eb8f62be7f4523544e2a5db64caf80100f0a"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1218,7 +1218,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"shlex",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"which 4.4.2",
|
||||
]
|
||||
|
||||
@@ -1237,7 +1237,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"shlex",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1381,7 +1381,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"syn_derive",
|
||||
]
|
||||
|
||||
@@ -1484,7 +1484,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1599,9 +1599,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.31"
|
||||
version = "1.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||
checksum = "e3788d6ac30243803df38a3e9991cf37e41210232916d41a8222ae378f912624"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -1744,7 +1744,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2124,7 +2124,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2172,7 +2172,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.11.1",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2194,7 +2194,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core 0.20.10",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2715,7 +2715,7 @@ dependencies = [
|
||||
"quote",
|
||||
"strum 0.25.0",
|
||||
"strum_macros 0.25.3",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -2907,7 +2907,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.1",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3004,7 +3004,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3160,7 +3160,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3180,7 +3180,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3260,9 +3260,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fastdivide"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59668941c55e5c186b8b58c391629af56774ec768f73c08bbcd56f09348eb00b"
|
||||
checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
@@ -3395,7 +3395,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"swc_macros_common",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3427,7 +3427,7 @@ checksum = "e99b8b3c28ae0e84b604c75f721c21dc77afb3706076af5e8216d15fd1deaae3"
|
||||
dependencies = [
|
||||
"frunk_proc_macro_helpers",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3439,7 +3439,7 @@ dependencies = [
|
||||
"frunk_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3451,7 +3451,7 @@ dependencies = [
|
||||
"frunk_core",
|
||||
"frunk_proc_macro_helpers",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3575,7 +3575,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3828,7 +3828,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4463,7 +4463,7 @@ dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5113,7 +5113,7 @@ checksum = "a7ce64b975ed4f123575d11afd9491f2e37bbd5813fbfbc0f09ae1fbddea74e0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5152,7 +5152,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"termcolor",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -5562,7 +5562,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5839,7 +5839,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5880,7 +5880,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6062,7 +6062,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6115,7 +6115,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f"
|
||||
dependencies = [
|
||||
"proc-macro-rules-macros",
|
||||
"proc-macro2",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6127,7 +6127,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6188,7 +6188,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"thiserror",
|
||||
"typify",
|
||||
"unicode-ident",
|
||||
@@ -6208,7 +6208,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_tokenstream",
|
||||
"serde_yaml",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6251,7 +6251,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6935,7 +6935,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -7315,7 +7315,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7462,7 +7462,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7473,7 +7473,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7548,7 +7548,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7603,7 +7603,7 @@ dependencies = [
|
||||
"darling 0.20.10",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7972,7 +7972,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8043,7 +8043,7 @@ dependencies = [
|
||||
"quote",
|
||||
"sqlx-core",
|
||||
"sqlx-macros-core",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8066,7 +8066,7 @@ dependencies = [
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"url",
|
||||
@@ -8218,7 +8218,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"swc_macros_common",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8272,7 +8272,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8285,7 +8285,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8393,7 +8393,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"swc_macros_common",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8442,7 +8442,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"swc_macros_common",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8527,7 +8527,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"swc_macros_common",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8634,7 +8634,7 @@ checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8645,7 +8645,7 @@ checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8668,7 +8668,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"swc_macros_common",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8684,9 +8684,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.85"
|
||||
version = "2.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -8702,7 +8702,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8728,7 +8728,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8936,9 +8936,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.42"
|
||||
version = "0.4.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020"
|
||||
checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
@@ -8978,22 +8978,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.65"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||
checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.65"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||
checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9218,7 +9218,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9545,7 +9545,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9782,7 +9782,7 @@ dependencies = [
|
||||
"regress",
|
||||
"schemars",
|
||||
"serde_json",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"thiserror",
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -9799,7 +9799,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"typify-impl",
|
||||
]
|
||||
|
||||
@@ -10194,7 +10194,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -10228,7 +10228,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -10261,7 +10261,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10395,7 +10395,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -10437,7 +10437,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -10522,7 +10522,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
@@ -10540,7 +10540,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -10553,7 +10553,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-autoscaling"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rsmq_async",
|
||||
@@ -10568,7 +10568,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -10614,7 +10614,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"rsmq_async",
|
||||
@@ -10629,7 +10629,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -10651,19 +10651,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -10672,7 +10672,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10684,7 +10684,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -10696,7 +10696,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10708,7 +10708,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
@@ -10719,7 +10719,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
@@ -10730,7 +10730,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10748,7 +10748,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -10758,14 +10758,14 @@ dependencies = [
|
||||
"quote",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"toml 0.7.8",
|
||||
"windmill-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10777,7 +10777,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10795,7 +10795,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wasm"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.15",
|
||||
@@ -10816,7 +10816,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -10826,7 +10826,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10859,7 +10859,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
@@ -10869,7 +10869,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -11235,7 +11235,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -11257,7 +11257,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11277,7 +11277,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.86",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -29,7 +29,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -160,7 +160,7 @@ hex = "^0"
|
||||
sql-builder = "^3"
|
||||
argon2 = "^0"
|
||||
quick_cache = "^0"
|
||||
rand = "0.8.5"
|
||||
rand = "^0"
|
||||
rand_core = { version = "^0", features = ["std"] }
|
||||
magic-crypt = "^3"
|
||||
git-version = "^0"
|
||||
@@ -172,7 +172,7 @@ urlencoding = "^2"
|
||||
url = "^2"
|
||||
async-oauth2 = "^0"
|
||||
reqwest = { version = "^0.12", features = ["json", "stream", "gzip"] }
|
||||
time = "0.3.16"
|
||||
time = "^0"
|
||||
serde_urlencoded = "^0"
|
||||
tokio-tar = "^0"
|
||||
tempfile = "^3"
|
||||
@@ -198,7 +198,7 @@ swc_ecma_visit = "=0.104.8"
|
||||
|
||||
async-recursion = "^1"
|
||||
|
||||
base64 = "0.21.0"
|
||||
base64 = "^0"
|
||||
base32 = "^0"
|
||||
hmac = "0.12.1"
|
||||
sha2 = "0.10.6"
|
||||
|
||||
@@ -1 +1 @@
|
||||
f667fcebdb5a2d1c22c1928d6a316ba57bb43d72
|
||||
f136a2f499e0fe7c10c54c79488851980d796eb2
|
||||
+2
-1
@@ -284,7 +284,8 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
tracing::info!("Binary is in 'indexer' mode");
|
||||
#[cfg(not(feature = "tantivy"))]
|
||||
{
|
||||
panic!("Indexer mode requires the tantivy feature flag");
|
||||
tracing::error!("Cannot start the indexer because tantivy is not included in this binary/image. Make sure you are using the EE image if you want to access the full text search features.");
|
||||
panic!("Indexer mode requires compiling with the tantivy feature flag.");
|
||||
}
|
||||
#[cfg(feature = "tantivy")]
|
||||
Mode::Indexer
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.415.1
|
||||
version: 1.416.2
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
|
||||
@@ -53,6 +53,7 @@ use crate::{resources::ResourceType, HTTP_CLIENT};
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref EMBEDDINGS_DB: Arc<RwLock<Option<EmbeddingsDb>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref MODEL_INSTANCE: Arc<RwLock<Option<Arc<ModelInstance>>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref HUB_EMBEDDINGS_PULLING_INTERVAL_SECS: u64 = std::env::var("HUB_EMBEDDINGS_PULLING_INTERVAL_SECS").ok().map(|x| x.parse::<u64>().ok()).flatten().unwrap_or(3600 * 24);
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
@@ -607,7 +608,10 @@ pub fn load_embeddings_db(db: &Pool<Postgres>) -> () {
|
||||
drop(model_instance_lock);
|
||||
loop {
|
||||
update_embeddings_db(&db_clone).await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(3600 * 24)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(
|
||||
*HUB_EMBEDDINGS_PULLING_INTERVAL_SECS,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
tracing::error!(
|
||||
|
||||
@@ -84,12 +84,14 @@ mod stripe_ee;
|
||||
mod tracing_init;
|
||||
mod triggers;
|
||||
mod users;
|
||||
mod users_ee;
|
||||
mod utils;
|
||||
mod variables;
|
||||
mod webhook_util;
|
||||
mod websocket_triggers;
|
||||
mod workers;
|
||||
mod workspaces;
|
||||
mod workspaces_ee;
|
||||
|
||||
pub const GIT_VERSION: &str =
|
||||
git_version!(args = ["--tag", "--always"], fallback = "unknown-version");
|
||||
|
||||
@@ -28,13 +28,13 @@ use serde::Deserialize;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_common::ee::{send_critical_alert, CriticalAlertKind, CriticalErrorChannel};
|
||||
use windmill_common::{
|
||||
email_ee::send_email,
|
||||
error::{self, JsonResult, Result},
|
||||
global_settings::{
|
||||
AUTOMATE_USERNAME_CREATION_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS,
|
||||
HUB_BASE_URL_SETTING,
|
||||
HUB_ACCESSIBLE_URL_SETTING, HUB_BASE_URL_SETTING,
|
||||
},
|
||||
server::Smtp,
|
||||
utils::send_email,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -262,6 +262,7 @@ pub async fn get_global_setting(
|
||||
&& !key.starts_with("default_success_handler_")
|
||||
&& key != AUTOMATE_USERNAME_CREATION_SETTING
|
||||
&& key != HUB_BASE_URL_SETTING
|
||||
&& key != HUB_ACCESSIBLE_URL_SETTING
|
||||
&& key != EMAIL_DOMAIN_SETTING
|
||||
{
|
||||
require_super_admin(&db, &authed.email).await?;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
|
||||
use crate::users::{EditPassword, NewUser};
|
||||
use crate::{db::DB, webhook_util::WebhookShared};
|
||||
use argon2::Argon2;
|
||||
|
||||
use http::StatusCode;
|
||||
|
||||
use windmill_common::error::{Error, Result};
|
||||
|
||||
pub async fn create_user(
|
||||
_authed: ApiAuthed,
|
||||
_db: DB,
|
||||
_webhook: WebhookShared,
|
||||
_argon2: Arc<Argon2<'_>>,
|
||||
_rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
mut _nu: NewUser,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
Err(Error::InternalErr(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn set_password(
|
||||
_db: DB,
|
||||
_argon2: Arc<Argon2<'_>>,
|
||||
_authed: ApiAuthed,
|
||||
_ep: EditPassword,
|
||||
) -> Result<String> {
|
||||
Err(Error::InternalErr(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn send_email_if_possible(_subject: &str, _content: &str, _to: &str) {
|
||||
tracing::warn!(
|
||||
"send_email_if_possible is not implemented in Windmill's Open Source repository"
|
||||
);
|
||||
}
|
||||
@@ -155,25 +155,6 @@ pub async fn get_instance_username_or_create_pending<'c>(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_and_delete_pending_username_or_generate<'c>(
|
||||
tx: &mut Transaction<'c, Postgres>,
|
||||
email: &str,
|
||||
) -> error::Result<String> {
|
||||
let username = sqlx::query_scalar!("SELECT username FROM pending_user WHERE email = $1", email)
|
||||
.fetch_optional(&mut **tx)
|
||||
.await?;
|
||||
|
||||
if let Some(username) = username {
|
||||
sqlx::query!("DELETE FROM pending_user WHERE email = $1", email)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
Ok(username)
|
||||
} else {
|
||||
let username = generate_instance_wide_unique_username(&mut *tx, email).await?;
|
||||
Ok(username)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn content_plain(body: Body) -> Response {
|
||||
use axum::http::header;
|
||||
Response::builder()
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::utils::{get_instance_username_or_create_pending, INVALID_USERNAME_CHARS};
|
||||
use crate::users_ee::send_email_if_possible;
|
||||
use crate::utils::get_instance_username_or_create_pending;
|
||||
use crate::BASE_URL;
|
||||
use crate::{
|
||||
apps::AppWithLastVersion,
|
||||
db::DB,
|
||||
folders::Folder,
|
||||
resources::{Resource, ResourceType},
|
||||
users::{send_email_if_possible, WorkspaceInvite, VALID_USERNAME},
|
||||
users::{WorkspaceInvite, VALID_USERNAME},
|
||||
utils::require_super_admin,
|
||||
webhook_util::WebhookShared,
|
||||
};
|
||||
@@ -34,7 +35,7 @@ use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
|
||||
use uuid::Uuid;
|
||||
use windmill_audit::audit_ee::{audit_log, AuditAuthor, AuditAuthorable};
|
||||
use windmill_audit::audit_ee::audit_log;
|
||||
use windmill_audit::ActionKind;
|
||||
use windmill_common::db::UserDB;
|
||||
use windmill_common::s3_helpers::LargeFileStorage;
|
||||
@@ -218,11 +219,12 @@ struct EditDeployTo {
|
||||
deploy_to: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
struct EditAutoInvite {
|
||||
operator: Option<bool>,
|
||||
invite_all: Option<bool>,
|
||||
auto_add: Option<bool>,
|
||||
pub struct EditAutoInvite {
|
||||
pub operator: Option<bool>,
|
||||
pub invite_all: Option<bool>,
|
||||
pub auto_add: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -573,250 +575,21 @@ async fn edit_deploy_to() -> Result<String> {
|
||||
));
|
||||
}
|
||||
|
||||
const BANNED_DOMAINS: &str = include_str!("../banned_domains.txt");
|
||||
pub const BANNED_DOMAINS: &str = include_str!("../banned_domains.txt");
|
||||
|
||||
async fn is_allowed_auto_domain(ApiAuthed { email, .. }: ApiAuthed) -> JsonResult<bool> {
|
||||
let domain = email.split('@').last().unwrap();
|
||||
return Ok(Json(!BANNED_DOMAINS.contains(domain)));
|
||||
}
|
||||
|
||||
async fn auto_add_user(
|
||||
email: &str,
|
||||
w_id: &str,
|
||||
operator: &bool,
|
||||
tx: &mut Transaction<'_, Postgres>,
|
||||
authorable: &impl AuditAuthorable,
|
||||
) -> Result<String> {
|
||||
let automate_username_creation = sqlx::query_scalar!(
|
||||
"SELECT value FROM global_settings WHERE name = $1",
|
||||
AUTOMATE_USERNAME_CREATION_SETTING,
|
||||
)
|
||||
.fetch_optional(&mut **tx)
|
||||
.await?
|
||||
.map(|v| v.as_bool())
|
||||
.flatten()
|
||||
.unwrap_or(false);
|
||||
|
||||
let username = if automate_username_creation {
|
||||
get_instance_username_or_create_pending(&mut *tx, &email).await?
|
||||
} else {
|
||||
let mut username = email
|
||||
.split('@')
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.replace(".", "");
|
||||
|
||||
username = INVALID_USERNAME_CHARS
|
||||
.replace_all(&mut username, "")
|
||||
.to_string();
|
||||
|
||||
if username.is_empty() {
|
||||
username = "user".to_string()
|
||||
}
|
||||
|
||||
let base_username = username.clone();
|
||||
let mut username_conflict = true;
|
||||
let mut i = 1;
|
||||
while username_conflict {
|
||||
if i > 1000 {
|
||||
return Err(Error::InternalErr(format!(
|
||||
"too many username conflicts for {}",
|
||||
email
|
||||
)));
|
||||
}
|
||||
if i > 1 {
|
||||
username = format!("{}{}", base_username, i)
|
||||
}
|
||||
username_conflict = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM usr WHERE username = $1 AND workspace_id = $2)",
|
||||
&username,
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&mut **tx)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
i += 1;
|
||||
}
|
||||
username
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO usr (workspace_id, username, email, is_admin, operator) VALUES ($1, $2, $3, false, $4) ON CONFLICT DO NOTHING",
|
||||
&w_id,
|
||||
&username,
|
||||
&email,
|
||||
&operator
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query_as!(
|
||||
Group,
|
||||
"INSERT INTO usr_to_group (workspace_id, usr, group_) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING",
|
||||
&w_id,
|
||||
username,
|
||||
"all",
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
let audit_author = if authorable.username() == authorable.email() && authorable.email() == email
|
||||
{
|
||||
// if the user is auto adding themselves (e.g. by joining the instance), we use their newly created workspace username for audit logs
|
||||
AuditAuthor {
|
||||
username: username.clone(),
|
||||
email: email.to_string(),
|
||||
username_override: None,
|
||||
}
|
||||
} else {
|
||||
AuditAuthor {
|
||||
username: authorable.username().to_string(),
|
||||
email: authorable.email().to_string(),
|
||||
username_override: authorable.username_override().map(|x| x.to_string()),
|
||||
}
|
||||
};
|
||||
audit_log(
|
||||
&mut **tx,
|
||||
&audit_author,
|
||||
"users.auto_invite_add",
|
||||
ActionKind::Create,
|
||||
&w_id,
|
||||
Some(email),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(username)
|
||||
}
|
||||
|
||||
async fn edit_auto_invite(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path(w_id): Path<String>,
|
||||
ApiAuthed { is_admin, email, username, .. }: ApiAuthed,
|
||||
Json(ea): Json<EditAutoInvite>,
|
||||
) -> Result<String> {
|
||||
require_admin(is_admin, &username)?;
|
||||
|
||||
// #[cfg(not(feature = "enterprise"))]
|
||||
// {
|
||||
// return Err(Error::BadRequest(
|
||||
// "Auto-invite is only available on enterprise".to_string(),
|
||||
// ));
|
||||
// }
|
||||
|
||||
let domain = if ea.invite_all.is_some_and(|x| x) {
|
||||
if *CLOUD_HOSTED {
|
||||
return Err(Error::BadRequest(
|
||||
"invite_all is only available locally".to_string(),
|
||||
));
|
||||
} else {
|
||||
"*"
|
||||
}
|
||||
} else {
|
||||
email.split('@').last().unwrap()
|
||||
};
|
||||
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
let mut users_to_auto_add = Option::None;
|
||||
|
||||
if let (Some(operator), Some(auto_add)) = (ea.operator, ea.auto_add) {
|
||||
if BANNED_DOMAINS.contains(domain) {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"Domain {} is not allowed",
|
||||
domain
|
||||
)));
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE workspace_settings SET auto_invite_domain = $1, auto_invite_operator = $2, auto_add = $4 WHERE workspace_id = $3",
|
||||
domain,
|
||||
operator,
|
||||
&w_id,
|
||||
auto_add,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
if auto_add {
|
||||
users_to_auto_add = Some(sqlx::query!(
|
||||
"SELECT email FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%', $2::text)) AND NOT EXISTS (
|
||||
SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email
|
||||
)",
|
||||
&w_id,
|
||||
domain
|
||||
)
|
||||
.fetch_all(&mut *tx).await?);
|
||||
|
||||
for user in users_to_auto_add.as_ref().unwrap() {
|
||||
auto_add_user(&user.email, &w_id, &operator, &mut tx, &authed).await?;
|
||||
send_email_if_possible(
|
||||
&format!("Added to Windmill's workspace: {w_id}"),
|
||||
&format!(
|
||||
"You have been granted access to Windmill's workspace {w_id} by {email}.
|
||||
|
||||
Access the workspace at {}/?workspace={w_id}",
|
||||
BASE_URL.read().await.clone()
|
||||
),
|
||||
&user.email,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"INSERT INTO workspace_invite
|
||||
(workspace_id, email, is_admin, operator)
|
||||
SELECT $1::text, email, false, $3 FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%', $2::text)) AND NOT EXISTS (
|
||||
SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email
|
||||
)
|
||||
ON CONFLICT DO NOTHING",
|
||||
&w_id,
|
||||
domain,
|
||||
operator
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"UPDATE workspace_settings SET auto_invite_domain = NULL, auto_invite_operator = NULL, auto_add = NULL WHERE workspace_id = $1",
|
||||
&w_id,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"workspaces.edit_auto_invite_domain",
|
||||
ActionKind::Update,
|
||||
&w_id,
|
||||
Some(&authed.email),
|
||||
Some([("operator", &format!("{:?}", ea.operator)[..])].into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
if let Some(users) = users_to_auto_add {
|
||||
for user in users {
|
||||
handle_deployment_metadata(
|
||||
&email,
|
||||
&username,
|
||||
&db,
|
||||
&w_id,
|
||||
windmill_git_sync::DeployedObject::User { email: user.email.clone() },
|
||||
Some(format!("Auto-added user '{}' to workspace", &user.email)),
|
||||
rsmq.clone(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(format!(
|
||||
"Edit auto-invite for workspace {} to {}",
|
||||
&w_id, domain
|
||||
))
|
||||
crate::workspaces_ee::edit_auto_invite(authed, db, rsmq, w_id, ea).await
|
||||
}
|
||||
|
||||
async fn edit_webhook(
|
||||
@@ -2001,61 +1774,6 @@ async fn delete_workspace(
|
||||
Ok(format!("Deleted workspace {}", &w_id))
|
||||
}
|
||||
|
||||
pub async fn invite_user_to_all_auto_invite_worspaces(
|
||||
db: &DB,
|
||||
email: &str,
|
||||
rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
authorable: &impl AuditAuthorable,
|
||||
) -> Result<()> {
|
||||
let mut tx = db.begin().await?;
|
||||
let domain = email.split('@').last().unwrap();
|
||||
let workspaces = sqlx::query!(
|
||||
"SELECT workspace_id, auto_invite_operator, auto_add FROM workspace_settings ws WHERE (auto_invite_domain = $1 OR auto_invite_domain = '*') AND NOT EXISTS (SELECT 1 FROM usr WHERE workspace_id = ws.workspace_id AND email = $2)",
|
||||
domain,
|
||||
email
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
let mut auto_added_workspace_usernames: Vec<(String, String)> = vec![];
|
||||
for r in workspaces {
|
||||
if r.auto_add.is_some() && r.auto_add.unwrap() {
|
||||
let operator = r.auto_invite_operator.unwrap_or(false);
|
||||
let username =
|
||||
auto_add_user(email, &r.workspace_id, &operator, &mut tx, authorable).await?;
|
||||
auto_added_workspace_usernames.push((r.workspace_id, username));
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"INSERT INTO workspace_invite
|
||||
(workspace_id, email, is_admin, operator)
|
||||
VALUES ($1, $2, false, $3)
|
||||
ON CONFLICT DO NOTHING",
|
||||
r.workspace_id,
|
||||
email,
|
||||
r.auto_invite_operator
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
for workspace_username_tuple in auto_added_workspace_usernames {
|
||||
let (w_id, username) = workspace_username_tuple;
|
||||
handle_deployment_metadata(
|
||||
&email,
|
||||
&username,
|
||||
db,
|
||||
&w_id,
|
||||
windmill_git_sync::DeployedObject::User { email: email.to_string() },
|
||||
Some(format!("Auto-added user '{}' to workspace", email)),
|
||||
rsmq.clone(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn invite_user(
|
||||
ApiAuthed { username, is_admin, .. }: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
workspaces::EditAutoInvite,
|
||||
};
|
||||
|
||||
pub async fn edit_auto_invite(
|
||||
_authed: ApiAuthed,
|
||||
_db: DB,
|
||||
_rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
_w_id: String,
|
||||
_ea: EditAutoInvite,
|
||||
) -> windmill_common::error::Result<String> {
|
||||
Err(windmill_common::error::Error::InternalErr(
|
||||
"Not implemented on OSS".to_string(),
|
||||
))
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::db::DB;
|
||||
use crate::ee::LicensePlan::Community;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::error;
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref LICENSE_KEY_VALID: Arc<RwLock<bool>> = Arc::new(RwLock::new(true));
|
||||
pub static ref LICENSE_KEY_ID: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
pub static ref LICENSE_KEY: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
}
|
||||
|
||||
pub enum LicensePlan {
|
||||
Community,
|
||||
Pro,
|
||||
Enterprise,
|
||||
}
|
||||
|
||||
pub async fn get_license_plan() -> LicensePlan {
|
||||
// Implementation is not open source
|
||||
return Community;
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CriticalErrorChannel {}
|
||||
|
||||
pub enum CriticalAlertKind {
|
||||
#[cfg(feature = "enterprise")]
|
||||
CriticalError,
|
||||
#[cfg(feature = "enterprise")]
|
||||
RecoveredCriticalError,
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn send_critical_alert(
|
||||
_error_message: String,
|
||||
_db: &DB,
|
||||
_kind: CriticalAlertKind,
|
||||
_channels: Option<Vec<CriticalErrorChannel>>,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::db::DB) -> () {
|
||||
// Implementation is not open source
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn renew_license_key(
|
||||
_http_client: &reqwest::Client,
|
||||
_db: &crate::db::DB,
|
||||
_key: Option<String>,
|
||||
_manual: bool,
|
||||
) -> String {
|
||||
// Implementation is not open source
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn create_customer_portal_session(
|
||||
_http_client: &reqwest::Client,
|
||||
_key: Option<String>,
|
||||
) -> error::Result<String> {
|
||||
// Implementation is not open source
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn worker_groups_alerts(_db: &DB) {}
|
||||
@@ -1,76 +0,0 @@
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::db::DB;
|
||||
use crate::ee::LicensePlan::Community;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::error;
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref LICENSE_KEY_VALID: Arc<RwLock<bool>> = Arc::new(RwLock::new(true));
|
||||
pub static ref LICENSE_KEY_ID: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
pub static ref LICENSE_KEY: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
}
|
||||
|
||||
pub enum LicensePlan {
|
||||
Community,
|
||||
Pro,
|
||||
Enterprise,
|
||||
}
|
||||
|
||||
pub async fn get_license_plan() -> LicensePlan {
|
||||
// Implementation is not open source
|
||||
return Community;
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CriticalErrorChannel {
|
||||
Email { email: String },
|
||||
Slack { slack_channel: String },
|
||||
}
|
||||
|
||||
pub enum CriticalAlertKind {
|
||||
#[cfg(feature = "enterprise")]
|
||||
CriticalError,
|
||||
#[cfg(feature = "enterprise")]
|
||||
RecoveredCriticalError,
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn send_critical_alert(
|
||||
_error_message: String,
|
||||
_db: &DB,
|
||||
_kind: CriticalAlertKind,
|
||||
_channels: Option<Vec<CriticalErrorChannel>>,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::db::DB) -> () {
|
||||
// Implementation is not open source
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn renew_license_key(
|
||||
_http_client: &reqwest::Client,
|
||||
_db: &crate::db::DB,
|
||||
_key: Option<String>,
|
||||
_manual: bool,
|
||||
) -> String {
|
||||
// Implementation is not open source
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn create_customer_portal_session(
|
||||
_http_client: &reqwest::Client,
|
||||
_key: Option<String>,
|
||||
) -> error::Result<String> {
|
||||
// Implementation is not open source
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn worker_groups_alerts(_db: &DB) {}
|
||||
@@ -0,0 +1,11 @@
|
||||
use crate::server::Smtp;
|
||||
|
||||
pub async fn send_email(
|
||||
_subject: &str,
|
||||
_content: &str,
|
||||
_to: Vec<String>,
|
||||
_smtp: Smtp,
|
||||
_client_timeout: Option<tokio::time::Duration>,
|
||||
) -> crate::error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -27,6 +27,7 @@ pub const OBJECT_STORE_CACHE_CONFIG_SETTING: &str = "object_store_cache_config";
|
||||
|
||||
pub const AUTOMATE_USERNAME_CREATION_SETTING: &str = "automate_username_creation";
|
||||
pub const HUB_BASE_URL_SETTING: &str = "hub_base_url";
|
||||
pub const HUB_ACCESSIBLE_URL_SETTING: &str = "hub_accessible_url";
|
||||
pub const CRITICAL_ERROR_CHANNELS_SETTING: &str = "critical_error_channels";
|
||||
pub const DEV_INSTANCE_SETTING: &str = "dev_instance";
|
||||
pub const JWT_SECRET_SETTING: &str = "jwt_secret";
|
||||
|
||||
@@ -22,6 +22,7 @@ pub mod auth;
|
||||
pub mod bench;
|
||||
pub mod db;
|
||||
pub mod ee;
|
||||
pub mod email_ee;
|
||||
pub mod error;
|
||||
pub mod external_ip;
|
||||
pub mod flow_status;
|
||||
|
||||
@@ -117,10 +117,10 @@ pub struct S3Object {
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
pub async fn get_etag_or_empty(
|
||||
object_store_resource: &ObjectStoreResource,
|
||||
object_store_resource: &mut ObjectStoreResource,
|
||||
s3_object: S3Object,
|
||||
) -> Option<String> {
|
||||
let object_store_client = build_object_store_client(object_store_resource);
|
||||
let object_store_client = build_object_store_client(object_store_resource).await;
|
||||
if object_store_client.is_err() {
|
||||
return None;
|
||||
}
|
||||
@@ -166,11 +166,11 @@ pub fn render_endpoint(
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
pub fn build_object_store_client(
|
||||
pub async fn build_object_store_client(
|
||||
resource_ref: &ObjectStoreResource,
|
||||
) -> error::Result<Arc<dyn ObjectStore>> {
|
||||
match resource_ref {
|
||||
ObjectStoreResource::S3(s3_resource_ref) => build_s3_client(&s3_resource_ref, None),
|
||||
ObjectStoreResource::S3(s3_resource_ref) => build_s3_client(&s3_resource_ref).await,
|
||||
ObjectStoreResource::Azure(azure_blob_resource_ref) => {
|
||||
build_azure_blob_client(&azure_blob_resource_ref)
|
||||
}
|
||||
@@ -225,10 +225,21 @@ use aws_config::{default_provider::credentials::DefaultCredentialsChain, Region}
|
||||
use object_store::CredentialProvider;
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
pub fn build_s3_client(
|
||||
s3_resource_ref: &S3Resource,
|
||||
credential_providers: Option<DefaultCredentialsChain>,
|
||||
) -> error::Result<Arc<dyn ObjectStore>> {
|
||||
pub async fn build_s3_client(s3_resource_ref: &S3Resource) -> error::Result<Arc<dyn ObjectStore>> {
|
||||
let static_creds = s3_resource_ref.access_key.as_ref().is_some_and(|x| x != "")
|
||||
|| s3_resource_ref.secret_key.as_ref().is_some_and(|x| x != "");
|
||||
|
||||
let credentials_provider = if !static_creds {
|
||||
Some(
|
||||
DefaultCredentialsChain::builder()
|
||||
.region(Region::new(s3_resource_ref.region.clone()))
|
||||
.build()
|
||||
.await,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let s3_resource = s3_resource_ref.clone();
|
||||
let endpoint = render_endpoint(
|
||||
s3_resource.endpoint,
|
||||
@@ -244,7 +255,7 @@ pub fn build_s3_client(
|
||||
.with_bucket_name(s3_resource.bucket)
|
||||
.with_endpoint(endpoint);
|
||||
|
||||
if let Some(credentials_provider) = credential_providers {
|
||||
if let Some(credentials_provider) = credentials_provider {
|
||||
store_builder = store_builder.with_credentials(Arc::new(AwsCredentialAdapter {
|
||||
inner: credentials_provider,
|
||||
}));
|
||||
@@ -399,18 +410,7 @@ pub async fn build_s3_client_from_settings(
|
||||
) -> error::Result<Arc<dyn ObjectStore>> {
|
||||
let region = none_if_empty(settings.region)
|
||||
.unwrap_or_else(|| std::env::var("AWS_REGION").unwrap_or_else(|_| "us-east-1".to_string()));
|
||||
let access_key = none_if_empty(settings.access_key);
|
||||
let secret_key = none_if_empty(settings.secret_key);
|
||||
let credentials_provider = if access_key.is_none() && secret_key.is_none() {
|
||||
Some(
|
||||
DefaultCredentialsChain::builder()
|
||||
.region(Region::new(region.clone()))
|
||||
.build()
|
||||
.await,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let s3_resource = S3Resource {
|
||||
endpoint: none_if_empty(settings.endpoint).unwrap_or_else(|| {
|
||||
std::env::var("S3_ENDPOINT").unwrap_or_else(|_| format!("s3.{region}.amazonaws.com"))
|
||||
@@ -419,15 +419,15 @@ pub async fn build_s3_client_from_settings(
|
||||
std::env::var("S3_CACHE_BUCKET").unwrap_or_else(|_| "missingbucket".to_string())
|
||||
}),
|
||||
region,
|
||||
access_key,
|
||||
secret_key,
|
||||
access_key: settings.access_key,
|
||||
secret_key: settings.secret_key,
|
||||
use_ssl: !settings.allow_http.unwrap_or(true),
|
||||
path_style: settings.path_style,
|
||||
port: settings.port,
|
||||
token: None,
|
||||
};
|
||||
|
||||
build_s3_client(&s3_resource, credentials_provider)
|
||||
build_s3_client(&s3_resource).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
|
||||
@@ -11,13 +11,11 @@ use crate::ee::LICENSE_KEY_ID;
|
||||
use crate::ee::{send_critical_alert, CriticalAlertKind};
|
||||
use crate::error::{to_anyhow, Error, Result};
|
||||
use crate::global_settings::UNIQUE_ID_SETTING;
|
||||
use crate::server::Smtp;
|
||||
use crate::DB;
|
||||
use anyhow::Context;
|
||||
use gethostname::gethostname;
|
||||
use git_version::git_version;
|
||||
use mail_send::mail_builder::MessageBuilder;
|
||||
use mail_send::SmtpClientBuilder;
|
||||
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -243,58 +241,6 @@ pub fn generate_lock_id(database_name: &str) -> i64 {
|
||||
0x3d32ad9e * (CRC_IEEE.checksum(database_name.as_bytes()) as i64)
|
||||
}
|
||||
|
||||
pub async fn send_email(
|
||||
subject: &str,
|
||||
content: &str,
|
||||
to: Vec<String>,
|
||||
smtp: Smtp,
|
||||
client_timeout: Option<tokio::time::Duration>,
|
||||
) -> Result<()> {
|
||||
let mut client = SmtpClientBuilder::new(smtp.host, smtp.port)
|
||||
.implicit_tls(smtp.tls_implicit.unwrap_or(false));
|
||||
if std::env::var("ACCEPT_INVALID_CERTS").is_ok() {
|
||||
client = client.allow_invalid_certs();
|
||||
}
|
||||
let client = if let (Some(username), Some(password)) = (smtp.username, smtp.password) {
|
||||
if !username.is_empty() {
|
||||
client.credentials((username, password))
|
||||
} else {
|
||||
client
|
||||
}
|
||||
} else {
|
||||
client
|
||||
};
|
||||
let message = MessageBuilder::new()
|
||||
.from(("Windmill", smtp.from.as_str()))
|
||||
.to(to.clone())
|
||||
.subject(subject)
|
||||
.text_body(content);
|
||||
|
||||
match client_timeout {
|
||||
Some(timeout) => {
|
||||
tokio::time::timeout(timeout, client.connect())
|
||||
.await
|
||||
.map_err(to_anyhow)?
|
||||
.map_err(to_anyhow)?
|
||||
.send(message)
|
||||
.await
|
||||
.map_err(to_anyhow)?;
|
||||
}
|
||||
None => {
|
||||
client
|
||||
.connect()
|
||||
.await
|
||||
.map_err(to_anyhow)?
|
||||
.send(message)
|
||||
.await
|
||||
.map_err(to_anyhow)?;
|
||||
}
|
||||
}
|
||||
tracing::info!("Sent email to {:#?}: {subject}", to);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub async fn report_critical_error(error_message: String, _db: DB) -> () {
|
||||
tracing::error!("CRITICAL ERROR: {error_message}");
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::{
|
||||
handle_child::handle_child,
|
||||
python_executor::{create_dependencies_dir, handle_python_reqs, uv_pip_compile},
|
||||
AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV,
|
||||
TZ_ENV,
|
||||
PROXY_ENVS, TZ_ENV,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -148,6 +148,7 @@ async fn install_galaxy_collections(
|
||||
galaxy_command
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
// .env("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -396,6 +397,7 @@ fi
|
||||
nsjail_cmd
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(PROXY_ENVS.clone())
|
||||
// inject PYTHONPATH here - for some reason I had to do it in nsjail conf
|
||||
.envs(reserved_variables)
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
@@ -557,19 +559,12 @@ async fn create_file_resources(
|
||||
}
|
||||
|
||||
for file_res in &r.file_resources {
|
||||
let r = get_resource_or_variable_content(
|
||||
client,
|
||||
&file_res.resource_path,
|
||||
job_id.to_string(),
|
||||
)
|
||||
.await?;
|
||||
let r =
|
||||
get_resource_or_variable_content(client, &file_res.resource_path, job_id.to_string())
|
||||
.await?;
|
||||
let path = file_res.target_path.clone();
|
||||
let validated_path = write_file_at_user_defined_location(
|
||||
job_dir,
|
||||
path.as_str(),
|
||||
&r,
|
||||
)
|
||||
.map_err(|e| anyhow!("Couldn't write text file at {}: {}", path, e))?;
|
||||
let validated_path = write_file_at_user_defined_location(job_dir, path.as_str(), &r)
|
||||
.map_err(|e| anyhow!("Couldn't write text file at {}: {}", path, e))?;
|
||||
|
||||
nsjail_mounts.push(
|
||||
define_nsjail_mount(job_dir, &validated_path)
|
||||
@@ -597,13 +592,14 @@ async fn get_resource_or_variable_content(
|
||||
.get_resource_value_interpolated::<serde_json::Value>(&p, Some(job_id))
|
||||
.await?;
|
||||
|
||||
r.get("content").and_then(|v| v.as_str()).ok_or(anyhow!(
|
||||
"Invalid text file resource {}, `content` field absent or invalid",
|
||||
p
|
||||
))?.to_string()
|
||||
}
|
||||
ResourceOrVariablePath::Variable(p) => {
|
||||
client.get_variable_value(&p).await?
|
||||
r.get("content")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or(anyhow!(
|
||||
"Invalid text file resource {}, `content` field absent or invalid",
|
||||
p
|
||||
))?
|
||||
.to_string()
|
||||
}
|
||||
ResourceOrVariablePath::Variable(p) => client.get_variable_value(&p).await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ use crate::{
|
||||
},
|
||||
handle_child::handle_child,
|
||||
AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV,
|
||||
POWERSHELL_CACHE_DIR, POWERSHELL_PATH, TZ_ENV,
|
||||
POWERSHELL_CACHE_DIR, POWERSHELL_PATH, PROXY_ENVS, TZ_ENV,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -143,6 +143,7 @@ wait
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(reserved_variables)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.args(cmd_args)
|
||||
@@ -435,6 +436,7 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
|
||||
Command::new(NSJAIL_PATH.as_str())
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(reserved_variables)
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::{
|
||||
handle_child::handle_child,
|
||||
AuthedClientBackgroundTask, BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR,
|
||||
BUN_DEPSTAR_CACHE_DIR, BUN_PATH, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH,
|
||||
NODE_PATH, NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, TZ_ENV,
|
||||
NODE_PATH, NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TZ_ENV,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -275,6 +275,7 @@ pub async fn install_bun_lockfile(
|
||||
child_cmd
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(common_bun_proc_envs)
|
||||
.args(vec!["install"])
|
||||
.stdout(Stdio::piped())
|
||||
|
||||
@@ -736,8 +736,8 @@ async fn arg_value_hash_additions(
|
||||
.await;
|
||||
storage = s3_object.storage.clone();
|
||||
|
||||
if let Some(s3_resource) = s3_resource_opt.ok().flatten() {
|
||||
let etag = get_etag_or_empty(&s3_resource, s3_object.clone()).await;
|
||||
if let Some(mut s3_resource) = s3_resource_opt.ok().flatten() {
|
||||
let etag = get_etag_or_empty(&mut s3_resource, s3_object.clone()).await;
|
||||
tracing::warn!("Enriching s3 arg value with etag: {:?}", etag);
|
||||
result.insert(s3_object.s3.clone(), etag.unwrap_or_default()); // TODO: maybe inject a random value to invalidate the cache?
|
||||
}
|
||||
@@ -793,9 +793,9 @@ pub async fn get_cached_resource_value_if_valid(
|
||||
return None;
|
||||
}
|
||||
for (s3_file_key, s3_file_etag) in s3_etags {
|
||||
if let Some(object_store_resource) = object_store_resource_opt.clone() {
|
||||
if let Some(mut object_store_resource) = object_store_resource_opt.clone() {
|
||||
let etag = get_etag_or_empty(
|
||||
&object_store_resource,
|
||||
&mut object_store_resource,
|
||||
S3Object {
|
||||
s3: s3_file_key.clone(),
|
||||
storage: cached_resource.storage.clone(),
|
||||
|
||||
@@ -77,7 +77,7 @@ pub async fn handle_dedicated_process(
|
||||
) -> std::result::Result<(), error::Error> {
|
||||
//do not cache local dependencies
|
||||
|
||||
use crate::handle_child::process_status;
|
||||
use crate::{handle_child::process_status, PROXY_ENVS};
|
||||
|
||||
let mut child = {
|
||||
let mut cmd = Command::new(command_path);
|
||||
@@ -85,6 +85,7 @@ pub async fn handle_dedicated_process(
|
||||
.env_clear()
|
||||
.envs(context_envs)
|
||||
.envs(envs)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(
|
||||
reserved_variables
|
||||
.iter()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::PROXY_ENVS;
|
||||
use std::{collections::HashMap, fs::DirBuilder, process::Stdio};
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -189,6 +190,7 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.env("GOPATH", GO_CACHE_DIR)
|
||||
.env("HOME", HOME_ENV.as_str())
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.args(vec!["build", "main.go"])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
@@ -71,7 +71,7 @@ use crate::{
|
||||
read_result, start_child_process, OccupancyMetrics,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, HTTPS_PROXY, HTTP_PROXY,
|
||||
AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, PROXY_ENVS,
|
||||
LOCK_CACHE_DIR, NO_PROXY, NSJAIL_PATH, PATH_ENV, PIP_CACHE_DIR, PIP_EXTRA_INDEX_URL,
|
||||
PIP_INDEX_URL, PY311_CACHE_DIR, TZ_ENV, UV_CACHE_DIR,
|
||||
};
|
||||
@@ -571,6 +571,7 @@ mount {{
|
||||
.env_clear()
|
||||
// inject PYTHONPATH here - for some reason I had to do it in nsjail conf
|
||||
.envs(reserved_variables)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("TZ", TZ_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -1020,15 +1021,6 @@ pub async fn handle_python_reqs(
|
||||
if let Some(host) = PIP_TRUSTED_HOST.as_ref() {
|
||||
vars.push(("TRUSTED_HOST", host));
|
||||
}
|
||||
if let Some(http_proxy) = HTTP_PROXY.as_ref() {
|
||||
vars.push(("HTTP_PROXY", http_proxy));
|
||||
}
|
||||
if let Some(https_proxy) = HTTPS_PROXY.as_ref() {
|
||||
vars.push(("HTTPS_PROXY", https_proxy));
|
||||
}
|
||||
if let Some(no_proxy) = NO_PROXY.as_ref() {
|
||||
vars.push(("NO_PROXY", no_proxy));
|
||||
}
|
||||
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
@@ -1177,6 +1169,7 @@ pub async fn handle_python_reqs(
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(vars)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.args(vec!["--config", "download.config.proto"])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
@@ -1256,15 +1249,6 @@ pub async fn handle_python_reqs(
|
||||
}
|
||||
|
||||
let mut envs = vec![("PATH", PATH_ENV.as_str())];
|
||||
if let Some(http_proxy) = HTTP_PROXY.as_ref() {
|
||||
envs.push(("HTTP_PROXY", http_proxy));
|
||||
}
|
||||
if let Some(https_proxy) = HTTPS_PROXY.as_ref() {
|
||||
envs.push(("HTTPS_PROXY", https_proxy));
|
||||
}
|
||||
if let Some(no_proxy) = NO_PROXY.as_ref() {
|
||||
envs.push(("NO_PROXY", no_proxy));
|
||||
}
|
||||
|
||||
envs.push(("HOME", HOME_ENV.as_str()));
|
||||
|
||||
@@ -1275,6 +1259,7 @@ pub async fn handle_python_reqs(
|
||||
let mut flock_cmd = Command::new(FLOCK_PATH.as_str());
|
||||
flock_cmd
|
||||
.env_clear()
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.envs(envs)
|
||||
.args([
|
||||
"-x",
|
||||
@@ -1299,6 +1284,7 @@ pub async fn handle_python_reqs(
|
||||
pip_cmd
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.env("SystemRoot", SYSTEM_ROOT.as_str())
|
||||
.args(&command_args[1..])
|
||||
.stdout(Stdio::piped())
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::{
|
||||
},
|
||||
handle_child::handle_child,
|
||||
AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV,
|
||||
RUST_CACHE_DIR, TZ_ENV,
|
||||
PROXY_ENVS, RUST_CACHE_DIR, TZ_ENV,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -192,6 +192,7 @@ pub async fn build_rust_crate(
|
||||
build_rust_cmd
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(PROXY_ENVS.clone())
|
||||
.env("PATH", PATH_ENV.as_str())
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.env("HOME", HOME_ENV.as_str())
|
||||
|
||||
@@ -333,6 +333,20 @@ lazy_static::lazy_static! {
|
||||
pub static ref NO_PROXY: Option<String> = std::env::var("no_proxy").ok().or(std::env::var("NO_PROXY").ok());
|
||||
pub static ref HTTP_PROXY: Option<String> = std::env::var("http_proxy").ok().or(std::env::var("HTTP_PROXY").ok());
|
||||
pub static ref HTTPS_PROXY: Option<String> = std::env::var("https_proxy").ok().or(std::env::var("HTTPS_PROXY").ok());
|
||||
|
||||
pub static ref PROXY_ENVS: Vec<(&'static str, String)> = {
|
||||
let mut proxy_env = Vec::new();
|
||||
if let Some(no_proxy) = NO_PROXY.as_ref() {
|
||||
proxy_env.push(("NO_PROXY", no_proxy.to_string()));
|
||||
}
|
||||
if let Some(http_proxy) = HTTP_PROXY.as_ref() {
|
||||
proxy_env.push(("HTTP_PROXY", http_proxy.to_string()));
|
||||
}
|
||||
if let Some(https_proxy) = HTTPS_PROXY.as_ref() {
|
||||
proxy_env.push(("HTTPS_PROXY", https_proxy.to_string()));
|
||||
}
|
||||
proxy_env
|
||||
};
|
||||
pub static ref DENO_PATH: String = std::env::var("DENO_PATH").unwrap_or_else(|_| "/usr/bin/deno".to_string());
|
||||
pub static ref BUN_PATH: String = std::env::var("BUN_PATH").unwrap_or_else(|_| "/usr/bin/bun".to_string());
|
||||
pub static ref NPM_PATH: String = std::env::var("NPM_PATH").unwrap_or_else(|_| "/usr/bin/npm".to_string());
|
||||
@@ -1289,16 +1303,16 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(feature = "prometheus")]
|
||||
if let Some(wb) = worker_busy.as_ref() {
|
||||
wb.set(1);
|
||||
tracing::debug!("set worker busy to 1");
|
||||
}
|
||||
|
||||
occupancy_metrics.running_job_started_at = Some(Instant::now());
|
||||
|
||||
match next_job {
|
||||
Ok(Some(job)) => {
|
||||
#[cfg(feature = "prometheus")]
|
||||
if let Some(wb) = worker_busy.as_ref() {
|
||||
wb.set(1);
|
||||
tracing::debug!("set worker busy to 1");
|
||||
}
|
||||
|
||||
occupancy_metrics.running_job_started_at = Some(Instant::now());
|
||||
|
||||
last_executed_job = None;
|
||||
jobs_executed += 1;
|
||||
|
||||
@@ -2083,15 +2097,10 @@ pub fn build_envs(
|
||||
hm
|
||||
};
|
||||
|
||||
if let Some(ref env) = *HTTPS_PROXY {
|
||||
envs.insert("HTTPS_PROXY".to_string(), env.to_string());
|
||||
}
|
||||
if let Some(ref env) = *HTTP_PROXY {
|
||||
envs.insert("HTTP_PROXY".to_string(), env.to_string());
|
||||
}
|
||||
if let Some(ref env) = *NO_PROXY {
|
||||
envs.insert("NO_PROXY".to_string(), env.to_string());
|
||||
for (k, v) in PROXY_ENVS.iter() {
|
||||
envs.insert(k.to_string(), v.to_string());
|
||||
}
|
||||
|
||||
Ok(envs)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
|
||||
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
|
||||
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
|
||||
|
||||
export const VERSION = "v1.415.1";
|
||||
export const VERSION = "v1.416.2";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.415.1";
|
||||
export const VERSION = "1.416.2";
|
||||
|
||||
const command = new Command()
|
||||
.name("wmill")
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.415.1",
|
||||
"version": "1.416.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.415.1",
|
||||
"version": "1.416.2",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.415.1",
|
||||
"version": "1.416.2",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -666,6 +666,7 @@ $res = json_decode(curl_exec($ch));`)
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<slot name="right" />
|
||||
{#if scriptPath && !noHistory}
|
||||
<Button
|
||||
btnClasses="!font-medium text-tertiary"
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { skipAllTodos } from '$lib/tutorialUtils'
|
||||
import FlowBranchAll from './tutorials/FlowBranchAll.svelte'
|
||||
import FlowBranchOne from './tutorials/FlowBranchOne.svelte'
|
||||
import FlowBuilderTutorialBranchAll from './tutorials/FlowBuilderTutorialBranchAll.svelte'
|
||||
import FlowBuilderTutorialBranchOne from './tutorials/FlowBuilderTutorialBranchOne.svelte'
|
||||
import FlowBuilderTutorialSimpleFlow from './tutorials/FlowBuilderTutorialSimpleFlow.svelte'
|
||||
import FlowBuilderTutorialsForLoop from './tutorials/FlowBuilderTutorialsForLoop.svelte'
|
||||
import FlowErrorHandler from './tutorials/FlowErrorHandler.svelte'
|
||||
import FlowBuilderTutorialForLoop from './tutorials/FlowBuilderTutorialForLoop.svelte'
|
||||
import FlowBuilderTutorialErrorHandler from './tutorials/FlowBuilderTutorialErrorHandler.svelte'
|
||||
|
||||
let flowBuilderTutorialSimpleFlow: FlowBuilderTutorialSimpleFlow | undefined = undefined
|
||||
let flowBuilderTutorialsForLoop: FlowBuilderTutorialsForLoop | undefined = undefined
|
||||
let flowBranchOne: FlowBranchOne | undefined = undefined
|
||||
let flowBranchAll: FlowBranchAll | undefined = undefined
|
||||
let flowErrorHandler: FlowErrorHandler | undefined = undefined
|
||||
let flowBuilderTutorialForLoop: FlowBuilderTutorialForLoop | undefined = undefined
|
||||
let flowBuilderTutorialBranchOne: FlowBuilderTutorialBranchOne | undefined = undefined
|
||||
let flowBuilderTutorialBranchAll: FlowBuilderTutorialBranchAll | undefined = undefined
|
||||
let flowBuilderTutorialErrorHandler: FlowBuilderTutorialErrorHandler | undefined = undefined
|
||||
|
||||
export function runTutorialById(id: string, indexToInsertAt?: number | undefined) {
|
||||
if (id === 'forloop') {
|
||||
flowBuilderTutorialsForLoop?.runTutorial(indexToInsertAt)
|
||||
flowBuilderTutorialForLoop?.runTutorial(indexToInsertAt)
|
||||
} else if (id === 'branchone') {
|
||||
flowBranchOne?.runTutorial()
|
||||
flowBuilderTutorialBranchOne?.runTutorial()
|
||||
} else if (id === 'branchall') {
|
||||
flowBranchAll?.runTutorial()
|
||||
flowBuilderTutorialBranchAll?.runTutorial()
|
||||
} else if (id === 'action') {
|
||||
flowBuilderTutorialSimpleFlow?.runTutorial()
|
||||
} else if (id === 'error-handler') {
|
||||
flowErrorHandler?.runTutorial()
|
||||
flowBuilderTutorialErrorHandler?.runTutorial()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +37,27 @@
|
||||
on:skipAll={skipAll}
|
||||
on:reload
|
||||
/>
|
||||
<FlowBuilderTutorialsForLoop
|
||||
bind:this={flowBuilderTutorialsForLoop}
|
||||
<FlowBuilderTutorialForLoop
|
||||
bind:this={flowBuilderTutorialForLoop}
|
||||
on:error
|
||||
on:skipAll={skipAll}
|
||||
on:reload
|
||||
/>
|
||||
<FlowBuilderTutorialBranchOne
|
||||
bind:this={flowBuilderTutorialBranchOne}
|
||||
on:error
|
||||
on:skipAll={skipAll}
|
||||
on:reload
|
||||
/>
|
||||
<FlowBuilderTutorialBranchAll
|
||||
bind:this={flowBuilderTutorialBranchAll}
|
||||
on:error
|
||||
on:skipAll={skipAll}
|
||||
on:reload
|
||||
/>
|
||||
<FlowBuilderTutorialErrorHandler
|
||||
bind:this={flowBuilderTutorialErrorHandler}
|
||||
on:error
|
||||
on:skipAll={skipAll}
|
||||
on:reload
|
||||
/>
|
||||
<FlowBranchOne bind:this={flowBranchOne} on:error on:skipAll={skipAll} on:reload />
|
||||
<FlowBranchAll bind:this={flowBranchAll} on:error on:skipAll={skipAll} on:reload />
|
||||
<FlowErrorHandler bind:this={flowErrorHandler} on:error on:skipAll={skipAll} on:reload />
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import KeycloakSetting from './KeycloakSetting.svelte'
|
||||
import Alert from './common/alert/Alert.svelte'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { capitalize, classNames } from '$lib/utils'
|
||||
import { capitalize, classNames, sleep } from '$lib/utils'
|
||||
import { enterpriseLicense } from '$lib/stores'
|
||||
import CustomOauth from './CustomOauth.svelte'
|
||||
import {
|
||||
@@ -111,6 +111,7 @@
|
||||
}
|
||||
|
||||
export async function saveSettings() {
|
||||
let shouldReloadPage = false
|
||||
if (values) {
|
||||
const allSettings = Object.values(settings).flatMap((x) => Object.entries(x))
|
||||
let licenseKeySet = false
|
||||
@@ -129,6 +130,9 @@
|
||||
if (x.key == 'license_key') {
|
||||
licenseKeySet = true
|
||||
}
|
||||
if (x.requiresReloadOnChange) {
|
||||
shouldReloadPage = true
|
||||
}
|
||||
return await SettingService.setGlobal({
|
||||
key: x.key,
|
||||
requestBody: { value: values?.[x.key] }
|
||||
@@ -158,8 +162,14 @@
|
||||
} else {
|
||||
console.error('Values not loaded')
|
||||
}
|
||||
sendUserToast('Settings updated')
|
||||
dispatch('saved')
|
||||
if (shouldReloadPage) {
|
||||
sendUserToast('Settings updated, reloading page...')
|
||||
await sleep(1000)
|
||||
window.location.reload()
|
||||
} else {
|
||||
sendUserToast('Settings updated')
|
||||
dispatch('saved')
|
||||
}
|
||||
}
|
||||
|
||||
let oauths: Record<string, any> = {}
|
||||
@@ -548,7 +558,7 @@
|
||||
<div>
|
||||
<div class="flex-col flex gap-2 pb-4">
|
||||
{#each settings[category] as setting}
|
||||
{#if (!setting.cloudonly || isCloudHosted()) && showSetting(setting.key, values)}
|
||||
{#if (!setting.cloudonly || isCloudHosted()) && showSetting(setting.key, values) && !(setting.hiddenIfNull && values[setting.key] == null)}
|
||||
{#if setting.ee_only != undefined && !$enterpriseLicense}
|
||||
<div class="flex text-xs items-center gap-1 text-yellow-500 whitespace-nowrap">
|
||||
<AlertTriangle size={16} />
|
||||
@@ -579,6 +589,20 @@
|
||||
: ''}
|
||||
bind:value={values[setting.key]}
|
||||
/>
|
||||
{#if setting.advancedToggle}
|
||||
<div class="mt-1">
|
||||
<Toggle
|
||||
size="xs"
|
||||
options={{ right: setting.advancedToggle.label }}
|
||||
checked={setting.advancedToggle.checked(values)}
|
||||
on:change={() => {
|
||||
if (setting.advancedToggle) {
|
||||
values = setting.advancedToggle.onChange(values)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if setting.fieldType == 'textarea'}
|
||||
<textarea
|
||||
rows="2"
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
import { Plus, Loader2, Link2Off } from 'lucide-svelte'
|
||||
import type { AppViewerContext } from './apps/types'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import path from 'path'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
|
||||
@@ -293,7 +293,9 @@
|
||||
{args}
|
||||
{noHistory}
|
||||
{saveToWorkspace}
|
||||
/>
|
||||
>
|
||||
<slot name="editor-bar-right" slot="right" />
|
||||
</EditorBar>
|
||||
{#if !noSyncFromGithub && customUi?.editorBar?.useVsCode != false}
|
||||
<div class="py-1">
|
||||
<Button
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { workerTags } from '$lib/stores'
|
||||
import AssignableTags from './AssignableTags.svelte'
|
||||
import { WorkerService } from '$lib/gen'
|
||||
import WorkerTagSelect from './WorkerTagSelect.svelte'
|
||||
|
||||
export let tag: string | undefined
|
||||
export let popupPlacement: 'bottom-end' | 'top-end' = 'bottom-end'
|
||||
@@ -20,28 +21,7 @@
|
||||
<div class="max-w-sm grow">
|
||||
{#if $workerTags}
|
||||
{#if $workerTags?.length ?? 0 > 0}
|
||||
<select
|
||||
value={tag}
|
||||
on:change={(e) => {
|
||||
if (e.target?.['value'] == '') {
|
||||
tag = undefined
|
||||
} else if (e?.target?.['value']) {
|
||||
tag = e.target?.['value']
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if tag}
|
||||
<option value="">reset to default</option>
|
||||
{:else}
|
||||
<option value="" disabled selected>Worker Group Tag</option>
|
||||
{/if}
|
||||
{#if tag && tag != '' && !($workerTags ?? []).includes(tag)}
|
||||
<option value={tag} selected>{tag}</option>
|
||||
{/if}
|
||||
{#each $workerTags ?? [] as tag (tag)}
|
||||
<option value={tag}>{tag}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<WorkerTagSelect noLabel bind:tag />
|
||||
{:else}
|
||||
<div class="text-sm text-secondary flex flex-row gap-2">
|
||||
No custom worker group tag defined on this instance in "Workers {'->'} Custom Tags"
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
import { workerTags } from '$lib/stores'
|
||||
import { WorkerService } from '$lib/gen'
|
||||
|
||||
export let tag: string | undefined
|
||||
export let noLabel: boolean = false
|
||||
export let nullTag: string | undefined = undefined
|
||||
|
||||
loadWorkerGroups()
|
||||
|
||||
async function loadWorkerGroups() {
|
||||
if (!$workerTags) {
|
||||
$workerTags = await WorkerService.getCustomTags()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-1 items-center">
|
||||
{#if !noLabel}
|
||||
<div class="text-tertiary text-2xs">tag</div>
|
||||
{/if}
|
||||
<select
|
||||
class="min-w-32"
|
||||
placeholder="Tag"
|
||||
bind:value={tag}
|
||||
on:change={(e) => {
|
||||
if (tag == '') {
|
||||
tag = undefined
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if tag}
|
||||
<option value="">reset to default</option>
|
||||
{:else}
|
||||
<option value={undefined} disabled selected>{nullTag ? `default: ${nullTag}` : ''}</option>
|
||||
{/if}
|
||||
{#if tag && tag != '' && !($workerTags ?? []).includes(tag)}
|
||||
<option value={tag} selected>{tag}</option>
|
||||
{/if}
|
||||
{#each $workerTags ?? [] as tag (tag)}
|
||||
<option value={tag}>{tag}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
@@ -292,7 +292,7 @@
|
||||
{/if}
|
||||
</Scrollable>
|
||||
{/if}
|
||||
<Scrollable id="flow-editor-flow-atom" bind:this={scrollable} scrollableClass="grow min-w-0">
|
||||
<Scrollable id="flow-editor-flow-atoms" bind:this={scrollable} scrollableClass="grow min-w-0">
|
||||
{#if kind == 'script'}
|
||||
{#each topLevelNodes as [label, kind], i (label)}
|
||||
<FlowToplevelNode
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
export let noEditor: boolean
|
||||
export let enableAi: boolean
|
||||
|
||||
let tag: string | undefined = undefined
|
||||
|
||||
let editor: Editor
|
||||
let diffEditor: DiffEditor
|
||||
let modulePreview: ModulePreview
|
||||
@@ -229,6 +231,7 @@
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<FlowModuleHeader
|
||||
{tag}
|
||||
bind:module={flowModule}
|
||||
on:toggleSuspend={() => selectAdvanced('suspend')}
|
||||
on:toggleSleep={() => selectAdvanced('sleep')}
|
||||
@@ -349,6 +352,7 @@
|
||||
<div class="border-t">
|
||||
{#key forceReload}
|
||||
<FlowModuleScript
|
||||
bind:tag
|
||||
showAllCode={false}
|
||||
path={flowModule.value.path}
|
||||
hash={flowModule.value.hash}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
import FlowModuleWorkerTagSelect from './FlowModuleWorkerTagSelect.svelte'
|
||||
|
||||
export let module: FlowModule
|
||||
export let tag: string | undefined
|
||||
const { scriptEditorDrawer } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -125,7 +126,7 @@
|
||||
Edit {#if module.value.hash != undefined} (locked hash){/if}
|
||||
</Button>
|
||||
{/if}
|
||||
<FlowModuleWorkerTagSelect bind:tag={module.value.tag_override} />
|
||||
<FlowModuleWorkerTagSelect nullTag={tag} bind:tag={module.value.tag_override} />
|
||||
{#if customUi?.scriptFork != false}
|
||||
<Button
|
||||
size="xs"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
export let previousHash: string | undefined = undefined
|
||||
export let showDate = false
|
||||
export let showAllCode: boolean = true
|
||||
export let tag: string | undefined = undefined
|
||||
|
||||
let code: string
|
||||
let previousCode: string
|
||||
@@ -31,6 +32,7 @@
|
||||
language = script.language
|
||||
lock = script.lock
|
||||
date = script.created_at
|
||||
tag = script.tag
|
||||
} catch (e) {
|
||||
notFound = true
|
||||
console.error(e)
|
||||
@@ -63,6 +65,9 @@
|
||||
{#if showDate && date}
|
||||
<span class="text-xs text-tertiary mb-4"><TimeAgo agoOnlyIfRecent {date} /></span>
|
||||
{/if}
|
||||
{#if tag}
|
||||
<div class="text-xs text-tertiary mb-4">tag: {tag}</div>
|
||||
{/if}
|
||||
{#if notFound}
|
||||
<div class="text-red-400">script not found at {path} in workspace {$workspaceStore}</div>
|
||||
{:else if showAllCode}
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import { workerTags } from '$lib/stores'
|
||||
import { WorkerService } from '$lib/gen'
|
||||
import WorkerTagSelect from '$lib/components/WorkerTagSelect.svelte'
|
||||
|
||||
export let tag: string | undefined
|
||||
export let nullTag: string | undefined = undefined
|
||||
|
||||
const { flowStore, selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
@@ -21,28 +23,11 @@
|
||||
{#if $workerTags?.length > 0}
|
||||
<div class="w-40">
|
||||
{#if $flowStore.tag == undefined}
|
||||
<select
|
||||
placeholder="Tag"
|
||||
bind:value={tag}
|
||||
on:change={(e) => {
|
||||
if (tag == '') {
|
||||
tag = undefined
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if tag}
|
||||
<option value="">reset to default</option>
|
||||
{:else}
|
||||
<option value={undefined} disabled selected>Tag</option>
|
||||
{/if}
|
||||
{#each $workerTags ?? [] as tag (tag)}
|
||||
<option value={tag}>{tag}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<WorkerTagSelect {nullTag} bind:tag />
|
||||
{:else}
|
||||
<button
|
||||
title="Worker Group is defined at the flow level"
|
||||
class="w-full text-left items-center font-normal p-1 border text-xs rounded"
|
||||
class="w-full text-left items-center font-normal p-1 py-2 border text-xs rounded"
|
||||
on:click={() => ($selectedId = 'settings-worker-group')}
|
||||
>
|
||||
Flow's WG: {$flowStore.tag}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
} from '$lib/utils'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import WorkerTagSelect from '$lib/components/WorkerTagSelect.svelte'
|
||||
|
||||
let scriptEditorDrawer: Drawer
|
||||
|
||||
@@ -41,6 +42,7 @@
|
||||
language: Preview['language']
|
||||
content: string
|
||||
schema?: any
|
||||
tag?: string
|
||||
kind: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | undefined
|
||||
envs?: string[]
|
||||
ws_error_handler_muted?: boolean
|
||||
@@ -60,6 +62,7 @@
|
||||
language: Preview['language']
|
||||
content: string
|
||||
schema?: any
|
||||
tag?: string
|
||||
kind: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | undefined
|
||||
envs?: string[]
|
||||
ws_error_handler_muted?: boolean
|
||||
@@ -90,6 +93,7 @@
|
||||
description: script.description ?? '',
|
||||
parent_hash: script.hash != '' ? script.hash : undefined,
|
||||
is_template: false,
|
||||
tag: script.tag,
|
||||
kind: script.kind as Script['kind'] | undefined,
|
||||
lock: undefined
|
||||
}
|
||||
@@ -199,11 +203,15 @@
|
||||
noSyncFromGithub
|
||||
lang={script.language}
|
||||
path={script.path}
|
||||
tag={script.tag}
|
||||
fixedOverflowWidgets={false}
|
||||
bind:code={script.content}
|
||||
bind:schema={script.schema}
|
||||
tag={undefined}
|
||||
/>
|
||||
>
|
||||
<div slot="editor-bar-right">
|
||||
<WorkerTagSelect bind:tag={script.tag} />
|
||||
</div>
|
||||
</ScriptEditor>
|
||||
{/key}
|
||||
{:else}
|
||||
<div
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
|
||||
<div
|
||||
id="flow-editor-error-handler"
|
||||
class={classNames(
|
||||
'z-10',
|
||||
$copilotCurrentStepStore !== undefined ? 'border-gray-500/75' : 'cursor-pointer',
|
||||
|
||||
@@ -73,7 +73,7 @@ shouldUsePortal={true} -->
|
||||
'w-5 h-5 flex items-center justify-center',
|
||||
'outline-[1px] outline dark:outline-gray-500 outline-gray-300',
|
||||
'text-secondary',
|
||||
'bg-surface focus:outline-none hover:bg-surface-hover rounded '
|
||||
'bg-surface focus:outline-none hover:bg-surface-hover rounded'
|
||||
)}
|
||||
on:pointerdown|preventDefault|stopPropagation={pointerdown}
|
||||
on:pointerup={pointerup}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
</script>
|
||||
|
||||
<button
|
||||
id={`flow-editor-flow-kind-${label.replaceAll(' ', '-').toLowerCase()}`}
|
||||
class={twMerge(
|
||||
'w-full text-left py-2 px-1.5 hover:bg-surface-hover text-xs font-medium transition-all whitespace-nowrap flex flex-row gap-2 items-center rounded-md',
|
||||
selected ? 'bg-surface-hover' : '',
|
||||
|
||||
@@ -21,18 +21,26 @@ export interface Setting {
|
||||
| 'slack_connect'
|
||||
| 'smtp_connect'
|
||||
storage: SettingStorage
|
||||
advancedToggle?: {
|
||||
label: string
|
||||
onChange: (values: Record<string, any>) => Record<string, any>
|
||||
checked: (values: Record<string, any>) => boolean
|
||||
}
|
||||
hiddenIfNull?: boolean
|
||||
requiresReloadOnChange?: boolean
|
||||
isValid?: (value: any) => boolean
|
||||
error?: string
|
||||
defaultValue?: () => any
|
||||
}
|
||||
|
||||
export type SettingStorage = 'setting'
|
||||
export type SettingStorage = 'setting'
|
||||
|
||||
export const settings: Record<string, Setting[]> = {
|
||||
Core: [
|
||||
{
|
||||
label: 'Base url',
|
||||
description: 'Public base url of the instance. <a href="https://www.windmill.dev/docs/advanced/instance_settings#global-users">Learn more</a>',
|
||||
description:
|
||||
'Public base url of the instance. <a href="https://www.windmill.dev/docs/advanced/instance_settings#global-users">Learn more</a>',
|
||||
key: 'base_url',
|
||||
fieldType: 'text',
|
||||
placeholder: 'https://windmill.com',
|
||||
@@ -67,14 +75,16 @@ export const settings: Record<string, Setting[]> = {
|
||||
{
|
||||
label: 'Default timeout',
|
||||
key: 'job_default_timeout',
|
||||
description: 'Default timeout for individual jobs. <a href="https://www.windmill.dev/docs/core_concepts/jobs#retention-policy">Learn more</a>',
|
||||
description:
|
||||
'Default timeout for individual jobs. <a href="https://www.windmill.dev/docs/core_concepts/jobs#retention-policy">Learn more</a>',
|
||||
fieldType: 'seconds',
|
||||
storage: 'setting',
|
||||
cloudonly: false
|
||||
},
|
||||
{
|
||||
label: 'Max timeout for sync endpoints',
|
||||
description: 'Maximum amount of time (measured in seconds) that a <a href="https://www.windmill.dev/docs/core_concepts/webhooks">sync endpoint</a> is allowed to run before it is forcibly stopped or timed out.',
|
||||
description:
|
||||
'Maximum amount of time (measured in seconds) that a <a href="https://www.windmill.dev/docs/core_concepts/webhooks">sync endpoint</a> is allowed to run before it is forcibly stopped or timed out.',
|
||||
key: 'timeout_wait_result',
|
||||
cloudonly: true,
|
||||
fieldType: 'seconds',
|
||||
@@ -83,7 +93,8 @@ export const settings: Record<string, Setting[]> = {
|
||||
},
|
||||
{
|
||||
label: 'License key',
|
||||
description: 'License key required to use the EE (switch image for windmill-ee). <a href="https://www.windmill.dev/docs/advanced/instance_settings#license-key">Learn more</a>',
|
||||
description:
|
||||
'License key required to use the EE (switch image for windmill-ee). <a href="https://www.windmill.dev/docs/advanced/instance_settings#license-key">Learn more</a>',
|
||||
key: 'license_key',
|
||||
fieldType: 'license_key',
|
||||
placeholder: 'only needed to prepare upgrade to EE',
|
||||
@@ -91,7 +102,8 @@ export const settings: Record<string, Setting[]> = {
|
||||
},
|
||||
{
|
||||
label: 'Non-prod instance',
|
||||
description: 'Whether we should consider the reported usage of this instance as non-prod. <a href="https://www.windmill.dev/docs/advanced/instance_settings#non-prod-instance">Learn more</a>',
|
||||
description:
|
||||
'Whether we should consider the reported usage of this instance as non-prod. <a href="https://www.windmill.dev/docs/advanced/instance_settings#non-prod-instance">Learn more</a>',
|
||||
key: 'dev_instance',
|
||||
fieldType: 'boolean',
|
||||
storage: 'setting'
|
||||
@@ -99,7 +111,8 @@ export const settings: Record<string, Setting[]> = {
|
||||
{
|
||||
label: 'Retention period in secs',
|
||||
key: 'retention_period_secs',
|
||||
description: 'How long to keep the jobs data in the database (max 30 days on CE). <a href="https://www.windmill.dev/docs/advanced/instance_settings#retention-period-in-secs">Learn more</a>',
|
||||
description:
|
||||
'How long to keep the jobs data in the database (max 30 days on CE). <a href="https://www.windmill.dev/docs/advanced/instance_settings#retention-period-in-secs">Learn more</a>',
|
||||
fieldType: 'seconds',
|
||||
placeholder: '30',
|
||||
storage: 'setting',
|
||||
@@ -108,7 +121,8 @@ export const settings: Record<string, Setting[]> = {
|
||||
},
|
||||
{
|
||||
label: 'Expose metrics',
|
||||
description: 'Expose Prometheus metrics for workers and servers on port 8001 at /metrics. <a href="https://www.windmill.dev/docs/advanced/instance_settings#expose-metrics">Learn more</a>',
|
||||
description:
|
||||
'Expose Prometheus metrics for workers and servers on port 8001 at /metrics. <a href="https://www.windmill.dev/docs/advanced/instance_settings#expose-metrics">Learn more</a>',
|
||||
key: 'expose_metrics',
|
||||
fieldType: 'boolean',
|
||||
storage: 'setting',
|
||||
@@ -116,7 +130,8 @@ export const settings: Record<string, Setting[]> = {
|
||||
},
|
||||
{
|
||||
label: 'Instance object storage',
|
||||
description: ' S3/Azure bucket to store large logs and global cache for Python and Go. <a href="https://www.windmill.dev/docs/core_concepts/object_storage_in_windmill#instance-object-storage">Learn more</a>',
|
||||
description:
|
||||
' S3/Azure bucket to store large logs and global cache for Python and Go. <a href="https://www.windmill.dev/docs/core_concepts/object_storage_in_windmill#instance-object-storage">Learn more</a>',
|
||||
key: 'object_store_cache_config',
|
||||
fieldType: 'object_store_config',
|
||||
storage: 'setting',
|
||||
@@ -124,7 +139,8 @@ export const settings: Record<string, Setting[]> = {
|
||||
},
|
||||
{
|
||||
label: 'Critical alert channels',
|
||||
description: 'Channels to send critical alerts to. SMTP must be configured for the email channel. A Slack workspace must be connected to the instance for the Slack channel. <a href="https://www.windmill.dev/docs/core_concepts/critical_alert_channels">Learn more</a>',
|
||||
description:
|
||||
'Channels to send critical alerts to. SMTP must be configured for the email channel. A Slack workspace must be connected to the instance for the Slack channel. <a href="https://www.windmill.dev/docs/core_concepts/critical_alert_channels">Learn more</a>',
|
||||
key: 'critical_error_channels',
|
||||
fieldType: 'critical_error_channels',
|
||||
storage: 'setting',
|
||||
@@ -132,7 +148,8 @@ export const settings: Record<string, Setting[]> = {
|
||||
},
|
||||
{
|
||||
label: 'Azure OpenAI base path',
|
||||
description: 'All Windmill AI features will run on the specified deployed model. Format: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}. <a href="https://www.windmill.dev/docs/core_concepts/ai_generation#azure-openai-advanced-models">Learn more</a>',
|
||||
description:
|
||||
'All Windmill AI features will run on the specified deployed model. Format: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}. <a href="https://www.windmill.dev/docs/core_concepts/ai_generation#azure-openai-advanced-models">Learn more</a>',
|
||||
key: 'openai_azure_base_path',
|
||||
fieldType: 'text',
|
||||
storage: 'setting',
|
||||
@@ -140,12 +157,38 @@ export const settings: Record<string, Setting[]> = {
|
||||
},
|
||||
{
|
||||
label: 'Private Hub base url',
|
||||
description: 'Base url of your private Hub instance, without trailing slash. <a href="https://www.windmill.dev/docs/core_concepts/private_hub">Learn more</a>',
|
||||
description:
|
||||
'Base URL of your private Hub instance, without trailing slash. <a href="https://www.windmill.dev/docs/core_concepts/private_hub">Learn more</a>',
|
||||
placeholder: 'https://hub.company.com',
|
||||
key: 'hub_base_url',
|
||||
fieldType: 'text',
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
ee_only: '',
|
||||
advancedToggle: {
|
||||
label: 'I have a different URL for Hub access from end-user browsers',
|
||||
onChange(values) {
|
||||
if (values['hub_accessible_url']) {
|
||||
values['hub_accessible_url'] = null
|
||||
} else {
|
||||
values['hub_accessible_url'] = values['hub_base_url'] || 'https://hub.company.com'
|
||||
}
|
||||
return values
|
||||
},
|
||||
checked: (values) => values['hub_accessible_url'] != null
|
||||
},
|
||||
requiresReloadOnChange: true
|
||||
},
|
||||
{
|
||||
label: 'Private Hub accessible url',
|
||||
description:
|
||||
'Base URL accessible from end-user browsers, without trailing slash. <a href="https://www.windmill.dev/docs/core_concepts/private_hub">Learn more</a>',
|
||||
key: 'hub_accessible_url',
|
||||
fieldType: 'text',
|
||||
hiddenIfNull: true,
|
||||
|
||||
storage: 'setting',
|
||||
ee_only: '',
|
||||
requiresReloadOnChange: true
|
||||
}
|
||||
],
|
||||
'SSO/OAuth': [],
|
||||
@@ -189,12 +232,12 @@ export const settings: Record<string, Setting[]> = {
|
||||
}
|
||||
],
|
||||
SMTP: [
|
||||
{
|
||||
label: 'SMTP',
|
||||
key: 'smtp_settings',
|
||||
fieldType: 'smtp_connect',
|
||||
storage: 'setting',
|
||||
ee_only: '',
|
||||
{
|
||||
label: 'SMTP',
|
||||
key: 'smtp_settings',
|
||||
fieldType: 'smtp_connect',
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
}
|
||||
],
|
||||
Slack: [
|
||||
|
||||
+5
-8
@@ -1,12 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from '../flows/types'
|
||||
import { triggerAddFlowStep, selectFlowStepKind } from './utils'
|
||||
import { triggerPointerDown } from './utils'
|
||||
import Tutorial from './Tutorial.svelte'
|
||||
import { updateProgress } from '$lib/tutorialUtils'
|
||||
import { nextId } from '../flows/flowModuleNextId'
|
||||
|
||||
const { flowStore, flowStateStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let tutorial: Tutorial | undefined = undefined
|
||||
|
||||
@@ -22,9 +21,7 @@
|
||||
on:error
|
||||
on:skipAll
|
||||
getSteps={(driver, options) => {
|
||||
const id = nextId($flowStateStore, $flowStore)
|
||||
const index = options?.indexToInsertAt ?? $flowStore.value.modules.length
|
||||
const isFirst = id === 'a'
|
||||
|
||||
const steps = [
|
||||
{
|
||||
@@ -40,7 +37,7 @@
|
||||
title: 'Add a step',
|
||||
description: 'Click here to add a step to your flow',
|
||||
onNextClick: () => {
|
||||
triggerAddFlowStep(index)
|
||||
triggerPointerDown(`#flow-editor-add-step-${index}`)
|
||||
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
@@ -62,14 +59,14 @@
|
||||
title: 'Insert Branch all',
|
||||
description: "Let's pick branch all",
|
||||
onNextClick: () => {
|
||||
selectFlowStepKind(isFirst ? 6 : 5)
|
||||
triggerPointerDown('#flow-editor-flow-kind-branch-to-all')
|
||||
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
})
|
||||
}
|
||||
},
|
||||
element: `#flow-editor-insert-module > div > button:nth-child(${isFirst ? 6 : 5})`
|
||||
element: '#flow-editor-flow-kind-branch-to-all'
|
||||
},
|
||||
{
|
||||
element: '#flow-editor-branch-all-wrapper',
|
||||
+6
-16
@@ -1,12 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from '../flows/types'
|
||||
import {
|
||||
clickButtonBySelector,
|
||||
triggerAddFlowStep,
|
||||
selectFlowStepKind,
|
||||
updateFlowModuleById
|
||||
} from './utils'
|
||||
import { clickButtonBySelector, updateFlowModuleById, triggerPointerDown } from './utils'
|
||||
import Tutorial from './Tutorial.svelte'
|
||||
import { updateProgress } from '$lib/tutorialUtils'
|
||||
import { nextId } from '../flows/flowModuleNextId'
|
||||
@@ -30,7 +25,6 @@
|
||||
getSteps={(driver, options) => {
|
||||
const id = nextId($flowStateStore, $flowStore)
|
||||
const index = options?.indexToInsertAt ?? $flowStore.value.modules.length
|
||||
const isFirst = id === 'a'
|
||||
|
||||
const steps = [
|
||||
{
|
||||
@@ -46,7 +40,7 @@
|
||||
title: 'Branch one',
|
||||
description: 'Windmill supports branches, let us add one',
|
||||
onNextClick: () => {
|
||||
triggerAddFlowStep(index)
|
||||
triggerPointerDown(`#flow-editor-add-step-${index}`)
|
||||
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
@@ -66,16 +60,15 @@
|
||||
title: 'Insert Branch one',
|
||||
description: "Let's pick branch one",
|
||||
onNextClick: () => {
|
||||
selectFlowStepKind(isFirst ? 5 : 4)
|
||||
triggerPointerDown('#flow-editor-flow-kind-branch-to-one')
|
||||
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
})
|
||||
}
|
||||
},
|
||||
element: `#flow-editor-insert-module > div > button:nth-child(${isFirst ? 5 : 4})`
|
||||
element: '#flow-editor-flow-kind-branch-to-one'
|
||||
},
|
||||
|
||||
{
|
||||
element: '#flow-editor-edit-predicate',
|
||||
popover: {
|
||||
@@ -96,7 +89,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
element: '#flow-editor-branch-one-wrapper',
|
||||
popover: {
|
||||
@@ -111,16 +103,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
popover: {
|
||||
title: 'Add steps',
|
||||
description: 'You can now add step to one of the branches',
|
||||
description: 'You can now add a step to one of the branches',
|
||||
onNextClick: () => {
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
|
||||
updateProgress(2)
|
||||
driver.moveNext()
|
||||
})
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -2,7 +2,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from '../flows/types'
|
||||
import Tutorial from './Tutorial.svelte'
|
||||
import { clickButtonBySelector } from './utils'
|
||||
import { clickButtonBySelector, triggerPointerDown } from './utils'
|
||||
import { updateProgress } from '$lib/tutorialUtils'
|
||||
|
||||
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
@@ -51,14 +51,14 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#error-handler-toggle',
|
||||
element: '#flow-editor-error-handler',
|
||||
popover: {
|
||||
title: 'Error handler',
|
||||
description:
|
||||
'You can add an error handler to your flow. It will be executed if any of the steps in the flow fails.',
|
||||
|
||||
onNextClick: () => {
|
||||
clickButtonBySelector('#error-handler-toggle')
|
||||
triggerPointerDown('#flow-editor-error-handler button')
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
})
|
||||
@@ -73,26 +73,26 @@
|
||||
element: '#flow-editor-insert-module'
|
||||
},
|
||||
{
|
||||
element: '#flow-editor-flow-inputs',
|
||||
element: '#flow-editor-flow-providers',
|
||||
popover: {
|
||||
title: 'Action configuration',
|
||||
description: 'An action can be inlined, imported from your workspace or the Hub.'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#flow-editor-action-script',
|
||||
element: '#flow-editor-flow-atoms',
|
||||
popover: {
|
||||
title: 'Supported languages',
|
||||
description: 'Windmill support the following languages/runtimes.'
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#flow-editor-action-script > button:nth-child(1)',
|
||||
element: '#flow-editor-new-bun',
|
||||
popover: {
|
||||
title: 'Typescript',
|
||||
description: "Let's pick an action to add to your flow",
|
||||
description: "Let's create a Typescript error handler for your flow",
|
||||
onNextClick: () => {
|
||||
clickButtonBySelector('#flow-editor-action-script > button > div > button:nth-child(1)')
|
||||
clickButtonBySelector('#flow-editor-new-bun')
|
||||
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
+6
-12
@@ -2,12 +2,7 @@
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from '../flows/types'
|
||||
import { emptyFlowModuleState } from '../flows/utils'
|
||||
import {
|
||||
clickButtonBySelector,
|
||||
triggerAddFlowStep,
|
||||
selectFlowStepKind,
|
||||
updateFlowModuleById
|
||||
} from './utils'
|
||||
import { clickButtonBySelector, updateFlowModuleById, triggerPointerDown } from './utils'
|
||||
import Tutorial from './Tutorial.svelte'
|
||||
import { updateProgress } from '$lib/tutorialUtils'
|
||||
import { nextId } from '../flows/flowModuleNextId'
|
||||
@@ -33,7 +28,6 @@
|
||||
getSteps={(driver, options) => {
|
||||
const id = nextId($flowStateStore, $flowStore)
|
||||
const index = options?.indexToInsertAt ?? $flowStore.value.modules.length
|
||||
const isFirst = id === 'a'
|
||||
|
||||
let tempId = ''
|
||||
|
||||
@@ -52,7 +46,7 @@
|
||||
title: 'Add a step',
|
||||
description: 'Click here to add a step to your flow',
|
||||
onNextClick: () => {
|
||||
triggerAddFlowStep(index)
|
||||
triggerPointerDown(`#flow-editor-add-step-${index}`)
|
||||
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
@@ -71,16 +65,16 @@
|
||||
|
||||
{
|
||||
popover: {
|
||||
title: 'Insert loop',
|
||||
description: "Let's pick forloop",
|
||||
title: 'Insert a loop',
|
||||
description: "Let's pick a for loop",
|
||||
onNextClick: () => {
|
||||
selectFlowStepKind(isFirst ? 4 : 3)
|
||||
triggerPointerDown('#flow-editor-flow-kind-for-loop')
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
})
|
||||
}
|
||||
},
|
||||
element: `#flow-editor-insert-module > div > button:nth-child(${isFirst ? 4 : 3})`
|
||||
element: '#flow-editor-flow-kind-for-loop'
|
||||
},
|
||||
|
||||
{
|
||||
@@ -5,9 +5,8 @@
|
||||
import {
|
||||
clickButtonBySelector,
|
||||
isFlowTainted,
|
||||
selectFlowStepKind,
|
||||
setInputBySelector,
|
||||
triggerAddFlowStep,
|
||||
triggerPointerDown,
|
||||
waitForElementLoading
|
||||
} from './utils'
|
||||
import Tutorial from './Tutorial.svelte'
|
||||
@@ -82,7 +81,7 @@
|
||||
title: 'Add a step',
|
||||
description: 'Click here to add a step to your flow',
|
||||
onNextClick: () => {
|
||||
triggerAddFlowStep(0)
|
||||
triggerPointerDown(`#flow-editor-add-step-0`)
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
})
|
||||
@@ -101,7 +100,7 @@
|
||||
title: 'Pick an action',
|
||||
description: 'Let’s pick an action to add to your flow',
|
||||
onNextClick: () => {
|
||||
selectFlowStepKind(1)
|
||||
triggerPointerDown('#flow-editor-insert-module > div > div > button:nth-child(1)')
|
||||
|
||||
setTimeout(() => {
|
||||
driver.moveNext()
|
||||
@@ -118,7 +117,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
element: '#flow-editor-flow-atom',
|
||||
element: '#flow-editor-flow-atoms',
|
||||
popover: {
|
||||
title: 'Supported languages',
|
||||
description: 'Windmill support the following languages/runtimes.'
|
||||
@@ -143,7 +142,7 @@
|
||||
title: 'Action editor',
|
||||
description: 'Windmill provides a full code editor to write your actions',
|
||||
onPrevClick: () => {
|
||||
triggerAddFlowStep(0)
|
||||
triggerPointerDown(`#flow-editor-add-step-0`)
|
||||
setTimeout(() => {
|
||||
driver.movePrevious()
|
||||
})
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
</Alert>
|
||||
{/if}
|
||||
<div class="flex flex-row gap-2 justify-between w-full items-center">
|
||||
<div class="text-xs">
|
||||
Step {activeIndex} of {totalSteps}
|
||||
</div>
|
||||
|
||||
{#if activeIndex !== undefined && totalSteps !== undefined}
|
||||
<div class="text-xs">
|
||||
Step {activeIndex + 1} of {totalSteps}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-row gap-2">
|
||||
<Button
|
||||
size="xs2"
|
||||
|
||||
@@ -29,21 +29,11 @@ export function clickFirstButtonBySelector(selector: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function triggerAddFlowStep(index: number) {
|
||||
const button = document.querySelector(`#flow-editor-add-step-${index}`) as HTMLButtonElement
|
||||
export function triggerPointerDown(selector: string) {
|
||||
const elem = document.querySelector(selector) as HTMLElement
|
||||
|
||||
if (button) {
|
||||
button.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
||||
}
|
||||
}
|
||||
|
||||
export function selectFlowStepKind(index: number) {
|
||||
const button = document.querySelector(
|
||||
`#flow-editor-insert-module > div > div > button:nth-child(${index})`
|
||||
) as HTMLButtonElement
|
||||
|
||||
if (button) {
|
||||
button?.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
||||
if (elem) {
|
||||
elem.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,8 @@
|
||||
|
||||
async function loadHubBaseUrl() {
|
||||
$hubBaseUrlStore =
|
||||
((await SettingService.getGlobal({ key: 'hub_base_url' })) as string) ??
|
||||
((await SettingService.getGlobal({ key: 'hub_accessible_url' })) as string) ||
|
||||
((await SettingService.getGlobal({ key: 'hub_base_url' })) as string) ||
|
||||
'https://hub.windmill.dev'
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.415.1"
|
||||
wmill_pg = ">=1.415.1"
|
||||
wmill = ">=1.416.2"
|
||||
wmill_pg = ">=1.416.2"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.415.1
|
||||
version: 1.416.2
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'WindmillClient.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.415.1'
|
||||
ModuleVersion = '1.416.2'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.415.1"
|
||||
version = "1.416.2"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@windmill/windmill",
|
||||
"version": "1.415.1",
|
||||
"version": "1.416.2",
|
||||
"exports": "./src/index.ts",
|
||||
"publish": {
|
||||
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.415.1",
|
||||
"version": "1.416.2",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.415.1
|
||||
1.416.2
|
||||
|
||||
Reference in New Issue
Block a user