diff --git a/.github/workflows/backend-check.yml b/.github/workflows/backend-check.yml index 58a1926d6c..38f6f6df21 100644 --- a/.github/workflows/backend-check.yml +++ b/.github/workflows/backend-check.yml @@ -16,7 +16,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend @@ -40,7 +40,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend @@ -77,7 +77,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend @@ -117,7 +117,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend diff --git a/.github/workflows/backend-test.yml b/.github/workflows/backend-test.yml index 1cbefbaf91..6d12e4f69c 100644 --- a/.github/workflows/backend-test.yml +++ b/.github/workflows/backend-test.yml @@ -51,7 +51,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend diff --git a/.github/workflows/build-publish-rh-image.yml b/.github/workflows/build-publish-rh-image.yml index fd878a4e7a..26b4bd6896 100644 --- a/.github/workflows/build-publish-rh-image.yml +++ b/.github/workflows/build-publish-rh-image.yml @@ -64,7 +64,7 @@ jobs: platforms: linux/amd64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,mqtt_trigger,websocket,python,smtp,csharp,static_frontend,rust secrets: | rh_username=${{ secrets.RH_USERNAME }} rh_password=${{ secrets.RH_PASSWORD }} @@ -81,7 +81,7 @@ jobs: platforms: linux/arm64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,mqtt_trigger,websocket,python,smtp,csharp,static_frontend,rust secrets: | rh_username=${{ secrets.RH_USERNAME }} rh_password=${{ secrets.RH_PASSWORD }} diff --git a/.github/workflows/build_windows_worker_.yml b/.github/workflows/build_windows_worker_.yml index 8657bfca6b..c108f8a0d1 100644 --- a/.github/workflows/build_windows_worker_.yml +++ b/.github/workflows/build_windows_worker_.yml @@ -30,6 +30,12 @@ jobs: token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} fetch-depth: 0 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.85.0 + override: true + - name: Substitute EE code shell: bash run: | @@ -45,7 +51,7 @@ jobs: $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,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,php,mysql,mssql,bigquery,oracledb,postgres_trigger,mqtt_trigger,websocket,python,smtp,csharp,static_frontend,rust - name: Rename binary with corresponding architecture run: | diff --git a/.github/workflows/docker-image-rpi4.yml b/.github/workflows/docker-image-rpi4.yml index c450723835..c0fc7ad576 100644 --- a/.github/workflows/docker-image-rpi4.yml +++ b/.github/workflows/docker-image-rpi4.yml @@ -67,7 +67,7 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=embedding,parquet,openidconnect,deno_core,license,http_trigger,zip,oauth2,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=embedding,parquet,openidconnect,deno_core,license,http_trigger,zip,oauth2,php,mysql,mssql,bigquery,oracledb,postgres_trigger,mqtt_trigger,websocket,python,smtp,csharp,static_frontend,rust tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev ${{ steps.meta-public.outputs.tags }} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 0dfb22537a..3b3e417f3d 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -200,7 +200,7 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,php,mysql,mssql,bigquery,oracledb,postgres_trigger,mqtt_trigger,websocket,python,smtp,csharp,static_frontend,rust tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }} ${{ steps.meta-ee-public.outputs.tags }} diff --git a/.github/workflows/publish_windows_worker.yml b/.github/workflows/publish_windows_worker.yml index b4b03a99e2..50576fc519 100644 --- a/.github/workflows/publish_windows_worker.yml +++ b/.github/workflows/publish_windows_worker.yml @@ -32,6 +32,12 @@ jobs: token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} fetch-depth: 0 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.85.0 + override: true + - name: Substitute EE code shell: bash run: | @@ -47,7 +53,7 @@ jobs: $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,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,mqtt_trigger,websocket,python,smtp,csharp,static_frontend,rust - name: Rename binary with corresponding architecture run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 363786091d..1f65c9615e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,157 @@ # Changelog +## [1.475.0](https://github.com/windmill-labs/windmill/compare/v1.474.0...v1.475.0) (2025-03-06) + + +### Features + +* **backend:** option to invalidate all sessions on logout ([#5419](https://github.com/windmill-labs/windmill/issues/5419)) ([e9044f0](https://github.com/windmill-labs/windmill/commit/e9044f0b9b1647e0fc74e5e3cce39a4fc2718672)) +* deploy triggers to prod/staging workspace ([#5429](https://github.com/windmill-labs/windmill/issues/5429)) ([b210ae3](https://github.com/windmill-labs/windmill/commit/b210ae36f7c1fd8860241aa5908cefc0ac956b7b)) +* **frontend:** improve flow suspend status display ([#5425](https://github.com/windmill-labs/windmill/issues/5425)) ([a845733](https://github.com/windmill-labs/windmill/commit/a8457337cec870e9e3d6a053f4fbe89f58229401)) +* **frontend:** pick image from workspace storage bucket ([#5382](https://github.com/windmill-labs/windmill/issues/5382)) ([8dbe0fa](https://github.com/windmill-labs/windmill/commit/8dbe0fa6446a34bd60484f1b5ac828ff9f892735)) +* kafka mTLS ([#5449](https://github.com/windmill-labs/windmill/issues/5449)) ([371c892](https://github.com/windmill-labs/windmill/commit/371c892f9aa43fe2757b4d23ee076781b237f11f)) +* MQTT triggers ([#5277](https://github.com/windmill-labs/windmill/issues/5277)) ([5c39037](https://github.com/windmill-labs/windmill/commit/5c39037aea35f9bdf780f1946abbb384533ee547)) + + +### Bug Fixes + +* **cli:** fix wmill user create-token with email and password ([a16cab0](https://github.com/windmill-labs/windmill/commit/a16cab0923f3be46f181dffc209a10c711873f86)) +* **frontend:** fix many s3 file picker bugs ([#5428](https://github.com/windmill-labs/windmill/issues/5428)) ([4fabc2a](https://github.com/windmill-labs/windmill/commit/4fabc2a8256b5088b1af61baf81355cd556d23e2)) +* **frontend:** improve capture payload preview ([#5417](https://github.com/windmill-labs/windmill/issues/5417)) ([fd56a63](https://github.com/windmill-labs/windmill/commit/fd56a639d21366bea5129409d805feefb22659c1)) +* improve objectviewer performance ([2444f4f](https://github.com/windmill-labs/windmill/commit/2444f4f23e5212fa4d2ff8542d864118d4b4feb9)) +* s3 file picker delete + better s3 path handling ([#5454](https://github.com/windmill-labs/windmill/issues/5454)) ([ae618c7](https://github.com/windmill-labs/windmill/commit/ae618c79dff08e8e9fe1f4ce69665ad7faeac006)) + +## [1.474.0](https://github.com/windmill-labs/windmill/compare/v1.473.1...v1.474.0) (2025-03-04) + + +### Features + +* add template script for all triggers ([#5424](https://github.com/windmill-labs/windmill/issues/5424)) ([0a9d8c6](https://github.com/windmill-labs/windmill/commit/0a9d8c6b8b95c0e1093aef89b2a1956dbe59bbee)) +* **frontend:** global recompute helper function ([#5408](https://github.com/windmill-labs/windmill/issues/5408)) ([b961efa](https://github.com/windmill-labs/windmill/commit/b961efa8691f806d437ba2cb303ad1a50fa618c4)) +* more controls on setting token duration ([#5421](https://github.com/windmill-labs/windmill/issues/5421)) ([534a824](https://github.com/windmill-labs/windmill/commit/534a8249d60f7de6b542c3b5b5ee0b5eda360f22)) + + +### Bug Fixes + +* do not depend on public schema anymore ([90b00f5](https://github.com/windmill-labs/windmill/commit/90b00f55011288115e00b7a30b3e8e91bc0b7f4b)) +* **python:** windows worker fails to install 3.10 ([#5409](https://github.com/windmill-labs/windmill/issues/5409)) ([ebb58e0](https://github.com/windmill-labs/windmill/commit/ebb58e0dc7cc104e0bbfd90cf7f37e40ffd0bbf5)) + +## [1.473.1](https://github.com/windmill-labs/windmill/compare/v1.473.0...v1.473.1) (2025-03-03) + + +### Bug Fixes + +* **backend:** copilot info exists_ai_resource ([#5415](https://github.com/windmill-labs/windmill/issues/5415)) ([844edd1](https://github.com/windmill-labs/windmill/commit/844edd1117bc5fdcc108adf53aec3974a7c66384)) +* improve cancel performance ([fba9e7e](https://github.com/windmill-labs/windmill/commit/fba9e7ef03b91d3e2c78ff833ddcc5207b7436d2)) + +## [1.473.0](https://github.com/windmill-labs/windmill/compare/v1.472.1...v1.473.0) (2025-03-03) + + +### Features + +* app s3 input anonymous delete ([#5401](https://github.com/windmill-labs/windmill/issues/5401)) ([46c7845](https://github.com/windmill-labs/windmill/commit/46c784574add176cb75d3627c9d3f55b6fb945f8)) +* track workspace runnables used in flows ([#5369](https://github.com/windmill-labs/windmill/issues/5369)) ([7bf9e25](https://github.com/windmill-labs/windmill/commit/7bf9e25ede82486115eae71865202f85aa931a8d)) + + +### Bug Fixes + +* improve db loads by adding index on audit ([e1ff001](https://github.com/windmill-labs/windmill/commit/e1ff00117ca5b66dd0b9365e63b9ff7dc277bc2c)) +* migrations do not refer to public schema anymore ([#5400](https://github.com/windmill-labs/windmill/issues/5400)) ([3063001](https://github.com/windmill-labs/windmill/commit/3063001491b49a4b6d0cd5985818b32aa4d3f16f)) +* remove typings_extensions from python sdk ([04ffbf8](https://github.com/windmill-labs/windmill/commit/04ffbf8c266a06c3efcebcbbaee767f0ab0771e2)) + +## [1.472.1](https://github.com/windmill-labs/windmill/compare/v1.472.0...v1.472.1) (2025-02-26) + + +### Bug Fixes + +* disable bundling using env var ([#5396](https://github.com/windmill-labs/windmill/issues/5396)) ([cb559d6](https://github.com/windmill-labs/windmill/commit/cb559d6083553c400e18e6077002c4891289a8a2)) + +## [1.472.0](https://github.com/windmill-labs/windmill/compare/v1.471.1...v1.472.0) (2025-02-26) + + + +### Bug Fixes + +* downgrade v8 to fix some rare panics ([5569e4d](https://github.com/windmill-labs/windmill/commit/5569e4d4953a01f2ad03ea8b71e695e833964bea)) +* **frontend:** markdown shows single backtick in single line code block ([#5391](https://github.com/windmill-labs/windmill/issues/5391)) ([7f290bb](https://github.com/windmill-labs/windmill/commit/7f290bbf6a33e2811dbe2bd8ee905c0fa8e8db3b)) +* migrate toggle to melt (4/4) ([#5329](https://github.com/windmill-labs/windmill/issues/5329)) ([69fc8a9](https://github.com/windmill-labs/windmill/commit/69fc8a98ae78bc01dc3d97f9732ee28864b323dd)) + +## [1.471.1](https://github.com/windmill-labs/windmill/compare/v1.471.0...v1.471.1) (2025-02-26) + + +### Bug Fixes + +* update to rust 1.86.0 ([3ada264](https://github.com/windmill-labs/windmill/commit/3ada264c4ad49f666c3a053eb48c7df294bf085b)) + +## [1.471.0](https://github.com/windmill-labs/windmill/compare/v1.470.1...v1.471.0) (2025-02-26) + + +### Features + +* add support for claude sonnet 3.7 thinking ([#5387](https://github.com/windmill-labs/windmill/issues/5387)) ([487d84b](https://github.com/windmill-labs/windmill/commit/487d84bd7fdc39a2401df4108fca6183189cf38a)) + + +### Bug Fixes + +* **frontend:** improve pagination handling and filter refreshes ([#5378](https://github.com/windmill-labs/windmill/issues/5378)) ([a85ebfb](https://github.com/windmill-labs/windmill/commit/a85ebfbbf48590812c0931ad93179c322f819849)) + +## [1.470.1](https://github.com/windmill-labs/windmill/compare/v1.470.0...v1.470.1) (2025-02-26) + + +### Bug Fixes + +* multiple app initializations fixes ([630e54f](https://github.com/windmill-labs/windmill/commit/630e54f65c950ec0073b3cdac9974cb666c1ab3f)) + +## [1.470.0](https://github.com/windmill-labs/windmill/compare/v1.469.0...v1.470.0) (2025-02-26) + + +### Features + +* **frontend:** set default app refesh interval ([#5380](https://github.com/windmill-labs/windmill/issues/5380)) ([478d3fb](https://github.com/windmill-labs/windmill/commit/478d3fbf4a7e52d19fcb5cf8d601b2eeb3487716)) + + +### Bug Fixes + +* multiple app initializations fixes ([24b6003](https://github.com/windmill-labs/windmill/commit/24b600378025632aecb2ca898b63d6032e08eb2e)) + +## [1.469.0](https://github.com/windmill-labs/windmill/compare/v1.468.0...v1.469.0) (2025-02-25) + + +### Features + +* limit the number of times a job can be restarted (3) after loss of pings ([c8a9596](https://github.com/windmill-labs/windmill/commit/c8a959691c37350def37fe3eb9f24c6f7789960d)) +* python-client now support mocked api via `WM_MOCKED_API_FILE` env ([#5372](https://github.com/windmill-labs/windmill/issues/5372)) ([50607c7](https://github.com/windmill-labs/windmill/commit/50607c7625e4a48fb397cff167b41bb6602716c0)) + + +### Bug Fixes + +* improve flow editor for vscode extension ([44b26d2](https://github.com/windmill-labs/windmill/commit/44b26d2ccec0c9dd65d1f53b057d031f841d7dba)) +* improve infinite grid behavior ([56d1da7](https://github.com/windmill-labs/windmill/commit/56d1da78fd3424ae5b4abbb009c7437ea98765ef)) + +## [1.468.0](https://github.com/windmill-labs/windmill/compare/v1.467.1...v1.468.0) (2025-02-24) + + +### Features + +* add audit logs scope filter in admins workspace ([#5352](https://github.com/windmill-labs/windmill/issues/5352)) ([b3e00b7](https://github.com/windmill-labs/windmill/commit/b3e00b7fdc3ad4c689fc30216accbed05822794c)) +* add support for | None and Optional in python ([#5361](https://github.com/windmill-labs/windmill/issues/5361)) ([9736355](https://github.com/windmill-labs/windmill/commit/9736355d5f82615100212698c5537997e5a0de39)) +* make flow lock deployment error visible in UI ([b8e6d0d](https://github.com/windmill-labs/windmill/commit/b8e6d0da79ca57b115e7cb0ccff9f5623b23f3f3)) + + +### Bug Fixes + +* add LOCALAPPDATA env variable to python execution on windows ([8806870](https://github.com/windmill-labs/windmill/commit/8806870b1bf67c2f77beaf04d986cf172c7b4bf4)) +* fix confirmation modal check on deploy ([3028325](https://github.com/windmill-labs/windmill/commit/3028325615e2f7e5ee3d1b6278580121880db14f)) +* **frontend:** make html app component content selectable ([#5359](https://github.com/windmill-labs/windmill/issues/5359)) ([f1c5b77](https://github.com/windmill-labs/windmill/commit/f1c5b77d7af8433905937d274b97c2d5cd6c1316)) +* handle better forced value propagation in apps ([7c842c8](https://github.com/windmill-labs/windmill/commit/7c842c88bf5225b6bc39857109b1b1ba5f99d708)) +* handle better optional chaining operator ([d45c1f6](https://github.com/windmill-labs/windmill/commit/d45c1f69d48a5ad93f4399ce0150bbff6fd4fa6b)) +* improve app markdown rendering ([96597d3](https://github.com/windmill-labs/windmill/commit/96597d3d6b3d31298e5582a55e11e1d48edbf175)) +* improve cancel/back behavior on editors ([0565981](https://github.com/windmill-labs/windmill/commit/05659816e722effcba27e71f855e819c606f8756)) +* improve custom component rendering ([4ee4ff7](https://github.com/windmill-labs/windmill/commit/4ee4ff78d389d61c63952c84dee967113c783c45)) +* improve webhook settings cache invalidation ([0456272](https://github.com/windmill-labs/windmill/commit/0456272e3f36996c5f223fc332b150c7a64c2f05)) +* update bun t.1.43->1.2.3 ([4e477d1](https://github.com/windmill-labs/windmill/commit/4e477d1f589343980d7bd2953909ff6a6be30739)) +* update deno 2.1.2->2.2.1 ([b102ff4](https://github.com/windmill-labs/windmill/commit/b102ff4a4643e2f06d44d493f2f776b44ae721cc)) + ## [1.467.1](https://github.com/windmill-labs/windmill/compare/v1.467.0...v1.467.1) (2025-02-22) diff --git a/Dockerfile b/Dockerfile index 06922e7a60..3e670c37e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ARG DEBIAN_IMAGE=debian:bookworm-slim -ARG RUST_IMAGE=rust:1.83-slim-bookworm +ARG RUST_IMAGE=rust:1.85-slim-bookworm FROM ${RUST_IMAGE} AS rust_base diff --git a/LICENSE b/LICENSE index efd4abf0fe..aea6c58c6f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Source code in this repository is variously licensed under the Apache License Version 2.0 (see file ./LICENSE-APACHE), or the AGPLv3 License (see file -./LICENSE-AGPL) +./LICENSE-AGPL) and a proprietary license for certain enterprise features. Every file is under copyright (c) Windmill Labs, Inc 2022 unless otherwise specified. Every file is under License AGPL unless otherwise specified or @@ -12,11 +12,23 @@ and commercial license. The files under frontend/ are AGPLv3 Licensed, except any snippets of code that require a positive license check to be activated. Those snippets and files are under a proprietary and commercial license. Private and public forks MUST not include any of the above proprietary and commercial -code. Windmill Labs, Inc. provide tools to clean the codebase from those -snippets upon demand. The files under python-client/ deno-client/ go-client/ powershell-client/ are +code. The files under python-client/ deno-client/ go-client/ powershell-client/ +are Apache 2.0 Licensed. The openapi files, including the OpenFlow spec is Apache 2.0 Licensed. -The openapi files, including the OpenFlow spec is Apache 2.0 Licensed. +The binary compilable from source code in this repository without the +"enterprise" feature flag is open-source under the AGPLv3 License terms and +conditions. + +The "Community Edition" of Windmill available in the docker images hosted under +ghcr.io/windmill-labs/windmill and the github binary releases contains the files +under the AGPLv3 and Apache 2 sources but also includes proprietary and +non-public code and features which are not open source and under the following +terms: Windmill Labs, Inc. grants a right to use all the features of the +"Community Edition" for free without restrictions other than the limits and +quotas set in the software and a right to distribute the community edition as is +but not to sell, resell, serve as a managed service, modify or wrap under any +form without an explicit agreement. All third party components incorporated into the Windmill Software are licensed under the original license provided by the owner of the applicable component. diff --git a/README.md b/README.md index d8ec51b3b0..e2c9a27aa6 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,8 @@ You can build your entire infra on top of Windmill! ```typescript //import any dependency from npm -import * as wmill from "windmill-client" -import * as cowsay from 'cowsay@1.5.0'; +import * as wmill from "windmill-client"; +import * as cowsay from "cowsay@1.5.0"; // fill the type, or use the +Resource type to get a type-safe reference to a resource type Postgresql = { @@ -146,7 +146,9 @@ export async function main( ## CLI -We have a powerful CLI to interact with the windmill platform and sync your scripts from local files, GitHub repos and to run scripts and flows on the instance from local commands. See +We have a powerful CLI to interact with the windmill platform and sync your +scripts from local files, GitHub repos and to run scripts and flows on the +instance from local commands. See [more details](https://www.windmill.dev/docs/advanced/cli). ![CLI Screencast](./cli/vhs/output/setup.gif) @@ -168,7 +170,8 @@ Code extension: . Architecture: - Stateless API backend. - Workers that pull jobs from a queue in Postgres (and later, Kafka or Redis. - Upvote [#173](#https://github.com/windmill-labs/windmill/issues/173) if interested). + Upvote [#173](#https://github.com/windmill-labs/windmill/issues/173) if + interested). - Frontend in Svelte. - Scripts executions are sandboxed using Google's [nsjail](https://github.com/google/nsjail). @@ -284,22 +287,37 @@ edition. ### Commercial license -To self-host Windmill, you must respect the terms of the -[AGPLv3 license](https://www.gnu.org/licenses/agpl-3.0.en.html) which you do not -need to worry about for personal uses. For business uses, you should be fine if -you do not re-expose Windmill in any way to your users and are comfortable with -AGPLv3. +See the [LICENSE](https://github.com/windmill-labs/windmill/blob/main/LICENSE) +file for the full license text. + +The "Community Edition" of Windmill available in the docker images hosted under +ghcr.io/windmill-labs/windmill and the github binary releases contains the files +under the AGPLv3 and Apache 2 sources but also includes proprietary and +non-public code and features which are not open source and under the following +terms: Windmill Labs, Inc. grants a right to use all the features of the +"Community Edition" for free without restrictions other than the limits and +quotas set in the software and a right to distribute the community edition as is +but not to sell, resell, serve Windmill as a managed service, modify or wrap +under any form without an explicit agreement. + +The binary compilable from source code in this repository without the +"enterprise" feature flag is open-source under the +[LICENSE-AGPLv3](https://github.com/windmill-labs/windmill/blob/main/LICENSE-AGPL) +License terms and conditions. To -[re-expose any Windmill parts to your users](https://www.windmill.dev/docs/misc/white_labelling) -as a feature of your product, or to build a feature on top of Windmill, to -comply with AGPLv3 your product must be AGPLv3 or you must get a commercial -license. Contact us at if you have any doubts. +[re-expose directly any Windmill parts to your users](https://www.windmill.dev/docs/misc/white_labelling) +as a feature of your product, with the exception of iframed public Windmill +"apps", or to build a feature on top of "Windmill Community Edition" that you +sell commercially or embed in a distributable product or binary, you must get a +commercial license. Contact us at if you have any +questions. To do the same from the binary compiled from the source code in this +repository without the "enterprise" feature flag, you must comply with the +AGPLv3 license terms and conditions or get a commercial license from Windmill +Labs, Inc. -In addition, a commercial license grants you a dedicated engineer to transition -your current infrastructure to Windmill, support with tight SLA, and our global -cache sync for high-performance/no dependency cache miss of cluster from 10+ -nodes to 200+ nodes. +To use Windmill "Community Edition" as is internally in your organization, or to +use its APIs as is, you do NOT need a commercial license. ### Integrations @@ -314,70 +332,76 @@ you to have it being synced automatically everyday. ## Environment Variables -| Environment Variable name | Default | Description | Api Server/Worker/All | -| ------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| DATABASE_URL | | The Postgres database url. | All | -| WORKER_GROUP | default | The worker group the worker belongs to and get its configuration pulled from | Worker | -| MODE | standalone | The mode if the binary. Possible values: standalone, worker, server, agent | All | -| METRICS_ADDR | None | (ee only) The socket addr at which to expose Prometheus metrics at the /metrics path. Set to "true" to expose it on port 8001 | All | -| JSON_FMT | false | Output the logs in json format instead of logfmt | All | -| BASE_URL | http://localhost:8000 | The base url that is exposed publicly to access your instance. Is overriden by the instance settings if any. | Server | -| ZOMBIE_JOB_TIMEOUT | 30 | The timeout after which a job is considered to be zombie if the worker did not send pings about processing the job (every server check for zombie jobs every 30s) | Server | -| RESTART_ZOMBIE_JOBS | true | If true then a zombie job is restarted (in-place with the same uuid and some logs), if false the zombie job is failed | Server | -| SLEEP_QUEUE | 50 | The number of ms to sleep in between the last check for new jobs in the DB. It is multiplied by NUM_WORKERS such that in average, for one worker instance, there is one pull every SLEEP_QUEUE ms. | Worker | -| KEEP_JOB_DIR | false | Keep the job directory after the job is done. Useful for debugging. | Worker | -| LICENSE_KEY (EE only) | None | License key checked at startup for the Enterprise Edition of Windmill | Worker | -| SLACK_SIGNING_SECRET | None | The signing secret of your Slack app. See [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack) | Server | -| COOKIE_DOMAIN | None | The domain of the cookie. If not set, the cookie will be set by the browser based on the full origin | Server | -| DENO_PATH | /usr/bin/deno | The path to the deno binary. | Worker | -| PYTHON_PATH | | The path to the python binary if wanting to not have it managed by uv. | Worker | -| GO_PATH | /usr/bin/go | The path to the go binary. | Worker | -| GOPRIVATE | | The GOPRIVATE env variable to use private go modules | Worker | -| GOPROXY | | The GOPROXY env variable to use | Worker | -| NETRC | | The netrc content to use a private go registry | Worker | | Worker | -| PY_CONCURRENT_DOWNLOADS | 20 | Sets the maximum number of in-flight concurrent python downloads that windmill will perform at any given time. | Worker | -| PATH | None | The path environment variable, usually inherited | Worker | -| HOME | None | The home directory to use for Go and Bash , usually inherited | Worker | -| DATABASE_CONNECTIONS | 50 (Server)/3 (Worker) | The max number of connections in the database connection pool | All | -| SUPERADMIN_SECRET | None | A token that would let the caller act as a virtual superadmin superadmin@windmill.dev | Server | -| TIMEOUT_WAIT_RESULT | 20 | The number of seconds to wait before timeout on the 'run_wait_result' endpoint | Worker | -| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker | -| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker | -| DISABLE_RESPONSE_LOGS | false | Disable response logs | Server | -| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server | +| Environment Variable name | Default | Description | Api Server/Worker/All | +| ----------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | --- | ------ | +| DATABASE_URL | | The Postgres database url. | All | +| WORKER_GROUP | default | The worker group the worker belongs to and get its configuration pulled from | Worker | +| MODE | standalone | The mode if the binary. Possible values: standalone, worker, server, agent | All | +| METRICS_ADDR | None | (ee only) The socket addr at which to expose Prometheus metrics at the /metrics path. Set to "true" to expose it on port 8001 | All | +| JSON_FMT | false | Output the logs in json format instead of logfmt | All | +| BASE_URL | http://localhost:8000 | The base url that is exposed publicly to access your instance. Is overriden by the instance settings if any. | Server | +| ZOMBIE_JOB_TIMEOUT | 30 | The timeout after which a job is considered to be zombie if the worker did not send pings about processing the job (every server check for zombie jobs every 30s) | Server | +| RESTART_ZOMBIE_JOBS | true | If true then a zombie job is restarted (in-place with the same uuid and some logs), if false the zombie job is failed | Server | +| SLEEP_QUEUE | 50 | The number of ms to sleep in between the last check for new jobs in the DB. It is multiplied by NUM_WORKERS such that in average, for one worker instance, there is one pull every SLEEP_QUEUE ms. | Worker | +| KEEP_JOB_DIR | false | Keep the job directory after the job is done. Useful for debugging. | Worker | +| LICENSE_KEY (EE only) | None | License key checked at startup for the Enterprise Edition of Windmill | Worker | +| SLACK_SIGNING_SECRET | None | The signing secret of your Slack app. See [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack) | Server | +| COOKIE_DOMAIN | None | The domain of the cookie. If not set, the cookie will be set by the browser based on the full origin | Server | +| DENO_PATH | /usr/bin/deno | The path to the deno binary. | Worker | +| PYTHON_PATH | | The path to the python binary if wanting to not have it managed by uv. | Worker | +| GO_PATH | /usr/bin/go | The path to the go binary. | Worker | +| GOPRIVATE | | The GOPRIVATE env variable to use private go modules | Worker | +| GOPROXY | | The GOPROXY env variable to use | Worker | +| NETRC | | The netrc content to use a private go registry | Worker | | Worker | +| PY_CONCURRENT_DOWNLOADS | 20 | Sets the maximum number of in-flight concurrent python downloads that windmill will perform at any given time. | Worker | +| PATH | None | The path environment variable, usually inherited | Worker | +| HOME | None | The home directory to use for Go and Bash , usually inherited | Worker | +| DATABASE_CONNECTIONS | 50 (Server)/3 (Worker) | The max number of connections in the database connection pool | All | +| SUPERADMIN_SECRET | None | A token that would let the caller act as a virtual superadmin superadmin@windmill.dev | Server | +| TIMEOUT_WAIT_RESULT | 20 | The number of seconds to wait before timeout on the 'run_wait_result' endpoint | Worker | +| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker | +| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker | +| DISABLE_RESPONSE_LOGS | false | Disable response logs | Server | +| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server | ## Run a local dev setup + 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. +with hot-code reloading. Note that you will need to use a username / password +login due to CSRF checks using a different auth provider. In the `frontend/` directory: 1. install the dependencies with `npm install` (or `pnpm install` or `yarn`) 2. generate the windmill client: - ``` - npm run generate-backend-client - ## on mac use - npm run generate-backend-client-mac - ``` + +``` +npm run generate-backend-client +## on mac use +npm run generate-backend-client-mac +``` + 3. Run your dev server with `npm run dev` 4. Et voilĂ , windmill should be available at `http://localhost/` ### Backend + Frontend + See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all running options. + 1. Create a Postgres Database for Windmill and create an admin role inside your - Postgres setup. - The easiest way to get a working db is to run - ``` + Postgres setup. The easiest way to get a working db is to run + ``` cargo install sqlx-cli env DATABASE_URL= sqlx migrate run - ``` + ``` This will also avoid compile time issue with sqlx's `query!` macro 2. Install [nsjail](https://github.com/google/nsjail) and have it accessible in your PATH @@ -387,15 +411,15 @@ running options. 5. Install the [lld linker](https://lld.llvm.org/) 6. Go to `frontend/`: 1. `npm install`, `npm run generate-backend-client` then `npm run dev` - 2. You might need to set some extra heap space for the node runtime `export NODE_OPTIONS="--max-old-space-size=4096"` - 3. In another shell `npm run build` otherwise the backend will not find the `frontend/build` folder and will not compile. + 2. You might need to set some extra heap space for the node runtime + `export NODE_OPTIONS="--max-old-space-size=4096"` + 3. In another shell `npm run build` otherwise the backend will not find the + `frontend/build` folder and will not compile. 4. In another shell `sudo caddy run --config Caddyfile` 7. Go to `backend/`: `env DATABASE_URL= RUST_LOG=info cargo run` 8. Et voilĂ , windmill should be available at `http://localhost/` - - ## Contributors @@ -404,4 +428,4 @@ running options. ## Copyright -Windmill Labs, Inc 2023 \ No newline at end of file +Windmill Labs, Inc 2023 diff --git a/backend/.sqlx/query-07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5.json b/backend/.sqlx/query-07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5.json index 6eacfceb62..b905255754 100644 --- a/backend/.sqlx/query-07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5.json +++ b/backend/.sqlx/query-07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5.json @@ -20,7 +20,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json b/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json index 2be39fce26..4bcf3c6ce3 100644 --- a/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json +++ b/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json @@ -130,28 +130,28 @@ }, { "ordinal": 25, - "name": "teams_command_script", - "type_info": "Text" - }, - { - "ordinal": 26, - "name": "teams_team_id", - "type_info": "Text" - }, - { - "ordinal": 27, - "name": "teams_team_name", - "type_info": "Text" - }, - { - "ordinal": 28, "name": "ai_models", "type_info": "VarcharArray" }, { - "ordinal": 29, + "ordinal": 26, "name": "code_completion_model", "type_info": "Varchar" + }, + { + "ordinal": 27, + "name": "teams_command_script", + "type_info": "Text" + }, + { + "ordinal": 28, + "name": "teams_team_id", + "type_info": "Text" + }, + { + "ordinal": 29, + "name": "teams_team_name", + "type_info": "Text" } ], "parameters": { @@ -185,10 +185,10 @@ true, true, true, - true, - true, - true, false, + true, + true, + true, true ] }, diff --git a/backend/.sqlx/query-115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a.json b/backend/.sqlx/query-115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a.json new file mode 100644 index 0000000000..66a2152ae9 --- /dev/null +++ b/backend/.sqlx/query-115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a" +} diff --git a/backend/.sqlx/query-186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e.json b/backend/.sqlx/query-186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e.json new file mode 100644 index 0000000000..648c5dda19 --- /dev/null +++ b/backend/.sqlx/query-186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e" +} diff --git a/backend/.sqlx/query-1ab0d1ba1fbfad31ffb28a01a6c9640d0ac142aabee8d288a4f9c56ad9dbeac4.json b/backend/.sqlx/query-1ab0d1ba1fbfad31ffb28a01a6c9640d0ac142aabee8d288a4f9c56ad9dbeac4.json new file mode 100644 index 0000000000..1b5ddc6ac6 --- /dev/null +++ b/backend/.sqlx/query-1ab0d1ba1fbfad31ffb28a01a6c9640d0ac142aabee8d288a4f9c56ad9dbeac4.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO job_logs (job_id, logs)\n VALUES ($1, $2)\n ON CONFLICT (job_id) DO UPDATE SET logs = job_logs.logs || '\n' || EXCLUDED.logs\n WHERE job_logs.job_id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [] + }, + "hash": "1ab0d1ba1fbfad31ffb28a01a6c9640d0ac142aabee8d288a4f9c56ad9dbeac4" +} diff --git a/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json b/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json new file mode 100644 index 0000000000..b58e3bf8d8 --- /dev/null +++ b/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH email_lookup AS (\n SELECT email FROM token WHERE token = $1\n )\n DELETE FROM token\n WHERE email = (SELECT email FROM email_lookup) AND label = 'session'\n RETURNING email", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "email", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf" +} diff --git a/backend/.sqlx/query-2bb2cf6accb18d3e37a63388cca52a6591e7593b1a7c3d7a6848587679a48187.json b/backend/.sqlx/query-2bb2cf6accb18d3e37a63388cca52a6591e7593b1a7c3d7a6848587679a48187.json new file mode 100644 index 0000000000..11e7df65bf --- /dev/null +++ b/backend/.sqlx/query-2bb2cf6accb18d3e37a63388cca52a6591e7593b1a7c3d7a6848587679a48187.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE flow_workspace_runnables SET workspace_id = $1 WHERE workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Text" + ] + }, + "nullable": [] + }, + "hash": "2bb2cf6accb18d3e37a63388cca52a6591e7593b1a7c3d7a6848587679a48187" +} diff --git a/backend/.sqlx/query-93d0ee34c7b7c56ab9cae28071f8e5fdaff0e601e2760cc5653d197c51b106bb.json b/backend/.sqlx/query-31b6fccad46b22bcbba6bbce22209ccb1825116004ed72682854ce3352f454a5.json similarity index 55% rename from backend/.sqlx/query-93d0ee34c7b7c56ab9cae28071f8e5fdaff0e601e2760cc5653d197c51b106bb.json rename to backend/.sqlx/query-31b6fccad46b22bcbba6bbce22209ccb1825116004ed72682854ce3352f454a5.json index 1945784892..0871986a4e 100644 --- a/backend/.sqlx/query-93d0ee34c7b7c56ab9cae28071f8e5fdaff0e601e2760cc5653d197c51b106bb.json +++ b/backend/.sqlx/query-31b6fccad46b22bcbba6bbce22209ccb1825116004ed72682854ce3352f454a5.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n \n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\", \n \n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\",\n EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS \"sqs_used!\"\n ", + "query": "\n SELECT \n \n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\", \n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\",\n EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS \"mqtt_used!\",\n EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS \"sqs_used!\"\n ", "describe": { "columns": [ { @@ -30,6 +30,11 @@ }, { "ordinal": 5, + "name": "mqtt_used!", + "type_info": "Bool" + }, + { + "ordinal": 6, "name": "sqs_used!", "type_info": "Bool" } @@ -45,8 +50,9 @@ null, null, null, + null, null ] }, - "hash": "93d0ee34c7b7c56ab9cae28071f8e5fdaff0e601e2760cc5653d197c51b106bb" + "hash": "31b6fccad46b22bcbba6bbce22209ccb1825116004ed72682854ce3352f454a5" } diff --git a/backend/.sqlx/query-35795d27c4ca69d2f145b4dba08a6ed16c25aea4584103c1b9a3651eb31bfe53.json b/backend/.sqlx/query-35795d27c4ca69d2f145b4dba08a6ed16c25aea4584103c1b9a3651eb31bfe53.json new file mode 100644 index 0000000000..06730d3e26 --- /dev/null +++ b/backend/.sqlx/query-35795d27c4ca69d2f145b4dba08a6ed16c25aea4584103c1b9a3651eb31bfe53.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO flow_workspace_runnables (flow_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, TRUE, $3) ON CONFLICT DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "35795d27c4ca69d2f145b4dba08a6ed16c25aea4584103c1b9a3651eb31bfe53" +} diff --git a/backend/.sqlx/query-3bbde0fa35d935ec2dd8bd1fb14cfecf48305f5f4b644b3c35355074e1ccce28.json b/backend/.sqlx/query-3bbde0fa35d935ec2dd8bd1fb14cfecf48305f5f4b644b3c35355074e1ccce28.json deleted file mode 100644 index 815f1fed70..0000000000 --- a/backend/.sqlx/query-3bbde0fa35d935ec2dd8bd1fb14cfecf48305f5f4b644b3c35355074e1ccce28.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "CREATE INDEX CONCURRENTLY queue_sort_v2 ON v2_job_queue (priority DESC NULLS LAST, scheduled_for, tag) WHERE running = false", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "3bbde0fa35d935ec2dd8bd1fb14cfecf48305f5f4b644b3c35355074e1ccce28" -} diff --git a/backend/.sqlx/query-3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74.json b/backend/.sqlx/query-3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74.json new file mode 100644 index 0000000000..113f888545 --- /dev/null +++ b/backend/.sqlx/query-3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE \n capture_config \n SET \n last_server_ping = NULL \n WHERE \n workspace_id = $1 AND \n path = $2 AND \n is_flow = $3 AND \n trigger_kind = 'mqtt' AND \n server_id IS NULL\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74" +} diff --git a/backend/.sqlx/query-458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929.json b/backend/.sqlx/query-458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929.json new file mode 100644 index 0000000000..1acc3d1efc --- /dev/null +++ b/backend/.sqlx/query-458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "CREATE INDEX CONCURRENTLY IF NOT EXISTS queue_sort_v2 ON v2_job_queue (priority DESC NULLS LAST, scheduled_for, tag) WHERE running = false", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929" +} diff --git a/backend/.sqlx/query-4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a.json b/backend/.sqlx/query-4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a.json new file mode 100644 index 0000000000..3f66c4c9b4 --- /dev/null +++ b/backend/.sqlx/query-4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a" +} diff --git a/backend/.sqlx/query-4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b.json b/backend/.sqlx/query-4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b.json new file mode 100644 index 0000000000..e5b87450d3 --- /dev/null +++ b/backend/.sqlx/query-4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT EXISTS(\n SELECT \n 1 \n FROM \n mqtt_trigger \n WHERE \n path = $1 AND \n workspace_id = $2\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b" +} diff --git a/backend/.sqlx/query-548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19.json b/backend/.sqlx/query-548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19.json new file mode 100644 index 0000000000..554b0fff0b --- /dev/null +++ b/backend/.sqlx/query-548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19.json @@ -0,0 +1,50 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n path,\n is_flow,\n workspace_id,\n owner,\n email,\n trigger_config as \"trigger_config!: _\"\n FROM\n capture_config\n WHERE\n trigger_kind = 'mqtt' AND\n last_client_ping > NOW() - INTERVAL '10 seconds' AND\n trigger_config IS NOT NULL AND\n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "trigger_config!: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + true + ] + }, + "hash": "548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19" +} diff --git a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json index 9243288c9d..14685a8bfa 100644 --- a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json +++ b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json @@ -130,28 +130,28 @@ }, { "ordinal": 25, - "name": "teams_command_script", - "type_info": "Text" - }, - { - "ordinal": 26, - "name": "teams_team_id", - "type_info": "Text" - }, - { - "ordinal": 27, - "name": "teams_team_name", - "type_info": "Text" - }, - { - "ordinal": 28, "name": "ai_models", "type_info": "VarcharArray" }, { - "ordinal": 29, + "ordinal": 26, "name": "code_completion_model", "type_info": "Varchar" + }, + { + "ordinal": 27, + "name": "teams_command_script", + "type_info": "Text" + }, + { + "ordinal": 28, + "name": "teams_team_id", + "type_info": "Text" + }, + { + "ordinal": 29, + "name": "teams_team_name", + "type_info": "Text" } ], "parameters": { @@ -185,10 +185,10 @@ true, true, true, - true, - true, - true, false, + true, + true, + true, true ] }, diff --git a/backend/.sqlx/query-5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd.json b/backend/.sqlx/query-5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd.json index 0184cadf74..61c4d20b04 100644 --- a/backend/.sqlx/query-5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd.json +++ b/backend/.sqlx/query-5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd.json @@ -28,7 +28,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } @@ -62,7 +63,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531.json b/backend/.sqlx/query-5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531.json new file mode 100644 index 0000000000..7dee8c0003 --- /dev/null +++ b/backend/.sqlx/query-5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531.json @@ -0,0 +1,36 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET\n mqtt_resource_path = $1,\n subscribe_topics = $2,\n client_version = $3,\n client_id = $4,\n v3_config = $5,\n v5_config = $6,\n is_flow = $7, \n edited_by = $8, \n email = $9,\n script_path = $10,\n path = $11,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $12 AND \n path = $13\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "JsonbArray", + { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + }, + "Varchar", + "Jsonb", + "Jsonb", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531" +} diff --git a/backend/.sqlx/query-62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a.json b/backend/.sqlx/query-62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a.json index 9e86b3dbf4..d2eab003f2 100644 --- a/backend/.sqlx/query-62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a.json +++ b/backend/.sqlx/query-62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a.json @@ -20,7 +20,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a.json b/backend/.sqlx/query-653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a.json deleted file mode 100644 index 3d3309cb5c..0000000000 --- a/backend/.sqlx/query-653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH zombie_jobs AS (\n UPDATE v2_job_queue q SET running = false, started_at = null\n FROM v2_job j, v2_job_runtime r\n WHERE j.id = q.id AND j.id = r.id\n AND ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')\n AND same_worker = false\n RETURNING q.id, q.workspace_id, ping\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id, workspace_id, ping FROM zombie_jobs", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "ping", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - true - ] - }, - "hash": "653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a" -} diff --git a/backend/.sqlx/query-65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db.json b/backend/.sqlx/query-65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db.json new file mode 100644 index 0000000000..6d0ec8bd68 --- /dev/null +++ b/backend/.sqlx/query-65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db.json @@ -0,0 +1,135 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n mqtt_resource_path,\n subscribe_topics as \"subscribe_topics!: Vec>\",\n v3_config as \"v3_config!: Option>\",\n v5_config as \"v5_config!: Option>\",\n client_version AS \"client_version: _\",\n client_id,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n mqtt_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mqtt_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscribe_topics!: Vec>", + "type_info": "JsonbArray" + }, + { + "ordinal": 2, + "name": "v3_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "v5_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "client_version: _", + "type_info": { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "client_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db" +} diff --git a/backend/.sqlx/query-67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4.json b/backend/.sqlx/query-67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4.json deleted file mode 100644 index a09be9741e..0000000000 --- a/backend/.sqlx/query-67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO job_logs (job_id, logs)\n VALUES ($1, 'Restarted job after not receiving job''s ping for too long the ' || now() || '\n\n')\n ON CONFLICT (job_id) DO UPDATE SET logs = job_logs.logs || '\n' || EXCLUDED.logs\n WHERE job_logs.job_id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4" -} diff --git a/backend/.sqlx/query-71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447.json b/backend/.sqlx/query-71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447.json index 4b5e3a41fd..643e0854bb 100644 --- a/backend/.sqlx/query-71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447.json +++ b/backend/.sqlx/query-71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447.json @@ -31,7 +31,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e.json b/backend/.sqlx/query-7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e.json new file mode 100644 index 0000000000..a30054b9dc --- /dev/null +++ b/backend/.sqlx/query-7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e.json @@ -0,0 +1,132 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n mqtt_resource_path,\n subscribe_topics as \"subscribe_topics!: Vec>\",\n v3_config as \"v3_config!: Option>\",\n v5_config as \"v5_config!: Option>\",\n client_version as \"client_version: _\",\n client_id,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n mqtt_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mqtt_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscribe_topics!: Vec>", + "type_info": "JsonbArray" + }, + { + "ordinal": 2, + "name": "v3_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "v5_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "client_version: _", + "type_info": { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "client_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e" +} diff --git a/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json b/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json new file mode 100644 index 0000000000..93ed95dd5e --- /dev/null +++ b/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM token WHERE email = $1 AND label = 'session'", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f" +} diff --git a/backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json b/backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json similarity index 75% rename from backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json rename to backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json index 75f58e98e3..90a2f78d2c 100644 --- a/backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json +++ b/backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)", + "query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)", "describe": { "columns": [], "parameters": { @@ -14,5 +14,5 @@ }, "nullable": [] }, - "hash": "b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27" + "hash": "8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d" } diff --git a/backend/.sqlx/query-4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5.json b/backend/.sqlx/query-8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513.json similarity index 82% rename from backend/.sqlx/query-4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5.json rename to backend/.sqlx/query-8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513.json index 06eaac04ff..191b010aec 100644 --- a/backend/.sqlx/query-4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5.json +++ b/backend/.sqlx/query-8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND label != 'ephemeral-script'\n ORDER BY created_at DESC LIMIT $2 OFFSET $3", + "query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND (label != 'ephemeral-script' OR label IS NULL)\n ORDER BY created_at DESC LIMIT $2 OFFSET $3", "describe": { "columns": [ { @@ -50,5 +50,5 @@ true ] }, - "hash": "4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5" + "hash": "8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513" } diff --git a/backend/.sqlx/query-8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365.json b/backend/.sqlx/query-8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365.json new file mode 100644 index 0000000000..0d30c8ca0a --- /dev/null +++ b/backend/.sqlx/query-8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET \n enabled = $1, \n email = $2, \n edited_by = $3, \n edited_at = now(), \n server_id = NULL, \n error = NULL\n WHERE \n path = $4 AND \n workspace_id = $5 \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Bool", + "Varchar", + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365" +} diff --git a/backend/.sqlx/query-9aeee333b1dbe58ba819ba3b2713242b54d77b46b5f785ae66b8104f89f43219.json b/backend/.sqlx/query-9aeee333b1dbe58ba819ba3b2713242b54d77b46b5f785ae66b8104f89f43219.json new file mode 100644 index 0000000000..271a508a83 --- /dev/null +++ b/backend/.sqlx/query-9aeee333b1dbe58ba819ba3b2713242b54d77b46b5f785ae66b8104f89f43219.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT f.path\n FROM flow_workspace_runnables fwr \n JOIN flow f \n ON fwr.flow_path = f.path AND fwr.workspace_id = f.workspace_id\n WHERE fwr.runnable_path = $1 AND fwr.runnable_is_flow = $2 AND fwr.workspace_id = $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "9aeee333b1dbe58ba819ba3b2713242b54d77b46b5f785ae66b8104f89f43219" +} diff --git a/backend/.sqlx/query-9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f.json b/backend/.sqlx/query-9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f.json new file mode 100644 index 0000000000..bb2a3c682b --- /dev/null +++ b/backend/.sqlx/query-9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE \n FROM \n mqtt_trigger \n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f" +} diff --git a/backend/.sqlx/query-abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5.json b/backend/.sqlx/query-abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5.json new file mode 100644 index 0000000000..55039055c8 --- /dev/null +++ b/backend/.sqlx/query-abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH to_update AS (\n SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter\n FROM v2_job_queue q\n JOIN v2_job j ON j.id = q.id\n JOIN v2_job_runtime r ON r.id = j.id\n LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id\n WHERE ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')\n AND same_worker = false\n AND (zjc.counter IS NULL OR zjc.counter <= $2)\n FOR UPDATE of q SKIP LOCKED\n ),\n zombie_jobs AS (\n UPDATE v2_job_queue q\n SET running = false, started_at = null\n FROM to_update tu\n WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2)\n RETURNING q.id, q.workspace_id, ping, tu.counter\n ),\n update_ping AS (\n UPDATE v2_job_runtime r\n SET ping = null\n FROM zombie_jobs zj\n WHERE r.id = zj.id\n ),\n increment_counter AS (\n INSERT INTO zombie_job_counter (job_id, counter)\n SELECT id, 1 FROM to_update WHERE counter < $2\n ON CONFLICT (job_id) DO UPDATE \n SET counter = zombie_job_counter.counter + 1\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id!", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "workspace_id!", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 3, + "name": "counter", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Int4" + ] + }, + "nullable": [ + false, + false, + true, + null + ] + }, + "hash": "abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5" +} diff --git a/backend/.sqlx/query-acbea8740b28c26942c50edcf5618cd141e68cf83a7dcae7d3c1b8a7ba94425b.json b/backend/.sqlx/query-acbea8740b28c26942c50edcf5618cd141e68cf83a7dcae7d3c1b8a7ba94425b.json new file mode 100644 index 0000000000..410aed9cc5 --- /dev/null +++ b/backend/.sqlx/query-acbea8740b28c26942c50edcf5618cd141e68cf83a7dcae7d3c1b8a7ba94425b.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM flow_workspace_runnables WHERE flow_path = $1 AND workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "acbea8740b28c26942c50edcf5618cd141e68cf83a7dcae7d3c1b8a7ba94425b" +} diff --git a/backend/.sqlx/query-b656927cd70b6667f3c72186ec04f0bf040da3af9e2eac3229264ec95b4755d8.json b/backend/.sqlx/query-b656927cd70b6667f3c72186ec04f0bf040da3af9e2eac3229264ec95b4755d8.json new file mode 100644 index 0000000000..c05edddc9f --- /dev/null +++ b/backend/.sqlx/query-b656927cd70b6667f3c72186ec04f0bf040da3af9e2eac3229264ec95b4755d8.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE flow_workspace_runnables SET flow_path = REGEXP_REPLACE(flow_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE flow_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "b656927cd70b6667f3c72186ec04f0bf040da3af9e2eac3229264ec95b4755d8" +} diff --git a/backend/.sqlx/query-bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8.json b/backend/.sqlx/query-bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8.json new file mode 100644 index 0000000000..88b910fb04 --- /dev/null +++ b/backend/.sqlx/query-bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_audit_timestamps ON audit (timestamp DESC)", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8" +} diff --git a/backend/.sqlx/query-c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8.json b/backend/.sqlx/query-c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8.json index a2d04fed03..45ce8d3f95 100644 --- a/backend/.sqlx/query-c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8.json +++ b/backend/.sqlx/query-c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8.json @@ -23,7 +23,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-c35f44f91b08fa57e29a2b4a685706f62e700695810f23108e975dfcd1fee7a3.json b/backend/.sqlx/query-c35f44f91b08fa57e29a2b4a685706f62e700695810f23108e975dfcd1fee7a3.json new file mode 100644 index 0000000000..77e89c52a8 --- /dev/null +++ b/backend/.sqlx/query-c35f44f91b08fa57e29a2b4a685706f62e700695810f23108e975dfcd1fee7a3.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO flow_workspace_runnables (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, FALSE, $4) ON CONFLICT DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Int8", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "c35f44f91b08fa57e29a2b4a685706f62e700695810f23108e975dfcd1fee7a3" +} diff --git a/backend/.sqlx/query-c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18.json b/backend/.sqlx/query-c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18.json new file mode 100644 index 0000000000..9ab57a7e3a --- /dev/null +++ b/backend/.sqlx/query-c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET \n enabled = FALSE, \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18" +} diff --git a/backend/.sqlx/query-c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534.json b/backend/.sqlx/query-c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534.json index e29d94f8f5..8c2a88d485 100644 --- a/backend/.sqlx/query-c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534.json +++ b/backend/.sqlx/query-c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534.json @@ -20,7 +20,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794.json b/backend/.sqlx/query-cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794.json new file mode 100644 index 0000000000..a9e508d01d --- /dev/null +++ b/backend/.sqlx/query-cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n last_server_ping = now(), \n error = $1 \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'mqtt' AND \n server_id = $5 AND \n last_client_ping > NOW() - INTERVAL '10 seconds' \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794" +} diff --git a/backend/.sqlx/query-d8186f0cee285aa50db7626409aec7e0504b068ffa8bb185d9384ce1422fd3d1.json b/backend/.sqlx/query-d8186f0cee285aa50db7626409aec7e0504b068ffa8bb185d9384ce1422fd3d1.json new file mode 100644 index 0000000000..70872a1328 --- /dev/null +++ b/backend/.sqlx/query-d8186f0cee285aa50db7626409aec7e0504b068ffa8bb185d9384ce1422fd3d1.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM audit WHERE timestamp <= now() - ($1::bigint::text || ' s')::interval", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + }, + "hash": "d8186f0cee285aa50db7626409aec7e0504b068ffa8bb185d9384ce1422fd3d1" +} diff --git a/backend/.sqlx/query-dc58e5b4715601a93b3c01a2564a4420f232867a23cacb9a62b386f129a86a4b.json b/backend/.sqlx/query-dc58e5b4715601a93b3c01a2564a4420f232867a23cacb9a62b386f129a86a4b.json new file mode 100644 index 0000000000..d3b8f33ff0 --- /dev/null +++ b/backend/.sqlx/query-dc58e5b4715601a93b3c01a2564a4420f232867a23cacb9a62b386f129a86a4b.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE flow_workspace_runnables SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "dc58e5b4715601a93b3c01a2564a4420f232867a23cacb9a62b386f129a86a4b" +} diff --git a/backend/.sqlx/query-e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7.json b/backend/.sqlx/query-e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7.json index 66578654a3..60332810b2 100644 --- a/backend/.sqlx/query-e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7.json +++ b/backend/.sqlx/query-e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7.json @@ -28,7 +28,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039.json b/backend/.sqlx/query-e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039.json index 8d3bf8c927..7b30e079f0 100644 --- a/backend/.sqlx/query-e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039.json +++ b/backend/.sqlx/query-e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039.json @@ -36,7 +36,8 @@ "email", "nats", "postgres", - "sqs" + "sqs", + "mqtt" ] } } diff --git a/backend/.sqlx/query-c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25.json b/backend/.sqlx/query-eb68469026be39048c5f42a80a2c538fbb54ad269ec81aea89e431a511245a1e.json similarity index 57% rename from backend/.sqlx/query-c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25.json rename to backend/.sqlx/query-eb68469026be39048c5f42a80a2c538fbb54ad269ec81aea89e431a511245a1e.json index ee362d8845..60a98ba32a 100644 --- a/backend/.sqlx/query-c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25.json +++ b/backend/.sqlx/query-eb68469026be39048c5f42a80a2c538fbb54ad269ec81aea89e431a511245a1e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\" FROM v2_job WHERE parent_job = $1 AND workspace_id = $2", + "query": "SELECT id AS \"id!\" FROM v2_job_queue INNER JOIN v2_job USING (id) WHERE parent_job = $1 AND v2_job.workspace_id = $2", "describe": { "columns": [ { @@ -19,5 +19,5 @@ false ] }, - "hash": "c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25" + "hash": "eb68469026be39048c5f42a80a2c538fbb54ad269ec81aea89e431a511245a1e" } diff --git a/backend/.sqlx/query-f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8.json b/backend/.sqlx/query-f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8.json new file mode 100644 index 0000000000..7334c70fb4 --- /dev/null +++ b/backend/.sqlx/query-f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'mqtt'\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8" +} diff --git a/backend/.sqlx/query-f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4.json b/backend/.sqlx/query-f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4.json new file mode 100644 index 0000000000..db7c69c456 --- /dev/null +++ b/backend/.sqlx/query-f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4.json @@ -0,0 +1,36 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO mqtt_trigger (\n mqtt_resource_path,\n subscribe_topics,\n client_version,\n client_id,\n v3_config,\n v5_config,\n workspace_id,\n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4, \n $5, \n $6, \n $7,\n $8,\n $9,\n $10,\n $11,\n $12,\n $13\n )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "JsonbArray", + { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + }, + "Varchar", + "Jsonb", + "Jsonb", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4" +} diff --git a/backend/.sqlx/query-fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96.json b/backend/.sqlx/query-fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96.json new file mode 100644 index 0000000000..091d205b40 --- /dev/null +++ b/backend/.sqlx/query-fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n server_id = $1,\n last_server_ping = now(), \n error = 'Connecting...' \n WHERE \n last_client_ping > NOW() - INTERVAL '10 seconds' AND \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'mqtt' AND \n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96" +} diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json index 6ebd36cf07..ab8340f5c8 100644 --- a/backend/.vscode/settings.json +++ b/backend/.vscode/settings.json @@ -11,5 +11,5 @@ "remote.autoForwardPorts": true, "conventionalCommits.scopes": [ "restructring triggers, decoding trigger message on work" - ] + ], } diff --git a/backend/Cargo.lock b/backend/Cargo.lock index e801413197..0128144432 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -225,15 +225,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "arc-swap" @@ -502,11 +496,11 @@ dependencies = [ [[package]] name = "ash" -version = "0.38.0+1.3.281" +version = "0.37.3+1.3.251" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" dependencies = [ - "libloading 0.8.6", + "libloading 0.7.4", ] [[package]] @@ -557,7 +551,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -573,12 +567,12 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +checksum = "06575e6a9673580f52661c92107baabffbf41e2141373441cbcdc47cb733003c" dependencies = [ "brotli 7.0.0", - "bzip2", + "bzip2 0.5.2", "flate2", "futures-core", "futures-io", @@ -607,7 +601,7 @@ dependencies = [ "portable-atomic", "rand 0.8.5", "regex", - "ring 0.17.10", + "ring 0.17.12", "rustls-native-certs 0.7.3", "rustls-pemfile 2.2.0", "rustls-webpki 0.102.8", @@ -618,7 +612,7 @@ dependencies = [ "thiserror 1.0.69", "time", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", "tokio-websockets", "tracing", @@ -636,7 +630,7 @@ dependencies = [ "bytes", "http 1.2.0", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde-aux", "serde_json", @@ -659,7 +653,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -681,7 +675,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -711,13 +705,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -772,9 +766,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.16" +version = "1.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50236e4d60fe8458de90a71c0922c761e41755adf091b1b03de1cef537179915" +checksum = "90aff65e86db5fe300752551c1b015ef72b708ac54bded8ef43d0d53cb7cb0b1" dependencies = [ "aws-credential-types", "aws-runtime", @@ -782,7 +776,7 @@ dependencies = [ "aws-sdk-ssooidc", "aws-sdk-sts", "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.61.1", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -792,7 +786,7 @@ dependencies = [ "fastrand 2.3.0", "hex", "http 0.2.12", - "ring 0.17.10", + "ring 0.17.12", "time", "tokio", "tracing", @@ -821,7 +815,7 @@ dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.60.12", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -834,19 +828,19 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] name = "aws-sdk-sqs" -version = "1.59.0" +version = "1.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d1dd5eb75af0917715e5bcbdce4df7acfb44da354bcbc55f37b751218dc9c33" +checksum = "c50ff7694f857436b7c9f8e8e930190bdf8381251151886f4a0ac32eb0cb8fd1" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.61.1", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -861,14 +855,14 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.59.0" +version = "1.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a35fc7e74f5be45839eb753568535c074a592185dd0a2d406685018d581c43" +checksum = "e65ff295979977039a25f5a0bf067a64bc5e6aa38f3cef4037cf42516265553c" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.61.1", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -883,14 +877,14 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.60.0" +version = "1.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fa655b4f313124ce272cbc38c5fef13793c832279cec750103e5e6b71a54b8" +checksum = "91430a60f754f235688387b75ee798ef00cfd09709a582be2b7525ebb5306d4f" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.61.1", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -905,14 +899,14 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.60.0" +version = "1.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1cfe5e16b90421ea031f4c6348b534ef442e76f6bf4a1b2b592c12cc2c6af9" +checksum = "9276e139d39fff5a0b0c984fc2d30f970f9a202da67234f948fda02e5bea1dbe" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.61.1", "aws-smithy-json", "aws-smithy-query", "aws-smithy-runtime", @@ -933,7 +927,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bfe75fad52793ce6dec0dc3d4b1f388f038b5eb866c8d4d7f3a8e21b5ea5051" dependencies = [ "aws-credential-types", - "aws-smithy-http", + "aws-smithy-http 0.60.12", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", @@ -980,6 +974,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "aws-smithy-http" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f276f21c7921fe902826618d1423ae5bf74cf8c1b8472aee8434f3dfd31824" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + [[package]] name = "aws-smithy-json" version = "0.61.2" @@ -1006,7 +1020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d526a12d9ed61fadefda24abe2e682892ba288c2018bcb38b1b4c111d13f6d92" dependencies = [ "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.60.12", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", @@ -1272,20 +1286,20 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.12.1", "lazy_static", "lazycell", "log", - "prettyplease 0.2.29", + "prettyplease 0.2.30", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.98", + "syn 2.0.99", "which 4.4.2", ] @@ -1295,18 +1309,18 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.13.0", "log", - "prettyplease 0.2.29", + "prettyplease 0.2.30", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1315,16 +1329,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec 0.6.3", -] - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] [[package]] @@ -1333,12 +1338,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - [[package]] name = "bitflags" version = "1.3.2" @@ -1347,9 +1346,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" dependencies = [ "serde", ] @@ -1386,16 +1385,15 @@ dependencies = [ [[package]] name = "blake3" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" +checksum = "675f87afced0413c9bb02843499dbbd3882a237645883f71a2b59644a6d2f753" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "memmap2", ] [[package]] @@ -1473,7 +1471,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-util", "tower-service", @@ -1499,7 +1497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5430e3be710b68d984d1391c854eb431a9d548640711faa54eecb1df93db91cc" dependencies = [ "borsh-derive", - "cfg_aliases", + "cfg_aliases 0.2.1", ] [[package]] @@ -1512,7 +1510,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1522,7 +1520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" dependencies = [ "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1619,9 +1617,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" dependencies = [ "bytemuck_derive", ] @@ -1634,7 +1632,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1643,17 +1641,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -1679,13 +1671,21 @@ dependencies = [ ] [[package]] -name = "bzip2-sys" -version = "0.1.12+1.0.8" +name = "bzip2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ebc2f1a417f01e1da30ef264ee86ae31d2dcd2d603ea283d3c244a883ca2a9" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" +dependencies = [ + "bzip2-sys", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -1704,7 +1704,7 @@ dependencies = [ "byteorder", "gemm", "half", - "memmap2", + "memmap2 0.9.5", "num-traits", "num_cpus", "rand 0.8.5", @@ -1778,7 +1778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1787,7 +1787,7 @@ version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e11c675378efb449ed3ce8de78d75d0d80542fc98487c26aba28eb3b82feac72" dependencies = [ - "semver 1.0.25", + "semver 1.0.26", "serde", "toml 0.7.8", "url", @@ -1804,9 +1804,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.15" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", @@ -1834,6 +1834,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "cfg_aliases" version = "0.2.1" @@ -1930,9 +1936,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -1940,9 +1946,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -1959,7 +1965,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2014,9 +2020,15 @@ dependencies = [ "nom 7.1.3", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.3" @@ -2050,9 +2062,9 @@ checksum = "510ca239cf13b7f8d16a2b48f263de7b4f8c566f0af58d901031473c76afb1e3" [[package]] name = "console" -version = "0.15.10" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", "libc", @@ -2061,16 +2073,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -2212,129 +2214,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71de5e59f616d79d14d2c71aa2799ce898241d7f10f7e64a4997014b4000a28" -dependencies = [ - "cranelift-codegen", - "cranelift-frontend", - "cranelift-module", -] - -[[package]] -name = "cranelift-bforest" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e15d04a0ce86cb36ead88ad68cf693ffd6cda47052b9e0ac114bc47fd9cd23c4" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-bitset" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c6e3969a7ce267259ce244b7867c5d3bc9e65b0a87e81039588dfdeaede9f34" - -[[package]] -name = "cranelift-codegen" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c22032c4cb42558371cf516bb47f26cdad1819d3475c133e93c49f50ebf304e" -dependencies = [ - "bumpalo", - "cranelift-bforest", - "cranelift-bitset", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-control", - "cranelift-entity", - "cranelift-isle", - "gimli", - "hashbrown 0.14.5", - "log", - "regalloc2", - "rustc-hash 2.1.1", - "serde", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904bc71c61b27fc57827f4a1379f29de64fe95653b620a3db77d59655eee0b8" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40180f5497572f644ce88c255480981ae2ec1d7bb4d8e0c0136a13b87a2f2ceb" - -[[package]] -name = "cranelift-control" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d132c6d0bd8a489563472afc171759da0707804a65ece7ceb15a8c6d7dd5ef" -dependencies = [ - "arbitrary", -] - -[[package]] -name = "cranelift-entity" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d0d9618275474fbf679dd018ac6e009acbd6ae6850f6a67be33fb3b00b323" -dependencies = [ - "cranelift-bitset", -] - -[[package]] -name = "cranelift-frontend" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fac41e16729107393174b0c9e3730fb072866100e1e64e80a1a963b2e484d57" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-isle" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca20d576e5070044d0a72a9effc2deacf4d6aa650403189d8ea50126483944d" - -[[package]] -name = "cranelift-module" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d55612bebcf16ff7306c8a6f5bdb6d45662b8aa1ee058ecce8807ad87db719b" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-control", -] - -[[package]] -name = "cranelift-native" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - [[package]] name = "crc" version = "3.2.1" @@ -2527,7 +2406,18 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", +] + +[[package]] +name = "d3d12" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" +dependencies = [ + "bitflags 2.9.0", + "libloading 0.8.6", + "winapi", ] [[package]] @@ -2599,7 +2489,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2632,7 +2522,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2674,7 +2564,7 @@ dependencies = [ "async-compression", "async-trait", "bytes", - "bzip2", + "bzip2 0.4.4", "chrono", "dashmap", "datafusion-common", @@ -2709,7 +2599,7 @@ dependencies = [ "tokio", "tokio-util", "url", - "uuid 1.14.0", + "uuid 1.15.1", "xz2", "zstd", ] @@ -2809,7 +2699,7 @@ dependencies = [ "regex", "sha2 0.10.8", "unicode-segmentation", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -2896,7 +2786,7 @@ dependencies = [ "itertools 0.12.1", "log", "paste", - "petgraph", + "petgraph 0.6.5", "regex", ] @@ -2976,7 +2866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -3016,60 +2906,46 @@ dependencies = [ "swc_visit", "swc_visit_macros", "text_lines", - "thiserror 2.0.11", + "thiserror 2.0.12", "unicode-width 0.1.14", "url", ] [[package]] name = "deno_broadcast_channel" -version = "0.186.0" +version = "0.184.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05dcdbba132f2cd8695fec123f7e19ef3b527dd86db613116855a07434a43452" +checksum = "33db5dacb54c6fda4c5ea4103c5687b76a51202343379af8b21120ba9d20f3c2" dependencies = [ "async-trait", "deno_core", "deno_error", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] name = "deno_cache" -version = "0.124.0" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1cf75bf39851cc0d3c46e9e693e3ecec94c460bc42a7db70783fa41e124d57" +checksum = "e0daca6ec4e6142a994d38e7bc587dda7948fa00e6194b671a5d4340f5a918a3" dependencies = [ - "anyhow", - "async-stream", "async-trait", - "base64 0.21.7", - "bytes", - "chrono", "deno_core", "deno_error", - "futures", - "http 1.2.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.6.0", - "hyper-util", - "log", "rusqlite", "serde", "sha2 0.10.8", - "slab", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", - "tokio-util", ] [[package]] name = "deno_cache_dir" -version = "0.18.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cadb05700726eb97cb4914d8016ff81ece094b1c3ed442d6f48f297961a5a96" +checksum = "27429da4d0e601baaa41415a43468d49a586645d13497f12e8a9346f9f6b1347" dependencies = [ "async-trait", "base32", @@ -3096,27 +2972,23 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.61.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8aab0236481f0ff849595c69363b13e3b91a7b8afef0170d3e263b8883765f" +checksum = "35ca8f93d60d96d6f6cb0da632303afb98567accf07d9b6f8d2ef88617589d9e" dependencies = [ - "bytemuck", "deno_core", "deno_error", - "deno_terminal", "deno_webgpu", "image", - "lcms2", - "num-traits", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] name = "deno_config" -version = "0.49.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb717829d8b6071bf54ac6139e4ea53878ad5523c1cbdad4483c09be9b81606" +checksum = "08fe512a72c4300bd997c6849450a1f050da0c909a2a4fbdc44891647392bacf" dependencies = [ "boxed_error", "capacity_builder 0.5.0", @@ -3135,30 +3007,30 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_console" -version = "0.192.0" +version = "0.190.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca40d9ecd49a0320c058eff8ad8b53c83b1b743e3087001afe2e14ec50197d34" +checksum = "94352b8d75c288a26ef748ad0ddae07e181109374a02c547850f96eef76b5389" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.338.0" +version = "0.336.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113f3f08bd5daf99f1a7876c0f99cd8c3c609439fa0b808311ec856a253e95f0" +checksum = "fdd50476c4325d5fa52bb906804a1e35b127d2a1dcf674e3447b53dcf25525bf" dependencies = [ "anyhow", "az", "bincode", - "bit-set 0.5.3", - "bit-vec 0.6.3", + "bit-set", + "bit-vec", "bytes", "capacity_builder 0.1.3", "cooked-waker", @@ -3180,7 +3052,7 @@ dependencies = [ "smallvec", "sourcemap 8.0.1", "static_assertions", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "url", "v8", @@ -3195,9 +3067,9 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_cron" -version = "0.72.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e304cef3ff3958bec6769cc52de1bbe20bbd2cfcff684c2a250026b42c4abf64" +checksum = "e8ec283bef14bcf655b209619766bdeab67f2a5e093991cca73f5d502f7bf6e8" dependencies = [ "anyhow", "async-trait", @@ -3205,15 +3077,15 @@ dependencies = [ "deno_core", "deno_error", "saffron", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", ] [[package]] name = "deno_crypto" -version = "0.206.0" +version = "0.204.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b83ea41f55c3059b4669b13543307a9fb7685fc7f0a550715ff2c37da7a78533" +checksum = "4f0493142a437e49b46aa8e08d715942076ff48c3cb776f0b015b4224ed0d37a" dependencies = [ "aes 0.8.3", "aes-gcm", @@ -3234,7 +3106,7 @@ dependencies = [ "p384", "p521", "rand 0.8.5", - "ring 0.17.10", + "ring 0.17.12", "rsa", "sec1", "serde", @@ -3243,9 +3115,9 @@ dependencies = [ "sha2 0.10.8", "signature", "spki", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", - "uuid 1.14.0", + "uuid 1.15.1", "x25519-dalek", ] @@ -3271,14 +3143,14 @@ checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "deno_fetch" -version = "0.216.0" +version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36844f2845e236a5109a5fe7311ce7f3673a913c3e7861b0744c3c3c4f22b8" +checksum = "3df032ca1f7f06a5cc459189b960793f64d415ddc9f59f262e0ad5059865002d" dependencies = [ "base64 0.21.7", "bytes", @@ -3303,9 +3175,9 @@ dependencies = [ "rustls-webpki 0.102.8", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-socks", "tokio-util", "tower 0.5.2", @@ -3315,34 +3187,32 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.179.0" +version = "0.177.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d86d3fdbcd53f6324ab10fd4e0593d32ea4727b730150378ac5b433b3a50805" +checksum = "bfbdc4e55c79ec1bc8a3ac72313e6f70d76340222f1e50c5c91e050296f83544" dependencies = [ - "cranelift", - "cranelift-native", "deno_core", "deno_error", "deno_permissions", "dlopen2 0.6.1", + "dynasmrt", "libffi", "libffi-sys", "log", - "memmap2", "num-bigint", "serde", "serde-value", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "winapi", ] [[package]] name = "deno_fs" -version = "0.102.0" +version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ae998dfd660c0274ff8865f0c88d4f638c347b15671f323b8a55388d5155969" +checksum = "c82f79b71403b93b248727a89746026104b3a5ac1d82e79a02af6fa8e8487666" dependencies = [ "async-trait", "base32", @@ -3359,16 +3229,16 @@ dependencies = [ "rand 0.8.5", "rayon", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "winapi", "windows-sys 0.59.0", ] [[package]] name = "deno_http" -version = "0.190.0" +version = "0.188.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194afb4594206299913b46a5592afd92715633e0f359817715b28f4ab325776a" +checksum = "d0b7e7a3bcac31ebd4677a96318003a98f0fda4613f6ba6d7f5ba57928727191" dependencies = [ "async-compression", "async-trait", @@ -3379,7 +3249,6 @@ dependencies = [ "deno_core", "deno_error", "deno_net", - "deno_telemetry", "deno_websocket", "flate2", "http 0.2.12", @@ -3395,20 +3264,20 @@ dependencies = [ "percent-encoding", "phf", "pin-project", - "ring 0.17.10", + "ring 0.17.12", "scopeguard", "serde", "smallvec", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-util", ] [[package]] name = "deno_io" -version = "0.102.0" +version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954183ae279458892f5c4db5d3797e4cd07d1de03addce4a1df8330b21f7d7d5" +checksum = "1e72489fe0dcada08047611d1ab92db1baebf7b606ab7c78790f622ecb30e22b" dependencies = [ "async-trait", "deno_core", @@ -3423,16 +3292,16 @@ dependencies = [ "pin-project", "rand 0.8.5", "tokio", - "uuid 1.14.0", + "uuid 1.15.1", "winapi", "windows-sys 0.59.0", ] [[package]] name = "deno_kv" -version = "0.100.0" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ae119e36e95a5e0d15dc7075811d10f4a7b13ed301cb2b2900e815b69a92d7" +checksum = "b0e3930d0195a3350c05eb9a4bc619ec598ea84ad8bdb9b6c3e4bef798e7cf34" dependencies = [ "anyhow", "async-trait", @@ -3459,7 +3328,7 @@ dependencies = [ "rand 0.8.5", "rusqlite", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] @@ -3472,14 +3341,14 @@ dependencies = [ "deno_semver", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] name = "deno_media_type" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480223262efd08f96b3be5f0457c82bac7296e70dc4e7ef7350751f66293812c" +checksum = "600222d059ab31ff31182b3e12615df2134a9e01605836b78ad8df91ba39eab3" dependencies = [ "data-url", "serde", @@ -3488,9 +3357,9 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.123.0" +version = "0.121.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b0810f50062903a80034f73ac5ccad00e76866c0c47f67a03740eb55eb6f8e" +checksum = "13f30bf147cc46dba87e3088d037cf99a2845ead1138033d3b346178cb781558" dependencies = [ "deno_core", "deno_error", @@ -3499,7 +3368,7 @@ dependencies = [ "libloading 0.7.4", "log", "napi_sym", - "thiserror 2.0.11", + "thiserror 2.0.12", "windows-sys 0.59.0", ] @@ -3518,9 +3387,9 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.184.0" +version = "0.182.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067dc3247f76bf8ea13b6559e045d5bcc340c26fc1adeab55e40c78b9136a6c" +checksum = "ab869063cbfe428a707511835865d55247886eb175659e538af4d5096c3d4d9d" dependencies = [ "deno_core", "deno_error", @@ -3532,19 +3401,16 @@ dependencies = [ "quinn", "rustls-tokio-stream", "serde", - "sha2 0.10.8", "socket2", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", - "url", - "web-transport-proto", ] [[package]] name = "deno_node" -version = "0.130.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c66de7b1046f1a2247a157ac0257b093985c107012cd2166759c1c79c9275ea" +checksum = "9638e803a668b0a5793ff94c9b2e82c54a05d9fc510901e9f3093d2d63dbdaab" dependencies = [ "aead-gcm-stream", "aes 0.8.3", @@ -3590,7 +3456,6 @@ dependencies = [ "k256", "lazy-regex", "libc", - "libsqlite3-sys", "libz-sys", "md-5 0.10.6", "md4", @@ -3609,10 +3474,9 @@ dependencies = [ "pkcs8", "rand 0.8.5", "regex", - "ring 0.17.10", + "ring 0.17.12", "ripemd", "rsa", - "rusqlite", "scrypt", "sec1", "serde", @@ -3625,7 +3489,7 @@ dependencies = [ "spki", "stable_deref_trait", "sys_traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-eld", "url", @@ -3653,15 +3517,15 @@ dependencies = [ "monch", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_ops" -version = "0.214.0" +version = "0.212.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ad885bf882be535f7714c713042129acba6f31a8efb5e6b2298f6e40cab9b16" +checksum = "c2d328067139909aa81522a5d90f119368b541fbddd73ab630e4d9f777865f0d" dependencies = [ "indexmap 2.7.1", "proc-macro-rules", @@ -3670,15 +3534,15 @@ dependencies = [ "stringcase", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.98", - "thiserror 2.0.11", + "syn 2.0.99", + "thiserror 2.0.12", ] [[package]] name = "deno_os" -version = "0.9.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8237d69398c1cda21d503620b4db3081ebab635e173c59c508a0c0585458cc8e" +checksum = "e8371d206f6265c4e0b74116c1a58cc8c464c45da0b43c1e8c19a911e88feb2b" dependencies = [ "deno_core", "deno_error", @@ -3692,7 +3556,7 @@ dependencies = [ "serde", "signal-hook", "signal-hook-registry", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "winapi", ] @@ -3711,7 +3575,7 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] @@ -3724,15 +3588,15 @@ dependencies = [ "deno_error", "percent-encoding", "sys_traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_permissions" -version = "0.51.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a586a97e08b58a9cf3e59bccb5481aa8af79d03409d00688c1e9e7e53fe9c44" +checksum = "abf879dff0b3de4dbcb78d6dda3a55e711369d5b9f479270a82853ef106c4176" dependencies = [ "capacity_builder 0.5.0", "deno_core", @@ -3745,16 +3609,16 @@ dependencies = [ "once_cell", "percent-encoding", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "which 6.0.3", "winapi", ] [[package]] name = "deno_process" -version = "0.7.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9caf3ba8de002634a2b741d22134a928fc773da9263fcf885750cfbbfe0dc083" +checksum = "700f8a2c9d369e7035e693f26a671489a73724450cbbc0a1e32f3966ef2f21fb" dependencies = [ "deno_core", "deno_error", @@ -3772,7 +3636,7 @@ dependencies = [ "serde", "simd-json", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "which 6.0.3", "winapi", @@ -3781,9 +3645,9 @@ dependencies = [ [[package]] name = "deno_resolver" -version = "0.23.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "425038100809c32ad34b811c35ea8a21cb7a4a56f05bf9787829a0af1bc6c39d" +checksum = "93c4ceec7b6e22344047b8a5577bb8239dc0a99884c25c1fa7d8611f4c3ed28b" dependencies = [ "anyhow", "async-once-cell", @@ -3801,24 +3665,20 @@ dependencies = [ "deno_semver", "deno_terminal", "futures", - "import_map", - "indexmap 2.7.1", "log", "node_resolver", "once_cell", "parking_lot", - "serde", - "serde_json", "sys_traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_runtime" -version = "0.200.0" +version = "0.198.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058631ca499b33d6689aaa15a4a1fbb329f8f948ee2ab2884e2856974bb5cff8" +checksum = "26a54d54ca920e5256c1e910c7574787d009a34afd41b20ad250278bb1aea290" dependencies = [ "color-print", "deno_ast", @@ -3875,11 +3735,11 @@ dependencies = [ "serde", "sys_traits", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-metrics", "twox-hash 1.6.3", - "uuid 1.14.0", + "uuid 1.15.1", "which 6.0.3", "winapi", "windows-sys 0.59.0", @@ -3898,15 +3758,15 @@ dependencies = [ "monch", "once_cell", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_telemetry" -version = "0.14.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04904d0253c29d26de3e82d36e9e75499ffc898781cca6dc435c53d4e232840b" +checksum = "d73802ee27361bbb6c0e3c04a799b39f458afbed1972c4aff0867420d1c36fdb" dependencies = [ "async-trait", "deno_core", @@ -3925,7 +3785,7 @@ dependencies = [ "opentelemetry_sdk", "pin-project", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", ] @@ -3941,9 +3801,9 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.179.0" +version = "0.177.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fccae99d44b1088c20a7e2f4864287ce2547c28b9a46ce90d82f9b877cd7b9b9" +checksum = "a1e3ceb2be448150d8214e8fc454c947e0ea94f6ce16556544f05a67ad5a16b8" dependencies = [ "deno_core", "deno_error", @@ -3953,7 +3813,7 @@ dependencies = [ "rustls-tokio-stream", "rustls-webpki 0.102.8", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "webpki-roots", ] @@ -3971,21 +3831,21 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.192.0" +version = "0.190.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a705094c7fbbb01338e89c12367da939cf831dd5b202e3a521f75a614a6082" +checksum = "d79e743ad841f7826d46c6944580f5ba665fe9ab4c31a68c4eed8b5a78225da3" dependencies = [ "deno_core", "deno_error", - "thiserror 2.0.11", + "thiserror 2.0.12", "urlpattern", ] [[package]] name = "deno_web" -version = "0.223.0" +version = "0.221.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dba24f2142104095572e1e52271b199165fa9eb055e1e3effcda5039f2b257" +checksum = "8041ba73bb2f238c61b5e4ed341d2fe1f9464a71115a240ab3390480b3c10e12" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -3997,25 +3857,22 @@ dependencies = [ "flate2", "futures", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] name = "deno_webgpu" -version = "0.159.0" +version = "0.157.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb48531b1d5abda71e7140050faa799f7bfb6d16628621e9080221e0ff0474a" +checksum = "4077584c0ccfde0737e576c396bbf1645f25ed0ebf4f44543e0ad13729285cf3" dependencies = [ "deno_core", "deno_error", - "deno_unsync", - "indexmap 2.7.1", "raw-window-handle", "serde", - "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "wgpu-core", "wgpu-types", @@ -4023,18 +3880,18 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.192.0" +version = "0.190.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d2d820d865651e0ce4eca898176577c8693bf2694af32545ab4b9ffb9934fa" +checksum = "c4ff81a990196bf3a80fe5d339b4eb8b411ef17634d60d399a63bae6e71a37c9" dependencies = [ "deno_core", ] [[package]] name = "deno_websocket" -version = "0.197.0" +version = "0.195.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da100506ccfafae1d85243ca96e5fd00b98bea60afd5ec5b4f0803e8f1b0b09" +checksum = "2ad15c3856dd1748f9a36102e90b4e345e40d7d64a9bf6672d584201e1fded28" dependencies = [ "bytes", "deno_core", @@ -4051,21 +3908,21 @@ dependencies = [ "once_cell", "rustls-tokio-stream", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", ] [[package]] name = "deno_webstorage" -version = "0.187.0" +version = "0.185.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043d606ff3844e3e770b9ae326c0f68cabf5a3cb70776d89100ba9881f294d36" +checksum = "079dc4f6ce91f53bb848bad8d743dc20d16ca44cbed3425531cf5d922b1a45bc" dependencies = [ "deno_core", "deno_error", "deno_web", "rusqlite", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -4091,7 +3948,7 @@ dependencies = [ "num-bigint", "prost", "serde", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -4113,11 +3970,11 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-util", "url", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -4138,10 +3995,10 @@ dependencies = [ "rand 0.8.5", "rusqlite", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-stream", - "uuid 1.14.0", + "uuid 1.15.1", "v8_valueserializer", ] @@ -4179,7 +4036,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4233,7 +4090,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4338,7 +4195,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4373,7 +4230,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4440,17 +4297,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "dyn-clone" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "dyn-iter" @@ -4468,6 +4319,32 @@ dependencies = [ "reborrow", ] +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + [[package]] name = "ecb" version = "0.1.2" @@ -4540,9 +4417,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" dependencies = [ "serde", ] @@ -4601,7 +4478,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4621,7 +4498,7 @@ checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4817,6 +4694,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flatbuffers" version = "24.12.23" @@ -4829,9 +4712,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "libz-sys", @@ -4897,7 +4780,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4935,7 +4818,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -5079,7 +4962,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -5349,11 +5232,6 @@ name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -dependencies = [ - "fallible-iterator 0.3.0", - "indexmap 2.7.1", - "stable_deref_trait", -] [[package]] name = "git-version" @@ -5372,7 +5250,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -5407,9 +5285,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" dependencies = [ "aho-corasick", "bstr", @@ -5432,9 +5310,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.16.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" dependencies = [ "js-sys", "slotmap", @@ -5444,9 +5322,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.6.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" dependencies = [ "gl_generator", ] @@ -5469,7 +5347,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "gpu-alloc-types", ] @@ -5479,19 +5357,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.8.0", -] - -[[package]] -name = "gpu-allocator" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" -dependencies = [ - "log", - "presser", - "thiserror 1.0.69", - "windows", + "bitflags 2.9.0", ] [[package]] @@ -5500,7 +5366,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "gpu-descriptor-types", "hashbrown 0.15.2", ] @@ -5511,7 +5377,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -5643,7 +5509,7 @@ checksum = "f208758247e68e239acaa059e72e4ce1f30f2a4b6523f19c1b923d25b7e9cceb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -5744,7 +5610,7 @@ dependencies = [ "once_cell", "rand 0.9.0", "serde", - "thiserror 2.0.11", + "thiserror 2.0.12", "tinyvec", "tokio", "tracing", @@ -5768,7 +5634,7 @@ dependencies = [ "resolv-conf", "serde", "smallvec", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -5921,9 +5787,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -6027,16 +5893,16 @@ dependencies = [ "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tower-service", "webpki-roots", ] [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper 1.6.0", "hyper-util", @@ -6246,7 +6112,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -6300,16 +6166,15 @@ dependencies = [ [[package]] name = "image" -version = "0.25.5" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", - "byteorder-lite", + "byteorder", + "color_quant", "num-traits", "png", - "zune-core", - "zune-jpeg", ] [[package]] @@ -6325,7 +6190,7 @@ dependencies = [ "percent-encoding", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] @@ -6454,7 +6319,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -6510,9 +6375,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jni-sys" @@ -6682,7 +6547,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -6700,29 +6565,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "lcms2" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680ec3fa42c36e0af9ca02f20a3742a82229c7f1ee0e6754294de46a80be6f74" -dependencies = [ - "bytemuck", - "foreign-types 0.5.0", - "lcms2-sys", -] - -[[package]] -name = "lcms2-sys" -version = "4.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "593265f9a3172180024fb62580ee31348f31be924b19416da174ebb7fb623d2e" -dependencies = [ - "cc", - "dunce", - "libc", - "pkg-config", -] - [[package]] name = "levenshtein_automata" version = "0.2.1" @@ -6795,9 +6637,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libffi" @@ -6862,9 +6704,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", - "redox_syscall 0.5.9", + "redox_syscall 0.5.10", ] [[package]] @@ -6873,7 +6715,6 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ - "bindgen 0.69.5", "cc", "pkg-config", "vcpkg", @@ -6916,9 +6757,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "litrs" @@ -7060,7 +6901,7 @@ dependencies = [ "rustls-pki-types", "smtp-proto", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "webpki-roots", ] @@ -7210,6 +7051,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.9.5" @@ -7237,11 +7087,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.31.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -7266,6 +7116,16 @@ dependencies = [ "unicase", ] +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -7330,7 +7190,7 @@ dependencies = [ "smallvec", "tagptr", "thiserror 1.0.69", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -7341,9 +7201,9 @@ checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" [[package]] name = "monostate" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d208407d7552cd041d8cdb69a1bc3303e029c598738177a3d87082004dc0e1e" +checksum = "aafe1be9d0c75642e3e50fedc7ecadf1ef1cbce6eb66462153fc44245343fbee" dependencies = [ "monostate-impl", "serde", @@ -7351,13 +7211,13 @@ dependencies = [ [[package]] name = "monostate-impl" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ce64b975ed4f123575d11afd9491f2e37bbd5813fbfbc0f09ae1fbddea74e0" +checksum = "c402a4092d5e204f32c9e155431046831fa712637043c58cb73bc6bc6c9663b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -7402,7 +7262,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "termcolor", "thiserror 1.0.69", ] @@ -7430,7 +7290,7 @@ dependencies = [ "serde", "serde_json", "socket2", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-native-tls", "tokio-util", @@ -7445,7 +7305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34a9141e735d5bb02414a7ac03add09522466d4db65bdd827069f76ae0850e58" dependencies = [ "base64 0.22.1", - "bitflags 2.8.0", + "bitflags 2.9.0", "btoi", "byteorder", "bytes", @@ -7467,43 +7327,42 @@ dependencies = [ "sha2 0.10.8", "subprocess", "thiserror 1.0.69", - "uuid 1.14.0", + "uuid 1.15.1", "zstd", ] [[package]] name = "naga" -version = "24.0.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" dependencies = [ "arrayvec", - "bit-set 0.8.0", - "bitflags 2.8.0", - "cfg_aliases", + "bit-set", + "bitflags 2.9.0", "codespan-reporting", "hexf-parse", "indexmap 2.7.1", "log", + "num-traits", "rustc-hash 1.1.0", "serde", "spirv", - "strum 0.26.3", "termcolor", - "thiserror 2.0.11", + "thiserror 1.0.69", "unicode-xid", ] [[package]] name = "napi_sym" -version = "0.122.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62fc0fdf05342119d887829b12c87cfe69b3043188f0e1e8de4d79bc7d42db67" +checksum = "33a55ec137cebb7f4a594edd16157a5b9d9addf7ebd29c88198ec4e0cff2e93e" dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -7563,7 +7422,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "libc", ] @@ -7585,15 +7444,14 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.30.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f10e6b8d3e7773ad2ae50506fdaa7070d27307f8240df8937cc476e4ad97ef6a" +checksum = "808426e80ce77a311b24ac080caf18c23c632e035d797edb217ce74cdf6a0e71" dependencies = [ "anyhow", "async-trait", "boxed_error", "dashmap", - "deno_config", "deno_error", "deno_media_type", "deno_package_json", @@ -7606,7 +7464,7 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "url", ] @@ -7636,7 +7494,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -7806,7 +7664,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -7826,7 +7684,7 @@ dependencies = [ "getrandom 0.2.15", "http 1.2.0", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "serde_path_to_error", @@ -7872,8 +7730,8 @@ dependencies = [ "percent-encoding", "quick-xml 0.36.2", "rand 0.8.5", - "reqwest 0.12.9", - "ring 0.17.10", + "reqwest 0.12.12", + "ring 0.17.12", "serde", "serde_json", "snafu", @@ -7909,9 +7767,9 @@ checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "oneshot" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d72a7c0f743d2ebb0a2ad1d219db75fdc799092ed3a884c9144c42a31225bd" +checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" [[package]] name = "onig" @@ -7989,7 +7847,7 @@ version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -8006,7 +7864,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -8179,15 +8037,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-float" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" -dependencies = [ - "num-traits", -] - [[package]] name = "os_pipe" version = "1.1.5" @@ -8299,7 +8148,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.9", + "redox_syscall 0.5.10", "smallvec", "windows-targets 0.52.6", ] @@ -8428,7 +8277,17 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset", + "fixedbitset 0.4.2", + "indexmap 2.7.1", +] + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset 0.5.7", "indexmap 2.7.1", ] @@ -8481,7 +8340,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -8507,22 +8366,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -8577,9 +8436,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" @@ -8608,9 +8467,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "postgres-native-tls" @@ -8687,14 +8546,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" dependencies = [ "array-init", - "bit-vec 0.6.3", + "bit-vec", "bytes", "chrono", "fallible-iterator 0.2.0", "postgres-protocol 0.6.8", "serde", "serde_json", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -8712,12 +8571,6 @@ dependencies = [ "zerocopy 0.7.35", ] -[[package]] -name = "presser" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" - [[package]] name = "pretty-hex" version = "0.3.0" @@ -8736,12 +8589,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" dependencies = [ "proc-macro2", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -8755,13 +8608,37 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit 0.22.24", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -8781,7 +8658,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -8792,7 +8669,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -8804,14 +8681,14 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -8871,7 +8748,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "syn 2.0.98", + "syn 2.0.99", "thiserror 1.0.69", "typify", "unicode-ident", @@ -8891,7 +8768,7 @@ dependencies = [ "serde_json", "serde_tokenstream", "serde_yaml", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -8935,12 +8812,12 @@ dependencies = [ "log", "multimap", "once_cell", - "petgraph", - "prettyplease 0.2.29", + "petgraph 0.7.1", + "prettyplease 0.2.30", "prost", "prost-types", "regex", - "syn 2.0.98", + "syn 2.0.99", "tempfile", ] @@ -8954,7 +8831,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -9001,7 +8878,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "getopts", "memchr", "unicase", @@ -9047,13 +8924,13 @@ dependencies = [ [[package]] name = "quick_cache" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f67cfc9c723c39f3615eb0840b00c4cb9e2b068d2fa761a30d845ec91730a59" +checksum = "0af25b4e960ffdf0dead61cf0cec0c2e44c76927bf933ab4f02e2858fb449397" dependencies = [ "ahash 0.8.11", "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "parking_lot", ] @@ -9070,7 +8947,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustls 0.23.23", "socket2", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -9084,12 +8961,12 @@ dependencies = [ "bytes", "getrandom 0.2.15", "rand 0.8.5", - "ring 0.17.10", + "ring 0.17.12", "rustc-hash 2.1.1", "rustls 0.23.23", "rustls-pki-types", "slab", - "thiserror 2.0.11", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -9101,7 +8978,7 @@ version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.1", "libc", "once_cell", "socket2", @@ -9111,9 +8988,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] @@ -9165,8 +9042,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.1", - "zerocopy 0.8.20", + "rand_core 0.9.3", + "zerocopy 0.8.22", ] [[package]] @@ -9196,7 +9073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.1", + "rand_core 0.9.3", ] [[package]] @@ -9219,12 +9096,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.20", ] [[package]] @@ -9347,11 +9223,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -9367,36 +9243,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", -] - -[[package]] -name = "regalloc2" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" -dependencies = [ - "allocator-api2", - "bumpalo", - "hashbrown 0.15.2", - "log", - "rustc-hash 2.1.1", - "smallvec", + "syn 2.0.99", ] [[package]] @@ -9518,9 +9380,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "async-compression", "base64 0.22.1", @@ -9556,8 +9418,9 @@ dependencies = [ "system-configuration 0.6.1", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", @@ -9611,9 +9474,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.10" +version = "0.17.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34b5020fcdea098ef7d95e9f89ec15952123a4a039badd09fabebe9e963e839" +checksum = "ed9b823fa29b721a59671b41d6b06e66b29e0628e207e8b1c3ceeda701ec928d" dependencies = [ "cc", "cfg-if", @@ -9647,7 +9510,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -9668,7 +9531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.8.0", + "bitflags 2.9.0", "serde", "serde_derive", ] @@ -9693,13 +9556,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rumqttc" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1568e15fab2d546f940ed3a21f48bbbd1c494c90c99c4481339364a497f94a9" +dependencies = [ + "bytes", + "flume", + "futures-util", + "log", + "native-tls", + "rustls-native-certs 0.7.3", + "rustls-pemfile 2.2.0", + "rustls-webpki 0.102.8", + "thiserror 1.0.69", + "tokio", + "tokio-native-tls", + "tokio-rustls 0.25.0", +] + [[package]] name = "rusqlite" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "fallible-iterator 0.3.0", "fallible-streaming-iterator", "hashlink 0.9.1", @@ -9728,7 +9611,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.98", + "syn 2.0.99", "walkdir", ] @@ -9802,7 +9685,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.25", + "semver 1.0.26", ] [[package]] @@ -9833,7 +9716,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -9847,11 +9730,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.10", + "ring 0.17.12", "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring 0.17.12", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + [[package]] name = "rustls" version = "0.23.23" @@ -9860,7 +9757,7 @@ checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "log", "once_cell", - "ring 0.17.10", + "ring 0.17.12", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -9949,7 +9846,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.10", + "ring 0.17.12", "untrusted 0.9.0", ] @@ -9959,7 +9856,7 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.10", + "ring 0.17.12", "rustls-pki-types", "untrusted 0.9.0", ] @@ -10023,9 +9920,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rustyline" @@ -10033,7 +9930,7 @@ version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "clipboard-win", "fd-lock", @@ -10051,9 +9948,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "ryu-js" @@ -10114,7 +10011,7 @@ dependencies = [ "serde", "thiserror 1.0.69", "url", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -10143,28 +10040,28 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "chrono", "dyn-clone", "schemars_derive", "serde", "serde_json", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] name = "schemars_derive" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -10197,7 +10094,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.10", + "ring 0.17.12", "untrusted 0.9.0", ] @@ -10228,7 +10125,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -10241,7 +10138,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -10269,9 +10166,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] @@ -10284,9 +10181,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "seq-macro" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" @@ -10315,7 +10212,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "ordered-float 2.10.1", + "ordered-float", "serde", ] @@ -10332,9 +10229,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e" dependencies = [ "serde", ] @@ -10347,7 +10244,7 @@ checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -10358,14 +10255,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "indexmap 2.7.1", "itoa", @@ -10385,9 +10282,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", @@ -10426,13 +10323,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -10453,7 +10350,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -10470,15 +10367,15 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.247.0" +version = "0.245.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bbfafb7b707cbed49d1eaf48f4aa41b5ff57f813d1a80f77244e6e2fa4507e" +checksum = "945f93c91e0c7e4799b5fefff076756141aae92e262c4dc4833310dd3d2d845e" dependencies = [ "deno_error", "num-bigint", "serde", "smallvec", - "thiserror 2.0.11", + "thiserror 2.0.12", "v8", ] @@ -10509,7 +10406,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -10689,7 +10586,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", ] @@ -10783,9 +10680,9 @@ dependencies = [ [[package]] name = "smtp-proto" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b8ad3dd187f0d4debab02ad65405a9919d6a4f7bce25bd64a258781063a53a" +checksum = "b7d3950ab75b03c52f2f13fd52aab91c9d62698b231b67240e85c3ef5301e63e" [[package]] name = "snafu" @@ -10884,7 +10781,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -10943,7 +10840,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -10989,12 +10886,12 @@ dependencies = [ "serde_json", "sha2 0.10.8", "smallvec", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-stream", "tracing", "url", - "uuid 1.14.0", + "uuid 1.15.1", "webpki-roots", ] @@ -11008,7 +10905,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11031,7 +10928,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.98", + "syn 2.0.99", "tempfile", "tokio", "url", @@ -11046,7 +10943,7 @@ dependencies = [ "atoi", "base64 0.22.1", "bigdecimal", - "bitflags 2.8.0", + "bitflags 2.9.0", "byteorder", "bytes", "chrono", @@ -11076,9 +10973,9 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.11", + "thiserror 2.0.12", "tracing", - "uuid 1.14.0", + "uuid 1.15.1", "whoami", ] @@ -11091,7 +10988,7 @@ dependencies = [ "atoi", "base64 0.22.1", "bigdecimal", - "bitflags 2.8.0", + "bitflags 2.9.0", "byteorder", "chrono", "crc", @@ -11117,9 +11014,9 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.11", + "thiserror 2.0.12", "tracing", - "uuid 1.14.0", + "uuid 1.15.1", "whoami", ] @@ -11145,7 +11042,7 @@ dependencies = [ "sqlx-core", "tracing", "url", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -11182,7 +11079,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11242,7 +11139,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11255,7 +11152,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11363,7 +11260,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11372,7 +11269,7 @@ version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "is-macro", "num-bigint", "phf", @@ -11412,7 +11309,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11458,7 +11355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" dependencies = [ "better_scoped_tls", - "bitflags 2.8.0", + "bitflags 2.9.0", "indexmap 2.7.1", "once_cell", "phf", @@ -11497,7 +11394,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11604,7 +11501,7 @@ checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11615,7 +11512,7 @@ checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11638,7 +11535,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -11654,9 +11551,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -11698,14 +11595,14 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "sys_traits" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "638f0e61b5134e56b2abdf4c704fd44672603f15ca09013f314649056f3fee4d" +checksum = "5b46ac05dfbe9fd3a9703eff20e17f5b31e7b6a54daf27a421dcd56c7a27ecdd" dependencies = [ "libc", "windows-sys 0.59.0", @@ -11717,7 +11614,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "byteorder", "enum-as-inner", "libc", @@ -11742,7 +11639,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -11798,7 +11695,7 @@ dependencies = [ "lru", "lz4_flex", "measure_time", - "memmap2", + "memmap2 0.9.5", "num_cpus", "once_cell", "oneshot", @@ -11820,7 +11717,7 @@ dependencies = [ "tempfile", "thiserror 1.0.69", "time", - "uuid 1.14.0", + "uuid 1.15.1", "winapi", ] @@ -11922,21 +11819,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", "xattr", ] -[[package]] -name = "target-lexicon" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" - [[package]] name = "tempfile" version = "3.17.1" @@ -11980,11 +11871,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -11995,18 +11886,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -12027,7 +11918,7 @@ checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" dependencies = [ "byteorder", "integer-encoding", - "ordered-float 2.10.1", + "ordered-float", ] [[package]] @@ -12056,7 +11947,7 @@ dependencies = [ "tokio-rustls 0.24.1", "tokio-util", "tracing", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] @@ -12103,9 +11994,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" dependencies = [ "deranged", "itoa", @@ -12118,15 +12009,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", @@ -12153,9 +12044,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -12251,7 +12142,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -12339,9 +12230,20 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls 0.23.23", "tokio", @@ -12429,11 +12331,11 @@ dependencies = [ "http 1.2.0", "httparse", "rand 0.8.5", - "ring 0.17.10", + "ring 0.17.12", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", ] @@ -12521,7 +12423,7 @@ dependencies = [ "rustls-pemfile 2.2.0", "socket2", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-stream", "tower 0.4.13", "tower-layer", @@ -12538,7 +12440,7 @@ dependencies = [ "home", "once_cell", "regex", - "semver 1.0.25", + "semver 1.0.26", "walkdir", ] @@ -12602,7 +12504,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", - "bitflags 2.8.0", + "bitflags 2.9.0", "bytes", "futures-core", "http 1.2.0", @@ -12660,7 +12562,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -12701,7 +12603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3beec919fbdf99d719de8eda6adae3281f8a5b71ae40431f44dc7423053d34" dependencies = [ "loki-api", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "snap", @@ -12890,7 +12792,7 @@ dependencies = [ "regress", "schemars", "serde_json", - "syn 2.0.98", + "syn 2.0.99", "thiserror 1.0.69", "unicode-ident", ] @@ -12907,7 +12809,7 @@ dependencies = [ "serde", "serde_json", "serde_tokenstream", - "syn 2.0.98", + "syn 2.0.99", "typify-impl", ] @@ -12918,7 +12820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab82fc73182c29b02e2926a6df32f2241dbadb5cfc111fd595515b3598f46bb3" dependencies = [ "rand 0.9.0", - "uuid 1.14.0", + "uuid 1.15.1", "web-time", ] @@ -13012,9 +12914,9 @@ checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-normalization" @@ -13210,9 +13112,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ "getrandom 0.3.1", "serde", @@ -13220,12 +13122,12 @@ dependencies = [ [[package]] name = "v8" -version = "134.4.0" +version = "130.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224c6c3d1fd3c0356224b2ad355b61c242cdafa9d14cc31b7f161ea177b3b4e9" +checksum = "a511192602f7b435b0a241c1947aa743eb7717f20a9195f4b5e8ed1952e01db1" dependencies = [ "bindgen 0.70.1", - "bitflags 2.8.0", + "bitflags 2.9.0", "fslock", "gzip-header", "home", @@ -13241,7 +13143,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "encoding_rs", "indexmap 2.7.1", "num-bigint", @@ -13360,18 +13262,19 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] @@ -13394,7 +13297,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -13410,13 +13313,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-test" -version = "0.3.42" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -13424,20 +13326,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.42" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -13453,7 +13355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" dependencies = [ "deno_error", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -13485,18 +13387,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-transport-proto" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c564e589056437e3a73383f4e0903965879087c9ee0f715e562bbf4e47c08b75" -dependencies = [ - "bytes", - "http 1.2.0", - "thiserror 2.0.11", - "url", -] - [[package]] name = "webpki-root-certs" version = "0.26.8" @@ -13517,14 +13407,15 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "24.0.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a39b8842dc9ffcbe34346e3ab6d496b32a47f6497e119d762c97fcaae3cb37" +checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" dependencies = [ "arrayvec", - "bit-vec 0.8.0", - "bitflags 2.8.0", - "cfg_aliases", + "bit-vec", + "bitflags 2.9.0", + "cfg_aliases 0.1.1", + "codespan-reporting", "document-features", "indexmap 2.7.1", "log", @@ -13537,30 +13428,30 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "smallvec", - "thiserror 2.0.11", + "thiserror 1.0.69", + "web-sys", "wgpu-hal", "wgpu-types", ] [[package]] name = "wgpu-hal" -version = "24.0.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a782e5056b060b0b4010881d1decddd059e44f2ecd01e2db2971b48ad3627e5" +checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" dependencies = [ "android_system_properties", "arrayvec", "ash", - "bit-set 0.8.0", - "bitflags 2.8.0", + "bit-set", + "bitflags 2.9.0", "block", - "bytemuck", - "cfg_aliases", + "cfg_aliases 0.1.1", "core-graphics-types", + "d3d12", "glow", "glutin_wgl_sys", "gpu-alloc", - "gpu-allocator", "gpu-descriptor", "js-sys", "khronos-egl", @@ -13572,30 +13463,27 @@ dependencies = [ "ndk-sys", "objc", "once_cell", - "ordered-float 4.6.0", "parking_lot", "profiling", "range-alloc", "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.11", + "thiserror 1.0.69", "wasm-bindgen", "web-sys", "wgpu-types", - "windows", - "windows-core 0.58.0", + "winapi", ] [[package]] name = "wgpu-types" -version = "24.0.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "js-sys", - "log", "serde", "web-sys", ] @@ -13630,7 +13518,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.5.9", + "redox_syscall 0.5.10", "wasite", "web-sys", ] @@ -13674,7 +13562,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "axum", @@ -13692,7 +13580,7 @@ dependencies = [ "prometheus", "quote", "rand 0.9.0", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "sha2 0.10.8", @@ -13703,7 +13591,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid 1.14.0", + "uuid 1.15.1", "v8", "windmill-api", "windmill-api-client", @@ -13717,7 +13605,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "argon2", @@ -13770,8 +13658,9 @@ dependencies = [ "rand 0.9.0", "rdkafka", "regex", - "reqwest 0.12.9", + "reqwest 0.12.12", "rsa", + "rumqttc", "rust-embed", "rust_decimal", "samael", @@ -13782,7 +13671,7 @@ dependencies = [ "sql-builder", "sqlx", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tinyvector", "tokenizers", @@ -13800,7 +13689,7 @@ dependencies = [ "ulid", "url", "urlencoding", - "uuid 1.14.0", + "uuid 1.15.1", "windmill-audit", "windmill-common", "windmill-git-sync", @@ -13813,7 +13702,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.467.1" +version = "1.475.0" dependencies = [ "base64 0.22.1", "chrono", @@ -13826,12 +13715,12 @@ dependencies = [ "serde", "serde_json", "syn 1.0.109", - "uuid 1.14.0", + "uuid 1.15.1", ] [[package]] name = "windmill-audit" -version = "1.467.1" +version = "1.475.0" dependencies = [ "chrono", "serde", @@ -13844,27 +13733,28 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "serde", "serde_json", "sqlx", "tracing", - "uuid 1.14.0", + "uuid 1.15.1", "windmill-common", "windmill-queue", ] [[package]] name = "windmill-common" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "async-stream", "aws-config", "aws-sdk-sts", "axum", + "backon", "bytes", "chrono", "chrono-tz 0.10.1", @@ -13881,6 +13771,7 @@ dependencies = [ "hyper 1.6.0", "indexmap 2.7.1", "itertools 0.14.0", + "jsonwebtoken", "lazy_static", "magic-crypt", "mail-send", @@ -13895,14 +13786,14 @@ dependencies = [ "quick_cache", "rand 0.9.0", "regex", - "reqwest 0.12.9", - "semver 1.0.25", + "reqwest 0.12.12", + "semver 1.0.26", "serde", "serde_json", "sha2 0.10.8", "sqlx", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "tikv-jemalloc-ctl", "tokio", "tonic", @@ -13911,27 +13802,27 @@ dependencies = [ "tracing-loki", "tracing-opentelemetry", "tracing-subscriber", - "uuid 1.14.0", + "uuid 1.15.1", "windmill-macros", ] [[package]] name = "windmill-git-sync" -version = "1.467.1" +version = "1.475.0" dependencies = [ "regex", "serde", "serde_json", "sqlx", "tracing", - "uuid 1.14.0", + "uuid 1.15.1", "windmill-common", "windmill-queue", ] [[package]] name = "windmill-indexer" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "bytes", @@ -13948,25 +13839,25 @@ dependencies = [ "tokio", "tokio-tar", "tracing", - "uuid 1.14.0", + "uuid 1.15.1", "windmill-common", ] [[package]] name = "windmill-macros" -version = "1.467.1" +version = "1.475.0" dependencies = [ "itertools 0.14.0", "lazy_static", "proc-macro2", "quote", "regex", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "windmill-parser" -version = "1.467.1" +version = "1.475.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -13975,7 +13866,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "lazy_static", @@ -13987,7 +13878,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "serde_json", @@ -13999,7 +13890,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "gosyn", @@ -14011,7 +13902,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "lazy_static", @@ -14023,7 +13914,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14034,7 +13925,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14045,7 +13936,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "async-recursion", @@ -14065,7 +13956,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -14075,14 +13966,14 @@ dependencies = [ "quote", "regex", "serde_json", - "syn 2.0.98", + "syn 2.0.99", "toml 0.7.8", "windmill-parser", ] [[package]] name = "windmill-parser-sql" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "lazy_static", @@ -14094,7 +13985,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "lazy_static", @@ -14112,7 +14003,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "getrandom 0.2.15", @@ -14134,7 +14025,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "serde_json", @@ -14144,7 +14035,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "async-recursion", @@ -14161,7 +14052,7 @@ dependencies = [ "lazy_static", "prometheus", "regex", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "serde_urlencoded", @@ -14170,14 +14061,14 @@ dependencies = [ "tokio", "tracing", "ulid", - "uuid 1.14.0", + "uuid 1.15.1", "windmill-audit", "windmill-common", ] [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.467.1" +version = "1.475.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -14187,13 +14078,13 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.467.1" +version = "1.475.0" dependencies = [ "anyhow", "async-recursion", "backon", "base64 0.22.1", - "bit-vec 0.6.3", + "bit-vec", "bollard", "bytes", "chrono", @@ -14235,7 +14126,7 @@ dependencies = [ "prometheus", "rand 0.9.0", "regex", - "reqwest 0.12.9", + "reqwest 0.12.12", "rust_decimal", "serde", "serde_json", @@ -14248,7 +14139,7 @@ dependencies = [ "tokio-util", "tracing", "urlencoding", - "uuid 1.14.0", + "uuid 1.15.1", "windmill-audit", "windmill-common", "windmill-git-sync", @@ -14308,7 +14199,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -14319,7 +14210,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -14549,7 +14440,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -14675,7 +14566,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "synstructure 0.13.1", ] @@ -14691,11 +14582,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +checksum = "09612fda0b63f7cb9e0af7e5916fe5a1f8cdcb066829f10f36883207628a4872" dependencies = [ - "zerocopy-derive 0.8.20", + "zerocopy-derive 0.8.22", ] [[package]] @@ -14706,38 +14597,38 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "zerocopy-derive" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +checksum = "79f81d38d7a2ed52d8f034e62c568e111df9bf8aba2f7cf19ddc5bf7bd89d520" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "synstructure 0.13.1", ] @@ -14758,7 +14649,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -14780,7 +14671,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -14821,18 +14712,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-jpeg" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" -dependencies = [ - "zune-core", -] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index f50794b56b..f5d1be0897 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.467.1" +version = "1.475.0" authors.workspace = true edition.workspace = true @@ -30,7 +30,7 @@ members = [ ] [workspace.package] -version = "1.467.1" +version = "1.475.0" authors = ["Ruben Fiszel "] edition = "2021" @@ -59,7 +59,7 @@ flow_testing = ["windmill-worker/flow_testing"] openidconnect = ["windmill-api/openidconnect"] cloud = ["windmill-queue/cloud", "windmill-worker/cloud"] jemalloc = ["windmill-common/jemalloc", "dep:tikv-jemallocator", "dep:tikv-jemalloc-sys", "dep:tikv-jemalloc-ctl"] -tantivy = ["dep:windmill-indexer", "windmill-api/tantivy", "windmill-indexer/enterprise", "windmill-indexer/parquet", "enterprise", "parquet"] +tantivy = ["dep:windmill-indexer", "windmill-api/tantivy", "windmill-indexer/enterprise", "windmill-indexer/parquet", "windmill-common/tantivy", "enterprise", "parquet"] sqlx = ["windmill-worker/sqlx"] deno_core = ["windmill-worker/deno_core", "dep:deno_core", "dep:v8"] kafka = ["windmill-api/kafka"] @@ -74,6 +74,7 @@ mssql = ["windmill-worker/mssql"] bigquery = ["windmill-worker/bigquery"] websocket = ["windmill-api/websocket"] postgres_trigger = ["windmill-api/postgres_trigger"] +mqtt_trigger = ["windmill-api/mqtt_trigger"] sqs_trigger = ["windmill-api/sqs_trigger"] python = ["windmill-worker/python"] smtp = ["windmill-api/smtp", "windmill-common/smtp"] @@ -173,7 +174,7 @@ serde_json = { version = "^1", features = ["preserve_order", "raw_value"] } uuid = { version = "^1", features = ["serde", "v4"] } thiserror = "^2" anyhow = "^1" -chrono = { version = "0.4.35", features = ["serde"] } +chrono = { version = "=0.4.39", features = ["serde"] } chrono-tz = "^0.10.1" tracing = "^0" tracing-subscriber = { version = "^0", features = ["env-filter", "json"] } @@ -211,20 +212,20 @@ itertools = "^0" regex = "^1" semver = "^1" -v8 = "=134.4.0" # Exact version -deno_fetch = "0.216.0" -deno_tls = "0.179.0" -deno_console = "0.192.0" -deno_url = "0.192.0" -deno_webidl = "0.192.0" -deno_web = "0.223.0" -deno_io = "0.102.0" -deno_net = "0.184.0" -deno_core = "0.338.0" +v8 = "=130.0.7" # Exact version +deno_fetch = "0.214.0" +deno_tls = "0.177.0" +deno_console = "0.190.0" +deno_url = "0.190.0" +deno_webidl = "0.190.0" +deno_web = "0.221.0" +deno_io = "0.100.0" +deno_net = "0.182.0" +deno_core = "0.336.0" deno_ast = { version = "=0.44.0", features = ["transpiling"] } -deno_permissions = "0.51.0" -deno_runtime = { version = "0.200.0", features = ["transpile"] } -deno_telemetry = "0.14.0" +deno_permissions = "0.49.0" +deno_runtime = { version = "0.198.0", features = ["transpile"] } +deno_telemetry = "0.12.0" deno_error = "=0.5.5" swc_common = "=0.37.5" @@ -355,3 +356,4 @@ tokio-tungstenite = { version = "0.24.0", features = ["native-tls"] } tree-sitter = {version = "0.23.0", features = []} tree-sitter-c-sharp = "0.23.0" oracle = { version = "0.6.3", features = ["chrono"] } +rumqttc = { version = "0.24.0", features = ["use-native-tls"]} \ No newline at end of file diff --git a/backend/custom_migrations/grant_all_current_schema.sql b/backend/custom_migrations/grant_all_current_schema.sql new file mode 100644 index 0000000000..4a2860cf29 --- /dev/null +++ b/backend/custom_migrations/grant_all_current_schema.sql @@ -0,0 +1,27 @@ +DO +$$ +DECLARE + tbl_name text; + policy_exists boolean; + current_sch text; + tbl_names text[] := ARRAY['account', 'app', 'audit', 'capture', 'completed_job', 'flow', 'folder', 'http_trigger', 'queue', 'raw_app', 'resource', 'schedule', 'script', 'usr_to_group', 'variable']; +BEGIN + -- Get the current schema + SELECT current_schema() INTO current_sch; + + FOR tbl_name IN SELECT unnest(tbl_names) + LOOP + SELECT EXISTS ( + SELECT 1 + FROM pg_policies + WHERE schemaname = current_sch + AND tablename = tbl_name + AND policyname = 'admin_policy' + ) INTO policy_exists; + + IF NOT policy_exists THEN + EXECUTE format('CREATE POLICY admin_policy ON %I.%I TO windmill_admin USING (true);', current_sch, tbl_name); + END IF; + END LOOP; +END; +$$; diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 31bf66cae8..4c2e4d7211 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -2dca712cde013e7dda96dc3162e47a148973a0dc \ No newline at end of file +d3e20c73b06a7c6820868769a49b5fa621591653 \ No newline at end of file diff --git a/backend/migrations/20250205131521_clear_cache_on_webhook_changes.down.sql b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.down.sql new file mode 100644 index 0000000000..179a2cd6de --- /dev/null +++ b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.down.sql @@ -0,0 +1,4 @@ +-- Add down migration script here + +DROP TRIGGER webhook_change_trigger ON workspace_settings; +DROP FUNCTION notify_webhook_change(); diff --git a/backend/migrations/20250205131521_clear_cache_on_webhook_changes.up.sql b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.up.sql new file mode 100644 index 0000000000..a8a13c10cf --- /dev/null +++ b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.up.sql @@ -0,0 +1,15 @@ +-- Add up migration script here + +CREATE OR REPLACE FUNCTION notify_webhook_change() +RETURNS TRIGGER AS $$ +BEGIN + PERFORM pg_notify('notify_webhook_change', NEW.workspace_id); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER webhook_change_trigger +AFTER UPDATE OF webhook ON workspace_settings +FOR EACH ROW +WHEN (OLD.webhook IS DISTINCT FROM NEW.webhook) +EXECUTE FUNCTION notify_webhook_change(); diff --git a/backend/migrations/20250205131522_add_zombie_job_counter.down.sql b/backend/migrations/20250205131522_add_zombie_job_counter.down.sql new file mode 100644 index 0000000000..e8686738c5 --- /dev/null +++ b/backend/migrations/20250205131522_add_zombie_job_counter.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here + +DROP TABLE zombie_job_counter; \ No newline at end of file diff --git a/backend/migrations/20250205131522_add_zombie_job_counter.up.sql b/backend/migrations/20250205131522_add_zombie_job_counter.up.sql new file mode 100644 index 0000000000..cc8c7457e3 --- /dev/null +++ b/backend/migrations/20250205131522_add_zombie_job_counter.up.sql @@ -0,0 +1,7 @@ +-- Add up migration script here + +CREATE TABLE IF NOT EXISTS zombie_job_counter ( + job_id UUID PRIMARY KEY REFERENCES v2_job (id) ON DELETE CASCADE, + counter INTEGER NOT NULL DEFAULT 0 +); + diff --git a/backend/migrations/20250205131523_grant_all_in_current_schema.down.sql b/backend/migrations/20250205131523_grant_all_in_current_schema.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250205131523_grant_all_in_current_schema.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250205131523_grant_all_in_current_schema.up.sql b/backend/migrations/20250205131523_grant_all_in_current_schema.up.sql new file mode 100644 index 0000000000..04047fa2b0 --- /dev/null +++ b/backend/migrations/20250205131523_grant_all_in_current_schema.up.sql @@ -0,0 +1,37 @@ +DO +$do$ +DECLARE + current_schema_name TEXT; +BEGIN + -- Get the current schema for the session + SELECT current_schema() INTO current_schema_name; + + -- Lock the roles table to prevent race conditions + LOCK TABLE pg_catalog.pg_roles; + + + + EXECUTE format('GRANT USAGE ON SCHEMA %I TO windmill_user', current_schema_name); + EXECUTE format('GRANT USAGE ON SCHEMA %I TO windmill_admin', current_schema_name); + + + -- Grant privileges dynamically to the current schema + EXECUTE format('GRANT ALL ON ALL TABLES IN SCHEMA %I TO windmill_user', current_schema_name); + EXECUTE format('GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA %I TO windmill_user', current_schema_name); + + -- Alter default privileges dynamically + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON TABLES TO windmill_user', current_schema_name); + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON SEQUENCES TO windmill_user', current_schema_name); + + -- Grant privileges dynamically to the current schema + EXECUTE format('GRANT ALL ON ALL TABLES IN SCHEMA %I TO windmill_admin', current_schema_name); + EXECUTE format('GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA %I TO windmill_admin', current_schema_name); + + -- Alter default privileges dynamically + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON TABLES TO windmill_admin', current_schema_name); + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON SEQUENCES TO windmill_admin', current_schema_name); + +EXCEPTION WHEN OTHERS THEN + RAISE NOTICE 'Error granting proper permissions to windmill users: %', SQLERRM; +END +$do$; diff --git a/backend/migrations/20250207172929_mqtt_trigger.down.sql b/backend/migrations/20250207172929_mqtt_trigger.down.sql new file mode 100644 index 0000000000..5b2fba771a --- /dev/null +++ b/backend/migrations/20250207172929_mqtt_trigger.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +DROP TABLE mqtt_trigger; +DROP TYPE MQTT_CLIENT_VERSION; \ No newline at end of file diff --git a/backend/migrations/20250207172929_mqtt_trigger.up.sql b/backend/migrations/20250207172929_mqtt_trigger.up.sql new file mode 100644 index 0000000000..feebd3602c --- /dev/null +++ b/backend/migrations/20250207172929_mqtt_trigger.up.sql @@ -0,0 +1,72 @@ + -- Add up migration script here +CREATE TYPE MQTT_CLIENT_VERSION AS ENUM ('v3', 'v5'); + +CREATE TABLE mqtt_trigger ( + mqtt_resource_path VARCHAR(255) NOT NULL, + subscribe_topics JSONB[] NOT NULL, + client_version MQTT_CLIENT_VERSION DEFAULT 'v5' NOT NULL, + v5_config JSONB NULL, + v3_config JSONB NULL, + client_id VARCHAR(65535) DEFAULT NULL, + path VARCHAR(255) NOT NULL, + script_path VARCHAR(255) NOT NULL, + is_flow BOOLEAN NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + edited_by VARCHAR(50) NOT NULL, + email VARCHAR(255) NOT NULL, + edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + extra_perms JSONB NOT NULL DEFAULT '{}', + server_id VARCHAR(50) NULL, + last_server_ping TIMESTAMPTZ NULL, + error TEXT NULL, + enabled BOOLEAN NOT NULL, + PRIMARY KEY (path, workspace_id) +); + +GRANT ALL ON mqtt_trigger TO windmill_user; +GRANT ALL ON mqtt_trigger TO windmill_admin; + +ALTER TABLE mqtt_trigger ENABLE ROW LEVEL SECURITY; + +CREATE POLICY admin_policy ON mqtt_trigger FOR ALL TO windmill_admin USING (true); + +CREATE POLICY see_folder_extra_perms_user_select ON mqtt_trigger FOR SELECT TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_insert ON mqtt_trigger FOR INSERT TO windmill_user +WITH CHECK (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_update ON mqtt_trigger FOR UPDATE TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_delete ON mqtt_trigger FOR DELETE TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); + +CREATE POLICY see_own ON mqtt_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'u' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = current_setting('session.user')); +CREATE POLICY see_member ON mqtt_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'g' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[])); + +CREATE POLICY see_extra_perms_user_select ON mqtt_trigger FOR SELECT TO windmill_user +USING (extra_perms ? CONCAT('u/', current_setting('session.user'))); +CREATE POLICY see_extra_perms_user_insert ON mqtt_trigger FOR INSERT TO windmill_user +WITH CHECK ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_update ON mqtt_trigger FOR UPDATE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_delete ON mqtt_trigger FOR DELETE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); + +CREATE POLICY see_extra_perms_groups_select ON mqtt_trigger FOR SELECT TO windmill_user +USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]); +CREATE POLICY see_extra_perms_groups_insert ON mqtt_trigger FOR INSERT TO windmill_user +WITH CHECK (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_update ON mqtt_trigger FOR UPDATE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_delete ON mqtt_trigger FOR DELETE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); \ No newline at end of file diff --git a/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.down.sql b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.down.sql new file mode 100644 index 0000000000..0197d4e7b1 --- /dev/null +++ b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.down.sql @@ -0,0 +1 @@ +-- Add down migration script here \ No newline at end of file diff --git a/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.up.sql b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.up.sql new file mode 100644 index 0000000000..62d397bc94 --- /dev/null +++ b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'mqtt'; \ No newline at end of file diff --git a/backend/migrations/20250224131521_flow_workspace_runnables.down.sql b/backend/migrations/20250224131521_flow_workspace_runnables.down.sql new file mode 100644 index 0000000000..930245a236 --- /dev/null +++ b/backend/migrations/20250224131521_flow_workspace_runnables.down.sql @@ -0,0 +1 @@ +DROP TABLE flow_workspace_runnables; \ No newline at end of file diff --git a/backend/migrations/20250224131521_flow_workspace_runnables.up.sql b/backend/migrations/20250224131521_flow_workspace_runnables.up.sql new file mode 100644 index 0000000000..fe61497c6d --- /dev/null +++ b/backend/migrations/20250224131521_flow_workspace_runnables.up.sql @@ -0,0 +1,12 @@ +CREATE TABLE flow_workspace_runnables ( + flow_path VARCHAR(255) NOT NULL, + runnable_path VARCHAR(255) NOT NULL, + script_hash BIGINT NULL, + runnable_is_flow BOOLEAN NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + FOREIGN KEY (workspace_id, flow_path) REFERENCES flow (workspace_id, path) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX flow_workspace_without_hash_unique_idx ON flow_workspace_runnables (flow_path, runnable_path, runnable_is_flow, workspace_id) WHERE script_hash IS NULL; +CREATE UNIQUE INDEX flow_workspace_with_hash_unique_idx ON flow_workspace_runnables (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) WHERE script_hash IS NOT NULL; +CREATE INDEX flow_workspace_runnable_path_is_flow_idx ON flow_workspace_runnables (runnable_path, runnable_is_flow, workspace_id); diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index dfb18f1156..0000000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "backend", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/backend/parsers/windmill-parser-py/src/lib.rs b/backend/parsers/windmill-parser-py/src/lib.rs index 76f0c415ea..f5f9574888 100644 --- a/backend/parsers/windmill-parser-py/src/lib.rs +++ b/backend/parsers/windmill-parser-py/src/lib.rs @@ -96,11 +96,11 @@ pub fn parse_python_signature( .iter() .enumerate() .map(|(i, x)| { - let mut typ = x + let (mut typ, has_default) = x .as_arg() .annotation .as_ref() - .map_or(Typ::Unknown, |e| parse_expr(e)); + .map_or((Typ::Unknown, false), |e| parse_expr(e)); let default = if i >= def_arg_start { params @@ -138,7 +138,7 @@ pub fn parse_python_signature( otyp: None, name: x.as_arg().arg.to_string(), typ, - has_default: default.is_some(), + has_default: has_default || default.is_some(), default, oidx: None, } @@ -158,17 +158,27 @@ pub fn parse_python_signature( } } -fn parse_expr(e: &Box) -> Typ { +fn parse_expr(e: &Box) -> (Typ, bool) { match e.as_ref() { - Expr::Name(ExprName { id, .. }) => parse_typ(id.as_ref()), + Expr::Name(ExprName { id, .. }) => (parse_typ(id.as_ref()), false), Expr::Attribute(x) => { if x.value .as_name_expr() .is_some_and(|x| x.id.as_str() == "wmill") { - parse_typ(x.attr.as_str()) + (parse_typ(x.attr.as_str()), false) } else { - Typ::Unknown + (Typ::Unknown, false) + } + } + Expr::BinOp(x) => { + if matches!( + x.right.as_ref(), + Expr::Constant(ExprConstant { value: Constant::None, .. }) + ) { + (parse_expr(&x.left).0, true) + } else { + (Typ::Unknown, false) } } Expr::Subscript(x) => match x.value.as_ref() { @@ -193,14 +203,15 @@ fn parse_expr(e: &Box) -> Typ { } _ => None, }; - Typ::Str(values) + (Typ::Str(values), false) } - "List" => Typ::List(Box::new(parse_expr(&x.slice))), - _ => Typ::Unknown, + "List" => (Typ::List(Box::new(parse_expr(&x.slice).0)), false), + "Optional" => (parse_expr(&x.slice).0, true), + _ => (Typ::Unknown, false), }, - _ => Typ::Unknown, + _ => (Typ::Unknown, false), }, - _ => Typ::Unknown, + _ => (Typ::Unknown, false), } } @@ -676,4 +687,53 @@ def main(a: list, e: List[int], b: list = [1,2,3,4], c = [1,2,3,4], d = ["a", "b Ok(()) } + + #[test] + fn test_parse_python_sig_9() -> anyhow::Result<()> { + let code = r#" +from typing import Optional +def main(a: str, b: Optional[str], c: str | None): return +"#; + println!( + "{}", + serde_json::to_string(&parse_python_signature(code, None, false)?)? + ); + assert_eq!( + parse_python_signature(code, None, false)?, + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + otyp: None, + name: "a".to_string(), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + Arg { + otyp: None, + name: "b".to_string(), + typ: Typ::Str(None), + default: None, + has_default: true, + oidx: None + }, + Arg { + otyp: None, + name: "c".to_string(), + typ: Typ::Str(None), + default: None, + has_default: true, + oidx: None + }, + ], + no_main_func: Some(false), + has_preprocessor: Some(false) + } + ); + + Ok(()) + } } diff --git a/backend/parsers/windmill-parser-wasm/tests/wasm.rs b/backend/parsers/windmill-parser-wasm/tests/wasm.rs index 9f6d452a21..e8dc1e986b 100644 --- a/backend/parsers/windmill-parser-wasm/tests/wasm.rs +++ b/backend/parsers/windmill-parser-wasm/tests/wasm.rs @@ -4,6 +4,7 @@ use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ}; use windmill_parser_bash::parse_powershell_sig; use windmill_parser_ts::{parse_deno_signature, parse_expr_for_ids, parse_expr_for_imports}; +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_deno_sig() -> anyhow::Result<()> { let code = " @@ -146,6 +147,8 @@ export function main(test1?: string, test2: string = \"burkina\", Ok(()) } + +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_deno_sig_implicit_types() -> anyhow::Result<()> { let code = " @@ -224,6 +227,7 @@ export function main(test2 = \"burkina\", Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_deno_types() -> anyhow::Result<()> { let code = " @@ -274,6 +278,7 @@ export function main(foo: FooBar, {a, b}: FooBar, {c, d}: FooBar = {a: \"foo\", Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_enum_list() -> anyhow::Result<()> { let code = " @@ -305,6 +310,7 @@ export function main(foo: (\"foo\" | \"bar\")[]) { Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_extract_ident() -> anyhow::Result<()> { let code = " @@ -324,6 +330,7 @@ fn test_parse_extract_ident() -> anyhow::Result<()> { Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_imports() -> anyhow::Result<()> { let code = " @@ -347,6 +354,7 @@ fn test_parse_imports() -> anyhow::Result<()> { Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_imports_dts() -> anyhow::Result<()> { let code = " @@ -359,6 +367,7 @@ export type foo = number Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_powershell_sig() -> anyhow::Result<()> { let code = " diff --git a/backend/src/main.rs b/backend/src/main.rs index b7e11b0bf7..3795a157ec 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -44,7 +44,7 @@ use windmill_common::{ }, scripts::ScriptLang, stats_ee::schedule_stats, - utils::{hostname, rd_string, Mode, GIT_VERSION}, + utils::{hostname, rd_string, Mode, GIT_VERSION, MODE_AND_ADDONS}, worker::{reload_custom_tags_setting, HUB_CACHE_DIR, TMP_DIR, TMP_LOGS_DIR, WORKER_GROUP}, DB, METRICS_ENABLED, }; @@ -115,6 +115,25 @@ where } pub fn main() -> anyhow::Result<()> { + // https://github.com/denoland/deno/blob/main/cli/main.rs#L477 + #[cfg(feature = "deno_core")] + let unrecognized_v8_flags = deno_core::v8_set_flags(vec![ + "--stack-size=1024".to_string(), + // TODO(bartlomieju): I think this can be removed as it's handled by `deno_core` + // and its settings. + // deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript + // TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast + "--no-harmony-import-assertions".to_string(), + ]) + .into_iter() + .skip(1) + .collect::>(); + + #[cfg(feature = "deno_core")] + if !unrecognized_v8_flags.is_empty() { + println!("Unrecognized V8 flags: {:?}", unrecognized_v8_flags); + } + #[cfg(feature = "deno_core")] deno_core::JsRuntime::init_platform(None, false); create_and_run_current_thread_inner(windmill_main()) @@ -219,63 +238,12 @@ async fn windmill_main() -> anyhow::Result<()> { let hostname = hostname(); - let mut enable_standalone_indexer: bool = false; + let mode_and_addons = MODE_AND_ADDONS.clone(); + let mode = mode_and_addons.mode; - let mode = std::env::var("MODE") - .map(|x| x.to_lowercase()) - .map(|x| { - if &x == "server" { - println!("server mode"); - Mode::Server - } else if &x == "worker" { - tracing::info!("worker mode"); - #[cfg(windows)] - { - println!("It is highly recommended to use the agent mode instead on windows (MODE=agent) and to pass a BASE_INTERNAL_URL"); - } - Mode::Worker - } else if &x == "agent" { - println!("worker agent mode"); - if std::env::var("BASE_INTERNAL_URL").is_err() { - panic!("BASE_INTERNAL_URL is required in agent mode") - } - if std::env::var("JOB_TOKEN").is_err() { - println!("JOB_TOKEN is not passed, hence workers will still need to create permissions for each job and the DATABASE_URL needs to be of a role that can INSERT into the job_perms table") - } - - #[cfg(not(feature = "enterprise"))] - { - panic!("Agent mode is only available in the EE, ignoring..."); - } - #[cfg(feature = "enterprise")] - Mode::Agent - } else if &x == "indexer" { - tracing::info!("indexer mode"); - #[cfg(not(feature = "tantivy"))] - { - eprintln!("Cannot start the indexer because tantivy is not included in this binary/image. Make sure you are using the EE image if you want to access the full text search features."); - panic!("Indexer mode requires compiling with the tantivy feature flag."); - } - #[cfg(feature = "tantivy")] - Mode::Indexer - } else if &x == "standalone+search"{ - enable_standalone_indexer = true; - println!("standalone mode with search enabled"); - Mode::Standalone - } - else { - if &x != "standalone" { - eprintln!("mode not recognized, defaulting to standalone: {x}"); - } else { - println!("standalone mode"); - } - Mode::Standalone - } - }) - .unwrap_or_else(|_| { - tracing::info!("Mode not specified, defaulting to standalone"); - Mode::Standalone - }); + if mode == Mode::Standalone { + println!("Running in standalone mode"); + } #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] println!("jemalloc enabled"); @@ -518,8 +486,7 @@ Windmill Community Edition {GIT_VERSION} .expect("could not create initial server dir"); #[cfg(feature = "tantivy")] - let should_index_jobs = - mode == Mode::Indexer || (enable_standalone_indexer && mode == Mode::Standalone); + let should_index_jobs = mode == Mode::Indexer || mode_and_addons.indexer; reload_indexer_config(&db).await; @@ -722,6 +689,11 @@ Windmill Community Edition {GIT_VERSION} } } }, + "notify_webhook_change" => { + let workspace_id = n.payload(); + tracing::info!("Webhook change detected, invalidating webhook cache: {}", workspace_id); + windmill_api::webhook_util::WEBHOOK_CACHE.remove(workspace_id); + }, "notify_global_setting_change" => { tracing::info!("Global setting change detected: {}", n.payload()); match n.payload() { @@ -960,7 +932,11 @@ async fn listen_pg(db: &DB) -> Option { }; if let Err(e) = listener - .listen_all(vec!["notify_config_change", "notify_global_setting_change"]) + .listen_all(vec![ + "notify_config_change", + "notify_global_setting_change", + "notify_webhook_change", + ]) .await { tracing::error!(error = %e, "Could not listen to database"); @@ -1119,9 +1095,12 @@ pub async fn run_workers( async fn send_delayed_killpill( tx: &tokio::sync::broadcast::Sender<()>, - max_delay_secs: u64, + mut max_delay_secs: u64, context: &str, ) { + if max_delay_secs == 0 { + max_delay_secs = 1; + } // Random delay to avoid all servers/workers shutting down simultaneously let rd_delay = rand::rng().random_range(0..max_delay_secs); tracing::info!("Scheduling {context} shutdown in {rd_delay}s"); diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index a3f117721f..6fe8070014 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -34,7 +34,6 @@ use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts}; #[cfg(feature = "oauth2")] use windmill_common::global_settings::OAUTH_SETTING; use windmill_common::{ - auth::JWT_SECRET, ee::CriticalErrorChannel, error, flow_status::{FlowStatus, FlowStatusModule}, @@ -51,6 +50,7 @@ use windmill_common::{ }, indexer::load_indexer_config, jobs::QueuedJob, + jwt::JWT_SECRET, oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH, server::load_smtp_config, tracing_init::JSON_FMT, @@ -242,19 +242,40 @@ pub async fn load_otel(db: &DB) { if let Some(v) = v { let deser = serde_json::from_value::(v); if let Ok(o) = deser { - let metrics_enabled = o.metrics_enabled.unwrap_or(false); - let logs_enabled = o.logs_enabled.unwrap_or(false); - let tracing_enabled = o.tracing_enabled.unwrap_or(false); + let metrics_enabled = o.metrics_enabled.unwrap_or_else(|| { + std::env::var("OTEL_METRICS_ENABLED") + .map(|x| x.parse::().unwrap_or(false)) + .unwrap_or(false) + }); + let logs_enabled = o.logs_enabled.unwrap_or_else(|| { + std::env::var("OTEL_LOGS_ENABLED") + .map(|x| x.parse::().unwrap_or(false)) + .unwrap_or(false) + }); + let tracing_enabled = o.tracing_enabled.unwrap_or_else(|| { + std::env::var("OTEL_TRACING_ENABLED") + .map(|x| x.parse::().unwrap_or(false)) + .unwrap_or(false) + }); OTEL_METRICS_ENABLED.store(metrics_enabled, Ordering::Relaxed); OTEL_LOGS_ENABLED.store(logs_enabled, Ordering::Relaxed); OTEL_TRACING_ENABLED.store(tracing_enabled, Ordering::Relaxed); - if let Some(endpoint) = o.otel_exporter_otlp_endpoint.as_ref() { - std::env::set_var("OTEL_EXPORTER_OTLP_ENDPOINT", endpoint); - } - if let Some(headers) = o.otel_exporter_otlp_headers.as_ref() { - std::env::set_var("OTEL_EXPORTER_OTLP_HEADERS", headers); - } + + let endpoint = if let Some(endpoint) = o.otel_exporter_otlp_endpoint { + std::env::set_var("OTEL_EXPORTER_OTLP_ENDPOINT", endpoint.clone()); + Some(endpoint.clone()) + } else { + std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok() + }; + + let headers = if let Some(headers) = o.otel_exporter_otlp_headers { + std::env::set_var("OTEL_EXPORTER_OTLP_HEADERS", headers.clone()); + Some(headers.clone()) + } else { + std::env::var("OTEL_EXPORTER_OTLP_HEADERS").ok() + }; + if let Some(protocol) = o.otel_exporter_otlp_protocol { std::env::set_var("OTEL_EXPORTER_OTLP_PROTOCOL", protocol); } @@ -262,7 +283,7 @@ pub async fn load_otel(db: &DB) { std::env::set_var("OTEL_EXPORTER_OTLP_COMPRESSION", compression); } println!("OTEL settings loaded: tracing ({tracing_enabled}), logs ({logs_enabled}), metrics ({metrics_enabled}), endpoint ({:?}), headers defined: ({})", - o.otel_exporter_otlp_endpoint, o.otel_exporter_otlp_headers.is_some()); + endpoint, headers.is_some()); } else { tracing::error!("Error deserializing otel settings"); } @@ -731,6 +752,22 @@ pub async fn delete_expired_items(db: &DB) -> () { Err(e) => tracing::error!("Error deleting log file: {:?}", e), } + #[cfg(not(feature = "enterprise"))] + let audit_retention_secs = 1 * 60 * 60 * 24 * 14; + + #[cfg(feature = "enterprise")] + let audit_retention_secs = 1 * 60 * 60 * 24 * 365; + + if let Err(e) = sqlx::query_scalar!( + "DELETE FROM audit WHERE timestamp <= now() - ($1::bigint::text || ' s')::interval", + audit_retention_secs, + ) + .fetch_all(db) + .await + { + tracing::error!("Error deleting audit log on CE: {:?}", e); + } + let job_retention_secs = *JOB_RETENTION_SECS.read().await; if job_retention_secs > 0 { match db.begin().await { @@ -1517,18 +1554,44 @@ pub async fn reload_base_url_setting(db: &DB) -> error::Result<()> { Ok(()) } +const RESTART_LIMIT: i32 = 3; + async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker_name: &str) { + let mut zombie_jobs_uuid_restart_limit_reached = vec![]; + if *RESTART_ZOMBIE_JOBS { let restarted = sqlx::query!( - "WITH zombie_jobs AS ( - UPDATE v2_job_queue q SET running = false, started_at = null - FROM v2_job j, v2_job_runtime r - WHERE j.id = q.id AND j.id = r.id - AND ping < now() - ($1 || ' seconds')::interval + "WITH to_update AS ( + SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter + FROM v2_job_queue q + JOIN v2_job j ON j.id = q.id + JOIN v2_job_runtime r ON r.id = j.id + LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id + WHERE ping < now() - ($1 || ' seconds')::interval AND running = true AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow') AND same_worker = false - RETURNING q.id, q.workspace_id, ping + AND (zjc.counter IS NULL OR zjc.counter <= $2) + FOR UPDATE of q SKIP LOCKED + ), + zombie_jobs AS ( + UPDATE v2_job_queue q + SET running = false, started_at = null + FROM to_update tu + WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2) + RETURNING q.id, q.workspace_id, ping, tu.counter + ), + update_ping AS ( + UPDATE v2_job_runtime r + SET ping = null + FROM zombie_jobs zj + WHERE r.id = zj.id + ), + increment_counter AS ( + INSERT INTO zombie_job_counter (job_id, counter) + SELECT id, 1 FROM to_update WHERE counter < $2 + ON CONFLICT (job_id) DO UPDATE + SET counter = zombie_job_counter.counter + 1 ), update_concurrency AS ( UPDATE concurrency_counter cc @@ -1537,8 +1600,9 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker INNER JOIN concurrency_key ck ON ck.job_id = zj.id WHERE cc.concurrency_id = ck.key ) - SELECT id, workspace_id, ping FROM zombie_jobs", + SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update", *ZOMBIE_JOB_TIMEOUT, + RESTART_LIMIT ) .fetch_all(db) .await @@ -1558,22 +1622,61 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker "no last ping".to_string() }; let url = format!("{}/run/{}?workspace={}", base_url, r.id, r.workspace_id,); - let error_message = format!( - "Zombie job {} on {} ({}) detected, restarting it, {}", - r.id, r.workspace_id, url, last_ping - ); + let restart = r.counter.is_none_or(|x| x < RESTART_LIMIT); + let (critical_error_message, restart_message) = if restart { + ( + format!( + "Zombie job {} on {} ({}) detected, restarting it ({}/{} attempts), last ping: {}", + r.id, + r.workspace_id, + url, + r.counter.unwrap_or(0) + 1, + RESTART_LIMIT, + last_ping + ), + format!( + "Restarted job after not receiving job's ping for too long the {} ({}/{} attempts)\n\n", + last_ping, + r.counter.unwrap_or(0) + 1, + RESTART_LIMIT + ) + ) + } else { + ( + format!( + "Zombie job {} on {} ({}) detected, but restart limit ({}) reached, job will be processed as an error, last ping: {}", + r.id, r.workspace_id, url, RESTART_LIMIT, last_ping + ), + format!( + "job's ping was received last at {}, job will be processed as an error since all {} restart attempts failed", + last_ping, RESTART_LIMIT + ) + ) + }; - let _ = sqlx::query!(" + let _ = sqlx::query!( + " INSERT INTO job_logs (job_id, logs) - VALUES ($1, 'Restarted job after not receiving job''s ping for too long the ' || now() || '\n\n') + VALUES ($1, $2) ON CONFLICT (job_id) DO UPDATE SET logs = job_logs.logs || '\n' || EXCLUDED.logs WHERE job_logs.job_id = $1", - r.id + r.id, + restart_message ) .execute(db) .await; - tracing::error!(error_message); - report_critical_error(error_message, db.clone(), Some(&r.workspace_id), None).await; + tracing::error!(critical_error_message); + report_critical_error( + critical_error_message, + db.clone(), + Some(&r.workspace_id), + None, + ) + .await; + + if !restart { + zombie_jobs_uuid_restart_limit_reached.push(r.id); + } } } @@ -1648,9 +1751,43 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker .unwrap_or_else(|| vec![]) }; + enum ErrorMessage { + RestartLimit, + SameWorker, + RestartDisabled, + } + + impl ErrorMessage { + fn to_string(&self) -> String { + match self { + ErrorMessage::RestartLimit => format!("RestartLimit ({})", RESTART_LIMIT), + ErrorMessage::SameWorker => "SameWorker".to_string(), + ErrorMessage::RestartDisabled => "RestartDisabled".to_string(), + } + } + } + + let zombie_jobs_restart_limit_reached = + sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE id = ANY($1)") + .bind(&zombie_jobs_uuid_restart_limit_reached[..]) + .fetch_all(db) + .await + .ok() + .unwrap_or_else(|| vec![]); + let timeouts = non_restartable_jobs .into_iter() - .chain(same_worker_timeout_jobs) + .map(|x| (x, ErrorMessage::RestartDisabled)) + .chain( + same_worker_timeout_jobs + .into_iter() + .map(|x| (x, ErrorMessage::SameWorker)), + ) + .chain( + zombie_jobs_restart_limit_reached + .into_iter() + .map(|x| (x, ErrorMessage::RestartLimit)), + ) .collect::>(); #[cfg(feature = "prometheus")] @@ -1658,7 +1795,7 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker QUEUE_ZOMBIE_DELETE_COUNT.inc_by(timeouts.len() as _); } - for job in timeouts { + for (job, error_kind) in timeouts { // since the job is unrecoverable, the same worker queue should never be sent anything let (same_worker_tx_never_used, _same_worker_rx_never_used) = mpsc::channel::(1); @@ -1693,20 +1830,21 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker }; let last_ping = job.last_ping.clone(); + let error_message = format!( + "Job timed out after no ping from job since {} (ZOMBIE_JOB_TIMEOUT: {}, reason: {:?})", + last_ping + .map(|x| x.to_string()) + .unwrap_or_else(|| "no ping".to_string()), + *ZOMBIE_JOB_TIMEOUT, + error_kind.to_string() + ); let _ = handle_job_error( db, &client, &job, 0, None, - error::Error::ExecutionErr(format!( - "Job timed out after no ping from job since {} (ZOMBIE_JOB_TIMEOUT: {}, same_worker: {})", - last_ping - .map(|x| x.to_string()) - .unwrap_or_else(|| "no ping".to_string()), - *ZOMBIE_JOB_TIMEOUT, - job.same_worker - )), + error::Error::ExecutionErr(error_message), true, same_worker_tx_never_used, "", @@ -1785,16 +1923,27 @@ async fn handle_zombie_flows(db: &DB) -> error::Result<()> { let id = flow.id.clone(); let last_ping = flow.last_ping.clone(); let now = now_from_db(db).await?; + let base_url = BASE_URL.read().await; + let workspace_id = flow.workspace_id.clone(); let reason = format!( "{} was hanging in between 2 steps. Last ping: {last_ping:?} (now: {now})", if flow.is_flow_step.unwrap_or(false) && flow.parent_job.is_some() { - format!("Flow was cancelled because subflow {id}") + format!("Flow was cancelled because subflow {id} ({base_url}/run/{id}?workspace={workspace_id})") } else { - format!("Flow {id} was cancelled because it") + format!("Flow {id} ({base_url}/run/{id}?workspace={workspace_id}) was cancelled because it") } ); report_critical_error(reason.clone(), db.clone(), Some(&flow.workspace_id), None).await; - cancel_zombie_flow_job(db, flow.id, &flow.workspace_id, reason).await?; + cancel_zombie_flow_job(db, flow.id, &flow.workspace_id, + format!(r#"{reason} +This would happen if a worker was interrupted, killed or crashed while doing a state transition at the end of a job which is always an unexpected behavior that should never happen. +Please check your worker logs for more details and feel free to report it to the Windmill team on our Discord or support@windmill.dev (response for non EE customers will be best effort) with as much context as possible, ideally: +- Windmill version +- Worker logs right after the job referenced has finished running +- Is the error consistent when running the same flow +- A minimal flow and its flow.yaml that reproduces the error and that is importable in a fresh workspace +- Your infra setup (helm, docker-compose, configuration of the workers and their number, memory of the database, etc.) +"#)).await?; } } diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index ea7dea3bd2..2f7abde33d 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -22,12 +22,12 @@ use windmill_api_client::types::{CreateFlowBody, RawScript}; use windmill_api_client::types::{EditSchedule, NewSchedule, ScriptArgs}; use serde::Serialize; -use windmill_common::auth::JWT_SECRET; use windmill_common::worker::WORKER_CONFIG; use windmill_common::{ flow_status::{FlowStatus, FlowStatusModule, RestartedFrom}, flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform}, jobs::{JobKind, JobPayload, RawCode}, + jwt::JWT_SECRET, scripts::{ScriptHash, ScriptLang}, worker::{ MIN_VERSION_IS_AT_LEAST_1_427, MIN_VERSION_IS_AT_LEAST_1_432, MIN_VERSION_IS_AT_LEAST_1_440, diff --git a/backend/update_sqlx.sh b/backend/update_sqlx.sh index 9ee8ab350d..11b5196ad8 100755 --- a/backend/update_sqlx.sh +++ b/backend/update_sqlx.sh @@ -1,3 +1,22 @@ ./substitute_ee_code.sh --dir ../windmill-ee-private + +# Check if running on macOS +if [[ "$(uname)" == "Darwin" ]]; then + echo "Running on macOS - substituting samael..." + # Comment out the version-based samael dependency + sed -i '' 's/^samael = { version="0.0.14", features = \["xmlsec"\] }/#samael = { version="0.0.14", features = ["xmlsec"] }/' Cargo.toml + # Uncomment the git-based samael dependency + sed -i '' 's/^# \(samael = { git="https:\/\/github.com\/njaremko\/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = \["xmlsec"\] }\)/\1/' Cargo.toml +fi + cargo sqlx prepare --workspace -- --all-targets --all-features ./substitute_ee_code.sh -r --dir ../windmill-ee-private + +# Undo the samael changes on macOS +if [[ "$(uname)" == "Darwin" ]]; then + echo "Reverting samael changes..." + # Uncomment the version-based samael dependency + sed -i '' 's/^#samael = { version="0.0.14", features = \["xmlsec"\] }/samael = { version="0.0.14", features = ["xmlsec"] }/' Cargo.toml + # Comment out the git-based samael dependency + sed -i '' 's/^\(samael = { git="https:\/\/github.com\/njaremko\/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = \["xmlsec"\] }\)/# \1/' Cargo.toml +fi diff --git a/backend/windmill-api/Cargo.toml b/backend/windmill-api/Cargo.toml index d5f8f6f420..253a125df7 100644 --- a/backend/windmill-api/Cargo.toml +++ b/backend/windmill-api/Cargo.toml @@ -29,6 +29,7 @@ oauth2 = ["dep:async-oauth2"] http_trigger = ["dep:matchit"] static_frontend = ["dep:rust-embed"] postgres_trigger = ["dep:rust-postgres", "dep:pg_escape", "dep:byteorder", "dep:thiserror", "dep:rust_decimal", "dep:rust-postgres-native-tls"] +mqtt_trigger = ["dep:thiserror", "dep:rumqttc"] sqs_trigger = ["dep:aws-sdk-sqs", "dep:thiserror", "dep:aws-config"] [dependencies] @@ -120,5 +121,6 @@ byteorder = { workspace = true, optional = true } thiserror = { workspace = true, optional = true } rust_decimal = { workspace = true, optional = true } rust-postgres-native-tls = { workspace = true, optional = true} +rumqttc = { workspace = true, optional = true } aws-sdk-sqs = { workspace = true, optional = true } -aws-config = { workspace = true, optional = true} \ No newline at end of file +aws-config = { workspace = true, optional = true} diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index c6252a36d9..ad59bda610 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.467.1 + version: 1.475.0 title: Windmill API contact: @@ -126,6 +126,11 @@ paths: type: string - $ref: "#/components/parameters/ResourceName" - $ref: "#/components/parameters/ActionKind" + - name: all_workspaces + in: query + description: get audit logs for all workspaces + schema: + type: boolean responses: "200": @@ -2611,6 +2616,8 @@ paths: type: boolean postgres_used: type: boolean + mqtt_used: + type: boolean sqs_used: type: boolean required: @@ -2619,6 +2626,7 @@ paths: - kafka_used - nats_used - postgres_used + - mqtt_used - sqs_used /w/{workspace}/users/list: get: @@ -5287,6 +5295,26 @@ paths: schema: $ref: "#/components/schemas/FlowVersion" + /w/{workspace}/flows/list_paths_from_workspace_runnable/{runnable_kind}/{path}: + get: + summary: list flow paths from workspace runnable + operationId: listFlowPathsFromWorkspaceRunnable + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/RunnableKind" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: list of flow paths + content: + application/json: + schema: + type: array + items: + type: string + /w/{workspace}/flows/get/v/{version}/p/{path}: get: summary: get flow version @@ -6207,6 +6235,92 @@ paths: schema: type: string + /w/{workspace}/apps_u/upload_s3_file/{path}: + post: + summary: upload s3 file from app + operationId: uploadS3FileFromApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: file_key + in: query + required: false + schema: + type: string + - name: file_extension + in: query + required: false + schema: + type: string + - name: s3_resource_path + in: query + required: false + schema: + type: string + - name: resource_type + in: query + required: false + schema: + type: string + - name: storage + 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 + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + "200": + description: file uploaded + content: + application/json: + schema: + type: object + properties: + file_key: + type: string + delete_token: + type: string + required: + - file_key + - delete_token + + /w/{workspace}/apps_u/delete_s3_file: + delete: + summary: delete s3 file from app + operationId: deleteS3FileFromApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: delete_token + in: query + required: true + schema: + type: string + responses: + "200": + description: file deleted + content: + text/plain: + schema: + type: string + /w/{workspace}/jobs/run/f/{path}: post: summary: run flow by path @@ -8777,6 +8891,195 @@ paths: schema: type: string + /w/{workspace}/mqtt_triggers/create: + post: + summary: create mqtt trigger + operationId: createMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new mqtt trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewMqttTrigger" + responses: + "201": + description: mqtt trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/update/{path}: + post: + summary: update mqtt trigger + operationId: updateMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditMqttTrigger" + responses: + "200": + description: mqtt trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/delete/{path}: + delete: + summary: delete mqtt trigger + operationId: deleteMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: mqtt trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/get/{path}: + get: + summary: get mqtt trigger + operationId: getMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: mqtt trigger deleted + content: + application/json: + schema: + $ref: "#/components/schemas/MqttTrigger" + + /w/{workspace}/mqtt_triggers/list: + get: + summary: list mqtt triggers + operationId: listMqttTriggers + tags: + - mqtt_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: mqtt trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/MqttTrigger" + + /w/{workspace}/mqtt_triggers/exists/{path}: + get: + summary: does mqtt trigger exists + operationId: existsMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: mqtt trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/mqtt_triggers/setenabled/{path}: + post: + summary: set enabled mqtt trigger + operationId: setMqttTriggerEnabled + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated mqtt trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + "200": + description: mqtt trigger enabled set + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/test: + post: + summary: test mqtt connection + operationId: testMqttConnection + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: test mqtt connection + required: true + content: + application/json: + schema: + type: object + properties: + connection: + type: object + required: + - connection + responses: + "200": + description: successfully connected to mqtt + content: + text/plain: + schema: + type: string + /w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}: get: @@ -9730,6 +10033,23 @@ paths: application/json: schema: $ref: "#/components/schemas/Folder" + + /w/{workspace}/folders/exists/{name}: + get: + summary: exists folder + operationId: existsFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: folder exists + content: + application/json: + schema: + type: boolean /w/{workspace}/folders/getusage/{name}: get: @@ -10065,6 +10385,7 @@ paths: kafka_trigger, nats_trigger, postgres_trigger, + mqtt_trigger, sqs_trigger ] responses: @@ -10107,6 +10428,7 @@ paths: kafka_trigger, nats_trigger, postgres_trigger, + mqtt_trigger, sqs_trigger ] requestBody: @@ -10160,6 +10482,7 @@ paths: kafka_trigger, nats_trigger, postgres_trigger, + mqtt_trigger, sqs_trigger ] requestBody: @@ -11092,7 +11415,7 @@ paths: /w/{workspace}/job_helpers/download_s3_file: get: - summary: Download file to S3 bucket + summary: Download file from S3 bucket operationId: fileDownload tags: - helpers @@ -13569,6 +13892,8 @@ components: type: number nats_count: type: number + mqtt_count: + type: number sqs_count: type: number @@ -13718,6 +14043,140 @@ components: - is_flow required: - runnable_result + + MqttQoS: + type: string + enum: ['qos0', 'qos1', 'qos2'] + + MqttV3Config: + type: object + properties: + clean_session: + type: boolean + + MqttV5Config: + type: object + properties: + clean_start: + type: boolean + topic_alias: + type: number + session_expiry_interval: + type: number + + MqttSubscribeTopic: + type: object + properties: + qos: + $ref: "#/components/schemas/MqttQoS" + topic: + type: string + required: + - qos + - topic + + MqttClientVersion: + type: string + enum: ["v3", "v5"] + + MqttTrigger: + allOf: + - $ref: "#/components/schemas/TriggerExtraProperty" + type: object + properties: + mqtt_resource_path: + type: string + subscribe_topics: + type: array + items: + $ref: "#/components/schemas/MqttSubscribeTopic" + v3_config: + $ref: "#/components/schemas/MqttV3Config" + v5_config: + $ref: "#/components/schemas/MqttV5Config" + client_id: + type: string + client_version: + $ref: "#/components/schemas/MqttClientVersion" + server_id: + type: string + last_server_ping: + type: string + format: date-time + error: + type: string + enabled: + type: boolean + + required: + - enabled + - subscribe_topics + - mqtt_resource_path + + NewMqttTrigger: + type: object + properties: + mqtt_resource_path: + type: string + subscribe_topics: + type: array + items: + $ref: "#/components/schemas/MqttSubscribeTopic" + client_id: + type: string + v3_config: + $ref: "#/components/schemas/MqttV3Config" + v5_config: + $ref: "#/components/schemas/MqttV5Config" + client_version: + $ref: "#/components/schemas/MqttClientVersion" + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + required: + - path + - script_path + - is_flow + - subscribe_topics + - mqtt_resource_path + + EditMqttTrigger: + type: object + properties: + mqtt_resource_path: + type: string + subscribe_topics: + type: array + items: + $ref: "#/components/schemas/MqttSubscribeTopic" + client_id: + type: string + v3_config: + $ref: "#/components/schemas/MqttV3Config" + v5_config: + $ref: "#/components/schemas/MqttV5Config" + client_version: + $ref: "#/components/schemas/MqttClientVersion" + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + required: + - path + - script_path + - is_flow + - enabled + - subscribe_topics + - mqtt_resource_path SqsTrigger: allOf: @@ -14477,6 +14936,15 @@ components: type: array items: type: object + allowed_s3_keys: + type: array + items: + type: object + properties: + s3_path: + type: string + resource: + type: string execution_mode: type: string enum: [viewer, publisher, anonymous] @@ -14824,6 +15292,7 @@ components: - resource - variable - secret + - trigger WorkspaceDefaultScripts: type: object @@ -15094,7 +15563,7 @@ components: CaptureTriggerKind: type: string - enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs] + enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt] Capture: type: object @@ -15213,4 +15682,4 @@ components: service_url: type: string description: The service URL for the channel - example: "https://smba.trafficmanager.net/amer/12345678-1234-1234-1234-123456789012/" + example: "https://smba.trafficmanager.net/amer/12345678-1234-1234-1234-123456789012/" \ No newline at end of file diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 952ff6d77e..0a8878fad6 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -21,7 +21,7 @@ use crate::{ job_helpers_ee::{ download_s3_file_internal, get_random_file_name, get_s3_resource, get_workspace_s3_resource, load_image_preview_internal, upload_file_from_req, - DownloadFileQuery, LoadImagePreviewQuery, UploadFileResponse, + DownloadFileQuery, LoadImagePreviewQuery, }, users::fetch_api_authed_from_permissioned_as, }; @@ -51,8 +51,6 @@ 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::variables::encrypt; use windmill_common::{ apps::{AppScriptId, ListAppQuery}, @@ -69,6 +67,8 @@ use windmill_common::{ worker::{to_raw_value, CLOUD_HOSTED}, HUB_BASE_URL, }; +#[cfg(feature = "parquet")] +use windmill_common::{jwt, s3_helpers::build_object_store_client}; use windmill_git_sync::{handle_deployment_metadata, DeployedObject}; use windmill_queue::{push, PushArgs, PushArgsOwned, PushIsolationLevel}; @@ -96,6 +96,7 @@ 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("/delete_s3_file", delete(delete_s3_file_from_app)) .route("/download_s3_file/*path", get(download_s3_file_from_app)) .route( "/load_image_preview/*path", @@ -224,6 +225,12 @@ pub struct S3Input { file_key_regex: String, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct S3Key { + s3_path: String, + resource: String, +} + #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct Policy { pub on_behalf_of: Option, @@ -238,6 +245,7 @@ pub struct Policy { pub triggerables_v2: Option>, pub execution_mode: ExecutionMode, pub s3_inputs: Option>, + pub allowed_s3_keys: Option>, } #[derive(Deserialize)] @@ -1497,6 +1505,13 @@ async fn upload_s3_file_from_app() -> Result<()> { )); } +#[cfg(not(feature = "parquet"))] +async fn delete_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 { @@ -1511,6 +1526,24 @@ struct UploadFileToS3Query { force_viewer_allowed_resources: Option, } +#[cfg(feature = "parquet")] +#[derive(Serialize, Deserialize)] +struct DeleteTokenClaims { + file_key: String, + on_behalf_of_email: String, + permissioned_as: String, + username: String, + s3_resource_path: Option, + pub exp: usize, +} + +#[cfg(feature = "parquet")] +#[derive(Serialize)] +struct AppUploadFileResponse { + file_key: String, + delete_token: String, +} + #[cfg(feature = "parquet")] async fn upload_s3_file_from_app( OptAuthed(opt_authed): OptAuthed, @@ -1518,7 +1551,7 @@ async fn upload_s3_file_from_app( Path((w_id, path)): Path<(String, StripPath)>, Query(query): Query, request: axum::extract::Request, -) -> JsonResult { +) -> JsonResult { let policy = if let Some(file_key_regex) = query.force_viewer_file_key_regex { Some(Policy { execution_mode: ExecutionMode::Viewer, @@ -1537,6 +1570,7 @@ async fn upload_s3_file_from_app( .map(|s| s.split(',').map(|s| s.to_string()).collect()) .unwrap_or_default(), }]), + allowed_s3_keys: None, }) } else { let policy_o = sqlx::query_scalar!( @@ -1554,7 +1588,10 @@ async fn upload_s3_file_from_app( 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 (s3_resource_opt, file_key, on_behalf_of_email, permissioned_as, username) = 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(); @@ -1562,11 +1599,11 @@ async fn upload_s3_file_from_app( 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, + permissioned_as.clone(), + email.clone(), &w_id, &db, - Some(username), + Some(username.clone()), ) .await?; @@ -1617,6 +1654,9 @@ async fn upload_s3_file_from_app( .await?, ), file_key, + email, + permissioned_as, + username, ) } else { return Err(Error::BadRequest( @@ -1640,13 +1680,16 @@ async fn upload_s3_file_from_app( .await?, ), file_key, + email, + permissioned_as, + username, ) } } else { let (_, s3_resource_opt) = get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None) .await?; - (s3_resource_opt, file_key) + (s3_resource_opt, file_key, email, permissioned_as, username) } } else { return Err(Error::BadRequest( @@ -1672,7 +1715,7 @@ async fn upload_s3_file_from_app( let (_, s3_resource_opt) = get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None).await?; - (s3_resource_opt, file_key) + (s3_resource_opt, file_key, email, permissioned_as, username) } } else { // backward compatibility (no policy) @@ -1682,6 +1725,12 @@ async fn upload_s3_file_from_app( .file_key .unwrap_or_else(|| get_random_file_name(query.file_extension)); + let (on_behalf_of_email, permissioned_as, username) = ( + authed.email.clone(), + username_to_permissioned_as(&authed.username), + authed.display_username().to_string(), + ); + if let Some(ref s3_resource_path) = query.s3_resource_path { ( Some( @@ -1698,12 +1747,21 @@ async fn upload_s3_file_from_app( .await?, ), file_key, + on_behalf_of_email, + permissioned_as, + username, ) } else { let (_, s3_resource) = get_workspace_s3_resource(&authed, &db, None, "", &w_id, None).await?; - (s3_resource, file_key) + ( + s3_resource, + file_key, + on_behalf_of_email, + permissioned_as, + username, + ) } } else { return Err(Error::BadRequest("Missing s3 policy".to_string())); @@ -1733,7 +1791,82 @@ async fn upload_s3_file_from_app( upload_file_from_req(s3_client, &file_key, request, options).await?; - return Ok(Json(UploadFileResponse { file_key })); + let delete_token = jwt::encode_with_internal_secret(DeleteTokenClaims { + file_key: file_key.clone(), + on_behalf_of_email, + permissioned_as, + username, + s3_resource_path: query.s3_resource_path, + exp: (chrono::Utc::now() + chrono::Duration::seconds(3600 * 24)).timestamp() as usize, + }) + .await?; + + return Ok(Json(AppUploadFileResponse { file_key, delete_token })); +} + +#[cfg(feature = "parquet")] +#[derive(Deserialize)] +struct DeleteS3FileQuery { + delete_token: String, +} + +#[cfg(feature = "parquet")] +async fn delete_s3_file_from_app( + Extension(db): Extension, + Extension(user_db): Extension, + Path(w_id): Path, + Query(query): Query, +) -> Result<()> { + let DeleteTokenClaims { + file_key, + on_behalf_of_email, + permissioned_as, + username, + s3_resource_path, + .. + } = jwt::decode_with_internal_secret::(&query.delete_token).await?; + + let on_behalf_authed = fetch_api_authed_from_permissioned_as( + permissioned_as, + on_behalf_of_email, + &w_id, + &db, + Some(username), + ) + .await?; + + let s3_resource = if let Some(s3_resource_path) = s3_resource_path { + get_s3_resource( + &on_behalf_authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path.as_str(), + None, + None, + ) + .await? + } else { + let (_, s3_resource) = + get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None).await?; + + s3_resource.ok_or(Error::internal_err( + "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::parse(file_key.as_str()) + .map_err(|e| Error::internal_err(format!("Error parsing file key: {}", e)))?; + + s3_client.delete(&path).await.map_err(|err| { + tracing::error!("Error deleting file: {:?}", err); + Error::internal_err(format!("Error deleting file: {}", err.to_string())) + })?; + + Ok(()) } #[cfg(not(feature = "parquet"))] @@ -1749,26 +1882,41 @@ async fn get_on_behalf_authed_from_app( path: &str, w_id: &str, opt_authed: &Option, -) -> Result { - let policy_o = sqlx::query_scalar!( - "SELECT policy from app WHERE path = $1 AND workspace_id = $2", - path, - w_id - ) - .fetch_optional(db) - .await?; - - let policy = policy_o - .map(|p| serde_json::from_value::(p).map_err(to_anyhow)) - .transpose()? - .unwrap_or_else(|| Policy { + force_allowed_s3_keys: Option>, +) -> Result<(ApiAuthed, Policy)> { + let policy = if let Some(force_allowed_s3_keys) = force_allowed_s3_keys { + Policy { execution_mode: ExecutionMode::Viewer, triggerables: None, triggerables_v2: None, on_behalf_of: None, on_behalf_of_email: None, s3_inputs: None, - }); + allowed_s3_keys: Some(force_allowed_s3_keys), + } + } else { + // TODO: improve db query to not return uneeded fields + let policy_o = sqlx::query_scalar!( + "SELECT policy from app WHERE path = $1 AND workspace_id = $2", + path, + w_id + ) + .fetch_optional(db) + .await?; + + policy_o + .map(|p| serde_json::from_value::(p).map_err(to_anyhow)) + .transpose()? + .unwrap_or_else(|| Policy { + execution_mode: ExecutionMode::Viewer, + triggerables: None, + triggerables_v2: None, + on_behalf_of: None, + on_behalf_of_email: None, + s3_inputs: None, + allowed_s3_keys: None, + }) + }; let (username, permissioned_as, email) = get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?; @@ -1777,7 +1925,7 @@ async fn get_on_behalf_authed_from_app( fetch_api_authed_from_permissioned_as(permissioned_as, email, &w_id, &db, Some(username)) .await?; - Ok(on_behalf_authed) + Ok((on_behalf_authed, policy)) } #[cfg(feature = "parquet")] @@ -1787,6 +1935,7 @@ async fn check_if_allowed_to_access_s3_file_from_app( file_key: &str, w_id: &str, path: &str, + policy: &Policy, ) -> Result<()> { // if anonymous, check that the file was the result of an app script ran by an anonymous user in the last 3 hours // otherwise, if logged in, allow any file (TODO: change that when we implement better s3 policy) @@ -1808,7 +1957,10 @@ async fn check_if_allowed_to_access_s3_file_from_app( ) .fetch_one(db) .await? - .unwrap_or(false); + .unwrap_or(false) + + // check if the file is allowed by the allowed_s3_keys policy + || policy.allowed_s3_keys.as_ref().unwrap().iter().any(|key| key.s3_path == file_key); if !allowed { Err(Error::BadRequest("File restricted".to_string())) @@ -1817,21 +1969,46 @@ async fn check_if_allowed_to_access_s3_file_from_app( } } +#[cfg(feature = "parquet")] +#[derive(Deserialize)] +pub struct DownloadFileQueryWithForceViewerAllowedS3Keys { + #[serde(flatten)] + pub file_query: DownloadFileQuery, + pub force_viewer_allowed_s3_keys: Option, +} + #[cfg(feature = "parquet")] async fn download_s3_file_from_app( OptAuthed(opt_authed): OptAuthed, Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, - Query(query): Query, + Query(query): Query, ) -> Result { let path = path.to_path(); - let on_behalf_authed = get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed).await?; + let force_viewer_allowed_s3_keys = if let Some(force_viewer_allowed_s3_keys) = + query.force_viewer_allowed_s3_keys.clone() + { + Some(serde_json::from_str::>(&force_viewer_allowed_s3_keys).unwrap_or_default()) + } else { + None + }; - check_if_allowed_to_access_s3_file_from_app(&db, &opt_authed, &query.file_key, &w_id, &path) - .await?; + let (on_behalf_authed, policy) = + get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed, force_viewer_allowed_s3_keys) + .await?; - download_s3_file_internal(on_behalf_authed, &db, None, "", &w_id, query).await + check_if_allowed_to_access_s3_file_from_app( + &db, + &opt_authed, + &query.file_query.file_key, + &w_id, + &path, + &policy, + ) + .await?; + + download_s3_file_internal(on_behalf_authed, &db, None, "", &w_id, query.file_query).await } #[cfg(not(feature = "parquet"))] @@ -1850,10 +2027,18 @@ async fn load_s3_file_image_preview_from_app( ) -> Result { let path = path.to_path(); - let on_behalf_authed = get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed).await?; + let (on_behalf_authed, policy) = + get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed, None).await?; - check_if_allowed_to_access_s3_file_from_app(&db, &opt_authed, &query.file_key, &w_id, &path) - .await?; + check_if_allowed_to_access_s3_file_from_app( + &db, + &opt_authed, + &query.file_key, + &w_id, + &path, + &policy, + ) + .await?; load_image_preview_internal(on_behalf_authed, &db, "", &w_id, query).await } diff --git a/backend/windmill-api/src/auth.rs b/backend/windmill-api/src/auth.rs index 71c6270477..b0a18112d4 100644 --- a/backend/windmill-api/src/auth.rs +++ b/backend/windmill-api/src/auth.rs @@ -21,7 +21,8 @@ use std::sync::{ use tokio::sync::RwLock; use windmill_common::{ - auth::{get_folders_for_user, get_groups_for_user, JWTAuthClaims, JWT_SECRET}, + auth::{get_folders_for_user, get_groups_for_user, JWTAuthClaims}, + jwt, users::{COOKIE_NAME, SUPERADMIN_SECRET_EMAIL}, }; @@ -99,55 +100,44 @@ impl AuthCache { } } _ if token.starts_with("jwt_") => { - let jwt_secret = JWT_SECRET.read().await; - if !jwt_secret.is_empty() { - let jwt_token = token.trim_start_matches("jwt_"); + let jwt_token = token.trim_start_matches("jwt_"); - let jwt_result = jsonwebtoken::decode::( - jwt_token, - &jsonwebtoken::DecodingKey::from_secret(jwt_secret.as_bytes()), - &jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256), - ); + let jwt_result = jwt::decode_with_internal_secret::(jwt_token).await; - match jwt_result { - Ok(payload) => { - if w_id.is_some_and(|w_id| w_id != payload.claims.workspace_id) { - tracing::error!("JWT auth error: workspace_id mismatch"); - return None; - } - - let username_override = - username_override_from_label(payload.claims.label); - let authed = crate::db::ApiAuthed { - email: payload.claims.email, - username: payload.claims.username, - is_admin: payload.claims.is_admin, - is_operator: payload.claims.is_operator, - groups: payload.claims.groups, - folders: payload.claims.folders, - scopes: None, - username_override, - }; - - self.cache.insert( - key, - ExpiringAuthCache { - authed: authed.clone(), - expiry: chrono::Utc - .timestamp_nanos(payload.claims.exp as i64 * 1_000_000_000), - }, - ); - - Some(authed) - } - Err(err) => { - tracing::error!("JWT auth error: {:?}", err); - None + match jwt_result { + Ok(claims) => { + if w_id.is_some_and(|w_id| w_id != claims.workspace_id) { + tracing::error!("JWT auth error: workspace_id mismatch"); + return None; } + + let username_override = username_override_from_label(claims.label); + let authed = crate::db::ApiAuthed { + email: claims.email, + username: claims.username, + is_admin: claims.is_admin, + is_operator: claims.is_operator, + groups: claims.groups, + folders: claims.folders, + scopes: None, + username_override, + }; + + self.cache.insert( + key, + ExpiringAuthCache { + authed: authed.clone(), + expiry: chrono::Utc + .timestamp_nanos(claims.exp as i64 * 1_000_000_000), + }, + ); + + Some(authed) + } + Err(err) => { + tracing::error!("JWT auth error: {:?}", err); + None } - } else { - tracing::error!("JWT auth error: no jwt secret set"); - None } } _ => { diff --git a/backend/windmill-api/src/capture.rs b/backend/windmill-api/src/capture.rs index 7606822d65..f52cee3fab 100644 --- a/backend/windmill-api/src/capture.rs +++ b/backend/windmill-api/src/capture.rs @@ -6,36 +6,12 @@ * LICENSE-AGPL for a copy of the license. */ -use axum::{ - extract::{Extension, Path, Query}, - routing::{delete, get, head, post}, - Json, Router, -}; -#[cfg(feature = "http_trigger")] -use http::HeaderMap; -use hyper::StatusCode; -#[cfg(feature = "http_trigger")] -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use serde_json::value::RawValue; -use sqlx::types::Json as SqlxJson; -#[cfg(feature = "http_trigger")] -use std::collections::HashMap; -use std::fmt; -#[cfg(feature = "http_trigger")] -use windmill_common::error::Error; -use windmill_common::{ - db::UserDB, - error::{JsonResult, Result}, - utils::{not_found_if_none, paginate, Pagination, StripPath}, - worker::{to_raw_value, CLOUD_HOSTED}, -}; -use windmill_queue::{PushArgs, PushArgsOwned}; - #[cfg(feature = "http_trigger")] use crate::http_triggers::{build_http_trigger_extra, HttpMethod}; #[cfg(all(feature = "enterprise", feature = "kafka"))] use crate::kafka_triggers_ee::KafkaTriggerConfigConnection; +#[cfg(feature = "mqtt_trigger")] +use crate::mqtt_triggers::{MqttClientVersion, MqttV3Config, MqttV5Config, SubscribeTopic}; #[cfg(all(feature = "enterprise", feature = "nats"))] use crate::nats_triggers_ee::NatsTriggerConfigConnection; #[cfg(feature = "postgres_trigger")] @@ -43,16 +19,42 @@ use crate::postgres_triggers::{ create_logical_replication_slot_query, create_publication_query, drop_publication_query, generate_random_string, get_database_connection, PublicationData, }; +#[cfg(feature = "http_trigger")] +use http::HeaderMap; #[cfg(feature = "postgres_trigger")] use itertools::Itertools; #[cfg(feature = "postgres_trigger")] use pg_escape::quote_literal; +#[cfg(feature = "http_trigger")] +use serde::de::DeserializeOwned; +#[cfg(feature = "http_trigger")] +use std::collections::HashMap; +#[cfg(feature = "http_trigger")] +use windmill_common::error::Error; use crate::{ args::WebhookArgs, db::{ApiAuthed, DB}, users::fetch_api_authed, + utils::RunnableKind, }; +use axum::{ + extract::{Extension, Path, Query}, + routing::{delete, get, head, post}, + Json, Router, +}; +use hyper::StatusCode; +use serde::{Deserialize, Serialize}; +use serde_json::value::RawValue; +use sqlx::types::Json as SqlxJson; +use std::fmt; +use windmill_common::{ + db::UserDB, + error::{JsonResult, Result}, + utils::{not_found_if_none, paginate, Pagination, StripPath}, + worker::{to_raw_value, CLOUD_HOSTED}, +}; +use windmill_queue::{PushArgs, PushArgsOwned}; const KEEP_LAST: i64 = 20; @@ -98,6 +100,7 @@ pub enum TriggerKind { Kafka, Email, Nats, + Mqtt, Sqs, Postgres, } @@ -111,6 +114,7 @@ impl fmt::Display for TriggerKind { TriggerKind::Kafka => "kafka", TriggerKind::Email => "email", TriggerKind::Nats => "nats", + TriggerKind::Mqtt => "mqtt", TriggerKind::Sqs => "sqs", TriggerKind::Postgres => "postgres", }; @@ -139,7 +143,7 @@ pub struct KafkaTriggerConfig { pub struct SqsTriggerConfig { pub queue_url: String, pub aws_resource_path: String, - pub message_attributes: Option> + pub message_attributes: Option>, } #[cfg(all(feature = "enterprise", feature = "nats"))] @@ -155,6 +159,16 @@ pub struct NatsTriggerConfig { pub use_jetstream: bool, } +#[cfg(feature = "mqtt_trigger")] +#[derive(Debug, Serialize, Deserialize)] +pub struct MqttTriggerConfig { + pub mqtt_resource_path: String, + pub subscribe_topics: Vec, + pub v3_config: Option, + pub v5_config: Option, + pub client_version: Option, + pub client_id: Option, +} #[cfg(feature = "postgres_trigger")] #[derive(Serialize, Deserialize, Debug)] pub struct PostgresTriggerConfig { @@ -187,6 +201,8 @@ enum TriggerConfig { Kafka(KafkaTriggerConfig), #[cfg(all(feature = "enterprise", feature = "nats"))] Nats(NatsTriggerConfig), + #[cfg(feature = "mqtt_trigger")] + Mqtt(MqttTriggerConfig), } #[derive(Serialize, Deserialize)] @@ -300,8 +316,7 @@ async fn set_config( #[cfg(feature = "postgres_trigger")] let nc = if let TriggerKind::Postgres = nc.trigger_kind { set_postgres_trigger_config(&w_id, authed.clone(), &db, user_db.clone(), nc).await? - } - else { + } else { nc }; @@ -362,13 +377,6 @@ struct Capture { trigger_extra: Option>>, } -#[derive(Deserialize)] -#[serde(rename_all = "lowercase")] -enum RunnableKind { - Script, - Flow, -} - #[derive(Deserialize)] struct ListCapturesQuery { trigger_kind: Option, diff --git a/backend/windmill-api/src/db.rs b/backend/windmill-api/src/db.rs index 9b694ad3bf..1ecef03fc5 100644 --- a/backend/windmill-api/src/db.rs +++ b/backend/windmill-api/src/db.rs @@ -32,6 +32,30 @@ async fn current_database(conn: &mut PgConnection) -> Result = vec![(20221207103910, include_str!( + "../../custom_migrations/create_workspace_without_md5.sql" + ).to_string()), + (20240216100535, include_str!( + "../../migrations/20240216100535_improve_policies.up.sql" + ).replace("public.", "")), + (20240403083110, include_str!( + "../../migrations/20240403083110_remove_team_id_constraint.up.sql" + ).replace("public.", "")), + (20240613150524, include_str!( + "../../migrations/20240613150524_add_job_perms.up.sql" + ).replace("public.", "")), + (20250102145420, include_str!( + "../../migrations/20250102145420_more_captures.up.sql" + ).replace("public.", "")), + (20241006144414, include_str!( + "../../custom_migrations/grant_all_current_schema.sql" + ).to_string()), + (20221105003256, "DELETE FROM workspace_invite WHERE workspace_id = 'demo' AND email = 'ruben@windmill.dev';".to_string()), + (20221123151919, "".to_string()), + ].into_iter().collect(); +} + struct CustomMigrator { inner: PoolConnection, } @@ -132,12 +156,13 @@ impl Migrate for CustomMigrator { migration.version, migration.description ); - if migration.version == 20221207103910 { - tracing::info!("Skipping migration 20221207103910 to avoid using md5"); + + + if let Some(migration_sql) = OVERRIDDEN_MIGRATIONS.get(&migration.version) { + tracing::info!("Using custom migration for version {}", migration.version); + self.inner - .execute(include_str!( - "../../custom_migrations/create_workspace_without_md5.sql" - )) + .execute(&**migration_sql) .await?; let _ = sqlx::query( r#" @@ -528,6 +553,7 @@ async fn v2_finalize(db: &DB) -> Result<(), Error> { ) .await?; }); + Ok(()) } @@ -729,7 +755,7 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { }); run_windmill_migration!("v2_improve_v2_queued_jobs_indices", &db, |tx| { - sqlx::query!("CREATE INDEX CONCURRENTLY queue_sort_v2 ON v2_job_queue (priority DESC NULLS LAST, scheduled_for, tag) WHERE running = false") + sqlx::query!("CREATE INDEX CONCURRENTLY IF NOT EXISTS queue_sort_v2 ON v2_job_queue (priority DESC NULLS LAST, scheduled_for, tag) WHERE running = false") .execute(db) .await?; @@ -745,6 +771,14 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { .execute(db) .await?; }); + + run_windmill_migration!("audit_timestamps", db, |tx| { + sqlx::query!( + "CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_audit_timestamps ON audit (timestamp DESC)" + ) + .execute(db) + .await?; + }); Ok(()) } diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 57770ff674..8cd65dd55a 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -12,7 +12,7 @@ use crate::db::ApiAuthed; use crate::triggers::{ get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail, }; -use crate::utils::WithStarredInfoQuery; +use crate::utils::{RunnableKind, WithStarredInfoQuery}; use crate::{ db::DB, schedule::clear_schedule, @@ -65,6 +65,10 @@ pub fn workspaced_service() -> Router { .route("/list_paths", get(list_paths)) .route("/history/p/*path", get(get_flow_history)) .route("/get_latest_version/*path", get(get_latest_version)) + .route( + "/list_paths_from_workspace_runnable/:runnable_kind/*path", + get(list_paths_from_workspace_runnable), + ) .route( "/history_update/v/:version/p/*path", post(update_flow_history), @@ -324,6 +328,28 @@ async fn check_path_conflict<'c>( return Ok(()); } +async fn list_paths_from_workspace_runnable( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, runnable_kind, path)): Path<(String, RunnableKind, StripPath)>, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + let runnables = sqlx::query_scalar!( + r#"SELECT f.path + FROM flow_workspace_runnables fwr + JOIN flow f + ON fwr.flow_path = f.path AND fwr.workspace_id = f.workspace_id + WHERE fwr.runnable_path = $1 AND fwr.runnable_is_flow = $2 AND fwr.workspace_id = $3"#, + path.to_path(), + matches!(runnable_kind, RunnableKind::Flow), + w_id + ) + .fetch_all(&mut *tx) + .await?; + tx.commit().await?; + Ok(Json(runnables)) +} + async fn create_flow( authed: ApiAuthed, Extension(db): Extension, diff --git a/backend/windmill-api/src/folders.rs b/backend/windmill-api/src/folders.rs index cab039c64f..5047a96cad 100644 --- a/backend/windmill-api/src/folders.rs +++ b/backend/windmill-api/src/folders.rs @@ -42,6 +42,7 @@ pub fn workspaced_service() -> Router { .route("/listnames", get(list_foldernames)) .route("/create", post(create_folder)) .route("/get/:name", get(get_folder)) + .route("/exists/:name", get(exists_folder)) .route("/update/:name", post(update_folder)) .route("/getusage/:name", get(get_folder_usage)) .route("/delete/:name", delete(delete_folder)) @@ -426,6 +427,22 @@ async fn get_folder( Ok(Json(folder)) } +async fn exists_folder( + Extension(db): Extension, + Path((w_id, name)): Path<(String, String)>, +) -> JsonResult { + let exists = sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM folder WHERE name = $1 AND workspace_id = $2)", + name, + w_id + ) + .fetch_one(&db) + .await? + .unwrap_or(false); + + Ok(Json(exists)) +} + #[derive(Serialize)] struct FolderUsage { pub scripts: i64, diff --git a/backend/windmill-api/src/granular_acls.rs b/backend/windmill-api/src/granular_acls.rs index 1f0f5bebf0..0cdac8c3f0 100644 --- a/backend/windmill-api/src/granular_acls.rs +++ b/backend/windmill-api/src/granular_acls.rs @@ -23,7 +23,7 @@ use windmill_common::{ utils::{not_found_if_none, StripPath}, }; -const KINDS: [&str; 13] = [ +const KINDS: [&str; 14] = [ "script", "group_", "resource", @@ -37,6 +37,7 @@ const KINDS: [&str; 13] = [ "websocket_trigger", "kafka_trigger", "nats_trigger", + "mqtt_trigger" ]; pub fn workspaced_service() -> Router { diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index b86dae1ed0..ad0376fe51 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -28,7 +28,6 @@ use windmill_common::flow_status::{JobResult, RestartedFrom}; use windmill_common::jobs::{format_completed_job_result, format_result, ENTRYPOINT_OVERRIDE}; use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR}; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use windmill_common::scripts::PREVIEW_IS_CODEBASE_HASH; use windmill_common::variables::get_workspace_key; @@ -4397,7 +4396,6 @@ async fn run_preview_script( Ok((StatusCode::CREATED, uuid.to_string())) } -#[cfg(all(feature = "enterprise", feature = "parquet"))] async fn run_bundle_preview_script( authed: ApiAuthed, Extension(db): Extension, @@ -4408,8 +4406,6 @@ async fn run_bundle_preview_script( ) -> error::Result<(StatusCode, String)> { use windmill_common::scripts::PREVIEW_IS_TAR_CODEBASE_HASH; - check_license_key_valid().await?; - check_scopes(&authed, || format!("jobs:runscript"))?; if authed.is_operator { return Err(error::Error::NotAuthorized( @@ -4457,8 +4453,8 @@ async fn run_bundle_preview_script( path: preview.path, language: preview.language.unwrap_or(ScriptLang::Deno), lock: preview.lock, - concurrent_limit: None, // TODO(gbouv): once I find out how to store limits in the content of a script, should be easy to plug limits here - concurrency_time_window_s: None, // TODO(gbouv): same as above + concurrent_limit: None, + concurrency_time_window_s: None, cache_ttl: None, dedicated_worker: preview.dedicated_worker, custom_concurrency_key: None, @@ -4504,21 +4500,48 @@ async fn run_bundle_preview_script( uploaded = true; - if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let object_store = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS .read() .await - .clone() + .clone(); + + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let object_store: Option<()> = None; + + if &windmill_common::utils::MODE_AND_ADDONS.mode + == &windmill_common::utils::Mode::Standalone + && object_store.is_none() { - let path = windmill_common::s3_helpers::bundle(&w_id, &id); - if let Err(e) = os - .put(&object_store::path::Path::from(path.clone()), data.into()) - .await - { - tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); - return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); - } + std::fs::create_dir_all( + windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR.clone(), + )?; + windmill_common::worker::write_file_bytes( + &windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR, + &id, + &data, + )?; } else { - return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + { + return Err(Error::ExecutionErr("codebase is an EE feature".to_string())); + } + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if let Some(os) = object_store { + check_license_key_valid().await?; + + let path = windmill_common::s3_helpers::bundle(&w_id, &id); + if let Err(e) = os + .put(&object_store::path::Path::from(path.clone()), data.into()) + .await + { + tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); + return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); + } + } else { + return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + } } } // println!("Length of `{}` is {} bytes", name, data.len()); @@ -4537,13 +4560,6 @@ async fn run_bundle_preview_script( Ok((StatusCode::CREATED, job_id.unwrap().to_string())) } -#[cfg(not(all(feature = "enterprise", feature = "parquet")))] -async fn run_bundle_preview_script() -> error::Result<(StatusCode, String)> { - return Err(Error::BadRequest( - "bundle preview is an ee feature".to_string(), - )); -} - #[derive(Deserialize)] pub struct RunDependenciesRequest { pub raw_scripts: Vec, diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 57a49dc724..843a409906 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -89,6 +89,8 @@ pub mod job_metrics; pub mod jobs; #[cfg(all(feature = "enterprise", feature = "kafka"))] mod kafka_triggers_ee; +#[cfg(feature = "mqtt_trigger")] +mod mqtt_triggers; #[cfg(all(feature = "enterprise", feature = "nats"))] mod nats_triggers_ee; #[cfg(feature = "oauth2")] @@ -116,7 +118,7 @@ mod users; mod users_ee; mod utils; mod variables; -mod webhook_util; +pub mod webhook_util; #[cfg(feature = "websocket")] mod websocket_triggers; mod workers; @@ -321,6 +323,18 @@ pub async fn run_server( } }; + let mqtt_triggers_service = { + #[cfg(all(feature = "mqtt_trigger"))] + { + mqtt_triggers::workspaced_service() + } + + #[cfg(not(feature = "mqtt_trigger"))] + { + Router::new() + } + }; + let sqs_triggers_service = { #[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] { @@ -387,6 +401,12 @@ pub async fn run_server( let db_killpill_rx = rx.resubscribe(); postgres_triggers::start_database(db.clone(), db_killpill_rx); } + + #[cfg(feature = "mqtt_trigger")] + { + let mqtt_killpill_rx = rx.resubscribe(); + mqtt_triggers::start_mqtt_consumer(db.clone(), mqtt_killpill_rx); + } #[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] { @@ -447,6 +467,7 @@ pub async fn run_server( .nest("/websocket_triggers", websocket_triggers_service) .nest("/kafka_triggers", kafka_triggers_service) .nest("/nats_triggers", nats_triggers_service) + .nest("/mqtt_triggers", mqtt_triggers_service) .nest("/sqs_triggers", sqs_triggers_service) .nest("/postgres_triggers", postgres_triggers_service), ) @@ -579,7 +600,6 @@ pub async fn run_server( .on_failure(MyOnFailure {}), ) }; - let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); let port = listener.local_addr().map(|x| x.port()).unwrap_or(8000); let ip = listener diff --git a/backend/windmill-api/src/mqtt_triggers.rs b/backend/windmill-api/src/mqtt_triggers.rs new file mode 100644 index 0000000000..5f63e95b9e --- /dev/null +++ b/backend/windmill-api/src/mqtt_triggers.rs @@ -0,0 +1,1802 @@ +use crate::{ + capture::{insert_capture_payload, MqttTriggerConfig, TriggerKind}, + db::{ApiAuthed, DB}, + jobs::{run_flow_by_path_inner, run_script_by_path_inner, RunJobQuery}, + resources::try_get_resource_from_db_as, + users::fetch_api_authed, +}; +use axum::{ + async_trait, + extract::{Path, Query}, + Extension, Json, +}; +use axum::{ + routing::{delete, get, post}, + Router, +}; +use base64::prelude::*; +use bytes::Bytes; +use http::StatusCode; +use itertools::Itertools; +use rumqttc::{ + v5::{ + mqttbytes::{ + v5::{ConnectProperties, Filter, PublishProperties}, + QoS as V5QoS, + }, + AsyncClient as V5AsyncClient, Event as V5Event, EventLoop as V5EventLoop, + Incoming as V5Incoming, MqttOptions as V5MqttOptions, + }, + AsyncClient as V3AsyncClient, Event as V3Event, EventLoop as V3EventLoop, + Incoming as V3Incoming, MqttOptions as V3MqttOptions, QoS as V3QoS, SubscribeFilter, + TlsConfiguration, Transport, +}; +use serde::{Deserialize, Serialize}; +use sql_builder::{bind::Bind, SqlBuilder}; +use sqlx::{FromRow, Type}; +use std::collections::HashMap; +use std::time::Duration; +use windmill_audit::{audit_ee::audit_log, ActionKind}; +use windmill_common::{ + db::UserDB, + error::{self, JsonResult}, + utils::{not_found_if_none, paginate, report_critical_error, Pagination, StripPath}, + worker::{to_raw_value, CLOUD_HOSTED}, + INSTANCE_NAME, +}; + +use rand::seq::SliceRandom; +use serde_json::value::RawValue; +use sqlx::types::Json as SqlxJson; + +use windmill_queue::PushArgsOwned; + +pub fn workspaced_service() -> Router { + Router::new() + .route("/create", post(create_mqtt_trigger)) + .route("/list", get(list_mqtt_triggers)) + .route("/get/*path", get(get_mqtt_trigger)) + .route("/update/*path", post(update_mqtt_trigger)) + .route("/delete/*path", delete(delete_mqtt_trigger)) + .route("/exists/*path", get(exists_mqtt_trigger)) + .route("/setenabled/*path", post(set_enabled)) + .route("/test", post(test_mqtt_connection)) +} + +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("{0}")] + Common(#[from] windmill_common::error::Error), + #[error("{0}")] + V5RumqttClient(#[from] rumqttc::v5::ClientError), + #[error("{0}")] + V5ConnectionError(#[from] rumqttc::v5::ConnectionError), + #[error("{0}")] + V3RumqttClient(#[from] rumqttc::ClientError), + #[error("{0}")] + V3ConnectionError(#[from] rumqttc::ConnectionError), + #[error("{0}")] + Base64Decode(#[from] base64::DecodeError), +} + +async fn run_job( + args: Option>>, + extra: Option>>, + db: &DB, + trigger: &MqttTrigger, +) -> anyhow::Result<()> { + let args = PushArgsOwned { args: args.unwrap_or_default(), extra }; + + let authed = fetch_api_authed( + trigger.edited_by.clone(), + trigger.email.clone(), + &trigger.workspace_id, + db, + Some(format!("mqtt-{}", trigger.path)), + ) + .await?; + + let user_db = UserDB::new(db.clone()); + + let run_query = RunJobQuery::default(); + + if trigger.is_flow { + run_flow_by_path_inner( + authed, + db.clone(), + user_db, + trigger.workspace_id.clone(), + StripPath(trigger.script_path.to_owned()), + run_query, + args, + None, + ) + .await?; + } else { + run_script_by_path_inner( + authed, + db.clone(), + user_db, + trigger.workspace_id.clone(), + StripPath(trigger.script_path.to_owned()), + run_query, + args, + None, + ) + .await?; + } + + Ok(()) +} + +#[derive(Clone, Debug, Deserialize, Serialize, Type)] +#[serde(rename_all = "lowercase")] +pub enum QualityOfService { + Qos0, + Qos1, + Qos2, +} + +impl From for V3QoS { + fn from(value: QualityOfService) -> Self { + match value { + QualityOfService::Qos0 => V3QoS::AtMostOnce, + QualityOfService::Qos1 => V3QoS::AtLeastOnce, + QualityOfService::Qos2 => V3QoS::ExactlyOnce, + } + } +} + +impl From for V5QoS { + fn from(value: QualityOfService) -> Self { + match value { + QualityOfService::Qos0 => V5QoS::AtMostOnce, + QualityOfService::Qos1 => V5QoS::AtLeastOnce, + QualityOfService::Qos2 => V5QoS::ExactlyOnce, + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct MqttV3Config { + clean_session: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct MqttV5Config { + clean_start: Option, + session_expiry_interval: Option, + topic_alias_maximum: Option, +} + +#[derive(Debug, Deserialize, Serialize, Type)] +#[sqlx(type_name = "MQTT_CLIENT_VERSION")] +#[sqlx(rename_all = "lowercase")] +#[serde(rename_all = "lowercase")] +pub enum MqttClientVersion { + V3, + V5, +} + +#[derive(Debug, Deserialize)] +pub struct Tls { + enabled: bool, + ca_certificate: String, + //encoded in base64 + pkcs12_client_certificate: Option, + pkcs12_certificate_password: Option, +} + +#[derive(Debug, Deserialize)] +pub struct Credentials { + username: Option, + password: Option, +} + +#[derive(Debug, Deserialize)] +pub struct MqttResource { + broker: String, + port: u16, + credentials: Option, + tls: Option, +} +#[derive(Clone, Debug, FromRow, Serialize, Deserialize)] +pub struct SubscribeTopic { + qos: QualityOfService, + topic: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NewMqttTrigger { + mqtt_resource_path: String, + subscribe_topics: Vec, + v3_config: Option, + v5_config: Option, + #[serde(skip_serializing_if = "Option::is_none")] + client_version: Option, + client_id: Option, + path: String, + script_path: String, + is_flow: bool, + enabled: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct EditMqttTrigger { + mqtt_resource_path: String, + subscribe_topics: Vec, + v3_config: Option, + v5_config: Option, + client_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + client_version: Option, + path: String, + script_path: String, + is_flow: bool, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct MqttTrigger { + mqtt_resource_path: String, + subscribe_topics: Vec>, + v3_config: Option>, + v5_config: Option>, + client_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + client_version: Option, + path: String, + script_path: String, + is_flow: bool, + workspace_id: String, + edited_by: String, + email: String, + edited_at: chrono::DateTime, + extra_perms: Option, + error: Option, + server_id: Option, + last_server_ping: Option>, + enabled: bool, +} + +#[derive(Deserialize, Serialize)] +pub struct ListMqttTriggerQuery { + page: Option, + per_page: Option, + path: Option, + is_flow: Option, + path_start: Option, +} + +#[derive(Deserialize)] +pub struct SetEnabled { + enabled: bool, +} + +const KEEP_ALIVE: u64 = 60; +const CLIENT_CONNECTION_TIMEOUT: u64 = 60; +const TOPIC_ALIAS_MAXIMUM: u16 = 65535; +struct MqttClientBuilder<'client> { + mqtt_resource: MqttResource, + client_id: &'client str, + subscribe_topics: Vec, + v3_config: Option<&'client MqttV3Config>, + v5_config: Option<&'client MqttV5Config>, + mqtt_client_version: Option<&'client MqttClientVersion>, +} + +impl<'client> MqttClientBuilder<'client> { + fn new( + mqtt_resource: MqttResource, + client_id: Option<&'client str>, + subscribe_topics: Vec, + v3_config: Option<&'client MqttV3Config>, + v5_config: Option<&'client MqttV5Config>, + mqtt_client_version: Option<&'client MqttClientVersion>, + ) -> Self { + Self { + mqtt_resource, + client_id: client_id.unwrap_or(""), + subscribe_topics, + v3_config, + v5_config, + mqtt_client_version, + } + } + + async fn build_client(&self) -> Result { + match self.mqtt_client_version { + Some(MqttClientVersion::V5) | None => self.build_v5_client().await, + Some(MqttClientVersion::V3) => self.build_v3_client().await, + } + } + + fn get_tls_configuration(&self) -> Result, Error> { + let transport = match self.mqtt_resource.tls { + Some(ref tls) if tls.enabled => { + let transport = match tls.ca_certificate.trim().is_empty() { + true => rumqttc::Transport::Tls(TlsConfiguration::Native), + false => rumqttc::Transport::Tls(TlsConfiguration::SimpleNative { + ca: tls.ca_certificate.as_bytes().to_vec(), + client_auth: { + match tls.pkcs12_client_certificate.as_ref() { + Some(client_certificate) + if !client_certificate.trim().is_empty() => + { + let client_certificate = + BASE64_STANDARD.decode(client_certificate)?; + let password = tls + .pkcs12_certificate_password + .clone() + .unwrap_or("".to_string()); + Some((client_certificate, password)) + } + _ => None, + } + }, + }), + }; + + Some(transport) + } + _ => None, + }; + + Ok(transport) + } + + async fn build_v5_client(&self) -> Result { + let mut mqtt_options = V5MqttOptions::new( + self.client_id, + &self.mqtt_resource.broker, + self.mqtt_resource.port, + ); + + if let Some(credentials) = &self.mqtt_resource.credentials { + let username = credentials.username.as_deref().unwrap_or(""); + let password = credentials.password.as_deref().unwrap_or(""); + mqtt_options.set_credentials(username, password); + } + + if let Some(transport) = self.get_tls_configuration()? { + mqtt_options.set_transport(transport); + } + + mqtt_options.set_connection_timeout(CLIENT_CONNECTION_TIMEOUT); + + mqtt_options.set_keep_alive(Duration::from_secs(KEEP_ALIVE)); + + if let Some(v5_config) = self.v5_config { + mqtt_options.set_clean_start(v5_config.clean_start.unwrap_or(true)); + mqtt_options.set_connect_properties(ConnectProperties { + session_expiry_interval: v5_config.session_expiry_interval, + receive_maximum: None, + max_packet_size: None, + topic_alias_max: v5_config.topic_alias_maximum.or(Some(TOPIC_ALIAS_MAXIMUM)), + request_response_info: None, + request_problem_info: None, + user_properties: vec![], + authentication_method: None, + authentication_data: None, + }); + } + + let (async_client, mut event_loop) = + V5AsyncClient::new(mqtt_options, self.subscribe_topics.len()); + event_loop.verify_connection().await?; + + if !self.subscribe_topics.is_empty() { + let subscribe_filters = self + .subscribe_topics + .iter() + .map(|topic| Filter::new(topic.topic.clone(), topic.qos.clone().into())) + .collect_vec(); + + async_client.subscribe_many(subscribe_filters).await?; + } + Ok(MqttClientResult::V5((V5MqttHandler, event_loop))) + } + + async fn build_v3_client(&self) -> Result { + let mut mqtt_options = V3MqttOptions::new( + self.client_id, + &self.mqtt_resource.broker, + self.mqtt_resource.port, + ); + + if let Some(credentials) = &self.mqtt_resource.credentials { + let username = credentials.username.as_deref().unwrap_or(""); + let password = credentials.password.as_deref().unwrap_or(""); + mqtt_options.set_credentials(username, password); + } + + if let Some(transport) = self.get_tls_configuration()? { + mqtt_options.set_transport(transport); + } + mqtt_options.set_keep_alive(Duration::from_secs(KEEP_ALIVE)); + if let Some(v3_config) = self.v3_config { + mqtt_options.set_clean_session(v3_config.clean_session.unwrap_or(true)); + } + + let (async_client, mut event_loop) = + V3AsyncClient::new(mqtt_options, self.subscribe_topics.len()); + event_loop.verify_connection().await?; + + if !self.subscribe_topics.is_empty() { + let subscribe_filters = self + .subscribe_topics + .iter() + .map(|topic| SubscribeFilter::new(topic.topic.clone(), topic.qos.clone().into())) + .collect_vec(); + + async_client.subscribe_many(subscribe_filters).await?; + } + Ok(MqttClientResult::V3((V3MqttHandler, event_loop))) + } +} + +fn convert_disconnect_packet_into_string( + disconnect: rumqttc::v5::mqttbytes::v5::Disconnect, +) -> String { + let err_message = disconnect + .properties + .map(|properties| properties.reason_string) + .flatten(); + let reason_code = disconnect.reason_code as u8; + format!( + "Disconnected by the broker, reason code: {}, {}", + reason_code, + err_message + .map(|err| format!("message: {}", err)) + .unwrap_or("".to_string()) + ) +} + +#[derive(Debug, Deserialize)] +pub struct TestMqttConnection { + mqtt_resource_path: String, + client_version: Option, + v3_config: Option, + v5_config: Option, +} + +pub async fn test_mqtt_connection( + authed: ApiAuthed, + Extension(db): Extension, + Extension(user_db): Extension, + Path(workspace_id): Path, + Json(test_postgres): Json, +) -> error::Result<()> { + let TestMqttConnection { mqtt_resource_path, client_version, v3_config, v5_config } = + test_postgres; + + let mqtt_resource = try_get_resource_from_db_as::( + authed, + Some(user_db), + &db, + &mqtt_resource_path, + &workspace_id, + ) + .await?; + + let connect_f = async { + let client_builder = MqttClientBuilder::new( + mqtt_resource, + Some(""), + vec![], + v3_config.as_ref(), + v5_config.as_ref(), + client_version.as_ref(), + ); + + client_builder.build_client().await.map_err(|err| { + error::Error::BadConfig(format!( + "Error connecting to mqtt broker: {}", + err.to_string() + )) + }) + }; + tokio::time::timeout(tokio::time::Duration::from_secs(30), connect_f) + .await + .map_err(|_| { + error::Error::BadConfig(format!( + "Timeout occurred while trying to connect to mqtt broker after 30 seconds" + )) + })??; + + Ok(()) +} + +pub async fn create_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path(w_id): Path, + Json(new_mqtt_trigger): Json, +) -> error::Result<(StatusCode, String)> { + if *CLOUD_HOSTED { + return Err(error::Error::BadRequest( + "Mqtt triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(), + )); + } + + let NewMqttTrigger { + mqtt_resource_path, + subscribe_topics, + path, + script_path, + enabled, + is_flow, + v3_config, + v5_config, + client_version, + client_id, + } = new_mqtt_trigger; + + let mut tx = user_db.begin(&authed).await?; + + let subscribe_topics = subscribe_topics.into_iter().map(SqlxJson).collect_vec(); + let v3_config = v3_config.map(SqlxJson); + let v5_config = v5_config.map(SqlxJson); + + sqlx::query!( + r#" + INSERT INTO mqtt_trigger ( + mqtt_resource_path, + subscribe_topics, + client_version, + client_id, + v3_config, + v5_config, + workspace_id, + path, + script_path, + is_flow, + email, + enabled, + edited_by + ) + VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10, + $11, + $12, + $13 + )"#, + mqtt_resource_path, + subscribe_topics.as_slice() as &[SqlxJson], + client_version as Option, + client_id, + v3_config as Option>, + v5_config as Option>, + &w_id, + &path, + script_path, + is_flow, + &authed.email, + enabled, + &authed.username + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.create", + ActionKind::Create, + &w_id, + Some(path.as_str()), + None, + ) + .await?; + + tx.commit().await?; + + Ok((StatusCode::CREATED, path.to_string())) +} + +pub async fn list_mqtt_triggers( + authed: ApiAuthed, + Extension(user_db): Extension, + Path(w_id): Path, + Query(lst): Query, +) -> error::JsonResult> { + let mut tx = user_db.begin(&authed).await?; + let (per_page, offset) = paginate(Pagination { per_page: lst.per_page, page: lst.page }); + let mut sqlb = SqlBuilder::select_from("mqtt_trigger") + .fields(&[ + "mqtt_resource_path", + "subscribe_topics", + "v3_config", + "v5_config", + "client_version", + "client_id", + "workspace_id", + "path", + "script_path", + "is_flow", + "edited_by", + "email", + "edited_at", + "server_id", + "last_server_ping", + "extra_perms", + "error", + "enabled", + ]) + .order_by("edited_at", true) + .and_where("workspace_id = ?".bind(&w_id)) + .offset(offset) + .limit(per_page) + .clone(); + if let Some(path) = lst.path { + sqlb.and_where_eq("script_path", "?".bind(&path)); + } + if let Some(is_flow) = lst.is_flow { + sqlb.and_where_eq("is_flow", "?".bind(&is_flow)); + } + if let Some(path_start) = &lst.path_start { + sqlb.and_where_like_left("path", path_start); + } + let sql = sqlb + .sql() + .map_err(|e| error::Error::InternalErr(e.to_string()))?; + let rows = sqlx::query_as::<_, MqttTrigger>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|e| { + tracing::debug!("Error fetching mqtt_trigger: {:#?}", e); + windmill_common::error::Error::InternalErr("server error".to_string()) + })?; + tx.commit().await.map_err(|e| { + tracing::debug!("Error committing mqtt_trigger: {:#?}", e); + windmill_common::error::Error::InternalErr("server error".to_string()) + })?; + + Ok(Json(rows)) +} + +pub async fn get_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> JsonResult { + let mut tx = user_db.begin(&authed).await?; + let path = path.to_path(); + let trigger = sqlx::query_as!( + MqttTrigger, + r#" + SELECT + mqtt_resource_path, + subscribe_topics as "subscribe_topics!: Vec>", + v3_config as "v3_config!: Option>", + v5_config as "v5_config!: Option>", + client_version AS "client_version: _", + client_id, + workspace_id, + path, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled + FROM + mqtt_trigger + WHERE + workspace_id = $1 AND + path = $2 + "#, + w_id, + &path + ) + .fetch_optional(&mut *tx) + .await?; + tx.commit().await?; + + let trigger = not_found_if_none(trigger, "Mqtt Trigger", path)?; + + Ok(Json(trigger)) +} + +pub async fn update_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + Json(mqtt_trigger): Json, +) -> error::Result { + let workspace_path = path.to_path(); + let EditMqttTrigger { + mqtt_resource_path, + subscribe_topics, + script_path, + path, + is_flow, + v3_config, + v5_config, + client_version, + client_id, + } = mqtt_trigger; + + let mut tx = user_db.begin(&authed).await?; + + let subscribe_topics = subscribe_topics.into_iter().map(SqlxJson).collect_vec(); + + let v3_config = v3_config.map(SqlxJson); + let v5_config = v5_config.map(SqlxJson); + + sqlx::query!( + r#" + UPDATE + mqtt_trigger + SET + mqtt_resource_path = $1, + subscribe_topics = $2, + client_version = $3, + client_id = $4, + v3_config = $5, + v5_config = $6, + is_flow = $7, + edited_by = $8, + email = $9, + script_path = $10, + path = $11, + edited_at = now(), + error = NULL, + server_id = NULL + WHERE + workspace_id = $12 AND + path = $13 + "#, + mqtt_resource_path, + subscribe_topics.as_slice() as &[SqlxJson], + client_version as Option, + client_id, + v3_config as Option>, + v5_config as Option>, + is_flow, + &authed.username, + &authed.email, + script_path, + path, + w_id, + workspace_path, + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.update", + ActionKind::Create, + &w_id, + Some(&path), + None, + ) + .await?; + + tx.commit().await?; + + Ok(workspace_path.to_string()) +} + +pub async fn delete_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> error::Result { + let path = path.to_path(); + let mut tx = user_db.begin(&authed).await?; + sqlx::query!( + r#" + DELETE + FROM + mqtt_trigger + WHERE + workspace_id = $1 AND + path = $2 + "#, + w_id, + path, + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.delete", + ActionKind::Delete, + &w_id, + Some(path), + None, + ) + .await?; + + tx.commit().await?; + + Ok(format!("Mqtt trigger {path} deleted")) +} + +pub async fn exists_mqtt_trigger( + Extension(db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> JsonResult { + let path = path.to_path(); + let exists = sqlx::query_scalar!( + r#" + SELECT EXISTS( + SELECT + 1 + FROM + mqtt_trigger + WHERE + path = $1 AND + workspace_id = $2 + )"#, + path, + w_id, + ) + .fetch_one(&db) + .await? + .unwrap_or(false); + Ok(Json(exists)) +} + +pub async fn set_enabled( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + Json(payload): Json, +) -> error::Result { + let mut tx = user_db.begin(&authed).await?; + let path = path.to_path(); + + // important to set server_id, last_server_ping and error to NULL to stop current mqtt listener + let one_o = sqlx::query_scalar!( + r#" + UPDATE + mqtt_trigger + SET + enabled = $1, + email = $2, + edited_by = $3, + edited_at = now(), + server_id = NULL, + error = NULL + WHERE + path = $4 AND + workspace_id = $5 + RETURNING 1 + "#, + payload.enabled, + &authed.email, + &authed.username, + path, + w_id, + ) + .fetch_optional(&mut *tx) + .await? + .flatten(); + + not_found_if_none(one_o, "Mqtt trigger", path)?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.setenabled", + ActionKind::Update, + &w_id, + Some(path), + Some([("enabled", payload.enabled.to_string().as_ref())].into()), + ) + .await?; + + tx.commit().await?; + + Ok(format!( + "successfully updated mqtt trigger at path {} to status {}", + path, payload.enabled + )) +} + +async fn loop_ping(db: &DB, mqtt: &MqttConfig, error: Option<&str>) { + loop { + if mqtt.update_ping(db, error).await.is_none() { + return; + } + + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + } +} + +enum MqttClientResult { + V3((V3MqttHandler, V3EventLoop)), + V5((V5MqttHandler, V5EventLoop)), +} + +trait MqttEvent { + type IncomingPacket; + type PublishPacket; + type Event; + + fn handle_publish_packet(publish_packet: Self::PublishPacket) -> PublishData; + fn handle_event(&self, event: Self::Event) -> Result, String>; +} + +struct V5MqttHandler; + +impl MqttEvent for V5MqttHandler { + type IncomingPacket = V5Incoming; + type PublishPacket = rumqttc::v5::mqttbytes::v5::Publish; + type Event = V5Event; + + fn handle_publish_packet(publish_packet: Self::PublishPacket) -> PublishData { + PublishData::new( + String::from_utf8(publish_packet.topic.as_ref().to_vec()).unwrap_or("".to_string()), + publish_packet.retain, + publish_packet.pkid, + publish_packet.properties, + publish_packet.qos as u8, + ) + } + + fn handle_event(&self, event: Self::Event) -> Result, String> { + tracing::debug!("Inside V5 event"); + match event { + Self::Event::Incoming(packet) => match packet { + Self::IncomingPacket::Publish(publish_packet) => { + return Ok(Some(( + publish_packet.payload.clone(), + Self::handle_publish_packet(publish_packet), + ))) + } + Self::IncomingPacket::Disconnect(disconnect) => { + return Err(convert_disconnect_packet_into_string(disconnect)); + } + packet => { + tracing::debug!("Received = {:#?}", packet); + } + }, + Self::Event::Outgoing(packet) => { + tracing::debug!("Outgoing Received = {:#?}", packet); + } + } + + Ok(None) + } +} + +struct V3MqttHandler; + +impl MqttEvent for V3MqttHandler { + type IncomingPacket = V3Incoming; + type PublishPacket = rumqttc::mqttbytes::v4::Publish; + type Event = V3Event; + + fn handle_publish_packet(publish_packet: Self::PublishPacket) -> PublishData { + PublishData::new( + publish_packet.topic, + publish_packet.retain, + publish_packet.pkid, + None, + publish_packet.qos as u8, + ) + } + + fn handle_event(&self, event: Self::Event) -> Result, String> { + tracing::debug!("Inside V3 event"); + match event { + Self::Event::Incoming(packet) => match packet { + Self::IncomingPacket::Publish(publish_packet) => { + return Ok(Some(( + publish_packet.payload.clone(), + Self::handle_publish_packet(publish_packet), + ))) + } + packet => { + tracing::debug!("Received = {:?}", packet); + } + }, + Self::Event::Outgoing(packet) => { + tracing::debug!("Outgoing Received = {:?}", packet); + } + } + + Ok(None) + } +} + +const TIMEOUT_DURATION: u64 = 10; +const CONNECTION_TIMEOUT: Duration = Duration::from_secs(TIMEOUT_DURATION); + +#[async_trait] +trait EventLoop { + type Event; + type Error; + + async fn poll(&mut self) -> Result; + async fn verify_connection(&mut self) -> Result<(), Error>; +} + +#[async_trait] +impl EventLoop for V5EventLoop { + type Event = V5Event; + type Error = rumqttc::v5::ConnectionError; + async fn poll(&mut self) -> Result { + self.poll().await + } + + async fn verify_connection(&mut self) -> Result<(), Error> { + let start = std::time::Instant::now(); + + while start.elapsed() < CONNECTION_TIMEOUT { + match self.poll().await? { + Self::Event::Incoming(V5Incoming::ConnAck(_)) => return Ok(()), + Self::Event::Incoming(V5Incoming::Disconnect(disconnect)) => { + return Err(Error::Common(error::Error::BadConfig( + convert_disconnect_packet_into_string(disconnect), + ))); + } + _ => continue, + } + } + + Err(Error::Common(error::Error::BadConfig(format!( + "Timeout occurred while trying to connect to mqtt broker after {} seconds", + TIMEOUT_DURATION + )))) + } +} + +#[async_trait] +impl EventLoop for V3EventLoop { + type Event = V3Event; + type Error = rumqttc::ConnectionError; + + async fn poll(&mut self) -> Result { + self.poll().await + } + + async fn verify_connection(&mut self) -> Result<(), Error> { + let start = std::time::Instant::now(); + + while start.elapsed() < CONNECTION_TIMEOUT { + match self.poll().await? { + Self::Event::Incoming(rumqttc::Packet::ConnAck(_)) => return Ok(()), + _ => continue, + } + } + + Err(Error::Common(error::Error::BadConfig(format!( + "Timeout occurred while trying to connect to mqtt broker after {} seconds", + TIMEOUT_DURATION + )))) + } +} + +async fn handle_publish_packet(db: &DB, mqtt: &MqttConfig, payload: Bytes, publish: PublishData) { + let args = HashMap::from([("payload".to_string(), to_raw_value(&payload.as_ref()))]); + let extra = Some(HashMap::from([( + "wm_trigger".to_string(), + to_raw_value(&serde_json::json!({ + "kind": "mqtt", + "mqtt": { + "topic": publish.topic, + "retain": publish.retain, + "pkid": publish.pkid, + "qos": publish.qos, + "v5": publish.v5.map(|properties| { + serde_json::json!({ + "payload_format_indicator": properties.payload_format_indicator, + "topic_alias": properties.topic_alias, + "response_topic": properties.response_topic, + "correlation_data": properties.correlation_data.as_deref(), + "user_properties": properties.user_properties, + "subscription_identifiers": properties.subscription_identifiers, + "content_type": properties.content_type, + }) + }) + } + })), + )])); + mqtt.handle(&db, Some(args), extra).await; +} + +async fn handle_event(db: &DB, mqtt: &MqttConfig, handler: H, mut event_loop: E) -> () +where + H: MqttEvent, + E: EventLoop, + E::Error: ToString, +{ + loop { + let event = event_loop.poll().await; + + match event { + Ok(event) => { + let publish_data = handler.handle_event(event); + if let Ok(Some((payload, publish_data))) = publish_data { + handle_publish_packet(db, mqtt, payload, publish_data).await; + } + } + Err(err) => { + let err = err.to_string(); + tracing::debug!("Error: {}", &err); + mqtt.disable_with_error(&db, err).await; + return; + } + } + } +} + +#[derive(Debug)] +enum MqttConfig { + Trigger(MqttTrigger), + Capture(CaptureConfigForMqttTrigger), +} + +impl MqttConfig { + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + match self { + MqttConfig::Trigger(trigger) => trigger.update_ping(db, error).await, + MqttConfig::Capture(capture) => capture.update_ping(db, error).await, + } + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + match self { + MqttConfig::Trigger(trigger) => trigger.disable_with_error(&db, error).await, + MqttConfig::Capture(capture) => capture.disable_with_error(db, error).await, + } + } + + async fn start_consuming_messages( + &self, + db: &DB, + ) -> std::result::Result { + let mqtt_resource_path; + let subscribe_topics; + let workspace_id; + let authed; + let client_version; + let client_id; + let v3_config; + let v5_config; + match self { + MqttConfig::Capture(capture) => { + mqtt_resource_path = &capture.trigger_config.0.mqtt_resource_path; + subscribe_topics = capture.trigger_config.0.subscribe_topics.clone(); + workspace_id = &capture.workspace_id; + authed = capture.fetch_authed(&db).await?; + client_version = capture.trigger_config.0.client_version.as_ref(); + client_id = capture.trigger_config.0.client_id.as_deref(); + v3_config = capture.trigger_config.0.v3_config.as_ref(); + v5_config = capture.trigger_config.0.v5_config.as_ref(); + } + MqttConfig::Trigger(trigger) => { + mqtt_resource_path = &trigger.mqtt_resource_path; + subscribe_topics = trigger + .subscribe_topics + .iter() + .map(|topic| topic.0.clone()) + .collect_vec(); + workspace_id = &trigger.workspace_id; + client_version = trigger.client_version.as_ref(); + authed = trigger.fetch_authed(&db).await?; + client_id = trigger.client_id.as_deref(); + v3_config = trigger.v3_config.as_ref().map(|v3_config| &v3_config.0); + v5_config = trigger.v5_config.as_ref().map(|v5_config| &v5_config.0); + } + } + let mqtt_resource = try_get_resource_from_db_as::( + authed, + Some(UserDB::new(db.clone())), + db, + mqtt_resource_path, + workspace_id, + ) + .await?; + let client_builder = MqttClientBuilder::new( + mqtt_resource, + client_id, + subscribe_topics, + v3_config, + v5_config, + client_version, + ); + + client_builder.build_client().await + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + match self { + MqttConfig::Trigger(trigger) => trigger.handle(&db, args, extra).await, + MqttConfig::Capture(capture) => capture.handle(&db, args, extra).await, + } + } +} + +impl MqttTrigger { + async fn try_to_listen_to_mqtt_messages( + self, + db: DB, + killpill_rx: tokio::sync::broadcast::Receiver<()>, + ) -> () { + let mqtt_trigger = sqlx::query_scalar!( + r#" + UPDATE + mqtt_trigger + SET + server_id = $1, + last_server_ping = now(), + error = 'Connecting...' + WHERE + enabled IS TRUE + AND workspace_id = $2 + AND path = $3 + AND (last_server_ping IS NULL + OR last_server_ping < now() - INTERVAL '15 seconds' + ) + RETURNING true + "#, + *INSTANCE_NAME, + self.workspace_id, + self.path, + ) + .fetch_optional(&db) + .await; + match mqtt_trigger { + Ok(has_lock) => { + if has_lock.flatten().unwrap_or(false) { + tracing::info!("Spawning new task to listen to mqtt notifications"); + tokio::spawn(async move { + listen_to_messages(MqttConfig::Trigger(self), db.clone(), killpill_rx) + .await; + }); + } else { + tracing::info!("Mqtt trigger {} already being listened to", self.path); + } + } + Err(err) => { + tracing::error!( + "Error acquiring lock for mqtt trigger {}: {:?}", + self.path, + err + ); + } + }; + } + + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + let updated = sqlx::query_scalar!( + r#" + UPDATE + mqtt_trigger + SET + last_server_ping = now(), + error = $1 + WHERE + workspace_id = $2 + AND path = $3 + AND server_id = $4 + AND enabled IS TRUE + RETURNING 1 + "#, + error, + &self.workspace_id, + &self.path, + *INSTANCE_NAME + ) + .fetch_optional(db) + .await; + + match updated { + Ok(updated) => { + if updated.flatten().is_none() { + // allow faster restart of mqtt trigger + sqlx::query!( + r#" + UPDATE + mqtt_trigger + SET + last_server_ping = NULL + WHERE + workspace_id = $1 + AND path = $2 + AND server_id IS NULL"#, + &self.workspace_id, + &self.path, + ) + .execute(db) + .await + .ok(); + tracing::info!( + "Mqtt trigger {} changed, disabled, or deleted, stopping...", + self.path + ); + return None; + } + } + Err(err) => { + tracing::warn!( + "Error updating ping of mqtt trigger {}: {:?}", + self.path, + err + ); + } + }; + + Some(()) + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + match sqlx::query!( + r#" + UPDATE + mqtt_trigger + SET + enabled = FALSE, + error = $1, + server_id = NULL, + last_server_ping = NULL + WHERE + workspace_id = $2 AND + path = $3 + "#, + error, + self.workspace_id, + self.path, + ) + .execute(db) + .await + { + Ok(_) => { + report_critical_error( + format!( + "Disabling mqtt trigger {} because of error: {}", + self.path, error + ), + db.clone(), + Some(&self.workspace_id), + None, + ) + .await; + } + Err(disable_err) => { + report_critical_error( + format!("Could not disable mqtt trigger {} with err {}, disabling because of error {}", self.path, disable_err, error), + db.clone(), + Some(&self.workspace_id), + None, + ).await; + } + } + } + + async fn fetch_authed(&self, db: &DB) -> error::Result { + fetch_api_authed( + self.edited_by.clone(), + self.email.clone(), + &self.workspace_id, + db, + Some(format!("mqtt-{}", self.path)), + ) + .await + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + if let Err(err) = run_job(args, extra, db, self).await { + report_critical_error( + format!("Failed to trigger job from mqtt {}: {:?}", self.path, err), + db.clone(), + Some(&self.workspace_id), + None, + ) + .await; + }; + } +} + +struct PublishData { + topic: String, + retain: bool, + pkid: u16, + v5: Option, + qos: u8, +} + +impl PublishData { + fn new( + topic: String, + retain: bool, + pkid: u16, + v5: Option, + qos: u8, + ) -> PublishData { + PublishData { topic, retain, pkid, v5, qos } + } +} + +async fn listen_to_messages( + mqtt: MqttConfig, + db: DB, + mut killpill_rx: tokio::sync::broadcast::Receiver<()>, +) { + tokio::select! { + biased; + + _ = killpill_rx.recv() => { + return; + } + + _ = loop_ping(&db, &mqtt, Some("Connecting...")) => { + return; + } + + result = mqtt.start_consuming_messages(&db) => { + tokio::select! { + biased; + + _ = killpill_rx.recv() => { + return; + } + + _ = loop_ping(&db, &mqtt, None) => { + return; + } + + _ = async { + match result { + Ok(connection) => { + match connection { + MqttClientResult::V3((v3_handler, event_loop)) => handle_event(&db, &mqtt, v3_handler, event_loop).await, + MqttClientResult::V5((v5_handler, event_loop)) => handle_event(&db, &mqtt, v5_handler, event_loop).await, + } + } + Err(err) => { + tracing::error!( + "Mqtt trigger error while trying to start listening to notifications: {}", + &err + ); + mqtt.disable_with_error(&db, err.to_string()).await + } + } + } => {} + } + } + } +} + +#[derive(Debug, Deserialize)] +struct CaptureConfigForMqttTrigger { + trigger_config: SqlxJson, + path: String, + is_flow: bool, + workspace_id: String, + owner: String, + email: String, +} + +impl CaptureConfigForMqttTrigger { + async fn try_to_listen_to_mqtt_messages( + self, + db: DB, + killpill_rx: tokio::sync::broadcast::Receiver<()>, + ) -> () { + match sqlx::query_scalar!( + r#" + UPDATE + capture_config + SET + server_id = $1, + last_server_ping = now(), + error = 'Connecting...' + WHERE + last_client_ping > NOW() - INTERVAL '10 seconds' AND + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'mqtt' AND + (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') + RETURNING true + "#, + *INSTANCE_NAME, + self.workspace_id, + self.path, + self.is_flow, + ) + .fetch_optional(&db) + .await + { + Ok(has_lock) => { + if has_lock.flatten().unwrap_or(false) { + tokio::spawn(listen_to_messages( + MqttConfig::Capture(self), + db, + killpill_rx, + )); + } else { + tracing::info!("Mqtt {} already being listened to", self.path); + } + } + Err(err) => { + tracing::error!( + "Error acquiring lock for capture mqtt {}: {:?}", + self.path, + err + ); + } + }; + } + + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + match sqlx::query_scalar!( + r#" + UPDATE + capture_config + SET + last_server_ping = now(), + error = $1 + WHERE + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'mqtt' AND + server_id = $5 AND + last_client_ping > NOW() - INTERVAL '10 seconds' + RETURNING 1 + "#, + error, + self.workspace_id, + self.path, + self.is_flow, + *INSTANCE_NAME + ) + .fetch_optional(db) + .await + { + Ok(updated) => { + if updated.flatten().is_none() { + // allow faster restart of mqtt capture + sqlx::query!( + r#"UPDATE + capture_config + SET + last_server_ping = NULL + WHERE + workspace_id = $1 AND + path = $2 AND + is_flow = $3 AND + trigger_kind = 'mqtt' AND + server_id IS NULL + "#, + self.workspace_id, + self.path, + self.is_flow, + ) + .execute(db) + .await + .ok(); + tracing::info!( + "Mqtt capture {} changed, disabled, or deleted, stopping...", + self.path + ); + return None; + } + } + Err(err) => { + tracing::warn!( + "Error updating ping of capture mqtt {}: {:?}", + self.path, + err + ); + } + }; + + Some(()) + } + + async fn fetch_authed(&self, db: &DB) -> error::Result { + fetch_api_authed( + self.owner.clone(), + self.email.clone(), + &self.workspace_id, + db, + Some(format!("mqtt-{}", self.get_trigger_path())), + ) + .await + } + + fn get_trigger_path(&self) -> String { + format!( + "{}-{}", + if self.is_flow { "flow" } else { "script" }, + self.path + ) + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + if let Err(err) = sqlx::query!( + r#" + UPDATE + capture_config + SET + error = $1, + server_id = NULL, + last_server_ping = NULL + WHERE + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'mqtt' + "#, + error, + self.workspace_id, + self.path, + self.is_flow, + ) + .execute(db) + .await + { + tracing::error!( + "Could not disable mqtt capture {} ({}) with err {}, disabling because of error {}", + self.path, + self.workspace_id, + err, + error + ); + } + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + let args = PushArgsOwned { args: args.unwrap_or_default(), extra: None }; + let extra = extra.as_ref().map(to_raw_value); + if let Err(err) = insert_capture_payload( + db, + &self.workspace_id, + &self.path, + self.is_flow, + &TriggerKind::Mqtt, + args, + extra, + &self.owner, + ) + .await + { + tracing::error!("Error inserting capture payload: {:?}", err); + } + } +} + +async fn listen_to_unlistened_mqtt_events( + db: &DB, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, +) { + let mqtt_triggers = sqlx::query_as!( + MqttTrigger, + r#" + SELECT + mqtt_resource_path, + subscribe_topics as "subscribe_topics!: Vec>", + v3_config as "v3_config!: Option>", + v5_config as "v5_config!: Option>", + client_version as "client_version: _", + client_id, + workspace_id, + path, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled + FROM + mqtt_trigger + WHERE + enabled IS TRUE + AND (last_server_ping IS NULL OR + last_server_ping < now() - interval '15 seconds' + ) + "# + ) + .fetch_all(db) + .await; + + match mqtt_triggers { + Ok(mut triggers) => { + triggers.shuffle(&mut rand::rng()); + for trigger in triggers { + trigger + .try_to_listen_to_mqtt_messages(db.clone(), killpill_rx.resubscribe()) + .await; + } + } + Err(err) => { + tracing::error!("Error fetching mqtt triggers: {:?}", err); + } + }; + + let mqtt_triggers_capture = sqlx::query_as!( + CaptureConfigForMqttTrigger, + r#" + SELECT + path, + is_flow, + workspace_id, + owner, + email, + trigger_config as "trigger_config!: _" + FROM + capture_config + WHERE + trigger_kind = 'mqtt' AND + last_client_ping > NOW() - INTERVAL '10 seconds' AND + trigger_config IS NOT NULL AND + (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') + "# + ) + .fetch_all(db) + .await; + + match mqtt_triggers_capture { + Ok(mut captures) => { + captures.shuffle(&mut rand::rng()); + for capture in captures { + capture + .try_to_listen_to_mqtt_messages(db.clone(), killpill_rx.resubscribe()) + .await; + } + } + Err(err) => { + tracing::error!("Error fetching captures mqtt triggers: {:?}", err); + } + }; +} + +pub fn start_mqtt_consumer(db: DB, mut killpill_rx: tokio::sync::broadcast::Receiver<()>) { + tokio::spawn(async move { + listen_to_unlistened_mqtt_events(&db, &killpill_rx).await; + loop { + tokio::select! { + biased; + _ = killpill_rx.recv() => { + return; + } + _ = tokio::time::sleep(tokio::time::Duration::from_secs(15)) => { + listen_to_unlistened_mqtt_events(&db, &killpill_rx).await + } + } + } + }); +} diff --git a/backend/windmill-api/src/postgres_triggers/trigger.rs b/backend/windmill-api/src/postgres_triggers/trigger.rs index 5d84cbdba2..81f439d0ae 100644 --- a/backend/windmill-api/src/postgres_triggers/trigger.rs +++ b/backend/windmill-api/src/postgres_triggers/trigger.rs @@ -11,7 +11,8 @@ use crate::{ }, run_job, }, - users::fetch_api_authed, resources::try_get_resource_from_db_as, + resources::try_get_resource_from_db_as, + users::fetch_api_authed, }; use bytes::{BufMut, Bytes, BytesMut}; use chrono::TimeZone; @@ -878,8 +879,8 @@ impl CaptureConfigForPostgresTrigger { args: Option>>, extra: Option>>, ) -> () { - let args = PushArgsOwned { args: args.unwrap_or_default(), extra }; - let extra = args.extra.as_ref().map(to_raw_value); + let args = PushArgsOwned { args: args.unwrap_or_default(), extra: None }; + let extra = extra.as_ref().map(to_raw_value); if let Err(err) = insert_capture_payload( db, &self.workspace_id, diff --git a/backend/windmill-api/src/resources.rs b/backend/windmill-api/src/resources.rs index f41a2b8e5d..2a3331ef9a 100644 --- a/backend/windmill-api/src/resources.rs +++ b/backend/windmill-api/src/resources.rs @@ -1208,10 +1208,7 @@ async fn update_resource_type( Ok(format!("resource_type {} updated", name)) } -#[cfg(any( - feature = "postgres_trigger", - all(feature = "sqs_trigger", feature = "enterprise") -))] +#[cfg(any(feature = "postgres_trigger", feature = "mqtt_trigger", all(feature = "sqs_trigger", feature = "enterprise")))] pub async fn try_get_resource_from_db_as( authed: ApiAuthed, user_db: Option, diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 0fd80d0fe4..d1c6e43902 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -18,7 +18,6 @@ use crate::{ webhook_util::{WebhookMessage, WebhookShared}, HTTP_CLIENT, }; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use axum::extract::Multipart; use axum::{ @@ -42,7 +41,6 @@ use std::{ use windmill_audit::audit_ee::audit_log; use windmill_audit::ActionKind; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use windmill_common::error::to_anyhow; use windmill_common::{ @@ -358,12 +356,6 @@ fn hash_script(ns: &NewScript) -> i64 { dh.finish() as i64 } -#[cfg(not(all(feature = "enterprise", feature = "parquet")))] -async fn create_snapshot_script() -> Result<(StatusCode, String)> { - Err(Error::BadRequest("Upgrade to EE to use bundle".to_string())) -} - -#[cfg(all(feature = "enterprise", feature = "parquet"))] async fn create_snapshot_script( authed: ApiAuthed, Extension(user_db): Extension, @@ -404,21 +396,48 @@ async fn create_snapshot_script( })?; uploaded = true; - if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let object_store = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS .read() .await - .clone() + .clone(); + + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let object_store: Option<()> = None; + + if &windmill_common::utils::MODE_AND_ADDONS.mode + == &windmill_common::utils::Mode::Standalone + && object_store.is_none() { - let path = windmill_common::s3_helpers::bundle(&w_id, &hash); - if let Err(e) = os - .put(&object_store::path::Path::from(path.clone()), data.into()) - .await - { - tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); - return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); - } + std::fs::create_dir_all( + windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR.clone(), + )?; + windmill_common::worker::write_file( + &windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR, + &hash, + &String::from_utf8_lossy(&data), + )?; } else { - return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + { + return Err(Error::ExecutionErr("codebase is an EE feature".to_string())); + } + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if let Some(os) = object_store { + let path = windmill_common::s3_helpers::bundle(&w_id, &hash); + + if let Err(e) = os + .put(&object_store::path::Path::from(path.clone()), data.into()) + .await + { + tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); + return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); + } + } else { + return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + } } } // println!("Length of `{}` is {} bytes", name, data.len()); diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index 3760fafb61..b8cac903b6 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -55,7 +55,6 @@ use windmill_common::{ utils::{not_found_if_none, rd_string, require_admin, Pagination, StripPath}, }; use windmill_git_sync::handle_deployment_metadata; -pub const TTL_TOKEN_DB_H: u32 = 72; const COOKIE_PATH: &str = "/"; @@ -606,6 +605,13 @@ async fn list_invites( Ok(Json(rows)) } +lazy_static::lazy_static! { + static ref INVALIDATE_ALL_SESSIONS_ON_LOGOUT: bool = std::env::var("INVALIDATE_ALL_SESSIONS_ON_LOGOUT") + .unwrap_or("false".to_string()) + .parse::() + .unwrap_or(false); +} + #[derive(Deserialize)] struct LogoutQuery { rd: Option, @@ -623,15 +629,36 @@ async fn logout( } cookies.remove(cookie); let mut tx = db.begin().await?; - let email = sqlx::query_scalar!("DELETE FROM token WHERE token = $1 RETURNING email", token) + + let email = if *INVALIDATE_ALL_SESSIONS_ON_LOGOUT { + sqlx::query_scalar!( + "WITH email_lookup AS ( + SELECT email FROM token WHERE token = $1 + ) + DELETE FROM token + WHERE email = (SELECT email FROM email_lookup) AND label = 'session' + RETURNING email", + token + ) .fetch_optional(&mut *tx) - .await?; + .await? + } else { + sqlx::query_scalar!("DELETE FROM token WHERE token = $1 RETURNING email", token) + .fetch_optional(&mut *tx) + .await? + }; + if let Some(email) = email { let email = email.unwrap_or("noemail".to_string()); + let audit_message = if *INVALIDATE_ALL_SESSIONS_ON_LOGOUT { + "users.logout_all" + } else { + "users.logout" + }; audit_log( &mut *tx, &AuditAuthor { email: email.clone(), username: email, username_override: None }, - "users.logout", + audit_message, ActionKind::Delete, "global", Some(&truncate_token(&token)), @@ -1696,6 +1723,11 @@ async fn refresh_token( Ok("token refreshed".to_string()) } +lazy_static::lazy_static! { + static ref MAX_SESSION_VALIDITY_SECONDS: i64 = std::env::var("MAX_SESSION_VALIDITY_SECONDS").ok().unwrap_or_else(|| String::new()).parse::().unwrap_or(3 * 24 * 60 * 60); + static ref INVALIDATE_OLD_SESSIONS: bool = std::env::var("INVALIDATE_OLD_SESSIONS").ok().unwrap_or_else(|| String::new()).parse::().unwrap_or(false); +} + pub async fn create_session_token<'c>( email: &str, super_admin: bool, @@ -1703,18 +1735,45 @@ pub async fn create_session_token<'c>( cookies: Cookies, ) -> Result { let token = rd_string(32); + + if *INVALIDATE_OLD_SESSIONS { + sqlx::query!( + "DELETE FROM token WHERE email = $1 AND label = 'session'", + email + ) + .execute(&mut **tx) + .await?; + + audit_log( + &mut **tx, + &AuditAuthor { + email: email.to_string(), + username: email.to_string(), + username_override: None, + }, + "users.token.invalidate_old_sessions", + ActionKind::Delete, + &"global", + None, + None, + ) + .instrument(tracing::info_span!("token", email)) + .await?; + } + sqlx::query!( "INSERT INTO token (token, email, label, expiration, super_admin) - VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)", + VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)", token, email, "session", - TTL_TOKEN_DB_H.to_string(), + &MAX_SESSION_VALIDITY_SECONDS.to_string(), super_admin ) .execute(&mut **tx) .await?; + let mut cookie = Cookie::new(COOKIE_NAME, token.clone()); cookie.set_secure(IS_SECURE.read().await.clone()); cookie.set_same_site(Some(tower_cookies::cookie::SameSite::Lax)); @@ -1725,7 +1784,7 @@ pub async fn create_session_token<'c>( } let mut expire: OffsetDateTime = time::OffsetDateTime::now_utc(); - expire += time::Duration::days(3); + expire += time::Duration::seconds(*MAX_SESSION_VALIDITY_SECONDS); cookie.set_expires(expire); cookies.add(cookie); Ok(token) @@ -1845,7 +1904,7 @@ async fn list_tokens( sqlx::query_as!( TruncatedToken, "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, \ - last_used_at, scopes FROM token WHERE email = $1 AND label != 'ephemeral-script' + last_used_at, scopes FROM token WHERE email = $1 AND (label != 'ephemeral-script' OR label IS NULL) ORDER BY created_at DESC LIMIT $2 OFFSET $3", email, per_page as i64, @@ -2478,6 +2537,22 @@ async fn update_username_in_workpsace<'c>( .execute(&mut **tx) .await?; + sqlx::query!( + r#"UPDATE flow_workspace_runnables SET flow_path = REGEXP_REPLACE(flow_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE flow_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#, + new_username, + old_username, + w_id + ).execute(&mut **tx) + .await?; + + sqlx::query!( + r#"UPDATE flow_workspace_runnables SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#, + new_username, + old_username, + w_id + ).execute(&mut **tx) + .await?; + sqlx::query!( r#"UPDATE flow_node SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#, new_username, diff --git a/backend/windmill-api/src/utils.rs b/backend/windmill-api/src/utils.rs index 439944e2b2..1da91ae448 100644 --- a/backend/windmill-api/src/utils.rs +++ b/backend/windmill-api/src/utils.rs @@ -29,6 +29,13 @@ pub struct WithStarredInfoQuery { pub with_starred_info: Option, } +#[derive(Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum RunnableKind { + Script, + Flow, +} + pub async fn require_super_admin(db: &DB, email: &str) -> error::Result<()> { let is_admin = is_super_admin_email(db, email).await?; diff --git a/backend/windmill-api/src/variables.rs b/backend/windmill-api/src/variables.rs index 475fe7db98..425c4d86fc 100644 --- a/backend/windmill-api/src/variables.rs +++ b/backend/windmill-api/src/variables.rs @@ -19,6 +19,7 @@ use axum::{ }; use hyper::StatusCode; use serde_json::Value; + use windmill_audit::audit_ee::{audit_log, AuditAuthorable}; use windmill_audit::ActionKind; use windmill_common::{ diff --git a/backend/windmill-api/src/webhook_util.rs b/backend/windmill-api/src/webhook_util.rs index 652e3b4a83..ecf87fcc39 100644 --- a/backend/windmill-api/src/webhook_util.rs +++ b/backend/windmill-api/src/webhook_util.rs @@ -25,6 +25,8 @@ lazy_static::lazy_static! { pub static ref INSTANCE_EVENTS_WEBHOOK: Option = std::env::var("INSTANCE_EVENTS_WEBHOOK").ok(); + pub static ref WEBHOOK_CACHE: Cache> = Cache::new(100); + } pub enum WebhookPayload { @@ -76,7 +78,6 @@ impl WebhookShared { .timeout(Duration::from_secs(5)) .build() .unwrap(); - let cache = Cache::new(100); loop { select! { @@ -84,12 +85,12 @@ impl WebhookShared { _ = shutdown_rx.recv() => break, r = rx.recv() => match r { Some(WebhookPayload::WorkspaceEvent(workspace_id, message)) => { - let webhook_opt = match cache.get(&workspace_id) { + let webhook_opt = match WEBHOOK_CACHE.get(&workspace_id) { Some(guard) => { guard }, None => { - let Ok(webook_opt) = + let Ok(mut webhook_opt) = sqlx::query_scalar!( "SELECT webhook FROM workspace_settings WHERE workspace_id = $1", workspace_id @@ -101,13 +102,17 @@ impl WebhookShared { tracing::error!("Webhook Message to send - but cannot get workspace settings! Workspace: {workspace_id}"); continue; }; - cache.insert(workspace_id, webook_opt.clone()); - webook_opt + if webhook_opt.as_ref().is_some_and(|x| x.is_empty()) { + webhook_opt = None; + } + WEBHOOK_CACHE.insert(workspace_id, webhook_opt.clone()); + webhook_opt } }; if let Some(url) = webhook_opt { #[cfg(feature = "prometheus")] let timer = if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) { Some(WEBHOOK_REQUEST_COUNT.start_timer()) } else { None }; + tracing::info!("Sending webhook message to {}", url); let _ = client.post(url).json(&message).send().await; #[cfg(feature = "prometheus")] timer.map(|x| x.stop_and_record()); diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index b0f59cfd51..c83370bfb5 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -459,7 +459,6 @@ async fn get_settings( .fetch_one(&mut *tx) .await .map_err(|e| Error::internal_err(format!("getting settings: {e:#}")))?; - tx.commit().await?; Ok(Json(settings)) } @@ -796,7 +795,7 @@ async fn get_copilot_info( let (ai_provider, exists_ai_resource) = if let Some(ai_resource) = record.ai_resource { let ai_resource = serde_json::from_value::(ai_resource)?; - (Some(ai_resource.provider), true) + (Some(ai_resource.provider), ai_resource.path.is_some()) } else { (None, false) }; @@ -926,7 +925,7 @@ async fn edit_git_sync_config( Ok(format!("Edit git sync config for workspace {}", &w_id)) } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct EditDeployUIConfig { #[cfg(feature = "enterprise")] deploy_ui_settings: Option, @@ -954,7 +953,6 @@ async fn edit_deploy_ui_config( require_admin(is_admin, &username)?; let mut tx = db.begin().await?; - let args_for_audit = format!("{:?}", new_config.deploy_ui_settings); audit_log( &mut *tx, @@ -1363,7 +1361,8 @@ struct UsedTriggers { pub kafka_used: bool, pub nats_used: bool, pub postgres_used: bool, - pub sqs_used: bool, + pub mqtt_used: bool, + pub sqs_used: bool } async fn get_used_triggers( @@ -1378,11 +1377,11 @@ async fn get_used_triggers( SELECT EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS "websocket_used!", - EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS "http_routes_used!", EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as "kafka_used!", EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as "nats_used!", EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS "postgres_used!", + EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS "mqtt_used!", EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS "sqs_used!" "#, w_id @@ -1447,7 +1446,12 @@ async fn user_workspaces( Ok(Json(WorkspaceList { email, workspaces })) } -async fn check_name_conflict<'c>(tx: &mut Transaction<'c, Postgres>, w_id: &str) -> Result<()> { +pub async fn check_w_id_conflict<'c>(tx: &mut Transaction<'c, Postgres>, w_id: &str) -> Result<()> { + if w_id == "global" { + return Err(windmill_common::error::Error::BadRequest( + "'global' is not allowed as a workspace ID".to_string(), + )); + } let exists = sqlx::query_scalar!("SELECT EXISTS(SELECT 1 FROM workspace WHERE id = $1)", w_id) .fetch_one(&mut **tx) .await? @@ -1505,7 +1509,7 @@ async fn create_workspace( let mut tx: Transaction<'_, Postgres> = db.begin().await?; - check_name_conflict(&mut tx, &nw.id).await?; + check_w_id_conflict(&mut tx, &nw.id).await?; sqlx::query!( "INSERT INTO workspace (id, name, owner) diff --git a/backend/windmill-api/src/workspaces_extra.rs b/backend/windmill-api/src/workspaces_extra.rs index 2378e85c02..2f77112331 100644 --- a/backend/windmill-api/src/workspaces_extra.rs +++ b/backend/windmill-api/src/workspaces_extra.rs @@ -1,6 +1,6 @@ use crate::db::ApiAuthed; -use crate::workspaces::CREATE_WORKSPACE_REQUIRE_SUPERADMIN; +use crate::workspaces::{check_w_id_conflict, CREATE_WORKSPACE_REQUIRE_SUPERADMIN}; use crate::{db::DB, utils::require_super_admin}; use axum::{ @@ -46,20 +46,7 @@ pub(crate) async fn change_workspace_id( let mut tx = db.begin().await?; - let workspace_conflict = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM workspace WHERE id = $1)", - &rw.new_id, - ) - .fetch_one(&mut *tx) - .await? - .unwrap_or(false); - - if workspace_conflict { - return Err(Error::BadRequest(format!( - "workspace id {} already used", - &rw.new_id - ))); - } + check_w_id_conflict(&mut tx, &rw.new_id).await?; // duplicate workspace with new id name sqlx::query!( @@ -202,6 +189,14 @@ pub(crate) async fn change_workspace_id( .execute(&mut *tx) .await?; + sqlx::query!( + "UPDATE flow_workspace_runnables SET workspace_id = $1 WHERE workspace_id = $2", + &rw.new_id, + &old_id + ) + .execute(&mut *tx) + .await?; + sqlx::query!( "UPDATE flow_node SET workspace_id = $1 WHERE workspace_id = $2", &rw.new_id, diff --git a/backend/windmill-audit/src/lib.rs b/backend/windmill-audit/src/lib.rs index b6522186ca..10894798fb 100644 --- a/backend/windmill-audit/src/lib.rs +++ b/backend/windmill-audit/src/lib.rs @@ -34,4 +34,5 @@ pub struct ListAuditLogQuery { pub resource: Option, pub before: Option>, pub after: Option>, + pub all_workspaces: Option, } diff --git a/backend/windmill-common/Cargo.toml b/backend/windmill-common/Cargo.toml index 8f1bc299ea..f19ef1b06c 100644 --- a/backend/windmill-common/Cargo.toml +++ b/backend/windmill-common/Cargo.toml @@ -8,6 +8,7 @@ edition.workspace = true default = [] enterprise = [] jemalloc = ["dep:tikv-jemalloc-ctl"] +tantivy = [] prometheus = ["dep:prometheus"] loki = ["dep:tracing-loki"] benchmark = [] @@ -61,6 +62,8 @@ async-stream.workspace = true const_format.workspace = true crc.workspace = true windmill-macros.workspace = true +jsonwebtoken.workspace = true +backon.workspace = true semver.workspace = true croner = "2.0.6" diff --git a/backend/windmill-common/src/auth.rs b/backend/windmill-common/src/auth.rs index 52a966b105..451157b115 100644 --- a/backend/windmill-common/src/auth.rs +++ b/backend/windmill-common/src/auth.rs @@ -1,6 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::sync::Arc; -use tokio::sync::RwLock; use crate::{ db::Authed, @@ -9,10 +7,6 @@ use crate::{ DB, }; -lazy_static::lazy_static! { - pub static ref JWT_SECRET : Arc> = Arc::new(RwLock::new("".to_string())); -} - #[derive(Deserialize, Serialize)] pub struct JWTAuthClaims { pub email: String, diff --git a/backend/windmill-common/src/db.rs b/backend/windmill-common/src/db.rs index a669c36173..96be430e73 100644 --- a/backend/windmill-common/src/db.rs +++ b/backend/windmill-common/src/db.rs @@ -59,6 +59,10 @@ impl Authable for Authed { } } +lazy_static::lazy_static! { + pub static ref PG_SCHEMA: Option = std::env::var("PG_SCHEMA").ok(); +} + impl UserDB { pub fn new(db: DB) -> Self { Self { db } @@ -95,6 +99,12 @@ impl UserDB { .execute(&mut *tx) .await?; + if let Some(schema) = PG_SCHEMA.as_ref() { + sqlx::query(&format!("SET LOCAL search_path TO {}", schema)) + .execute(&mut *tx) + .await?; + } + sqlx::query!( "SELECT set_config('session.user', $1, true)", authed.username() diff --git a/backend/windmill-common/src/global_settings.rs b/backend/windmill-common/src/global_settings.rs index 31d8214e0c..816a99486e 100644 --- a/backend/windmill-common/src/global_settings.rs +++ b/backend/windmill-common/src/global_settings.rs @@ -40,7 +40,7 @@ pub const JWT_SECRET_SETTING: &str = "jwt_secret"; pub const EMAIL_DOMAIN_SETTING: &str = "email_domain"; pub const OTEL_SETTING: &str = "otel"; -pub const ENV_SETTINGS: [&str; 56] = [ +pub const ENV_SETTINGS: [&str; 57] = [ "DISABLE_NSJAIL", "MODE", "NUM_WORKERS", @@ -97,6 +97,7 @@ pub const ENV_SETTINGS: [&str; 56] = [ "OTEL_TRACING", "OTEL_LOGS", "DISABLE_S3_STORE", + "PG_SCHEMA", ]; use crate::error; diff --git a/backend/windmill-common/src/jwt.rs b/backend/windmill-common/src/jwt.rs new file mode 100644 index 0000000000..be93ed6614 --- /dev/null +++ b/backend/windmill-common/src/jwt.rs @@ -0,0 +1,42 @@ +use crate::error::{self, to_anyhow, Error}; +use serde::{de::DeserializeOwned, Serialize}; +use std::sync::Arc; +use tokio::sync::RwLock; + +lazy_static::lazy_static! { + pub static ref JWT_SECRET: Arc> = Arc::new(RwLock::new("".to_string())); +} + +pub async fn encode_with_internal_secret(claims: T) -> error::Result { + let jwt_secret = JWT_SECRET.read().await; + + if jwt_secret.is_empty() { + return Err(Error::internal_err("JWT secret is not set".to_string())); + } + + let token = jsonwebtoken::encode( + &jsonwebtoken::Header::new(jsonwebtoken::Algorithm::HS256), + &claims, + &jsonwebtoken::EncodingKey::from_secret(jwt_secret.as_bytes()), + ) + .map_err(to_anyhow)?; + + Ok(token) +} + +pub async fn decode_with_internal_secret(token: &str) -> error::Result { + let jwt_secret = JWT_SECRET.read().await; + + if jwt_secret.is_empty() { + return Err(Error::internal_err("JWT secret is not set".to_string())); + } + + let result = jsonwebtoken::decode::( + token, + &jsonwebtoken::DecodingKey::from_secret(jwt_secret.as_bytes()), + &jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256), + ) + .map_err(to_anyhow)?; + + Ok(result.claims) +} diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index 1bbc890728..9eeeec3cdf 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -33,6 +33,7 @@ pub mod job_metrics; #[cfg(feature = "parquet")] pub mod job_s3_helpers_ee; pub mod jobs; +pub mod jwt; pub mod more_serde; pub mod oauth2; pub mod otel_ee; diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index 9d896e7f5b..5658d505b6 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -19,6 +19,8 @@ use crate::{ use crate::worker::HUB_CACHE_DIR; use anyhow::Context; +use backon::ConstantBuilder; +use backon::{BackoffBuilder, Retryable}; use serde::de::Error as _; use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize}; @@ -415,6 +417,8 @@ pub async fn get_hub_script_by_path( Some(db), ) .await? + .error_for_status() + .map_err(to_anyhow)? .text() .await .map_err(to_anyhow); @@ -440,6 +444,8 @@ pub async fn get_hub_script_by_path( Some(db), ) .await? + .error_for_status() + .map_err(to_anyhow)? .text() .await .map_err(to_anyhow)?; @@ -494,49 +500,67 @@ async fn get_full_hub_script_by_path_inner( ) -> crate::error::Result { let hub_base_url = HUB_BASE_URL.read().await.clone(); - let result = http_get_from_hub( - http_client, - &format!("{}/raw2/{}", hub_base_url, path), - true, - None, - db, - ) - .await? - .json::() - .await - .context("Decoding hub response to script"); + let response = (|| async { + let response = http_get_from_hub( + http_client, + &format!("{}/raw2/{}", hub_base_url, path), + true, + None, + db, + ) + .await + .and_then(|r| r.error_for_status().map_err(|e| to_anyhow(e).into())); - match result { - Ok(result) => Ok(result), - Err(e) => { - if hub_base_url != DEFAULT_HUB_BASE_URL - && path - .split("/") - .next() - .is_some_and(|x| x.parse::().is_ok_and(|x| x < 10_000_000)) - { - tracing::info!( - "Not found on private hub, fallback to default hub for {}", - path - ); - let value = http_get_from_hub( - http_client, - &format!("{}/raw2/{}", DEFAULT_HUB_BASE_URL, path), - true, - None, - db, - ) - .await? - .json::() - .await - .context("Decoding hub response to script")?; - - Ok(value) - } else { - Err(e)? + match response { + Ok(response) => Ok(response), + Err(e) => { + if hub_base_url != DEFAULT_HUB_BASE_URL + && path + .split("/") + .next() + .is_some_and(|x| x.parse::().is_ok_and(|x| x < 10_000_000)) + { + // TODO: should only fallback to default hub if status is 404 (hub returns 500 currently) + tracing::info!( + "Not found on private hub, fallback to default hub for {}", + path + ); + http_get_from_hub( + http_client, + &format!("{}/raw2/{}", DEFAULT_HUB_BASE_URL, path), + true, + None, + db, + ) + .await? + .error_for_status() + .map_err(|e| to_anyhow(e).into()) + } else { + Err(e) + } } } - } + }) + .retry( + ConstantBuilder::default() + .with_delay(std::time::Duration::from_secs(5)) + .with_max_times(2) + .build(), + ) + .notify(|err, dur| { + tracing::warn!( + "Could not get hub script at path {path}, retrying in {dur:#?}, err: {err:#?}" + ); + }) + .sleep(tokio::time::sleep) + .await?; + + let script = response + .json::() + .await + .context(format!("Decoding hub response for script at path {path}"))?; + + Ok(script) } #[derive(Deserialize, Serialize)] diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 4295a0edaa..539064fcf1 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -53,6 +53,76 @@ lazy_static::lazy_static! { GIT_VERSION } ).unwrap_or(Version::new(0, 1, 0)); + + + pub static ref MODE_AND_ADDONS: ModeAndAddons = { + let mut search_addon = false; + let mode = std::env::var("MODE") + .map(|x| x.to_lowercase()) + .map(|x| { + if &x == "server" { + println!("Binary is in 'server' mode"); + Mode::Server + } else if &x == "worker" { + tracing::info!("Binary is in 'worker' mode"); + #[cfg(windows)] + { + println!("It is highly recommended to use the agent mode instead on windows (MODE=agent) and to pass a BASE_INTERNAL_URL"); + } + Mode::Worker + } else if &x == "agent" { + println!("Binary is in 'agent' mode"); + if std::env::var("BASE_INTERNAL_URL").is_err() { + panic!("BASE_INTERNAL_URL is required in agent mode") + } + if std::env::var("JOB_TOKEN").is_err() { + println!("JOB_TOKEN is not passed, hence workers will still need to create permissions for each job and the DATABASE_URL needs to be of a role that can INSERT into the job_perms table") + } + + #[cfg(not(feature = "enterprise"))] + { + panic!("Agent mode is only available in the EE, ignoring..."); + } + #[cfg(feature = "enterprise")] + Mode::Agent + } else if &x == "indexer" { + tracing::info!("Binary is in 'indexer' mode"); + #[cfg(not(feature = "tantivy"))] + { + eprintln!("Cannot start the indexer because tantivy is not included in this binary/image. Make sure you are using the EE image if you want to access the full text search features."); + panic!("Indexer mode requires compiling with the tantivy feature flag."); + } + #[cfg(feature = "tantivy")] + Mode::Indexer + } else if &x == "standalone+search"{ + search_addon = true; + println!("Binary is in 'standalone' mode with search enabled"); + Mode::Standalone + } + else { + if &x != "standalone" { + eprintln!("mode not recognized, defaulting to standalone: {x}"); + } else { + println!("Binary is in 'standalone' mode"); + } + Mode::Standalone + } + }) + .unwrap_or_else(|_| { + tracing::info!("Mode not specified, defaulting to standalone"); + Mode::Standalone + }); + ModeAndAddons { + indexer: search_addon, + mode, + } + }; +} + +#[derive(Clone)] +pub struct ModeAndAddons { + pub indexer: bool, + pub mode: Mode, } #[derive(Deserialize, Clone)] diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs index f07226265a..b2429f5ae0 100644 --- a/backend/windmill-common/src/worker.rs +++ b/backend/windmill-common/src/worker.rs @@ -1,4 +1,5 @@ use anyhow::anyhow; +use bytes::Bytes; use const_format::concatcp; use itertools::Itertools; use regex::Regex; @@ -102,8 +103,12 @@ lazy_static::lazy_static! { // Features flags: pub static ref DISABLE_FLOW_SCRIPT: bool = std::env::var("DISABLE_FLOW_SCRIPT").ok().is_some_and(|x| x == "1" || x == "true"); + + pub static ref ROOT_STANDALONE_BUNDLE_DIR: String = format!("{}/.windmill/standalone_bundle/", std::env::var("HOME").unwrap_or_else(|_| "/root".to_string())); } +pub const ROOT_CACHE_NOMOUNT_DIR: &str = concatcp!(TMP_DIR, "/cache_nomount/"); + pub static MIN_VERSION_IS_LATEST: AtomicBool = AtomicBool::new(false); fn format_pull_query(peek: String) -> String { @@ -208,6 +213,13 @@ pub fn write_file(dir: &str, path: &str, content: &str) -> error::Result { Ok(file) } +pub fn write_file_bytes(dir: &str, path: &str, content: &Bytes) -> error::Result { + let path = format!("{}/{}", dir, path); + let mut file = File::create(&path)?; + file.write_all(content)?; + file.flush()?; + Ok(file) +} /// from : https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61 fn normalize_path(path: &Path) -> PathBuf { let mut components = path.components().peekable(); diff --git a/backend/windmill-common/src/workspaces.rs b/backend/windmill-common/src/workspaces.rs index d674285479..8fd5471a00 100644 --- a/backend/windmill-common/src/workspaces.rs +++ b/backend/windmill-common/src/workspaces.rs @@ -27,6 +27,7 @@ pub enum ObjectType { ResourceType, User, Group, + Trigger } #[derive(Serialize, Deserialize, Debug)] diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index e582fa39dc..c676d1e6f7 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -247,7 +247,7 @@ pub async fn cancel_job<'c>( while !jobs.is_empty() { let p_job = jobs.pop(); let new_jobs = sqlx::query_scalar!( - "SELECT id AS \"id!\" FROM v2_job WHERE parent_job = $1 AND workspace_id = $2", + "SELECT id AS \"id!\" FROM v2_job_queue INNER JOIN v2_job USING (id) WHERE parent_job = $1 AND v2_job.workspace_id = $2", p_job, w_id ) diff --git a/backend/windmill-queue/src/schedule.rs b/backend/windmill-queue/src/schedule.rs index 820c849a63..af1fca6f5d 100644 --- a/backend/windmill-queue/src/schedule.rs +++ b/backend/windmill-queue/src/schedule.rs @@ -64,13 +64,12 @@ pub async fn push_scheduled_job<'c>( }; let next = sched.find_next(&starting_from); - // println!("next event ({:?}): {}", tz, next); // println!("next event(UTC): {}", next.with_timezone(&chrono::Utc)); // Scheduled events must be stored in the database in UTC let next = next.with_timezone(&chrono::Utc); - + // panic!("next: {}", next); let already_exists: bool = sqlx::query_scalar!( // Query plan: // - use of the `ix_v2_job_root_by_path` index; hence the `parent_job IS NULL` clause. diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index ede17da758..29ffbc3044 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -43,7 +43,7 @@ use windmill_common::{ get_latest_hash_for_path, jobs::QueuedJob, scripts::ScriptLang, - worker::{exists_in_cache, save_cache, write_file}, + worker::{exists_in_cache, save_cache, write_file, DISABLE_BUNDLING}, DB, }; @@ -579,60 +579,96 @@ pub async fn generate_bun_bundle( Ok(()) } -#[cfg(all(feature = "enterprise", feature = "parquet"))] pub async fn pull_codebase(w_id: &str, id: &str, job_dir: &str) -> Result<()> { - use crate::global_cache::extract_tar; - let path = windmill_common::s3_helpers::bundle(&w_id, &id); - let bun_cache_path = format!("{}/{}", crate::ROOT_CACHE_NOMOUNT_DIR, path); + let bun_cache_path = format!( + "{}/{}", + windmill_common::worker::ROOT_CACHE_NOMOUNT_DIR, + path + ); let is_tar = id.ends_with(".tar"); let dst = format!( "{job_dir}/{}", if is_tar { "codebase.tar" } else { "main.js" } ); - let dirs_splitted = bun_cache_path.split("/").collect_vec(); - tokio::fs::create_dir_all(dirs_splitted[..dirs_splitted.len() - 1].join("/")).await?; - if tokio::fs::metadata(&bun_cache_path).await.is_ok() { + + if std::fs::metadata(&bun_cache_path).is_ok() { tracing::info!("loading {bun_cache_path} from cache"); - if is_tar { - extract_tar(fs::read(bun_cache_path)?.into(), job_dir).await?; + + extract_saved_codebase(job_dir, &bun_cache_path, is_tar, &dst, false)?; + } else { + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let object_store = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone(); + + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let object_store: Option<()> = None; + + if &windmill_common::utils::MODE_AND_ADDONS.mode + == &windmill_common::utils::Mode::Standalone + && object_store.is_none() + { + let bun_cache_path = format!( + "{}{}", + *windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR, + id + ); + if std::fs::metadata(&bun_cache_path).is_ok() { + tracing::info!("loading {bun_cache_path} from standalone bundle cache"); + extract_saved_codebase(job_dir, &bun_cache_path, is_tar, &dst, true)?; + } else { + return Err(error::Error::ExecutionErr(format!( + "(standalone bundle test mode) could not find codebase at {bun_cache_path}" + ))); + } } else { - #[cfg(unix)] - tokio::fs::symlink(&bun_cache_path, dst).await?; + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + return Err(error::Error::ExecutionErr( + "codebase is an EE feature".to_string(), + )); - #[cfg(windows)] - std::os::windows::fs::symlink_dir(&bun_cache_path, &dst)?; + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if let Some(os) = object_store { + let dirs_splitted = bun_cache_path.split("/").collect_vec(); + std::fs::create_dir_all(dirs_splitted[..dirs_splitted.len() - 1].join("/"))?; + + let bytes = attempt_fetch_bytes(os, &path).await?; + tracing::info!("loading {bun_cache_path} from object store"); + + std::fs::write(&bun_cache_path, &bytes)?; + extract_saved_codebase(job_dir, &bun_cache_path, is_tar, &dst, false)?; + } } - } else if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS - .read() - .await - .clone() - { - let bytes = attempt_fetch_bytes(os, &path).await?; - - tokio::fs::write(&bun_cache_path, &bytes).await?; - if is_tar { - extract_tar(bytes, job_dir).await?; - } else { - #[cfg(unix)] - tokio::fs::symlink(bun_cache_path, dst).await?; - - #[cfg(windows)] - std::os::windows::fs::symlink_dir(&bun_cache_path, &dst)?; - } - - // extract_tar(bytes, job_dir).await?; } - return Ok(()); + Ok(()) } -#[cfg(not(all(feature = "enterprise", feature = "parquet")))] -pub async fn pull_codebase(_w_id: &str, _id: &str, _job_dir: &str) -> Result<()> { - return Err(error::Error::ExecutionErr( - "codebase is an EE feature".to_string(), - )); +fn extract_saved_codebase( + job_dir: &str, + bun_cache_path: &String, + is_tar: bool, + dst: &str, + copy: bool, +) -> Result<()> { + use crate::global_cache::extract_tar; + + Ok(if is_tar { + extract_tar(fs::read(bun_cache_path)?.into(), job_dir)?; + } else { + if copy { + std::fs::copy(bun_cache_path, dst)?; + } else { + #[cfg(unix)] + std::os::unix::fs::symlink(bun_cache_path, dst)?; + + #[cfg(windows)] + std::os::windows::fs::symlink_dir(bun_cache_path, dst)?; + } + }) } pub async fn prebundle_bun_script( @@ -660,7 +696,7 @@ pub async fn prebundle_bun_script( return Ok(()); } let annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content); - if annotation.nobundling { + if annotation.nobundling || *DISABLE_BUNDLING { return Ok(()); } let origin = format!("{job_dir}/main.js"); @@ -802,23 +838,25 @@ pub async fn handle_bun_job( ) -> error::Result> { let mut annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content); - let (mut has_bundle_cache, cache_logs, local_path, remote_path) = - if requirements_o.is_some() && !annotation.nobundling && codebase.is_none() { - let (local_path, remote_path) = compute_bundle_local_and_remote_path( - inner_content, - requirements_o, - job.script_path(), - Some(db.clone()), - &job.workspace_id, - ) - .await; + let (mut has_bundle_cache, cache_logs, local_path, remote_path) = if requirements_o.is_some() + && !annotation.nobundling + && !*DISABLE_BUNDLING + && codebase.is_none() + { + let (local_path, remote_path) = compute_bundle_local_and_remote_path( + inner_content, + requirements_o, + job.script_path(), + Some(db.clone()), + &job.workspace_id, + ) + .await; - let (cache, logs) = - windmill_common::worker::load_cache(&local_path, &remote_path).await; - (cache, logs, local_path, remote_path) - } else { - (false, "".to_string(), "".to_string(), "".to_string()) - }; + let (cache, logs) = windmill_common::worker::load_cache(&local_path, &remote_path).await; + (cache, logs, local_path, remote_path) + } else { + (false, "".to_string(), "".to_string(), "".to_string()) + }; if !codebase.is_some() && !has_bundle_cache { let _ = write_file(job_dir, "main.ts", inner_content)?; @@ -835,13 +873,6 @@ pub async fn handle_bun_job( let main_override = job.script_entrypoint_override.as_deref(); let apply_preprocessor = !job.is_flow_step && job.preprocessed == Some(false); - #[cfg(not(feature = "enterprise"))] - if annotation.nodejs || annotation.npm { - return Err(error::Error::ExecutionErr( - "Nodejs / npm mode is an EE feature".to_string(), - )); - } - if has_bundle_cache { let target; let symlink; @@ -1088,6 +1119,7 @@ try {{ let build_cache = !has_bundle_cache && !annotation.nobundling + && !*DISABLE_BUNDLING && !codebase.is_some() && (requirements_o.is_some() || annotation.native); diff --git a/backend/windmill-worker/src/global_cache.rs b/backend/windmill-worker/src/global_cache.rs index 3bc652d8be..23ce255615 100644 --- a/backend/windmill-worker/src/global_cache.rs +++ b/backend/windmill-worker/src/global_cache.rs @@ -1,15 +1,12 @@ // #[cfg(feature = "enterprise")] // use rand::Rng; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use tokio::time::Instant; +use windmill_common::error; #[cfg(all(feature = "enterprise", feature = "parquet", unix))] use object_store::ObjectStore; -#[cfg(all(feature = "enterprise", feature = "parquet"))] -use windmill_common::error; - #[cfg(all(feature = "enterprise", feature = "parquet", unix))] use std::sync::Arc; @@ -96,7 +93,7 @@ pub async fn pull_from_tar( let tar_path = format!("tar/{TARGET}/{python_xyz}/{folder_name}.tar"); let bytes = attempt_fetch_bytes(client, &tar_path).await?; - extract_tar(bytes, &folder).await.map_err(|e| { + extract_tar(bytes, &folder).map_err(|e| { tracing::error!("Failed to extract piptar {folder_name}. Error: {:?}", e); e })?; @@ -109,21 +106,20 @@ pub async fn pull_from_tar( Ok(()) } -#[cfg(all(feature = "enterprise", feature = "parquet"))] -pub async fn extract_tar(tar: bytes::Bytes, folder: &str) -> error::Result<()> { +pub fn extract_tar(tar: bytes::Bytes, folder: &str) -> error::Result<()> { use bytes::Buf; - use tokio::fs::{self}; let start: Instant = Instant::now(); - fs::create_dir_all(&folder).await?; + std::fs::create_dir_all(&folder)?; let mut ar = tar::Archive::new(tar.reader()); if let Err(e) = ar.unpack(folder) { tracing::info!("Failed to untar to {folder}. Error: {:?}", e); - fs::remove_dir_all(&folder).await?; + std::fs::remove_dir_all(&folder)?; return Err(error::Error::ExecutionErr(format!( - "Failed to untar tar {folder}" + "Failed to untar tar {folder}. Error: {:?}", + e ))); } tracing::info!( diff --git a/backend/windmill-worker/src/js_eval.rs b/backend/windmill-worker/src/js_eval.rs index 2aa8ae6403..ea6d3cf763 100644 --- a/backend/windmill-worker/src/js_eval.rs +++ b/backend/windmill-worker/src/js_eval.rs @@ -376,9 +376,10 @@ fn replace_with_await(expr: String, fn_name: &str) -> String { } lazy_static! { static ref RE: Regex = - Regex::new(r#"(?m)(?Presults(?:(?:\.[a-zA-Z_0-9]+)|(?:\[\".*?\"\])))"#).unwrap(); + Regex::new(r#"(?m)(?Presults(?:\?)?(?:(?:\.[a-zA-Z_0-9]+)|(?:\[\".*?\"\])))"#).unwrap(); static ref RE_FULL: Regex = - Regex::new(r"(?m)^results\.([a-zA-Z_0-9]+)(?:\[(\d+)\])?((?:\.[a-zA-Z_0-9]+)+)?$").unwrap(); + Regex::new(r"(?m)^results(?:\?)?\.([a-zA-Z_0-9]+)(?:\[(\d+)\])?((?:\.[a-zA-Z_0-9]+)+)?$") + .unwrap(); static ref RE_PROXY: Regex = Regex::new(r"^(https?)://(([^:@\s]+):([^:@\s]+)@)?([^:@\s]+)(:(\d+))?$").unwrap(); } diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index e98871daac..1180652d35 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -96,19 +96,23 @@ pub enum PyVersion { } impl PyVersion { - pub async fn from_instance_version() -> Self { - match INSTANCE_PYTHON_VERSION.read().await.clone() { + pub async fn from_instance_version(job_id: &Uuid, w_id: &str, db: &Pool) -> Self { + let mut err = None; + let pyv = match INSTANCE_PYTHON_VERSION.read().await.clone() { Some(v) => PyVersion::from_string_with_dots(&v).unwrap_or_else(|| { let v = PyVersion::default(); - tracing::error!( - "Cannot parse INSTANCE_PYTHON_VERSION ({:?}), fallback to latest_stable ({v:?})", - *INSTANCE_PYTHON_VERSION - ); + err = Some(format!("\nCannot parse INSTANCE_PYTHON_VERSION ({:?}), fallback to latest_stable ({v:?})", *INSTANCE_PYTHON_VERSION)); v }), // Use latest stable None => PyVersion::default(), + }; + + if let Some(msg) = err { + append_logs(job_id, w_id, &msg, db).await; + tracing::error!(msg); } + pyv } /// e.g.: `/tmp/windmill/cache/python_3xy` pub fn to_cache_dir(&self) -> String { @@ -223,9 +227,18 @@ impl PyVersion { if let Err(ref e) = res { tracing::error!( - "worker_name: {worker_name}, w_id: {w_id}, job_id: {job_id}\n + "worker_name: {worker_name}, w_id: {w_id}, job_id: {job_id}\n Error while getting python from uv, falling back to system python: {e:?}" ); + append_logs( + job_id, + w_id, + format!( + "\nError while getting python from uv, falling back to system python: {e:?}" + ), + db, + ) + .await; } res } @@ -315,6 +328,11 @@ impl PyVersion { .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), ); } @@ -346,6 +364,8 @@ impl PyVersion { let mut child_cmd = Command::new(uv_cmd); + child_cmd.env_clear(); + #[cfg(windows)] { child_cmd @@ -354,12 +374,16 @@ impl PyVersion { .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), ); } let output = child_cmd // .current_dir(job_dir) - .env_clear() .env("HOME", HOME_ENV.to_string()) .env("PATH", PATH_ENV.to_string()) .args([ @@ -595,6 +619,11 @@ pub async fn uv_pip_compile( child_cmd .env("SystemRoot", SYSTEM_ROOT.as_str()) .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ) .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), @@ -1110,6 +1139,11 @@ mount {{ { python_cmd.env("SystemRoot", SYSTEM_ROOT.as_str()); python_cmd.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); + python_cmd.env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ); } start_child_process(python_cmd, &python_path).await? @@ -1410,7 +1444,7 @@ async fn handle_python_deps( let mut annotated_pyv = None; let mut annotated_pyv_numeric = None; let is_deployed = requirements_o.is_some(); - let instance_pyv = PyVersion::from_instance_version().await; + let instance_pyv = PyVersion::from_instance_version(job_id, w_id, db).await; let annotations = windmill_common::worker::PythonAnnotations::parse(inner_content); let requirements = match requirements_o { Some(r) => r, @@ -1453,15 +1487,6 @@ async fn handle_python_deps( } }; - let requirements_lines: Vec<&str> = if requirements.len() > 0 { - requirements - .split("\n") - .filter(|x| !x.starts_with("--") && !x.trim().is_empty()) - .collect() - } else { - vec![] - }; - /* For deployed scripts we want to find out version in following order: 1. Assigned version (written in lockfile) @@ -1472,20 +1497,9 @@ async fn handle_python_deps( 2. Instance version 3. Latest Stable */ + let requirements_lines = split_requirements(requirements.as_str()); let final_version = if is_deployed { - // If script is deployed we can try to parse first line to get assigned version - if let Some(v) = requirements_lines - .get(0) - .and_then(|line| PyVersion::parse_version(line)) - { - // We have valid assigned version, we use it - v - } else { - // If there is no assigned version in lockfile we automatically fallback to 3.11 - // In this case we have dependencies, but no associated python version - // This is the case for old deployed scripts - PyVersion::Py311 - } + get_pyv_from_requirements_lines(&requirements_lines) } else { // This is not deployed script, meaning we test run it (Preview) annotated_pyv.unwrap_or(instance_pyv) @@ -1683,6 +1697,11 @@ async fn spawn_uv_install( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ) .args(&command_args[1..]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); @@ -2249,6 +2268,29 @@ pub async fn handle_python_reqs( }; } +fn split_requirements(requirements: &str) -> Vec<&str> { + requirements + .split("\n") + .filter(|x| !x.trim_start().starts_with("--") && !x.trim().is_empty()) + .collect() +} +/// Check requirements/lockfile to figure out python version assigned to it. +fn get_pyv_from_requirements_lines(requirements_lines: &[&str]) -> PyVersion { + // If script is deployed we can try to parse first line to get assigned version + if let Some(v) = requirements_lines + .get(0) + .and_then(|line| PyVersion::parse_version(*line)) + { + // We have valid assigned version, we use it + v + } else { + // If there is no assigned version in lockfile we automatically fallback to 3.11 + // In this case we have dependencies, but no associated python version + // This is the case for old deployed scripts + PyVersion::Py311 + } +} + #[cfg(feature = "enterprise")] use crate::JobCompletedSender; #[cfg(feature = "enterprise")] @@ -2419,7 +2461,13 @@ for line in sys.stdin: ); proc_envs.insert("BASE_URL".to_string(), base_internal_url.to_string()); - let py_version = PyVersion::from_instance_version().await; + let py_version = if let Some(requirements) = requirements_o { + get_pyv_from_requirements_lines(&split_requirements(requirements.as_str())) + } else { + tracing::warn!(workspace_id = %w_id, "lockfile is empty for dedicated worker, thus python version cannot be inferred. Fallback to 3.11"); + PyVersion::Py311 + }; + let python_path = get_python_path( py_version, worker_name, diff --git a/backend/windmill-worker/src/result_processor.rs b/backend/windmill-worker/src/result_processor.rs index 4b433c4546..bb4c1937ae 100644 --- a/backend/windmill-worker/src/result_processor.rs +++ b/backend/windmill-worker/src/result_processor.rs @@ -653,7 +653,6 @@ pub async fn handle_job_error( if let Some(f) = update_job_future { let _ = f().await; } - tracing::error!(job_id = %job.id, "error handling job: {err:?} {} {} {}", job.id, job.workspace_id, job.created_by); } #[derive(Debug, Serialize)] diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 5e647dd7ce..db6ea56bb5 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -11,13 +11,14 @@ use windmill_common::{ apps::AppScriptId, - auth::{fetch_authed_from_permissioned_as, JWTAuthClaims, JobPerms, JWT_SECRET}, + auth::{fetch_authed_from_permissioned_as, JWTAuthClaims, JobPerms}, cache::{ScriptData, ScriptMetadata}, + jwt, scripts::PREVIEW_IS_TAR_CODEBASE_HASH, utils::WarnAfterExt, worker::{ get_memory, get_vcpus, get_windmill_memory_usage, get_worker_memory_usage, write_file, - ROOT_CACHE_DIR, TMP_DIR, + ROOT_CACHE_DIR, ROOT_CACHE_NOMOUNT_DIR, TMP_DIR, }, }; @@ -205,12 +206,6 @@ pub async fn create_token_for_owner( return Ok(token.clone()); } - let jwt_secret = JWT_SECRET.read().await; - - if jwt_secret.is_empty() { - return Err(Error::internal_err("No JWT secret found".to_string())); - } - let job_authed = match sqlx::query_as!( JobPerms, "SELECT * FROM job_perms WHERE job_id = $1 AND workspace_id = $2", @@ -248,22 +243,13 @@ pub async fn create_token_for_owner( scopes: None, }; - let token = jsonwebtoken::encode( - &jsonwebtoken::Header::new(jsonwebtoken::Algorithm::HS256), - &payload, - &jsonwebtoken::EncodingKey::from_secret(jwt_secret.as_bytes()), - ) - .map_err(|err| { - Error::internal_err(format!( - "Could not encode JWT token for job {job_id}: {:?}", - err - )) - })?; + let token = jwt::encode_with_internal_secret(&payload) + .await + .with_context(|| format!("Could not encode JWT token for job {job_id}"))?; + Ok(format!("jwt_{}", token)) } -pub const ROOT_CACHE_NOMOUNT_DIR: &str = concatcp!(TMP_DIR, "/cache_nomount/"); - pub const PY310_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "python_310"); pub const PY311_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "python_311"); pub const PY312_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "python_312"); @@ -286,6 +272,7 @@ pub const RUST_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "rust"); pub const CSHARP_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "csharp"); pub const BUN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_NOMOUNT_DIR, "bun"); pub const BUN_BUNDLE_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "bun"); +pub const BUN_CODEBASE_BUNDLE_CACHE_DIR: &str = concatcp!(ROOT_CACHE_NOMOUNT_DIR, "script_bundle"); pub const GO_BIN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "gobin"); pub const POWERSHELL_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "powershell"); @@ -785,7 +772,7 @@ pub async fn run_worker( worker_dir.clone(), ); tokio::spawn(async move { - if let Err(e) = PyVersion::from_instance_version() + if let Err(e) = PyVersion::from_instance_version(&Uuid::nil(), "", &db) .await .get_python(&Uuid::nil(), &mut 0, &db, &worker_name, "", &mut None) .await @@ -2041,10 +2028,16 @@ async fn handle_queued_job( | JobKind::FlowPreview | JobKind::Flow | JobKind::FlowDependencies, - None, - ) => Some(cache::job::fetch_preview(db, &job.id, raw_lock, raw_code, raw_flow).await?), + x, + ) => match x.map(|x| x.0) { + None | Some(PREVIEW_IS_CODEBASE_HASH) | Some(PREVIEW_IS_TAR_CODEBASE_HASH) => { + Some(cache::job::fetch_preview(db, &job.id, raw_lock, raw_code, raw_flow).await?) + } + _ => None, + }, _ => None, }; + let cached_res_path = if job.cache_ttl.is_some() { Some(cached_result_path(db, &client.get_authed().await, &job, preview_data.as_ref()).await) } else { diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index f215a6783c..7bf0929448 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -620,6 +620,13 @@ pub async fn handle_flow_dependency_job( tx = clear_dependency_parent_path(&parent_path, &job_path, &job.workspace_id, "flow", tx) .await?; + sqlx::query!( + "DELETE FROM flow_workspace_runnables WHERE flow_path = $1 AND workspace_id = $2", + job_path, + job.workspace_id + ) + .execute(&mut *tx) + .await?; let modified_ids; let errors; (flow.modules, tx, modified_ids, errors) = lock_modules( @@ -981,6 +988,27 @@ async fn lock_modules<'c>( } .into(); } + FlowModuleValue::Script { path, hash, .. } if !path.starts_with("hub/") => { + sqlx::query!( + "INSERT INTO flow_workspace_runnables (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, FALSE, $4) ON CONFLICT DO NOTHING", + job_path, + path, + hash.map(|h| h.0), + job.workspace_id + ) + .execute(&mut *tx) + .await?; + } + FlowModuleValue::Flow { path, .. } => { + sqlx::query!( + "INSERT INTO flow_workspace_runnables (flow_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, TRUE, $3) ON CONFLICT DO NOTHING", + job_path, + path, + job.workspace_id + ) + .execute(&mut *tx) + .await?; + } _ => (), }; modified_ids.extend(nmodified_ids); @@ -1673,7 +1701,7 @@ async fn python_dep( let final_version = annotated_pyv_numeric .and_then(|pyv| PyVersion::from_numeric(pyv)) - .unwrap_or(PyVersion::from_instance_version().await); + .unwrap_or(PyVersion::from_instance_version(job_id, w_id, db).await); let req: std::result::Result = uv_pip_compile( job_id, diff --git a/benchmarks/bench.sh b/benchmarks/bench.sh new file mode 100644 index 0000000000..5e2837c2c6 --- /dev/null +++ b/benchmarks/bench.sh @@ -0,0 +1,43 @@ +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker $USER +newgrp docker + + +mkdir pg_logs +chmod 777 pg_logs + +sudo docker run --network=host -e POSTGRES_PASSWORD=changeme -e POSTGRES_USER=postgres -e POSTGRES_DB=windmill -e POSTGRES_INITDB_ARGS="-c log_duration=on -c log_statement=all -c log_min_duration_statement=0 -c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB -c shared_preload_libraries=auto_explain -c auto_explain.log_min_duration=5 -c auto_explain.log_analyze=on -c auto_explain.log_timing=on -c auto_explain.log_buffers=on -c auto_explain.log_verbose=on \ + -c log_statement=all \ + -c log_min_duration_statement=0 \ + -c shared_buffers=2GB \ + -c work_mem=32MB \ + -c effective_cache_size=4GB \ + -c shared_preload_libraries=auto_explain \ + -c auto_explain.log_min_duration=5 \ + -c auto_explain.log_analyze=on \ + -c auto_explain.log_timing=on \ + -c auto_explain.log_buffers=on \ + -c auto_explain.log_verbose=on \ + -c auto_explain.log_nested_statements=on \ + -c logging_collector=on \ + -c log_directory='/var/log/postgresql' \ + -c log_filename='postgresql.log'" \ + -v ~/pg_logs:/var/log/postgresql \ + postgres + + +docker run -it --network=host -e DATABASE_URL=postgres://postgres:changeme@localhost/windmill ghcr.io/windmill-labs/windmill:main + + +curl -fsSL https://deno.land/install.sh | sh + +cat < suite.json +[ + { + "kind": "noop", + "jobs": 90000 + } +] +EOF + +deno run --unstable -A -r https://raw.githubusercontent.com/windmill-labs/windmill/main/benchmarks/benchmark_suite.ts -c suite.json diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 789aff41cc..21768c4827 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.467.1"; +export const VERSION = "v1.475.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/gen/core/OpenAPI.ts b/cli/gen/core/OpenAPI.ts index 7dbf2fb3d4..fda891b01d 100644 --- a/cli/gen/core/OpenAPI.ts +++ b/cli/gen/core/OpenAPI.ts @@ -54,7 +54,7 @@ export const OpenAPI: OpenAPIConfig = { PASSWORD: undefined, TOKEN: getEnv("WM_TOKEN"), USERNAME: undefined, - VERSION: '1.460.1', + VERSION: '1.465.0', WITH_CREDENTIALS: true, interceptors: { request: new Interceptors(), diff --git a/cli/gen/services.gen.ts b/cli/gen/services.gen.ts index 5148049bc9..0eefeb39a4 100644 --- a/cli/gen/services.gen.ts +++ b/cli/gen/services.gen.ts @@ -3,7 +3,7 @@ import type { CancelablePromise } from './core/CancelablePromise.ts'; import { OpenAPI } from './core/OpenAPI.ts'; import { request as __request } from './core/request.ts'; -import type { BackendVersionResponse, BackendUptodateResponse, GetLicenseIdResponse, GetOpenApiYamlResponse, GetAuditLogData, GetAuditLogResponse, ListAuditLogsData, ListAuditLogsResponse, LoginData, LoginResponse, LogoutResponse, GetUserData, GetUserResponse, UpdateUserData, UpdateUserResponse, IsOwnerOfPathData, IsOwnerOfPathResponse, SetPasswordData, SetPasswordResponse, SetPasswordForUserData, SetPasswordForUserResponse, SetLoginTypeForUserData, SetLoginTypeForUserResponse, CreateUserGloballyData, CreateUserGloballyResponse, GlobalUserUpdateData, GlobalUserUpdateResponse, GlobalUsernameInfoData, GlobalUsernameInfoResponse, GlobalUserRenameData, GlobalUserRenameResponse, GlobalUserDeleteData, GlobalUserDeleteResponse, GlobalUsersOverwriteData, GlobalUsersOverwriteResponse, GlobalUsersExportResponse, DeleteUserData, DeleteUserResponse, ListWorkspacesResponse, IsDomainAllowedResponse, ListUserWorkspacesResponse, ListWorkspacesAsSuperAdminData, ListWorkspacesAsSuperAdminResponse, CreateWorkspaceData, CreateWorkspaceResponse, ExistsWorkspaceData, ExistsWorkspaceResponse, ExistsUsernameData, ExistsUsernameResponse, GetGlobalData, GetGlobalResponse, SetGlobalData, SetGlobalResponse, GetLocalResponse, TestSmtpData, TestSmtpResponse, TestCriticalChannelsData, TestCriticalChannelsResponse, GetCriticalAlertsData, GetCriticalAlertsResponse, AcknowledgeCriticalAlertData, AcknowledgeCriticalAlertResponse, AcknowledgeAllCriticalAlertsResponse, TestLicenseKeyData, TestLicenseKeyResponse, TestObjectStorageConfigData, TestObjectStorageConfigResponse, SendStatsResponse, GetLatestKeyRenewalAttemptResponse, RenewLicenseKeyData, RenewLicenseKeyResponse, CreateCustomerPortalSessionData, CreateCustomerPortalSessionResponse, TestMetadataData, TestMetadataResponse, ListGlobalSettingsResponse, GetCurrentEmailResponse, RefreshUserTokenData, RefreshUserTokenResponse, GetTutorialProgressResponse, UpdateTutorialProgressData, UpdateTutorialProgressResponse, LeaveInstanceResponse, GetUsageResponse, GetRunnableResponse, GlobalWhoamiResponse, ListWorkspaceInvitesResponse, WhoamiData, WhoamiResponse, AcceptInviteData, AcceptInviteResponse, DeclineInviteData, DeclineInviteResponse, InviteUserData, InviteUserResponse, AddUserData, AddUserResponse, DeleteInviteData, DeleteInviteResponse, ArchiveWorkspaceData, ArchiveWorkspaceResponse, UnarchiveWorkspaceData, UnarchiveWorkspaceResponse, DeleteWorkspaceData, DeleteWorkspaceResponse, LeaveWorkspaceData, LeaveWorkspaceResponse, GetWorkspaceNameData, GetWorkspaceNameResponse, ChangeWorkspaceNameData, ChangeWorkspaceNameResponse, ChangeWorkspaceIdData, ChangeWorkspaceIdResponse, ChangeWorkspaceColorData, ChangeWorkspaceColorResponse, WhoisData, WhoisResponse, UpdateOperatorSettingsData, UpdateOperatorSettingsResponse, ExistsEmailData, ExistsEmailResponse, ListUsersAsSuperAdminData, ListUsersAsSuperAdminResponse, ListPendingInvitesData, ListPendingInvitesResponse, GetSettingsData, GetSettingsResponse, GetDeployToData, GetDeployToResponse, GetIsPremiumData, GetIsPremiumResponse, GetPremiumInfoData, GetPremiumInfoResponse, SetAutomaticBillingData, SetAutomaticBillingResponse, GetThresholdAlertData, GetThresholdAlertResponse, SetThresholdAlertData, SetThresholdAlertResponse, EditSlackCommandData, EditSlackCommandResponse, RunSlackMessageTestJobData, RunSlackMessageTestJobResponse, EditDeployToData, EditDeployToResponse, EditAutoInviteData, EditAutoInviteResponse, EditWebhookData, EditWebhookResponse, EditCopilotConfigData, EditCopilotConfigResponse, GetCopilotInfoData, GetCopilotInfoResponse, EditErrorHandlerData, EditErrorHandlerResponse, EditLargeFileStorageConfigData, EditLargeFileStorageConfigResponse, EditWorkspaceGitSyncConfigData, EditWorkspaceGitSyncConfigResponse, EditWorkspaceDeployUiSettingsData, EditWorkspaceDeployUiSettingsResponse, EditWorkspaceDefaultAppData, EditWorkspaceDefaultAppResponse, EditDefaultScriptsData, EditDefaultScriptsResponse, GetDefaultScriptsData, GetDefaultScriptsResponse, SetEnvironmentVariableData, SetEnvironmentVariableResponse, GetWorkspaceEncryptionKeyData, GetWorkspaceEncryptionKeyResponse, SetWorkspaceEncryptionKeyData, SetWorkspaceEncryptionKeyResponse, GetWorkspaceDefaultAppData, GetWorkspaceDefaultAppResponse, GetLargeFileStorageConfigData, GetLargeFileStorageConfigResponse, GetWorkspaceUsageData, GetWorkspaceUsageResponse, GetUsedTriggersData, GetUsedTriggersResponse, ListUsersData, ListUsersResponse, ListUsersUsageData, ListUsersUsageResponse, ListUsernamesData, ListUsernamesResponse, UsernameToEmailData, UsernameToEmailResponse, CreateTokenData, CreateTokenResponse, CreateTokenImpersonateData, CreateTokenImpersonateResponse, DeleteTokenData, DeleteTokenResponse, ListTokensData, ListTokensResponse, GetOidcTokenData, GetOidcTokenResponse, CreateVariableData, CreateVariableResponse, EncryptValueData, EncryptValueResponse, DeleteVariableData, DeleteVariableResponse, UpdateVariableData, UpdateVariableResponse, GetVariableData, GetVariableResponse, GetVariableValueData, GetVariableValueResponse, ExistsVariableData, ExistsVariableResponse, ListVariableData, ListVariableResponse, ListContextualVariablesData, ListContextualVariablesResponse, WorkspaceGetCriticalAlertsData, WorkspaceGetCriticalAlertsResponse, WorkspaceAcknowledgeCriticalAlertData, WorkspaceAcknowledgeCriticalAlertResponse, WorkspaceAcknowledgeAllCriticalAlertsData, WorkspaceAcknowledgeAllCriticalAlertsResponse, WorkspaceMuteCriticalAlertsUiData, WorkspaceMuteCriticalAlertsUiResponse, LoginWithOauthData, LoginWithOauthResponse, ConnectSlackCallbackData, ConnectSlackCallbackResponse, ConnectSlackCallbackInstanceData, ConnectSlackCallbackInstanceResponse, ConnectCallbackData, ConnectCallbackResponse, CreateAccountData, CreateAccountResponse, RefreshTokenData, RefreshTokenResponse, DisconnectAccountData, DisconnectAccountResponse, DisconnectSlackData, DisconnectSlackResponse, ListOauthLoginsResponse, ListOauthConnectsResponse, GetOauthConnectData, GetOauthConnectResponse, SyncTeamsResponse, CreateResourceData, CreateResourceResponse, DeleteResourceData, DeleteResourceResponse, UpdateResourceData, UpdateResourceResponse, UpdateResourceValueData, UpdateResourceValueResponse, GetResourceData, GetResourceResponse, GetResourceValueInterpolatedData, GetResourceValueInterpolatedResponse, GetResourceValueData, GetResourceValueResponse, ExistsResourceData, ExistsResourceResponse, ListResourceData, ListResourceResponse, ListSearchResourceData, ListSearchResourceResponse, ListResourceNamesData, ListResourceNamesResponse, CreateResourceTypeData, CreateResourceTypeResponse, FileResourceTypeToFileExtMapData, FileResourceTypeToFileExtMapResponse, DeleteResourceTypeData, DeleteResourceTypeResponse, UpdateResourceTypeData, UpdateResourceTypeResponse, GetResourceTypeData, GetResourceTypeResponse, ExistsResourceTypeData, ExistsResourceTypeResponse, ListResourceTypeData, ListResourceTypeResponse, ListResourceTypeNamesData, ListResourceTypeNamesResponse, QueryResourceTypesData, QueryResourceTypesResponse, ListHubIntegrationsData, ListHubIntegrationsResponse, ListHubFlowsResponse, GetHubFlowByIdData, GetHubFlowByIdResponse, ListHubAppsResponse, GetHubAppByIdData, GetHubAppByIdResponse, GetPublicAppByCustomPathData, GetPublicAppByCustomPathResponse, GetHubScriptContentByPathData, GetHubScriptContentByPathResponse, GetHubScriptByPathData, GetHubScriptByPathResponse, GetTopHubScriptsData, GetTopHubScriptsResponse, QueryHubScriptsData, QueryHubScriptsResponse, ListSearchScriptData, ListSearchScriptResponse, ListScriptsData, ListScriptsResponse, ListScriptPathsData, ListScriptPathsResponse, CreateDraftData, CreateDraftResponse, DeleteDraftData, DeleteDraftResponse, CreateScriptData, CreateScriptResponse, ToggleWorkspaceErrorHandlerForScriptData, ToggleWorkspaceErrorHandlerForScriptResponse, GetCustomTagsData, GetCustomTagsResponse, GeDefaultTagsResponse, IsDefaultTagsPerWorkspaceResponse, ArchiveScriptByPathData, ArchiveScriptByPathResponse, ArchiveScriptByHashData, ArchiveScriptByHashResponse, DeleteScriptByHashData, DeleteScriptByHashResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, GetScriptByPathData, GetScriptByPathResponse, GetTriggersCountOfScriptData, GetTriggersCountOfScriptResponse, ListTokensOfScriptData, ListTokensOfScriptResponse, GetScriptByPathWithDraftData, GetScriptByPathWithDraftResponse, GetScriptHistoryByPathData, GetScriptHistoryByPathResponse, GetScriptLatestVersionData, GetScriptLatestVersionResponse, UpdateScriptHistoryData, UpdateScriptHistoryResponse, RawScriptByPathData, RawScriptByPathResponse, RawScriptByPathTokenedData, RawScriptByPathTokenedResponse, ExistsScriptByPathData, ExistsScriptByPathResponse, GetScriptByHashData, GetScriptByHashResponse, RawScriptByHashData, RawScriptByHashResponse, GetScriptDeploymentStatusData, GetScriptDeploymentStatusResponse, RunScriptByPathData, RunScriptByPathResponse, OpenaiSyncScriptByPathData, OpenaiSyncScriptByPathResponse, RunWaitResultScriptByPathData, RunWaitResultScriptByPathResponse, RunWaitResultScriptByPathGetData, RunWaitResultScriptByPathGetResponse, OpenaiSyncFlowByPathData, OpenaiSyncFlowByPathResponse, RunWaitResultFlowByPathData, RunWaitResultFlowByPathResponse, ResultByIdData, ResultByIdResponse, ListFlowPathsData, ListFlowPathsResponse, ListSearchFlowData, ListSearchFlowResponse, ListFlowsData, ListFlowsResponse, GetFlowHistoryData, GetFlowHistoryResponse, GetFlowLatestVersionData, GetFlowLatestVersionResponse, GetFlowVersionData, GetFlowVersionResponse, UpdateFlowHistoryData, UpdateFlowHistoryResponse, GetFlowByPathData, GetFlowByPathResponse, GetTriggersCountOfFlowData, GetTriggersCountOfFlowResponse, ListTokensOfFlowData, ListTokensOfFlowResponse, ToggleWorkspaceErrorHandlerForFlowData, ToggleWorkspaceErrorHandlerForFlowResponse, GetFlowByPathWithDraftData, GetFlowByPathWithDraftResponse, ExistsFlowByPathData, ExistsFlowByPathResponse, CreateFlowData, CreateFlowResponse, UpdateFlowData, UpdateFlowResponse, ArchiveFlowByPathData, ArchiveFlowByPathResponse, DeleteFlowByPathData, DeleteFlowByPathResponse, ListRawAppsData, ListRawAppsResponse, ExistsRawAppData, ExistsRawAppResponse, GetRawAppDataData, GetRawAppDataResponse, ListSearchAppData, ListSearchAppResponse, ListAppsData, ListAppsResponse, CreateAppData, CreateAppResponse, ExistsAppData, ExistsAppResponse, GetAppByPathData, GetAppByPathResponse, GetAppLiteByPathData, GetAppLiteByPathResponse, GetAppByPathWithDraftData, GetAppByPathWithDraftResponse, GetAppHistoryByPathData, GetAppHistoryByPathResponse, GetAppLatestVersionData, GetAppLatestVersionResponse, UpdateAppHistoryData, UpdateAppHistoryResponse, GetPublicAppBySecretData, GetPublicAppBySecretResponse, GetPublicResourceData, GetPublicResourceResponse, GetPublicSecretOfAppData, GetPublicSecretOfAppResponse, GetAppByVersionData, GetAppByVersionResponse, CreateRawAppData, CreateRawAppResponse, UpdateRawAppData, UpdateRawAppResponse, DeleteRawAppData, DeleteRawAppResponse, DeleteAppData, DeleteAppResponse, UpdateAppData, UpdateAppResponse, CustomPathExistsData, CustomPathExistsResponse, ExecuteComponentData, ExecuteComponentResponse, RunFlowByPathData, RunFlowByPathResponse, RestartFlowAtStepData, RestartFlowAtStepResponse, RunScriptByHashData, RunScriptByHashResponse, RunScriptPreviewData, RunScriptPreviewResponse, RunCodeWorkflowTaskData, RunCodeWorkflowTaskResponse, RunRawScriptDependenciesData, RunRawScriptDependenciesResponse, RunFlowPreviewData, RunFlowPreviewResponse, ListQueueData, ListQueueResponse, GetQueueCountData, GetQueueCountResponse, GetCompletedCountData, GetCompletedCountResponse, CountCompletedJobsData, CountCompletedJobsResponse, ListFilteredUuidsData, ListFilteredUuidsResponse, CancelSelectionData, CancelSelectionResponse, ListCompletedJobsData, ListCompletedJobsResponse, ListJobsData, ListJobsResponse, GetDbClockResponse, CountJobsByTagData, CountJobsByTagResponse, GetJobData, GetJobResponse, GetRootJobIdData, GetRootJobIdResponse, GetJobLogsData, GetJobLogsResponse, GetJobArgsData, GetJobArgsResponse, GetJobUpdatesData, GetJobUpdatesResponse, GetLogFileFromStoreData, GetLogFileFromStoreResponse, GetFlowDebugInfoData, GetFlowDebugInfoResponse, GetCompletedJobData, GetCompletedJobResponse, GetCompletedJobResultData, GetCompletedJobResultResponse, GetCompletedJobResultMaybeData, GetCompletedJobResultMaybeResponse, DeleteCompletedJobData, DeleteCompletedJobResponse, CancelQueuedJobData, CancelQueuedJobResponse, CancelPersistentQueuedJobsData, CancelPersistentQueuedJobsResponse, ForceCancelQueuedJobData, ForceCancelQueuedJobResponse, CreateJobSignatureData, CreateJobSignatureResponse, GetResumeUrlsData, GetResumeUrlsResponse, GetSlackApprovalPayloadData, GetSlackApprovalPayloadResponse, ResumeSuspendedJobGetData, ResumeSuspendedJobGetResponse, ResumeSuspendedJobPostData, ResumeSuspendedJobPostResponse, SetFlowUserStateData, SetFlowUserStateResponse, GetFlowUserStateData, GetFlowUserStateResponse, ResumeSuspendedFlowAsOwnerData, ResumeSuspendedFlowAsOwnerResponse, CancelSuspendedJobGetData, CancelSuspendedJobGetResponse, CancelSuspendedJobPostData, CancelSuspendedJobPostResponse, GetSuspendedJobFlowData, GetSuspendedJobFlowResponse, PreviewScheduleData, PreviewScheduleResponse, CreateScheduleData, CreateScheduleResponse, UpdateScheduleData, UpdateScheduleResponse, SetScheduleEnabledData, SetScheduleEnabledResponse, DeleteScheduleData, DeleteScheduleResponse, GetScheduleData, GetScheduleResponse, ExistsScheduleData, ExistsScheduleResponse, ListSchedulesData, ListSchedulesResponse, ListSchedulesWithJobsData, ListSchedulesWithJobsResponse, SetDefaultErrorOrRecoveryHandlerData, SetDefaultErrorOrRecoveryHandlerResponse, CreateHttpTriggerData, CreateHttpTriggerResponse, UpdateHttpTriggerData, UpdateHttpTriggerResponse, DeleteHttpTriggerData, DeleteHttpTriggerResponse, GetHttpTriggerData, GetHttpTriggerResponse, ListHttpTriggersData, ListHttpTriggersResponse, ExistsHttpTriggerData, ExistsHttpTriggerResponse, ExistsRouteData, ExistsRouteResponse, CreateWebsocketTriggerData, CreateWebsocketTriggerResponse, UpdateWebsocketTriggerData, UpdateWebsocketTriggerResponse, DeleteWebsocketTriggerData, DeleteWebsocketTriggerResponse, GetWebsocketTriggerData, GetWebsocketTriggerResponse, ListWebsocketTriggersData, ListWebsocketTriggersResponse, ExistsWebsocketTriggerData, ExistsWebsocketTriggerResponse, SetWebsocketTriggerEnabledData, SetWebsocketTriggerEnabledResponse, TestWebsocketConnectionData, TestWebsocketConnectionResponse, CreateKafkaTriggerData, CreateKafkaTriggerResponse, UpdateKafkaTriggerData, UpdateKafkaTriggerResponse, DeleteKafkaTriggerData, DeleteKafkaTriggerResponse, GetKafkaTriggerData, GetKafkaTriggerResponse, ListKafkaTriggersData, ListKafkaTriggersResponse, ExistsKafkaTriggerData, ExistsKafkaTriggerResponse, SetKafkaTriggerEnabledData, SetKafkaTriggerEnabledResponse, TestKafkaConnectionData, TestKafkaConnectionResponse, CreateNatsTriggerData, CreateNatsTriggerResponse, UpdateNatsTriggerData, UpdateNatsTriggerResponse, DeleteNatsTriggerData, DeleteNatsTriggerResponse, GetNatsTriggerData, GetNatsTriggerResponse, ListNatsTriggersData, ListNatsTriggersResponse, ExistsNatsTriggerData, ExistsNatsTriggerResponse, SetNatsTriggerEnabledData, SetNatsTriggerEnabledResponse, TestNatsConnectionData, TestNatsConnectionResponse, CreateSqsTriggerData, CreateSqsTriggerResponse, UpdateSqsTriggerData, UpdateSqsTriggerResponse, DeleteSqsTriggerData, DeleteSqsTriggerResponse, GetSqsTriggerData, GetSqsTriggerResponse, ListSqsTriggersData, ListSqsTriggersResponse, ExistsSqsTriggerData, ExistsSqsTriggerResponse, SetSqsTriggerEnabledData, SetSqsTriggerEnabledResponse, TestSqsConnectionData, TestSqsConnectionResponse, IsValidPostgresConfigurationData, IsValidPostgresConfigurationResponse, CreateTemplateScriptData, CreateTemplateScriptResponse, GetTemplateScriptData, GetTemplateScriptResponse, ListPostgresReplicationSlotData, ListPostgresReplicationSlotResponse, CreatePostgresReplicationSlotData, CreatePostgresReplicationSlotResponse, DeletePostgresReplicationSlotData, DeletePostgresReplicationSlotResponse, ListPostgresPublicationData, ListPostgresPublicationResponse, GetPostgresPublicationData, GetPostgresPublicationResponse, CreatePostgresPublicationData, CreatePostgresPublicationResponse, UpdatePostgresPublicationData, UpdatePostgresPublicationResponse, DeletePostgresPublicationData, DeletePostgresPublicationResponse, CreatePostgresTriggerData, CreatePostgresTriggerResponse, UpdatePostgresTriggerData, UpdatePostgresTriggerResponse, DeletePostgresTriggerData, DeletePostgresTriggerResponse, GetPostgresTriggerData, GetPostgresTriggerResponse, ListPostgresTriggersData, ListPostgresTriggersResponse, ExistsPostgresTriggerData, ExistsPostgresTriggerResponse, SetPostgresTriggerEnabledData, SetPostgresTriggerEnabledResponse, TestPostgresConnectionData, TestPostgresConnectionResponse, ListInstanceGroupsResponse, GetInstanceGroupData, GetInstanceGroupResponse, CreateInstanceGroupData, CreateInstanceGroupResponse, UpdateInstanceGroupData, UpdateInstanceGroupResponse, DeleteInstanceGroupData, DeleteInstanceGroupResponse, AddUserToInstanceGroupData, AddUserToInstanceGroupResponse, RemoveUserFromInstanceGroupData, RemoveUserFromInstanceGroupResponse, ExportInstanceGroupsResponse, OverwriteInstanceGroupsData, OverwriteInstanceGroupsResponse, ListGroupsData, ListGroupsResponse, ListGroupNamesData, ListGroupNamesResponse, CreateGroupData, CreateGroupResponse, UpdateGroupData, UpdateGroupResponse, DeleteGroupData, DeleteGroupResponse, GetGroupData, GetGroupResponse, AddUserToGroupData, AddUserToGroupResponse, RemoveUserToGroupData, RemoveUserToGroupResponse, ListFoldersData, ListFoldersResponse, ListFolderNamesData, ListFolderNamesResponse, CreateFolderData, CreateFolderResponse, UpdateFolderData, UpdateFolderResponse, DeleteFolderData, DeleteFolderResponse, GetFolderData, GetFolderResponse, GetFolderUsageData, GetFolderUsageResponse, AddOwnerToFolderData, AddOwnerToFolderResponse, RemoveOwnerToFolderData, RemoveOwnerToFolderResponse, ListWorkersData, ListWorkersResponse, ExistsWorkerWithTagData, ExistsWorkerWithTagResponse, GetQueueMetricsResponse, GetCountsOfJobsWaitingPerTagResponse, ListWorkerGroupsResponse, GetConfigData, GetConfigResponse, UpdateConfigData, UpdateConfigResponse, DeleteConfigData, DeleteConfigResponse, ListConfigsResponse, ListAutoscalingEventsData, ListAutoscalingEventsResponse, GetGranularAclsData, GetGranularAclsResponse, AddGranularAclsData, AddGranularAclsResponse, RemoveGranularAclsData, RemoveGranularAclsResponse, SetCaptureConfigData, SetCaptureConfigResponse, PingCaptureConfigData, PingCaptureConfigResponse, GetCaptureConfigsData, GetCaptureConfigsResponse, ListCapturesData, ListCapturesResponse, GetCaptureData, GetCaptureResponse, DeleteCaptureData, DeleteCaptureResponse, StarData, StarResponse, UnstarData, UnstarResponse, GetInputHistoryData, GetInputHistoryResponse, GetArgsFromHistoryOrSavedInputData, GetArgsFromHistoryOrSavedInputResponse, ListInputsData, ListInputsResponse, CreateInputData, CreateInputResponse, UpdateInputData, UpdateInputResponse, DeleteInputData, DeleteInputResponse, DuckdbConnectionSettingsData, DuckdbConnectionSettingsResponse, DuckdbConnectionSettingsV2Data, DuckdbConnectionSettingsV2Response, PolarsConnectionSettingsData, PolarsConnectionSettingsResponse, PolarsConnectionSettingsV2Data, PolarsConnectionSettingsV2Response, S3ResourceInfoData, S3ResourceInfoResponse, DatasetStorageTestConnectionData, DatasetStorageTestConnectionResponse, ListStoredFilesData, ListStoredFilesResponse, LoadFileMetadataData, LoadFileMetadataResponse, LoadFilePreviewData, LoadFilePreviewResponse, LoadParquetPreviewData, LoadParquetPreviewResponse, LoadTableRowCountData, LoadTableRowCountResponse, LoadCsvPreviewData, LoadCsvPreviewResponse, DeleteS3FileData, DeleteS3FileResponse, MoveS3FileData, MoveS3FileResponse, FileUploadData, FileUploadResponse, FileDownloadData, FileDownloadResponse, FileDownloadParquetAsCsvData, FileDownloadParquetAsCsvResponse, GetJobMetricsData, GetJobMetricsResponse, SetJobProgressData, SetJobProgressResponse, GetJobProgressData, GetJobProgressResponse, ListLogFilesData, ListLogFilesResponse, GetLogFileData, GetLogFileResponse, ListConcurrencyGroupsResponse, DeleteConcurrencyGroupData, DeleteConcurrencyGroupResponse, GetConcurrencyKeyData, GetConcurrencyKeyResponse, ListExtendedJobsData, ListExtendedJobsResponse, SearchJobsIndexData, SearchJobsIndexResponse, SearchLogsIndexData, SearchLogsIndexResponse, CountSearchLogsIndexData, CountSearchLogsIndexResponse, ClearIndexData, ClearIndexResponse } from './types.gen.ts'; +import type { BackendVersionResponse, BackendUptodateResponse, GetLicenseIdResponse, GetOpenApiYamlResponse, GetAuditLogData, GetAuditLogResponse, ListAuditLogsData, ListAuditLogsResponse, LoginData, LoginResponse, LogoutResponse, GetUserData, GetUserResponse, UpdateUserData, UpdateUserResponse, IsOwnerOfPathData, IsOwnerOfPathResponse, SetPasswordData, SetPasswordResponse, SetPasswordForUserData, SetPasswordForUserResponse, SetLoginTypeForUserData, SetLoginTypeForUserResponse, CreateUserGloballyData, CreateUserGloballyResponse, GlobalUserUpdateData, GlobalUserUpdateResponse, GlobalUsernameInfoData, GlobalUsernameInfoResponse, GlobalUserRenameData, GlobalUserRenameResponse, GlobalUserDeleteData, GlobalUserDeleteResponse, GlobalUsersOverwriteData, GlobalUsersOverwriteResponse, GlobalUsersExportResponse, DeleteUserData, DeleteUserResponse, ListWorkspacesResponse, IsDomainAllowedResponse, ListUserWorkspacesResponse, ListWorkspacesAsSuperAdminData, ListWorkspacesAsSuperAdminResponse, CreateWorkspaceData, CreateWorkspaceResponse, ExistsWorkspaceData, ExistsWorkspaceResponse, ExistsUsernameData, ExistsUsernameResponse, GetGlobalData, GetGlobalResponse, SetGlobalData, SetGlobalResponse, GetLocalResponse, TestSmtpData, TestSmtpResponse, TestCriticalChannelsData, TestCriticalChannelsResponse, GetCriticalAlertsData, GetCriticalAlertsResponse, AcknowledgeCriticalAlertData, AcknowledgeCriticalAlertResponse, AcknowledgeAllCriticalAlertsResponse, TestLicenseKeyData, TestLicenseKeyResponse, TestObjectStorageConfigData, TestObjectStorageConfigResponse, SendStatsResponse, GetLatestKeyRenewalAttemptResponse, RenewLicenseKeyData, RenewLicenseKeyResponse, CreateCustomerPortalSessionData, CreateCustomerPortalSessionResponse, TestMetadataData, TestMetadataResponse, ListGlobalSettingsResponse, GetCurrentEmailResponse, RefreshUserTokenData, RefreshUserTokenResponse, GetTutorialProgressResponse, UpdateTutorialProgressData, UpdateTutorialProgressResponse, LeaveInstanceResponse, GetUsageResponse, GetRunnableResponse, GlobalWhoamiResponse, ListWorkspaceInvitesResponse, WhoamiData, WhoamiResponse, AcceptInviteData, AcceptInviteResponse, DeclineInviteData, DeclineInviteResponse, InviteUserData, InviteUserResponse, AddUserData, AddUserResponse, DeleteInviteData, DeleteInviteResponse, ArchiveWorkspaceData, ArchiveWorkspaceResponse, UnarchiveWorkspaceData, UnarchiveWorkspaceResponse, DeleteWorkspaceData, DeleteWorkspaceResponse, LeaveWorkspaceData, LeaveWorkspaceResponse, GetWorkspaceNameData, GetWorkspaceNameResponse, ChangeWorkspaceNameData, ChangeWorkspaceNameResponse, ChangeWorkspaceIdData, ChangeWorkspaceIdResponse, ChangeWorkspaceColorData, ChangeWorkspaceColorResponse, WhoisData, WhoisResponse, UpdateOperatorSettingsData, UpdateOperatorSettingsResponse, ExistsEmailData, ExistsEmailResponse, ListUsersAsSuperAdminData, ListUsersAsSuperAdminResponse, ListPendingInvitesData, ListPendingInvitesResponse, GetSettingsData, GetSettingsResponse, GetDeployToData, GetDeployToResponse, GetIsPremiumData, GetIsPremiumResponse, GetPremiumInfoData, GetPremiumInfoResponse, SetAutomaticBillingData, SetAutomaticBillingResponse, GetThresholdAlertData, GetThresholdAlertResponse, SetThresholdAlertData, SetThresholdAlertResponse, EditSlackCommandData, EditSlackCommandResponse, EditTeamsCommandData, EditTeamsCommandResponse, ListAvailableTeamsIdsData, ListAvailableTeamsIdsResponse, ListAvailableTeamsChannelsData, ListAvailableTeamsChannelsResponse, ConnectTeamsData, ConnectTeamsResponse, RunSlackMessageTestJobData, RunSlackMessageTestJobResponse, RunTeamsMessageTestJobData, RunTeamsMessageTestJobResponse, EditDeployToData, EditDeployToResponse, EditAutoInviteData, EditAutoInviteResponse, EditWebhookData, EditWebhookResponse, EditCopilotConfigData, EditCopilotConfigResponse, GetCopilotInfoData, GetCopilotInfoResponse, EditErrorHandlerData, EditErrorHandlerResponse, EditLargeFileStorageConfigData, EditLargeFileStorageConfigResponse, EditWorkspaceGitSyncConfigData, EditWorkspaceGitSyncConfigResponse, EditWorkspaceDeployUiSettingsData, EditWorkspaceDeployUiSettingsResponse, EditWorkspaceDefaultAppData, EditWorkspaceDefaultAppResponse, EditDefaultScriptsData, EditDefaultScriptsResponse, GetDefaultScriptsData, GetDefaultScriptsResponse, SetEnvironmentVariableData, SetEnvironmentVariableResponse, GetWorkspaceEncryptionKeyData, GetWorkspaceEncryptionKeyResponse, SetWorkspaceEncryptionKeyData, SetWorkspaceEncryptionKeyResponse, GetWorkspaceDefaultAppData, GetWorkspaceDefaultAppResponse, GetLargeFileStorageConfigData, GetLargeFileStorageConfigResponse, GetWorkspaceUsageData, GetWorkspaceUsageResponse, GetUsedTriggersData, GetUsedTriggersResponse, ListUsersData, ListUsersResponse, ListUsersUsageData, ListUsersUsageResponse, ListUsernamesData, ListUsernamesResponse, UsernameToEmailData, UsernameToEmailResponse, CreateTokenData, CreateTokenResponse, CreateTokenImpersonateData, CreateTokenImpersonateResponse, DeleteTokenData, DeleteTokenResponse, ListTokensData, ListTokensResponse, GetOidcTokenData, GetOidcTokenResponse, CreateVariableData, CreateVariableResponse, EncryptValueData, EncryptValueResponse, DeleteVariableData, DeleteVariableResponse, UpdateVariableData, UpdateVariableResponse, GetVariableData, GetVariableResponse, GetVariableValueData, GetVariableValueResponse, ExistsVariableData, ExistsVariableResponse, ListVariableData, ListVariableResponse, ListContextualVariablesData, ListContextualVariablesResponse, WorkspaceGetCriticalAlertsData, WorkspaceGetCriticalAlertsResponse, WorkspaceAcknowledgeCriticalAlertData, WorkspaceAcknowledgeCriticalAlertResponse, WorkspaceAcknowledgeAllCriticalAlertsData, WorkspaceAcknowledgeAllCriticalAlertsResponse, WorkspaceMuteCriticalAlertsUiData, WorkspaceMuteCriticalAlertsUiResponse, LoginWithOauthData, LoginWithOauthResponse, ConnectSlackCallbackData, ConnectSlackCallbackResponse, ConnectSlackCallbackInstanceData, ConnectSlackCallbackInstanceResponse, ConnectCallbackData, ConnectCallbackResponse, CreateAccountData, CreateAccountResponse, RefreshTokenData, RefreshTokenResponse, DisconnectAccountData, DisconnectAccountResponse, DisconnectSlackData, DisconnectSlackResponse, DisconnectTeamsData, DisconnectTeamsResponse, ListOauthLoginsResponse, ListOauthConnectsResponse, GetOauthConnectData, GetOauthConnectResponse, SyncTeamsResponse, SendMessageToConversationData, SendMessageToConversationResponse, CreateResourceData, CreateResourceResponse, DeleteResourceData, DeleteResourceResponse, UpdateResourceData, UpdateResourceResponse, UpdateResourceValueData, UpdateResourceValueResponse, GetResourceData, GetResourceResponse, GetResourceValueInterpolatedData, GetResourceValueInterpolatedResponse, GetResourceValueData, GetResourceValueResponse, ExistsResourceData, ExistsResourceResponse, ListResourceData, ListResourceResponse, ListSearchResourceData, ListSearchResourceResponse, ListResourceNamesData, ListResourceNamesResponse, CreateResourceTypeData, CreateResourceTypeResponse, FileResourceTypeToFileExtMapData, FileResourceTypeToFileExtMapResponse, DeleteResourceTypeData, DeleteResourceTypeResponse, UpdateResourceTypeData, UpdateResourceTypeResponse, GetResourceTypeData, GetResourceTypeResponse, ExistsResourceTypeData, ExistsResourceTypeResponse, ListResourceTypeData, ListResourceTypeResponse, ListResourceTypeNamesData, ListResourceTypeNamesResponse, QueryResourceTypesData, QueryResourceTypesResponse, ListHubIntegrationsData, ListHubIntegrationsResponse, ListHubFlowsResponse, GetHubFlowByIdData, GetHubFlowByIdResponse, ListHubAppsResponse, GetHubAppByIdData, GetHubAppByIdResponse, GetPublicAppByCustomPathData, GetPublicAppByCustomPathResponse, GetHubScriptContentByPathData, GetHubScriptContentByPathResponse, GetHubScriptByPathData, GetHubScriptByPathResponse, GetTopHubScriptsData, GetTopHubScriptsResponse, QueryHubScriptsData, QueryHubScriptsResponse, ListSearchScriptData, ListSearchScriptResponse, ListScriptsData, ListScriptsResponse, ListScriptPathsData, ListScriptPathsResponse, CreateDraftData, CreateDraftResponse, DeleteDraftData, DeleteDraftResponse, CreateScriptData, CreateScriptResponse, ToggleWorkspaceErrorHandlerForScriptData, ToggleWorkspaceErrorHandlerForScriptResponse, GetCustomTagsData, GetCustomTagsResponse, GeDefaultTagsResponse, IsDefaultTagsPerWorkspaceResponse, ArchiveScriptByPathData, ArchiveScriptByPathResponse, ArchiveScriptByHashData, ArchiveScriptByHashResponse, DeleteScriptByHashData, DeleteScriptByHashResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, GetScriptByPathData, GetScriptByPathResponse, GetTriggersCountOfScriptData, GetTriggersCountOfScriptResponse, ListTokensOfScriptData, ListTokensOfScriptResponse, GetScriptByPathWithDraftData, GetScriptByPathWithDraftResponse, GetScriptHistoryByPathData, GetScriptHistoryByPathResponse, GetScriptLatestVersionData, GetScriptLatestVersionResponse, UpdateScriptHistoryData, UpdateScriptHistoryResponse, RawScriptByPathData, RawScriptByPathResponse, RawScriptByPathTokenedData, RawScriptByPathTokenedResponse, ExistsScriptByPathData, ExistsScriptByPathResponse, GetScriptByHashData, GetScriptByHashResponse, RawScriptByHashData, RawScriptByHashResponse, GetScriptDeploymentStatusData, GetScriptDeploymentStatusResponse, RunScriptByPathData, RunScriptByPathResponse, OpenaiSyncScriptByPathData, OpenaiSyncScriptByPathResponse, RunWaitResultScriptByPathData, RunWaitResultScriptByPathResponse, RunWaitResultScriptByPathGetData, RunWaitResultScriptByPathGetResponse, OpenaiSyncFlowByPathData, OpenaiSyncFlowByPathResponse, RunWaitResultFlowByPathData, RunWaitResultFlowByPathResponse, ResultByIdData, ResultByIdResponse, ListFlowPathsData, ListFlowPathsResponse, ListSearchFlowData, ListSearchFlowResponse, ListFlowsData, ListFlowsResponse, GetFlowHistoryData, GetFlowHistoryResponse, GetFlowLatestVersionData, GetFlowLatestVersionResponse, GetFlowVersionData, GetFlowVersionResponse, UpdateFlowHistoryData, UpdateFlowHistoryResponse, GetFlowByPathData, GetFlowByPathResponse, GetTriggersCountOfFlowData, GetTriggersCountOfFlowResponse, ListTokensOfFlowData, ListTokensOfFlowResponse, ToggleWorkspaceErrorHandlerForFlowData, ToggleWorkspaceErrorHandlerForFlowResponse, GetFlowByPathWithDraftData, GetFlowByPathWithDraftResponse, ExistsFlowByPathData, ExistsFlowByPathResponse, CreateFlowData, CreateFlowResponse, UpdateFlowData, UpdateFlowResponse, ArchiveFlowByPathData, ArchiveFlowByPathResponse, DeleteFlowByPathData, DeleteFlowByPathResponse, ListRawAppsData, ListRawAppsResponse, ExistsRawAppData, ExistsRawAppResponse, GetRawAppDataData, GetRawAppDataResponse, ListSearchAppData, ListSearchAppResponse, ListAppsData, ListAppsResponse, CreateAppData, CreateAppResponse, ExistsAppData, ExistsAppResponse, GetAppByPathData, GetAppByPathResponse, GetAppLiteByPathData, GetAppLiteByPathResponse, GetAppByPathWithDraftData, GetAppByPathWithDraftResponse, GetAppHistoryByPathData, GetAppHistoryByPathResponse, GetAppLatestVersionData, GetAppLatestVersionResponse, UpdateAppHistoryData, UpdateAppHistoryResponse, GetPublicAppBySecretData, GetPublicAppBySecretResponse, GetPublicResourceData, GetPublicResourceResponse, GetPublicSecretOfAppData, GetPublicSecretOfAppResponse, GetAppByVersionData, GetAppByVersionResponse, CreateRawAppData, CreateRawAppResponse, UpdateRawAppData, UpdateRawAppResponse, DeleteRawAppData, DeleteRawAppResponse, DeleteAppData, DeleteAppResponse, UpdateAppData, UpdateAppResponse, CustomPathExistsData, CustomPathExistsResponse, ExecuteComponentData, ExecuteComponentResponse, RunFlowByPathData, RunFlowByPathResponse, RestartFlowAtStepData, RestartFlowAtStepResponse, RunScriptByHashData, RunScriptByHashResponse, RunScriptPreviewData, RunScriptPreviewResponse, RunCodeWorkflowTaskData, RunCodeWorkflowTaskResponse, RunRawScriptDependenciesData, RunRawScriptDependenciesResponse, RunFlowPreviewData, RunFlowPreviewResponse, ListQueueData, ListQueueResponse, GetQueueCountData, GetQueueCountResponse, GetCompletedCountData, GetCompletedCountResponse, CountCompletedJobsData, CountCompletedJobsResponse, ListFilteredUuidsData, ListFilteredUuidsResponse, CancelSelectionData, CancelSelectionResponse, ListCompletedJobsData, ListCompletedJobsResponse, ListJobsData, ListJobsResponse, GetDbClockResponse, CountJobsByTagData, CountJobsByTagResponse, GetJobData, GetJobResponse, GetRootJobIdData, GetRootJobIdResponse, GetJobLogsData, GetJobLogsResponse, GetJobArgsData, GetJobArgsResponse, GetJobUpdatesData, GetJobUpdatesResponse, GetLogFileFromStoreData, GetLogFileFromStoreResponse, GetFlowDebugInfoData, GetFlowDebugInfoResponse, GetCompletedJobData, GetCompletedJobResponse, GetCompletedJobResultData, GetCompletedJobResultResponse, GetCompletedJobResultMaybeData, GetCompletedJobResultMaybeResponse, DeleteCompletedJobData, DeleteCompletedJobResponse, CancelQueuedJobData, CancelQueuedJobResponse, CancelPersistentQueuedJobsData, CancelPersistentQueuedJobsResponse, ForceCancelQueuedJobData, ForceCancelQueuedJobResponse, CreateJobSignatureData, CreateJobSignatureResponse, GetResumeUrlsData, GetResumeUrlsResponse, GetSlackApprovalPayloadData, GetSlackApprovalPayloadResponse, ResumeSuspendedJobGetData, ResumeSuspendedJobGetResponse, ResumeSuspendedJobPostData, ResumeSuspendedJobPostResponse, SetFlowUserStateData, SetFlowUserStateResponse, GetFlowUserStateData, GetFlowUserStateResponse, ResumeSuspendedFlowAsOwnerData, ResumeSuspendedFlowAsOwnerResponse, CancelSuspendedJobGetData, CancelSuspendedJobGetResponse, CancelSuspendedJobPostData, CancelSuspendedJobPostResponse, GetSuspendedJobFlowData, GetSuspendedJobFlowResponse, PreviewScheduleData, PreviewScheduleResponse, CreateScheduleData, CreateScheduleResponse, UpdateScheduleData, UpdateScheduleResponse, SetScheduleEnabledData, SetScheduleEnabledResponse, DeleteScheduleData, DeleteScheduleResponse, GetScheduleData, GetScheduleResponse, ExistsScheduleData, ExistsScheduleResponse, ListSchedulesData, ListSchedulesResponse, ListSchedulesWithJobsData, ListSchedulesWithJobsResponse, SetDefaultErrorOrRecoveryHandlerData, SetDefaultErrorOrRecoveryHandlerResponse, CreateHttpTriggerData, CreateHttpTriggerResponse, UpdateHttpTriggerData, UpdateHttpTriggerResponse, DeleteHttpTriggerData, DeleteHttpTriggerResponse, GetHttpTriggerData, GetHttpTriggerResponse, ListHttpTriggersData, ListHttpTriggersResponse, ExistsHttpTriggerData, ExistsHttpTriggerResponse, ExistsRouteData, ExistsRouteResponse, CreateWebsocketTriggerData, CreateWebsocketTriggerResponse, UpdateWebsocketTriggerData, UpdateWebsocketTriggerResponse, DeleteWebsocketTriggerData, DeleteWebsocketTriggerResponse, GetWebsocketTriggerData, GetWebsocketTriggerResponse, ListWebsocketTriggersData, ListWebsocketTriggersResponse, ExistsWebsocketTriggerData, ExistsWebsocketTriggerResponse, SetWebsocketTriggerEnabledData, SetWebsocketTriggerEnabledResponse, TestWebsocketConnectionData, TestWebsocketConnectionResponse, CreateKafkaTriggerData, CreateKafkaTriggerResponse, UpdateKafkaTriggerData, UpdateKafkaTriggerResponse, DeleteKafkaTriggerData, DeleteKafkaTriggerResponse, GetKafkaTriggerData, GetKafkaTriggerResponse, ListKafkaTriggersData, ListKafkaTriggersResponse, ExistsKafkaTriggerData, ExistsKafkaTriggerResponse, SetKafkaTriggerEnabledData, SetKafkaTriggerEnabledResponse, TestKafkaConnectionData, TestKafkaConnectionResponse, CreateNatsTriggerData, CreateNatsTriggerResponse, UpdateNatsTriggerData, UpdateNatsTriggerResponse, DeleteNatsTriggerData, DeleteNatsTriggerResponse, GetNatsTriggerData, GetNatsTriggerResponse, ListNatsTriggersData, ListNatsTriggersResponse, ExistsNatsTriggerData, ExistsNatsTriggerResponse, SetNatsTriggerEnabledData, SetNatsTriggerEnabledResponse, TestNatsConnectionData, TestNatsConnectionResponse, CreateSqsTriggerData, CreateSqsTriggerResponse, UpdateSqsTriggerData, UpdateSqsTriggerResponse, DeleteSqsTriggerData, DeleteSqsTriggerResponse, GetSqsTriggerData, GetSqsTriggerResponse, ListSqsTriggersData, ListSqsTriggersResponse, ExistsSqsTriggerData, ExistsSqsTriggerResponse, SetSqsTriggerEnabledData, SetSqsTriggerEnabledResponse, TestSqsConnectionData, TestSqsConnectionResponse, CreateMqttTriggerData, CreateMqttTriggerResponse, UpdateMqttTriggerData, UpdateMqttTriggerResponse, DeleteMqttTriggerData, DeleteMqttTriggerResponse, GetMqttTriggerData, GetMqttTriggerResponse, ListMqttTriggersData, ListMqttTriggersResponse, ExistsMqttTriggerData, ExistsMqttTriggerResponse, SetMqttTriggerEnabledData, SetMqttTriggerEnabledResponse, TestMqttConnectionData, TestMqttConnectionResponse, IsValidPostgresConfigurationData, IsValidPostgresConfigurationResponse, CreateTemplateScriptData, CreateTemplateScriptResponse, GetTemplateScriptData, GetTemplateScriptResponse, ListPostgresReplicationSlotData, ListPostgresReplicationSlotResponse, CreatePostgresReplicationSlotData, CreatePostgresReplicationSlotResponse, DeletePostgresReplicationSlotData, DeletePostgresReplicationSlotResponse, ListPostgresPublicationData, ListPostgresPublicationResponse, GetPostgresPublicationData, GetPostgresPublicationResponse, CreatePostgresPublicationData, CreatePostgresPublicationResponse, UpdatePostgresPublicationData, UpdatePostgresPublicationResponse, DeletePostgresPublicationData, DeletePostgresPublicationResponse, CreatePostgresTriggerData, CreatePostgresTriggerResponse, UpdatePostgresTriggerData, UpdatePostgresTriggerResponse, DeletePostgresTriggerData, DeletePostgresTriggerResponse, GetPostgresTriggerData, GetPostgresTriggerResponse, ListPostgresTriggersData, ListPostgresTriggersResponse, ExistsPostgresTriggerData, ExistsPostgresTriggerResponse, SetPostgresTriggerEnabledData, SetPostgresTriggerEnabledResponse, TestPostgresConnectionData, TestPostgresConnectionResponse, ListInstanceGroupsResponse, GetInstanceGroupData, GetInstanceGroupResponse, CreateInstanceGroupData, CreateInstanceGroupResponse, UpdateInstanceGroupData, UpdateInstanceGroupResponse, DeleteInstanceGroupData, DeleteInstanceGroupResponse, AddUserToInstanceGroupData, AddUserToInstanceGroupResponse, RemoveUserFromInstanceGroupData, RemoveUserFromInstanceGroupResponse, ExportInstanceGroupsResponse, OverwriteInstanceGroupsData, OverwriteInstanceGroupsResponse, ListGroupsData, ListGroupsResponse, ListGroupNamesData, ListGroupNamesResponse, CreateGroupData, CreateGroupResponse, UpdateGroupData, UpdateGroupResponse, DeleteGroupData, DeleteGroupResponse, GetGroupData, GetGroupResponse, AddUserToGroupData, AddUserToGroupResponse, RemoveUserToGroupData, RemoveUserToGroupResponse, ListFoldersData, ListFoldersResponse, ListFolderNamesData, ListFolderNamesResponse, CreateFolderData, CreateFolderResponse, UpdateFolderData, UpdateFolderResponse, DeleteFolderData, DeleteFolderResponse, GetFolderData, GetFolderResponse, GetFolderUsageData, GetFolderUsageResponse, AddOwnerToFolderData, AddOwnerToFolderResponse, RemoveOwnerToFolderData, RemoveOwnerToFolderResponse, ListWorkersData, ListWorkersResponse, ExistsWorkerWithTagData, ExistsWorkerWithTagResponse, GetQueueMetricsResponse, GetCountsOfJobsWaitingPerTagResponse, ListWorkerGroupsResponse, GetConfigData, GetConfigResponse, UpdateConfigData, UpdateConfigResponse, DeleteConfigData, DeleteConfigResponse, ListConfigsResponse, ListAutoscalingEventsData, ListAutoscalingEventsResponse, GetGranularAclsData, GetGranularAclsResponse, AddGranularAclsData, AddGranularAclsResponse, RemoveGranularAclsData, RemoveGranularAclsResponse, SetCaptureConfigData, SetCaptureConfigResponse, PingCaptureConfigData, PingCaptureConfigResponse, GetCaptureConfigsData, GetCaptureConfigsResponse, ListCapturesData, ListCapturesResponse, GetCaptureData, GetCaptureResponse, DeleteCaptureData, DeleteCaptureResponse, StarData, StarResponse, UnstarData, UnstarResponse, GetInputHistoryData, GetInputHistoryResponse, GetArgsFromHistoryOrSavedInputData, GetArgsFromHistoryOrSavedInputResponse, ListInputsData, ListInputsResponse, CreateInputData, CreateInputResponse, UpdateInputData, UpdateInputResponse, DeleteInputData, DeleteInputResponse, DuckdbConnectionSettingsData, DuckdbConnectionSettingsResponse, DuckdbConnectionSettingsV2Data, DuckdbConnectionSettingsV2Response, PolarsConnectionSettingsData, PolarsConnectionSettingsResponse, PolarsConnectionSettingsV2Data, PolarsConnectionSettingsV2Response, S3ResourceInfoData, S3ResourceInfoResponse, DatasetStorageTestConnectionData, DatasetStorageTestConnectionResponse, ListStoredFilesData, ListStoredFilesResponse, LoadFileMetadataData, LoadFileMetadataResponse, LoadFilePreviewData, LoadFilePreviewResponse, LoadParquetPreviewData, LoadParquetPreviewResponse, LoadTableRowCountData, LoadTableRowCountResponse, LoadCsvPreviewData, LoadCsvPreviewResponse, DeleteS3FileData, DeleteS3FileResponse, MoveS3FileData, MoveS3FileResponse, FileUploadData, FileUploadResponse, FileDownloadData, FileDownloadResponse, FileDownloadParquetAsCsvData, FileDownloadParquetAsCsvResponse, GetJobMetricsData, GetJobMetricsResponse, SetJobProgressData, SetJobProgressResponse, GetJobProgressData, GetJobProgressResponse, ListLogFilesData, ListLogFilesResponse, GetLogFileData, GetLogFileResponse, ListConcurrencyGroupsResponse, DeleteConcurrencyGroupData, DeleteConcurrencyGroupResponse, GetConcurrencyKeyData, GetConcurrencyKeyResponse, ListExtendedJobsData, ListExtendedJobsResponse, SearchJobsIndexData, SearchJobsIndexResponse, SearchLogsIndexData, SearchLogsIndexResponse, CountSearchLogsIndexData, CountSearchLogsIndexResponse, ClearIndexData, ClearIndexResponse } from './types.gen.ts'; /** * get backend version @@ -1194,6 +1194,72 @@ export const editSlackCommand = (data: EditSlackCommandData): CancelablePromise< mediaType: 'application/json' }); }; +/** + * edit teams command + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceInvite + * @returns string status + * @throws ApiError + */ +export const editTeamsCommand = (data: EditTeamsCommandData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/workspaces/edit_teams_command', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * list available teams ids + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ +export const listAvailableTeamsIds = (data: ListAvailableTeamsIdsData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/workspaces/available_teams_ids', + path: { + workspace: data.workspace + } +}); }; + +/** + * list available teams channels + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ +export const listAvailableTeamsChannels = (data: ListAvailableTeamsChannelsData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/workspaces/available_teams_channels', + path: { + workspace: data.workspace + } +}); }; + +/** + * connect teams + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody connect teams + * @returns string status + * @throws ApiError + */ +export const connectTeams = (data: ConnectTeamsData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/workspaces/connect_teams', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * run a job that sends a message to Slack * @param data The data for the request. @@ -1212,6 +1278,24 @@ export const runSlackMessageTestJob = (data: RunSlackMessageTestJobData): Cancel mediaType: 'application/json' }); }; +/** + * run a job that sends a message to Teams + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody path to hub script to run and its corresponding args + * @returns unknown status + * @throws ApiError + */ +export const runTeamsMessageTestJob = (data: RunTeamsMessageTestJobData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/workspaces/run_teams_message_test_job', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * edit deploy to * @param data The data for the request. @@ -2065,6 +2149,21 @@ export const disconnectSlack = (data: DisconnectSlackData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/oauth/disconnect_teams', + path: { + workspace: data.workspace + } +}); }; + /** * list oauth logins * @returns unknown list of oauth and saml login clients @@ -2110,6 +2209,21 @@ export const syncTeams = (): CancelablePromise => { return __ url: '/teams/sync' }); }; +/** + * send update to Microsoft Teams activity + * Respond to a Microsoft Teams activity after a workspace command is run + * @param data The data for the request. + * @param data.requestBody + * @returns unknown Activity processed successfully + * @throws ApiError + */ +export const sendMessageToConversation = (data: SendMessageToConversationData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/teams/activities', + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * create resource * @param data The data for the request. @@ -6245,6 +6359,160 @@ export const testSqsConnection = (data: TestSqsConnectionData): CancelablePromis mediaType: 'application/json' }); }; +/** + * create mqtt trigger + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new mqtt trigger + * @returns string mqtt trigger created + * @throws ApiError + */ +export const createMqttTrigger = (data: CreateMqttTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/mqtt_triggers/create', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * update mqtt trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated trigger + * @returns string mqtt trigger updated + * @throws ApiError + */ +export const updateMqttTrigger = (data: UpdateMqttTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/mqtt_triggers/update/{path}', + path: { + workspace: data.workspace, + path: data.path + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * delete mqtt trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string mqtt trigger deleted + * @throws ApiError + */ +export const deleteMqttTrigger = (data: DeleteMqttTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'DELETE', + url: '/w/{workspace}/mqtt_triggers/delete/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * get mqtt trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns MqttTrigger mqtt trigger deleted + * @throws ApiError + */ +export const getMqttTrigger = (data: GetMqttTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/mqtt_triggers/get/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * list mqtt triggers + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.path filter by path + * @param data.isFlow + * @param data.pathStart + * @returns MqttTrigger mqtt trigger list + * @throws ApiError + */ +export const listMqttTriggers = (data: ListMqttTriggersData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/mqtt_triggers/list', + path: { + workspace: data.workspace + }, + query: { + page: data.page, + per_page: data.perPage, + path: data.path, + is_flow: data.isFlow, + path_start: data.pathStart + } +}); }; + +/** + * does mqtt trigger exists + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean mqtt trigger exists + * @throws ApiError + */ +export const existsMqttTrigger = (data: ExistsMqttTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/mqtt_triggers/exists/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * set enabled mqtt trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated mqtt trigger enable + * @returns string mqtt trigger enabled set + * @throws ApiError + */ +export const setMqttTriggerEnabled = (data: SetMqttTriggerEnabledData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/mqtt_triggers/setenabled/{path}', + path: { + workspace: data.workspace, + path: data.path + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * test mqtt connection + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody test mqtt connection + * @returns string successfuly connected to mqtt + * @throws ApiError + */ +export const testMqttConnection = (data: TestMqttConnectionData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/mqtt_triggers/test', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * check if postgres configuration is set to logical * @param data The data for the request. diff --git a/cli/gen/types.gen.ts b/cli/gen/types.gen.ts index 8fdbed7a57..f0de8f56f7 100644 --- a/cli/gen/types.gen.ts +++ b/cli/gen/types.gen.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -export type AIProvider = 'openai' | 'anthropic' | 'mistral' | 'deepseek' | 'groq' | 'openrouter' | 'customai'; +export type AIProvider = 'openai' | 'anthropic' | 'mistral' | 'deepseek' | 'googleai' | 'groq' | 'openrouter' | 'customai'; export type AIResource = { path: string; @@ -626,6 +626,7 @@ export type TriggersCount = { postgres_count?: number; kafka_count?: number; nats_count?: number; + mqtt_count?: number; sqs_count?: number; }; @@ -683,6 +684,75 @@ export type WebsocketTriggerInitialMessage = { }; }; +export type QoS = 0 | 1 | 2; + +export type CommonMqttConfig = { + will?: { + topic?: string; + message?: Array<(number)>; + qos?: QoS; + retain?: boolean; + }; +}; + +export type MqttV3Config = CommonMqttConfig & { + clean_session?: boolean; +}; + +export type MqttV5Config = CommonMqttConfig & { + clean_start?: boolean; + keep_alive?: number; + session_expiration?: number; + receive_maximum?: number; + maximum_packet_size?: number; +}; + +export type SubscribeTopic = { + qos: QoS; + topic: string; +}; + +export type MqttClientVersion = 'v3' | 'v5'; + +export type MqttTrigger = TriggerExtraProperty & { + mqtt_resource_path: string; + subscribe_topics: Array; + v3_config?: MqttV3Config; + v5_config?: MqttV5Config; + client_id?: string; + client_version?: MqttClientVersion; + server_id?: string; + last_server_ping?: string; + error?: string; + enabled: boolean; +}; + +export type NewMqttTrigger = { + mqtt_resource_path: string; + subscribe_topics: Array; + client_id?: string; + v3_config?: MqttV3Config; + v5_config?: MqttV5Config; + client_version?: MqttClientVersion; + path: string; + script_path: string; + is_flow: boolean; + enabled?: boolean; +}; + +export type EditMqttTrigger = { + mqtt_resource_path: string; + subscribe_topics: Array; + client_id?: string; + v3_config?: MqttV3Config; + v5_config?: MqttV5Config; + client_version?: MqttClientVersion; + path: string; + script_path: string; + is_flow: boolean; + enabled: boolean; +}; + export type SqsTrigger = TriggerExtraProperty & { queue_url: string; aws_resource_path: string; @@ -1287,7 +1357,7 @@ export type CriticalAlert = { workspace_id?: (string) | null; }; -export type CaptureTriggerKind = 'webhook' | 'http' | 'websocket' | 'kafka' | 'email' | 'nats' | 'postgres' | 'sqs'; +export type CaptureTriggerKind = 'webhook' | 'http' | 'websocket' | 'kafka' | 'email' | 'nats' | 'postgres' | 'sqs' | 'mqtt'; export type Capture = { trigger_kind: CaptureTriggerKind; @@ -2442,6 +2512,9 @@ export type GetSettingsResponse = ({ slack_name?: string; slack_team_id?: string; slack_command_script?: string; + teams_team_id?: string; + teams_command_script?: string; + teams_team_name?: string; auto_invite_domain?: string; auto_invite_operator?: boolean; auto_add?: boolean; @@ -2538,6 +2611,51 @@ export type EditSlackCommandData = { export type EditSlackCommandResponse = (string); +export type EditTeamsCommandData = { + /** + * WorkspaceInvite + */ + requestBody: { + slack_command_script?: string; + }; + workspace: string; +}; + +export type EditTeamsCommandResponse = (string); + +export type ListAvailableTeamsIdsData = { + workspace: string; +}; + +export type ListAvailableTeamsIdsResponse = (Array<{ + team_name?: string; + team_id?: string; +}>); + +export type ListAvailableTeamsChannelsData = { + workspace: string; +}; + +export type ListAvailableTeamsChannelsResponse = (Array<{ + channel_name?: string; + channel_id?: string; + service_url?: string; + tenant_id?: string; +}>); + +export type ConnectTeamsData = { + /** + * connect teams + */ + requestBody: { + team_id?: string; + team_name?: string; + }; + workspace: string; +}; + +export type ConnectTeamsResponse = (string); + export type RunSlackMessageTestJobData = { /** * path to hub script to run and its corresponding args @@ -2554,6 +2672,22 @@ export type RunSlackMessageTestJobResponse = ({ job_uuid?: string; }); +export type RunTeamsMessageTestJobData = { + /** + * path to hub script to run and its corresponding args + */ + requestBody: { + hub_script_path?: string; + channel?: string; + test_msg?: string; + }; + workspace: string; +}; + +export type RunTeamsMessageTestJobResponse = ({ + job_uuid?: string; +}); + export type EditDeployToData = { requestBody: { deploy_to?: string; @@ -2756,6 +2890,7 @@ export type GetUsedTriggersResponse = ({ kafka_used: boolean; nats_used: boolean; postgres_used: boolean; + mqtt_used: boolean; sqs_used: boolean; }); @@ -3063,6 +3198,12 @@ export type DisconnectSlackData = { export type DisconnectSlackResponse = (string); +export type DisconnectTeamsData = { + workspace: string; +}; + +export type DisconnectTeamsResponse = (string); + export type ListOauthLoginsResponse = ({ oauth: Array<{ type: string; @@ -3089,6 +3230,31 @@ export type GetOauthConnectResponse = ({ export type SyncTeamsResponse = (Array); +export type SendMessageToConversationData = { + requestBody: { + /** + * The ID of the Teams conversation/activity + */ + conversation_id: string; + /** + * Used for styling the card conditionally + */ + success?: boolean; + /** + * The message text to be sent in the Teams card + */ + text: string; + /** + * The card block to be sent in the Teams card + */ + card_block?: { + [key: string]: unknown; + }; + }; +}; + +export type SendMessageToConversationResponse = (unknown); + export type CreateResourceData = { /** * new resource @@ -5945,6 +6111,95 @@ export type TestSqsConnectionData = { export type TestSqsConnectionResponse = (string); +export type CreateMqttTriggerData = { + /** + * new mqtt trigger + */ + requestBody: NewMqttTrigger; + workspace: string; +}; + +export type CreateMqttTriggerResponse = (string); + +export type UpdateMqttTriggerData = { + path: string; + /** + * updated trigger + */ + requestBody: EditMqttTrigger; + workspace: string; +}; + +export type UpdateMqttTriggerResponse = (string); + +export type DeleteMqttTriggerData = { + path: string; + workspace: string; +}; + +export type DeleteMqttTriggerResponse = (string); + +export type GetMqttTriggerData = { + path: string; + workspace: string; +}; + +export type GetMqttTriggerResponse = (MqttTrigger); + +export type ListMqttTriggersData = { + isFlow?: boolean; + /** + * which page to return (start at 1, default 1) + */ + page?: number; + /** + * filter by path + */ + path?: string; + pathStart?: string; + /** + * number of items to return for a given page (default 30, max 100) + */ + perPage?: number; + workspace: string; +}; + +export type ListMqttTriggersResponse = (Array); + +export type ExistsMqttTriggerData = { + path: string; + workspace: string; +}; + +export type ExistsMqttTriggerResponse = (boolean); + +export type SetMqttTriggerEnabledData = { + path: string; + /** + * updated mqtt trigger enable + */ + requestBody: { + enabled: boolean; + }; + workspace: string; +}; + +export type SetMqttTriggerEnabledResponse = (string); + +export type TestMqttConnectionData = { + /** + * test mqtt connection + */ + requestBody: { + connection: { + [key: string]: unknown; + }; + }; + workspace: string; +}; + +export type TestMqttConnectionResponse = (string); + export type IsValidPostgresConfigurationData = { path: string; workspace: string; @@ -6479,7 +6734,7 @@ export type ListAutoscalingEventsData = { export type ListAutoscalingEventsResponse = (Array); export type GetGranularAclsData = { - kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'sqs_trigger'; + kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'sqs_trigger'; path: string; workspace: string; }; @@ -6489,7 +6744,7 @@ export type GetGranularAclsResponse = ({ }); export type AddGranularAclsData = { - kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'sqs_trigger'; + kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'sqs_trigger'; path: string; /** * acl to add @@ -6504,7 +6759,7 @@ export type AddGranularAclsData = { export type AddGranularAclsResponse = (string); export type RemoveGranularAclsData = { - kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'sqs_trigger'; + kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'sqs_trigger'; path: string; /** * acl to add diff --git a/cli/main.ts b/cli/main.ts index 168c37be9d..07f6b2fd31 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -62,7 +62,7 @@ export { // } // }); -export const VERSION = "1.467.1"; +export const VERSION = "1.475.0"; const command = new Command() .name("wmill") diff --git a/cli/script.ts b/cli/script.ts index 37ae635380..2d6295b50f 100644 --- a/cli/script.ts +++ b/cli/script.ts @@ -119,7 +119,7 @@ export async function findResourceFile(path: string) { if (validCandidates.length > 1) { throw new Error( "Found two resource files for the same resource" + - validCandidates.join(", ") + validCandidates.join(", ") ); } if (validCandidates.length < 1) { @@ -214,7 +214,9 @@ export async function handleFile( const endTime = performance.now(); bundleContent = out.outputFiles[0].text; log.info( - `Finished bundling ${path}: ${(bundleContent.length / 1024).toFixed(0)}kB (${(endTime - startTime).toFixed(0)}ms)` + `Finished bundling ${path}: ${(bundleContent.length / 1024).toFixed( + 0 + )}kB (${(endTime - startTime).toFixed(0)}ms)` ); } if (Array.isArray(codebase.assets) && codebase.assets.length > 0) { @@ -236,25 +238,30 @@ export async function handleFile( tarball.append(file); } const endTime = performance.now(); - log.info(`Finished creating tarball for ${path}: ${(tarball.size / 1024).toFixed(0)}kB (${(endTime - startTime).toFixed(0)}ms)`); + log.info( + `Finished creating tarball for ${path}: ${( + tarball.size / 1024 + ).toFixed(0)}kB (${(endTime - startTime).toFixed(0)}ms)` + ); bundleContent = tarball; } } - let typed = - opts?.skipScriptsMetadata ? undefined : - (await parseMetadataFile( - remotePath, - opts - ? { - ...opts, - path, - workspaceRemote: workspace, - schemaOnly: codebase ? true : undefined, - } - : undefined, - globalDeps, - codebases - ) + let typed = opts?.skipScriptsMetadata + ? undefined + : ( + await parseMetadataFile( + remotePath, + opts + ? { + ...opts, + path, + workspaceRemote: workspace, + schemaOnly: codebase ? true : undefined, + } + : undefined, + globalDeps, + codebases + ) )?.payload; const workspaceId = workspace.workspaceId; @@ -341,19 +348,19 @@ export async function handleFile( deepEqual(typed.schema, remote.schema) && typed.tag == remote.tag && (typed.ws_error_handler_muted ?? false) == - remote.ws_error_handler_muted && + remote.ws_error_handler_muted && typed.dedicated_worker == remote.dedicated_worker && typed.cache_ttl == remote.cache_ttl && typed.concurrency_time_window_s == - remote.concurrency_time_window_s && + remote.concurrency_time_window_s && typed.concurrent_limit == remote.concurrent_limit && Boolean(typed.restart_unless_cancelled) == - Boolean(remote.restart_unless_cancelled) && + Boolean(remote.restart_unless_cancelled) && Boolean(typed.visible_to_runner_only) == - Boolean(remote.visible_to_runner_only) && + Boolean(remote.visible_to_runner_only) && Boolean(typed.no_main_func) == Boolean(remote.no_main_func) && Boolean(typed.has_preprocessor) == - Boolean(remote.has_preprocessor) && + Boolean(remote.has_preprocessor) && typed.priority == Boolean(remote.priority) && typed.timeout == remote.timeout && //@ts-ignore @@ -366,23 +373,39 @@ export async function handleFile( } } - log.info(`Updating script ${remotePath} ...`); const body = { ...requestBodyCommon, parent_hash: remote.hash, }; - const execTime = await createScript(bundleContent, workspaceId, body, workspace); - log.info(colors.yellow.bold(`Updated script ${remotePath} (${execTime.toFixed(0)}ms)`)); + const execTime = await createScript( + bundleContent, + workspaceId, + body, + workspace + ); + log.info( + colors.yellow.bold( + `Updated script ${remotePath} (${execTime.toFixed(0)}ms)` + ) + ); } else { log.info(`Creating new script ${remotePath} ...`); const body = { ...requestBodyCommon, parent_hash: undefined, }; - const execTime = await createScript(bundleContent, workspaceId, body, workspace); - log.info(colors.yellow.bold(`Created new script ${remotePath} (${execTime.toFixed(0)}ms)`)); - + const execTime = await createScript( + bundleContent, + workspaceId, + body, + workspace + ); + log.info( + colors.yellow.bold( + `Created new script ${remotePath} (${execTime.toFixed(0)}ms)` + ) + ); } return true; } @@ -428,7 +451,8 @@ async function createScript( }); } catch (e: any) { throw Error( - `Script creation for ${body.path} with parent ${body.parent_hash + `Script creation for ${body.path} with parent ${ + body.parent_hash } was not successful: ${e.body ?? e.message} ` ); } @@ -454,7 +478,8 @@ async function createScript( }); if (req.status != 201) { throw Error( - `Script snapshot creation was not successful: ${req.status} - ${req.statusText + `Script snapshot creation was not successful: ${req.status} - ${ + req.statusText } - ${await req.text()} ` ); } @@ -466,8 +491,8 @@ export async function findContentFile(filePath: string) { const candidates = filePath.endsWith("script.json") ? exts.map((x) => filePath.replace(".script.json", x)) : filePath.endsWith("script.lock") - ? exts.map((x) => filePath.replace(".script.lock", x)) - : exts.map((x) => filePath.replace(".script.yaml", x)); + ? exts.map((x) => filePath.replace(".script.lock", x)) + : exts.map((x) => filePath.replace(".script.yaml", x)); const validCandidates = ( await Promise.all( @@ -486,7 +511,7 @@ export async function findContentFile(filePath: string) { if (validCandidates.length > 1) { throw new Error( "No content path given and more than one candidate found: " + - validCandidates.join(", ") + validCandidates.join(", ") ); } if (validCandidates.length < 1) { @@ -841,13 +866,13 @@ export async function findGlobalDeps(): Promise { const composers: { [key: string]: string } = {}; const els = await FSFSElement(Deno.cwd(), []); for await (const entry of readDirRecursiveWithIgnore((p, isDir) => { - p = "/" + p; + p = SEP + p; return ( !isDir && !( - p.endsWith("/package.json") || - p.endsWith("requirements.txt") || - p.endsWith("composer.json") + p.endsWith(SEP + "package.json") || + p.endsWith(SEP + "requirements.txt") || + p.endsWith(SEP + "composer.json") ) ); }, els)) { @@ -871,7 +896,9 @@ async function generateMetadata( } & SyncOptions, scriptPath: string | undefined ) { - log.info("This command only works for workspace scripts, for flows inline scripts use `wmill flow generate - locks`"); + log.info( + "This command only works for workspace scripts, for flows inline scripts use `wmill flow generate - locks`" + ); if (scriptPath == "") { scriptPath = undefined; } diff --git a/cli/sync.ts b/cli/sync.ts index 151244709b..55f5852960 100644 --- a/cli/sync.ts +++ b/cli/sync.ts @@ -596,6 +596,9 @@ export async function* readDirRecursiveWithIgnore( const e = stack.pop()!; yield e; for await (const e2 of e.c()) { + if (e2.path.startsWith(".git" + SEP)) { + continue; + } stack.push({ path: e2.path, ignored: e.ignored || ignore(e2.path, e2.isDirectory), @@ -641,6 +644,7 @@ export async function elementsToMap( path.endsWith(".kafka_trigger" + ext) || path.endsWith(".nats_trigger" + ext) || path.endsWith(".postgres_trigger" + ext) || + path.endsWith(".mqtt_trigger" + ext) || path.endsWith(".sqs_trigger" + ext)) ) continue; @@ -887,6 +891,7 @@ function getOrderFromPath(p: string) { typ == "kafka_trigger" || typ == "nats_trigger" || typ == "postgres_trigger" || + typ == "mqtt_trigger" || typ == "sqs_trigger" ) { return 8; @@ -1376,6 +1381,8 @@ export async function push(opts: GlobalOptions & SyncOptions) { const globalDeps = await findGlobalDeps(); + console.log("globalDeps", globalDeps); + const tracker: ChangeTracker = await buildTracker(changes); const staleScripts: string[] = []; @@ -1721,6 +1728,12 @@ export async function push(opts: GlobalOptions & SyncOptions) { path: removeSuffix(target, ".postgres_trigger.json"), }); break; + case "mqtt_trigger": + await wmill.deleteMqttTrigger({ + workspace: workspaceId, + path: removeSuffix(target, ".mqtt_trigger.json"), + }); + break; case "sqs_trigger": await wmill.deleteSqsTrigger({ workspace: workspaceId, diff --git a/cli/trigger.ts b/cli/trigger.ts index 39a95890b9..15e165e1c8 100644 --- a/cli/trigger.ts +++ b/cli/trigger.ts @@ -2,6 +2,7 @@ import * as wmill from "./gen/services.gen.ts"; import { HttpTrigger, KafkaTrigger, + MqttTrigger, NatsTrigger, PostgresTrigger, SqsTrigger, @@ -24,6 +25,7 @@ type Trigger = { kafka: KafkaTrigger; nats: NatsTrigger; postgres: PostgresTrigger; + mqtt: MqttTrigger; sqs: SqsTrigger; }; @@ -56,6 +58,7 @@ async function getTrigger( kafka: wmill.getKafkaTrigger, nats: wmill.getNatsTrigger, postgres: wmill.getPostgresTrigger, + mqtt: wmill.getMqttTrigger, sqs: wmill.getSqsTrigger, }; const triggerFunction = triggerFunctions[triggerType]; @@ -82,6 +85,7 @@ async function updateTrigger( kafka: wmill.updateKafkaTrigger, nats: wmill.updateNatsTrigger, postgres: wmill.updatePostgresTrigger, + mqtt: wmill.updateMqttTrigger, sqs: wmill.updateSqsTrigger, }; const triggerFunction = triggerFunctions[triggerType]; @@ -106,6 +110,7 @@ async function createTrigger( kafka: wmill.createKafkaTrigger, nats: wmill.createNatsTrigger, postgres: wmill.createPostgresTrigger, + mqtt: wmill.createMqttTrigger, sqs: wmill.createSqsTrigger, }; const triggerFunction = triggerFunctions[triggerType]; @@ -180,6 +185,9 @@ async function list(opts: GlobalOptions) { const postgresTriggers = await wmill.listPostgresTriggers({ workspace: workspace.workspaceId, }); + const mqttTriggers = await wmill.listMqttTriggers({ + workspace: workspace.workspaceId, + }); const sqsTriggers = await wmill.listSqsTriggers({ workspace: workspace.workspaceId, }); @@ -190,6 +198,7 @@ async function list(opts: GlobalOptions) { ...kafkaTriggers.map((x) => ({ path: x.path, kind: "kafka" })), ...natsTriggers.map((x) => ({ path: x.path, kind: "nats" })), ...postgresTriggers.map((x) => ({ path: x.path, kind: "postgres" })), + ...mqttTriggers.map((x) => ({ path: x.path, kind: "mqtt" })), ...sqsTriggers.map((x) => ({ path: x.path, kind: "sqs" })), ]; @@ -204,7 +213,7 @@ async function list(opts: GlobalOptions) { function checkIfValidTrigger(kind: string | undefined): kind is TriggerType { if ( kind && - ["http", "websocket", "kafka", "nats", "postgres", "sqs"].includes(kind) + ["http", "websocket", "kafka", "nats", "postgres", "mqtt", "sqs"].includes(kind) ) { return true; } else { diff --git a/cli/types.ts b/cli/types.ts index 18479edbcb..62f4f9bf53 100644 --- a/cli/types.ts +++ b/cli/types.ts @@ -150,6 +150,8 @@ export async function pushObj( await pushTrigger("nats", workspace, p, befObj, newObj); } else if (typeEnding === "postgres_trigger") { await pushTrigger("postgres", workspace, p, befObj, newObj); + } else if (typeEnding === "mqtt_trigger") { + await pushTrigger("mqtt", workspace, p, befObj, newObj); } else if (typeEnding === "sqs_trigger") { await pushTrigger("sqs", workspace, p, befObj, newObj); } else if (typeEnding === "user") { @@ -199,6 +201,7 @@ export function getTypeStrFromPath( | "kafka_trigger" | "nats_trigger" | "postgres_trigger" + | "mqtt_trigger" | "sqs_trigger" | "user" | "group" @@ -245,6 +248,7 @@ export function getTypeStrFromPath( typeEnding === "kafka_trigger" || typeEnding === "nats_trigger" || typeEnding === "postgres_trigger" || + typeEnding === "mqtt_trigger" || typeEnding === "sqs_trigger" || typeEnding === "user" || typeEnding === "group" || diff --git a/cli/user.ts b/cli/user.ts index fcd979222b..70e069f303 100644 --- a/cli/user.ts +++ b/cli/user.ts @@ -109,6 +109,8 @@ async function remove(opts: GlobalOptions, email: string) { async function createToken( opts: GlobalOptions & { email: string; password: string } ) { + await requireLogin(opts); + if (opts.email && opts.password) { log.info( "Token: " + @@ -119,9 +121,9 @@ async function createToken( }, })) ); + return; } - await requireLogin(opts); log.info("Token: " + (await wmill.createToken({ requestBody: {} }))); } diff --git a/cli/variable.ts b/cli/variable.ts index 8faa1cf319..789334c44c 100644 --- a/cli/variable.ts +++ b/cli/variable.ts @@ -161,7 +161,7 @@ async function add( undefined, { value, - is_secret: !opts.public, + is_secret: !opts.public && !opts.plainSecrets, description: "", }, opts.plainSecrets ?? false @@ -184,7 +184,9 @@ const command = new Command() "Create a new variable on the remote. This will update the variable if it already exists." ) .arguments(" ") - .option("--public", "Make a public variable") + .option("--plain-secrets", "Push secrets as plain text") + .option("--public", "Legacy option, use --plain-secrets instead") + .action(add as any); export default command; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ed218686e5..e24c708849 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,15 +1,15 @@ { "name": "windmill-components", - "version": "1.467.1", + "version": "1.475.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.467.1", + "version": "1.475.0", "license": "AGPL-3.0", "dependencies": { - "@anthropic-ai/sdk": "^0.32.1", + "@anthropic-ai/sdk": "^0.37.0", "@aws-crypto/sha256-js": "^4.0.0", "@codingame/monaco-vscode-configuration-service-override": "~11.1.2", "@codingame/monaco-vscode-standalone-css-language-features": "~11.1.2", @@ -70,7 +70,7 @@ "windmill-parser-wasm-csharp": "^1.437.1", "windmill-parser-wasm-go": "^1.429.0", "windmill-parser-wasm-php": "^1.429.0", - "windmill-parser-wasm-py": "^1.429.0", + "windmill-parser-wasm-py": "^1.467.1", "windmill-parser-wasm-regex": "^1.439.0", "windmill-parser-wasm-rust": "^1.429.0", "windmill-parser-wasm-ts": "^1.429.0", @@ -185,9 +185,9 @@ } }, "node_modules/@anthropic-ai/sdk": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.32.1.tgz", - "integrity": "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.37.0.tgz", + "integrity": "sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw==", "license": "MIT", "dependencies": { "@types/node": "^18.11.18", @@ -12594,9 +12594,9 @@ "integrity": "sha512-SGJAtNpfdRZftkGboxWsm/yQDnJBJodwPQUbX2cWk/aoNook6ULesZwsYtBC9WN1VH6TIskLiVPohMmu6jtXmw==" }, "node_modules/windmill-parser-wasm-py": { - "version": "1.429.0", - "resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.429.0.tgz", - "integrity": "sha512-cqc+tblQVHVrc8wNA4esVYD1Dv59XQJ4mHXFFPa8Lx5UjXv7FO/0VQxyQuRyXaP/V6J6DDy0oeN107eFNLxrBg==" + "version": "1.467.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.467.1.tgz", + "integrity": "sha512-Px/UvNjCCScf5V7B90PirxYe7Mn/zMnhk++cLuvlg08j45GR/mzFOIfC7Xk9e555ppX3gTlCk+auH4aIlG2tVw==" }, "node_modules/windmill-parser-wasm-regex": { "version": "1.439.0", diff --git a/frontend/package.json b/frontend/package.json index c46d653493..2c65561532 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.467.1", + "version": "1.475.0", "scripts": { "dev": "vite dev", "build": "vite build", @@ -9,7 +9,7 @@ "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. .", - "package": "./preprocess_pkg_toggle.sh && svelte-package -o package && ./preprocess_pkg_toggle.sh", + "package": "svelte-package -o package", "generate-backend-client": "openapi-ts --input ../backend/windmill-api/openapi.yaml --output ./src/lib/gen --useOptions --enums javascript --format false", "generate-backend-client-mac": "openapi-ts --input ../backend/windmill-api/openapi.yaml --output ./src/lib/gen --useOptions --enums javascript", "pretest": "tsc --incremental -p tests/tsconfig.json", @@ -84,7 +84,7 @@ }, "type": "module", "dependencies": { - "@anthropic-ai/sdk": "^0.32.1", + "@anthropic-ai/sdk": "^0.37.0", "@aws-crypto/sha256-js": "^4.0.0", "@codingame/monaco-vscode-configuration-service-override": "~11.1.2", "@codingame/monaco-vscode-standalone-css-language-features": "~11.1.2", @@ -120,8 +120,8 @@ "minimatch": "^10.0.1", "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~11.1.2", "monaco-editor-wrapper": "6.1.1", - "monaco-languageclient": "9.1.1", "monaco-graphql": "^1.6.0", + "monaco-languageclient": "9.1.1", "monaco-vim": "^0.4.1", "ol": "^7.4.0", "openai": "^4.57.2", @@ -145,7 +145,7 @@ "windmill-parser-wasm-csharp": "^1.437.1", "windmill-parser-wasm-go": "^1.429.0", "windmill-parser-wasm-php": "^1.429.0", - "windmill-parser-wasm-py": "^1.429.0", + "windmill-parser-wasm-py": "^1.467.1", "windmill-parser-wasm-regex": "^1.439.0", "windmill-parser-wasm-rust": "^1.429.0", "windmill-parser-wasm-ts": "^1.429.0", diff --git a/frontend/preprocess_pkg_toggle.sh b/frontend/preprocess_pkg_toggle.sh deleted file mode 100755 index a700cae919..0000000000 --- a/frontend/preprocess_pkg_toggle.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Define the file to modify -FILE="src/lib/components/apps/editor/AppEditorHeader.svelte" - -# Function to toggle commenting of a line -toggle_line_comment() { - local line="$1" - if grep -q "^//$line" "$FILE"; then - # Uncomment the line - sed -i "s|^//$line|$line|" "$FILE" - else - # Comment the line - sed -i "s|^$line|//$line|" "$FILE" - fi -} - -# Function to toggle commenting of the first matching block -toggle_first_block_comment() { - local start_marker="$1" - local end_marker="$2" - - # Escape the end marker for use in sed - local escaped_end_marker=$(echo "$end_marker" | sed 's|/|\\/|g') - - # Check if the block is already commented - if sed -n "/$start_marker/,/$escaped_end_marker/p" "$FILE" | grep -q "^$|| - }" "$FILE" - else - # Comment the block - sed -i "/$start_marker/,/$escaped_end_marker/{ - s|^|| - }" "$FILE" - fi -} - -# Define the line and block -LINE=" import UnsavedConfirmationModal " -BLOCK_START=" { - onclick_outside?: CompositionEventHandler - onpointerdown_outside?: (event: CustomEvent) => void - onpointerdown_connecting?: (event: CustomEvent) => void +declare namespace svelteHTML { + interface HTMLAttributes { + 'on:click_outside'?: (event: CustomEvent) => void + 'on:pointerdown_outside'?: (event: CustomEvent) => void + 'on:pointerdown_connecting'?: (event: CustomEvent) => void } } diff --git a/frontend/src/lib/components/AddUser.svelte b/frontend/src/lib/components/AddUser.svelte index 90ce1e8d31..6144d6e84b 100644 --- a/frontend/src/lib/components/AddUser.svelte +++ b/frontend/src/lib/components/AddUser.svelte @@ -2,7 +2,8 @@ import { createEventDispatcher } from 'svelte' import { globalEmailInvite, superadmin, workspaceStore } from '$lib/stores' import { SettingService, UserService, WorkspaceService } from '$lib/gen' - import { Button, Popup } from './common' + import { Button } from './common' + import Popover from './meltComponents/Popover.svelte' import { sendUserToast } from '$lib/toast' import { isCloudHosted } from '$lib/cloud' import { goto } from '$lib/navigation' @@ -72,64 +73,61 @@ let selected: 'operator' | 'developer' | 'admin' = 'developer' - - + + -
- Add a new user + +
+ Add a new user - Email - + Email + - {#if !automateUsernameCreation} - Username - - {/if} + {#if !automateUsernameCreation} + Username + + {/if} - Role - - Role + + + + + + -
- + on:click={() => { + addUser().then(() => { + // @ts-ignore + email = undefined + // @ts-ignore + username = undefined + }) + }} + disabled={email === undefined || (!automateUsernameCreation && username === undefined)} + > + Add + +
+
+ diff --git a/frontend/src/lib/components/ApiConnectForm.svelte b/frontend/src/lib/components/ApiConnectForm.svelte index 0e90ec56c7..b2fc7410e1 100644 --- a/frontend/src/lib/components/ApiConnectForm.svelte +++ b/frontend/src/lib/components/ApiConnectForm.svelte @@ -8,7 +8,7 @@ import Toggle from './Toggle.svelte' import TestConnection from './TestConnection.svelte' import SupabaseIcon from './icons/SupabaseIcon.svelte' - import Popup from './common/popup/Popup.svelte' + import Popover from './meltComponents/Popover.svelte' import Button from './common/button/Button.svelte' import { Loader2 } from 'lucide-svelte' @@ -129,13 +129,12 @@ /> {#if resourceType == 'postgresql'} - - + + +
+
+
+ + +
+ {#if !validConnectionString} +

Could not parse connection string

+ {/if}
- {#if !validConnectionString} -

Could not parse connection string

- {/if}
- -
+ + {/if} {#if resourceType == 'postgresql' && supabaseWizard}
{ + setCodeDisabled = false + }, 1000) + } + } onMount(() => { computeDefaultValue() @@ -713,7 +743,9 @@ dispatch('blur') }} code={JSON.stringify(value ?? defaultValue ?? { s3: '' }, null, 2)} - bind:value + on:changeValue={(e) => { + setNewValueFromCode(e.detail) + }} /> {/await}