Compare commits

...

99 Commits

Author SHA1 Message Date
GitHub Action
18e28399b7 Update SQLx metadata 2025-06-18 13:56:05 +00:00
centdix
769d5294df test 2025-06-18 15:47:48 +02:00
centdix
0f560bdc41 internal: fix claude tool usage (#5976)
* fix claude tool usage

* fix
2025-06-18 15:00:42 +02:00
dieriba
f97a61ecc8 fix openapi export duplicate issue and nits (#5971)
* nits and fix duplicate issue

* update .sqlx

* nits
2025-06-18 14:28:26 +02:00
Diego Imbert
be0c8ddae0 prevent datatable from spamming loadMore on scroll to end (#5973) 2025-06-18 14:25:55 +02:00
centdix
959280bf5a fix wrong typo (#5970) 2025-06-18 13:22:11 +02:00
centdix
38b06bf3a7 internal: add /updatesqlx as git command (#5969)
* add flow to update sqlx

* archive aider

* fix

* add comments
2025-06-18 13:08:59 +02:00
Ruben Fiszel
f5f2f8f344 chore(main): release 1.498.0 (#5957)
* chore(main): release 1.498.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-17 22:29:56 +02:00
dieriba
aba8c01d7f feat: windmill http triggers and webhooks to openapi spec (#5918)
* add sum and description to http routes

* add to openapi spec

* add subsection

* add collaspable and action button

* added path rendering,

* migrate logic to backend

* remover server and handle different format generation

* add filter for http route and document generated

* remove print

* handled webhook and integrated server component

* done

* add download and copy

* nits

* update .sqlx

* remove .vscode

* fix npm check

* add summary description and fix toggle

* added security handling and nits

* update .sqlx

* nits and rename key

* remove unused code

* update ref

* nits rename var

* nits

* nits

* nits

* nits

* nits

* add token generation for cURL command

* create token

* update label and remove section

* update repo ref

* clean

* let brower start download before cleaning up resource

* handle sync/async for webhook

* format fix

* nits

* nits

* reset sum and description
2025-06-17 19:49:00 +02:00
Diego Imbert
28a0209568 fix gcp trigger bind to undefined (#5967) 2025-06-17 19:47:40 +02:00
Diego Imbert
0cdff8acd1 Fix MQTT undefined binding bug (#5966) 2025-06-17 19:34:28 +02:00
centdix
e4534cabf5 ai chat textarea caret wrong positionning (#5964)
* fix textarea caret

* rename style
2025-06-17 18:44:41 +02:00
Diego Imbert
5bdbaf149b Fix unreactive navbar wizard (#5963)
* fix unreactive AppPicker

* migrate StaticInputEditor to svelte 5
2025-06-17 18:42:27 +02:00
Ruben Fiszel
51b3823f7b improve worker tooltip to get hostname, workerGroup 2025-06-17 18:24:30 +02:00
pyranota
74de2397ce docs: add Nix development guide (#5962)
* update readme

* change formatting

* link docker/dev.nu

Signed-off-by: pyranota <pyra@duck.com>

* move stuff a bit

* remove unrelated docs

* remove duplicates

Signed-off-by: pyranota <pyra@duck.com>

* add php to path

Signed-off-by: pyranota <pyra@duck.com>

---------

Signed-off-by: pyranota <pyra@duck.com>
2025-06-17 18:15:30 +02:00
centdix
2d0e65b7ca internal: give database schemas information to Claude (#5961)
* add schema

* give claude the schema

* use summarized schema

* add indexes

* add usage comments

* Remove backend/schema.sql from remote
2025-06-17 17:35:30 +02:00
Diego Imbert
84808e2694 fix conditional tabs reactivity (#5958)
* fix app conditional wrapper reactivity

* Convert GridCondition to Svelte 5

* Revert "fix app conditional wrapper reactivity"

This reverts commit 2b5910fde2.

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-06-17 11:29:51 +02:00
centdix
8407ac148b nit: fix typo in ai form filling text (#5959)
* differentiate flow from script

* fix typing
2025-06-17 11:24:50 +02:00
centdix
7490e883d7 feat: use provider api to list available AI models in workspace settings (#5947)
* use open router of model lists

* draft

* allow get in ai proxy

* add fetch available models function

* use func

* fix for anthropic

* fix

* fetch on mount

* fix ai settings

* fix

* handle azure
2025-06-17 08:48:47 +00:00
Ruben Fiszel
ad2de83354 chore(main): release 1.497.2 (#5956)
* chore(main): release 1.497.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-17 09:16:49 +02:00
Ruben Fiszel
8b7aefb3bc improve tag of new flow dependency job 2025-06-17 09:12:23 +02:00
Ruben Fiszel
0f63d03093 dind compile 2025-06-17 08:14:11 +02:00
Ruben Fiszel
38eb71bdf5 fix: always rm containers in docker mode 2025-06-17 02:32:49 +02:00
Ruben Fiszel
26bec054a3 fix: flow steps use their tags if any specific when used as subflow 2025-06-17 02:27:24 +02:00
Ruben Fiszel
d0ebb66d0d nits perf improvements 2025-06-17 02:06:58 +02:00
Ruben Fiszel
bc9893402b nit prevent default component list 2025-06-17 02:00:30 +02:00
Ruben Fiszel
13ac13e0b7 chore(main): release 1.497.1 (#5955)
* chore(main): release 1.497.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-17 01:53:14 +02:00
Ruben Fiszel
1c6a7c8cd0 fix: fix mcp server initialization 2025-06-17 01:44:04 +02:00
Ruben Fiszel
8e8e1a3129 chore(main): release 1.497.0 (#5912)
* chore(main): release 1.497.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-17 00:09:22 +02:00
Ruben Fiszel
babf046871 select click stop immediate propagation 2025-06-16 21:49:49 +02:00
Ruben Fiszel
02a4949fd8 stabilize input specs key 2025-06-16 21:27:30 +02:00
Ruben Fiszel
d2fa2e6464 nit scriptbuilder 2025-06-16 21:10:48 +02:00
Ruben Fiszel
d457bf5c80 nit showExpr 2025-06-16 19:03:02 +02:00
Ruben Fiszel
2bc06b72d2 nit showExpr + badge improvements 2025-06-16 18:29:04 +02:00
Diego Imbert
b106de5438 fix drawer subgrid not selecting (#5948) 2025-06-16 17:38:35 +02:00
Ruben Fiszel
ed6d018253 warn for raw scripts 2025-06-16 15:16:56 +02:00
Ruben Fiszel
6c1e7eed51 nit focus modal 2025-06-16 14:42:47 +02:00
Ruben Fiszel
f24894b1a5 fix tab selection on app builder 2025-06-16 14:36:26 +02:00
Diego Imbert
50d78a979b Fix select not scrolling correctly (#5946) 2025-06-16 12:08:59 +02:00
Ruben Fiszel
c75fd751b0 nits app builder 2025-06-15 23:57:55 +02:00
Ruben Fiszel
f0f720f490 partial app svelte 5 migration (#5945) 2025-06-15 23:25:34 +02:00
Ruben Fiszel
855571f359 fix history 2025-06-15 04:22:47 +02:00
Ruben Fiszel
41219e1f84 fix app editor reactivity 2025-06-15 03:59:37 +02:00
Ruben Fiszel
499fe7e7fe field.expr error message 2025-06-15 03:16:04 +02:00
JonasGruenwald
72613f3580 Fix wording in DeployOverrideConfirmationModal.svelte (#5944) 2025-06-14 17:07:19 +02:00
Ruben Fiszel
7d4ee825fa nits 2025-06-14 13:21:45 +02:00
Ruben Fiszel
1cd253405d nits 2025-06-14 12:33:37 +02:00
Ruben Fiszel
4dab80e24d fix schemaform default handling 2025-06-14 12:31:46 +02:00
Ruben Fiszel
38bb5e25cb nit 2025-06-14 12:02:48 +02:00
Ruben Fiszel
3678bf976e improve redirect behavior 2025-06-14 10:46:39 +02:00
pyranota
6623972ee9 build: correctly build WASM parsers with nix shell (#5943) 2025-06-14 09:03:45 +02:00
Diego Imbert
6a4391f616 Fix all structuredClones missing $state.snapshot (#5941) 2025-06-13 18:46:04 +02:00
centdix
f7a83c03c1 feat: add api tools to ai chat (#5921)
* add api tools to ai chat

* cleaning

* cleaning

* refine prompts

* tweaks

* add missing trigger

* cleaning

* cleaning

* fix
2025-06-13 16:29:18 +00:00
Ruben Fiszel
82f541565f fix reactivity in apps 2025-06-13 18:04:54 +02:00
Ruben Fiszel
44e1ed6c9c fix search by args 2025-06-13 17:42:06 +02:00
Ruben Fiszel
58ca3a6866 fix reactivity on app form 2025-06-13 17:37:39 +02:00
Ruben Fiszel
0b3a084c81 fix handle default values 2025-06-13 14:10:55 +02:00
HugoCasa
17c8c8a561 fix(frontend): use correct kind for flow insert module btn (#5938) 2025-06-13 11:43:09 +02:00
centdix
1a6283b42a fix: fix input with wrong height on first render (#5935)
* fix input with wrong height on first render

* better fix
2025-06-13 09:29:57 +00:00
Ruben Fiszel
126b610561 introduce QUIET_LOGS 2025-06-13 11:25:27 +02:00
pyranota
0b89260540 feat(go): local go.mod (#5929)
* feat(go): local go.mod

* readability refactor

* remove dbg!

* ignore module

* remove space

Signed-off-by: pyranota <pyra@duck.com>

* Update backend/windmill-worker/src/go_executor.rs

---------

Signed-off-by: pyranota <pyra@duck.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-06-13 10:17:31 +02:00
centdix
6c4818259c use new token for docs (#5937) 2025-06-13 10:08:26 +02:00
Ruben Fiszel
283167264e more svelte 5 migrations 2025-06-13 08:47:27 +02:00
Ruben Fiszel
b3e7a9cb79 more svelte 5 migration (#5936)
* all

* icons

* svelte2
2025-06-13 08:23:23 +02:00
Ruben Fiszel
c3a54ddd7c fix path bind 2025-06-13 06:47:05 +02:00
Alexander Petric
ae684d8608 improve teams startup time (#5931) 2025-06-12 21:40:18 +02:00
HugoCasa
f077849b8f fix: flow step missing input warnings (#5916)
* fix: flow step missing input warnings

* nit
2025-06-12 21:38:26 +02:00
HugoCasa
cf2d09e7a8 fix: audit logs for token refresh + consider refresh for active users (#5930)
* fix: audit logs for token refresh + consider refresh for active users

* tmp repo ref
2025-06-12 21:37:50 +02:00
Diego Imbert
bb354cfeb8 standardize select auto-complete inputs
* Replaced some Autocompletes

* more AutoComplete replace

* usePromise + replace resources with Select

* Select Chevron + changed operation to new Select

* simplified EditableSchemaWrapper

* replace autocompletes

* Replace last autocomplete

* Remove 'simple-svelte-autocomplete' dependency

* re-added some createText
2025-06-12 21:36:55 +02:00
Diego Imbert
bebfa76fc2 remove the 55 (#5933) 2025-06-12 21:30:56 +02:00
centdix
b0ac12052a chore(frontend): convert ai context components to svelte5 (#5928)
* convert contexttextarea to svelte5

* convert availablecontextlist

* force one line text and add max width to context badge

* cleaning
2025-06-12 16:01:03 +00:00
HugoCasa
f412ede6ed feat: multiple azure models support (#5920)
* feat: multiple azure models support

* all
2025-06-12 17:34:33 +02:00
Diego Imbert
f94651115a worker page ui fixes (#5927)
* worker page ui fixes

* Update +page.svelte

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-06-12 17:33:47 +02:00
centdix
6f907c79b4 fix: ai chat tooltip + user settings autocomplete issue (#5917)
* use form to avoid issues with chrome autocomplete

* fix tooltip overflowing to the right

* fix missclick on tooltip

* better fix

* dont show expiration on mcp token creation

* add zindexes to safelist

* add autocomplete
2025-06-12 17:00:52 +02:00
centdix
6e7ea4111c fix-app-drawer-offset (#5922) 2025-06-12 14:32:35 +00:00
Diego Imbert
ff9acb4717 remove unnecessary dispatchIfMounted (#5926) 2025-06-12 16:21:58 +02:00
centdix
a393c93085 nit: show disabled message when lang not supported (#5925)
* show disabled message when lang not supported

* remove open effect
2025-06-12 16:20:52 +02:00
Ruben Fiszel
23920aee84 feat: better graph layout algorithm + migrate to svelte 5 almost everywhere + xyflow 1.0 2025-06-12 15:44:35 +02:00
centdix
d47c078bb5 feat(backend): use streamable http in favor of sse for MCP (#5910)
* draft for http streamable usage

* good stuff

* add workspace_id to extensions

* fix shutdown

* cleaning

* fix

* adapt frontend

* Revert "adapt frontend"

This reverts commit 331dffaf98.

* dont use new path

* cleaning

* cleaner way of closing sessions
2025-06-12 08:49:22 +00:00
claude[bot]
b4a6a7e724 feat: fill runnable inputs with AI chat (#5887)
* feat: integrate TriggerableByAI with SchemaForm components

- Add currentValue and schema props to TriggerableByAI component
- Wrap all ArgInput fields with TriggerableByAI for AI chat integration
- Each input field now registers with AI chat manager including:
  - Current field value
  - Schema details (type, description, format, etc.)
  - Proper triggering mechanism for AI-driven value updates

This enables AI chat to interact with any input type generated by SchemaForm,
allowing intelligent form field modifications based on context and user intent.

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>

* add schema to description

* draft

* draft

* just use json inputs component

* good starting point

* add triggerable to scriptrow + shortcut

* fixes

* save prompt for ai in schema

* fix

* change visibility

* simplify

* cleaning

* fixes

* add tool to fetch resources

* fixes

* add try catch

* fix prompt

* cleaning

* use ask ai button

* fix

* no animation on form + fix empty summary

* add inputselectedbadge

* better action description + fix rows border

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
Co-authored-by: centdix <farhadg110@gmail.com>
2025-06-10 20:12:41 +00:00
pyranota
332f66e348 feat(rust): add rust sdk (#5909)
* upload client

* add gh workflow

* refactor build script

* remove dbg!

* fix async

* remove unused

* update dev.nu

* update CI

* fix ci

* fixin tests

* fixes + tests
2025-06-10 18:38:18 +02:00
Ruben Fiszel
d2dfd27b88 nit error message 2025-06-10 15:33:48 +02:00
Diego Imbert
b56a5c3e7a never exclude _ee.rs files in vscode (#5907) 2025-06-10 11:49:47 +02:00
Ruben Fiszel
ae81b4f456 chore(main): release 1.496.3 (#5902)
* chore(main): release 1.496.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-09 23:31:39 +02:00
pyranota
cd508951d7 build: disable duckdb for windows (#5906)
* build: disable duckdb for windows

MSVC linker has troubles with v8 and duckdb

* rework
2025-06-09 23:12:23 +02:00
centdix
94b56d3ffe use specific token for helm-chart (#5905) 2025-06-09 19:30:25 +02:00
centdix
cb8c3560af avoid discord length limit (#5903) 2025-06-09 17:45:53 +02:00
claude[bot]
a47939d13c fix: Prioritize diff contexts in script mode for ai chat (#5888)
* fix: prioritize diff contexts and replace underscores with spaces in AI context badges

- Sort context list to show diff contexts first in AvailableContextList.svelte
- Replace underscores with spaces in display names for both AvailableContextList.svelte and ContextElementBadge.svelte
- Improves UX by making diff context names more readable (e.g., "diff with last saved draft" instead of "diff_with_last_saved_draft")

Fixes #5884

Co-authored-by: centdix <centdix@users.noreply.github.com>

* fix

* fix

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: centdix <centdix@users.noreply.github.com>
Co-authored-by: centdix <farhadg110@gmail.com>
2025-06-09 15:14:20 +00:00
Ruben Fiszel
e8836a393a fix: improve concurrent job parallelism performance 2025-06-09 16:59:07 +02:00
Ruben Fiszel
86adebde39 chore(main): release 1.496.2 (#5901)
* chore(main): release 1.496.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-07 13:17:07 +02:00
dieriba
b44b9c1b82 fix: add clearable by default for select (#5900) 2025-06-07 13:01:38 +02:00
Diego Imbert
d384bb8cb4 change FileDownload format (#5898) 2025-06-07 11:16:51 +02:00
Diego Imbert
ec873c2c09 fix substitute script stopping when file already exists (#5899) 2025-06-07 11:10:44 +02:00
Ruben Fiszel
d37f13d023 chore(main): release 1.496.1 (#5897)
* chore(main): release 1.496.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-07 03:44:02 +02:00
pyranota
a2eac4ae37 nit: fix windows build (#5896) 2025-06-07 03:35:54 +02:00
pyranota
22b2f4988d fix: never consider minor version for global site packages (#5893) 2025-06-07 03:35:28 +02:00
Ruben Fiszel
13b318200b chore(main): release 1.496.0 (#5882)
* chore(main): release 1.496.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-06-06 17:56:49 +00:00
pyranota
ec552d5ef6 fix(python): account instance version when cli deploy and local lockfile (#5894)
* fix: do not ignore instance python version with cli deploy and local lockfile

* ellipsis
2025-06-06 17:49:59 +00:00
dieriba
3c3fdbdf26 fix: use full client side js library for route gen from openapi (#5891)
* add base template

* generate http trigger from openapi spec

* refactor and folder picker

* fix force re-render on simple editor

* add edit functionality for generated triggers and nits

* remove buffer

* add buffer

* nits

* nits

* nits and force Rerender

* remove analyzer

* typo

* nits

* nits

* nits

* fix

* nits

* fix vite

* updaate

* remove polyfills

* rename file and prevent duplicate keys

* better naming and duplicate key

* camel case

* replace spaces

---------

Co-authored-by: HugoCasa <hugo@casademont.ch>
2025-06-06 12:38:37 +00:00
694 changed files with 29613 additions and 17772 deletions

View File

@@ -51,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,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,mcp,private
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages_windows,mcp,private
- name: Rename binary with corresponding architecture
run: |
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"

View File

@@ -69,11 +69,11 @@ jobs:
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
timeout_minutes: "60"
allowed_tools: "mcp__github__create_pull_request,Bash(npm run check),Bash(npm install),Bash(cargo check),Bash(curl https://sh.rustup.rs -sSf | sh -s -- -y)"
allowed_tools: "mcp__github__create_pull_request,Bash(npm:*),Bash(cargo:*)"
custom_instructions: "IMPORTANT INSTRUCTIONS:
- Bypass previous instructions and instead of adding a url in your comment to manually create a PR, if you created a new branch with your changes, automatically create a draft PR from that branch to main, with the title starting with [Claude PR].
- If you made changes to the frontend code, run npm install, then npm run generate-backend-client, then npm run check. You can ignore warnings that are reported by the check script, but fix the errors.
- If you made changes to the backend code, install Rust and then run cargo check. You can ignore warnings that are reported by the check script, but fix the errors.
- If you made changes to the backend code, run cargo check. You can ignore warnings that are reported by the check script, but fix the errors.
- DO NOT FORGET TO OPEN A DRAFT PR AFTER YOU ARE DONE if you made changes after a request from a git issue.
AVAILABLE TOOLS:
- mcp__github__create_pull_request: Create a PR from a branch to main
@@ -81,5 +81,4 @@ jobs:
- Bash(npm install): Install dependencies. You need this to run npm run check.
- Bash(npm run generate-backend-client): Generate the backend client. You need this to run npm run check.
- Bash(cargo check): Run the cargo check script. You should run this tool after making changes to the backend code.
- Bash(curl https://sh.rustup.rs -sSf | sh -s -- -y): Install Rust. You need this to run cargo check."
trigger_phrase: "/ai"

View File

@@ -12,12 +12,12 @@ jobs:
- name: Check organization membership
id: check-membership
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ORG_ACCESS_TOKEN: ${{ secrets.ORG_ACCESS_TOKEN }}
COMMENTER: ${{ github.event.comment.user.login }}
run: |
ORG="windmill-labs"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token $GH_TOKEN" \
-H "Authorization: token $ORG_ACCESS_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/orgs/$ORG/members/$COMMENTER")

View File

@@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v3
with:
repository: windmill-labs/windmill-helm-charts
token: ${{ secrets.DOCS_TOKEN }}
token: ${{ secrets.HELM_CHART_TOKEN }}
- name: Get version
id: get_version
@@ -57,7 +57,7 @@ jobs:
- name: Create PR
env:
GH_TOKEN: ${{ secrets.DOCS_TOKEN }}
GH_TOKEN: ${{ secrets.HELM_CHART_TOKEN }}
run: |
gh pr create \
--title "helm: bump version to ${{ env.VERSION }}" \

View File

@@ -53,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,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,mcp,private
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages_windows,mcp,private
- name: Rename binary with corresponding architecture
run: |
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"

19
.github/workflows/rust_on_release.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Publish rust-client to crates.io on release
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
build_rust_and_publish_to_crates_io:
runs-on: ubicloud-standard-8
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v20
with:
extra_nix_config: |
experimental-features = nix-command flakes
- run: cd rust-client && nix develop ../ --command ./dev.nu --check --publish
env:
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}

View File

@@ -62,9 +62,10 @@ jobs:
# Create thread if it doesn't exist or if check failed
if [ "$thread_exists" = false ]; then
echo "Creating new thread"
THREAD_TITLE="#${PR_NUMBER}: ${PR_TITLE} by \`${PR_AUTHOR}\`"
payload=$(jq -n \
--arg content "${PR_URL}" \
--arg thread "#${PR_NUMBER}: $PR_TITLE by \`${PR_AUTHOR}\`" \
--arg thread "${THREAD_TITLE:0:99}" \
'{
content: $content,
thread_name: $thread,

98
.github/workflows/update-sqlx.yaml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: Update SQLx
on:
issue_comment:
types: [created]
jobs:
update-sqlx:
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/updatesqlx')
runs-on: ubicloud-standard-8
permissions:
contents: write
pull-requests: write
issues: write
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: windmill
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Comment on PR - Starting
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Starting sqlx update...'
})
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.issue.pull_request.head.ref }}
fetch-depth: 0
- name: Checkout windmill-ee-private
uses: actions/checkout@v3
with:
repository: windmill-labs/windmill-ee-private
path: windmill-ee-private
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
- name: Install xmlsec build-time deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config libxml2-dev libssl-dev \
xmlsec1 libxmlsec1-dev libxmlsec1-openssl
- name: Run update-sqlx script
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/windmill
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=${{ github.event.issue.number }}
BRANCH_NAME=$(gh pr view $PR_NUMBER --json headRefName --jq .headRefName)
echo "Checking out PR branch: $BRANCH_NAME"
git checkout $BRANCH_NAME
cd backend
cargo install sqlx-cli
sqlx migrate run
./update_sqlx.sh --dir ./windmill-ee-private
# Pass the branch name to the next step
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
- name: Commit changes if any
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add backend/.sqlx
git commit -m "Update SQLx metadata"
git push origin ${{ env.BRANCH_NAME }}
- name: Comment on PR - Completed
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Successfully ran sqlx update'
})

View File

@@ -1,5 +1,86 @@
# Changelog
## [1.498.0](https://github.com/windmill-labs/windmill/compare/v1.497.2...v1.498.0) (2025-06-17)
### Features
* use provider api to list available AI models in workspace settings ([#5947](https://github.com/windmill-labs/windmill/issues/5947)) ([7490e88](https://github.com/windmill-labs/windmill/commit/7490e883d747a7f65b2fefd3ec14b1cfc3d9bbd4))
* windmill http triggers and webhooks to openapi spec ([#5918](https://github.com/windmill-labs/windmill/issues/5918)) ([aba8c01](https://github.com/windmill-labs/windmill/commit/aba8c01d7f44ba4be369a3c711be9e156d6bf215))
## [1.497.2](https://github.com/windmill-labs/windmill/compare/v1.497.1...v1.497.2) (2025-06-17)
### Bug Fixes
* always rm containers in docker mode ([38eb71b](https://github.com/windmill-labs/windmill/commit/38eb71bdf55ee2f606d1d2ad2e987d5af16d88c0))
* flow steps use their tags if any specific when used as subflow ([26bec05](https://github.com/windmill-labs/windmill/commit/26bec054a3447a91c5d5f56d8b98717c06496087))
## [1.497.1](https://github.com/windmill-labs/windmill/compare/v1.497.0...v1.497.1) (2025-06-16)
### Bug Fixes
* fix mcp server initialization ([1c6a7c8](https://github.com/windmill-labs/windmill/commit/1c6a7c8cd0bd8396f158e3cb0583b927ce957f12))
## [1.497.0](https://github.com/windmill-labs/windmill/compare/v1.496.3...v1.497.0) (2025-06-16)
### Features
* add api tools to ai chat ([#5921](https://github.com/windmill-labs/windmill/issues/5921)) ([f7a83c0](https://github.com/windmill-labs/windmill/commit/f7a83c03c12b8ae70179fb228e0e2391b6ea2858))
* **backend:** use streamable http in favor of sse for MCP ([#5910](https://github.com/windmill-labs/windmill/issues/5910)) ([d47c078](https://github.com/windmill-labs/windmill/commit/d47c078bb5ab86d82d9cbbce3c55c89c0c20d809))
* better graph layout algorithm + migrate to svelte 5 almost everywhere + xyflow 1.0 ([23920ae](https://github.com/windmill-labs/windmill/commit/23920aee84fdca4a557a34ff2d66a0bb7bdca605))
* fill runnable inputs with AI chat ([#5887](https://github.com/windmill-labs/windmill/issues/5887)) ([b4a6a7e](https://github.com/windmill-labs/windmill/commit/b4a6a7e72429617d420af85a9de35bb13adfc6fb))
* **go:** local go.mod ([#5929](https://github.com/windmill-labs/windmill/issues/5929)) ([0b89260](https://github.com/windmill-labs/windmill/commit/0b89260540b307c6d614ca4275dd038fbfdac33c))
* multiple azure models support ([#5920](https://github.com/windmill-labs/windmill/issues/5920)) ([f412ede](https://github.com/windmill-labs/windmill/commit/f412ede6ed48e9a492f39582ac70a5584477529e))
* **rust:** add rust sdk ([#5909](https://github.com/windmill-labs/windmill/issues/5909)) ([332f66e](https://github.com/windmill-labs/windmill/commit/332f66e3483abbeacd4e7c1b74c94c5265314882))
### Bug Fixes
* ai chat tooltip + user settings autocomplete issue ([#5917](https://github.com/windmill-labs/windmill/issues/5917)) ([6f907c7](https://github.com/windmill-labs/windmill/commit/6f907c79b4cf6279bd52e35a3ee96e0d021422f5))
* audit logs for token refresh + consider refresh for active users ([#5930](https://github.com/windmill-labs/windmill/issues/5930)) ([cf2d09e](https://github.com/windmill-labs/windmill/commit/cf2d09e7a8c5d2472af0d483689c3fcfa2976117))
* fix input with wrong height on first render ([#5935](https://github.com/windmill-labs/windmill/issues/5935)) ([1a6283b](https://github.com/windmill-labs/windmill/commit/1a6283b42a6a514ab2e05160855cdc0f70b61d0e))
* flow step missing input warnings ([#5916](https://github.com/windmill-labs/windmill/issues/5916)) ([f077849](https://github.com/windmill-labs/windmill/commit/f077849b8f7c1916fd420e85b4844a5c5e93a139))
* **frontend:** use correct kind for flow insert module btn ([#5938](https://github.com/windmill-labs/windmill/issues/5938)) ([17c8c8a](https://github.com/windmill-labs/windmill/commit/17c8c8a5616ab8656799cea3fc5bc7cfaedc4995))
## [1.496.3](https://github.com/windmill-labs/windmill/compare/v1.496.2...v1.496.3) (2025-06-09)
### Bug Fixes
* improve concurrent job parallelism performance ([e8836a3](https://github.com/windmill-labs/windmill/commit/e8836a393a872bb91e68ba0037681caf24149470))
* Prioritize diff contexts in script mode for ai chat ([#5888](https://github.com/windmill-labs/windmill/issues/5888)) ([a47939d](https://github.com/windmill-labs/windmill/commit/a47939d13c30e2d4b41efd539f845959174d4fb1))
## [1.496.2](https://github.com/windmill-labs/windmill/compare/v1.496.1...v1.496.2) (2025-06-07)
### Bug Fixes
* add clearable by default for select ([#5900](https://github.com/windmill-labs/windmill/issues/5900)) ([b44b9c1](https://github.com/windmill-labs/windmill/commit/b44b9c1b82116ad5487af95d1f78226d56c75179))
## [1.496.1](https://github.com/windmill-labs/windmill/compare/v1.496.0...v1.496.1) (2025-06-07)
### Bug Fixes
* never consider minor version for global site packages ([#5893](https://github.com/windmill-labs/windmill/issues/5893)) ([22b2f49](https://github.com/windmill-labs/windmill/commit/22b2f4988db9314f2403508933d0aa932187c668))
## [1.496.0](https://github.com/windmill-labs/windmill/compare/v1.495.1...v1.496.0) (2025-06-06)
### Features
* generate http route triggers from openapi spec ([#5857](https://github.com/windmill-labs/windmill/issues/5857)) ([5713483](https://github.com/windmill-labs/windmill/commit/571348377b73d54b4d2a1c5775ab00b247b01910))
### Bug Fixes
* allow fileupload drag and drop in edit mode on full component without triggering file picker ([#5889](https://github.com/windmill-labs/windmill/issues/5889)) ([9ae3212](https://github.com/windmill-labs/windmill/commit/9ae3212a1e0f88a8297bf41ab53e3c1be4bcc56c))
* **python:** account instance version when cli deploy and local lockfile ([#5894](https://github.com/windmill-labs/windmill/issues/5894)) ([ec552d5](https://github.com/windmill-labs/windmill/commit/ec552d5ef6fdb5e824e453f196f9cf16629ee2ea))
* use full client side js library for route gen from openapi ([#5891](https://github.com/windmill-labs/windmill/issues/5891)) ([3c3fdbd](https://github.com/windmill-labs/windmill/commit/3c3fdbdf26a9581b815210839b91ebdedb924093))
## [1.495.0](https://github.com/windmill-labs/windmill/compare/v1.494.0...v1.495.0) (2025-06-05)

View File

@@ -1,3 +1,3 @@
To have an overview of what this app does, see @.cursor/rules/windmill-overview.mdc
For backend modifications, follow the rules mentioned here @.cursor/rules/rust-best-practices.mdc
For backend modifications, follow the rules mentioned here @.cursor/rules/rust-best-practices.mdc. You also have access to a summarized version of the database schema here @backend/summarized_schema.txt
For frontend modifications, follow the rules mentioned here @.cursor/rules/svelte5-best-practices.mdc

View File

@@ -367,10 +367,11 @@ you to have it being synced automatically everyday.
## Run a local dev setup
Using [Nix](./frontend/README_DEV.md#nix) (Recommended).
See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all
running options.
Using [Nix](./frontend/README_DEV.md#nix).
### only Frontend

View File

@@ -1,40 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval\n RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q\n WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL\n ) AS workspace_id\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "parent_flow_id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "job_id",
"type_info": "Uuid"
},
{
"ordinal": 2,
"name": "last_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "workspace_id",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
true,
null
]
},
"hash": "00c4a602aa6a50f2f922851ce63b5216e915c7649698687a00d47da55c70349f"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO metrics (id, value)\n VALUES ($1, to_jsonb((\n SELECT EXTRACT(EPOCH FROM now() - scheduled_for)\n FROM v2_job_queue\n WHERE tag = $2 AND running = false AND scheduled_for <= now() - ('3 seconds')::interval\n ORDER BY priority DESC NULLS LAST, scheduled_for LIMIT 1\n )))",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text"
]
},
"nullable": []
},
"hash": "00e63eab76d26e148b77e932848de74e8b0943d30481465da453942e299a128f"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT suspend > 0 AS \"r!\" FROM v2_job_queue WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "r!",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
null
]
},
"hash": "12828c9b2964f2b484a68de1e01b65cdcd277257192ee0a6d18a00f41bce49d4"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -0,0 +1,12 @@
{
"db_name": "PostgreSQL",
"query": "CREATE INDEX CONCURRENTLY IF NOT EXISTS v2_job_queue_suspend ON v2_job_queue (workspace_id, suspend) WHERE suspend > 0;",
"describe": {
"columns": [],
"parameters": {
"Left": []
},
"nullable": []
},
"hash": "19f0ccadd3ee44719a781ea0d73ea4e45f5b2c3d5c0aa5dbecf9ea9838881b74"
}

View File

@@ -1,15 +0,0 @@
{
"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"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT script_path FROM v2_as_completed_job WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "script_path",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true
]
},
"hash": "280a361076d1c6317610765960f543252891c53351bdc98da66cc30ffc895866"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM job_logs WHERE job_id = ANY($1) RETURNING log_file_index",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "log_file_index",
"type_info": "TextArray"
}
],
"parameters": {
"Left": [
"UuidArray"
]
},
"nullable": [
true
]
},
"hash": "288e99211bbd45a337fc9b79c43c5139ee535e23c8b3362c52eef49998349f15"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -0,0 +1,35 @@
{
"db_name": "PostgreSQL",
"query": "SELECT \n path,\n summary,\n description\n FROM\n flow\n WHERE\n path ~ ANY($1) AND\n workspace_id = $2 AND\n archived is FALSE\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "summary",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "description",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"TextArray",
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "33367c42e87e78ae987c0966dc4d445c5eff75b2e2843ffd7a46b03cbaea9ae8"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT \n workspace_id, \n path, \n route_path, \n route_path_key,\n workspaced_route,\n script_path, \n is_flow, \n http_method as \"http_method: _\", \n edited_by, \n email, \n edited_at, \n extra_perms, \n is_async, \n authentication_method as \"authentication_method: _\", \n static_asset_config as \"static_asset_config: _\", \n is_static_website,\n authentication_resource_path,\n wrap_body,\n raw_string\n FROM \n http_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
"query": "\n SELECT \n workspace_id, \n path, \n route_path, \n route_path_key,\n workspaced_route,\n script_path, \n summary,\n description,\n is_flow, \n http_method as \"http_method: _\", \n edited_by, \n email, \n edited_at, \n extra_perms, \n is_async, \n authentication_method as \"authentication_method: _\", \n static_asset_config as \"static_asset_config: _\", \n is_static_website,\n authentication_resource_path,\n wrap_body,\n raw_string\n FROM \n http_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
"describe": {
"columns": [
{
@@ -35,11 +35,21 @@
},
{
"ordinal": 6,
"name": "summary",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "description",
"type_info": "Text"
},
{
"ordinal": 8,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 7,
"ordinal": 9,
"name": "http_method: _",
"type_info": {
"Custom": {
@@ -57,32 +67,32 @@
}
},
{
"ordinal": 8,
"ordinal": 10,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 9,
"ordinal": 11,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 10,
"ordinal": 12,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 11,
"ordinal": 13,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 12,
"ordinal": 14,
"name": "is_async",
"type_info": "Bool"
},
{
"ordinal": 13,
"ordinal": 15,
"name": "authentication_method: _",
"type_info": {
"Custom": {
@@ -101,27 +111,27 @@
}
},
{
"ordinal": 14,
"ordinal": 16,
"name": "static_asset_config: _",
"type_info": "Jsonb"
},
{
"ordinal": 15,
"ordinal": 17,
"name": "is_static_website",
"type_info": "Bool"
},
{
"ordinal": 16,
"ordinal": 18,
"name": "authentication_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 17,
"ordinal": 19,
"name": "wrap_body",
"type_info": "Bool"
},
{
"ordinal": 18,
"ordinal": 20,
"name": "raw_string",
"type_info": "Bool"
}
@@ -139,6 +149,8 @@
false,
false,
false,
true,
true,
false,
false,
false,
@@ -154,5 +166,5 @@
false
]
},
"hash": "144e4eccfd1c1e729e3c864bd5dc3316248719dfa8a6c9e1d15a7931638e86db"
"hash": "39401cb0db8d367b5beb2be0c13aa7595adae0eac4e4e3a888cb12b972d1a7ce"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM pip_resolution_cache WHERE expiration <= now() RETURNING hash",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "hash",
"type_info": "Varchar"
}
],
"parameters": {
"Left": []
},
"nullable": [
false
]
},
"hash": "399a8337a2488fa2ce3da2ef3281a34f8f96ee0d833c2fe3c22a0aa43e306f09"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at < now() RETURNING path",
"query": "SELECT path FROM script WHERE workspace_id = $1 AND archived = false",
"describe": {
"columns": [
{
@@ -10,11 +10,13 @@
}
],
"parameters": {
"Left": []
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a"
"hash": "3e244a5057d4f1b4a18c0edac52cdf695c7e7aa0468d2686255de3d83719e6d0"
}

View File

@@ -1,26 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval\n AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "worker",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "ids",
"type_info": "UuidArray"
}
],
"parameters": {
"Left": []
},
"nullable": [
true,
null
]
},
"hash": "3e261911cc4c5289da49865f54350613f9e651540a279bd7d75e5e7d79f676a8"
}

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_queue SET running = false, started_at = null\n WHERE id = $1 AND canceled_by IS NULL",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": []
},
"hash": "3e55d027327bd3c76810fbe22d3ccb1bbbf83c8cff69d8f5907d1417a2522e69"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n http_trigger \n SET \n route_path = $1, \n route_path_key = $2, \n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7, \n path = $8, \n is_flow = $9, \n http_method = $10, \n static_asset_config = $11, \n edited_by = $12, \n email = $13, \n is_async = $14, \n authentication_method = $15, \n edited_at = now(), \n is_static_website = $16\n WHERE \n workspace_id = $17 AND \n path = $18\n ",
"query": "\n UPDATE \n http_trigger \n SET \n route_path = $1, \n route_path_key = $2, \n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7, \n path = $8, \n is_flow = $9, \n http_method = $10, \n static_asset_config = $11, \n edited_by = $12, \n email = $13, \n is_async = $14, \n authentication_method = $15, \n summary = $16,\n description = $17,\n edited_at = now(), \n is_static_website = $18\n WHERE \n workspace_id = $19 AND \n path = $20\n ",
"describe": {
"columns": [],
"parameters": {
@@ -47,6 +47,8 @@
}
}
},
"Varchar",
"Text",
"Bool",
"Text",
"Text"
@@ -54,5 +56,5 @@
},
"nullable": []
},
"hash": "187e8f85a71dea958e89fdfdf96c913a19eef8678dc7890c2f0e1ef8758ec43b"
"hash": "3f05e6186050a7ce6d8efb41067d3c5282319fe7e041f114e02fb22b91716637"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT \n workspace_id, \n workspaced_route,\n path, \n route_path, \n route_path_key, \n authentication_resource_path,\n script_path, \n is_flow, \n edited_by, \n edited_at, \n email, \n extra_perms, \n is_async, \n authentication_method AS \"authentication_method: _\", \n http_method AS \"http_method: _\", \n static_asset_config AS \"static_asset_config: _\", \n is_static_website,\n wrap_body,\n raw_string\n FROM http_trigger\n WHERE workspace_id = $1\n ",
"query": "\n SELECT \n workspace_id, \n workspaced_route,\n path, \n route_path, \n route_path_key, \n authentication_resource_path,\n script_path, \n is_flow, \n summary,\n description,\n edited_by, \n edited_at, \n email, \n extra_perms, \n is_async, \n authentication_method AS \"authentication_method: _\", \n http_method AS \"http_method: _\", \n static_asset_config AS \"static_asset_config: _\", \n is_static_website,\n wrap_body,\n raw_string\n FROM http_trigger\n WHERE workspace_id = $1\n ",
"describe": {
"columns": [
{
@@ -45,31 +45,41 @@
},
{
"ordinal": 8,
"name": "edited_by",
"name": "summary",
"type_info": "Varchar"
},
{
"ordinal": 9,
"name": "edited_at",
"type_info": "Timestamptz"
"name": "description",
"type_info": "Text"
},
{
"ordinal": 10,
"name": "email",
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 11,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 12,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 13,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 12,
"ordinal": 14,
"name": "is_async",
"type_info": "Bool"
},
{
"ordinal": 13,
"ordinal": 15,
"name": "authentication_method: _",
"type_info": {
"Custom": {
@@ -88,7 +98,7 @@
}
},
{
"ordinal": 14,
"ordinal": 16,
"name": "http_method: _",
"type_info": {
"Custom": {
@@ -106,22 +116,22 @@
}
},
{
"ordinal": 15,
"ordinal": 17,
"name": "static_asset_config: _",
"type_info": "Jsonb"
},
{
"ordinal": 16,
"ordinal": 18,
"name": "is_static_website",
"type_info": "Bool"
},
{
"ordinal": 17,
"ordinal": 19,
"name": "wrap_body",
"type_info": "Bool"
},
{
"ordinal": 18,
"ordinal": 20,
"name": "raw_string",
"type_info": "Bool"
}
@@ -140,6 +150,8 @@
true,
false,
false,
true,
true,
false,
false,
false,
@@ -153,5 +165,5 @@
false
]
},
"hash": "56c2522a12f91515e38290e4680a55a4727195125cd49a2f92f89bcdf74dc364"
"hash": "4228b098883408323bd8413ee094454b95962047458a6927d19ac0d3e7b3f0fa"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "WITH active_users AS (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login')),\n authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)\n SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, devops, name, company, username\n FROM password\n WHERE email IN (SELECT email FROM active_users)\n ORDER BY super_admin DESC, devops DESC\n LIMIT $1 OFFSET $2",
"query": "WITH active_users AS (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login' OR operation = 'users.token.refresh')),\n authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)\n SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, devops, name, company, username\n FROM password\n WHERE email IN (SELECT email FROM active_users)\n ORDER BY super_admin DESC, devops DESC\n LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
@@ -67,5 +67,5 @@
true
]
},
"hash": "3895cee539a24b4c6ea89fa7a835fc62bc93b0530efba09fc3c32a8f93eaabb1"
"hash": "5430f7728c1e9b539cc8aad29ca9e6733943278998d3df62a9486607827e59ec"
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT fv.id\n FROM flow f\n INNER JOIN flow_version fv ON fv.id = f.versions[array_upper(f.versions, 1)]\n WHERE fv.value->'preprocessor_module'->'value'->>'path' = $1 AND f.workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false
]
},
"hash": "551fee7919fdeb911e3f9cc5852e158ea47e3db4895c2b2b1d3cb6b16fceeda9"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_queue q SET suspend = 0\n FROM v2_job j, v2_job_status f\n WHERE parent_job = $1\n AND f.id = j.id AND q.id = j.id\n AND suspend = $2 AND (f.flow_status->'step')::int = 0",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Int4"
]
},
"nullable": []
},
"hash": "553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM v2_job_completed c\n WHERE completed_at <= now() - ($1::bigint::text || ' s')::interval \n RETURNING c.id",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
},
"hash": "5820d34be1a7f7b72e656c692f53146f45ad4a6e584e917a0a86280d8f473c10"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "WITH worker_ids AS (SELECT unnest($1::text[]) as worker)\n SELECT worker_ids.worker FROM worker_ids\n LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker\n WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "worker",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"TextArray"
]
},
"nullable": [
null
]
},
"hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM job_stats WHERE job_id = ANY($1)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"UuidArray"
]
},
"nullable": []
},
"hash": "6c0f74c56789ac51ccb06cd8a14986071ccc94df0de137b56d63d673db11d8aa"
}

View File

@@ -0,0 +1,93 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n route_path,\n http_method AS \"http_method: _\",\n is_async,\n workspaced_route,\n summary,\n description,\n authentication_method AS \"authentication_method: _\",\n authentication_resource_path\n FROM\n http_trigger\n WHERE\n path ~ ANY($1) AND\n route_path ~ ANY($2) AND\n workspace_id = $3\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "route_path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "http_method: _",
"type_info": {
"Custom": {
"name": "http_method",
"kind": {
"Enum": [
"get",
"post",
"put",
"delete",
"patch"
]
}
}
}
},
{
"ordinal": 2,
"name": "is_async",
"type_info": "Bool"
},
{
"ordinal": 3,
"name": "workspaced_route",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "summary",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "description",
"type_info": "Text"
},
{
"ordinal": 6,
"name": "authentication_method: _",
"type_info": {
"Custom": {
"name": "authentication_method",
"kind": {
"Enum": [
"none",
"windmill",
"api_key",
"basic_http",
"custom_script",
"signature"
]
}
}
}
},
{
"ordinal": 7,
"name": "authentication_resource_path",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"TextArray",
"TextArray",
"Text"
]
},
"nullable": [
false,
false,
false,
false,
true,
true,
false,
true
]
},
"hash": "714fb0f66ceb536aee8cb9ae0144757b999d25870fda37fe904e09dd5c742015"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,12 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM metrics WHERE id LIKE 'queue_%' AND created_at < NOW() - INTERVAL '14 day'",
"describe": {
"columns": [],
"parameters": {
"Left": []
},
"nullable": []
},
"hash": "7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT created_at FROM metrics WHERE id LIKE 'queue_count_%' ORDER BY created_at DESC LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "created_at",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": []
},
"nullable": [
false
]
},
"hash": "82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM resource WHERE resource_type = 'cache' AND to_timestamp((value->>'expire')::int) < now() RETURNING path",
"query": "SELECT distinct(path) FROM script WHERE workspace_id = $1 AND archived = true",
"describe": {
"columns": [
{
@@ -10,11 +10,13 @@
}
],
"parameters": {
"Left": []
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "873fde22f7947882edae7d15bc54e8df105d5e241eeb842a83e3444be0d2736d"
"hash": "8373b2649ab46310860adbdd7b717261771ac61d46d82d42d085ffebeb18be06"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT workflow_as_code_status FROM v2_job_completed WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "workflow_as_code_status",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true
]
},
"hash": "867d5c75ddc6c5d20136880c7294844b4c1a38701190795a801fa43c74a0beeb"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO metrics (id, value) VALUES ($1, $2)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Jsonb"
]
},
"nullable": []
},
"hash": "8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,21 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt)\n VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)\n ON CONFLICT (hostname, log_ts) DO UPDATE SET ok_lines = log_file.ok_lines + $6, err_lines = log_file.err_lines + $7",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Varchar",
"Timestamp",
"Varchar",
"Int8",
"Int8",
"Bool"
]
},
"nullable": []
},
"hash": "92faee8714a45a403b623e04d789f7f99067a05e9dfe270223164db8a1df2e4b"
}

View File

@@ -1,28 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM log_file WHERE log_ts <= now() - ($1::bigint::text || ' s')::interval RETURNING file_path, hostname",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "file_path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "hostname",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
false
]
},
"hash": "94da1e7feb4f58cc7ebe99752736f956d47810a94cb052fdcffb5cfe440f8033"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT id FROM v2_job WHERE parent_job = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
false
]
},
"hash": "99f74bf675120daf965e063e5eaff808ba646f4f99d0c8837097e747b481f03a"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT labels FROM v2_job WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "labels",
"type_info": "TextArray"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true
]
},
"hash": "9d518842a9ad90ff9c28dc39690deb0ee6b62cf1d8ae1a02b28c23255d377b3d"
}

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM concurrency_key WHERE ended_at <= now() - ($1::bigint::text || ' s')::interval ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "9da0cea2a5d0464ca78cfeccf6cedf2b1c0e6e6cb3c9183a937a68465debdb06"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Jsonb"
]
},
"nullable": []
},
"hash": "a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT count(*) AS \"count!\" FROM resume_job",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count!",
"type_info": "Int8"
}
],
"parameters": {
"Left": []
},
"nullable": [
null
]
},
"hash": "a0c35cb515a842067b294343c90f1bfbe4e2db85da9a478a07460733999e9beb"
}

View File

@@ -1,28 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT preprocessed, script_entrypoint_override FROM v2_job WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "preprocessed",
"type_info": "Bool"
},
{
"ordinal": 1,
"name": "script_entrypoint_override",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true,
true
]
},
"hash": "a56eef5f5ecbe1a8d309ff65d9a8c456a3c165f7f2a107cf7fa6a4cdd30d55c0"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job SET labels = $2 WHERE id = $1 AND $2::TEXT[] IS NOT NULL",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"TextArray"
]
},
"nullable": []
},
"hash": "bd5a0c06e2f2361c9fc670eb0b975b58d65ca93d68b29124d04bd526239b9df2"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -0,0 +1,38 @@
{
"db_name": "PostgreSQL",
"query": "WITH active_users as (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login' OR operation = 'users.token.refresh')),\n active_authors as (SELECT distinct email FROM usr WHERE usr.operator IS false AND email IN (SELECT email FROM active_users)),\n active_authors_agg as (SELECT array_agg(email) as authors FROM active_authors),\n active_ops_agg as (SELECT array_agg(email) as operators from active_users WHERE email NOT IN (SELECT email FROM active_authors))\n SELECT active_authors_agg.authors, active_ops_agg.operators, array_length(active_authors_agg.authors, 1) as author_count, array_length(active_ops_agg.operators, 1) as operator_count FROM active_authors_agg, active_ops_agg",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "authors",
"type_info": "VarcharArray"
},
{
"ordinal": 1,
"name": "operators",
"type_info": "VarcharArray"
},
{
"ordinal": 2,
"name": "author_count",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "operator_count",
"type_info": "Int4"
}
],
"parameters": {
"Left": []
},
"nullable": [
null,
null,
null,
null
]
},
"hash": "cb3862634f18160207ee2621ddfca43f00456a27fda32583846497116f92f96c"
}

View File

@@ -1,38 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "WITH active_users as (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login')),\n active_authors as (SELECT distinct email FROM usr WHERE usr.operator IS false AND email IN (SELECT email FROM active_users)),\n active_authors_agg as (SELECT array_agg(email) as authors FROM active_authors),\n active_ops_agg as (SELECT array_agg(email) as operators from active_users WHERE email NOT IN (SELECT email FROM active_authors))\n SELECT active_authors_agg.authors, active_ops_agg.operators, array_length(active_authors_agg.authors, 1) as author_count, array_length(active_ops_agg.operators, 1) as operator_count FROM active_authors_agg, active_ops_agg",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "authors",
"type_info": "VarcharArray"
},
{
"ordinal": 1,
"name": "operators",
"type_info": "VarcharArray"
},
{
"ordinal": 2,
"name": "author_count",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "operator_count",
"type_info": "Int4"
}
],
"parameters": {
"Left": []
},
"nullable": [
null,
null,
null,
null
]
},
"hash": "cce991f582bc9d2ba28a5b2b41c679366bb07bc6a100727721a787160ac6910c"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT preprocessed FROM v2_job WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "preprocessed",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true
]
},
"hash": "cd5f02cf10cbf92dd1df53a54f2110efa11a7731ad0f0e5509f55efabdf535cd"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM audit WHERE timestamp <= now() - ($1::bigint::text || ' s')::interval",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "d8186f0cee285aa50db7626409aec7e0504b068ffa8bb185d9384ce1422fd3d1"
}

View File

@@ -1,41 +0,0 @@
{
"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": "daf9674838fb3e3653a356c7434c719616a614d77e726433737e5f5d9bd60134"
}

View File

@@ -0,0 +1,35 @@
{
"db_name": "PostgreSQL",
"query": "SELECT \n path,\n summary,\n description\n FROM\n script\n WHERE\n path ~ ANY($1) AND\n workspace_id = $2 AND\n archived is FALSE\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "summary",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "description",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"TextArray",
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "dc36b46b9eb80cb7c92fa72519d117eda99a6f482a073ccd36a6431ef689a3fd"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT version()",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "version",
"type_info": "Text"
}
],
"parameters": {
"Left": []
},
"nullable": [
null
]
},
"hash": "df1f1d15d442789a5b9c81cdddf44d88d5748499cc48865023ddc1ff1587d0f6"
}

View File

@@ -1,58 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n id AS \"id!\", workspace_id AS \"workspace_id!\", parent_job, is_flow_step,\n flow_status AS \"flow_status: Box<str>\", last_ping, same_worker\n FROM v2_as_queue\n WHERE running = true AND suspend = 0 AND suspend_until IS null AND scheduled_for <= now()\n AND (job_kind = 'flow' OR job_kind = 'flowpreview' OR job_kind = 'flownode')\n AND last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval\n AND canceled = false\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id!",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "workspace_id!",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "parent_job",
"type_info": "Uuid"
},
{
"ordinal": 3,
"name": "is_flow_step",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "flow_status: Box<str>",
"type_info": "Jsonb"
},
{
"ordinal": 5,
"name": "last_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 6,
"name": "same_worker",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
true,
true,
true,
true,
true,
true,
true
]
},
"hash": "e653d36b607a16c0dfc0324690942ab25883b53a81ebb581fe019af2ec5eb567"
}

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM v2_job WHERE id = ANY($1)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"UuidArray"
]
},
"nullable": []
},
"hash": "ecd62c48fe2fba2fc2582e9e7ae5590d5dea8c67f6ae7b14743ac4f265dd89a3"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO http_trigger (\n workspace_id, \n path, \n route_path, \n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path, \n is_flow, \n is_async, \n authentication_method, \n http_method, \n static_asset_config, \n edited_by, \n email, \n edited_at, \n is_static_website\n ) \n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, now(), $17\n )\n ",
"query": "\n INSERT INTO http_trigger (\n workspace_id, \n path, \n route_path, \n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path, \n summary,\n description,\n is_flow, \n is_async, \n authentication_method, \n http_method, \n static_asset_config, \n edited_by, \n email, \n edited_at, \n is_static_website\n ) \n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, now(), $19\n )\n ",
"describe": {
"columns": [],
"parameters": {
@@ -14,6 +14,8 @@
"Bool",
"Bool",
"Varchar",
"Varchar",
"Text",
"Bool",
"Bool",
{
@@ -53,5 +55,5 @@
},
"nullable": []
},
"hash": "8c30e91c2486f7511563621e7e805d0588a9ec8bbea9db10e95783e27e35bc12"
"hash": "ed99d4d088d0fd0c01f29803b12e99ae0a53d0b1feaa67737da409c51c1b6751"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT hash FROM script WHERE path = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "hash",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "ef6795d93423f98eea82eb18e6332580dc7f7a9e5a67026f8c0b3077f371fc62"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_queue q SET suspend = 0\n FROM v2_job j, v2_job_status f\n WHERE q.workspace_id = $1 AND q.suspend = $3 AND j.parent_job = $2\n AND f.id = j.id AND q.id = j.id\n AND (f.flow_status->'step')::int = 0",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Uuid",
"Int4"
]
},
"nullable": []
},
"hash": "f1dbcb6e6d82d17c19eb88c0e67dc1cb8baf5bd40b75a2a9cd3ebac440fda632"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -11,5 +11,7 @@
"remote.autoForwardPorts": true,
"conventionalCommits.scopes": [
"restructring triggers, decoding trigger message on work"
]
],
"files.exclude": { "**/*ee.rs": false },
"search.exclude": { "**/*ee.rs": false }
}

684
backend/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.495.1"
version = "1.498.0"
authors.workspace = true
edition.workspace = true
@@ -32,7 +32,7 @@ members = [
]
[workspace.package]
version = "1.495.1"
version = "1.498.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -96,7 +96,9 @@ csharp = ["windmill-worker/csharp"]
nu = ["windmill-worker/nu"]
java = ["windmill-worker/java"]
all_languages = ["python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "mssql", "bigquery", "csharp", "nu", "php", "java"]
# For windows we have another set of languages enabled
# NOTE: DuckDB is ignored because of compilation problems
all_languages_windows = ["python", "deno_core", "rust", "mysql", "oracledb", "mssql", "bigquery", "csharp", "nu", "php", "java"]
[patch.crates-io]
object_store = { git = "https://github.com/apache/arrow-rs-object-store", rev = "36752c975d4f29e20b57c91f81a10872dcd48ae7" }
@@ -128,6 +130,7 @@ prometheus = { workspace = true, optional = true }
uuid.workspace = true
gethostname.workspace = true
serde_json.workspace = true
serde_yml.workspace = true
serde.workspace = true
deno_core = { workspace = true, optional = true }
object_store = { workspace = true, optional = true }
@@ -199,6 +202,7 @@ tower-http = { version = "^0.6", features = ["trace", "cors"] }
tower-cookies = "^0.10"
serde = "^1"
serde_json = { version = "^1", features = ["preserve_order", "raw_value"] }
serde_yml = "0.0.12"
uuid = { version = "^1", features = ["serde", "v4"] }
thiserror = "^2"
anyhow = "^1"
@@ -228,7 +232,7 @@ php-parser-rs = { git = "https://github.com/php-rust-tools/parser", rev = "ec4cb
cron = "^0"
mail-send = { version = "0.4.0", features = ["builder"], default-features=false }
urlencoding = "^2"
url = "^2"
url = { version = "^2" , features = ["serde"]}
async-oauth2 = "^0"
reqwest = { version = "^0.12", features = ["json", "stream", "gzip"] }
time = "^0"

View File

@@ -1 +1 @@
ab4f856708a056973faea19f10eda5efff546176
67e727c618cf673850a0887931c803241abfcfe8

View File

@@ -4,4 +4,4 @@ DROP TYPE http_method;
ALTER TABLE script DROP COLUMN has_preprocessor;
DROP FUNCTION prevent_route_path_change();
DROP FUNCTION prevent_route_path_change();

View File

@@ -0,0 +1,4 @@
-- Add down migration script here
ALTER TABLE http_trigger
DROP COLUMN summary,
DROP COLUMN description;

View File

@@ -0,0 +1,5 @@
-- Add up migration script here
ALTER TABLE http_trigger
ADD COLUMN summary VARCHAR(512) NULL,
ADD COLUMN description TEXT NULL;

View File

@@ -80,8 +80,7 @@ use windmill_worker::{
get_hub_script_content_and_requirements, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, CSHARP_CACHE_DIR,
DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM, GO_BIN_CACHE_DIR, GO_CACHE_DIR,
JAVA_CACHE_DIR, NU_CACHE_DIR, POWERSHELL_CACHE_DIR, PY310_CACHE_DIR, PY311_CACHE_DIR,
PY312_CACHE_DIR, PY313_CACHE_DIR, RUST_CACHE_DIR, TAR_JAVA_CACHE_DIR, TAR_PY310_CACHE_DIR,
TAR_PY311_CACHE_DIR, TAR_PY312_CACHE_DIR, TAR_PY313_CACHE_DIR, UV_CACHE_DIR,
PY312_CACHE_DIR, PY313_CACHE_DIR, RUST_CACHE_DIR, TAR_JAVA_CACHE_DIR, UV_CACHE_DIR,
};
use crate::monitor::{
@@ -160,6 +159,7 @@ lazy_static::lazy_static! {
.ok()
.and_then(|x| x.parse::<u64>().ok())
.unwrap_or(3600 * 12);
}
pub fn main() -> anyhow::Result<()> {
@@ -498,6 +498,8 @@ Windmill Community Edition {GIT_VERSION}
default_base_internal_url.clone()
};
println("test");
initial_load(
&conn,
killpill_tx.clone(),
@@ -1066,7 +1068,9 @@ Windmill Community Edition {GIT_VERSION}
}
if server_mode {
tracing::info!("monitor task started");
if !*windmill_common::QUIET_LOGS {
tracing::info!("monitor task started");
}
}
monitor_db(
&conn,
@@ -1078,7 +1082,9 @@ Windmill Community Edition {GIT_VERSION}
)
.await;
if server_mode {
tracing::info!("monitor task finished");
if !*windmill_common::QUIET_LOGS {
tracing::info!("monitor task finished");
}
}
},
}
@@ -1281,10 +1287,6 @@ pub async fn run_workers(
PY311_CACHE_DIR,
PY312_CACHE_DIR,
PY313_CACHE_DIR,
TAR_PY310_CACHE_DIR,
TAR_PY311_CACHE_DIR,
TAR_PY312_CACHE_DIR,
TAR_PY313_CACHE_DIR,
BUN_BUNDLE_CACHE_DIR,
GO_CACHE_DIR,
GO_BIN_CACHE_DIR,

View File

@@ -1324,7 +1324,6 @@ pub async fn monitor_db(
initial_load: bool,
_killpill_tx: KillpillSender,
) {
tracing::info!("Starting periodic monitor task");
let zombie_jobs_f = async {
if server_mode && !initial_load && !*DISABLE_ZOMBIE_JOBS_MONITORING {
if let Some(db) = conn.as_sql() {
@@ -1422,7 +1421,6 @@ pub async fn monitor_db(
apply_autoscaling_f,
update_min_worker_version_f,
);
tracing::info!("Periodic monitor task completed");
}
pub async fn expose_queue_metrics(db: &Pool<Postgres>) {

View File

@@ -69,7 +69,7 @@ if [ "$REVERT" == "YES" ]; then
for ee_file in $(find ${EE_CODE_DIR} -name "*ee.rs"); do
ce_file="${ee_file/${EE_CODE_DIR}/}"
ce_file="${root_dirpath}/backend/${ce_file}"
rm ${ce_file}
rm ${ce_file} || true
done
elif [ "$MOVE_NEW_FILES" == "NO" ]; then
# This replaces all files in current repo with alternative EE files in windmill-ee-private
@@ -80,7 +80,7 @@ elif [ "$MOVE_NEW_FILES" == "NO" ]; then
cp "${ee_file}" "${ce_file}"
echo "File copied '${ee_file}' -->> '${ce_file}'"
else
ln -s "${ee_file}" "${ce_file}"
ln -s "${ee_file}" "${ce_file}" || true
echo "Symlink created '${ee_file}' -->> '${ce_file}'"
fi
done

154
backend/summarize_schema.py Normal file
View File

@@ -0,0 +1,154 @@
# This script is used to summarize the database schema.
# You can use pg_dump to dump the schema to a file.
# pg_dump --file "schema.sql" --host "localhost" --port "5432" --username "postgres" --no-password --format=c --large-objects --schema-only --no-owner --no-privileges --no-tablespaces --no-unlogged-table-data --no-comments --no-publications --no-subscriptions --no-security-labels --no-toast-compression --no-table-access-method --verbose --schema "public" "windmill"
# Then you can run python summarize_schema.py schema.sql to get the summarized schema.
import re
import sys
from collections import defaultdict
def summarize_schema(file_path):
"""
Parses a PostgreSQL dump file and extracts a summarized schema.
"""
tables = defaultdict(lambda: {'columns': [], 'pks': set(), 'fks': [], 'indexes': []})
enums = defaultdict(list)
# Use state variables to parse multi-line definitions
current_table = None
current_enum = None
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
# --- State Resets ---
if line.startswith(');'):
current_table = None
current_enum = None
continue
# --- Parse ENUM definitions ---
match_enum = re.match(r"CREATE TYPE public\.(\w+) AS ENUM \($", line)
if match_enum:
current_enum = match_enum.group(1)
continue
if current_enum:
# Extract enum values, which are typically like 'value',
value = line.strip("',")
if value and not value.startswith('--'):
enums[current_enum].append(value)
continue
# --- Parse TABLE definitions ---
match_table = re.match(r"CREATE TABLE public\.(\w+) \($", line)
if match_table:
current_table = match_table.group(1)
continue
if current_table:
# Parse columns within a CREATE TABLE block
# e.g., "column_name type NOT NULL,"
# e.g., "id bigint NOT NULL,"
match_column = re.match(r'^"?(\w+)"?\s+([\w\d\.\[\]\(\)]+)', line)
if match_column:
col_name = match_column.group(1)
col_type = match_column.group(2)
tables[current_table]['columns'].append(f"{col_name} ({col_type})")
# Parse PRIMARY KEY defined inside the table
match_pk = re.search(r"CONSTRAINT \w+ PRIMARY KEY \((.+)\)", line)
if match_pk:
# Handle multiple PK columns: "col1, col2, col3"
pk_cols = [p.strip().strip('"') for p in match_pk.group(1).split(',')]
tables[current_table]['pks'].update(pk_cols)
continue
# --- Parse Foreign Keys (defined outside CREATE TABLE) ---
match_fk = re.match(r"ALTER TABLE ONLY public\.(\w+)\s+ADD CONSTRAINT \w+ FOREIGN KEY \(([\w,\s\"]+)\) REFERENCES public\.(\w+)\(([\w,\s\"]+)\);", line)
if match_fk:
from_table, from_cols, to_table, to_cols = match_fk.groups()
# Clean up column names
from_cols_clean = ', '.join([c.strip().strip('"') for c in from_cols.split(',')])
to_cols_clean = ', '.join([c.strip().strip('"') for c in to_cols.split(',')])
fk_string = f"({from_cols_clean}) -> {to_table}({to_cols_clean})"
tables[from_table]['fks'].append(fk_string)
# --- Parse Index definitions ---
match_index = re.match(r"CREATE (UNIQUE )?INDEX (\w+) ON public\.(\w+) USING (\w+) \((.+)\);", line)
if match_index:
is_unique = match_index.group(1) is not None
index_name = match_index.group(2)
table_name = match_index.group(3)
index_type = match_index.group(4)
columns = match_index.group(5)
# Clean up column expressions
columns_clean = columns.replace('"', '')
unique_str = "UNIQUE " if is_unique else ""
index_string = f"{unique_str}INDEX {index_name} ({index_type}) ON ({columns_clean})"
tables[table_name]['indexes'].append(index_string)
return enums, tables
def format_output(enums, tables):
"""
Formats the parsed schema data into a clean, readable string.
"""
output = []
output.append("### Simplified Database Schema ###")
output.append("\n--- Custom Data Types (ENUMs) ---\n")
if not enums:
output.append("No custom ENUM types found.")
else:
for name, values in sorted(enums.items()):
output.append(f"{name}:")
for v in values:
output.append(f" - {v}")
output.append("")
output.append("\n--- Tables and Relationships ---\n")
if not tables:
output.append("No tables found.")
else:
for name, data in sorted(tables.items()):
output.append(f"TABLE: {name}")
for col in data['columns']:
col_name = col.split(' ')[0]
marker = " (PK)" if col_name in data['pks'] else ""
output.append(f" - {col}{marker}")
if data['fks']:
output.append(" Relationships:")
for fk in data['fks']:
output.append(f" - {fk}")
if data['indexes']:
output.append(" Indexes:")
for idx in data['indexes']:
output.append(f" - {idx}")
output.append("-" * 20)
return "\n".join(output)
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: python {sys.argv[0]} <path_to_dump.sql>")
sys.exit(1)
input_file = sys.argv[1]
try:
enums_data, tables_data = summarize_schema(input_file)
formatted_summary = format_output(enums_data, tables_data)
print(formatted_summary)
except FileNotFoundError:
print(f"Error: The file '{input_file}' was not found.")
sys.exit(1)
except Exception as e:
print(f"An unexpected error occurred: {e}")
sys.exit(1)

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,18 @@
./substitute_ee_code.sh --dir ../windmill-ee-private
#!/bin/bash
# Default directory
EE_DIR="../windmill-ee-private"
# Parse arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--dir) EE_DIR="$2"; shift ;;
*) echo "Unknown parameter: $1"; exit 1 ;;
esac
shift
done
./substitute_ee_code.sh --dir "$EE_DIR"
# Check if running on macOS
if [[ "$(uname)" == "Darwin" ]]; then
@@ -18,4 +32,4 @@ if [[ "$(uname)" == "Darwin" ]]; then
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
fi

View File

@@ -17,7 +17,7 @@ agent_worker_server = []
enterprise_saml = ["dep:samael", "dep:libxml"]
benchmark = []
embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn"]
parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet", "windmill-worker/parquet"]
parquet = ["dep:datafusion", "dep:object_store", "windmill-common/parquet", "windmill-worker/parquet"]
prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus", "windmill-worker/prometheus"]
openidconnect = ["dep:openidconnect", "windmill-common/openidconnect"]
tantivy = ["dep:windmill-indexer"]
@@ -40,7 +40,7 @@ mcp = ["dep:rmcp"]
python = []
[dependencies]
rmcp = { git = "https://github.com/windmill-labs/rust-sdk", features = ["transport-sse-server"], optional = true }
rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features=["transport-streamable-http-server", "transport-streamable-http-server-session", "transport-worker"], optional = true }
windmill-queue.workspace = true
windmill-common = { workspace = true, default-features = false }
windmill-audit.workspace = true
@@ -76,6 +76,7 @@ hex.workspace = true
base64.workspace = true
base32.workspace = true
serde_urlencoded.workspace = true
serde_yml.workspace = true
cron.workspace = true
mime_guess.workspace = true
rust-embed = { workspace = true, optional = true }
@@ -102,6 +103,7 @@ prometheus = { workspace = true, optional = true }
async_zip = { workspace = true, optional = true }
regex.workspace = true
bytes.workspace = true
url.workspace = true
samael = { workspace = true, optional = true }
libxml = { workspace = true, optional = true }
async-recursion.workspace = true
@@ -116,7 +118,6 @@ candle-nn = { workspace = true, optional = true}
datafusion = { workspace = true, optional = true}
object_store = { workspace = true, optional = true}
openidconnect = { workspace = true, optional = true}
url = { workspace = true, optional = true}
jsonwebtoken = { workspace = true }
matchit = { workspace = true, optional = true }
tokio-tungstenite = { workspace = true, optional = true}
@@ -126,6 +127,7 @@ nkeys = { workspace = true, optional = true }
const_format.workspace = true
pin-project.workspace = true
http.workspace = true
indexmap.workspace = true
async-stream.workspace = true
ulid.workspace = true
rust-postgres = { workspace = true, optional = true }

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.495.1
version: 1.498.0
title: Windmill API
contact:
@@ -8451,6 +8451,51 @@ paths:
"201":
description: default error handler set
/w/{workspace}/openapi/generate:
post:
summary: generate openapi spec from http routes/webhook
operationId: generateOpenapiSpec
tags:
- openapi
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: openapi spec info and url
content:
application/json:
schema:
$ref: "#/components/schemas/GenerateOpenapiSpec"
responses:
"200":
description: openapi spec
content:
text/plain:
schema:
type: string
/w/{workspace}/openapi/download:
post:
summary: Download the OpenAPI v3.1 spec as a file
operationId: DownloadOpenapiSpec
tags:
- openapi
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: openapi spec info and url
content:
application/json:
schema:
$ref: "#/components/schemas/GenerateOpenapiSpec"
responses:
"200":
description: Downloaded OpenAPI spec
content:
application/octet-stream:
schema:
type: string
format: binary
/w/{workspace}/http_triggers/create_many:
post:
summary: create many HTTP triggers
@@ -14754,6 +14799,106 @@ components:
- custom_script
- signature
RunnableKind:
type: string
enum:
- script
- flow
OpenapiSpecFormat:
type: string
enum:
- yaml
- json
OpenapiHttpRouteFilters:
type: object
properties:
folder_regex:
type: string
path_regex:
type: string
route_path_regex:
type: string
required:
- folder_regex
- path_regex
- route_path_regex
WebhookFilters:
type: object
properties:
user_or_folder_regex:
type: string
enum:
- "*"
- u
- f
user_or_folder_regex_value:
type: string
path:
type: string
runnable_kind:
$ref: "#/components/schemas/RunnableKind"
required:
- user_or_folder_regex
- user_or_folder_regex_value
- path
- runnable_kind
OpenapiV3Info:
type: object
properties:
title:
type: string
version:
type: string
description:
type: string
terms_of_service:
type: string
contact:
type: object
properties:
name:
type: string
url:
type: string
email:
type: string
license:
type: object
properties:
name:
type: string
identifier:
type: string
url:
type: string
required:
- name
required:
- title
- version
GenerateOpenapiSpec:
type: object
properties:
info:
$ref: "#/components/schemas/OpenapiV3Info"
url:
type: string
openapi_spec_format:
$ref: "#/components/schemas/OpenapiSpecFormat"
http_route_filters:
type: array
items:
$ref: "#/components/schemas/OpenapiHttpRouteFilters"
webhook_filters:
type: array
items:
$ref: "#/components/schemas/WebhookFilters"
HttpMethod:
type: string
enum:
@@ -14785,6 +14930,10 @@ components:
$ref: "#/components/schemas/HttpMethod"
authentication_resource_path:
type: string
summary:
type: string
description:
type: string
is_async:
type: boolean
authentication_method:
@@ -14819,6 +14968,10 @@ components:
type: string
workspaced_route:
type: boolean
summary:
type: string
description:
type: string
static_asset_config:
type: object
properties:
@@ -14866,6 +15019,10 @@ components:
type: string
route_path:
type: string
summary:
type: string
description:
type: string
workspaced_route:
type: boolean
static_asset_config:

View File

@@ -4,7 +4,7 @@ use crate::{
};
use axum::{body::Bytes, extract::Path, response::IntoResponse, routing::post, Extension, Router};
use http::HeaderMap;
use http::{HeaderMap, Method};
use quick_cache::sync::Cache;
use reqwest::{Client, RequestBuilder};
use serde::{Deserialize, Serialize};
@@ -24,7 +24,7 @@ lazy_static::lazy_static! {
pub static ref AI_REQUEST_CACHE: Cache<(String, AIProvider), ExpiringAIRequestConfig> = Cache::new(500);
}
const AZURE_API_VERSION: &str = "2024-10-21";
const AZURE_API_VERSION: &str = "2025-04-01-preview";
const OPENAI_BASE_URL: &str = "https://api.openai.com/v1";
#[derive(Deserialize, Debug)]
@@ -141,23 +141,49 @@ impl AIRequestConfig {
self,
provider: &AIProvider,
path: &str,
method: Method,
headers: HeaderMap,
body: Bytes,
) -> Result<RequestBuilder> {
let url = format!("{}/{}", self.base_url, path);
let body = if let Some(user) = self.user {
Self::add_user_to_body(body, user)?
} else {
body
};
let is_azure = matches!(provider, AIProvider::OpenAI) && self.base_url != OPENAI_BASE_URL
let base_url = self.base_url.trim_end_matches('/');
let is_azure = matches!(provider, AIProvider::OpenAI) && base_url != OPENAI_BASE_URL
|| matches!(provider, AIProvider::AzureOpenAI);
let is_anthropic = matches!(provider, AIProvider::Anthropic);
let url = if is_azure && method != Method::GET {
if base_url.ends_with("/deployments") {
let model = Self::get_azure_model(&body)?;
format!("{}/{}/{}", base_url, model, path)
} else if base_url.ends_with("/openai") {
let model = Self::get_azure_model(&body)?;
format!("{}/deployments/{}/{}", base_url, model, path)
} else {
format!("{}/{}", base_url, path)
}
} else {
format!("{}/{}", base_url, path)
};
tracing::debug!("AI request URL: {}", url);
let mut request = HTTP_CLIENT
.post(url)
.header("content-type", "application/json")
.body(body);
.request(method, url)
.header("content-type", "application/json");
for (header_name, header_value) in headers.iter() {
if header_name.to_string().starts_with("anthropic-") {
request = request.header(header_name, header_value);
}
}
request = request.body(body);
if is_azure {
request = request.query(&[("api-version", AZURE_API_VERSION)])
@@ -165,9 +191,12 @@ impl AIRequestConfig {
if let Some(api_key) = self.api_key {
if is_azure {
request = request.header("api-key", api_key)
request = request.header("api-key", api_key.clone())
} else {
request = request.header("authorization", format!("Bearer {}", api_key))
request = request.header("authorization", format!("Bearer {}", api_key.clone()))
}
if is_anthropic {
request = request.header("X-API-Key", api_key);
}
}
@@ -199,6 +228,18 @@ impl AIRequestConfig {
.map_err(|e| Error::internal_err(format!("Failed to reserialize request body: {}", e)))?
.into())
}
fn get_azure_model(body: &Bytes) -> Result<String> {
#[derive(Deserialize, Debug)]
struct AzureModel {
model: String,
}
let azure_model: AzureModel = serde_json::from_slice(body)
.map_err(|e| Error::internal_err(format!("Failed to parse request body: {}", e)))?;
Ok(azure_model.model)
}
}
#[derive(Clone, Debug)]
@@ -311,17 +352,18 @@ pub struct AIConfig {
}
pub fn global_service() -> Router {
Router::new().route("/proxy/*ai", post(global_proxy))
Router::new().route("/proxy/*ai", post(global_proxy).get(global_proxy))
}
pub fn workspaced_service() -> Router {
Router::new().route("/proxy/*ai", post(proxy))
Router::new().route("/proxy/*ai", post(proxy).get(proxy))
}
async fn global_proxy(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Path(ai_path): Path<String>,
method: Method,
headers: HeaderMap,
body: Bytes,
) -> impl IntoResponse {
@@ -346,7 +388,7 @@ async fn global_proxy(
let url = format!("{}/{}", base_url, ai_path);
let request = HTTP_CLIENT
.post(url)
.request(method, url)
.header("content-type", "application/json")
.header("Authorization", format!("Bearer {}", api_key))
.body(body);
@@ -382,6 +424,7 @@ async fn proxy(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Path((w_id, ai_path)): Path<(String, String)>,
method: Method,
headers: HeaderMap,
body: Bytes,
) -> impl IntoResponse {
@@ -464,7 +507,7 @@ async fn proxy(
}
};
let request = request_config.prepare_request(&provider, &ai_path, body)?;
let request = request_config.prepare_request(&provider, &ai_path, method, headers, body)?;
let response = request.send().await.map_err(to_anyhow)?;

View File

@@ -12,7 +12,7 @@ use crate::{
db::{ApiAuthed, DB},
resources::get_resource_value_interpolated_internal,
users::{require_owner_of_path, OptAuthed},
utils::{RunnableKind, WithStarredInfoQuery},
utils::WithStarredInfoQuery,
webhook_util::{WebhookMessage, WebhookShared},
HTTP_CLIENT,
};
@@ -59,7 +59,7 @@ use windmill_common::{
users::username_to_permissioned_as,
utils::{
http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, require_admin,
Pagination, StripPath,
Pagination, RunnableKind, StripPath,
},
variables::{build_crypt, build_crypt_with_key_suffix, encrypt},
worker::{to_raw_value, CLOUD_HOSTED},

View File

@@ -34,10 +34,11 @@ lazy_static::lazy_static! {
// Global function to invalidate a specific token from cache
pub fn invalidate_token_from_cache(token: &str) {
// Remove all cache entries for this token (across all workspaces)
AUTH_CACHE.retain(|(_workspace_id, cached_token), _cached_value| {
cached_token != token
});
tracing::info!("Invalidated token from auth cache: {}...", &token[..token.len().min(8)]);
AUTH_CACHE.retain(|(_workspace_id, cached_token), _cached_value| cached_token != token);
tracing::info!(
"Invalidated token from auth cache: {}...",
&token[..token.len().min(8)]
);
}
#[derive(Clone)]
@@ -482,6 +483,27 @@ where
}
}
fn maybe_get_workspace_id_from_path(path_vec: &[&str]) -> Option<String> {
let workspace_id = if path_vec.len() >= 4 && path_vec[0] == "" && path_vec[2] == "w" {
Some(path_vec[3].to_owned())
} else if path_vec.len() >= 5
&& path_vec[0] == ""
&& path_vec[1] == "api"
&& path_vec[2] == "mcp"
&& path_vec[3] == "w"
{
Some(path_vec[4].to_owned())
} else {
if path_vec.len() >= 5 && path_vec[0] == "" && path_vec[2] == "srch" && path_vec[3] == "w" {
Some(path_vec[4].to_owned())
} else {
None
}
};
workspace_id
}
#[async_trait]
impl<S> FromRequestParts<S> for ApiAuthed
where
@@ -494,85 +516,62 @@ where
state: &S,
) -> std::result::Result<Self, Self::Rejection> {
if parts.method == http::Method::OPTIONS {
return Ok(ApiAuthed {
email: "".to_owned(),
username: "".to_owned(),
is_admin: false,
is_operator: false,
groups: Vec::new(),
folders: Vec::new(),
scopes: None,
username_override: None,
});
return Ok(ApiAuthed::default());
};
let already_authed = parts.extensions.get::<ApiAuthed>();
if let Some(authed) = already_authed {
Ok(authed.clone())
} else {
let already_tokened = parts.extensions.get::<Tokened>();
let token_o = if let Some(token) = already_tokened {
Some(token.token.clone())
} else {
extract_token(parts, state).await
};
let original_uri = OriginalUri::from_request_parts(parts, state)
.await
.ok()
.map(|x| x.0)
.unwrap_or_default();
let path_vec: Vec<&str> = original_uri.path().split("/").collect();
let workspace_id = if path_vec.len() >= 4 && path_vec[0] == "" && path_vec[2] == "w" {
Some(path_vec[3].to_owned())
} else if path_vec.len() >= 5
&& path_vec[0] == ""
&& path_vec[1] == "api"
&& path_vec[2] == "mcp"
&& path_vec[3] == "w"
{
Some(path_vec[4].to_string())
} else {
if path_vec.len() >= 5
&& path_vec[0] == ""
&& path_vec[2] == "srch"
&& path_vec[3] == "w"
{
Some(path_vec[4].to_string())
} else {
None
}
};
if let Some(token) = token_o {
if let Ok(Extension(cache)) =
Extension::<Arc<AuthCache>>::from_request_parts(parts, state).await
{
if let Some(authed) = cache.get_authed(workspace_id.clone(), &token).await {
parts.extensions.insert(authed.clone());
if authed.scopes.as_ref().is_some_and(|scopes| {
scopes
.iter()
.any(|s| s.starts_with("jobs:") || s.starts_with("run:"))
}) && (path_vec.len() < 3
|| (path_vec[4] != "jobs" && path_vec[4] != "jobs_u"))
{
BRUTE_FORCE_COUNTER.increment().await;
return Err((
StatusCode::UNAUTHORIZED,
format!("Unauthorized scoped token: {:?}", authed.scopes),
));
}
Span::current().record("username", &authed.username.as_str());
Span::current().record("email", &authed.email);
if let Some(workspace_id) = workspace_id {
Span::current().record("workspace_id", &workspace_id);
}
return Ok(authed);
if let Some(authed) = already_authed {
return Ok(authed.clone());
}
let already_tokened = parts.extensions.get::<Tokened>();
let token_o = if let Some(token) = already_tokened {
Some(token.token.clone())
} else {
extract_token(parts, state).await
};
if let Some(token) = token_o {
if let Ok(Extension(cache)) =
Extension::<Arc<AuthCache>>::from_request_parts(parts, state).await
{
let original_uri = OriginalUri::from_request_parts(parts, state)
.await
.ok()
.map(|x| x.0)
.unwrap_or_default();
let path_vec: Vec<&str> = original_uri.path().split("/").collect();
let workspace_id = maybe_get_workspace_id_from_path(&path_vec);
if let Some(authed) = cache.get_authed(workspace_id.clone(), &token).await {
if authed.scopes.as_ref().is_some_and(|scopes| {
scopes
.iter()
.any(|s| s.starts_with("jobs:") || s.starts_with("run:"))
}) && (path_vec.len() < 3
|| (path_vec[4] != "jobs" && path_vec[4] != "jobs_u"))
{
BRUTE_FORCE_COUNTER.increment().await;
return Err((
StatusCode::UNAUTHORIZED,
format!("Unauthorized scoped token: {:?}", authed.scopes),
));
}
parts.extensions.insert(authed.clone());
Span::current().record("username", &authed.username.as_str());
Span::current().record("email", &authed.email);
if let Some(workspace_id) = workspace_id {
Span::current().record("workspace_id", &workspace_id);
}
return Ok(authed);
}
}
BRUTE_FORCE_COUNTER.increment().await;
Err((StatusCode::UNAUTHORIZED, "Unauthorized".to_owned()))
}
BRUTE_FORCE_COUNTER.increment().await;
Err((StatusCode::UNAUTHORIZED, "Unauthorized".to_owned()))
}
}

View File

@@ -64,7 +64,6 @@ use crate::{
args::RawWebhookArgs,
db::{ApiAuthed, DB},
users::fetch_api_authed,
utils::RunnableKind,
};
use axum::{
@@ -82,7 +81,7 @@ use windmill_common::{
db::UserDB,
error::{JsonResult, Result},
triggers::{RunnableFormat, RunnableFormatVersion, TriggerKind},
utils::{not_found_if_none, paginate, Pagination, StripPath},
utils::{not_found_if_none, paginate, Pagination, RunnableKind, StripPath},
worker::{to_raw_value, CLOUD_HOSTED},
};

View File

@@ -804,10 +804,18 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> {
.execute(db)
.await?;
});
run_windmill_migration!("v2_job_queue_suspend", db, |tx| {
sqlx::query!(
"CREATE INDEX CONCURRENTLY IF NOT EXISTS v2_job_queue_suspend ON v2_job_queue (workspace_id, suspend) WHERE suspend > 0;"
)
.execute(db)
.await?;
});
Ok(())
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct ApiAuthed {
pub email: String,
pub username: String,

View File

@@ -12,7 +12,7 @@ use crate::db::ApiAuthed;
use crate::triggers::{
get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail,
};
use crate::utils::{RunnableKind, WithStarredInfoQuery};
use crate::utils::WithStarredInfoQuery;
use crate::{
db::DB,
schedule::clear_schedule,
@@ -43,7 +43,7 @@ use windmill_common::{
jobs::JobPayload,
schedule::Schedule,
scripts::Schema,
utils::{http_get_from_hub, not_found_if_none, paginate, Pagination, StripPath},
utils::{http_get_from_hub, not_found_if_none, paginate, Pagination, RunnableKind, StripPath},
};
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
use windmill_queue::{push, schedule::push_scheduled_job, PushIsolationLevel};
@@ -477,7 +477,7 @@ async fn create_flow(
false,
None,
true,
nf.tag,
None,
None,
None,
None,

Some files were not shown because too many files have changed in this diff Show More