Compare commits

..

99 Commits

Author SHA1 Message Date
HugoCasa
24bf1f1493 nit 2025-04-11 18:28:15 +02:00
HugoCasa
a34ed9a24d typo 2025-04-11 17:22:57 +02:00
HugoCasa
fc261b4ac1 nit 2025-04-11 15:50:25 +02:00
HugoCasa
ed6627032e fix sqlx 2025-04-11 15:33:11 +02:00
HugoCasa
577c748b55 Merge remote-tracking branch 'origin/main' into hc/signed-s3-objects 2025-04-11 15:07:56 +02:00
HugoCasa
62b0925db9 b 2025-04-11 15:00:16 +02:00
Ruben Fiszel
5e054d3385 load base url and critical channels in initial_load 2025-04-11 08:22:21 +00:00
HugoCasa
5409b280c3 Merge remote-tracking branch 'origin/main' into hc/signed-s3-objects 2025-04-11 09:57:39 +02:00
HugoCasa
c57490a572 fix build gcp (#5601)
* fix build gcp

* f
2025-04-10 21:13:44 +00:00
HugoCasa
dea74c0846 fix build 2025-04-10 23:06:09 +02:00
HugoCasa
31d15d6d33 Update frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-04-10 23:04:20 +02:00
HugoCasa
5c0da7f7bd Update frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-04-10 23:04:13 +02:00
dieriba
6339775404 feat: add gcp trigger (#5501)
* update: add migration for gcp_trigger table, add cli method gcp and add gcp_trigger args for github ci

* update: add gcp module and routing it in lib.rs

* update: added gcp type to TRIGGER KIND type in db, add new feature condition to try_get_fn and added gcp_trigger feature in cargo file

* update: add ui for gcp trigger, added missing triggers type for job kin, add gcp trigger in deploy to functionallity

* adding google cloud crate

* update: handle pull and push delivery

* update: handle pull and push delivery

* update: handle push event, changed front ui

* update: ui

* fix: capture for gcp

* update: automatically manage pub sub subscription and refactoring

* capture done

* update cli types

* fix: wrong func argument and type openapi

* fix: missing import

* update .sqlx

* update: svg color and remove pulse button for capture push gcp

* update: handle deployto

* update script helper and link to hub gcp script/flow

* update gcp representation

* update: add confirmation modal

* add unique index to mitigate same subscriber id, fix `zombie worker`, update test connection logic

* Update frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update cli/gen/services.gen.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* nits

* update repo ref

* fix feature function

* update dependencies

* update .sqlx

* fix missing import

* update: handle existing subscription and creating new one or update it

* fix ee build

* handle existing config properly

* update .sqlx

* update .sqlx

* remove unused

* update documentation link fix ci

* use on instance of empty_string_as_none fn

* update refo ref

* updat script_helper

* update ui

* update migration

* add missing arguments

* fix and nits

* update repo ref

* remove unused

* nits

* add doc links

* nits

* Update frontend/src/lib/components/triggers/gcp/GcpTriggerPanel.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* use trigger path as default route path

* update .sqlx

* update repo ref

* update ref

* update route path

* update types

* fix deploy to

* nits

* update repo ref

* update .sqlx

* Update frontend/src/lib/components/triggers/gcp/GcpTriggerEditorConfigSection.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* nits

* nits

* nits

* remove dupliacte fn and update repo ref

* remove unused import

* fix missing import

* fix: wong name var

* update script helper and template script

* Update frontend/src/lib/script_helpers.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update ee-repo-ref.txt

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: HugoCasa <hugo@casademont.ch>
2025-04-10 20:39:01 +00:00
HugoCasa
0c5abc1f2f presigned 2025-04-10 22:37:19 +02:00
Diego Imbert
785da73abf fix infinite loop (#5600) 2025-04-10 20:28:04 +00:00
dieriba
c740eee311 add variable option to queue url (#5595)
* add variable option to queue url

* remove unused error

* update repo ref

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-04-10 18:53:19 +02:00
HugoCasa
9b9006c1d6 fix agent worker build (#5597) 2025-04-10 13:35:00 +02:00
Diego Imbert
2da10e62a4 fix janky loader in trigger captures table (#5596) 2025-04-10 11:29:28 +00:00
HugoCasa
c8cc52d840 Merge remote-tracking branch 'origin/main' into hc/signed-s3-objects 2025-04-10 11:38:32 +02:00
Ruben Fiszel
1f1164199b nit 2025-04-10 01:00:29 +00:00
Ruben Fiszel
63fa499015 feat: agent workers v2 using http (#5588) 2025-04-10 00:56:11 +00:00
Ruben Fiszel
eabd3d1346 fix bundle standalone save 2025-04-09 23:20:48 +00:00
HugoCasa
3950cfd7e3 fix: add support for ${} syntax without default in bash (#5594) 2025-04-09 19:40:44 +02:00
HugoCasa
4b9cc2ec28 fix build 2025-04-09 18:48:21 +02:00
HugoCasa
091a4717f4 fix build 2025-04-09 18:25:54 +02:00
HugoCasa
cb7c83c374 nti 2025-04-09 18:11:15 +02:00
HugoCasa
51eb0be80a Update python-client/wmill/wmill/client.py
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-04-09 18:10:14 +02:00
HugoCasa
6fcdf2af2e fix build 2025-04-09 18:09:52 +02:00
HugoCasa
38d8cf842a Update backend/windmill-api/openapi.yaml
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-04-09 18:07:20 +02:00
HugoCasa
0eff6b139d feat: accept signed s3 objects for s3 file keys in apps + sign endpoint and helpers 2025-04-09 18:02:03 +02:00
Ruben Fiszel
e4d57f9549 add grants to workspace_runnable_dependencies 2025-04-09 09:38:48 +00:00
Diego Imbert
a74d68a5e6 fix infinite loop in app multiselect due to bind (#5590) 2025-04-09 09:23:46 +00:00
HugoCasa
f497a4bfae fix: improve app image picker UX (#5589) 2025-04-08 22:41:49 +00:00
HugoCasa
b4f8c88c19 fix: mssql ca_cert deserializing (#5587) 2025-04-08 17:12:20 +02:00
Diego Imbert
f5d46d5751 fix: binding not working in nested array script arg (#5585) 2025-04-08 09:14:06 +00:00
Guilhem
0e3793356a add space (#5582) 2025-04-08 08:17:47 +00:00
HugoCasa
07094b6aa2 fix: freeze when clicking script history diff button (#5581)
* fix: validate saved module before passing to flow module editor

* fix: freeze when clicking script history diff button
2025-04-07 20:52:51 +00:00
HugoCasa
2eb1a161d1 fix: validate saved module before passing to flow module editor (#5580) 2025-04-07 20:44:14 +00:00
Guilhem
d874d98cc1 fix monaco suggestion z-index (#5578) 2025-04-07 14:58:22 +02:00
HugoCasa
35fc4822f0 chore: update hub sync script (#5579) 2025-04-07 14:58:08 +02:00
Guilhem
c2180c6eb3 fix(frontend): prevent deploy popover to show if deploy dropdown is open (#5542)
* prevent deploy popover to show if deploy dropdown is open

* wip

* Revert "wip"

This reverts commit 85434654af.

* Revert "prevent deploy popover to show if deploy dropdown is open"

This reverts commit edd9eda156.

* add prop to hide popup fro dropdown

* feat: button can have tooltip

* improve deploy tooltip and dropdown behavior

* rename tooltip to tooltipPopover

* nit

* add deploy button component

* use svelte 5 runes

* use new deploy button for script builder

* add delay to deploy popover when dropdown is open

* add delay to deploy popover when dropdown is open

# Conflicts:
#	frontend/src/lib/components/DeployButton.svelte
#	frontend/src/lib/components/common/button/Button.svelte

* Update frontend/src/lib/components/common/button/Button.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* remove unsused field

* nit

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-04-07 14:43:00 +02:00
Piyush मिश्र
1007f37f4b function takes 13 arguments but 14 arguments were supplied (#5577) 2025-04-06 20:38:39 +00:00
Piyush मिश्र
1f3e7d9029 fix: Dynamic select does not work with tag //native (#5576)
closes #5490
2025-04-05 20:43:53 +00:00
Ruben Fiszel
62f14d1cb9 feat: add wildcards filter for worker/label/tags 2025-04-05 14:10:53 +00:00
HugoCasa
3507925624 fix: legacy script gen model selection (#5574) 2025-04-05 13:35:04 +00:00
Alexander Petric
102b58a5f4 fix: tenant id to never be undefined on teams (#5572)
* fix: tenant id to never be undefined

* simplify azure oauth

* simplify azure oauth

* update ee ref

* sqlx prepare

* sqlx prepare
2025-04-05 12:17:26 +00:00
Ruben Fiszel
0c3cb3700a fix: fix list jobs by tag 2025-04-05 10:50:28 +00:00
Ruben Fiszel
efacda7453 fix tabs selected behavior change from svelte 5 2025-04-04 19:50:17 +00:00
centdix
b47c15165f feat: add windmill context to autocomplete (#5548)
* add windmill context to autocomplete

* fix formatting

* remove console log

* do not mention tool call for autocomplete

* apply logic to php

---------

Co-authored-by: HugoCasa <hugo@casademont.ch>
2025-04-04 18:33:57 +00:00
HugoCasa
5bd66b3f1f select border (#5571)
* fix: properly bind to array elements in Svelte each loops

This commit fixes an issue where binding directly to loop variables in Svelte's #each loops doesn't properly update the original array. Instead of binding directly to the loop variable, we now bind to the array elements using index variables.

The pattern used is: - Change: {#each arr as el} -> {#each arr as _, index} - Change: bind:value={el} -> bind:value={arr[index]}

Modified files: - frontend/src/lib/components/ArrayTypeNarrowing.svelte - frontend/src/lib/components/apps/editor/AppInputs.svelte - frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte

* better app settings panel reactivity

* fix: app editor table svelte 5 fixes

* fix: select border

---------

Co-authored-by: Guilhem <guilhemlemouel@gmail.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-04-04 18:17:40 +00:00
HugoCasa
b926076988 fix: app editor svelte 5 fixes (#5570)
* fix: properly bind to array elements in Svelte each loops

This commit fixes an issue where binding directly to loop variables in Svelte's #each loops doesn't properly update the original array. Instead of binding directly to the loop variable, we now bind to the array elements using index variables.

The pattern used is: - Change: {#each arr as el} -> {#each arr as _, index} - Change: bind:value={el} -> bind:value={arr[index]}

Modified files: - frontend/src/lib/components/ArrayTypeNarrowing.svelte - frontend/src/lib/components/apps/editor/AppInputs.svelte - frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte

* better app settings panel reactivity

* fix: app editor table svelte 5 fixes

---------

Co-authored-by: Guilhem <guilhemlemouel@gmail.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-04-04 17:55:50 +00:00
Guilhem
4c71af8a74 fix(frontend): proper each block binding + better app settings reactivity (#5568)
* fix: properly bind to array elements in Svelte each loops

This commit fixes an issue where binding directly to loop variables in Svelte's #each loops doesn't properly update the original array. Instead of binding directly to the loop variable, we now bind to the array elements using index variables.

The pattern used is: - Change: {#each arr as el} -> {#each arr as _, index} - Change: bind:value={el} -> bind:value={arr[index]}

Modified files: - frontend/src/lib/components/ArrayTypeNarrowing.svelte - frontend/src/lib/components/apps/editor/AppInputs.svelte - frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte

* better app settings panel reactivity

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: HugoCasa <hugo@casademont.ch>
2025-04-04 17:11:14 +00:00
centdix
b3ecde3316 feat: add diff toggle to flow inline scripts (#5550)
* draft flow diff

* add missing import

* cleaning

* code cleaning

* fix for recursive renderings

* fix typo

* cleaning

---------

Co-authored-by: HugoCasa <hugo@casademont.ch>
2025-04-04 16:03:40 +00:00
HugoCasa
4f6be6ed34 fix: flow editor svelte 5 issues (#5567) 2025-04-04 11:30:03 +02:00
centdix
5abdc3e440 feat: handle sending selected lines to ai context (#5527)
* allow mentioning specific files in instructions

* remove not working highlight implementation

* make highlighted text work

* fix tooltip position

* clean code

* cleaning

* use lib for tooltip positioning

* fix logic

* draft for db in context

* use tools for db in context

* fixes

* cleaning and bug fixes

* fix

* cleaning

* fix when script is db type

* simplify logic

* put schema in context if already here

* fix imports

* fix tooltip position and make it scrollable

* remove console logs

* check if selected is in available

* fix tooltip list

* add back lost logic

* last fix

* fix type errors

* use loaded schema from dbSchemas

* fix typing, content and lang are always there

* remove from context if not available anymore

* add not loaded yet mention if schema not loaded

* add missing callback logic

* fix prompt

* fix usage of updateselectedContext function

* fix styling for white theme

* handle tab and arrows

* fix schemas not being refreshed on contexts

* also refresh displayMessages when dbschemas change

* fix duplicate available contexts

* fix logic for new scripts

* fix new lines inside text area

* implement sending diff in context

* add button in deploy options to ask ai about diff

* also visualize change when asking for diff

* better prompt

* add limit to diff size

* put diff mode toggle in editor bar

* add button to see history from editor

* adjustements

* put see diff button in dropdown

* fixes

* better styling

* handle adding code piece to context

* add code piece in context

* draft start end markers

* adapt code

* draft

* apply code pieces before sending request

* cleaning

* highlight if diff mode

* format files

* change buttons based on diffmode

* remove diff after sending message

* fix type error

* smaller buttons

* draft

* use existing editor in diff editor

* fix number of db resources fetches

* fix apply and add buttons on diff mode

* cleaning

* undo ai gen button show

* better buttons

* better prompt

* remove console log

* fix merge

* avoid duplicates

* fix merge

* fix

* fix apply logic

* remove useless if

* focus text area + close chat if no selected lines

---------

Co-authored-by: HugoCasa <hugo@casademont.ch>
2025-04-04 09:13:24 +00:00
Ruben Fiszel
1bb402bcfc fix benchmarks 2025-04-03 21:17:13 +00:00
Ruben Fiszel
26294584d6 fix(cli): properly handle enabled/disabled updates of schedules 2025-04-03 20:18:53 +00:00
Guilhem
bc69116ff1 feat(frontend) add flow step result viewer (#5398) 2025-04-03 19:38:11 +00:00
HugoCasa
2c5e58cf1a feat: make azure a standalone AI provider (#5558)
* feat: make azure a standalone AI provider

* oups

* nit

* fix: openai/azure oauth

* nit

* nits
2025-04-03 19:26:56 +00:00
Alexander Petric
6fae3a566b fix(frontend): app builder - force json configuration in rich result (#5565) 2025-04-03 19:25:34 +00:00
HugoCasa
9276c717a2 fix: prevent invalid returned ai completion object errors (#5564) 2025-04-03 16:32:11 +02:00
centdix
e118d2cd5f feat: Implement sending diff to ai (#5510)
* allow mentioning specific files in instructions

* remove not working highlight implementation

* make highlighted text work

* fix tooltip position

* clean code

* cleaning

* use lib for tooltip positioning

* fix logic

* draft for db in context

* use tools for db in context

* fixes

* cleaning and bug fixes

* fix

* cleaning

* fix when script is db type

* simplify logic

* put schema in context if already here

* fix imports

* fix tooltip position and make it scrollable

* remove console logs

* check if selected is in available

* fix tooltip list

* add back lost logic

* last fix

* fix type errors

* use loaded schema from dbSchemas

* fix typing, content and lang are always there

* remove from context if not available anymore

* add not loaded yet mention if schema not loaded

* add missing callback logic

* fix prompt

* fix usage of updateselectedContext function

* fix styling for white theme

* handle tab and arrows

* fix schemas not being refreshed on contexts

* also refresh displayMessages when dbschemas change

* fix duplicate available contexts

* fix logic for new scripts

* fix new lines inside text area

* implement sending diff in context

* add button in deploy options to ask ai about diff

* also visualize change when asking for diff

* better prompt

* add limit to diff size

* put diff mode toggle in editor bar

* add button to see history from editor

* adjustements

* put see diff button in dropdown

* fixes

* better styling

* highlight if diff mode

* format files

* change buttons based on diffmode

* remove diff after sending message

* fix type error

* smaller buttons

* draft

* use existing editor in diff editor

* fix number of db resources fetches

* fix apply and add buttons on diff mode

* cleaning

* undo ai gen button show

* better buttons

* styling asjustements + show diff in badge

* styling

* fix deployed code check

* cleaning and styling

* better quick actions

* dont send code when analyzing

* remove apply in chat if only code and no diff

* fix bad code refactor

---------

Co-authored-by: HugoCasa <hugo@casademont.ch>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-04-03 12:15:22 +00:00
Diego Imbert
87cf1d0c28 fix tutorial (#5562)
* fix tutorial first part

* fix tutorial

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-04-03 11:48:59 +00:00
Ruben Fiszel
e454b3e05b regen package-lock 2025-04-03 11:47:33 +00:00
Diego Imbert
6e9ec6323c fix: Remaining svelte 5 bugs (#5563)
* hack fix dnd with tick

* fix: infinite loading in CodeDisplay after update to svelte 5
2025-04-03 13:07:21 +02:00
Ruben Fiszel
3c99b3fdc7 feat: migrate to svelte5 + vite6 (#4813)
* runs on svelte 5

* Line component from svelte-chartjs

* Replaced all svelte-chartjs occurrences with custom wrapper

* Fix props mistake

* Fix illegal table structures

* self-closing-tags fix

* aria labels

* Fixed trivial warnings and errors

* @tanstack/svelte-table fix

* upgrade to vite 6

* svelte-kit sync before running svelte-check

* Remove on:clear which is actually on:removeAll and already handled by on:change

* fix worker tags not displaying in Autoscaling

* Try to fix svelte-kit sync not working during CI

* remove warnings

* Fix add flow page crashing

* access worldStore before assignment fix

* fix infinite recursions in App Editor

* Replaced JSON.stringify with proper deepEqual

* component mount api changed (no longer classes)

* fix ci errors

* Fix infinite loops in background runnable panel

* factored effect on deep equal logic in onObjChange

* fix "Add" not working in AgGrid Table

* Replaced legacy component.$set api

* Fix multiselect infinite value reaction

* Fix flow input fields resetting when opening their edit tab

* fix date input resetting when typing year

* Remove !p-0 affecting subgrid dotted borders

* fix missing debounceTemplate causing hundreds of updates

* Fix AgGrid action refreshes and disppearing

* resolve getItems generating random ids every rerun

* fix cannot access items before init

* fix sort lambda arguments being undefined

* Revert "Remove !p-0 affecting subgrid dotted borders"

This reverts commit c62809bb45d682a48376b071680645ed4e1c601b.

* fix input not updating in decision tree editor

* Update frontend/src/lib/components/schema/EditableSchemaWrapper.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Re-added padding affecting subgrid dotted borders (#5479)

* remove !p-0 in preset components

* removed extra padding on accordion tabs subgrid

* Fix non-reactive SchemaForm

* dirty fix for the oneOf bug

* Fix warnings and update svelte-exmarkdown for svelte 5

* fix dnd not working

* don't mount component like objects

---------

Co-authored-by: Diego Imbert <diegoimbert@protonmail.com>
Co-authored-by: Diego Imbert <70353967+diegoimbert@users.noreply.github.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-04-02 21:33:50 +00:00
pyranota
f3ed108e43 nit: replace KJQXZ with more meaningful notation (#5547)
* nit: replace `KJQXZ` with more meaningful notation

Originally this string is located in all places, where modification is needed in order to add new language support

* relative -> related

* revert shebang in substitue.sh

* remove '}'
2025-04-02 21:03:04 +00:00
Ruben Fiszel
dcb307861e chore(main): release 1.481.0 (#5560)
* chore(main): release 1.481.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-04-02 20:52:22 +00:00
Alexander Petric
e5519f79aa feat: mssql support cert configuration (#5559)
* feat: mssql support cert configuration

* use tokio async

* using jobdir

* use std
2025-04-02 20:10:51 +00:00
Ruben Fiszel
13fb55e2a8 chore(main): release 1.480.1 (#5551)
* chore(main): release 1.480.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-04-02 19:44:48 +02:00
Alexander Petric
3fd7a5ce9c fix: aad_token can be empty string (#5557)
* fix: mssql aad_token can be empty string

* simplify deserializer
2025-04-02 19:26:32 +02:00
Alexander Petric
dd30692617 fix: mssql aad_token can be empty string (#5556) 2025-04-02 18:51:02 +02:00
Ruben Fiszel
7083efd051 fix: improve workspace selection for default tag settings 2025-04-01 20:19:11 +00:00
Ruben Fiszel
fd26abf5c1 chore(main): release 1.480.0 (#5538)
* chore(main): release 1.480.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-04-01 19:27:05 +00:00
HugoCasa
113f038fc0 fix: only format valid resource types (#5541) 2025-03-31 17:10:21 +00:00
HugoCasa
5c922a5f27 preprocessor templates nits (#5543) 2025-03-31 17:10:01 +00:00
HugoCasa
b31090cb54 fix: ignore invalid chunks in completion stream: empty choices when using azure (#5545) 2025-03-31 16:55:40 +00:00
centdix
ca0cda3ecf fix(frontend): use custom caret position function (#5544)
* use custom caret position function

* add try catch to not show the tooltip if any error occured

* log error
2025-03-31 16:13:55 +00:00
centdix
f7c8654987 feat: put db resources in ai context (#5507)
* allow mentioning specific files in instructions

* remove not working highlight implementation

* make highlighted text work

* fix tooltip position

* clean code

* cleaning

* use lib for tooltip positioning

* fix logic

* draft for db in context

* use tools for db in context

* fixes

* cleaning and bug fixes

* fix

* cleaning

* fix when script is db type

* simplify logic

* put schema in context if already here

* fix imports

* fix tooltip position and make it scrollable

* remove console logs

* check if selected is in available

* fix tooltip list

* add back lost logic

* last fix

* fix type errors

* use loaded schema from dbSchemas

* fix typing, content and lang are always there

* remove from context if not available anymore

* add not loaded yet mention if schema not loaded

* add missing callback logic

* fix prompt

* fix usage of updateselectedContext function

* fix styling for white theme

* handle tab and arrows

* fix schemas not being refreshed on contexts

* also refresh displayMessages when dbschemas change

* fix duplicate available contexts

* fix logic for new scripts

* fix new lines inside text area

* format files

* fix number of db resources fetches

* remove padding

* fix tooltip y position

---------

Co-authored-by: HugoCasa <hugo@casademont.ch>
2025-03-31 12:37:49 +00:00
Alexander Petric
c230e2aed9 feat: ms sql aad authentication support (#5539)
* feat: ms sql aad authentication support

* format

* format
2025-03-31 04:05:08 +00:00
Ruben Fiszel
7c96ef15d1 add disable_jobs_monitoring_env_variable 2025-03-30 00:17:16 +00:00
Ruben Fiszel
45a6d5934e add disable_jobs_monitoring_env_variable 2025-03-30 00:12:27 +00:00
HugoCasa
3d3250194d fix: correctly run empty flow with preprocessor from UI (#5537) 2025-03-28 11:26:26 -07:00
Ruben Fiszel
0ee6bcc97c chore(main): release 1.479.3 (#5536)
* chore(main): release 1.479.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-03-28 17:30:06 +01:00
HugoCasa
1ac05f0b79 fix: tab for intellisense (#5530) 2025-03-28 17:29:49 +01:00
pyranota
8850fc9f5e nit: update sqlx for java (#5534) 2025-03-28 17:29:22 +01:00
HugoCasa
dd488a2bdb perf: improve hub resource type pulling when using the cli (#5535) 2025-03-28 17:24:24 +01:00
pyranota
c020af5a98 nit: fix windows build (#5531)
rror[E0425]: cannot find function `pull_from_tar` in module `crate::global_cache`
    --> windmill-worker\src\common.rs:1214:43
     |
1214 |                 Some(crate::global_cache::pull_from_tar(
     |                                           ^^^^^^^^^^^^^ not found in `crate::global_cache`
     |
note: found an item that was configured out
    --> windmill-worker\src\global_cache.rs:87:14
     |
87   | pub async fn pull_from_tar(
     |              ^^^^^^^^^^^^^
note: the item is gated here
    --> windmill-worker\src\global_cache.rs:86:1
     |
86   | #[cfg(all(feature = "enterprise", feature = "parquet", unix))]
     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-03-28 17:17:23 +01:00
Ruben Fiszel
4703e3c848 fix(cli): pin encodeHex to 1.0.4 to work with dnt 2025-03-28 17:16:56 +01:00
Ruben Fiszel
6dffe6d460 chore(main): release 1.479.2 (#5533)
* chore(main): release 1.479.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-03-28 11:13:14 -04:00
Alexander Petric
08e8283c58 fix: fetch correct resource for interactive slack when multiple workspaces connected (#5532) 2025-03-28 10:59:18 -04:00
Ruben Fiszel
7caf76cf94 update deno to 2.x everywhere 2025-03-28 00:32:26 +01:00
Ruben Fiszel
4bc5ed6311 chore(main): release 1.479.1 (#5529)
* chore(main): release 1.479.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-03-28 00:24:49 +01:00
Ruben Fiszel
6cd2dc7178 fix: pin backend deps half to 2.4.1 2025-03-28 00:21:04 +01:00
Ruben Fiszel
9d9a49235c fix cli build 2025-03-28 00:16:14 +01:00
Ruben Fiszel
5957c6e9b3 chore(main): release 1.479.0 (#5493)
* chore(main): release 1.479.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-03-28 00:09:47 +01:00
Ruben Fiszel
a743f3e286 nit 2025-03-28 00:05:02 +01:00
Ruben Fiszel
1ac954c67c create long hash 2025-03-28 00:03:27 +01:00
Ruben Fiszel
f3a0af8fe6 bump versions (#5526)
* all

* version bump

* ci

* fix bump

* fix
2025-03-27 23:57:09 +01:00
575 changed files with 19878 additions and 11030 deletions

View File

@@ -4,11 +4,6 @@ on:
workflows: ["Change versions"]
types:
- completed
pull_request:
types: [opened, synchronize, reopened]
paths:
- "backend/**"
- ".github/workflows/backend-check.yml"
push:
paths:
- "backend/**"

View File

@@ -37,10 +37,10 @@ jobs:
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
deno-version: v2.x
- name: benchmark
timeout-minutes: 30
run: deno run --unstable -A -r
run: deno run -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
@@ -79,10 +79,10 @@ jobs:
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
deno-version: v2.x
- name: benchmark
timeout-minutes: 20
run: deno run --unstable -A -r
run: deno run -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
--no-warm-up -c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_dedicated.json
@@ -154,10 +154,10 @@ jobs:
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
deno-version: v2.x
- name: benchmark
timeout-minutes: 20
run: deno run --unstable -A -r
run: deno run -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
@@ -274,10 +274,10 @@ jobs:
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
deno-version: v2.x
- name: benchmark
timeout-minutes: 20
run: deno run --unstable -A -r
run: deno run -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
@@ -300,7 +300,7 @@ jobs:
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
deno-version: v2.x
- uses: actions/checkout@v4
with:
ref: benchmarks
@@ -309,7 +309,7 @@ jobs:
with:
merge-multiple: true
- name: graphs
run: deno run --unstable -A -r
run: deno run -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_graphs.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/graphs_config.json

View File

@@ -64,7 +64,7 @@ jobs:
platforms: linux/amd64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}
@@ -81,7 +81,7 @@ jobs:
platforms: linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}

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,mqtt_trigger,websocket,smtp,static_frontend,all_languages
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
- name: Rename binary with corresponding architecture
run: |
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"

View File

@@ -95,7 +95,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
${{ steps.meta-public.outputs.tags }}
@@ -157,7 +157,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
${{ steps.meta-ee-public.outputs.tags }}

View File

@@ -4,11 +4,6 @@ on:
workflows: ["Change versions"]
types:
- completed
pull_request:
types: [opened, synchronize, reopened]
paths:
- "frontend/**"
- ".github/workflows/frontend-check.yml"
merge_group:
push:

View File

@@ -27,7 +27,7 @@ jobs:
registry-url: "https://registry.npmjs.org"
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
deno-version: v2.x
- run: cd cli && ./build.sh && cd npm && npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

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,websocket,smtp,static_frontend,all_languages
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
- name: Rename binary with corresponding architecture
run: |
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"

View File

@@ -1,5 +1,97 @@
# Changelog
## [1.481.0](https://github.com/windmill-labs/windmill/compare/v1.480.1...v1.481.0) (2025-04-02)
### Features
* mssql support cert configuration ([#5559](https://github.com/windmill-labs/windmill/issues/5559)) ([e5519f7](https://github.com/windmill-labs/windmill/commit/e5519f79aaa83f04014364c7d1ec11157044011d))
## [1.480.1](https://github.com/windmill-labs/windmill/compare/v1.480.0...v1.480.1) (2025-04-02)
### Bug Fixes
* aad_token can be empty string ([#5557](https://github.com/windmill-labs/windmill/issues/5557)) ([3fd7a5c](https://github.com/windmill-labs/windmill/commit/3fd7a5ce9c02332be40c34c0b6da57894b0b3d55))
* improve workspace selection for default tag settings ([7083efd](https://github.com/windmill-labs/windmill/commit/7083efd051aeb7f653cccc97db099f4d9b2591a0))
* mssql aad_token can be empty string ([#5556](https://github.com/windmill-labs/windmill/issues/5556)) ([dd30692](https://github.com/windmill-labs/windmill/commit/dd30692617e3cbc852239c4b1c50f975ff247c33))
## [1.480.0](https://github.com/windmill-labs/windmill/compare/v1.479.3...v1.480.0) (2025-03-31)
### Features
* ms sql aad authentication support ([#5539](https://github.com/windmill-labs/windmill/issues/5539)) ([c230e2a](https://github.com/windmill-labs/windmill/commit/c230e2aed9b7fafb86548a4f4151939d5aca5127))
* put db resources in ai context ([#5507](https://github.com/windmill-labs/windmill/issues/5507)) ([f7c8654](https://github.com/windmill-labs/windmill/commit/f7c86549879582c7f9dc72d52524f3a394f493f3))
### Bug Fixes
* correctly run empty flow with preprocessor from UI ([#5537](https://github.com/windmill-labs/windmill/issues/5537)) ([3d32501](https://github.com/windmill-labs/windmill/commit/3d3250194d43aee1a640a57505bc7a6afee62c84))
* **frontend:** use custom caret position function ([#5544](https://github.com/windmill-labs/windmill/issues/5544)) ([ca0cda3](https://github.com/windmill-labs/windmill/commit/ca0cda3ecf5bd449f9c371cf5102c11d880c9822))
* ignore invalid chunks in completion stream: empty choices when using azure ([#5545](https://github.com/windmill-labs/windmill/issues/5545)) ([b31090c](https://github.com/windmill-labs/windmill/commit/b31090cb544632680947492dc28f7b7c1a9c7287))
* only format valid resource types ([#5541](https://github.com/windmill-labs/windmill/issues/5541)) ([113f038](https://github.com/windmill-labs/windmill/commit/113f038fc0e53e37c3bc319f85b3f7fa780c6fe5))
## [1.479.3](https://github.com/windmill-labs/windmill/compare/v1.479.2...v1.479.3) (2025-03-28)
### Bug Fixes
* **cli:** pin encodeHex to 1.0.4 to work with dnt ([4703e3c](https://github.com/windmill-labs/windmill/commit/4703e3c848c9b06603b83885267023ccf84316c3))
### Performance Improvements
* improve hub resource type pulling when using the cli ([#5535](https://github.com/windmill-labs/windmill/issues/5535)) ([dd488a2](https://github.com/windmill-labs/windmill/commit/dd488a2bdbc0c9c7311c06dc25504a1336661cde))
## [1.479.2](https://github.com/windmill-labs/windmill/compare/v1.479.1...v1.479.2) (2025-03-28)
### Bug Fixes
* fetch correct resource for interactive slack when multiple workspaces connected ([#5532](https://github.com/windmill-labs/windmill/issues/5532)) ([08e8283](https://github.com/windmill-labs/windmill/commit/08e8283c58c94f773936bac09d56bc6430382bbb))
## [1.479.1](https://github.com/windmill-labs/windmill/compare/v1.479.0...v1.479.1) (2025-03-27)
### Bug Fixes
* pin backend deps half to 2.4.1 ([6cd2dc7](https://github.com/windmill-labs/windmill/commit/6cd2dc7178c62530f893d69f6e76b6cbc465e419))
## [1.479.0](https://github.com/windmill-labs/windmill/compare/v1.478.1...v1.479.0) (2025-03-27)
### Features
* add description option to schedule page ([#5500](https://github.com/windmill-labs/windmill/issues/5500)) ([4c6f600](https://github.com/windmill-labs/windmill/commit/4c6f60010fec7d82181867e0082079e446797ce2))
* add java support ([#5458](https://github.com/windmill-labs/windmill/issues/5458)) ([59740c0](https://github.com/windmill-labs/windmill/commit/59740c047816ad90d7383b15c846302db1a2e354))
* add nu-lang support ([#5217](https://github.com/windmill-labs/windmill/issues/5217)) ([a3faea1](https://github.com/windmill-labs/windmill/commit/a3faea16e77796a1b989db4285b3fef722ac55b2))
* api key/basic/hmac auth for http triggers ([#5476](https://github.com/windmill-labs/windmill/issues/5476)) ([e920101](https://github.com/windmill-labs/windmill/commit/e920101107256589bb5aee09fa8f04f5bd9707e4))
* autocomplete v2 + AI chat ([#5323](https://github.com/windmill-labs/windmill/issues/5323)) ([234b20f](https://github.com/windmill-labs/windmill/commit/234b20f8bd55ea19b17b80f08d9ff1e0e00ba739))
* github app token instead of pat for git sync ([#5279](https://github.com/windmill-labs/windmill/issues/5279)) ([b822c66](https://github.com/windmill-labs/windmill/commit/b822c66262f7c4c01ea4baad9383a12d138b0815))
* list references upon renaming a script or a flow ([#5487](https://github.com/windmill-labs/windmill/issues/5487)) ([e868fe2](https://github.com/windmill-labs/windmill/commit/e868fe2bf5695b968151e27826854def3e847eb1))
* make custom ai CE + add together AI provider ([#5522](https://github.com/windmill-labs/windmill/issues/5522)) ([a28c78d](https://github.com/windmill-labs/windmill/commit/a28c78dd920c695c3dfac05bc48c82f1477b022d))
* **python:** fully qualified imports mapping ([#5511](https://github.com/windmill-labs/windmill/issues/5511)) ([1a5566b](https://github.com/windmill-labs/windmill/commit/1a5566b8c29773d94a681c86676d4cdb0b7c7777))
* remove stripe dep ([#5508](https://github.com/windmill-labs/windmill/issues/5508)) ([7a62527](https://github.com/windmill-labs/windmill/commit/7a625275752ba69e26d7e3b41416e335496eff84))
* unsafe parameters for sql queries (table names, column names) ([#5488](https://github.com/windmill-labs/windmill/issues/5488)) ([38ee018](https://github.com/windmill-labs/windmill/commit/38ee0183aaa014c740da7b54d66928ec851fb522))
### Bug Fixes
* add missing privileged hub script for app slack reports ([#5515](https://github.com/windmill-labs/windmill/issues/5515)) ([63fe9c1](https://github.com/windmill-labs/windmill/commit/63fe9c1852c1f87901f42eff8904c3482f7ceb43))
* clean job dirs between flow locks ([8129672](https://github.com/windmill-labs/windmill/commit/8129672d9e8c6b591c1a46c30060a9d4f207e499))
* **cli:** add --dry-run option ([4667507](https://github.com/windmill-labs/windmill/commit/466750752f6ffcb098cecd4ef6d6f33fb42d39ba))
* correct private hub url in CLI for resource types sync ([#5513](https://github.com/windmill-labs/windmill/issues/5513)) ([9fd224c](https://github.com/windmill-labs/windmill/commit/9fd224cc469ae6f47c3ba9839ed43c85ff4d2181))
* **frontend:** use stable path for capture tables + nits ([#5495](https://github.com/windmill-labs/windmill/issues/5495)) ([e16d629](https://github.com/windmill-labs/windmill/commit/e16d6299f52564def484e78fb2f48e9bf39cbd3d))
* improve cancel for flows with many substeps ([ec11d57](https://github.com/windmill-labs/windmill/commit/ec11d577c6089df0b6019cd05064f5ea63fb317c))
### Performance Improvements
* cache workspace env variables to avoid one query ([#5499](https://github.com/windmill-labs/windmill/issues/5499)) ([a3f6db7](https://github.com/windmill-labs/windmill/commit/a3f6db7dca983a4dfd62b30423340f899c4d1da6))
* cache workspace premium check ([5573d88](https://github.com/windmill-labs/windmill/commit/5573d886954182efcac71b3baa54d455f5086b30))
* optimize number of queries needed for job run ([#5504](https://github.com/windmill-labs/windmill/issues/5504)) ([3edca4b](https://github.com/windmill-labs/windmill/commit/3edca4bc91ee9a1f1c0a98d39bc673dc56f899b6))
## [1.478.1](https://github.com/windmill-labs/windmill/compare/v1.478.0...v1.478.1) (2025-03-20)

View File

@@ -0,0 +1,128 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "gcp_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "subscription_id",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "topic_id",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "delivery_type: _",
"type_info": {
"Custom": {
"name": "delivery_mode",
"kind": {
"Enum": [
"push",
"pull"
]
}
}
}
},
{
"ordinal": 5,
"name": "delivery_config: _",
"type_info": "Jsonb"
},
{
"ordinal": 6,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 8,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 9,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 10,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 11,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 12,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 13,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 14,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 15,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 16,
"name": "enabled",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
true,
false,
false,
false,
false,
false,
false,
true,
true,
false,
true,
false
]
},
"hash": "0102308ffa1c0dbfba54d29246535bb81146a4cfae0ec408435570e0813a3bef"
}

View File

@@ -0,0 +1,26 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n gcp_trigger \n SET \n enabled = $1, \n email = $2, \n edited_by = $3, \n edited_at = now(), \n server_id = NULL, \n error = NULL\n WHERE \n path = $4 AND \n workspace_id = $5 \n RETURNING 1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Bool",
"Varchar",
"Varchar",
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "070b8ad0b59f485fa5bf68082b060f5c3561c37e9c6f2834d234a862a475a6eb"
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value\n FROM resource\n WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true
]
},
"hash": "0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE worker_ping SET \n ping_at = now(), \n jobs_executed = 1, \n current_job_id = $1, \n current_job_workspace_id = 'admins' \n WHERE worker = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": []
},
"hash": "0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182"
}

View File

@@ -0,0 +1,126 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "gcp_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "subscription_id",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "topic_id",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "delivery_type: _",
"type_info": {
"Custom": {
"name": "delivery_mode",
"kind": {
"Enum": [
"push",
"pull"
]
}
}
}
},
{
"ordinal": 5,
"name": "delivery_config: _",
"type_info": "Jsonb"
},
{
"ordinal": 6,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 8,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 9,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 10,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 11,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 12,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 13,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 14,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 15,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 16,
"name": "enabled",
"type_info": "Bool"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
false,
false,
false,
false,
true,
false,
false,
false,
false,
false,
false,
true,
true,
false,
true,
false
]
},
"hash": "15fbe481789a7817bf37415fb935f9ed537fd7d3b266d928af4d5d3dd8bb5c18"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT trigger_config as \"trigger_config: _\", owner, email\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'",
"query": "\n SELECT \n trigger_config AS \"trigger_config: _\", \n owner, \n email\n FROM \n capture_config\n WHERE \n workspace_id = $1\n AND path = $2\n AND is_flow = $3\n AND trigger_kind = $4\n AND last_client_ping > NOW() - INTERVAL '10 seconds'\n AND (\n $5::bool IS FALSE\n OR (\n trigger_config IS NOT NULL\n AND trigger_config ->> 'delivery_type' = 'push'\n )\n )\n ",
"describe": {
"columns": [
{
@@ -37,11 +37,13 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
}
},
"Bool"
]
},
"nullable": [
@@ -50,5 +52,5 @@
false
]
},
"hash": "e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039"
"hash": "23419adcd74c326d716527293eff518b42f4cdb33e034441015494bd26c172d2"
}

View File

@@ -69,7 +69,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -0,0 +1,25 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n capture_config \n SET \n server_id = $1,\n last_server_ping = now(), \n error = 'Connecting...' \n WHERE \n last_client_ping > NOW() - INTERVAL '10 seconds' AND \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp' AND \n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') \n RETURNING true\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text",
"Bool"
]
},
"nullable": [
null
]
},
"hash": "299e16725162888c01712f371785199960264b54c1ddf928c0c654ab15176f63"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_status SET\n workflow_as_code_status = jsonb_set(\n jsonb_set(\n COALESCE(workflow_as_code_status, '{}'::jsonb),\n array[$1],\n COALESCE(workflow_as_code_status->$1, '{}'::jsonb)\n ),\n array[$1, 'started_at'],\n to_jsonb(now()::text)\n )\n WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Uuid"
]
},
"nullable": []
},
"hash": "2ebb0463b790ddf7ba0ee22d8c9afc88eb57c4110a202775003fb48b2f4e317f"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_runtime r SET\n memory_peak = $1,\n ping = now()\n FROM v2_job_queue q\n WHERE r.id = $2 AND q.id = r.id\n RETURNING canceled_by, canceled_reason",
"query": "UPDATE v2_job_runtime r SET\n memory_peak = $1,\n ping = now()\n FROM v2_job_queue q\n WHERE r.id = $2 AND q.id = r.id\n RETURNING canceled_by, canceled_reason",
"describe": {
"columns": [
{
@@ -25,5 +25,5 @@
true
]
},
"hash": "6ff7a025f529c077c1b6c9632a367aa29e2f0fdac3f1984550484d5a06a6ea21"
"hash": "2faa27519624249f16cf89814ab5efe8f8daf928c1194cecacfa8223165fb9f2"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value as \"value: sqlx::types::Json<Box<RawValue>>\" FROM resource WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value: sqlx::types::Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true
]
},
"hash": "30483ae46f6d0452126eb2cd07fc4d960961cc6ee61cf065113b7a48f97caecc"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT EXISTS(\n SELECT \n 1 \n FROM \n gcp_trigger \n WHERE \n path = $1 AND \n workspace_id = $2\n )",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "33ee913ce263600a3f94f90e4a42cf0e4086030f3b7994e4892392765cbe1517"
}

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n capture\n SET \n path = $1\n WHERE \n path = $2 \n AND workspace_id = $3 \n AND is_flow = $4\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text",
"Bool"
]
},
"nullable": []
},
"hash": "37011e7f4cdfc87294e44252cca4f4683a12b82b972842f88f5c01111580224d"
}

View File

@@ -0,0 +1,35 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO gcp_trigger (\n gcp_resource_path,\n subscription_id,\n topic_id,\n delivery_type,\n delivery_config,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4,\n $5,\n $6, \n $7, \n $8, \n $9,\n $10,\n $11,\n $12\n )",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
{
"Custom": {
"name": "delivery_mode",
"kind": {
"Enum": [
"push",
"pull"
]
}
}
},
"Jsonb",
"Varchar",
"Varchar",
"Varchar",
"Bool",
"Varchar",
"Bool",
"Varchar"
]
},
"nullable": []
},
"hash": "3f5520e0ea00569bf169da9abde31617043fcc0bea3ef62c50fcd881f3d48605"
}

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM capture WHERE id = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "41e557e1b63b13c9fcc195901c0bd0de7e03c539ee046955543d9693551246f7"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE capture_config SET last_client_ping = now() WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4",
"query": "\n UPDATE \n capture_config\n SET \n last_client_ping = NOW()\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND trigger_kind = $4\n ",
"describe": {
"columns": [],
"parameters": {
@@ -21,7 +21,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -30,5 +31,5 @@
},
"nullable": []
},
"hash": "c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534"
"hash": "42b4b73e9d60348e2d90fcade9dcad6d8995242dc20a4e14c1a8fae4fc6a9fd2"
}

View File

@@ -1,24 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS (\n SELECT 1 FROM v2_as_completed_job \n WHERE workspace_id = $2 \n AND (job_kind = 'appscript' OR job_kind = 'preview')\n AND created_by = 'anonymous' \n AND started_at > now() - interval '3 hours'\n AND script_path LIKE $3 || '/%' \n AND result @> ('{\"s3\":\"' || $1 || '\"}')::jsonb \n )",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text",
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "43aa468aac174529a74e6108af55a383f8a20e98b8c502929f4dc5041a55e72f"
}

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n capture_config \n SET \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp'\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text",
"Text",
"Bool"
]
},
"nullable": []
},
"hash": "43c8cd9f8560412bb06d9966ccfa2524943ba277c5f9a37c06bd5ee14fd46bda"
}

View File

@@ -0,0 +1,36 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n gcp_trigger \n SET \n gcp_resource_path = $1,\n subscription_id = $2,\n topic_id = $3,\n delivery_type = $4,\n delivery_config = $5,\n is_flow = $6, \n edited_by = $7, \n email = $8,\n script_path = $9,\n path = $10,\n enabled = $11,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $12 AND \n path = $13\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
{
"Custom": {
"name": "delivery_mode",
"kind": {
"Enum": [
"push",
"pull"
]
}
}
},
"Jsonb",
"Bool",
"Varchar",
"Varchar",
"Varchar",
"Varchar",
"Bool",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "44b9bea3651edc8ee732def1241b3d956c004376102ccc1707fc016801599dbd"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_status SET\n workflow_as_code_status = jsonb_set(\n jsonb_set(\n COALESCE(workflow_as_code_status, '{}'::jsonb),\n array[$1],\n COALESCE(workflow_as_code_status->$1, '{}'::jsonb)\n ),\n array[$1, 'started_at'],\n to_jsonb(now()::text)\n )\n WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Uuid"
]
},
"nullable": []
},
"hash": "45d616c92ebcbe30a563e1fa7d2d0e53392e238144b039cfe042587d7fe1dea3"
}

View File

@@ -69,7 +69,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -136,7 +136,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}
@@ -219,7 +220,9 @@
"schedule",
"app",
"ui",
"postgres"
"postgres",
"sqs",
"gcp"
]
}
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_queue\n SET canceled_by = $1\n , canceled_reason = $2\nWHERE id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Uuid"
]
},
"nullable": []
},
"hash": "505250098ab003ff0ca30046df283e54bf44be74305070f10a5720a04c4789f3"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2, memory_usage = $3, wm_memory_usage = $4,\n occupancy_rate = $6, occupancy_rate_15s = $7, occupancy_rate_5m = $8, occupancy_rate_30m = $9 WHERE worker = $5",
"query": "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2, memory_usage = $3, wm_memory_usage = $4,\n occupancy_rate = $6, occupancy_rate_15s = $7, occupancy_rate_5m = $8, occupancy_rate_30m = $9 WHERE worker = $5",
"describe": {
"columns": [],
"parameters": {
@@ -18,5 +18,5 @@
},
"nullable": []
},
"hash": "e968e879d3c52f7dd502c3cd15fc8fbd983a4a3ab25648c562497a27c74b5c8c"
"hash": "506066203c49424e9944eb3948dc1657d3d796e6233e9f0ec925879c705d4773"
}

View File

@@ -0,0 +1,65 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT \n gcp_resource_path, \n script_path,\n is_flow, \n workspace_id,\n path,\n edited_by,\n email,\n delivery_config AS \"delivery_config: _\"\n FROM\n gcp_trigger\n WHERE\n workspace_id = $1 AND\n path = $2 AND\n delivery_type = 'push'::DELIVERY_MODE \n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "gcp_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 3,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "delivery_config: _",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true
]
},
"hash": "5303206bbed76ee3ddc56d8057d8b82359c182ee2a5da6df35a4375d5d2d1ef7"
}

View File

@@ -60,7 +60,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value FROM resource WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true
]
},
"hash": "63c16a4277983aaed0aed54972923919cee3cc444725ac6b7906922554bae800"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE \n capture_config \n SET \n last_server_ping = NULL \n WHERE \n workspace_id = $1 AND \n path = $2 AND \n is_flow = $3 AND \n trigger_kind = 'gcp' AND \n server_id IS NULL\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text",
"Bool"
]
},
"nullable": []
},
"hash": "673564e6c4dcf30dae3d7a75c397998ea800860ede856e5fc6cd1f57d5408333"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT id, created_at, trigger_kind as \"trigger_kind: _\", payload as \"payload!: _\", trigger_extra as \"trigger_extra: _\" FROM capture WHERE id = $1 AND workspace_id = $2",
"query": "\n SELECT \n id, \n created_at, \n trigger_kind AS \"trigger_kind: _\", \n payload AS \"payload!: _\", \n trigger_extra AS \"trigger_extra: _\"\n FROM \n capture\n WHERE \n id = $1 \n AND workspace_id = $2\n ",
"describe": {
"columns": [
{
@@ -29,7 +29,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -60,5 +61,5 @@
true
]
},
"hash": "e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7"
"hash": "6781ba76dfce321dca4634566496ea5d698ac09d8264e35dfaa4cd8edc9e8414"
}

View File

@@ -60,7 +60,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_queue SET tag = $1, running = false WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Uuid"
]
},
"nullable": []
},
"hash": "77701b16ee1f6dd827372835db59bbffc7254af47a8d48b7ba3cf969c2f8398c"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT id, created_at, trigger_kind as \"trigger_kind: _\", CASE WHEN pg_column_size(payload) < 40000 THEN payload ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as \"payload!: _\", trigger_extra as \"trigger_extra: _\"\n FROM capture\n WHERE workspace_id = $1\n AND path = $2 AND is_flow = $3\n AND ($4::trigger_kind IS NULL OR trigger_kind = $4)\n ORDER BY created_at DESC\n OFFSET $5\n LIMIT $6",
"query": "\n SELECT \n id, \n created_at, \n trigger_kind AS \"trigger_kind: _\",\n CASE \n WHEN pg_column_size(payload) < 40000 THEN payload \n ELSE '\"WINDMILL_TOO_BIG\"'::jsonb \n END AS \"payload!: _\",\n trigger_extra AS \"trigger_extra: _\"\n FROM \n capture\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND ($4::trigger_kind IS NULL OR trigger_kind = $4)\n ORDER BY \n created_at DESC\n OFFSET $5\n LIMIT $6\n ",
"describe": {
"columns": [
{
@@ -29,7 +29,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -64,7 +65,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -81,5 +83,5 @@
true
]
},
"hash": "5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd"
"hash": "7cba31d597215a343cb0bca5b204a15fbba193262f7895c2bba90feb4215d6f3"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "\n DELETE FROM \n capture\n WHERE \n id = $1\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "7d5a161d916cb8d1485f8d72e6b4044f505f49edcd2ebeee4a77eae737f48795"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n values ($1, $2, $3, $4, $5, $6, $7, $8) \n ON CONFLICT (job_id) DO UPDATE SET email = $2, username = $3, is_admin = $4, is_operator = $5, folders = $6, groups = $7, workspace_id = $8",
"query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n values ($1, $2, $3, $4, $5, $6, $7, $8) \n ON CONFLICT (job_id) DO UPDATE SET email = $2, username = $3, is_admin = $4, is_operator = $5, folders = $6, groups = $7, workspace_id = $8",
"describe": {
"columns": [],
"parameters": {
@@ -17,5 +17,5 @@
},
"nullable": []
},
"hash": "8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047"
"hash": "7d78efab0a588f56a13a7b5251f0a72f5d341b053218e7aec83a834cf7ccc98f"
}

View File

@@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE capture_config SET path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text",
"Bool"
]
},
"nullable": []
},
"hash": "7e891e053b8545c800d629421f239319a854f761a300970e2fd909f8058ec566"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT trigger_config as \"trigger_config: _\", trigger_kind as \"trigger_kind: _\", error, last_server_ping\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3",
"query": "\n SELECT \n trigger_config AS \"trigger_config: _\", \n trigger_kind AS \"trigger_kind: _\", \n error, \n last_server_ping\n FROM \n capture_config\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3\n ",
"describe": {
"columns": [
{
@@ -24,7 +24,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -55,5 +56,5 @@
true
]
},
"hash": "c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8"
"hash": "7fbf72d9059fcd77e4c1112fa4fa22e4276c1da653475628889ce17dc904fbaa"
}

View File

@@ -33,7 +33,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT owner, email\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'",
"query": "\n SELECT \n owner, \n email\n FROM \n capture_config\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND trigger_kind = $4 \n AND last_client_ping > NOW() - INTERVAL '10 seconds'\n ",
"describe": {
"columns": [
{
@@ -32,7 +32,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -44,5 +45,5 @@
false
]
},
"hash": "71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447"
"hash": "87564a196a1662f524407d853db506bf08c28efe82b68b3d44bafbd3d0e91c29"
}

View File

@@ -60,7 +60,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n gcp_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "92c2b66eb6287449f5f8cc9f8d1329f748006ab131489d2fcac21d32641bb633"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM capture\n WHERE workspace_id = $1\n AND created_at <=\n (\n SELECT created_at\n FROM capture\n WHERE workspace_id = $1\n ORDER BY created_at DESC\n OFFSET $2\n LIMIT 1\n )",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Int8"
]
},
"nullable": []
},
"hash": "97942578df746c8c8103b403cfc4e44ef5a0f082bdde854900064325adc4dd77"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n gcp_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": []
},
"hash": "999edc6f54a9efb6dc6237992dad59f418ed6fc2a98ddb9bbc33dce5f029d904"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value\n FROM global_settings\n WHERE name = 'openai_azure_base_path'",
"query": "SELECT value\n FROM global_settings\n WHERE name = 'openai_azure_base_path'",
"describe": {
"columns": [
{
@@ -16,5 +16,5 @@
false
]
},
"hash": "0cc3618495d5d024b2a173c58a3a8bb2a9d69b7b6e7ed6b0d0064fa2ce9c2e31"
"hash": "a21a16064b51580a8f5c2505cb0c701281dbfa94e40994fdd1cadc86a26c294e"
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text) RETURNING length(logs)",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "length",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text",
"Uuid",
"Varchar"
]
},
"nullable": [
null
]
},
"hash": "a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c"
}

View File

@@ -0,0 +1,26 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n capture_config \n SET \n last_server_ping = now(), \n error = $1 \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp' AND \n server_id = $5 AND \n last_client_ping > NOW() - INTERVAL '10 seconds' \n RETURNING 1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text",
"Text",
"Text",
"Bool",
"Text"
]
},
"nullable": [
null
]
},
"hash": "a7ffc5b983d365159ef379ec6a2ab0dc7217d1b3f86f362c213982984d2cf652"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT content AS \"content!: String\",\n lock AS \"lock: String\", language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", schema AS \"schema: String\", schema_validation AS \"schema_validation: bool\", codebase LIKE '%.tar' as use_tar FROM script WHERE hash = $1 LIMIT 1",
"query": "SELECT content AS \"content!: String\",\n lock AS \"lock: String\", language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", schema AS \"schema: String\", schema_validation AS \"schema_validation: bool\", codebase LIKE '%.tar' as use_tar FROM script WHERE hash = $1 LIMIT 1",
"describe": {
"columns": [
{
@@ -40,7 +40,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}
@@ -82,5 +83,5 @@
null
]
},
"hash": "03ae5b1c912b13a8a7aadf50cb4984a2ea952e782fd52eb3088454690bd13dd1"
"hash": "a9db7b2f435bb82acb8c5eeb7f800b28f3256491fdaa168591adc7b4b9f3327a"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\",\n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\",\n EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS \"mqtt_used!\",\n EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS \"sqs_used!\"\n ",
"query": "\n SELECT\n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\",\n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\",\n EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS \"mqtt_used!\",\n EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS \"sqs_used!\",\n EXISTS(SELECT 1 FROM gcp_trigger WHERE workspace_id = $1) AS \"gcp_used!\"\n ",
"describe": {
"columns": [
{
@@ -37,6 +37,11 @@
"ordinal": 6,
"name": "sqs_used!",
"type_info": "Bool"
},
{
"ordinal": 7,
"name": "gcp_used!",
"type_info": "Bool"
}
],
"parameters": {
@@ -51,8 +56,9 @@
null,
null,
null,
null,
null
]
},
"hash": "9ae20f2f29406cfa5337736ad710c496f1757d950cdec63c93c77fe79d5212cc"
"hash": "aa0215d4174c1aeda8631bcd582c895329d2daf722d360fbcbdef6f04bb1400f"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2,\n occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus),\n memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11 WHERE worker = $6",
"query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2,\n occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus),\n memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11 WHERE worker = $6",
"describe": {
"columns": [],
"parameters": {
@@ -20,5 +20,5 @@
},
"nullable": []
},
"hash": "a439552f74ed0ba305e3d9cb99ae9e5d24834082ebf2fe9fd3964fdd80b69ccb"
"hash": "aa523c363186575b4bd2537b8e2430e6938e7cc35f8c9e2d1c5459a85443cbdd"
}

View File

@@ -64,7 +64,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO capture_config\n (workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (workspace_id, path, is_flow, trigger_kind)\n DO UPDATE SET trigger_config = $5, owner = $6, email = $7, server_id = NULL, error = NULL",
"query": "\n INSERT INTO capture_config (\n workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7\n )\n ON CONFLICT (workspace_id, path, is_flow, trigger_kind)\n DO UPDATE \n SET \n trigger_config = $5, \n owner = $6, \n email = $7, \n server_id = NULL, \n error = NULL\n ",
"describe": {
"columns": [],
"parameters": {
@@ -21,7 +21,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -33,5 +34,5 @@
},
"nullable": []
},
"hash": "62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a"
"hash": "b3f0595cacba194e08b9a3e244d9e637e9e156cd85b69126c87dfff89a47711d"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_queue\n SET canceled_by = 'timeout'\n , canceled_reason = $1\n WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Uuid"
]
},
"nullable": []
},
"hash": "c00bae0d8c9bee37cbad4de4cb02c80d00f52a3fc32bf32271ebc90f7837abda"
}

View File

@@ -0,0 +1,50 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n path,\n is_flow,\n workspace_id,\n owner,\n email,\n trigger_config as \"trigger_config!: _\"\n FROM\n capture_config\n WHERE\n trigger_kind = 'gcp' AND\n last_client_ping > NOW() - INTERVAL '10 seconds' AND\n trigger_config IS NOT NULL AND\n trigger_config->>'delivery_type' IS DISTINCT FROM 'push' AND\n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 2,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "owner",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "trigger_config!: _",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
false,
false,
false,
false,
true
]
},
"hash": "c0e6dbce7a401b06e1bf45155c3f81572818177a6024e743b12f558b46edf74c"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "\n DELETE \n FROM \n gcp_trigger \n WHERE \n workspace_id = $1 AND \n path = $2\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": []
},
"hash": "c19a60a9dc3f95af218baf40c62f14572ac204cfe377166aa1d91cf58f731f50"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE worker_ping SET \nping_at = now(), \njobs_executed = 1, \ncurrent_job_id = $1, \ncurrent_job_workspace_id = 'admins' \nWHERE worker = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": []
},
"hash": "c3025cdb6e421e1225d420e8b1efd18d1dd3bb2fac53c1f2df648b61fb7488aa"
}

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n capture_config\n SET \n path = $1\n WHERE \n path = $2 \n AND workspace_id = $3 \n AND is_flow = $4\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text",
"Bool"
]
},
"nullable": []
},
"hash": "c353aa9abe673749f0836fd16894d91ba89aab2318d85621ad2868017adfb48a"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO outstanding_wait_time(job_id, self_wait_time_ms) VALUES ($1, $2)\n ON CONFLICT (job_id) DO UPDATE SET self_wait_time_ms = EXCLUDED.self_wait_time_ms",
"query": "INSERT INTO outstanding_wait_time(job_id, self_wait_time_ms) VALUES ($1, $2)\n ON CONFLICT (job_id) DO UPDATE SET self_wait_time_ms = EXCLUDED.self_wait_time_ms",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
"hash": "f367a1c8f80dd414dcbcd949374eeb5770796f00b5b3d547163bcfdaed65d8ae"
"hash": "c53e1c7133c8ae187656eef5999509fae17fb0ba43e084327accbb5b24c3dfbd"
}

View File

@@ -41,7 +41,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -0,0 +1,25 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n gcp_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text",
"Text",
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "df2a426658f0a36683cc2163fe70912d0221d191b8ab091601cb53cdd932ad58"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "\n DELETE FROM \n capture\n WHERE \n workspace_id = $1\n AND created_at <= (\n SELECT \n created_at\n FROM \n capture\n WHERE \n workspace_id = $1\n ORDER BY \n created_at DESC\n OFFSET $2\n LIMIT 1\n )\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Int8"
]
},
"nullable": []
},
"hash": "e04a8a9f1e9cc3bb5c990194585e08f4c248e1af1c6580bdf8f2735ae1388981"
}

View File

@@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE capture SET path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text",
"Bool"
]
},
"nullable": []
},
"hash": "e70c3da5864b7946735b7ad6c416874ee8b277a2914dff6f9c33a1f5a2351114"
}

View File

@@ -55,7 +55,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS (\n SELECT 1 FROM v2_as_completed_job\n WHERE workspace_id = $2\n AND (job_kind = 'appscript' OR job_kind = 'preview')\n AND created_by = 'anonymous'\n AND started_at > now() - interval '3 hours'\n AND script_path LIKE $3 || '/%'\n AND result @> ('{\"s3\":\"' || $1 || '\"}')::jsonb\n )",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text",
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "ec9980f80a8bfa4b09225035e8a1f78d7d61fbf83dda9b39bb22e0f9584d221b"
}

View File

@@ -0,0 +1,129 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "gcp_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "subscription_id",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "topic_id",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "delivery_type: _",
"type_info": {
"Custom": {
"name": "delivery_mode",
"kind": {
"Enum": [
"push",
"pull"
]
}
}
}
},
{
"ordinal": 5,
"name": "delivery_config: _",
"type_info": "Jsonb"
},
{
"ordinal": 6,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 8,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 9,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 10,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 11,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 12,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 13,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 14,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 15,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 16,
"name": "enabled",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
true,
false,
false,
false,
false,
false,
false,
true,
true,
false,
true,
false
]
},
"hash": "f4f6336fc671b00bed7835124892f7a4d3bbe673f7c48153819dab385a5cb357"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT EXISTS (SELECT 1\n FROM workspace_settings\n WHERE workspace_id <> $1\n AND slack_command_script IS NOT NULL\n AND slack_team_id = $2\n AND (SELECT slack_command_script IS NOT NULL FROM workspace_settings WHERE workspace_id = $1))\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "f955a01779f5441efc6aa9364b24c79b3cbc6413046c3b6099d19f675d8a395b"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO capture (workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7)",
"query": "\n INSERT INTO \n capture (\n workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7\n )\n ",
"describe": {
"columns": [],
"parameters": {
@@ -21,7 +21,8 @@
"nats",
"postgres",
"sqs",
"mqtt"
"mqtt",
"gcp"
]
}
}
@@ -33,5 +34,5 @@
},
"nullable": []
},
"hash": "07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5"
"hash": "f9f7f31390f8ea1f4facd8e6a888886ea136f2327169bf93f126e2ef130d0946"
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT EXISTS (SELECT 1 \n FROM workspace_settings \n WHERE workspace_id <> $1 \n AND slack_command_script IS NOT NULL\n AND slack_team_id = $2\n AND (SELECT slack_command_script IS NOT NULL FROM workspace_settings WHERE workspace_id = $1))\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "fa59674af1d1a4ceb696fc883005ef114772f7d2ee0f60cb1358cdb7f0b5cd0c"
}

View File

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

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE \n gcp_trigger \n SET \n enabled = FALSE, \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "fd0a0ca4a107dc813240ad71f372ab7e0bf26431158fbfb5f0023ed847ca7dc5"
}

View File

@@ -64,7 +64,8 @@
"ansible",
"csharp",
"oracledb",
"nu"
"nu",
"java"
]
}
}

View File

@@ -11,5 +11,5 @@
"remote.autoForwardPorts": true,
"conventionalCommits.scopes": [
"restructring triggers, decoding trigger message on work"
]
],
}

617
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.478.1"
version = "1.481.0"
authors.workspace = true
edition.workspace = true
@@ -32,7 +32,7 @@ members = [
]
[workspace.package]
version = "1.478.1"
version = "1.481.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -49,6 +49,7 @@ lto = "thin"
[features]
default = []
agent_worker_server = ["windmill-api/agent_worker_server"]
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise"]
enterprise_saml = ["windmill-api/enterprise_saml", "oauth2"]
stripe = ["windmill-api/stripe"]
@@ -68,13 +69,14 @@ nats = ["windmill-api/nats"]
otel = ["windmill-common/otel", "windmill-worker/otel"]
dind = ["windmill-worker/dind"]
websocket = ["windmill-api/websocket"]
http_trigger = ["windmill-api/http_trigger"]
postgres_trigger = ["windmill-api/postgres_trigger"]
mqtt_trigger = ["windmill-api/mqtt_trigger"]
sqs_trigger = ["windmill-api/sqs_trigger"]
gcp_trigger = ["windmill-api/gcp_trigger"]
smtp = ["windmill-api/smtp", "windmill-common/smtp"]
license = ["windmill-api/license"]
oauth2 = ["windmill-api/oauth2"]
http_trigger = ["windmill-api/http_trigger"]
zip = ["windmill-api/zip"]
static_frontend = ["windmill-api/static_frontend"]
scoped_cache = ["windmill-common/scoped_cache"]
@@ -170,6 +172,9 @@ windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" }
windmill-parser-php = { path = "./parsers/windmill-parser-php" }
windmill-api-client = { path = "./windmill-api-client" }
reqwest-retry = "^0"
reqwest-middleware = { version = "^0", features = ["json"] }
rustls = "0.23.0"
memchr = "2.7.4"
axum = { version = "^0.7", features = ["multipart"] }
@@ -238,6 +243,8 @@ deno_runtime = { version = "0.198.0", features = ["transpile"] }
deno_telemetry = "0.12.0"
deno_error = "=0.5.5"
google-cloud-pubsub = "0.30.0"
google-cloud-googleapis = {version = "0.16.1", features = ["pubsub"]}
# TODO: remove once deno fixes the issue on their end
# https://github.com/denoland/deno/issues/28557
winapi = { version = "0.3.9", features = ["sysinfoapi"] }
@@ -249,8 +256,6 @@ swc_ecma_visit = "=0.104.8"
async-recursion = "^1"
# remove when async-strip is removed, needed to pin rand
half = "=2.4.1"
base64 = "^0"
base32 = "^0"
hmac = "0.12.1"
@@ -343,9 +348,12 @@ opentelemetry-semantic-conventions = { version = "0.27.0", features = ["semconv_
bollard = "0.18.1"
tonic = { version = "^0", features = ["tls-native-roots"] }
tonic = { version = "=0.12.3", features = ["tls-native-roots"] }
byteorder = "1.5.0"
# todo remove
half = "=2.4.1"
tikv-jemallocator = { version = "0.5" }
tikv-jemalloc-sys = { version = "^0.5" }
tikv-jemalloc-ctl = { version = "^0.5" }

View File

@@ -1 +1 @@
94cbac33756c1fba56da037456f44a4d294475b1
85c37983ffb8f622458425c182613206625c6cee

View File

@@ -0,0 +1,3 @@
-- Add down migration script here
DROP TABLE gcp_trigger;
DROP TYPE DELIVERY_MODE;

View File

@@ -0,0 +1,80 @@
-- Add up migration script here
ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'gcp';
CREATE TYPE DELIVERY_MODE AS ENUM ('push', 'pull');
CREATE TABLE gcp_trigger (
gcp_resource_path VARCHAR(255) NOT NULL,
topic_id VARCHAR(255) NOT NULL CHECK (
CHAR_LENGTH(topic_id) BETWEEN 3 AND 255
),
subscription_id VARCHAR(255) NOT NULL CHECK (
CHAR_LENGTH(subscription_id) BETWEEN 3 AND 255
),
delivery_type DELIVERY_MODE NOT NULL,
delivery_config JSONB NULL CHECK (delivery_type != 'push'::DELIVERY_MODE OR (delivery_config IS NOT NULL)),
path VARCHAR(255) NOT NULL,
script_path VARCHAR(255) NOT NULL,
is_flow BOOLEAN NOT NULL,
workspace_id VARCHAR(50) NOT NULL,
edited_by VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL,
edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
extra_perms JSONB NOT NULL DEFAULT '{}',
server_id VARCHAR(50),
last_server_ping TIMESTAMPTZ,
error TEXT,
enabled BOOLEAN NOT NULL,
PRIMARY KEY (path, workspace_id)
);
CREATE UNIQUE INDEX unique_subscription_per_gcp_resource
ON gcp_trigger (subscription_id, gcp_resource_path, workspace_id);
GRANT ALL ON gcp_trigger TO windmill_user;
GRANT ALL ON gcp_trigger TO windmill_admin;
ALTER TABLE gcp_trigger ENABLE ROW LEVEL SECURITY;
CREATE POLICY admin_policy ON gcp_trigger FOR ALL TO windmill_admin USING (true);
CREATE POLICY see_folder_extra_perms_user_select ON gcp_trigger FOR SELECT TO windmill_user
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]));
CREATE POLICY see_folder_extra_perms_user_insert ON gcp_trigger FOR INSERT TO windmill_user
WITH CHECK (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
CREATE POLICY see_folder_extra_perms_user_update ON gcp_trigger FOR UPDATE TO windmill_user
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
CREATE POLICY see_folder_extra_perms_user_delete ON gcp_trigger FOR DELETE TO windmill_user
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
CREATE POLICY see_own ON gcp_trigger FOR ALL TO windmill_user
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'u' AND SPLIT_PART(gcp_trigger.path, '/', 2) = current_setting('session.user'));
CREATE POLICY see_member ON gcp_trigger FOR ALL TO windmill_user
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'g' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[]));
CREATE POLICY see_extra_perms_user_select ON gcp_trigger FOR SELECT TO windmill_user
USING (extra_perms ? CONCAT('u/', current_setting('session.user')));
CREATE POLICY see_extra_perms_user_insert ON gcp_trigger FOR INSERT TO windmill_user
WITH CHECK ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
CREATE POLICY see_extra_perms_user_update ON gcp_trigger FOR UPDATE TO windmill_user
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
CREATE POLICY see_extra_perms_user_delete ON gcp_trigger FOR DELETE TO windmill_user
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
CREATE POLICY see_extra_perms_groups_select ON gcp_trigger FOR SELECT TO windmill_user
USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]);
CREATE POLICY see_extra_perms_groups_insert ON gcp_trigger FOR INSERT TO windmill_user
WITH CHECK (exists(
SELECT key, value FROM jsonb_each_text(extra_perms)
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
AND value::boolean));
CREATE POLICY see_extra_perms_groups_update ON gcp_trigger FOR UPDATE TO windmill_user
USING (exists(
SELECT key, value FROM jsonb_each_text(extra_perms)
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
AND value::boolean));
CREATE POLICY see_extra_perms_groups_delete ON gcp_trigger FOR DELETE TO windmill_user
USING (exists(
SELECT key, value FROM jsonb_each_text(extra_perms)
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
AND value::boolean));

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,3 @@
-- Add up migration script here
ALTER TYPE job_trigger_kind ADD VALUE IF NOT EXISTS 'sqs';
ALTER TYPE job_trigger_kind ADD VALUE IF NOT EXISTS 'gcp';

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,284 @@
-- Add up migration script here
-- Add up migration script here
UPDATE script SET content = 'import * as wmill from "windmill-cli@1.481.0"
export async function main() {
await wmill.hubPull({ workspace: "admins", token: process.env["WM_TOKEN"], baseUrl: globalThis.process.env["BASE_URL"] });
}
', language = 'bun',
lock = '{
"dependencies": {
"windmill-cli": "1.481.0"
}
}
//bun.lock
{
"lockfileVersion": 1,
"workspaces": {
"": {
"dependencies": {
"windmill-cli": "1.481.0",
},
},
},
"packages": {
"@ayonli/jsext": ["@ayonli/jsext@1.6.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "sudo-prompt": "^9.2.1", "ws": "^8.17.0", "zod": "^3.23.8" } }, "sha512-dMQuZJIVadEgQ6xp1Q5hRv2JfwANDwnElH6kZWuSjcnAjhvtCoQQ02CdOipfCd4cyrFpaI+8yBboiT9OkRaVyg=="],
"@deno/shim-deno": ["@deno/shim-deno@0.18.2", "", { "dependencies": { "@deno/shim-deno-test": "^0.5.0", "which": "^4.0.0" } }, "sha512-oQ0CVmOio63wlhwQF75zA4ioolPvOwAoK0yuzcS5bDC1JUvH3y1GS8xPh8EOpcoDQRU4FTG8OQfxhpR+c6DrzA=="],
"@deno/shim-deno-test": ["@deno/shim-deno-test@0.5.0", "", {}, "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.2", "", { "os": "android", "cpu": "arm" }, "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.2", "", { "os": "android", "cpu": "x64" }, "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.2", "", { "os": "linux", "cpu": "arm" }, "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.2", "", { "os": "linux", "cpu": "x64" }, "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.2", "", { "os": "none", "cpu": "arm64" }, "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.2", "", { "os": "none", "cpu": "x64" }, "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.2", "", { "os": "win32", "cpu": "x64" }, "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA=="],
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
"brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
"content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="],
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"default-browser": ["default-browser@5.2.1", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg=="],
"default-browser-id": ["default-browser-id@5.0.0", "", {}, "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA=="],
"define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
"diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-main": ["es-main@1.3.0", "", {}, "sha512-AzORKdz1Zt97TzbYQnIrI3ZiibWpRXUfpo/w0xOJ20GpNYd2bd3MU9m31zS/aJ1TJl6JfLTok83Y8HjNunYT0A=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"esbuild": ["esbuild@0.25.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.2", "@esbuild/android-arm": "0.25.2", "@esbuild/android-arm64": "0.25.2", "@esbuild/android-x64": "0.25.2", "@esbuild/darwin-arm64": "0.25.2", "@esbuild/darwin-x64": "0.25.2", "@esbuild/freebsd-arm64": "0.25.2", "@esbuild/freebsd-x64": "0.25.2", "@esbuild/linux-arm": "0.25.2", "@esbuild/linux-arm64": "0.25.2", "@esbuild/linux-ia32": "0.25.2", "@esbuild/linux-loong64": "0.25.2", "@esbuild/linux-mips64el": "0.25.2", "@esbuild/linux-ppc64": "0.25.2", "@esbuild/linux-riscv64": "0.25.2", "@esbuild/linux-s390x": "0.25.2", "@esbuild/linux-x64": "0.25.2", "@esbuild/netbsd-arm64": "0.25.2", "@esbuild/netbsd-x64": "0.25.2", "@esbuild/openbsd-arm64": "0.25.2", "@esbuild/openbsd-x64": "0.25.2", "@esbuild/sunos-x64": "0.25.2", "@esbuild/win32-arm64": "0.25.2", "@esbuild/win32-ia32": "0.25.2", "@esbuild/win32-x64": "0.25.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ=="],
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
"express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
"is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
"jszip": ["jszip@3.7.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "set-immediate-shim": "~1.0.1" } }, "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg=="],
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
"minimatch": ["minimatch@10.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"open": ["open@10.1.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw=="],
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
"path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="],
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
"raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="],
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
"run-applescript": ["run-applescript@7.0.0", "", {}, "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
"set-immediate-shim": ["set-immediate-shim@1.0.1", "", {}, "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ=="],
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"sudo-prompt": ["sudo-prompt@9.2.1", "", {}, "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw=="],
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
"windmill-cli": ["windmill-cli@1.481.0", "", { "dependencies": { "@ayonli/jsext": "*", "@deno/shim-deno": "~0.18.0", "diff": "*", "es-main": "*", "esbuild": "*", "express": "*", "get-port": "7.1.0", "jszip": "3.7.1", "minimatch": "*", "open": "*", "ws": "*" }, "bin": { "wmill": "esm/main.js" } }, "sha512-nIIrt+/+TqeyHlgcDnPMTBH3CZX4TMVwxy2UFD+5lOI5OY9JOtCpWk5UPn6BOs6fw1DG0MIBJG7AhCuPelfSiQ=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
"zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
"readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
}
}'
WHERE hash = -28028598712388162 AND workspace_id = 'admins';

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,3 @@
-- Add up migration script here
GRANT ALL on workspace_runnable_dependencies TO windmill_user;
GRANT ALL on workspace_runnable_dependencies TO windmill_admin;

View File

@@ -45,7 +45,7 @@ pub fn parse_powershell_sig(code: &str) -> anyhow::Result<MainArgSignature> {
}
lazy_static::lazy_static! {
static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?$"#).unwrap();
static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+)\}|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?$"#).unwrap();
pub static ref RE_POWERSHELL_PARAM: Regex = Regex::new(r#"(?m)param[\t ]*\(([^)]*)\)"#).unwrap();
static ref RE_POWERSHELL_ARGS: Regex = Regex::new(r#"(?:\[(\w+)\])?\$(\w+)[\t ]*(?:=[\t ]*(?:(?:(?:"|')([^"\n\r\$]*)(?:"|'))|([\d.]+)))?"#).unwrap();
@@ -57,11 +57,12 @@ fn parse_bash_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
hm.insert(
cap.get(2)
.or(cap.get(3))
.or(cap.get(4))
.and_then(|x| x.as_str().parse::<i32>().ok())
.ok_or_else(|| anyhow!("Impossible to parse arg digit"))?,
(
cap[1].to_string(),
cap.get(4).map(|x| x.as_str().to_string()),
cap.get(5).map(|x| x.as_str().to_string()),
),
);
}

View File

@@ -162,4 +162,4 @@ pub fn parse_java(code: &str) -> String {
wrap_sig(windmill_parser_java::parse_java_signature(code))
}
// KJQXZ
// for related places search: ADD_NEW_LANG

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@ use std::{
use chrono::{NaiveDateTime, Utc};
use futures::{stream::FuturesUnordered, StreamExt};
use serde::{de::DeserializeOwned, Deserializer};
use serde::de::DeserializeOwned;
use sqlx::{Pool, Postgres};
use tokio::{
join,
@@ -34,7 +34,8 @@ use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts};
#[cfg(feature = "oauth2")]
use windmill_common::global_settings::OAUTH_SETTING;
use windmill_common::{
ee::CriticalErrorChannel, error, flow_status::{FlowStatus, FlowStatusModule}, global_settings::{
utils::empty_string_as_none,
agent_workers::DECODED_AGENT_TOKEN, auth::create_token_for_owner, ee::CriticalErrorChannel, error, flow_status::{FlowStatus, FlowStatusModule}, global_settings::{
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING,
CRITICAL_ERROR_CHANNELS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
DEFAULT_TAGS_WORKSPACES_SETTING, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
@@ -45,14 +46,12 @@ use windmill_common::{
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
}, indexer::load_indexer_config, jobs::QueuedJob, jwt::JWT_SECRET, oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH, server::load_smtp_config, tracing_init::JSON_FMT, users::truncate_token, utils::{now_from_db, rd_string, report_critical_error, Mode}, worker::{
load_worker_config, make_pull_query, make_suspended_pull_query, reload_custom_tags_setting,
update_min_version, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG,
SMTP_CONFIG, TMP_DIR, WORKER_CONFIG, WORKER_GROUP,
}, KillpillSender, BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS
load_worker_config, reload_custom_tags_setting, store_pull_query, store_suspended_pull_query, update_min_version, Connection, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, SCRIPT_TOKEN_EXPIRY, SMTP_CONFIG, TMP_DIR, WORKER_CONFIG, WORKER_GROUP
}, KillpillSender, BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS,
};
use windmill_queue::{cancel_job, MiniPulledJob};
use windmill_queue::{cancel_job, MiniPulledJob, SameWorkerPayload};
use windmill_worker::{
create_token_for_owner, handle_job_error, AuthedClient, SameWorkerPayload, SameWorkerSender, SendResult, BUNFIG_INSTALL_SCOPES, INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, SCRIPT_TOKEN_EXPIRY
handle_job_error, AuthedClient, JobCompletedSender, SameWorkerSender, BUNFIG_INSTALL_SCOPES, INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL
};
#[cfg(feature = "parquet")]
@@ -107,6 +106,11 @@ lazy_static::lazy_static! {
.and_then(|x| x.parse::<bool>().ok())
.unwrap_or(true);
pub static ref DISABLE_ZOMBIE_JOBS_MONITORING: bool = std::env::var("DISABLE_ZOMBIE_JOBS_MONITORING")
.ok()
.and_then(|x| x.parse::<bool>().ok())
.unwrap_or(false);
static ref QUEUE_COUNT_TAGS: Arc<RwLock<Vec<String>>> = Arc::new(RwLock::new(Vec::new()));
@@ -115,82 +119,113 @@ lazy_static::lazy_static! {
}
pub async fn initial_load(
db: &Pool<Postgres>,
conn: &Connection,
tx: KillpillSender,
worker_mode: bool,
server_mode: bool,
#[cfg(feature = "parquet")] disable_s3_store: bool,
) {
if let Err(e) = load_metrics_enabled(db).await {
if let Err(e) = reload_base_url_setting(&conn).await {
tracing::error!("Error loading base url: {:?}", e)
}
if let Some(db) = conn.as_sql() {
if let Err(e) = reload_critical_error_channels_setting(&db).await {
tracing::error!("Could loading critical error emails setting: {:?}", e);
}
}
if let Err(e) = load_metrics_enabled(conn).await {
tracing::error!("Error loading expose metrics: {e:#}");
}
if let Err(e) = load_metrics_debug_enabled(db).await {
if let Err(e) = load_metrics_debug_enabled(conn).await {
tracing::error!("Error loading expose debug metrics: {e:#}");
}
if let Err(e) = reload_critical_alert_mute_ui_setting(db).await {
if let Err(e) = reload_critical_alert_mute_ui_setting(conn).await {
tracing::error!("Error loading critical alert mute ui setting: {e:#}");
}
if let Err(e) = load_tag_per_workspace_enabled(db).await {
tracing::error!("Error loading default tag per workpsace: {e:#}");
}
if let Some(db) = conn.as_sql() {
if let Err(e) = load_tag_per_workspace_enabled(db).await {
tracing::error!("Error loading default tag per workpsace: {e:#}");
}
if let Err(e) = load_tag_per_workspace_workspaces(db).await {
tracing::error!("Error loading default tag per workpsace workspaces: {e:#}");
if let Err(e) = load_tag_per_workspace_workspaces(db).await {
tracing::error!("Error loading default tag per workpsace workspaces: {e:#}");
}
}
if server_mode {
load_require_preexisting_user(db).await;
if let Some(db) = conn.as_sql() {
load_require_preexisting_user(db).await;
}
}
if worker_mode {
load_keep_job_dir(db).await;
reload_worker_config(&db, tx, false).await;
load_keep_job_dir(conn).await;
match conn {
Connection::Sql(db) => {
reload_worker_config(&db, tx, false).await;
}
Connection::Http(_) => {
// TODO: reload worker config from http
WORKER_CONFIG.write().await.worker_tags = DECODED_AGENT_TOKEN.as_ref().map(|x| x.tags.clone()).unwrap_or_default();
}
}
}
if let Err(e) = reload_custom_tags_setting(db).await {
tracing::error!("Error reloading custom tags: {:?}", e)
}
if let Err(e) = reload_hub_base_url_setting(db, server_mode).await {
if let Err(e) = reload_hub_base_url_setting(conn, server_mode).await {
tracing::error!("Error reloading hub base url: {:?}", e)
}
if let Err(e) = reload_jwt_secret_setting(&db).await {
tracing::error!("Could not reload jwt secret setting: {:?}", e);
if let Some(db) = conn.as_sql() {
if let Err(e) = reload_jwt_secret_setting(db).await {
tracing::error!("Could not reload jwt secret setting: {:?}", e);
}
if let Err(e) = reload_custom_tags_setting(db).await {
tracing::error!("Error reloading custom tags: {:?}", e)
}
}
#[cfg(feature = "parquet")]
if !disable_s3_store {
reload_s3_cache_setting(&db).await;
if let Some(db) = conn.as_sql() {
reload_s3_cache_setting(db).await;
}
}
reload_smtp_config(&db).await;
if let Some(db) = conn.as_sql() {
reload_smtp_config(db).await;
}
if server_mode {
reload_retention_period_setting(&db).await;
reload_request_size(&db).await;
reload_saml_metadata_setting(&db).await;
reload_scim_token_setting(&db).await;
reload_retention_period_setting(&conn).await;
reload_request_size(&conn).await;
reload_saml_metadata_setting(&conn).await;
reload_scim_token_setting(&conn).await;
}
if worker_mode {
reload_job_default_timeout_setting(&db).await;
reload_extra_pip_index_url_setting(&db).await;
reload_pip_index_url_setting(&db).await;
reload_npm_config_registry_setting(&db).await;
reload_bunfig_install_scopes_setting(&db).await;
reload_instance_python_version_setting(&db).await;
reload_nuget_config_setting(&db).await;
reload_maven_repos_setting(&db).await;
reload_no_default_maven_setting(&db).await;
reload_job_default_timeout_setting(&conn).await;
reload_extra_pip_index_url_setting(&conn).await;
reload_pip_index_url_setting(&conn).await;
reload_npm_config_registry_setting(&conn).await;
reload_bunfig_install_scopes_setting(&conn).await;
reload_instance_python_version_setting(&conn).await;
reload_nuget_config_setting(&conn).await;
reload_maven_repos_setting(&conn).await;
reload_no_default_maven_setting(&conn).await;
}
}
pub async fn load_metrics_enabled(db: &DB) -> error::Result<()> {
let metrics_enabled = load_value_from_global_settings(db, EXPOSE_METRICS_SETTING).await;
pub async fn load_metrics_enabled(conn: &Connection) -> error::Result<()> {
let metrics_enabled = load_value_from_global_settings_with_conn(conn, EXPOSE_METRICS_SETTING, true).await;
match metrics_enabled {
Ok(Some(serde_json::Value::Bool(t))) => METRICS_ENABLED.store(t, Ordering::Relaxed),
_ => (),
@@ -198,14 +233,6 @@ pub async fn load_metrics_enabled(db: &DB) -> error::Result<()> {
Ok(())
}
fn empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let option = <Option<String> as serde::Deserialize>::deserialize(deserializer)?;
Ok(option.filter(|s| !s.is_empty()))
}
#[derive(serde::Deserialize)]
struct OtelSetting {
metrics_enabled: Option<bool>,
@@ -313,9 +340,9 @@ pub async fn load_tag_per_workspace_workspaces(db: &DB) -> error::Result<()> {
Ok(())
}
pub async fn reload_critical_alert_mute_ui_setting(db: &DB) -> error::Result<()> {
pub async fn reload_critical_alert_mute_ui_setting(conn: &Connection) -> error::Result<()> {
if let Ok(Some(serde_json::Value::Bool(t))) =
load_value_from_global_settings(db, CRITICAL_ALERT_MUTE_UI_SETTING).await
load_value_from_global_settings_with_conn(conn, CRITICAL_ALERT_MUTE_UI_SETTING, true).await
{
CRITICAL_ALERT_MUTE_UI_ENABLED.store(t, Ordering::Relaxed);
@@ -323,8 +350,8 @@ pub async fn reload_critical_alert_mute_ui_setting(db: &DB) -> error::Result<()>
Ok(())
}
pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> {
let metrics_enabled = load_value_from_global_settings(db, EXPOSE_DEBUG_METRICS_SETTING).await;
pub async fn load_metrics_debug_enabled(conn: &Connection) -> error::Result<()> {
let metrics_enabled = load_value_from_global_settings_with_conn(conn, EXPOSE_DEBUG_METRICS_SETTING, true).await;
match metrics_enabled {
Ok(Some(serde_json::Value::Bool(t))) => {
METRICS_DEBUG_ENABLED.store(t, Ordering::Relaxed);
@@ -475,8 +502,8 @@ fn get_worker_group(mode: &Mode) -> Option<String> {
}
}
pub fn send_logs_to_object_store(db: &DB, hostname: &str, mode: &Mode) {
let db = db.clone();
pub fn send_logs_to_object_store(conn: &Connection, hostname: &str, mode: &Mode) {
let conn = conn.clone();
let hostname = hostname.to_string();
let mode = mode.clone();
let worker_group = get_worker_group(&mode);
@@ -491,7 +518,7 @@ pub fn send_logs_to_object_store(db: &DB, hostname: &str, mode: &Mode) {
&hostname,
&mode,
&worker_group,
&db,
&conn,
snd_highest_file,
false,
)
@@ -500,11 +527,11 @@ pub fn send_logs_to_object_store(db: &DB, hostname: &str, mode: &Mode) {
});
}
pub async fn send_current_log_file_to_object_store(db: &DB, hostname: &str, mode: &Mode) {
pub async fn send_current_log_file_to_object_store(conn: &Connection, hostname: &str, mode: &Mode) {
tracing::info!("Sending current log file to object store");
let (highest_file, _) = find_two_highest_files(hostname).await;
let worker_group = get_worker_group(&mode);
send_log_file_to_object_store(hostname, mode, &worker_group, db, highest_file, true).await;
send_log_file_to_object_store(hostname, mode, &worker_group, conn, highest_file, true).await;
}
fn get_now_and_str() -> (NaiveDateTime, String) {
@@ -524,7 +551,7 @@ async fn send_log_file_to_object_store(
hostname: &str,
mode: &Mode,
worker_group: &Option<String>,
db: &Pool<Postgres>,
conn: &Connection,
snd_highest_file: Option<String>,
use_now: bool,
) {
@@ -584,18 +611,23 @@ async fn send_log_file_to_object_store(
let (ok_lines, err_lines) = read_log_counters(ts_str);
if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)",
hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT)
.execute(db)
.await {
tracing::error!("Error inserting log file: {:?}", e);
} else {
if let Err(e) = LAST_LOG_FILE_SENT.lock().map(|mut last_log_file_sent| {
last_log_file_sent.replace(ts);
}) {
tracing::error!("Error updating last log file sent: {:?}", e);
if let Some(db) = conn.as_sql() {
if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)",
hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT)
.execute(db)
.await {
tracing::error!("Error inserting log file: {:?}", e);
} else {
if let Err(e) = LAST_LOG_FILE_SENT.lock().map(|mut last_log_file_sent| {
last_log_file_sent.replace(ts);
}) {
tracing::error!("Error updating last log file sent: {:?}", e);
}
tracing::info!("Log file sent: {}", highest_file);
}
tracing::info!("Log file sent: {}", highest_file);
} else {
// tracing::warn!("Not sending log file to object store in agent mode");
()
}
}
}
@@ -618,8 +650,8 @@ fn read_log_counters(ts_str: String) -> (usize, usize) {
(ok_lines, err_lines)
}
pub async fn load_keep_job_dir(db: &DB) {
let value = load_value_from_global_settings(db, KEEP_JOB_DIR_SETTING).await;
pub async fn load_keep_job_dir(conn: &Connection) {
let value = load_value_from_global_settings_with_conn(conn, KEEP_JOB_DIR_SETTING, true).await;
match value {
Ok(Some(serde_json::Value::Bool(t))) => KEEP_JOB_DIR.store(t, Ordering::Relaxed),
Err(e) => {
@@ -886,23 +918,23 @@ async fn delete_log_files_from_disk_and_store(
let _: Vec<_> = delete_futures.collect().await;
}
pub async fn reload_scim_token_setting(db: &DB) {
reload_option_setting_with_tracing(db, SCIM_TOKEN_SETTING, "SCIM_TOKEN", SCIM_TOKEN.clone())
pub async fn reload_scim_token_setting(conn: &Connection) {
reload_option_setting_with_tracing(conn, SCIM_TOKEN_SETTING, "SCIM_TOKEN", SCIM_TOKEN.clone())
.await;
}
pub async fn reload_timeout_wait_result_setting(db: &DB) {
pub async fn reload_timeout_wait_result_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
TIMEOUT_WAIT_RESULT_SETTING,
"TIMEOUT_WAIT_RESULT",
TIMEOUT_WAIT_RESULT.clone(),
)
.await;
}
pub async fn reload_saml_metadata_setting(db: &DB) {
pub async fn reload_saml_metadata_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
SAML_METADATA_SETTING,
"SAML_METADATA",
SAML_METADATA.clone(),
@@ -910,9 +942,9 @@ pub async fn reload_saml_metadata_setting(db: &DB) {
.await;
}
pub async fn reload_extra_pip_index_url_setting(db: &DB) {
pub async fn reload_extra_pip_index_url_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
EXTRA_PIP_INDEX_URL_SETTING,
"PIP_EXTRA_INDEX_URL",
PIP_EXTRA_INDEX_URL.clone(),
@@ -920,9 +952,9 @@ pub async fn reload_extra_pip_index_url_setting(db: &DB) {
.await;
}
pub async fn reload_pip_index_url_setting(db: &DB) {
pub async fn reload_pip_index_url_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
PIP_INDEX_URL_SETTING,
"PIP_INDEX_URL",
PIP_INDEX_URL.clone(),
@@ -930,9 +962,9 @@ pub async fn reload_pip_index_url_setting(db: &DB) {
.await;
}
pub async fn reload_instance_python_version_setting(db: &DB) {
pub async fn reload_instance_python_version_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
INSTANCE_PYTHON_VERSION_SETTING,
"INSTANCE_PYTHON_VERSION",
INSTANCE_PYTHON_VERSION.clone(),
@@ -940,9 +972,9 @@ pub async fn reload_instance_python_version_setting(db: &DB) {
.await;
}
pub async fn reload_npm_config_registry_setting(db: &DB) {
pub async fn reload_npm_config_registry_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
NPM_CONFIG_REGISTRY_SETTING,
"NPM_CONFIG_REGISTRY",
NPM_CONFIG_REGISTRY.clone(),
@@ -950,9 +982,9 @@ pub async fn reload_npm_config_registry_setting(db: &DB) {
.await;
}
pub async fn reload_bunfig_install_scopes_setting(db: &DB) {
pub async fn reload_bunfig_install_scopes_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
BUNFIG_INSTALL_SCOPES_SETTING,
"BUNFIG_INSTALL_SCOPES",
BUNFIG_INSTALL_SCOPES.clone(),
@@ -960,21 +992,21 @@ pub async fn reload_bunfig_install_scopes_setting(db: &DB) {
.await;
}
pub async fn reload_nuget_config_setting(db: &DB) {
pub async fn reload_nuget_config_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
NUGET_CONFIG_SETTING,
"NUGET_CONFIG",
NUGET_CONFIG.clone(),
)
.await;
}
pub async fn reload_maven_repos_setting(db: &DB) {
reload_option_setting_with_tracing(db, windmill_common::global_settings::MAVEN_REPOS_SETTING, "MAVEN_REPOS", MAVEN_REPOS.clone())
pub async fn reload_maven_repos_setting(conn: &Connection) {
reload_option_setting_with_tracing(conn, windmill_common::global_settings::MAVEN_REPOS_SETTING, "MAVEN_REPOS", MAVEN_REPOS.clone())
.await;
}
pub async fn reload_no_default_maven_setting(db: &DB) {
let value = load_value_from_global_settings(db, windmill_common::global_settings::NO_DEFAULT_MAVEN_SETTING).await;
pub async fn reload_no_default_maven_setting(conn: &Connection) {
let value = load_value_from_global_settings_with_conn(conn, windmill_common::global_settings::NO_DEFAULT_MAVEN_SETTING, true).await;
match value {
Ok(Some(serde_json::Value::Bool(t))) => NO_DEFAULT_MAVEN.store(t, Ordering::Relaxed),
Err(e) => {
@@ -984,9 +1016,9 @@ pub async fn reload_no_default_maven_setting(db: &DB) {
};
}
pub async fn reload_retention_period_setting(db: &DB) {
pub async fn reload_retention_period_setting(conn: &Connection) {
if let Err(e) = reload_setting(
db,
conn,
RETENTION_PERIOD_SECS_SETTING,
"JOB_RETENTION_SECS",
60 * 60 * 24 * 30,
@@ -998,9 +1030,9 @@ pub async fn reload_retention_period_setting(db: &DB) {
tracing::error!("Error reloading retention period: {:?}", e)
}
}
pub async fn reload_delete_logs_periodically_setting(db: &DB) {
pub async fn reload_delete_logs_periodically_setting(conn: &Connection) {
if let Err(e) = reload_setting(
db,
conn,
MONITOR_LOGS_ON_OBJECT_STORE_SETTING,
"MONITOR_LOGS_ON_OBJECT_STORE",
false,
@@ -1068,9 +1100,9 @@ pub async fn reload_s3_cache_setting(db: &DB) {
}
}
pub async fn reload_job_default_timeout_setting(db: &DB) {
pub async fn reload_job_default_timeout_setting(conn: &Connection) {
reload_option_setting_with_tracing(
db,
conn,
JOB_DEFAULT_TIMEOUT_SECS_SETTING,
"JOB_DEFAULT_TIMEOUT_SECS",
JOB_DEFAULT_TIMEOUT.clone(),
@@ -1078,9 +1110,9 @@ pub async fn reload_job_default_timeout_setting(db: &DB) {
.await;
}
pub async fn reload_request_size(db: &DB) {
pub async fn reload_request_size(conn: &Connection) {
if let Err(e) = reload_setting(
db,
conn,
REQUEST_SIZE_LIMIT_SETTING,
"REQUEST_SIZE_LIMIT",
DEFAULT_BODY_LIMIT,
@@ -1093,8 +1125,8 @@ pub async fn reload_request_size(db: &DB) {
}
}
pub async fn reload_license_key(db: &DB) -> anyhow::Result<()> {
let q = load_value_from_global_settings(db, LICENSE_KEY_SETTING)
pub async fn reload_license_key(conn: &Connection) -> anyhow::Result<()> {
let q = load_value_from_global_settings_with_conn(conn, LICENSE_KEY_SETTING, true)
.await
.map_err(|err| anyhow::anyhow!("Error reloading license key: {}", err.to_string()))?;
@@ -1119,12 +1151,12 @@ pub async fn reload_license_key(db: &DB) -> anyhow::Result<()> {
}
pub async fn reload_option_setting_with_tracing<T: FromStr + DeserializeOwned>(
db: &DB,
conn: &Connection,
setting_name: &str,
std_env_var: &str,
lock: Arc<RwLock<Option<T>>>,
) {
if let Err(e) = reload_option_setting(db, setting_name, std_env_var, lock.clone()).await {
if let Err(e) = reload_option_setting(conn, setting_name, std_env_var, lock.clone()).await {
tracing::error!("Error reloading setting {}: {:?}", setting_name, e)
}
}
@@ -1143,8 +1175,28 @@ pub async fn load_value_from_global_settings(
Ok(r)
}
pub async fn load_value_from_global_settings_with_conn(
conn: &Connection,
setting_name: &str,
load_from_http: bool,
) -> anyhow::Result<Option<serde_json::Value>> {
match conn {
Connection::Sql(db) => Ok(load_value_from_global_settings(db, setting_name).await?),
Connection::Http(client) => {
if load_from_http {
client.get::<Option<serde_json::Value>>(&format!("/api/agent_workers/get_global_setting/{}", setting_name)).await
.map_err(|e| anyhow::anyhow!("Error loading setting {}: {}", setting_name, e))
} else {
Ok(None)
}
}
}
}
pub async fn reload_option_setting<T: FromStr + DeserializeOwned>(
db: &DB,
conn: &Connection,
setting_name: &str,
std_env_var: &str,
lock: Arc<RwLock<Option<T>>>,
@@ -1159,7 +1211,7 @@ pub async fn reload_option_setting<T: FromStr + DeserializeOwned>(
return Ok(());
}
let q = load_value_from_global_settings(db, setting_name).await?;
let q = load_value_from_global_settings_with_conn(conn, setting_name, true).await?;
let mut value = std::env::var(std_env_var)
.ok()
@@ -1186,14 +1238,14 @@ pub async fn reload_option_setting<T: FromStr + DeserializeOwned>(
}
pub async fn reload_setting<T: FromStr + DeserializeOwned + Display>(
db: &DB,
conn: &Connection,
setting_name: &str,
std_env_var: &str,
default: T,
lock: Arc<RwLock<T>>,
transformer: fn(T) -> T,
) -> error::Result<()> {
let q = load_value_from_global_settings(db, setting_name).await?;
let q = load_value_from_global_settings_with_conn(conn, setting_name, true).await?;
let mut value = std::env::var(std_env_var)
.ok()
@@ -1251,27 +1303,32 @@ pub async fn monitor_pool(db: &DB) {
}
pub async fn monitor_db(
db: &Pool<Postgres>,
conn: &Connection,
base_internal_url: &str,
server_mode: bool,
_worker_mode: bool,
initial_load: bool,
_killpill_tx: KillpillSender,
) {
tracing::info!("Starting periodic monitor task");
let zombie_jobs_f = async {
if server_mode && !initial_load {
if server_mode && !initial_load && !*DISABLE_ZOMBIE_JOBS_MONITORING {
if let Some(db) = conn.as_sql() {
handle_zombie_jobs(db, base_internal_url, "server").await;
match handle_zombie_flows(db).await {
Err(err) => {
tracing::error!("Error handling zombie flows: {:?}", err);
}
},
_ => {}
}
}
}
};
let expired_items_f = async {
if server_mode && !initial_load {
if let Some(db) = conn.as_sql() {
delete_expired_items(&db).await;
}
}
};
@@ -1284,35 +1341,43 @@ pub async fn monitor_db(
let expose_queue_metrics_f = async {
if !initial_load && server_mode {
expose_queue_metrics(&db).await;
if let Some(db) = conn.as_sql() {
expose_queue_metrics(&db).await;
}
}
};
let worker_groups_alerts_f = async {
#[cfg(feature = "enterprise")]
if server_mode && !initial_load {
worker_groups_alerts(&db).await;
if let Some(db) = conn.as_sql() {
worker_groups_alerts(&db).await;
}
}
};
let jobs_waiting_alerts_f = async {
#[cfg(feature = "enterprise")]
if server_mode {
jobs_waiting_alerts(&db).await;
if let Some(db) = conn.as_sql() {
jobs_waiting_alerts(&db).await;
}
}
};
let apply_autoscaling_f = async {
#[cfg(feature = "enterprise")]
if server_mode && !initial_load {
if let Err(e) = windmill_autoscaling::apply_all_autoscaling(db).await {
tracing::error!("Error applying autoscaling: {:?}", e);
if let Some(db) = conn.as_sql() {
if let Err(e) = windmill_autoscaling::apply_all_autoscaling(db).await {
tracing::error!("Error applying autoscaling: {:?}", e);
}
}
}
};
let update_min_worker_version_f = async {
update_min_version(db).await;
update_min_version(conn).await;
};
join!(
@@ -1325,6 +1390,7 @@ 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>) {
@@ -1435,7 +1501,7 @@ pub async fn reload_worker_config(
tx: KillpillSender,
kill_if_change: bool,
) {
let config = load_worker_config(&db, tx.clone()).await;
let config = load_worker_config(db, tx.clone()).await;
if let Err(e) = config {
tracing::error!("Error reloading worker config: {:?}", e)
} else {
@@ -1469,15 +1535,15 @@ pub async fn reload_worker_config(
let mut wc = WORKER_CONFIG.write().await;
tracing::info!("Reloading worker config...");
make_suspended_pull_query(&config).await;
make_pull_query(&config).await;
store_suspended_pull_query(&config).await;
store_pull_query(&config).await;
*wc = config
}
}
}
pub async fn load_base_url(db: &DB) -> error::Result<String> {
let q_base_url = load_value_from_global_settings(db, BASE_URL_SETTING).await?;
pub async fn load_base_url(conn: &Connection) -> error::Result<String> {
let q_base_url = load_value_from_global_settings_with_conn(conn, BASE_URL_SETTING, false).await?;
let std_base_url = std::env::var("BASE_URL")
.ok()
@@ -1507,34 +1573,39 @@ pub async fn load_base_url(db: &DB) -> error::Result<String> {
Ok(base_url)
}
pub async fn reload_base_url_setting(db: &DB) -> error::Result<()> {
#[cfg(feature = "oauth2")]
let q_oauth = load_value_from_global_settings(db, OAUTH_SETTING).await?;
pub async fn reload_base_url_setting(conn: &Connection) -> error::Result<()> {
#[cfg(feature = "oauth2")]
let oauths = if let Some(q) = q_oauth {
if let Ok(v) = serde_json::from_value::<
Option<HashMap<String, windmill_api::oauth2_ee::OAuthClient>>,
>(q.clone())
{
v
let oauths = if let Some(db) = conn.as_sql() {
let q_oauth = load_value_from_global_settings (db, OAUTH_SETTING).await?;
if let Some(q) = q_oauth {
if let Ok(v) = serde_json::from_value::<
Option<HashMap<String, windmill_api::oauth2_ee::OAuthClient>>,
>(q.clone())
{
v
} else {
tracing::error!("Could not parse oauth setting as a json, found: {:#?}", &q);
None
}
} else {
tracing::error!("Could not parse oauth setting as a json, found: {:#?}", &q);
None
}
} else {
None
};
let base_url = load_base_url(db).await?;
let base_url = load_base_url(conn).await?;
let is_secure = base_url.starts_with("https://");
#[cfg(feature = "oauth2")]
{
let mut l = windmill_api::OAUTH_CLIENTS.write().await;
*l = windmill_api::oauth2_ee::build_oauth_clients(&base_url, oauths, db).await
.map_err(|e| tracing::error!("Error building oauth clients (is the oauth.json mounted and in correct format? Use '{}' as minimal oauth.json): {}", "{}", e))
.unwrap();
if let Some(db) = conn.as_sql() {
let mut l = windmill_api::OAUTH_CLIENTS.write().await;
*l = windmill_api::oauth2_ee::build_oauth_clients(&base_url, oauths, db).await
.map_err(|e| tracing::error!("Error building oauth clients (is the oauth.json mounted and in correct format? Use '{}' as minimal oauth.json): {}", "{}", e))
.unwrap();
}
}
{
@@ -1792,7 +1863,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
mpsc::channel::<SameWorkerPayload>(1);
let same_worker_tx_never_used =
SameWorkerSender(same_worker_tx_never_used, Arc::new(AtomicU16::new(0)));
let (send_result_never_used, _send_result_rx_never_used) = mpsc::channel::<SendResult>(1);
let (send_result_never_used, _send_result_rx_never_used) = JobCompletedSender::new_never_used();
let label = if job.permissioned_as != format!("u/{}", job.created_by)
&& job.permissioned_as != job.created_by
@@ -2002,8 +2073,8 @@ async fn cancel_zombie_flow_job(
Ok(())
}
pub async fn reload_hub_base_url_setting(db: &DB, server_mode: bool) -> error::Result<()> {
let hub_base_url = load_value_from_global_settings(db, HUB_BASE_URL_SETTING).await?;
pub async fn reload_hub_base_url_setting(conn: &Connection, server_mode: bool) -> error::Result<()> {
let hub_base_url = load_value_from_global_settings_with_conn(conn, HUB_BASE_URL_SETTING, true).await?;
let base_url = if let Some(q) = hub_base_url {
if let Ok(v) = serde_json::from_value::<String>(q.clone()) {
@@ -2026,16 +2097,18 @@ pub async fn reload_hub_base_url_setting(db: &DB, server_mode: bool) -> error::R
let mut l = HUB_BASE_URL.write().await;
if server_mode {
#[cfg(feature = "embedding")]
if *l != base_url {
let disable_embedding = std::env::var("DISABLE_EMBEDDING")
.ok()
.map(|x| x.parse::<bool>().unwrap_or(false))
.unwrap_or(false);
if !disable_embedding {
let db_clone = db.clone();
tokio::spawn(async move {
update_embeddings_db(&db_clone).await;
});
if let Some(db) = conn.as_sql() {
if *l != base_url {
let disable_embedding = std::env::var("DISABLE_EMBEDDING")
.ok()
.map(|x| x.parse::<bool>().unwrap_or(false))
.unwrap_or(false);
if !disable_embedding {
let db_clone = db.clone();
tokio::spawn(async move {
update_embeddings_db(&db_clone).await;
});
}
}
}
}
@@ -2044,9 +2117,9 @@ pub async fn reload_hub_base_url_setting(db: &DB, server_mode: bool) -> error::R
Ok(())
}
pub async fn reload_critical_error_channels_setting(db: &DB) -> error::Result<()> {
pub async fn reload_critical_error_channels_setting(conn: &DB) -> error::Result<()> {
let critical_error_channels =
load_value_from_global_settings(db, CRITICAL_ERROR_CHANNELS_SETTING).await?;
load_value_from_global_settings(conn, CRITICAL_ERROR_CHANNELS_SETTING).await?;
let critical_error_channels = if let Some(q) = critical_error_channels {
if let Ok(v) = serde_json::from_value::<Vec<CriticalErrorChannel>>(q.clone()) {

View File

@@ -137,7 +137,6 @@ impl ApiServer {
rx,
port_tx,
false,
#[cfg(feature = "smtp")]
format!("http://localhost:{}", addr.port()),
));
@@ -315,7 +314,7 @@ mod suspend_resume {
let second = completed.next().await.unwrap();
// print_job(second, &db).await;
let token = windmill_worker::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil(), None).await.unwrap();
let token = windmill_common::auth::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil(), None).await.unwrap();
let secret = reqwest::get(format!(
"http://localhost:{port}/api/w/test-workspace/jobs/job_signature/{second}/0?token={token}&approver=ruben"
))
@@ -418,7 +417,7 @@ mod suspend_resume {
/* ... and send a request resume it. */
let second = completed.next().await.unwrap();
let token = windmill_worker::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil(), None).await.unwrap();
let token = windmill_common::auth::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil(), None).await.unwrap();
let secret = reqwest::get(format!(
"http://localhost:{port}/api/w/test-workspace/jobs/job_signature/{second}/0?token={token}"
))
@@ -1033,11 +1032,11 @@ fn spawn_test_worker(
priority: 0,
tags: (*wc).worker_tags.clone(),
}];
windmill_common::worker::make_suspended_pull_query(&wc).await;
windmill_common::worker::make_pull_query(&wc).await;
windmill_common::worker::store_suspended_pull_query(&wc).await;
windmill_common::worker::store_pull_query(&wc).await;
}
windmill_worker::run_worker(
&db,
&db.into(),
worker_instance,
worker_name,
1,
@@ -1046,7 +1045,6 @@ fn spawn_test_worker(
rx,
tx2,
&base_internal_url,
false,
)
.await
};
@@ -3843,7 +3841,7 @@ async fn test_result_format(db: Pool<Postgres>) {
let port = server.addr.port();
let token = windmill_worker::create_token_for_owner(
let token = windmill_common::auth::create_token_for_owner(
&db,
"test-workspace",
"u/test-user",
@@ -3884,7 +3882,7 @@ async fn test_result_format(db: Pool<Postgres>) {
assert_eq!(job_result.get(), correct_result);
let response = windmill_api::jobs::run_wait_result(
&db,
&db.into(),
Uuid::parse_str(ordered_result_job_id).unwrap(),
"test-workspace".to_string(),
None,

View File

@@ -10,13 +10,14 @@ path = "src/lib.rs"
[features]
default = []
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise"]
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker/enterprise"]
stripe = []
agent_worker_server = []
enterprise_saml = ["dep:samael"]
benchmark = []
embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn", "dep:half"]
parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet"]
prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus"]
parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet", "windmill-worker/parquet"]
prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus", "windmill-worker/prometheus"]
openidconnect = ["dep:openidconnect"]
tantivy = ["dep:windmill-indexer"]
kafka = ["dep:rdkafka"]
@@ -31,6 +32,7 @@ static_frontend = ["dep:rust-embed"]
postgres_trigger = ["dep:rust-postgres", "dep:pg_escape", "dep:byteorder", "dep:thiserror", "dep:rust_decimal", "dep:rust-postgres-native-tls"]
mqtt_trigger = ["dep:thiserror", "dep:rumqttc"]
sqs_trigger = ["dep:aws-sdk-sqs", "dep:thiserror", "dep:aws-config"]
gcp_trigger = ["dep:thiserror", "dep:google-cloud-pubsub", "dep:google-cloud-googleapis", "dep:tonic"]
cloud = ["windmill-common/cloud"]
[dependencies]
@@ -40,8 +42,10 @@ windmill-audit.workspace = true
windmill-parser.workspace = true
windmill-parser-ts.workspace = true
windmill-parser-py.workspace = true
windmill-parser-py-imports.workspace = true
windmill-git-sync.workspace = true
windmill-indexer = { workspace = true, optional = true }
windmill-worker.workspace = true
tokio.workspace = true
anyhow.workspace = true
argon2.workspace = true
@@ -126,4 +130,7 @@ rust_decimal = { workspace = true, optional = true }
rust-postgres-native-tls = { workspace = true, optional = true}
rumqttc = { workspace = true, optional = true }
aws-sdk-sqs = { workspace = true, optional = true }
aws-config = { workspace = true, optional = true}
aws-config = { workspace = true, optional = true }
google-cloud-pubsub = { workspace = true, optional = true }
google-cloud-googleapis = { workspace = true , optional = true }
tonic = { workspace = true, optional = true }

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.478.1
version: 1.481.0
title: Windmill API
contact:
@@ -2702,6 +2702,8 @@ paths:
type: boolean
mqtt_used:
type: boolean
gcp_used:
type: boolean
sqs_used:
type: boolean
required:
@@ -2711,6 +2713,7 @@ paths:
- nats_used
- postgres_used
- mqtt_used
- gcp_used
- sqs_used
/w/{workspace}/users/list:
get:
@@ -6304,6 +6307,38 @@ paths:
schema:
type: boolean
/w/{workspace}/apps/sign_s3_objects:
post:
summary: sign s3 objects, to be used by anonymous users in public apps
operationId: signS3Objects
tags:
- app
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: s3 objects to sign
required: true
content:
application/json:
schema:
type: object
properties:
s3_objects:
type: array
items:
$ref: "#/components/schemas/S3Object"
required:
- s3_objects
responses:
"200":
description: signed s3 objects
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/S3Object"
/w/{workspace}/apps_u/execute_component/{path}:
post:
summary: executeComponent
@@ -6786,6 +6821,7 @@ paths:
- $ref: "#/components/parameters/Running"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/ResultFilter"
- $ref: "#/components/parameters/AllowWildcards"
- $ref: "#/components/parameters/Tag"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
@@ -6914,6 +6950,7 @@ paths:
- $ref: "#/components/parameters/Running"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/ResultFilter"
- $ref: "#/components/parameters/AllowWildcards"
- $ref: "#/components/parameters/Tag"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
@@ -6992,6 +7029,7 @@ paths:
- $ref: "#/components/parameters/JobKinds"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/ResultFilter"
- $ref: "#/components/parameters/AllowWildcards"
- $ref: "#/components/parameters/Tag"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
@@ -7055,6 +7093,7 @@ paths:
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/Tag"
- $ref: "#/components/parameters/ResultFilter"
- $ref: "#/components/parameters/AllowWildcards"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
- name: is_skipped
@@ -9218,6 +9257,264 @@ paths:
schema:
type: string
/w/{workspace}/gcp_triggers/create:
post:
summary: create gcp trigger
operationId: createGcpTrigger
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: new gcp trigger
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewGcpTrigger"
responses:
"201":
description: gcp trigger created
content:
text/plain:
schema:
type: string
/w/{workspace}/gcp_triggers/update/{path}:
post:
summary: update gcp trigger
operationId: updateGcpTrigger
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
requestBody:
description: updated trigger
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/EditGcpTrigger"
responses:
"200":
description: gcp trigger updated
content:
text/plain:
schema:
type: string
/w/{workspace}/gcp_triggers/delete/{path}:
delete:
summary: delete gcp trigger
operationId: deleteGcpTrigger
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: gcp trigger deleted
content:
text/plain:
schema:
type: string
/w/{workspace}/gcp_triggers/get/{path}:
get:
summary: get gcp trigger
operationId: getGcpTrigger
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: gcp trigger deleted
content:
application/json:
schema:
$ref: "#/components/schemas/GcpTrigger"
/w/{workspace}/gcp_triggers/list:
get:
summary: list gcp triggers
operationId: listGcpTriggers
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
required: true
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
- name: path
description: filter by path
in: query
schema:
type: string
- name: is_flow
in: query
schema:
type: boolean
- name: path_start
in: query
schema:
type: string
responses:
"200":
description: gcp trigger list
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/GcpTrigger"
/w/{workspace}/gcp_triggers/exists/{path}:
get:
summary: does gcp trigger exists
operationId: existsGcpTrigger
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: gcp trigger exists
content:
application/json:
schema:
type: boolean
/w/{workspace}/gcp_triggers/setenabled/{path}:
post:
summary: set enabled gcp trigger
operationId: setGcpTriggerEnabled
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
requestBody:
description: updated gcp trigger enable
required: true
content:
application/json:
schema:
type: object
properties:
enabled:
type: boolean
required:
- enabled
responses:
"200":
description: gcp trigger enabled set
content:
text/plain:
schema:
type: string
/w/{workspace}/gcp_triggers/test:
post:
summary: test gcp connection
operationId: testGcpConnection
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: test gcp connection
required: true
content:
application/json:
schema:
type: object
properties:
connection:
type: object
required:
- connection
responses:
"200":
description: try to connect to a gcp broker
content:
text/plain:
schema:
type: string
/w/{workspace}/gcp_triggers/subscriptions/delete/{path}:
delete:
summary: delete gcp trigger
operationId: deleteGcpSubscription
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
requestBody:
description: args to delete subscription from google cloud
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/DeleteGcpSubscription"
responses:
"200":
description: gcp trigger deleted
content:
text/plain:
schema:
type: string
/w/{workspace}/gcp_triggers/topics/list/{path}:
get:
summary: list all topics of google cloud service
operationId: listGoogleTopics
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: get all google topics
content:
application/json:
schema:
type: array
items:
type: string
/w/{workspace}/gcp_triggers/subscriptions/list/{path}:
post:
summary: list all subscription of a give topic from google cloud service
operationId: listAllTGoogleTopicSubscriptions
tags:
- gcp_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
requestBody:
description: args to get subscription's topic from google cloud
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/GetAllTopicSubscription"
responses:
"200":
description: get all google topic subscriptions name
content:
application/json:
schema:
type: array
items:
type: string
/w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}:
get:
@@ -10494,6 +10791,41 @@ paths:
items:
$ref: "#/components/schemas/AutoscalingEvent"
/agent_workers/create_agent_token:
post:
summary: create agent token
operationId: createAgentToken
tags:
- agent_workers
requestBody:
description: agent token
required: true
content:
application/json:
schema:
type: object
properties:
worker_group:
type: string
tags:
type: array
items:
type: string
exp:
type: integer
required:
- worker_group
- tags
- exp
responses:
"200":
description: agent token created
content:
application/json:
schema:
type: string
/w/{workspace}/acls/get/{kind}/{path}:
get:
summary: get granular acls
@@ -10524,6 +10856,7 @@ paths:
nats_trigger,
postgres_trigger,
mqtt_trigger,
gcp_trigger,
sqs_trigger
]
responses:
@@ -10567,6 +10900,7 @@ paths:
nats_trigger,
postgres_trigger,
mqtt_trigger,
gcp_trigger,
sqs_trigger
]
requestBody:
@@ -10621,6 +10955,7 @@ paths:
nats_trigger,
postgres_trigger,
mqtt_trigger,
gcp_trigger,
sqs_trigger
]
requestBody:
@@ -10673,6 +11008,10 @@ paths:
responses:
"200":
description: capture config set
content:
application/json:
schema:
type: object
/w/{workspace}/capture/ping_config/{trigger_kind}/{runnable_kind}/{path}:
post:
@@ -11888,6 +12227,7 @@ paths:
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/Tag"
- $ref: "#/components/parameters/ResultFilter"
- $ref: "#/components/parameters/AllowWildcards"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
- name: is_skipped
@@ -12393,6 +12733,12 @@ components:
in: query
schema:
type: boolean
AllowWildcards:
name: allow_wildcards
description: allow wildcards (*) in the filter of label, tag, worker
in: query
schema:
type: boolean
ArgsFilter:
name: args
description: filter on jobs containing those args as a json subset (@> in postgres)
@@ -12497,7 +12843,7 @@ components:
AIProvider:
type: string
enum: [openai, anthropic, mistral, deepseek, googleai, groq, openrouter, togetherai, customai]
enum: [openai, azure_openai, anthropic, mistral, deepseek, googleai, groq, openrouter, togetherai, customai]
AIProviderModel:
type: object
@@ -13531,7 +13877,7 @@ components:
csharp,
nu,
java
# KJQXZ
# for related places search: ADD_NEW_LANG
]
Preview:
@@ -14144,6 +14490,8 @@ components:
type: number
mqtt_count:
type: number
gcp_count:
type: number
sqs_count:
type: number
@@ -14428,6 +14776,173 @@ components:
- subscribe_topics
- mqtt_resource_path
DeliveryType:
type: string
enum:
- push
- pull
PushConfig:
type: object
properties:
audience:
type: string
authenticate:
type: boolean
base_endpoint:
type: string
required:
- authenticate
- base_endpoint
GcpTrigger:
allOf:
- $ref: "#/components/schemas/TriggerExtraProperty"
type: object
properties:
gcp_resource_path:
type: string
topic_id:
type: string
subscription_id:
type: string
server_id:
type: string
delivery_type:
$ref: "#/components/schemas/DeliveryType"
delivery_config:
$ref: "#/components/schemas/PushConfig"
last_server_ping:
type: string
format: date-time
error:
type: string
enabled:
type: boolean
required:
- gcp_resource_path
- topic_id
- subscription_id
- enabled
- delivery_type
SubscriptionMode:
type: string
enum:
- existing
- create_update
description: "The mode of subscription. 'existing' means using an existing GCP subscription, while 'create_update' involves creating or updating a new subscription."
GcpExistingSubscription:
type: object
properties:
subscription_id:
type: string
base_endpoint:
type: string
required:
- subscription_id
- base_endpoint
GcpCreateUpdateSubscription:
type: object
properties:
subscription_id:
type: string
delivery_type:
$ref: "#/components/schemas/DeliveryType"
delivery_config:
$ref: "#/components/schemas/PushConfig"
required:
- delivery_type
GcpSubscriptionModeConfig:
type: object
properties:
subscription_mode:
$ref: "#/components/schemas/SubscriptionMode"
required:
- subscription_mode
allOf:
- oneOf:
- $ref: "#/components/schemas/GcpExistingSubscription"
- $ref: "#/components/schemas/GcpCreateUpdateSubscription"
description: |
"This is a union type representing the subscription mode.
- 'existing': Represents an existing GCP subscription, and should be accompanied by an 'ExistingGcpSubscription' object.
- 'create_update': Represents a new or updated GCP subscription, and should be accompanied by a 'CreateUpdateConfig' object."
NewGcpTrigger:
type: object
properties:
gcp_resource_path:
type: string
topic_id:
type: string
subscription_mode:
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
path:
type: string
script_path:
type: string
is_flow:
type: boolean
enabled:
type: boolean
required:
- path
- script_path
- is_flow
- gcp_resource_path
- topic_id
- subscription_mode
EditGcpTrigger:
type: object
properties:
gcp_resource_path:
type: string
topic_id:
type: string
subscription_mode:
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
path:
type: string
script_path:
type: string
is_flow:
type: boolean
enabled:
type: boolean
required:
- path
- script_path
- is_flow
- enabled
- mqtt_resource_path
- subscription_id
- delivery_type
- topic_id
- subscription_mode
GetAllTopicSubscription:
type: object
properties:
topic_id:
type: string
required:
- topic_id
DeleteGcpSubscription:
type: object
properties:
subscription_id:
type: string
required:
- subscription_id
SqsTrigger:
allOf:
- $ref: "#/components/schemas/TriggerExtraProperty"
@@ -15813,7 +16328,7 @@ components:
CaptureTriggerKind:
type: string
enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt]
enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt, gcp]
Capture:
type: object
@@ -15972,3 +16487,17 @@ components:
required:
- account_id
- installation_id
S3Object:
type: object
properties:
s3:
type: string
filename:
type: string
storage:
type: string
presigned:
type: string
required:
- s3

View File

@@ -0,0 +1,52 @@
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2042
* This file and its contents are licensed under the AGPLv3 License.
* Please see the included NOTICE for copyright information and
* LICENSE-AGPL for a copy of the license.
*/
use crate::db::DB;
use axum::Router;
use serde::{Deserialize, Serialize};
pub fn global_service() -> Router {
Router::new()
}
pub fn workspaced_service(
db: DB,
_base_internal_url: String,
) -> (
Router,
Option<tokio::task::JoinHandle<()>>,
windmill_worker::JobCompletedSender,
) {
use windmill_common::worker::Connection;
use windmill_worker::JobCompletedSender;
let (job_completed_tx, _job_completed_rx) =
JobCompletedSender::new(&Connection::Sql(db.clone()), 100);
let router = Router::new();
(router, None, job_completed_tx)
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AgentAuth {
pub worker_group: String,
pub suffix: Option<String>,
pub tags: Vec<String>,
pub exp: Option<usize>,
}
pub struct AgentCache {}
impl AgentCache {
pub fn new() -> Self {
AgentCache {}
}
}

View File

@@ -3,16 +3,12 @@ use crate::{
variables::get_variable_or_self,
};
use anyhow::Context;
use axum::{body::Bytes, extract::Path, response::IntoResponse, routing::post, Extension, Router};
use http::HeaderMap;
use lazy_static::lazy_static;
use openai::OpenaiCache;
use openai_api_compatible::OpenaiApiCompatibleCache;
use quick_cache::sync::Cache;
use reqwest::{Client, RequestBuilder};
use serde::{Deserialize, Serialize};
use serde_json::value::{RawValue, Value};
use serde_json::value::RawValue;
use std::collections::HashMap;
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_common::error::{to_anyhow, Error, Result};
@@ -22,175 +18,94 @@ lazy_static::lazy_static! {
.timeout(std::time::Duration::from_secs(60 * 5))
.user_agent("windmill/beta")
.build().unwrap();
static ref OPENAI_AZURE_BASE_PATH: Option<String> = std::env::var("OPENAI_AZURE_BASE_PATH").ok();
pub static ref AI_REQUEST_CACHE: Cache<(String, AIProvider), ExpiringAIRequestConfig> = Cache::new(500);
}
mod openai_api_compatible {
use super::*;
const AZURE_API_VERSION: &str = "2024-10-21";
const OPENAI_BASE_URL: &str = "https://api.openai.com/v1";
#[derive(Deserialize, Clone, Debug)]
pub struct OpenaiApiCompatibleCache {
pub base_url: String,
pub api_key: Option<String>,
}
#[derive(Deserialize, Debug)]
struct AIOAuthResource {
client_id: String,
client_secret: String,
token_url: String,
user: Option<String>,
}
impl OpenaiApiCompatibleCache {
pub fn prepare_request(self, path: &str, body: Bytes) -> Result<RequestBuilder> {
let url = format!("{}/{}", self.base_url, path);
#[derive(Deserialize, Debug)]
struct AIStandardResource {
#[serde(alias = "baseUrl")]
base_url: Option<String>,
#[serde(alias = "apiKey")]
api_key: Option<String>,
organization_id: Option<String>,
}
let mut request = HTTP_CLIENT
.post(url)
.header("content-type", "application/json")
.body(body);
#[derive(Deserialize, Debug)]
struct OAuthTokens {
access_token: String,
}
if let Some(api_key) = self.api_key {
request = request.header("Authorization", format!("Bearer {}", api_key));
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum AIResource {
OAuth(AIOAuthResource),
Standard(AIStandardResource),
}
Ok(request)
}
}
#[derive(Deserialize, Clone, Debug)]
struct AIRequestConfig {
pub base_url: String,
pub api_key: Option<String>,
pub access_token: Option<String>,
pub organization_id: Option<String>,
pub user: Option<String>,
}
pub async fn get_cached_value(
impl AIRequestConfig {
pub async fn new(
provider: &AIProvider,
db: &DB,
w_id: &str,
resource: Value,
base_url: Option<String>,
) -> Result<KeyCache> {
let mut resource: OpenaiApiCompatibleCache = if let Some(base_url) = base_url {
let api_key = match resource {
Value::Object(mut obj) => obj
.remove("api_key")
.map(|v| serde_json::from_value::<String>(v.clone()).ok())
.flatten()
.or_else(|| {
obj.remove("apiKey")
.map(|v| serde_json::from_value::<String>(v.clone()).ok())
.flatten()
}),
_ => None,
};
OpenaiApiCompatibleCache { base_url, api_key }
} else {
serde_json::from_value(resource).with_context(|| "validating custom AI resource")?
resource: AIResource,
) -> Result<Self> {
let (api_key, access_token, organization_id, base_url, user) = match resource {
AIResource::Standard(resource) => {
let base_url = provider.get_base_url(resource.base_url, db).await?;
let api_key = if let Some(api_key) = resource.api_key {
Some(get_variable_or_self(api_key, db, w_id).await?)
} else {
None
};
let organization_id = if let Some(organization_id) = resource.organization_id {
Some(get_variable_or_self(organization_id, db, w_id).await?)
} else {
None
};
(api_key, None, organization_id, base_url, None)
}
AIResource::OAuth(resource) => {
let user = if let Some(user) = resource.user.clone() {
Some(get_variable_or_self(user, db, w_id).await?)
} else {
None
};
let token = Self::get_token_using_oauth(resource, db, w_id).await?;
let base_url = provider.get_base_url(None, db).await?;
(None, Some(token), None, base_url, user)
}
};
if let Some(api_key) = resource.api_key {
resource.api_key = Some(get_variable_or_self(api_key, db, w_id).await?);
}
Ok(KeyCache::OpenaiApiCompatible(resource))
}
}
mod openai {
use super::*;
const API_VERSION: &str = "2024-10-21";
#[derive(Deserialize, Debug)]
struct OpenaiResource {
api_key: String,
organization_id: Option<String>,
Ok(Self { base_url, organization_id, api_key, access_token, user })
}
#[derive(Deserialize, Debug)]
struct OpenaiClientCredentialsOauthResource {
client_id: String,
client_secret: String,
token_url: String,
user: Option<String>,
}
#[derive(Deserialize, Debug)]
#[serde(untagged, rename_all = "snake_case")]
enum OpenaiConfig {
Resource(OpenaiResource),
ClientCredentialsOauthResource(OpenaiClientCredentialsOauthResource),
}
lazy_static::lazy_static! {
pub static ref OPENAI_AZURE_BASE_PATH: Option<String> = std::env::var("OPENAI_AZURE_BASE_PATH").ok();
}
#[derive(Deserialize, Debug)]
struct OpenaiCredentials {
access_token: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct OpenaiCache {
api_key: String,
organization_id: Option<String>,
azure_base_path: Option<String>,
user: Option<String>,
}
impl OpenaiCache {
pub fn new(
api_key: String,
organization_id: Option<String>,
azure_base_path: Option<String>,
user: Option<String>,
) -> Self {
Self { api_key, organization_id, azure_base_path, user }
}
}
pub const BASE_URL: &str = "https://api.openai.com/v1";
impl OpenaiCache {
pub fn prepare_request(self, openai_path: &str, mut body: Bytes) -> Result<RequestBuilder> {
let OpenaiCache { api_key, azure_base_path, organization_id, user } = self;
if user.is_some() {
tracing::debug!("Adding user to request body");
let mut json_body: HashMap<String, Box<RawValue>> = serde_json::from_slice(&body)
.map_err(|e| {
Error::internal_err(format!("Failed to parse request body: {}", e))
})?;
let user_json_string = serde_json::Value::String(user.unwrap()).to_string(); // makes sure to escape characters
json_body.insert(
"user".to_string(),
RawValue::from_string(user_json_string)
.map_err(|e| Error::internal_err(format!("Failed to parse user: {}", e)))?,
);
body = serde_json::to_vec(&json_body)
.map_err(|e| {
Error::internal_err(format!("Failed to reserialize request body: {}", e))
})?
.into();
}
let base_url = if let Some(base_url) = azure_base_path {
base_url
} else {
BASE_URL.to_string()
};
let url = format!("{}/{}", base_url, openai_path);
let mut request = HTTP_CLIENT
.post(url)
.header("content-type", "application/json")
.body(body);
if base_url != BASE_URL {
request = request
.header("api-key", api_key)
.query(&[("api-version", API_VERSION)])
} else {
request = request.header("authorization", format!("Bearer {}", api_key))
}
if let Some(org_id) = organization_id {
request = request.header("OpenAI-Organization", org_id);
}
Ok(request)
}
}
async fn get_openai_key_using_credentials_flow(
mut resource: OpenaiClientCredentialsOauthResource,
async fn get_token_using_oauth(
mut resource: AIOAuthResource,
db: &DB,
w_id: &str,
) -> Result<String> {
@@ -199,115 +114,114 @@ mod openai {
resource.token_url = get_variable_or_self(resource.token_url, db, w_id).await?;
let mut params = HashMap::new();
params.insert("grant_type", "client_credentials");
params.insert("scope", "https://cognitiveservices.azure.com/.default");
let response = HTTP_CLIENT
.post(resource.token_url)
.form(&params)
.basic_auth(resource.client_id, Some(resource.client_secret))
.send()
.await
.and_then(|r| r.error_for_status())
.map_err(|err| {
Error::internal_err(format!(
"Failed to get OpenAI credentials using credentials flow: {}",
"Failed to get access token using credentials flow: {}",
err
))
})?;
let response = response.json::<OpenaiCredentials>().await.map_err(|err| {
let response = response.json::<OAuthTokens>().await.map_err(|err| {
Error::internal_err(format!(
"Failed to parse OpenAI credentials from credentials flow: {}",
"Failed to parse access token from credentials flow: {}",
err
))
})?;
Ok(response.access_token)
}
pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result<KeyCache> {
let config = serde_json::from_value(resource)
.map_err(|e| Error::internal_err(format!("validating openai resource {e:#}")))?;
pub fn prepare_request(
self,
provider: &AIProvider,
path: &str,
body: Bytes,
) -> Result<RequestBuilder> {
let url = format!("{}/{}", self.base_url, path);
let mut user = None::<String>;
let mut resource = match config {
OpenaiConfig::Resource(resource) => {
tracing::debug!("Getting OpenAI key from static resource");
resource
}
OpenaiConfig::ClientCredentialsOauthResource(resource) => {
tracing::debug!("Getting OpenAI key with client credentials flow");
user = resource.user.clone();
let token = get_openai_key_using_credentials_flow(resource, db, w_id).await?;
OpenaiResource { api_key: token, organization_id: None }
}
};
resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?;
if let Some(organization_id) = resource.organization_id {
resource.organization_id = Some(get_variable_or_self(organization_id, db, w_id).await?);
}
if user.is_some() {
user = Some(get_variable_or_self(user.unwrap(), db, w_id).await?);
}
let azure_base_path = sqlx::query_scalar!(
"SELECT value
FROM global_settings
WHERE name = 'openai_azure_base_path'",
)
.fetch_optional(db)
.await?;
let azure_base_path = if let Some(azure_base_path) = azure_base_path {
Some(
serde_json::from_value::<String>(azure_base_path).map_err(|e| {
Error::internal_err(format!("validating openai azure base path {e:#}"))
})?,
)
let body = if let Some(user) = self.user {
Self::add_user_to_body(body, user)?
} else {
OPENAI_AZURE_BASE_PATH.clone()
body
};
let workspace_cache = OpenaiCache::new(
resource.api_key.clone(),
resource.organization_id.clone(),
azure_base_path.clone(),
user.clone(),
let is_azure = matches!(provider, AIProvider::OpenAI) && self.base_url != OPENAI_BASE_URL
|| matches!(provider, AIProvider::AzureOpenAI);
let mut request = HTTP_CLIENT
.post(url)
.header("content-type", "application/json")
.body(body);
if is_azure {
request = request.query(&[("api-version", AZURE_API_VERSION)])
}
if let Some(api_key) = self.api_key {
if is_azure {
request = request.header("api-key", api_key)
} else {
request = request.header("authorization", format!("Bearer {}", api_key))
}
}
if let Some(access_token) = self.access_token {
request = request.header("authorization", format!("Bearer {}", access_token))
}
if let Some(org_id) = self.organization_id {
request = request.header("OpenAI-Organization", org_id);
}
Ok(request)
}
fn add_user_to_body(body: Bytes, user: String) -> Result<Bytes> {
tracing::debug!("Adding user to request body");
let mut json_body: HashMap<String, Box<RawValue>> = serde_json::from_slice(&body)
.map_err(|e| Error::internal_err(format!("Failed to parse request body: {}", e)))?;
let user_json_string = serde_json::Value::String(user).to_string(); // makes sure to escape characters
json_body.insert(
"user".to_string(),
RawValue::from_string(user_json_string)
.map_err(|e| Error::internal_err(format!("Failed to parse user: {}", e)))?,
);
Ok(KeyCache::Openai(workspace_cache))
Ok(serde_json::to_vec(&json_body)
.map_err(|e| Error::internal_err(format!("Failed to reserialize request body: {}", e)))?
.into())
}
}
#[derive(Clone, Debug)]
pub enum KeyCache {
Openai(OpenaiCache),
OpenaiApiCompatible(OpenaiApiCompatibleCache),
pub struct ExpiringAIRequestConfig {
config: AIRequestConfig,
expires_at: std::time::Instant,
}
#[derive(Clone, Debug)]
pub struct AICache {
pub cached_key: KeyCache,
pub expires_at: std::time::Instant,
}
impl AICache {
pub fn new(cached_key: KeyCache) -> Self {
Self {
cached_key,
expires_at: std::time::Instant::now() + std::time::Duration::from_secs(60),
}
impl ExpiringAIRequestConfig {
fn new(config: AIRequestConfig) -> Self {
Self { config, expires_at: std::time::Instant::now() + std::time::Duration::from_secs(60) }
}
fn is_expired(&self) -> bool {
self.expires_at < std::time::Instant::now()
}
}
lazy_static! {
pub static ref AI_KEY_CACHE: Cache<(String, AIProvider), AICache> = Cache::new(500);
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone)]
#[serde(rename_all = "lowercase")]
pub enum AIProvider {
OpenAI,
#[serde(rename = "azure_openai")]
AzureOpenAI,
Anthropic,
Mistral,
DeepSeek,
@@ -319,19 +233,48 @@ pub enum AIProvider {
}
impl AIProvider {
pub fn get_base_url(&self) -> Result<Option<String>> {
pub async fn get_base_url(&self, resource_base_url: Option<String>, db: &DB) -> Result<String> {
match self {
AIProvider::DeepSeek => Ok(Some("https://api.deepseek.com/v1".to_string())),
AIProvider::GoogleAI => Ok(Some(
"https://generativelanguage.googleapis.com/v1beta/openai".to_string(),
)),
AIProvider::Groq => Ok(Some("https://api.groq.com/openai/v1".to_string())),
AIProvider::OpenRouter => Ok(Some("https://openrouter.ai/api/v1".to_string())),
AIProvider::TogetherAI => Ok(Some("https://api.together.xyz/v1".to_string())),
AIProvider::Anthropic => Ok(Some("https://api.anthropic.com/v1".to_string())),
AIProvider::Mistral => Ok(Some("https://api.mistral.ai/v1".to_string())),
AIProvider::CustomAI => Ok(None),
AIProvider::OpenAI => Ok(Some(openai::BASE_URL.to_string())),
AIProvider::OpenAI => {
let azure_base_path = sqlx::query_scalar!(
"SELECT value
FROM global_settings
WHERE name = 'openai_azure_base_path'",
)
.fetch_optional(db)
.await?;
let azure_base_path = if let Some(azure_base_path) = azure_base_path {
Some(
serde_json::from_value::<String>(azure_base_path).map_err(|e| {
Error::internal_err(format!("validating openai azure base path {e:#}"))
})?,
)
} else {
OPENAI_AZURE_BASE_PATH.clone()
};
Ok(azure_base_path.unwrap_or(OPENAI_BASE_URL.to_string()))
}
AIProvider::DeepSeek => Ok("https://api.deepseek.com/v1".to_string()),
AIProvider::GoogleAI => {
Ok("https://generativelanguage.googleapis.com/v1beta/openai".to_string())
}
AIProvider::Groq => Ok("https://api.groq.com/openai/v1".to_string()),
AIProvider::OpenRouter => Ok("https://openrouter.ai/api/v1".to_string()),
AIProvider::TogetherAI => Ok("https://api.together.xyz/v1".to_string()),
AIProvider::Anthropic => Ok("https://api.anthropic.com/v1".to_string()),
AIProvider::Mistral => Ok("https://api.mistral.ai/v1".to_string()),
p @ (AIProvider::CustomAI | AIProvider::AzureOpenAI) => {
if let Some(base_url) = resource_base_url {
Ok(base_url)
} else {
Err(Error::BadRequest(format!(
"{:?} provider requires a base URL in the resource",
p
)))
}
}
}
}
}
@@ -339,18 +282,9 @@ impl AIProvider {
impl TryFrom<&str> for AIProvider {
type Error = Error;
fn try_from(s: &str) -> Result<Self> {
match s {
"openai" => Ok(AIProvider::OpenAI),
"anthropic" => Ok(AIProvider::Anthropic),
"mistral" => Ok(AIProvider::Mistral),
"groq" => Ok(AIProvider::Groq),
"openrouter" => Ok(AIProvider::OpenRouter),
"togetherai" => Ok(AIProvider::TogetherAI),
"deepseek" => Ok(AIProvider::DeepSeek),
"googleai" => Ok(AIProvider::GoogleAI),
"customai" => Ok(AIProvider::CustomAI),
_ => Err(Error::BadRequest(format!("Invalid AI provider: {}", s))),
}
let s = serde_json::from_value::<AIProvider>(serde_json::Value::String(s.to_string()))
.map_err(|e| Error::BadRequest(format!("Invalid AI provider: {}", e)))?;
Ok(s)
}
}
@@ -407,11 +341,7 @@ async fn global_proxy(
return Err(Error::BadRequest("API key is required".to_string()));
};
let base_url = provider.get_base_url()?;
let Some(base_url) = base_url else {
return Err(Error::BadRequest("Provider is not supported".to_string()));
};
let base_url = provider.get_base_url(None, &db).await?;
let url = format!("{}/{}", base_url, ai_path);
@@ -439,7 +369,7 @@ async fn global_proxy(
if response.error_for_status_ref().is_err() {
let err_msg = response.text().await.unwrap_or("".to_string());
return Err(Error::AiError(err_msg));
return Err(Error::AIError(err_msg));
}
let status_code = response.status();
@@ -464,32 +394,19 @@ async fn proxy(
None => return Err(Error::BadRequest("Provider is required".to_string())),
};
let workspace_cache = AI_KEY_CACHE.get(&(w_id.clone(), provider.clone()));
let workspace_cache = AI_REQUEST_CACHE.get(&(w_id.clone(), provider.clone()));
let forced_resource_path = headers
.get("X-Resource-Path")
.map(|v| v.to_str().unwrap_or("").to_string());
let ai_cache = match workspace_cache {
Some(cache) if !cache.is_expired() && forced_resource_path.is_none() => cache.cached_key,
let request_config = match workspace_cache {
Some(request_cache) if !request_cache.is_expired() && forced_resource_path.is_none() => {
request_cache.config
}
_ => {
let (resource, ai_provider, save_to_cache) = if let Some(resource_path) =
forced_resource_path
{
// forced resource path, get the resource directly
let resource = sqlx::query_scalar!(
"SELECT value FROM resource WHERE path = $1 AND workspace_id = $2",
&resource_path,
&w_id
)
.fetch_optional(&db)
.await?
.ok_or_else(|| {
Error::NotFound(format!(
"Could not find the resource {}, update the resource path in the workspace settings", resource_path
))
})?;
(resource, provider, false)
let (resource_path, save_to_cache) = if let Some(resource_path) = forced_resource_path {
// forced resource path
(resource_path, false)
} else {
let ai_config = sqlx::query_scalar!(
"SELECT ai_config FROM workspace_settings WHERE workspace_id = $1",
@@ -504,13 +421,13 @@ async fn proxy(
));
}
let ai_config = serde_json::from_value::<AIConfig>(ai_config.unwrap())
let mut ai_config = serde_json::from_value::<AIConfig>(ai_config.unwrap())
.map_err(|e| Error::BadRequest(e.to_string()))?;
let provider_config = ai_config
.providers
.as_ref()
.map(|providers| providers.get(&provider))
.as_mut()
.map(|providers| providers.remove(&provider))
.flatten()
.ok_or_else(|| {
Error::BadRequest(format!("Provider {:?} not configured", provider))
@@ -519,56 +436,37 @@ async fn proxy(
if provider_config.resource_path.is_empty() {
return Err(Error::BadRequest("Resource path is empty".to_string()));
}
let resource = sqlx::query_scalar!(
"SELECT value
FROM resource
WHERE path = $1 AND workspace_id = $2",
&provider_config.resource_path,
&w_id
)
.fetch_optional(&db)
.await?
.ok_or_else(|| {
Error::NotFound(format!(
"Could not find the {:?} resource at path {}, update the resource path in the workspace settings", provider, provider_config.resource_path
))
})?;
(resource, provider, true)
(provider_config.resource_path, true)
};
let Some(resource) = resource else {
return Err(Error::internal_err(format!(
"{:?} resource missing value",
ai_provider
)));
};
let resource= sqlx::query_scalar!(
"SELECT value as \"value: sqlx::types::Json<Box<RawValue>>\" FROM resource WHERE path = $1 AND workspace_id = $2",
&resource_path,
&w_id
)
.fetch_optional(&db)
.await?
.ok_or_else(|| Error::NotFound(format!("Could not find the resource {}, update the resource path in the workspace settings", resource_path)))?
.ok_or_else(|| Error::BadRequest(format!("Empty resource value for {}", resource_path)))?;
let ai_cache = match ai_provider {
AIProvider::OpenAI => openai::get_cached_value(&db, &w_id, resource).await?,
_ => {
openai_api_compatible::get_cached_value(
&db,
&w_id,
resource,
ai_provider.get_base_url()?,
)
.await?
}
};
let resource = serde_json::from_str::<AIResource>(resource.0.get())
.map_err(|e| Error::BadRequest(e.to_string()))?;
let request_config = AIRequestConfig::new(&provider, &db, &w_id, resource).await?;
if save_to_cache {
AI_KEY_CACHE.insert((w_id.clone(), ai_provider), AICache::new(ai_cache.clone()));
AI_REQUEST_CACHE.insert(
(w_id.clone(), provider.clone()),
ExpiringAIRequestConfig::new(request_config.clone()),
);
}
ai_cache
request_config
}
};
let request = match ai_cache {
KeyCache::Openai(cached) => cached.prepare_request(&ai_path, body),
KeyCache::OpenaiApiCompatible(cached) => cached.prepare_request(&ai_path, body),
};
let request = request_config.prepare_request(&provider, &ai_path, body)?;
let response = request?.send().await.map_err(to_anyhow)?;
let response = request.send().await.map_err(to_anyhow)?;
let mut tx = db.begin().await?;
@@ -586,7 +484,7 @@ async fn proxy(
if response.error_for_status_ref().is_err() {
let err_msg = response.text().await.unwrap_or("".to_string());
return Err(Error::AiError(err_msg));
return Err(Error::AIError(err_msg));
}
let status_code = response.status();

View File

@@ -20,8 +20,7 @@ use crate::{
use crate::{
job_helpers_ee::{
download_s3_file_internal, get_random_file_name, get_s3_resource,
get_workspace_s3_resource, load_image_preview_internal, upload_file_from_req,
DownloadFileQuery, LoadImagePreviewQuery,
get_workspace_s3_resource, upload_file_from_req, DownloadFileQuery,
},
users::fetch_api_authed_from_permissioned_as,
};
@@ -51,7 +50,6 @@ use sqlx::{types::Uuid, FromRow};
use std::str;
use windmill_audit::audit_ee::audit_log;
use windmill_audit::ActionKind;
use windmill_common::variables::encrypt;
use windmill_common::{
apps::{AppScriptId, ListAppQuery},
cache::{self, future::FutureCachedExt},
@@ -63,16 +61,24 @@ use windmill_common::{
http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, require_admin,
Pagination, StripPath,
},
variables::{build_crypt, build_crypt_with_key_suffix},
variables::{build_crypt, build_crypt_with_key_suffix, encrypt},
worker::{to_raw_value, CLOUD_HOSTED},
HUB_BASE_URL,
};
#[cfg(feature = "parquet")]
use windmill_common::{jwt, s3_helpers::build_object_store_client};
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
use windmill_queue::{push, PushArgs, PushArgsOwned, PushIsolationLevel};
#[cfg(feature = "parquet")]
use hmac::Mac;
#[cfg(feature = "parquet")]
use windmill_common::{
jwt,
oauth2::HmacSha256,
s3_helpers::{build_object_store_client, S3Object},
variables::get_workspace_key,
};
pub fn workspaced_service() -> Router {
Router::new()
.route("/list", get(list_apps))
@@ -94,6 +100,7 @@ pub fn workspaced_service() -> Router {
get(list_paths_from_workspace_runnable),
)
.route("/custom_path_exists/*custom_path", get(custom_path_exists))
.route("/sign_s3_objects", post(sign_s3_objects))
}
pub fn unauthed_service() -> Router {
@@ -102,10 +109,6 @@ pub fn unauthed_service() -> Router {
.route("/upload_s3_file/*path", post(upload_s3_file_from_app))
.route("/delete_s3_file", delete(delete_s3_file_from_app))
.route("/download_s3_file/*path", get(download_s3_file_from_app))
.route(
"/load_image_preview/*path",
get(load_s3_file_image_preview_from_app),
)
.route("/public_app/:secret", get(get_public_app_by_secret))
.route("/public_resource/*path", get(get_public_resource))
}
@@ -232,7 +235,8 @@ pub struct S3Input {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct S3Key {
s3_path: String,
resource: String,
#[serde(skip_serializing_if = "Option::is_none")]
storage: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
@@ -1554,15 +1558,97 @@ struct UploadFileToS3Query {
#[cfg(feature = "parquet")]
#[derive(Serialize, Deserialize)]
struct DeleteTokenClaims {
struct S3DeleteTokenClaims {
file_key: String,
on_behalf_of_email: String,
permissioned_as: String,
username: String,
s3_resource_path: Option<String>,
workspace: String,
pub exp: usize,
}
#[cfg(feature = "parquet")]
#[derive(Deserialize)]
struct S3TokenRequestBody {
s3_objects: Vec<S3Object>,
}
#[cfg(feature = "parquet")]
async fn sign_s3_objects(
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
Json(body): Json<S3TokenRequestBody>,
) -> Result<Json<Vec<S3Object>>> {
let workspace_key = get_workspace_key(&w_id, &db).await?;
let futures = body.s3_objects.into_iter().map(|s3_object| async {
let exp = (chrono::Utc::now() + chrono::Duration::hours(12)).timestamp();
let mut message = format!("file_key={}&exp={}", s3_object.s3.clone(), exp);
if let Some(ref storage) = s3_object.storage {
message = format!("{}&storage={}", message, storage);
}
let mut max = HmacSha256::new_from_slice(workspace_key.as_bytes())
.map_err(|err| Error::internal_err(format!("Failed to create hmac: {}", err)))?;
max.update(message.as_bytes());
let result = max.finalize();
let signature = hex::encode(result.into_bytes());
let presigned = format!("exp={}&sig={}", exp, signature);
Ok::<_, Error>(S3Object { presigned: Some(presigned), ..s3_object })
});
let signed_s3_objects = futures::future::try_join_all(futures).await?;
Ok(Json(signed_s3_objects))
}
#[cfg(feature = "parquet")]
async fn validate_s3_signature(file_query: &AppS3FileQuery, w_id: &str, db: &DB) -> Result<()> {
let workspace_key = get_workspace_key(w_id, &db).await?;
let Some(exp) = file_query
.exp
.as_ref()
.map(|e| e.parse::<i64>().unwrap_or_default())
else {
return Err(Error::BadRequest("Missing exp".to_string()));
};
let Some(ref sig) = file_query.sig else {
return Err(Error::BadRequest("Missing signature".to_string()));
};
let mut message = format!("file_key={}&exp={}", file_query.s3, exp);
if let Some(ref storage) = file_query.storage {
message = format!("{}&storage={}", message, storage);
}
let mut mac = HmacSha256::new_from_slice(workspace_key.as_bytes())
.map_err(|err| Error::internal_err(format!("Failed to create hmac: {}", err)))?;
mac.update(message.as_bytes());
let sig_bytes = hex::decode(sig)?;
mac.verify_slice(&sig_bytes)
.map_err(|err| Error::BadRequest(format!("Invalid signature: {}", err)))?;
if exp < chrono::Utc::now().timestamp() {
return Err(Error::BadRequest("Signature expired".to_string()));
}
Ok(())
}
#[cfg(not(feature = "parquet"))]
async fn sign_s3_objects() -> Result<()> {
return Err(Error::BadRequest(
"This endpoint requires the parquet feature to be enabled".to_string(),
));
}
#[cfg(feature = "parquet")]
#[derive(Serialize)]
struct AppUploadFileResponse {
@@ -1817,13 +1903,14 @@ async fn upload_s3_file_from_app(
upload_file_from_req(s3_client, &file_key, request, options).await?;
let delete_token = jwt::encode_with_internal_secret(DeleteTokenClaims {
let delete_token = jwt::encode_with_internal_secret(S3DeleteTokenClaims {
file_key: file_key.clone(),
on_behalf_of_email,
permissioned_as,
username,
s3_resource_path: query.s3_resource_path,
exp: (chrono::Utc::now() + chrono::Duration::seconds(3600 * 24)).timestamp() as usize,
workspace: w_id.clone(),
exp: (chrono::Utc::now() + chrono::Duration::hours(12)).timestamp() as usize,
})
.await?;
@@ -1843,14 +1930,19 @@ async fn delete_s3_file_from_app(
Path(w_id): Path<String>,
Query(query): Query<DeleteS3FileQuery>,
) -> Result<()> {
let DeleteTokenClaims {
let S3DeleteTokenClaims {
file_key,
on_behalf_of_email,
permissioned_as,
username,
s3_resource_path,
workspace,
..
} = jwt::decode_with_internal_secret::<DeleteTokenClaims>(&query.delete_token).await?;
} = jwt::decode_with_internal_secret::<S3DeleteTokenClaims>(&query.delete_token).await?;
if workspace != w_id {
return Err(Error::BadRequest("Invalid workspace".to_string()));
}
let on_behalf_authed = fetch_api_authed_from_permissioned_as(
permissioned_as,
@@ -1958,7 +2050,7 @@ async fn get_on_behalf_authed_from_app(
async fn check_if_allowed_to_access_s3_file_from_app(
db: &DB,
opt_authed: &Option<ApiAuthed>,
file_key: &str,
file_query: &AppS3FileQuery,
w_id: &str,
path: &str,
policy: &Policy,
@@ -1966,40 +2058,59 @@ async fn check_if_allowed_to_access_s3_file_from_app(
// if anonymous, check that the file was the result of an app script ran by an anonymous user in the last 3 hours
// otherwise, if logged in, allow any file (TODO: change that when we implement better s3 policy)
let allowed = opt_authed.is_some()
|| sqlx::query_scalar!(
r#"SELECT EXISTS (
SELECT 1 FROM v2_as_completed_job
WHERE workspace_id = $2
AND (job_kind = 'appscript' OR job_kind = 'preview')
AND created_by = 'anonymous'
AND started_at > now() - interval '3 hours'
AND script_path LIKE $3 || '/%'
AND result @> ('{"s3":"' || $1 || '"}')::jsonb
)"#,
file_key,
w_id,
path,
)
.fetch_one(db)
.await?
.unwrap_or(false)
// check if the file is allowed by the allowed_s3_keys policy
|| policy.allowed_s3_keys.as_ref().unwrap().iter().any(|key| key.s3_path == file_key);
if !allowed {
Err(Error::BadRequest("File restricted".to_string()))
} else {
if file_query.sig.is_some() {
validate_s3_signature(file_query, w_id, &db).await
} else if opt_authed.is_some() {
Ok(())
} else {
let allowed = policy
.allowed_s3_keys
.as_ref()
.unwrap()
.iter()
.any(|key| key.s3_path == file_query.s3 && key.storage == file_query.storage)
|| {
sqlx::query_scalar!(
r#"SELECT EXISTS (
SELECT 1 FROM v2_as_completed_job
WHERE workspace_id = $2
AND (job_kind = 'appscript' OR job_kind = 'preview')
AND created_by = 'anonymous'
AND started_at > now() - interval '3 hours'
AND script_path LIKE $3 || '/%'
AND result @> ('{"s3":"' || $1 || '"}')::jsonb
)"#,
file_query.s3,
w_id,
path,
)
.fetch_one(db)
.await?
.unwrap_or(false)
};
if !allowed {
Err(Error::BadRequest("File restricted".to_string()))
} else {
Ok(())
}
}
}
#[cfg(feature = "parquet")]
#[derive(Deserialize)]
pub struct DownloadFileQueryWithForceViewerAllowedS3Keys {
#[derive(Deserialize, Debug)]
struct AppS3FileQuery {
s3: String,
storage: Option<String>,
sig: Option<String>,
exp: Option<String>,
}
#[cfg(feature = "parquet")]
#[derive(Deserialize, Debug)]
struct AppS3FileQueryWithForceViewerAllowedS3Keys {
#[serde(flatten)]
pub file_query: DownloadFileQuery,
pub file_query: AppS3FileQuery,
pub force_viewer_allowed_s3_keys: Option<String>,
}
@@ -2008,7 +2119,7 @@ async fn download_s3_file_from_app(
OptAuthed(opt_authed): OptAuthed,
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
Query(query): Query<DownloadFileQueryWithForceViewerAllowedS3Keys>,
Query(query): Query<AppS3FileQueryWithForceViewerAllowedS3Keys>,
) -> Result<Response> {
let path = path.to_path();
@@ -2027,46 +2138,26 @@ async fn download_s3_file_from_app(
check_if_allowed_to_access_s3_file_from_app(
&db,
&opt_authed,
&query.file_query.file_key,
&query.file_query,
&w_id,
&path,
&policy,
)
.await?;
download_s3_file_internal(on_behalf_authed, &db, None, "", &w_id, query.file_query).await
}
#[cfg(not(feature = "parquet"))]
async fn load_s3_file_image_preview_from_app() -> Result<()> {
return Err(Error::BadRequest(
"This endpoint requires the parquet feature to be enabled".to_string(),
));
}
#[cfg(feature = "parquet")]
async fn load_s3_file_image_preview_from_app(
OptAuthed(opt_authed): OptAuthed,
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
Query(query): Query<LoadImagePreviewQuery>,
) -> Result<Response> {
let path = path.to_path();
let (on_behalf_authed, policy) =
get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed, None).await?;
check_if_allowed_to_access_s3_file_from_app(
download_s3_file_internal(
on_behalf_authed,
&db,
&opt_authed,
&query.file_key,
None,
"",
&w_id,
&path,
&policy,
DownloadFileQuery {
file_key: query.file_query.s3,
s3_resource_path: None,
storage: query.file_query.storage,
},
)
.await?;
load_image_preview_internal(on_behalf_authed, &db, "", &w_id, query).await
.await
}
fn get_on_behalf_of(policy: &Policy) -> Result<(String, String)> {

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