diff --git a/.github/workflows/build_windows_worker_.yml b/.github/workflows/build_windows_worker_.yml new file mode 100644 index 0000000000..256ce0de1a --- /dev/null +++ b/.github/workflows/build_windows_worker_.yml @@ -0,0 +1,58 @@ +name: Build windows executable for this branch + +on: + workflow_dispatch: + +env: + CARGO_INCREMENTAL: 0 + SQLX_OFFLINE: true + DISABLE_EMBEDDING: true + RUST_LOG: info + +jobs: + cargo_build_windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Read EE repo commit hash + shell: pwsh + run: | + $ee_repo_ref = Get-Content .\backend\ee-repo-ref.txt + echo "ee_repo_ref=$ee_repo_ref" | Out-File -FilePath $env:GITHUB_ENV -Append + + - name: Checkout windmill-ee-private repository + uses: actions/checkout@v4 + with: + repository: windmill-labs/windmill-ee-private + path: ./windmill-ee-private + ref: ${{ env.ee_repo_ref }} + token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} + fetch-depth: 0 + + - name: Substitute EE code + shell: bash + run: | + ./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private + + - name: Cargo build windows + timeout-minutes: 90 + run: | + vcpkg.exe install openssl-windows:x64-windows + vcpkg.exe install openssl:x64-windows-static + vcpkg.exe integrate install + $env:VCPKGRS_DYNAMIC=1 + $env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static" + mkdir frontend/build && cd backend + New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core + + - name: Rename binary with corresponding architecture + run: | + Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe" + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: windmill-ee-binary + path: ./backend/target/release/windmill-ee.exe diff --git a/.github/workflows/build_windows_worker.yml b/.github/workflows/publish_windows_worker.yml similarity index 100% rename from .github/workflows/build_windows_worker.yml rename to .github/workflows/publish_windows_worker.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ffa8c6630..9dfc4d73b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,88 @@ # Changelog +## [1.423.2](https://github.com/windmill-labs/windmill/compare/v1.423.1...v1.423.2) (2024-11-13) + + +### Bug Fixes + +* fix intempestive expr type change in flow transform ([84bd06e](https://github.com/windmill-labs/windmill/commit/84bd06e90650e949c11e2b09447a7bad1ab60a95)) + +## [1.423.1](https://github.com/windmill-labs/windmill/compare/v1.423.0...v1.423.1) (2024-11-12) + + +### Bug Fixes + +* **autoscaling:** autoscaling thresholds to be >= and not > ([de00944](https://github.com/windmill-labs/windmill/commit/de00944f09699ff33a382f5a14f515ccf90b2454)) + +## [1.423.0](https://github.com/windmill-labs/windmill/compare/v1.422.1...v1.423.0) (2024-11-12) + + +### Features + +* s3 input available for public apps ([#4685](https://github.com/windmill-labs/windmill/issues/4685)) ([1671005](https://github.com/windmill-labs/windmill/commit/167100510032ab53cd609fb2c7629e67faceb093)) + + +### Bug Fixes + +* prevent underflow for autoscaling scalein ([5e9870a](https://github.com/windmill-labs/windmill/commit/5e9870a8a993032ec7d45c62e0023008da42c74a)) +* support multiple pip-extra-index-url with commas ([50861fc](https://github.com/windmill-labs/windmill/commit/50861fccc0cd86d4c76120c3306adf94f375330e)) + +## [1.422.1](https://github.com/windmill-labs/windmill/compare/v1.422.0...v1.422.1) (2024-11-12) + + +### Bug Fixes + +* fix password inputs ([e5e174a](https://github.com/windmill-labs/windmill/commit/e5e174ae9516f4c6b94ceb6e258b467f5c9a1f1a)) + +## [1.422.0](https://github.com/windmill-labs/windmill/compare/v1.421.2...v1.422.0) (2024-11-11) + + +### Features + +* expandable subflows in flows ([#4683](https://github.com/windmill-labs/windmill/issues/4683)) ([d44976f](https://github.com/windmill-labs/windmill/commit/d44976f35e45ade510d1ec220b5a1503e11f3db9)) +* **frontend:** critical alerts UI ([#4653](https://github.com/windmill-labs/windmill/issues/4653)) ([d9148ea](https://github.com/windmill-labs/windmill/commit/d9148eaa78680a93d81d71847a7df67e01f3c110)) + +## [1.421.2](https://github.com/windmill-labs/windmill/compare/v1.421.1...v1.421.2) (2024-11-08) + + +### Bug Fixes + +* **bash:** correctly propagate exit errors ([8bc9a02](https://github.com/windmill-labs/windmill/commit/8bc9a021a88391147c9170f56b5a0edddd55bc7d)) + +## [1.421.1](https://github.com/windmill-labs/windmill/compare/v1.421.0...v1.421.1) (2024-11-08) + + +### Bug Fixes + +* **python-client:** fix small break params of write_s3_file ([3ea12f1](https://github.com/windmill-labs/windmill/commit/3ea12f1821500e8b84549b892e3e1bb56a6ace4b)) + +## [1.421.0](https://github.com/windmill-labs/windmill/compare/v1.420.1...v1.421.0) (2024-11-07) + + +### Features + +* http custom routes for static assets ([#4666](https://github.com/windmill-labs/windmill/issues/4666)) ([dc8bd6d](https://github.com/windmill-labs/windmill/commit/dc8bd6d2b5d6f5cd8521f9034853175ec78d5639)) + + +### Bug Fixes + +* improve nested schema editor field change ([a3feca7](https://github.com/windmill-labs/windmill/commit/a3feca719799ea2bf08f2b49350e6b732a24abf4)) + +## [1.420.1](https://github.com/windmill-labs/windmill/compare/v1.420.0...v1.420.1) (2024-11-07) + + +### Bug Fixes + +* improve prop filtering on flow prop picker ([0501ad8](https://github.com/windmill-labs/windmill/commit/0501ad8a99f6f7d9fd26c996b754e8afe8f958b1)) + +## [1.420.0](https://github.com/windmill-labs/windmill/compare/v1.419.0...v1.420.0) (2024-11-07) + + +### Features + +* **frontend:** detect expr in flow input transform + filter right panel based on expr ([#4651](https://github.com/windmill-labs/windmill/issues/4651)) ([e9b7dca](https://github.com/windmill-labs/windmill/commit/e9b7dca20387e775fa50aaecd832890251582cf9)) +* **frontend:** nodes from flow can be connected directly in expr input through a plug icon ([#4652](https://github.com/windmill-labs/windmill/issues/4652)) ([ceaf56c](https://github.com/windmill-labs/windmill/commit/ceaf56c21ee2e548bc93859f9e0303e53b25b241)) + ## [1.419.0](https://github.com/windmill-labs/windmill/compare/v1.418.0...v1.419.0) (2024-11-06) diff --git a/README.md b/README.md index eb6c64ee2a..e85d34111b 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,8 @@ you to have it being synced automatically everyday. See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all running options. +Using [Nix](./frontend/README_DEV.md#nix). + ### only Frontend This will use the backend of but your own frontend with hot-code reloading. Note that you will need to use a username / password login due to CSRF checks using a different auth provider. diff --git a/backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json b/backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json deleted file mode 100644 index e4909fc4da..0000000000 --- a/backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO alerts (alert_type, message) VALUES ('recovered_critical_error', $1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9" -} diff --git a/backend/.sqlx/query-0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995.json b/backend/.sqlx/query-0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995.json new file mode 100644 index 0000000000..820e4f45ab --- /dev/null +++ b/backend/.sqlx/query-0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995.json @@ -0,0 +1,48 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, alert_type, message, created_at, acknowledged \n FROM alerts \n WHERE acknowledged = $1\n ORDER BY created_at DESC \n LIMIT $2 OFFSET $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "alert_type", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "acknowledged", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Bool", + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + true + ] + }, + "hash": "0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995" +} diff --git a/backend/.sqlx/query-fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183.json b/backend/.sqlx/query-11b698f82a54aac68b3617047dfe2b18dd6da7d962118fee276af354218baac2.json similarity index 84% rename from backend/.sqlx/query-fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183.json rename to backend/.sqlx/query-11b698f82a54aac68b3617047dfe2b18dd6da7d962118fee276af354218baac2.json index 65cb5c6633..9900461761 100644 --- a/backend/.sqlx/query-fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183.json +++ b/backend/.sqlx/query-11b698f82a54aac68b3617047dfe2b18dd6da7d962118fee276af354218baac2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\" FROM http_trigger", + "query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\", static_asset_config as \"static_asset_config: _\" FROM http_trigger", "describe": { "columns": [ { @@ -65,6 +65,11 @@ } } } + }, + { + "ordinal": 10, + "name": "static_asset_config: _", + "type_info": "Jsonb" } ], "parameters": { @@ -80,8 +85,9 @@ false, false, false, - false + false, + true ] }, - "hash": "fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183" + "hash": "11b698f82a54aac68b3617047dfe2b18dd6da7d962118fee276af354218baac2" } diff --git a/backend/.sqlx/query-31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9.json b/backend/.sqlx/query-31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9.json new file mode 100644 index 0000000000..1295a4b36f --- /dev/null +++ b/backend/.sqlx/query-31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM completed_job\n WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9" +} diff --git a/backend/.sqlx/query-c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8.json b/backend/.sqlx/query-333b484ffa030dee08e7b1161fcbc48af411377d2d9f58f92fc9d5eacdf0fba1.json similarity index 76% rename from backend/.sqlx/query-c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8.json rename to backend/.sqlx/query-333b484ffa030dee08e7b1161fcbc48af411377d2d9f58f92fc9d5eacdf0fba1.json index df94d893a6..bb21c2a9db 100644 --- a/backend/.sqlx/query-c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8.json +++ b/backend/.sqlx/query-333b484ffa030dee08e7b1161fcbc48af411377d2d9f58f92fc9d5eacdf0fba1.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now())", + "query": "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, static_asset_config, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now())", "describe": { "columns": [], "parameters": { @@ -27,11 +27,12 @@ } } }, + "Jsonb", "Varchar", "Varchar" ] }, "nullable": [] }, - "hash": "c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8" + "hash": "333b484ffa030dee08e7b1161fcbc48af411377d2d9f58f92fc9d5eacdf0fba1" } diff --git a/backend/.sqlx/query-3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e.json b/backend/.sqlx/query-3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e.json new file mode 100644 index 0000000000..5a79a3d793 --- /dev/null +++ b/backend/.sqlx/query-3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('recovered_critical_error', $1, $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e" +} diff --git a/backend/.sqlx/query-4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea.json b/backend/.sqlx/query-487d377e2df67fc3ea39d183ba9f99d45828d7c8e0ff10c5d74c454472e0493c.json similarity index 72% rename from backend/.sqlx/query-4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea.json rename to backend/.sqlx/query-487d377e2df67fc3ea39d183ba9f99d45828d7c8e0ff10c5d74c454472e0493c.json index b66edc6d73..7cd37b4c50 100644 --- a/backend/.sqlx/query-4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea.json +++ b/backend/.sqlx/query-487d377e2df67fc3ea39d183ba9f99d45828d7c8e0ff10c5d74c454472e0493c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE http_trigger \n SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, edited_by = $7, email = $8, is_async = $9, requires_auth = $10, edited_at = now() \n WHERE workspace_id = $11 AND path = $12", + "query": "UPDATE http_trigger \n SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, static_asset_config = $7, edited_by = $8, email = $9, is_async = $10, requires_auth = $11, edited_at = now() \n WHERE workspace_id = $12 AND path = $13", "describe": { "columns": [], "parameters": { @@ -24,6 +24,7 @@ } } }, + "Jsonb", "Varchar", "Varchar", "Bool", @@ -34,5 +35,5 @@ }, "nullable": [] }, - "hash": "4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea" + "hash": "487d377e2df67fc3ea39d183ba9f99d45828d7c8e0ff10c5d74c454472e0493c" } diff --git a/backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json b/backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json deleted file mode 100644 index 32295ea1e1..0000000000 --- a/backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO alerts (alert_type, message) VALUES ('critical_error', $1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f" -} diff --git a/backend/.sqlx/query-4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91.json b/backend/.sqlx/query-4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91.json new file mode 100644 index 0000000000..b57d834f87 --- /dev/null +++ b/backend/.sqlx/query-4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('critical_error', $1, $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91" +} diff --git a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json index 1fa370e682..75b8108281 100644 --- a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json +++ b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "bool", + "name": "?column?", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0.json b/backend/.sqlx/query-7113d7cc72e44e4b7e01b69cc18cbe7b0399cf8ec0e9e6d2b05ceef589c432df.json similarity index 68% rename from backend/.sqlx/query-55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0.json rename to backend/.sqlx/query-7113d7cc72e44e4b7e01b69cc18cbe7b0399cf8ec0e9e6d2b05ceef589c432df.json index ca0a922067..da3503cf35 100644 --- a/backend/.sqlx/query-55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0.json +++ b/backend/.sqlx/query-7113d7cc72e44e4b7e01b69cc18cbe7b0399cf8ec0e9e6d2b05ceef589c432df.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, edited_by = $5, email = $6, is_async = $7, requires_auth = $8, edited_at = now() \n WHERE workspace_id = $9 AND path = $10", + "query": "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, static_asset_config = $5, edited_by = $6, email = $7, is_async = $8, requires_auth = $9, edited_at = now() \n WHERE workspace_id = $10 AND path = $11", "describe": { "columns": [], "parameters": { @@ -22,6 +22,7 @@ } } }, + "Jsonb", "Varchar", "Varchar", "Bool", @@ -32,5 +33,5 @@ }, "nullable": [] }, - "hash": "55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0" + "hash": "7113d7cc72e44e4b7e01b69cc18cbe7b0399cf8ec0e9e6d2b05ceef589c432df" } diff --git a/backend/.sqlx/query-0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18.json b/backend/.sqlx/query-a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284.json similarity index 82% rename from backend/.sqlx/query-0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18.json rename to backend/.sqlx/query-a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284.json index a4c8e3ea0c..13f1319f04 100644 --- a/backend/.sqlx/query-0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18.json +++ b/backend/.sqlx/query-a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $11)\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14 FROM uuid_table) \n RETURNING id", + "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $11)\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout, raw_code, raw_lock, raw_flow, flow_status)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14, $15, $16, $17, $18 FROM uuid_table) \n RETURNING id", "describe": { "columns": [ { @@ -72,12 +72,16 @@ "Int4", "Int4", "Int4", - "Int4" + "Int4", + "Text", + "Text", + "Jsonb", + "Jsonb" ] }, "nullable": [ false ] }, - "hash": "0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18" + "hash": "a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284" } diff --git a/backend/.sqlx/query-a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d.json b/backend/.sqlx/query-a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d.json new file mode 100644 index 0000000000..53ef15ffa5 --- /dev/null +++ b/backend/.sqlx/query-a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts SET acknowledged = true WHERE acknowledged = false", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d" +} diff --git a/backend/.sqlx/query-aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42.json b/backend/.sqlx/query-aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42.json new file mode 100644 index 0000000000..0eee98b120 --- /dev/null +++ b/backend/.sqlx/query-aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM queue\n WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42" +} diff --git a/backend/.sqlx/query-ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a.json b/backend/.sqlx/query-ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a.json new file mode 100644 index 0000000000..c97780ba06 --- /dev/null +++ b/backend/.sqlx/query-ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT flow_version.value AS \"value: sqlx::types::Json>\" FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 AND flow.workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "value: sqlx::types::Json>", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a" +} diff --git a/backend/.sqlx/query-be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580.json b/backend/.sqlx/query-be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580.json new file mode 100644 index 0000000000..e726f11fe2 --- /dev/null +++ b/backend/.sqlx/query-be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts SET acknowledged = true WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [] + }, + "hash": "be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580" +} diff --git a/backend/.sqlx/query-02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441.json b/backend/.sqlx/query-c9930fcfe79541af570eace58ba7e15a0816a6b4fd036cf7b991a210654b2633.json similarity index 83% rename from backend/.sqlx/query-02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441.json rename to backend/.sqlx/query-c9930fcfe79541af570eace58ba7e15a0816a6b4fd036cf7b991a210654b2633.json index a27bc6365a..da7b44d1b9 100644 --- a/backend/.sqlx/query-02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441.json +++ b/backend/.sqlx/query-c9930fcfe79541af570eace58ba7e15a0816a6b4fd036cf7b991a210654b2633.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\" FROM http_trigger WHERE workspace_id = $1", + "query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\", static_asset_config as \"static_asset_config: _\" FROM http_trigger WHERE workspace_id = $1", "describe": { "columns": [ { @@ -65,6 +65,11 @@ } } } + }, + { + "ordinal": 10, + "name": "static_asset_config: _", + "type_info": "Jsonb" } ], "parameters": { @@ -82,8 +87,9 @@ false, false, false, - false + false, + true ] }, - "hash": "02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441" + "hash": "c9930fcfe79541af570eace58ba7e15a0816a6b4fd036cf7b991a210654b2633" } diff --git a/backend/.sqlx/query-cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b.json b/backend/.sqlx/query-cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b.json new file mode 100644 index 0000000000..18bc159e6c --- /dev/null +++ b/backend/.sqlx/query-cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b.json @@ -0,0 +1,47 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, alert_type, message, created_at, acknowledged \n FROM alerts \n ORDER BY created_at DESC \n LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "alert_type", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "acknowledged", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + true + ] + }, + "hash": "cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b" +} diff --git a/backend/.sqlx/query-4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491.json b/backend/.sqlx/query-f904702536c106b0e5da8facae119c6af887c49a29ae44b3a95350ff27fb1ccf.json similarity index 83% rename from backend/.sqlx/query-4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491.json rename to backend/.sqlx/query-f904702536c106b0e5da8facae119c6af887c49a29ae44b3a95350ff27fb1ccf.json index d954f2c26c..08592a3f04 100644 --- a/backend/.sqlx/query-4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491.json +++ b/backend/.sqlx/query-f904702536c106b0e5da8facae119c6af887c49a29ae44b3a95350ff27fb1ccf.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as \"http_method: _\", edited_by, email, edited_at, extra_perms, is_async, requires_auth\n FROM http_trigger\n WHERE workspace_id = $1 AND path = $2", + "query": "SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as \"http_method: _\", edited_by, email, edited_at, extra_perms, is_async, requires_auth, static_asset_config as \"static_asset_config: _\"\n FROM http_trigger\n WHERE workspace_id = $1 AND path = $2", "describe": { "columns": [ { @@ -80,6 +80,11 @@ "ordinal": 12, "name": "requires_auth", "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "static_asset_config: _", + "type_info": "Jsonb" } ], "parameters": { @@ -101,8 +106,9 @@ false, false, false, - false + false, + true ] }, - "hash": "4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491" + "hash": "f904702536c106b0e5da8facae119c6af887c49a29ae44b3a95350ff27fb1ccf" } diff --git a/backend/Cargo.lock b/backend/Cargo.lock index a28c70fc4c..d37f3cb23a 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "android-tzdata" @@ -511,8 +511,8 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.1", - "futures-lite 2.4.0", + "fastrand 2.2.0", + "futures-lite 2.5.0", "slab", ] @@ -527,21 +527,21 @@ dependencies = [ "async-io", "async-lock", "blocking", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "once_cell", ] [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "parking", "polling", "rustix", @@ -576,7 +576,7 @@ dependencies = [ "serde-aux", "serde_json", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -605,7 +605,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "gloo-timers", "kv-log-macro", "log", @@ -659,7 +659,7 @@ dependencies = [ "sha2 0.10.8", "smart-default", "smol_str", - "thiserror", + "thiserror 1.0.69", "tokio", "uuid 0.8.2", ] @@ -692,7 +692,7 @@ dependencies = [ "crc32fast", "log", "pin-project", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -732,9 +732,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.9" +version = "1.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6448cfb224dd6a9b9ac734f58622dd0d4751f3589f3b777345745f46b2eb14" +checksum = "9b49afaa341e8dd8577e1a2200468f98956d6eda50bcf4a53246cc00174ba924" dependencies = [ "aws-credential-types", "aws-runtime", @@ -749,7 +749,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.1.1", + "fastrand 2.2.0", "hex", "http 0.2.12", "ring 0.17.8", @@ -787,7 +787,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.1.1", + "fastrand 2.2.0", "http 0.2.12", "http-body 0.4.6", "once_cell", @@ -799,9 +799,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded855583fa1d22e88fe39fd6062b062376e50a8211989e07cf5e38d52eb3453" +checksum = "09677244a9da92172c8dc60109b4a9658597d4d298b188dd0018b6a66b410ca4" dependencies = [ "aws-credential-types", "aws-runtime", @@ -821,9 +821,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.49.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9177ea1192e6601ae16c7273385690d88a7ed386a00b74a6bc894d12103cd933" +checksum = "81fea2f3a8bb3bd10932ae7ad59cc59f65f270fc9183a7e91f501dc5efbef7ee" dependencies = [ "aws-credential-types", "aws-runtime", @@ -843,9 +843,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823ef553cf36713c97453e2ddff1eb8f62be7f4523544e2a5db64caf80100f0a" +checksum = "53dcf5e7d9bd1517b8b998e170e650047cea8a2b85fe1835abe3210713e541b7" dependencies = [ "aws-credential-types", "aws-runtime", @@ -948,7 +948,7 @@ dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "bytes", - "fastrand 2.1.1", + "fastrand 2.2.0", "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", @@ -1357,15 +1357,15 @@ dependencies = [ "async-channel 2.3.1", "async-task", "futures-io", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "piper", ] [[package]] name = "borsh" -version = "1.5.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" dependencies = [ "borsh-derive", "cfg_aliases", @@ -1373,16 +1373,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.87", - "syn_derive", ] [[package]] @@ -1546,7 +1545,7 @@ dependencies = [ "rand_distr", "rayon", "safetensors", - "thiserror", + "thiserror 1.0.69", "yoke", "zip", ] @@ -1563,7 +1562,7 @@ dependencies = [ "rayon", "safetensors", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1599,9 +1598,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.36" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" dependencies = [ "jobserver", "libc", @@ -1715,9 +1714,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -1725,9 +1724,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -1749,9 +1748,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "cmake" @@ -1784,13 +1783,13 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "e0d05af1e006a2407bedef5af410552494ce5be9090444dbbcb57258c1af3d56" dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -1817,7 +1816,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -1944,9 +1943,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -1986,9 +1985,9 @@ dependencies = [ [[package]] name = "cron" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8c3e73077b4b4a6ab1ea5047c37c57aee77657bc8ecd6f29b0af082d0b0c07" +checksum = "eee8b2b4516038bc0f1d3c9934bcb4a13dd316e04abbc63c96757a6d75978532" dependencies = [ "chrono", "nom", @@ -2081,9 +2080,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -2554,7 +2553,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", - "sourcemap 9.0.1", + "sourcemap 9.1.0", "swc_atoms", "swc_common", "swc_config", @@ -2577,8 +2576,8 @@ dependencies = [ "swc_visit", "swc_visit_macros", "text_lines", - "thiserror", - "unicode-width", + "thiserror 1.0.69", + "unicode-width 0.1.14", "url", ] @@ -2716,7 +2715,7 @@ dependencies = [ "strum 0.25.0", "strum_macros 0.25.3", "syn 2.0.87", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2726,7 +2725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4889646c1ce8437a6fde3acb057fd7e2d039e62c61f5063fc125ed1ede114dc6" dependencies = [ "percent-encoding", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -3306,9 +3305,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "ff" @@ -3587,11 +3586,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ - "fastrand 2.1.1", + "fastrand 2.2.0", "futures-core", "futures-io", "parking", @@ -3655,7 +3654,7 @@ dependencies = [ "rustls-pemfile 1.0.4", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "time", "tokio", "tracing", @@ -3809,7 +3808,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -3901,7 +3900,7 @@ checksum = "5eb37859fda6792e95231aef1c5838f4043ec0ee352d8313421e311c606df612" dependencies = [ "anyhow", "strum 0.25.0", - "thiserror", + "thiserror 1.0.69", "unic-ucd-category", ] @@ -4068,7 +4067,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "ureq", ] @@ -4561,15 +4560,15 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -4832,9 +4831,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libgit2-sys" @@ -5307,20 +5306,20 @@ checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" [[package]] name = "mysql-common-derive" -version = "0.31.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe0450cc9344afff34915f8328600ab5ae19260802a334d0f72d2d5bdda3bfe" +checksum = "63c3512cf11487168e0e9db7157801bf5273be13055a9cc95356dc9e0035e49c" dependencies = [ "darling 0.20.10", - "heck 0.4.1", + "heck 0.5.0", "num-bigint", "proc-macro-crate", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.87", "termcolor", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -5346,7 +5345,7 @@ dependencies = [ "serde", "serde_json", "socket2 0.5.7", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-native-tls", "tokio-util", @@ -5387,7 +5386,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "subprocess", - "thiserror", + "thiserror 1.0.69", "time", "uuid 1.11.0", "zstd 0.13.2", @@ -5579,7 +5578,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -5701,7 +5700,7 @@ dependencies = [ "serde_with", "sha2 0.10.8", "subtle", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -6068,7 +6067,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.1", + "fastrand 2.2.0", "futures-io", ] @@ -6123,9 +6122,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", @@ -6250,27 +6249,25 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ + "proc-macro-error-attr2", "proc-macro2", "quote", - "version_check", + "syn 2.0.87", ] [[package]] @@ -6355,7 +6352,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.87", - "thiserror", + "thiserror 1.0.69", "typify", "unicode-ident", ] @@ -6394,7 +6391,7 @@ dependencies = [ "lazy_static", "memchr", "parking_lot", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -6431,9 +6428,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] @@ -6522,9 +6519,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", @@ -6533,26 +6530,29 @@ dependencies = [ "rustc-hash 2.0.0", "rustls 0.23.16", "socket2 0.5.7", - "thiserror", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom 0.2.15", "rand 0.8.5", "ring 0.17.8", "rustc-hash 2.0.0", "rustls 0.23.16", + "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] @@ -6765,7 +6765,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -6776,7 +6776,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -6791,9 +6791,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -7078,7 +7078,7 @@ dependencies = [ "radix_fmt", "rand 0.8.5", "redis", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7187,16 +7187,16 @@ checksum = "f1adc9dfed5cc999077978cc7163b9282c5751c8d39827c4ea8c8c220ca5a440" dependencies = [ "serde", "tempfile", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "toolchain_find", ] [[package]] name = "rustix" -version = "0.38.39" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -7293,6 +7293,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-tokio-stream" @@ -7430,7 +7433,7 @@ dependencies = [ "quick-xml 0.30.0", "rand 0.8.5", "serde", - "thiserror", + "thiserror 1.0.69", "url", "uuid 1.11.0", ] @@ -7542,9 +7545,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -7582,9 +7585,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -7623,9 +7626,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -7683,7 +7686,7 @@ checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7694,7 +7697,7 @@ checksum = "8cac3f1e2ca2fe333923a1ae72caca910b98ed0630bb35ef6f8c8517d6e81afa" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7739,7 +7742,7 @@ dependencies = [ "num-bigint", "serde", "smallvec", - "thiserror", + "thiserror 1.0.69", "v8", ] @@ -7903,7 +7906,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -8046,9 +8049,9 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.0.1" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86721155737d89818020dc35713595c6e7b21016073c101e6590c47ca58c2d16" +checksum = "4d146f02f4bbbabbbe3da0f9cd3ea2ab779defc4ed1f070b5bd83ea48ed78811" dependencies = [ "base64-simd 0.7.0", "bitvec", @@ -8117,7 +8120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1008d95d2ec2d062959352527be30e10fec42a1aa5e5a48d990a5ff0fb9bdc0" dependencies = [ "anyhow", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8200,7 +8203,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -8287,7 +8290,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "uuid 1.11.0", "whoami", @@ -8329,7 +8332,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "uuid 1.11.0", "whoami", @@ -8536,13 +8539,13 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "siphasher", - "sourcemap 9.0.1", + "sourcemap 9.1.0", "swc_allocator", "swc_atoms", "swc_eq_ignore_macros", "swc_visit", "tracing", - "unicode-width", + "unicode-width 0.1.14", "url", ] @@ -8600,7 +8603,7 @@ dependencies = [ "num-bigint", "once_cell", "serde", - "sourcemap 9.0.1", + "sourcemap 9.1.0", "swc_allocator", "swc_atoms", "swc_common", @@ -8869,18 +8872,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.87", -] - [[package]] name = "sync_wrapper" version = "0.1.2" @@ -8917,7 +8908,7 @@ dependencies = [ "byteorder", "enum-as-inner", "libc", - "thiserror", + "thiserror 1.0.69", "walkdir", ] @@ -9008,7 +8999,7 @@ dependencies = [ "tantivy-stacker", "tantivy-tokenizer-api", "tempfile", - "thiserror", + "thiserror 1.0.69", "time", "uuid 1.11.0", "winapi", @@ -9123,12 +9114,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", - "fastrand 2.1.1", + "fastrand 2.2.0", "once_cell", "rustix", "windows-sys 0.59.0", @@ -9154,18 +9145,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", ] [[package]] name = "thiserror-impl" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", @@ -9213,7 +9224,7 @@ dependencies = [ "pretty-hex", "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-rustls 0.24.1", "tokio-util", @@ -9339,7 +9350,7 @@ dependencies = [ "rayon", "schemars", "serde", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -9370,7 +9381,7 @@ dependencies = [ "serde", "serde_json", "spm_precompiled", - "thiserror", + "thiserror 1.0.69", "unicode-normalization-alignments", "unicode-segmentation", "unicode_categories", @@ -9472,7 +9483,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -9718,7 +9729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.69", "time", "tracing-subscriber", ] @@ -9868,7 +9879,7 @@ dependencies = [ "rand 0.8.5", "serde", "smallvec", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -9891,7 +9902,7 @@ dependencies = [ "resolv-conf", "serde", "smallvec", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "trust-dns-proto", @@ -9918,7 +9929,7 @@ dependencies = [ "native-tls", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.69", "utf-8", ] @@ -9969,7 +9980,7 @@ dependencies = [ "schemars", "serde_json", "syn 2.0.87", - "thiserror", + "thiserror 1.0.69", "unicode-ident", ] @@ -10131,6 +10142,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -10593,7 +10610,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "axum", @@ -10635,7 +10652,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "argon2", @@ -10720,7 +10737,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.419.0" +version = "1.423.2" dependencies = [ "base64 0.22.1", "chrono", @@ -10738,7 +10755,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.419.0" +version = "1.423.2" dependencies = [ "chrono", "serde", @@ -10751,7 +10768,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "rsmq_async", @@ -10766,7 +10783,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "async-stream", @@ -10798,7 +10815,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "sqlx", - "thiserror", + "thiserror 1.0.69", "tikv-jemalloc-ctl", "tokio", "tracing", @@ -10812,7 +10829,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.419.0" +version = "1.423.2" dependencies = [ "regex", "rsmq_async", @@ -10827,7 +10844,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "bytes", @@ -10849,7 +10866,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.419.0" +version = "1.423.2" dependencies = [ "itertools 0.13.0", "lazy_static", @@ -10861,7 +10878,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.419.0" +version = "1.423.2" dependencies = [ "convert_case 0.6.0", "serde", @@ -10870,7 +10887,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10882,7 +10899,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "gosyn", @@ -10894,7 +10911,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10906,7 +10923,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "itertools 0.13.0", @@ -10917,7 +10934,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "itertools 0.13.0", @@ -10928,7 +10945,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "async-recursion", @@ -10946,7 +10963,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -10963,7 +10980,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10975,7 +10992,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10993,7 +11010,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "getrandom 0.2.15", @@ -11014,7 +11031,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "serde_json", @@ -11024,7 +11041,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "async-recursion", @@ -11057,7 +11074,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.419.0" +version = "1.423.2" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -11067,7 +11084,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.419.0" +version = "1.423.2" dependencies = [ "anyhow", "async-recursion", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 26251822fb..800e165e33 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.419.0" +version = "1.423.2" authors.workspace = true edition.workspace = true @@ -29,7 +29,7 @@ members = [ ] [workspace.package] -version = "1.419.0" +version = "1.423.2" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 9923d56e66..f3e6e24f4e 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -aedaf655693cb9bab59fcf79a48c60aad6822668 \ No newline at end of file +e3df64af864f9d3546a97ff21f719c553e34ff1c \ No newline at end of file diff --git a/backend/migrations/20241105225151_alert_add_acknowledged.down.sql b/backend/migrations/20241105225151_alert_add_acknowledged.down.sql new file mode 100644 index 0000000000..ed9a1323d6 --- /dev/null +++ b/backend/migrations/20241105225151_alert_add_acknowledged.down.sql @@ -0,0 +1 @@ +ALTER TABLE alerts DROP COLUMN acknowledged; \ No newline at end of file diff --git a/backend/migrations/20241105225151_alert_add_acknowledged.up.sql b/backend/migrations/20241105225151_alert_add_acknowledged.up.sql new file mode 100644 index 0000000000..5174aec6e0 --- /dev/null +++ b/backend/migrations/20241105225151_alert_add_acknowledged.up.sql @@ -0,0 +1,9 @@ +-- Step 1: Add the new column 'acknowledged' to the 'alerts' table +ALTER TABLE alerts +ADD COLUMN acknowledged BOOLEAN DEFAULT NULL; + +-- Step 2: Update all existing rows to set 'acknowledged' to true +-- we don't want to pop up notifications to all users after migrations +-- but only show new alerts from the point of the upgrade +UPDATE alerts +SET acknowledged = TRUE; \ No newline at end of file diff --git a/backend/migrations/20241107163553_http_route_static_asset.down.sql b/backend/migrations/20241107163553_http_route_static_asset.down.sql new file mode 100644 index 0000000000..b16c347028 --- /dev/null +++ b/backend/migrations/20241107163553_http_route_static_asset.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE http_trigger DROP COLUMN static_asset_config; \ No newline at end of file diff --git a/backend/migrations/20241107163553_http_route_static_asset.up.sql b/backend/migrations/20241107163553_http_route_static_asset.up.sql new file mode 100644 index 0000000000..fdd1a25b71 --- /dev/null +++ b/backend/migrations/20241107163553_http_route_static_asset.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE http_trigger ADD COLUMN static_asset_config JSONB; \ No newline at end of file diff --git a/backend/parsers/windmill-parser-py-imports/src/lib.rs b/backend/parsers/windmill-parser-py-imports/src/lib.rs index 841a763ec2..38869fcd39 100644 --- a/backend/parsers/windmill-parser-py-imports/src/lib.rs +++ b/backend/parsers/windmill-parser-py-imports/src/lib.rs @@ -62,6 +62,7 @@ static PYTHON_IMPORTS_REPLACEMENT: phf::Map<&'static str, &'static str> = phf_ma "lokalise" => "python-lokalise-api", "msgraph" => "msgraph-sdk", "pythonjsonlogger" => "python-json-logger", + "socks" => "PySocks", }; fn replace_import(x: String) -> String { diff --git a/backend/src/main.rs b/backend/src/main.rs index 61ee657d2c..6869a24647 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -36,7 +36,7 @@ use windmill_common::{ ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, - OAUTH_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + OAUTH_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TIMEOUT_WAIT_RESULT_SETTING, }, @@ -78,7 +78,7 @@ use crate::monitor::{ reload_hub_base_url_setting, reload_job_default_timeout_setting, reload_jwt_secret_setting, reload_license_key, reload_npm_config_registry_setting, reload_pip_index_url_setting, reload_retention_period_setting, reload_scim_token_setting, reload_smtp_config, - reload_worker_config, + reload_worker_config, reload_critical_alert_mute_ui_setting, }; #[cfg(feature = "parquet")] @@ -817,6 +817,12 @@ Windmill Community Edition {GIT_VERSION} tracing::error!(error = %e, "Could not reload jwt secret setting"); } }, + CRITICAL_ALERT_MUTE_UI_SETTING => { + tracing::info!("Critical alert UI setting changed"); + if let Err(e) = reload_critical_alert_mute_ui_setting(&db).await { + tracing::error!(error = %e, "Could not reload critical alert UI setting"); + } + }, a @_ => { tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a); } diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index b6cbd55330..da811bb6ff 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -39,7 +39,7 @@ use windmill_common::{ EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING, - PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING, }, @@ -55,7 +55,7 @@ use windmill_common::{ WORKER_GROUP, }, BASE_URL, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, - METRICS_DEBUG_ENABLED, METRICS_ENABLED, + METRICS_DEBUG_ENABLED, METRICS_ENABLED, CRITICAL_ALERT_MUTE_UI_ENABLED }; use windmill_queue::cancel_job; use windmill_worker::{ @@ -131,6 +131,10 @@ pub async fn initial_load( tracing::error!("Error loading expose debug metrics: {e:#}"); } + if let Err(e) = reload_critical_alert_mute_ui_setting(db).await { + tracing::error!("Error loading critical alert mute ui setting: {e:#}"); + } + if let Err(e) = load_tag_per_workspace_enabled(db).await { tracing::error!("Error loading default tag per workpsace: {e:#}"); } @@ -226,6 +230,17 @@ pub async fn load_tag_per_workspace_workspaces(db: &DB) -> error::Result<()> { Ok(()) } +pub async fn reload_critical_alert_mute_ui_setting(db: &DB) -> error::Result<()> { + let mute = load_value_from_global_settings(db, CRITICAL_ALERT_MUTE_UI_SETTING).await; + match mute { + Ok(Some(serde_json::Value::Bool(t))) => { + CRITICAL_ALERT_MUTE_UI_ENABLED.store(t, Ordering::Relaxed); + } + _ => (), + }; + Ok(()) +} + pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> { let metrics_enabled = load_value_from_global_settings(db, EXPOSE_DEBUG_METRICS_SETTING).await; match metrics_enabled { diff --git a/backend/windmill-api/openapi-deref.json b/backend/windmill-api/openapi-deref.json index db789c2163..a6e225259a 100644 --- a/backend/windmill-api/openapi-deref.json +++ b/backend/windmill-api/openapi-deref.json @@ -1,7 +1,7 @@ { "openapi": "3.0.3", "info": { - "version": "1.409.0", + "version": "1.418.0", "title": "Windmill API", "contact": { "name": "Windmill Team", @@ -1182,6 +1182,115 @@ } } }, + "/settings/critical_alerts": { + "get": { + "summary": "Get all critical alerts", + "operationId": "getCriticalAlerts", + "tags": [ + "setting" + ], + "parameters": [ + { + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "default": 1, + "description": "The page number to retrieve (minimum value is 1)" + } + }, + { + "in": "query", + "name": "page_size", + "schema": { + "type": "integer", + "default": 10, + "maximum": 100, + "description": "Number of alerts per page (maximum is 100)" + } + }, + { + "in": "query", + "name": "acknowledged", + "schema": { + "type": "boolean", + "nullable": true, + "description": "Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts" + } + } + ], + "responses": { + "200": { + "description": "Successfully retrieved all critical alerts", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CriticalAlert" + } + } + } + } + } + } + } + }, + "/settings/critical_alerts/{id}/acknowledge": { + "post": { + "summary": "Acknowledge a critical alert", + "operationId": "acknowledgeCriticalAlert", + "tags": [ + "setting" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "The ID of the critical alert to acknowledge" + } + ], + "responses": { + "200": { + "description": "Successfully acknowledged the critical alert", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Critical alert acknowledged" + } + } + } + } + } + } + }, + "/settings/critical_alerts/acknowledge_all": { + "post": { + "summary": "Acknowledge all unacknowledged critical alerts", + "operationId": "acknowledgeAllCriticalAlerts", + "tags": [ + "setting" + ], + "responses": { + "200": { + "description": "Successfully acknowledged all unacknowledged critical alerts.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "All unacknowledged critical alerts acknowledged" + } + } + } + } + } + } + }, "/settings/test_license_key": { "post": { "summary": "test license key", @@ -2239,6 +2348,14 @@ }, { "$ref": "#/components/parameters/PerPage" + }, + { + "name": "active_only", + "in": "query", + "description": "filter only active users", + "schema": { + "type": "boolean" + } } ], "responses": { @@ -3328,6 +3445,44 @@ } } }, + "/w/{workspace}/workspaces/used_triggers": { + "get": { + "summary": "get used triggers", + "operationId": "getUsedTriggers", + "tags": [ + "workspace" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + } + ], + "responses": { + "200": { + "description": "status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "http_routes_used": { + "type": "boolean" + }, + "websocket_used": { + "type": "boolean" + } + }, + "required": [ + "http_routes_used", + "websocket_used" + ] + } + } + } + } + } + } + }, "/w/{workspace}/users/list": { "get": { "summary": "list users", @@ -6362,6 +6517,36 @@ } } }, + "/w/{workspace}/scripts/get_latest_version/{path}": { + "get": { + "summary": "get scripts's latest version (hash)", + "operationId": "getScriptLatestVersion", + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/ScriptPath" + } + ], + "tags": [ + "script" + ], + "responses": { + "200": { + "description": "Script version/hash", + "content": { + "application/json": { + "required": false, + "schema": { + "$ref": "#/components/schemas/ScriptHistory" + } + } + } + } + } + } + }, "/w/{workspace}/scripts/history_update/h/{hash}/p/{path}": { "post": { "summary": "update history of a script", @@ -7178,6 +7363,36 @@ } } }, + "/w/{workspace}/flows/get_latest_version/{path}": { + "get": { + "summary": "get flow's latest version", + "operationId": "getFlowLatestVersion", + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/ScriptPath" + } + ], + "tags": [ + "flow" + ], + "responses": { + "200": { + "description": "Flow version", + "content": { + "application/json": { + "required": false, + "schema": { + "$ref": "#/components/schemas/FlowVersion" + } + } + } + } + } + } + }, "/w/{workspace}/flows/get/v/{version}/p/{path}": { "get": { "summary": "get flow version", @@ -8096,6 +8311,36 @@ } } }, + "/w/{workspace}/apps/get_latest_version/{path}": { + "get": { + "summary": "get apps's latest version", + "operationId": "getAppLatestVersion", + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/ScriptPath" + } + ], + "tags": [ + "app" + ], + "responses": { + "200": { + "description": "App version", + "content": { + "application/json": { + "required": false, + "schema": { + "$ref": "#/components/schemas/AppHistory" + } + } + } + } + } + } + }, "/w/{workspace}/apps/history_update/a/{id}/v/{version}": { "post": { "summary": "update app history", @@ -11495,21 +11740,217 @@ } } }, - "/w/{workspace}/http_triggers/used": { - "get": { - "summary": "whether http triggers are used", - "operationId": "used", + "/w/{workspace}/websocket_triggers/create": { + "post": { + "summary": "create websocket trigger", + "operationId": "createWebsocketTrigger", "tags": [ - "http_trigger" + "websocket_trigger" ], "parameters": [ { "$ref": "#/components/parameters/WorkspaceId" } ], + "requestBody": { + "description": "new websocket trigger", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewWebsocketTrigger" + } + } + } + }, + "responses": { + "201": { + "description": "websocket trigger created", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/update/{path}": { + "post": { + "summary": "update websocket trigger", + "operationId": "updateWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "requestBody": { + "description": "updated trigger", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditWebsocketTrigger" + } + } + } + }, "responses": { "200": { - "description": "whether http triggers are used", + "description": "websocket trigger updated", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/delete/{path}": { + "delete": { + "summary": "delete websocket trigger", + "operationId": "deleteWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "responses": { + "200": { + "description": "websocket trigger deleted", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/get/{path}": { + "get": { + "summary": "get websocket trigger", + "operationId": "getWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "responses": { + "200": { + "description": "websocket trigger deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsocketTrigger" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/list": { + "get": { + "summary": "list websocket triggers", + "operationId": "listWebsocketTriggers", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId", + "required": true + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PerPage" + }, + { + "name": "path", + "description": "filter by path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "is_flow", + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "path_start", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "websocket trigger list", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTrigger" + } + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/exists/{path}": { + "get": { + "summary": "does websocket trigger exists", + "operationId": "existsWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "responses": { + "200": { + "description": "websocket trigger exists", "content": { "application/json": { "schema": { @@ -11521,6 +11962,54 @@ } } }, + "/w/{workspace}/websocket_triggers/setenabled/{path}": { + "post": { + "summary": "set enabled websocket trigger", + "operationId": "setWebsocketTriggerEnabled", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "requestBody": { + "description": "updated websocket trigger enable", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ] + } + } + } + }, + "responses": { + "200": { + "description": "websocket trigger enabled set", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/groups/list": { "get": { "summary": "list instance groups", @@ -12812,6 +13301,40 @@ } } }, + "/configs/list_autoscaling_events/{worker_group}": { + "get": { + "summary": "List autoscaling events", + "operationId": "listAutoscalingEvents", + "tags": [ + "config" + ], + "parameters": [ + { + "name": "worker_group", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "List of autoscaling events", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AutoscalingEvent" + } + } + } + } + } + } + } + }, "/w/{workspace}/acls/get/{kind}/{path}": { "get": { "summary": "get granular acls", @@ -12842,7 +13365,8 @@ "folder", "app", "raw_app", - "http_trigger" + "http_trigger", + "websocket_trigger" ] } } @@ -12894,7 +13418,8 @@ "folder", "app", "raw_app", - "http_trigger" + "http_trigger", + "websocket_trigger" ] } } @@ -12965,7 +13490,8 @@ "folder", "app", "raw_app", - "http_trigger" + "http_trigger", + "websocket_trigger" ] } } @@ -14959,6 +15485,165 @@ } } } + }, + "/srch/index/search/service_logs": { + "get": { + "summary": "Search through service logs with a string query", + "operationId": "searchLogsIndex", + "tags": [ + "indexSearch" + ], + "parameters": [ + { + "name": "search_query", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "mode", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "worker_group", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "hostname", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "min_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "max_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "search results", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query_parse_errors": { + "description": "a list of the terms that couldn't be parsed (and thus ignored)", + "type": "array", + "items": { + "type": "string" + } + }, + "hits": { + "description": "log files that matched the query", + "type": "array", + "items": { + "$ref": "#/components/schemas/LogSearchHit" + } + } + } + } + } + } + } + } + } + }, + "/srch/index/search/count_service_logs": { + "get": { + "summary": "Search and count the log line hits on every provided host", + "operationId": "countSearchLogsIndex", + "tags": [ + "indexSearch" + ], + "parameters": [ + { + "name": "search_query", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "hosts", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "min_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "max_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "search results", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query_parse_errors": { + "description": "a list of the terms that couldn't be parsed (and thus ignored)", + "type": "array", + "items": { + "type": "string" + } + }, + "count_per_host": { + "description": "count of log lines that matched the query per hostname", + "type": "object" + } + } + } + } + } + } + } + } } }, "components": { @@ -16560,6 +17245,7 @@ "users.delete", "users.update", "users.login", + "users.login_failure", "users.logout", "users.accept_invite", "users.decline_invite", @@ -16571,6 +17257,7 @@ "users.impersonate", "users.leave_workspace", "oauth.login", + "oauth.login_failure", "oauth.signup", "variables.create", "variables.delete", @@ -17533,9 +18220,248 @@ }, "email_count": { "type": "number" + }, + "websocket_count": { + "type": "number" } } }, + "WebsocketTrigger": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "edited_by": { + "type": "string" + }, + "edited_at": { + "type": "string", + "format": "date-time" + }, + "script_path": { + "type": "string" + }, + "url": { + "type": "string" + }, + "is_flow": { + "type": "boolean" + }, + "extra_perms": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "email": { + "type": "string" + }, + "workspace_id": { + "type": "string" + }, + "server_id": { + "type": "string" + }, + "last_server_ping": { + "type": "string", + "format": "date-time" + }, + "error": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "filters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ] + } + }, + "initial_messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTriggerInitialMessage" + } + }, + "url_runnable_args": { + "$ref": "#/components/schemas/ScriptArgs" + } + }, + "required": [ + "path", + "edited_by", + "edited_at", + "script_path", + "url", + "extra_perms", + "is_flow", + "email", + "workspace_id", + "enabled", + "filters", + "initial_messages", + "url_runnable_args" + ] + }, + "NewWebsocketTrigger": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "script_path": { + "type": "string" + }, + "is_flow": { + "type": "boolean" + }, + "url": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "filters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ] + } + }, + "initial_messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTriggerInitialMessage" + } + }, + "url_runnable_args": { + "$ref": "#/components/schemas/ScriptArgs" + } + }, + "required": [ + "path", + "script_path", + "url", + "is_flow", + "filters", + "initial_messages", + "url_runnable_args" + ] + }, + "EditWebsocketTrigger": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "path": { + "type": "string" + }, + "script_path": { + "type": "string" + }, + "is_flow": { + "type": "boolean" + }, + "filters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ] + } + }, + "initial_messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTriggerInitialMessage" + } + }, + "url_runnable_args": { + "$ref": "#/components/schemas/ScriptArgs" + } + }, + "required": [ + "path", + "script_path", + "url", + "is_flow", + "filters", + "initial_messages", + "url_runnable_args" + ] + }, + "WebsocketTriggerInitialMessage": { + "anyOf": [ + { + "type": "object", + "properties": { + "raw_message": { + "type": "string" + } + }, + "required": [ + "raw_message" + ] + }, + { + "type": "object", + "properties": { + "runnable_result": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "args": { + "$ref": "#/components/schemas/ScriptArgs" + }, + "is_flow": { + "type": "boolean" + } + }, + "required": [ + "path", + "args", + "is_flow" + ] + } + }, + "required": [ + "runnable_result" + ] + } + ] + }, "Group": { "type": "object", "properties": { @@ -17817,6 +18743,9 @@ }, "username": { "type": "string" + }, + "operator_only": { + "type": "boolean" } }, "required": [ @@ -18796,6 +19725,66 @@ } } }, + "LogSearchHit": { + "type": "object", + "properties": { + "dancer": { + "type": "string" + } + } + }, + "AutoscalingEvent": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "worker_group": { + "type": "string" + }, + "event_type": { + "type": "string" + }, + "desired_workers": { + "type": "integer" + }, + "reason": { + "type": "string" + }, + "applied_at": { + "type": "string", + "format": "date-time" + } + } + }, + "CriticalAlert": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Unique identifier for the alert" + }, + "alert_type": { + "type": "string", + "description": "Type of alert (e.g., critical_error)" + }, + "message": { + "type": "string", + "description": "The message content of the alert" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Time when the alert was created" + }, + "acknowledged": { + "type": "boolean", + "nullable": true, + "description": "Acknowledgment status of the alert, can be true, false, or null if not set" + } + } + }, "StaticTransform": { "type": "object", "properties": { @@ -18901,6 +19890,9 @@ }, "custom_concurrency_key": { "type": "string" + }, + "is_trigger": { + "type": "boolean" } }, "required": [ @@ -18933,6 +19925,9 @@ }, "tag_override": { "type": "string" + }, + "is_trigger": { + "type": "boolean" } }, "required": [ diff --git a/backend/windmill-api/openapi-deref.yaml b/backend/windmill-api/openapi-deref.yaml index e2a0b00a85..94206795c2 100644 --- a/backend/windmill-api/openapi-deref.yaml +++ b/backend/windmill-api/openapi-deref.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 1.409.0 + version: 1.418.0 title: Windmill API contact: name: Windmill Team @@ -142,6 +142,7 @@ paths: - users.delete - users.update - users.login + - users.login_failure - users.logout - users.accept_invite - users.decline_invite @@ -153,6 +154,7 @@ paths: - users.impersonate - users.leave_workspace - oauth.login + - oauth.login_failure - oauth.signup - variables.create - variables.delete @@ -231,24 +233,24 @@ paths: - name: before description: filter on started before (inclusive) timestamp in: query - schema: &ref_109 + schema: &ref_118 type: string format: date-time - name: after description: filter on created after (exclusive) timestamp in: query - schema: &ref_110 + schema: &ref_119 type: string format: date-time - name: username description: filter on exact username of user in: query - schema: &ref_119 + schema: &ref_128 type: string - name: operation description: filter on exact or prefix name of operation in: query - schema: &ref_120 + schema: &ref_129 type: string - name: operations in: query @@ -263,12 +265,12 @@ paths: - name: resource description: filter on exact or prefix name of resource in: query - schema: &ref_121 + schema: &ref_130 type: string - name: action_kind description: filter on type of operation in: query - schema: &ref_122 + schema: &ref_131 type: string enum: - Create @@ -300,12 +302,12 @@ paths: application/json: schema: type: object - properties: &ref_136 + properties: &ref_143 email: type: string password: type: string - required: &ref_137 + required: &ref_144 - email - password responses: @@ -428,7 +430,7 @@ paths: application/json: schema: type: object - properties: &ref_138 + properties: &ref_145 is_admin: type: boolean operator: @@ -782,7 +784,7 @@ paths: application/json: schema: type: object - properties: &ref_172 + properties: &ref_183 email: type: string workspaces: @@ -800,7 +802,7 @@ paths: - id - name - username - required: &ref_173 + required: &ref_184 - email - workspaces /workspaces/list_as_superadmin: @@ -842,14 +844,14 @@ paths: application/json: schema: type: object - properties: &ref_174 + properties: &ref_185 id: type: string name: type: string username: type: string - required: &ref_175 + required: &ref_186 - id - name responses: @@ -1042,6 +1044,98 @@ paths: text/plain: schema: type: string + /settings/critical_alerts: + get: + summary: Get all critical alerts + operationId: getCriticalAlerts + tags: + - setting + parameters: + - in: query + name: page + schema: + type: integer + default: 1 + description: The page number to retrieve (minimum value is 1) + - in: query + name: page_size + schema: + type: integer + default: 10 + maximum: 100 + description: Number of alerts per page (maximum is 100) + - in: query + name: acknowledged + schema: + type: boolean + nullable: true + description: >- + Filter by acknowledgment status; true for acknowledged, false for + unacknowledged, and omit for all alerts + responses: + '200': + description: Successfully retrieved all critical alerts + content: + application/json: + schema: + type: array + items: + type: object + properties: &ref_228 + id: + type: integer + description: Unique identifier for the alert + alert_type: + type: string + description: Type of alert (e.g., critical_error) + message: + type: string + description: The message content of the alert + created_at: + type: string + format: date-time + description: Time when the alert was created + acknowledged: + type: boolean + nullable: true + description: >- + Acknowledgment status of the alert, can be true, false, + or null if not set + /settings/critical_alerts/{id}/acknowledge: + post: + summary: Acknowledge a critical alert + operationId: acknowledgeCriticalAlert + tags: + - setting + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The ID of the critical alert to acknowledge + responses: + '200': + description: Successfully acknowledged the critical alert + content: + application/json: + schema: + type: string + example: Critical alert acknowledged + /settings/critical_alerts/acknowledge_all: + post: + summary: Acknowledge all unacknowledged critical alerts + operationId: acknowledgeAllCriticalAlerts + tags: + - setting + responses: + '200': + description: Successfully acknowledged all unacknowledged critical alerts. + content: + application/json: + schema: + type: string + example: All unacknowledged critical alerts acknowledged /settings/test_license_key: post: summary: test license key @@ -1197,12 +1291,12 @@ paths: type: array items: type: object - properties: &ref_214 + properties: &ref_221 name: type: string value: type: object - required: &ref_215 + required: &ref_222 - name - value /users/email: @@ -1361,6 +1455,8 @@ paths: type: string username: type: string + operator_only: + type: boolean required: &ref_13 - email - login_type @@ -1784,6 +1880,11 @@ paths: description: number of items to return for a given page (default 30, max 100) in: query schema: *ref_6 + - name: active_only + in: query + description: filter only active users + schema: + type: boolean responses: '200': description: user @@ -1932,7 +2033,7 @@ paths: type: array items: type: object - properties: &ref_198 + properties: &ref_205 script_path: type: string git_repo_resource_path: @@ -1957,7 +2058,7 @@ paths: - schedule - user - group - required: &ref_199 + required: &ref_206 - script_path - git_repo_resource_path deploy_ui: @@ -2639,6 +2740,32 @@ paths: text/plain: schema: type: number + /w/{workspace}/workspaces/used_triggers: + get: + summary: get used triggers + operationId: getUsedTriggers + tags: + - workspace + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + responses: + '200': + description: status + content: + application/json: + schema: + type: object + properties: + http_routes_used: + type: boolean + websocket_used: + type: boolean + required: + - http_routes_used + - websocket_used /w/{workspace}/users/list: get: summary: list users @@ -2681,7 +2808,7 @@ paths: type: array items: type: object - properties: &ref_135 + properties: &ref_142 email: type: string executions: @@ -2742,7 +2869,7 @@ paths: application/json: schema: type: object - properties: &ref_139 + properties: &ref_146 label: type: string expiration: @@ -2774,7 +2901,7 @@ paths: application/json: schema: type: object - properties: &ref_140 + properties: &ref_147 label: type: string expiration: @@ -2784,7 +2911,7 @@ paths: type: string workspace_id: type: string - required: &ref_141 + required: &ref_148 - impersonate_email responses: '201': @@ -2909,7 +3036,7 @@ paths: application/json: schema: type: object - properties: &ref_144 + properties: &ref_151 path: type: string value: @@ -2925,7 +3052,7 @@ paths: expires_at: type: string format: date-time - required: &ref_145 + required: &ref_152 - path - value - is_secret @@ -3010,7 +3137,7 @@ paths: application/json: schema: type: object - properties: &ref_146 + properties: &ref_153 path: type: string value: @@ -3195,7 +3322,7 @@ paths: type: array items: type: object - properties: &ref_142 + properties: &ref_149 name: type: string value: @@ -3204,7 +3331,7 @@ paths: type: string is_custom: type: boolean - required: &ref_143 + required: &ref_150 - name - value - description @@ -3344,7 +3471,7 @@ paths: application/json: schema: type: object - properties: &ref_191 + properties: &ref_198 access_token: type: string expires_in: @@ -3355,7 +3482,7 @@ paths: type: array items: type: string - required: &ref_192 + required: &ref_199 - access_token /w/{workspace}/oauth/create_account: post: @@ -3560,7 +3687,7 @@ paths: application/json: schema: type: object - properties: &ref_153 + properties: &ref_160 path: type: string value: {} @@ -3568,7 +3695,7 @@ paths: type: string resource_type: type: string - required: &ref_154 + required: &ref_161 - path - value - resource_type @@ -3623,7 +3750,7 @@ paths: application/json: schema: type: object - properties: &ref_155 + properties: &ref_162 path: type: string description: @@ -3689,7 +3816,7 @@ paths: application/json: schema: type: object - properties: &ref_156 + properties: &ref_163 workspace_id: type: string path: @@ -3710,7 +3837,7 @@ paths: edited_at: type: string format: date-time - required: &ref_157 + required: &ref_164 - path - resource_type - is_oauth @@ -3826,7 +3953,7 @@ paths: type: array items: type: object - properties: &ref_158 + properties: &ref_165 workspace_id: type: string path: @@ -3857,7 +3984,7 @@ paths: edited_at: type: string format: date-time - required: &ref_159 + required: &ref_166 - path - resource_type - is_oauth @@ -3904,7 +4031,7 @@ paths: - name: name in: path required: true - schema: &ref_92 + schema: &ref_101 type: string responses: '200': @@ -4026,7 +4153,7 @@ paths: application/json: schema: type: object - properties: &ref_160 + properties: &ref_167 schema: {} description: type: string @@ -4254,14 +4381,14 @@ paths: properties: flow: type: object - properties: &ref_50 + properties: &ref_52 summary: type: string description: type: string value: type: object - properties: &ref_58 + properties: &ref_64 modules: type: array items: @@ -4270,32 +4397,32 @@ paths: id: type: string value: - oneOf: &ref_239 + oneOf: &ref_249 - type: object - properties: &ref_223 + properties: &ref_233 input_transforms: type: object additionalProperties: oneOf: &ref_29 - type: object - properties: &ref_219 + properties: &ref_229 value: {} type: type: string enum: - javascript - required: &ref_220 + required: &ref_230 - expr - type - type: object - properties: &ref_221 + properties: &ref_231 expr: type: string type: type: string enum: - javascript - required: &ref_222 + required: &ref_232 - expr - type discriminator: &ref_30 @@ -4338,13 +4465,15 @@ paths: type: number custom_concurrency_key: type: string - required: &ref_224 + is_trigger: + type: boolean + required: &ref_234 - type - content - language - input_transforms - type: object - properties: &ref_225 + properties: &ref_235 input_transforms: type: object additionalProperties: @@ -4360,12 +4489,14 @@ paths: - script tag_override: type: string - required: &ref_226 + is_trigger: + type: boolean + required: &ref_236 - type - path - input_transforms - type: object - properties: &ref_227 + properties: &ref_237 input_transforms: type: object additionalProperties: @@ -4377,12 +4508,12 @@ paths: type: string enum: - flow - required: &ref_228 + required: &ref_238 - type - path - input_transforms - type: object - properties: &ref_229 + properties: &ref_239 modules: type: array items: @@ -4404,13 +4535,13 @@ paths: type: boolean parallelism: type: integer - required: &ref_230 + required: &ref_240 - modules - iterator - skip_failures - type - type: object - properties: &ref_231 + properties: &ref_241 modules: type: array items: @@ -4427,12 +4558,12 @@ paths: type: boolean parallelism: type: integer - required: &ref_232 + required: &ref_242 - modules - skip_failures - type - type: object - properties: &ref_233 + properties: &ref_243 branches: type: array items: @@ -4463,12 +4594,12 @@ paths: type: string enum: - branchone - required: &ref_234 + required: &ref_244 - branches - default - type - type: object - properties: &ref_235 + properties: &ref_245 branches: type: array items: @@ -4493,20 +4624,20 @@ paths: - branchall parallel: type: boolean - required: &ref_236 + required: &ref_246 - branches - type - type: object - properties: &ref_237 + properties: &ref_247 type: type: string enum: - identity flow: type: boolean - required: &ref_238 + required: &ref_248 - type - discriminator: &ref_240 + discriminator: &ref_250 propertyName: type mapping: rawscript: '#/components/schemas/RawScript' @@ -4588,7 +4719,7 @@ paths: type: boolean retry: type: object - properties: &ref_87 + properties: &ref_93 constant: type: object properties: @@ -4634,11 +4765,11 @@ paths: type: number early_return: type: string - required: &ref_59 + required: &ref_65 - modules schema: type: object - required: &ref_51 + required: &ref_53 - summary - value /apps/hub/list: @@ -4945,12 +5076,12 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: &ref_48 + schema: &ref_50 type: boolean - name: created_by description: mask to filter exact matching user creator in: query - schema: &ref_49 + schema: &ref_51 type: string - name: path_start description: mask to filter matching starting path @@ -5611,7 +5742,7 @@ paths: application/json: schema: type: object - properties: &ref_53 + properties: &ref_57 primary_schedule: type: object properties: @@ -5625,6 +5756,8 @@ paths: type: number email_count: type: number + websocket_count: + type: number /w/{workspace}/scripts/list_tokens/{path}: get: summary: get tokens with script scope @@ -5672,7 +5805,7 @@ paths: content: application/json: schema: - allOf: &ref_126 + allOf: &ref_135 - type: object properties: *ref_40 required: *ref_41 @@ -5710,13 +5843,38 @@ paths: type: array items: type: object - properties: &ref_127 + properties: &ref_42 script_hash: type: string deployment_msg: type: string - required: &ref_128 + required: &ref_43 - script_hash + /w/{workspace}/scripts/get_latest_version/{path}: + get: + summary: get scripts's latest version (hash) + operationId: getScriptLatestVersion + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_33 + tags: + - script + responses: + '200': + description: Script version/hash + content: + application/json: + required: false + schema: + type: object + properties: *ref_42 + required: *ref_43 /w/{workspace}/scripts/history_update/h/{hash}/p/{path}: post: summary: update history of a script @@ -5791,7 +5949,7 @@ paths: - name: token in: path required: true - schema: &ref_115 + schema: &ref_124 type: string - name: path in: path @@ -5939,20 +6097,20 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: &ref_42 + schema: &ref_44 type: string format: uuid - name: tag description: Override the tag to use in: query - schema: &ref_44 + schema: &ref_46 type: string - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: &ref_45 + schema: &ref_47 type: string - name: job_id description: >- @@ -5961,7 +6119,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: &ref_43 + schema: &ref_45 type: string format: uuid - name: invisible_to_owner @@ -6005,7 +6163,7 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6013,7 +6171,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -6022,14 +6180,14 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: &ref_46 + schema: &ref_48 type: string - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: &ref_47 + schema: &ref_49 type: string requestBody: description: script args @@ -6065,17 +6223,17 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6083,7 +6241,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -6092,13 +6250,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 requestBody: description: script args required: true @@ -6132,17 +6290,17 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6150,7 +6308,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -6159,13 +6317,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 - name: payload description: > The base64 encoded payload that has been encoded as a JSON. e.g how @@ -6173,7 +6331,7 @@ paths: `encodeURIComponent(btoa(JSON.stringify({a: 2})))` in: query - schema: &ref_86 + schema: &ref_92 type: string responses: '200': @@ -6204,13 +6362,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6218,7 +6376,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: script args required: true @@ -6256,13 +6414,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6270,7 +6428,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: script args required: true @@ -6381,11 +6539,11 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: path_start description: mask to filter matching starting path in: query @@ -6439,12 +6597,12 @@ paths: type: array items: allOf: - - allOf: &ref_52 + - allOf: &ref_56 - type: object - properties: *ref_50 - required: *ref_51 + properties: *ref_52 + required: *ref_53 - type: object - properties: &ref_177 + properties: &ref_188 workspace_id: type: string path: @@ -6458,7 +6616,7 @@ paths: type: boolean extra_perms: type: object - additionalProperties: &ref_176 + additionalProperties: &ref_187 type: boolean starred: type: boolean @@ -6476,7 +6634,7 @@ paths: type: number visible_to_runner_only: type: boolean - required: &ref_178 + required: &ref_189 - path - edited_by - edited_at @@ -6512,7 +6670,7 @@ paths: type: array items: type: object - properties: &ref_189 + properties: &ref_54 id: type: integer created_at: @@ -6520,9 +6678,34 @@ paths: format: date-time deployment_msg: type: string - required: &ref_190 + required: &ref_55 - id - created_at + /w/{workspace}/flows/get_latest_version/{path}: + get: + summary: get flow's latest version + operationId: getFlowLatestVersion + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_33 + tags: + - flow + responses: + '200': + description: Flow version + content: + application/json: + required: false + schema: + type: object + properties: *ref_54 + required: *ref_55 /w/{workspace}/flows/get/v/{version}/p/{path}: get: summary: get flow version @@ -6550,7 +6733,7 @@ paths: content: application/json: schema: - allOf: *ref_52 + allOf: *ref_56 /w/{workspace}/flows/history_update/v/{version}/p/{path}: post: summary: update flow history @@ -6616,7 +6799,7 @@ paths: content: application/json: schema: - allOf: *ref_52 + allOf: *ref_56 /w/{workspace}/flows/get_triggers_count/{path}: get: summary: get triggers count of flow @@ -6639,7 +6822,7 @@ paths: application/json: schema: type: object - properties: *ref_53 + properties: *ref_57 /w/{workspace}/flows/list_tokens/{path}: get: summary: get tokens with flow scope @@ -6720,11 +6903,11 @@ paths: application/json: schema: allOf: - - allOf: *ref_52 + - allOf: *ref_56 - type: object properties: draft: - allOf: *ref_52 + allOf: *ref_56 /w/{workspace}/flows/exists/{path}: get: summary: exists flow by path @@ -6765,10 +6948,10 @@ paths: application/json: schema: allOf: - - allOf: &ref_54 + - allOf: &ref_58 - type: object - properties: *ref_50 - required: *ref_51 + properties: *ref_52 + required: *ref_53 - type: object properties: path: @@ -6822,7 +7005,7 @@ paths: application/json: schema: allOf: - - allOf: *ref_54 + - allOf: *ref_58 - type: object properties: deployment_message: @@ -6910,11 +7093,11 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: path_start description: mask to filter matching starting path in: query @@ -6941,7 +7124,7 @@ paths: type: array items: type: object - properties: &ref_184 + properties: &ref_195 workspace_id: type: string path: @@ -6959,7 +7142,7 @@ paths: edited_at: type: string format: date-time - required: &ref_185 + required: &ref_196 - workspace_id - path - summary @@ -7002,7 +7185,7 @@ paths: - name: version in: path required: true - schema: &ref_114 + schema: &ref_123 type: number - name: path in: path @@ -7064,11 +7247,11 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: path_start description: mask to filter matching starting path in: query @@ -7109,7 +7292,7 @@ paths: type: array items: type: object - properties: &ref_182 + properties: &ref_193 id: type: integer workspace_id: @@ -7135,7 +7318,7 @@ paths: - viewer - publisher - anonymous - required: &ref_183 + required: &ref_194 - id - workspace_id - path @@ -7170,7 +7353,7 @@ paths: type: string policy: type: object - properties: &ref_55 + properties: &ref_59 triggerables: type: object additionalProperties: @@ -7253,7 +7436,7 @@ paths: application/json: schema: type: object - properties: &ref_56 + properties: &ref_60 id: type: integer workspace_id: @@ -7275,7 +7458,7 @@ paths: type: object policy: type: object - properties: *ref_55 + properties: *ref_59 execution_mode: type: string enum: @@ -7286,7 +7469,7 @@ paths: type: object additionalProperties: type: boolean - required: &ref_57 + required: &ref_61 - id - workspace_id - path @@ -7319,10 +7502,10 @@ paths: content: application/json: schema: - allOf: &ref_186 + allOf: &ref_197 - type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 - type: object properties: draft_only: @@ -7352,13 +7535,38 @@ paths: type: array items: type: object - properties: &ref_187 + properties: &ref_62 version: type: integer deployment_msg: type: string - required: &ref_188 + required: &ref_63 - version + /w/{workspace}/apps/get_latest_version/{path}: + get: + summary: get apps's latest version + operationId: getAppLatestVersion + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_33 + tags: + - app + responses: + '200': + description: App version + content: + application/json: + required: false + schema: + type: object + properties: *ref_62 + required: *ref_63 /w/{workspace}/apps/history_update/a/{id}/v/{version}: post: summary: update app history @@ -7377,7 +7585,7 @@ paths: - name: version in: path required: true - schema: &ref_116 + schema: &ref_125 type: integer requestBody: description: App deployment message @@ -7418,8 +7626,8 @@ paths: application/json: schema: type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 /w/{workspace}/apps_u/public_resource/{path}: get: summary: get public resource @@ -7485,8 +7693,8 @@ paths: application/json: schema: type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 /w/{workspace}/raw_apps/create: post: summary: create raw app @@ -7633,7 +7841,7 @@ paths: value: {} policy: type: object - properties: *ref_55 + properties: *ref_59 deployment_message: type: string responses: @@ -7741,11 +7949,11 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -7753,7 +7961,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -7762,7 +7970,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the flow owner (default false) in: query @@ -7798,7 +8006,7 @@ paths: - name: id in: path required: true - schema: &ref_83 + schema: &ref_89 type: string format: uuid - name: step_id @@ -7831,11 +8039,11 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -7843,7 +8051,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -7852,7 +8060,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the flow owner (default false) in: query @@ -7910,17 +8118,17 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -7928,7 +8136,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -7937,7 +8145,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the script owner (default false) in: query @@ -7977,7 +8185,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the script owner (default false) in: query @@ -7990,7 +8198,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: preview required: true @@ -7998,7 +8206,7 @@ paths: application/json: schema: type: object - properties: &ref_147 + properties: &ref_154 content: type: string path: @@ -8037,7 +8245,7 @@ paths: type: boolean lock: type: string - required: &ref_148 + required: &ref_155 - args responses: '201': @@ -8075,11 +8283,11 @@ paths: application/json: schema: type: object - properties: &ref_149 + properties: &ref_156 args: type: object additionalProperties: *ref_16 - required: &ref_150 + required: &ref_157 - args responses: '201': @@ -8112,7 +8320,7 @@ paths: type: array items: type: object - properties: &ref_208 + properties: &ref_215 raw_code: type: string path: @@ -8136,7 +8344,7 @@ paths: - php - rust - ansible - required: &ref_209 + required: &ref_216 - raw_code - path - language @@ -8176,7 +8384,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the script owner (default false) in: query @@ -8189,7 +8397,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: preview required: true @@ -8197,11 +8405,11 @@ paths: application/json: schema: type: object - properties: &ref_179 + properties: &ref_190 value: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 path: type: string args: @@ -8211,7 +8419,7 @@ paths: type: string restarted_from: type: object - properties: &ref_181 + properties: &ref_192 flow_job_id: type: string format: uuid @@ -8219,7 +8427,7 @@ paths: type: string branch_or_iteration_n: type: integer - required: &ref_180 + required: &ref_191 - value - content - args @@ -8245,94 +8453,94 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: &ref_62 + schema: &ref_68 type: string - name: script_path_start description: mask to filter matching starting path in: query - schema: &ref_63 + schema: &ref_69 type: string - name: schedule_path description: mask to filter by schedule path in: query - schema: &ref_64 + schema: &ref_70 type: string - name: script_hash description: mask to filter exact matching path in: query - schema: &ref_65 + schema: &ref_71 type: string - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: &ref_66 + schema: &ref_72 type: string format: date-time - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: &ref_67 + schema: &ref_73 type: string format: date-time - name: success description: filter on successful jobs in: query - schema: &ref_68 + schema: &ref_74 type: boolean - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: &ref_69 + schema: &ref_75 type: boolean - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: &ref_70 + schema: &ref_76 type: string - name: suspended description: filter on suspended jobs in: query - schema: &ref_71 + schema: &ref_77 type: boolean - name: running description: filter on running jobs in: query - schema: &ref_72 + schema: &ref_78 type: boolean - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: &ref_73 + schema: &ref_79 type: string - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: &ref_74 + schema: &ref_80 type: string - name: tag description: filter on jobs with a given tag/worker group in: query - schema: &ref_75 + schema: &ref_81 type: string - name: page description: which page to return (start at 1, default 1) @@ -8363,7 +8571,7 @@ paths: type: array items: type: object - properties: &ref_81 + properties: &ref_87 workspace_id: type: string id: @@ -8430,14 +8638,14 @@ paths: by extension its DT_TOKEN. flow_status: type: object - properties: &ref_76 + properties: &ref_82 step: type: integer modules: type: array items: type: object - properties: &ref_60 + properties: &ref_66 type: type: string enum: @@ -8514,20 +8722,20 @@ paths: format: uuid skipped: type: boolean - required: &ref_61 + required: &ref_67 - type user_states: additionalProperties: true preprocessor_module: allOf: - type: object - properties: *ref_60 - required: *ref_61 + properties: *ref_66 + required: *ref_67 failure_module: allOf: - type: object - properties: *ref_60 - required: *ref_61 + properties: *ref_66 + required: *ref_67 - type: object properties: parent_module: @@ -8542,14 +8750,14 @@ paths: items: type: string format: uuid - required: &ref_77 + required: &ref_83 - step - modules - failure_module raw_flow: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 is_flow_step: type: boolean language: @@ -8587,7 +8795,7 @@ paths: type: number suspend: type: number - required: &ref_82 + required: &ref_88 - id - running - canceled @@ -8666,79 +8874,79 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: success description: filter on successful jobs in: query - schema: *ref_68 + schema: *ref_74 - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: suspended description: filter on suspended jobs in: query - schema: *ref_71 + schema: *ref_77 - name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: page description: which page to return (start at 1, default 1) in: query @@ -8816,75 +9024,75 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: label description: >- mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: &ref_78 + schema: &ref_84 type: string - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: success description: filter on successful jobs in: query - schema: *ref_68 + schema: *ref_74 - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: page description: which page to return (start at 1, default 1) in: query @@ -8922,7 +9130,7 @@ paths: type: array items: type: object - properties: &ref_79 + properties: &ref_85 workspace_id: type: string id: @@ -8988,12 +9196,12 @@ paths: by extension its DT_TOKEN. flow_status: type: object - properties: *ref_76 - required: *ref_77 + properties: *ref_82 + required: *ref_83 raw_flow: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 is_flow_step: type: boolean language: @@ -9035,7 +9243,7 @@ paths: type: number aggregate_wait_time_ms: type: number - required: &ref_80 + required: &ref_86 - id - created_by - duration_ms @@ -9064,54 +9272,54 @@ paths: - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: label description: >- mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: *ref_78 + schema: *ref_84 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: created_before description: filter on created before (inclusive) timestamp in: query - schema: &ref_117 + schema: &ref_126 type: string format: date-time - name: created_after description: filter on created after (exclusive) timestamp in: query - schema: &ref_118 + schema: &ref_127 type: string format: date-time - name: created_or_started_before @@ -9119,23 +9327,23 @@ paths: filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp in: query - schema: &ref_111 + schema: &ref_120 type: string format: date-time - name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 - name: created_or_started_after description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp in: query - schema: &ref_112 + schema: &ref_121 type: string format: date-time - name: created_or_started_after_completed_jobs @@ -9144,7 +9352,7 @@ paths: otherwise after (exclusive) timestamp but only for the completed jobs in: query - schema: &ref_113 + schema: &ref_122 type: string format: date-time - name: job_kinds @@ -9152,27 +9360,27 @@ paths: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: suspended description: filter on suspended jobs in: query - schema: *ref_71 + schema: *ref_77 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: page description: which page to return (start at 1, default 1) in: query @@ -9221,11 +9429,11 @@ paths: schema: type: array items: - oneOf: &ref_84 + oneOf: &ref_90 - allOf: - type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 - type: object properties: type: @@ -9234,15 +9442,15 @@ paths: - CompletedJob - allOf: - type: object - properties: *ref_81 - required: *ref_82 + properties: *ref_87 + required: *ref_88 - type: object properties: type: type: string enum: - QueuedJob - discriminator: &ref_85 + discriminator: &ref_91 propertyName: type /jobs/db_clock: get: @@ -9309,7 +9517,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: no_logs in: query schema: @@ -9320,8 +9528,8 @@ paths: content: application/json: schema: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 /w/{workspace}/jobs_u/get_root_job_id/{id}: get: summary: get root job id @@ -9336,7 +9544,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: get root job id @@ -9358,7 +9566,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job details @@ -9380,7 +9588,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job args @@ -9401,7 +9609,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: running in: query schema: @@ -9436,9 +9644,9 @@ paths: type: integer flow_status: type: object - additionalProperties: &ref_151 + additionalProperties: &ref_158 type: object - properties: &ref_152 + properties: &ref_159 scheduled_for: type: string format: date-time @@ -9485,7 +9693,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: flow debug info details @@ -9506,7 +9714,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job details @@ -9514,8 +9722,8 @@ paths: application/json: schema: type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 /w/{workspace}/jobs_u/completed/get_result/{id}: get: summary: get completed job result @@ -9530,7 +9738,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: suspended_job in: query schema: @@ -9567,10 +9775,10 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: get_started in: query - schema: &ref_124 + schema: &ref_133 type: boolean responses: '200': @@ -9604,7 +9812,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job details @@ -9612,8 +9820,8 @@ paths: application/json: schema: type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 /w/{workspace}/jobs_u/queue/cancel/{id}: post: summary: cancel queued or running job @@ -9628,7 +9836,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: reason required: true @@ -9692,7 +9900,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: reason required: true @@ -9724,7 +9932,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -9755,7 +9963,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -9797,7 +10005,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: payload description: > The base64 encoded payload that has been encoded as a JSON. e.g how @@ -9805,7 +10013,7 @@ paths: `encodeURIComponent(btoa(JSON.stringify({a: 2})))` in: query - schema: *ref_86 + schema: *ref_92 - name: resume_id in: path required: true @@ -9840,7 +10048,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -9882,7 +10090,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: key in: path required: true @@ -9914,7 +10122,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: key in: path required: true @@ -9940,7 +10148,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: required: true content: @@ -9968,7 +10176,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -10003,7 +10211,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -10045,7 +10253,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -10069,8 +10277,8 @@ paths: type: object properties: job: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 approvers: type: array items: @@ -10135,7 +10343,7 @@ paths: application/json: schema: type: object - properties: &ref_162 + properties: &ref_169 path: type: string schedule: @@ -10176,7 +10384,7 @@ paths: type: boolean retry: type: object - properties: *ref_87 + properties: *ref_93 no_flow_overlap: type: boolean summary: @@ -10186,7 +10394,7 @@ paths: paused_until: type: string format: date-time - required: &ref_163 + required: &ref_170 - path - schedule - timezone @@ -10222,7 +10430,7 @@ paths: application/json: schema: type: object - properties: &ref_164 + properties: &ref_171 schedule: type: string timezone: @@ -10255,7 +10463,7 @@ paths: type: boolean retry: type: object - properties: *ref_87 + properties: *ref_93 no_flow_overlap: type: boolean summary: @@ -10265,7 +10473,7 @@ paths: paused_until: type: string format: date-time - required: &ref_165 + required: &ref_172 - schedule - timezone - script_path @@ -10356,7 +10564,7 @@ paths: application/json: schema: type: object - properties: &ref_88 + properties: &ref_94 path: type: string edited_by: @@ -10410,7 +10618,7 @@ paths: type: boolean retry: type: object - properties: *ref_87 + properties: *ref_93 summary: type: string no_flow_overlap: @@ -10420,7 +10628,7 @@ paths: paused_until: type: string format: date-time - required: &ref_89 + required: &ref_95 - path - edited_by - edited_at @@ -10477,7 +10685,7 @@ paths: filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: path description: filter by path in: query @@ -10500,8 +10708,8 @@ paths: type: array items: type: object - properties: *ref_88 - required: *ref_89 + properties: *ref_94 + required: *ref_95 /w/{workspace}/schedules/list_with_jobs: get: summary: list schedules with last 20 jobs @@ -10529,10 +10737,10 @@ paths: schema: type: array items: - allOf: &ref_161 + allOf: &ref_168 - type: object - properties: *ref_88 - required: *ref_89 + properties: *ref_94 + required: *ref_95 - type: object properties: jobs: @@ -10611,7 +10819,7 @@ paths: application/json: schema: type: object - properties: &ref_166 + properties: &ref_173 path: type: string script_path: @@ -10632,7 +10840,7 @@ paths: type: boolean requires_auth: type: boolean - required: &ref_167 + required: &ref_174 - path - script_path - route_path @@ -10669,7 +10877,7 @@ paths: application/json: schema: type: object - properties: &ref_168 + properties: &ref_175 path: type: string script_path: @@ -10690,7 +10898,7 @@ paths: type: boolean requires_auth: type: boolean - required: &ref_169 + required: &ref_176 - path - script_path - is_flow @@ -10749,7 +10957,7 @@ paths: application/json: schema: type: object - properties: &ref_90 + properties: &ref_96 path: type: string edited_by: @@ -10783,7 +10991,7 @@ paths: type: boolean requires_auth: type: boolean - required: &ref_91 + required: &ref_97 - path - edited_by - edited_at @@ -10837,8 +11045,8 @@ paths: type: array items: type: object - properties: *ref_90 - required: *ref_91 + properties: *ref_96 + required: *ref_97 /w/{workspace}/http_triggers/exists/{path}: get: summary: does http trigger exists @@ -10901,24 +11109,362 @@ paths: application/json: schema: type: boolean - /w/{workspace}/http_triggers/used: - get: - summary: whether http triggers are used - operationId: used + /w/{workspace}/websocket_triggers/create: + post: + summary: create websocket trigger + operationId: createWebsocketTrigger tags: - - http_trigger + - websocket_trigger parameters: - name: workspace in: path required: true schema: *ref_0 + requestBody: + description: new websocket trigger + required: true + content: + application/json: + schema: + type: object + properties: &ref_177 + path: + type: string + script_path: + type: string + is_flow: + type: boolean + url: + type: string + enabled: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + anyOf: &ref_98 + - type: object + properties: + raw_message: + type: string + required: + - raw_message + - type: object + properties: + runnable_result: + type: object + properties: + path: + type: string + args: + type: object + additionalProperties: *ref_16 + is_flow: + type: boolean + required: + - path + - args + - is_flow + required: + - runnable_result + url_runnable_args: + type: object + additionalProperties: *ref_16 + required: &ref_178 + - path + - script_path + - url + - is_flow + - filters + - initial_messages + - url_runnable_args + responses: + '201': + description: websocket trigger created + content: + text/plain: + schema: + type: string + /w/{workspace}/websocket_triggers/update/{path}: + post: + summary: update websocket trigger + operationId: updateWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + type: object + properties: &ref_179 + url: + type: string + path: + type: string + script_path: + type: string + is_flow: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + anyOf: *ref_98 + url_runnable_args: + type: object + additionalProperties: *ref_16 + required: &ref_180 + - path + - script_path + - url + - is_flow + - filters + - initial_messages + - url_runnable_args responses: '200': - description: whether http triggers are used + description: websocket trigger updated + content: + text/plain: + schema: + type: string + /w/{workspace}/websocket_triggers/delete/{path}: + delete: + summary: delete websocket trigger + operationId: deleteWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + responses: + '200': + description: websocket trigger deleted + content: + text/plain: + schema: + type: string + /w/{workspace}/websocket_triggers/get/{path}: + get: + summary: get websocket trigger + operationId: getWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + responses: + '200': + description: websocket trigger deleted + content: + application/json: + schema: + type: object + properties: &ref_99 + path: + type: string + edited_by: + type: string + edited_at: + type: string + format: date-time + script_path: + type: string + url: + type: string + is_flow: + type: boolean + extra_perms: + type: object + additionalProperties: + type: boolean + email: + type: string + workspace_id: + type: string + server_id: + type: string + last_server_ping: + type: string + format: date-time + error: + type: string + enabled: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + anyOf: *ref_98 + url_runnable_args: + type: object + additionalProperties: *ref_16 + required: &ref_100 + - path + - edited_by + - edited_at + - script_path + - url + - extra_perms + - is_flow + - email + - workspace_id + - enabled + - filters + - initial_messages + - url_runnable_args + /w/{workspace}/websocket_triggers/list: + get: + summary: list websocket triggers + operationId: listWebsocketTriggers + tags: + - websocket_trigger + parameters: + - required: true + name: workspace + in: path + schema: *ref_0 + - name: page + description: which page to return (start at 1, default 1) + in: query + schema: *ref_5 + - name: per_page + description: number of items to return for a given page (default 30, max 100) + in: query + schema: *ref_6 + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + '200': + description: websocket trigger list + content: + application/json: + schema: + type: array + items: + type: object + properties: *ref_99 + required: *ref_100 + /w/{workspace}/websocket_triggers/exists/{path}: + get: + summary: does websocket trigger exists + operationId: existsWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + responses: + '200': + description: websocket trigger exists content: application/json: schema: type: boolean + /w/{workspace}/websocket_triggers/setenabled/{path}: + post: + summary: set enabled websocket trigger + operationId: setWebsocketTriggerEnabled + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + requestBody: + description: updated websocket trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + '200': + description: websocket trigger enabled set + content: + text/plain: + schema: + type: string /groups/list: get: summary: list instance groups @@ -10934,7 +11480,7 @@ paths: type: array items: type: object - properties: &ref_93 + properties: &ref_102 name: type: string summary: @@ -10943,7 +11489,7 @@ paths: type: array items: type: string - required: &ref_94 + required: &ref_103 - name /groups/get/{name}: get: @@ -10955,7 +11501,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: instance group @@ -10963,8 +11509,8 @@ paths: application/json: schema: type: object - properties: *ref_93 - required: *ref_94 + properties: *ref_102 + required: *ref_103 /groups/create: post: summary: create instance group @@ -11002,7 +11548,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: update instance group required: true @@ -11032,7 +11578,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: instance group deleted @@ -11050,7 +11596,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: user to add to instance group required: true @@ -11080,7 +11626,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: user to remove from instance group required: true @@ -11115,7 +11661,7 @@ paths: type: array items: type: object - properties: &ref_95 + properties: &ref_104 name: type: string summary: @@ -11130,7 +11676,7 @@ paths: type: string external_id: type: string - required: &ref_96 + required: &ref_105 - name /groups/overwrite: post: @@ -11147,8 +11693,8 @@ paths: type: array items: type: object - properties: *ref_95 - required: *ref_96 + properties: *ref_104 + required: *ref_105 responses: '200': description: success message @@ -11184,7 +11730,7 @@ paths: type: array items: type: object - properties: &ref_97 + properties: &ref_106 name: type: string summary: @@ -11197,7 +11743,7 @@ paths: type: object additionalProperties: type: boolean - required: &ref_98 + required: &ref_107 - name /w/{workspace}/groups/listnames: get: @@ -11270,7 +11816,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: updated group required: true @@ -11302,7 +11848,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: group deleted @@ -11324,7 +11870,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: group @@ -11332,8 +11878,8 @@ paths: application/json: schema: type: object - properties: *ref_97 - required: *ref_98 + properties: *ref_106 + required: *ref_107 /w/{workspace}/groups/adduser/{name}: post: summary: add user to group @@ -11348,7 +11894,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: added user to group required: true @@ -11380,7 +11926,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: added user to group required: true @@ -11426,7 +11972,7 @@ paths: type: array items: type: object - properties: &ref_99 + properties: &ref_108 name: type: string owners: @@ -11444,7 +11990,7 @@ paths: edited_at: type: string format: date-time - required: &ref_100 + required: &ref_109 - name - owners - extra_perms @@ -11526,7 +12072,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: update folder required: true @@ -11565,7 +12111,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: folder deleted @@ -11587,7 +12133,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: folder @@ -11595,8 +12141,8 @@ paths: application/json: schema: type: object - properties: *ref_99 - required: *ref_100 + properties: *ref_108 + required: *ref_109 /w/{workspace}/folders/getusage/{name}: get: summary: get folder usage @@ -11611,7 +12157,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: folder @@ -11653,7 +12199,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: owner user to folder required: true @@ -11687,7 +12233,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: added owner to folder required: true @@ -11741,7 +12287,7 @@ paths: type: array items: type: object - properties: &ref_170 + properties: &ref_181 worker: type: string worker_instance: @@ -11783,7 +12329,7 @@ paths: type: number wm_memory_usage: type: number - required: &ref_171 + required: &ref_182 - worker - worker_instance - ping_at @@ -11876,7 +12422,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: a config @@ -11893,7 +12439,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: worker group required: true @@ -11916,7 +12462,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: Delete config @@ -11939,13 +12485,49 @@ paths: type: array items: type: object - properties: &ref_216 + properties: &ref_223 name: type: string config: type: object - required: &ref_217 + required: &ref_224 - name + /configs/list_autoscaling_events/{worker_group}: + get: + summary: List autoscaling events + operationId: listAutoscalingEvents + tags: + - config + parameters: + - name: worker_group + in: path + required: true + schema: + type: string + responses: + '200': + description: List of autoscaling events + content: + application/json: + schema: + type: array + items: + type: object + properties: &ref_227 + id: + type: integer + format: int64 + worker_group: + type: string + event_type: + type: string + desired_workers: + type: integer + reason: + type: string + applied_at: + type: string + format: date-time /w/{workspace}/acls/get/{kind}/{path}: get: summary: get granular acls @@ -11977,6 +12559,7 @@ paths: - app - raw_app - http_trigger + - websocket_trigger responses: '200': description: acls @@ -12017,6 +12600,7 @@ paths: - app - raw_app - http_trigger + - websocket_trigger requestBody: description: acl to add required: true @@ -12069,6 +12653,7 @@ paths: - app - raw_app - http_trigger + - websocket_trigger requestBody: description: acl to add required: true @@ -12217,13 +12802,13 @@ paths: schema: *ref_0 - name: runnable_id in: query - schema: &ref_101 + schema: &ref_110 type: string - name: runnable_type in: query - schema: &ref_102 + schema: &ref_111 type: string - enum: &ref_133 + enum: &ref_140 - ScriptHash - ScriptPath - FlowPath @@ -12244,7 +12829,7 @@ paths: type: array items: type: object - properties: &ref_103 + properties: &ref_112 id: type: string name: @@ -12258,7 +12843,7 @@ paths: type: boolean success: type: boolean - required: &ref_104 + required: &ref_113 - id - name - args @@ -12308,10 +12893,10 @@ paths: schema: *ref_0 - name: runnable_id in: query - schema: *ref_101 + schema: *ref_110 - name: runnable_type in: query - schema: *ref_102 + schema: *ref_111 - name: page description: which page to return (start at 1, default 1) in: query @@ -12329,8 +12914,8 @@ paths: type: array items: type: object - properties: *ref_103 - required: *ref_104 + properties: *ref_112 + required: *ref_113 /w/{workspace}/inputs/create: post: summary: Create an Input for future use in a script or flow @@ -12344,10 +12929,10 @@ paths: schema: *ref_0 - name: runnable_id in: query - schema: *ref_101 + schema: *ref_110 - name: runnable_type in: query - schema: *ref_102 + schema: *ref_111 requestBody: description: Input required: true @@ -12355,12 +12940,12 @@ paths: application/json: schema: type: object - properties: &ref_129 + properties: &ref_136 name: type: string args: type: object - required: &ref_130 + required: &ref_137 - name - args - created_by @@ -12390,14 +12975,14 @@ paths: application/json: schema: type: object - properties: &ref_131 + properties: &ref_138 id: type: string name: type: string is_public: type: boolean - required: &ref_132 + required: &ref_139 - id - name - is_public @@ -12423,7 +13008,7 @@ paths: - name: input in: path required: true - schema: &ref_123 + schema: &ref_132 type: string responses: '200': @@ -12456,7 +13041,7 @@ paths: properties: s3_resource: type: object - properties: &ref_105 + properties: &ref_114 bucket: type: string region: @@ -12471,7 +13056,7 @@ paths: type: string pathStyle: type: boolean - required: &ref_106 + required: &ref_115 - bucket - region - endPoint @@ -12547,8 +13132,8 @@ paths: properties: s3_resource: type: object - properties: *ref_105 - required: *ref_106 + properties: *ref_114 + required: *ref_115 responses: '200': description: Connection settings @@ -12569,10 +13154,10 @@ paths: type: boolean client_kwargs: type: object - properties: &ref_107 + properties: &ref_116 region_name: type: string - required: &ref_108 + required: &ref_117 - region_name required: - endpoint_url @@ -12627,8 +13212,8 @@ paths: type: boolean client_kwargs: type: object - properties: *ref_107 - required: *ref_108 + properties: *ref_116 + required: *ref_117 required: - endpoint_url - use_ssl @@ -12686,8 +13271,8 @@ paths: application/json: schema: type: object - properties: *ref_105 - required: *ref_106 + properties: *ref_114 + required: *ref_115 /w/{workspace}/job_helpers/test_connection: get: summary: Test connection to the workspace object storage @@ -12751,10 +13336,10 @@ paths: type: array items: type: object - properties: &ref_193 + properties: &ref_200 s3: type: string - required: &ref_194 + required: &ref_201 - s3 restricted_access: type: boolean @@ -12787,7 +13372,7 @@ paths: application/json: schema: type: object - properties: &ref_195 + properties: &ref_202 mime_type: type: string size_in_bytes: @@ -12851,7 +13436,7 @@ paths: application/json: schema: type: object - properties: &ref_196 + properties: &ref_203 msg: type: string content: @@ -12863,7 +13448,7 @@ paths: - Csv - Parquet - Unknown - required: &ref_197 + required: &ref_204 - content_type /w/{workspace}/job_helpers/load_parquet_preview/{path}: get: @@ -13201,7 +13786,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: parameters for statistics retrieval required: true @@ -13230,46 +13815,46 @@ paths: type: array items: type: object - properties: &ref_200 + properties: &ref_207 id: type: string name: type: string - required: &ref_201 + required: &ref_208 - id scalar_metrics: type: array items: type: object - properties: &ref_202 + properties: &ref_209 metric_id: type: string value: type: number - required: &ref_203 + required: &ref_210 - id - value timeseries_metrics: type: array items: type: object - properties: &ref_204 + properties: &ref_211 metric_id: type: string values: type: array items: type: object - properties: &ref_206 + properties: &ref_213 timestamp: type: string format: date-time value: type: number - required: &ref_207 + required: &ref_214 - timestamp - value - required: &ref_205 + required: &ref_212 - id - values /w/{workspace}/job_metrics/set_progress/{id}: @@ -13286,7 +13871,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: parameters for statistics retrieval required: true @@ -13320,7 +13905,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job progress between 0 and 99 @@ -13338,11 +13923,11 @@ paths: - name: before description: filter on started before (inclusive) timestamp in: query - schema: *ref_109 + schema: *ref_118 - name: after description: filter on created after (exclusive) timestamp in: query - schema: *ref_110 + schema: *ref_119 - name: with_error in: query required: false @@ -13414,12 +13999,12 @@ paths: type: array items: type: object - properties: &ref_210 + properties: &ref_217 concurrency_key: type: string total_running: type: number - required: &ref_211 + required: &ref_218 - concurrency_key - total_running /concurrency_groups/prune/{concurrency_id}: @@ -13432,7 +14017,7 @@ paths: - name: concurrency_id in: path required: true - schema: &ref_125 + schema: &ref_134 type: string responses: '200': @@ -13452,7 +14037,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: concurrency key for given job @@ -13485,93 +14070,93 @@ paths: - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: label description: >- mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: *ref_78 + schema: *ref_84 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: created_or_started_before description: >- filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp in: query - schema: *ref_111 + schema: *ref_120 - name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 - name: created_or_started_after description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp in: query - schema: *ref_112 + schema: *ref_121 - name: created_or_started_after_completed_jobs description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs in: query - schema: *ref_113 + schema: *ref_122 - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: page description: which page to return (start at 1, default 1) in: query @@ -13619,17 +14204,17 @@ paths: application/json: schema: type: object - properties: &ref_212 + properties: &ref_219 jobs: type: array items: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 obscured_jobs: type: array items: type: object - properties: &ref_134 + properties: &ref_141 typ: type: string started_at: @@ -13642,7 +14227,7 @@ paths: Obscured jobs omitted for security because of too specific filtering type: boolean - required: &ref_213 + required: &ref_220 - jobs - obscured_jobs /srch/w/{workspace}/index/search/job: @@ -13684,9 +14269,118 @@ paths: type: array items: type: object - properties: &ref_218 + properties: &ref_225 dancer: type: string + /srch/index/search/service_logs: + get: + summary: Search through service logs with a string query + operationId: searchLogsIndex + tags: + - indexSearch + parameters: + - name: search_query + in: query + required: true + schema: + type: string + - name: mode + in: query + required: true + schema: + type: string + - name: worker_group + in: query + required: false + schema: + type: string + - name: hostname + in: query + required: true + schema: + type: string + - name: min_ts + in: query + required: false + schema: + type: string + format: date-time + - name: max_ts + in: query + required: false + schema: + type: string + format: date-time + responses: + '200': + description: search results + content: + application/json: + schema: + type: object + properties: + query_parse_errors: + description: >- + a list of the terms that couldn't be parsed (and thus + ignored) + type: array + items: + type: string + hits: + description: log files that matched the query + type: array + items: + type: object + properties: &ref_226 + dancer: + type: string + /srch/index/search/count_service_logs: + get: + summary: Search and count the log line hits on every provided host + operationId: countSearchLogsIndex + tags: + - indexSearch + parameters: + - name: search_query + in: query + required: true + schema: + type: string + - name: hosts + in: query + required: true + schema: + type: string + - name: min_ts + in: query + required: false + schema: + type: string + format: date-time + - name: max_ts + in: query + required: false + schema: + type: string + format: date-time + responses: + '200': + description: search results + content: + application/json: + schema: + type: object + properties: + query_parse_errors: + description: >- + a list of the terms that couldn't be parsed (and thus + ignored) + type: array + items: + type: string + count_per_host: + description: count of log lines that matched the query per hostname + type: object components: securitySchemes: bearerAuth: @@ -13711,12 +14405,12 @@ components: name: version in: path required: true - schema: *ref_114 + schema: *ref_123 Token: name: token in: path required: true - schema: *ref_115 + schema: *ref_124 AccountId: name: id in: path @@ -13741,7 +14435,7 @@ components: name: id in: path required: true - schema: *ref_83 + schema: *ref_89 Path: name: path in: path @@ -13756,12 +14450,12 @@ components: name: version in: path required: true - schema: *ref_116 + schema: *ref_125 Name: name: name in: path required: true - schema: *ref_92 + schema: *ref_101 Page: name: page description: which page to return (start at 1, default 1) @@ -13776,12 +14470,12 @@ components: name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 CreatedBy: name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 Label: name: label description: >- @@ -13789,26 +14483,26 @@ components: with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: *ref_78 + schema: *ref_84 ParentJob: name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 WorkerTag: name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 CacheTtl: name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 NewJobId: name: job_id description: >- @@ -13816,7 +14510,7 @@ components: randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 IncludeHeader: name: include_header description: > @@ -13826,14 +14520,14 @@ components: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 QueueLimit: name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 Payload: name: payload description: > @@ -13842,162 +14536,162 @@ components: `encodeURIComponent(btoa(JSON.stringify({a: 2})))` in: query - schema: *ref_86 + schema: *ref_92 ScriptStartPath: name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 SchedulePath: name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 ScriptExactPath: name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 ScriptExactHash: name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 CreatedBefore: name: created_before description: filter on created before (inclusive) timestamp in: query - schema: *ref_117 + schema: *ref_126 CreatedAfter: name: created_after description: filter on created after (exclusive) timestamp in: query - schema: *ref_118 + schema: *ref_127 StartedBefore: name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 StartedAfter: name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 Before: name: before description: filter on started before (inclusive) timestamp in: query - schema: *ref_109 + schema: *ref_118 CreatedOrStartedAfter: name: created_or_started_after description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp in: query - schema: *ref_112 + schema: *ref_121 CreatedOrStartedAfterCompletedJob: name: created_or_started_after_completed_jobs description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs in: query - schema: *ref_113 + schema: *ref_122 CreatedOrStartedBefore: name: created_or_started_before description: >- filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp in: query - schema: *ref_111 + schema: *ref_120 Success: name: success description: filter on successful jobs in: query - schema: *ref_68 + schema: *ref_74 ScheduledForBeforeNow: name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 Suspended: name: suspended description: filter on suspended jobs in: query - schema: *ref_71 + schema: *ref_77 Running: name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 ArgsFilter: name: args description: filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 Tag: name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 ResultFilter: name: result description: filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 After: name: after description: filter on created after (exclusive) timestamp in: query - schema: *ref_110 + schema: *ref_119 Username: name: username description: filter on exact username of user in: query - schema: *ref_119 + schema: *ref_128 Operation: name: operation description: filter on exact or prefix name of operation in: query - schema: *ref_120 + schema: *ref_129 ResourceName: name: resource description: filter on exact or prefix name of resource in: query - schema: *ref_121 + schema: *ref_130 ActionKind: name: action_kind description: filter on type of operation in: query - schema: *ref_122 + schema: *ref_131 JobKinds: name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 RunnableId: name: runnable_id in: query - schema: *ref_101 + schema: *ref_110 RunnableTypeQuery: name: runnable_type in: query - schema: *ref_102 + schema: *ref_111 InputId: name: input in: path required: true - schema: *ref_123 + schema: *ref_132 GetStarted: name: get_started in: query - schema: *ref_124 + schema: *ref_133 ConcurrencyId: name: concurrency_id in: path required: true - schema: *ref_125 + schema: *ref_134 schemas: Script: type: object @@ -14008,83 +14702,83 @@ components: properties: *ref_40 required: *ref_41 NewScriptWithDraft: - allOf: *ref_126 + allOf: *ref_135 ScriptHistory: type: object - properties: *ref_127 - required: *ref_128 + properties: *ref_42 + required: *ref_43 ScriptArgs: type: object additionalProperties: *ref_16 Input: type: object - properties: *ref_103 - required: *ref_104 + properties: *ref_112 + required: *ref_113 CreateInput: type: object - properties: *ref_129 - required: *ref_130 + properties: *ref_136 + required: *ref_137 UpdateInput: type: object - properties: *ref_131 - required: *ref_132 + properties: *ref_138 + required: *ref_139 RunnableType: type: string - enum: *ref_133 + enum: *ref_140 QueuedJob: type: object - properties: *ref_81 - required: *ref_82 + properties: *ref_87 + required: *ref_88 CompletedJob: type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 ObscuredJob: type: object - properties: *ref_134 + properties: *ref_141 Job: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 User: type: object properties: *ref_10 required: *ref_11 UserUsage: type: object - properties: *ref_135 + properties: *ref_142 Login: type: object - properties: *ref_136 - required: *ref_137 + properties: *ref_143 + required: *ref_144 EditWorkspaceUser: type: object - properties: *ref_138 + properties: *ref_145 TruncatedToken: type: object properties: *ref_38 required: *ref_39 NewToken: type: object - properties: *ref_139 + properties: *ref_146 NewTokenImpersonate: type: object - properties: *ref_140 - required: *ref_141 + properties: *ref_147 + required: *ref_148 ListableVariable: type: object properties: *ref_22 required: *ref_23 ContextualVariable: type: object - properties: *ref_142 - required: *ref_143 + properties: *ref_149 + required: *ref_150 CreateVariable: type: object - properties: *ref_144 - required: *ref_145 + properties: *ref_151 + required: *ref_152 EditVariable: type: object - properties: *ref_146 + properties: *ref_153 AuditLog: type: object properties: *ref_1 @@ -14216,93 +14910,107 @@ components: - has_preprocessor Preview: type: object - properties: *ref_147 - required: *ref_148 + properties: *ref_154 + required: *ref_155 WorkflowTask: - type: object - properties: *ref_149 - required: *ref_150 - WorkflowStatusRecord: - type: object - additionalProperties: *ref_151 - WorkflowStatus: - type: object - properties: *ref_152 - CreateResource: - type: object - properties: *ref_153 - required: *ref_154 - EditResource: - type: object - properties: *ref_155 - Resource: type: object properties: *ref_156 required: *ref_157 + WorkflowStatusRecord: + type: object + additionalProperties: *ref_158 + WorkflowStatus: + type: object + properties: *ref_159 + CreateResource: + type: object + properties: *ref_160 + required: *ref_161 + EditResource: + type: object + properties: *ref_162 + Resource: + type: object + properties: *ref_163 + required: *ref_164 ListableResource: type: object - properties: *ref_158 - required: *ref_159 + properties: *ref_165 + required: *ref_166 ResourceType: type: object properties: *ref_26 required: *ref_27 EditResourceType: type: object - properties: *ref_160 + properties: *ref_167 Schedule: type: object - properties: *ref_88 - required: *ref_89 + properties: *ref_94 + required: *ref_95 ScheduleWJobs: - allOf: *ref_161 + allOf: *ref_168 NewSchedule: type: object - properties: *ref_162 - required: *ref_163 + properties: *ref_169 + required: *ref_170 EditSchedule: type: object - properties: *ref_164 - required: *ref_165 + properties: *ref_171 + required: *ref_172 HttpTrigger: type: object - properties: *ref_90 - required: *ref_91 + properties: *ref_96 + required: *ref_97 NewHttpTrigger: type: object - properties: *ref_166 - required: *ref_167 + properties: *ref_173 + required: *ref_174 EditHttpTrigger: type: object - properties: *ref_168 - required: *ref_169 + properties: *ref_175 + required: *ref_176 TriggersCount: type: object - properties: *ref_53 - Group: - type: object - properties: *ref_97 - required: *ref_98 - InstanceGroup: - type: object - properties: *ref_93 - required: *ref_94 - Folder: + properties: *ref_57 + WebsocketTrigger: type: object properties: *ref_99 required: *ref_100 + NewWebsocketTrigger: + type: object + properties: *ref_177 + required: *ref_178 + EditWebsocketTrigger: + type: object + properties: *ref_179 + required: *ref_180 + WebsocketTriggerInitialMessage: + anyOf: *ref_98 + Group: + type: object + properties: *ref_106 + required: *ref_107 + InstanceGroup: + type: object + properties: *ref_102 + required: *ref_103 + Folder: + type: object + properties: *ref_108 + required: *ref_109 WorkerPing: type: object - properties: *ref_170 - required: *ref_171 + properties: *ref_181 + required: *ref_182 UserWorkspaceList: type: object - properties: *ref_172 - required: *ref_173 + properties: *ref_183 + required: *ref_184 CreateWorkspace: type: object - properties: *ref_174 - required: *ref_175 + properties: *ref_185 + required: *ref_186 Workspace: type: object properties: *ref_7 @@ -14316,48 +15024,48 @@ components: properties: *ref_12 required: *ref_13 Flow: - allOf: *ref_52 + allOf: *ref_56 ExtraPerms: type: object - additionalProperties: *ref_176 + additionalProperties: *ref_187 FlowMetadata: type: object - properties: *ref_177 - required: *ref_178 + properties: *ref_188 + required: *ref_189 OpenFlowWPath: - allOf: *ref_54 + allOf: *ref_58 FlowPreview: type: object - properties: *ref_179 - required: *ref_180 + properties: *ref_190 + required: *ref_191 RestartedFrom: type: object - properties: *ref_181 + properties: *ref_192 Policy: type: object - properties: *ref_55 + properties: *ref_59 ListableApp: type: object - properties: *ref_182 - required: *ref_183 + properties: *ref_193 + required: *ref_194 ListableRawApp: type: object - properties: *ref_184 - required: *ref_185 + properties: *ref_195 + required: *ref_196 AppWithLastVersion: type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 AppWithLastVersionWDraft: - allOf: *ref_186 + allOf: *ref_197 AppHistory: type: object - properties: *ref_187 - required: *ref_188 + properties: *ref_62 + required: *ref_63 FlowVersion: type: object - properties: *ref_189 - required: *ref_190 + properties: *ref_54 + required: *ref_55 SlackToken: type: object properties: @@ -14379,33 +15087,33 @@ components: - bot TokenResponse: type: object - properties: *ref_191 - required: *ref_192 + properties: *ref_198 + required: *ref_199 HubScriptKind: name: kind schema: *ref_34 PolarsClientKwargs: type: object - properties: *ref_107 - required: *ref_108 + properties: *ref_116 + required: *ref_117 LargeFileStorage: type: object properties: *ref_17 WindmillLargeFile: type: object - properties: *ref_193 - required: *ref_194 + properties: *ref_200 + required: *ref_201 WindmillFileMetadata: type: object - properties: *ref_195 + properties: *ref_202 WindmillFilePreview: type: object - properties: *ref_196 - required: *ref_197 + properties: *ref_203 + required: *ref_204 S3Resource: type: object - properties: *ref_105 - required: *ref_106 + properties: *ref_114 + required: *ref_115 WorkspaceGitSyncSettings: type: object properties: *ref_18 @@ -14417,8 +15125,8 @@ components: properties: *ref_20 GitRepositorySettings: type: object - properties: *ref_198 - required: *ref_199 + properties: *ref_205 + required: *ref_206 UploadFilePart: type: object properties: @@ -14431,117 +15139,126 @@ components: - tag MetricMetadata: type: object - properties: *ref_200 - required: *ref_201 + properties: *ref_207 + required: *ref_208 ScalarMetric: type: object - properties: *ref_202 - required: *ref_203 + properties: *ref_209 + required: *ref_210 TimeseriesMetric: type: object - properties: *ref_204 - required: *ref_205 + properties: *ref_211 + required: *ref_212 MetricDataPoint: type: object - properties: *ref_206 - required: *ref_207 + properties: *ref_213 + required: *ref_214 RawScriptForDependencies: type: object - properties: *ref_208 - required: *ref_209 + properties: *ref_215 + required: *ref_216 ConcurrencyGroup: type: object - properties: *ref_210 - required: *ref_211 + properties: *ref_217 + required: *ref_218 ExtendedJobs: type: object - properties: *ref_212 - required: *ref_213 + properties: *ref_219 + required: *ref_220 ExportedUser: type: object properties: *ref_3 required: *ref_4 GlobalSetting: - type: object - properties: *ref_214 - required: *ref_215 - Config: - type: object - properties: *ref_216 - required: *ref_217 - ExportedInstanceGroup: - type: object - properties: *ref_95 - required: *ref_96 - JobSearchHit: - type: object - properties: *ref_218 - StaticTransform: - type: object - properties: *ref_219 - required: *ref_220 - JavascriptTransform: type: object properties: *ref_221 required: *ref_222 + Config: + type: object + properties: *ref_223 + required: *ref_224 + ExportedInstanceGroup: + type: object + properties: *ref_104 + required: *ref_105 + JobSearchHit: + type: object + properties: *ref_225 + LogSearchHit: + type: object + properties: *ref_226 + AutoscalingEvent: + type: object + properties: *ref_227 + CriticalAlert: + type: object + properties: *ref_228 + StaticTransform: + type: object + properties: *ref_229 + required: *ref_230 + JavascriptTransform: + type: object + properties: *ref_231 + required: *ref_232 InputTransform: oneOf: *ref_29 discriminator: *ref_30 RawScript: type: object - properties: *ref_223 - required: *ref_224 + properties: *ref_233 + required: *ref_234 PathScript: type: object - properties: *ref_225 - required: *ref_226 + properties: *ref_235 + required: *ref_236 PathFlow: type: object - properties: *ref_227 - required: *ref_228 + properties: *ref_237 + required: *ref_238 FlowModule: type: object properties: *ref_31 required: *ref_32 ForloopFlow: type: object - properties: *ref_229 - required: *ref_230 + properties: *ref_239 + required: *ref_240 WhileloopFlow: type: object - properties: *ref_231 - required: *ref_232 + properties: *ref_241 + required: *ref_242 BranchOne: type: object - properties: *ref_233 - required: *ref_234 + properties: *ref_243 + required: *ref_244 BranchAll: type: object - properties: *ref_235 - required: *ref_236 + properties: *ref_245 + required: *ref_246 Identity: type: object - properties: *ref_237 - required: *ref_238 + properties: *ref_247 + required: *ref_248 FlowModuleValue: - oneOf: *ref_239 - discriminator: *ref_240 + oneOf: *ref_249 + discriminator: *ref_250 Retry: type: object - properties: *ref_87 + properties: *ref_93 FlowValue: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 OpenFlow: type: object - properties: *ref_50 - required: *ref_51 + properties: *ref_52 + required: *ref_53 FlowStatusModule: type: object - properties: *ref_60 - required: *ref_61 + properties: *ref_66 + required: *ref_67 FlowStatus: type: object - properties: *ref_76 - required: *ref_77 + properties: *ref_82 + required: *ref_83 diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 67e52eb9fa..a85619dde7 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.419.0 + version: 1.423.2 title: Windmill API contact: @@ -190,7 +190,7 @@ paths: schema: type: string - /w/{workspace}/users/{username}: + /w/{workspace}/users/get/{username}: get: summary: get user (require admin privilege) operationId: getUser @@ -773,6 +773,78 @@ paths: schema: type: string + /settings/critical_alerts: + get: + summary: Get all critical alerts + operationId: getCriticalAlerts + tags: + - setting + parameters: + - in: query + name: page + schema: + type: integer + default: 1 + description: The page number to retrieve (minimum value is 1) + - in: query + name: page_size + schema: + type: integer + default: 10 + maximum: 100 + description: Number of alerts per page (maximum is 100) + - in: query + name: acknowledged + schema: + type: boolean + nullable: true + description: Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts + responses: + "200": + description: Successfully retrieved all critical alerts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + + /settings/critical_alerts/{id}/acknowledge: + post: + summary: Acknowledge a critical alert + operationId: acknowledgeCriticalAlert + tags: + - setting + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The ID of the critical alert to acknowledge + responses: + "200": + description: Successfully acknowledged the critical alert + content: + application/json: + schema: + type: string + example: "Critical alert acknowledged" + + /settings/critical_alerts/acknowledge_all: + post: + summary: Acknowledge all unacknowledged critical alerts + operationId: acknowledgeAllCriticalAlerts + tags: + - setting + responses: + "200": + description: Successfully acknowledged all unacknowledged critical alerts. + content: + application/json: + schema: + type: string + example: "All unacknowledged critical alerts acknowledged" /settings/test_license_key: post: @@ -9146,6 +9218,14 @@ paths: in: query schema: type: string + - name: content_type + in: query + schema: + type: string + - name: content_disposition + in: query + schema: + type: string requestBody: description: File content required: true @@ -11463,6 +11543,17 @@ components: type: string route_path: type: string + static_asset_config: + type: object + properties: + s3: + type: string + storage: + type: string + filename: + type: string + required: + - s3 is_flow: type: boolean extra_perms: @@ -11509,6 +11600,17 @@ components: type: string route_path: type: string + static_asset_config: + type: object + properties: + s3: + type: string + storage: + type: string + filename: + type: string + required: + - s3 is_flow: type: boolean http_method: @@ -11542,6 +11644,17 @@ components: type: string route_path: type: string + static_asset_config: + type: object + properties: + s3: + type: string + storage: + type: string + filename: + type: string + required: + - s3 is_flow: type: boolean http_method: @@ -12073,6 +12186,10 @@ components: type: object additionalProperties: type: object + s3_inputs: + type: array + items: + type: object execution_mode: type: string enum: [viewer, publisher, anonymous] @@ -12680,3 +12797,23 @@ components: type: string format: date-time + CriticalAlert: + type: object + properties: + id: + type: integer + description: Unique identifier for the alert + alert_type: + type: string + description: Type of alert (e.g., critical_error) + message: + type: string + description: The message content of the alert + created_at: + type: string + format: date-time + description: Time when the alert was created + acknowledged: + type: boolean + nullable: true + description: Acknowledgment status of the alert, can be true, false, or null if not set diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index cd65136a2a..4ec07393dc 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -7,6 +7,12 @@ use std::collections::HashMap; * Please see the included NOTICE for copyright information and * LICENSE-AGPL for a copy of the license. */ + +#[cfg(feature = "parquet")] +use crate::{job_helpers_ee::{ + get_random_file_name, get_s3_resource, get_workspace_s3_resource, upload_file_internal, + UploadFileResponse, +}, users::fetch_api_authed_from_permissioned_as}; use crate::{ db::{ApiAuthed, DB}, resources::get_resource_value_interpolated_internal, @@ -23,7 +29,13 @@ use axum::{ Router, }; use hyper::StatusCode; +#[cfg(feature = "parquet")] +use itertools::Itertools; use magic_crypt::MagicCryptTrait; +#[cfg(feature = "parquet")] +use object_store::{Attribute, Attributes}; +#[cfg(feature = "parquet")] +use regex::Regex; use serde::{Deserialize, Serialize}; use serde_json::{json, value::RawValue}; use sha2::{Digest, Sha256}; @@ -32,6 +44,8 @@ use sqlx::{types::Uuid, FromRow}; use std::str; use windmill_audit::audit_ee::audit_log; use windmill_audit::ActionKind; +#[cfg(feature = "parquet")] +use windmill_common::s3_helpers::build_object_store_client; use windmill_common::{ apps::ListAppQuery, db::UserDB, @@ -69,6 +83,7 @@ pub fn workspaced_service() -> Router { pub fn unauthed_service() -> Router { Router::new() .route("/execute_component/*path", post(execute_component)) + .route("/upload_s3_file/*path", post(upload_s3_file_from_app)) .route("/public_app/:secret", get(get_public_app_by_secret)) .route("/public_resource/*path", get(get_public_resource)) } @@ -179,6 +194,14 @@ pub struct PolicyTriggerableInputs { allow_user_resources: AllowUserResources, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct S3Input { + allowed_resources: Vec, + allow_user_resources: bool, + allow_workspace_resource: bool, + file_key_regex: String, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Policy { pub on_behalf_of: Option, @@ -192,6 +215,7 @@ pub struct Policy { #[serde(skip_serializing_if = "Option::is_none")] pub triggerables_v2: Option>, pub execution_mode: ExecutionMode, + pub s3_inputs: Option>, } #[derive(Deserialize)] @@ -432,9 +456,7 @@ async fn get_latest_version( authed: ApiAuthed, Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, - ) -> JsonResult> { - let mut tx = user_db.begin(&authed).await?; let row = sqlx::query!( "SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg @@ -457,7 +479,6 @@ async fn get_latest_version( } else { return Ok(Json(None)); } - } async fn update_app_history( @@ -1067,6 +1088,49 @@ fn digest(code: &str) -> String { format!("rawscript/{:x}", result) } +async fn get_on_behalf_details_from_policy_and_authed( + policy: &Policy, + opt_authed: &Option, +) -> Result<(String, String, String)> { + let (username, permissioned_as, email) = match policy.execution_mode { + ExecutionMode::Anonymous => { + let username = opt_authed + .as_ref() + .map(|a| a.username.clone()) + .unwrap_or_else(|| "anonymous".to_string()); + let (permissioned_as, email) = get_on_behalf_of(&policy)?; + (username, permissioned_as, email) + } + ExecutionMode::Publisher => { + let username = opt_authed + .as_ref() + .map(|a| a.username.clone()) + .ok_or_else(|| { + Error::BadRequest( + "publisher execution mode requires authentication".to_string(), + ) + })?; + let (permissioned_as, email) = get_on_behalf_of(&policy)?; + (username, permissioned_as, email) + } + ExecutionMode::Viewer => { + let (username, email) = opt_authed + .as_ref() + .map(|a| (a.username.clone(), a.email.clone())) + .ok_or_else(|| { + Error::BadRequest("Required to be authed in viewer mode".to_string()) + })?; + ( + username.clone(), + username_to_permissioned_as(&username), + email, + ) + } + }; + + Ok((username, permissioned_as, email)) +} + async fn execute_component( OptAuthed(opt_authed): OptAuthed, Extension(db): Extension, @@ -1129,6 +1193,7 @@ async fn execute_component( triggerables_v2: Some(hm), on_behalf_of: None, on_behalf_of_email: None, + s3_inputs: None, } } _ => { @@ -1146,41 +1211,8 @@ async fn execute_component( } }; - let (username, permissioned_as, email) = match policy.execution_mode { - ExecutionMode::Anonymous => { - let username = opt_authed - .as_ref() - .map(|a| a.username.clone()) - .unwrap_or_else(|| "anonymous".to_string()); - let (permissioned_as, email) = get_on_behalf_of(&policy)?; - (username, permissioned_as, email) - } - ExecutionMode::Publisher => { - let username = opt_authed - .as_ref() - .map(|a| a.username.clone()) - .ok_or_else(|| { - Error::BadRequest( - "publisher execution mode requires authentication".to_string(), - ) - })?; - let (permissioned_as, email) = get_on_behalf_of(&policy)?; - (username, permissioned_as, email) - } - ExecutionMode::Viewer => { - let (username, email) = opt_authed - .as_ref() - .map(|a| (a.username.clone(), a.email.clone())) - .ok_or_else(|| { - Error::BadRequest("Required to be authed in viewer mode".to_string()) - })?; - ( - username.clone(), - username_to_permissioned_as(&username), - email, - ) - } - }; + let (username, permissioned_as, email) = + get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?; let (job_payload, (args, job_id), tag) = match payload { ExecuteApp { args, component, raw_code: Some(raw_code), path: None, .. } => { @@ -1249,6 +1281,248 @@ async fn execute_component( Ok(uuid.to_string()) } +#[cfg(not(feature = "parquet"))] +async fn upload_s3_file_from_app() -> Result<()> { + return Err(Error::BadRequest( + "This endpoint requires the parquet feature to be enabled".to_string(), + )); +} + + +#[cfg(feature = "parquet")] +#[derive(Debug, Deserialize, Clone)] +struct UploadFileToS3Query { + file_key: Option, + file_extension: Option, + s3_resource_path: Option, + content_type: Option, + content_disposition: Option, + force_viewer_file_key_regex: Option, + force_viewer_allow_user_resources: Option, + force_viewer_allow_workspace_resource: Option, + force_viewer_allowed_resources: Option, +} + +#[cfg(feature = "parquet")] +async fn upload_s3_file_from_app( + OptAuthed(opt_authed): OptAuthed, + Extension(db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + Query(query): Query, + request: axum::extract::Request, +) -> JsonResult { + let policy = if let Some(file_key_regex) = query.force_viewer_file_key_regex { + Some(Policy { + execution_mode: ExecutionMode::Viewer, + triggerables: None, + triggerables_v2: None, + on_behalf_of: None, + on_behalf_of_email: None, + s3_inputs: Some(vec![S3Input { + file_key_regex: file_key_regex, + allow_user_resources: query.force_viewer_allow_user_resources.unwrap_or(false), + allow_workspace_resource: query + .force_viewer_allow_workspace_resource + .unwrap_or(false), + allowed_resources: query + .force_viewer_allowed_resources + .map(|s| s.split(',').map(|s| s.to_string()).collect()) + .unwrap_or_default(), + }]), + }) + } else { + let policy_o = sqlx::query_scalar!( + "SELECT policy from app WHERE path = $1 AND workspace_id = $2", + &path.0, + &w_id + ) + .fetch_optional(&db) + .await?; + + policy_o + .map(|p| serde_json::from_value::(p).map_err(to_anyhow)) + .transpose()? + }; + + let user_db = UserDB::new(db.clone()); + + let (s3_resource_opt, file_key) = if policy.as_ref().is_some_and(|p| p.s3_inputs.is_some()) { + let policy = policy.unwrap(); + let s3_inputs = policy.s3_inputs.as_ref().unwrap(); + + let (username, permissioned_as, email) = + get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?; + + let on_behalf_authed = + fetch_api_authed_from_permissioned_as(permissioned_as, email, &w_id, &db, username) + .await?; + + if let Some(file_key) = query.file_key { + // file key is provided => requires workspace, user or list policy and must match the regex + let matching_s3_inputs = if let Some(ref s3_resource_path) = query.s3_resource_path { + s3_inputs + .iter() + .filter(|s3_input| { + s3_input.allowed_resources.contains(s3_resource_path) + || s3_input.allow_user_resources + }) + .sorted_by_key(|i| i.allow_user_resources) // consider user resources last + .collect::>() + } else { + s3_inputs + .iter() + .filter(|s3_input| s3_input.allow_workspace_resource) + .collect::>() + }; + + let matched_input = matching_s3_inputs.iter().find(|s3_input| { + match Regex::new(&s3_input.file_key_regex) { + Ok(re) => re.is_match(&file_key), + Err(e) => { + tracing::error!("Error compiling regex: {}", e); + false + } + } + }); + + if let Some(matched_input) = matched_input { + if let Some(ref s3_resource_path) = query.s3_resource_path { + if matched_input.allow_user_resources { + if let Some(authed) = opt_authed { + ( + Some( + get_s3_resource( + &authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path, + None, + None, + ) + .await?, + ), + file_key, + ) + } else { + return Err(Error::BadRequest( + "User resources are not allowed without being logged in" + .to_string(), + )); + } + } else { + ( + Some( + get_s3_resource( + &on_behalf_authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path, + None, + None, + ) + .await?, + ), + file_key, + ) + } + } else { + let (_, s3_resource_opt) = + get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None) + .await?; + (s3_resource_opt, file_key) + } + } else { + return Err(Error::BadRequest( + "No matching s3 resource found for the given file key".to_string(), + )); + } + } else { + // no file key => requires unnamed upload policy => allow workspace resource and file_key_regex is empty + let has_unnamed_policy = s3_inputs.iter().any(|s3_input| { + s3_input.allow_workspace_resource && s3_input.file_key_regex.is_empty() + }); + + if !has_unnamed_policy { + return Err(Error::BadRequest( + "no policy found for unnamed s3 file uplooad".to_string(), + )); + } + + // for now, we place all files into `windmill_uploads` folder with a random name + // TODO: make the folder configurable via the workspace settings + let file_key = get_random_file_name(query.file_extension); + + let (_, s3_resource_opt) = + get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None).await?; + + (s3_resource_opt, file_key) + } + } else { + // backward compatibility (no policy) + // if no policy but logged in, use the user's auth to get the s3 resource + if let Some(authed) = opt_authed { + let file_key = query + .file_key + .unwrap_or_else(|| get_random_file_name(query.file_extension)); + + if let Some(ref s3_resource_path) = query.s3_resource_path { + ( + Some( + get_s3_resource( + &authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path, + None, + None, + ) + .await?, + ), + file_key, + ) + } else { + let (_, s3_resource) = + get_workspace_s3_resource(&authed, &db, None, "", &w_id, None).await?; + + (s3_resource, file_key) + } + } else { + return Err(Error::BadRequest("Missing s3 policy".to_string())); + } + }; + + let s3_resource = s3_resource_opt.ok_or(Error::InternalErr( + "No files storage resource defined at the workspace level".to_string(), + ))?; + let s3_client = build_object_store_client(&s3_resource).await?; + + let options = Attributes::from_iter(vec![ + ( + Attribute::ContentType, + query.content_type.unwrap_or_else(|| { + mime_guess::from_path(&file_key) + .first_or_octet_stream() + .to_string() + }), + ), + ( + Attribute::ContentDisposition, + query.content_disposition.unwrap_or("inline".to_string()), + ), + ]) + .into(); + + upload_file_internal(s3_client, &file_key, request, options).await?; + + return Ok(Json(UploadFileResponse { file_key })); +} + fn get_on_behalf_of(policy: &Policy) -> Result<(String, String)> { let permissioned_as = policy .on_behalf_of diff --git a/backend/windmill-api/src/http_triggers.rs b/backend/windmill-api/src/http_triggers.rs index 61757cb164..2c323c0e8c 100644 --- a/backend/windmill-api/src/http_triggers.rs +++ b/backend/windmill-api/src/http_triggers.rs @@ -1,24 +1,5 @@ -use axum::{ - extract::{Path, Query}, - response::IntoResponse, - routing::{delete, get, post}, - Extension, Json, Router, -}; -use http::{HeaderMap, StatusCode}; -use serde::{Deserialize, Serialize}; -use sql_builder::{bind::Bind, SqlBuilder}; -use sqlx::prelude::FromRow; -use std::collections::HashMap; -use tower_http::cors::CorsLayer; -use windmill_audit::{audit_ee::audit_log, ActionKind}; -use windmill_common::{ - db::UserDB, - error::{self, JsonResult}, - utils::{not_found_if_none, paginate, require_admin, Pagination, StripPath}, - worker::{to_raw_value, CLOUD_HOSTED}, -}; -use windmill_queue::PushArgsOwned; - +#[cfg(feature = "parquet")] +use crate::job_helpers_ee::get_workspace_s3_resource; use crate::{ db::{ApiAuthed, DB}, jobs::{ @@ -27,6 +8,31 @@ use crate::{ }, users::{fetch_api_authed, OptAuthed}, }; +use axum::{ + extract::{Path, Query}, + response::IntoResponse, + routing::{delete, get, post}, + Extension, Json, Router, +}; +#[cfg(feature = "parquet")] +use http::header::IF_NONE_MATCH; +use http::{HeaderMap, StatusCode}; +use serde::{Deserialize, Serialize}; +use sql_builder::{bind::Bind, SqlBuilder}; +use sqlx::prelude::FromRow; +use std::collections::HashMap; +use tower_http::cors::CorsLayer; +use windmill_audit::{audit_ee::audit_log, ActionKind}; +#[cfg(feature = "parquet")] +use windmill_common::s3_helpers::build_object_store_client; +use windmill_common::{ + db::UserDB, + error::{self, JsonResult}, + s3_helpers::S3Object, + utils::{not_found_if_none, paginate, require_admin, Pagination, StripPath}, + worker::{to_raw_value, CLOUD_HOSTED}, +}; +use windmill_queue::PushArgsOwned; lazy_static::lazy_static! { static ref ROUTE_PATH_KEY_RE: regex::Regex = regex::Regex::new(r"/:\w+").unwrap(); @@ -99,6 +105,7 @@ struct NewTrigger { is_async: bool, requires_auth: bool, http_method: HttpMethod, + static_asset_config: Option>, } #[derive(FromRow, Serialize)] @@ -116,6 +123,7 @@ struct Trigger { is_async: bool, requires_auth: bool, http_method: HttpMethod, + static_asset_config: Option>, } #[derive(Deserialize)] @@ -127,6 +135,7 @@ struct EditTrigger { is_async: bool, requires_auth: bool, http_method: HttpMethod, + static_asset_config: Option>, } #[derive(Deserialize)] @@ -182,7 +191,7 @@ async fn get_trigger( let path = path.to_path(); let trigger = sqlx::query_as!( Trigger, - r#"SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as "http_method: _", edited_by, email, edited_at, extra_perms, is_async, requires_auth + r#"SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as "http_method: _", edited_by, email, edited_at, extra_perms, is_async, requires_auth, static_asset_config as "static_asset_config: _" FROM http_trigger WHERE workspace_id = $1 AND path = $2"#, w_id, @@ -209,7 +218,7 @@ async fn create_trigger( let mut tx = user_db.begin(&authed).await?; sqlx::query!( - "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now())", + "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, static_asset_config, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now())", w_id, ct.path, ct.route_path, @@ -218,7 +227,8 @@ async fn create_trigger( ct.is_flow, ct.is_async, ct.requires_auth, - ct.http_method as HttpMethod, + ct.http_method as _, + ct.static_asset_config as _, &authed.username, &authed.email ) @@ -261,14 +271,15 @@ async fn update_trigger( sqlx::query!( "UPDATE http_trigger - SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, edited_by = $7, email = $8, is_async = $9, requires_auth = $10, edited_at = now() - WHERE workspace_id = $11 AND path = $12", + SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, static_asset_config = $7, edited_by = $8, email = $9, is_async = $10, requires_auth = $11, edited_at = now() + WHERE workspace_id = $12 AND path = $13", ct.route_path, &route_path_key, ct.script_path, ct.path, ct.is_flow, - ct.http_method as HttpMethod, + ct.http_method as _, + ct.static_asset_config as _, &authed.username, &authed.email, ct.is_async, @@ -279,12 +290,13 @@ async fn update_trigger( .execute(&mut *tx).await?; } else { sqlx::query!( - "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, edited_by = $5, email = $6, is_async = $7, requires_auth = $8, edited_at = now() - WHERE workspace_id = $9 AND path = $10", + "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, static_asset_config = $5, edited_by = $6, email = $7, is_async = $8, requires_auth = $9, edited_at = now() + WHERE workspace_id = $10 AND path = $11", ct.script_path, ct.path, ct.is_flow, - ct.http_method as HttpMethod, + ct.http_method as _, + ct.static_asset_config as _, &authed.username, &authed.email, ct.is_async, @@ -405,6 +417,7 @@ struct TriggerRoute { edited_by: String, email: String, http_method: HttpMethod, + static_asset_config: Option>, } async fn get_http_route_trigger( @@ -421,7 +434,7 @@ async fn get_http_route_trigger( let route_path = StripPath(splitted.collect::>().join("/")); let triggers = sqlx::query_as!( TriggerRoute, - r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _" FROM http_trigger WHERE workspace_id = $1"#, + r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _", static_asset_config as "static_asset_config: _" FROM http_trigger WHERE workspace_id = $1"#, w_id ) .fetch_all(db) @@ -430,7 +443,7 @@ async fn get_http_route_trigger( } else { let triggers = sqlx::query_as!( TriggerRoute, - r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _" FROM http_trigger"#, + r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _", static_asset_config as "static_asset_config: _" FROM http_trigger"#, ) .fetch_all(db) .await?; @@ -518,6 +531,90 @@ async fn route_job( Ok(trigger) => trigger, Err(e) => return e.into_response(), }; + + #[cfg(not(feature = "parquet"))] + if trigger.static_asset_config.is_some() { + return error::Error::InternalErr( + "Static asset configuration is not supported in this build".to_string(), + ) + .into_response(); + } + + #[cfg(feature = "parquet")] + if let Some(sqlx::types::Json(config)) = trigger.static_asset_config { + let build_static_response_f = async { + let (_, s3_resource_opt) = get_workspace_s3_resource( + &authed, + &db, + None, + &"NO_TOKEN".to_string(), // no token is provided in this case + &trigger.workspace_id, + config.storage, + ) + .await?; + let s3_resource = s3_resource_opt.ok_or(error::Error::InternalErr( + "No files storage resource defined at the workspace level".to_string(), + ))?; + let s3_client = build_object_store_client(&s3_resource).await?; + let path = object_store::path::Path::from(config.s3); + let s3_object = s3_client.get(&path).await.map_err(|err| { + tracing::warn!("Error retrieving file from S3: {:?}", err); + error::Error::InternalErr(format!("Error retrieving file: {}", err.to_string())) + })?; + let mut response_headers = http::HeaderMap::new(); + if let Some(ref e_tag) = s3_object.meta.e_tag { + if let Some(if_none_match) = headers.get(IF_NONE_MATCH) { + if if_none_match == e_tag { + return Ok::<_, error::Error>(( + StatusCode::NOT_MODIFIED, + response_headers, + axum::body::Body::empty(), + )); + } + } + if let Ok(e_tag) = e_tag.parse() { + response_headers.insert("etag", e_tag); + } + } + response_headers.insert( + "content-type", + s3_object + .attributes + .get(&object_store::Attribute::ContentType) + .map(|s| s.parse().ok()) + .flatten() + .unwrap_or("application/octet-stream".parse().unwrap()), + ); + response_headers.insert( + "content-disposition", + config.filename.as_ref().map_or_else( + || { + s3_object + .attributes + .get(&object_store::Attribute::ContentDisposition) + .map(|s| s.parse().ok()) + .flatten() + .unwrap_or("inline".parse().unwrap()) + }, + |filename| { + format!("inline; filename=\"{}\"", filename) + .parse() + .unwrap_or("inline".parse().unwrap()) + }, + ), + ); + + let body_stream = axum::body::Body::from_stream(s3_object.into_stream()); + Ok::<_, error::Error>((StatusCode::OK, response_headers, body_stream)) + }; + match build_static_response_f.await { + Ok((status, headers, body_stream)) => { + return (status, headers, body_stream).into_response() + } + Err(e) => return e.into_response(), + } + } + let headers = headers .iter() .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) diff --git a/backend/windmill-api/src/job_helpers_ee.rs b/backend/windmill-api/src/job_helpers_ee.rs index 61a946bb84..3033a2ec5a 100644 --- a/backend/windmill-api/src/job_helpers_ee.rs +++ b/backend/windmill-api/src/job_helpers_ee.rs @@ -1,5 +1,61 @@ use axum::Router; +use serde::Serialize; +use uuid::Uuid; +use windmill_common::s3_helpers::StorageResourceType; + +#[cfg(feature = "parquet")] +use crate::db::{ApiAuthed, DB}; +#[cfg(feature = "parquet")] +use object_store::{ObjectStore, PutMultipartOpts}; +#[cfg(feature = "parquet")] +use std::sync::Arc; +use windmill_common::error; +#[cfg(feature = "parquet")] +use windmill_common::{db::UserDB, s3_helpers::ObjectStoreResource}; +#[derive(Serialize)] +pub struct UploadFileResponse { + pub file_key: String, +} pub fn workspaced_service() -> Router { Router::new() } + +#[cfg(feature = "parquet")] +pub async fn get_workspace_s3_resource<'c>( + _authed: &ApiAuthed, + _db: &DB, + _user_db: Option, + _token: &str, + _w_id: &str, + _storage: Option, +) -> windmill_common::error::Result<(Option, Option)> { + // implementation is not open source + Ok((None, None)) +} + +pub fn get_random_file_name(_file_extension: Option) -> String { + todo!() +} + +pub async fn get_s3_resource<'c>( + _authed: &ApiAuthed, + _db: &DB, + _user_db: Option, + _token: &str, + _w_id: &str, + _resource_path: &str, + _resource_type: Option, + _job_id: Option, +) -> error::Result { + todo!() +} + +pub async fn upload_file_internal( + _s3_client: Arc, + _file_key: &str, + _request: axum::extract::Request, + _options: PutMultipartOpts, +) -> error::Result<()> { + todo!() +} diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 7244b2be17..433c859619 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -65,7 +65,7 @@ use windmill_common::{ db::UserDB, error::{self, to_anyhow, Error}, flow_status::{Approval, FlowStatus, FlowStatusModule}, - flows::FlowValue, + flows::{add_virtual_items_if_necessary, FlowValue}, jobs::{script_path_to_payload, CompletedJob, JobKind, JobPayload, QueuedJob, RawCode}, oauth2::HmacSha256, scripts::{ScriptHash, ScriptLang}, @@ -4275,18 +4275,26 @@ async fn run_flow_dependencies_job( wait_result } +#[derive(Deserialize)] +struct BatchRawScript { + content: String, + language: Option, + lock: Option, +} + #[derive(Deserialize)] struct BatchInfo { kind: String, flow_value: Option, path: Option, + rawscript: Option, } #[tracing::instrument(level = "trace", skip_all)] async fn add_batch_jobs( authed: ApiAuthed, Extension(db): Extension, - Extension(rsmq): Extension>, + Extension(_rsmq): Extension>, Path((w_id, n)): Path<(String, i32)>, Json(batch_info): Json, ) -> error::JsonResult> { @@ -4302,6 +4310,10 @@ async fn add_batch_jobs( concurrent_limit, concurrent_time_window_s, timeout, + raw_code, + raw_lock, + raw_flow, + flow_status ) = match batch_info.kind.as_str() { "script" => { if let Some(path) = batch_info.path { @@ -4329,6 +4341,10 @@ async fn add_batch_jobs( concurrent_limit, concurrency_time_window_s, timeout, + None, + None, + None, + None, ) } else { Err(anyhow::anyhow!( @@ -4336,61 +4352,72 @@ async fn add_batch_jobs( ))? } } - "flow" => { - let mut uuids: Vec = Vec::new(); - let payload = if let Some(ref fv) = batch_info.flow_value { - JobPayload::RawFlow { value: fv.clone(), path: None, restarted_from: None } - } else { - if let Some(path) = batch_info.path.as_ref() { - JobPayload::Flow { - path: path.to_string(), - dedicated_worker: None, - apply_preprocessor: false, - } - } else { - Err(anyhow::anyhow!( - "Path is required if no value is not provided" - ))? - } - }; - let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq); - for _ in 0..n { - let ehm = HashMap::new(); - let (uuid, ntx) = push( - &db, - tx, - &w_id, - payload.clone(), - PushArgs::from(&ehm), - authed.display_username(), - &authed.email, - username_to_permissioned_as(&authed.username), + "rawscript" => { + if let Some(rawscript) = batch_info.rawscript { + ( + None, + None, + JobKind::Preview, + rawscript.language, None, None, None, None, None, - false, - false, - None, - true, + Some(rawscript.content), + rawscript.lock, None, None, - None, - None, - Some(&authed.clone().into()), ) - .await?; - tx = PushIsolationLevel::Transaction(ntx); - uuids.push(uuid); + } else { + Err(anyhow::anyhow!( + "rawscript is required for `rawscript` kind" + ))? } - match tx { - PushIsolationLevel::Transaction(tx) => { - tx.commit().await?; - } - _ => (), - } - return Ok(Json(uuids)); + } + "flow" => { + let (mut value, job_kind, path) = if let Some(value) = batch_info.flow_value { + (value, JobKind::FlowPreview, None) + } else if let Some(path) = batch_info.path { + let value_json = sqlx::query!( + "SELECT flow_version.value AS \"value: sqlx::types::Json>\" FROM flow + LEFT JOIN flow_version + ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] + WHERE flow.path = $1 AND flow.workspace_id = $2", + &path, &w_id + ) + .fetch_optional(&db) + .await? + .ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", path)))?; + let value = + serde_json::from_str::(value_json.value.get()).map_err(|err| { + Error::InternalErr(format!( + "could not convert json to flow for {path}: {err:?}" + )) + })?; + (value, JobKind::Flow, Some(path)) + } else { + Err(anyhow::anyhow!( + "Path is required if no value is not provided" + ))? + }; + add_virtual_items_if_necessary(&mut value.modules); + let flow_status = FlowStatus::new(&value); + ( + None, // script_hash + path, // script_path + job_kind, // job_kind + None, // language + None, // dedicated_worker + value.concurrency_key.clone(), // custom_concurrency_key + value.concurrent_limit.clone(), // concurrent_limit + value.concurrency_time_window_s, // concurrency_time_window_s + None, // timeout + None, // raw_code + None, // raw_lock + Some(value), // raw_flow + Some(flow_status), // flow_status + ) } "noop" => ( None, @@ -4402,6 +4429,10 @@ async fn add_batch_jobs( None, None, None, + None, + None, + None, + None, ), _ => { return Err(error::Error::BadRequest(format!( @@ -4428,8 +4459,8 @@ async fn add_batch_jobs( select gen_random_uuid() as uuid from generate_series(1, $11) ) INSERT INTO queue - (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout) - (SELECT uuid, $1, $2, $3, $4, ('{ "uuid": "' || uuid || '" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14 FROM uuid_table) + (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout, raw_code, raw_lock, raw_flow, flow_status) + (SELECT uuid, $1, $2, $3, $4, ('{ "uuid": "' || uuid || '" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14, $15, $16, $17, $18 FROM uuid_table) RETURNING id"#, hash.map(|h| h.0), path, @@ -4444,7 +4475,11 @@ async fn add_batch_jobs( n, concurrent_limit, concurrent_time_window_s, - timeout + timeout, + raw_code, + raw_lock, + raw_flow.map(sqlx::types::Json) as Option>, + flow_status.map(sqlx::types::Json) as Option> ) .fetch_all(&db) .await?; diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index 0299145a38..5dc46e4382 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -58,7 +58,10 @@ pub fn global_service() -> Router { ) .route("/renew_license_key", post(renew_license_key)) .route("/customer_portal", post(create_customer_portal_session)) - .route("/test_critical_channels", post(test_critical_channels)); + .route("/test_critical_channels", post(test_critical_channels)) + .route("/critical_alerts", get(get_critical_alerts)) + .route("/critical_alerts/:id/acknowledge", post(acknowledge_critical_alert)) + .route("/critical_alerts/acknowledge_all", post(acknowledge_all_critical_alerts)); #[cfg(feature = "parquet")] { @@ -430,3 +433,117 @@ pub async fn test_critical_channels( pub async fn test_critical_channels() -> Result { Ok("Critical channels require EE".to_string()) } + +use serde::Serialize; + +#[derive(Serialize)] +pub struct CriticalAlert { + id: i32, + alert_type: String, + message: String, + created_at: chrono::DateTime, + acknowledged: Option, +} + +#[cfg(feature = "enterprise")] +#[derive(Deserialize)] +pub struct AlertQueryParams { + pub page: Option, + pub page_size: Option, + pub acknowledged: Option, +} + +#[cfg(feature = "enterprise")] +pub async fn get_critical_alerts( + Extension(db): Extension, + authed: ApiAuthed, + Query(params): Query, +) -> JsonResult> { + require_super_admin(&db, &authed.email).await?; + + // Default pagination values if not provided + let page = params.page.unwrap_or(1).max(1); + let page_size = params.page_size.unwrap_or(10).min(100) as i64; + let offset = ((page - 1) * page_size as i32) as i64; + + let alerts = if let Some(acknowledged) = params.acknowledged { + sqlx::query_as!( + CriticalAlert, + "SELECT id, alert_type, message, created_at, acknowledged + FROM alerts + WHERE acknowledged = $1 + ORDER BY created_at DESC + LIMIT $2 OFFSET $3", + acknowledged, + page_size, + offset + ) + .fetch_all(&db) + .await? + } else { + sqlx::query_as!( + CriticalAlert, + "SELECT id, alert_type, message, created_at, acknowledged + FROM alerts + ORDER BY created_at DESC + LIMIT $1 OFFSET $2", + page_size, + offset + ) + .fetch_all(&db) + .await? + }; + + Ok(Json(alerts)) +} + +#[cfg(not(feature = "enterprise"))] +pub async fn get_critical_alerts() -> error::Error { + error::Error::NotFound("Critical Alerts require EE".to_string()) +} + +#[cfg(feature = "enterprise")] +pub async fn acknowledge_critical_alert( + Extension(db): Extension, + authed: ApiAuthed, + Path(id): Path, +) -> error::Result { + require_super_admin(&db, &authed.email).await?; + + sqlx::query!( + "UPDATE alerts SET acknowledged = true WHERE id = $1", + id + ) + .execute(&db) + .await?; + + tracing::info!("Acknowledged critical alert with id: {}", id); + Ok("Critical alert acknowledged".to_string()) +} + +#[cfg(not(feature = "enterprise"))] +pub async fn acknowledge_critical_alert() -> error::Error { + error::Error::NotFound("Critical Alerts require EE".to_string()) +} + +#[cfg(feature = "enterprise")] +pub async fn acknowledge_all_critical_alerts( + Extension(db): Extension, + authed: ApiAuthed, +) -> error::Result { + require_super_admin(&db, &authed.email).await?; + + sqlx::query!( + "UPDATE alerts SET acknowledged = true WHERE acknowledged = false" + ) + .execute(&db) + .await?; + + tracing::info!("Acknowledged all unacknowledged critical alerts"); + Ok("All unacknowledged critical alerts acknowledged".to_string()) +} + +#[cfg(not(feature = "enterprise"))] +pub async fn acknowledge_all_critical_alerts() -> error::Error { + error::Error::NotFound("Critical Alerts require EE".to_string()) +} diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index 005dfad342..380084d524 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -746,10 +746,20 @@ pub async fn fetch_api_authed( username_override: String, ) -> error::Result { let permissioned_as = username_to_permissioned_as(username.as_str()); + fetch_api_authed_from_permissioned_as(permissioned_as, email, w_id, db, username_override).await +} + +pub async fn fetch_api_authed_from_permissioned_as( + permissioned_as: String, + email: String, + w_id: &str, + db: &DB, + username_override: String, +) -> error::Result { let authed = fetch_authed_from_permissioned_as(permissioned_as, email.clone(), w_id, db).await?; Ok(ApiAuthed { - username: username, + username: authed.username, email: email, is_admin: authed.is_admin, is_operator: authed.is_operator, diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index d357a5ec72..5d7e1b4621 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -16,6 +16,7 @@ use rand::Rng; use serde::{Deserialize, Serialize, Serializer}; use crate::{ + error::Error, more_serde::{default_empty_string, default_id, default_null, default_true, is_default}, scripts::{Schema, ScriptHash, ScriptLang}, }; @@ -651,3 +652,29 @@ pub fn add_virtual_items_if_necessary(modules: &mut Vec) { }); } } + +pub async fn has_failure_module<'c>(flow: sqlx::types::Uuid, db: &sqlx::Pool, completed: bool) -> Result { + if completed { + sqlx::query_scalar!( + "SELECT raw_flow->'failure_module' != 'null'::jsonb + FROM completed_job + WHERE id = $1", + flow + ) + } else { + sqlx::query_scalar!( + "SELECT raw_flow->'failure_module' != 'null'::jsonb + FROM queue + WHERE id = $1", + flow + ) + } + .fetch_one(db) + .await + .map_err(|e| { + Error::InternalErr(format!( + "error during retrieval of has_failure_module: {e:#}" + )) + }) + .map(|v| v.unwrap_or(false)) +} diff --git a/backend/windmill-common/src/global_settings.rs b/backend/windmill-common/src/global_settings.rs index b0420ca248..a48db22b1f 100644 --- a/backend/windmill-common/src/global_settings.rs +++ b/backend/windmill-common/src/global_settings.rs @@ -29,6 +29,7 @@ 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 CRITICAL_ALERT_MUTE_UI_SETTING: &str = "critical_alert_mute_ui"; pub const DEV_INSTANCE_SETTING: &str = "dev_instance"; pub const JWT_SECRET_SETTING: &str = "jwt_secret"; pub const EMAIL_DOMAIN_SETTING: &str = "email_domain"; diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index ad96594ac8..8eb57cb236 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -86,6 +86,8 @@ lazy_static::lazy_static! { pub static ref METRICS_ENABLED: AtomicBool = AtomicBool::new(std::env::var("METRICS_PORT").is_ok() || std::env::var("METRICS_ADDR").is_ok()); pub static ref METRICS_DEBUG_ENABLED: AtomicBool = AtomicBool::new(false); + pub static ref CRITICAL_ALERT_MUTE_UI_ENABLED: AtomicBool = AtomicBool::new(false); + pub static ref BASE_URL: Arc> = Arc::new(RwLock::new("".to_string())); pub static ref IS_READY: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); diff --git a/backend/windmill-common/src/s3_helpers.rs b/backend/windmill-common/src/s3_helpers.rs index 4547c29d51..715615f37f 100644 --- a/backend/windmill-common/src/s3_helpers.rs +++ b/backend/windmill-common/src/s3_helpers.rs @@ -109,10 +109,13 @@ pub struct S3AwsOidcResource { pub audience: Option, } -#[derive(Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone)] pub struct S3Object { pub s3: String, + #[serde(skip_serializing_if = "Option::is_none")] pub storage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub filename: Option, } #[cfg(feature = "parquet")] diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 04f95a3406..18c0c93205 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -28,6 +28,9 @@ pub const DEFAULT_PER_PAGE: usize = 1000; pub const GIT_VERSION: &str = git_version!(args = ["--tag", "--always"], fallback = "unknown-version"); +use std::sync::atomic::Ordering; +use crate::CRITICAL_ALERT_MUTE_UI_ENABLED; + lazy_static::lazy_static! { pub static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new() .user_agent("windmill/beta") @@ -244,9 +247,12 @@ pub fn generate_lock_id(database_name: &str) -> i64 { pub async fn report_critical_error(error_message: String, _db: DB) -> () { tracing::error!("CRITICAL ERROR: {error_message}"); + let mute = CRITICAL_ALERT_MUTE_UI_ENABLED.load(Ordering::Relaxed); + if let Err(err) = sqlx::query!( - "INSERT INTO alerts (alert_type, message) VALUES ('critical_error', $1)", - error_message + "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('critical_error', $1, $2)", + error_message, + mute ) .execute(&_db) .await @@ -261,9 +267,12 @@ pub async fn report_critical_error(error_message: String, _db: DB) -> () { pub async fn report_recovered_critical_error(message: String, _db: DB) -> () { tracing::info!("RECOVERED CRITICAL ERROR: {message}"); + let mute = CRITICAL_ALERT_MUTE_UI_ENABLED.load(Ordering::Relaxed); + if let Err(err) = sqlx::query!( - "INSERT INTO alerts (alert_type, message) VALUES ('recovered_critical_error', $1)", - message + "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('recovered_critical_error', $1, $2)", + message, + mute ) .execute(&_db) .await diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 9b94c0f198..275034efe7 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -71,6 +71,9 @@ use windmill_common::BASE_URL; #[cfg(feature = "cloud")] use windmill_common::users::SUPERADMIN_SYNC_EMAIL; +#[cfg(feature = "enterprise")] +use windmill_common::flows::has_failure_module; + #[cfg(feature = "enterprise")] use windmill_common::worker::CLOUD_HOSTED; @@ -482,13 +485,6 @@ where } } -#[cfg(feature = "enterprise")] -#[derive(Deserialize)] -struct RawFlowFailureModule { - #[cfg(feature = "enterprise")] - failure_module: Option>, -} - #[instrument(level = "trace", skip_all)] pub async fn add_completed_job_error( db: &Pool, @@ -952,15 +948,7 @@ pub async fn add_completed_job< } else if !skip_downstream_error_handlers && (matches!(queued_job.job_kind, JobKind::Script) || matches!(queued_job.job_kind, JobKind::Flow) - && queued_job - .raw_flow - .as_ref() - .and_then(|v| { - serde_json::from_str::((**v).get()) - .ok() - .and_then(|v| v.failure_module) - }) - .is_none()) + && !has_failure_module(job_id, db, true).await.unwrap_or(false)) && queued_job.parent_job.is_none() { let result = serde_json::from_str( diff --git a/backend/windmill-worker/src/bash_executor.rs b/backend/windmill-worker/src/bash_executor.rs index 2c4c46ab7d..2582f98b58 100644 --- a/backend/windmill-worker/src/bash_executor.rs +++ b/backend/windmill-worker/src/bash_executor.rs @@ -89,8 +89,18 @@ cat bp | tail -1 >> ./result2.out & # Run main.sh in the same process group {bash} ./main.sh "$@" 2>&1 | tee bp & -# Wait for all background processes to finish -wait +pid=$! + +# Wait for main.sh to finish and capture its exit status +wait $pid +exit_status=$? + +# Clean up the named pipe and background processes +rm -f bp + + +# Exit with the captured status +exit $exit_status "#, bash = BIN_BASH.as_str(), ); diff --git a/backend/windmill-worker/src/common.rs b/backend/windmill-worker/src/common.rs index 80269f7777..b406226120 100644 --- a/backend/windmill-worker/src/common.rs +++ b/backend/windmill-worker/src/common.rs @@ -799,6 +799,7 @@ pub async fn get_cached_resource_value_if_valid( S3Object { s3: s3_file_key.clone(), storage: cached_resource.storage.clone(), + filename: None, }, ) .await; diff --git a/backend/windmill-worker/src/handle_child.rs b/backend/windmill-worker/src/handle_child.rs index 6846e9dfee..4d09c36abc 100644 --- a/backend/windmill-worker/src/handle_child.rs +++ b/backend/windmill-worker/src/handle_child.rs @@ -48,6 +48,7 @@ use futures::{ use crate::common::{resolve_job_timeout, OccupancyMetrics}; use crate::job_logger::{append_job_logs, append_with_limit, LARGE_LOG_THRESHOLD_SIZE}; +use crate::job_logger_ee::process_streaming_log_lines; use crate::{MAX_RESULT_SIZE, MAX_WAIT_FOR_SIGINT, MAX_WAIT_FOR_SIGTERM}; lazy_static::lazy_static! { @@ -434,6 +435,8 @@ pub async fn handle_child( } } + + async fn get_mem_peak(pid: Option, nsjail: bool) -> i32 { if pid.is_none() { return -1; @@ -689,19 +692,24 @@ fn child_joined_output_stream( let stdout = BufReader::new(stdout).lines(); let stderr = BufReader::new(stderr).lines(); - stream::select(lines_to_stream(stderr), lines_to_stream(stdout)) + stream::select(lines_to_stream(stderr, true), lines_to_stream(stdout, false)) } pub fn lines_to_stream( mut lines: tokio::io::Lines, + stderr: bool, ) -> impl futures::Stream> { stream::poll_fn(move |cx| { std::pin::Pin::new(&mut lines) .poll_next_line(cx) - .map(|result| result.transpose()) + .map(|result| { + process_streaming_log_lines(result, stderr) + }) }) } + + pub fn process_status(status: ExitStatus) -> error::Result<()> { if status.success() { Ok(()) diff --git a/backend/windmill-worker/src/job_logger.rs b/backend/windmill-worker/src/job_logger.rs index 05b9e5812b..f92ee16536 100644 --- a/backend/windmill-worker/src/job_logger.rs +++ b/backend/windmill-worker/src/job_logger.rs @@ -1,15 +1,6 @@ -use itertools::Itertools; -use swc_ecma_parser::lexer::util::CharExt; - -#[cfg(all(feature = "enterprise", feature = "parquet"))] -use object_store::path::Path; use regex::Regex; -#[cfg(all(feature = "enterprise", feature = "parquet"))] -use windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS; - -use windmill_common::error::{self}; -use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR}; +use windmill_common::worker::CLOUD_HOSTED; use windmill_queue::append_logs; @@ -19,6 +10,12 @@ use std::sync::Arc; use uuid::Uuid; use windmill_common::DB; +use crate::job_logger_ee::default_disk_log_storage; + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +use crate::job_logger_ee::s3_storage; + + pub enum CompactLogs { #[cfg(not(all(feature = "enterprise", feature = "parquet")))] NotEE, @@ -28,150 +25,7 @@ pub enum CompactLogs { S3, } -async fn compact_logs( - job_id: Uuid, - w_id: &str, - db: &DB, - nlogs: String, - total_size: Arc, - compact_kind: CompactLogs, - _worker_name: &str, -) -> error::Result<(String, String)> { - let mut prev_logs = sqlx::query_scalar!( - "SELECT logs FROM job_logs WHERE job_id = $1 AND workspace_id = $2", - job_id, - w_id - ) - .fetch_optional(db) - .await? - .flatten() - .unwrap_or_default(); - let size = prev_logs.char_indices().count() as i32; - let nlogs_len = nlogs.char_indices().count(); - let to_keep_in_db = usize::max( - usize::min(nlogs_len, 3000), - nlogs_len % LARGE_LOG_THRESHOLD_SIZE, - ); - let extra_split = to_keep_in_db < nlogs_len; - let stored_in_storage_len = if extra_split { - nlogs_len - to_keep_in_db - } else { - 0 - }; - let extra_to_newline = nlogs - .chars() - .skip(stored_in_storage_len) - .find_position(|x| x.is_line_break()) - .map(|(i, _)| i) - .unwrap_or(to_keep_in_db); - let stored_in_storage_to_newline = stored_in_storage_len + extra_to_newline; - let (append_to_storage, stored_in_db) = if extra_split { - if stored_in_storage_to_newline == nlogs.len() { - (nlogs.as_ref(), "".to_string()) - } else { - let split_idx = nlogs - .char_indices() - .nth(stored_in_storage_to_newline) - .map(|(i, _)| i) - .unwrap_or(0); - let (append_to_storage, stored_in_db) = nlogs.split_at(split_idx); - // tracing::error!("{append_to_storage} ||||| {stored_in_db}"); - // tracing::error!( - // "{:?} {:?} {} {}", - // excess_prev_logs.lines().last(), - // current_logs.lines().next(), - // split_idx, - // excess_size_modulo - // ); - (append_to_storage, stored_in_db.to_string()) - } - } else { - // tracing::error!("{:?}", nlogs.lines().last()); - ("", nlogs.to_string()) - }; - - let new_size_with_excess = size + stored_in_storage_to_newline as i32; - - let new_size = total_size.fetch_add( - new_size_with_excess as u32, - std::sync::atomic::Ordering::SeqCst, - ) + new_size_with_excess as u32; - - let path = format!( - "logs/{job_id}/{}_{new_size}.txt", - chrono::Utc::now().timestamp_millis() - ); - - let mut new_current_logs = match compact_kind { - CompactLogs::NoS3 => format!("\n[windmill] No object storage set in instance settings. Previous logs have been saved to disk at {path}"), - CompactLogs::S3 => format!("\n[windmill] Previous logs have been saved to object storage at {path}"), - #[cfg(not(all(feature = "enterprise", feature = "parquet")))] - CompactLogs::NotEE => format!("\n[windmill] Previous logs have been saved to disk at {path}"), - }; - new_current_logs.push_str(&stored_in_db); - - sqlx::query!( - "UPDATE job_logs SET logs = $1, log_offset = $2, - log_file_index = array_append(coalesce(log_file_index, array[]::text[]), $3) - WHERE workspace_id = $4 AND job_id = $5", - new_current_logs, - new_size as i32, - path, - w_id, - job_id - ) - .execute(db) - .await?; - prev_logs.push_str(&append_to_storage); - - return Ok((prev_logs, path)); -} - -async fn default_disk_log_storage( - job_id: Uuid, - w_id: &str, - db: &DB, - nlogs: String, - total_size: Arc, - compact_kind: CompactLogs, - worker_name: &str, -) { - match compact_logs( - job_id, - &w_id, - &db, - nlogs, - total_size, - compact_kind, - worker_name, - ) - .await - { - Err(e) => tracing::error!("Could not compact logs for job {job_id}: {e:?}",), - Ok((prev_logs, path)) => { - let path = format!("{}/{}", TMP_DIR, path); - let splitted = &path.split("/").collect_vec(); - tokio::fs::create_dir_all(splitted.into_iter().take(splitted.len() - 1).join("/")) - .await - .map_err(|e| { - tracing::error!("Could not create logs directory: {e:?}",); - e - }) - .ok(); - let created = tokio::fs::File::create(&path).await; - if let Err(e) = created { - tracing::error!("Could not create logs file {path}: {e:?}",); - return; - } - if let Err(e) = tokio::fs::write(&path, prev_logs).await { - tracing::error!("Could not write to logs file {path}: {e:?}"); - } else { - tracing::info!("Logs length of {job_id} has exceeded a threshold. Previous logs have been saved to disk at {path}"); - } - } - } -} pub(crate) async fn append_job_logs( job_id: Uuid, @@ -184,43 +38,7 @@ pub(crate) async fn append_job_logs( ) -> () { if must_compact_logs { #[cfg(all(feature = "enterprise", feature = "parquet"))] - if let Some(os) = OBJECT_STORE_CACHE_SETTINGS.read().await.clone() { - match compact_logs( - job_id, - &w_id, - &db, - logs, - total_size, - CompactLogs::S3, - &worker_name, - ) - .await - { - Err(e) => tracing::error!("Could not compact logs for job {job_id}: {e:?}",), - Ok((prev_logs, path)) => { - tracing::info!("Logs length of {job_id} has exceeded a threshold. Previous logs have been saved to object storage at {path}"); - let path2 = path.clone(); - if let Err(e) = os - .put(&Path::from(path), prev_logs.to_string().into_bytes().into()) - .await - { - tracing::error!("Could not save logs to s3: {e:?}"); - } - tracing::info!("Logs of {job_id} saved to object storage at {path2}"); - } - } - } else { - default_disk_log_storage( - job_id, - &w_id, - &db, - logs, - total_size, - CompactLogs::NoS3, - &worker_name, - ) - .await; - } + s3_storage(job_id, &w_id, &db, logs, total_size, &worker_name).await; #[cfg(not(all(feature = "enterprise", feature = "parquet")))] { @@ -252,6 +70,7 @@ pub fn append_with_limit(dst: &mut String, src: &str, limit: &mut usize) { if *NO_LOGS_AT_ALL { return; } + let src_str; let src = { src_str = RE_00.replace_all(src, ""); diff --git a/backend/windmill-worker/src/job_logger_ee.rs b/backend/windmill-worker/src/job_logger_ee.rs new file mode 100644 index 0000000000..e0758b31f3 --- /dev/null +++ b/backend/windmill-worker/src/job_logger_ee.rs @@ -0,0 +1,30 @@ +use std::io; +use std::sync::atomic::AtomicU32; +use std::sync::Arc; + +use uuid::Uuid; +use windmill_common::DB; + +use crate::job_logger::CompactLogs; + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +pub(crate) async fn s3_storage(_job_id: Uuid, _w_id: &String, _db: &sqlx::Pool, _logs: &String, _total_size: &Arc, _worker_name: &String) { + tracing::info!("Logs length of {job_id} has exceeded a threshold. Implementation to store excess on s3 in not OSS"); +} + +pub(crate) async fn default_disk_log_storage( + job_id: Uuid, + _w_id: &str, + _db: &DB, + _nlogs: String, + _total_size: Arc, + _compact_kind: CompactLogs, + _worker_name: &str, +) { + tracing::info!("Logs length of {job_id} has exceeded a threshold. Implementation to store excess on disk in not OSS"); +} + + +pub(crate) fn process_streaming_log_lines(r: Result, io::Error>, _stderr: bool) -> Option> { + r.transpose() +} \ No newline at end of file diff --git a/backend/windmill-worker/src/lib.rs b/backend/windmill-worker/src/lib.rs index b790eb3faf..680f3ca178 100644 --- a/backend/windmill-worker/src/lib.rs +++ b/backend/windmill-worker/src/lib.rs @@ -29,7 +29,7 @@ mod rust_executor; mod worker; mod worker_flow; mod worker_lockfiles; - +mod job_logger_ee; pub use worker::*; pub use result_processor::handle_job_error; diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index 112b3c84ca..9db4ed529b 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -199,8 +199,11 @@ pub async fn uv_pip_compile( .clone() .map(handle_ephemeral_token); if let Some(url) = pip_extra_index_url.as_ref() { - args.extend(["--extra-index-url", url, "--no-emit-index-url"]); - pip_args.push(format!("--extra-index-url {}", url)); + url.split(",").for_each(|url| { + args.extend(["--extra-index-url", url]); + pip_args.push(format!("--extra-index-url {}", url)); + }); + args.push("--no-emit-index-url"); } let pip_index_url = PIP_INDEX_URL .read() @@ -279,7 +282,9 @@ pub async fn uv_pip_compile( .clone() .map(handle_ephemeral_token); if let Some(url) = pip_extra_index_url.as_ref() { - args.extend(["--extra-index-url", url]); + url.split(",").for_each(|url| { + args.extend(["--extra-index-url", url]); + }); } let pip_index_url = PIP_INDEX_URL .read() @@ -1167,7 +1172,9 @@ pub async fn handle_python_reqs( .map(handle_ephemeral_token); if let Some(url) = pip_extra_index_url.as_ref() { - command_args.extend(["--extra-index-url", url]); + url.split(",").for_each(|url| { + command_args.extend(["--extra-index-url", url]); + }); } let pip_index_url = PIP_INDEX_URL .read() diff --git a/backend/windmill-worker/src/result_processor.rs b/backend/windmill-worker/src/result_processor.rs index 6c4f43746f..fa08fe377b 100644 --- a/backend/windmill-worker/src/result_processor.rs +++ b/backend/windmill-worker/src/result_processor.rs @@ -295,6 +295,7 @@ pub async fn process_result( message: format!("error during execution of the script:\n{}", err), name: "ExecutionErr".to_string(), step_id: job.flow_step_id.clone(), + exit_code: None, }), }; @@ -608,6 +609,8 @@ pub struct SerializedError { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub step_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub exit_code: Option, } pub fn extract_error_value(log_lines: &str, i: i32, step_id: Option) -> Box { return to_raw_value(&SerializedError { @@ -617,5 +620,6 @@ pub fn extract_error_value(log_lines: &str, i: i32, step_id: Option) -> ), name: "ExecutionErr".to_string(), step_id, + exit_code: Some(i), }); } diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index a1a4fd9904..5421b502e4 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -49,7 +49,7 @@ use windmill_common::{ Approval, BranchAllStatus, BranchChosen, FlowStatus, FlowStatusModule, RetryStatus, MAX_RETRY_ATTEMPTS, MAX_RETRY_INTERVAL, }, - flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform, Retry, Suspend}, + flows::{has_failure_module, FlowModule, FlowModuleValue, FlowValue, InputTransform, Retry, Suspend}, }; use windmill_queue::schedule::get_schedule_opt; use windmill_queue::{ @@ -963,7 +963,7 @@ pub async fn update_flow_status_after_job_completion_internal< false if !is_failure_step && !skip_error_handler - && has_failure_module(flow, db).await? => + && has_failure_module(flow, db, false).await? => { true } @@ -1290,23 +1290,6 @@ async fn compute_skip_branchall_failure<'c>( })) } -async fn has_failure_module<'c>(flow: Uuid, db: &DB) -> Result { - sqlx::query_scalar::<_, Option>( - "SELECT raw_flow->'failure_module' != 'null'::jsonb - FROM queue - WHERE id = $1", - ) - .bind(flow) - .fetch_one(db) - .await - .map_err(|e| { - Error::InternalErr(format!( - "error during retrieval of has_failure_module: {e:#}" - )) - }) - .map(|v| v.unwrap_or(false)) -} - // async fn retrieve_cleanup_module<'c>(flow_uuid: Uuid, db: &DB) -> Result { // tracing::warn!("Retrieving cleanup module of flow {}", flow_uuid); // let raw_value = sqlx::query_scalar!( diff --git a/benchmarks/benchmark_oneoff.ts b/benchmarks/benchmark_oneoff.ts index 24ba4e2c13..41321b3ec6 100644 --- a/benchmarks/benchmark_oneoff.ts +++ b/benchmarks/benchmark_oneoff.ts @@ -8,6 +8,7 @@ import { DenoLandProvider } from "https://deno.land/x/cliffy@v0.25.7/command/upg 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"; import { VERSION, createBenchScript, getFlowPayload, login } from "./lib.ts"; @@ -172,8 +173,8 @@ export async function main({ kind: "script", path: "f/benchmarks/" + kind, }); - } else if (["2steps"].includes(kind)) { - nStepsFlow = 2; + } else if (["2steps", "bigscriptinflow"].includes(kind)) { + nStepsFlow = kind == "2steps" ? 2 : 1; const payload = getFlowPayload(kind); body = JSON.stringify({ kind: "flow", @@ -194,6 +195,15 @@ export async function main({ kind: "script", path: kind.substr(7), }); + } else if (kind == "bigrawscript") { + noVerify = true; + body = JSON.stringify({ + kind: "rawscript", + rawscript: { + language: api.RawScript.language.BASH, + content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(25000) + "echo \"$WM_FLOW_JOB_ID\"\n", + }, + }); } else { throw new Error("Unknown script pattern " + kind); } @@ -246,7 +256,7 @@ export async function main({ } else { const elapsed = start ? Date.now() - start : 0; completedJobs = await getCompletedJobsCount(); - if (kind === "2steps" || kind.startsWith("flow:")) { + if (nStepsFlow > 0) { completedJobs = Math.floor(completedJobs / (nStepsFlow + 1)); } const avgThr = ((completedJobs / elapsed) * 1000).toFixed(2); diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 85484f8915..d59c074be2 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.419.0"; +export const VERSION = "v1.423.2"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ @@ -235,6 +235,23 @@ export const getFlowPayload = (flowPattern: string): api.FlowPreview => { ], }, }; + } else if (flowPattern == "bigscriptinflow") { + return { + path: "bigscriptinflow", + args: {}, + value: { + modules: [ + { + value: { + input_transforms: {}, + language: api.RawScript.language.BASH, + type: "rawscript", + content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(25000) + "echo \"$WM_FLOW_JOB_ID\"\n", + }, + } + ], + } + }; } else { return { path: "2steps", diff --git a/benchmarks/suite_config.json b/benchmarks/suite_config.json index 568402ca2d..7619d4d3b9 100644 --- a/benchmarks/suite_config.json +++ b/benchmarks/suite_config.json @@ -30,5 +30,13 @@ { "kind": "nativets", "jobs": 2000 + }, + { + "kind": "bigrawscript", + "jobs": 10000 + }, + { + "kind": "bigscriptinflow", + "jobs": 10000 } ] \ No newline at end of file diff --git a/cli/main.ts b/cli/main.ts index 87dba5b461..c35b0c7a27 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -60,7 +60,7 @@ export { // } // }); -export const VERSION = "1.419.0"; +export const VERSION = "1.423.2"; const command = new Command() .name("wmill") diff --git a/flake.nix b/flake.nix index d6fbd0fdf2..2c1845d176 100644 --- a/flake.nix +++ b/flake.nix @@ -13,31 +13,23 @@ overlays = [ (import rust-overlay) ]; }; buildInputs = with pkgs; [ - openssl.dev libxml2.dev xmlsec.dev libxslt.dev - rust-bin.beta.latest.default nodejs go - xcaddy sqlx-cli pkg-config + openssl openssl.dev libxml2.dev xmlsec.dev libxslt.dev + rust-bin.stable.latest.default nodejs + postgresql + pkg-config ]; - environment = { - NODE_ENV = "development"; - NODE_OPTIONS = "--max-old-space-size=16384"; - DATABASE_URL = "postgres://postgres:changeme@127.0.0.1:5432/"; - PKG_CONFIG_PATH = pkgs.lib.makeSearchPath "lib/pkgconfig" (with pkgs; [ - openssl.dev - libxml2.dev - xmlsec.dev - libxslt.dev - ]); - }; - rustcTarget = pkgs.hostPlatform.rust.rustcTarget; - fetch_librusty_v8 = { version, shas }: pkgs.fetchurl { - name = "librusty_v8-${version}"; - url = "https://github.com/denoland/rusty_v8/releases/download/v${version}/librusty_v8_release_${rustcTarget}.a.gz"; - sha256 = shas.${system}; - }; + PKG_CONFIG_PATH = pkgs.lib.makeSearchPath "lib/pkgconfig" (with pkgs; [ + openssl.dev + libxml2.dev + xmlsec.dev + libxslt.dev + ]); in { devShell = pkgs.mkShell { - inherit (environment) NODE_ENV NODE_OPTIONS DATABASE_URL PKG_CONFIG_PATH; - buildInputs = buildInputs; + buildInputs = buildInputs ++ (with pkgs; [ + git xcaddy sqlx-cli flock rust-analyzer + deno python3 python3Packages.pip go bun uv + ]); packages = [ (pkgs.writeScriptBin "wm-caddy" '' cd ./frontend @@ -54,34 +46,55 @@ '') (pkgs.writeScriptBin "wm-migrate" '' cd ./backend - sqlx database reset - sqlx database create sqlx migrate run '') (pkgs.writeScriptBin "wm-setup" '' + sqlx database create wm-build wm-caddy wm-migrate '') + (pkgs.writeScriptBin "wm-reset" '' + sqlx database drop -f + sqlx database create + wm-migrate + '') + (pkgs.writeScriptBin "wm-bench" '' + deno run -A benchmarks/main.ts -e admin@windmill.dev -p changeme $* + '') (pkgs.writeScriptBin "wm" '' cd ./frontend npm run dev $* '') ]; + + inherit PKG_CONFIG_PATH; + NODE_ENV = "development"; + NODE_OPTIONS = "--max-old-space-size=16384"; + DATABASE_URL = "postgres://postgres:changeme@127.0.0.1:5432/"; + REMOTE = "http://127.0.0.1:8000"; + REMOTE_LSP = "http://127.0.0.1:3001"; + DENO_PATH = "${pkgs.deno}/bin/deno"; + PYTHON_PATH = "${pkgs.python3}/bin/python3"; + GO_PATH = "${pkgs.go}/bin/go"; + BUN_PATH = "${pkgs.bun}/bin/bun"; + UV_PATH = "${pkgs.uv}/bin/uv"; + FLOCK_PATH = "${pkgs.flock}/bin/flock"; }; + packages.default = self.packages.${system}.windmill; packages.windmill-client = pkgs.stdenv.mkDerivation { pname = "windmill-client"; version = (pkgs.lib.strings.trim (builtins.readFile ./version.txt)); src = ./.; - buildInputs = with pkgs; [ nodejs go ]; + buildInputs = with pkgs; [ nodejs ]; buildPhase = '' export HOME=$(pwd) npm config set strict-ssl false cd frontend npm install --verbose - npm run generate-backend-client-mac + npm run ${if pkgs.stdenv.isDarwin then "generate-backend-client-mac" else "generate-backend-client"} NODE_OPTIONS="--max-old-space-size=8192" npm run build ''; @@ -91,8 +104,6 @@ ''; }; packages.windmill = pkgs.rustPlatform.buildRustPackage { - inherit (environment) DATABASE_URL PKG_CONFIG_PATH; - pname = "windmill"; version = (pkgs.lib.strings.trim (builtins.readFile ./version.txt)); @@ -111,26 +122,32 @@ }; }; - buildFeatures = [ "enterprise" ]; + buildFeatures = [ ]; doCheck = false; preBuild = '' export HOME=$(pwd) npm config set strict-ssl false cd backend - sqlx database create - sqlx migrate run ''; + inherit PKG_CONFIG_PATH; + SQLX_OFFLINE = true; FRONTEND_BUILD_DIR = "${self.packages.${system}.windmill-client}/build"; - RUSTY_V8_ARCHIVE = (fetch_librusty_v8 { - version = "130.0.1"; - shas = { - x86_64-linux = pkgs.lib.fakeHash; - aarch64-linux = pkgs.lib.fakeHash; - x86_64-darwin = pkgs.lib.fakeHash; - aarch64-darwin = "sha256-d1QTLt8gOUFxACes4oyIYgDF/srLOEk+5p5Oj1ECajQ="; + RUSTY_V8_ARCHIVE = + let + version = "130.0.1"; + target = pkgs.hostPlatform.rust.rustcTarget; + sha256 = { + x86_64-linux = pkgs.lib.fakeHash; + aarch64-linux = pkgs.lib.fakeHash; + x86_64-darwin = pkgs.lib.fakeHash; + aarch64-darwin = "sha256-d1QTLt8gOUFxACes4oyIYgDF/srLOEk+5p5Oj1ECajQ="; + }.${system}; + in pkgs.fetchurl { + name = "librusty_v8-${version}"; + url = "https://github.com/denoland/rusty_v8/releases/download/v${version}/librusty_v8_release_${target}.a.gz"; + inherit sha256; }; - }); }; }); } diff --git a/frontend/README_DEV.md b/frontend/README_DEV.md index 198657d3af..ba90278be4 100644 --- a/frontend/README_DEV.md +++ b/frontend/README_DEV.md @@ -1,5 +1,7 @@ # Developing +[Using Nix](#nix), otherwise: + In the `frontend/` directory: - install the dependencies with `npm install` (or `pnpm install` or `yarn`) @@ -136,3 +138,45 @@ NOTCATCHALL=true npm run build which will generate an index.html and allow you to serve the frontend with any static server. Env variables used for build are set in .env file. See [https://vitejs.dev/guide/env-and-mode.html#env-files](https://vitejs.dev/guide/env-and-mode.html#env-files) for more details. + +## Nix + +Windmill use [nix flakes](https://nixos.wiki/wiki/Flakes): +```bash +nix run github:windmill-labs/windmill +``` + +### Development +```bash +nix develop # enter a dev shell containing all necessary packages. + +wm-setup # build the frontend and setup the database. +wm # run the frontend. + +# In an other shell: +nix develop +cd backend +cargo run +``` + +### Updating [`flake.nix`](../flake.nix) + +```bash +nix flake update # update the lock file. +``` + +Some cargo dependencies use fixed git revisions, which are also fixed in `flake.nix`: +```nix +outputHashes = { + "php-parser-rs-0.1.3" = "sha256-ZeI3KgUPmtjlRfq6eAYveqt8Ay35gwj6B9iOQRjQa9A="; + # ... +}; +``` + +When updating a revision, replace the incorrect `sha256` with `pkgs.lib.fakeHash`: +```diff +- "php-parser-rs-0.1.3" = "sha256-ZeI3KgUPmtjlRfq6eAYveqt8Ay35gwj6B9iOQRjQa9A="; ++ "php-parser-rs-0.1.3" = pkgs.lib.fakeHash; +``` + +Then run `nix build .#windmill` and update with the correct sha. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 49ea5ca116..2b1bd0e614 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.419.0", + "version": "1.423.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.419.0", + "version": "1.423.2", "license": "AGPL-3.0", "dependencies": { "@aws-crypto/sha256-js": "^4.0.0", diff --git a/frontend/package.json b/frontend/package.json index 48dca74fed..4465bf84b5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.419.0", + "version": "1.423.2", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/frontend/src/lib/components/ArgInfo.svelte b/frontend/src/lib/components/ArgInfo.svelte index dc6f8e153d..16ba420372 100644 --- a/frontend/src/lib/components/ArgInfo.svelte +++ b/frontend/src/lib/components/ArgInfo.svelte @@ -98,7 +98,7 @@ }}> {/if} -
+
diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index bbaf166e0d..ef20ebdb35 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -418,6 +418,9 @@ bind:selected={value} options={itemsType?.multiselect ?? []} selectedOptionsDraggable={true} + on:open={() => { + dispatch('focus') + }} /> {:else if itemsType?.enum != undefined && Array.isArray(itemsType?.enum) && Array.isArray(value)} @@ -428,6 +431,9 @@ bind:selected={value} options={itemsType?.enum ?? []} selectedOptionsDraggable={true} + on:open={() => { + dispatch('focus') + }} /> {:else} @@ -816,6 +822,12 @@ bind:value {enum_} {autofocus} + on:focus={() => { + dispatch('focus') + }} + on:blur={(e) => { + dispatch('blur') + }} enumLabels={extra['enumLabels']} /> @@ -884,14 +896,16 @@
{#if password || extra?.['password'] == true} - {#if value && typeof value == 'string' && value?.startsWith('$var:')} - - {:else if onlyMaskPassword} - + {#if onlyMaskPassword} + {#if value && typeof value == 'string' && value?.startsWith('$var:')} + + {:else} + + {/if} {:else} {/if} diff --git a/frontend/src/lib/components/EditableSchemaForm.svelte b/frontend/src/lib/components/EditableSchemaForm.svelte index e85616319b..3bb5765ddc 100644 --- a/frontend/src/lib/components/EditableSchemaForm.svelte +++ b/frontend/src/lib/components/EditableSchemaForm.svelte @@ -349,6 +349,7 @@ {isAppInput} on:change={() => { schema = schema + // console.log('schema', schema) dispatch('change', schema) }} > diff --git a/frontend/src/lib/components/FlowGraphViewer.svelte b/frontend/src/lib/components/FlowGraphViewer.svelte index 1ecef2943f..326e1275b6 100644 --- a/frontend/src/lib/components/FlowGraphViewer.svelte +++ b/frontend/src/lib/components/FlowGraphViewer.svelte @@ -7,6 +7,7 @@ import FlowGraphViewerStep from './FlowGraphViewerStep.svelte' import FlowGraphV2 from './graph/FlowGraphV2.svelte' import { dfs } from './flows/dfs' + import { workspaceStore } from '$lib/stores' export let flow: { summary: string @@ -22,6 +23,7 @@ export let noGraph = false export let triggerNode = false export let stepDetail: FlowModule | string | undefined = undefined + export let workspace: string | undefined = $workspaceStore const dispatch = createEventDispatcher() @@ -37,6 +39,7 @@ path={flow?.path} {download} minHeight={400} + {workspace} modules={flow?.value?.modules} failureModule={flow?.value?.failure_module} preprocessorModule={flow?.value?.preprocessor_module} diff --git a/frontend/src/lib/components/FlowStatusViewerInner.svelte b/frontend/src/lib/components/FlowStatusViewerInner.svelte index ec6982a5df..2c9aa54461 100644 --- a/frontend/src/lib/components/FlowStatusViewerInner.svelte +++ b/frontend/src/lib/components/FlowStatusViewerInner.svelte @@ -29,6 +29,7 @@ import Alert from './common/alert/Alert.svelte' import FlowGraphViewerStep from './FlowGraphViewerStep.svelte' import FlowGraphV2 from './graph/FlowGraphV2.svelte' + import { buildPrefix } from './graph/graphBuilder' const dispatch = createEventDispatcher() @@ -74,6 +75,10 @@ export let wideResults = false export let hideFlowResult = false + export let workspace: string | undefined = $workspaceStore + export let prefix: string | undefined = undefined + export let subflowParentsGlobalModuleStates: Writable>[] = [] + export let subflowParentsDurationStatuses: Writable>[] = [] let jobResults: any[] = flowJobIds?.flowJobs?.map((x, id) => `iter #${id + 1} not loaded by frontend yet`) ?? [] @@ -83,6 +88,7 @@ let localModuleStates: Writable> = writable({}) let localDurationStatuses: Writable> = writable({}) + let expandedSubflows: Record = {} export let job: Job | undefined = undefined @@ -94,6 +100,24 @@ // } // } + function updateModuleStates( + moduleState: Writable>, + key: string, + newValue: GraphModuleState, + keepType: boolean | undefined + ) { + moduleState.update((x) => { + if (keepType && (x[key]?.type == 'Success' || x[key]?.type == 'Failure')) { + newValue.type = x[key].type + } + x[key] = newValue + return x + }) + } + + function buildSubflowKey(key: string, prefix: string | undefined) { + return prefix ? 'subflow:' + prefix + key : key + } function setModuleState( key: string, value: Partial, @@ -102,15 +126,14 @@ ) { let newValue = { ...($localModuleStates[key] ?? {}), ...value } if (!deepEqual($localModuleStates[key], value) || force) { - ;[localModuleStates, ...globalModuleStates].forEach((s) => { - s.update((x) => { - if (keepType && (x[key]?.type == 'Success' || x[key]?.type == 'Failure')) { - newValue.type = x[key].type - } - x[key] = newValue - return x - }) - }) + ;[localModuleStates, ...globalModuleStates].forEach((s) => + updateModuleStates(s, key, newValue, keepType) + ) + if (prefix) { + subflowParentsGlobalModuleStates.forEach((s) => + updateModuleStates(s, buildSubflowKey(key, prefix), newValue, keepType) + ) + } } } @@ -124,6 +147,14 @@ return x }) }) + if (prefix) { + subflowParentsDurationStatuses.forEach((s) => { + s.update((x) => { + x[buildSubflowKey(key, prefix)].byJob[id] = value + return x + }) + }) + } } } @@ -131,15 +162,25 @@ if ($localDurationStatuses[modId] == undefined) { $localDurationStatuses[modId] = { byJob: {} } } - let prefixed = modId globalDurationStatuses.forEach((x) => x.update((x) => { - if (x[prefixed] == undefined) { - x[prefixed] = { byJob: {} } + if (x[modId] == undefined) { + x[modId] = { byJob: {} } } return x }) ) + if (prefix) { + subflowParentsDurationStatuses.forEach((x) => + x.update((x) => { + let key = buildSubflowKey(modId, prefix) + if (x[key] == undefined) { + x[key] = { byJob: {} } + } + return x + }) + ) + } } let innerModules: FlowStatusModule[] = [] @@ -368,7 +409,7 @@ if (globalRefreshes) { let modId = flowJobIds?.moduleId if (modId) { - globalRefreshes[modId] = async (loopJob) => { + globalRefreshes[buildSubflowKey(modId, prefix)] = async (loopJob) => { setIteration(loopJob.index, loopJob.job, false, modId ?? '') refresh(true, loopJob) } @@ -631,6 +672,36 @@ }) } } + + function allModulesForTimeline( + modules: FlowModule[], + expandedSubflows: Record + ): string[] { + const ids = dfs(modules, (x) => x.id) + + function rec(ids: string[], prefix: string | undefined): string[] { + return ids.concat( + ids.flatMap((id) => { + let fms = expandedSubflows[id] + let oid = id.split(':').pop() + if (!oid) { + return [] + } + let nprefix = buildPrefix(prefix, oid) + return fms + ? rec( + dfs(fms, (x) => + x.id.startsWith('subflow:') ? x.id : buildSubflowKey(x.id, nprefix) + ), + nprefix + ) + : [] + }) + ) + } + + return rec(ids, undefined) + } {#if notAnonynmous} @@ -803,6 +874,9 @@ job={storedListJobs[j]} globalModuleStates={[localModuleStates, ...globalModuleStates]} globalDurationStatuses={[localDurationStatuses, ...globalDurationStatuses]} + {prefix} + {subflowParentsGlobalModuleStates} + {subflowParentsDurationStatuses} render={forloop_selected == loopJobId && selected == 'sequence' && render} reducedPolling={flowJobIds?.flowJobs.length && flowJobIds?.flowJobs.length > 20} {workspaceId} @@ -880,6 +954,9 @@ {childFlow} globalModuleStates={[localModuleStates, ...globalModuleStates]} globalDurationStatuses={[localDurationStatuses, ...globalDurationStatuses]} + {prefix} + {subflowParentsGlobalModuleStates} + {subflowParentsDurationStatuses} render={failedRetry == retry_selected && render} reducedPolling={false} {workspaceId} @@ -895,6 +972,17 @@ bind:refresh={recursiveRefresh[mod.job ?? '']} globalModuleStates={[]} globalDurationStatuses={[]} + prefix={buildPrefix(prefix, mod.id ?? '')} + subflowParentsGlobalModuleStates={[ + localModuleStates, + ...globalModuleStates, + ...subflowParentsGlobalModuleStates + ]} + subflowParentsDurationStatuses={[ + localDurationStatuses, + ...globalDurationStatuses, + ...subflowParentsDurationStatuses + ]} render={selected == 'sequence' && render} {workspaceId} jobId={mod.job} @@ -915,6 +1003,9 @@ globalDurationStatuses={[localDurationStatuses, ...globalDurationStatuses]} render={selected == 'sequence' && render} {workspaceId} + {prefix} + {subflowParentsGlobalModuleStates} + {subflowParentsDurationStatuses} jobId={mod.job} innerModule={mod.flow_jobs ? job.raw_flow?.modules[i]?.value : undefined} flowJobIds={mod.flow_jobs @@ -973,6 +1064,7 @@ minHeight={wrapperHeight} success={jobId != undefined && isSuccess(job?.['success'])} flowModuleStates={$localModuleStates} + bind:expandedSubflows on:select={(e) => { if (rightColumnSelect != 'node_definition') { rightColumnSelect = 'node_status' @@ -1004,11 +1096,13 @@ selectedForloopIndex: detail.index }) globalRefreshes[detail.moduleId]?.({ job: detail.id, index: detail.index }) + // console.log('selectedIteration', prefix, detail.moduleId, globalRefreshes) }} modules={job.raw_flow?.modules ?? []} failureModule={job.raw_flow?.failure_module} preprocessorModule={job.raw_flow?.preprocessor_module} allowSimplifiedPoll={false} + {workspace} />
x.id)} + flowModules={allModulesForTimeline( + job?.raw_flow?.modules ?? [], + expandedSubflows ?? {} + )} durationStatuses={localDurationStatuses} /> {:else if rightColumnSelect == 'node_status'} diff --git a/frontend/src/lib/components/FlowTimeline.svelte b/frontend/src/lib/components/FlowTimeline.svelte index 3f918c8c6b..4027eca9c3 100644 --- a/frontend/src/lib/components/FlowTimeline.svelte +++ b/frontend/src/lib/components/FlowTimeline.svelte @@ -178,7 +178,7 @@ {/if}
-
{k}
+
{k.startsWith('subflow:') ? k.substring(8) : k}
{#if min && total}
diff --git a/frontend/src/lib/components/InputTransformForm.svelte b/frontend/src/lib/components/InputTransformForm.svelte index 07a7ee534d..aa0c31057b 100644 --- a/frontend/src/lib/components/InputTransformForm.svelte +++ b/frontend/src/lib/components/InputTransformForm.svelte @@ -238,7 +238,7 @@ setPropertyType(arg?.value) codeInjectionDetected = checkCodeInjection(arg?.value) != undefined } else if (propertyType == 'javascript' && focused) { - setPropertyType(arg?.expr) + // setPropertyType(arg?.expr) $inputMatches = checkCodeInjection(arg?.expr) } } @@ -279,7 +279,7 @@ class={twMerge( 'pl-2 pt-2 pb-2 ml-2 relative hover:bg-surface hover:shadow-md transition-all duration-200', $propPickerConfig?.propName == argName - ? 'bg-surface border-l-4 border-blue-500 shadow-md rounded-l-md z-2000' + ? 'bg-surface border-l-4 border-blue-500 shadow-md rounded-l-md z-50 ' : 'hover:rounded-md', $$props.class )} @@ -565,10 +565,13 @@
{#if $propPickerConfig?.propName == argName} -
+
0} {#each keys as argName (argName)} {#if (!filter || filter.includes(argName)) && Object.keys(schema.properties ?? {}).includes(argName)} -
+
+ | { + allowed_resources: string[] + allow_user_resources: boolean + allow_workspace_resource: boolean + file_key_regex: string + } + | undefined) + | undefined = undefined + export let workspace: string | undefined = undefined let oneOfSelected: string | undefined = undefined async function updateOneOfSelected(oneOf: SchemaProperty[] | undefined) { @@ -428,6 +440,9 @@ .toLowerCase() == 's3object'}
{ diff --git a/frontend/src/lib/components/LightweightSchemaForm.svelte b/frontend/src/lib/components/LightweightSchemaForm.svelte index d4da597066..55b1373b4d 100644 --- a/frontend/src/lib/components/LightweightSchemaForm.svelte +++ b/frontend/src/lib/components/LightweightSchemaForm.svelte @@ -16,6 +16,18 @@ export let defaultValues: Record = {} export let dynamicEnums: Record = {} export let disabled: boolean = false + export let appPath: string | undefined = undefined + export let computeS3ForceViewerPolicies: + | (() => + | { + allowed_resources: string[] + allow_user_resources: boolean + allow_workspace_resource: boolean + file_key_regex: string + } + | undefined) + | undefined = undefined + export let workspace: string | undefined = undefined let inputCheck: { [id: string]: boolean } = {} let errors: { [id: string]: string } = {} @@ -104,6 +116,9 @@ {displayType} {css} disabled={disabled || schema.properties[argName].disabled} + {appPath} + {computeS3ForceViewerPolicies} + {workspace} /> {/if} {/each} diff --git a/frontend/src/lib/components/PasswordArgInput.svelte b/frontend/src/lib/components/PasswordArgInput.svelte index 9b87e8843b..c9250eab6b 100644 --- a/frontend/src/lib/components/PasswordArgInput.svelte +++ b/frontend/src/lib/components/PasswordArgInput.svelte @@ -10,41 +10,53 @@ let path = '' let password = value && !value.startsWith('$var:') ? value : '' + let isGenerating = false async function generateValue() { - let npath = - 'u/' + - ($userStore?.username ?? $userStore?.email)?.split('@')[0] + - '/secret_arg/' + - generateRandomString(12) - let nvalue = '$var:' + npath - await VariableService.createVariable({ - workspace: $workspaceStore!, - requestBody: { - value: password, - is_secret: true, - path: npath, - description: '', - expires_at: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString() - } - }) - path = npath - value = nvalue + if (isGenerating) return + isGenerating = true + try { + let npath = + 'u/' + + ($userStore?.username ?? $userStore?.email)?.split('@')[0] + + '/secret_arg/' + + generateRandomString(12) + let nvalue = '$var:' + npath + await VariableService.createVariable({ + workspace: $workspaceStore!, + requestBody: { + value: password, + is_secret: true, + path: npath, + description: 'Ephemeral secret variable', + expires_at: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString() + } + }) + path = npath + value = nvalue + debouncedUpdate() + } finally { + isGenerating = false + } } async function updateValue() { - await VariableService.updateVariable({ - workspace: $workspaceStore!, - path: path, - requestBody: { - value: password - } - }) + try { + await VariableService.updateVariable({ + workspace: $workspaceStore!, + path: path, + requestBody: { + value: password + } + }) + } catch (e) { + generateValue() + } } let timeout: NodeJS.Timeout | undefined = undefined function debouncedUpdate() { timeout && clearTimeout(timeout) - setTimeout(updateValue, 500) + timeout = setTimeout(updateValue, 500) } $: password && debouncedUpdate() diff --git a/frontend/src/lib/components/S3FilePicker.svelte b/frontend/src/lib/components/S3FilePicker.svelte index bd4270a836..57adc334b4 100644 --- a/frontend/src/lib/components/S3FilePicker.svelte +++ b/frontend/src/lib/components/S3FilePicker.svelte @@ -299,7 +299,7 @@ let storage: string | undefined = undefined export async function open( - preSelectedFileKey: { s3: string; storage: string | undefined } | undefined = undefined + preSelectedFileKey: { s3: string; storage?: string } | undefined = undefined ) { storage = preSelectedFileKey?.storage if (preSelectedFileKey !== undefined) { diff --git a/frontend/src/lib/components/SuperadminSettings.svelte b/frontend/src/lib/components/SuperadminSettings.svelte index 1e81ace411..1670a51086 100644 --- a/frontend/src/lib/components/SuperadminSettings.svelte +++ b/frontend/src/lib/components/SuperadminSettings.svelte @@ -23,6 +23,7 @@ import { isCloudHosted } from '$lib/cloud' import InstanceNameEditor from './InstanceNameEditor.svelte' import Toggle from './Toggle.svelte' + import { instanceSettingsSelectedTab } from '$lib/stores'; let drawer: Drawer let filter = '' @@ -56,6 +57,9 @@ let tab: 'users' | string = 'users' + $: $instanceSettingsSelectedTab, tab = $instanceSettingsSelectedTab + $: tab, instanceSettingsSelectedTab.set(tab) + let nbDisplayed = 50 let instanceSettings diff --git a/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte b/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte index fcbd4e789a..a77d708e13 100644 --- a/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte +++ b/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte @@ -18,6 +18,9 @@ import ResolveConfig from '../helpers/ResolveConfig.svelte' import ResolveStyle from '../helpers/ResolveStyle.svelte' import { deepEqual } from 'fast-equals' + import { computeWorkspaceS3FileInputPolicy } from '../../editor/appUtilsS3' + import { defaultIfEmptyString } from '$lib/utils' + import { userStore } from '$lib/stores' export let id: string export let componentInput: AppInput | undefined @@ -26,8 +29,16 @@ export let configuration: RichConfigurations export let customCss: ComponentCustomCSS<'schemaformcomponent'> | undefined = undefined - const { worldStore, connectingInput, app, selectedComponent, componentControl } = - getContext('AppViewerContext') + const { + worldStore, + connectingInput, + app, + selectedComponent, + componentControl, + appPath, + isEditor, + workspace + } = getContext('AppViewerContext') const iterContext = getContext('ListWrapperContext') const listInputs: ListInputs | undefined = getContext('ListInputs') @@ -100,6 +111,14 @@ previousDefault = structuredClone(resolvedConfig.defaultValues) args = previousDefault ?? {} } + + function computeS3ForceViewerPolicies() { + if (!isEditor) { + return undefined + } + const policy = computeWorkspaceS3FileInputPolicy() + return policy + } {#each Object.keys(components['schemaformcomponent'].initialData.configuration) as key (key)} @@ -140,6 +159,9 @@ bind:this={schemaForm} displayType={Boolean(resolvedConfig.displayType)} largeGap={Boolean(resolvedConfig.largeGap)} + appPath={defaultIfEmptyString(appPath, `u/${$userStore?.username ?? 'unknown'}/newapp`)} + {computeS3ForceViewerPolicies} + {workspace} {css} />
diff --git a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte index 66fd746285..286c5ac573 100644 --- a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte +++ b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte @@ -26,6 +26,7 @@ import { get } from 'svelte/store' import RefreshButton from '$lib/components/apps/components/helpers/RefreshButton.svelte' import { ctxRegex } from '../../utils' + import { computeWorkspaceS3FileInputPolicy } from '../../editor/appUtilsS3' // Component props export let id: string @@ -671,6 +672,14 @@ return undefined } } + + function computeS3ForceViewerPolicies() { + if (!isEditor) { + return undefined + } + const policy = computeWorkspaceS3FileInputPolicy() + return policy + } {#each Object.entries(fields ?? {}) as [key, v] (key)} @@ -754,6 +763,9 @@
('AppViewerContext') + function computeForceViewerPolicies() { + if (!isEditor) { + return undefined + } + const policy = computeS3FileInputPolicy((configuration as any)?.type?.configuration?.s3, $app) + return policy + } @@ -139,5 +151,7 @@ outputs.result.set(value) }} {forceDisplayUploads} + appPath={defaultIfEmptyString(appPath, `u/${$userStore?.username ?? 'unknown'}/newapp`)} + {computeForceViewerPolicies} /> {/if} diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index af2f4563df..662631f752 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -87,6 +87,7 @@ import ToggleEnable from '$lib/components/common/toggleButton-v2/ToggleEnable.svelte' import HideButton from './settingsPanel/HideButton.svelte' import DeployOverrideConfirmationModal from '$lib/components/common/confirmationModal/DeployOverrideConfirmationModal.svelte' + import { computeS3FileInputPolicy, computeWorkspaceS3FileInputPolicy } from './appUtilsS3' async function hash(message) { try { @@ -195,8 +196,12 @@ } async function computeTriggerables() { + const items = allItems($app.grid, $app.subgrids) + + console.log('items', items) + const allTriggers: ([string, TriggerableV2] | undefined)[] = (await Promise.all( - allItems($app.grid, $app.subgrids) + items .flatMap((x) => { let c = x.data as AppComponent let r: { input: AppInput | undefined; id: string }[] = [ @@ -296,6 +301,44 @@ allTriggers.filter(Boolean) as [string, TriggerableV2][] ) policy.triggerables_v2 = ntriggerables + + const s3_inputs = items + .filter((x) => (x.data as AppComponent).type === 's3fileinputcomponent') + .map((x) => { + const c = x.data as AppComponent + const config = c.configuration as any + return computeS3FileInputPolicy(config?.type?.configuration?.s3, $app) + }) + .filter(Boolean) as { + allowed_resources: string[] + allow_user_resources: boolean + file_key_regex: string + }[] + + if ( + items.findIndex((x) => { + const c = x.data as AppComponent + if (c.type === 'schemaformcomponent') { + return ( + Object.values((c.componentInput as any)?.value?.properties ?? {}).findIndex( + (p: any) => p?.type === 'object' && p?.format === 'resource-s3_object' + ) !== -1 + ) + } else if (c.type === 'formbuttoncomponent' || c.type === 'formcomponent') { + return ( + Object.values((c.componentInput as any)?.fields ?? {}).findIndex( + (p: any) => p?.fieldType === 'object' && p?.format === 'resource-s3_object' + ) !== -1 + ) + } else { + return false + } + }) !== -1 + ) { + s3_inputs.push(computeWorkspaceS3FileInputPolicy()) + } + + policy.s3_inputs = s3_inputs } async function processRunnable( diff --git a/frontend/src/lib/components/apps/editor/appUtilsS3.ts b/frontend/src/lib/components/apps/editor/appUtilsS3.ts new file mode 100644 index 0000000000..2256331cc4 --- /dev/null +++ b/frontend/src/lib/components/apps/editor/appUtilsS3.ts @@ -0,0 +1,82 @@ +import type { AppInput, EvalInputV2 } from '../inputType' +import type { App } from '../types' +import { collectOneOfFields } from './appUtils' + +function filenameExprToRegex(template: string) { + const filenameEscaped = template.replaceAll('${file.name}', '') // replace filename with placeholder + const escapedTemplate = filenameEscaped + .slice(1, -1) // remove quotes + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // escape regex special characters + const regexPattern = escapedTemplate.replaceAll('', '[^/]+') // replace filename placeholder with regex pattern + return `^${regexPattern}$` +} + +function staticToRegex(str: string) { + return `^${str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$` +} + +function checkIfExprIsString(input: string) { + return /^(['"`])[^'"`]*\1$/g.test(input) +} + +function checkIfEvalIsStringWithFilename(input: EvalInputV2) { + if (input.connections.length > 0) { + return false + } else { + return checkIfExprIsString(input.expr.replaceAll('${file.name}', '')) + } +} + +function removeResourcePrefix(resource: string) { + return resource.replace(/^\$res:/, '') +} + +export function computeWorkspaceS3FileInputPolicy() { + return { + allow_workspace_resource: true, + allowed_resources: [], + allow_user_resources: false, + file_key_regex: '' + } +} + +export function computeS3FileInputPolicy(s3Config: any, app: App) { + const resourceInput = s3Config?.resource as AppInput | undefined + const pathTemplateInput = s3Config?.pathTemplate as AppInput | undefined + + const allow_workspace_resource = + !resourceInput || (resourceInput.type === 'static' && !resourceInput.value) + const allowed_resources: string[] = resourceInput + ? resourceInput.type === 'static' + ? resourceInput.value + ? [removeResourcePrefix(resourceInput.value)] + : [] + : collectOneOfFields( + { + s3_resource: resourceInput + }, + app + ).s3_resource?.map((s) => removeResourcePrefix(s)) ?? [] + : [] + + const allow_user_resources = + (resourceInput?.type === 'evalv2' && resourceInput?.allowUserResources) ?? false + let file_key_regex = '^.*$' + if (pathTemplateInput) { + if (pathTemplateInput.type === 'static') { + file_key_regex = staticToRegex(pathTemplateInput.value) + } else if ( + pathTemplateInput.type === 'evalv2' && + checkIfEvalIsStringWithFilename(pathTemplateInput) + ) { + file_key_regex = filenameExprToRegex(pathTemplateInput.expr) + } + } + + return { + allow_workspace_resource, + allowed_resources, + allow_user_resources, + file_key_regex + } +} diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte index ca5824ad6a..067d5f9178 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte @@ -195,7 +195,7 @@ {:else if componentInput?.type === 'user'} Field's value is set by the user {/if} - {#if (componentInput?.type === 'evalv2' || componentInput?.type === 'connected' || componentInput?.type === 'user') && fieldType == 'object' && format?.startsWith('resource-')} + {#if (componentInput?.type === 'evalv2' || componentInput?.type === 'connected' || componentInput?.type === 'user') && ((fieldType == 'object' && format?.startsWith('resource-') && format !== 'resource-s3_object') || fieldType == 'resource')}
- + { + if ($isCtrlOrMetaPressedStore) { + $isCtrlOrMetaPressedStore = false + } + }} + on:keydown={handleKeyDown} + on:keyup={handleKeyUp} +/>
1) { + sendUserToast('Only one file can be uploaded at a time') + return + } else { + onChange(event.dataTransfer.files) + } } } } diff --git a/frontend/src/lib/components/common/fileUpload/FileUpload.svelte b/frontend/src/lib/components/common/fileUpload/FileUpload.svelte index 4ba06e3d99..d2c326d5fe 100644 --- a/frontend/src/lib/components/common/fileUpload/FileUpload.svelte +++ b/frontend/src/lib/components/common/fileUpload/FileUpload.svelte @@ -4,7 +4,7 @@ import Button from '$lib/components/common/button/Button.svelte' import { sendUserToast } from '$lib/toast' - import { workspaceStore } from '$lib/stores' + import { userStore, workspaceStore } from '$lib/stores' import { HelpersService } from '$lib/gen' import { writable, type Writable } from 'svelte/store' import { Ban, CheckCheck, FileWarning, Files, RefreshCcw, Trash } from 'lucide-svelte' @@ -14,7 +14,9 @@ export let acceptedFileTypes: string[] | undefined = ['*'] export let allowMultiple: boolean = true - export let containerText: string = 'Drag and drop files here or click to browse' + export let containerText: string = allowMultiple + ? 'Drag and drop files here or click to browse' + : 'Drag and drop a file here or click to browse' export let customResourcePath: string | undefined = undefined export let customResourceType: 's3' | 'azure_blob' | undefined = undefined // when customResourcePath is provided, this should be provided as well. Will default to S3 if not export let customClass: string = '' @@ -25,6 +27,17 @@ export let defaultValue: string | undefined = undefined export let workspace: string | undefined = undefined export let fileUploads: Writable = writable([]) + export let appPath: string | undefined = undefined + export let computeForceViewerPolicies: + | (() => + | { + allowed_resources: string[] + allow_user_resources: boolean + allow_workspace_resource: boolean + file_key_regex: string + } + | undefined) + | undefined = undefined const dispatch = createEventDispatcher() @@ -109,6 +122,30 @@ if (fileExtension) { params.append('file_extension', fileExtension) } + if (fileToUpload.type) { + params.append('content_type', fileToUpload.type) + } + + if (computeForceViewerPolicies !== undefined) { + const forceViewerPolicies = computeForceViewerPolicies() + + if (forceViewerPolicies) { + params.append( + 'force_viewer_allowed_resources', + forceViewerPolicies.allowed_resources.join(',') + ) + params.append( + 'force_viewer_allow_user_resources', + JSON.stringify(forceViewerPolicies.allow_user_resources) + ) + params.append( + 'force_viewer_allow_workspace_resource', + JSON.stringify(forceViewerPolicies.allow_workspace_resource) + ) + params.append('force_viewer_file_key_regex', forceViewerPolicies.file_key_regex) + } + } + // let response = await fetch( // `/api/w/${$workspaceStore}/job_helpers/multipart_upload_s3_file?${params.toString()}`, // { @@ -152,9 +189,16 @@ } } }) + xhr?.open( 'POST', - `/api/w/${workspace ?? $workspaceStore}/job_helpers/upload_s3_file?${params.toString()}`, + appPath + ? `/api/w/${ + workspace ?? $workspaceStore + }/apps_u/upload_s3_file/${appPath}?${params.toString()}` + : `/api/w/${ + workspace ?? $workspaceStore + }/job_helpers/upload_s3_file?${params.toString()}`, true ) xhr?.setRequestHeader('Content-Type', 'application/octet-stream') @@ -303,7 +347,7 @@ {/if} - {#if fileUpload.progress === 100 && !fileUpload.cancelled} + {#if fileUpload.progress === 100 && !fileUpload.cancelled && $userStore} + {/if} + {:else if id?.startsWith('subflow:')} + + {id.substring('subflow:'.length)} {/if}
diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte index 28167493cf..220b597384 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte @@ -26,7 +26,7 @@ import { getDependentComponents } from '../flowExplorer' import type { FlowCopilotContext } from '$lib/components/copilot/flow' import { fade } from 'svelte/transition' - import { copilotInfo, tutorialsToDo } from '$lib/stores' + import { copilotInfo, tutorialsToDo, workspaceStore } from '$lib/stores' import FlowTutorials from '$lib/components/FlowTutorials.svelte' import { ignoredTutorials } from '$lib/components/tutorials/ignoredTutorials' @@ -44,6 +44,7 @@ export let disableSettings = false export let newFlow: boolean = false export let smallErrorHandler = false + export let workspace: string | undefined = $workspaceStore let flowTutorials: FlowTutorials | undefined = undefined @@ -345,6 +346,7 @@ preprocessorModule={$flowStore.value?.preprocessor_module} {selectedId} {flowInputsStore} + {workspace} on:delete={({ detail }) => { let e = detail.detail dependents = getDependentComponents(e.id, $flowStore) diff --git a/frontend/src/lib/components/flows/map/MapItem.svelte b/frontend/src/lib/components/flows/map/MapItem.svelte index ebd4ffdb9b..05cfc49d90 100644 --- a/frontend/src/lib/components/flows/map/MapItem.svelte +++ b/frontend/src/lib/components/flows/map/MapItem.svelte @@ -20,7 +20,6 @@ export let modules: FlowModule[] export let moving: string | undefined = undefined export let duration_ms: number | undefined = undefined - export let isTrigger: boolean = false export let retries: number | undefined = undefined export let flowJobs: diff --git a/frontend/src/lib/components/flows/map/VirtualItem.svelte b/frontend/src/lib/components/flows/map/VirtualItem.svelte index a2e022e9ef..3f5350b7ed 100644 --- a/frontend/src/lib/components/flows/map/VirtualItem.svelte +++ b/frontend/src/lib/components/flows/map/VirtualItem.svelte @@ -54,7 +54,7 @@
{preLabel}
{/if}
- {#if id && !hideId} + {#if id && !hideId && !id?.startsWith('subflow:')}
{id} diff --git a/frontend/src/lib/components/flows/previousResults.ts b/frontend/src/lib/components/flows/previousResults.ts index e922c55978..3e2a297390 100644 --- a/frontend/src/lib/components/flows/previousResults.ts +++ b/frontend/src/lib/components/flows/previousResults.ts @@ -74,7 +74,10 @@ function getFlowInput( if (parentState && parentModule) { if ( parentState.previewArgs && - !(parentModule.value?.type === 'forloopflow' && parentModule.value?.modules?.length === 1) + !(parentModule.value?.type === 'forloopflow' && parentModule.value?.modules?.length === 1) && + parentState && + typeof parentState.previewArgs == 'object' && + `iter` in parentState.previewArgs ) { return { ...topFlowInput, ...parentState.previewArgs } } else { @@ -283,11 +286,8 @@ export function filterNestedObject(obj: any, nestedKeys: string[]) { if (nestedKeys[0] === '') { return obj } - const regexes = buildPrefixRegex(Object.keys(obj)) - const matches = regexes.filter(({ regex }) => regex.test(nestedKeys[0])) - return Object.fromEntries( - Object.entries(obj).filter(([key]) => matches.some(({ word }) => word === key)) - ) + + return Object.fromEntries(Object.entries(obj).filter(([key]) => key.startsWith(nestedKeys[0]))) } const [key, ...rest] = nestedKeys if (obj && typeof obj === 'object' && key in obj) { diff --git a/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte index cd3a55fb8a..41f05c1fb5 100644 --- a/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte +++ b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte @@ -43,6 +43,7 @@ export let noPadding: boolean = false export let alwaysOn: boolean = false export let paneClass: string = '' + export let noFlowPlugConnect = false const propPickerConfig: Writable = writable< PropPickerConfig | undefined @@ -67,13 +68,15 @@ } } propPickerConfig.set(config) - flowPropPickerConfig.set({ - ...config, - clearFocus: () => { - propPickerConfig.set(undefined) - flowPropPickerConfig.set(undefined) - } - }) + if (!noFlowPlugConnect) { + flowPropPickerConfig.set({ + ...config, + clearFocus: () => { + propPickerConfig.set(undefined) + flowPropPickerConfig.set(undefined) + } + }) + } }, clearFocus: () => { flowPropPickerConfig.set(undefined) @@ -121,7 +124,7 @@ : 'transparent'} animationDuration="4s" > - {#if result} + {#if result != undefined} { diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index caf28e43ce..d09d22cac7 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -1,5 +1,5 @@ + {#if data.module.value.type == 'flow'} + + {/if} + import VirtualItem from '$lib/components/flows/map/VirtualItem.svelte' + import NodeWrapper from './NodeWrapper.svelte' + import { Minimize2 } from 'lucide-svelte' + import type { GraphModuleState } from '../../model' + import { getStateColor } from '../../util' + import type { FlowModule } from '$lib/gen' + import type { GraphEventHandlers } from '../../graphBuilder' + + export let data: { + label: string + preLabel: string | undefined + insertable: boolean + flowModuleStates: Record | undefined + subflowId: string + id: string + modules: FlowModule[] + selected: boolean + eventHandlers: GraphEventHandlers + offset: number + } + + + + { + data.eventHandlers.select(data.id) + }} + /> + + diff --git a/frontend/src/lib/components/instanceSettings.ts b/frontend/src/lib/components/instanceSettings.ts index 016b5eef78..bd998970e7 100644 --- a/frontend/src/lib/components/instanceSettings.ts +++ b/frontend/src/lib/components/instanceSettings.ts @@ -146,6 +146,15 @@ export const settings: Record = { storage: 'setting', ee_only: 'Channels other than tracing are only available in the EE version' }, + { + label: 'Mute critical alerts in UI', + description: 'Enable to mute critical alerts in the UI', + key: 'critical_alert_mute_ui', + fieldType: 'boolean', + storage: 'setting', + requiresReloadOnChange: true, + ee_only: 'Critical alerts in UI are only available in the EE version' + }, { label: 'Azure OpenAI base path', description: diff --git a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte index 8d2d196134..ffe0269b52 100644 --- a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte +++ b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte @@ -20,6 +20,7 @@ export let allowCopy = true export let collapseLevel: number | undefined = undefined export let prefix = '' + export let expandedEvenOnLevel0: string | undefined = undefined let s3FileViewer: S3FilePicker @@ -94,8 +95,12 @@ >- {/if} - {#if level == 0 && topBrackets}{/if} -