Compare commits

..

172 Commits

Author SHA1 Message Date
Ruben Fiszel
f6f524686a chore(main): release 1.415.2 (#4616)
* chore(main): release 1.415.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-11-01 01:15:42 +01:00
wendrul
c75ad12d4f Clarify message about indexer being EE-only (#4613) 2024-11-01 01:06:17 +01:00
BretzelLudique
8cd1df4dca Fix other flow tutorials (#4614)
* fix(cli): improve --instance handling wmill instance push

* fix(cli): improve --instance handling wmill instance push

* fix for-loop

* fix flow tutorials

* chore: harmonize, rename, cleaning etc

* fix last merge commit (raw copy from upstream)

---------

Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2024-11-01 01:05:54 +01:00
Ruben Fiszel
984c6dd10c fix(s3): align s3 handler additional creds providers 2024-11-01 00:51:28 +01:00
Ruben Fiszel
b0fffe11eb chore(main): release 1.415.1 (#4611)
* chore(main): release 1.415.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-31 14:55:42 +01:00
BretzelLudique
52b534e77e fix simple flow tutorial (#4612) 2024-10-31 14:54:56 +01:00
Ruben Fiszel
cb005a15ba fix(cli): improve --instance handling wmill instance push 2024-10-31 14:52:05 +01:00
Ruben Fiszel
d4724b7fed chore(main): release 1.415.0 (#4608)
* chore(main): release 1.415.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-31 14:28:00 +01:00
pyranota
efb61e63d4 Add rollback for multipython (#4610) 2024-10-31 14:27:34 +01:00
Ruben Fiszel
8c7f53b2eb fix: improve user resource input 2024-10-31 13:29:54 +01:00
Ruben Fiszel
7811b83e86 chore: tighten when tag latest is triggered 2024-10-31 10:49:38 +01:00
Wítallo
a07f57e698 feat(cli): opts.instance as instace name and prefix (#4609) 2024-10-31 10:19:07 +01:00
Ruben Fiszel
ba4aed5bf5 fix: improve express oauth setup 2024-10-30 20:40:50 +01:00
HugoCasa
b349067ce1 cache old slack error handler (#4607) 2024-10-30 17:41:43 +01:00
HugoCasa
57e3fafc51 fix slack error handler when error is too long (#4606) 2024-10-30 17:36:38 +01:00
HugoCasa
1ff221f2c0 improve array params handling in postgres (#4605) 2024-10-30 14:44:42 +01:00
Ruben Fiszel
f2db73be86 nit confirmCallback edit script 2024-10-30 14:44:32 +01:00
Ruben Fiszel
ebd971f153 chore: fix nsjail image build 2024-10-29 19:40:10 +01:00
Ruben Fiszel
23c589d1b3 chore(main): release 1.414.2 (#4603)
* chore(main): release 1.414.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-29 18:35:47 +01:00
Ruben Fiszel
212579a514 fix(cli): improve instance sync for CI/CD + --folder-per-instance 2024-10-29 18:30:45 +01:00
Ruben Fiszel
eb369676ad chore(main): release 1.414.1 (#4600)
* chore(main): release 1.414.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-29 14:18:35 +01:00
Ruben Fiszel
dcf5e2f03f fix(bun): handle bun lockfile created with windows (#4602) 2024-10-29 14:09:38 +01:00
Ruben Fiszel
703db7d441 fix(apps): public apps can take full height 2024-10-29 12:40:50 +01:00
Ruben Fiszel
b0b9180fb9 fix(apps): enable text selection on aggrid tables by default 2024-10-29 12:27:41 +01:00
Ruben Fiszel
6e222b3b1a fix(frontend): make script and schema scrollable on script detail page 2024-10-29 12:23:56 +01:00
Ruben Fiszel
b86de6280e fix(frontend): new approval steps default to timeout 1800 2024-10-29 10:47:06 +01:00
Ruben Fiszel
905e67bda9 chore(main): release 1.414.0 (#4599)
* chore(main): release 1.414.0

* Apply automatic changes

* Update CHANGELOG.md

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-29 09:34:42 +01:00
HugoCasa
ec2d00c374 improve key renewal (#4519)
* improve key renewal

* sqlx and fix

* fix build

* improve everything

* nits

* nits

* update ee ref

* nit
2024-10-29 09:26:45 +01:00
Ruben Fiszel
ddab1f7c44 chore(main): release 1.413.2 (#4598)
* chore(main): release 1.413.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-29 09:13:48 +01:00
Ruben Fiszel
925a742cde nit test serializer 2024-10-29 09:09:08 +01:00
Ruben Fiszel
a32a0abd3f handle better empty tag overrides 2024-10-29 09:01:02 +01:00
Ruben Fiszel
94cf0184a8 improve test this step for flow tag overide 2024-10-29 08:19:17 +01:00
Ruben Fiszel
65551fcb8b furhter nits 2024-10-29 08:09:48 +01:00
Ruben Fiszel
d743e00563 fix(frontend): fix script and flow renaming 2024-10-29 07:49:05 +01:00
Ruben Fiszel
5b7c6d7d62 fix(backend): in flows, workspace scripts should use their set tags instead of the default one 2024-10-29 07:29:39 +01:00
Ruben Fiszel
3c62f5ea83 fix(cli): fix wmill instance pull --instance 2024-10-28 17:46:15 +01:00
Ruben Fiszel
95c2eaff69 chore(main): release 1.413.1 (#4595)
* chore(main): release 1.413.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-28 17:32:43 +01:00
Ruben Fiszel
82987105a6 fix(cli): fix wmill instance push --base-url and --instance 2024-10-28 17:22:51 +01:00
Ruben Fiszel
1e20438fa4 chore(main): release 1.413.0 (#4591)
* chore(main): release 1.413.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-28 16:36:18 +01:00
Ruben Fiszel
0037112905 improve fullscaleout behavior 2024-10-28 12:37:27 +01:00
Ruben Fiszel
6ff7b2d085 fix cli compilation with crypto 2024-10-28 12:09:29 +01:00
Ruben Fiszel
e54d253685 fix: add run immediately popover to run again 2024-10-28 09:38:10 +01:00
Ruben Fiszel
8ac5b10313 allow columnDefs with no field but children in aggrid infinite table 2024-10-28 08:18:07 +01:00
Ruben Fiszel
fe7d044a66 feat: autoscaling v0 (#4593)
* all

* all

all

all

all

all

all

all

all

all

* all

* all

* nits

* nits
2024-10-28 00:47:19 +01:00
Alexander Petric
bac3205725 fix(docs): smtp setup documentation link (#4590) 2024-10-26 08:43:18 +02:00
Ruben Fiszel
aefff1d0e2 chore(main): release 1.412.0 (#4573)
* chore(main): release 1.412.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-25 20:02:57 +02:00
Ruben Fiszel
d1a28eb7ca fix: do not update created_at of scripts on lockfile generation 2024-10-25 17:44:41 +02:00
wendrul
5621bd30d3 Make file_resources that are filled with a variable in ansible (#4584)
* Make file_resources that are filled with a variable in ansible

* Rename file_resources section to files

* Add variable example to init code
2024-10-25 14:18:28 +02:00
Alec Minchington
a46aa644b0 feat: add Spotify oauth provider (#4581) 2024-10-25 08:19:42 +02:00
Ruben Fiszel
a49f7e5d42 Update OAuthSetting.svelte 2024-10-25 06:18:06 +02:00
Alexander Petric
f18a61f7a2 ux: job queue critical alert settings ux improvements (#4577)
* improving the ux of queued jobs alert settings

* minor improvements

* removing name column
2024-10-24 22:58:39 +02:00
Stephen Beckstrand
f0eebd9bd7 chore: Update references to windmill-ee-full image (#4578)
* chore: Update windmill-ee-full image references

The messaging in a few backend executors references the need to
use the full enterprise image rather than `windmill-ee`. These
messages reference the `windmill-full-ee` image but its the image
is actually named `windmill-ee-full` under the available Packages.

* chore: Update refs to rust to ansible in ansible executor
2024-10-24 17:40:59 +02:00
Ruben Fiszel
134cfdb30e fix(bash): correctly propagate sigterm for cancelled bash scripts 2024-10-24 14:42:37 +02:00
Ruben Fiszel
84e0524950 fix: initialize empty smtp settings correctly 2024-10-24 13:40:09 +02:00
Ruben Fiszel
9f43d5dcd9 fix(app builder): date input default value improvements 2024-10-23 11:11:24 +02:00
Alexander Petric
7e4f280e75 adding oauth.login_failure to openapi spec (#4570) 2024-10-22 22:09:31 +02:00
Ruben Fiszel
2e2fda5a06 chore(main): release 1.411.1 (#4569)
* chore(main): release 1.411.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-22 20:26:35 +02:00
Ruben Fiszel
0586446671 fix: update bun to 1.1.32 (#4568)
* fix: update bun to 1.1.32

* Update DockerfileSlimEe

* Update Dockerfile
2024-10-22 20:08:26 +02:00
Ruben Fiszel
a357cf46e4 remove issues_comment trigger for ci 2024-10-22 19:40:31 +02:00
Guilhem
32fd866ce9 Glm/fix cron builder (#4566)
* fix cron expression

* fix cron builder

* Fix prefilled schedule

---------

Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>
2024-10-22 17:50:43 +02:00
Alexander Petric
c8a2f8fee0 update docker build on comment (#4567)
* adding github workflow build triggers on comment

* fix conditions

* revert formating

* simplify

* event_name fix

* build ee on nsjail as well
2024-10-22 17:18:14 +02:00
Alexander Petric
669c8f81c7 adding github workflow build triggers on comment (#4565)
* adding github workflow build triggers on comment

* fix conditions

* revert formating

* simplify

* event_name fix
2024-10-22 17:02:59 +02:00
HugoCasa
2d5097e376 improve stripe team plan switching (#4552)
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-10-22 15:49:08 +02:00
Alexander Petric
18fc0e4f93 audit logs for failed login attempts (#4564)
* audit logs for failed login attempts

* updating ee ref
2024-10-22 02:42:12 +02:00
Ruben Fiszel
ae3961ec59 chore(main): release 1.411.0 (#4559)
* chore(main): release 1.411.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-21 20:20:59 +02:00
wendrul
2079b2e7e1 fix: Do not ignore file resources with json file ext (#4562) 2024-10-21 17:44:16 +02:00
Ruben Fiszel
b8a6a11635 feat(cli): encrypt sensitive instance settings (#4561) 2024-10-21 14:38:08 +02:00
Ruben Fiszel
0d9039641b fix: update bun to 1.1.31 and deno to 2.0.2 2024-10-21 11:29:14 +02:00
Ruben Fiszel
4ca7c2bf07 chore(main): release 1.410.3 (#4556)
* chore(main): release 1.410.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-20 12:57:00 +02:00
Ruben Fiszel
87f5c078dd fix(go-client): reduce runtime dependencies by bumping oai-codeen to v2.4.1 2024-10-20 12:50:57 +02:00
Ruben Fiszel
444bfb85a3 chore(main): release 1.410.2 (#4555)
* chore(main): release 1.410.2

* Update version.txt

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-19 17:14:57 +02:00
Ruben Fiszel
a92a2fd6fd fix(frontend): improve display of error handlers 2024-10-19 16:57:12 +02:00
Ruben Fiszel
3cd30d2376 chore(main): release 1.410.1 (#4553)
* chore(main): release 1.410.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-19 12:30:25 +02:00
Ruben Fiszel
68c07c23f5 add a way to skip deno cli warning 2024-10-19 12:18:15 +02:00
Ruben Fiszel
26a40d1944 fix(cli): improve wmill init behavior 2024-10-19 12:15:59 +02:00
Ruben Fiszel
5115aee577 various nits 2024-10-19 12:11:29 +02:00
HugoCasa
17d9541dc2 fix ws killpill (#4551)
* websocket killpill

* oups

* nit

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-10-19 01:17:15 +02:00
HugoCasa
fc7bbb9168 websocket killpill (#4550) 2024-10-19 00:02:48 +02:00
Ruben Fiszel
0f0c6ce2df chore(main): release 1.410.0 (#4546)
* chore(main): release 1.410.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-18 20:35:20 +02:00
wendrul
eb5ad0efee Remove debug line (#4548) 2024-10-18 20:31:53 +02:00
HugoCasa
9b4e7be908 support colon in job filter tags scope (#4549) 2024-10-18 20:31:38 +02:00
Ruben Fiszel
549b11dcfb fix(frontend): add back script lockfile to script details pae 2024-10-18 20:30:34 +02:00
Ruben Fiszel
248fdc24a6 feat(typescript-bun): support relative imports without the .ts extension 2024-10-18 12:26:57 +02:00
Ruben Fiszel
ba5539a18f adapt websocket triggers to multi-tenant cloud 2024-10-18 00:52:23 +02:00
HugoCasa
8807e99f06 feat: websocket triggers (#4505)
* feat: websocket triggers

* fix

* frontend nits

* nits

* new trigger UI

* sqlx
2024-10-18 00:21:52 +02:00
Ruben Fiszel
08595c6f14 fix: cache js static assets by default 2024-10-18 00:12:07 +02:00
Ruben Fiszel
d7cf5ea37d fix: improve cancelling of jobs on public apps for anonymous users 2024-10-17 20:37:56 +02:00
wendrul
52c1ac099f Add list dir logs after commit for indexer (#4545) 2024-10-17 19:49:17 +02:00
Ruben Fiszel
655a206f5a npm check fix 2024-10-17 18:09:13 +02:00
pyranota
7f5e294d2c Deploy collision warning fix (#4544)
* feat: Multiplayer deploy collision warning

Show warning with diff viewer if 2+ users try to deploy flow/script/app at the same time

In other words, if user A and user B started editing same flow/script/app and user A deployed it,
user B will get warning if they try to deploy as well.

* Add warning for scripts

* Add `deployedBy` to Apps

* Format

* Fix advanced deployment on scripts

* Write comments and cleanup

* feat(frontend): unify all triggers UX and simplify flow settings (#4259)

* feat(frontend): added list of triggers in the flow graph

* feat(frontend): added list of triggers in the flow graph

* feat(frontend): clean up

* feat(frontend): improve UX

* feat(frontend): triggers

* feat(frontend): triggers

* feat(frontend): done

* feat(frontend): fix trigger when position when a preprocessor is presetn

* Glm/rework flow settings v2 (#4497)

* fat(frontend): simplify flow settings menu

* improve scroll

* changing mute toggle

* Add advanced settings badge

* Add nord theme colors

* Add bage for advanced options

* fix minor issue

* fix minor issue

* Add triggers menu to flow settings

* Add quick trigger access

* remove triggers in flow settings

* fix minor issue

* Move triggers settings to flow right panel

* polishing

* fix unset store

* remove save up to for triggers

* fix padding

* reset default tag color

* remove custom select component

* revert path change

* revert section modif

* Revert unused feature

---------

Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>

* Connect top bar cron to schedules settings

* Turn copilot into node

* fix copilot placement

* remove useless import

* fix center copilot

* fix binding

* remove copilot on top of preprocessor

* render copilot node on condition

* quickfix

* remove copilot node

* fix minor issues

* fix route count update

* fix schedule sync

* harmonize colors

* fix alignment and add edges

* recenter node summary

* fix schedules sync

* Add id title

* all

* all

* all

* iteration

* all

* all

* done

* fix

* more fixes

---------

Co-authored-by: Guilhem <guilhemlemouel@gmail.com>
Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>

* Update ScriptBuilder.svelte

* Remove `onMount` for flows

* Use version instead of last_updated_at in flows

* Use only versions for apps

* Fetch latest data in Diffs in Apps

* Optimize with (script/flow/app)GetLatestVersion

Create several new endpoints, that returns just latest version without rest of the history

* Sync Diffs with deployed

* Improve Diff's data

* Use `getFlowLatestVersion`

* fix failing endpoints for deployed collision warning

---------

Co-authored-by: Faton Ramadani <faton.ramadani14@gmail.com>
Co-authored-by: Guilhem <guilhemlemouel@gmail.com>
Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2024-10-17 18:05:44 +02:00
pyranota
ce80d6b07b Multiplayer deploy collision warning (#4533)
* feat: Multiplayer deploy collision warning

Show warning with diff viewer if 2+ users try to deploy flow/script/app at the same time

In other words, if user A and user B started editing same flow/script/app and user A deployed it,
user B will get warning if they try to deploy as well.

* Add warning for scripts

* Add `deployedBy` to Apps

* Format

* Fix advanced deployment on scripts

* Write comments and cleanup

* feat(frontend): unify all triggers UX and simplify flow settings (#4259)

* feat(frontend): added list of triggers in the flow graph

* feat(frontend): added list of triggers in the flow graph

* feat(frontend): clean up

* feat(frontend): improve UX

* feat(frontend): triggers

* feat(frontend): triggers

* feat(frontend): done

* feat(frontend): fix trigger when position when a preprocessor is presetn

* Glm/rework flow settings v2 (#4497)

* fat(frontend): simplify flow settings menu

* improve scroll

* changing mute toggle

* Add advanced settings badge

* Add nord theme colors

* Add bage for advanced options

* fix minor issue

* fix minor issue

* Add triggers menu to flow settings

* Add quick trigger access

* remove triggers in flow settings

* fix minor issue

* Move triggers settings to flow right panel

* polishing

* fix unset store

* remove save up to for triggers

* fix padding

* reset default tag color

* remove custom select component

* revert path change

* revert section modif

* Revert unused feature

---------

Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>

* Connect top bar cron to schedules settings

* Turn copilot into node

* fix copilot placement

* remove useless import

* fix center copilot

* fix binding

* remove copilot on top of preprocessor

* render copilot node on condition

* quickfix

* remove copilot node

* fix minor issues

* fix route count update

* fix schedule sync

* harmonize colors

* fix alignment and add edges

* recenter node summary

* fix schedules sync

* Add id title

* all

* all

* all

* iteration

* all

* all

* done

* fix

* more fixes

---------

Co-authored-by: Guilhem <guilhemlemouel@gmail.com>
Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>

* Update ScriptBuilder.svelte

* Remove `onMount` for flows

* Use version instead of last_updated_at in flows

* Use only versions for apps

* Fetch latest data in Diffs in Apps

* Optimize with (script/flow/app)GetLatestVersion

Create several new endpoints, that returns just latest version without rest of the history

* Sync Diffs with deployed

* Improve Diff's data

* Use `getFlowLatestVersion`

---------

Co-authored-by: Faton Ramadani <faton.ramadani14@gmail.com>
Co-authored-by: Guilhem <guilhemlemouel@gmail.com>
Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2024-10-17 17:14:48 +02:00
Ruben Fiszel
658a9345a2 chore(main): release 1.409.4 (#4543)
* chore(main): release 1.409.4

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-17 17:12:22 +02:00
Ruben Fiszel
59a1e67465 fix: fix flow viewer renderer outside of flow details and editor context 2024-10-17 17:09:44 +02:00
Ruben Fiszel
64e2334c2c chore(main): release 1.409.3 (#4542)
* chore(main): release 1.409.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-17 15:35:11 +02:00
Ruben Fiszel
77702268d2 improve primary schedule direct button on script editor 2024-10-17 15:30:30 +02:00
Ruben Fiszel
c3c2fe462c fix: do not delete primary schedule of script/flow on redeploy even if schedule wasn't loaded 2024-10-17 15:25:19 +02:00
pyranota
b237873a87 Annotations refactor (#4512)
* Refactor annotation system

* Move tests to different location

* Limit annotations per line (15)

* Write initial benchmark

* Optimize

* More optimizations

* Rewrite with 3x speed!

* Remove benchmarks

They were in wrong place and for development

* Polishing

* Remove unused leftover

* Integrate new annotation system

* Add comment explanation

* Remove `hashe-based matching`

Turned to be it is impure and non-deterministic method

* Add `tracing::error` for `// native` without `deno_core`

If you build backend without `deno_core` feature flag, and add //native
annotation to ts script, it would just silently exit.

This commit prints error to logs

* add more tests

* Update annotations.rs

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-10-17 09:14:31 +02:00
Ruben Fiszel
b7ad19bb75 fix(nsjail): improve memory reading when using nsjail 2024-10-17 07:42:30 +02:00
Ruben Fiszel
45bf59ba32 chore(main): release 1.409.2 (#4541)
* chore(main): release 1.409.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-17 01:22:18 +02:00
Ruben Fiszel
1466da3999 fix: add extra args support for exception to bun scripts 2024-10-17 01:19:25 +02:00
Ruben Fiszel
3174024d8e fix: fix script persistence in url + add support for extra error args in python 2024-10-17 01:07:56 +02:00
Ruben Fiszel
4e6c0d1e80 chore(main): release 1.409.1 (#4540)
* chore(main): release 1.409.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-17 00:08:06 +02:00
Ruben Fiszel
6abb346013 fix(git-sync): propagate update of folders with git sync 2024-10-16 23:44:56 +02:00
Ruben Fiszel
79100f8d0a move to lto thin 2024-10-16 23:39:51 +02:00
Ruben Fiszel
28f6f6b067 chore: update bun to 1.30.0 2024-10-16 22:53:42 +02:00
Ruben Fiszel
d24e153065 fix(apidocs): fix generated openapi files 2024-10-16 22:11:56 +02:00
Ruben Fiszel
8df2cec40a chore(main): release 1.409.0 (#4530)
* chore(main): release 1.409.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-16 20:49:06 +02:00
Ruben Fiszel
a8c4ea2334 fix(python-client): improve get_job_status for running jobs 2024-10-16 20:09:20 +02:00
Ruben Fiszel
20e8be15fe add lto fat to release build 2024-10-16 19:51:08 +02:00
Ruben Fiszel
c3b536b1b8 fix(app): make s3 uploads persistent across tabs change 2024-10-16 19:43:20 +02:00
wendrul
c06c42a1ed Win 465 index observability 1 (#4538)
* Factor isJobCancellable function in a utils file

* Add message for last indexed job and warning of deployement

* Prepare sqlx

* Update ee repo ref
2024-10-16 19:25:50 +02:00
Ruben Fiszel
01b95ebd8d nitso utline triggers 2024-10-16 19:23:16 +02:00
Ruben Fiszel
99814c7ab0 nits schedule UX 2024-10-16 19:19:24 +02:00
Ruben Fiszel
10414df4a7 fix email token generation 2024-10-16 18:23:06 +02:00
Faton Ramadani
91a3d06529 feat(frontend): unify all triggers UX and simplify flow settings (#4259)
* feat(frontend): added list of triggers in the flow graph

* feat(frontend): added list of triggers in the flow graph

* feat(frontend): clean up

* feat(frontend): improve UX

* feat(frontend): triggers

* feat(frontend): triggers

* feat(frontend): done

* feat(frontend): fix trigger when position when a preprocessor is presetn

* Glm/rework flow settings v2 (#4497)

* fat(frontend): simplify flow settings menu

* improve scroll

* changing mute toggle

* Add advanced settings badge

* Add nord theme colors

* Add bage for advanced options

* fix minor issue

* fix minor issue

* Add triggers menu to flow settings

* Add quick trigger access

* remove triggers in flow settings

* fix minor issue

* Move triggers settings to flow right panel

* polishing

* fix unset store

* remove save up to for triggers

* fix padding

* reset default tag color

* remove custom select component

* revert path change

* revert section modif

* Revert unused feature

---------

Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>

* Connect top bar cron to schedules settings

* Turn copilot into node

* fix copilot placement

* remove useless import

* fix center copilot

* fix binding

* remove copilot on top of preprocessor

* render copilot node on condition

* quickfix

* remove copilot node

* fix minor issues

* fix route count update

* fix schedule sync

* harmonize colors

* fix alignment and add edges

* recenter node summary

* fix schedules sync

* Add id title

* all

* all

* all

* iteration

* all

* all

* done

* fix

* more fixes

---------

Co-authored-by: Guilhem <guilhemlemouel@gmail.com>
Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2024-10-16 17:58:15 +02:00
Alexander Petric
f38b3d14e8 fix(ui): dark mode support for queue metrics based critical alert (#4535) 2024-10-16 16:26:07 +02:00
wendrul
e736572db1 fix: canceled jobs button reporting 0 jobs cancelled (#4534) 2024-10-16 16:15:25 +02:00
wendrul
7126ba12c7 feat: Scroll to element in virtual list when clicking on graph point (#4532)
* Scroll to element in virtual list when clicking on graph point

* Remove debug statements

* Remove reactive statement in favor of eventful triggering
2024-10-16 14:37:20 +02:00
Alexander Petric
29cb954639 rhel9 build workflow (#4528)
* dockerfile for rhel9

* attach rh to release page workflow

* changing name

* updating new workflow

* updating new workflow

* pushing new workflow

* trigger workflow

* trigger workflow

* trigger workflow

* trigger workflow

* finished workflow

* ellipsis comment
2024-10-16 14:36:36 +02:00
wendrul
411bce7e13 fix: Add indexer backup lock to fit the deployment model (#4531)
* Pass db to init_index

* Prepare sqlx

* Update ee repo ref
2024-10-16 14:36:12 +02:00
Alexander Petric
99c5b3ecda feat(sso): adding the ability to define a custom display name for sso (#4529)
* feat(sso): adding the ability to define a custom display name for sso

* adding openapi-deref.json

* make the display_name field optional

* Update ee-repo-ref.txt
2024-10-15 17:32:39 +02:00
Ruben Fiszel
9eb1ecc9f3 fix(app): accept connecting to non yet existing state output for convenience 2024-10-15 09:47:22 +02:00
Ruben Fiszel
dcdbf1afb4 fix(app): improve absolute url handling in download button and downloadFile 2024-10-15 09:18:24 +02:00
Ruben Fiszel
34c39ce0ab nit closing flowgraph 2024-10-13 01:30:01 +02:00
Ruben Fiszel
288dfe7c70 custom windmill icon2 2024-10-13 00:47:10 +02:00
Ruben Fiszel
8867260e43 display version in backend tests 2024-10-13 00:03:36 +02:00
Ruben Fiszel
4112eb6072 backend tests 2024-10-12 23:33:09 +02:00
Ruben Fiszel
9d2cb91f9c chore(main): release 1.408.1 (#4526)
* chore(main): release 1.408.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-12 18:03:06 +02:00
Ruben Fiszel
a323d2b29a fix deno lockall tests 2024-10-12 17:59:05 +02:00
Ruben Fiszel
fa642c8db1 fix deno lockall tests 2024-10-12 17:57:28 +02:00
Ruben Fiszel
42fe31f804 fix: fix deno cache --allow-import on deno 2 2024-10-12 17:18:46 +02:00
Ruben Fiszel
242a251ab1 use push isolation level to isolated in more push cases 2024-10-12 17:00:54 +02:00
Ruben Fiszel
7a92a152d1 use isolated transctions for workflow_as_code 2024-10-12 16:23:53 +02:00
Ruben Fiszel
fad4785b0a verify vulnerability only on releases + tag latest on full + cuda 2024-10-12 15:39:13 +02:00
Ruben Fiszel
296aa97bd8 add trace_id to all request spans 2024-10-12 15:19:43 +02:00
Ruben Fiszel
d77412cef6 add more tracing on cloud to workflow_as_code 2024-10-12 14:11:21 +02:00
Ruben Fiszel
685eb5d471 support skip_update 2024-10-12 13:39:59 +02:00
Ruben Fiszel
fcdf82af36 chore(main): release 1.408.0 (#4522)
* chore(main): release 1.408.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-12 04:38:44 +02:00
Ruben Fiszel
d2c4d3fa20 fix: improve workflow as code row-lock on db to handle more concurrency 2024-10-12 03:39:43 +02:00
Ruben Fiszel
e06c845ed4 cli yaml parsing error more verbose 2024-10-12 03:01:54 +02:00
Alexander Petric
f82f091290 feat(app builder): file download helper (#4511)
* feat(app builder): file download helper

* surface error via toast

* save

* handle s3 object
2024-10-12 00:39:31 +02:00
Ruben Fiszel
9f7edbaf1a make cli more reliable 2024-10-11 16:33:18 +02:00
Ruben Fiszel
2dab068fd3 nit FileDownload 2024-10-11 16:04:25 +02:00
Ruben Fiszel
7e754a1e50 improve handling of toString arg in schema 2024-10-11 15:55:02 +02:00
Ruben Fiszel
be112408e7 fix(frontend): set unused schema property fields to null 2024-10-11 15:25:32 +02:00
Ruben Fiszel
568cc66932 fix(cli): handle case where 'toString' is a schema field 2024-10-11 12:28:40 +02:00
Ruben Fiszel
0915968eba allow python multiprocessing on nsjail 2024-10-11 01:40:09 +02:00
Ruben Fiszel
982dde2b9d fix(frontend): s3 file uploader works on public apps too 2024-10-11 01:08:40 +02:00
Ruben Fiszel
7db74eecb9 add --allow-import to nsjail deno 2024-10-10 15:29:49 +02:00
Ruben Fiszel
81b0ff1ad1 chore(main): release 1.407.2 (#4518)
* chore(main): release 1.407.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-10 15:14:15 +02:00
Ruben Fiszel
b133f19333 nit deno_core 2024-10-10 15:13:55 +02:00
Ruben Fiszel
d9bdc5a5b0 fix: improve default properties of new nodes of flows (suspend, branchone, branchall) 2024-10-10 15:00:46 +02:00
Ruben Fiszel
ba17dfc841 chore(main): release 1.407.1 (#4515)
* chore(main): release 1.407.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-10 04:42:55 +02:00
Ruben Fiszel
7ca5bf2fae fix: improve handling of empty lock files on deno 2.0 2024-10-10 04:39:57 +02:00
Ruben Fiszel
cf8d443603 chore(main): release 1.407.0 (#4513)
* chore(main): release 1.407.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-10 04:24:17 +02:00
Ruben Fiszel
0e61f8db78 deno 2.0 use frozen for cache 2024-10-10 04:06:27 +02:00
Ruben Fiszel
c3a5736641 fix: update internal deno runtime to latest (deno 2.0) 2024-10-10 03:42:28 +02:00
Ruben Fiszel
26b11a0015 feat: upgrade to deno 2 2024-10-10 02:30:42 +02:00
Ruben Fiszel
7dbe49b77b improve init script assignment 2024-10-10 02:25:25 +02:00
Ruben Fiszel
2e9312216e chore(main): release 1.406.0 (#4509)
* chore(main): release 1.406.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-10-10 00:18:49 +02:00
Ruben Fiszel
7b0503332d fix: remove monaco-editor for app preview code path for faster app loads 2024-10-10 00:04:25 +02:00
Ruben Fiszel
25e417538f fix tests 2024-10-09 22:37:07 +02:00
Ruben Fiszel
6280ed3524 fix s3 bucket test 2024-10-09 21:30:28 +02:00
Ruben Fiszel
ffaf756e9a sqlx 2024-10-09 21:17:01 +02:00
Ruben Fiszel
21cd7a63ae fix no resource picker 2024-10-09 21:00:30 +02:00
Alexander Petric
d90d6c2b89 feat(monitoring): Critical Alerts for Jobs Waiting in Queue [enterprise] (#4491)
* adding the job queue monitor

* first draft of queue metrics ui

* fix svelte check complaints

* harmonizing naming

* adding concurrency_lock table

* updating ee repo ref

* Rename 20241008155800_alert_lock_table.up.sql to 20241008155800_concurrency_lock_table.up.sql

* Rename 20241008155800_alert_lock_table.down.sql to 20241008155800_concurrency_lock_table.down.sql

* updating ee.rs

* adding owner as optional column

* handling of ee/non-ee

* Update ee.rs with newline
2024-10-09 20:48:18 +02:00
Faton Ramadani
111bfc6a65 feat(frontend): components can be moved inside containers by holding ctrl/cmd
* feat(frontend): wip

* feat(frontend): wip

* feat(frontend): wip

* feat(frontend): move component inside containers

* feat(frontend): done

* feat(frontend): done

* fix(frontend): wip

* fix(frontend): wip

* fix(frontend): done

* fix(frontend): done

* fix(frontend): fix anchored

* fix(frontend): restore event

* feat(frontend): support dropping on the top level + hide the shadow when leaving the grid

* feat(frontend): drop in the correct subgrid when there are multiple

* feat(frontend): wip

* feat(frontend): fix hide shadow

* feat(frontend): remove dead code

* feat(frontend): wip

* feat(frontend): fix overflow

* feat(frontend): wip

* feat(frontend): done

* feat(frontend): fix z-index

* feat(frontend): wip

* feat(frontend): fix shadow

* feat(frontend): fix resize + drop position

* feat(frontend): fix dropped when no movement

* feat(frontend): hide the fake shadow if the move mode is set

* feat(frontend): add small timeout when we leave the insert mode

* feat(frontend): render the shadow where the component will drop

* feat(frontend): done

* feat(frontend): done

* feat(frontend): improve perf

* feat(frontend): done

* feat(frontend): remove console.log

* feat(frontend): fix move toplevel

* feat(frontend): Components positions are restored when the mode changes

* feat(frontend): Components positions are restored when the mode changes

* feat(frontend): Components positions are restored when the mode changes

* feat(frontend): fix edges cases + improve perf

* feat(frontend): fix drop position

* feat(frontend): fix final drop position

* feat(frontend): fix final drop position

* feat(frontend): fix insert from component bar

* feat(frontend): Fix disapearing components
2024-10-09 13:08:16 +02:00
Ruben Fiszel
257f0971f8 fix(cli): instance sync push does not require sync pull 2024-10-09 12:58:49 +02:00
Alexander Petric
f831b9b1fe improving pwsh error handling (#4504)
* making pwsh error handling more robust

* cargo fmt
2024-10-09 05:20:46 +02:00
Ruben Fiszel
b579db61ec allow more customisation of frontend (#4508)
* all

* npm check
2024-10-09 04:34:54 +02:00
HugoCasa
e44decb9c2 BYPASSRLS admin policy falllback (#4493)
* BYPASSRLS admin policy falllback

* use fixed list instead

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-10-08 20:08:29 +02:00
Guilhem
644dd87c50 fix icon alignment in add App button (#4496)
Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home>
2024-10-07 21:20:28 +02:00
401 changed files with 20487 additions and 6265 deletions

View File

@@ -1,22 +1,6 @@
ARG DEBIAN_IMAGE=debian:bookworm-slim
ARG RUST_IMAGE=rust:1.80-slim-bookworm
ARG PYTHON_IMAGE=python:3.11.4-slim-bookworm
FROM ${DEBIAN_IMAGE} as downloader
ARG TARGETPLATFORM
SHELL ["/bin/bash", "-c"]
RUN apt update -y
RUN apt install -y unzip curl
RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.46.3/deno-x86_64-unknown-linux-gnu.zip -o deno.zip || true
RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.46.3/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
RUN unzip deno.zip && rm deno.zip
FROM ${RUST_IMAGE} as builder
@@ -31,7 +15,7 @@ ENV SQLX_OFFLINE=true
RUN mkdir -p /frontend/build
RUN apt-get update \
&& apt-get install -y ca-certificates tzdata libpq5 cmake\
&& apt-get install -y ca-certificates tzdata libpq5 cmake unzip\
make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev \
libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libxml2-dev \
libxmlsec1-dev libffi-dev liblzma-dev mecab-ipadic-utf8 libgdbm-dev libc6-dev git libprotobuf-dev libnl-route-3-dev \
@@ -56,13 +40,14 @@ RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VER
RUN /usr/local/bin/python3 -m pip install pip-tools
COPY --from=oven/bun:1.1.27 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.1.31 /usr/local/bin/bun /usr/bin/bun
ARG TARGETPLATFORM
RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.41.0/deno-x86_64-unknown-linux-gnu.zip -o deno.zip || true
RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.41.0/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
RUN curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.2/deno-x86_64-unknown-linux-gnu.zip -o deno.zip
# RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.0/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
COPY --from=downloader --chmod=755 /deno /usr/bin/deno
RUN unzip deno.zip && rm deno.zip && mv deno /usr/bin/deno
RUN apt-get update \
&& apt-get install -y postgresql-client --allow-unauthenticated

View File

@@ -41,6 +41,10 @@ jobs:
- name: cargo test
timeout-minutes: 15
run:
/usr/bin/deno --version &&
/usr/bin/bun -v &&
go version &&
/usr/local/bin/python3 --version &&
mkdir frontend/build && cd backend && touch
windmill-api/openapi-deref.yaml &&
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill

View File

@@ -0,0 +1,128 @@
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
name: Build and publish windmill for RHEL9
on:
workflow_dispatch
permissions: write-all
jobs:
build_ee:
runs-on: ubicloud
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Read EE repo commit hash
run: |
echo "ee_repo_ref=$(cat ./backend/ee-repo-ref.txt)" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
with:
repository: windmill-labs/windmill-ee-private
path: ./windmill-ee-private
ref: ${{ env.ee_repo_ref }}
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
fetch-depth: 0
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v2
- uses: depot/setup-action@v1
- name: Docker meta
id: meta-ee-public
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-rhel9
flavor: |
latest=false
tags: |
type=sha
- name: Login to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Substitute EE code
run: |
./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
- name: Copy RHEL9 Dockerfile
run: |
cp ./docker/RHEL9/Dockerfile ./Dockerfile
- name: Build and push publicly ee amd64
uses: depot/build-push-action@v1
with:
context: .
platforms: linux/amd64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}
tags: |
${{ steps.meta-ee-public.outputs.tags }}-amd64
labels: |
${{ steps.meta-ee-public.outputs.labels }}-amd64
org.opencontainers.image.licenses=Windmill-Enterprise-License
- name: Build and push publicly ee arm64
uses: depot/build-push-action@v1
with:
context: .
platforms: linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}
tags: |
${{ steps.meta-ee-public.outputs.tags }}-arm64
labels: |
${{ steps.meta-ee-public.outputs.labels }}-arm64
org.opencontainers.image.licenses=Windmill-Enterprise-License
- uses: shrink/actions-docker-extract@v3
id: extract-ee-amd64
with:
image: ${{ steps.meta-ee-public.outputs.tags}}-amd64
path: "/windmill/target/release/windmill"
- uses: shrink/actions-docker-extract@v3
id: extract-ee-arm64
with:
image: ${{ steps.meta-ee-public.outputs.tags}}-arm64
path: "/windmill/target/release/windmill"
- name: Rename binary with corresponding architecture
run: |
mv "${{ steps.extract-ee-amd64.outputs.destination }}/windmill" "${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9"
mv "${{ steps.extract-ee-arm64.outputs.destination }}/windmill" "${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9"
- uses: actions/upload-artifact@v4
with:
name: RHEL9-amd64 build
path: ${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9
- uses: actions/upload-artifact@v4
with:
name: RHEL9-arm64 build
path: ${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9
# - name: Attach binary to release
# uses: softprops/action-gh-release@v2
# if: startsWith(github.ref, 'refs/tags/')
# with:
# files: |
# ${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9
# ${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9

View File

@@ -26,6 +26,7 @@ permissions: write-all
jobs:
build:
runs-on: ubicloud
if: (github.event_name != 'issue_comment') || (contains(github.event.comment.body, '/buildimage_all') || contains(github.event.comment.body, '/buildimage_base'))
steps:
- uses: actions/checkout@v4
with:
@@ -87,6 +88,7 @@ jobs:
build_ee:
runs-on: ubicloud
if: (github.event_name != 'issue_comment') || (contains(github.event.comment.body, '/buildimage_ee') || contains(github.event.comment.body, '/buildimage_nsjail')) || contains(github.event.comment.body, '/buildimage_all')
steps:
- uses: actions/checkout@v4
with:
@@ -148,7 +150,7 @@ jobs:
build_ee_312:
runs-on: ubicloud
if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
steps:
- uses: actions/checkout@v4
with:
@@ -244,7 +246,7 @@ jobs:
attach_amd64_binary_to_release:
needs: [build, build_ee]
runs-on: ubicloud
if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
env:
ARCH: amd64
steps:
@@ -337,10 +339,10 @@ jobs:
with:
fetch-depth: 0
- name: Prepare test run
if: ${{ ! startsWith(github.ref, 'refs/tags/') }}
if: ${{ ! startsWith(github.ref, 'refs/tags/v') }}
run: cd integration_tests && ./build.sh
- name: Test run
if: ${{ ! startsWith(github.ref, 'refs/tags/') }}
if: ${{ ! startsWith(github.ref, 'refs/tags/v') }}
timeout-minutes: 15
env:
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
@@ -356,7 +358,7 @@ jobs:
tag_latest:
runs-on: ubicloud
needs: [run_integration_test, build]
if: github.event_name != 'pull_request'
if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
steps:
- uses: actions/checkout@v4
with:
@@ -375,7 +377,7 @@ jobs:
tag_latest_ee:
runs-on: ubicloud
needs: [run_integration_test, build_ee]
if: github.event_name != 'pull_request'
if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
steps:
- uses: actions/checkout@v4
with:
@@ -394,7 +396,7 @@ jobs:
verify_ee_image_vulnerabilities:
runs-on: ubicloud
needs: [tag_latest_ee]
# if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -436,7 +438,7 @@ jobs:
build_ee_nsjail:
needs: [build_ee]
runs-on: ubicloud
if: github.event_name != 'pull_request'
if: (github.event_name != 'issue_comment') || (github.event_name != 'pull_request') || (contains(github.event.comment.body, '/buildimage_nsjail') || contains(github.event.comment.body, '/buildimage_all'))
steps:
- uses: actions/checkout@v4
with:
@@ -459,6 +461,8 @@ jobs:
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,enable=true,priority=100,prefix=,suffix=,format=short
type=ref,event=branch
type=ref,event=pr
- name: Login to registry
uses: docker/login-action@v3
@@ -476,7 +480,6 @@ jobs:
file: "./docker/DockerfileNsjail"
tags: |
${{ steps.meta-ee-public.outputs.tags }}
ghcr.io/windmill-labs/windmill-ee-nsjail:main
labels: |
${{ steps.meta-ee-public.outputs.labels }}
org.opencontainers.image.licenses=Windmill-Enterprise-License
@@ -570,7 +573,7 @@ jobs:
bucket-region: us-east-1
build_ee_cuda:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
needs: [build_ee]
runs-on: ubicloud
steps:
@@ -589,8 +592,6 @@ jobs:
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-cuda
flavor: |
latest=false
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -616,7 +617,7 @@ jobs:
org.opencontainers.image.licenses=Windmill-Enterprise-License
build_slim:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
needs: [build]
runs-on: ubicloud
steps:
@@ -635,8 +636,6 @@ jobs:
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-slim
flavor: |
latest=false
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -661,7 +660,7 @@ jobs:
${{ steps.meta-ee-public.outputs.labels }}
build_ee_slim:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
needs: [build_ee]
runs-on: ubicloud
steps:
@@ -680,8 +679,6 @@ jobs:
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-slim
flavor: |
latest=false
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -707,7 +704,7 @@ jobs:
org.opencontainers.image.licenses=Windmill-Enterprise-License
build_full:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
needs: [build]
runs-on: ubicloud
steps:
@@ -726,8 +723,6 @@ jobs:
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-full
flavor: |
latest=false
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -752,7 +747,7 @@ jobs:
${{ steps.meta-public.outputs.labels }}
build_ee_full:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
needs: [build_ee]
runs-on: ubicloud
steps:
@@ -771,8 +766,6 @@ jobs:
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-full
flavor: |
latest=false
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}

View File

@@ -1,5 +1,268 @@
# Changelog
## [1.415.2](https://github.com/windmill-labs/windmill/compare/v1.415.1...v1.415.2) (2024-11-01)
### Bug Fixes
* **s3:** align s3 handler additional creds providers ([984c6dd](https://github.com/windmill-labs/windmill/commit/984c6dd10c63097eb195883c4d8a9681ab1b49e0))
## [1.415.1](https://github.com/windmill-labs/windmill/compare/v1.415.0...v1.415.1) (2024-10-31)
### Bug Fixes
* **cli:** improve --instance handling wmill instance push ([cb005a1](https://github.com/windmill-labs/windmill/commit/cb005a15baef4272bc58c7e80a43e44723556d31))
## [1.415.0](https://github.com/windmill-labs/windmill/compare/v1.414.2...v1.415.0) (2024-10-31)
### Features
* **cli:** opts.instance as instace name and prefix ([#4609](https://github.com/windmill-labs/windmill/issues/4609)) ([a07f57e](https://github.com/windmill-labs/windmill/commit/a07f57e698107056d045d8d5c2458e04c809fcc8))
### Bug Fixes
* improve express oauth setup ([ba4aed5](https://github.com/windmill-labs/windmill/commit/ba4aed5bf51c65204332cfc158d0ffd9c7095ec7))
* improve user resource input ([8c7f53b](https://github.com/windmill-labs/windmill/commit/8c7f53b2ebe0990cd93879258d004ac89dc8b24c))
## [1.414.2](https://github.com/windmill-labs/windmill/compare/v1.414.1...v1.414.2) (2024-10-29)
### Bug Fixes
* **cli:** improve instance sync for CI/CD + --folder-per-instance ([212579a](https://github.com/windmill-labs/windmill/commit/212579a514d070355fe0d9e0215593bacfa05e1f))
## [1.414.1](https://github.com/windmill-labs/windmill/compare/v1.414.0...v1.414.1) (2024-10-29)
### Bug Fixes
* **apps:** enable text selection on aggrid tables by default ([b0b9180](https://github.com/windmill-labs/windmill/commit/b0b9180fb907c92b95a48ff286eb1dae59bb4981))
* **apps:** public apps can take full height ([703db7d](https://github.com/windmill-labs/windmill/commit/703db7d4412795b4323a2eefcc39ae3cf43bc748))
* **bun:** handle bun lockfile created with windows ([#4602](https://github.com/windmill-labs/windmill/issues/4602)) ([dcf5e2f](https://github.com/windmill-labs/windmill/commit/dcf5e2f03f977e241f6785530dad61a70c5bdd79))
* **frontend:** make script and schema scrollable on script detail page ([6e222b3](https://github.com/windmill-labs/windmill/commit/6e222b3b1a419e5543fdf35420a7413699688b41))
* **frontend:** new approval steps default to timeout 1800 ([b86de62](https://github.com/windmill-labs/windmill/commit/b86de6280e03e014e8ddf85b2b5f8fd030d0467a))
## [1.414.0](https://github.com/windmill-labs/windmill/compare/v1.413.2...v1.414.0) (2024-10-29)
* Issue with previous release, re-releasing
## [1.413.2](https://github.com/windmill-labs/windmill/compare/v1.413.1...v1.413.2) (2024-10-29)
### Bug Fixes
* **backend:** in flows, workspace scripts should use their set tags instead of the default one ([5b7c6d7](https://github.com/windmill-labs/windmill/commit/5b7c6d7d62dcfd09fec374e781bdf5c5bafe4a9d))
* **cli:** fix wmill instance pull --instance ([3c62f5e](https://github.com/windmill-labs/windmill/commit/3c62f5ea83d1da8bd3705468969d56e2fe680751))
* **frontend:** fix script and flow renaming ([d743e00](https://github.com/windmill-labs/windmill/commit/d743e0056353a4fca445a7089e3afc1fd4e8c219))
## [1.413.1](https://github.com/windmill-labs/windmill/compare/v1.413.0...v1.413.1) (2024-10-28)
### Bug Fixes
* **cli:** fix wmill instance push --base-url and --instance ([8298710](https://github.com/windmill-labs/windmill/commit/82987105a6fd6ec272c170fb094453a0267143be))
## [1.413.0](https://github.com/windmill-labs/windmill/compare/v1.412.0...v1.413.0) (2024-10-28)
### Features
* autoscaling v0 ([#4593](https://github.com/windmill-labs/windmill/issues/4593)) ([fe7d044](https://github.com/windmill-labs/windmill/commit/fe7d044a66e8ec223a337cb704d3e58942dd1502))
### Bug Fixes
* add run immediately popover to run again ([e54d253](https://github.com/windmill-labs/windmill/commit/e54d25368541dc6109a0f99022a120d28455f9bd))
* **docs:** smtp setup documentation link ([#4590](https://github.com/windmill-labs/windmill/issues/4590)) ([bac3205](https://github.com/windmill-labs/windmill/commit/bac32057259d893140d649c9dfec2ca75e395ad4))
## [1.412.0](https://github.com/windmill-labs/windmill/compare/v1.411.1...v1.412.0) (2024-10-25)
### Features
* add Spotify oauth provider ([#4581](https://github.com/windmill-labs/windmill/issues/4581)) ([a46aa64](https://github.com/windmill-labs/windmill/commit/a46aa644b096e71e75b59507224ed92f7d2f99ba))
### Bug Fixes
* **app builder:** date input default value improvements ([9f43d5d](https://github.com/windmill-labs/windmill/commit/9f43d5dcd92ddcd0c0baeaa5271779520af652f4))
* **bash:** correctly propagate sigterm for cancelled bash scripts ([134cfdb](https://github.com/windmill-labs/windmill/commit/134cfdb30eb8d29c2ecb1b78b8fabae2b6e10700))
* do not update created_at of scripts on lockfile generation ([d1a28eb](https://github.com/windmill-labs/windmill/commit/d1a28eb7cac5f465e2b07f870a507b0cc5cc722a))
* initialize empty smtp settings correctly ([84e0524](https://github.com/windmill-labs/windmill/commit/84e05249505c3d2f7eb7a4917f6bca503df81017))
## [1.411.1](https://github.com/windmill-labs/windmill/compare/v1.411.0...v1.411.1) (2024-10-22)
### Bug Fixes
* update bun to 1.1.32 ([#4568](https://github.com/windmill-labs/windmill/issues/4568)) ([0586446](https://github.com/windmill-labs/windmill/commit/058644667129f0d79ec147aacdda449142ae0ab9))
## [1.411.0](https://github.com/windmill-labs/windmill/compare/v1.410.3...v1.411.0) (2024-10-21)
### Features
* **cli:** encrypt sensitive instance settings ([#4561](https://github.com/windmill-labs/windmill/issues/4561)) ([b8a6a11](https://github.com/windmill-labs/windmill/commit/b8a6a116354b10f5977e54edb365d6711e160538))
### Bug Fixes
* Do not ignore file resources with json file ext ([#4562](https://github.com/windmill-labs/windmill/issues/4562)) ([2079b2e](https://github.com/windmill-labs/windmill/commit/2079b2e7e19aa2fe327f2ae66d1b5eba988b9b0a))
* update bun to 1.1.31 and deno to 2.0.2 ([0d90396](https://github.com/windmill-labs/windmill/commit/0d9039641b3348e75599937188c35dd89a000584))
## [1.410.3](https://github.com/windmill-labs/windmill/compare/v1.410.2...v1.410.3) (2024-10-20)
### Bug Fixes
* **go-client:** reduce runtime dependencies by bumping oai-codeen to v2.4.1 ([87f5c07](https://github.com/windmill-labs/windmill/commit/87f5c078dd63cd3e0ba3e719c2da7b83f1458e2c))
## [1.410.1](https://github.com/windmill-labs/windmill/compare/v1.410.0...v1.410.1) (2024-10-19)
### Bug Fixes
* **cli:** improve wmill init behavior ([26a40d1](https://github.com/windmill-labs/windmill/commit/26a40d19441aa816ee711ce30f3435dddd3542a7))
* **frontend:** improve display of error handlers ([a92a2fd](https://github.com/windmill-labs/windmill/commit/a92a2fd6fd67c11c04554a90b8ae6d7a0dd9067c))
## [1.410.1](https://github.com/windmill-labs/windmill/compare/v1.410.0...v1.410.1) (2024-10-19)
### Bug Fixes
* **cli:** improve wmill init behavior ([26a40d1](https://github.com/windmill-labs/windmill/commit/26a40d19441aa816ee711ce30f3435dddd3542a7))
## [1.410.0](https://github.com/windmill-labs/windmill/compare/v1.409.4...v1.410.0) (2024-10-18)
### Features
* **typescript-bun:** support relative imports without the .ts extension ([248fdc2](https://github.com/windmill-labs/windmill/commit/248fdc24a61aca946902b20b9d8187101a9b7bfa))
* websocket triggers ([#4505](https://github.com/windmill-labs/windmill/issues/4505)) ([8807e99](https://github.com/windmill-labs/windmill/commit/8807e99f06caf3e26c526eefdc83dbc2f7aa93ee))
### Bug Fixes
* cache js static assets by default ([08595c6](https://github.com/windmill-labs/windmill/commit/08595c6f14a89df1559974e8835e63881a1a9601))
* **frontend:** add back script lockfile to script details pae ([549b11d](https://github.com/windmill-labs/windmill/commit/549b11dcfb39a60acd6d3899fce51b31149f8594))
* improve cancelling of jobs on public apps for anonymous users ([d7cf5ea](https://github.com/windmill-labs/windmill/commit/d7cf5ea37db313e78865708bc22e87a1feea9e0d))
## [1.409.4](https://github.com/windmill-labs/windmill/compare/v1.409.3...v1.409.4) (2024-10-17)
### Bug Fixes
* fix flow viewer renderer outside of flow details and editor context ([59a1e67](https://github.com/windmill-labs/windmill/commit/59a1e67465cd21ded5539cb5b829bc0d4e7169ed))
## [1.409.3](https://github.com/windmill-labs/windmill/compare/v1.409.2...v1.409.3) (2024-10-17)
### Bug Fixes
* do not delete primary schedule of script/flow on redeploy even if schedule wasn't loaded ([c3c2fe4](https://github.com/windmill-labs/windmill/commit/c3c2fe462c52f48795a55025414b16f8d3d98fe0))
* **nsjail:** improve memory reading when using nsjail ([b7ad19b](https://github.com/windmill-labs/windmill/commit/b7ad19bb75bd885a254ebac778349c7d88af8326))
## [1.409.2](https://github.com/windmill-labs/windmill/compare/v1.409.1...v1.409.2) (2024-10-16)
### Bug Fixes
* add extra args support for exception to bun scripts ([1466da3](https://github.com/windmill-labs/windmill/commit/1466da3999add0238b9c42ca13df52194f082fc0))
* fix script persistence in url + add support for extra error args in python ([3174024](https://github.com/windmill-labs/windmill/commit/3174024d8e6ecbe9f8c9e1ea055d611f652c0057))
## [1.409.1](https://github.com/windmill-labs/windmill/compare/v1.409.0...v1.409.1) (2024-10-16)
### Bug Fixes
* **apidocs:** fix generated openapi files ([d24e153](https://github.com/windmill-labs/windmill/commit/d24e1530655d27ffda9bb4c19471dc1431124cc2))
* **git-sync:** propagate update of folders with git sync ([6abb346](https://github.com/windmill-labs/windmill/commit/6abb346013da4a907a860713a8a67642985b8025))
## [1.409.0](https://github.com/windmill-labs/windmill/compare/v1.408.1...v1.409.0) (2024-10-16)
### Features
* **frontend:** unify all triggers UX and simplify flow settings ([#4259](https://github.com/windmill-labs/windmill/issues/4259)) ([91a3d06](https://github.com/windmill-labs/windmill/commit/91a3d065298cce7a882464fa0cd31d8f1ae9dda2))
* Scroll to element in virtual list when clicking on graph point ([#4532](https://github.com/windmill-labs/windmill/issues/4532)) ([7126ba1](https://github.com/windmill-labs/windmill/commit/7126ba12c7eb52d2cfbe8d83311b5592a5707bce))
* **sso:** adding the ability to define a custom display name for sso ([#4529](https://github.com/windmill-labs/windmill/issues/4529)) ([99c5b3e](https://github.com/windmill-labs/windmill/commit/99c5b3ecdacb1158c2cba5b891c4c3b8b70c3b6a))
### Bug Fixes
* Add indexer backup lock to fit the deployment model ([#4531](https://github.com/windmill-labs/windmill/issues/4531)) ([411bce7](https://github.com/windmill-labs/windmill/commit/411bce7e13aabfa53db80d55261ea4511f6d1ae9))
* **app:** accept connecting to non yet existing state output for convenience ([9eb1ecc](https://github.com/windmill-labs/windmill/commit/9eb1ecc9f3017e2f4284a827a2bcd21f36b1b8ac))
* **app:** improve absolute url handling in download button and downloadFile ([dcdbf1a](https://github.com/windmill-labs/windmill/commit/dcdbf1afb4d5a18e00b9bbb1eb0bef129ea5667f))
* **app:** make s3 uploads persistent across tabs change ([c3b536b](https://github.com/windmill-labs/windmill/commit/c3b536b1b8069898131768867a187b186b21e537))
* canceled jobs button reporting 0 jobs cancelled ([#4534](https://github.com/windmill-labs/windmill/issues/4534)) ([e736572](https://github.com/windmill-labs/windmill/commit/e736572db10929ae5e123c4f6cef73b8e90fc29b))
* **python-client:** improve get_job_status for running jobs ([a8c4ea2](https://github.com/windmill-labs/windmill/commit/a8c4ea2334d2535fa7d5d43f58d65565afe8f3e5))
* **ui:** dark mode support for queue metrics based critical alert ([#4535](https://github.com/windmill-labs/windmill/issues/4535)) ([f38b3d1](https://github.com/windmill-labs/windmill/commit/f38b3d14e8092ae58817511aea91a4e77725ead6))
## [1.408.1](https://github.com/windmill-labs/windmill/compare/v1.408.0...v1.408.1) (2024-10-12)
### Bug Fixes
* fix deno cache --allow-import on deno 2 ([42fe31f](https://github.com/windmill-labs/windmill/commit/42fe31f804c9e6643cd90167494e45270831e013))
## [1.408.0](https://github.com/windmill-labs/windmill/compare/v1.407.2...v1.408.0) (2024-10-12)
### Features
* **app builder:** file download helper ([#4511](https://github.com/windmill-labs/windmill/issues/4511)) ([f82f091](https://github.com/windmill-labs/windmill/commit/f82f09129096cfff975370d8bb7b6d832a2b8f9f))
### Bug Fixes
* **cli:** handle case where 'toString' is a schema field ([568cc66](https://github.com/windmill-labs/windmill/commit/568cc66932fb0470f5e89de7b02d94dba4050638))
* **frontend:** s3 file uploader works on public apps too ([982dde2](https://github.com/windmill-labs/windmill/commit/982dde2b9dfe6d9eda300c683af729d97a03cb4d))
* **frontend:** set unused schema property fields to null ([be11240](https://github.com/windmill-labs/windmill/commit/be112408e7c4601314726e9517c37daaeaa1bf09))
* improve workflow as code row-lock on db to handle more concurrency ([d2c4d3f](https://github.com/windmill-labs/windmill/commit/d2c4d3fa207379cb0b8ac180f6ccc759045580e8))
## [1.407.2](https://github.com/windmill-labs/windmill/compare/v1.407.1...v1.407.2) (2024-10-10)
### Bug Fixes
* improve default properties of new nodes of flows (suspend, branchone, branchall) ([d9bdc5a](https://github.com/windmill-labs/windmill/commit/d9bdc5a5b08dd4d0381304656af097315398c9d4))
## [1.407.1](https://github.com/windmill-labs/windmill/compare/v1.407.0...v1.407.1) (2024-10-10)
### Bug Fixes
* improve handling of empty lock files on deno 2.0 ([7ca5bf2](https://github.com/windmill-labs/windmill/commit/7ca5bf2faeff44a7543b1afa9369c140fcb71dfc))
## [1.407.0](https://github.com/windmill-labs/windmill/compare/v1.406.0...v1.407.0) (2024-10-10)
### Features
* upgrade to deno 2 ([26b11a0](https://github.com/windmill-labs/windmill/commit/26b11a00150acbe101abe4bb542f24379da0cc56))
### Bug Fixes
* update internal deno runtime to latest (deno 2.0) ([c3a5736](https://github.com/windmill-labs/windmill/commit/c3a57366419882ea2de1938bea592c795b1a1d03))
## [1.406.0](https://github.com/windmill-labs/windmill/compare/v1.405.5...v1.406.0) (2024-10-09)
### Features
* **frontend:** components can be moved inside containers by holding ctrl/cmd ([111bfc6](https://github.com/windmill-labs/windmill/commit/111bfc6a659037ae7029e8f557256e2fffcf979b))
* **monitoring:** Critical Alerts for Jobs Waiting in Queue [enterprise] ([#4491](https://github.com/windmill-labs/windmill/issues/4491)) ([d90d6c2](https://github.com/windmill-labs/windmill/commit/d90d6c2b896c5f99e00681656f376b180901f272))
### Bug Fixes
* **cli:** instance sync push does not require sync pull ([257f097](https://github.com/windmill-labs/windmill/commit/257f0971f86938da71b879f32d93473976eaa920))
* remove monaco-editor for app preview code path for faster app loads ([7b05033](https://github.com/windmill-labs/windmill/commit/7b0503332d1bdd7f5999a5ef99150f8c9f6f18be))
## [1.405.5](https://github.com/windmill-labs/windmill/compare/v1.405.4...v1.405.5) (2024-10-04)

View File

@@ -162,7 +162,7 @@ ENV GO_PATH=/usr/local/go/bin/go
RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.4.18/uv-installer.sh | sh && mv /root/.cargo/bin/uv /usr/local/bin/uv
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get -y update && apt-get install -y curl nodejs awscli && apt-get clean \
RUN apt-get -y update && apt-get install -y curl procps nodejs awscli && apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# go build is slower the first time it is ran, so we prewarm it in the build
@@ -175,9 +175,9 @@ RUN /usr/local/bin/python3 -m pip install pip-tools
COPY --from=builder /frontend/build /static_frontend
COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
COPY --from=denoland/deno:1.46.3 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=denoland/deno:2.0.2 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=oven/bun:1.1.27 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.1.32 /usr/local/bin/bun /usr/bin/bun
COPY --from=php:8.3.7-cli /usr/local/bin/php /usr/bin/php
COPY --from=composer:2.7.6 /usr/bin/composer /usr/bin/composer

View File

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

View File

@@ -1,26 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT tag, count(*) as count FROM queue WHERE\n scheduled_for <= now() - ('3 seconds')::interval AND running = false\n GROUP BY tag",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "tag",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
null
]
},
"hash": "02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM healthchecks WHERE check_type = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a"
}

View File

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

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO concurrency_locks (id, last_locked_at, owner)\n VALUES ($1, now(), $2)\n ON CONFLICT (id)\n DO UPDATE SET\n last_locked_at = now(),\n owner = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d"
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT email, login_type::text, verified, super_admin, name, company, username from password ORDER BY super_admin DESC, email LIMIT $1 OFFSET $2",
"query": "SELECT email, login_type::text, verified, super_admin, name, company, username, NULL::bool as operator_only FROM password ORDER BY super_admin DESC, email LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
@@ -37,6 +37,11 @@
"ordinal": 6,
"name": "username",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "operator_only",
"type_info": "Bool"
}
],
"parameters": {
@@ -52,8 +57,9 @@
false,
true,
true,
true
true,
null
]
},
"hash": "c2060e8cacef6c3b5ce51ed203a2dbafc18d66f2924d1fe518c6728997647db2"
"hash": "1a4d291c2f239f7b50c116594cebb031862e1a18ad9204e02a0194817db26d6a"
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM schedule WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool",
"Text"
]
},
"nullable": [
null
]
},
"hash": "1ca5bc2d35c0498b587fd0618434def64233dc4f8fc3344d8d74be8e96ded659"
}

View File

@@ -0,0 +1,28 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO autoscaling_event (worker_group, event_type, desired_workers, reason) VALUES ($1, $2, $3, $4)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
{
"Custom": {
"name": "autoscaling_event_type",
"kind": {
"Enum": [
"full_scaleout",
"scalein",
"scaleout"
]
}
}
},
"Int4",
"Text"
]
},
"nullable": []
},
"hash": "1f5f0858909eb5bac63c4e3b1add95226bd94ca3facb92a620ffa59dacad6705"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM healthchecks WHERE healthy = true AND created_at < NOW() - INTERVAL '14 days'",
"describe": {
"columns": [],
"parameters": {
"Left": []
},
"nullable": []
},
"hash": "2041526bc58872d71f91f7698144039bd67f8e37895befa94a15b7e4019e114b"
}

View File

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

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO healthchecks (check_type, healthy) VALUES ($1, false)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "27920aaa55666ffc14a36a247f89ff7994ee40d3953b9f772d0e0ab999bccb7b"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT email, login_type::TEXT, super_admin, verified, name, company, username FROM password WHERE email = $1",
"query": "SELECT email, login_type::TEXT, super_admin, verified, name, company, username, NULL::bool as operator_only FROM password WHERE email = $1",
"describe": {
"columns": [
{
@@ -37,6 +37,11 @@
"ordinal": 6,
"name": "username",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "operator_only",
"type_info": "Bool"
}
],
"parameters": {
@@ -51,8 +56,9 @@
false,
true,
true,
true
true,
null
]
},
"hash": "8813665f8adfcab0daefbac2cc6b50e427dfd1c12d12895451affd307dc59c37"
"hash": "2c14d3a88193f16ad3b8cd590749cb5537995f2499f6cb8f0f316fb62902d542"
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM websocket_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool",
"Text"
]
},
"nullable": [
null
]
},
"hash": "2e6165543e34216dfaedf6e10729f733cff812e62dea1c1bce8cefd3a3979b14"
}

View File

@@ -0,0 +1,65 @@
{
"db_name": "PostgreSQL",
"query": "WITH active_users AS (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login')),\n authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)\n SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, name, company, username\n FROM password\n WHERE email IN (SELECT email FROM active_users)\n ORDER BY super_admin DESC\n LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "operator_only",
"type_info": "Bool"
},
{
"ordinal": 2,
"name": "login_type",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "verified",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "super_admin",
"type_info": "Bool"
},
{
"ordinal": 5,
"name": "name",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "company",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "username",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8",
"Int8"
]
},
"nullable": [
false,
null,
null,
false,
false,
true,
true,
true
]
},
"hash": "2eec077cc9e27d7ccd160cbaac118c321c422705f79e69550bb60f377083bcef"
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM http_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool",
"Text"
]
},
"nullable": [
null
]
},
"hash": "31bc3dcea29be9cc0242771d25a232f173446d29c08fc29ddb8d55294f2c070e"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT created_at FROM metrics WHERE id = 'license_key_renewal' ORDER BY created_at DESC LIMIT 1",
"query": "SELECT created_at FROM healthchecks WHERE check_type = $1 ORDER BY created_at DESC LIMIT 1",
"describe": {
"columns": [
{
@@ -10,11 +10,13 @@
}
],
"parameters": {
"Left": []
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "9ed77e78e6295c62745ac3ac3b7e5f544f654d7cddc13acb5d3f4fdc12a8875f"
"hash": "34a45763bb4d14162f4cd3fa07cd8020f1f6085f4ee85f5eab3458637edf26cd"
}

View File

@@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT COUNT(*) as count, \n MIN(scheduled_for) as oldest_job\n FROM queue \n WHERE tag = $1 \n AND scheduled_for <= NOW() - $2::interval \n AND running = false\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "oldest_job",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Text",
"Interval"
]
},
"nullable": [
null,
null
]
},
"hash": "3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30"
}

View File

@@ -0,0 +1,108 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO websocket_trigger (workspace_id, path, url, script_path, is_flow, enabled, filters, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, now()) RETURNING *",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "url",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 9,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 10,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 11,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "enabled",
"type_info": "Bool"
},
{
"ordinal": 13,
"name": "filters",
"type_info": "JsonbArray"
}
],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Varchar",
"Bool",
"Bool",
"JsonbArray",
"Varchar",
"Varchar"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false,
false
]
},
"hash": "4e9668a46bad9e82baa51422946d373b18b6577198df7545c94bd19be3446775"
}

View File

@@ -0,0 +1,39 @@
{
"db_name": "PostgreSQL",
"query": "SELECT event_type::AUTOSCALING_EVENT_TYPE AS \"event_type: _\", EXTRACT(EPOCH FROM (NOW() - applied_at))::int as seconds_ago FROM autoscaling_event WHERE worker_group = $1 ORDER BY applied_at DESC LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "event_type: _",
"type_info": {
"Custom": {
"name": "autoscaling_event_type",
"kind": {
"Enum": [
"full_scaleout",
"scalein",
"scaleout"
]
}
}
}
},
{
"ordinal": 1,
"name": "seconds_ago",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
null
]
},
"hash": "4eca060026a0cb19c5794cd56ace89fc04765191f251945d14bbe78718714f6e"
}

View File

@@ -0,0 +1,98 @@
{
"db_name": "PostgreSQL",
"query": "SELECT *\n FROM websocket_trigger\n WHERE enabled IS TRUE AND (server_id IS NULL OR last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "url",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 9,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 10,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 11,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "enabled",
"type_info": "Bool"
},
{
"ordinal": 13,
"name": "filters",
"type_info": "JsonbArray"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false,
false
]
},
"hash": "5303cb9dd5903aa4791ef8e5e5881a50a832e65c8c9632e2e12cd9c2747f2fc7"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE concurrency_locks SET\n last_locked_at = now()\n WHERE id = $1 AND owner = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": []
},
"hash": "57e270e032e8c04dda7b5c1ca949861756b3ad367a4a500728332a7cb91560a4"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:' || $2]::text[]",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "5fc6b4a4dbb7875bdec76f876c18543435a95b019b20081f52f6ed6f4457e3c7"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT owner FROM concurrency_locks WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "owner",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
true
]
},
"hash": "5fd70c70ce52cbc51fa9124cb05f82b5951f17d1b7eade53c6d89253d55f8b9f"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT pg_try_advisory_xact_lock($1)",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "pg_try_advisory_xact_lock",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
null
]
},
"hash": "6776dc50f184188756ad7fe263b0304333536768527525a43bdd45aedffa3c4f"
}

View File

@@ -0,0 +1,52 @@
{
"db_name": "PostgreSQL",
"query": "SELECT id, worker_group, event_type::text, desired_workers, reason, applied_at FROM autoscaling_event WHERE worker_group = $1 ORDER BY applied_at DESC LIMIT 5",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "worker_group",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "event_type",
"type_info": "Text"
},
{
"ordinal": 3,
"name": "desired_workers",
"type_info": "Int4"
},
{
"ordinal": 4,
"name": "reason",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "applied_at",
"type_info": "Timestamp"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
null,
false,
true,
false
]
},
"hash": "6d134b137ae81534e145fc5b6474cf963ee26a3ad3a0a3d8dc064cb14c8fd9a6"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO windmill_migrations (name) VALUES ('bypassrls_1-2')",
"describe": {
"columns": [],
"parameters": {
"Left": []
},
"nullable": []
},
"hash": "722a3096f03d25ef94292d53801d41037de4bc69dd434232029c731cbbcbc22f"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO concurrency_locks (id, last_locked_at) VALUES ($1, NOW()) ON CONFLICT (id) DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "900ac59515e4283f4b57516210575dfe92f74a7220ed69e61899a6e0f053d9cd"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "a7f5431e3b8960e9dc46fae69dd4391516d8b169186548ec44528c84078b80d8"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE websocket_trigger SET url = $1, script_path = $2, path = $3, is_flow = $4, filters = $5, edited_by = $6, email = $7, edited_at = now(), server_id = NULL, last_server_ping = NULL, error = NULL\n WHERE workspace_id = $8 AND path = $9",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Bool",
"JsonbArray",
"Varchar",
"Varchar",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "acbf74cf3302bfcf7615285070d3f8958932bb8a2dda715f1b9152ab44442780"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE healthchecks SET healthy = true WHERE check_type = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "ad42118ccf6a9d2d1e072c4df064ddf964a5b3cd088fc162d0d8222325d4a5ea"
}

View File

@@ -18,8 +18,8 @@
"Left": []
},
"nullable": [
true,
false
false,
true
]
},
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"

View File

@@ -52,7 +52,8 @@
"trigger",
"failure",
"command",
"approval"
"approval",
"preprocessor"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT schedule FROM schedule WHERE path = $1 AND script_path = $1 AND is_flow = $2 AND workspace_id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "schedule",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text",
"Bool",
"Text"
]
},
"nullable": [
false
]
},
"hash": "c060b8bbc5af7d2e7d0aaff64f0f62ec9db58611a99b0ba7f0375638b128ab89"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE concurrency_locks SET last_locked_at = NOW() WHERE id = $1 AND last_locked_at < NOW() - INTERVAL '1 second' * $2 RETURNING 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text",
"Float8"
]
},
"nullable": [
null
]
},
"hash": "c4e1873bfc7b905e7299a021f4baa2a97e95f4797c5e11f37822e19828422b7e"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin, scopes)\n VALUES ($1, $2, $3, $4, $5, $6)",
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin, scopes, workspace_id)\n VALUES ($1, $2, $3, $4, $5, $6, $7)",
"describe": {
"columns": [],
"parameters": {
@@ -10,10 +10,11 @@
"Varchar",
"Timestamptz",
"Bool",
"TextArray"
"TextArray",
"Varchar"
]
},
"nullable": []
},
"hash": "34ad8a2a5bd89b9b8e25847a7e5e94ef99e35a178ad6328c1bcde2a6d6f88cb5"
"hash": "c624f15f3e321b1eecf123da9bf0b18e8c1d16ef25ffb9d04e5447d0d583d55c"
}

View File

@@ -0,0 +1,59 @@
{
"db_name": "PostgreSQL",
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "label",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "token_prefix",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "expiration",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "created_at",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "last_used_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "scopes",
"type_info": "TextArray"
},
{
"ordinal": 6,
"name": "email",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true,
null,
true,
false,
false,
true,
true
]
},
"hash": "c7ee7ce64686cef41cebd99ad7ef31572fc1bf12e6ae473fd58fafb025989965"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT last_locked_at\n FROM concurrency_locks\n WHERE id = $1\n FOR UPDATE",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "last_locked_at",
"type_info": "Timestamp"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "cecf1addc4aecb087a14786b2a9165895ca61ef042947c7314f66514d7f29edc"
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE script SET lock = $1, created_at = now() WHERE hash = $2 AND workspace_id = $3",
"query": "UPDATE script SET lock = $1 WHERE hash = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
@@ -12,5 +12,5 @@
},
"nullable": []
},
"hash": "ec47955683d811b12e82ff3e6aeafa0df46a320c436bad5eb1acc127df138a61"
"hash": "d697b7311430e7bd5375ec5494179f4071e3bfe123d9600249cfa80c1103edd8"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "d7a0f19f9e18d2ea49316012375ad78b69292ba091d69880945e42bebe890d66"
}

View File

@@ -0,0 +1,28 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) as \"websocket_used!\", EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) as \"http_routes_used!\"",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "websocket_used!",
"type_info": "Bool"
},
{
"ordinal": 1,
"name": "http_routes_used!",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
null,
null
]
},
"hash": "db24f1f6ef1e26b7de7926bd3f32d1fa673c4970c25ff9ad98f0fdf199801b53"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = 'bypassrls_1-2')",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": []
},
"nullable": [
null
]
},
"hash": "eb1f916f9beea3eea83ce359f5305d0cfb0d6cdba9cc56c6139f57e46345f843"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1)",
"query": "SELECT EXISTS(SELECT 1 FROM healthchecks WHERE check_type = $1 AND healthy = false)",
"describe": {
"columns": [
{
@@ -18,5 +18,5 @@
null
]
},
"hash": "9ebb9c16948a695a053068d9a1df0152691271ac33d8c82db47be11746ccbcef"
"hash": "eb932b613a6dbb2cdff97e5512d42b538ba83115c0ea798be00b01659600f45a"
}

View File

@@ -0,0 +1,59 @@
{
"db_name": "PostgreSQL",
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "label",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "token_prefix",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "expiration",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "created_at",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "last_used_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "scopes",
"type_info": "TextArray"
},
{
"ordinal": 6,
"name": "email",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true,
null,
true,
false,
false,
true,
true
]
},
"hash": "eff32aeac25a75d06f73e08c26dd3fd25f6b85cbea870505751c6a82457ae1da"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "f06e0e4fa358b26792df22fff48b71a6fcfa1e5603ea472892917c1accd1aafb"
}

View File

@@ -0,0 +1,101 @@
{
"db_name": "PostgreSQL",
"query": "SELECT *\n FROM websocket_trigger\n WHERE workspace_id = $1 AND path = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "url",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 9,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 10,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 11,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "enabled",
"type_info": "Bool"
},
{
"ordinal": 13,
"name": "filters",
"type_info": "JsonbArray"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false,
false
]
},
"hash": "f2baee15e6d1fecd6d2d7b39fda1b50a15ec8bd349f1081af54da5dc2f5e3021"
}

View File

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

1105
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.405.5"
version = "1.415.2"
authors.workspace = true
edition.workspace = true
@@ -13,7 +13,9 @@ members = [
"./windmill-common",
"./windmill-audit",
"./windmill-git-sync",
"./windmill-autoscaling",
"./windmill-indexer",
"./windmill-macros",
"./parsers/windmill-parser",
"./parsers/windmill-parser-ts",
"./parsers/windmill-parser-wasm",
@@ -23,11 +25,11 @@ members = [
"./parsers/windmill-parser-py",
"./parsers/windmill-parser-py-imports",
"./parsers/windmill-sql-datatype-parser-wasm",
"./parsers/windmill-parser-yaml",
"./parsers/windmill-parser-yaml", "windmill-macros",
]
[workspace.package]
version = "1.405.5"
version = "1.415.2"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -39,9 +41,12 @@ path = "./src/main.rs"
opt-level = 0
incremental = true
[profile.release]
lto = "thin"
[features]
default = []
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise", "windmill-indexer/enterprise"]
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", "windmill-indexer/enterprise"]
enterprise_saml = ["windmill-api/enterprise_saml"]
stripe = ["windmill-api/stripe"]
benchmark = ["windmill-api/benchmark", "windmill-worker/benchmark", "windmill-queue/benchmark", "windmill-common/benchmark"]
@@ -68,6 +73,7 @@ windmill-git-sync.workspace = true
windmill-api = { workspace = true, default-features = false }
windmill-worker.workspace = true
windmill-indexer = { workspace = true, optional = true }
windmill-autoscaling = { workspace = true, optional = true }
futures.workspace = true
tracing.workspace = true
sqlx.workspace = true
@@ -112,7 +118,9 @@ windmill-worker = { path = "./windmill-worker" }
windmill-common = { path = "./windmill-common", default-features = false }
windmill-audit = { path = "./windmill-audit" }
windmill-git-sync = { path = "./windmill-git-sync" }
windmill-autoscaling = { path = "./windmill-autoscaling" }
windmill-indexer = {path = "./windmill-indexer"}
windmill-macros = {path = "./windmill-macros"}
windmill-parser = { path = "./parsers/windmill-parser" }
windmill-parser-ts = { path = "./parsers/windmill-parser-ts" }
windmill-parser-py = { path = "./parsers/windmill-parser-py" }
@@ -172,20 +180,24 @@ tokio-util = { version = "^0", features = ["io"] }
json-pointer = "^0"
itertools = "^0"
regex = "^1"
deno_fetch = "0.187.0"
deno_tls = "0.150.0"
deno_console = "0.163.0"
deno_url = "0.163.0"
deno_webidl = "0.163.0"
deno_web = "0.194.0"
deno_net = "0.155.0"
deno_core = "0.299.0"
deno_ast = { version = "=0.40.0", features = ["transpiling"] }
deno_fetch = "0.195.0"
deno_tls = "0.158.0"
deno_console = "0.171.0"
deno_url = "0.171.0"
deno_webidl = "0.171.0"
deno_web = "0.202.0"
deno_net = "0.163.0"
deno_core = "0.311.0"
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
swc_common = "=0.37.5"
swc_ecma_parser = "=0.149.1"
swc_ecma_ast = "=0.118.2"
swc_ecma_visit = "=0.104.8"
async-recursion = "^1"
swc_common = "=0.33.26"
swc_ecma_parser = "=0.144.3"
swc_ecma_ast = "=0.113.7"
swc_ecma_visit = "=0.99.1"
base64 = "0.21.0"
base32 = "^0"
hmac = "0.12.1"
@@ -210,7 +222,7 @@ serde_derive = "1.0.147"
const_format = { version = "0.2", features = ["rust_1_64", "rust_1_51"] }
dyn-iter = "0.2.0"
rsa = "0.7.2"
async-stripe = { version = "0.34.1", features = [
async-stripe = { version = "0.39.1", features = [
"runtime-tokio-hyper",
"checkout",
"billing",
@@ -270,14 +282,16 @@ tikv-jemallocator = { version = "0.5" }
tikv-jemalloc-sys = { version = "^0.5" }
tikv-jemalloc-ctl = { version = "^0.5" }
# 0.1.12 broken (nested dependency of swc_common)
triomphe = "<0.1.12"
triomphe = "^0"
tantivy = "0.22.0"
# Macro-related
proc-macro2 = "1.0"
pulldown-cmark = "0.9"
toml = "0.7"
syn = { version = "2.0.74", features = ["full"] }
quote = "1.0.36"
regex-lite = "0.1.6"
yaml-rust = "0.4.5"
tokio-tungstenite = { version = "0.24.0", features = ["native-tls"] }

View File

@@ -1,14 +0,0 @@
CREATE POLICY admin_policy ON account TO windmill_admin USING (true);
CREATE POLICY admin_policy ON app TO windmill_admin USING (true);
CREATE POLICY admin_policy ON audit TO windmill_admin USING (true);
CREATE POLICY admin_policy ON capture TO windmill_admin USING (true);
CREATE POLICY admin_policy ON completed_job TO windmill_admin USING (true);
CREATE POLICY admin_policy ON flow TO windmill_admin USING (true);
CREATE POLICY admin_policy ON folder TO windmill_admin USING (true);
CREATE POLICY admin_policy ON queue TO windmill_admin USING (true);
CREATE POLICY admin_policy ON raw_app TO windmill_admin USING (true);
CREATE POLICY admin_policy ON resource TO windmill_admin USING (true);
CREATE POLICY admin_policy ON schedule TO windmill_admin USING (true);
CREATE POLICY admin_policy ON script TO windmill_admin USING (true);
CREATE POLICY admin_policy ON usr_to_group TO windmill_admin USING (true);
CREATE POLICY admin_policy ON variable TO windmill_admin USING (true);

View File

@@ -1 +1 @@
0f5f42d2f8f5f1af05c8086f3dc7ad38d83750df
150c3176f2c192366f2e8298ef6ece43780b529e

View File

@@ -0,0 +1,2 @@
-- Add down migration script here
DROP TABLE websocket_trigger;

View File

@@ -0,0 +1,67 @@
-- Add up migration script here
CREATE TABLE websocket_trigger (
path VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
script_path VARCHAR(255) NOT NULL,
is_flow BOOLEAN NOT NULL,
workspace_id VARCHAR(50) NOT NULL,
edited_by VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL,
edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
extra_perms JSONB NOT NULL DEFAULT '{}',
server_id VARCHAR(50) NULL,
last_server_ping TIMESTAMPTZ NULL,
error TEXT NULL,
enabled BOOLEAN NOT NULL,
filters JSONB[] NOT NULL DEFAULT '{}',
PRIMARY KEY (path, workspace_id)
);
GRANT ALL ON websocket_trigger TO windmill_user;
GRANT ALL ON websocket_trigger TO windmill_admin;
ALTER TABLE websocket_trigger ENABLE ROW LEVEL SECURITY;
CREATE POLICY admin_policy ON websocket_trigger FOR ALL TO windmill_admin USING (true);
CREATE POLICY see_folder_extra_perms_user_select ON websocket_trigger FOR SELECT TO windmill_user
USING (SPLIT_PART(websocket_trigger.path, '/', 1) = 'f' AND SPLIT_PART(websocket_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]));
CREATE POLICY see_folder_extra_perms_user_insert ON websocket_trigger FOR INSERT TO windmill_user
WITH CHECK (SPLIT_PART(websocket_trigger.path, '/', 1) = 'f' AND SPLIT_PART(websocket_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
CREATE POLICY see_folder_extra_perms_user_update ON websocket_trigger FOR UPDATE TO windmill_user
USING (SPLIT_PART(websocket_trigger.path, '/', 1) = 'f' AND SPLIT_PART(websocket_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
CREATE POLICY see_folder_extra_perms_user_delete ON websocket_trigger FOR DELETE TO windmill_user
USING (SPLIT_PART(websocket_trigger.path, '/', 1) = 'f' AND SPLIT_PART(websocket_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
CREATE POLICY see_own ON websocket_trigger FOR ALL TO windmill_user
USING (SPLIT_PART(websocket_trigger.path, '/', 1) = 'u' AND SPLIT_PART(websocket_trigger.path, '/', 2) = current_setting('session.user'));
CREATE POLICY see_member ON websocket_trigger FOR ALL TO windmill_user
USING (SPLIT_PART(websocket_trigger.path, '/', 1) = 'g' AND SPLIT_PART(websocket_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[]));
CREATE POLICY see_extra_perms_user_select ON websocket_trigger FOR SELECT TO windmill_user
USING (extra_perms ? CONCAT('u/', current_setting('session.user')));
CREATE POLICY see_extra_perms_user_insert ON websocket_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 websocket_trigger FOR UPDATE TO windmill_user
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
CREATE POLICY see_extra_perms_user_delete ON websocket_trigger FOR DELETE TO windmill_user
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
CREATE POLICY see_extra_perms_groups_select ON websocket_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 websocket_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 websocket_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 websocket_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,24 @@
-- Add up migration script here
DO
$$
DECLARE
tbl_name text;
policy_exists boolean;
tbl_names text[] := ARRAY['account', 'app', 'audit', 'capture', 'completed_job', 'flow', 'folder', 'http_trigger', 'queue', 'raw_app', 'resource', 'schedule', 'script', 'usr_to_group', 'variable'];
BEGIN
FOR tbl_name IN SELECT unnest(tbl_names)
LOOP
SELECT EXISTS (
SELECT 1
FROM pg_policies
WHERE schemaname = 'public'
AND tablename = tbl_name
AND policyname = 'admin_policy'
) INTO policy_exists;
IF NOT policy_exists THEN
EXECUTE format('CREATE POLICY admin_policy ON %I TO windmill_admin USING (true);', tbl_name);
END IF;
END LOOP;
END;
$$;

View File

@@ -0,0 +1,2 @@
-- Drop the alert_locks table
DROP TABLE IF EXISTS concurrency_locks;

View File

@@ -0,0 +1,6 @@
-- Create the alert_locks table
CREATE TABLE concurrency_locks (
id VARCHAR PRIMARY KEY,
last_locked_at TIMESTAMP NOT NULL,
owner VARCHAR NULL
);

View File

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

View File

@@ -0,0 +1,13 @@
-- Add up migration script here
CREATE TYPE AUTOSCALING_EVENT_TYPE AS ENUM ('full_scaleout', 'scalein', 'scaleout');
CREATE TABLE autoscaling_event (
id SERIAL PRIMARY KEY,
worker_group TEXT NOT NULL,
event_type AUTOSCALING_EVENT_TYPE NOT NULL,
desired_workers INTEGER NOT NULL,
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
reason TEXT
);
CREATE INDEX autoscaling_event_worker_group_idx ON autoscaling_event (worker_group, applied_at);

View File

@@ -152,5 +152,24 @@
"vismanet_erp_interactive_api:read",
"vismanet_erp_interactive_api:update"
]
},
"spotify": {
"auth_url": "https://accounts.spotify.com/authorize",
"token_url": "https://accounts.spotify.com/api/token",
"scopes": [
"user-read-playback-state",
"user-modify-playback-state",
"user-read-currently-playing",
"playlist-read-private",
"playlist-read-collaborative",
"playlist-modify-private",
"playlist-modify-public",
"user-follow-read",
"user-read-playback-position",
"user-read-recently-played",
"user-top-read",
"user-library-modify",
"user-library-read"
]
}
}

View File

@@ -61,6 +61,7 @@ static PYTHON_IMPORTS_REPLACEMENT: phf::Map<&'static str, &'static str> = phf_ma
"opensearchpy" => "opensearch-py",
"lokalise" => "python-lokalise-api",
"msgraph" => "msgraph-sdk",
"pythonjsonlogger" => "python-json-logger",
};
fn replace_import(x: String) -> String {

View File

@@ -15,13 +15,13 @@ use windmill_parser::{
use swc_common::{sync::Lrc, FileName, SourceMap, SourceMapper, Span, Spanned};
use swc_ecma_ast::{
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, FnDecl, Ident, Lit,
MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat, Param, Pat, Str,
TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, TsOptionalType,
TsParenthesizedType, TsPropertySignature, TsType, TsTypeAnn, TsTypeElement, TsTypeLit,
TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, FnDecl, Ident,
IdentName, Lit, MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat,
Param, Pat, Str, TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType,
TsOptionalType, TsParenthesizedType, TsPropertySignature, TsType, TsTypeAnn, TsTypeElement,
TsTypeLit, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
};
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_parser::{lexer::Lexer, EsSyntax, Parser, StringInput, Syntax, TsSyntax};
use regex::Regex;
#[cfg(target_arch = "wasm32")]
@@ -48,9 +48,9 @@ impl Visit for ImportsFinder {
pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
let cm: Lrc<SourceMap> = Default::default();
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()), code.into());
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()).into(), code.into());
let lexer = Lexer::new(
Syntax::Typescript(TsConfig::default()),
Syntax::Typescript(TsSyntax::default()),
// EsVersion defaults to es5
Default::default(),
StringInput::from(&*fm),
@@ -69,7 +69,7 @@ pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
})?;
let mut visitor = ImportsFinder { imports: HashSet::new() };
swc_ecma_visit::visit_module(&mut visitor, &expr);
visitor.visit_module(&expr);
Ok(visitor.imports.into_iter().collect())
}
@@ -87,7 +87,7 @@ impl Visit for OutputFinder {
c.visit_with(self);
}
match m {
MemberExpr { obj, prop: MemberProp::Ident(Ident { sym, .. }), .. } => {
MemberExpr { obj, prop: MemberProp::Ident(IdentName { sym, .. }), .. } => {
match *obj.to_owned() {
Expr::Ident(Ident { sym: sym_i, .. }) => {
self.idents.insert((sym_i.to_string(), sym.to_string()));
@@ -102,10 +102,10 @@ impl Visit for OutputFinder {
pub fn parse_expr_for_ids(code: &str) -> anyhow::Result<Vec<(String, String)>> {
let cm: Lrc<SourceMap> = Default::default();
let fm = cm.new_source_file(FileName::Custom("main.ts".into()), code.into());
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
let lexer = Lexer::new(
// We want to parse ecmascript
Syntax::Es(EsConfig { jsx: false, ..Default::default() }),
Syntax::Es(EsSyntax { jsx: false, ..Default::default() }),
// EsVersion defaults to es5
Default::default(),
StringInput::from(&*fm),
@@ -124,7 +124,7 @@ pub fn parse_expr_for_ids(code: &str) -> anyhow::Result<Vec<(String, String)>> {
})?;
let mut visitor = OutputFinder { idents: HashSet::new() };
swc_ecma_visit::visit_module(&mut visitor, &expr);
visitor.visit_module(&expr);
Ok(visitor.idents.into_iter().collect())
}
@@ -135,10 +135,10 @@ pub fn parse_deno_signature(
main_override: Option<String>,
) -> anyhow::Result<MainArgSignature> {
let cm: Lrc<SourceMap> = Default::default();
let fm = cm.new_source_file(FileName::Custom("main.ts".into()), code.into());
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
let lexer = Lexer::new(
// We want to parse ecmascript
Syntax::Typescript(TsConfig::default()),
Syntax::Typescript(TsSyntax::default()),
// EsVersion defaults to es5
Default::default(),
StringInput::from(&*fm),

View File

@@ -188,9 +188,15 @@ pub struct AnsiblePlaybookOptions {
pub force_handlers: Option<()>,
}
#[derive(Debug, Clone)]
pub enum ResourceOrVariablePath {
Resource(String),
Variable(String),
}
#[derive(Debug, Clone)]
pub struct FileResource {
pub resource_path: String,
pub resource_path: ResourceOrVariablePath,
pub target_path: String,
}
@@ -309,7 +315,7 @@ pub fn parse_ansible_reqs(
}
}
}
Yaml::String(key) if key == "file_resources" => {
Yaml::String(key) if key == "files" || key == "file_resources" => {
if let Yaml::Array(file_resources) = value {
let resources: anyhow::Result<Vec<FileResource>> =
file_resources.iter().map(parse_file_resource).collect();
@@ -440,7 +446,24 @@ fn parse_file_resource(yaml: &Yaml) -> anyhow::Result<FileResource> {
"No `target` provided for file resource {}. Please input a target relative path for the ansible playbook to see this file.",
resource_path
))?;
return Ok(FileResource { resource_path: resource_path.clone(), target_path });
return Ok(FileResource {
resource_path: ResourceOrVariablePath::Resource(resource_path.clone()),
target_path,
});
}
if let Some(Yaml::String(resource_path)) = f.get(&Yaml::String("variable".to_string())) {
let target_path = f
.get(&Yaml::String("target".to_string()))
.and_then(|x| x.as_str())
.map(|x| x.to_string())
.ok_or(anyhow!(
"No `target` provided for file resource {}. Please input a target relative path for the ansible playbook to see this file.",
resource_path
))?;
return Ok(FileResource {
resource_path: ResourceOrVariablePath::Variable(resource_path.clone()),
target_path,
});
}
return Err(anyhow!(
"File resource should have a `resource` field, linking to a text file resource"

View File

@@ -1,16 +1,8 @@
use anyhow::anyhow;
#[cfg(feature = "enterprise")]
use windmill_common::error::{Error, Result};
pub async fn set_license_key(_license_key: String) -> anyhow::Result<()> {
pub async fn set_license_key(_license_key: String) -> () {
// Implementation is not open source
Err(anyhow!("License cannot be set in Windmill CE"))
}
#[cfg(feature = "enterprise")]
pub async fn verify_license_key() -> Result<()> {
pub async fn verify_license_key() -> () {
// Implementation is not open source
Err(Error::InternalErr(
"License always invalid in Windmill CE".to_string(),
))
}

View File

@@ -27,7 +27,7 @@ use uuid::Uuid;
use windmill_api::HTTP_CLIENT;
#[cfg(feature = "enterprise")]
use windmill_common::ee::schedule_key_renewal;
use windmill_common::ee::{maybe_renew_license_key_on_start, LICENSE_KEY_ID, LICENSE_KEY_VALID};
use windmill_common::{
global_settings::{
@@ -116,7 +116,7 @@ where
pub fn main() -> anyhow::Result<()> {
#[cfg(feature = "deno_core")]
deno_core::JsRuntime::init_platform(None);
deno_core::JsRuntime::init_platform(None, false);
create_and_run_current_thread_inner(windmill_main())
}
@@ -284,7 +284,8 @@ async fn windmill_main() -> anyhow::Result<()> {
tracing::info!("Binary is in 'indexer' mode");
#[cfg(not(feature = "tantivy"))]
{
panic!("Indexer mode requires the tantivy feature flag");
tracing::error!("Cannot start the indexer because tantivy is not included in this binary/image. Make sure you are using the EE image if you want to access the full text search features.");
panic!("Indexer mode requires compiling with the tantivy feature flag.");
}
#[cfg(feature = "tantivy")]
Mode::Indexer
@@ -307,7 +308,8 @@ async fn windmill_main() -> anyhow::Result<()> {
Mode::Standalone
});
let num_workers = if mode == Mode::Server || mode == Mode::Indexer {
#[allow(unused_mut)]
let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer {
0
} else {
std::env::var("NUM_WORKERS")
@@ -422,6 +424,50 @@ Windmill Community Edition {GIT_VERSION}
display_config(&ENV_SETTINGS);
if let Err(e) = reload_base_url_setting(&db).await {
tracing::error!("Error loading base url: {:?}", e)
}
if let Err(e) = reload_critical_error_channels_setting(&db).await {
tracing::error!("Could loading critical error emails setting: {:?}", e);
}
#[cfg(feature = "enterprise")]
{
// load the license key and check if it's valid
// if not valid and not server mode just quit
// if not expired and server mode then force renewal
// if key still invalid and num_workers > 0, set to 0
if let Err(err) = reload_license_key(&db).await {
tracing::error!("Failed to reload license key: {err:#}");
}
let valid_key = *LICENSE_KEY_VALID.read().await;
if !valid_key && !server_mode {
panic!("Invalid license key, workers require a valid license key");
}
if server_mode {
// only force renewal if invalid but not empty (= expired)
let renewed_now = maybe_renew_license_key_on_start(
&HTTP_CLIENT,
&db,
!valid_key && !LICENSE_KEY_ID.read().await.is_empty(),
)
.await;
if renewed_now {
if let Err(err) = reload_license_key(&db).await {
tracing::error!("Failed to reload license key: {err:#}");
}
}
if num_workers > 0 {
let valid_key = *LICENSE_KEY_VALID.read().await;
if !valid_key {
tracing::warn!("License key invalid, setting num_workers to 0");
num_workers = 0;
}
}
}
}
let worker_mode = num_workers > 0;
if server_mode || worker_mode || indexer_mode {
@@ -448,7 +494,16 @@ Windmill Community Edition {GIT_VERSION}
initial_load(&db, killpill_tx.clone(), worker_mode, server_mode, is_agent).await;
monitor_db(&db, &base_internal_url, rsmq.clone(), server_mode, true).await;
monitor_db(
&db,
&base_internal_url,
rsmq.clone(),
server_mode,
worker_mode,
true,
killpill_tx.clone(),
)
.await;
monitor_pool(&db).await;
@@ -476,7 +531,7 @@ Windmill Community Edition {GIT_VERSION}
#[cfg(feature = "tantivy")]
let (index_reader, index_writer) = if should_index_jobs {
let (r, w) = windmill_indexer::indexer_ee::init_index().await?;
let (r, w) = windmill_indexer::indexer_ee::init_index(&db).await?;
(Some(r), Some(w))
} else {
(None, None)
@@ -548,8 +603,11 @@ Windmill Community Edition {GIT_VERSION}
rx.recv().await?;
}
}
tracing::info!("Starting phase 2 of shutdown");
killpill_phase2_tx.send(())?;
if killpill_phase2_tx.receiver_count() > 0 {
tracing::info!("Starting phase 2 of shutdown");
killpill_phase2_tx.send(())?;
tracing::info!("Phase 2 of shutdown completed");
}
Ok(()) as anyhow::Result<()>
};
@@ -575,7 +633,9 @@ Windmill Community Edition {GIT_VERSION}
&base_internal_url,
rsmq.clone(),
server_mode,
false
worker_mode,
false,
tx.clone(),
)
.await;
},
@@ -618,7 +678,15 @@ Windmill Community Edition {GIT_VERSION}
},
LICENSE_KEY_SETTING => {
if let Err(e) = reload_license_key(&db).await {
tracing::error!(error = %e, "Could not reload license key setting");
tracing::error!("Failed to reload license key: {e:#}");
}
#[cfg(feature = "enterprise")]
if worker_mode {
let valid_key = *LICENSE_KEY_VALID.read().await;
if !valid_key {
tracing::error!("Invalid license key, exiting...");
tx.send(()).expect("send");
}
}
},
DEFAULT_TAGS_PER_WORKSPACE_SETTING => {
@@ -764,15 +832,10 @@ Windmill Community Edition {GIT_VERSION}
Ok(()) as anyhow::Result<()>
};
if mode == Mode::Server || mode == Mode::Standalone {
if server_mode {
schedule_stats(&db, &HTTP_CLIENT).await;
}
#[cfg(feature = "enterprise")]
if mode == Mode::Server || mode == Mode::Standalone {
schedule_key_renewal(&HTTP_CLIENT, &db).await;
}
futures::try_join!(
shutdown_signal,
workers_f,

View File

@@ -27,7 +27,7 @@ use windmill_api::{
DEFAULT_BODY_LIMIT, IS_SECURE, OAUTH_CLIENTS, REQUEST_SIZE_LIMIT, SAML_METADATA, SCIM_TOKEN,
};
#[cfg(feature = "enterprise")]
use windmill_common::ee::worker_groups_alerts;
use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts, LICENSE_KEY_VALID};
use windmill_common::{
auth::JWT_SECRET,
ee::CriticalErrorChannel,
@@ -76,9 +76,6 @@ use windmill_common::global_settings::OBJECT_STORE_CACHE_CONFIG_SETTING;
#[cfg(feature = "enterprise")]
use crate::ee::verify_license_key;
#[cfg(feature = "enterprise")]
use windmill_common::ee::LICENSE_KEY_VALID;
use crate::ee::set_license_key;
lazy_static::lazy_static! {
@@ -152,18 +149,10 @@ pub async fn initial_load(
tracing::error!("Error reloading custom tags: {:?}", e)
}
if let Err(e) = reload_base_url_setting(db).await {
tracing::error!("Error reloading base url: {:?}", e)
}
if let Err(e) = reload_hub_base_url_setting(db, server_mode).await {
tracing::error!("Error reloading hub base url: {:?}", e)
}
if let Err(e) = reload_critical_error_channels_setting(&db).await {
tracing::error!("Could not reload critical error emails setting: {:?}", e);
}
if let Err(e) = reload_jwt_secret_setting(&db).await {
tracing::error!("Could not reload jwt secret setting: {:?}", e);
}
@@ -182,11 +171,6 @@ pub async fn initial_load(
reload_scim_token_setting(&db).await;
}
#[cfg(feature = "enterprise")]
if let Err(e) = reload_license_key(&db).await {
tracing::error!("Error reloading license key: {:?}", e)
}
if worker_mode {
reload_extra_pip_index_url_setting(&db).await;
reload_pip_index_url_setting(&db).await;
@@ -854,8 +838,10 @@ pub async fn reload_request_size(db: &DB) {
}
}
pub async fn reload_license_key(db: &DB) -> error::Result<()> {
let q = load_value_from_global_settings(db, LICENSE_KEY_SETTING).await?;
pub async fn reload_license_key(db: &DB) -> anyhow::Result<()> {
let q = load_value_from_global_settings(db, LICENSE_KEY_SETTING)
.await
.map_err(|err| anyhow::anyhow!("Error reloading license key: {}", err.to_string()))?;
let mut value = std::env::var("LICENSE_KEY")
.ok()
@@ -873,9 +859,7 @@ pub async fn reload_license_key(db: &DB) -> error::Result<()> {
tracing::error!("Could not parse LICENSE_KEY found: {:#?}", &q);
}
};
set_license_key(value).await?;
set_license_key(value).await;
Ok(())
}
@@ -1014,7 +998,9 @@ pub async fn monitor_db(
base_internal_url: &str,
rsmq: Option<MultiplexedRsmq>,
server_mode: bool,
_worker_mode: bool,
initial_load: bool,
_killpill_tx: tokio::sync::broadcast::Sender<()>,
) {
let zombie_jobs_f = async {
if server_mode && !initial_load {
@@ -1035,15 +1021,14 @@ pub async fn monitor_db(
let verify_license_key_f = async {
#[cfg(feature = "enterprise")]
if let Err(e) = verify_license_key().await {
tracing::error!("Error verifying license key: {:?}", e);
let mut l = LICENSE_KEY_VALID.write().await;
*l = false;
} else {
let is_valid = LICENSE_KEY_VALID.read().await.clone();
if !is_valid {
let mut l = LICENSE_KEY_VALID.write().await;
*l = true;
if !initial_load {
verify_license_key().await;
if _worker_mode {
let valid_key = *LICENSE_KEY_VALID.read().await;
if !valid_key {
tracing::error!("Invalid license key, exiting...");
_killpill_tx.send(()).expect("send");
}
}
}
};
@@ -1061,12 +1046,30 @@ pub async fn monitor_db(
}
};
let jobs_waiting_alerts_f = async {
#[cfg(feature = "enterprise")]
if server_mode {
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);
}
}
};
join!(
expired_items_f,
zombie_jobs_f,
expose_queue_metrics_f,
verify_license_key_f,
worker_groups_alerts_f
worker_groups_alerts_f,
jobs_waiting_alerts_f,
apply_autoscaling_f,
);
}
@@ -1084,19 +1087,11 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
.unwrap_or(true);
if metrics_enabled || save_metrics {
let queue_counts = sqlx::query!(
"SELECT tag, count(*) as count FROM queue WHERE
scheduled_for <= now() - ('3 seconds')::interval AND running = false
GROUP BY tag"
)
.fetch_all(db)
.await
.ok()
.unwrap_or_else(|| vec![]);
let queue_counts = windmill_common::queue::get_queue_counts(db).await;
for q in queue_counts {
let count = q.count.unwrap_or(0);
let tag = q.tag;
let count = q.1;
let tag = q.0;
if metrics_enabled {
let metric = (*QUEUE_COUNT).with_label_values(&[&tag]);
metric.set(count as i64);

View File

@@ -2745,7 +2745,7 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
"lock": null,
"path": null,
"type": "rawscript",
"content": "import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main() {\n return \"Hello\"\n}\n",
"content": "import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main() {\n return wmill\n}\n",
"language": "deno",
"input_transforms": {}
},

View File

@@ -95,6 +95,7 @@ openidconnect = { workspace = true, optional = true}
url = { workspace = true, optional = true}
jsonwebtoken = { workspace = true }
matchit.workspace = true
tokio-tungstenite.workspace = true
pin-project.workspace = true
http.workspace = true

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.405.5
version: 1.415.2
title: Windmill API
contact:
@@ -1467,6 +1467,11 @@ paths:
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
- name: active_only
in: query
description: filter only active users
schema:
type: boolean
responses:
"200":
description: user
@@ -2162,6 +2167,30 @@ paths:
schema:
type: number
/w/{workspace}/workspaces/used_triggers:
get:
summary: get used triggers
operationId: getUsedTriggers
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
responses:
"200":
description: status
content:
application/json:
schema:
type: object
properties:
http_routes_used:
type: boolean
websocket_used:
type: boolean
required:
- http_routes_used
- websocket_used
/w/{workspace}/users/list:
get:
summary: list users
@@ -2789,7 +2818,14 @@ paths:
oauth:
type: array
items:
type: string
type: object
properties:
type:
type: string
display_name:
type: string
required:
- type
saml:
type: string
required:
@@ -4013,6 +4049,42 @@ paths:
schema:
$ref: "#/components/schemas/Script"
/w/{workspace}/scripts/get_triggers_count/{path}:
get:
summary: get triggers count of script
operationId: getTriggersCountOfScript
tags:
- script
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: triggers count
content:
application/json:
schema:
$ref: "#/components/schemas/TriggersCount"
/w/{workspace}/scripts/list_tokens/{path}:
get:
summary: get tokens with script scope
operationId: listTokensOfScript
tags:
- script
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: tokens list
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/TruncatedToken"
/w/{workspace}/scripts/get/draft/{path}:
get:
summary: get script by path with draft
@@ -4049,6 +4121,26 @@ paths:
items:
$ref: "#/components/schemas/ScriptHistory"
/w/{workspace}/scripts/get_latest_version/{path}:
get:
summary: get scripts's latest version (hash)
operationId: getScriptLatestVersion
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
tags:
- script
responses:
"200":
description: Script version/hash
content:
application/json:
required: false
schema:
$ref: "#/components/schemas/ScriptHistory"
/w/{workspace}/scripts/history_update/h/{hash}/p/{path}:
post:
summary: update history of a script
@@ -4537,6 +4629,25 @@ paths:
items:
$ref: "#/components/schemas/FlowVersion"
/w/{workspace}/flows/get_latest_version/{path}:
get:
summary: get flow's latest version
operationId: getFlowLatestVersion
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
tags:
- flow
responses:
"200":
description: Flow version
content:
application/json:
required: false
schema:
$ref: "#/components/schemas/FlowVersion"
/w/{workspace}/flows/get/v/{version}/p/{path}:
get:
summary: get flow version
@@ -4617,6 +4728,43 @@ paths:
schema:
$ref: "#/components/schemas/Flow"
/w/{workspace}/flows/get_triggers_count/{path}:
get:
summary: get triggers count of flow
operationId: getTriggersCountOfFlow
tags:
- flow
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: triggers count
content:
application/json:
schema:
$ref: "#/components/schemas/TriggersCount"
/w/{workspace}/flows/list_tokens/{path}:
get:
summary: get tokens with flow scope
operationId: listTokensOfFlow
tags:
- flow
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: tokens list
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/TruncatedToken"
/w/{workspace}/flows/toggle_workspace_error_handler/{path}:
post:
summary: Toggle ON and OFF the workspace error handler for a given flow
@@ -5054,6 +5202,24 @@ paths:
items:
$ref: "#/components/schemas/AppHistory"
/w/{workspace}/apps/get_latest_version/{path}:
get:
summary: get apps's latest version
operationId: getAppLatestVersion
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
tags:
- app
responses:
"200":
description: App version
content:
application/json:
required: false
schema:
$ref: "#/components/schemas/AppHistory"
/w/{workspace}/apps/history_update/a/{id}/v/{version}:
post:
summary: update app history
@@ -7072,22 +7238,170 @@ paths:
schema:
type: boolean
/w/{workspace}/http_triggers/used:
get:
summary: whether http triggers are used
operationId: used
/w/{workspace}/websocket_triggers/create:
post:
summary: create websocket trigger
operationId: createWebsocketTrigger
tags:
- http_trigger
- websocket_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: new websocket trigger
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewWebsocketTrigger"
responses:
"201":
description: websocket trigger created
content:
text/plain:
schema:
type: string
/w/{workspace}/websocket_triggers/update/{path}:
post:
summary: update websocket trigger
operationId: updateWebsocketTrigger
tags:
- websocket_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
requestBody:
description: updated trigger
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/EditWebsocketTrigger"
responses:
"200":
description: whether http triggers are used
description: websocket trigger updated
content:
text/plain:
schema:
type: string
/w/{workspace}/websocket_triggers/delete/{path}:
delete:
summary: delete websocket trigger
operationId: deleteWebsocketTrigger
tags:
- websocket_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: websocket trigger deleted
content:
text/plain:
schema:
type: string
/w/{workspace}/websocket_triggers/get/{path}:
get:
summary: get websocket trigger
operationId: getWebsocketTrigger
tags:
- websocket_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: websocket trigger deleted
content:
application/json:
schema:
$ref: "#/components/schemas/WebsocketTrigger"
/w/{workspace}/websocket_triggers/list:
get:
summary: list websocket triggers
operationId: listWebsocketTriggers
tags:
- websocket_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
required: true
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
- name: path
description: filter by path
in: query
schema:
type: string
- name: is_flow
in: query
schema:
type: boolean
- name: path_start
in: query
schema:
type: string
responses:
"200":
description: websocket trigger list
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/WebsocketTrigger"
/w/{workspace}/websocket_triggers/exists/{path}:
get:
summary: does websocket trigger exists
operationId: existsWebsocketTrigger
tags:
- websocket_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: websocket trigger exists
content:
application/json:
schema:
type: boolean
/w/{workspace}/websocket_triggers/setenabled/{path}:
post:
summary: set enabled websocket trigger
operationId: setWebsocketTriggerEnabled
tags:
- websocket_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
requestBody:
description: updated websocket trigger enable
required: true
content:
application/json:
schema:
type: object
properties:
enabled:
type: boolean
required:
- enabled
responses:
"200":
description: websocket trigger enabled set
content:
text/plain:
schema:
type: string
/groups/list:
get:
summary: list instance groups
@@ -7889,6 +8203,29 @@ paths:
items:
$ref: "#/components/schemas/Config"
/configs/list_autoscaling_events/{worker_group}:
get:
summary: List autoscaling events
operationId: listAutoscalingEvents
tags:
- config
parameters:
- name: worker_group
in: path
required: true
schema:
type: string
responses:
"200":
description: List of autoscaling events
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/AutoscalingEvent"
/w/{workspace}/acls/get/{kind}/{path}:
get:
summary: get granular acls
@@ -7915,6 +8252,7 @@ paths:
app,
raw_app,
http_trigger,
websocket_trigger,
]
responses:
"200":
@@ -7952,6 +8290,7 @@ paths:
app,
raw_app,
http_trigger,
websocket_trigger,
]
requestBody:
description: acl to add
@@ -8000,6 +8339,7 @@ paths:
app,
raw_app,
http_trigger,
websocket_trigger,
]
requestBody:
description: acl to add
@@ -10247,6 +10587,8 @@ components:
type: array
items:
type: string
email:
type: string
required:
- token_prefix
- created_at
@@ -10264,6 +10606,8 @@ components:
type: array
items:
type: string
workspace_id:
type: string
NewTokenImpersonate:
type: object
@@ -10275,6 +10619,8 @@ components:
format: date-time
impersonate_email:
type: string
workspace_id:
type: string
required:
- impersonate_email
@@ -10417,6 +10763,7 @@ components:
- "users.delete"
- "users.update"
- "users.login"
- "users.login_failure"
- "users.logout"
- "users.accept_invite"
- "users.decline_invite"
@@ -10428,6 +10775,7 @@ components:
- "users.impersonate"
- "users.leave_workspace"
- "oauth.login"
- "oauth.login_failure"
- "oauth.signup"
- "variables.create"
- "variables.delete"
@@ -11111,6 +11459,145 @@ components:
- requires_auth
- http_method
TriggersCount:
type: object
properties:
primary_schedule:
type: object
properties:
schedule:
type: string
schedule_count:
type: number
http_routes_count:
type: number
webhook_count:
type: number
email_count:
type: number
websocket_count:
type: number
WebsocketTrigger:
type: object
properties:
path:
type: string
edited_by:
type: string
edited_at:
type: string
format: date-time
script_path:
type: string
url:
type: string
is_flow:
type: boolean
extra_perms:
type: object
additionalProperties:
type: boolean
email:
type: string
workspace_id:
type: string
server_id:
type: string
last_server_ping:
type: string
format: date-time
error:
type: string
enabled:
type: boolean
filters:
type: array
items:
type: object
properties:
key:
type: string
value: {}
required:
- key
- value
required:
- path
- edited_by
- edited_at
- script_path
- url
- extra_perms
- is_flow
- email
- workspace_id
- enabled
- filters
NewWebsocketTrigger:
type: object
properties:
path:
type: string
script_path:
type: string
is_flow:
type: boolean
url:
type: string
enabled:
type: boolean
filters:
type: array
items:
type: object
properties:
key:
type: string
value: {}
required:
- key
- value
required:
- path
- script_path
- url
- is_flow
- filters
EditWebsocketTrigger:
type: object
properties:
url:
type: string
path:
type: string
script_path:
type: string
is_flow:
type: boolean
filters:
type: array
items:
type: object
properties:
key:
type: string
value: {}
required:
- key
- value
required:
- path
- script_path
- url
- is_flow
- filters
Group:
type: object
properties:
@@ -11309,6 +11796,8 @@ components:
type: string
username:
type: string
operator_only:
type: boolean
required:
- email
@@ -12010,3 +12499,21 @@ components:
properties:
dancer:
type: string
AutoscalingEvent:
type: object
properties:
id:
type: integer
format: int64
worker_group:
type: string
event_type:
type: string
desired_workers:
type: integer
reason:
type: string
applied_at:
type: string
format: date-time

View File

@@ -62,6 +62,7 @@ pub fn workspaced_service() -> Router {
.route("/delete/*path", delete(delete_app))
.route("/create", post(create_app))
.route("/history/p/*path", get(get_app_history))
.route("/get_latest_version/*path", get(get_latest_version))
.route("/history_update/a/:id/v/:version", post(update_app_history))
}
@@ -427,6 +428,38 @@ async fn get_app_history(
return Ok(Json(result));
}
async fn get_latest_version(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Option<AppHistory>> {
let mut tx = user_db.begin(&authed).await?;
let row = sqlx::query!(
"SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg
FROM app a LEFT JOIN app_version av ON a.id = av.app_id LEFT JOIN deployment_metadata dm ON av.id = dm.app_version
WHERE a.workspace_id = $1 AND a.path = $2
ORDER BY created_at DESC",
w_id,
path.to_path(),
).fetch_optional(&mut *tx).await?;
tx.commit().await?;
if let Some(row) = row {
let result = AppHistory {
app_id: row.app_id,
version: row.version_id,
deployment_msg: row.deployment_msg,
};
return Ok(Json(Some(result)));
} else {
return Ok(Json(None));
}
}
async fn update_app_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,

View File

@@ -29,6 +29,10 @@ pub fn global_service() -> Router {
.route("/update/:name", post(update_config).delete(delete_config))
.route("/get/:name", get(get_config))
.route("/list", get(list_configs))
.route(
"/list_autoscaling_events/:worker_group",
get(list_autoscaling_events),
)
}
#[derive(Serialize, Deserialize, FromRow)]
@@ -177,6 +181,30 @@ async fn delete_config(
Ok(format!("Deleted config {name}"))
}
#[derive(Serialize, Deserialize, FromRow)]
struct AutoscalingEvent {
id: i64,
worker_group: String,
event_type: Option<String>,
desired_workers: i32,
reason: Option<String>,
applied_at: chrono::NaiveDateTime,
}
async fn list_autoscaling_events(
Extension(db): Extension<DB>,
Path(worker_group): Path<String>,
) -> error::JsonResult<Vec<AutoscalingEvent>> {
let events = sqlx::query_as!(
AutoscalingEvent,
"SELECT id, worker_group, event_type::text, desired_workers, reason, applied_at FROM autoscaling_event WHERE worker_group = $1 ORDER BY applied_at DESC LIMIT 5",
worker_group
)
.fetch_all(&db)
.await?;
Ok(Json(events))
}
#[cfg(feature = "enterprise")]
async fn list_configs(
authed: ApiAuthed,

View File

@@ -173,17 +173,6 @@ pub async fn migrate(db: &DB) -> Result<(), Error> {
let migrator = db.acquire().await?;
let mut custom_migrator = CustomMigrator { inner: migrator };
if let Err(err) = fix_flow_versioning_migration(&mut custom_migrator, db).await {
tracing::error!("Could not apply flow versioning fix migration: {err:#}");
}
let db2 = db.clone();
let _ = tokio::task::spawn(async move {
if let Err(err) = fix_job_completed_index(&db2).await {
tracing::error!("Could not apply job completed index fix migration: {err:#}");
}
});
match sqlx::migrate!("../migrations")
.run_direct(&mut custom_migrator)
.await
@@ -199,11 +188,17 @@ pub async fn migrate(db: &DB) -> Result<(), Error> {
Err(err) => Err(err),
}?;
#[cfg(feature = "enterprise")]
if let Err(e) = windmill_migrations(&mut custom_migrator, db).await {
tracing::error!("Could not apply windmill custom migrations: {e:#}")
if let Err(err) = fix_flow_versioning_migration(&mut custom_migrator, db).await {
tracing::error!("Could not apply flow versioning fix migration: {err:#}");
}
let db2 = db.clone();
let _ = tokio::task::spawn(async move {
if let Err(err) = fix_job_completed_index(&db2).await {
tracing::error!("Could not apply job completed index fix migration: {err:#}");
}
});
Ok(())
}
@@ -307,7 +302,7 @@ macro_rules! run_windmill_migration {
.await?;
tracing::info!("Finished applying {migration_job_name} migration");
} else {
tracing::info!("migration {migration_job_name} already done");
tracing::debug!("migration {migration_job_name} already done");
}
let _ = sqlx::query("SELECT pg_advisory_unlock(4242)")
@@ -316,7 +311,7 @@ macro_rules! run_windmill_migration {
tx.commit().await?;
tracing::info!("released lock for {migration_job_name}");
} else {
tracing::info!("migration {migration_job_name} already done");
tracing::debug!("migration {migration_job_name} already done");
}
}
@@ -497,33 +492,6 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "enterprise")]
async fn windmill_migrations(migrator: &mut CustomMigrator, db: &DB) -> Result<(), Error> {
if std::env::var("MIGRATION_NO_BYPASSRLS").is_ok() {
migrator.lock().await?;
let has_done_migration = sqlx::query_scalar!(
"SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = 'bypassrls_1-2')",
)
.fetch_one(db)
.await?
.unwrap_or(false);
if !has_done_migration {
let query = include_str!("../../custom_migrations/bypassrls_1.sql");
tracing::info!("Applying bypassrls_1.sql");
let mut tx: sqlx::Transaction<'_, Postgres> = db.begin().await?;
tx.execute(query).await?;
tracing::info!("Applied bypassrls_1.sql");
sqlx::query!("INSERT INTO windmill_migrations (name) VALUES ('bypassrls_1-2')")
.execute(&mut *tx)
.await?;
tx.commit().await?;
}
migrator.unlock().await?;
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct ApiAuthed {
pub email: String,

View File

@@ -4,7 +4,7 @@ use std::sync::Arc;
#[cfg(feature = "enterprise")]
use tokio::sync::RwLock;
pub async fn validate_license_key(_license_key: String) -> anyhow::Result<String> {
pub async fn validate_license_key(_license_key: String) -> anyhow::Result<(String, bool)> {
// Implementation is not open source
Err(anyhow!("License can't be validated in Windmill CE"))
}

View File

@@ -9,6 +9,9 @@
use std::collections::HashMap;
use crate::db::ApiAuthed;
use crate::triggers::{
get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail,
};
use crate::utils::WithStarredInfoQuery;
use crate::{
db::DB,
@@ -53,11 +56,14 @@ pub fn workspaced_service() -> Router {
.route("/update/*path", post(update_flow))
.route("/archive/*path", post(archive_flow_by_path))
.route("/delete/*path", delete(delete_flow_by_path))
.route("/get_triggers_count/*path", get(get_triggers_count))
.route("/list_tokens/*path", get(list_tokens))
.route("/get/*path", get(get_flow_by_path))
.route("/get/draft/*path", get(get_flow_by_path_w_draft))
.route("/exists/*path", get(exists_flow_by_path))
.route("/list_paths", get(list_paths))
.route("/history/p/*path", get(get_flow_history))
.route("/get_latest_version/*path", get(get_latest_version))
.route(
"/history_update/v/:version/p/*path",
post(update_flow_history),
@@ -533,6 +539,30 @@ async fn get_flow_history(
Ok(Json(flows))
}
async fn get_latest_version(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Option<FlowVersion>> {
let path = path.to_path();
let mut tx = user_db.begin(&authed).await?;
let version = sqlx::query_as!(
FlowVersion,
"SELECT flow_version.id, flow_version.created_at, deployment_metadata.deployment_msg FROM flow_version
LEFT JOIN deployment_metadata ON flow_version.id = deployment_metadata.flow_version
WHERE flow_version.path = $1 AND flow_version.workspace_id = $2
ORDER BY flow_version.created_at DESC",
path,
w_id
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
Ok(Json(version))
}
async fn get_flow_version(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -874,6 +904,22 @@ async fn update_flow(
Ok(nf.path.to_string())
}
async fn get_triggers_count(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<TriggersCount> {
let path = path.to_path();
get_triggers_count_internal(&db, &w_id, &path, true).await
}
async fn list_tokens(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
let path = path.to_path();
list_tokens_internal(&db, &w_id, &path, true).await
}
async fn get_flow_by_path(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -1286,7 +1332,6 @@ mod tests {
},
"type": "script",
"path": "test",
"tag_override": Option::<String>::None,
},
},
{
@@ -1330,14 +1375,12 @@ mod tests {
"input_transforms": {},
"type": "script",
"path": "test",
"tag_override": Option::<String>::None,
},
"stop_after_if": {
"expr": "previous.isEmpty()",
"skip_if_stopped": false
}
},
"preprocessor_module": Option::<String>::None
});
assert_eq!(dbg!(serde_json::json!(fv)), dbg!(expect));
}

View File

@@ -275,8 +275,10 @@ pub fn require_is_owner(authed: &ApiAuthed, name: &str) -> Result<()> {
async fn update_folder(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
Extension(webhook): Extension<WebhookShared>,
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
Path((w_id, name)): Path<(String, String)>,
Json(mut ng): Json<UpdateFolder>,
) -> Result<String> {
@@ -367,6 +369,18 @@ async fn update_folder(
}
}
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
DeployedObject::Folder { path: format!("f/{}", name) },
Some(format!("Folder '{}' updated", name)),
rsmq,
true,
)
.await?;
audit_log(
&mut *tx,
&authed,

View File

@@ -12,10 +12,8 @@ use std::collections::HashMap;
use tower_http::cors::CorsLayer;
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_common::{
auth::fetch_authed_from_permissioned_as,
db::UserDB,
error::{self, JsonResult},
users::username_to_permissioned_as,
utils::{not_found_if_none, paginate, require_admin, Pagination, StripPath},
worker::{to_raw_value, CLOUD_HOSTED},
};
@@ -27,7 +25,7 @@ use crate::{
run_flow_by_path_inner, run_script_by_path_inner, run_wait_result_flow_by_path_internal,
run_wait_result_script_by_path_internal, RunJobQuery,
},
users::OptAuthed,
users::{fetch_api_authed, OptAuthed},
};
lazy_static::lazy_static! {
@@ -66,7 +64,6 @@ pub fn workspaced_service() -> Router {
.route("/update/*path", post(update_trigger))
.route("/delete/*path", delete(delete_trigger))
.route("/exists/*path", get(exists_trigger))
.route("/used", get(used))
.route("/route_exists", post(exists_route))
}
@@ -346,17 +343,6 @@ async fn delete_trigger(
Ok(format!("HTTP trigger {path} deleted"))
}
async fn used(Extension(db): Extension<DB>, Path(w_id): Path<String>) -> JsonResult<bool> {
let used = sqlx::query_scalar!(
r#"SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1)"#,
w_id,
)
.fetch_one(&db)
.await?
.unwrap_or(false);
Ok(Json(used))
}
async fn exists_trigger(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
@@ -421,28 +407,6 @@ struct TriggerRoute {
http_method: HttpMethod,
}
async fn fetch_api_authed(
username: String,
email: String,
w_id: &str,
db: &DB,
username_override: String,
) -> error::Result<ApiAuthed> {
let permissioned_as = username_to_permissioned_as(username.as_str());
let authed =
fetch_authed_from_permissioned_as(permissioned_as, email.clone(), w_id, db).await?;
Ok(ApiAuthed {
username: username,
email: email,
is_admin: authed.is_admin,
is_operator: authed.is_operator,
groups: authed.groups,
folders: authed.folders,
scopes: authed.scopes,
username_override: Some(username_override),
})
}
async fn get_http_route_trigger(
route_path: &str,
opt_authed: Option<ApiAuthed>,

View File

@@ -24,7 +24,7 @@ use windmill_common::jobs::{
format_completed_job_result, format_result, CompletedJobWithFormattedResult, FormattedResult,
ENTRYPOINT_OVERRIDE,
};
use windmill_common::worker::TMP_DIR;
use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR};
#[cfg(all(feature = "enterprise", feature = "parquet"))]
use windmill_common::scripts::PREVIEW_IS_CODEBASE_HASH;
@@ -70,7 +70,10 @@ use windmill_common::{
oauth2::HmacSha256,
scripts::{ScriptHash, ScriptLang},
users::username_to_permissioned_as,
utils::{not_found_if_none, now_from_db, paginate, paginate_without_limits, require_admin, Pagination, StripPath},
utils::{
not_found_if_none, now_from_db, paginate, paginate_without_limits, require_admin,
Pagination, StripPath,
},
};
#[cfg(all(feature = "enterprise", feature = "parquet"))]
@@ -81,7 +84,7 @@ use windmill_common::{METRICS_DEBUG_ENABLED, METRICS_ENABLED};
use windmill_common::{get_latest_deployed_hash_for_path, BASE_URL};
use windmill_queue::{
cancel_job, get_queued_job, get_result_by_id_from_running_flow, job_is_complete, push,
DecodeQueries, PushArgs, PushArgsOwned, PushIsolationLevel, QueueTransaction,
DecodeQueries, PushArgs, PushArgsOwned, PushIsolationLevel,
};
#[cfg(feature = "prometheus")]
@@ -544,8 +547,8 @@ pub async fn get_path_for_hash<'c>(
Ok(path)
}
pub async fn get_path_tag_limits_cache_for_hash<'c, R: rsmq_async::RsmqConnection + Send>(
tx: &mut QueueTransaction<'c, R>,
pub async fn get_path_tag_limits_cache_for_hash(
tx: &DB,
w_id: &str,
hash: i64,
) -> error::Result<(
@@ -1469,6 +1472,8 @@ async fn cancel_jobs(
}
}
uuids.extend(trivial_jobs);
Ok(Json(uuids))
}
@@ -2810,8 +2815,6 @@ pub async fn run_flow_by_path_inner(
let flow_path = flow_path.to_path();
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (tag, dedicated_worker, has_preprocessor) = sqlx::query!(
"SELECT tag, dedicated_worker, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor
FROM flow
@@ -2821,7 +2824,7 @@ pub async fn run_flow_by_path_inner(
flow_path,
w_id
)
.fetch_optional(&mut tx)
.fetch_optional(&db)
.await?
.map(|x| (x.tag, x.dedicated_worker, x.has_preprocessor))
.ok_or_else(|| {
@@ -2834,7 +2837,7 @@ pub async fn run_flow_by_path_inner(
check_tag_available_for_workspace(&w_id, &tag).await?;
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
tx,
@@ -2906,14 +2909,12 @@ pub async fn restart_flow(
) -> error::Result<(StatusCode, String)> {
check_license_key_valid().await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let completed_job = sqlx::query_as::<_, CompletedJob>(
"SELECT *, result->'wm_labels' as labels from completed_job WHERE id = $1 and workspace_id = $2",
)
.bind(job_id)
.bind(&w_id)
.fetch_optional(&mut tx)
.fetch_optional(&db)
.await?
.with_context(|| "Unable to find completed job with the given job UUID")?;
@@ -2931,7 +2932,7 @@ pub async fn restart_flow(
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -3007,16 +3008,14 @@ pub async fn run_script_by_path_inner(
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (job_payload, tag, _delete_after_use, timeout) =
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -3049,6 +3048,11 @@ pub async fn run_script_by_path_inner(
Ok((StatusCode::CREATED, uuid.to_string()))
}
#[derive(Deserialize)]
pub struct WorkflowAsCodeQuery {
pub skip_update: Option<bool>,
}
pub async fn run_workflow_as_code(
authed: ApiAuthed,
Extension(db): Extension<DB>,
@@ -3056,15 +3060,32 @@ pub async fn run_workflow_as_code(
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
Path((w_id, job_id, entrypoint)): Path<(String, Uuid, String)>,
Query(run_query): Query<RunJobQuery>,
Query(wkflow_query): Query<WorkflowAsCodeQuery>,
Json(task): Json<WorkflowTask>,
) -> error::Result<(StatusCode, String)> {
let mut i = 1;
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
check_tag_available_for_workspace(&w_id, &run_query.tag).await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
let job = get_queued_job(&job_id, &w_id, &db).await?;
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
let job = not_found_if_none(job, "Queued Job", &job_id.to_string())?;
let (job_payload, tag, _delete_after_use, timeout) = match job.job_kind {
JobKind::Preview => (
@@ -3087,17 +3108,17 @@ pub async fn run_workflow_as_code(
run_query.timeout,
),
JobKind::Script => {
script_path_to_payload(
job.script_path(),
&mut tx,
&w_id,
run_query.skip_preprocessor,
)
.await?
script_path_to_payload(job.script_path(), &db, &w_id, run_query.skip_preprocessor)
.await?
}
_ => return Err(anyhow::anyhow!("Not supported").into()),
};
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
let mut extra = HashMap::new();
extra.insert(ENTRYPOINT_OVERRIDE.to_string(), to_raw_value(&entrypoint));
@@ -3106,7 +3127,17 @@ pub async fn run_workflow_as_code(
let tag = run_query.tag.clone().or(tag).or(Some(job.tag));
let tx = PushIsolationLevel::Transaction(tx);
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
let (uuid, mut tx) = push(
&db,
@@ -3133,14 +3164,35 @@ pub async fn run_workflow_as_code(
Some(&authed.clone().into()),
)
.await?;
sqlx::query!(
"UPDATE queue SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($4::text))) WHERE id = $2 AND workspace_id = $3",
uuid.to_string(),
job_id,
w_id,
entrypoint
).execute(&mut tx).await?;
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
if !wkflow_query.skip_update.unwrap_or(false) {
sqlx::query!(
"UPDATE queue SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($4::text))) WHERE id = $2 AND workspace_id = $3",
uuid.to_string(),
job_id,
w_id,
entrypoint
).execute(&mut tx).await?;
} else {
tracing::info!("Skipping update of flow status for job {job_id} in workspace {w_id}");
}
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
i += 1;
}
tx.commit().await?;
if *CLOUD_HOSTED {
tracing::info!("workflow_as_code_tracing id {i} ");
}
Ok((StatusCode::CREATED, uuid.to_string()))
}
@@ -3504,15 +3556,13 @@ pub async fn run_wait_result_job_by_path_get(
let script_path = script_path.to_path();
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (job_payload, tag, delete_after_use, timeout) =
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -3629,15 +3679,13 @@ pub async fn run_wait_result_script_by_path_internal(
let script_path = script_path.to_path();
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (job_payload, tag, delete_after_use, timeout) =
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -3689,8 +3737,6 @@ pub async fn run_wait_result_script_by_hash(
check_queue_too_long(&db, run_query.queue_limit).await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let hash = script_hash.0;
let (
path,
@@ -3705,7 +3751,7 @@ pub async fn run_wait_result_script_by_hash(
delete_after_use,
timeout,
has_preprocessor,
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
cache_ttl = Some(run_query_cache_ttl);
}
@@ -3714,7 +3760,7 @@ pub async fn run_wait_result_script_by_hash(
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -3796,8 +3842,6 @@ pub async fn run_wait_result_flow_by_path_internal(
let flow_path = flow_path.to_path();
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let (tag, dedicated_worker, early_return, has_preprocessor) = sqlx::query!(
@@ -3809,7 +3853,7 @@ pub async fn run_wait_result_flow_by_path_internal(
flow_path,
w_id
)
.fetch_optional(&mut tx)
.fetch_optional(&db)
.await?
.map(|x| (x.tag, x.dedicated_worker, x.early_return, x.has_preprocessor))
.ok_or_else(|| {
@@ -3821,7 +3865,7 @@ pub async fn run_wait_result_flow_by_path_internal(
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -4127,10 +4171,10 @@ async fn run_dependencies_job(
JsonRawValue::from_string("true".to_string()).unwrap(),
);
if language == ScriptLang::Bun {
let annotation = windmill_common::worker::get_annotation_ts(&raw_code);
let annotation = windmill_common::worker::TypeScriptAnnotations::parse(&raw_code);
hm.insert(
"npm_mode".to_string(),
JsonRawValue::from_string(annotation.npm_mode.to_string()).unwrap(),
JsonRawValue::from_string(annotation.npm.to_string()).unwrap(),
);
}
(PushArgs { extra: Some(hm), args: &ehm }, deps)
@@ -4293,8 +4337,6 @@ async fn add_batch_jobs(
}
}
"flow" => {
let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
let mut uuids: Vec<Uuid> = Vec::new();
let payload = if let Some(ref fv) = batch_info.flow_value {
JobPayload::RawFlow { value: fv.clone(), path: None, restarted_from: None }
@@ -4311,6 +4353,7 @@ async fn add_batch_jobs(
))?
}
};
let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
for _ in 0..n {
let ehm = HashMap::new();
let (uuid, ntx) = push(
@@ -4511,8 +4554,6 @@ pub async fn run_job_by_hash_inner(
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let hash = script_hash.0;
let (
path,
@@ -4527,7 +4568,7 @@ pub async fn run_job_by_hash_inner(
_delete_after_use, // not taken into account in async endpoints
timeout,
has_preprocessor,
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
check_scopes(&authed, || format!("run:script/{path}"))?;
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
cache_ttl = Some(run_query_cache_ttl);
@@ -4536,7 +4577,7 @@ pub async fn run_job_by_hash_inner(
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -4686,7 +4727,6 @@ async fn get_job_update(
&w_id,
job_id,
"progress_perc"
)
.fetch_optional(&db)
.await?.and_then(|inner| inner)

View File

@@ -39,7 +39,7 @@ use tower_http::{
trace::TraceLayer,
};
use windmill_common::db::UserDB;
use windmill_common::worker::ALL_TAGS;
use windmill_common::worker::{ALL_TAGS, CLOUD_HOSTED};
use windmill_common::{BASE_URL, INSTANCE_NAME};
use crate::scim_ee::has_scim_token;
@@ -82,10 +82,12 @@ pub mod smtp_server_ee;
mod static_assets;
mod stripe_ee;
mod tracing_init;
mod triggers;
mod users;
mod utils;
mod variables;
mod webhook_util;
mod websocket_triggers;
mod workers;
mod workspaces;
@@ -224,7 +226,7 @@ pub async fn run_server(
db: db.clone(),
user_db: user_db,
auth_cache: auth_cache.clone(),
rsmq: rsmq,
rsmq: rsmq.clone(),
base_internal_url: base_internal_url.clone(),
});
if let Err(err) = smtp_server.start_listener_thread(addr).await {
@@ -244,6 +246,11 @@ pub async fn run_server(
}
};
if !*CLOUD_HOSTED {
let ws_killpill_rx = rx.resubscribe();
websocket_triggers::start_websockets(db.clone(), rsmq, ws_killpill_rx).await;
}
// build our application with a route
let app = Router::new()
.nest(
@@ -284,7 +291,11 @@ pub async fn run_server(
.nest("/variables", variables::workspaced_service())
.nest("/workspaces", workspaces::workspaced_service())
.nest("/oidc", oidc_ee::workspaced_service())
.nest("/http_triggers", http_triggers::workspaced_service()),
.nest("/http_triggers", http_triggers::workspaced_service())
.nest(
"/websocket_triggers",
websocket_triggers::workspaced_service(),
),
)
.nest("/workspaces", workspaces::global_service())
.nest(
@@ -445,9 +456,13 @@ async fn ee_license() -> &'static str {
#[cfg(feature = "enterprise")]
async fn ee_license() -> String {
use windmill_common::ee::LICENSE_KEY_ID;
use windmill_common::ee::{LICENSE_KEY_ID, LICENSE_KEY_VALID};
LICENSE_KEY_ID.read().await.clone()
if *LICENSE_KEY_VALID.read().await {
LICENSE_KEY_ID.read().await.clone()
} else {
"".to_string()
}
}
async fn openapi() -> &'static str {

View File

@@ -9,6 +9,9 @@
use crate::{
db::{ApiAuthed, DB},
schedule::clear_schedule,
triggers::{
get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail,
},
users::{maybe_refresh_folders, require_owner_of_path, AuthCache},
utils::WithStarredInfoQuery,
webhook_util::{WebhookMessage, WebhookShared},
@@ -53,7 +56,7 @@ use windmill_common::{
utils::{
not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath,
},
worker::{get_annotation_ts, to_raw_value},
worker::to_raw_value,
HUB_BASE_URL,
};
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
@@ -132,6 +135,8 @@ pub fn workspaced_service() -> Router {
.route("/archive/p/*path", post(archive_script_by_path))
.route("/get/draft/*path", get(get_script_by_path_w_draft))
.route("/get/p/*path", get(get_script_by_path))
.route("/get_triggers_count/*path", get(get_triggers_count))
.route("/list_tokens/*path", get(list_tokens))
.route("/raw/p/*path", get(raw_script_by_path))
.route("/raw_unpinned/p/*path", get(raw_script_by_path_unpinned))
.route("/exists/p/*path", get(exists_script_by_path))
@@ -147,6 +152,7 @@ pub fn workspaced_service() -> Router {
post(toggle_workspace_error_handler),
)
.route("/history/p/*path", get(get_script_history))
.route("/get_latest_version/*path", get(get_latest_version))
.route(
"/history_update/h/:hash/p/*path",
post(update_script_history),
@@ -601,8 +607,8 @@ async fn create_script_internal<'c>(
};
let lang = if &ns.language == &ScriptLang::Bun || &ns.language == &ScriptLang::Bunnative {
let anns = get_annotation_ts(&ns.content);
if anns.native_mode {
let anns = windmill_common::worker::TypeScriptAnnotations::parse(&ns.content);
if anns.native {
ScriptLang::Bunnative
} else {
ScriptLang::Bun
@@ -874,6 +880,22 @@ async fn get_script_by_path(
Ok(Json(script))
}
async fn list_tokens(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
let path = path.to_path();
list_tokens_internal(&db, &w_id, &path, false).await
}
async fn get_triggers_count(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<TriggersCount> {
let path = path.to_path();
get_triggers_count_internal(&db, &w_id, &path, false).await
}
async fn get_script_by_path_w_draft(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -927,6 +949,38 @@ async fn get_script_history(
return Ok(Json(result));
}
async fn get_latest_version(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Option<ScriptHistory>> {
let mut tx = user_db.begin(&authed).await?;
let row_o = sqlx::query!(
"SELECT s.hash as hash, dm.deployment_msg as deployment_msg
FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash
WHERE s.workspace_id = $1 AND s.path = $2
ORDER by created_at DESC",
w_id,
path.to_path(),
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
if let Some(row) = row_o {
let result = ScriptHistory {
script_hash: ScriptHash(row.hash),
deployment_msg: row.deployment_msg, //
};
return Ok(Json(Some(result)));
} else {
return Ok(Json(None));
}
}
async fn update_script_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,

View File

@@ -163,8 +163,13 @@ pub async fn test_license_key(
Json(TestKey { license_key }): Json<TestKey>,
) -> error::Result<String> {
require_super_admin(&db, &authed.email).await?;
validate_license_key(license_key).await?;
Ok("Sent test email".to_string())
let (_, expired) = validate_license_key(license_key).await?;
if expired {
Err(error::Error::BadRequest("Expired license key".to_string()))
} else {
Ok("Valid license key".to_string())
}
}
pub async fn get_local_settings(
@@ -301,7 +306,6 @@ pub async fn send_stats(Extension(db): Extension<DB>, authed: ApiAuthed) -> Resu
windmill_common::stats_ee::send_stats(
&HTTP_CLIENT,
&db,
true,
windmill_common::stats_ee::SendStatsReason::Manual,
)
.await?;

View File

@@ -48,7 +48,10 @@ fn serve_path(path: &str) -> Response<Body> {
let mut res = Response::builder()
.header(header::CONTENT_TYPE, mime.as_ref())
.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");
if mime.as_ref() == mime::APPLICATION_JAVASCRIPT || path.ends_with(".wasm") {
if mime.as_ref() == mime::APPLICATION_JAVASCRIPT
|| mime.as_ref() == mime::TEXT_JAVASCRIPT
|| path.ends_with(".wasm")
{
res = res.header(header::CACHE_CONTROL, "max-age=31536000");
} else if (mime.type_(), mime.subtype()) == (mime::TEXT, mime::CSS) {
res = res.header(header::CACHE_CONTROL, "max-age=31536000");

View File

@@ -9,6 +9,7 @@
use ::tracing::{field, Span};
use hyper::Response;
use tower_http::trace::{MakeSpan, OnFailure, OnResponse};
use uuid::Uuid;
lazy_static::lazy_static! {
static ref LOG_REQUESTS: bool = std::env::var("LOG_REQUESTS")
@@ -45,17 +46,28 @@ impl<B> OnFailure<B> for MyOnFailure {
// tracing::error!(latency = latency.as_millis(), "response")
}
}
lazy_static::lazy_static! {
static ref TRACING_HEADER: String = std::env::var("TRACING_HEADER")
.ok().unwrap_or_else(|| "x-tracing-id".to_string());
}
#[derive(Clone)]
pub struct MyMakeSpan {}
impl<B> MakeSpan<B> for MyMakeSpan {
fn make_span(&mut self, request: &hyper::Request<B>) -> Span {
let tracing_id = request
.headers()
.get(TRACING_HEADER.as_str())
.and_then(|x| x.to_str().map(|x| x.to_string()).ok())
.unwrap_or(Uuid::new_v4().to_string());
tracing::info_span!(
"request",
method = %request.method(),
uri = %request.uri(),
username = field::Empty,
workspace_id = field::Empty,
trace_id = tracing_id,
email = field::Empty,
)
}

View File

@@ -0,0 +1,143 @@
use axum::Json;
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use windmill_common::error::JsonResult;
use crate::db::DB;
#[derive(Serialize, Deserialize, Debug)]
pub struct TriggerPrimarySchedule {
schedule: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct TriggersCount {
primary_schedule: Option<TriggerPrimarySchedule>,
schedule_count: i64,
http_routes_count: i64,
webhook_count: i64,
email_count: i64,
websocket_count: i64,
}
pub(crate) async fn get_triggers_count_internal(
db: &DB,
w_id: &str,
path: &str,
is_flow: bool,
) -> JsonResult<TriggersCount> {
let primary_schedule = sqlx::query_scalar!(
"SELECT schedule FROM schedule WHERE path = $1 AND script_path = $1 AND is_flow = $2 AND workspace_id = $3",
path,
is_flow,
w_id
)
.fetch_optional(db)
.await?;
let schedule_count = sqlx::query_scalar!(
"SELECT COUNT(*) FROM schedule WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
path,
is_flow,
w_id
)
.fetch_one(db)
.await?
.unwrap_or(0);
let http_routes_count = sqlx::query_scalar!(
"SELECT COUNT(*) FROM http_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
path,
is_flow,
w_id
)
.fetch_one(db)
.await?
.unwrap_or(0);
let websocket_count = sqlx::query_scalar!(
"SELECT COUNT(*) FROM websocket_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
path,
is_flow,
w_id
)
.fetch_one(db)
.await?
.unwrap_or(0);
let webhook_count = (if is_flow {
sqlx::query_scalar!(
"SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
w_id,
path,
)
} else {
sqlx::query_scalar!(
"SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:' || $2]::text[]",
w_id,
path,
)
}).fetch_one(db)
.await?
.unwrap_or(0);
let email_count = (if is_flow {
sqlx::query_scalar!(
"SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
w_id,
path,
)
} else {
sqlx::query_scalar!(
"SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
w_id,
path,
)
}).fetch_one(db)
.await?
.unwrap_or(0);
Ok(Json(TriggersCount {
primary_schedule: primary_schedule.map(|s| TriggerPrimarySchedule { schedule: s }),
schedule_count,
http_routes_count,
webhook_count,
email_count,
websocket_count,
}))
}
#[derive(FromRow, Serialize)]
pub struct TruncatedTokenWithEmail {
pub label: Option<String>,
pub token_prefix: Option<String>,
pub expiration: Option<chrono::DateTime<chrono::Utc>>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub last_used_at: chrono::DateTime<chrono::Utc>,
pub scopes: Option<Vec<String>>,
pub email: Option<String>,
}
pub async fn list_tokens_internal(
db: &DB,
w_id: &str,
path: &str,
is_flow: bool,
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
let tokens = if is_flow {
sqlx::query_as!(
TruncatedTokenWithEmail,
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
w_id, path).fetch_all(db)
.await?
} else {
sqlx::query_as!(
TruncatedTokenWithEmail,
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
w_id, path)
.fetch_all(db)
.await?
};
Ok(Json(tokens))
}

View File

@@ -48,8 +48,9 @@ use tower_cookies::{Cookie, Cookies};
use tracing::{Instrument, Span};
use windmill_audit::audit_ee::{audit_log, AuditAuthor};
use windmill_audit::ActionKind;
use windmill_common::auth::fetch_authed_from_permissioned_as;
use windmill_common::global_settings::AUTOMATE_USERNAME_CREATION_SETTING;
use windmill_common::users::truncate_token;
use windmill_common::users::{truncate_token, username_to_permissioned_as};
use windmill_common::utils::{paginate, send_email};
use windmill_common::worker::{CLOUD_HOSTED, SMTP_CONFIG};
use windmill_common::{
@@ -128,7 +129,13 @@ pub fn make_unauthed_service() -> Router {
fn username_override_from_label(label: Option<String>) -> Option<String> {
match label {
Some(label) if label.starts_with("webhook-") => Some(label),
Some(label)
if label.starts_with("webhook-")
|| label.starts_with("http-")
|| label.starts_with("email-") =>
{
Some(label)
}
Some(label) if label.starts_with("ephemeral-script-end-user-") => Some(
label
.trim_start_matches("ephemeral-script-end-user-")
@@ -270,9 +277,10 @@ impl AuthCache {
_ => {
let user_o = sqlx::query_as::<_, (Option<String>, Option<String>, bool, Option<Vec<String>>, Option<String>)>(
"UPDATE token SET last_used_at = now() WHERE token = $1 AND (expiration > NOW() \
OR expiration IS NULL) RETURNING owner, email, super_admin, scopes, label",
OR expiration IS NULL) AND (workspace_id IS NULL OR workspace_id = $2) RETURNING owner, email, super_admin, scopes, label",
)
.bind(token)
.bind(w_id.as_ref())
.fetch_optional(&self.db)
.await
.ok()
@@ -698,14 +706,17 @@ where
}
pub fn get_scope_tags(authed: &ApiAuthed) -> Option<Vec<&str>> {
authed
.scopes
.as_ref()?
.iter()
.find_map(|s| match s.split(":").collect::<Vec<_>>().as_slice() {
["if_jobs", "filter_tags", tags] => Some(tags.split(",").collect::<Vec<_>>()),
_ => None,
})
authed.scopes.as_ref()?.iter().find_map(|s| {
if s.starts_with("if_jobs:filter_tags:") {
Some(
s.trim_start_matches("if_jobs:filter_tags:")
.split(",")
.collect::<Vec<_>>(),
)
} else {
None
}
})
}
#[derive(Clone, Debug)]
@@ -729,6 +740,28 @@ where
}
}
pub async fn fetch_api_authed(
username: String,
email: String,
w_id: &str,
db: &DB,
username_override: String,
) -> error::Result<ApiAuthed> {
let permissioned_as = username_to_permissioned_as(username.as_str());
let authed =
fetch_authed_from_permissioned_as(permissioned_as, email.clone(), w_id, db).await?;
Ok(ApiAuthed {
username: username,
email: email,
is_admin: authed.is_admin,
is_operator: authed.is_operator,
groups: authed.groups,
folders: authed.folders,
scopes: authed.scopes,
username_override: Some(username_override),
})
}
#[derive(FromRow, Serialize)]
pub struct User {
pub workspace_id: String,
@@ -756,6 +789,8 @@ pub struct GlobalUserInfo {
name: Option<String>,
company: Option<String>,
username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
operator_only: Option<bool>,
}
#[derive(Serialize, Debug)]
@@ -837,6 +872,7 @@ pub struct NewToken {
pub expiration: Option<chrono::DateTime<chrono::Utc>>,
pub impersonate_email: Option<String>,
pub scopes: Option<Vec<String>>,
pub workspace_id: Option<String>,
}
#[derive(Deserialize)]
@@ -967,24 +1003,48 @@ async fn list_user_usage(
Ok(Json(rows))
}
#[derive(Deserialize)]
struct ActiveUsersOnly {
active_only: Option<bool>,
}
async fn list_users_as_super_admin(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Query(pagination): Query<Pagination>,
Query(ActiveUsersOnly { active_only }): Query<ActiveUsersOnly>,
) -> JsonResult<Vec<GlobalUserInfo>> {
require_super_admin(&db, &authed.email).await?;
let per_page = pagination.per_page.unwrap_or(10000).max(1);
let offset = (pagination.page.unwrap_or(1).max(1) - 1) * per_page;
let rows = sqlx::query_as!(
GlobalUserInfo,
"SELECT email, login_type::text, verified, super_admin, name, company, username from password ORDER BY super_admin DESC, email LIMIT \
$1 OFFSET $2",
per_page as i32,
offset as i32
)
.fetch_all(&db)
.await?;
let rows = if active_only.is_some_and(|x| x) {
sqlx::query_as!(
GlobalUserInfo,
"WITH active_users AS (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login')),
authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)
SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, name, company, username
FROM password
WHERE email IN (SELECT email FROM active_users)
ORDER BY super_admin DESC
LIMIT $1 OFFSET $2",
per_page as i32,
offset as i32
)
.fetch_all(&db)
.await?
} else {
sqlx::query_as!(
GlobalUserInfo,
"SELECT email, login_type::text, verified, super_admin, name, company, username, NULL::bool as operator_only FROM password ORDER BY super_admin DESC, email LIMIT \
$1 OFFSET $2",
per_page as i32,
offset as i32
)
.fetch_all(&db)
.await?
};
Ok(Json(rows))
}
@@ -1139,7 +1199,7 @@ async fn global_whoami(
) -> JsonResult<GlobalUserInfo> {
let user = sqlx::query_as!(
GlobalUserInfo,
"SELECT email, login_type::TEXT, super_admin, verified, name, company, username FROM password WHERE \
"SELECT email, login_type::TEXT, super_admin, verified, name, company, username, NULL::bool as operator_only FROM password WHERE \
email = $1",
email
)
@@ -1158,6 +1218,7 @@ async fn global_whoami(
name: None,
company: None,
username: None,
operator_only: None,
}))
} else {
Err(user.unwrap_err())
@@ -2256,6 +2317,8 @@ async fn login(
) -> Result<String> {
let mut tx = db.begin().await?;
let email = email.to_lowercase();
let audit_author =
AuditAuthor { email: email.clone(), username: email.clone(), username_override: None };
let email_w_h: Option<(String, String, bool, bool)> = sqlx::query_as(
"SELECT email, password_hash, super_admin, first_time_user FROM password WHERE email = $1 AND login_type = \
'password'",
@@ -2271,6 +2334,16 @@ async fn login(
.verify_password(password.as_bytes(), &parsed_hash)
.is_err()
{
audit_log(
&mut *tx,
&audit_author,
"users.login_failure",
ActionKind::Create,
"global",
None,
None,
)
.await?;
Err(Error::BadRequest("Invalid login".to_string()))
} else {
if first_time_user {
@@ -2296,11 +2369,7 @@ async fn login(
audit_log(
&mut *tx,
&AuditAuthor {
username: email.clone(),
email: email.clone(),
username_override: None,
},
&audit_author,
"users.login",
ActionKind::Create,
"global",
@@ -2313,6 +2382,16 @@ async fn login(
Ok(token)
}
} else {
audit_log(
&mut *tx,
&audit_author,
"users.login_failure",
ActionKind::Create,
"global",
None,
None,
)
.await?;
Err(Error::BadRequest("Invalid login".to_string()))
}
}
@@ -2389,14 +2468,15 @@ async fn create_token(
.unwrap_or(false);
sqlx::query!(
"INSERT INTO token
(token, email, label, expiration, super_admin, scopes)
VALUES ($1, $2, $3, $4, $5, $6)",
(token, email, label, expiration, super_admin, scopes, workspace_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)",
token,
authed.email,
new_token.label,
new_token.expiration,
is_super_admin,
new_token.scopes.as_ref().map(|x| x.as_slice())
new_token.scopes.as_ref().map(|x| x.as_slice()),
new_token.workspace_id,
)
.execute(&mut *tx)
.await?;

View File

@@ -0,0 +1,684 @@
use axum::{
extract::{Path, Query},
routing::{delete, get, post},
Extension, Json, Router,
};
use futures::StreamExt;
use http::StatusCode;
use itertools::Itertools;
use rand::seq::SliceRandom;
use serde::{
de::{self, MapAccess, Visitor},
Deserialize, Deserializer, Serialize,
};
use serde_json::Value;
use sql_builder::{bind::Bind, SqlBuilder};
use sqlx::prelude::FromRow;
use std::{collections::HashMap, fmt};
use tokio_tungstenite::connect_async;
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_common::{
db::UserDB,
error::{self, JsonResult},
utils::{not_found_if_none, paginate, require_admin, Pagination, StripPath},
worker::{to_raw_value, CLOUD_HOSTED},
INSTANCE_NAME,
};
use windmill_queue::PushArgsOwned;
use crate::{
db::{ApiAuthed, DB},
jobs::{
run_wait_result_flow_by_path_internal, run_wait_result_script_by_path_internal, RunJobQuery,
},
users::fetch_api_authed,
};
pub fn workspaced_service() -> Router {
Router::new()
.route("/create", post(create_websocket_trigger))
.route("/list", get(list_websocket_triggers))
.route("/get/*path", get(get_websocket_trigger))
.route("/update/*path", post(update_websocket_trigger))
.route("/delete/*path", delete(delete_websocket_trigger))
.route("/exists/*path", get(exists_websocket_trigger))
.route("/setenabled/*path", post(set_enabled))
}
#[derive(Deserialize)]
struct NewWebsocketTrigger {
path: String,
url: String,
script_path: String,
is_flow: bool,
enabled: Option<bool>,
filters: Vec<serde_json::Value>,
}
#[derive(FromRow, Serialize, Clone)]
pub struct WebsocketTrigger {
workspace_id: String,
path: String,
url: String,
script_path: String,
is_flow: bool,
edited_by: String,
email: String,
edited_at: chrono::DateTime<chrono::Utc>,
server_id: Option<String>,
last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
extra_perms: serde_json::Value,
error: Option<String>,
enabled: bool,
filters: Vec<serde_json::Value>,
}
#[derive(Deserialize)]
struct EditWebsocketTrigger {
path: String,
url: String,
script_path: String,
is_flow: bool,
filters: Vec<serde_json::Value>,
}
#[derive(Deserialize)]
pub struct ListWebsocketTriggerQuery {
pub page: Option<usize>,
pub per_page: Option<usize>,
pub path: Option<String>,
pub is_flow: Option<bool>,
pub path_start: Option<String>,
}
async fn list_websocket_triggers(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path(w_id): Path<String>,
Query(lst): Query<ListWebsocketTriggerQuery>,
) -> error::JsonResult<Vec<WebsocketTrigger>> {
let mut tx = user_db.begin(&authed).await?;
let (per_page, offset) = paginate(Pagination { per_page: lst.per_page, page: lst.page });
let mut sqlb = SqlBuilder::select_from("websocket_trigger")
.field("*")
.order_by("edited_at", true)
.and_where("workspace_id = ?".bind(&w_id))
.offset(offset)
.limit(per_page)
.clone();
if let Some(path) = lst.path {
sqlb.and_where_eq("script_path", "?".bind(&path));
}
if let Some(is_flow) = lst.is_flow {
sqlb.and_where_eq("is_flow", "?".bind(&is_flow));
}
if let Some(path_start) = &lst.path_start {
sqlb.and_where_like_left("path", path_start);
}
let sql = sqlb
.sql()
.map_err(|e| error::Error::InternalErr(e.to_string()))?;
let rows = sqlx::query_as::<_, WebsocketTrigger>(&sql)
.fetch_all(&mut *tx)
.await?;
tx.commit().await?;
Ok(Json(rows))
}
async fn get_websocket_trigger(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> error::JsonResult<WebsocketTrigger> {
let mut tx = user_db.begin(&authed).await?;
let path = path.to_path();
let trigger = sqlx::query_as!(
WebsocketTrigger,
r#"SELECT *
FROM websocket_trigger
WHERE workspace_id = $1 AND path = $2"#,
w_id,
path,
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
let trigger = not_found_if_none(trigger, "Trigger", path)?;
Ok(Json(trigger))
}
async fn create_websocket_trigger(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path(w_id): Path<String>,
Json(ct): Json<NewWebsocketTrigger>,
) -> error::Result<(StatusCode, String)> {
if *CLOUD_HOSTED {
return Err(error::Error::BadRequest(
"Websocket triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(),
));
}
require_admin(authed.is_admin, &authed.username)?;
let mut tx = user_db.begin(&authed).await?;
sqlx::query_as!(
WebsocketTrigger,
"INSERT INTO websocket_trigger (workspace_id, path, url, script_path, is_flow, enabled, filters, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, now()) RETURNING *",
w_id,
ct.path,
ct.url,
ct.script_path,
ct.is_flow,
ct.enabled.unwrap_or(true),
&ct.filters,
&authed.username,
&authed.email
)
.fetch_one(&mut *tx).await?;
audit_log(
&mut *tx,
&authed,
"websocket_triggers.create",
ActionKind::Create,
&w_id,
Some(ct.path.as_str()),
None,
)
.await?;
tx.commit().await?;
Ok((StatusCode::CREATED, format!("{}", ct.path)))
}
async fn update_websocket_trigger(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
Json(ct): Json<EditWebsocketTrigger>,
) -> error::Result<String> {
let path = path.to_path();
let mut tx = user_db.begin(&authed).await?;
// important to update server_id, last_server_ping and error to NULL to stop current websocket listener
sqlx::query!(
"UPDATE websocket_trigger SET url = $1, script_path = $2, path = $3, is_flow = $4, filters = $5, edited_by = $6, email = $7, edited_at = now(), server_id = NULL, last_server_ping = NULL, error = NULL
WHERE workspace_id = $8 AND path = $9",
ct.url,
ct.script_path,
ct.path,
ct.is_flow,
&ct.filters,
&authed.username,
&authed.email,
w_id,
path,
)
.execute(&mut *tx).await?;
audit_log(
&mut *tx,
&authed,
"websocket_triggers.update",
ActionKind::Create,
&w_id,
Some(path),
None,
)
.await?;
tx.commit().await?;
Ok(path.to_string())
}
#[derive(Deserialize)]
pub struct SetEnabled {
pub enabled: bool,
}
pub async fn set_enabled(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
Json(payload): Json<SetEnabled>,
) -> error::Result<String> {
let mut tx = user_db.begin(&authed).await?;
let path = path.to_path();
// important to set server_id, last_server_ping and error to NULL to stop current websocket listener
let one_o = sqlx::query_scalar!(
"UPDATE websocket_trigger SET enabled = $1, email = $2, edited_by = $3, edited_at = now(), server_id = NULL, last_server_ping = NULL, error = NULL
WHERE path = $4 AND workspace_id = $5 RETURNING 1",
payload.enabled,
&authed.email,
&authed.username,
path,
w_id,
).fetch_optional(&mut *tx).await?;
not_found_if_none(one_o.flatten(), "Websocket trigger", path)?;
audit_log(
&mut *tx,
&authed,
"websocket_triggers.setenabled",
ActionKind::Update,
&w_id,
Some(path),
Some([("enabled", payload.enabled.to_string().as_ref())].into()),
)
.await?;
tx.commit().await?;
Ok(format!(
"succesfully updated websocket trigger at path {} to status {}",
path, payload.enabled
))
}
async fn delete_websocket_trigger(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> error::Result<String> {
require_admin(authed.is_admin, &authed.username)?;
let path = path.to_path();
let mut tx = user_db.begin(&authed).await?;
sqlx::query!(
"DELETE FROM websocket_trigger WHERE workspace_id = $1 AND path = $2",
w_id,
path,
)
.execute(&mut *tx)
.await?;
audit_log(
&mut *tx,
&authed,
"websocket_triggers.delete",
ActionKind::Delete,
&w_id,
Some(path),
None,
)
.await?;
tx.commit().await?;
Ok(format!("Websocket trigger {path} deleted"))
}
async fn exists_websocket_trigger(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<bool> {
let path = path.to_path();
let exists = sqlx::query_scalar!(
"SELECT EXISTS(SELECT 1 FROM websocket_trigger WHERE path = $1 AND workspace_id = $2)",
path,
w_id,
)
.fetch_one(&db)
.await?
.unwrap_or(false);
Ok(Json(exists))
}
async fn listen_to_unlistened_websockets(
db: &DB,
rsmq: &Option<rsmq_async::MultiplexedRsmq>,
killpill_rx: &tokio::sync::broadcast::Receiver<()>,
) -> () {
match sqlx::query_as!(
WebsocketTrigger,
r#"SELECT *
FROM websocket_trigger
WHERE enabled IS TRUE AND (server_id IS NULL OR last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')"#
)
.fetch_all(db)
.await
{
Ok(mut triggers) => {
triggers.shuffle(&mut rand::thread_rng());
for trigger in triggers {
maybe_listen_to_websocket(trigger, db.clone(), rsmq.clone(), killpill_rx.resubscribe()).await;
}
}
Err(err) => {
tracing::error!("Error fetching websocket triggers: {:?}", err);
}
};
}
pub async fn start_websockets(
db: DB,
rsmq: Option<rsmq_async::MultiplexedRsmq>,
mut killpill_rx: tokio::sync::broadcast::Receiver<()>,
) -> () {
tokio::spawn(async move {
listen_to_unlistened_websockets(&db, &rsmq, &killpill_rx).await;
loop {
tokio::select! {
biased;
_ = killpill_rx.recv() => {
return;
}
_ = tokio::time::sleep(tokio::time::Duration::from_secs(15)) => {
listen_to_unlistened_websockets(&db, &rsmq, &killpill_rx).await;
}
}
}
});
}
async fn maybe_listen_to_websocket(
ws_trigger: WebsocketTrigger,
db: DB,
rsmq: Option<rsmq_async::MultiplexedRsmq>,
killpill_rx: tokio::sync::broadcast::Receiver<()>,
) -> () {
match sqlx::query_scalar!(
"UPDATE websocket_trigger SET server_id = $1, last_server_ping = now() WHERE enabled IS TRUE AND workspace_id = $2 AND path = $3 AND (server_id IS NULL OR last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') RETURNING true",
*INSTANCE_NAME,
ws_trigger.workspace_id,
ws_trigger.path,
).fetch_optional(&db).await {
Ok(has_lock) => {
if has_lock.flatten().unwrap_or(false) {
tokio::spawn(listen_to_websocket(ws_trigger, db, rsmq, killpill_rx));
} else {
tracing::info!("Websocket {} already being listened to", ws_trigger.url);
}
},
Err(err) => {
tracing::error!("Error acquiring lock for websocket {}: {:?}", ws_trigger.path, err);
}
};
}
struct SupersetVisitor<'a> {
key: &'a str,
value_to_check: &'a Value,
}
impl<'de, 'a> Visitor<'de> for SupersetVisitor<'a> {
type Value = bool;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a JSON object with a specific key at the top level")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'de>,
{
while let Some(key) = map.next_key::<String>()? {
if key == self.key {
// Deserialize the value for the key and check if it's a superset
let json_value: Value = map.next_value()?;
tracing::info!("json_value: {:?}", json_value);
tracing::info!("value_to_check: {:?}", self.value_to_check);
return Ok(is_superset(&json_value, self.value_to_check));
} else {
// Skip the value if it's not the one we're interested in
let _ = map.next_value::<de::IgnoredAny>()?;
}
}
// If the key was not found, return false
Ok(false)
}
}
// Function to check if json_value is a superset of value_to_check
fn is_superset(json_value: &Value, value_to_check: &Value) -> bool {
match (json_value, value_to_check) {
(Value::Object(json_map), Value::Object(check_map)) => {
// Check that all keys and values in check_map exist and match in json_map
check_map.iter().all(|(k, v)| {
json_map
.get(k)
.map_or(false, |json_val| is_superset(json_val, v))
})
}
(Value::Array(json_array), Value::Array(check_array)) => {
// Check that all elements in check_array exist in json_array
check_array.iter().all(|check_item| {
json_array
.iter()
.any(|json_item| is_superset(json_item, check_item))
})
}
_ => json_value == value_to_check,
}
}
// A function to deserialize and check if the value at the given key is a superset of a passed value
fn is_value_superset<'a, 'de, D>(
deserializer: D,
key: &'a str,
value_to_check: &'a Value,
) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(SupersetVisitor { key, value_to_check })
}
async fn listen_to_websocket(
ws_trigger: WebsocketTrigger,
db: DB,
rsmq: Option<rsmq_async::MultiplexedRsmq>,
mut killpill_rx: tokio::sync::broadcast::Receiver<()>,
) -> () {
async fn update_ping(db: DB, ws_trigger: &WebsocketTrigger, error: Option<&str>) -> Option<()> {
match sqlx::query_scalar!(
"UPDATE websocket_trigger SET last_server_ping = now(), error = $1 WHERE workspace_id = $2 AND path = $3 AND server_id = $4 AND enabled IS TRUE RETURNING 1",
error,
ws_trigger.workspace_id,
ws_trigger.path,
*INSTANCE_NAME
).fetch_optional(&db).await {
Ok(updated) => {
if updated.flatten().is_none() {
tracing::info!("Websocket {} changed, disabled, or deleted, stopping...", ws_trigger.url);
return None;
}
},
Err(err) => {
tracing::warn!("Error updating ping of websocket {}: {:?}", ws_trigger.url, err);
}
};
Some(())
}
let url = ws_trigger.url.as_str();
#[derive(Deserialize)]
struct JsonFilter {
key: String,
value: serde_json::Value,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum Filter {
JsonFilter(JsonFilter),
}
let filters: Vec<Filter> = ws_trigger
.filters
.iter()
.filter_map(|m| serde_json::from_value(m.clone()).ok())
.collect_vec();
loop {
tokio::select! {
biased;
_ = killpill_rx.recv() => {
return;
},
connection = connect_async(url) => {
match connection {
Ok((ws_stream, _)) => {
tracing::info!("Listening to websocket {}", url);
if let None = update_ping(db.clone(), &ws_trigger, None).await {
return;
}
let mut last_ping = tokio::time::Instant::now();
let (_, mut read) = ws_stream.split();
loop {
tokio::select! {
biased;
_ = killpill_rx.recv() => {
return;
}
msg = read.next() => {
if let Some(msg) = msg {
if last_ping.elapsed() > tokio::time::Duration::from_secs(5) {
if let None = update_ping(db.clone(), &ws_trigger, None).await {
return;
}
last_ping = tokio::time::Instant::now();
}
match msg {
Ok(msg) => {
match msg {
tokio_tungstenite::tungstenite::Message::Text(text) => {
let mut should_handle = true;
for filter in &filters {
match filter {
Filter::JsonFilter(JsonFilter { key, value }) => {
let mut deserializer = serde_json::Deserializer::from_str(text.as_str());
should_handle = match is_value_superset(&mut deserializer, key, &value) {
Ok(filter_match) => {
filter_match
},
Err(err) => {
tracing::warn!("Error deserializing filter for websocket {}: {:?}", url, err);
false
}
};
}
}
if !should_handle {
break;
}
}
if should_handle {
let db_ = db.clone();
let rsmq_ = rsmq.clone();
let ws_trigger_ = ws_trigger.clone();
tokio::spawn(async move {
let url = ws_trigger_.url.clone();
if let Err(err) = run_job(db_, rsmq_, ws_trigger_, text).await {
tracing::error!("Error running job on websocket {}: {:?}", url, err);
};
});
}
},
_ => {}
}
},
Err(err) => {
tracing::error!("Error reading from websocket {}: {:?}", url, err);
}
}
} else {
tracing::error!("Websocket {} closed", url);
if let None =
update_ping(db.clone(), &ws_trigger, Some("Websocket closed")).await
{
return;
}
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
break;
}
},
_ = tokio::time::sleep(tokio::time::Duration::from_secs(5)) => {
if let None = update_ping(db.clone(), &ws_trigger, None).await {
return;
}
last_ping = tokio::time::Instant::now();
},
}
}
}
Err(err) => {
tracing::error!("Error connecting to websocket {}: {:?}", url, err);
if let None =
update_ping(db.clone(), &ws_trigger, Some(err.to_string().as_str())).await
{
return;
}
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
}
}
}
}
}
}
async fn run_job(
db: DB,
rsmq: Option<rsmq_async::MultiplexedRsmq>,
trigger: WebsocketTrigger,
msg: String,
) -> anyhow::Result<()> {
let args = PushArgsOwned {
args: HashMap::from([("msg".to_string(), to_raw_value(&msg))]),
extra: Some(HashMap::from([(
"wm_trigger".to_string(),
to_raw_value(&serde_json::json!({"kind": "websocket"})),
)])),
};
let label_prefix = Some(format!("ws-{}-", trigger.path));
let authed = fetch_api_authed(
trigger.edited_by.clone(),
trigger.email.clone(),
&trigger.workspace_id,
&db,
"anonymous".to_string(),
)
.await?;
let user_db = UserDB::new(db.clone());
let run_query = RunJobQuery::default();
if trigger.is_flow {
run_wait_result_flow_by_path_internal(
db,
run_query,
StripPath(trigger.script_path.to_owned()),
authed,
rsmq,
user_db,
args,
trigger.workspace_id.clone(),
label_prefix,
)
.await?;
} else {
run_wait_result_script_by_path_internal(
db,
run_query,
StripPath(trigger.script_path.to_owned()),
authed,
rsmq,
user_db,
trigger.workspace_id.clone(),
args,
label_prefix,
)
.await?;
}
Ok(())
}

View File

@@ -116,7 +116,8 @@ pub fn workspaced_service() -> Router {
.route("/get_workspace_name", get(get_workspace_name))
.route("/change_workspace_name", post(change_workspace_name))
.route("/change_workspace_id", post(change_workspace_id))
.route("/usage", get(get_usage));
.route("/usage", get(get_usage))
.route("/used_triggers", get(get_used_triggers));
#[cfg(feature = "stripe")]
{
@@ -1488,6 +1489,30 @@ async fn set_encryption_key(
return Ok(());
}
#[derive(Serialize)]
struct UsedTriggers {
pub websocket_used: bool,
pub http_routes_used: bool,
}
async fn get_used_triggers(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path(w_id): Path<String>,
) -> JsonResult<UsedTriggers> {
let mut tx = user_db.begin(&authed).await?;
let websocket_used = sqlx::query_as!(
UsedTriggers,
r#"SELECT EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) as "websocket_used!", EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) as "http_routes_used!""#,
w_id,
)
.fetch_one(&mut *tx)
.await?;
tx.commit().await?;
Ok(Json(websocket_used))
}
async fn list_workspaces_as_super_admin(
authed: ApiAuthed,
Extension(db): Extension<DB>,

View File

@@ -0,0 +1,24 @@
[package]
name = "windmill-autoscaling"
version.workspace = true
authors.workspace = true
edition.workspace = true
[lib]
name = "windmill_autoscaling"
path = "./src/lib.rs"
[features]
enterprise = ["windmill-queue/enterprise", "windmill-common/enterprise"]
default = []
[dependencies]
uuid.workspace = true
serde.workspace = true
sqlx.workspace = true
serde_json.workspace = true
tracing.workspace = true
windmill-common = { workspace = true, default-features = false }
windmill-queue.workspace = true
rsmq_async.workspace = true
anyhow.workspace = true

View File

@@ -0,0 +1,6 @@
use windmill_common::DB;
pub async fn apply_all_autoscaling(_db: &DB) -> anyhow::Result<()> {
// Autoscaling is an ee feature
Ok(())
}

View File

@@ -0,0 +1,2 @@
mod autoscaling_ee;
pub use autoscaling_ee::*;

View File

@@ -57,6 +57,7 @@ futures-core.workspace = true
async-stream.workspace = true
const_format.workspace = true
crc.workspace = true
windmill-macros.workspace = true
[target.'cfg(not(target_env = "msvc"))'.dependencies]
tikv-jemalloc-ctl = { optional = true, workspace = true }

View File

@@ -48,8 +48,13 @@ pub async fn send_critical_alert(
}
#[cfg(feature = "enterprise")]
pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::db::DB) -> () {
pub async fn maybe_renew_license_key_on_start(
_http_client: &reqwest::Client,
_db: &crate::db::DB,
force_renew_now: bool,
) -> bool {
// Implementation is not open source
force_renew_now
}
#[cfg(feature = "enterprise")]
@@ -81,3 +86,6 @@ pub async fn create_customer_portal_session(
#[cfg(feature = "enterprise")]
pub async fn worker_groups_alerts(_db: &DB) {}
#[cfg(feature = "enterprise")]
pub async fn jobs_waiting_alerts(_db: &DB) {}

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